GOOD feat: add Layer Styles panel and enhance filter categories

- Introduced a new Layer Styles panel that displays effects with toggle checkboxes for quick enable/disable functionality.
- Updated the filters panel to support collapsible filter categories, allowing users to expand or collapse categories.
- Enhanced the application state to manage expanded filter categories using a HashMap.
- Modified the layout and sizing of various panels to accommodate the new Layer Styles panel.
- Updated theme colors for better visual consistency across the application.
- Improved the toolbar to streamline brush size and opacity controls.
This commit is contained in:
2026-07-20 21:23:04 +03:00
parent 3659b886ff
commit 325bd0d8e8
12 changed files with 347 additions and 508 deletions
@@ -1,234 +1,135 @@
# ICED GUI Visual & Behavioral Polish Plan
# ICED GUI Visual Parity with egui
## 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.
The current Iced GUI has the same panels as the egui version but several visual/behavioral gaps remain. The goal is to bring the Iced GUI to egui-level polish. This plan is based on a side-by-side pixel-level comparison of `_images/egui.png` (target) vs the current Iced GUI.
---
## Gap Analysis (egui vs Iced)
| # | Area | egui | Iced | Gap |
|---|------|------|------|-----|
| 1 | **Menu bar** | File, Edit, Tools, Image, Layer, Filter, Select, View, Window, Help (10 items) | File, Edit, Image, Select, View, Filter, Plugin, Window, More, AI, Help (11 items) | Iced has extra Plugin/AI/More menus cluttering the bar; egui has Tools/Layer menus instead |
| 2 | **Toolbar** | Compact: Brush dropdown + Size + Opacity | Long: Brush Size + Opacity + Hardness + Tolerance + Feather + Blur Radius + brush tip icons | Iced toolbar is too wide/cluttered; egui is minimal |
| 3 | **Brushes grid** | 3-column grid with thumbnail previews | 2-column grid (constrained by narrow dock width) | Iced grid needs more horizontal space |
| 4 | **Filter list** | Categorized + collapsible (Blur ▸, Sharpen ▸, Pixelate ▸, Distort ▸, Stylize ▸, Color & Light ▸) | Flat list with no categories | Iced filters need categories + collapsible groups |
| 5 | **Layer panel** | Clean: thumbnail + name + eye + lock; opacity/blending inline; +Lay/+Vec/+Grp/Flatten toolbar | Shows "opacity: unavailable in hcie-engine-api fx" debug text; missing layer action buttons | Ugly debug text visible; missing quick-action buttons |
| 6 | **Color Palette** | Prominent: large color wheel, FG/BG swatches, Hex input, W/H/G tabs | Same panels exist but ColorPicker is squashed at bottom of right column | ColorPicker needs more vertical space |
| 7 | **Properties panel** | Shows: layer name, Opacity, Visible checkbox, Blend Mode dropdown, Type, Tool Settings (Size/Opacity/Hardness/Flow/Spacing/Color Variant) | Exists but layout differs | Should match egui's grouped layout |
| 8 | **Right column layout** | ColorPalette (top-right) + LayerStyles/History tabs (mid-right) + Properties (bottom-right) | Filters + Layers + ColorPicker + Properties all stacked | Right column overcrowded; Filters shouldn't be on right |
| 9 | **Background** | Consistent dark theme `#2b2b2b` with `#353535` panel bg | Lighter warm theme with inconsistent panel colors | Theme needs refinement for darker, more consistent panels |
| 10 | **Status bar** | Tool name + Mouse coords + Canvas size + Zoom% + Zoom slider | Similar but slightly different styling | Minor; acceptable |
---
## Tasks
### 1. Fix theme-inconsistent blue accent buttons
### 1. Fix layer panel debug text and add action buttons
**Problem:** Dialogs like `New Image` show preset buttons as solid blue (`#3355cc`), which clashes with the dark theme and looks unnatural.
**Problem:** Layer panel shows "opacity: unavailable in hcie-engine-api fx" which is debug text leaking into the UI. Also missing the `+Lay`, `+Vec`, `+Grp`, `Flatten` quick-action buttons that egui has.
**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)
- `hcie-iced-app/crates/hcie-iced-gui/src/panels/layers.rs`
**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.
- Remove or hide any debug/error text about "unavailable in hcie-engine-api fx"
- Add a row of action buttons at the bottom of the layer panel: `+Layer`, `+Group`, `Flatten`, matching egui's `[+Lay] [+Vec] [+Grp] [Flatten]` bar
- Wire buttons to existing messages (`LayerAdd`, `LayerFlatten`, etc.)
---
### 2. Fix color-picker persistence / session recall
### 2. Refactor Filters panel into categorized collapsible groups
**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.
**Problem:** egui shows filters organized into expandable categories (Blur ▸, Sharpen ▸, Pixelate ▸, Distort ▸, Stylize ▸, Color & Light ▸). Iced shows a flat unordered list.
**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)
- `hcie-iced-app/crates/hcie-iced-gui/src/panels/filters.rs`
**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.
- Define filter categories with labels: "Blur", "Sharpen", "Pixelate", "Distort", "Stylize", "Color & Light"
- Each category is a collapsible section (▶/▼ toggle) containing its filter items
- Add a `FilterCategoryExpanded` state field in `HcieIcedApp` to track which categories are open (default: all open)
- Add a `Message::FilterCategoryToggle(String)` message
- When no filter is selected, show "Filter Selection" header as in egui
- Keep existing filter selection + parameter panel behavior
---
### 11. Expose spray particle size / density in the UI
### 3. Simplify the merged toolbar to match egui
**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.
**Problem:** The Iced toolbar shows too many inline sliders (Size, Opacity, Hardness, Tolerance, Feather, Blur Radius, brush tip icons). egui shows only: Brush dropdown + Size + Opacity.
**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)
- `hcie-iced-app/crates/hcie-iced-gui/src/panels/toolbar.rs`
**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.
- Reduce toolbar to: [Tool Icon] [Brush Style Dropdown] [Size: ____] [Opacity: ____]
- Move Hardness, Tolerance, Feather, Blur Radius to the Properties/Tool Settings panel
- Keep brush tip icon row only when a brush tool is active
- Make the toolbar narrower and less cluttered
**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`).
### 4. Add Layer Styles panel tab alongside History
**Problem:** egui has a tabbed right panel with "Layer Styles" / "History" / "AI Assistant" tabs. Iced has no Layer Styles panel.
**Files:**
- `hcie-iced-app/crates/hcie-iced-gui/src/panels/layer_styles.rs` (new)
- `hcie-iced-app/crates/hcie-iced-gui/src/dock/state.rs` (add `PaneType::LayerStyles`)
- `hcie-iced-app/crates/hcie-iced-gui/src/dock/view.rs` (wire panel)
**Changes:**
- Create a minimal Layer Styles panel that lists common effects: Drop Shadow, Inner Shadow, Outer Glow, Inner Glow, Bevel & Emboss, Satin, Color Overlay, Gradient Overlay, Pattern Overlay, Stroke
- Each effect has a checkbox to enable and an expandable settings section
- Wire to existing `LayerStyle*` messages
- This is a display-only first pass; detailed per-effect parameter editing can follow
---
### 5. Widen the right column for Color Palette prominence
**Problem:** Color Palette is squeezed at the bottom of the right column in Iced. In egui, it's prominent with a large color wheel occupying the top-right area.
**Files:**
- `hcie-iced-app/crates/hcie-iced-gui/src/dock/state.rs` (adjust default layout ratios)
- `hcie-iced-app/crates/hcie-iced-gui/src/dock/sizing.rs` (adjust minimum sizes)
**Changes:**
- Swap the default right-column layout: put ColorPicker on top (with more space) and Layers below
- Adjust the right_col1 ratio from 0.5 to ~0.6 (more space for ColorPicker)
- Ensure ColorPicker minimum height is at least 400px
- Remove Properties/History from the right column entirely (move to auto-hide or tabbed)
---
### 6. Darken theme panel backgrounds for consistency
**Problem:** Iced panels use slightly lighter/warmer backgrounds than egui's `#353535`. The overall look feels less polished.
**Files:**
- `hcie-iced-app/crates/iced-panel-adapter/src/theme.rs`
**Changes:**
- Darken `bg_panel` in all themes by ~5% to match egui's darker panel backgrounds
- Ensure `border_low` is subtle (~0.2 opacity) for clean panel separation
- Verify `bg_active` provides enough contrast for selected items
- Do NOT change accent colors; only adjust background/border tones
---
### 7. Add document tab bar with proper styling
**Problem:** egui shows a document tab bar ("Untitled") above the canvas with a clear active tab style. Iced has a tab bar but it needs polish.
**Files:**
- `hcie-iced-app/crates/hcie-iced-gui/src/dock/view.rs` (document tab bar)
**Changes:**
- Style the active tab with a bottom accent border (matching egui)
- Add a `+` button for new document at the right end of the tab bar
- Ensure the tab bar uses `bg_panel` background, not `bg_active`
---
@@ -236,23 +137,10 @@ This plan addresses the visual and functional issues surfaced during recent ICED
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.)
3. Screenshot comparison after changes:
- F12 full viewport screenshot
- Compare right-column layout: ColorPicker should be prominent on top
- Filters panel should show categorized collapsible groups
- Layer panel should have no debug text, should show action buttons
- Toolbar should be simplified (3-4 items, not 7+)
- Theme colors should be consistently darker