Let the user choose what each folio editor toolbar holds

Configure QElectroTech > Toolbar contents: pick a toolbar, drag or add
commands from the list on the left, remove them with Delete, reorder,
add separators, and add, rename or delete toolbars of one's own.
Reset to defaults gives a toolbar its original contents back.

Each toolbar is stored as a list of command ids in QSettings
(diagrameditor/toolbars/<objectName>), the same pattern the shortcut
bar uses. A toolbar the user never changed stores nothing and is built
exactly as before; an id no command carries any more is skipped. The
four buttons that are widgets rather than commands (handle size, text
grid, background colour, conductor colour) get ids of their own and
can each be on one toolbar at a time. Stored scripts can go on any
toolbar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-07 18:34:51 +13:00
parent 437729df15
commit 3a61513f29
10 changed files with 1283 additions and 39 deletions
+4
View File
@@ -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/diagramtoolbarsettings.cpp
${QET_DIR}/sources/diagramtoolbarsettings.h
${QET_DIR}/sources/toolbarsettings.cpp
${QET_DIR}/sources/toolbarsettings.h
${QET_DIR}/sources/diagramcontexttoolbar.cpp
@@ -858,6 +860,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/toolbarcommandsconfigpage.cpp
${QET_DIR}/sources/ui/configpage/toolbarcommandsconfigpage.h
${QET_DIR}/sources/ui/configpage/toolbarsconfigpage.cpp
${QET_DIR}/sources/ui/configpage/toolbarsconfigpage.h
+352
View File
@@ -0,0 +1,352 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "diagramtoolbarsettings.h"
#include "shortcutmanager.h"
#include <QApplication>
#include <QCoreApplication>
#include <QSettings>
#include <QToolBar>
namespace {
const QString CONTENTS = QStringLiteral("diagrameditor/toolbars/");
const QString CUSTOM_NAMES = QStringLiteral("diagrameditor/custom_toolbars/names");
const QString CUSTOM_TITLES = QStringLiteral("diagrameditor/custom_toolbars/titles");
//Set on a window by markWindow()
const char *const MARKED = "qetDiagramToolbars";
}
/**
@return every toolbar, built-in first, then the user's own in the order
they were added
*/
QList<DiagramToolbarSettings::Toolbar> DiagramToolbarSettings::toolbars()
{
QList<Toolbar> list;
for (const QString &name : builtInNames()) {
list << Toolbar{name, builtInTitle(name), false};
}
return list << customToolbars();
}
/**
@return the object names of the toolbars the diagram editor always has.
The scripts toolbar is not one: it is filled from the scripts folder.
*/
QStringList DiagramToolbarSettings::builtInNames()
{
return {QStringLiteral("toolbar"),
QStringLiteral("display"),
QStringLiteral("diagram"),
QStringLiteral("adding"),
QStringLiteral("diagram_depth_toolbar")};
}
QString DiagramToolbarSettings::builtInTitle(const QString &name)
{
if (name == QLatin1String("toolbar"))
return QCoreApplication::translate("QETDiagramEditor", "Outils");
if (name == QLatin1String("display"))
return QCoreApplication::translate("QETDiagramEditor", "Affichage");
if (name == QLatin1String("diagram"))
return QCoreApplication::translate("QETDiagramEditor", "Schéma");
if (name == QLatin1String("adding"))
return QCoreApplication::translate("QETDiagramEditor", "Ajouter");
if (name == QLatin1String("diagram_depth_toolbar"))
return QCoreApplication::translate("QETDiagramEditor", "Profondeur", "toolbar title");
return QString();
}
/**
@return the toolbars the user added, in the order they were added
*/
QList<DiagramToolbarSettings::Toolbar> DiagramToolbarSettings::customToolbars()
{
QSettings settings;
const QStringList names = settings.value(CUSTOM_NAMES).toStringList();
const QStringList titles = settings.value(CUSTOM_TITLES).toStringList();
QList<Toolbar> list;
for (int i = 0 ; i < names.size() ; ++i) {
list << Toolbar{names.at(i), titles.value(i, names.at(i)), true};
}
return list;
}
/**
@brief DiagramToolbarSettings::setCustomToolbars
Store the user's own toolbars. The contents of one that is no longer
in @a toolbars are removed with it.
*/
void DiagramToolbarSettings::setCustomToolbars(const QList<Toolbar> &toolbars)
{
QStringList names, titles;
for (const Toolbar &toolbar : toolbars) {
names << toolbar.name;
titles << toolbar.title;
}
for (const Toolbar &old : customToolbars()) {
if (!names.contains(old.name)) {
removeIds(old.name);
}
}
QSettings settings;
if (names.isEmpty()) {
settings.remove(CUSTOM_NAMES);
settings.remove(CUSTOM_TITLES);
} else {
settings.setValue(CUSTOM_NAMES, names);
settings.setValue(CUSTOM_TITLES, titles);
}
}
/**
@return an object name for a new toolbar that none of @a existing has
and none had before (stored positions stay with the toolbar they
belonged to)
*/
QString DiagramToolbarSettings::newCustomName(const QList<Toolbar> &existing)
{
QStringList taken;
for (const Toolbar &toolbar : existing) taken << toolbar.name;
for (const Toolbar &toolbar : customToolbars()) taken << toolbar.name;
for (int i = 1 ; ; ++i) {
const QString name = QStringLiteral("custom_%1").arg(i);
if (!taken.contains(name)) return name;
}
}
/**
@return the ids toolbar @a name holds: the user's list if they saved
one, the defaults otherwise. A saved empty list stays empty.
*/
QStringList DiagramToolbarSettings::ids(const QString &name)
{
QSettings settings;
const QString key = CONTENTS + name;
if (!settings.contains(key)) {
return defaultIds(name);
}
return settings.value(key).toStringList();
}
/**
@return what toolbar @a name holds for a new user: the toolbars as
they were built before they could be changed. A user's own toolbar
starts empty.
*/
QStringList DiagramToolbarSettings::defaultIds(const QString &name)
{
const QString sep = separatorId();
if (name == QLatin1String("toolbar")) {
return {QStringLiteral("diagrameditor.new_file"),
QStringLiteral("diagrameditor.open_file"),
QStringLiteral("diagrameditor.save_file"),
QStringLiteral("diagrameditor.save_file_as"),
QStringLiteral("diagrameditor.close_file"),
QStringLiteral("diagrameditor.print"),
QStringLiteral("diagrameditor.export_to_pdf"),
sep,
QStringLiteral("diagrameditor.undo"),
QStringLiteral("diagrameditor.redo"),
sep,
QStringLiteral("diagrameditor.cut"),
QStringLiteral("diagrameditor.copy"),
QStringLiteral("diagrameditor.paste"),
QStringLiteral("diagrameditor.duplicate"),
sep,
QStringLiteral("diagrameditor.delete_selection"),
QStringLiteral("diagrameditor.rotate_selection")};
}
if (name == QLatin1String("display")) {
return {QStringLiteral("diagrameditor.mode_selection"),
QStringLiteral("diagrameditor.mode_visualise"),
sep,
QStringLiteral("widget:handler_size"),
sep,
QStringLiteral("diagrameditor.draw_grid"),
QStringLiteral("widget:text_grid"),
QStringLiteral("diagrameditor.draw_guides"),
QStringLiteral("widget:background_color"),
sep,
QStringLiteral("diagrameditor.zoom_content"),
QStringLiteral("diagrameditor.zoom_fit"),
QStringLiteral("diagrameditor.zoom_reset")};
}
if (name == QLatin1String("diagram")) {
return {QStringLiteral("diagrameditor.edit_diagram_properties"),
QStringLiteral("diagrameditor.conductor_reset"),
QStringLiteral("diagrameditor.auto_conductor"),
QStringLiteral("diagrameditor.auto_break_conductor"),
QStringLiteral("widget:conductor_color")};
}
if (name == QLatin1String("adding")) {
//add_pdf is skipped when built without QtPdf, as before
return {QStringLiteral("diagrameditor.add_text"),
QStringLiteral("diagrameditor.add_image"),
QStringLiteral("diagrameditor.add_pdf"),
QStringLiteral("diagrameditor.add_line"),
QStringLiteral("diagrameditor.add_rectangle"),
QStringLiteral("diagrameditor.add_ellipse"),
QStringLiteral("diagrameditor.add_arc"),
QStringLiteral("diagrameditor.add_polyline"),
QStringLiteral("diagrameditor.add_path"),
QStringLiteral("diagrameditor.add_fillet"),
QStringLiteral("diagrameditor.add_terminal_strip")};
}
if (name == QLatin1String("diagram_depth_toolbar")) {
return {QStringLiteral("depth.forward"),
QStringLiteral("depth.raise"),
QStringLiteral("depth.lower"),
QStringLiteral("depth.backward")};
}
return {};
}
/**
@brief DiagramToolbarSettings::setIds
Save @a ids for toolbar @a name. Saving the defaults removes the key, so
a later change of defaults still reaches this user.
*/
void DiagramToolbarSettings::setIds(const QString &name, const QStringList &ids)
{
if (ids == defaultIds(name)) {
removeIds(name);
} else {
QSettings().setValue(CONTENTS + name, ids);
}
}
void DiagramToolbarSettings::removeIds(const QString &name)
{
QSettings().remove(CONTENTS + name);
}
QString DiagramToolbarSettings::separatorId()
{
return QStringLiteral("separator");
}
/**
@return the toolbar buttons that are widgets, not commands. Each can be
on one toolbar at a time.
*/
QStringList DiagramToolbarSettings::widgetIds()
{
return {QStringLiteral("widget:handler_size"),
QStringLiteral("widget:text_grid"),
QStringLiteral("widget:background_color"),
QStringLiteral("widget:conductor_color")};
}
bool DiagramToolbarSettings::isWidget(const QString &id)
{
return id.startsWith(QLatin1String("widget:"));
}
QString DiagramToolbarSettings::widgetTitle(const QString &id)
{
if (id == QLatin1String("widget:handler_size"))
return QCoreApplication::translate("DiagramToolbarSettings", "Taille des poignées");
if (id == QLatin1String("widget:text_grid"))
return QCoreApplication::translate("DiagramToolbarSettings", "Grille des textes");
if (id == QLatin1String("widget:background_color"))
return QCoreApplication::translate("DiagramToolbarSettings", "Couleur de fond du folio");
if (id == QLatin1String("widget:conductor_color"))
return QCoreApplication::translate("DiagramToolbarSettings", "Couleur des conducteurs");
return id;
}
/**
@return every command that can go on a toolbar: the diagram editor's,
its stored scripts', and the depth commands it shares with the element
editor
*/
QStringList DiagramToolbarSettings::availableCommandIds()
{
QStringList ids;
for (const ShortcutManager::ShortcutInfo &info :
ShortcutManager::instance().allShortcuts())
{
if (info.id.startsWith(QLatin1String("diagrameditor."))
|| info.id.startsWith(QLatin1String("depth."))) {
ids << info.id;
}
}
return ids;
}
/**
@brief DiagramToolbarSettings::fill
Empty @a toolbar and put the commands of @a ids on it, in order.
@a resolve gives the window's action for an id, a widget id included;
an id it has no action for is skipped. A separator never starts or
ends the toolbar and never follows another: those are left out, as
Qt would otherwise draw them.
*/
void DiagramToolbarSettings::fill(QToolBar *toolbar, const QStringList &ids,
const std::function<QAction *(const QString &)> &resolve)
{
//Separators belong to the toolbar that made them; commands do not
const QList<QAction *> old_actions = toolbar->actions();
toolbar->clear();
for (QAction *action : old_actions) {
if (action->isSeparator() && action->parent() == toolbar) delete action;
}
bool pending_separator = false;
for (const QString &id : ids)
{
if (id == separatorId()) {
pending_separator = !toolbar->actions().isEmpty();
continue;
}
QAction *action = resolve(id);
if (!action) {
continue;
}
if (pending_separator) {
toolbar->addSeparator();
pending_separator = false;
}
toolbar->addAction(action);
}
}
/**
@brief DiagramToolbarSettings::markWindow
Mark @a window as one whose toolbars applyToAll() rebuilds. It must
have a rebuildToolBars() slot or invokable.
*/
void DiagramToolbarSettings::markWindow(QObject *window)
{
window->setProperty(MARKED, true);
}
/**
@brief DiagramToolbarSettings::applyToAll
Rebuild the toolbars of every open window marked by markWindow(),
after the settings change.
*/
void DiagramToolbarSettings::applyToAll()
{
for (QWidget *widget : QApplication::topLevelWidgets()) {
if (widget->property(MARKED).toBool()) {
QMetaObject::invokeMethod(widget, "rebuildToolBars");
}
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#ifndef DIAGRAMTOOLBARSETTINGS_H
#define DIAGRAMTOOLBARSETTINGS_H
#include <QList>
#include <QString>
#include <QStringList>
#include <functional>
class QAction;
class QToolBar;
/**
@brief What each of the diagram editor's toolbars holds.
A toolbar is a list of ids, in order: ShortcutManager command ids, the
separator id, and the ids of the few toolbar buttons that are widgets
rather than commands (the colour buttons, the text grid, the handle
size). Stored in QSettings, one key per toolbar, by the toolbar's
object name; a toolbar the user never changed uses the defaults below,
which are the toolbars as they were before they could be changed.
The user can also add toolbars of their own. They start empty.
An id no command carries any more is skipped, so a list saved by
another version still opens.
*/
class DiagramToolbarSettings
{
public:
struct Toolbar {
QString name; ///< object name, used for storage and by QMainWindow::saveState()
QString title; ///< shown to the user
bool custom = false;
};
static QList<Toolbar> toolbars();
static QStringList builtInNames();
static QString builtInTitle(const QString &name);
static QList<Toolbar> customToolbars();
static void setCustomToolbars(const QList<Toolbar> &toolbars);
static QString newCustomName(const QList<Toolbar> &existing);
static QStringList ids(const QString &name);
static QStringList defaultIds(const QString &name);
static void setIds(const QString &name, const QStringList &ids);
static void removeIds(const QString &name);
static QString separatorId();
static QStringList widgetIds();
static bool isWidget(const QString &id);
static QString widgetTitle(const QString &id);
static QStringList availableCommandIds();
static void fill(QToolBar *toolbar, const QStringList &ids,
const std::function<QAction *(const QString &)> &resolve);
static void markWindow(QObject *window);
static void applyToAll();
};
#endif // DIAGRAMTOOLBARSETTINGS_H
+2
View File
@@ -43,6 +43,7 @@
#include "ui/configpage/shortcutsconfigpage.h"
#include "ui/configpage/shortcutbarconfigpage.h"
#include "ui/configpage/toolbarsconfigpage.h"
#include "ui/configpage/toolbarcommandsconfigpage.h"
#include "machine_info.h"
#include "TerminalStrip/ui/terminalstripeditorwindow.h"
#include "qetversion.h"
@@ -2227,6 +2228,7 @@ void QETApp::configureQET()
cd.addPage(new ShortcutsConfigPage());
cd.addPage(new ShortcutBarConfigPage());
cd.addPage(new ToolbarsConfigPage());
cd.addPage(new ToolbarCommandsConfigPage());
#ifdef QET_SPACEMOUSE_SUPPORT
cd.addPage(new SpaceMouseConfigPage());
#endif
+75 -39
View File
@@ -27,9 +27,11 @@
#endif
#include <QCoreApplication>
#include <QToolButton>
#include <QWidgetAction>
#include "ElementsCollection/elementscollectionwidget.h"
#include "ElementsCollection/elementpickerpopup.h"
#include "shortcutbarsettings.h"
#include "diagramtoolbarsettings.h"
#include "qetgraphicsitem/conductor.h"
#include "itemgroups.h"
#include "commandsearchpopup.h"
@@ -1316,50 +1318,27 @@ void QETDiagramEditor::setUpToolBar()
diagram_tool_bar = new QToolBar(tr("Schéma"), this);
diagram_tool_bar -> setObjectName("diagram");
main_tool_bar -> addActions(m_file_actions_group.actions());
main_tool_bar -> addAction(m_print);
main_tool_bar -> addAction(m_export_to_pdf);
main_tool_bar -> addSeparator();
main_tool_bar -> addAction(undo);
main_tool_bar -> addAction(redo);
main_tool_bar -> addSeparator();
main_tool_bar -> addAction(m_cut);
main_tool_bar -> addAction(m_copy);
main_tool_bar -> addAction(m_paste);
main_tool_bar -> addAction(m_duplicate);
main_tool_bar -> addSeparator();
main_tool_bar -> addAction(m_delete_selection);
main_tool_bar -> addAction(m_rotate_selection);
// Modes selection / visualisation et zoom
view_tool_bar -> addAction(m_mode_selection);
view_tool_bar -> addAction(m_mode_visualise);
view_tool_bar -> addSeparator();
view_tool_bar -> addWidget(new DiagramEditorHandlerSizeWidget(this));
view_tool_bar -> addSeparator();
view_tool_bar -> addAction(m_draw_grid);
view_tool_bar -> addWidget(m_text_grid_button);
view_tool_bar -> addAction(m_draw_guides);
view_tool_bar -> addWidget(m_background_color_button);
view_tool_bar -> addSeparator();
view_tool_bar -> addActions(m_zoom_action_toolBar);
diagram_tool_bar -> addAction (m_edit_diagram_properties);
diagram_tool_bar -> addAction (m_conductor_reset);
diagram_tool_bar -> addAction (m_auto_conductor);
diagram_tool_bar -> addAction (m_auto_break_conductor);
//Sits with the conductor actions it works alongside: it colours
//the selected conductors and sets the colour of the next one drawn.
m_conductor_color_button = new ConductorColorToolButton(this, this);
diagram_tool_bar -> addWidget (m_conductor_color_button);
m_add_item_tool_bar = new QToolBar(tr("Ajouter"), this);
m_add_item_tool_bar->setObjectName("adding");
m_add_item_tool_bar->addActions(m_add_item_actions_group.actions());
m_depth_tool_bar = new QToolBar(tr("Profondeur", "toolbar title"));
m_depth_tool_bar->setObjectName("diagram_depth_toolbar");
m_depth_tool_bar->addActions(m_depth_action_group->actions());
//The toolbar buttons that are widgets, not commands. Each is held by
//an action of this window, so it survives being taken off a toolbar.
auto add_widget = [this](const QString &id, QWidget *widget) {
auto *action = new QWidgetAction(this);
action->setDefaultWidget(widget);
action->setText(DiagramToolbarSettings::widgetTitle(id));
m_toolbar_widgets.insert(id, action);
};
add_widget(QStringLiteral("widget:handler_size"), new DiagramEditorHandlerSizeWidget(this));
add_widget(QStringLiteral("widget:text_grid"), m_text_grid_button);
add_widget(QStringLiteral("widget:background_color"), m_background_color_button);
//Sits with the conductor actions it works alongside: it colours
//the selected conductors and sets the colour of the next one drawn.
m_conductor_color_button = new ConductorColorToolButton(this, this);
add_widget(QStringLiteral("widget:conductor_color"), m_conductor_color_button);
addToolBar(Qt::TopToolBarArea, main_tool_bar);
addToolBar(Qt::TopToolBarArea, view_tool_bar);
@@ -1367,6 +1346,9 @@ void QETDiagramEditor::setUpToolBar()
addToolBar(Qt::TopToolBarArea, m_add_item_tool_bar);
addToolBar(Qt::TopToolBarArea, m_depth_tool_bar);
DiagramToolbarSettings::markWindow(this);
rebuildToolBars();
m_scripts_tool_bar = new QToolBar(tr("Scripts", "toolbar title"), this);
m_scripts_tool_bar->setObjectName("scripts");
addToolBar(Qt::TopToolBarArea, m_scripts_tool_bar);
@@ -1376,6 +1358,59 @@ void QETDiagramEditor::setUpToolBar()
#endif
}
/**
@brief QETDiagramEditor::rebuildToolBars
Fill every toolbar from DiagramToolbarSettings, adding the user's own
toolbars that this window does not have yet and removing those that
were deleted. Called when the window is built, when the settings
change, and when the stored scripts change (a script can be on any
toolbar).
*/
void QETDiagramEditor::rebuildToolBars()
{
const QList<DiagramToolbarSettings::Toolbar> custom = DiagramToolbarSettings::customToolbars();
QStringList custom_names;
for (const DiagramToolbarSettings::Toolbar &toolbar : custom) {
custom_names << toolbar.name;
}
for (QToolBar *toolbar : m_custom_tool_bars) {
if (!custom_names.contains(toolbar->objectName())) {
removeToolBar(toolbar);
toolbar->deleteLater();
}
}
m_custom_tool_bars.erase(std::remove_if(m_custom_tool_bars.begin(), m_custom_tool_bars.end(),
[&custom_names](QToolBar *toolbar) { return !custom_names.contains(toolbar->objectName()); }),
m_custom_tool_bars.end());
for (const DiagramToolbarSettings::Toolbar &toolbar : custom)
{
auto existing = std::find_if(m_custom_tool_bars.cbegin(), m_custom_tool_bars.cend(),
[&toolbar](QToolBar *t) { return t->objectName() == toolbar.name; });
if (existing != m_custom_tool_bars.cend()) {
(*existing)->setWindowTitle(toolbar.title);
continue;
}
auto *new_tool_bar = new QToolBar(toolbar.title, this);
new_tool_bar->setObjectName(toolbar.name);
new_tool_bar->setMovable(!ToolbarSettings::locked());
addToolBar(Qt::TopToolBarArea, new_tool_bar);
m_custom_tool_bars << new_tool_bar;
}
auto resolve = [this](const QString &id) -> QAction * {
if (DiagramToolbarSettings::isWidget(id)) {
return m_toolbar_widgets.value(id);
}
return ShortcutManager::instance().action(id, this);
};
const QList<QToolBar *> built_in {main_tool_bar, view_tool_bar, diagram_tool_bar,
m_add_item_tool_bar, m_depth_tool_bar};
for (QToolBar *toolbar : built_in + m_custom_tool_bars) {
DiagramToolbarSettings::fill(toolbar, DiagramToolbarSettings::ids(toolbar->objectName()), resolve);
}
}
/**
@brief QETDiagramEditor::setUpMenu
*/
@@ -4039,6 +4074,7 @@ void QETDiagramEditor::rebuildScriptActions()
if (has_scripts != m_had_scripts) m_scripts_tool_bar->setVisible(has_scripts);
m_had_scripts = has_scripts;
rebuildToolBars();
updateScriptActions();
}
+5
View File
@@ -140,6 +140,7 @@ class QETDiagramEditor : public QETMainWindow
void rememberPlacedElement(const ElementsLocation &location);
void showElementPicker();
void showShortcutBar();
void rebuildToolBars();
bool repeatLastCommand();
void generateTerminalBlock();
void setWindowedMode();
@@ -328,6 +329,10 @@ class QETDiagramEditor : public QETMainWindow
*m_add_item_tool_bar = nullptr,
*m_depth_tool_bar = nullptr,
*m_scripts_tool_bar = nullptr; ///< One button per stored script
/// Toolbars the user added (DiagramToolbarSettings)
QList<QToolBar *> m_custom_tool_bars;
/// The toolbar buttons that are widgets, by DiagramToolbarSettings id
QHash<QString, QAction *> m_toolbar_widgets;
#ifdef QET_HAS_SCRIPTING
QMenu *m_scripts_menu = nullptr;
QList<QAction *> m_script_actions; ///< One per stored script, rebuilt when the folder changes
@@ -0,0 +1,423 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "toolbarcommandsconfigpage.h"
#include "../../qeticons.h"
#include "../../shortcutmanager.h"
#include <QAction>
#include <QComboBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
ToolbarCommandsConfigPage::ToolbarCommandsConfigPage(QWidget *parent) :
ConfigPage(parent)
{
const QStringList available = DiagramToolbarSettings::availableCommandIds();
for (const ShortcutManager::ShortcutInfo &info :
ShortcutManager::instance().allShortcuts()) {
if (available.contains(info.id)) {
m_descriptions.insert(info.id, info.description);
}
}
m_custom = DiagramToolbarSettings::customToolbars();
for (const DiagramToolbarSettings::Toolbar &toolbar : DiagramToolbarSettings::toolbars()) {
m_pending.insert(toolbar.name, DiagramToolbarSettings::ids(toolbar.name));
}
auto *explanation = new QLabel(
tr("Choisissez les commandes de chaque barre d'outils de l'éditeur "
"de schémas. Glissez une commande de la liste de gauche vers la "
"barre, ou double-cliquez dessus ; Suppr la retire."), this);
explanation->setWordWrap(true);
m_toolbar = new QComboBox(this);
m_toolbar->setObjectName(QStringLiteral("toolbarCombo"));
auto *new_toolbar = new QPushButton(tr("Nouvelle barre…"), this);
new_toolbar->setObjectName(QStringLiteral("newToolbarButton"));
m_rename = new QPushButton(tr("Renommer…"), this);
m_delete = new QPushButton(tr("Supprimer la barre"), this);
m_delete->setObjectName(QStringLiteral("deleteToolbarButton"));
auto *reset = new QPushButton(tr("Valeurs par défaut"), this);
reset->setObjectName(QStringLiteral("resetButton"));
m_filter = new QLineEdit(this);
m_filter->setPlaceholderText(tr("Rechercher une commande"));
m_filter->setClearButtonEnabled(true);
m_available = new QListWidget(this);
m_available->setObjectName(QStringLiteral("availableList"));
m_available->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_available->setSortingEnabled(true);
m_available->setDragDropMode(QAbstractItemView::DragOnly);
m_available->setDefaultDropAction(Qt::MoveAction);
m_chosen = new QListWidget(this);
m_chosen->setObjectName(QStringLiteral("chosenList"));
m_chosen->setSelectionMode(QAbstractItemView::ExtendedSelection);
//Takes commands dragged from the list on the left, and reorders
m_chosen->setDragDropMode(QAbstractItemView::DragDrop);
m_chosen->setDefaultDropAction(Qt::MoveAction);
auto *add = new QPushButton(tr("Ajouter →"), this);
auto *separator = new QPushButton(tr("Séparateur"), this);
separator->setObjectName(QStringLiteral("separatorButton"));
auto *remove = new QPushButton(tr("← Retirer"), this);
auto *up = new QPushButton(tr("Monter"), this);
auto *down = new QPushButton(tr("Descendre"), this);
auto *buttons = new QVBoxLayout();
buttons->addStretch();
buttons->addWidget(add);
buttons->addWidget(separator);
buttons->addWidget(remove);
buttons->addSpacing(12);
buttons->addWidget(up);
buttons->addWidget(down);
buttons->addStretch();
auto *grid = new QGridLayout();
grid->addWidget(new QLabel(tr("Commandes disponibles"), this), 0, 0);
grid->addWidget(new QLabel(tr("Dans la barre, dans l'ordre"), this), 0, 2);
grid->addWidget(m_filter, 1, 0);
grid->addWidget(m_available, 2, 0);
grid->addLayout(buttons, 2, 1);
grid->addWidget(m_chosen, 1, 2, 2, 1);
auto *toolbar_row = new QHBoxLayout();
toolbar_row->addWidget(new QLabel(tr("Barre d'outils :"), this));
toolbar_row->addWidget(m_toolbar, 1);
toolbar_row->addWidget(new_toolbar);
toolbar_row->addWidget(m_rename);
toolbar_row->addWidget(m_delete);
toolbar_row->addWidget(reset);
auto *layout = new QVBoxLayout(this);
layout->addWidget(explanation);
layout->addLayout(toolbar_row);
layout->addLayout(grid);
connect(m_toolbar, qOverload<int>(&QComboBox::currentIndexChanged), this, [this]() {
storeToolbar();
showToolbar();
});
connect(m_filter, &QLineEdit::textChanged, this, &ToolbarCommandsConfigPage::fillAvailable);
connect(add, &QPushButton::clicked, this, &ToolbarCommandsConfigPage::addSelected);
connect(separator, &QPushButton::clicked, this, &ToolbarCommandsConfigPage::addSeparator);
connect(remove, &QPushButton::clicked, this, &ToolbarCommandsConfigPage::removeSelected);
connect(up, &QPushButton::clicked, this, [this]() { moveSelected(-1); });
connect(down, &QPushButton::clicked, this, [this]() { moveSelected(1); });
connect(reset, &QPushButton::clicked, this, &ToolbarCommandsConfigPage::resetToolbar);
connect(new_toolbar, &QPushButton::clicked, this, &ToolbarCommandsConfigPage::newToolbar);
connect(m_rename, &QPushButton::clicked, this, &ToolbarCommandsConfigPage::renameToolbar);
connect(m_delete, &QPushButton::clicked, this, &ToolbarCommandsConfigPage::deleteToolbar);
connect(m_available, &QListWidget::itemDoubleClicked, this, &ToolbarCommandsConfigPage::addSelected);
connect(m_chosen, &QListWidget::itemDoubleClicked, this, &ToolbarCommandsConfigPage::removeSelected);
//A command dropped from the left is no longer available. Later,
//not now: a dropped row is inserted before its data is set.
connect(m_chosen->model(), &QAbstractItemModel::rowsInserted, this, [this]() {
if (m_chosen->property("filling").toBool()) return;
QTimer::singleShot(0, this, [this]() {
storeToolbar();
fillAvailable();
});
});
auto *delete_key = new QAction(m_chosen);
delete_key->setShortcut(QKeySequence::Delete);
delete_key->setShortcutContext(Qt::WidgetShortcut);
m_chosen->addAction(delete_key);
connect(delete_key, &QAction::triggered, this, &ToolbarCommandsConfigPage::removeSelected);
fillToolbarCombo(DiagramToolbarSettings::builtInNames().constFirst());
}
/**
@brief ToolbarCommandsConfigPage::applyConf
Save the user's own toolbars and every toolbar's contents, then
rebuild the toolbars of the open diagram editors.
*/
void ToolbarCommandsConfigPage::applyConf()
{
storeToolbar();
DiagramToolbarSettings::setCustomToolbars(m_custom);
for (const DiagramToolbarSettings::Toolbar &toolbar : DiagramToolbarSettings::toolbars()) {
DiagramToolbarSettings::setIds(toolbar.name, m_pending.value(toolbar.name));
}
DiagramToolbarSettings::applyToAll();
}
QString ToolbarCommandsConfigPage::title() const
{
return tr("Contenu des barres", "configuration page title: what each toolbar holds");
}
QIcon ToolbarCommandsConfigPage::icon() const
{
return QET::Icons::ConfigureToolbars;
}
/**
@brief ToolbarCommandsConfigPage::fillToolbarCombo
List the built-in toolbars and the user's own, and show @a current.
*/
void ToolbarCommandsConfigPage::fillToolbarCombo(const QString &current)
{
const QSignalBlocker blocker(m_toolbar);
m_toolbar->clear();
for (const QString &name : DiagramToolbarSettings::builtInNames()) {
m_toolbar->addItem(DiagramToolbarSettings::builtInTitle(name), name);
}
for (const DiagramToolbarSettings::Toolbar &toolbar : std::as_const(m_custom)) {
m_toolbar->addItem(toolbar.title, toolbar.name);
}
const int index = m_toolbar->findData(current);
m_toolbar->setCurrentIndex(index < 0 ? 0 : index);
showToolbar();
}
/**
@brief ToolbarCommandsConfigPage::showToolbar
Fill both lists for the toolbar chosen in the combo box.
*/
void ToolbarCommandsConfigPage::showToolbar()
{
m_shown = m_toolbar->currentData().toString();
const bool custom = !DiagramToolbarSettings::builtInNames().contains(m_shown);
m_rename->setEnabled(custom);
m_delete->setEnabled(custom);
m_chosen->setProperty("filling", true);
m_chosen->clear();
for (const QString &id : m_pending.value(m_shown)) {
m_chosen->addItem(makeItem(id));
}
m_chosen->setProperty("filling", false);
fillAvailable();
}
/**
@brief ToolbarCommandsConfigPage::storeToolbar
Keep the shown toolbar's list, in the order on screen.
*/
void ToolbarCommandsConfigPage::storeToolbar()
{
if (m_shown.isEmpty()) {
return;
}
QStringList ids;
for (int i = 0 ; i < m_chosen->count() ; ++i) {
ids << m_chosen->item(i)->data(Qt::UserRole).toString();
}
m_pending.insert(m_shown, ids);
}
/**
@brief ToolbarCommandsConfigPage::fillAvailable
List what can still go on the shown toolbar and matches the search: a
command not already on it, and a widget button on no toolbar at all
(a widget can only be in one place).
*/
void ToolbarCommandsConfigPage::fillAvailable()
{
QStringList on_shown;
for (int i = 0 ; i < m_chosen->count() ; ++i) {
on_shown << m_chosen->item(i)->data(Qt::UserRole).toString();
}
QStringList on_any = on_shown;
for (auto it = m_pending.cbegin() ; it != m_pending.cend() ; ++it) {
if (it.key() != m_shown) on_any << it.value();
}
QStringList ids;
for (auto it = m_descriptions.cbegin() ; it != m_descriptions.cend() ; ++it) {
if (!on_shown.contains(it.key())) ids << it.key();
}
for (const QString &id : DiagramToolbarSettings::widgetIds()) {
if (!on_any.contains(id)) ids << id;
}
m_available->clear();
const QString filter = m_filter->text().trimmed();
for (const QString &id : std::as_const(ids)) {
QListWidgetItem *item = makeItem(id);
if (filter.isEmpty() || item->text().contains(filter, Qt::CaseInsensitive)) {
m_available->addItem(item);
} else {
delete item;
}
}
}
void ToolbarCommandsConfigPage::addSelected()
{
const QList<QListWidgetItem *> selected = m_available->selectedItems();
if (selected.isEmpty()) {
return;
}
m_chosen->setProperty("filling", true);
for (QListWidgetItem *item : selected) {
m_chosen->addItem(m_available->takeItem(m_available->row(item)));
}
m_chosen->setProperty("filling", false);
storeToolbar();
fillAvailable();
}
/**
@brief ToolbarCommandsConfigPage::addSeparator
Insert a separator after the selected row, or at the end.
*/
void ToolbarCommandsConfigPage::addSeparator()
{
const int row = m_chosen->currentRow();
m_chosen->setProperty("filling", true);
m_chosen->insertItem(row < 0 ? m_chosen->count() : row + 1,
makeItem(DiagramToolbarSettings::separatorId()));
m_chosen->setProperty("filling", false);
storeToolbar();
}
void ToolbarCommandsConfigPage::removeSelected()
{
for (QListWidgetItem *item : m_chosen->selectedItems()) {
delete m_chosen->takeItem(m_chosen->row(item));
}
storeToolbar();
fillAvailable();
}
/**
@brief ToolbarCommandsConfigPage::moveSelected
Move the selected command @a step rows, keeping it selected.
*/
void ToolbarCommandsConfigPage::moveSelected(int step)
{
const int row = m_chosen->currentRow();
const int target = row + step;
if (row < 0 || target < 0 || target >= m_chosen->count()) {
return;
}
m_chosen->setProperty("filling", true);
QListWidgetItem *item = m_chosen->takeItem(row);
m_chosen->insertItem(target, item);
m_chosen->setProperty("filling", false);
m_chosen->setCurrentRow(target);
storeToolbar();
}
/**
@brief ToolbarCommandsConfigPage::resetToolbar
Give the shown toolbar its defaults back; the user's own toolbar is
emptied.
*/
void ToolbarCommandsConfigPage::resetToolbar()
{
m_pending.insert(m_shown, DiagramToolbarSettings::defaultIds(m_shown));
//A widget the defaults put here leaves the toolbar it was moved to
for (const QString &id : DiagramToolbarSettings::defaultIds(m_shown)) {
if (!DiagramToolbarSettings::isWidget(id)) continue;
for (auto it = m_pending.begin() ; it != m_pending.end() ; ++it) {
if (it.key() != m_shown) it.value().removeAll(id);
}
}
showToolbar();
}
void ToolbarCommandsConfigPage::newToolbar()
{
bool ok = false;
const QString title = QInputDialog::getText(this, tr("Nouvelle barre d'outils"),
tr("Nom de la barre d'outils :"),
QLineEdit::Normal, QString(), &ok).trimmed();
if (!ok || title.isEmpty()) {
return;
}
storeToolbar();
DiagramToolbarSettings::Toolbar toolbar;
toolbar.name = DiagramToolbarSettings::newCustomName(m_custom);
toolbar.title = title;
toolbar.custom = true;
m_custom << toolbar;
m_pending.insert(toolbar.name, QStringList());
fillToolbarCombo(toolbar.name);
}
void ToolbarCommandsConfigPage::renameToolbar()
{
for (DiagramToolbarSettings::Toolbar &toolbar : m_custom) {
if (toolbar.name != m_shown) continue;
bool ok = false;
const QString title = QInputDialog::getText(this, tr("Renommer la barre d'outils"),
tr("Nom de la barre d'outils :"),
QLineEdit::Normal, toolbar.title, &ok).trimmed();
if (ok && !title.isEmpty()) {
storeToolbar();
toolbar.title = title;
fillToolbarCombo(m_shown);
}
return;
}
}
void ToolbarCommandsConfigPage::deleteToolbar()
{
const QString name = m_shown;
for (int i = 0 ; i < m_custom.size() ; ++i) {
if (m_custom.at(i).name == name) {
m_custom.removeAt(i);
m_pending.remove(name);
m_shown.clear();
fillToolbarCombo(DiagramToolbarSettings::builtInNames().constFirst());
return;
}
}
}
/**
@brief ToolbarCommandsConfigPage::makeItem
@return a list item for @a id with the command's text and icon. An id
no live action carries (a command from a build without it, or a script
since removed) is still listed, by its id, so saving does not silently
drop it.
*/
QListWidgetItem *ToolbarCommandsConfigPage::makeItem(const QString &id) const
{
QString text = m_descriptions.value(id, id);
QString tip;
QIcon icon;
if (id == DiagramToolbarSettings::separatorId()) {
text = tr("─── Séparateur ───");
} else if (DiagramToolbarSettings::isWidget(id)) {
text = DiagramToolbarSettings::widgetTitle(id);
} else if (QAction *action = ShortcutManager::instance().action(id, nullptr)) {
icon = action->icon();
tip = action->statusTip();
}
auto *item = new QListWidgetItem(icon, text);
item->setData(Qt::UserRole, id);
//Tells apart commands with the same name ("Ajouter une ligne")
item->setToolTip(tip);
return item;
}
@@ -0,0 +1,78 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#ifndef TOOLBARCOMMANDSCONFIGPAGE_H
#define TOOLBARCOMMANDSCONFIGPAGE_H
#include "configpage.h"
#include "../../diagramtoolbarsettings.h"
#include <QHash>
class QComboBox;
class QLineEdit;
class QListWidget;
class QListWidgetItem;
class QPushButton;
/**
@brief The ToolbarCommandsConfigPage class
Choose what each of the diagram editor's toolbars holds, and add,
rename or delete toolbars of one's own. Commands are dragged or added
from the list on the left. Changes are kept per toolbar while the
dialog is open, then saved and applied to the open windows by
applyConf().
*/
class ToolbarCommandsConfigPage : public ConfigPage
{
Q_OBJECT
public:
explicit ToolbarCommandsConfigPage(QWidget *parent = nullptr);
void applyConf() override;
QString title() const override;
QIcon icon() const override;
private:
void fillToolbarCombo(const QString &current);
void showToolbar();
void storeToolbar();
void fillAvailable();
void addSelected();
void addSeparator();
void removeSelected();
void moveSelected(int step);
void resetToolbar();
void newToolbar();
void renameToolbar();
void deleteToolbar();
QListWidgetItem *makeItem(const QString &id) const;
QComboBox *m_toolbar;
QLineEdit *m_filter;
QListWidget *m_available;
QListWidget *m_chosen;
QPushButton *m_rename;
QPushButton *m_delete;
QString m_shown;
QList<DiagramToolbarSettings::Toolbar> m_custom;
QHash<QString, QStringList> m_pending;
QHash<QString, QString> m_descriptions;
};
#endif // TOOLBARCOMMANDSCONFIGPAGE_H
+20
View File
@@ -298,6 +298,26 @@ set_tests_properties(tst_toolbarsettings PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM
target_include_directories(tst_toolbarsettings PRIVATE ${QET_DIR}/sources)
target_link_libraries(tst_toolbarsettings PRIVATE Qt::Test Qt::Widgets Qt::Svg)
# DiagramToolbarSettings and its configuration page: unchanged toolbars
# store nothing and build as before, unknown ids and stray separators are
# skipped, the user's own toolbars are kept, a widget is in one place only.
add_executable(
tst_diagramtoolbarsettings
tst_diagramtoolbarsettings.cpp
${QET_DIR}/sources/diagramtoolbarsettings.cpp
${QET_DIR}/sources/diagramtoolbarsettings.h
${QET_DIR}/sources/shortcutmanager.cpp
${QET_DIR}/sources/ui/configpage/toolbarcommandsconfigpage.cpp
${QET_DIR}/sources/ui/configpage/toolbarcommandsconfigpage.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_diagramtoolbarsettings COMMAND tst_diagramtoolbarsettings)
set_tests_properties(tst_diagramtoolbarsettings PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
target_include_directories(tst_diagramtoolbarsettings PRIVATE ${QET_DIR}/sources)
target_link_libraries(tst_diagramtoolbarsettings 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.
+245
View File
@@ -0,0 +1,245 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "diagramtoolbarsettings.h"
#include "shortcutmanager.h"
#include "ui/configpage/toolbarcommandsconfigpage.h"
#include <QAction>
#include <QComboBox>
#include <QListWidget>
#include <QPushButton>
#include <QSettings>
#include <QStandardPaths>
#include <QTest>
#include <QToolBar>
#include <QWidget>
/**
A top-level window standing in for the diagram editor: counts the
calls applyToAll() makes.
*/
class RebuildCounter : public QWidget
{
Q_OBJECT
public:
int rebuilds = 0;
public slots:
void rebuildToolBars() { ++rebuilds; }
};
/**
DiagramToolbarSettings and its configuration page: an unchanged
toolbar stores nothing and is built as before, unknown ids and stray
separators are skipped, the user's own toolbars are kept and removed
with their contents, and the page saves what it shows.
*/
class tst_diagramtoolbarsettings : public QObject
{
Q_OBJECT
QObject m_owner;
QHash<QString, QAction *> m_actions;
/// The action for @a id, made on first use, as the editor's resolver
QAction *resolve(const QString &id)
{
if (id.startsWith(QLatin1String("missing."))) return nullptr;
if (!m_actions.contains(id)) {
auto *action = new QAction(id, &m_owner);
m_actions.insert(id, action);
}
return m_actions.value(id);
}
static QStringList shape(QToolBar *toolbar)
{
QStringList list;
for (QAction *action : toolbar->actions()) {
list << (action->isSeparator() ? QStringLiteral("|") : action->text());
}
return list;
}
static void clearSettings()
{
QSettings settings;
settings.remove(QStringLiteral("diagrameditor/toolbars"));
settings.remove(QStringLiteral("diagrameditor/custom_toolbars"));
}
private slots:
void initTestCase()
{
QStandardPaths::setTestModeEnabled(true);
clearSettings();
//A few of the diagram editor's commands, as it registers them
for (const QString &id : {QStringLiteral("diagrameditor.copy"),
QStringLiteral("diagrameditor.paste"),
QStringLiteral("diagrameditor.zoom_fit"),
QStringLiteral("depth.raise"),
QStringLiteral("elementeditor.copy")}) {
ShortcutManager::instance().registerAction(
new QAction(id, &m_owner), id, QStringLiteral("test"), QKeySequence());
}
}
void init() { clearSettings(); }
void defaultsStoreNothing()
{
for (const QString &name : DiagramToolbarSettings::builtInNames()) {
QVERIFY(!DiagramToolbarSettings::defaultIds(name).isEmpty());
QCOMPARE(DiagramToolbarSettings::ids(name), DiagramToolbarSettings::defaultIds(name));
DiagramToolbarSettings::setIds(name, DiagramToolbarSettings::defaultIds(name));
}
QVERIFY(!QSettings().contains(QStringLiteral("diagrameditor/toolbars/toolbar")));
DiagramToolbarSettings::setIds(QStringLiteral("toolbar"), {QStringLiteral("diagrameditor.copy")});
QCOMPARE(DiagramToolbarSettings::ids(QStringLiteral("toolbar")),
QStringList{QStringLiteral("diagrameditor.copy")});
//Saving an empty toolbar keeps it empty, not back to the defaults
DiagramToolbarSettings::setIds(QStringLiteral("toolbar"), {});
QVERIFY(DiagramToolbarSettings::ids(QStringLiteral("toolbar")).isEmpty());
}
/// The defaults are built exactly as the toolbars were hand-built
/// before: the main toolbar's three separators in place
void defaultsBuildAsBefore()
{
QToolBar toolbar;
DiagramToolbarSettings::fill(&toolbar, DiagramToolbarSettings::defaultIds(QStringLiteral("toolbar")),
[this](const QString &id) { return resolve(id); });
QCOMPARE(toolbar.actions().size(), 18);
QCOMPARE(shape(&toolbar).mid(6, 2),
(QStringList{QStringLiteral("diagrameditor.export_to_pdf"), QStringLiteral("|")}));
QCOMPARE(shape(&toolbar).count(QStringLiteral("|")), 3);
}
void fillSkipsUnknownAndStraySeparators()
{
const QString sep = DiagramToolbarSettings::separatorId();
QToolBar toolbar;
toolbar.addAction(QStringLiteral("left over"));
DiagramToolbarSettings::fill(&toolbar,
{sep, QStringLiteral("a"), sep, sep, QStringLiteral("missing.b"),
QStringLiteral("c"), sep, QStringLiteral("missing.d"), sep},
[this](const QString &id) { return resolve(id); });
QCOMPARE(shape(&toolbar), (QStringList{QStringLiteral("a"), QStringLiteral("|"), QStringLiteral("c")}));
DiagramToolbarSettings::fill(&toolbar, {}, [this](const QString &id) { return resolve(id); });
QVERIFY(toolbar.actions().isEmpty());
}
void customToolbarsKeptAndRemoved()
{
using T = DiagramToolbarSettings::Toolbar;
const QString first = DiagramToolbarSettings::newCustomName({});
QCOMPARE(first, QStringLiteral("custom_1"));
DiagramToolbarSettings::setCustomToolbars({T{first, QStringLiteral("Mine"), true},
T{QStringLiteral("custom_2"), QStringLiteral("Other"), true}});
DiagramToolbarSettings::setIds(first, {QStringLiteral("diagrameditor.copy")});
QCOMPARE(DiagramToolbarSettings::customToolbars().size(), 2);
QCOMPARE(DiagramToolbarSettings::customToolbars().first().title, QStringLiteral("Mine"));
QCOMPARE(DiagramToolbarSettings::toolbars().size(),
DiagramToolbarSettings::builtInNames().size() + 2);
QCOMPARE(DiagramToolbarSettings::newCustomName({}), QStringLiteral("custom_3"));
//A new toolbar starts empty
QVERIFY(DiagramToolbarSettings::ids(QStringLiteral("custom_2")).isEmpty());
DiagramToolbarSettings::setCustomToolbars({T{QStringLiteral("custom_2"), QStringLiteral("Other"), true}});
QCOMPARE(DiagramToolbarSettings::customToolbars().size(), 1);
QVERIFY(!QSettings().contains(QStringLiteral("diagrameditor/toolbars/") + first));
DiagramToolbarSettings::setCustomToolbars({});
QVERIFY(!QSettings().contains(QStringLiteral("diagrameditor/custom_toolbars/names")));
}
void availableCommandsAreTheDiagramEditors()
{
const QStringList ids = DiagramToolbarSettings::availableCommandIds();
QVERIFY(ids.contains(QStringLiteral("diagrameditor.copy")));
QVERIFY(ids.contains(QStringLiteral("depth.raise")));
QVERIFY(!ids.contains(QStringLiteral("elementeditor.copy")));
}
void pageSavesAndRebuilds()
{
RebuildCounter marked, unmarked;
DiagramToolbarSettings::markWindow(&marked);
ToolbarCommandsConfigPage page;
auto *combo = page.findChild<QComboBox *>(QStringLiteral("toolbarCombo"));
auto *available = page.findChild<QListWidget *>(QStringLiteral("availableList"));
auto *chosen = page.findChild<QListWidget *>(QStringLiteral("chosenList"));
QVERIFY(combo && available && chosen);
QCOMPARE(combo->count(), DiagramToolbarSettings::builtInNames().size());
QCOMPARE(chosen->count(), DiagramToolbarSettings::defaultIds(QStringLiteral("toolbar")).size());
//OK without a change stores nothing
page.applyConf();
QVERIFY(!QSettings().contains(QStringLiteral("diagrameditor/toolbars/toolbar")));
QCOMPARE(marked.rebuilds, 1);
QCOMPARE(unmarked.rebuilds, 0);
//Remove everything but the first command, add a separator
while (chosen->count() > 1) delete chosen->takeItem(1);
chosen->setCurrentRow(0);
page.findChild<QPushButton *>(QStringLiteral("separatorButton"))->click();
page.applyConf();
QCOMPARE(DiagramToolbarSettings::ids(QStringLiteral("toolbar")),
(QStringList{QStringLiteral("diagrameditor.new_file"), DiagramToolbarSettings::separatorId()}));
QCOMPARE(marked.rebuilds, 2);
}
/// A widget button can be in one place only: once moved to another
/// toolbar it is no longer offered, and resetting its own toolbar
/// takes it back
void widgetInOnePlace()
{
ToolbarCommandsConfigPage page;
auto *combo = page.findChild<QComboBox *>(QStringLiteral("toolbarCombo"));
auto *available = page.findChild<QListWidget *>(QStringLiteral("availableList"));
auto *chosen = page.findChild<QListWidget *>(QStringLiteral("chosenList"));
auto offered = [available](const QString &id) {
for (int i = 0 ; i < available->count() ; ++i)
if (available->item(i)->data(Qt::UserRole).toString() == id) return true;
return false;
};
const QString colour = QStringLiteral("widget:conductor_color");
//On "diagram" by default, so not offered for "toolbar"
QVERIFY(!offered(colour));
//Taken off "diagram" (switching toolbars keeps the change)
combo->setCurrentIndex(combo->findData(QStringLiteral("diagram")));
for (int i = chosen->count() - 1 ; i >= 0 ; --i) {
if (chosen->item(i)->data(Qt::UserRole).toString() == colour) delete chosen->takeItem(i);
}
combo->setCurrentIndex(combo->findData(QStringLiteral("toolbar")));
QVERIFY(offered(colour));
//Reset "diagram": the colour button goes back there
combo->setCurrentIndex(combo->findData(QStringLiteral("diagram")));
page.findChild<QPushButton *>(QStringLiteral("resetButton"))->click();
QCOMPARE(chosen->count(), DiagramToolbarSettings::defaultIds(QStringLiteral("diagram")).size());
combo->setCurrentIndex(combo->findData(QStringLiteral("toolbar")));
QVERIFY(!offered(colour));
}
};
QTEST_MAIN(tst_diagramtoolbarsettings)
#include "tst_diagramtoolbarsettings.moc"