Add working shortcut serialization

This commit is contained in:
Loren Burkholder 2023-09-26 16:38:13 -04:00
parent 72410c499d
commit 56dcdd7fd4
10 changed files with 464 additions and 289 deletions

View file

@ -397,8 +397,8 @@ set(SRC_FILES
src/ui/RoomSettings.h src/ui/RoomSettings.h
src/ui/RoomSummary.cpp src/ui/RoomSummary.cpp
src/ui/RoomSummary.h src/ui/RoomSummary.h
src/ui/ShortcutRegistry.cpp src/ui/KeySequenceRegistry.cpp
src/ui/ShortcutRegistry.h src/ui/KeySequenceRegistry.h
src/ui/Theme.cpp src/ui/Theme.cpp
src/ui/Theme.h src/ui/Theme.h
src/ui/UIA.cpp src/ui/UIA.cpp

View file

@ -112,15 +112,14 @@ Pane {
onActivated: Qt.quit() onActivated: Qt.quit()
} }
EditableShortcut { EditableKeySequence {
id: quickSwitcherShortcut id: quickSwitcherShortcut
name: qsTr("Room search") name: qsTr("Room search")
description: qsTr("Opens a search bar for quick switching between rooms") defaultKeySequence: "Ctrl+K"
shortcut: "Ctrl+K"
} }
Shortcut { Shortcut {
sequence: quickSwitcherShortcut.shortcut sequence: quickSwitcherShortcut.keySequence
onActivated: { onActivated: {
var component = Qt.createComponent("qrc:/resources/qml/QuickSwitcher.qml"); var component = Qt.createComponent("qrc:/resources/qml/QuickSwitcher.qml");
@ -134,45 +133,98 @@ Pane {
} }
} }
EditableShortcut { EditableKeySequence {
id: nextRoomWithActivityShortcut id: nextRoomWithActivityShortcut
name: qsTr("Next room with activity") name: qsTr("Next room with activity")
description: qsTr("Switches to the next unread room in the roomlist")
// Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit // Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit
shortcuts: ["Alt+A", "Ctrl+Shift+A"] defaultKeySequences: ["Alt+A", "Ctrl+Shift+A"]
} }
Shortcut { Shortcut {
sequences: nextRoomWithActivityShortcut.shortcuts sequences: nextRoomWithActivityShortcut.keySequence
onActivated: Rooms.nextRoomWithActivity() onActivated: Rooms.nextRoomWithActivity()
} }
EditableShortcut { EditableKeySequence {
id: nextRoomShortcut id: nextRoomShortcut
name: qsTr("Next room") name: qsTr("Next room")
description: qsTr("Switches to the room below the room that is currently open") defaultKeySequence: "Ctrl+Down"
shortcut: "Ctrl+Down"
} }
Shortcut { Shortcut {
sequence: nextRoomShortcut.shortcut sequence: nextRoomShortcut.keySequence
onActivated: Rooms.nextRoom() onActivated: Rooms.nextRoom()
} }
EditableShortcut { EditableKeySequence {
id: previousRoomShortcut id: previousRoomShortcut
name: qsTr("Previous room") name: qsTr("Previous room")
description: qsTr("Switches to the room above the room that is currently open") defaultKeySequence: "Ctrl+Up"
shortcut: "Ctrl+Up"
} }
Shortcut { Shortcut {
sequence: previousRoomShortcut.shortcut sequence: previousRoomShortcut.keySequence
onActivated: Rooms.previousRoom() onActivated: Rooms.previousRoom()
} }
EditableKeySequence {
id: nextSpaceShortcut
name: qsTr("Next space")
}
Shortcut {
sequence: nextSpaceShortcut.keySequence
// onActivated: Communities.setCurrentTagId(model.id)
}
EditableKeySequence {
id: previousSpaceShortcut
name: qsTr("Previous space")
}
Shortcut {
sequence: previousSpaceShortcut.keySequence
// onActivated: Communities.setCurrentTagId(model.id)
}
EditableKeySequence {
id: allRoomsSpaceShortcut
name: qsTr("Show all rooms")
}
Shortcut {
sequence: allRoomsSpaceShortcut.keySequence
onActivated: Communities.setCurrentTagId("global")
}
EditableKeySequence {
id: favoriteRoomsShortcut
name: qsTr("Show favorite rooms")
}
Shortcut {
sequence: favoriteRoomsShortcut.keySequence
onActivated: Communities.setCurrentTagId("m.favourite")
}
EditableKeySequence {
id: directChatsShortcut
name: qsTr("Show direct chats")
}
Shortcut {
sequence: directChatsShortcut.keySequence
onActivated: Communities.setCurrentTagId("dm")
}
Connections { Connections {
function onOpenJoinRoomDialog() { function onOpenJoinRoomDialog() {
var component = Qt.createComponent("qrc:/resources/qml/dialogs/JoinRoomDialog.qml"); var component = Qt.createComponent("qrc:/resources/qml/dialogs/JoinRoomDialog.qml");

View file

@ -28,42 +28,31 @@ ApplicationWindow {
anchors.fill: parent anchors.fill: parent
ListView { ListView {
model: ShortcutRegistry model: KeySequenceRegistry
delegate: RowLayout { delegate: RowLayout {
id: del id: del
required property string name required property string name
required property string description required property string keySequence
required property string shortcut
spacing: Nheko.paddingMedium spacing: Nheko.paddingMedium
width: ListView.view.width width: ListView.view.width
height: implicitHeight + Nheko.paddingSmall * 2 height: implicitHeight + Nheko.paddingSmall * 2
ColumnLayout { Label {
spacing: Nheko.paddingSmall text: del.name
Label {
text: del.name
font.bold: true
font.pointSize: fontMetrics.font.pointSize * 1.1
}
Label {
text: del.description
}
} }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
Button { Button {
property bool selectingNewShortcut: false property bool selectingNewKeySequence: false
text: selectingNewShortcut ? qsTr("Input..") : del.shortcut text: selectingNewKeySequence ? qsTr("Input..") : (del.keySequence === "" ? "None" : del.keySequence)
onClicked: selectingNewShortcut = !selectingNewShortcut onClicked: selectingNewKeySequence = !selectingNewKeySequence
Keys.onPressed: event => { Keys.onPressed: event => {
if (!selectingNewShortcut) if (!selectingNewKeySequence)
return; return;
event.accepted = true; event.accepted = true;
@ -77,12 +66,14 @@ ApplicationWindow {
if (event.modifiers & Qt.ShiftModifier) if (event.modifiers & Qt.ShiftModifier)
keySequence += "Shift+"; 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) 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_Super_L || event.key === Qt.Key_Super_R ||
event.key === Qt.Key_Shift)
keySequence += "..."; keySequence += "...";
else { else {
keySequence += ShortcutRegistry.keycodeToChar(event.key); keySequence += KeySequenceRegistry.keycodeToChar(event.key);
ShortcutRegistry.changeShortcut(del.name, keySequence); KeySequenceRegistry.changeKeySequence(del.name, keySequence);
selectingNewShortcut = false; selectingNewKeySequence = false;
} }
} }
} }

View file

@ -57,7 +57,6 @@ MainWindow *MainWindow::instance_ = nullptr;
MainWindow::MainWindow(QWindow *parent) MainWindow::MainWindow(QWindow *parent)
: QQuickView(parent) : QQuickView(parent)
, userSettings_{UserSettings::instance()} , userSettings_{UserSettings::instance()}
, shortcuts_{new ShortcutRegistry}
{ {
instance_ = this; instance_ = this;

View file

@ -11,7 +11,7 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include "ShortcutRegistry.h" #include "KeySequenceRegistry.h"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"
#include "dock/Dock.h" #include "dock/Dock.h"
@ -141,7 +141,6 @@ private:
//! Tray icon that shows the unread message count. //! Tray icon that shows the unread message count.
TrayIcon *trayIcon_; TrayIcon *trayIcon_;
Dock *dock_; Dock *dock_;
ShortcutRegistry *shortcuts_;
MxcImageProvider *imgProvider = nullptr; MxcImageProvider *imgProvider = nullptr;

View file

@ -30,8 +30,10 @@ QSharedPointer<UserSettings> UserSettings::instance_;
UserSettings::UserSettings() UserSettings::UserSettings()
{ {
connect( connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, []() {
QCoreApplication::instance(), &QCoreApplication::aboutToQuit, []() { instance_.clear(); }); instance_->save();
instance_.clear();
});
} }
QSharedPointer<UserSettings> QSharedPointer<UserSettings>
@ -164,6 +166,16 @@ UserSettings::load(std::optional<QString> profile)
disableCertificateValidation_ = disableCertificateValidation_ =
settings.value(QStringLiteral("disable_certificate_validation"), false).toBool(); settings.value(QStringLiteral("disable_certificate_validation"), false).toBool();
settings.beginGroup(QStringLiteral("user"));
settings.beginGroup(QStringLiteral("shortcuts"));
QMap<QString, QStringList> bindings;
for (const auto &key : settings.childKeys())
bindings[key] = settings.value(key).toStringList();
qDebug() << "restoring with size:" << bindings.size();
KeySequenceRegistry::instance()->restoreBindings(bindings);
settings.endGroup(); // user/shortcuts
settings.endGroup(); // user/shortcuts
applyTheme(); applyTheme();
} }
@ -882,7 +894,7 @@ UserSettings::save()
settings.beginGroup(QStringLiteral("sidebar")); settings.beginGroup(QStringLiteral("sidebar"));
settings.setValue(QStringLiteral("community_list_width"), communityListWidth_); settings.setValue(QStringLiteral("community_list_width"), communityListWidth_);
settings.setValue(QStringLiteral("room_list_width"), roomListWidth_); settings.setValue(QStringLiteral("room_list_width"), roomListWidth_);
settings.endGroup(); // window settings.endGroup(); // sidebar
settings.beginGroup(QStringLiteral("timeline")); settings.beginGroup(QStringLiteral("timeline"));
settings.setValue(QStringLiteral("buttons"), buttonsInTimeline_); settings.setValue(QStringLiteral("buttons"), buttonsInTimeline_);
@ -939,6 +951,13 @@ UserSettings::save()
settings.setValue(QStringLiteral("space_background_maintenance"), updateSpaceVias_); settings.setValue(QStringLiteral("space_background_maintenance"), updateSpaceVias_);
settings.setValue(QStringLiteral("expired_events_background_maintenance"), expireEvents_); settings.setValue(QStringLiteral("expired_events_background_maintenance"), expireEvents_);
settings.beginGroup(QStringLiteral("shortcuts"));
auto bindings = KeySequenceRegistry::instance()->dumpBindings();
for (const auto &[name, sequences] : bindings.asKeyValueRange())
settings.setValue(name, sequences);
qDebug() << "saved with size:" << bindings.size();
settings.endGroup(); // shortcuts
settings.endGroup(); // user settings.endGroup(); // user
QString prefix = (profile_ != QLatin1String("") && profile_ != QLatin1String("default")) QString prefix = (profile_ != QLatin1String("") && profile_ != QLatin1String("default"))

View file

@ -0,0 +1,241 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "KeySequenceRegistry.h"
KeySequenceRegistry *KeySequenceRegistry::s_instance = nullptr;
KeySequenceImpl::KeySequenceImpl(const QString &name,
const QStringList &keySequences,
QObject *parent)
: QObject{parent}
, m_name{name}
, m_keySequences{keySequences}
{
}
void
KeySequenceImpl::setKeySequences(const QStringList &keySequences)
{
if (keySequences == m_keySequences)
return;
m_keySequences = keySequences;
emit keySequencesChanged();
}
EditableKeySequence::EditableKeySequence(QObject *parent)
: QObject{parent}
{
KeySequenceRegistry::instance()->registerKeySequence(this);
}
EditableKeySequence::EditableKeySequence(const QString &name, QObject *parent)
: QObject{parent}
, m_name{name}
{
KeySequenceRegistry::instance()->registerKeySequence(this);
}
const QString
EditableKeySequence::keySequence() const
{
return (m_impl && m_impl->keySequences().size() > 0) ? m_impl->keySequences().first()
: defaultKeySequence();
}
const QStringList
EditableKeySequence::keySequences() const
{
return m_impl ? m_impl->keySequences() : defaultKeySequences();
}
const QString
EditableKeySequence::defaultKeySequence() const
{
return m_defaultKeySequences.size() > 0 ? m_defaultKeySequences.first().toString() : QString{};
}
const QStringList
EditableKeySequence::defaultKeySequences() const
{
QStringList dest;
dest.resize(m_defaultKeySequences.size());
std::transform(m_defaultKeySequences.begin(),
m_defaultKeySequences.end(),
dest.begin(),
[](const auto &keySequence) { return keySequence.toString(); });
return dest;
}
void
EditableKeySequence::setName(const QString &name)
{
if (name == m_name)
return;
m_name = name;
emit nameChanged();
KeySequenceRegistry::instance()->registerKeySequence(this);
}
void
EditableKeySequence::setKeySequence(const QString &keySequence)
{
setKeySequences({keySequence});
}
void
EditableKeySequence::setKeySequences(const QStringList &keySequences)
{
m_impl->setKeySequences(keySequences);
}
void
EditableKeySequence::setDefaultKeySequence(const QString &keySequence)
{
setDefaultKeySequences({keySequence});
}
void
EditableKeySequence::setDefaultKeySequences(const QStringList &keySequences)
{
QList<QKeySequence> temp;
temp.resize(keySequences.size());
std::transform(keySequences.begin(),
keySequences.end(),
temp.begin(),
[](const auto &keySequence) { return QKeySequence(keySequence); });
if (temp == m_defaultKeySequences)
return;
m_defaultKeySequences = temp;
emit defaultKeySequencesChanged();
if (m_impl && m_impl->keySequences().isEmpty())
m_impl->setKeySequences(keySequences);
}
KeySequenceRegistry::KeySequenceRegistry(QObject *parent)
: QAbstractListModel{parent}
{
s_instance = this;
}
KeySequenceRegistry *
KeySequenceRegistry::instance()
{
if (!s_instance)
s_instance = new KeySequenceRegistry;
return s_instance;
}
KeySequenceRegistry *
KeySequenceRegistry::create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(s_instance);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == s_instance->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(s_instance, QJSEngine::CppOwnership);
return s_instance;
}
QHash<int, QByteArray>
KeySequenceRegistry::roleNames() const
{
return {{Roles::Name, "name"}, {Roles::KeySequence, "keySequence"}};
}
QVariant
KeySequenceRegistry::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= m_keySequences.size() || index.row() < 0)
return {};
switch (role) {
case Roles::Name:
return m_keySequences.at(index.row())->name();
case Roles::KeySequence: {
const auto &data = m_keySequences.at(index.row())->keySequences();
return data.size() > 0 ? data.first() : QString{};
}
default:
return {};
}
}
void
KeySequenceRegistry::changeKeySequence(const QString &name, const QString &newKeySequence)
{
for (int i = 0; i < m_keySequences.size(); ++i) {
if (m_keySequences.at(i)->name() == name) {
m_keySequences.at(i)->setKeySequences({newKeySequence});
emit dataChanged(index(i), index(i), {Roles::KeySequence});
return;
}
}
}
QString
KeySequenceRegistry::keycodeToChar(int keycode) const
{
return QString((char)keycode);
}
QMap<QString, QStringList>
KeySequenceRegistry::dumpBindings() const
{
QMap<QString, QStringList> bindings;
for (const auto sequence : m_keySequences)
bindings[sequence->name()] = sequence->keySequences();
return bindings;
}
void
KeySequenceRegistry::restoreBindings(const QMap<QString, QStringList> &bindings)
{
for (const auto &[name, keySequences] : bindings.asKeyValueRange()) {
if (auto it = std::find_if(m_keySequences.begin(),
m_keySequences.end(),
[&name](const auto &impl) { return impl->name() == name; });
it != m_keySequences.end())
(*it)->setKeySequences(keySequences);
else
m_keySequences.push_back(new KeySequenceImpl{name, keySequences});
}
}
void
KeySequenceRegistry::registerKeySequence(EditableKeySequence *action)
{
if (action->name().isEmpty())
return;
KeySequenceImpl *impl = nullptr;
if (auto it =
std::find_if(m_keySequences.begin(),
m_keySequences.end(),
[action](const auto &impl) { return impl->name() == action->name(); });
it != m_keySequences.end())
impl = *it;
else {
impl = new KeySequenceImpl{action->name(), action->keySequences()};
m_keySequences.push_back(impl);
}
action->m_impl = impl;
connect(impl,
&KeySequenceImpl::keySequencesChanged,
action,
&EditableKeySequence::keySequencesChanged);
emit action->keySequencesChanged();
}

View file

@ -0,0 +1,116 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QAbstractListModel>
#include <QKeySequence>
#include <QQmlEngine>
class KeySequenceImpl : public QObject
{
Q_OBJECT
public:
KeySequenceImpl(const QString &name,
const QStringList &keySequences,
QObject *parent = nullptr);
const QString &name() const { return m_name; }
const QStringList &keySequences() const { return m_keySequences; }
void setKeySequences(const QStringList &keySequences);
signals:
void keySequencesChanged();
private:
const QString m_name;
QStringList m_keySequences;
};
class EditableKeySequence : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
Q_PROPERTY(
QString keySequence READ keySequence WRITE setKeySequence NOTIFY keySequencesChanged FINAL)
Q_PROPERTY(QStringList keySequences READ keySequences WRITE setKeySequences NOTIFY
keySequencesChanged FINAL)
Q_PROPERTY(QString defaultKeySequence READ defaultKeySequence WRITE setDefaultKeySequence NOTIFY
defaultKeySequencesChanged FINAL)
Q_PROPERTY(QStringList defaultKeySequences READ defaultKeySequences WRITE setDefaultKeySequences
NOTIFY defaultKeySequencesChanged FINAL)
public:
EditableKeySequence(QObject *parent = nullptr);
EditableKeySequence(const QString &name, QObject *parent = nullptr);
const QString &name() const { return m_name; }
const QString keySequence() const;
const QStringList keySequences() const;
const QString defaultKeySequence() const;
const QStringList defaultKeySequences() const;
void setName(const QString &name);
void setKeySequence(const QString &keySequence);
void setKeySequences(const QStringList &keySequences);
void setDefaultKeySequence(const QString &keySequence);
void setDefaultKeySequences(const QStringList &keySequences);
signals:
void nameChanged();
void keySequencesChanged();
void defaultKeySequencesChanged();
private:
QString m_name;
QList<QKeySequence> m_defaultKeySequences;
KeySequenceImpl *m_impl = nullptr;
friend class KeySequenceRegistry;
};
class KeySequenceRegistry : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
enum Roles
{
Name,
KeySequence,
};
static KeySequenceRegistry *instance();
static KeySequenceRegistry *create(QQmlEngine *qmlEngine, QJSEngine *);
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex & = QModelIndex()) const override
{
return m_keySequences.size();
}
QVariant data(const QModelIndex &index, int role) const override;
Q_INVOKABLE void changeKeySequence(const QString &name, const QString &newKeysequence);
Q_INVOKABLE QString keycodeToChar(int keycode) const;
QMap<QString, QStringList> dumpBindings() const;
void restoreBindings(const QMap<QString, QStringList> &bindings);
private:
explicit KeySequenceRegistry(QObject *parent = nullptr);
void registerKeySequence(EditableKeySequence *action);
static KeySequenceRegistry *s_instance;
QList<KeySequenceImpl *> m_keySequences;
friend EditableKeySequence;
};

View file

@ -1,158 +0,0 @@
// 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}
{
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
{
QStringList dest;
dest.resize(m_shortcuts.size());
std::transform(m_shortcuts.begin(), m_shortcuts.end(), dest.begin(), [](const auto &shortcut) {
return shortcut.toString();
});
return dest;
}
void
EditableShortcut::setName(const QString &name)
{
if (name == m_name)
return;
m_name = name;
emit nameChanged();
}
void
EditableShortcut::setDescription(const QString &description)
{
if (description == m_description)
return;
m_description = description;
emit descriptionChanged();
}
void
EditableShortcut::setShortcut(const QString &shortcut)
{
setShortcuts({shortcut});
}
void
EditableShortcut::setShortcuts(const QStringList &shortcuts)
{
QList<QKeySequence> temp;
temp.resize(shortcuts.size());
std::transform(shortcuts.begin(), shortcuts.end(), temp.begin(), [](const auto &shortcut) {
return QKeySequence(shortcut);
});
if (temp == m_shortcuts)
return;
m_shortcuts = temp;
emit shortcutsChanged();
}
ShortcutRegistry::ShortcutRegistry(QObject *parent)
: QAbstractListModel{parent}
{
if (s_instance)
m_shortcuts = s_instance->m_shortcuts;
s_instance = this;
}
ShortcutRegistry *
ShortcutRegistry::instance()
{
return s_instance;
}
ShortcutRegistry *
ShortcutRegistry::create(QQmlEngine *qmlEngine, QJSEngine *)
{
// The instance has to exist before it is used. We cannot replace it.
Q_ASSERT(s_instance);
// The engine has to have the same thread affinity as the singleton.
Q_ASSERT(qmlEngine->thread() == s_instance->thread());
// There can only be one engine accessing the singleton.
static QJSEngine *s_engine = nullptr;
if (s_engine)
Q_ASSERT(qmlEngine == s_engine);
else
s_engine = qmlEngine;
QJSEngine::setObjectOwnership(s_instance, QJSEngine::CppOwnership);
return s_instance;
}
QHash<int, QByteArray>
ShortcutRegistry::roleNames() const
{
return {
{Roles::Name, "name"}, {Roles::Description, "description"}, {Roles::Shortcut, "shortcut"}};
}
QVariant
ShortcutRegistry::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= m_shortcuts.size() || index.row() < 0)
return {};
switch (role) {
case Roles::Name:
return m_shortcuts[index.row()]->name();
case Roles::Description:
return m_shortcuts[index.row()]->description();
case Roles::Shortcut:
return m_shortcuts[index.row()]->shortcut();
default:
return {};
}
}
void
ShortcutRegistry::changeShortcut(const QString &name, const QString &newShortcut)
{
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;
}
}
}
QString
ShortcutRegistry::keycodeToChar(int keycode) const
{
return QString((char)keycode);
}
void
ShortcutRegistry::registerShortcut(EditableShortcut *action)
{
beginInsertRows({}, m_shortcuts.size(), m_shortcuts.size());
m_shortcuts.push_back(action);
endInsertRows();
}

