Files
hcie-rust-v3.05/.kilo/plans/1783518925862-qt-transition-plan.md
T
2026-07-09 02:59:53 +03:00

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 |