Skip to content

Commit

Permalink
Add UI to allow editing shortcuts dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
LorenDB committed Sep 26, 2023
1 parent 66ade75 commit 72410c4
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 49 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
92 changes: 92 additions & 0 deletions resources/qml/dialogs/ShortcutEditor.qml
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
}
}
}
18 changes: 18 additions & 0 deletions resources/qml/pages/UserSettingsPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ MainWindow *MainWindow::instance_ = nullptr;
MainWindow::MainWindow(QWindow *parent)
: QQuickView(parent)
, userSettings_{UserSettings::instance()}
, shortcuts_{new ShortcutRegistry}
{
instance_ = this;

Expand Down
2 changes: 2 additions & 0 deletions src/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <QSharedPointer>
#include <QSystemTrayIcon>

#include "ShortcutRegistry.h"
#include "UserSettingsPage.h"
#include "dock/Dock.h"

Expand Down Expand Up @@ -140,6 +141,7 @@ private slots:
//! Tray icon that shows the unread message count.
TrayIcon *trayIcon_;
Dock *dock_;
ShortcutRegistry *shortcuts_;

MxcImageProvider *imgProvider = nullptr;

Expand Down
5 changes: 5 additions & 0 deletions src/UserSettingsPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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()) {
Expand Down
2 changes: 2 additions & 0 deletions src/UserSettingsPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ class UserSettingsModel : public QAbstractListModel
#endif
UpdateSpaceVias,
ExpireEvents,
KeyboardShortcuts,

AccessibilitySection,
ReducedMotion,
Expand Down Expand Up @@ -562,6 +563,7 @@ class UserSettingsModel : public QAbstractListModel
KeyStatus,
SessionKeyImportExport,
XSignKeysRequestDownload,
ConfigureKeyboardShortcuts,
};
Q_ENUM(Types);

Expand Down
92 changes: 55 additions & 37 deletions src/ui/ShortcutRegistry.cpp
Original file line number Diff line number Diff line change
@@ -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);
}

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
const QStringList
EditableShortcut::shortcuts() const
{
QStringList dest;
dest.resize(m_shortcuts.size());
Expand All @@ -17,28 +31,32 @@ const QStringList EditableShortcut::shortcuts() const
return dest;
}

void EditableShortcut::setName(const QString &name)
void
EditableShortcut::setName(const QString &name)
{
if (name == m_name)
return;
m_name = name;
emit nameChanged();
}

void EditableShortcut::setDescription(const QString &description)
void
EditableShortcut::setDescription(const QString &description)
{
if (description == m_description)
return;
m_description = 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());
Expand All @@ -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 *
Expand All @@ -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);
Expand All @@ -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:
Expand All @@ -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();
}
Loading

0 comments on commit 72410c4

Please sign in to comment.