View file

@ -1,84 +0,0 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QAbstractListModel>
#include <QKeySequence>
#include <QQmlEngine>
class EditableShortcut : public QObject
{
Q_OBJECT
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 shortcut READ shortcut WRITE setShortcut 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);
const QString &name() const { return m_name; }
const QString &description() const { return m_description; }
const QString shortcut() const
{
return m_shortcuts.size() > 0 ? m_shortcuts.first().toString() : QString{};
}
const QStringList shortcuts() const;
void setName(const QString &name);
void setDescription(const QString &description);
void setShortcut(const QString &shortcut);
void setShortcuts(const QStringList &shortcuts);
signals:
void nameChanged();
void descriptionChanged();
void shortcutsChanged();
private:
QString m_name;
QString m_description;
QList<QKeySequence> m_shortcuts;
};
class ShortcutRegistry : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
enum Roles
{
Name,
Description,
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(); }
QVariant data(const QModelIndex &index, int role) const override;
Q_INVOKABLE void changeShortcut(const QString &name, const QString &newShortcut);
Q_INVOKABLE QString keycodeToChar(int keycode) const;
private:
void registerShortcut(EditableShortcut *action);
static ShortcutRegistry *s_instance;
QList<EditableShortcut *> m_shortcuts;
friend EditableShortcut;
};