Files
OrcaSlicer/docs/superpowers/specs/2026-07-23-wx-inspectable-on-dpiaware-design.md
Noisyfox 63044b7661 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>
2026-07-26 20:25:25 +08:00

4.2 KiB

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.hppDPIAware<P>

Add wxInspector::wxInspectable as a second base class, and call SetupInspectorAccelerator(this) in the constructor (after this->CenterOnParent()):

// 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):

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.hppDPIDialog

Remove the now-redundant wxInspector::wxInspectable and the SetupInspectorAccelerator(this) call:

// 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.hppMainFrame

Remove the now-redundant wxInspector::wxInspectable:

// Before:
class MainFrame : public DPIFrame, public wxInspector::wxInspectable

// After:
class MainFrame : public DPIFrame

MainFrame gets wxInspectable through DPIFrameDPIAware<wxFrame>.

Change 4: MainFrame.cppMainFrame 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.