2fb47520b3
feat(debug-logger): add DebugSignalLogger plugin for event logging and inspection feat(plugins): introduce built-in plugins module and integrate debug logger feat(plugin-integration): create integration layer for mapping Iced Messages to CoreEvents feat(plugin-registry): establish PluginRegistry for managing plugins and event dispatching
196 lines
14 KiB
Markdown
196 lines
14 KiB
Markdown
# 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.
|
||
- EGUI’s 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 picker’s 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.
|