Files
hcie-rust-v3.05/.kilo/plans/1784456713003-iced-gui-visual-polish-plan.md
T
phantom 3659b886ff Refactor and enhance the Iced GUI with new features and improvements
- Improved layout and styling in filter_params.rs for better visual consistency.
- Enhanced filters.rs to ensure scrollable parameters fill the available width.
- Added a new Custom Shapes panel to display user-defined SVG shapes.
- Updated menus.rs to include a new Custom Shapes menu item and adjusted related logic.
- Modified properties.rs to support additional spray tool properties (particle size and density).
- Adjusted title_bar.rs for improved menu button dimensions and layout.
- Enhanced toolbar.rs to include spray tool options in the toolbar.
- Fixed color persistence issues in settings.rs to ensure colors are saved and loaded correctly.
- Updated plain_slider.rs to ensure sliders utilize the full width of their containers.
- Created a visual polish plan to address various UI/UX issues and improve overall usability.
2026-07-20 20:24:40 +03:00

259 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ICED GUI Visual & Behavioral Polish Plan
## Context
This plan addresses the visual and functional issues surfaced during recent ICED GUI testing. The focus is on the Iced build (`hcie-iced-app/crates/hcie-iced-gui/`). Items are grouped by theme and ordered to minimize code conflicts.
---
## Tasks
### 1. Fix theme-inconsistent blue accent buttons
**Problem:** Dialogs like `New Image` show preset buttons as solid blue (`#3355cc`), which clashes with the dark theme and looks unnatural.
**Files:**
- `hcie-iced-app/crates/hcie-iced-gui/src/dialogs/new_image.rs`
- `hcie-iced-app/crates/hcie-iced-gui/src/dialogs/mod.rs` (primary/secondary helpers)
**Changes:**
- Add a `preset_btn` helper in `dialogs/mod.rs` that uses theme colors (`bg_hover`/`accent` border/text) rather than the default blue.
- Use it for all preset rows.
- Ensure primary button (Create) remains theme-accent but with hover/pressed states.
---
### 2. Fix color-picker persistence / session recall
**Problem:** The user reports that foreground/background colors are identical on startup and recent colors are not added/persisted.
**Observed code:**
- `load_colors()` is called in `app.rs:1719` and applied.
- `add_recent_color()` exists and is called by `FgColorChanged`/`SwapColors`.
- `save_colors()` is called when `colors_dirty` is true.
**Likely issues:**
- `load_colors` returns `PersistedColors` with `#[serde(default)]`, but if any field is absent it becomes `Default`, which is `None`/`empty`. This is fine.
- **Root cause candidates:**
- `Message::ResetColors` exists, but there may be no UI button mapped to it in the color picker panel.
- The saved colors file may not be written because `colors_dirty` is only set on `FgColorChanged`/`BgColorChanged`/`SwapColors`/`ResetColors`. Some popup color changes may not route through those messages.
- When the app starts, `fg_color` and `bg_color` are initialized to the same default (`[0,0,0,255]`) before `load_colors()` runs, and then `load_colors` overrides them. This should be correct, but verify.
**Changes:**
- Ensure `PopupColorChanged` routed to `Foreground`/`Background` actually triggers `add_recent_color` and sets `colors_dirty`. It does via `FgColorChanged`/`BgColorChanged`.
- Add a **Reset** button in the color picker panel (`color_picker.rs`) that emits `Message::ResetColors`.
- Verify `load_colors()` correctly falls back when file is missing; add debug log if parsing fails.
- Ensure `save_colors()` stores alpha and recent list (already does).
- Remove or explain why `fg_color`/`bg_color` default to the same value in `IcedDocument` / `HcieIcedApp::new` before loading.
---
### 3. Right-click to pick secondary color in color palette panel
**Problem:** Right-click on palette/recent colors should set the background color.
**Observed code:**
- `color_picker.rs` already has `.on_right_press(Message::BgColorChanged(...))` on palette grid cells and recent colors.
- However, the wheel canvas only handles right-click inside `ColorWheel::update` and sends `Message::BgColorChanged(color)`.
**Changes:**
- Verify right-click events are not swallowed by the panel/container.
- If Iced does not deliver right-clicks on `mouse_area` children reliably, wrap the whole color picker content in a `mouse_area` with `on_right_press` to set background color from current foreground? No — user expects picking the swatch under cursor.
- Prefer: keep per-swatch right-click handlers and add a test or visual verification.
- If still broken, investigate whether `mouse_area` in Iced 0.13 requires an explicit background hit; add a transparent container around each swatch.
---
### 4. Enforce color palette panel minimum height so content stays visible
**Problem:** Color Palette panel can be collapsed too short; its contents become hidden.
**File:** `hcie-iced-app/crates/hcie-iced-gui/src/dock/sizing.rs`
**Change:**
- Raise `PaneType::ColorPicker` minimum height from `300.0` to `420.0`.
- Consider also raising `min` width from `180.0` to `200.0`.
- Ensure the color picker `view` uses `scrollable(content)` so overflow remains usable.
---
### 5. Filters panel: parameter sliders should fill ~80% of panel width
**Problem:** Filter parameter sliders are too narrow and leave large unused space.
**File:** `hcie-iced-app/crates/hcie-iced-gui/src/panels/filter_params.rs`
**Changes:**
- Audit all `float_slider` / `int_slider` calls and ensure the underlying `plain_slider` width is `Length::Fill` inside a container with `width(Length::Fill)`.
- In `filters.rs`, wrap the parameter section in a container with `width(Length::Fill)` and ensure `scrollable(params_panel)` does not clamp width.
- Add `width(Length::Fill)` to the `column!` returned by `filter_params::view` if not already present.
---
### 6. Filter operations should integrate with history / undo
**Problem:** Filter Apply is not undoable via the History panel.
**File:** `hcie-iced-app/crates/hcie-iced-gui/src/app.rs`
**Change:**
- Before `engine.apply_filter(...)` in `Message::FilterApply`, call `engine.push_active_layer_snapshot(...)` (or the appropriate engine history API) with a description like `"Apply <filter_name>"`.
- After apply, refresh history cache (`refresh_history`) so the new entry appears in the History panel.
- Ensure undo (`Message::HistoryJump`) and redo paths still work after the snapshot is pushed.
---
### 7. Brush button should reveal Brushes panel if closed
**Problem:** User says brush button does not open the Brushes panel.
**Observed code:** `Message::ToolSelected(Tool::Brush)` already calls `self.dock.reopen_pane(PaneType::Brushes)` at `app.rs:2289`.
**Possible issue:** `reopen_pane` may silently fail when the pane is auto-hidden or floating; or the brush tool can also be selected via slot click without this path.
**Changes:**
- Verify `ToolSlotClicked`/`SubtoolsToggle` also triggers `reopen_pane(PaneType::Brushes)` when the resulting tool is `Tool::Brush`.
- Add a helper `ensure_brushes_panel()` and call it wherever the active tool becomes `Tool::Brush`.
- Test with panel closed, auto-hidden, and in a different tab group.
---
### 8. Selecting a filter from the Filters menu should reveal the Filters panel
**Problem:** Filter menu selection should open/raise the Filters panel.
**Observed code:** `Message::FilterSelect(filter)` already calls `self.dock.reopen_pane(PaneType::Filters)` at `app.rs:4628`.
**Possible issue:** Same as above — `reopen_pane` may not handle auto-hide/floating states.
**Changes:**
- Audit `DockState::reopen_pane` to ensure it restores auto-hidden panels, focuses existing floating panels, and brings the pane to front if stacked.
- If `reopen_pane` is already correct, add visual verification; otherwise fix it.
---
### 9. Menu bar vertical padding (+2 px already planned, verify in final screenshot)
**File:** `hcie-iced-app/crates/hcie-iced-gui/src/panels/title_bar.rs`
**Already changed:** `MENU_BAR_HEIGHT` 28→30 and menu button widths increased.
**Remaining:** Confirm no clipping occurs with the new widths; adjust further if "Image" still truncates.
---
### 10. Prevent zero-size document creation (wgpu crash)
**Problem:** The New Image dialog allows width or height to be set to 0. Creating a 0×N document causes the renderer to request a texture with zero height (`resize_texture 800×600 → 4096×0`), which triggers a wgpu validation error and panic.
**Files:**
- `hcie-iced-app/crates/hcie-iced-gui/src/dialogs/new_image.rs`
- `hcie-iced-app/crates/hcie-iced-gui/src/app.rs` (`DialogNewImageCreate` handler)
**Changes:**
- Clamp the width/height sliders to a minimum of `1` in `new_image.rs`.
- In `DialogNewImageCreate`, validate `dialog_new_width >= 1 && dialog_new_height >= 1` before creating the engine; if invalid, keep the dialog open and log a warning (or show inline validation text).
- Optionally disable the Create button when either dimension is zero.
- Remove or ignore the `Custom` preset with `width: 0, height: 0` since it is not a valid document size.
---
### 11. Expose spray particle size / density in the UI
**Problem:** Spray tool radius/density settings are present in `tool_settings.rs` but not visible in the merged toolbar or Properties panel.
**Observed code:**
- `tool_settings.rs:132-179` already has `spray_settings()` with Particle Size and Density sliders.
- `properties.rs:134` groups `Tool::Spray` with brush properties, which only shows Size/Opacity/Hardness.
- `toolbar.rs:95` also groups Spray with Brush and only shows Size/Hardness.
**Changes:**
- In `properties.rs`, when `active_tool == Tool::Spray`, call a new `spray_properties(...)` helper that includes Size, Particle Size, Density, Opacity, and Hardness.
- In `toolbar.rs`, add a Spray-specific branch that shows Size, Particle Size, and Density (or move the existing spray controls from the Tool Settings panel into the merged toolbar if that is the intended UX).
- Ensure the values are read from `settings.tool_settings.spray_particle_size` / `spray_density` and emit the existing `Message::SprayParticleSizeChanged` / `SprayDensityChanged` messages.
---
### 12. Add a custom-shape slot / panel so user SVGs under `~/.local/share/hcie/shapes/` are usable
**Problem:** The user copied extra SVG files into `/home/dev-user/.local/share/hcie/shapes/`, but they did not appear in the UI and could not be used.
**Observed code:**
- `Engine::new()` initializes `shape_catalog` from `dirs::data_local_dir()/hcie/shapes` (matches `~/.local/share/hcie/shapes`) in `hcie-engine-api/src/lib.rs:558-562`.
- `ShapeCatalog::refresh()` in `hcie-engine-api/src/shape_catalog.rs` scans `*.svg` files there and assigns them indices.
- `Tool::CustomShape(idx)` exists and is handled in `app.rs:5945-5966`, using a snapshot of `shape_catalog.all()` during `VectorDrawEnd`.
- `sidebar/mod.rs` `TOOL_SLOTS` does **not** contain a slot for `Tool::CustomShape`. The vector-shapes slot only lists built-in `VectorArrow`, `VectorStar`, etc.
- There is no panel or popup listing the catalog entries for selection.
**Root cause:** The ICED sidebar has no entry point for `Tool::CustomShape`, so even though the engine loads custom SVGs from `~/.local/share/hcie/shapes/`, the user cannot select or draw them.
**Decision:** New dock panel with real SVG previews via `iced::widget::svg::Handle::from_memory`.
**Changes:**
- Add `PaneType::CustomShapes` to `dock/state.rs` with label "Custom Shapes".
- Create `hcie-iced-app/crates/hcie-iced-gui/src/panels/custom_shapes.rs`.
- Accept `shapes: &[ShapeEntry]` and `selected_index: Option<usize>`.
- Render each entry as a small card (e.g. 56×56 preview + label below).
- Use `iced::widget::Svg::new(Handle::from_memory(entry.svg.into_bytes()))` for the preview, sized to fit the card.
- On click, emit `Message::ToolSelected(Tool::CustomShape(index))`.
- Highlight the selected card with `colors.accent` border.
- Wire the new panel in `dock/view.rs`.
- Add the panel to `settings.rs` default `visible_panels` / `panel_order` so it appears in the initial dock layout.
- Add a "Refresh" button in the panel that emits `Message::RefreshCustomShapes`; handler calls `engine.shape_catalog.refresh()` and updates `app.cached_custom_shapes`.
- Store a cached `Vec<(String, String)>` of custom shapes in `HcieIcedApp` so the panel can be rebuilt cheaply; refresh it whenever the panel is shown or when Refresh is pressed.
- Ensure `Tool::CustomShape(idx)` out-of-range cases return `None` in `VectorDrawEnd` (already mostly true; add explicit guard).
- Verify that copied SVGs are valid — `ShapeCatalog` silently skips unreadable / non-SVG files.
### 12b. Custom Shape drawing not triggering on canvas
**Problem:** Selecting a custom shape in the Custom Shapes panel does not actually draw it on the canvas when dragging.
**Root cause:** The shared vector-shape drag flow is gated by `is_vector_shape_tool()` and an explicit match arm in the `CanvasPointerPressed` handler. Both locations enumerate the built-in `Tool::Vector*` variants but omit `Tool::CustomShape(_)`, so the custom-shape tool never enters `VectorDrawStart`/`VectorDrawMove`/`VectorDrawEnd` and therefore never reaches the existing `VectorDrawEnd` handler that already knows how to build a `VectorShape::SvgShape` from the catalog.
**Files:**
- `hcie-iced-app/crates/hcie-iced-gui/src/app.rs`
- `hcie-iced-app/crates/hcie-iced-gui/src/sidebar/mod.rs` (icon support already present; no change)
**Changes:**
1. In `app.rs` helper `is_vector_shape_tool()` (around line 1287), add `Tool::CustomShape(_)` to the `matches!` pattern.
2. In `app.rs` `CanvasPointerPressed` handler (around line 2824), add `Tool::CustomShape(_)` to the arm that starts `Message::VectorDrawStart`.
3. Keep `VectorDrawEnd` unchanged; it already snapshots `shape_catalog.all()` (line 5528-5539) and builds a `VectorShape::SvgShape` for `Tool::CustomShape(idx)` with correct bounds.
**Edge cases to consider:**
- Out-of-range `Tool::CustomShape(idx)` already returns `None` from `VectorDrawEnd` and produces no shape.
- If the active layer is a locked vector layer, the shape-flow first switches to a safe non-vector layer before drawing, just like built-in vector shapes.
**Validation:**
1. Build: `cargo check -p hcie-iced-gui` and `cargo test -p hcie-iced-gui`.
2. Run the Iced GUI.
3. Window → Custom Shapes → select a shape.
4. Drag on the canvas → a new vector layer containing the custom SVG shape appears.
5. Undo reverts the inserted shape (history snapshot is pushed by `engine.add_vector_shape`).
---
## Validation Plan
1. `cargo check -p hcie-iced-gui`
2. `cargo test -p hcie-iced-gui`
3. Manual launch and visual checks:
- New Image dialog presets look theme-aware (not bright blue).
- Set width or height to 0 → Create button disabled; preset ignores 0-size values.
- Create documents with valid sizes → no wgpu panic.
- Spray tool selected → Particle Size / Density sliders visible in Tool Settings panel.
- Pick colors, close app, reopen — colors and recent list restored.
- Right-click a palette swatch sets background color.
- Color Palette panel cannot be resized below content height.
- Select Brush tool → Brushes panel appears.
- Filter menu → Filters panel appears and parameter sliders are wide.
- Apply a filter → History panel gains an "Apply …" entry; Undo reverts it.
- Custom SVG files in `~/.local/share/hcie/shapes/` appear in the new Custom Shapes panel and can be drawn.
---
## Open Questions
- Is there a reference image or exact hex for the desired preset button style? (Current theme tokens: `bg_hover`, `accent`, `text_primary`.)
- Should the New Image dialog enforce a single minimum (e.g. 1×1) or a practical minimum (e.g. 8×8) to avoid 1-pixel-high canvases?
- Should custom shapes be rendered as small SVG previews in the panel, or is a text label list acceptable as a first pass? (Recommended: text list first, SVG preview later.)