mirror of https://github.com/Nheko-Reborn/nheko
parent
72410c499d
commit
56dcdd7fd4
@ -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(); |
||||
} |
@ -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 new issue