feat(iced): add marching ants animation for selection

- 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
This commit is contained in:
2026-07-15 04:11:48 +03:00
parent 513f4cdb9b
commit 1fd0c5291a
6 changed files with 2142 additions and 57 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,288 @@
# 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