mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-21 18:50:47 +03:00
Add working shortcut serialization
This commit is contained in:
parent
72410c499d
commit
56dcdd7fd4
10 changed files with 464 additions and 289 deletions
|
@ -397,8 +397,8 @@ set(SRC_FILES
|
|||
src/ui/RoomSettings.h
|
||||
src/ui/RoomSummary.cpp
|
||||
src/ui/RoomSummary.h
|
||||
src/ui/ShortcutRegistry.cpp
|
||||
src/ui/ShortcutRegistry.h
|
||||
src/ui/KeySequenceRegistry.cpp
|
||||
src/ui/KeySequenceRegistry.h
|
||||
src/ui/Theme.cpp
|
||||
src/ui/Theme.h
|
||||
src/ui/UIA.cpp
|
||||
|
|
|
@ -112,15 +112,14 @@ Pane {
|
|||
onActivated: Qt.quit()
|
||||
}
|
||||
|
||||
EditableShortcut {
|
||||
EditableKeySequence {
|
||||
id: quickSwitcherShortcut
|
||||
|
||||
name: qsTr("Room search")
|
||||
description: qsTr("Opens a search bar for quick switching between rooms")
|
||||
shortcut: "Ctrl+K"
|
||||
defaultKeySequence: "Ctrl+K"
|
||||
}
|
||||
Shortcut {
|
||||
sequence: quickSwitcherShortcut.shortcut
|
||||
sequence: quickSwitcherShortcut.keySequence
|
||||
|
||||
onActivated: {
|
||||
var component = Qt.createComponent("qrc:/resources/qml/QuickSwitcher.qml");
|
||||
|
@ -134,45 +133,98 @@ Pane {
|
|||
}
|
||||
}
|
||||
|
||||
EditableShortcut {
|
||||
EditableKeySequence {
|
||||
id: nextRoomWithActivityShortcut
|
||||
|
||||
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
|
||||
shortcuts: ["Alt+A", "Ctrl+Shift+A"]
|
||||
defaultKeySequences: ["Alt+A", "Ctrl+Shift+A"]
|
||||
}
|
||||
Shortcut {
|
||||
sequences: nextRoomWithActivityShortcut.shortcuts
|
||||
sequences: nextRoomWithActivityShortcut.keySequence
|
||||
|
||||
onActivated: Rooms.nextRoomWithActivity()
|
||||
}
|
||||
|
||||
EditableShortcut {
|
||||
EditableKeySequence {
|
||||
id: nextRoomShortcut
|
||||
|
||||
name: qsTr("Next room")
|
||||
description: qsTr("Switches to the room below the room that is currently open")
|
||||
shortcut: "Ctrl+Down"
|
||||
defaultKeySequence: "Ctrl+Down"
|
||||
}
|
||||
Shortcut {
|
||||
sequence: nextRoomShortcut.shortcut
|
||||
sequence: nextRoomShortcut.keySequence
|
||||
|
||||
onActivated: Rooms.nextRoom()
|
||||
}
|
||||
|
||||
EditableShortcut {
|
||||
EditableKeySequence {
|
||||
id: previousRoomShortcut
|
||||
|
||||
name: qsTr("Previous room")
|
||||
description: qsTr("Switches to the room above the room that is currently open")
|
||||
shortcut: "Ctrl+Up"
|
||||
defaultKeySequence: "Ctrl+Up"
|
||||
}
|
||||
Shortcut {
|
||||
sequence: previousRoomShortcut.shortcut
|
||||
sequence: previousRoomShortcut.keySequence
|
||||
|
||||
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 {
|
||||
function onOpenJoinRoomDialog() {
|
||||
var component = Qt.createComponent("qrc:/resources/qml/dialogs/JoinRoomDialog.qml");
|
||||
|
|
|
@ -28,42 +28,31 @@ ApplicationWindow {
|
|||
anchors.fill: parent
|
||||
|
||||
ListView {
|
||||
model: ShortcutRegistry
|
||||
model: KeySequenceRegistry
|
||||
|
||||
delegate: RowLayout {
|
||||
id: del
|
||||
|
||||
required property string name
|
||||
required property string description
|
||||
required property string shortcut
|
||||
required property string keySequence
|
||||
|
||||
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
|
||||
property bool selectingNewKeySequence: false
|
||||
|
||||
text: selectingNewShortcut ? qsTr("Input..") : del.shortcut
|
||||
onClicked: selectingNewShortcut = !selectingNewShortcut
|
||||
text: selectingNewKeySequence ? qsTr("Input..") : (del.keySequence === "" ? "None" : del.keySequence)
|
||||
onClicked: selectingNewKeySequence = !selectingNewKeySequence
|
||||
Keys.onPressed: event => {
|
||||
if (!selectingNewShortcut)
|
||||
if (!selectingNewKeySequence)
|
||||
return;
|
||||
event.accepted = true;
|
||||
|
||||
|
@ -77,12 +66,14 @@ ApplicationWindow {
|
|||
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)
|
||||
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 += "...";
|
||||
else {
|
||||
keySequence += ShortcutRegistry.keycodeToChar(event.key);
|
||||
ShortcutRegistry.changeShortcut(del.name, keySequence);
|
||||
selectingNewShortcut = false;
|
||||
keySequence += KeySequenceRegistry.keycodeToChar(event.key);
|
||||
KeySequenceRegistry.changeKeySequence(del.name, keySequence);
|
||||
selectingNewKeySequence = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,6 @@ MainWindow *MainWindow::instance_ = nullptr;
|
|||
MainWindow::MainWindow(QWindow *parent)
|
||||
: QQuickView(parent)
|
||||
, userSettings_{UserSettings::instance()}
|
||||
, shortcuts_{new ShortcutRegistry}
|
||||
{
|
||||
instance_ = this;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <QSharedPointer>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include "ShortcutRegistry.h"
|
||||
#include "KeySequenceRegistry.h"
|
||||
#include "UserSettingsPage.h"
|
||||
#include "dock/Dock.h"
|
||||
|
||||
|
@ -141,7 +141,6 @@ private:
|
|||
//! Tray icon that shows the unread message count.
|
||||
TrayIcon *trayIcon_;
|
||||
Dock *dock_;
|
||||
ShortcutRegistry *shortcuts_;
|
||||
|
||||
MxcImageProvider *imgProvider = nullptr;
|
||||
|
||||
|
|
|
@ -30,8 +30,10 @@ QSharedPointer<UserSettings> UserSettings::instance_;
|
|||
|
||||
UserSettings::UserSettings()
|
||||
{
|
||||
connect(
|
||||
QCoreApplication::instance(), &QCoreApplication::aboutToQuit, []() { instance_.clear(); });
|
||||
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, []() {
|
||||
instance_->save();
|
||||
instance_.clear();
|
||||
});
|
||||
}
|
||||
|
||||
QSharedPointer<UserSettings>
|
||||
|
@ -164,6 +166,16 @@ UserSettings::load(std::optional<QString> profile)
|
|||
disableCertificateValidation_ =
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -882,7 +894,7 @@ UserSettings::save()
|
|||
settings.beginGroup(QStringLiteral("sidebar"));
|
||||
settings.setValue(QStringLiteral("community_list_width"), communityListWidth_);
|
||||
settings.setValue(QStringLiteral("room_list_width"), roomListWidth_);
|
||||
settings.endGroup(); // window
|
||||
settings.endGroup(); // sidebar
|
||||
|
||||
settings.beginGroup(QStringLiteral("timeline"));
|
||||
settings.setValue(QStringLiteral("buttons"), buttonsInTimeline_);
|
||||
|
@ -939,6 +951,13 @@ UserSettings::save()
|
|||
settings.setValue(QStringLiteral("space_background_maintenance"), updateSpaceVias_);
|
||||
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
|
||||
|
||||
QString prefix = (profile_ != QLatin1String("") && profile_ != QLatin1String("default"))
|
||||
|
|
241
src/ui/KeySequenceRegistry.cpp
Normal file
241
src/ui/KeySequenceRegistry.cpp
Normal 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();
|
||||
}
|
116
src/ui/KeySequenceRegistry.h
Normal file
116
src/ui/KeySequenceRegistry.h
Normal 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;
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
};
|
Loading…
Reference in a new issue