mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-07 20:34:14 +02:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ¤t)
|
||||
{
|
||||
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 ¤t);
|
||||
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
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user