Skip to content

PersistentDialog migrations: six dialogs to convert #2672

Description

@ten9876

Background

PR #2644 landed the `PersistentDialog` base class and migrated `ProfileManagerDialog` as the proof-of-concept (-99 LOC on the dialog file). Six more dialogs in `MainWindow.cpp` use the same hand-rolled lazy-construct + non-modal + geometry-persist + frameless-chrome pattern and are ripe for the same subtractive migration.

The constitution Principle III note in PR #2605: these per-dialog reimplementations were a contributor papercut source (PR #2591 required 9 fixes during review for boilerplate alone). With the base class on main, future dialog conversions and new dialogs only need to wire the content layout via `bodyWidget()`.

Dialogs to migrate

Each follows the same pattern — change the base class, drop the hand-rolled members + lifecycle methods, port content layout into `bodyWidget()`, switch the MainWindow construct site to `showOrRaisePersistent()`.

Dialog File Has geometry persist? Notes
`NetworkDiagnosticsDialog` `src/gui/NetworkDiagnosticsDialog.{cpp,h}` Reference dialog (#2580 set the original pattern)
`MemoryDialog` `src/gui/MemoryDialog.{cpp,h}`
`AetherDspDialog` `src/gui/AetherDspDialog.{cpp,h}` partial Geometry was added later; complete during migration
`DxClusterDialog` (SpotHub) `src/gui/DxClusterDialog.{cpp,h}` Largest dialog (tabs, tables)
`MultiFlexDialog` `src/gui/MultiFlexDialog.{cpp,h}` Add persist as part of migration
`MidiMappingDialog` `src/gui/MidiMappingDialog.{cpp,h}` Add persist as part of migration
`PanLayoutDialog` `src/gui/PanLayoutDialog.{cpp,h}` Add persist as part of migration

(7 total counting `ProfileManagerDialog` which already landed.)

Per-dialog migration recipe

For each dialog `FooDialog`:

  1. Change `class FooDialog : public QDialog` → `class FooDialog : public PersistentDialog`.
  2. Replace the ctor signature `(.., QWidget* parent = nullptr) : QDialog(parent), ...` with `: PersistentDialog("Foo", "FooDialogGeometry", parent), ...`.
  3. Delete `setFramelessMode()`, `closeEvent()`, `moveEvent()`, `resizeEvent()`, `saveGeometryToSettings()`, `restoreGeometryFromSettings()`.
  4. Delete `m_titleBar`, `m_bodyLayout`, `m_restoringGeometry` members.
  5. Move content layout from a local `auto* bodyWidget = new QWidget(this); auto* root = new QVBoxLayout(bodyWidget);` to `auto* root = new QVBoxLayout(bodyWidget());`.
  6. Delete `FramelessResizer::install(this);` and the manual `setFramelessMode(AppSettings::instance().value("FramelessWindow", "True"))` call from the ctor — base class owns both.
  7. In MainWindow's menu callback, replace the lazy-construct boilerplate with `showOrRaisePersistent(m_fooDialog, ctorArgs...)`.
  8. Drop the dialog's explicit `setFramelessMode(on)` call in `MainWindow::setFramelessWindow` — auto-registered via `m_persistentDialogs`.
  9. Verify geometry key reuse — keep the existing AppSettings key (e.g. `MemoryDialogGeometry`) so users don't lose saved positions on upgrade.

Scope and risk

  • Each migration is strictly subtractive for the dialog file (~50–100 LOC removed).
  • Geometry-persist key reuse means zero user impact on upgrade.
  • The three dialogs that don't currently persist geometry will gain it — a UX improvement, not a regression.

Pickup

  • All six are AetherClaude-eligible — mechanical, well-scoped, single-file changes guided by the recipe above.
  • Recommend one PR per dialog to keep review surface small.
  • `NetworkDiagnosticsDialog` is the natural first one (it was the original frameless-chrome reference).

73, Jeremy KK7GWY & Claude (AI dev partner)

Metadata

Metadata

Assignees

No one assigned

    Labels

    GUIUser interfaceenhancementImprovement to existing featuremaintainer-reviewRequires maintainer review before any action is taken

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions