Files

203 lines
8.4 KiB
Markdown
Raw Permalink 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.
# HCIE-Rust v3.05 — Session Handoff Document
**Date:** 2026-07-12
**Session ID:** ses_0a89f4c7dffeLzqNKSKFHyyaI1
---
## Summary
This session fixed critical engine bugs and completed iced GUI features for the HCIE-Rust image editor. The most significant fix was a fundamental architecture bug in the effects pipeline that caused new drawing strokes to disappear and effects to accumulate incorrectly.
---
## 1. Critical Engine Fixes (hcie-engine-api)
### 1.1 Effects Pipeline — `layer.pixels` Corruption Fix (CRITICAL)
**File:** `hcie-engine-api/src/partial_composite.rs` (line ~261)
**Root Cause:**
`apply_effects_and_sync_tiles()` wrote effects-applied pixels back to `layer.pixels`:
```rust
layer.pixels = processed; // BUG: corrupted raw drawing data
```
This caused a cascade of bugs:
1. User draws stroke → `layer.pixels` = raw + stroke
2. Effects pipeline runs → `layer.pixels` = effects(raw + stroke)
3. User draws next stroke → `layer.pixels` = effects(raw + stroke1) + stroke2
4. Effects pipeline restores from backup → `layer.pixels` = raw (stroke1+stroke2 LOST!)
5. Effects applied → `layer.pixels` = effects(raw) → **strokes vanished**
**Fix:** Removed `layer.pixels = processed`. Now `layer.pixels` always contains raw drawing data. The composite pipeline in `tiled.rs` reads from `effects_cache` when it exists (line 123), bypassing `layer.pixels` entirely for effect-bearing layers.
**Impact:** Fixes ALL of the following:
- New strokes disappearing after effects are applied
- Effects accumulating (shadow-on-shadow stacking)
- Toggle not working correctly (ON creating disabled styles)
- Slider preview showing corrupted state
### 1.2 `update_layer_style()` Missing Dirty Flags
**File:** `hcie-engine-api/src/lib.rs` (line ~1720)
**Root Cause:**
`update_layer_style()` set `effects_dirty`, `dirty`, and `composite_dirty` but did NOT set:
- `dirty_bounds` — caused full-canvas recomposite (~33MB for 4K) on every toggle
- `below_cache_dirty` — risked stale below-layer composite cache
**Fix:** Added:
```rust
let w = self.document.canvas_width;
let h = self.document.canvas_height;
self.document.dirty_bounds = Some([0, 0, w, h]);
self.below_cache_dirty = true;
```
Same fix applied to `remove_layer_style_by_index()`.
### 1.3 Stroke Drawing — `effects_dirty` During Strokes
**File:** `hcie-engine-api/src/stroke_brush.rs` (4 locations)
**Context:** `effects_dirty` is set during stroke drawing so the effects pipeline re-runs after the stroke ends. This is now safe because the effects pipeline no longer corrupts `layer.pixels` (fix 1.1).
**Functions modified:**
- `draw_brush_stroke` (line ~134)
- `draw_brush_stroke` (line ~172)
- `draw_pen_segment` (line ~203)
- `draw_stroke` (line ~266)
---
## 2. Iced GUI Fixes (hcie-iced-gui)
### 2.1 Layer Style Toggle Inversion
**File:** `hcie-iced-app/crates/hcie-iced-gui/src/app.rs` (line ~1044)
**Root Cause:**
```rust
enabled: existing.map_or(false, |s| matches!(s, LayerStyle::DropShadow { enabled, .. } if *enabled))
```
When style doesn't exist (`existing = None`), `map_or(false, ...)` returns `false`. First click ON created disabled style.
**Fix:** Changed to explicit logic:
- Style doesn't exist → `new_enabled = true` (first click enables)
- Style exists → `new_enabled = !was_enabled` (toggle)
### 2.2 Layer Style Panel — Draggable Floating Dialog
**Files:**
- `app.rs` — new state fields, messages, handlers
- `panels/layer_styles.rs` — draggable title bar, X button, offset positioning
**Implementation:**
- Added `layer_style_offset: (f32, f32)`, `layer_style_dragging: bool`, `layer_style_drag_start: Option<(f32, f32)>` state
- Added `LayerStyleDialogDragStart/Move/End` messages
- Title bar uses `mouse_area.on_press()` to start drag
- Global `iced::event::listen_with()` subscription tracks `CursorMoved` events during drag
- Delta-based position tracking: each move event adds delta to offset
- Dialog rendering uses `padding()` with offset to shift from center
- X close button (`\u{2715}`) wired to `LayerStyleCancel`
### 2.3 Layer Style Panel — Reopen Mechanism
**Files:**
- `panels/layers.rs` — "fx" button in toolbar
- `panels/menus.rs` — "Layer Styles..." in Layer menu (index 4,7)
- `app.rs` — menu action handler
**Buttons:**
- **"fx" button** in layers panel bottom toolbar → `Message::OpenLayerStyleDialog`
- **Layer menu → "Layer Styles..."** → menu action (4,7) → `Message::OpenLayerStyleDialog`
- Opening resets drag offset to (0, 0)
### 2.4 Layer Style Color Picker
**Files:**
- `app.rs``LayerStyleUpdateColor`, `ShowStyleColorPicker`, `HideStyleColorPicker` messages
- `panels/layer_styles.rs` — clickable color swatch, inline HSL picker popup
**Implementation:**
- Color swatch is clickable (opens HSL color picker popup)
- Popup has H/S/L sliders, color preview, hex display, Close button
- `LayerStyleUpdateColor` handler updates the style's color field in the engine
- HSL state tracked in `style_color_hsl: (f32, f32, f32)`
### 2.5 Color Picker Redesign
**File:** `color_picker.rs` (complete rewrite)
**Layout (matching egui version):**
1. Primary/Secondary swatch buttons + swap button (`\u{21C5}`)
2. Editable hex input field (`text_input`)
3. Tab bar: W (Color Wheel), H (HSL Sliders), G (Palette Grid)
4. Tab W: Hue ring (36 cells × 10°) + SL picker grid (12×12)
5. Tab H: HSL sliders with degree/percent readout
6. Tab G: 10×10 palette grid (algorithmically generated like egui)
7. Recent colors section
**Tab state:** `color_tab: usize` field in `HcieIcedApp`, `ColorTabChanged(usize)` message.
### 2.6 Recent Files Persistence
**Files:**
- `app.rs``load_recent_files()`, `save_recent_files()`, config helpers
- `Cargo.toml` — added `dirs = "5.0"` and `serde = { workspace = true }`
**Implementation:**
- Config stored at `~/.config/hcie-iced/recent_files.json`
- `RecentFileEntry` derives `serde::Serialize` and `serde::Deserialize`
- `add_recent_file()` persists to disk after each add
- `ClearRecentFiles` handler persists empty list
- `new()` loads from disk on startup
### 2.7 Backdrop for Modal Dialogs
**File:** `panels/layer_styles.rs`
**Fix:** Added semi-transparent backdrop (`rgba(0,0,0,0.5)`) around the dialog, matching the pattern used by all other dialogs (NewImage, BrightnessContrast, CloseConfirm).
---
## 3. Build Status
All changes compile successfully:
```
cargo build -p hcie-engine-api -p hcie-iced-gui
```
Pre-existing warnings only (dead code in `MenuDef`, unused `union_regions`, etc.).
---
## 4. Files Modified
### Engine (locked layer)
| File | Changes |
|------|---------|
| `hcie-engine-api/src/partial_composite.rs` | Removed `layer.pixels = processed` in effects pipeline |
| `hcie-engine-api/src/lib.rs` | Added `dirty_bounds`/`below_cache_dirty` to `update_layer_style` and `remove_layer_style_by_index` |
| `hcie-engine-api/src/stroke_brush.rs` | Restored `effects_dirty` in 4 drawing functions |
### Iced GUI (open layer)
| File | Changes |
|------|---------|
| `hcie-iced-app/crates/hcie-iced-gui/src/app.rs` | Toggle fix, drag state/messages/handlers, color messages, recent files, menu wiring |
| `hcie-iced-app/crates/hcie-iced-gui/src/panels/layer_styles.rs` | Complete rewrite: draggable, X button, color picker, backdrop |
| `hcie-iced-app/crates/hcie-iced-gui/src/panels/layers.rs` | Added "fx" button to toolbar |
| `hcie-iced-app/crates/hcie-iced-gui/src/color_picker.rs` | Complete rewrite: tabs, wheel, hex input, swatches |
| `hcie-iced-app/crates/hcie-iced-gui/src/dock/view.rs` | Updated `layer_styles::view()` calls with new parameters |
| `hcie-iced-app/crates/hcie-iced-gui/Cargo.toml` | Added `dirs` and `serde` dependencies |
---
## 5. Known Remaining Issues
1. **Panel positioning:** The drag uses `padding()` offset which may not perfectly center on all screen sizes. The dialog may need `top()`/`left()` absolute positioning for true floating behavior.
2. **Color picker wheel:** The "W" tab uses a grid approximation (36 hue cells + 12×12 SL grid). An actual circular wheel would require custom rendering (iced Canvas widget).
3. **Effects pipeline backup:** The `raw_pixel_backup` is never cleared when a style is removed and the layer has no more styles. This could accumulate memory for layers that had styles and then removed them all.
4. **egui version has the same toggle bug:** The `map_or(false, ...)` pattern exists in the egui version too. The engine fix (1.1) resolves the effects corruption for both GUIs, but the egui toggle inversion needs a separate fix.