From 0fef6b19cea3be48e4a4a353b87166fe39855817 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Wed, 7 Oct 2026 19:31:42 +1300 Subject: [PATCH] Add a Customise window: toolbars, shortcut bar, keyboard, gestures Configuration > Personnaliser... (also at the bottom of a toolbar's right-click menu) opens one window with a tab for each page that sets how the user works: Barres d'outils, Contenu des barres, Barre de raccourcis, Raccourcis and Gestes de la souris, like SolidWorks' Tools > Customize. The tabs are the same pages the configuration dialog shows, which keeps them. OK applies every tab, Cancel none. Co-Authored-By: Claude Opus 5.5 --- cmake/qet_compilation_vars.cmake | 2 + sources/qetapp.cpp | 32 ++++++++++ sources/qetapp.h | 1 + sources/qetmainwindow.cpp | 22 +++++++ sources/qetmainwindow.h | 2 + sources/ui/customizedialog.cpp | 77 +++++++++++++++++++++++ sources/ui/customizedialog.h | 51 +++++++++++++++ tests/qttest/CMakeLists.txt | 13 ++++ tests/qttest/tst_customizedialog.cpp | 93 ++++++++++++++++++++++++++++ 9 files changed, 293 insertions(+) create mode 100644 sources/ui/customizedialog.cpp create mode 100644 sources/ui/customizedialog.h create mode 100644 tests/qttest/tst_customizedialog.cpp diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index c259b6d46..6f5cbd9b6 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -862,6 +862,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/customizedialog.cpp + ${QET_DIR}/sources/ui/customizedialog.h ${QET_DIR}/sources/ui/configpage/toolbarcommandsconfigpage.cpp ${QET_DIR}/sources/ui/configpage/toolbarcommandsconfigpage.h ${QET_DIR}/sources/ui/configpage/toolbarsconfigpage.cpp diff --git a/sources/qetapp.cpp b/sources/qetapp.cpp index 6d7f03356..62390bab9 100644 --- a/sources/qetapp.cpp +++ b/sources/qetapp.cpp @@ -45,6 +45,8 @@ #include "ui/configpage/shortcutbarconfigpage.h" #include "ui/configpage/toolbarsconfigpage.h" #include "ui/configpage/toolbarcommandsconfigpage.h" +#include "ui/customizedialog.h" +#include #include "machine_info.h" #include "TerminalStrip/ui/terminalstripeditorwindow.h" #include "qetversion.h" @@ -2258,6 +2260,36 @@ void QETApp::configureQET() #endif } +/** + @brief QETApp::customizeQET + Open the Customise window: the toolbar, shortcut bar, keyboard and + gesture pages of the configuration dialog, as tabs of one window. + @param tab : the tab to show first +*/ +void QETApp::customizeQET(int tab) +{ + QWidget *parent_widget = qApp->activeWindow(); + + CustomizeDialog dialog; + //Same reason as the configuration dialog (#527) + dialog.setWindowModality(Qt::ApplicationModal); + dialog.addPage(new ToolbarsConfigPage()); + dialog.addPage(new ToolbarCommandsConfigPage()); + dialog.addPage(new ShortcutBarConfigPage()); + dialog.addPage(new ShortcutsConfigPage()); + dialog.addPage(new GesturesConfigPage()); + if (auto *tabs = dialog.findChild(QStringLiteral("customizeTabs"))) { + tabs->setCurrentIndex(tab); + } + QET::trackDialogGeometry(&dialog); + + if (parent_widget) { + dialog.setParent(parent_widget, dialog.windowFlags()); + } + dialog.exec(); + dialog.setParent(nullptr, dialog.windowFlags()); +} + /** @brief QETApp::exportConfiguration Save the settings of QElectroTech to a file the user chooses, to keep diff --git a/sources/qetapp.h b/sources/qetapp.h index cd58aa671..234db5fa5 100644 --- a/sources/qetapp.h +++ b/sources/qetapp.h @@ -294,6 +294,7 @@ class QETApp : public QObject void openTitleBlockTemplate(const QString &); void openTitleBlockTemplateFiles(const QStringList &); void configureQET(); + void customizeQET(int tab = 0); /// Save the settings to a file (discussion #610) void exportConfiguration(); /// Replace the settings with a saved file, then close QElectroTech diff --git a/sources/qetmainwindow.cpp b/sources/qetmainwindow.cpp index 3ee58586a..0ff5def85 100644 --- a/sources/qetmainwindow.cpp +++ b/sources/qetmainwindow.cpp @@ -93,6 +93,11 @@ void QETMainWindow::initCommonActions() } }); + customize_action_ = new QAction(QET::Icons::ConfigureToolbars, tr("&Personnaliser..."), this); + ShortcutManager::instance().registerAction(customize_action_, "mainwindow.customize", tr("Général"), QKeySequence()); + customize_action_ -> setStatusTip(tr("Barres d'outils, barre de raccourcis, clavier et gestes de la souris, dans une seule fenêtre", "status bar tip")); + connect(customize_action_, &QAction::triggered, qet_app, [qet_app]() { qet_app->customizeQET(); }); + export_config_action_ = new QAction(QET::Icons::DocumentExport, tr("Enregistrer la configuration sous..."), this); export_config_action_ -> setStatusTip(tr("Enregistre les réglages de QElectroTech dans un fichier", "status bar tip")); connect(export_config_action_, &QAction::triggered, qet_app, &QETApp::exportConfiguration); @@ -190,6 +195,7 @@ void QETMainWindow::initCommonMenus() settings_menu_ = new QMenu(tr("&Configuration", "window menu"), this); settings_menu_ -> addAction(fullscreen_action_); settings_menu_ -> addAction(configure_action_); + settings_menu_ -> addAction(customize_action_); settings_menu_ -> addSeparator(); settings_menu_ -> addAction(export_config_action_); settings_menu_ -> addAction(import_config_action_); @@ -274,6 +280,22 @@ void QETMainWindow::updateFullScreenAction() ShortcutManager::instance().registerAction(fullscreen_action_, "mainwindow.fullscreen", tr("Général"), Qt::CTRL | Qt::SHIFT | Qt::Key_F); } +/** + @brief QETMainWindow::createPopupMenu + The menu shown on a right-click on a toolbar or a dock title, and the + Configuration > Afficher submenu: Qt's list of toolbars and docks, + then Personnaliser..., as in most applications with toolbars. +*/ +QMenu *QETMainWindow::createPopupMenu() +{ + QMenu *menu = QMainWindow::createPopupMenu(); + if (menu) { + menu -> addSeparator(); + menu -> addAction(customize_action_); + } + return menu; +} + /** Check whether a sub menu dedicated to docks and toolbars can be inserted on top of the settings menu. diff --git a/sources/qetmainwindow.h b/sources/qetmainwindow.h index fe76fb7e0..38aac40e3 100644 --- a/sources/qetmainwindow.h +++ b/sources/qetmainwindow.h @@ -43,6 +43,7 @@ class QETMainWindow : public QMainWindow { protected: void activateMenuBar(); bool event(QEvent *) override; + QMenu *createPopupMenu() override; void dragEnterEvent(QDragEnterEvent *e) override; void dropEvent(QDropEvent *e) override; virtual void firstActivation(QEvent *); @@ -56,6 +57,7 @@ class QETMainWindow : public QMainWindow { // attributes protected: QAction *configure_action_; ///< Launch the QElectroTech configuration dialog + QAction *customize_action_; ///< Launch the Customise window (toolbars, keys, gestures) QAction *export_config_action_; ///< Save the settings to a file QAction *import_config_action_; ///< Replace the settings with a saved file QAction *fullscreen_action_; ///< Toggle full screen diff --git a/sources/ui/customizedialog.cpp b/sources/ui/customizedialog.cpp new file mode 100644 index 000000000..03a0bca97 --- /dev/null +++ b/sources/ui/customizedialog.cpp @@ -0,0 +1,77 @@ +/* + 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 "customizedialog.h" + +#include "configpage/configpage.h" + +#include +#include +#include +#include + +CustomizeDialog::CustomizeDialog(QWidget *parent) : + QDialog(parent) +{ + setWindowTitle(tr("Personnaliser", "window title")); + + m_tabs = new QTabWidget(this); + m_tabs->setObjectName(QStringLiteral("customizeTabs")); + m_tabs->setDocumentMode(true); + + auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(buttons, &QDialogButtonBox::accepted, this, [this]() { + applyConf(); + accept(); + }); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + + auto *layout = new QVBoxLayout(this); + layout->addWidget(m_tabs); + layout->addWidget(buttons); + + resize(1000, 700); +} + +/** + @brief CustomizeDialog::addPage + Add @a page as a tab, named after its title. The dialog takes it. +*/ +void CustomizeDialog::addPage(ConfigPage *page) +{ + if (!page || m_pages.contains(page)) { + return; + } + m_pages << page; + m_tabs->addTab(page, page->icon(), page->title()); +} + +QList CustomizeDialog::pages() const +{ + return m_pages; +} + +/** + @brief CustomizeDialog::applyConf + Apply every page, in tab order. +*/ +void CustomizeDialog::applyConf() +{ + for (ConfigPage *page : std::as_const(m_pages)) { + page->applyConf(); + } +} diff --git a/sources/ui/customizedialog.h b/sources/ui/customizedialog.h new file mode 100644 index 000000000..a764c1530 --- /dev/null +++ b/sources/ui/customizedialog.h @@ -0,0 +1,51 @@ +/* + 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 CUSTOMIZEDIALOG_H +#define CUSTOMIZEDIALOG_H + +#include +#include + +class ConfigPage; +class QTabWidget; + +/** + @brief The CustomizeDialog class + Everything about how the user works in one window, one tab per + configuration page: toolbars, what they hold, the shortcut bar, the + keyboard and the mouse gestures, like SolidWorks' Tools > Customize. + The pages are the ones of the configuration dialog, which still shows + them too. OK applies every page, Cancel none. +*/ +class CustomizeDialog : public QDialog +{ + Q_OBJECT + + public: + explicit CustomizeDialog(QWidget *parent = nullptr); + + void addPage(ConfigPage *page); + QList pages() const; + void applyConf(); + + private: + QTabWidget *m_tabs; + QList m_pages; +}; + +#endif // CUSTOMIZEDIALOG_H diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 2f2e6b048..502887209 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -341,6 +341,19 @@ set_tests_properties(tst_gesturesettings PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM target_include_directories(tst_gesturesettings PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_gesturesettings PRIVATE Qt::Test Qt::Widgets Qt::Svg Qt::Xml pugixml::pugixml) +# CustomizeDialog: the configuration pages as tabs of one window; OK +# applies every page once, Cancel none. +add_executable( + tst_customizedialog + tst_customizedialog.cpp + ${QET_DIR}/sources/ui/customizedialog.cpp + ${QET_DIR}/sources/ui/customizedialog.h + ${QET_DIR}/sources/ui/configpage/configpage.h) +add_test(NAME tst_customizedialog COMMAND tst_customizedialog) +set_tests_properties(tst_customizedialog PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +target_include_directories(tst_customizedialog PRIVATE ${QET_DIR}/sources) +target_link_libraries(tst_customizedialog PRIVATE Qt::Test Qt::Widgets) + # 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_customizedialog.cpp b/tests/qttest/tst_customizedialog.cpp new file mode 100644 index 000000000..7187bc282 --- /dev/null +++ b/tests/qttest/tst_customizedialog.cpp @@ -0,0 +1,93 @@ +/* + 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 "ui/customizedialog.h" +#include "ui/configpage/configpage.h" + +#include +#include +#include +#include +#include + +/// A configuration page that counts how often it was applied +class CountingPage : public ConfigPage +{ + public: + CountingPage(const QString &title) : ConfigPage(nullptr), m_title(title) {} + void applyConf() override { ++applied; } + QString title() const override { return m_title; } + QIcon icon() const override { return QIcon(); } + int applied = 0; + private: + QString m_title; +}; + +/** + CustomizeDialog: one tab per page in the order added, OK applies every + page once, Cancel applies none. +*/ +class tst_customizedialog : public QObject +{ + Q_OBJECT + +private slots: + void tabsInOrder() + { + CustomizeDialog dialog; + auto *a = new CountingPage(QStringLiteral("Barres d'outils")); + auto *b = new CountingPage(QStringLiteral("Clavier")); + dialog.addPage(a); + dialog.addPage(b); + dialog.addPage(a); // twice: ignored + dialog.addPage(nullptr); + auto *tabs = dialog.findChild(QStringLiteral("customizeTabs")); + QVERIFY(tabs); + QCOMPARE(tabs->count(), 2); + QCOMPARE(tabs->tabText(0), QStringLiteral("Barres d'outils")); + QCOMPARE(tabs->tabText(1), QStringLiteral("Clavier")); + QCOMPARE(tabs->widget(1), b); + } + + void okAppliesEveryPage() + { + CustomizeDialog dialog; + auto *a = new CountingPage(QStringLiteral("a")); + auto *b = new CountingPage(QStringLiteral("b")); + dialog.addPage(a); + dialog.addPage(b); + dialog.show(); + dialog.findChild()->button(QDialogButtonBox::Ok)->click(); + QCOMPARE(a->applied, 1); + QCOMPARE(b->applied, 1); + QCOMPARE(dialog.result(), int(QDialog::Accepted)); + } + + void cancelAppliesNothing() + { + CustomizeDialog dialog; + auto *a = new CountingPage(QStringLiteral("a")); + dialog.addPage(a); + dialog.show(); + dialog.findChild()->button(QDialogButtonBox::Cancel)->click(); + QCOMPARE(a->applied, 0); + QCOMPARE(dialog.result(), int(QDialog::Rejected)); + } +}; + +QTEST_MAIN(tst_customizedialog) +#include "tst_customizedialog.moc"