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
9.3 KiB
9.3 KiB
Iced GUI Selection Features Design
[S1] Problem
The iced GUI migration is missing critical selection-based features that exist in the egui GUI:
- #1350 Move/Rotate/Scale - No transform handles on selections
- #1351 Raster Copy/Paste - Only copies entire canvas, not selected pixels
- #1352 Vector Copy/Paste - No vector shape selection/copy/paste
- Selection menu - Missing feather, grow, shrink, border, fill operations
- 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:
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
#[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
#[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:
- Add
selection_transform: Option<SelectionTransform>toIcedDocument - Extend
OverlayProgramto draw transform handles - Add
TransformHandlehit-testing in overlayupdate() - Handle mouse drag on each handle type in
app.rs - Apply transform on Enter key or double-click
Messages to Add:
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:
- On Copy: extract pixels within selection mask, store in
internal_clipboard - On Paste: create
SelectionTransformfrom clipboard, enter transform mode - On Cut: copy then clear selected pixels in active layer
- On transform apply: composite floating pixels back to layer
Engine API Calls:
// 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:
- Add
selected_vector_indices: Vec<usize>to state - On VectorSelect click: hit-test vector shapes, select closest
- On Copy: store selected VectorShape data in clipboard
- On Paste: add duplicated shapes with offset
- Draw selection bounding box in overlay
Messages to Add:
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:
- 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)
- Wire menu items to these methods
- Add dialog for parameter input (px value)
Crop Tool Fixes
Issues to Fix:
- Enter key not working → Wire keyboard event to crop confirm
- Double-click not working → Add double-click handler
- Selection mixing between images → Store crop state per-document
- Marching ants instead of handles → Draw proper crop handles
Implementation:
- Add
crop_state: CropStatetoIcedDocument(per-document) - Draw crop overlay with 8 handles + rule-of-thirds grid
- Handle Enter/double-click to confirm crop
- Handle Escape to cancel crop
- 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
hcie-iced-app/crates/hcie-iced-gui/src/app.rs- Main state and update logichcie-iced-app/crates/hcie-iced-gui/src/canvas/mod.rs- Overlay drawinghcie-iced-app/crates/hcie-iced-gui/src/panels/menus.rs- Menu definitionshcie-iced-app/crates/hcie-iced-gui/src/panels/tool_options.rs- Tool options
New Files (if needed)
hcie-iced-app/crates/hcie-iced-gui/src/selection/transform.rs- Transform logichcie-iced-app/crates/hcie-iced-gui/src/selection/crop.rs- Crop tool logic
[S5] Implementation Order
-
Phase 1: Core Selection State
- Add SelectionTransform, TransformHandle, CropState structs
- Add selection_mask, selection_bounds to IcedDocument
- Basic selection drawing improvements
-
Phase 2: Transform Handles
- Draw 8 resize handles + rotation handle
- Handle hit-testing for each handle
- Implement drag behavior for resize/move/rotate
-
Phase 3: Raster Copy/Paste
- Implement extract_masked_pixels helper
- Wire Ctrl+C/V/X to selection operations
- Add transform mode on paste
-
Phase 4: Vector Selection
- Add vector shape hit-testing
- Implement VectorSelect tool behavior
- Wire vector copy/paste
-
Phase 5: Selection Menu
- Implement Inverse, Shrink, Feather, Border, Fill
- Add parameter dialogs where needed
-
Phase 6: Crop Tool
- Fix Enter/double-click handling
- Draw proper crop overlay with handles
- Per-document crop state isolation
[S6] Testing Strategy
-
Unit Tests:
- SelectionTransform::from_engine() with various masks
- TransformHandle hit-testing accuracy
- extract_masked_pixels() correctness
-
Integration Tests:
- Full copy/paste workflow
- Transform apply/cancel cycle
- Crop confirm/cancel cycle
-
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
- Empty selection - All operations should be no-ops or show warning
- Selection outside canvas - Clamp to canvas bounds
- Multiple documents - Each has independent selection state
- Undo/redo - Transform apply should create history entry
- Layer visibility - Selection should only affect visible layers
- Vector shapes on hidden layers - Should not be selectable