mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-28 13:22:06 +00:00
feat: Add layout debugging/inspecting tool (#14919)
* Add wxInspector dep
* Initial intergration of wxInspector
* docs: add wxInspector plugins design spec
Design spec for two wxInspector plugins (DPIAware + CustomWidgets) that expose
OrcaSlicer's custom control properties in the inspector property grid.
Covers: DPIAware scale-factor properties, Button, CheckBox, TextInput,
SwitchButton, ProgressBar, Label, and LabeledStaticBox.
* docs: add wxInspector plugins implementation plan
6-task plan covering: source changes to existing widget headers,
DPIAwarePlugin, CustomWidgetsPlugin, registration helper,
MainFrame/CMake wiring, and build verification.
* feat: add getters/setters for wxInspector plugin access
Add minimal public accessors to DPIAware (set_scale_factor,
set_prev_scale_factor, set_em_unit, force_rescale), Button
(GetStyle, GetType, IsSelected), CheckBox (IsHalfChecked),
TextInput (GetCornerRadius), and LabeledStaticBox
(GetCornerRadius, GetBorderWidth, GetBorderColor, GetScale).
* feat: add wxInspector plugin registration helper
Add RegisterOrcaInspectorPlugins() inline function that creates
and registers the DPIAwarePlugin and CustomWidgetsPlugin as
static instances (matching wxInspector's built-in pattern).
* feat: add DPIAware wxInspector plugin
Exposes DPI scaling properties (scale_factor, prev_scale_factor,
em_unit, normal_font, force_rescale) on DPIFrame and DPIDialog
widgets. Uses dynamic_cast for detection and a template helper
to capture the correct static type for lambda accessors.
* feat: add OrcaCustomWidgets wxInspector plugin
Exposes Orca-specific properties on 7 widget types:
- Button: Style, Type, Selected
- CheckBox: Half Checked
- TextInput: Label, Text Value, Corner Radius
- SwitchButton: Value
- ProgressBar: Proportion, Show Number
- Label: Is Hyperlink, Font Point Size
- LabeledStaticBox: Corner Radius, Border Width, Border Color, Scale
Each widget type uses dynamic_cast for safe detection.
* feat: wire wxInspector plugins into MainFrame and build
Call RegisterOrcaInspectorPlugins() in MainFrame constructor after
SetupInspectorAccelerator(). Add all 5 plugin source files to
SLIC3R_GUI_SOURCES in CMakeLists.txt.
* fix: move plugin registration to GUI_App::on_init_inner
Register plugins once in app init rather than in MainFrame
constructor, which may be recreated during the application
lifetime.
* fix: include plugin headers in Registration.hpp for complete types
Static locals require complete type. Include DPIAwarePlugin.hpp and
CustomWidgetsPlugin.hpp instead of forward-declaring. Also remove
unused include from MainFrame.cpp (registration moved to GUI_App).
* fix: qualify DPIFrame/DPIDialog with Slic3r::GUI namespace
* Make DPIDialog inspectable. For other dialogs, we will add them if necessary later.
* docs: add spec for moving wxInspectable into DPIAware template
Move wxInspector::wxInspectable base class from DPIDialog and MainFrame
into the common DPIAware<P> template, making all DPIAware widgets
automatically visible in the inspector tree.
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: add implementation plan for moving wxInspectable into DPIAware
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: update spec/plan — move SetupInspectorAccelerator into DPIAware too
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: move wxInspectable and SetupInspectorAccelerator into DPIAware
DPIAware<P> now inherits wxInspector::wxInspectable and calls
SetupInspectorAccelerator in its constructor, making all DPIAware
widgets automatically appear in the inspector tree with the
Ctrl+Shift+I shortcut. DPIDialog now uses 'using' to inherit the
constructor. Remove redundant wxInspectable inheritance and
SetupInspectorAccelerator calls from DPIDialog and MainFrame.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: use LB_HYPERLINK constant instead of magic number 0x0020
Co-Authored-By: Claude <noreply@anthropic.com>
* Clean up
* Fix Linux build
* Don't build wxInspector sample
* Use shallow clone
* Try fix flatpak build
* Attempt to fix build again
* Fix build failure caused by 436c16135e
* wxWidgets build only download required submodules
* This should fix build on Windows on ARM
* Enable PIC
* Disable layout inspector by default for public release
* Use wxInspector 1.0.0 release
---------
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# Move `wxInspectable` into `DPIAware` — Design Spec
|
||||
|
||||
Date: 2026-07-23
|
||||
Branch: `dev/layout-inspector`
|
||||
|
||||
## Overview
|
||||
|
||||
Move the `wxInspector::wxInspectable` base class from individual leaf classes (`DPIDialog`, `MainFrame`) into the common `DPIAware<P>` template. This makes every DPIAware widget automatically visible in the inspector tree without requiring each subclass to opt in.
|
||||
|
||||
## Motivation
|
||||
|
||||
Currently, only `DPIDialog` and `MainFrame` explicitly inherit `wxInspectable`. `DPIFrame` (which `MainFrame` inherits from) does not — `MainFrame` adds it manually. This means:
|
||||
|
||||
- Any `DPIAware<T>` widget that isn't `DPIDialog` or `MainFrame` is invisible in the inspector tree
|
||||
- `DPIFrame` subclasses (`BaseTransparentDPIFrame`, `ImageDPIFrame`, `ModelMallDialog`, `MediaFileFrame`, `SecondaryCheckDialog`, `PrintErrorDialog`, etc.) don't appear
|
||||
- Adding a new DPIAware widget type requires remembering to also inherit `wxInspectable`
|
||||
|
||||
Moving `wxInspectable` to `DPIAware` fixes this for all current and future DPIAware widgets at once.
|
||||
|
||||
## Design
|
||||
|
||||
### Change 1: `GUI_Utils.hpp` — `DPIAware<P>`
|
||||
|
||||
Add `wxInspector::wxInspectable` as a second base class, and call `SetupInspectorAccelerator(this)` in the constructor (after `this->CenterOnParent()`):
|
||||
|
||||
```cpp
|
||||
// Before:
|
||||
template<class P> class DPIAware : public P
|
||||
|
||||
// After:
|
||||
template<class P> class DPIAware : public P, public wxInspector::wxInspectable
|
||||
```
|
||||
|
||||
Add in the constructor body (after `this->CenterOnParent()` at line 110):
|
||||
```cpp
|
||||
SetupInspectorAccelerator(this);
|
||||
```
|
||||
|
||||
This gives every `DPIAware<T>` widget both inspectability and the Ctrl+Shift+I keyboard shortcut automatically. `#include <wx/inspector/inspector.h>` is already present in the file.
|
||||
|
||||
### Change 2: `GUI_Utils.hpp` — `DPIDialog`
|
||||
|
||||
Remove the now-redundant `wxInspector::wxInspectable` and the `SetupInspectorAccelerator(this)` call:
|
||||
|
||||
```cpp
|
||||
// Before:
|
||||
class DPIDialog : public DPIAware<wxDialog>, public wxInspector::wxInspectable
|
||||
// ...
|
||||
SetupInspectorAccelerator(this);
|
||||
|
||||
// After:
|
||||
class DPIDialog : public DPIAware<wxDialog>
|
||||
// (SetupInspectorAccelerator call removed — now done in DPIAware constructor)
|
||||
```
|
||||
|
||||
`DPIDialog` gets `wxInspectable` and the accelerator through `DPIAware<wxDialog>` now.
|
||||
|
||||
### Change 3: `MainFrame.hpp` — `MainFrame`
|
||||
|
||||
Remove the now-redundant `wxInspector::wxInspectable`:
|
||||
|
||||
```cpp
|
||||
// Before:
|
||||
class MainFrame : public DPIFrame, public wxInspector::wxInspectable
|
||||
|
||||
// After:
|
||||
class MainFrame : public DPIFrame
|
||||
```
|
||||
|
||||
`MainFrame` gets `wxInspectable` through `DPIFrame` → `DPIAware<wxFrame>`.
|
||||
|
||||
### Change 4: `MainFrame.cpp` — `MainFrame` constructor
|
||||
|
||||
Remove the now-redundant `SetupInspectorAccelerator(this)` call (line 304). It will be called automatically by the `DPIAware` constructor.
|
||||
|
||||
## Impact
|
||||
|
||||
| Widget | Before | After |
|
||||
|--------|--------|-------|
|
||||
| `DPIDialog` subclasses (~80) | ✓ inspectable | ✓ inspectable (transitive) |
|
||||
| `MainFrame` | ✓ inspectable | ✓ inspectable (transitive) |
|
||||
| `DPIFrame` subclasses (8 others) | ✗ invisible | ✓ inspectable |
|
||||
| Future `DPIAware<T>` | ✗ invisible | ✓ inspectable |
|
||||
|
||||
## Files Modified
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `src/slic3r/GUI/GUI_Utils.hpp` | `DPIAware<P>` gains `wxInspector::wxInspectable` + `SetupInspectorAccelerator(this)` call; `DPIDialog` drops redundant `wxInspector::wxInspectable` and `SetupInspectorAccelerator(this)` |
|
||||
| `src/slic3r/GUI/MainFrame.hpp` | `MainFrame` drops redundant `wxInspector::wxInspectable` |
|
||||
| `src/slic3r/GUI/MainFrame.cpp` | Remove redundant `SetupInspectorAccelerator(this)` from MainFrame constructor |
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- The `DPIAwarePlugin` detection logic (`dynamic_cast<DPIFrame*>` / `dynamic_cast<DPIDialog*>`) is unchanged
|
||||
- No new DPI properties — this is purely about tree visibility and accelerator setup
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
- **Multiple inheritance**: `DPIAware<P>` already has a vtable (virtual destructor). Adding `wxInspectable` adds a second base but no additional data members. The `wxInspector::wxInspectable` class is expected to be a lightweight marker interface.
|
||||
- **Build**: No new includes needed; `<wx/inspector/inspector.h>` is already included in `GUI_Utils.hpp`.
|
||||
- **Cross-platform**: The change is standard C++ multiple inheritance — no platform-specific concerns.
|
||||
244
docs/superpowers/specs/2026-07-23-wx-inspector-plugins-design.md
Normal file
244
docs/superpowers/specs/2026-07-23-wx-inspector-plugins-design.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# wxInspector Plugins for OrcaSlicer Custom Controls — Design Spec
|
||||
|
||||
Date: 2026-07-23
|
||||
Branch: `dev/layout-inspector`
|
||||
|
||||
## Overview
|
||||
|
||||
Create wxInspector plugins that expose OrcaSlicer's custom widget properties in the inspector's property grid. Without these plugins, the inspector shows only generic wxWidgets properties — missing all DPI-awareness data, custom styling, and Orca-specific control state.
|
||||
|
||||
## Goals
|
||||
|
||||
1. **DPIAware properties** — Inspect and update `scale_factor`, `prev_scale_factor`, `em_unit`, and `normal_font` on any DPIAware-derived widget
|
||||
2. **Custom widget properties** — Surface Orca-specific properties on `Button`, `CheckBox`, `TextInput`, `SwitchButton`, `ProgressBar`, `Label`, and `LabeledStaticBox`
|
||||
3. **Minimal source changes** — Only add trivial (one-line) getters/setters to existing classes; no architectural refactoring of Orca's widget hierarchy
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Custom inspector panels or AUI tabs (use the existing property grid and method invoker)
|
||||
- Python-plugin integration (this is C++ wxInspector, not Orca's Python plugin system)
|
||||
- Event logging customization (the built-in event logger already works)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Two Plugins
|
||||
|
||||
| Plugin | Class | Files |
|
||||
|--------|-------|-------|
|
||||
| DPIAware plugin | `DPIAwarePlugin` | `DPIAwarePlugin.hpp`, `DPIAwarePlugin.cpp` |
|
||||
| Custom widgets plugin | `CustomWidgetsPlugin` | `CustomWidgetsPlugin.hpp`, `CustomWidgetsPlugin.cpp` |
|
||||
| Registration helper | inline function | `Registration.hpp` |
|
||||
|
||||
All files live under `src/slic3r/Utils/wxInspectorPlugins/`.
|
||||
|
||||
### Plugin Detection Strategy
|
||||
|
||||
**DPIAware plugin**: Uses `dynamic_cast<DPIFrame*>` and `dynamic_cast<DPIDialog*>` as detection gates. `DPIFrame` = `DPIAware<wxFrame>`, `DPIDialog` = `DPIAware<wxDialog>`. Since these are concrete typedefs, `dynamic_cast` works at runtime. This covers `MainFrame`, `SettingsDialog`, and all 8 calibration dialogs (which inherit `DPIDialog`).
|
||||
|
||||
**Custom widgets plugin**: Gates broadly on `CLASSINFO(wxWindow)`, then uses per-type `dynamic_cast` inside `GetProperties` to check each Orca-specific type. Only matching types append properties.
|
||||
|
||||
### Registration
|
||||
|
||||
A single `RegisterOrcaInspectorPlugins()` inline function in `Registration.hpp` creates both plugins as function-local statics (matching the wxInspector built-in provider pattern) and registers them via `wxInspector::RegisterPlugin()`.
|
||||
|
||||
Called once from `MainFrame::MainFrame()` after `SetupInspectorAccelerator(this)`.
|
||||
|
||||
### Why Separate Plugins?
|
||||
|
||||
- DPIAware is a C++ template concept (not a wxClassInfo-isKindOf check), so it needs its own detection logic
|
||||
- Custom widgets use standard wxClassInfo-based detection, matching the built-in provider pattern
|
||||
- Two focused files are easier to review and maintain than one monolithic plugin
|
||||
- Compile-time failure isolation: if a widget header changes, only one plugin breaks
|
||||
|
||||
## DPIAware Plugin — Property Specification
|
||||
|
||||
### Source Changes (GUI_Utils.hpp)
|
||||
|
||||
Four one-liner methods added to the `DPIAware<P>` template class (public section):
|
||||
|
||||
```cpp
|
||||
float scale_factor() const { return m_scale_factor; } // already exists
|
||||
float prev_scale_factor() const { return m_prev_scale_factor; } // already exists
|
||||
int em_unit() const { return m_em_unit; } // already exists
|
||||
void set_scale_factor(float v) { m_scale_factor = v; } // NEW
|
||||
void set_prev_scale_factor(float v) { m_prev_scale_factor = v; } // NEW
|
||||
void set_em_unit(int v) { m_em_unit = v; } // NEW
|
||||
bool force_rescale() const { return m_force_rescale; } // NEW
|
||||
// m_normal_font getter already exists: normal_font()
|
||||
```
|
||||
|
||||
### Detection
|
||||
|
||||
```cpp
|
||||
bool CanProvideProperties(wxClassInfo* info) override {
|
||||
// Gated in GetProperties via dynamic_cast on the window itself
|
||||
return info->IsKindOf(CLASSINFO(wxWindow));
|
||||
}
|
||||
```
|
||||
|
||||
In `GetProperties`:
|
||||
```cpp
|
||||
auto* win = obj.AsWindow();
|
||||
bool isDPI = dynamic_cast<DPIFrame*>(win) || dynamic_cast<DPIDialog*>(win);
|
||||
if (!isDPI) return props;
|
||||
```
|
||||
|
||||
### Property Table (category: "DPI Scaling")
|
||||
|
||||
| Name | Type | Editable | Getter | Setter |
|
||||
|------|------|----------|--------|--------|
|
||||
| Scale Factor | String (float) | Yes | `dpi->scale_factor()` | `dpi->set_scale_factor(v)` |
|
||||
| Prev Scale Factor | String (float) | Yes | `dpi->prev_scale_factor()` | `dpi->set_prev_scale_factor(v)` |
|
||||
| EM Unit | Integer | Yes | `dpi->em_unit()` | `dpi->set_em_unit(v)` |
|
||||
| Normal Font | ReadOnly | No | `dpi->normal_font().GetNativeFontInfoDesc()` | — |
|
||||
| Force Rescale | Boolean (ReadOnly) | No | `dpi->force_rescale()` | — |
|
||||
|
||||
**Note on setters**: The setters simply store values. They do NOT trigger a widget rescale/layout. To see the effect of a changed scale factor, use the inspector's Methods panel to call `Layout()` or resize the window — which triggers the DPI_CHANGED event path naturally.
|
||||
|
||||
## Custom Widgets Plugin — Property Specification
|
||||
|
||||
All properties are appended to the built-in wxWindow properties. Each widget type is independently detected via `dynamic_cast`.
|
||||
|
||||
### Detection gates (in `GetProperties`)
|
||||
|
||||
```cpp
|
||||
auto* win = obj.AsWindow();
|
||||
if (auto* btn = dynamic_cast<Button*>(win)) { addButtonProperties(btn, props); }
|
||||
if (auto* cb = dynamic_cast<CheckBox*>(win)) { addCheckBoxProperties(cb, props); }
|
||||
if (auto* ti = dynamic_cast<TextInput*>(win)) { addTextInputProperties(ti, props); }
|
||||
if (auto* sb = dynamic_cast<SwitchButton*>(win)) { addSwitchButtonProperties(sb, props); }
|
||||
if (auto* pb = dynamic_cast<ProgressBar*>(win)) { addProgressBarProperties(pb, props); }
|
||||
if (auto* lbl = dynamic_cast<Label*>(win)) { addLabelProperties(lbl, props); }
|
||||
if (auto* lsb = dynamic_cast<LabeledStaticBox*>(win)) { addLabeledStaticBoxProperties(lsb, props); }
|
||||
```
|
||||
|
||||
### Orca Button (`Button`) — category: "Orca Button"
|
||||
|
||||
| Name | Type | Editable | Getter | Setter |
|
||||
|------|------|----------|--------|--------|
|
||||
| Button Style | Choice | Yes | enum→string | string→enum |
|
||||
| Button Type | Choice | Yes | enum→string | string→enum |
|
||||
| Selected | Boolean | Yes | `m_selected` (needs getter) | `SetSelected(v)` |
|
||||
| Active Icon | ReadOnly | No | icon name string | — |
|
||||
| Inactive Icon | ReadOnly | No | icon name string | — |
|
||||
|
||||
Choices for Button Style: `Regular`, `Confirm`, `Alert`, `Disabled`
|
||||
Choices for Button Type: `Compact`, `Window`, `Choice`, `Parameter`, `Icon`, `Expanded`
|
||||
|
||||
**Source changes needed**: Button's `m_selected` is private. Add one-liner getter:
|
||||
```cpp
|
||||
bool IsSelected() const { return m_selected; }
|
||||
```
|
||||
|
||||
### Orca CheckBox (`CheckBox`) — category: "Orca CheckBox"
|
||||
|
||||
| Name | Type | Editable | Getter | Setter |
|
||||
|------|------|----------|--------|--------|
|
||||
| Half Checked | Boolean | Yes | `m_half_checked` (needs getter) | `SetHalfChecked(v)` |
|
||||
|
||||
**Source changes needed**: `m_half_checked` is private. Add one-liner getter:
|
||||
```cpp
|
||||
bool IsHalfChecked() const { return m_half_checked; }
|
||||
```
|
||||
|
||||
### Orca TextInput (`TextInput`) — category: "Orca TextInput"
|
||||
|
||||
| Name | Type | Editable | Getter | Setter |
|
||||
|------|------|----------|--------|--------|
|
||||
| Label | String | Yes | `GetLabel()` (inherited from wxWindow) | `SetLabel(v)` (exists) |
|
||||
| Text Value | String | Yes | `GetTextCtrl()->GetValue()` (GetTextCtrl is public) | `GetTextCtrl()->SetValue(v)` |
|
||||
| Corner Radius | Integer | Yes | `GetCornerRadius()` (NEW) | `SetCornerRadius(v)` (exists) |
|
||||
|
||||
**Source changes needed**: Add one getter to `TextInput`:
|
||||
```cpp
|
||||
int GetCornerRadius() const { return static_cast<int>(radius); }
|
||||
```
|
||||
(`radius` is inherited from StaticBox. `SetCornerRadius(double)` already exists. `GetTextCtrl()` is already public.)
|
||||
|
||||
### Orca SwitchButton (`SwitchButton`) — category: "Orca SwitchButton"
|
||||
|
||||
| Name | Type | Editable | Getter | Setter |
|
||||
|------|------|----------|--------|--------|
|
||||
| Value | Boolean | Yes | existing getter | existing setter |
|
||||
|
||||
### Orca ProgressBar (`ProgressBar`) — category: "Orca ProgressBar"
|
||||
|
||||
| Name | Type | Editable | Getter | Setter |
|
||||
|------|------|----------|--------|--------|
|
||||
| Proportion | Float (0-1) | Yes | `pb->m_proportion` (public member) | `pb->m_proportion = v` |
|
||||
| Show Number | Boolean | Yes | `pb->m_shownumber` (public member) | `pb->m_shownumber = v` |
|
||||
|
||||
**No source changes needed**: `m_proportion` and `m_shownumber` are already public members. `SetValue(int)` and `SetProgress(int)` already exist as public methods.
|
||||
|
||||
### Orca Label (`Label`) — category: "Orca Label"
|
||||
|
||||
| Name | Type | Editable | Getter | Setter |
|
||||
|------|------|----------|--------|--------|
|
||||
| Is Hyperlink | Boolean | No | existing flag check | — |
|
||||
| Font Size | ReadOnly | No | `GetFont().GetPointSize()` | — |
|
||||
|
||||
### LabeledStaticBox — category: "LabeledStaticBox"
|
||||
|
||||
| Name | Type | Editable | Getter | Setter |
|
||||
|------|------|----------|--------|--------|
|
||||
| Corner Radius | Integer | Yes | `GetCornerRadius()` (NEW) | `SetCornerRadius(v)` (exists) |
|
||||
| Border Width | Integer | Yes | `GetBorderWidth()` (NEW) | `SetBorderWidth(v)` (exists) |
|
||||
| Border Color | String (hex) | Yes | `GetBorderColor()` (NEW) | `SetBorderColor(v)` (exists) |
|
||||
| Scale | Float (ReadOnly) | No | `m_scale` (protected, needs getter) | — |
|
||||
|
||||
**Source changes needed**: Four one-liner getters added to `LabeledStaticBox`:
|
||||
```cpp
|
||||
int GetCornerRadius() const { return m_radius; }
|
||||
int GetBorderWidth() const { return m_border_width; }
|
||||
StateColor GetBorderColor() const { return border_color; }
|
||||
float GetScale() const { return m_scale; }
|
||||
```
|
||||
|
||||
## Files Modified (Existing Code)
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `src/slic3r/GUI/GUI_Utils.hpp` | +4 methods in `DPIAware<P>`: `set_scale_factor()`, `set_prev_scale_factor()`, `set_em_unit()`, `force_rescale()` |
|
||||
| `src/slic3r/GUI/Widgets/LabeledStaticBox.hpp` | +4 getter declarations: `GetCornerRadius()`, `GetBorderWidth()`, `GetBorderColor()`, `GetScale()` |
|
||||
| `src/slic3r/GUI/Widgets/LabeledStaticBox.cpp` | +4 getter implementations |
|
||||
| `src/slic3r/GUI/Widgets/Button.hpp` | +1 getter: `IsSelected()` |
|
||||
| `src/slic3r/GUI/Widgets/CheckBox.hpp` | +1 getter: `IsHalfChecked()` |
|
||||
| `src/slic3r/GUI/Widgets/TextInput.hpp` | +1 getter: `GetCornerRadius()` |
|
||||
| `src/slic3r/GUI/Widgets/ProgressBar.hpp` | None (public members are used directly) |
|
||||
| `src/slic3r/GUI/MainFrame.cpp` | +1 `#include`, +1 call to `RegisterOrcaInspectorPlugins()` |
|
||||
| `src/slic3r/CMakeLists.txt` | +4 entries in `SLIC3R_GUI_SOURCES` (the .cpp plugin files) |
|
||||
|
||||
## Files Created
|
||||
|
||||
```
|
||||
src/slic3r/Utils/wxInspectorPlugins/
|
||||
├── DPIAwarePlugin.hpp
|
||||
├── DPIAwarePlugin.cpp
|
||||
├── CustomWidgetsPlugin.hpp
|
||||
├── CustomWidgetsPlugin.cpp
|
||||
└── Registration.hpp
|
||||
```
|
||||
|
||||
## Build & Linking
|
||||
|
||||
The `wxInspector` dependency is already wired:
|
||||
- `deps/wxInspector/wxInspector.cmake` fetches and builds wxInspector
|
||||
- `src/CMakeLists.txt` lines 92-93 link `wxInspector::wxInspector` into `wxWidgets_LIBRARIES`
|
||||
- The plugin files only need `#include <wx/inspector/plugin.h>` and `#include <wx/inspector/inspector.h>` — both available from the installed dependency
|
||||
|
||||
No new CMake dependencies needed. Only the new source files need listing in `SLIC3R_GUI_SOURCES`.
|
||||
|
||||
## Error Handling & Edge Cases
|
||||
|
||||
- **Stale pointers**: Plugin lambdas capture raw pointers, regenerated on every `GetProperties` call (matching wxInspector's built-in provider pattern). Pointers live only until the next tree selection.
|
||||
- **Widget destruction**: If a widget is destroyed while the inspector is showing its properties, `InspectableObject::IsValid()` returns false and properties are not displayed. The inspector won't show stale data.
|
||||
- **Invalid property values**: Setters use `sscanf` / `ToLong` with validation (matching built-in patterns). Bogus input is rejected — setter returns `false`, property grid shows error state.
|
||||
- **DPI drift**: Setting `scale_factor` without triggering rescale means displayed sizes don't match the new factor. This is acceptable — the inspector is a developer tool; operators know to call `Layout()` after making changes.
|
||||
- **Missing widget type**: If a `dynamic_cast` fails for all types, only built-in wxWindow properties are shown. No crash, no error — just reduced info.
|
||||
|
||||
## Future Work (Out of Scope)
|
||||
|
||||
- **StateColor visualization**: `StateColor` is a multi-value type (maps bitmask states to colors). A full solution would need a custom property editor (e.g., a table showing each state→color pair). Keep it simple for now.
|
||||
- **ScalableBitmap display**: Could show the bitmap as an inline thumbnail. Complex property editor work — deferred.
|
||||
- **More widget types**: `SwitchBoard`, `MultiSwitchButton`, `StepCtrl`, `FanControl`, `DropDown`, `ComboBox`, `AMS*` widgets could all benefit. Add as needed.
|
||||
- **Property refresh on tree selection**: Currently properties are static snapshots. A "refresh" button or auto-poll could keep values current for rapidly-changing widgets (progress bars, etc.). The built-in wxInspector already provides a tree-refresh button.
|
||||
Reference in New Issue
Block a user