mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-18 03:54:14 +02:00
Merge pull request #1054 from ispyisail/feature/shortcut-bar-customise
Add customising the shortcut bar in place
This commit is contained in:
@@ -22,7 +22,11 @@
|
||||
#include <QAction>
|
||||
#include <QGuiApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QListWidget>
|
||||
#include <QMenu>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
#include <QCloseEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
@@ -36,6 +40,7 @@
|
||||
#include <QSettings>
|
||||
|
||||
#include "../qetapp.h"
|
||||
#include "../shortcutmanager.h"
|
||||
#include "elementslocation.h"
|
||||
|
||||
//The palette is read from disk each time the picker opens. It is meant as
|
||||
@@ -67,6 +72,90 @@ ElementPickerPopup::ElementPickerPopup(ElementsCollectionWidget *source,
|
||||
m_commands_layout->setContentsMargins(0, 0, 0, 0);
|
||||
m_commands_layout->setSpacing(2);
|
||||
m_commands->hide();
|
||||
//SolidWorks: right-click the bar to customise it
|
||||
m_commands->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_commands, &QWidget::customContextMenuRequested, this, [this](const QPoint &pos) {
|
||||
QMenu menu;
|
||||
menu.addAction(tr("Personnaliser la barre…"), this, &ElementPickerPopup::startCustomising);
|
||||
menu.exec(m_commands->mapToGlobal(pos));
|
||||
});
|
||||
|
||||
//Customising, shown instead of everything else. Two lists that
|
||||
//commands are dragged between: the bar, left to right, and the
|
||||
//commands that are not on it.
|
||||
m_editor = new QWidget(this);
|
||||
auto *editor_layout = new QVBoxLayout(m_editor);
|
||||
editor_layout->setContentsMargins(0, 0, 0, 0);
|
||||
auto *help = new QLabel(
|
||||
tr("Glissez les commandes dans la barre, hors de la barre, ou "
|
||||
"d'une place à l'autre. Un double-clic fait passer une commande "
|
||||
"d'une liste à l'autre."), m_editor);
|
||||
help->setWordWrap(true);
|
||||
m_edit_row = new QListWidget(m_editor);
|
||||
m_edit_row->setFlow(QListView::LeftToRight);
|
||||
m_edit_row->setWrapping(false);
|
||||
m_edit_row->setIconSize(QSize(24, 24));
|
||||
m_edit_row->setFixedHeight(44);
|
||||
m_edit_available = new QListWidget(m_editor);
|
||||
m_edit_available->setIconSize(QSize(20, 20));
|
||||
m_edit_available->setMinimumHeight(220);
|
||||
for (QListWidget *list : {m_edit_row, m_edit_available}) {
|
||||
list->setDragDropMode(QAbstractItemView::DragDrop);
|
||||
list->setDefaultDropAction(Qt::MoveAction);
|
||||
list->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
}
|
||||
//A drop copies the item as it was in the other list, so a command
|
||||
//dragged onto the bar would keep its text and one dragged off would
|
||||
//have none. Set the text for the list it has landed in.
|
||||
//Deferred: a drop inserts the row first and copies the item's data
|
||||
//into it afterwards.
|
||||
auto relabel = [this](QListWidget *list, bool icon_only) {
|
||||
return [this, list, icon_only]() {
|
||||
QTimer::singleShot(0, this, [list, icon_only]() {
|
||||
for (int i = 0 ; i < list->count() ; ++i) {
|
||||
QListWidgetItem *item = list->item(i);
|
||||
item->setText(icon_only && !item->icon().isNull()
|
||||
? QString() : item->toolTip());
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
connect(m_edit_row->model(), &QAbstractItemModel::rowsInserted,
|
||||
this, relabel(m_edit_row, true));
|
||||
connect(m_edit_available->model(), &QAbstractItemModel::rowsInserted,
|
||||
this, relabel(m_edit_available, false));
|
||||
|
||||
connect(m_edit_row, &QListWidget::itemDoubleClicked, this, [this](QListWidgetItem *item) {
|
||||
const QString id = item->data(Qt::UserRole).toString();
|
||||
delete item;
|
||||
m_edit_available->addItem(commandItem(id, false));
|
||||
});
|
||||
connect(m_edit_available, &QListWidget::itemDoubleClicked, this, [this](QListWidgetItem *item) {
|
||||
const QString id = item->data(Qt::UserRole).toString();
|
||||
delete item;
|
||||
m_edit_row->addItem(commandItem(id, true));
|
||||
});
|
||||
auto *defaults = new QPushButton(tr("Valeurs par défaut"), m_editor);
|
||||
auto *cancel = new QPushButton(tr("Annuler"), m_editor);
|
||||
auto *done = new QPushButton(tr("Terminé"), m_editor);
|
||||
done->setDefault(true);
|
||||
connect(defaults, &QPushButton::clicked, this, [this]() {
|
||||
fillCustomising(ShortcutBarSettings::defaultIds(m_context));
|
||||
});
|
||||
connect(cancel, &QPushButton::clicked, this, [this]() { finishCustomising(false); });
|
||||
connect(done, &QPushButton::clicked, this, [this]() { finishCustomising(true); });
|
||||
auto *editor_buttons = new QHBoxLayout();
|
||||
editor_buttons->addWidget(defaults);
|
||||
editor_buttons->addStretch();
|
||||
editor_buttons->addWidget(cancel);
|
||||
editor_buttons->addWidget(done);
|
||||
editor_layout->addWidget(help);
|
||||
editor_layout->addWidget(new QLabel(tr("Dans la barre :"), m_editor));
|
||||
editor_layout->addWidget(m_edit_row);
|
||||
editor_layout->addWidget(new QLabel(tr("Autres commandes :"), m_editor));
|
||||
editor_layout->addWidget(m_edit_available);
|
||||
editor_layout->addLayout(editor_buttons);
|
||||
m_editor->hide();
|
||||
|
||||
m_search = new QLineEdit(this);
|
||||
m_search->setPlaceholderText(tr("Rechercher un élément…"));
|
||||
@@ -87,6 +176,7 @@ ElementPickerPopup::ElementPickerPopup(ElementsCollectionWidget *source,
|
||||
layout->addWidget(m_search);
|
||||
layout->addWidget(m_view);
|
||||
layout->addWidget(m_hint);
|
||||
layout->addWidget(m_editor);
|
||||
|
||||
//Search as you type, after a short idle: shorter than the dock's
|
||||
//500 ms, since the popup is opened and closed in a few seconds, but
|
||||
@@ -108,10 +198,51 @@ ElementPickerPopup::ElementPickerPopup(ElementsCollectionWidget *source,
|
||||
focused and any previous query cleared.
|
||||
@param global_pos
|
||||
*/
|
||||
void ElementPickerPopup::popUpAt(const QPoint &global_pos,
|
||||
const QList<QAction *> &commands)
|
||||
void ElementPickerPopup::popUpAt(const QPoint &global_pos)
|
||||
{
|
||||
m_bar_mode = false;
|
||||
setCommands({});
|
||||
show(global_pos);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::popUpShortcutBar
|
||||
Show the picker at @a global_pos with the shortcut bar's commands for
|
||||
@a context above it.
|
||||
@param global_pos
|
||||
@param context
|
||||
*/
|
||||
void ElementPickerPopup::popUpShortcutBar(const QPoint &global_pos,
|
||||
ShortcutBarSettings::Context context)
|
||||
{
|
||||
m_bar_mode = true;
|
||||
m_context = context;
|
||||
QList<QAction *> commands;
|
||||
for (const QString &id : ShortcutBarSettings::ids(context)) {
|
||||
if (QAction *action = commandAction(id)) {
|
||||
commands << action;
|
||||
}
|
||||
}
|
||||
setCommands(commands);
|
||||
show(global_pos);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::commandAction
|
||||
@return the action registered under @a id by the window this popup
|
||||
belongs to, or nullptr
|
||||
*/
|
||||
QAction *ElementPickerPopup::commandAction(const QString &id) const
|
||||
{
|
||||
return ShortcutManager::instance().action(id, parentWidget());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::show
|
||||
Reset the search and show at @a global_pos, kept on screen.
|
||||
*/
|
||||
void ElementPickerPopup::show(const QPoint &global_pos)
|
||||
{
|
||||
m_search->clear();
|
||||
m_model->clear();
|
||||
showPalette();
|
||||
@@ -130,7 +261,7 @@ void ElementPickerPopup::popUpAt(const QPoint &global_pos,
|
||||
}
|
||||
|
||||
move(pos);
|
||||
show();
|
||||
QFrame::show();
|
||||
m_search->setFocus();
|
||||
}
|
||||
|
||||
@@ -178,7 +309,138 @@ void ElementPickerPopup::setCommands(const QList<QAction *> &commands)
|
||||
m_commands_layout->addWidget(button);
|
||||
}
|
||||
m_commands_layout->addStretch();
|
||||
m_commands->setVisible(!commands.isEmpty());
|
||||
if (m_bar_mode)
|
||||
{
|
||||
auto *customise = new QToolButton(m_commands);
|
||||
customise->setAutoRaise(true);
|
||||
customise->setText(QStringLiteral("…"));
|
||||
customise->setToolTip(tr("Personnaliser la barre…"));
|
||||
customise->setFocusPolicy(Qt::NoFocus);
|
||||
connect(customise, &QToolButton::clicked, this, &ElementPickerPopup::startCustomising);
|
||||
m_commands_layout->addWidget(customise);
|
||||
}
|
||||
//An empty bar still shows, so it can be customised back
|
||||
m_commands->setVisible(m_bar_mode || !commands.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::commandItem
|
||||
@return a list item for command @a id: icon only, for the bar row, or
|
||||
icon and text, for the list of other commands. The id is in UserRole.
|
||||
*/
|
||||
QListWidgetItem *ElementPickerPopup::commandItem(const QString &id, bool icon_only) const
|
||||
{
|
||||
QAction *action = commandAction(id);
|
||||
const QString text = action ? action->text().remove(QLatin1Char('&')) : id;
|
||||
auto *item = new QListWidgetItem();
|
||||
item->setData(Qt::UserRole, id);
|
||||
item->setToolTip(text);
|
||||
if (action && !action->icon().isNull()) {
|
||||
item->setIcon(action->icon());
|
||||
}
|
||||
if (!icon_only || item->icon().isNull()) {
|
||||
item->setText(text);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::startCustomising
|
||||
Turn the bar into a small window holding the two lists. A Qt::Popup
|
||||
closes as soon as the mouse is pressed outside it and keeps the mouse
|
||||
grab during a drag, so the window is re-shown as a tool window for the
|
||||
time of the edit.
|
||||
*/
|
||||
void ElementPickerPopup::startCustomising()
|
||||
{
|
||||
const QPoint where = pos();
|
||||
m_customising = true;
|
||||
hide();
|
||||
setWindowFlags(Qt::Tool);
|
||||
setWindowTitle(tr("Personnaliser la barre de raccourcis : %1")
|
||||
.arg(ShortcutBarSettings::title(m_context)));
|
||||
|
||||
fillCustomising(ShortcutBarSettings::ids(m_context));
|
||||
m_commands->hide();
|
||||
setPickerVisible(false);
|
||||
m_editor->show();
|
||||
|
||||
adjustSize();
|
||||
move(where);
|
||||
QFrame::show();
|
||||
activateWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::fillCustomising
|
||||
Put @a ids on the bar row, and every other available command in the
|
||||
list below it.
|
||||
*/
|
||||
void ElementPickerPopup::fillCustomising(const QStringList &ids)
|
||||
{
|
||||
m_edit_row->clear();
|
||||
m_edit_available->clear();
|
||||
for (const QString &id : ids) {
|
||||
m_edit_row->addItem(commandItem(id, true));
|
||||
}
|
||||
for (const QString &id : ShortcutBarSettings::availableIds()) {
|
||||
if (!ids.contains(id)) {
|
||||
m_edit_available->addItem(commandItem(id, false));
|
||||
}
|
||||
}
|
||||
m_edit_available->sortItems();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::finishCustomising
|
||||
Leave the edit, saving the bar row when @a save, and show the bar again
|
||||
where it was so the result can be seen and used straight away.
|
||||
*/
|
||||
void ElementPickerPopup::finishCustomising(bool save)
|
||||
{
|
||||
if (save)
|
||||
{
|
||||
QStringList ids;
|
||||
for (int i = 0 ; i < m_edit_row->count() ; ++i) {
|
||||
ids << m_edit_row->item(i)->data(Qt::UserRole).toString();
|
||||
}
|
||||
ShortcutBarSettings::setIds(m_context, ids);
|
||||
}
|
||||
|
||||
const QPoint where = pos();
|
||||
m_customising = false;
|
||||
hide();
|
||||
m_editor->hide();
|
||||
setPickerVisible(true);
|
||||
setWindowFlags(Qt::Popup);
|
||||
setWindowTitle(QString());
|
||||
popUpShortcutBar(where, m_context);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::closeEvent
|
||||
Closing the customising window from its title bar cancels the edit,
|
||||
like the Annuler button, rather than leaving the bar half-edited.
|
||||
*/
|
||||
void ElementPickerPopup::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (m_customising) {
|
||||
event->ignore();
|
||||
finishCustomising(false);
|
||||
return;
|
||||
}
|
||||
QFrame::closeEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief ElementPickerPopup::setPickerVisible
|
||||
Show or hide the search field, the results and the hint line.
|
||||
*/
|
||||
void ElementPickerPopup::setPickerVisible(bool visible)
|
||||
{
|
||||
m_search->setVisible(visible);
|
||||
m_view->setVisible(visible);
|
||||
m_hint->setVisible(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,17 +521,25 @@ void ElementPickerPopup::keyPressEvent(QKeyEvent *event)
|
||||
switch (event->key())
|
||||
{
|
||||
case Qt::Key_Escape:
|
||||
hide();
|
||||
if (m_customising) {
|
||||
finishCustomising(false);
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
return;
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
chooseCurrent();
|
||||
if (m_customising) {
|
||||
finishCustomising(true);
|
||||
} else {
|
||||
chooseCurrent();
|
||||
}
|
||||
return;
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_PageDown:
|
||||
case Qt::Key_PageUp:
|
||||
if (m_model->rowCount()) {
|
||||
if (!m_customising && m_model->rowCount()) {
|
||||
//Forwarded rather than focus-switched, so typing carries
|
||||
//on going to the search field.
|
||||
QCoreApplication::sendEvent(m_view, event);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define ELEMENTPICKERPOPUP_H
|
||||
|
||||
#include "elementslocation.h"
|
||||
#include "../shortcutbarsettings.h"
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
@@ -29,6 +30,8 @@ class QStandardItemModel;
|
||||
class QLabel;
|
||||
class QAction;
|
||||
class QHBoxLayout;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
|
||||
/**
|
||||
@brief A cursor-anchored element picker.
|
||||
@@ -51,8 +54,9 @@ class ElementPickerPopup : public QFrame
|
||||
explicit ElementPickerPopup(ElementsCollectionWidget *source,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
void popUpAt(const QPoint &global_pos,
|
||||
const QList<QAction *> &commands = {});
|
||||
void popUpAt(const QPoint &global_pos);
|
||||
void popUpShortcutBar(const QPoint &global_pos,
|
||||
ShortcutBarSettings::Context context);
|
||||
|
||||
signals:
|
||||
/// Emitted when the user picks an element; the popup has closed
|
||||
@@ -60,12 +64,20 @@ class ElementPickerPopup : public QFrame
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private:
|
||||
void runSearch();
|
||||
void chooseCurrent();
|
||||
void showPalette();
|
||||
void show(const QPoint &global_pos);
|
||||
void setCommands(const QList<QAction *> &commands);
|
||||
QAction *commandAction(const QString &id) const;
|
||||
QListWidgetItem *commandItem(const QString &id, bool icon_only) const;
|
||||
void startCustomising();
|
||||
void fillCustomising(const QStringList &ids);
|
||||
void finishCustomising(bool save);
|
||||
void setPickerVisible(bool visible);
|
||||
int loadPaletteDir(const QString &dir_path, const QString &prefix,
|
||||
int depth);
|
||||
|
||||
@@ -76,6 +88,14 @@ class ElementPickerPopup : public QFrame
|
||||
QLabel *m_hint = nullptr;
|
||||
QWidget *m_commands = nullptr;
|
||||
QHBoxLayout *m_commands_layout = nullptr;
|
||||
/// Opened as the shortcut bar, as opposed to the plain picker
|
||||
bool m_bar_mode = false;
|
||||
ShortcutBarSettings::Context m_context = ShortcutBarSettings::Canvas;
|
||||
/// Customising the bar in place
|
||||
bool m_customising = false;
|
||||
QWidget *m_editor = nullptr;
|
||||
QListWidget *m_edit_row = nullptr;
|
||||
QListWidget *m_edit_available = nullptr;
|
||||
bool m_palette_mode = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -3205,14 +3205,7 @@ void QETDiagramEditor::showShortcutBar()
|
||||
: 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);
|
||||
elementPicker()->popUpShortcutBar(QCursor::pos(), context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
#include "shortcutbarsettings.h"
|
||||
|
||||
#include "shortcutmanager.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QSettings>
|
||||
|
||||
@@ -120,3 +122,21 @@ void ShortcutBarSettings::setIds(Context context, const QStringList &ids)
|
||||
settings.setValue(settingsKey(context), ids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@return every command that can go on the bar: those the diagram editor
|
||||
registered with ShortcutManager, except the one that opens the bar.
|
||||
*/
|
||||
QStringList ShortcutBarSettings::availableIds()
|
||||
{
|
||||
QStringList ids;
|
||||
for (const ShortcutManager::ShortcutInfo &info :
|
||||
ShortcutManager::instance().allShortcuts())
|
||||
{
|
||||
if (info.id.startsWith(QLatin1String("diagrameditor."))
|
||||
&& info.id != QLatin1String("diagrameditor.show_shortcut_bar")) {
|
||||
ids << info.id;
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ class ShortcutBarSettings
|
||||
static QStringList ids(Context context);
|
||||
static QStringList defaultIds(Context context);
|
||||
static void setIds(Context context, const QStringList &ids);
|
||||
static QStringList availableIds();
|
||||
};
|
||||
|
||||
#endif // SHORTCUTBARSETTINGS_H
|
||||
|
||||
@@ -29,16 +29,6 @@
|
||||
#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
|
||||
@@ -46,9 +36,10 @@ bool offerable(const QString &id)
|
||||
ShortcutBarConfigPage::ShortcutBarConfigPage(QWidget *parent) :
|
||||
ConfigPage(parent)
|
||||
{
|
||||
const QStringList available = ShortcutBarSettings::availableIds();
|
||||
for (const ShortcutManager::ShortcutInfo &info :
|
||||
ShortcutManager::instance().allShortcuts()) {
|
||||
if (offerable(info.id)) {
|
||||
if (available.contains(info.id)) {
|
||||
m_descriptions.insert(info.id, info.description);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user