Skip to content

feat(cpn): reorganise and refactor Edit Settings (Profile/Application/Simulator/Update) dialog  - #7623

Merged
pfeerick merged 13 commits into
mainfrom
elecpower/cpn-refactor-app-settings
Sep 13, 2026
Merged

pfeerick merged 13 commits into
mainfrom
elecpower/cpn-refactor-app-settings

Conversation

@elecpower

@elecpower elecpower commented Aug 6, 2026 •

Copy link
Copy Markdown
Collaborator

What users will notice:

  • reorganised tab contents, collapsible sections and scroll bars so content fits on screen
  • renamed menus from settings to preferences to remove ambiguity with model settings
  • firmware type picker dialog grouped by manufacturer

Under the covers:

  • complete ui rewrite
  • store code files in own directory
  • eliminate monolithic code files
  • change code references from settings to preferences
  • extend auto widgets to support getters and setters for field values
  • new auto widgets for paths (to and from native format), push button and file, folder, colour selection
  • split OpenTx firmware settings string into separate settings fields
  • add manufacturer getter to boards (yes this means another list to be maintained
  • housekeeping)

TODO as separate PRs (as this is big enough):

  • convert settings to meaningful names eg gePath holds Google Earth executable path
  • refactor Firmware and Boards classes
  • add models folder setting globally and per profile
  • consider firmwaredefs.json to simplify maintenance of opentxinterface

Some sample side by side screenshots:
image
image
image
image

image

@elecpower elecpower added enhancement ✨ New feature or request companion (cpn) Related to the companion software labels Aug 6, 2026
@elecpower

Copy link
Copy Markdown
Collaborator Author

Working on tree styled radio picker with radios grouped by manufacturer to replace the long dropdown combo box

@pfeerick

Copy link
Copy Markdown
Member

Oh, thanks Neil... wasn't going to look at that until next week :)

@pfeerick

Copy link
Copy Markdown
Member

Looking very nice, and should be good on my other two laptops given the much smaller screen footprint! Have only tried on macOS so far, and will take a bit of getting used to everything being collapsed by default... (although I wonder if that is actually strictly necessary since we can collapse on demand now 🤔 ).

It seems the simulator page is wider than all the others... is it worth getting the initial preferences dialog window size to be the same as that, so it doesn't resize as you change through the pages? Also, it forces the shrink even if I resized the size manually and re-open it... claude suggested this if that helps / is of interest?

Suggested fix: measure every tab's size hint once (each is invisible at this point in the constructor, so no flicker) and size the dialog to the largest, and only do that when there's no saved geometry to restore.

