8.4 KiB
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:
layer.pixels = processed; // BUG: corrupted raw drawing data
This caused a cascade of bugs:
- User draws stroke →
layer.pixels= raw + stroke - Effects pipeline runs →
layer.pixels= effects(raw + stroke) - User draws next stroke →
layer.pixels= effects(raw + stroke1) + stroke2 - Effects pipeline restores from backup →
layer.pixels= raw (stroke1+stroke2 LOST!) - 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 togglebelow_cache_dirty— risked stale below-layer composite cache
Fix: Added:
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:
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, handlerspanels/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/Endmessages - Title bar uses
mouse_area.on_press()to start drag - Global
iced::event::listen_with()subscription tracksCursorMovedevents 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 toLayerStyleCancel
2.3 Layer Style Panel — Reopen Mechanism
Files:
panels/layers.rs— "fx" button in toolbarpanels/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,HideStyleColorPickermessagespanels/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
LayerStyleUpdateColorhandler 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):
- Primary/Secondary swatch buttons + swap button (
\u{21C5}) - Editable hex input field (
text_input) - Tab bar: W (Color Wheel), H (HSL Sliders), G (Palette Grid)
- Tab W: Hue ring (36 cells × 10°) + SL picker grid (12×12)
- Tab H: HSL sliders with degree/percent readout
- Tab G: 10×10 palette grid (algorithmically generated like egui)
- 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 helpersCargo.toml— addeddirs = "5.0"andserde = { workspace = true }
Implementation:
- Config stored at
~/.config/hcie-iced/recent_files.json RecentFileEntryderivesserde::Serializeandserde::Deserializeadd_recent_file()persists to disk after each addClearRecentFileshandler persists empty listnew()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
- Panel positioning: The drag uses
padding()offset which may not perfectly center on all screen sizes. The dialog may needtop()/left()absolute positioning for true floating behavior. - 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).
- Effects pipeline backup: The
raw_pixel_backupis 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. - 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.