diff --git a/src/mumble/GlobalShortcut.cpp b/src/mumble/GlobalShortcut.cpp index bf777c094..1aa05633a 100644 --- a/src/mumble/GlobalShortcut.cpp +++ b/src/mumble/GlobalShortcut.cpp @@ -12,6 +12,7 @@ #include "EnvUtils.h" #include "MainWindow.h" #include "ServerHandler.h" +#include "widgets/EventFilters.h" #include "Global.h" #include "GlobalShortcutButtons.h" @@ -43,43 +44,70 @@ static ConfigRegistrar registrarGlobalShortcut(1200, GlobalShortcutConfigDialogN static const QString UPARROW = QString::fromUtf8("\xE2\x86\x91 "); -ShortcutActionWidget::ShortcutActionWidget(QWidget *p) : MUComboBox(p) { +ShortcutActionWidget::ShortcutActionWidget(QWidget *p) : QWidget(p) { int idx = 0; - insertItem(idx, tr("Unassigned")); - setItemData(idx, -1); + m_comboBox = new MUComboBox(); + m_comboBox->insertItem(idx, tr("Unassigned")); + m_comboBox->setItemData(idx, -1); #ifndef Q_OS_MAC - setSizeAdjustPolicy(AdjustToContents); + m_comboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); #endif idx++; + QFontMetrics fontMetrics = m_comboBox->fontMetrics(); + int maxWidth = m_comboBox->minimumWidth(); + foreach (GlobalShortcut *gs, GlobalShortcutEngine::engine->qmShortcuts) { - insertItem(idx, gs->name); - setItemData(idx, gs->idx); - if (!gs->qsToolTip.isEmpty()) - setItemData(idx, gs->qsToolTip, Qt::ToolTipRole); - if (!gs->qsWhatsThis.isEmpty()) - setItemData(idx, gs->qsWhatsThis, Qt::WhatsThisRole); + m_comboBox->insertItem(idx, gs->name); + m_comboBox->setItemData(idx, gs->idx); + if (!gs->qsToolTip.isEmpty()) { + m_comboBox->setItemData(idx, gs->qsToolTip, Qt::ToolTipRole); + } + if (!gs->qsWhatsThis.isEmpty()) { + m_comboBox->setItemData(idx, gs->qsWhatsThis, Qt::WhatsThisRole); + } idx++; + + maxWidth = std::max(maxWidth, fontMetrics.horizontalAdvance(gs->name)); } // Sort the ShortcutActionWidget items QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); - proxy->setSourceModel(model()); + proxy->setSourceModel(m_comboBox->model()); - model()->setParent(proxy); - setModel(proxy); + m_comboBox->model()->setParent(proxy); + m_comboBox->setModel(proxy); - model()->sort(0); + m_comboBox->model()->sort(0); + + m_comboBox->setFocusPolicy(Qt::NoFocus); + + setMinimumWidth(maxWidth); + m_comboBox->view()->setMinimumWidth(maxWidth); + m_comboBox->adjustSize(); + + m_comboBox->setParent(this); + + adjustSize(); + + KeyEventObserver *eventFilter = new KeyEventObserver(this, QEvent::KeyPress, true, { Qt::Key_Space }); + connect(eventFilter, &KeyEventObserver::keyEventObserved, this, [=]() { m_comboBox->showPopup(); }); + installEventFilter(eventFilter); + + QTreeWidget *treeWidget = qobject_cast< QTreeWidget * >(p->parentWidget()); + if (treeWidget) { + treeWidget->resizeColumnToContents(0); + } } void ShortcutActionWidget::setIndex(unsigned int idx) { - setCurrentIndex(findData(idx)); + m_comboBox->setCurrentIndex(m_comboBox->findData(idx)); } unsigned int ShortcutActionWidget::index() const { - return itemData(currentIndex()).toUInt(); + return m_comboBox->itemData(m_comboBox->currentIndex()).toUInt(); } ShortcutToggleWidget::ShortcutToggleWidget(QWidget *p) : MUComboBox(p) { @@ -344,6 +372,7 @@ void ShortcutTargetDialog::on_qpbRemove_clicked() { ShortcutTargetWidget::ShortcutTargetWidget(QWidget *p) : QFrame(p) { qleTarget = new QLineEdit(); qleTarget->setReadOnly(true); + qleTarget->setFocusPolicy(Qt::NoFocus); qtbEdit = new QToolButton(); qtbEdit->setText(tr("...")); @@ -355,9 +384,38 @@ ShortcutTargetWidget::ShortcutTargetWidget(QWidget *p) : QFrame(p) { l->addWidget(qleTarget, 1); l->addWidget(qtbEdit); + KeyEventObserver *eventFilter = new KeyEventObserver(this, QEvent::KeyPress, true, { Qt::Key_Space }); + connect(eventFilter, &KeyEventObserver::keyEventObserved, this, [=]() { qtbEdit->click(); }); + installEventFilter(eventFilter); + QMetaObject::connectSlotsByName(this); } +TextEditWidget::TextEditWidget(QWidget *p) : QWidget(p) { + m_lineEdit = new QLineEdit(); + m_lineEdit->setFocusPolicy(Qt::ClickFocus); + + QHBoxLayout *l = new QHBoxLayout(this); + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + l->addWidget(m_lineEdit); + + KeyEventObserver *eventFilter = new KeyEventObserver(this, QEvent::KeyPress, true, { Qt::Key_Space }); + connect(eventFilter, &KeyEventObserver::keyEventObserved, this, + [=]() { m_lineEdit->setFocus(Qt::MouseFocusReason); }); + installEventFilter(eventFilter); + + QMetaObject::connectSlotsByName(this); +} + +QString TextEditWidget::currentString() const { + return m_lineEdit->text(); +} + +void TextEditWidget::setCurrentString(const QString &str) { + m_lineEdit->setText(str); +} + /** * This function returns a textual representation of the given shortcut target st. */ @@ -461,7 +519,7 @@ ShortcutDelegate::ShortcutDelegate(QObject *p) : QStyledItemDelegate(p) { new QStandardItemEditorCreator< ShortcutTargetWidget >()); factory->registerEditor(static_cast< int >(QVariant::fromValue(ChannelTarget()).userType()), new QStandardItemEditorCreator< ChannelSelectWidget >()); - factory->registerEditor(QVariant::String, new QStandardItemEditorCreator< QLineEdit >()); + factory->registerEditor(QVariant::String, new QStandardItemEditorCreator< TextEditWidget >()); factory->registerEditor(QVariant::Invalid, new QStandardItemEditorCreator< QWidget >()); setItemEditorFactory(factory); } @@ -556,11 +614,16 @@ GlobalShortcutConfig::GlobalShortcutConfig(Settings &st) : ConfigWidget(st) { qtwShortcuts->setColumnCount(canSuppress ? 4 : 3); qtwShortcuts->setItemDelegate(new ShortcutDelegate(qtwShortcuts)); + qtwShortcuts->headerItem()->setData(0, Qt::AccessibleTextRole, tr("Shortcut action")); + qtwShortcuts->headerItem()->setData(1, Qt::AccessibleTextRole, tr("Shortcut data")); + qtwShortcuts->headerItem()->setData(2, Qt::AccessibleTextRole, tr("Shortcut input combinations")); + qtwShortcuts->header()->setSectionResizeMode(0, QHeaderView::Fixed); qtwShortcuts->header()->resizeSection(0, 150); qtwShortcuts->header()->setSectionResizeMode(2, QHeaderView::Stretch); - if (canSuppress) + if (canSuppress) { qtwShortcuts->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); + } qcbEnableGlobalShortcuts->setVisible(canDisable); @@ -670,6 +733,7 @@ void GlobalShortcutConfig::on_qpbAdd_clicked(bool) { sc.bSuppress = false; qlShortcuts << sc; reload(); + qtwShortcuts->setFocus(Qt::TabFocusReason); } void GlobalShortcutConfig::on_qpbRemove_clicked(bool) { @@ -705,6 +769,17 @@ void GlobalShortcutConfig::on_qtwShortcuts_itemChanged(QTreeWidgetItem *item, in if (gs && sc.qvData.userType() != gs->qvDefault.userType()) { item->setData(1, Qt::DisplayRole, gs->qvDefault); } + + if (gs) { + item->setData(0, Qt::AccessibleTextRole, gs->name); + } else { + item->setData(0, Qt::AccessibleTextRole, tr("Unassigned")); + } + item->setData(1, Qt::AccessibleTextRole, sc.qvData); + item->setData(3, Qt::AccessibleDescriptionRole, + item->checkState(3) == Qt::Checked ? tr("checked") : tr("unchecked")); + + qtwShortcuts->resizeColumnToContents(0); } QString GlobalShortcutConfig::title() const { @@ -811,6 +886,7 @@ void GlobalShortcutConfig::reload() { foreach (const Shortcut &sc, qlShortcuts) { QTreeWidgetItem *item = itemForShortcut(sc); qtwShortcuts->addTopLevelItem(item); + on_qtwShortcuts_itemChanged(item, 0); } #ifdef Q_OS_MAC if (!Global::get().s.bSuppressMacEventTapWarning) { diff --git a/src/mumble/GlobalShortcut.h b/src/mumble/GlobalShortcut.h index 64b9dbfc3..d7a9f35e0 100644 --- a/src/mumble/GlobalShortcut.h +++ b/src/mumble/GlobalShortcut.h @@ -51,11 +51,14 @@ public: * * @see GlobalShortcutEngine */ -class ShortcutActionWidget : public MUComboBox { +class ShortcutActionWidget : public QWidget { private: Q_OBJECT Q_DISABLE_COPY(ShortcutActionWidget) Q_PROPERTY(unsigned int index READ index WRITE setIndex USER true) + + MUComboBox *m_comboBox; + public: ShortcutActionWidget(QWidget *p = nullptr); unsigned int index() const; @@ -84,6 +87,20 @@ public: void setCurrentChannel(const ChannelTarget &); }; +class TextEditWidget : public QWidget { +private: + Q_OBJECT + Q_DISABLE_COPY(TextEditWidget) + Q_PROPERTY(QString currentString READ currentString WRITE setCurrentString USER true) + + QLineEdit *m_lineEdit; + +public: + TextEditWidget(QWidget *p = nullptr); + QString currentString() const; + void setCurrentString(const QString &); +}; + /** * Dialog which is used to select the targets of a targeted shortcut like Whisper. */ diff --git a/src/mumble/GlobalShortcut.ui b/src/mumble/GlobalShortcut.ui index f8d072cf4..8b4063289 100644 --- a/src/mumble/GlobalShortcut.ui +++ b/src/mumble/GlobalShortcut.ui @@ -124,13 +124,16 @@ - + List of configured shortcuts Configured shortcuts + + Use up and down keys to navigate through your added shortcuts. Use left and right keys to navigate between actions and options for a single shortcut. Entries can be added and deleted with the buttons below. + QAbstractItemView::AllEditTriggers @@ -186,6 +189,12 @@ This will add a new global shortcut + + Add unassigned shortcut + + + This adds a new empty entry to the "Configured Shortcut" tree above. The tree will be automatically focused. Assign a key or an action by selecting the entry in the tree above. + &Add @@ -202,6 +211,12 @@ This will permanently remove a selected shortcut. + + Remove selected shortcut + + + This removes the selected entry from the "Configured Shortcut" tree above + &Remove @@ -271,6 +286,13 @@ Without this option enabled, using Mumble's global shortcuts in privileged appli + + + MultiColumnTreeWidget + QTreeWidget +
widgets/MultiColumnTreeWidget.h
+
+
diff --git a/src/mumble/GlobalShortcutButtons.cpp b/src/mumble/GlobalShortcutButtons.cpp index d96bc06fb..931033f24 100644 --- a/src/mumble/GlobalShortcutButtons.cpp +++ b/src/mumble/GlobalShortcutButtons.cpp @@ -10,6 +10,8 @@ #include "Global.h" +#include + GlobalShortcutButtons::GlobalShortcutButtons(QWidget *parent) : QDialog(parent), m_ui(new Ui::GlobalShortcutButtons) { m_ui->setupUi(this); @@ -50,6 +52,16 @@ void GlobalShortcutButtons::setButtons(const QList< QVariant > &buttons) { } adjustSize(); + + // Without this the new dialog window will not be focused and the + // shortcut edit dialog is closed when TAB is pressed... + // Due to Qt action processing weirdness the timer is needed, otherwise + // the call has no effect. + if (buttons.isEmpty()) { + QTimer::singleShot(0, [&]() { m_ui->addButton->setFocus(Qt::TabFocusReason); }); + } else { + QTimer::singleShot(0, [&]() { m_ui->buttonTree->setFocus(Qt::TabFocusReason); }); + } } void GlobalShortcutButtons::addItem(const QVariant &button) {