From 9eef8f95c92389d40b0aecf3fc8a826b39594340 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Tue, 6 Oct 2026 09:14:35 +1300 Subject: [PATCH] Add a Toolbars settings page: icon size, text with icons, lock A new page in Configurer QElectroTech, "Barres d'outils", sets for every window (folio editor, element editor, title block editor): - the icon size: default, 16, 24, 32 or 48 px - the buttons: icon only, text beside the icon, or text under it - locking the toolbars so they can't be dragged out of place OK applies it to the open windows at once. The defaults are Qt's own, and a default value removes its key, so nothing changes for anyone who never opens the page. A toolbar inside a panel is left alone; only the window's own toolbars are locked. New test tst_toolbarsettings: defaults change nothing, saved values reach every toolbar of an open window, back to defaults removes the keys. Third stage of discussion #1319. Co-Authored-By: Claude Opus 5.5 --- cmake/qet_compilation_vars.cmake | 4 + sources/editor/ui/qetelementeditor.cpp | 2 + sources/qetapp.cpp | 2 + sources/qetdiagrameditor.cpp | 2 + sources/titleblock/qettemplateeditor.cpp | 2 + sources/toolbarsettings.cpp | 102 ++++++++++++++ sources/toolbarsettings.h | 45 +++++++ sources/ui/configpage/toolbarsconfigpage.cpp | 88 ++++++++++++ sources/ui/configpage/toolbarsconfigpage.h | 49 +++++++ tests/qttest/CMakeLists.txt | 18 +++ tests/qttest/tst_toolbarsettings.cpp | 133 +++++++++++++++++++ 11 files changed, 447 insertions(+) create mode 100644 sources/toolbarsettings.cpp create mode 100644 sources/toolbarsettings.h create mode 100644 sources/ui/configpage/toolbarsconfigpage.cpp create mode 100644 sources/ui/configpage/toolbarsconfigpage.h create mode 100644 tests/qttest/tst_toolbarsettings.cpp diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index 3e7b6f573..859e343c3 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -301,6 +301,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/shortcutmanager.h ${QET_DIR}/sources/shortcutbarsettings.cpp ${QET_DIR}/sources/shortcutbarsettings.h + ${QET_DIR}/sources/toolbarsettings.cpp + ${QET_DIR}/sources/toolbarsettings.h ${QET_DIR}/sources/diagramcontexttoolbar.cpp ${QET_DIR}/sources/diagramcontexttoolbar.h ${QET_DIR}/sources/diagramgestureoverlay.cpp @@ -854,6 +856,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.h ${QET_DIR}/sources/ui/configpage/shortcutbarconfigpage.cpp ${QET_DIR}/sources/ui/configpage/shortcutbarconfigpage.h + ${QET_DIR}/sources/ui/configpage/toolbarsconfigpage.cpp + ${QET_DIR}/sources/ui/configpage/toolbarsconfigpage.h ${QET_DIR}/sources/undocommand/addelementtextcommand.cpp ${QET_DIR}/sources/undocommand/addelementtextcommand.h diff --git a/sources/editor/ui/qetelementeditor.cpp b/sources/editor/ui/qetelementeditor.cpp index 7469a2a26..af39d81bb 100644 --- a/sources/editor/ui/qetelementeditor.cpp +++ b/sources/editor/ui/qetelementeditor.cpp @@ -54,6 +54,7 @@ #include "../../dxf/dxftoelmt.h" #include "../../qet_elementscaler/qet_elementscaler.h" #include "../UndoCommand/openelmtcommand.h" +#include "../../toolbarsettings.h" #include #include @@ -89,6 +90,7 @@ QETElementEditor::QETElementEditor(QWidget *parent) : readSettings(); // restoreGeometry before show() show(); readSettingsState(); // restoreState() must be called after show() in Qt6 + ToolbarSettings::applyTo(this); } /** diff --git a/sources/qetapp.cpp b/sources/qetapp.cpp index 67ba6ab23..ccd4981c7 100644 --- a/sources/qetapp.cpp +++ b/sources/qetapp.cpp @@ -42,6 +42,7 @@ #include "ui/configpage/generalconfigurationpage.h" #include "ui/configpage/shortcutsconfigpage.h" #include "ui/configpage/shortcutbarconfigpage.h" +#include "ui/configpage/toolbarsconfigpage.h" #include "machine_info.h" #include "TerminalStrip/ui/terminalstripeditorwindow.h" #include "qetversion.h" @@ -2225,6 +2226,7 @@ void QETApp::configureQET() cd.addPage(new PrintConfigPage()); cd.addPage(new ShortcutsConfigPage()); cd.addPage(new ShortcutBarConfigPage()); + cd.addPage(new ToolbarsConfigPage()); #ifdef QET_SPACEMOUSE_SUPPORT cd.addPage(new SpaceMouseConfigPage()); #endif diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index c7faddfa6..49150857f 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -85,6 +85,7 @@ #include "wiringlistexport.h" #include "ui/wiringlistdialog.h" #include "ui/terminalnumberingdialog.h" +#include "toolbarsettings.h" #include #include #include @@ -186,6 +187,7 @@ QETDiagramEditor::QETDiagramEditor(const QStringList &files, QWidget *parent) : readSettings(); // restoreGeometry before show() show(); readSettingsState(); // restoreState() must be called after show() in Qt6 + ToolbarSettings::applyTo(this); #ifdef QET_HAS_SCRIPTING setUpLiveIndicator(); setUpMacroRecorder(); diff --git a/sources/titleblock/qettemplateeditor.cpp b/sources/titleblock/qettemplateeditor.cpp index e3a44ecbb..04e57827b 100644 --- a/sources/titleblock/qettemplateeditor.cpp +++ b/sources/titleblock/qettemplateeditor.cpp @@ -27,6 +27,7 @@ #include "templatelocationsaver.h" #include "templatelogomanager.h" #include "templateview.h" +#include "../toolbarsettings.h" /** @param parent parent QWidget of this window @@ -46,6 +47,7 @@ QETTitleBlockTemplateEditor::QETTitleBlockTemplateEditor(QWidget *parent) : initActions(); initMenus(); initToolbars(); + ToolbarSettings::applyTo(this); readSettings(); } diff --git a/sources/toolbarsettings.cpp b/sources/toolbarsettings.cpp new file mode 100644 index 000000000..04d59ee11 --- /dev/null +++ b/sources/toolbarsettings.cpp @@ -0,0 +1,102 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "toolbarsettings.h" + +#include +#include +#include +#include + +namespace { + const QString ICON_SIZE = QStringLiteral("toolbars/icon_size"); + const QString BUTTON_STYLE = QStringLiteral("toolbars/button_style"); + const QString LOCKED = QStringLiteral("toolbars/locked"); +} + +int ToolbarSettings::iconSize() +{ + return QSettings().value(ICON_SIZE, 0).toInt(); +} + +Qt::ToolButtonStyle ToolbarSettings::buttonStyle() +{ + const int style = QSettings().value(BUTTON_STYLE, int(Qt::ToolButtonIconOnly)).toInt(); + switch (style) { + case Qt::ToolButtonTextBesideIcon: + case Qt::ToolButtonTextUnderIcon: + return Qt::ToolButtonStyle(style); + default: + return Qt::ToolButtonIconOnly; + } +} + +bool ToolbarSettings::locked() +{ + return QSettings().value(LOCKED, false).toBool(); +} + +/** + @brief ToolbarSettings::save + Store the three values; a default value removes its key. +*/ +void ToolbarSettings::save(int icon_size, Qt::ToolButtonStyle style, bool locked) +{ + QSettings settings; + if (icon_size > 0) settings.setValue(ICON_SIZE, icon_size); + else settings.remove(ICON_SIZE); + if (style != Qt::ToolButtonIconOnly) settings.setValue(BUTTON_STYLE, int(style)); + else settings.remove(BUTTON_STYLE); + if (locked) settings.setValue(LOCKED, true); + else settings.remove(LOCKED); +} + +/** + @brief ToolbarSettings::applyTo + Apply the settings to \a window and its toolbars. The window passes + its icon size and button style on to every toolbar that has none of + its own; an invalid size gives the style's default back. +*/ +void ToolbarSettings::applyTo(QMainWindow *window) +{ + if (!window) { + return; + } + const int size = iconSize(); + window->setIconSize(size > 0 ? QSize(size, size) : QSize()); + window->setToolButtonStyle(buttonStyle()); + const bool movable = !locked(); + for (QToolBar *toolbar : window->findChildren()) { + //Not a toolbar inside a panel: only the window's own + if (window->toolBarArea(toolbar) != Qt::NoToolBarArea) { + toolbar->setMovable(movable); + } + } +} + +/** + @brief ToolbarSettings::applyToAll + Apply the settings to every open window, after they change. +*/ +void ToolbarSettings::applyToAll() +{ + for (QWidget *widget : QApplication::topLevelWidgets()) { + if (auto *window = qobject_cast(widget)) { + applyTo(window); + } + } +} diff --git a/sources/toolbarsettings.h b/sources/toolbarsettings.h new file mode 100644 index 000000000..7e7dd1e1a --- /dev/null +++ b/sources/toolbarsettings.h @@ -0,0 +1,45 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef TOOLBARSETTINGS_H +#define TOOLBARSETTINGS_H + +#include +#include + +class QMainWindow; + +/** + @brief How every window's toolbars look: icon size, text with the + icons, and whether they can be moved. Stored in QSettings; a value + the user never changed keeps Qt's default, so nothing changes for + anyone who does not set one. +*/ +class ToolbarSettings +{ + public: + /// Icon size in pixels, or 0 for the style's default size + static int iconSize(); + static Qt::ToolButtonStyle buttonStyle(); + static bool locked(); + static void save(int icon_size, Qt::ToolButtonStyle style, bool locked); + + static void applyTo(QMainWindow *window); + static void applyToAll(); +}; + +#endif // TOOLBARSETTINGS_H diff --git a/sources/ui/configpage/toolbarsconfigpage.cpp b/sources/ui/configpage/toolbarsconfigpage.cpp new file mode 100644 index 000000000..bd1abd000 --- /dev/null +++ b/sources/ui/configpage/toolbarsconfigpage.cpp @@ -0,0 +1,88 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "toolbarsconfigpage.h" + +#include "../../qeticons.h" +#include "../../toolbarsettings.h" + +#include +#include +#include +#include +#include +#include + +ToolbarsConfigPage::ToolbarsConfigPage(QWidget *parent) : + ConfigPage(parent) +{ + auto *vlayout = new QVBoxLayout(this); + vlayout->addWidget(new QLabel(title(), this)); + auto *line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + vlayout->addWidget(line); + + m_icon_size = new QComboBox(this); + m_icon_size->setObjectName(QStringLiteral("iconSizeCombo")); + m_icon_size->addItem(tr("Par défaut"), 0); + m_icon_size->addItem(tr("Petites (16 px)"), 16); + m_icon_size->addItem(tr("Moyennes (24 px)"), 24); + m_icon_size->addItem(tr("Grandes (32 px)"), 32); + m_icon_size->addItem(tr("Très grandes (48 px)"), 48); + const int size_index = m_icon_size->findData(ToolbarSettings::iconSize()); + m_icon_size->setCurrentIndex(size_index < 0 ? 0 : size_index); + + m_button_style = new QComboBox(this); + m_button_style->setObjectName(QStringLiteral("buttonStyleCombo")); + m_button_style->addItem(tr("Icône seule"), int(Qt::ToolButtonIconOnly)); + m_button_style->addItem(tr("Texte à côté de l'icône"), int(Qt::ToolButtonTextBesideIcon)); + m_button_style->addItem(tr("Texte sous l'icône"), int(Qt::ToolButtonTextUnderIcon)); + m_button_style->setCurrentIndex(m_button_style->findData(int(ToolbarSettings::buttonStyle()))); + + m_locked = new QCheckBox(tr("Verrouiller les barres d'outils (elles ne peuvent plus être déplacées)"), this); + m_locked->setObjectName(QStringLiteral("lockedCheck")); + m_locked->setChecked(ToolbarSettings::locked()); + + auto *form = new QFormLayout(); + form->addRow(tr("Taille des icônes :"), m_icon_size); + form->addRow(tr("Boutons :"), m_button_style); + form->addRow(m_locked); + vlayout->addLayout(form); + + auto *hint = new QLabel(tr("Pour afficher ou masquer une barre d'outils, faites un clic droit sur une barre d'outils."), this); + hint->setWordWrap(true); + vlayout->addWidget(hint); + vlayout->addStretch(); +} + +void ToolbarsConfigPage::applyConf() +{ + ToolbarSettings::save(m_icon_size->currentData().toInt(), + Qt::ToolButtonStyle(m_button_style->currentData().toInt()), + m_locked->isChecked()); + ToolbarSettings::applyToAll(); +} + +QString ToolbarsConfigPage::title() const +{ + return tr("Barres d'outils", "configuration page title"); +} + +QIcon ToolbarsConfigPage::icon() const +{ + return QET::Icons::ConfigureToolbars; +} diff --git a/sources/ui/configpage/toolbarsconfigpage.h b/sources/ui/configpage/toolbarsconfigpage.h new file mode 100644 index 000000000..175c58369 --- /dev/null +++ b/sources/ui/configpage/toolbarsconfigpage.h @@ -0,0 +1,49 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef TOOLBARSCONFIGPAGE_H +#define TOOLBARSCONFIGPAGE_H + +#include "configpage.h" + +class QCheckBox; +class QComboBox; + +/** + @brief The ToolbarsConfigPage class + Toolbar icon size, text with the icons, and locking the toolbars in + place, for every window. Saved and applied to the open windows by + applyConf(). +*/ +class ToolbarsConfigPage : public ConfigPage +{ + Q_OBJECT + + public: + explicit ToolbarsConfigPage(QWidget *parent = nullptr); + + void applyConf() override; + QString title() const override; + QIcon icon() const override; + + private: + QComboBox *m_icon_size; + QComboBox *m_button_style; + QCheckBox *m_locked; +}; + +#endif // TOOLBARSCONFIGPAGE_H diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 3b7e8c2b5..9e2ae2e61 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -227,6 +227,24 @@ set_tests_properties(tst_commandsearch PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=o target_include_directories(tst_commandsearch PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_commandsearch PRIVATE Qt::Test Qt::Widgets) +# ToolbarSettings and its configuration page: defaults change nothing, +# saved values reach every open window's toolbars, panels are left alone. +add_executable( + tst_toolbarsettings + tst_toolbarsettings.cpp + ${QET_DIR}/sources/toolbarsettings.cpp + ${QET_DIR}/sources/toolbarsettings.h + ${QET_DIR}/sources/ui/configpage/toolbarsconfigpage.cpp + ${QET_DIR}/sources/ui/configpage/toolbarsconfigpage.h + ${QET_DIR}/sources/ui/configpage/configpage.h + ${QET_DIR}/sources/qeticons.cpp + ${QET_DIR}/sources/qetpalette.cpp + ${QET_DIR}/sources/qetstyle.cpp) +add_test(NAME tst_toolbarsettings COMMAND tst_toolbarsettings) +set_tests_properties(tst_toolbarsettings PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +target_include_directories(tst_toolbarsettings PRIVATE ${QET_DIR}/sources) +target_link_libraries(tst_toolbarsettings PRIVATE Qt::Test Qt::Widgets Qt::Svg) + # scriptheader.h is header-only: the // ==QETScript== block that turns a # stored script into a button; and ShortcutManager::unregisterAction(), which # takes a deleted script's command out of the lists again. diff --git a/tests/qttest/tst_toolbarsettings.cpp b/tests/qttest/tst_toolbarsettings.cpp new file mode 100644 index 000000000..1b77c01a3 --- /dev/null +++ b/tests/qttest/tst_toolbarsettings.cpp @@ -0,0 +1,133 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "toolbarsettings.h" +#include "ui/configpage/toolbarsconfigpage.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + ToolbarSettings and its configuration page: defaults change nothing, + saved values reach every open window's toolbars, and a toolbar inside + a panel is left alone. +*/ +class tst_toolbarsettings : public QObject +{ + Q_OBJECT + + QMainWindow *m_window = nullptr; + QToolBar *m_top = nullptr; + QToolBar *m_left = nullptr; + QToolBar *m_in_dock = nullptr; + QSize m_default_size; + +private slots: + void initTestCase() + { + QStandardPaths::setTestModeEnabled(true); + QSettings().remove(QStringLiteral("toolbars")); + + m_window = new QMainWindow(); + m_default_size = m_window->iconSize(); + m_top = new QToolBar(QStringLiteral("top"), m_window); + m_window->addToolBar(Qt::TopToolBarArea, m_top); + //Created without a parent, as some QET toolbars are + m_left = new QToolBar(QStringLiteral("left")); + m_window->addToolBar(Qt::LeftToolBarArea, m_left); + auto *dock = new QDockWidget(m_window); + m_in_dock = new QToolBar(dock); + dock->setWidget(m_in_dock); + m_window->addDockWidget(Qt::LeftDockWidgetArea, dock); + m_window->show(); + } + + void cleanupTestCase() + { + delete m_window; + } + + void defaultsChangeNothing() + { + QCOMPARE(ToolbarSettings::iconSize(), 0); + QCOMPARE(ToolbarSettings::buttonStyle(), Qt::ToolButtonIconOnly); + QCOMPARE(ToolbarSettings::locked(), false); + ToolbarSettings::applyTo(m_window); + QCOMPARE(m_window->iconSize(), m_default_size); + QCOMPARE(m_top->iconSize(), m_default_size); + QCOMPARE(m_top->toolButtonStyle(), Qt::ToolButtonIconOnly); + QVERIFY(m_top->isMovable()); + QVERIFY(m_left->isMovable()); + } + + void pageSavesAndAppliesToOpenWindows() + { + { + ToolbarsConfigPage page; + auto *size = page.findChild(QStringLiteral("iconSizeCombo")); + auto *style = page.findChild(QStringLiteral("buttonStyleCombo")); + size->setCurrentIndex(size->findData(32)); + style->setCurrentIndex(style->findData(int(Qt::ToolButtonTextUnderIcon))); + page.findChild(QStringLiteral("lockedCheck"))->setChecked(true); + page.applyConf(); + } + QCOMPARE(ToolbarSettings::iconSize(), 32); + QCOMPARE(ToolbarSettings::buttonStyle(), Qt::ToolButtonTextUnderIcon); + QVERIFY(ToolbarSettings::locked()); + + QCOMPARE(m_top->iconSize(), QSize(32, 32)); + QCOMPARE(m_left->iconSize(), QSize(32, 32)); + QCOMPARE(m_top->toolButtonStyle(), Qt::ToolButtonTextUnderIcon); + QVERIFY(!m_top->isMovable()); + QVERIFY(!m_left->isMovable()); + //A toolbar inside a panel is not one of the window's toolbars + QVERIFY(m_in_dock->isMovable()); + + //A page opened now shows what was saved + ToolbarsConfigPage page; + QCOMPARE(page.findChild(QStringLiteral("iconSizeCombo"))->currentData().toInt(), 32); + QVERIFY(page.findChild(QStringLiteral("lockedCheck"))->isChecked()); + } + + void backToDefaultsRemovesTheKeys() + { + ToolbarSettings::save(0, Qt::ToolButtonIconOnly, false); + QVERIFY(!QSettings().contains(QStringLiteral("toolbars/icon_size"))); + QVERIFY(!QSettings().contains(QStringLiteral("toolbars/button_style"))); + QVERIFY(!QSettings().contains(QStringLiteral("toolbars/locked"))); + ToolbarSettings::applyToAll(); + QCOMPARE(m_top->iconSize(), m_default_size); + QCOMPARE(m_top->toolButtonStyle(), Qt::ToolButtonIconOnly); + QVERIFY(m_top->isMovable()); + } + + void unknownStyleFallsBack() + { + QSettings().setValue(QStringLiteral("toolbars/button_style"), 99); + QCOMPARE(ToolbarSettings::buttonStyle(), Qt::ToolButtonIconOnly); + QSettings().remove(QStringLiteral("toolbars/button_style")); + } +}; + +QTEST_MAIN(tst_toolbarsettings) +#include "tst_toolbarsettings.moc"