--- a/companion/src/prefsedit/prefs_edit.cpp
+++ b/companion/src/prefsedit/prefs_edit.cpp
@@ -41,7 +41,7 @@ PrefsEditDialog::PrefsEditDialog(QWidget * parent, UpdateFactories * factories
  ui->setupUi(this);
   setWindowIcon(CompanionIcon("apppreferences.png"));
   setAttribute(Qt::WA_DeleteOnClose);
-  restoreGeometry(g.prefsEditGeo());
+  bool hasSavedGeo = restoreGeometry(g.prefsEditGeo());

   PrefsProfilePanel *prefsProfPanel = new PrefsProfilePanel(this, firmware, board, profile);
   PrefsPanel *profPanel = addTab(prefsProfPanel, tr("Radio Profile"));
@@ -61,7 +61,9 @@ PrefsEditDialog::PrefsEditDialog(QWidget * parent, UpdateFactories * factories
   connect(prefsProfPanel, &PrefsProfilePanel::sdPathChanged, prefsUpdatePanel, &PrefsUpdatePanel::onSDPathChanged);

   ui->tabWidget->setCurrentIndex(0);
-  shrink();
+
+  if (!hasSavedGeo)
+    shrink();
 }

 PrefsEditDialog::~PrefsEditDialog()
@@ -158,7 +160,20 @@ void PrefsEditDialog::setMainWinHasDirtyChild(bool value)

  void PrefsEditDialog::shrink()
 {
-  adjustSize();
+  // adjustSize() only accounts for the currently visible tab, since
+  // QStackedWidget doesn't lay out hidden pages. Walk every tab once so
+  // each panel reports its real size hint, and size the dialog to fit
+  // the largest one so switching tabs later doesn't resize the window.
+  QSize maxHint;
+  const int current = ui->tabWidget->currentIndex();
+
+  for (int i = 0; i < ui->tabWidget->count(); i++) {
+    ui->tabWidget->setCurrentIndex(i);
+    ui->tabWidget->currentWidget()->adjustSize();
+    maxHint = maxHint.expandedTo(ui->tabWidget->currentWidget()->sizeHint());
+  }
+
+  ui->tabWidget->setCurrentIndex(current);
+  adjustSize();
+  resize(maxHint.expandedTo(size()));
 }

@pfeerick

Copy link
Copy Markdown
Member
  1. Hitting the close button on the preferences dialog will trigger a double save prompt if you discard 🤪

  2. It seems

// delete dlg or deleteLater() triggers segfault with no debug stack trace in Linux

is due to a double-delete

prefs_edit.cpp:42 adds setAttribute(Qt::WA_DeleteOnClose) and correspondingly, mainwindow.cpp:461 lost its dialog->deleteLater() - That's a double-delete, not a mystery — done() already schedules the deletion

  1. I think we have a gremlin hiding in the undoFirmwareChange reset prompt... i.e.
  • Open a model file and make an unsaved change
  • Open Preferences → Radio Profile → change the radio to an incompatible board
  • Click OK on the Preferences dialog
  • You get the "You cannot switch Radio Type or change Build Options while there are unsaved file changes" prompt. Choose Reset.

Claude is waffling about

profile.fwName("") is no longer cleared on radio change. Old apppreferencesdialog.cpp:222 did profile.fwName("") alongside profile.fwType(newFw->getId()). Nothing in prefsedit/ does. FlashFirmwareDialog's ctor initialises fwName(g.profile[g.id()].fwName()), so after switching radio type, Flash Firmware prefills the previous radio's binary path.

New code emits resetFirmware(), then falls through to Firmware::setCurrentVariant(firmware); fwchange = true; and on to panel->save() for every panel — so Reset now saves everything and emits firmwareProfileChanged() for a change that was just undone. And the postChanged lambda at line 74-76 does profile.generalSettings(QByteArray()); profile.timeStamp(QString()); — which the old dialog never did here. That wipes the profile's radio-settings backup the moment you pick a different radio in the picker, and undoFirmwareChange() cannot restore it. The Reset option no longer fully resets.

And I'm not awake enough to even attempt to process that 🤪

I'm getting random instances where the preferences panels seem to go blank on Windows when interacting with the collapsible headers), but I've not figured out a reliable repro for that yet, or tell if it is windows specific yet.

Might have some issues here also

  • mainwindow.cpp:424 Firmware * newFw = Firmware::getFirmwareForId(g.getProfile(pid).fwType());
  • mainwindow.cpp:1301 Firmware::setCurrentVariant(Firmware::getFirmwareForId(g.currentProfile().fwType()));

Some potential cleanup

  • runPreUpdate() and applyPreUpdate() are identical; only applyPreUpdate() is called
  • resetUpdatesSettings() calls updDelDownloadsReset() twice
  • autocolorselectbtn is orphaned

@elecpower

elecpower commented Aug 20, 2026 •

Copy link
Copy Markdown
Collaborator Author
  1. not in my Ubuntu world. I actually found the dirty variable is always false when close clicked as opposed to reject which tests the value correctly. Yet the variable can be true before close clicked. So something is resetting it or the reference is to a 'random' memory address. Playing with setAttribute(Qt::WA_DeleteOnClose) and MainWindow::editPreferences dlg->deleteLater makes no difference. I did find one thread where there can be differences between linux and windows but whether that still has some degree of truth in the Qt version we are using is another story.
  2. makes sense.
  3. I've struck the blank collapsible headers but not consistent. If you switch tabs then by magic the widgets appear. So best guess its refresh event sequencing and animation.

Working through the other points...

@elecpower

elecpower commented Aug 20, 2026 •

Copy link
Copy Markdown
Collaborator Author

And the postChanged lambda at line 74-76 does profile.generalSettings(QByteArray()); profile.timeStamp(QString()); — which the old dialog never did here. That wipes the profile's radio-settings backup the moment you pick a different radio in the picker, and undoFirmwareChange() cannot restore it. The Reset option no longer fully resets

Yeah I caught that but since I'm lazy and have been working on this for some time thought 'well if you change the type then fool on you' but the inconsistency needs fixing.

@elecpower

Copy link
Copy Markdown
Collaborator Author

So I solved my dirty variable issue.

Steps to recreate:

  1. open Preferences
  2. change profile name and do not click anywhere else
  3. close window

Result dirty not updated and no prompt to Save

  1. re-open Preferences
  2. change profile name and click anywhere in the dialog
  3. close window

Result dirty was updated and prompt to Save pops up.

@elecpower

Copy link
Copy Markdown
Collaborator Author

And the postChanged lambda at line 74-76 does profile.generalSettings(QByteArray()); profile.timeStamp(QString()); — which the old dialog never did here. That wipes the profile's radio-settings backup the moment you pick a different radio in the picker, and undoFirmwareChange() cannot restore it. The Reset option no longer fully resets.

Fix defers clearing until save confirmed

@pfeerick

pfeerick commented Aug 21, 2026 •

Copy link
Copy Markdown
Member

So I solved my dirty variable issue ... do not click anywhere else

Oh, is that all you had to do... just not click anywhere... 🤣 🤭

well if you change the type then fool on you

Expecially if you do it that way... but hey... I would have also liked it to have been a big red threatening button ... that does nothing when you press it ... you were half-way there 🤣

@elecpower

Copy link
Copy Markdown
Collaborator Author

profile.fwName("") is no longer cleared on radio change. Old apppreferencesdialog.cpp:222 did profile.fwName("") alongside profile.fwType(newFw->getId()). Nothing in prefsedit/ does. FlashFirmwareDialog's ctor initialises fwName(g.profile[g.id()].fwName()), so after switching radio type, Flash Firmware prefills the previous radio's binary path.

Fixed

@elecpower

Copy link
Copy Markdown
Collaborator Author

New code emits resetFirmware(), then falls through to Firmware::setCurrentVariant(firmware); fwchange = true; and on to panel->save() for every panel — so Reset now saves everything and emits firmwareProfileChanged() for a change that was just undone.

The rework should fix this I think...

@elecpower

Copy link
Copy Markdown
Collaborator Author

Some potential cleanup
runPreUpdate() and applyPreUpdate() are identical; only applyPreUpdate() is called
resetUpdatesSettings() calls updDelDownloadsReset() twice
autocolorselectbtn is orphaned

Fixed

@elecpower

elecpower commented Aug 21, 2026 •

Copy link
Copy Markdown
Collaborator Author

Might have some issues here also
mainwindow.cpp:424 Firmware * newFw = Firmware::getFirmwareForId(g.getProfile(pid).fwType());
mainwindow.cpp:1301 Firmware::setCurrentVariant(Firmware::getFirmwareForId(g.currentProfile().fwType()));

These are ok in their context.

@pfeerick this completes my review and fixes for this round of feedback

@elecpower elecpower added the needs: rebase A git rebase on top of the latest destination branch version is required label Aug 25, 2026
@pfeerick
pfeerick self-requested a review August 30, 2026 08:58
@pfeerick
pfeerick force-pushed the elecpower/cpn-refactor-app-settings branch from e609030 to 3e97b3f Compare August 30, 2026 09:04
@pfeerick pfeerick removed the needs: rebase A git rebase on top of the latest destination branch version is required label Aug 30, 2026
@pfeerick pfeerick changed the title enh(cpn): refactor edit application preferences feat(cpn): refactor edit application preferences Aug 30, 2026
@pfeerick pfeerick changed the title feat(cpn): refactor edit application preferences feat(cpn): reorganise edit application preferences Aug 30, 2026
@pfeerick pfeerick changed the title feat(cpn): reorganise edit application preferences feat(cpn): reorganise Edit Setttings (Profile/Application/Simulator/Update) dialog  Aug 30, 2026
@pfeerick pfeerick changed the title feat(cpn): reorganise Edit Setttings (Profile/Application/Simulator/Update) dialog  feat(cpn): reorganise and refactor Edit Setttings (Profile/Application/Simulator/Update) dialog  Aug 30, 2026
@elecpower elecpower changed the title feat(cpn): reorganise and refactor Edit Setttings (Profile/Application/Simulator/Update) dialog  feat(cpn): reorganise and refactor Edit Settings (Profile/Application/Simulator/Update) dialog  Aug 30, 2026
@pfeerick

Copy link
Copy Markdown
Member

A quick walkthrough last night on Linux, macOS and Windows seemed ok.

Some followup perhaps - it seem the firmware language doesn't default to anything - not even the currently selected language? And I do wonder now that there are scrollbars if we really need the groups to collapse by default.

@pfeerick
pfeerick merged commit 6ebd3b9 into main Sep 13, 2026
14 checks passed
@pfeerick
pfeerick deleted the elecpower/cpn-refactor-app-settings branch September 13, 2026 21:38
@pfeerick pfeerick added this to the 3.0 milestone Sep 13, 2026
@elecpower

Copy link
Copy Markdown
Collaborator Author

Some followup perhaps - it seem the firmware language doesn't default to anything - not even the currently selected language? And I do wonder now that there are scrollbars if we really need the groups to collapse by default.

I'll look into the language bug.
In so far as collapsed on start or expanded to some degree it depends on the number of items/rows per tab and what groups you go to most often and the saved dialog size, etc. One of those personal preference ones.

I could have an option for the startup state either collapsed or expanded. Then next question becomes can we save the last state for every group or select start state for each group, etc.

I'd be happy with one start option collapsed or expanded otherwise as more groups are added the maintenance goes up.

@pfeerick

Copy link
Copy Markdown
Member

I'd be happy with one start option collapsed or expanded otherwise as more groups are added the maintenance goes up.

That is all I would want also... I prefer expanded here vs the radio as we have bigger screens, better inputs, etc, so can easily scroll around... so the present model means need to keep opening up the groups if I'm after an option, which also opens up the "now where was that option put" can of worms... so, when will there be a search box in the preferences screen (just kidding!) 🤪 )?

@elecpower

elecpower commented Sep 14, 2026 •

Copy link
Copy Markdown
Collaborator Author

So the language bug is dev specific one in that the conversion checks for settings version <= 3.0 and setting revision so your settings have not been converted and thus the old firmware settings have not been split out into the new fields and your settings language is blank.

I'll bump the settings revision and tweak the conversion check plus populate if blank after conversion with the OS default in a PR.

@elecpower

Copy link
Copy Markdown
Collaborator Author

@pfeerick I've added option to expand all sections on preferences dialog open and improve the default language in #7791

pfeerick added a commit that referenced this pull request Sep 28, 2026
Changing the radio in the Radio Profile settings left Default Internal
Module and External Module Size at the previous radio's values. The
internal module list was also built for the active profile's radio
rather than the one being edited. A new profile starts from the default
variant (TX16S), so switching it to e.g. NV14 or EL18 kept Multi
instead of AFHDS2A / AFHDS3. The pre-#7623 preferences dialog reset
these on a radio change.

When the radio changes, rebuild the internal module list for the new
radio and set both fields to its defaults, or back to the saved profile
values when changing back to the profile's radio (e.g. on Reset).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
elecpower pushed a commit that referenced this pull request Sep 29, 2026
Changing the radio in the Radio Profile settings left Default Internal
Module and External Module Size at the previous radio's values. The
internal module list was also built for the active profile's radio
rather than the one being edited. A new profile starts from the default
variant (TX16S), so switching it to e.g. NV14 or EL18 kept Multi
instead of AFHDS2A / AFHDS3. The pre-#7623 preferences dialog reset
these on a radio change.

When the radio changes, rebuild the internal module list for the new
radio and set both fields to its defaults, or back to the saved profile
values when changing back to the profile's radio (e.g. on Reset).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pfeerick added a commit that referenced this pull request Oct 7, 2026
Since #7623 the radio profile stores its build options in fwOptions,
separately from fwType, but the current firmware variant was still
looked up from fwType alone. Every capability that depends on a build
option (nogvars, haptic, internalelrs/crsf/multi, afhds2a/3,
flyskygimbals, danger, ...) was therefore always off.

Look the variant up from fwType plus the selected options, and refresh
it when only the options are changed in the profile settings, not just
when the radio type changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pfeerick added a commit that referenced this pull request Oct 7, 2026
Since #7623 the radio profile stores its build options in fwOptions,
separately from fwType, but the current firmware variant was still
looked up from fwType alone. Every capability that depends on a build
option (nogvars, haptic, internalelrs/crsf/multi, afhds2a/3,
flyskygimbals, danger, ...) was therefore always off.

Look the variant up from fwType plus the selected options, and refresh
it when only the options are changed in the profile settings, not just
when the radio type changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pfeerick added a commit that referenced this pull request Oct 21, 2026
Since #7623 the radio profile stores its build options in fwOptions,
separately from fwType, but the current firmware variant was still
looked up from fwType alone. Every capability that depends on a build
option (nogvars, haptic, internalelrs/crsf/multi, afhds2a/3,
flyskygimbals, danger, ...) was therefore always off.

Look the variant up from fwType plus the selected options, and refresh
it when only the options are changed in the profile settings, not just
when the radio type changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

companion (cpn) Related to the companion software enhancement ✨ New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants