1fd0c5291a
- Added marching_ants_offset to HcieIcedApp for animation state - Added selection_bounds field to IcedDocument for persistent selection display - Updated OverlayProgram to draw animated marching ants (black + white dashed lines) - Added timer subscription for continuous animation when selection is active - Updated SelectAll/Deselect handlers to properly manage selection state - Selection now remains visible after creation with animated border
289 lines
9.3 KiB
Markdown
289 lines
9.3 KiB
Markdown
# Iced GUI Selection Features Design
|
|
|
|
## [S1] Problem
|
|
|
|
The iced GUI migration is missing critical selection-based features that exist in the egui GUI:
|
|
|
|
1. **#1350 Move/Rotate/Scale** - No transform handles on selections
|
|
2. **#1351 Raster Copy/Paste** - Only copies entire canvas, not selected pixels
|
|
3. **#1352 Vector Copy/Paste** - No vector shape selection/copy/paste
|
|
4. **Selection menu** - Missing feather, grow, shrink, border, fill operations
|
|
5. **Crop tool** - Broken (Enter/double-click don't work, selection mixes between images, shows marching ants instead of proper handles)
|
|
|
|
## [S2] Solution Overview
|
|
|
|
### Core Architecture Changes
|
|
|
|
#### 1. SelectionTransform State (in `IcedDocument`)
|
|
Add to `IcedDocument`:
|
|
```rust
|
|
pub struct IcedDocument {
|
|
// ... existing fields ...
|
|
|
|
/// Active selection transform (floating pixels after cut/copy-paste)
|
|
pub selection_transform: Option<SelectionTransform>,
|
|
/// Transform handle being dragged
|
|
pub transform_drag_handle: TransformHandle,
|
|
/// Internal clipboard for copy/paste operations
|
|
pub internal_clipboard: Option<SelectionTransform>,
|
|
/// Selection mask (alpha channel) for the current selection
|
|
pub selection_mask: Option<Vec<u8>>,
|
|
/// Selection bounds (x, y, width, height)
|
|
pub selection_bounds: Option<(u32, u32, u32, u32)>,
|
|
}
|
|
```
|
|
|
|
#### 2. SelectionTransform Struct
|
|
```rust
|
|
#[derive(Clone)]
|
|
pub struct SelectionTransform {
|
|
pub pixels: Vec<u8>,
|
|
pub width: u32,
|
|
pub height: u32,
|
|
pub pos: iced::Vector,
|
|
pub size: iced::Vector,
|
|
pub rotation: f32, // radians
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
|
pub enum TransformHandle {
|
|
#[default]
|
|
None,
|
|
Move,
|
|
TopLeft,
|
|
TopRight,
|
|
BottomRight,
|
|
BottomLeft,
|
|
Top,
|
|
Bottom,
|
|
Left,
|
|
Right,
|
|
Rotate, // rotation handle (outside corner)
|
|
}
|
|
```
|
|
|
|
#### 3. CropState Struct
|
|
```rust
|
|
#[derive(Clone, Default)]
|
|
pub struct CropState {
|
|
/// Crop rectangle in canvas coordinates
|
|
pub rect: Option<(f32, f32, f32, f32)>,
|
|
/// Whether crop is being dragged
|
|
pub is_dragging: bool,
|
|
/// Drag start position
|
|
pub drag_start: Option<(f32, f32)>,
|
|
/// Whether to show crop overlay
|
|
pub active: bool,
|
|
}
|
|
```
|
|
|
|
## [S3] Feature Details
|
|
|
|
### Feature #1350: Raster Move & Rotate & Scale
|
|
|
|
**UI Requirements:**
|
|
- Selection shows 8 resize handles (corners + edges) + rotation handle
|
|
- Handles are 8x8px squares at zoom=1, scaled by 1/zoom
|
|
- Rotation handle is a small circle above the top-center handle
|
|
- Dotted rectangle around selection when in transform mode
|
|
- Cursor changes based on handle (resize arrows, move crosshair, rotation arc)
|
|
|
|
**Implementation:**
|
|
1. Add `selection_transform: Option<SelectionTransform>` to `IcedDocument`
|
|
2. Extend `OverlayProgram` to draw transform handles
|
|
3. Add `TransformHandle` hit-testing in overlay `update()`
|
|
4. Handle mouse drag on each handle type in `app.rs`
|
|
5. Apply transform on Enter key or double-click
|
|
|
|
**Messages to Add:**
|
|
```rust
|
|
Message::TransformStart(usize), // handle index
|
|
Message::TransformMove(f32, f32),
|
|
Message::TransformEnd,
|
|
Message::TransformApply,
|
|
Message::TransformCancel,
|
|
Message::TransformRotate(f32),
|
|
```
|
|
|
|
### Feature #1351: Raster Selection Copy & Paste
|
|
|
|
**UI Requirements:**
|
|
- Copy (Ctrl+C) copies only selected pixels (respecting mask)
|
|
- Paste (Ctrl+V) creates floating layer with copied pixels
|
|
- Cut (Ctrl+X) copies and clears selected pixels
|
|
- Internal clipboard stores SelectionTransform
|
|
- Paste positions at selection origin or center of viewport
|
|
|
|
**Implementation:**
|
|
1. On Copy: extract pixels within selection mask, store in `internal_clipboard`
|
|
2. On Paste: create `SelectionTransform` from clipboard, enter transform mode
|
|
3. On Cut: copy then clear selected pixels in active layer
|
|
4. On transform apply: composite floating pixels back to layer
|
|
|
|
**Engine API Calls:**
|
|
```rust
|
|
// Copy selected pixels
|
|
let pixels = engine.get_active_layer_pixels();
|
|
let mask = engine.get_selection_mask();
|
|
let selected = extract_masked_pixels(pixels, mask, canvas_w, canvas_h);
|
|
|
|
// Cut
|
|
engine.clear_selection(); // may need to add this API
|
|
|
|
// Paste
|
|
engine.add_layer("Floating");
|
|
engine.set_active_layer(new_layer_id);
|
|
engine.draw_pixels_at(floating_pixels, x, y);
|
|
```
|
|
|
|
### Feature #1352: Vector Selection Copy & Paste
|
|
|
|
**UI Requirements:**
|
|
- VectorSelect tool can click to select vector shapes
|
|
- Selected shapes show bounding box with handles
|
|
- Copy/Paste duplicates selected vector shapes
|
|
- Paste positions at offset from original
|
|
|
|
**Implementation:**
|
|
1. Add `selected_vector_indices: Vec<usize>` to state
|
|
2. On VectorSelect click: hit-test vector shapes, select closest
|
|
3. On Copy: store selected VectorShape data in clipboard
|
|
4. On Paste: add duplicated shapes with offset
|
|
5. Draw selection bounding box in overlay
|
|
|
|
**Messages to Add:**
|
|
```rust
|
|
Message::VectorSelectAt(f32, f32),
|
|
Message::VectorSelectToggle(usize),
|
|
Message::VectorCopy,
|
|
Message::VectorPaste,
|
|
Message::VectorCut,
|
|
Message::VectorDelete,
|
|
```
|
|
|
|
### Selection Menu Improvements
|
|
|
|
**Menu Items to Implement:**
|
|
- Select > All (Ctrl+A) - ✅ already exists
|
|
- Select > Deselect (Ctrl+D) - ✅ already exists
|
|
- Select > Inverse (Shift+Ctrl+I) - needs implementation
|
|
- Select > Grow - ✅ dialog exists, needs engine call
|
|
- Select > Shrink - needs implementation
|
|
- Select > Feather - needs implementation
|
|
- Select > Border - needs implementation
|
|
- Select > Fill - needs implementation
|
|
|
|
**Implementation:**
|
|
1. Add engine API methods if missing:
|
|
- `engine.invert_selection()`
|
|
- `engine.shrink_selection(px)`
|
|
- `engine.feather_selection(px)`
|
|
- `engine.border_selection(px)`
|
|
- `engine.fill_selection(color)`
|
|
2. Wire menu items to these methods
|
|
3. Add dialog for parameter input (px value)
|
|
|
|
### Crop Tool Fixes
|
|
|
|
**Issues to Fix:**
|
|
1. Enter key not working → Wire keyboard event to crop confirm
|
|
2. Double-click not working → Add double-click handler
|
|
3. Selection mixing between images → Store crop state per-document
|
|
4. Marching ants instead of handles → Draw proper crop handles
|
|
|
|
**Implementation:**
|
|
1. Add `crop_state: CropState` to `IcedDocument` (per-document)
|
|
2. Draw crop overlay with 8 handles + rule-of-thirds grid
|
|
3. Handle Enter/double-click to confirm crop
|
|
4. Handle Escape to cancel crop
|
|
5. Apply crop via `engine.crop(x, y, w, h)`
|
|
|
|
**Crop Overlay Drawing:**
|
|
```
|
|
┌─────────────────────┐
|
|
│ │ │ │ │ ← rule of thirds
|
|
│─────┼─────┼─────│ │
|
|
│ │ │ │ │
|
|
│─────┼─────┼─────│ │
|
|
│ │ │ │ │
|
|
└─────────────────────┘
|
|
□ □ ← corner handles
|
|
□ □ ← edge handles
|
|
```
|
|
|
|
## [S4] Files to Modify
|
|
|
|
### Primary Files
|
|
1. `hcie-iced-app/crates/hcie-iced-gui/src/app.rs` - Main state and update logic
|
|
2. `hcie-iced-app/crates/hcie-iced-gui/src/canvas/mod.rs` - Overlay drawing
|
|
3. `hcie-iced-app/crates/hcie-iced-gui/src/panels/menus.rs` - Menu definitions
|
|
4. `hcie-iced-app/crates/hcie-iced-gui/src/panels/tool_options.rs` - Tool options
|
|
|
|
### New Files (if needed)
|
|
1. `hcie-iced-app/crates/hcie-iced-gui/src/selection/transform.rs` - Transform logic
|
|
2. `hcie-iced-app/crates/hcie-iced-gui/src/selection/crop.rs` - Crop tool logic
|
|
|
|
## [S5] Implementation Order
|
|
|
|
1. **Phase 1: Core Selection State**
|
|
- Add SelectionTransform, TransformHandle, CropState structs
|
|
- Add selection_mask, selection_bounds to IcedDocument
|
|
- Basic selection drawing improvements
|
|
|
|
2. **Phase 2: Transform Handles**
|
|
- Draw 8 resize handles + rotation handle
|
|
- Handle hit-testing for each handle
|
|
- Implement drag behavior for resize/move/rotate
|
|
|
|
3. **Phase 3: Raster Copy/Paste**
|
|
- Implement extract_masked_pixels helper
|
|
- Wire Ctrl+C/V/X to selection operations
|
|
- Add transform mode on paste
|
|
|
|
4. **Phase 4: Vector Selection**
|
|
- Add vector shape hit-testing
|
|
- Implement VectorSelect tool behavior
|
|
- Wire vector copy/paste
|
|
|
|
5. **Phase 5: Selection Menu**
|
|
- Implement Inverse, Shrink, Feather, Border, Fill
|
|
- Add parameter dialogs where needed
|
|
|
|
6. **Phase 6: Crop Tool**
|
|
- Fix Enter/double-click handling
|
|
- Draw proper crop overlay with handles
|
|
- Per-document crop state isolation
|
|
|
|
## [S6] Testing Strategy
|
|
|
|
1. **Unit Tests:**
|
|
- SelectionTransform::from_engine() with various masks
|
|
- TransformHandle hit-testing accuracy
|
|
- extract_masked_pixels() correctness
|
|
|
|
2. **Integration Tests:**
|
|
- Full copy/paste workflow
|
|
- Transform apply/cancel cycle
|
|
- Crop confirm/cancel cycle
|
|
|
|
3. **Visual Tests:**
|
|
- Screenshot comparison for handle rendering
|
|
- Verify cursor changes on hover
|
|
- Check overlay alignment at different zoom levels
|
|
|
|
## [S7] Performance Considerations
|
|
|
|
- Selection mask should be cached, not recomputed per frame
|
|
- Transform preview should use existing composite refresh throttling
|
|
- Crop overlay is lightweight (few rectangles) - no performance concern
|
|
- Vector hit-testing uses bounding box first, then precise test only on candidates
|
|
|
|
## [S8] Edge Cases
|
|
|
|
1. **Empty selection** - All operations should be no-ops or show warning
|
|
2. **Selection outside canvas** - Clamp to canvas bounds
|
|
3. **Multiple documents** - Each has independent selection state
|
|
4. **Undo/redo** - Transform apply should create history entry
|
|
5. **Layer visibility** - Selection should only affect visible layers
|
|
6. **Vector shapes on hidden layers** - Should not be selectable
|