mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-27 12:34:14 +02:00
Add a shortcut bar that opens at the cursor with S
Pressing S on a folio opens the element picker at the cursor with a row
of commands above it, chosen by what is selected, like the SolidWorks
shortcut bar:
nothing selected insert last element, element picker, text, line,
rectangle, terminal strip plan, paste, folio
properties
elements selected rotate, rotate texts, edit, copy, cut, delete
only conductors reset path, edit, delete
Each row is a list of ShortcutManager ids, so any registered command can
go on it and the bar carries no command list of its own. The lists are
in QSettings (diagrameditor/shortcut_bar/<context>); a context the user
has not changed follows the defaults. A disabled command keeps its
place, greyed, so a row looks the same each time. Clicking a button
closes the bar and triggers the action.
A new configuration page, "Barre de raccourcis", edits the three lists:
add, remove and reorder any diagram editor command.
To make that possible:
- ShortcutManager::action(id, owner) returns the action a given window
registered under an id, since each editor window registers its own.
- The add-item actions (text, image, shapes, terminal strip plan) are
registered as diagrameditor.add_<kind>, with no default key. They also
appear in the Shortcuts page and can now be bound.
Discussion #1033.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG
(cherry picked from commit 53c1e213282b9f7226ca1c09e81703113236bb88)
This commit is contained in:
@@ -285,6 +285,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/recentfiles.h
|
||||
${QET_DIR}/sources/shortcutmanager.cpp
|
||||
${QET_DIR}/sources/shortcutmanager.h
|
||||
${QET_DIR}/sources/shortcutbarsettings.cpp
|
||||
${QET_DIR}/sources/shortcutbarsettings.h
|
||||
${QET_DIR}/sources/titleblockcell.cpp
|
||||
${QET_DIR}/sources/titleblockcell.h
|
||||
${QET_DIR}/sources/titleblockproperties.cpp
|
||||
@@ -799,6 +801,8 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/ui/configpage/guidespropertieswidget.h
|
||||
${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.cpp
|
||||
${QET_DIR}/sources/ui/configpage/shortcutsconfigpage.h
|
||||
${QET_DIR}/sources/ui/configpage/shortcutbarconfigpage.cpp
|
||||
${QET_DIR}/sources/ui/configpage/shortcutbarconfigpage.h
|
||||
|
||||
${QET_DIR}/sources/undocommand/addelementtextcommand.cpp
|
||||
${QET_DIR}/sources/undocommand/addelementtextcommand.h
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
|
||||
#include "elementscollectionwidget.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QGuiApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QToolButton>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
@@ -58,6 +61,13 @@ ElementPickerPopup::ElementPickerPopup(ElementsCollectionWidget *source,
|
||||
layout->setContentsMargins(6, 6, 6, 6);
|
||||
layout->setSpacing(4);
|
||||
|
||||
//Command row, shown when the picker is opened as the shortcut bar
|
||||
m_commands = new QWidget(this);
|
||||
m_commands_layout = new QHBoxLayout(m_commands);
|
||||
m_commands_layout->setContentsMargins(0, 0, 0, 0);
|
||||
m_commands_layout->setSpacing(2);
|
||||
m_commands->hide();
|
||||
|
||||
m_search = new QLineEdit(this);
|
||||
m_search->setPlaceholderText(tr("Rechercher un élément…"));
|
||||
m_search->setClearButtonEnabled(true);
|
||||
@@ -73,6 +83,7 @@ ElementPickerPopup::ElementPickerPopup(ElementsCollectionWidget *source,
|
||||
m_hint = new QLabel(tr("Entrée pour insérer · Échap pour fermer"), this);
|
||||
m_hint->setEnabled(false);
|
||||
|
||||
layout->addWidget(m_commands);
|
||||
layout->addWidget(m_search);
|
||||
layout->addWidget(m_view);
|
||||
layout->addWidget(m_hint);
|
||||
@@ -97,8 +108,10 @@ ElementPickerPopup::ElementPickerPopup(ElementsCollectionWidget *source,
|
||||
focused and any previous query cleared.
|
||||
@param global_pos
|
||||
*/
|
||||
void ElementPickerPopup::popUpAt(const QPoint &global_pos)
|
||||
void ElementPickerPopup::popUpAt(const QPoint &global_pos,
|
||||
const QList<QAction *> &commands)
|
||||
{
|
||||
setCommands(commands);
|
||||
m_search->clear();
|
||||
m_model->clear();
|
||||
showPalette();
|
||||
@@ -121,6 +134,53 @@ void ElementPickerPopup::popUpAt(const QPoint &global_pos)
|
||||
m_search->setFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::setCommands
|
||||
Show @a commands as a row of buttons above the search field, or hide the
|
||||
row when there are none. A disabled command keeps its place, greyed out,
|
||||
so the row looks the same every time for a given selection.
|
||||
|
||||
Clicking a button closes the picker first, then triggers the action: a
|
||||
command such as "add a line" starts a mode on the folio, which needs the
|
||||
focus the popup holds.
|
||||
@param commands
|
||||
*/
|
||||
void ElementPickerPopup::setCommands(const QList<QAction *> &commands)
|
||||
{
|
||||
while (QLayoutItem *item = m_commands_layout->takeAt(0)) {
|
||||
delete item->widget();
|
||||
delete item;
|
||||
}
|
||||
|
||||
for (QAction *action : commands)
|
||||
{
|
||||
auto *button = new QToolButton(m_commands);
|
||||
button->setAutoRaise(true);
|
||||
button->setIconSize(QSize(24, 24));
|
||||
const QString text = action->text().remove(QLatin1Char('&'));
|
||||
if (action->icon().isNull()) {
|
||||
button->setText(text);
|
||||
button->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||||
} else {
|
||||
button->setIcon(action->icon());
|
||||
}
|
||||
const QKeySequence key = action->shortcut();
|
||||
button->setToolTip(key.isEmpty()
|
||||
? text
|
||||
: QStringLiteral("%1 (%2)").arg(
|
||||
text, key.toString(QKeySequence::NativeText)));
|
||||
button->setEnabled(action->isEnabled());
|
||||
button->setFocusPolicy(Qt::NoFocus);
|
||||
connect(button, &QToolButton::clicked, this, [this, action]() {
|
||||
hide();
|
||||
action->trigger();
|
||||
});
|
||||
m_commands_layout->addWidget(button);
|
||||
}
|
||||
m_commands_layout->addStretch();
|
||||
m_commands->setVisible(!commands.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::runSearch
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,8 @@ class QLineEdit;
|
||||
class QListView;
|
||||
class QStandardItemModel;
|
||||
class QLabel;
|
||||
class QAction;
|
||||
class QHBoxLayout;
|
||||
|
||||
/**
|
||||
@brief A cursor-anchored element picker.
|
||||
@@ -49,7 +51,8 @@ class ElementPickerPopup : public QFrame
|
||||
explicit ElementPickerPopup(ElementsCollectionWidget *source,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
void popUpAt(const QPoint &global_pos);
|
||||
void popUpAt(const QPoint &global_pos,
|
||||
const QList<QAction *> &commands = {});
|
||||
|
||||
signals:
|
||||
/// Emitted when the user picks an element; the popup has closed
|
||||
@@ -62,6 +65,7 @@ class ElementPickerPopup : public QFrame
|
||||
void runSearch();
|
||||
void chooseCurrent();
|
||||
void showPalette();
|
||||
void setCommands(const QList<QAction *> &commands);
|
||||
int loadPaletteDir(const QString &dir_path, const QString &prefix,
|
||||
int depth);
|
||||
|
||||
@@ -70,6 +74,8 @@ class ElementPickerPopup : public QFrame
|
||||
QListView *m_view = nullptr;
|
||||
QStandardItemModel *m_model = nullptr;
|
||||
QLabel *m_hint = nullptr;
|
||||
QWidget *m_commands = nullptr;
|
||||
QHBoxLayout *m_commands_layout = nullptr;
|
||||
bool m_palette_mode = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "ui/aboutqetdialog.h"
|
||||
#include "ui/configpage/generalconfigurationpage.h"
|
||||
#include "ui/configpage/shortcutsconfigpage.h"
|
||||
#include "ui/configpage/shortcutbarconfigpage.h"
|
||||
#include "machine_info.h"
|
||||
#include "TerminalStrip/ui/terminalstripeditorwindow.h"
|
||||
#include "qetversion.h"
|
||||
@@ -2213,6 +2214,7 @@ void QETApp::configureQET()
|
||||
cd.addPage(new ExportConfigPage());
|
||||
cd.addPage(new PrintConfigPage());
|
||||
cd.addPage(new ShortcutsConfigPage());
|
||||
cd.addPage(new ShortcutBarConfigPage());
|
||||
#ifdef QET_SPACEMOUSE_SUPPORT
|
||||
cd.addPage(new SpaceMouseConfigPage());
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <QToolButton>
|
||||
#include "ElementsCollection/elementscollectionwidget.h"
|
||||
#include "ElementsCollection/elementpickerpopup.h"
|
||||
#include "shortcutbarsettings.h"
|
||||
#include "qetgraphicsitem/conductor.h"
|
||||
#include "QWidgetAnimation/qwidgetanimation.h"
|
||||
#include "autoNum/ui/autonumberingdockwidget.h"
|
||||
#include "conductornumexport.h"
|
||||
@@ -76,6 +78,7 @@
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QTimer>
|
||||
#include <algorithm>
|
||||
#ifdef BUILD_WITHOUT_KF
|
||||
# include "ui/nokde/kautosavefile.h"
|
||||
#else
|
||||
@@ -840,6 +843,20 @@ void QETDiagramEditor::setUpActions()
|
||||
this, &QETDiagramEditor::showElementPicker);
|
||||
addAction(m_show_element_picker);
|
||||
|
||||
//The picker with a row of commands above it, chosen by what is
|
||||
//selected -- the SolidWorks "S" shortcut bar. S is unbound in this
|
||||
//editor.
|
||||
m_show_shortcut_bar = new QAction(tr("Barre de raccourcis"), this);
|
||||
m_show_shortcut_bar->setStatusTip(
|
||||
tr("Ouvre à la position du curseur les commandes utiles pour la sélection, et le sélecteur d'éléments",
|
||||
"status bar tip"));
|
||||
ShortcutManager::instance().registerAction(
|
||||
m_show_shortcut_bar, "diagrameditor.show_shortcut_bar",
|
||||
tr("Éditeur de schémas"), Qt::Key_S);
|
||||
connect(m_show_shortcut_bar, &QAction::triggered,
|
||||
this, &QETDiagramEditor::showShortcutBar);
|
||||
addAction(m_show_shortcut_bar);
|
||||
|
||||
m_delete_selection->setStatusTip( tr("Enlève les éléments sélectionnés du folio", "status bar tip"));
|
||||
m_rotate_selection->setStatusTip( tr("Pivote les éléments et textes sélectionnés", "status bar tip"));
|
||||
m_rotate_group_selection->setStatusTip( tr("Pivote la sélection comme un groupe autour de son centre, au lieu de chaque élément sur place", "status bar tip"));
|
||||
@@ -971,6 +988,13 @@ void QETDiagramEditor::setUpActions()
|
||||
add_path->setCheckable(true);
|
||||
|
||||
connect(&m_add_item_actions_group, &QActionGroup::triggered, this, &QETDiagramEditor::addItemGroupTriggered);
|
||||
//No default key, but an id: they can then be bound in the Shortcuts
|
||||
//page and placed on the shortcut bar, like every other command.
|
||||
for (QAction *action : m_add_item_actions_group.actions()) {
|
||||
ShortcutManager::instance().registerAction(
|
||||
action, "diagrameditor.add_" + action->data().toString(),
|
||||
tr("Éditeur de schémas"), QKeySequence());
|
||||
}
|
||||
|
||||
//Depth action
|
||||
m_depth_action_group = QET::depthActionGroup(this);
|
||||
@@ -1126,6 +1150,7 @@ void QETDiagramEditor::setUpMenu()
|
||||
menu_edition -> addAction(m_configure_duplicate);
|
||||
menu_edition -> addAction(m_insert_last_element);
|
||||
menu_edition -> addAction(m_show_element_picker);
|
||||
menu_edition -> addAction(m_show_shortcut_bar);
|
||||
menu_edition -> addSeparator();
|
||||
//The same actions the "Ajouter" toolbar holds. They were toolbar-only,
|
||||
//which left them unreachable for anyone working without a mouse: a
|
||||
@@ -2035,6 +2060,7 @@ void QETDiagramEditor::slot_updateActions()
|
||||
m_add_item_actions_group. setEnabled(editable_project);
|
||||
m_insert_last_element-> setEnabled(opened_diagram && editable_project && !m_last_inserted_element.isNull());
|
||||
m_show_element_picker-> setEnabled(opened_diagram && editable_project);
|
||||
m_show_shortcut_bar-> setEnabled(opened_diagram && editable_project);
|
||||
m_row_column_actions_group. setEnabled(editable_project);
|
||||
m_background_color_button-> setEnabled(opened_diagram);
|
||||
m_draw_grid-> setEnabled(opened_diagram);
|
||||
@@ -3087,18 +3113,12 @@ void QETDiagramEditor::insertLastElement()
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETDiagramEditor::showElementPicker
|
||||
Open the element picker where the mouse is.
|
||||
|
||||
Built lazily: most sessions of the diagram editor never open it, and it
|
||||
holds a list view and a model of its own.
|
||||
@brief QETDiagramEditor::elementPicker
|
||||
@return the element picker, built on first use: most sessions never open
|
||||
it, and it holds a list view and a model of its own.
|
||||
*/
|
||||
void QETDiagramEditor::showElementPicker()
|
||||
ElementPickerPopup *QETDiagramEditor::elementPicker()
|
||||
{
|
||||
if (!currentDiagramView()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_element_picker)
|
||||
{
|
||||
m_element_picker = new ElementPickerPopup(m_element_collection_widget,
|
||||
@@ -3106,7 +3126,56 @@ void QETDiagramEditor::showElementPicker()
|
||||
connect(m_element_picker, &ElementPickerPopup::elementChosen,
|
||||
this, &QETDiagramEditor::insertElementFromCollection);
|
||||
}
|
||||
m_element_picker->popUpAt(QCursor::pos());
|
||||
return m_element_picker;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETDiagramEditor::showElementPicker
|
||||
Open the element picker where the mouse is.
|
||||
*/
|
||||
void QETDiagramEditor::showElementPicker()
|
||||
{
|
||||
if (!currentDiagramView()) {
|
||||
return;
|
||||
}
|
||||
|
||||
elementPicker()->popUpAt(QCursor::pos());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETDiagramEditor::showShortcutBar
|
||||
Open the element picker where the mouse is, with a row of commands above
|
||||
it chosen by what is selected: the folio's commands with nothing
|
||||
selected, conductor commands when only conductors are, and selection
|
||||
commands otherwise. The commands are ShortcutManager ids, listed in
|
||||
ShortcutBarSettings and editable in the configuration dialog.
|
||||
*/
|
||||
void QETDiagramEditor::showShortcutBar()
|
||||
{
|
||||
DiagramView *dv = currentDiagramView();
|
||||
if (!dv) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QList<QGraphicsItem *> selection = dv->diagram()->selectedItems();
|
||||
ShortcutBarSettings::Context context = ShortcutBarSettings::Canvas;
|
||||
if (!selection.isEmpty())
|
||||
{
|
||||
const bool only_conductors = std::all_of(
|
||||
selection.cbegin(), selection.cend(),
|
||||
[](QGraphicsItem *item) { return item->type() == Conductor::Type; });
|
||||
context = only_conductors ? ShortcutBarSettings::Conductor
|
||||
: ShortcutBarSettings::Selection;
|
||||
}
|
||||
|
||||
QList<QAction *> commands;
|
||||
for (const QString &id : ShortcutBarSettings::ids(context)) {
|
||||
if (QAction *action = ShortcutManager::instance().action(id, this)) {
|
||||
commands << action;
|
||||
}
|
||||
}
|
||||
|
||||
elementPicker()->popUpAt(QCursor::pos(), commands);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -132,6 +132,7 @@ class QETDiagramEditor : public QETMainWindow
|
||||
void insertLastElement();
|
||||
void rememberPlacedElement(const ElementsLocation &location);
|
||||
void showElementPicker();
|
||||
void showShortcutBar();
|
||||
void generateTerminalBlock();
|
||||
void setWindowedMode();
|
||||
void setTabbedMode();
|
||||
@@ -273,7 +274,9 @@ class QETDiagramEditor : public QETMainWindow
|
||||
*m_qdw_elmt_collection,
|
||||
*qdw_undo; /// Dock for the undo list
|
||||
|
||||
ElementPickerPopup *elementPicker();
|
||||
QAction *m_show_element_picker = nullptr;
|
||||
QAction *m_show_shortcut_bar = nullptr;
|
||||
ElementPickerPopup *m_element_picker = nullptr; ///< Built on first use
|
||||
ElementsCollectionWidget *m_element_collection_widget;
|
||||
/// Last element placed from the collection, for "insert last"
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
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 "shortcutbarsettings.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QSettings>
|
||||
|
||||
namespace {
|
||||
QString settingsKey(ShortcutBarSettings::Context context)
|
||||
{
|
||||
switch (context)
|
||||
{
|
||||
case ShortcutBarSettings::Canvas:
|
||||
return QStringLiteral("diagrameditor/shortcut_bar/canvas");
|
||||
case ShortcutBarSettings::Selection:
|
||||
return QStringLiteral("diagrameditor/shortcut_bar/selection");
|
||||
case ShortcutBarSettings::Conductor:
|
||||
return QStringLiteral("diagrameditor/shortcut_bar/conductor");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@return every context, in the order they are shown to the user
|
||||
*/
|
||||
QList<ShortcutBarSettings::Context> ShortcutBarSettings::contexts()
|
||||
{
|
||||
return {Canvas, Selection, Conductor};
|
||||
}
|
||||
|
||||
/**
|
||||
@return the name of @a context, for the configuration page
|
||||
*/
|
||||
QString ShortcutBarSettings::title(Context context)
|
||||
{
|
||||
switch (context)
|
||||
{
|
||||
case Canvas:
|
||||
return QCoreApplication::translate("ShortcutBarSettings", "Folio, rien de sélectionné");
|
||||
case Selection:
|
||||
return QCoreApplication::translate("ShortcutBarSettings", "Éléments sélectionnés");
|
||||
case Conductor:
|
||||
return QCoreApplication::translate("ShortcutBarSettings", "Conducteurs sélectionnés");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
/**
|
||||
@return the ids to show for @a context: the user's list if they saved
|
||||
one, the defaults otherwise. A saved empty list stays empty.
|
||||
*/
|
||||
QStringList ShortcutBarSettings::ids(Context context)
|
||||
{
|
||||
QSettings settings;
|
||||
const QString key = settingsKey(context);
|
||||
if (!settings.contains(key)) {
|
||||
return defaultIds(context);
|
||||
}
|
||||
return settings.value(key).toStringList();
|
||||
}
|
||||
|
||||
/**
|
||||
@return the commands a new user sees for @a context
|
||||
*/
|
||||
QStringList ShortcutBarSettings::defaultIds(Context context)
|
||||
{
|
||||
switch (context)
|
||||
{
|
||||
case Canvas:
|
||||
return {QStringLiteral("diagrameditor.insert_last_element"),
|
||||
QStringLiteral("diagrameditor.show_element_picker"),
|
||||
QStringLiteral("diagrameditor.add_text"),
|
||||
QStringLiteral("diagrameditor.add_line"),
|
||||
QStringLiteral("diagrameditor.add_rectangle"),
|
||||
QStringLiteral("diagrameditor.add_terminal_strip"),
|
||||
QStringLiteral("diagrameditor.paste"),
|
||||
QStringLiteral("diagrameditor.edit_diagram_properties")};
|
||||
case Selection:
|
||||
return {QStringLiteral("diagrameditor.rotate_selection"),
|
||||
QStringLiteral("diagrameditor.rotate_texts"),
|
||||
QStringLiteral("diagrameditor.edit_selection"),
|
||||
QStringLiteral("diagrameditor.copy"),
|
||||
QStringLiteral("diagrameditor.cut"),
|
||||
QStringLiteral("diagrameditor.delete_selection")};
|
||||
case Conductor:
|
||||
return {QStringLiteral("diagrameditor.conductor_reset"),
|
||||
QStringLiteral("diagrameditor.edit_selection"),
|
||||
QStringLiteral("diagrameditor.delete_selection")};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ShortcutBarSettings::setIds
|
||||
Save @a ids for @a context. Saving the defaults removes the key, so a
|
||||
later change of defaults still reaches this user.
|
||||
*/
|
||||
void ShortcutBarSettings::setIds(Context context, const QStringList &ids)
|
||||
{
|
||||
QSettings settings;
|
||||
if (ids == defaultIds(context)) {
|
||||
settings.remove(settingsKey(context));
|
||||
} else {
|
||||
settings.setValue(settingsKey(context), ids);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 SHORTCUTBARSETTINGS_H
|
||||
#define SHORTCUTBARSETTINGS_H
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
@brief The commands shown on the diagram editor's shortcut bar.
|
||||
|
||||
The bar opens at the cursor and shows a different row of commands
|
||||
depending on what is selected. Each row is a list of ShortcutManager ids,
|
||||
so any registered command can go on it and the bar needs no command list
|
||||
of its own. Stored in QSettings, one key per context; a context the user
|
||||
never changed uses the defaults below.
|
||||
*/
|
||||
class ShortcutBarSettings
|
||||
{
|
||||
public:
|
||||
enum Context {
|
||||
Canvas, ///< nothing selected
|
||||
Selection, ///< elements, texts or shapes selected
|
||||
Conductor ///< only conductors selected
|
||||
};
|
||||
|
||||
static QList<Context> contexts();
|
||||
static QString title(Context context);
|
||||
static QStringList ids(Context context);
|
||||
static QStringList defaultIds(Context context);
|
||||
static void setIds(Context context, const QStringList &ids);
|
||||
};
|
||||
|
||||
#endif // SHORTCUTBARSETTINGS_H
|
||||
@@ -197,3 +197,36 @@ bool ShortcutManager::trigger(const QString &id) const
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@return the QAction registered under @a id that belongs to @a owner --
|
||||
that is, has @a owner among its ancestors -- or nullptr. Several windows
|
||||
of the same kind each register their own action under one id, so a
|
||||
window asking for "its" action has to say which window it is.
|
||||
@param id
|
||||
@param owner : the window, or nullptr for the first live action
|
||||
*/
|
||||
QAction *ShortcutManager::action(const QString &id, const QObject *owner) const
|
||||
{
|
||||
auto it = m_entries.find(id);
|
||||
if (it == m_entries.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const QPointer<QObject> &target : qAsConst(it->targets))
|
||||
{
|
||||
auto *action = qobject_cast<QAction *>(target.data());
|
||||
if (!action) {
|
||||
continue;
|
||||
}
|
||||
if (!owner) {
|
||||
return action;
|
||||
}
|
||||
for (const QObject *o = action->parent(); o; o = o->parent()) {
|
||||
if (o == owner) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QStringList>
|
||||
|
||||
class QObject;
|
||||
class QAction;
|
||||
|
||||
/**
|
||||
@brief The ShortcutManager class
|
||||
@@ -84,6 +85,7 @@ class ShortcutManager
|
||||
/// multi-window case, not a guaranteed-correct dispatch.
|
||||
/// @return whether a live target was found and triggered.
|
||||
bool trigger(const QString &id) const;
|
||||
QAction *action(const QString &id, const QObject *owner) const;
|
||||
|
||||
private:
|
||||
ShortcutManager() = default;
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
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 "shortcutbarconfigpage.h"
|
||||
|
||||
#include "../../qeticons.h"
|
||||
#include "../../shortcutmanager.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QComboBox>
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace {
|
||||
/// The bar can hold any diagram editor command, except the one that
|
||||
/// opens it.
|
||||
bool offerable(const QString &id)
|
||||
{
|
||||
return id.startsWith(QLatin1String("diagrameditor."))
|
||||
&& id != QLatin1String("diagrameditor.show_shortcut_bar");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ShortcutBarConfigPage::ShortcutBarConfigPage
|
||||
@param parent
|
||||
*/
|
||||
ShortcutBarConfigPage::ShortcutBarConfigPage(QWidget *parent) :
|
||||
ConfigPage(parent)
|
||||
{
|
||||
for (const ShortcutManager::ShortcutInfo &info :
|
||||
ShortcutManager::instance().allShortcuts()) {
|
||||
if (offerable(info.id)) {
|
||||
m_descriptions.insert(info.id, info.description);
|
||||
}
|
||||
}
|
||||
for (const ShortcutBarSettings::Context c : ShortcutBarSettings::contexts()) {
|
||||
m_pending.insert(c, ShortcutBarSettings::ids(c));
|
||||
}
|
||||
|
||||
auto *explanation = new QLabel(
|
||||
tr("La barre de raccourcis s'ouvre à la position du curseur "
|
||||
"(touche S par défaut). Elle montre les commandes choisies "
|
||||
"ici selon ce qui est sélectionné, puis le sélecteur "
|
||||
"d'éléments."), this);
|
||||
explanation->setWordWrap(true);
|
||||
|
||||
m_context = new QComboBox(this);
|
||||
for (const ShortcutBarSettings::Context c : ShortcutBarSettings::contexts()) {
|
||||
m_context->addItem(ShortcutBarSettings::title(c), c);
|
||||
}
|
||||
|
||||
m_available = new QListWidget(this);
|
||||
m_available->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
m_available->setSortingEnabled(true);
|
||||
m_chosen = new QListWidget(this);
|
||||
m_chosen->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
m_chosen->setDragDropMode(QAbstractItemView::InternalMove);
|
||||
|
||||
auto *add = new QPushButton(tr("Ajouter →"), this);
|
||||
auto *remove = new QPushButton(tr("← Retirer"), this);
|
||||
auto *up = new QPushButton(tr("Monter"), this);
|
||||
auto *down = new QPushButton(tr("Descendre"), this);
|
||||
auto *reset = new QPushButton(tr("Valeurs par défaut"), this);
|
||||
|
||||
auto *buttons = new QVBoxLayout();
|
||||
buttons->addStretch();
|
||||
buttons->addWidget(add);
|
||||
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_available, 1, 0);
|
||||
grid->addLayout(buttons, 1, 1);
|
||||
grid->addWidget(m_chosen, 1, 2);
|
||||
|
||||
auto *context_row = new QHBoxLayout();
|
||||
context_row->addWidget(new QLabel(tr("Contexte :"), this));
|
||||
context_row->addWidget(m_context, 1);
|
||||
context_row->addWidget(reset);
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->addWidget(explanation);
|
||||
layout->addLayout(context_row);
|
||||
layout->addLayout(grid);
|
||||
|
||||
connect(m_context, qOverload<int>(&QComboBox::currentIndexChanged), this, [this]() {
|
||||
storeContext();
|
||||
showContext();
|
||||
});
|
||||
connect(add, &QPushButton::clicked, this, &ShortcutBarConfigPage::addSelected);
|
||||
connect(remove, &QPushButton::clicked, this, &ShortcutBarConfigPage::removeSelected);
|
||||
connect(up, &QPushButton::clicked, this, [this]() { moveSelected(-1); });
|
||||
connect(down, &QPushButton::clicked, this, [this]() { moveSelected(1); });
|
||||
connect(reset, &QPushButton::clicked, this, &ShortcutBarConfigPage::resetContext);
|
||||
connect(m_available, &QListWidget::itemDoubleClicked, this, &ShortcutBarConfigPage::addSelected);
|
||||
connect(m_chosen, &QListWidget::itemDoubleClicked, this, &ShortcutBarConfigPage::removeSelected);
|
||||
|
||||
showContext();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ShortcutBarConfigPage::applyConf
|
||||
Save every context's list.
|
||||
*/
|
||||
void ShortcutBarConfigPage::applyConf()
|
||||
{
|
||||
storeContext();
|
||||
for (const ShortcutBarSettings::Context c : ShortcutBarSettings::contexts()) {
|
||||
ShortcutBarSettings::setIds(c, m_pending.value(c));
|
||||
}
|
||||
}
|
||||
|
||||
QString ShortcutBarConfigPage::title() const
|
||||
{
|
||||
return tr("Barre de raccourcis", "configuration page title");
|
||||
}
|
||||
|
||||
QIcon ShortcutBarConfigPage::icon() const
|
||||
{
|
||||
return QET::Icons::ConfigureShortcuts;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ShortcutBarConfigPage::showContext
|
||||
Fill both lists for the context chosen in the combo box.
|
||||
*/
|
||||
void ShortcutBarConfigPage::showContext()
|
||||
{
|
||||
m_shown = static_cast<ShortcutBarSettings::Context>(
|
||||
m_context->currentData().toInt());
|
||||
const QStringList chosen = m_pending.value(m_shown);
|
||||
|
||||
m_available->clear();
|
||||
m_chosen->clear();
|
||||
for (const QString &id : chosen) {
|
||||
appendItem(m_chosen, id);
|
||||
}
|
||||
for (auto it = m_descriptions.cbegin(); it != m_descriptions.cend(); ++it) {
|
||||
if (!chosen.contains(it.key())) {
|
||||
appendItem(m_available, it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ShortcutBarConfigPage::storeContext
|
||||
Keep the shown context's list, in the order on screen.
|
||||
*/
|
||||
void ShortcutBarConfigPage::storeContext()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void ShortcutBarConfigPage::addSelected()
|
||||
{
|
||||
for (QListWidgetItem *item : m_available->selectedItems()) {
|
||||
m_chosen->addItem(m_available->takeItem(m_available->row(item)));
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutBarConfigPage::removeSelected()
|
||||
{
|
||||
for (QListWidgetItem *item : m_chosen->selectedItems()) {
|
||||
m_available->addItem(m_chosen->takeItem(m_chosen->row(item)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ShortcutBarConfigPage::moveSelected
|
||||
Move the selected command @a step rows, keeping it selected.
|
||||
*/
|
||||
void ShortcutBarConfigPage::moveSelected(int step)
|
||||
{
|
||||
const int row = m_chosen->currentRow();
|
||||
const int target = row + step;
|
||||
if (row < 0 || target < 0 || target >= m_chosen->count()) {
|
||||
return;
|
||||
}
|
||||
QListWidgetItem *item = m_chosen->takeItem(row);
|
||||
m_chosen->insertItem(target, item);
|
||||
m_chosen->setCurrentRow(target);
|
||||
}
|
||||
|
||||
void ShortcutBarConfigPage::resetContext()
|
||||
{
|
||||
m_pending.insert(m_shown, ShortcutBarSettings::defaultIds(m_shown));
|
||||
showContext();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ShortcutBarConfigPage::appendItem
|
||||
Add @a id to @a list with the command's text and icon. An id no live
|
||||
action carries (a command from a build without it) is still listed, by
|
||||
its id, so saving does not silently drop it.
|
||||
*/
|
||||
void ShortcutBarConfigPage::appendItem(QListWidget *list, const QString &id)
|
||||
{
|
||||
QAction *action = ShortcutManager::instance().action(id, nullptr);
|
||||
const QString text = m_descriptions.value(id, id);
|
||||
auto *item = new QListWidgetItem(action ? action->icon() : QIcon(), text);
|
||||
item->setData(Qt::UserRole, id);
|
||||
list->addItem(item);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 SHORTCUTBARCONFIGPAGE_H
|
||||
#define SHORTCUTBARCONFIGPAGE_H
|
||||
|
||||
#include "configpage.h"
|
||||
#include "../../shortcutbarsettings.h"
|
||||
|
||||
#include <QHash>
|
||||
|
||||
class QComboBox;
|
||||
class QListWidget;
|
||||
|
||||
/**
|
||||
@brief The ShortcutBarConfigPage class
|
||||
Choose which commands the diagram editor's shortcut bar shows, and in
|
||||
what order, for each selection context. Any command registered with
|
||||
ShortcutManager by the diagram editor can be added. Changes are kept
|
||||
per context while the dialog is open and saved by applyConf().
|
||||
*/
|
||||
class ShortcutBarConfigPage : public ConfigPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ShortcutBarConfigPage(QWidget *parent = nullptr);
|
||||
|
||||
void applyConf() override;
|
||||
QString title() const override;
|
||||
QIcon icon() const override;
|
||||
|
||||
private:
|
||||
void showContext();
|
||||
void storeContext();
|
||||
void addSelected();
|
||||
void removeSelected();
|
||||
void moveSelected(int step);
|
||||
void resetContext();
|
||||
void appendItem(QListWidget *list, const QString &id);
|
||||
|
||||
QComboBox *m_context;
|
||||
QListWidget *m_available;
|
||||
QListWidget *m_chosen;
|
||||
ShortcutBarSettings::Context m_shown = ShortcutBarSettings::Canvas;
|
||||
QHash<int, QStringList> m_pending;
|
||||
QHash<QString, QString> m_descriptions;
|
||||
};
|
||||
|
||||
#endif // SHORTCUTBARCONFIGPAGE_H
|
||||
Reference in New Issue
Block a user