Files
hcie-rust-v3.05/.kilo/plans/1784456713003-iced-svg-editor-trigger.md
T

196 lines
14 KiB
Markdown
Raw Normal View History

# ICED GUI — Advanced SVG Editor and UI/Color Fixes
## Scope
Implement the requested ICED improvements:
1. Replace the SVG coordinate-list dialog with an interactive visual node editor.
2. Make filter-history behavior explicit and regression-tested.
3. Widen filter sliders.
4. Open Brushes when Brush is selected.
5. Open Filters when a filter is selected from the menu.
6. Add secondary-color selection by right-click in the color picker.
The following requests are already implemented and require verification rather than duplicate code:
- Recent colors persist in `~/.config/hcie-iced/colors.json`.
- Foreground/background colors persist in the same file.
- Recent color swatches already use `.on_right_press(Message::BgColorChanged(c))`.
- Committed filters already enter history through `Engine::apply_filter()` and `push_draw_snapshot("Filter: <id>")`; only live preview uses `apply_filter_preview()` and correctly avoids history entries.
## Decisions
| Area | Decision |
|---|---|
| SVG geometry | Support polygon/line paths represented by existing `SvgEditable`; bezier/curve SVGs remain unsupported because the locked engine API rejects them. |
| SVG live editing | Update the visible canvas preview and engine SVG on every node drag; create one logical edit session. Cancel restores the original SVG; Save keeps the current SVG. Do not create per-mouse-move history entries. |
| SVG editor interaction | Add node drag, node hit testing, edge-midpoint node insertion, node deletion, polygon selection, numeric X/Y editing, zoom/pan, fit-to-view, grid/background, selection highlighting, keyboard Delete/Backspace, and Save/Cancel. |
| SVG rendering | Use an `iced::widget::canvas::Canvas`/`canvas::Program`, following `canvas/viewport.rs` event and coordinate-mapping patterns. Keep the existing modal overlay and `Message`-driven app state. |
| SVG engine history | Out of scope for GUI-only changes because `set_vector_shape_svg()` does not expose a vector-history snapshot API. Record this as a residual limitation; do not modify locked engine crates. |
| Filter history | No behavior rewrite. Add regression coverage or an auditable helper around the existing `apply_filter()` path to prove committed filters are undoable and previews are not committed. |
| Filter slider width | Make filter parameter sliders fill the available Filters pane width and ensure the Filters pane has a minimum width that leaves at least approximately 150px for the track. Use the existing responsive pane sizing rather than a hard-coded global width. |
| Brush auto-open | On `Message::ToolSelected(Tool::Brush)`, reopen the Brushes pane only if it is closed. Never toggle an already-open pane closed. |
| Filter auto-open | In the central `Message::FilterSelect` handler, reopen the Filters pane only if it is closed. This covers menu commands and other filter entry points consistently. |
| Right-click secondary color | Add right-click handling to the HSL/color-picker canvas. Keep the main canvas right-click context menu unchanged. Recent swatch right-click behavior remains as-is. |
## Current Findings
- `panels/svg_editor.rs` currently displays polygon buttons and a coordinate list only; it has no visual canvas, no drag state, and no numeric editing.
- EGUIs reference editor has `svg_to_screen`, `screen_to_svg`, polygon rendering, node circles, midpoint hit testing, node dragging, add/remove controls, and a resizable canvas.
- `SvgEditable` exposes `view_box`, `polygons`, `add_node`, `remove_node`, `move_node`, `from_svg`, and `to_svg`.
- ICED already has interactive Canvas examples in `canvas/viewport.rs` and color picker programs in `color_picker.rs`.
- `set_vector_shape_svg()` marks the vector layer/composite dirty and modified but does not push a vector history snapshot.
- `Engine::apply_filter()` already snapshots the pre-filter pixels with description `Filter: <id>`; `apply_filter_preview()` intentionally does not.
- `plain_slider()` uses `Length::Fill` internally, but filter content/pane sizing can leave its parent too narrow. The fix should make the filter slider row explicitly fill the panel and increase Filters sizing minimum if needed.
- `Message::ToolSelected` currently changes the active tool but does not reopen Brushes.
- `Message::FilterSelect` currently selects/preview-starts a filter but does not reopen Filters.
- `recent_colors`/`fg_color`/`bg_color` load from and save to `colors.json`; recent swatches already support right-click secondary selection.
## Affected Files
### Advanced SVG editor
- `hcie-iced-app/crates/hcie-iced-gui/src/panels/svg_editor.rs`
- Replace list-only layout with a visual editor canvas.
- Extend editor state with drag state, original SVG/session state, zoom, pan, hover/selection state, and any fit/grid state needed by the canvas program.
- Add coordinate conversion using `SvgEditable.view_box`, preserving aspect ratio and centering the viewBox.
- Draw polygon fills/outlines, node handles, edge midpoint handles, grid/background, and selected-node styling.
- Handle primary click/drag for node selection and movement, midpoint click for insertion, and wheel/secondary gestures for zoom/pan without interfering with modal buttons.
- Keep controls for polygon selection, Add Node, Remove Node, X/Y numeric editing, Fit, Zoom +/- or reset, Save, and Cancel.
- `hcie-iced-app/crates/hcie-iced-gui/src/app.rs`
- Add SVG canvas messages for pointer press/move/release, zoom/pan/fit, numeric X/Y changes, and any explicit node action needed by the new Program.
- Update existing SVG editor handlers to mutate `SvgEditable` and immediately call `set_vector_shape_svg()` for live engine preview.
- Capture the original SVG when opening; restore it on Cancel.
- Keep Save/Cancel dialog lifecycle intact and ensure editor drag sessions do not create per-event history entries.
- `hcie-iced-app/crates/hcie-iced-gui/src/dock/view.rs` or editor call site only if required by the new state/signature.
### Filter and panel behavior
- `hcie-iced-app/crates/hcie-iced-gui/src/app.rs`
- In `Message::ToolSelected`, call a new local helper or `dock.reopen_pane(PaneType::Brushes)` only when `!dock.is_pane_open(PaneType::Brushes)` and the selected tool is `Tool::Brush`.
- In `Message::FilterSelect`, ensure `PaneType::Filters` is open before starting the preview.
- Preserve the existing filter preview/commit lifecycle.
- Add focused tests or testable helper coverage proving `apply_filter()` creates a history entry and `apply_filter_preview()` does not create a committed entry, if the current test architecture permits without engine changes.
- Keep existing persistence code; add tests only if needed to prove colors/recent colors survive serialization/deserialization.
- `hcie-iced-app/crates/hcie-iced-gui/src/panels/filter_params.rs`
- Make `float_slider()` and `int_slider()` return a control wrapped with `Length::Fill` at the row/container level so the track consumes the available panel width.
- Avoid changing numeric behavior, labels, ranges, or parameter messages.
- `hcie-iced-app/crates/hcie-iced-gui/src/dock/sizing.rs`
- Increase the Filters pane minimum/preferred width enough for a roughly 150px slider after label/value columns, while preserving responsive resizing and existing other-pane policies.
### Color picker
- `hcie-iced-app/crates/hcie-iced-gui/src/color_picker.rs`
- Extend the interactive color wheel/picker Canvas program so right-click emits `Message::BgColorChanged` while left-click/drag continues emitting foreground changes.
- Keep current color calculations and alpha behavior.
- Ensure the pickers displayed source color remains stable and right-click does not alter foreground.
- Preserve existing recent-swatch `.on_right_press(Message::BgColorChanged(c))` behavior.
- `hcie-iced-app/crates/hcie-iced-gui/src/app.rs`
- Confirm `BgColorChanged` updates secondary color, shape synchronization, and persistence state as appropriate.
- Do not change main canvas right-click context-menu behavior.
## Ordered Implementation Tasks
### 1. SVG editor state and messages
1. Extend `SvgEditorState` with:
- `original_svg: String` for Cancel restoration.
- `drag_node: Option<(usize, usize)>`.
- `zoom: f32` and `pan: Vector` or equivalent.
- `hovered_node`/`hovered_midpoint` and fit/grid state if needed.
2. Add `Message` variants for:
- canvas pointer pressed/moved/released;
- node X/Y field changes;
- zoom/pan/fit/reset;
- retain existing polygon/node/add/remove/save/cancel messages where reusable.
3. On SVG editor open, store the original SVG alongside the parsed editable state.
### 2. SVG coordinate and rendering model
4. Implement `svg_to_screen()` and `screen_to_svg()` using the viewBox, canvas bounds, margin, zoom, and pan. Preserve aspect ratio so nodes are not distorted.
5. Implement a Canvas Program that:
- draws the viewBox background/grid;
- draws each polygon/path fill and outline;
- draws node handles with selected/hovered styles;
- draws smaller edge midpoint handles;
- uses `Cursor::position_in(bounds)` for hit testing;
- maps drag positions back to SVG coordinates.
6. Implement hit testing for nearest node and edge midpoint with screen-space thresholds that remain usable at different zoom levels.
### 3. SVG live editing behavior
7. On node press, select the polygon/node and begin drag.
8. On node drag, call `editable.move_node(...)`, update the numeric fields, and immediately emit/apply the current `to_svg()` to the active vector shape for live canvas preview.
9. On midpoint click, call `editable.add_node(...)` and update selection/live preview.
10. On Delete/Backspace or Remove button, remove the selected node when valid and update/live-preview.
11. Numeric X/Y edits update the selected node and live-preview.
12. Save keeps the current SVG, clears the original/session state, closes the editor, and requests composite refresh.
13. Cancel calls `set_vector_shape_svg()` with `original_svg`, refreshes the composite, clears session state, and closes without retaining edits.
14. Handle empty polygons, invalid selection indices, unsupported bezier SVGs, and zero-sized viewBoxes without panics.
### 4. Filter/panel behavior
15. Add a small `ensure_pane_open(PaneType)` helper if repeated logic is useful; otherwise use `is_pane_open` + `reopen_pane` directly.
16. Call it for Brush selection and Filter selection as specified above.
17. Widen filter slider rows and increase Filters pane minimum sizing. Verify at narrow and wide pane widths.
18. Verify committed filter history by applying a filter, checking the history description, undoing, and redoing. Do not add history entries during preview slider movement.
### 5. Color behavior
19. Add right-click output support to the color-wheel/picker Canvas Program.
20. Verify left-click changes foreground, right-click changes background, recent swatch right-click changes background, and both colors/recent colors persist across restart.
## Risks and Edge Cases
1. **SVG curve limitation:** `SvgEditable::from_svg()` rejects QuadTo/CubicTo. Show/retain a clear parse failure path; do not silently flatten curves.
2. **Live SVG update cost:** Updating the full SVG string on every drag is acceptable for the small vector shape payload, but avoid cloning unrelated document pixels.
3. **Cancel correctness:** Capture the original SVG before the first live update. Cancel must restore it exactly, including after multiple node additions/removals.
4. **SVG history limitation:** The public GUI-facing API has no vector snapshot method for `set_vector_shape_svg()`. Do not modify locked engine crates in this scope; document that SVG edits are not currently represented as a dedicated undo entry.
5. **Filter history semantics:** Do not push history from slider-preview events. Only the final `FilterApply`/direct `apply_filter` call should be undoable.
6. **Pane reopening:** Use `reopen_pane` only when closed; calling `toggle_pane` would incorrectly close an already visible pane.
7. **Color picker right-click:** Keep canvas context-menu right-click untouched; limit the new behavior to color picker surfaces.
8. **Color persistence:** Avoid resetting `recent_colors`, `fg_color`, or `bg_color` during app initialization after `load_colors()`.
## Validation
1. `cargo check -p hcie-iced-gui`.
2. `cargo test -p hcie-iced-gui` and existing menu tests.
3. SVG manual flow:
- Open a supported polygon SVG.
- Select a node and drag it; verify the SVG and main canvas update during the drag.
- Insert a node through an edge midpoint and move it.
- Edit X/Y numerically.
- Delete a node and verify minimum-node safeguards.
- Cancel after several edits and verify exact original restoration.
- Reopen, edit, Save, close, and verify the updated SVG persists.
- Try a bezier SVG and verify a controlled unsupported-geometry failure.
4. Filter history flow:
- Select a filter and change sliders; verify preview movement does not create multiple history records.
- Apply the filter; verify one `Filter: <id>` history entry.
- Undo and redo; verify pixels restore/reapply.
5. Panel flow:
- Close Brushes, select Brush via sidebar/shortcut/menu, verify Brushes opens.
- Close Filters, choose a filter from the Filter menu, verify Filters opens and the selected filter is shown.
6. Slider flow:
- Test Filters pane at minimum and expanded widths; verify slider track is at least approximately 150px or consumes the available panel width.
7. Color flow:
- Left-click picker changes foreground only.
- Right-click picker changes background only.
- Right-click recent swatch changes background only.
- Restart app and verify foreground, background, and recent colors persist.
8. Capture a focused Filters panel and SVG editor screenshot after the UI changes for visual verification.
## Explicitly Unchanged / Already Satisfied
- No engine crate modifications.
- No replacement of the main canvas context menu.
- No per-mouse-move SVG history entries.
- No duplicate recent-color persistence implementation.
- No duplicate filter-history implementation; existing engine snapshot behavior is retained and verified.
- Bezier/curve SVG editing remains a separate engine/API scope.