mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-21 18:50:47 +03:00
Add UI to allow editing shortcuts dynamically
This commit is contained in:
parent
66ade755eb
commit
72410c499d
9 changed files with 191 additions and 49 deletions
|
@ -779,6 +779,7 @@ set(QML_SOURCES
|
|||
resources/qml/dialogs/RoomMembers.qml
|
||||
resources/qml/dialogs/AllowedRoomsSettingsDialog.qml
|
||||
resources/qml/dialogs/RoomSettings.qml
|
||||
resources/qml/dialogs/ShortcutEditor.qml
|
||||
resources/qml/dialogs/UserProfile.qml
|
||||
resources/qml/emoji/StickerPicker.qml
|
||||
resources/qml/pages/LoginPage.qml
|
||||
|
|
92
resources/qml/dialogs/ShortcutEditor.qml
Normal file
92
resources/qml/dialogs/ShortcutEditor.qml
Normal file
|
@ -0,0 +1,92 @@
|
|||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import ".."
|
||||
import "../ui"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import im.nheko
|
||||
|
||||
ApplicationWindow {
|
||||
id: shortcutEditorDialog
|
||||
|
||||
minimumWidth: 500
|
||||
minimumHeight: 450
|
||||
width: 500
|
||||
height: 680
|
||||
color: palette.window
|
||||
modality: Qt.NonModal
|
||||
flags: Qt.Dialog | Qt.WindowCloseButtonHint | Qt.WindowTitleHint
|
||||
title: qsTr("Keyboard shortcuts")
|
||||
|
||||
ScrollView {
|
||||
padding: Nheko.paddingMedium
|
||||
ScrollBar.horizontal.visible: false
|
||||
anchors.fill: parent
|
||||
|
||||
ListView {
|
||||
model: ShortcutRegistry
|
||||
|
||||
delegate: RowLayout {
|
||||
id: del
|
||||
|
||||
required property string name
|
||||
required property string description
|
||||
required property string shortcut
|
||||
|
||||
spacing: Nheko.paddingMedium
|
||||
width: ListView.view.width
|
||||
height: implicitHeight + Nheko.paddingSmall * 2
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
Label {
|
||||
text: del.name
|
||||
font.bold: true
|
||||
font.pointSize: fontMetrics.font.pointSize * 1.1
|
||||
}
|
||||
|
||||
Label {
|
||||
text: del.description
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Button {
|
||||
property bool selectingNewShortcut: false
|
||||
|
||||
text: selectingNewShortcut ? qsTr("Input..") : del.shortcut
|
||||
onClicked: selectingNewShortcut = !selectingNewShortcut
|
||||
Keys.onPressed: event => {
|
||||
if (!selectingNewShortcut)
|
||||
return;
|
||||
event.accepted = true;
|
||||
|
||||
let keySequence = "";
|
||||
if (event.modifiers & Qt.ControlModifier)
|
||||
keySequence += "Ctrl+";
|
||||
if (event.modifiers & Qt.AltModifier)
|
||||
keySequence += "Alt+";
|
||||
if (event.modifiers & Qt.MetaModifier)
|
||||
keySequence += "Meta+";
|
||||
if (event.modifiers & Qt.ShiftModifier)
|
||||
keySequence += "Shift+";
|
||||
|
||||
if (event.key === 0 || event.key === Qt.Key_unknown || event.key === Qt.Key_Control || event.key === Qt.Key_Alt || event.key === Qt.Key_AltGr || event.key === Qt.Key_Meta || event.key === Qt.Key_Shift)
|
||||
keySequence += "...";
|
||||
else {
|
||||
keySequence += ShortcutRegistry.keycodeToChar(event.key);
|
||||
ShortcutRegistry.changeShortcut(del.name, keySequence);
|
||||
selectingNewShortcut = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import ".."
|
||||
import "../ui"
|
||||
import "../dialogs"
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
@ -215,6 +216,23 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: UserSettingsModel.ConfigureKeyboardShortcuts
|
||||
Button {
|
||||
text: qsTr("CONFIGURE")
|
||||
onClicked: {
|
||||
var dialog = keyboardShortcutsDialog.createObject();
|
||||
dialog.show();
|
||||
destroyOnClose(dialog);
|
||||
}
|
||||
|
||||
Component {
|
||||
id: keyboardShortcutsDialog
|
||||
|
||||
ShortcutEditor {}
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
Text {
|
||||
text: model.value
|
||||
|
|
|
@ -57,6 +57,7 @@ MainWindow *MainWindow::instance_ = nullptr;
|
|||
MainWindow::MainWindow(QWindow *parent)
|
||||
: QQuickView(parent)
|
||||
, userSettings_{UserSettings::instance()}
|
||||
, shortcuts_{new ShortcutRegistry}
|
||||
{
|
||||
instance_ = this;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QSharedPointer>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include "ShortcutRegistry.h"
|
||||
#include "UserSettingsPage.h"
|
||||
#include "dock/Dock.h"
|
||||
|
||||
|
@ -140,6 +141,7 @@ private:
|
|||
//! Tray icon that shows the unread message count.
|
||||
TrayIcon *trayIcon_;
|
||||
Dock *dock_;
|
||||
ShortcutRegistry *shortcuts_;
|
||||
|
||||
MxcImageProvider *imgProvider = nullptr;
|
||||
|
||||
|
|
|
@ -1145,6 +1145,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
|
|||
return tr("Periodically update community routing information");
|
||||
case ExpireEvents:
|
||||
return tr("Periodically delete expired events");
|
||||
case KeyboardShortcuts:
|
||||
return tr("Configure keyboard shortcuts");
|
||||
}
|
||||
} else if (role == Value) {
|
||||
switch (index.row()) {
|
||||
|
@ -1444,6 +1446,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
|
|||
case LoginInfoSection:
|
||||
case SessionKeys:
|
||||
case CrossSigningSecrets:
|
||||
case KeyboardShortcuts:
|
||||
return {};
|
||||
case OnlineBackupKey:
|
||||
return tr(
|
||||
|
@ -1562,6 +1565,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
|
|||
case UserSigningKey:
|
||||
case MasterKey:
|
||||
return KeyStatus;
|
||||
case KeyboardShortcuts:
|
||||
return ConfigureKeyboardShortcuts;
|
||||
}
|
||||
} else if (role == ValueLowerBound) {
|
||||
switch (index.row()) {
|
||||
|
|
|
@ -475,6 +475,7 @@ class UserSettingsModel : public QAbstractListModel
|
|||
#endif
|
||||
UpdateSpaceVias,
|
||||
ExpireEvents,
|
||||
KeyboardShortcuts,
|
||||
|
||||
AccessibilitySection,
|
||||
ReducedMotion,
|
||||
|
@ -562,6 +563,7 @@ public:
|
|||
KeyStatus,
|
||||
SessionKeyImportExport,
|
||||
XSignKeysRequestDownload,
|
||||
ConfigureKeyboardShortcuts,
|
||||
};
|
||||
Q_ENUM(Types);
|
||||
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "ShortcutRegistry.h"
|
||||
|
||||
ShortcutRegistry *ShortcutRegistry::s_instance = nullptr;
|
||||
|
||||
EditableShortcut::EditableShortcut(QObject *parent)
|
||||
: QObject{parent}
|
||||
: QObject{parent}
|
||||
{
|
||||
ShortcutRegistry::instance()->registerShortcut(this);
|
||||
}
|
||||
|
||||
const QStringList EditableShortcut::shortcuts() const
|
||||
EditableShortcut::EditableShortcut(const QString &name, const QString &description, QObject *parent)
|
||||
: QObject{parent}
|
||||
, m_name{name}
|
||||
, m_description{description}
|
||||
{
|
||||
ShortcutRegistry::instance()->registerShortcut(this);
|
||||
}
|
||||
|
||||
const QStringList
|
||||
EditableShortcut::shortcuts() const
|
||||
{
|
||||
QStringList dest;
|
||||
dest.resize(m_shortcuts.size());
|
||||
|
@ -17,7 +31,8 @@ const QStringList EditableShortcut::shortcuts() const
|
|||
return dest;
|
||||
}
|
||||
|
||||
void EditableShortcut::setName(const QString &name)
|
||||
void
|
||||
EditableShortcut::setName(const QString &name)
|
||||
{
|
||||
if (name == m_name)
|
||||
return;
|
||||
|
@ -25,7 +40,8 @@ void EditableShortcut::setName(const QString &name)
|
|||
emit nameChanged();
|
||||
}
|
||||
|
||||
void EditableShortcut::setDescription(const QString &description)
|
||||
void
|
||||
EditableShortcut::setDescription(const QString &description)
|
||||
{
|
||||
if (description == m_description)
|
||||
return;
|
||||
|
@ -33,12 +49,14 @@ void EditableShortcut::setDescription(const QString &description)
|
|||
emit descriptionChanged();
|
||||
}
|
||||
|
||||
void EditableShortcut::setShortcut(const QString &shortcut)
|
||||
void
|
||||
EditableShortcut::setShortcut(const QString &shortcut)
|
||||
{
|
||||
setShortcuts({shortcut});
|
||||
}
|
||||
|
||||
void EditableShortcut::setShortcuts(const QStringList &shortcuts)
|
||||
void
|
||||
EditableShortcut::setShortcuts(const QStringList &shortcuts)
|
||||
{
|
||||
QList<QKeySequence> temp;
|
||||
temp.resize(shortcuts.size());
|
||||
|
@ -52,12 +70,13 @@ void EditableShortcut::setShortcuts(const QStringList &shortcuts)
|
|||
emit shortcutsChanged();
|
||||
}
|
||||
|
||||
EditableShortcut::EditableShortcut(const QString &name, const QString &description, QObject *parent)
|
||||
: QObject{parent}
|
||||
, m_name{name}
|
||||
, m_description{description}
|
||||
ShortcutRegistry::ShortcutRegistry(QObject *parent)
|
||||
: QAbstractListModel{parent}
|
||||
{
|
||||
ShortcutRegistry::instance()->registerShortcut(this);
|
||||
if (s_instance)
|
||||
m_shortcuts = s_instance->m_shortcuts;
|
||||
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
ShortcutRegistry *
|
||||
|
@ -66,7 +85,8 @@ ShortcutRegistry::instance()
|
|||
return s_instance;
|
||||
}
|
||||
|
||||
ShortcutRegistry *ShortcutRegistry::create(QQmlEngine *qmlEngine, QJSEngine *)
|
||||
ShortcutRegistry *
|
||||
ShortcutRegistry::create(QQmlEngine *qmlEngine, QJSEngine *)
|
||||
{
|
||||
// The instance has to exist before it is used. We cannot replace it.
|
||||
Q_ASSERT(s_instance);
|
||||
|
@ -85,20 +105,20 @@ ShortcutRegistry *ShortcutRegistry::create(QQmlEngine *qmlEngine, QJSEngine *)
|
|||
return s_instance;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ShortcutRegistry::roleNames() const
|
||||
QHash<int, QByteArray>
|
||||
ShortcutRegistry::roleNames() const
|
||||
{
|
||||
return {{Roles::Name, "name"},
|
||||
{Roles::Description, "description"},
|
||||
{Roles::Shortcut, "shortcut"}};
|
||||
return {
|
||||
{Roles::Name, "name"}, {Roles::Description, "description"}, {Roles::Shortcut, "shortcut"}};
|
||||
}
|
||||
|
||||
QVariant ShortcutRegistry::data(const QModelIndex &index, int role) const
|
||||
QVariant
|
||||
ShortcutRegistry::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() >= m_shortcuts.size() || index.row() < 0)
|
||||
return {};
|
||||
|
||||
switch (role)
|
||||
{
|
||||
switch (role) {
|
||||
case Roles::Name:
|
||||
return m_shortcuts[index.row()]->name();
|
||||
case Roles::Description:
|
||||
|
@ -110,31 +130,29 @@ QVariant ShortcutRegistry::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
}
|
||||
|
||||
bool ShortcutRegistry::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
void
|
||||
ShortcutRegistry::changeShortcut(const QString &name, const QString &newShortcut)
|
||||
{
|
||||
if (!index.isValid() || index.row() >= m_shortcuts.size() || index.row() < 0)
|
||||
return false;
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Roles::Shortcut:
|
||||
if (auto shortcut = QKeySequence(value.toString()); !shortcut.isEmpty()) {
|
||||
m_shortcuts[index.row()]->setShortcut(shortcut.toString());
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
for (int i = 0; i < m_shortcuts.size(); ++i) {
|
||||
if (m_shortcuts[i]->name() == name) {
|
||||
qDebug() << "new:" << newShortcut;
|
||||
m_shortcuts[i]->setShortcut(newShortcut);
|
||||
emit dataChanged(index(i), index(i), {Roles::Shortcut});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShortcutRegistry::ShortcutRegistry(QObject *parent)
|
||||
: QAbstractListModel{parent}
|
||||
QString
|
||||
ShortcutRegistry::keycodeToChar(int keycode) const
|
||||
{
|
||||
s_instance = this;
|
||||
return QString((char)keycode);
|
||||
}
|
||||
|
||||
void ShortcutRegistry::registerShortcut(EditableShortcut *action)
|
||||
void
|
||||
ShortcutRegistry::registerShortcut(EditableShortcut *action)
|
||||
{
|
||||
beginInsertRows({}, m_shortcuts.size(), m_shortcuts.size());
|
||||
m_shortcuts.push_back(action);
|
||||
endInsertRows();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QAction>
|
||||
#include <QKeySequence>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class EditableShortcut : public QObject
|
||||
|
@ -10,15 +14,15 @@ class EditableShortcut : public QObject
|
|||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
|
||||
Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged FINAL)
|
||||
Q_PROPERTY(
|
||||
QString description READ description WRITE setDescription NOTIFY descriptionChanged FINAL)
|
||||
Q_PROPERTY(QString shortcut READ shortcut WRITE setShortcut NOTIFY shortcutsChanged FINAL)
|
||||
Q_PROPERTY(QStringList shortcuts READ shortcuts WRITE setShortcuts NOTIFY shortcutsChanged FINAL)
|
||||
Q_PROPERTY(
|
||||
QStringList shortcuts READ shortcuts WRITE setShortcuts NOTIFY shortcutsChanged FINAL)
|
||||
|
||||
public:
|
||||
EditableShortcut(QObject *parent = nullptr);
|
||||
EditableShortcut(const QString &name, const QString &description, QObject *parent = nullptr);
|
||||
EditableShortcut(const QString &name, const QString &description, const QString &text, QObject *parent = nullptr);
|
||||
EditableShortcut(const QString &name, const QString &description, const QIcon &icon, const QString &text, QObject *parent = nullptr);
|
||||
|
||||
const QString &name() const { return m_name; }
|
||||
const QString &description() const { return m_description; }
|
||||
|
@ -58,20 +62,19 @@ public:
|
|||
Shortcut,
|
||||
};
|
||||
|
||||
explicit ShortcutRegistry(QObject *parent = nullptr);
|
||||
|
||||
static ShortcutRegistry *instance();
|
||||
static ShortcutRegistry *create(QQmlEngine *qmlEngine, QJSEngine *);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex & = QModelIndex()) const override
|
||||
{
|
||||
return m_shortcuts.size();
|
||||
}
|
||||
int rowCount(const QModelIndex & = QModelIndex()) const override { return m_shortcuts.size(); }
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
Q_INVOKABLE void changeShortcut(const QString &name, const QString &newShortcut);
|
||||
Q_INVOKABLE QString keycodeToChar(int keycode) const;
|
||||
|
||||
private:
|
||||
explicit ShortcutRegistry(QObject *parent = nullptr);
|
||||
|
||||
void registerShortcut(EditableShortcut *action);
|
||||
|
||||
static ShortcutRegistry *s_instance;
|
||||
|
|
Loading…
Reference in a new issue