254 lines
14 KiB
Markdown
254 lines
14 KiB
Markdown
# Comprehensive Plan: Port All egui Signals/Events to Qt GUI
|
|
|
|
## Complete Event/Signal Map (50+ events from `AppEvent`)
|
|
|
|
Every event in `event_bus.rs` must have a Qt equivalent. Here is the full mapping:
|
|
|
|
### 1. Tool & Drawing Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `ToolChanged(Tool)` | `ToolsPanel::toolChanged(int)` → `MainWindow::onToolChanged(int)` | Already wired |
|
|
| `LayerAdded` | `LayersPanel` button → `MainWindow::onNewLayer()` | Already wired |
|
|
| `LayerDeleted(u64)` | `LayersPanel` delete button → `MainWindow::onDeleteLayer()` | Already wired |
|
|
| `LayerSelected(u64)` | `LayersPanel::itemClicked` → `engine->setActiveLayer(id)` | Already wired |
|
|
| `Undo` | `actionUndo` → `MainWindow::onUndo()` | Already wired |
|
|
| `Redo` | `actionRedo` → `MainWindow::onRedo()` | Already wired |
|
|
| `RenderRequested` | After every engine operation, call `canvas->update()` | Needs wiring |
|
|
| `DrawingFinished` | After `end_stroke`, no-op in egui | No-op in Qt too |
|
|
|
|
### 2. Document Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `DocSwitched(usize)` | `QTabWidget::currentChanged` → `MainWindow::onTabChanged(int)` | Already wired |
|
|
| `DocClosed(usize)` | `QTabWidget::tabCloseRequested` → `MainWindow::onTabCloseRequested(int)` | Already wired |
|
|
| `CloseAllDocs` | Menu action → iterate tabs, close all | Needs wiring |
|
|
| `DocCreated{name,w,h}` | `NewImageDialog` → `MainWindow::createDocument()` | Already wired |
|
|
|
|
### 3. File Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `FileOpenRequested` | `actionOpen` → `QFileDialog::getOpenFileName` → `engine->open_image()` | Stub exists |
|
|
| `FileSaveRequested` | `actionSave` → `engine->save_as()` | Stub exists |
|
|
| `FileSaveAsRequested` | `actionSaveAs` → `QFileDialog::getSaveFileName` → `engine->save_as()` | Stub exists |
|
|
| `FileOpened(path,data,w,h)` | After successful load, create document | Needs wiring |
|
|
| `ExportRequested` | `actionExport` → `QFileDialog::getSaveFileName` | Stub exists |
|
|
| `ImportRequested` | `actionImport` → `QFileDialog::getOpenFileName` | Stub exists |
|
|
| `ImportSvg(path)` | `actionImportSvg` → `engine->import_svg()` | Stub exists |
|
|
| `ImportBrushesRequested` | `actionImportBrushes` → `QFileDialog` → ABR parser | Stub exists |
|
|
|
|
### 4. Filter Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `ApplyFilter(id, params)` | `FiltersPanel::itemClicked` → `engine->apply_filter()` | Already wired |
|
|
|
|
### 5. Selection Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `SelectionAll` | `actionSelectAll` → `engine->selection_all()` | Already wired |
|
|
| `SelectionDeselect` | `actionSelectDeselect` → `engine->selection_clear()` | Already wired |
|
|
| `SelectionInvert` | `actionSelectInvert` → `engine->selection_invert()` | Already wired |
|
|
| `SelectionGrow(px)` | `actionSelectGrow` → dialog → `engine->selection_grow()` | Stub exists |
|
|
| `SelectionShrink(px)` | `actionSelectShrink` → dialog → `engine->selection_shrink()` | Stub exists |
|
|
| `SelectionFeather(r)` | `actionSelectFeather` → dialog → `engine->selection_feather()` | Stub exists |
|
|
| `SelectionErode(px)` | `actionSelectErode` → dialog → `engine->selection_shrink()` | Stub exists |
|
|
| `SelectionFade(px)` | `actionSelectFade` → dialog → `engine->selection_feather()` | Stub exists |
|
|
|
|
### 6. Image Transform Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `RotateClockwise` | `actionRotateCW` → `engine->rotate_layer(+90)` | Already wired |
|
|
| `RotateCounterClockwise` | `actionRotateCCW` → `engine->rotate_layer(-90)` | Already wired |
|
|
| `Rotate180` | `actionRotate180` → `engine->rotate_layer(180)` | Already wired |
|
|
| `FlipHorizontal` | `actionFlipH` → `engine->flip_layer_horizontal()` | Already wired |
|
|
| `FlipVertical` | `actionFlipV` → `engine->flip_layer_vertical()` | Already wired |
|
|
| `Crop` | `actionCropImage` → `engine->crop(selection_rect)` | Stub exists |
|
|
| `ImageSize` | `actionImageSize` → dialog | Stub exists |
|
|
| `CanvasSize` | `actionCanvasSize` → dialog | Stub exists |
|
|
| `Invert` | `actionInvert` → `engine->apply_filter("invert")` | Already wired |
|
|
|
|
### 7. Layer Operations
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `MergeDown` | `actionMergeDown` → `engine->merge_down()` | Already wired |
|
|
| `FlattenImage` | `actionFlattenImage` → `engine->flatten_image()` | Already wired |
|
|
| `LayerClear` | `actionClearLayer` → `engine->clear_active_layer_pixels()` | Already wired |
|
|
| `TransformStart` | `actionFreeTransform` → enter transform mode | Stub exists |
|
|
| `AlignLayer(axis)` | `Layer > Align > [axis]` → `engine->align_active_layer()` | Stub exists |
|
|
|
|
### 8. Clipboard Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `ClipboardCopy` | `actionCopy` → `QClipboard` → copy layer pixels | Stub exists |
|
|
| `ClipboardCut` | `actionCut` → copy + clear pixels | Stub exists |
|
|
| `ClipboardPaste` | `actionPaste` → `QClipboard` → paste as new layer | Stub exists |
|
|
| `ClipboardPasteSpecial` | `actionPasteSpecial` → paste with options | Stub exists |
|
|
|
|
### 9. Zoom/Pan Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `ZoomIn` | `actionZoomIn` → `canvas->zoomIn()` | Already wired |
|
|
| `ZoomOut` | `actionZoomOut` → `canvas->zoomOut()` | Already wired |
|
|
| `ZoomReset` | `actionZoomReset` → `canvas->setZoom(1.0)` | Already wired |
|
|
| `ZoomSet(f)` | `zoomSlider` valueChanged → `canvas->setZoom()` | Already wired |
|
|
| `Pan(dx,dy)` | Middle-mouse drag → `canvas->resetPan()` | Already wired |
|
|
| `PanReset` | `actionPanReset` → `canvas->resetPan()` | Already wired |
|
|
|
|
### 10. AI/Vision Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `AiChatSubmit(text)` | `AiChatPanel` send button → AI engine call | Stub exists |
|
|
| `AiChatStop` | AI panel stop button | Stub exists |
|
|
| `SmartSelectRun{x,y}` | Smart Select tool click → `engine->vision_object_segment()` | Stub exists |
|
|
| `VisionRunTask` | Vision panel run button → `engine->vision_*()` | Stub exists |
|
|
| `VisionSelectRun{x1,y1,x2,y2}` | Vision Select tool drag → `engine->vision_object_segment()` | Stub exists |
|
|
| `VisionBackendChanged(backend)` | Vision panel CPU/GPU toggle | Stub exists |
|
|
|
|
### 11. Text Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `ToolTextStart{x,y}` | Text tool click → `engine->add_text()` | Stub exists |
|
|
| `ToolTextAccepted` | Accept button → commit text layer | Stub exists |
|
|
| `ToolTextCancelled` | Cancel button → delete/revert text layer | Stub exists |
|
|
| `TextLayerSelected(id)` | Double-click text layer → enter edit mode | Stub exists |
|
|
|
|
### 12. Theme/Misc Events
|
|
| egui Event | Qt Signal/Slot | Implementation |
|
|
|-----------|---------------|----------------|
|
|
| `ThemeChanged(preset)` | Theme menu items → `applyTheme()` | Already wired |
|
|
| `ResetLayout` | `actionResetLayout` → recreate dock layout | Stub exists |
|
|
| `SaveSettings` | After settings change → save to file | Stub exists |
|
|
| `ShowStatusMessage(text)` | After operations → `statusBar->showMessage()` | Needs wiring |
|
|
| `OpenLayerStyles(id)` | Layer Styles button → open panel | Stub exists |
|
|
|
|
### 13. Brush Editor Events (from `brush_editor.rs`)
|
|
| egui Feature | Qt Implementation |
|
|
|-------------|------------------|
|
|
| Brush Tip category (size, hardness, spacing, opacity) | `BrushesPanel` needs sliders |
|
|
| Dynamics category (pressure, velocity, tilt) | `BrushesPanel` needs sliders |
|
|
| Color category (cyclic color, variant) | `BrushesPanel` needs checkboxes |
|
|
| Texture category (brush texture overlays) | `BrushesPanel` needs texture picker |
|
|
| Live stroke preview (engine-rendered) | `CanvasWidget` render brush preview |
|
|
| Preset save/load | `BrushesPanel` save/load buttons |
|
|
|
|
### 14. Geometry Panel Events (from `geometry_panel.rs`)
|
|
| egui Feature | Qt Implementation |
|
|
|-------------|------------------|
|
|
| Vector shape list (per-shape selection) | `PropertiesPanel` or new `GeometryPanel` |
|
|
| Per-shape stroke/opacity/hardness sliders | `PropertiesPanel` dynamic controls |
|
|
| Per-shape color picker (stroke + fill) | `PropertiesPanel` color buttons |
|
|
| Per-shape bounds display | `PropertiesPanel` label |
|
|
| Per-shape delete button | `PropertiesPanel` delete button |
|
|
| Boolean operations (union, subtract, intersect) | `PropertiesPanel` or context menu |
|
|
|
|
### 15. Layer Styles Events (from `layer_styles_panel.rs`)
|
|
| egui Feature | Qt Implementation |
|
|
|-------------|------------------|
|
|
| Drop Shadow (enable, opacity, angle, distance, spread, size, color, blend) | `LayerStylesPanel` sliders |
|
|
| Inner Shadow (same params) | `LayerStylesPanel` sliders |
|
|
| Outer Glow (enable, opacity, spread, size, color, blend) | `LayerStylesPanel` sliders |
|
|
| Inner Glow (enable, opacity, choke, size, color, blend) | `LayerStylesPanel` sliders |
|
|
| Bevel & Emboss (enable, depth, size, angle, altitude, highlight/shadow) | `LayerStylesPanel` sliders |
|
|
| Satin (enable, opacity, angle, distance, size, color, invert) | `LayerStylesPanel` sliders |
|
|
| Color Overlay (enable, opacity, color, blend) | `LayerStylesPanel` controls |
|
|
| Gradient Overlay (enable, opacity, blend, angle, scale, gradient) | `LayerStylesPanel` controls |
|
|
| Pattern Overlay (enable, opacity, blend, scale, pattern) | `LayerStylesPanel` controls |
|
|
| Stroke (enable, size, position, opacity, color, blend) | `LayerStylesPanel` controls |
|
|
|
|
## Tasks (Priority Order)
|
|
|
|
### P0: Fix QSS Colors to Match egui Exactly
|
|
**File:** `hcie-qt-app/resources/themes/photoshop_dark.qss`
|
|
|
|
All 12+ color tokens must match `ThemeColors::get(ThemePreset::Photoshop)`:
|
|
- `#1c1c1e` (bg_app), `#323236` (bg_panel), `#3e3e42` (bg_hover), `#242426` (bg_dark_depth)
|
|
- `#3a3a3e` (bg_elevated), `#161618` (bg_recessed), `#202022` (title_bar_bg)
|
|
- `#3878dc` (accent), `#404044` (border_high), `#28282a` (border_low)
|
|
- `#e0e0e0` (text_primary), `#949498` (text_secondary)
|
|
|
|
### P1: Wire All Menu Actions to Engine FFI
|
|
**File:** `hcie-qt-app/src/mainwindow.cpp`
|
|
|
|
Currently stub implementations exist for: Import, Export, ClipboardCopy/Cut/Paste, Crop, ImageSize, CanvasSize, SelectionGrow/Shrink/Feather/Erode/Fade, Align, Transform, LayerStyles, ImageSize, CanvasSize.
|
|
|
|
Each must call the corresponding `m_engine->methodName()` and then `refresh()` all panels + update canvas.
|
|
|
|
### P2: Wire Panel Signals to Engine
|
|
**Files:** All panel `.cpp` files
|
|
|
|
Each panel's `refresh()` must read from engine and populate widgets. Each widget change must call engine method and emit `renderRequested`.
|
|
|
|
Key panels needing signal wiring:
|
|
- **LayersPanel**: visibility toggle, blend mode change, opacity change, layer reorder, add/delete/group/flatten buttons
|
|
- **HistoryPanel**: jump-to-step click → `engine->jumpToHistory()`
|
|
- **PropertiesPanel**: tool-specific sliders (size, opacity, hardness, flow, spacing per tool)
|
|
- **BrushesPanel**: preset selection → `engine->set_brush_tip()`
|
|
- **FiltersPanel**: filter click → `engine->apply_filter(name, params)`
|
|
- **ColorsPanel**: color selection → `engine->set_color(rgba)`
|
|
- **AiChatPanel**: send button → `engine->ai_chat_submit()`
|
|
|
|
### P3: Add Missing Panels
|
|
**New files needed:**
|
|
- `geometry_panel.h/cpp` — vector shape list and per-shape editing
|
|
- `layer_styles_panel.cpp` — flesh out with 10 style editors (currently stub)
|
|
|
|
### P4: Canvas Rendering Loop
|
|
**File:** `hcie-qt-app/src/canvas_widget.cpp`
|
|
|
|
The `paintGL()` must call `engine->get_composite_pixels()` every frame (or on dirty). The current implementation does this but needs to handle the dirty flag properly — only re-upload when `engine->isCompositeDirty()`.
|
|
|
|
### P5: Properties Panel Tool-Specific Controls
|
|
**File:** `hcie-qt-app/src/panels/properties_panel.cpp`
|
|
|
|
Currently shows placeholder. Must rebuild tool settings based on active tool:
|
|
- Brush/Eraser: size, opacity, hardness, flow, spacing, color variant, cyclic color
|
|
- Pen: size, opacity, hardness, cyclic color
|
|
- Spray: radius, particle size, density, opacity, hardness
|
|
- Flood Fill: tolerance
|
|
- Magic Wand: tolerance
|
|
- Vector tools: stroke, opacity, fill toggle, round rect radius, star points
|
|
- Text: font, size, color, angle, alignment, orientation
|
|
- Gradient: type (linear/radial)
|
|
- VectorSelect: per-shape editing
|
|
|
|
### P6: Color Panel with Standard Qt ColorPicker
|
|
**File:** `hcie-qt-app/src/panels/colors_panel.cpp`
|
|
|
|
Replace custom HSL wheel (broken rendering) with:
|
|
- Foreground/Background color swatches
|
|
- "Pick Color" button → `QColorDialog::getColor()`
|
|
- Palette grid (basic colors)
|
|
- Recent colors list
|
|
|
|
### P7: Status Bar Message Expiry
|
|
**File:** `hcie-qt-app/src/mainwindow.cpp`
|
|
|
|
Implement `ShowStatusMessage` with 3-second timer expiry (matching egui).
|
|
|
|
## Validation
|
|
|
|
After each task:
|
|
1. `cmake --build build` — must compile
|
|
2. Visual comparison with egui screenshots
|
|
3. Functional test: each menu item, each panel interaction, each keyboard shortcut
|
|
|
|
## Files Modified
|
|
|
|
| File | Changes |
|
|
|------|---------|
|
|
| `hcie-qt-app/resources/themes/photoshop_dark.qss` | ~30 color values, all widget styling |
|
|
| `hcie-qt-app/src/mainwindow.cpp` | Wire all menu actions to engine |
|
|
| `hcie-qt-app/src/mainwindow.h` | Add missing method declarations |
|
|
| `hcie-qt-app/src/panels/properties_panel.cpp` | Tool-specific settings |
|
|
| `hcie-qt-app/src/panels/colors_panel.cpp` | QColorDialog-based picker |
|
|
| `hcie-qt-app/src/panels/brushes_panel.cpp` | Preset selection, brush tip editing |
|
|
| `hcie-qt-app/src/panels/history_panel.cpp` | Jump-to-step |
|
|
| `hcie-qt-app/src/panels/layers_panel.cpp` | Visibility/blend/opacity signals |
|
|
| `hcie-qt-app/src/panels/filters_panel.cpp` | Filter param dialogs |
|
|
| `hcie-qt-app/src/panels/ai_chat_panel.cpp` | AI chat submit |
|
|
| `hcie-qt-app/src/panels/layer_styles_panel.cpp` | 10 style editors |
|
|
| `hcie-qt-app/src/panels/layer_details_panel.cpp` | Read-only info display |
|
|
| `hcie-qt-app/src/canvas_widget.cpp` | Dirty flag, composite upload |
|
|
| `hcie-qt-app/src/hcie_engine_wrapper.h/cpp` | Missing FFI methods |
|
|
| `hcie-qt-app/src/dialogs/*` | Wire all dialog buttons |
|
|
| `hcie-engine-api/src/ffi.rs` | Add missing FFI functions |
|