GlobalShortcuts: refactor

I consider this a prerequisite commit to improving the quality of touchpad gestures provided by KWin.
In short, this breaks apart a weird all-in-one class into an std::variant for the different types
of shortcuts in order to make it easier to add new types of global shortcuts. For the byte shavers, this
roughly halves the size of the GlobalShortcut class as well. On top of all this, the code is about half
the size it was before, mostly due to newer C++ concepts being used that allow us to let the compiler
do more work for us.
This commit is contained in:
Jan Blackquill 2021-03-29 19:51:15 -04:00
parent 9f215a06fa
commit ccca3b2af2
2 changed files with 115 additions and 216 deletions

View file

@ -10,6 +10,7 @@
#include "globalshortcuts.h"
// kwin
#include <config-kwin.h>
#include "kwinglobals.h"
#include "main.h"
#include "gestures.h"
#include "utils.h"
@ -18,126 +19,60 @@
#include <KGlobalAccel/private/kglobalaccel_interface.h>
// Qt
#include <QAction>
#include <variant>
namespace KWin
{
uint qHash(SwipeDirection direction)
{
return uint(direction);
}
GlobalShortcut::GlobalShortcut(const QKeySequence &shortcut)
: m_shortcut(shortcut)
, m_pointerModifiers(Qt::NoModifier)
, m_pointerButtons(Qt::NoButton)
{
}
GlobalShortcut::GlobalShortcut(Qt::KeyboardModifiers pointerButtonModifiers, Qt::MouseButtons pointerButtons)
: m_shortcut(QKeySequence())
, m_pointerModifiers(pointerButtonModifiers)
, m_pointerButtons(pointerButtons)
{
}
GlobalShortcut::GlobalShortcut(Qt::KeyboardModifiers modifiers)
: m_shortcut(QKeySequence())
, m_pointerModifiers(modifiers)
, m_pointerButtons(Qt::NoButton)
{
}
GlobalShortcut::GlobalShortcut(SwipeDirection direction)
: m_shortcut(QKeySequence())
, m_pointerModifiers(Qt::NoModifier)
, m_pointerButtons(Qt::NoButton)
, m_swipeDirection(direction)
GlobalShortcut::GlobalShortcut(Shortcut&& sc, QAction* action) : m_shortcut(sc), m_action(action)
{
static const QMap<SwipeDirection,SwipeGesture::Direction> dirs = {
{SwipeDirection::Up, SwipeGesture::Direction::Up},
{SwipeDirection::Down, SwipeGesture::Direction::Down},
{SwipeDirection::Left, SwipeGesture::Direction::Left},
{SwipeDirection::Right, SwipeGesture::Direction::Right},
};
if (auto swipeGesture = std::get_if<FourFingerSwipeShortcut>(&sc)) {
m_gesture.reset(new SwipeGesture);
m_gesture->setDirection(dirs[swipeGesture->swipeDirection]);
m_gesture->setMaximumFingerCount(4);
m_gesture->setMinimumFingerCount(4);
QObject::connect(m_gesture.get(), &SwipeGesture::triggered, m_action, &QAction::trigger, Qt::QueuedConnection);
}
}
GlobalShortcut::~GlobalShortcut()
{
}
InternalGlobalShortcut::InternalGlobalShortcut(Qt::KeyboardModifiers modifiers, const QKeySequence &shortcut, QAction *action)
: GlobalShortcut(shortcut)
, m_action(action)
QAction* GlobalShortcut::action() const
{
Q_UNUSED(modifiers)
return m_action;
}
InternalGlobalShortcut::InternalGlobalShortcut(Qt::KeyboardModifiers pointerButtonModifiers, Qt::MouseButtons pointerButtons, QAction *action)
: GlobalShortcut(pointerButtonModifiers, pointerButtons)
, m_action(action)
void GlobalShortcut::invoke() const
{
}
InternalGlobalShortcut::InternalGlobalShortcut(Qt::KeyboardModifiers axisModifiers, PointerAxisDirection axis, QAction *action)
: GlobalShortcut(axisModifiers)
, m_action(action)
{
Q_UNUSED(axis)
}
static SwipeGesture::Direction toSwipeDirection(SwipeDirection direction)
{
switch (direction) {
case SwipeDirection::Up:
return SwipeGesture::Direction::Up;
case SwipeDirection::Down:
return SwipeGesture::Direction::Down;
case SwipeDirection::Left:
return SwipeGesture::Direction::Left;
case SwipeDirection::Right:
return SwipeGesture::Direction::Right;
case SwipeDirection::Invalid:
default:
Q_UNREACHABLE();
}
}
InternalGlobalShortcut::InternalGlobalShortcut(Qt::KeyboardModifiers swipeModifier, SwipeDirection direction, QAction *action)
: GlobalShortcut(direction)
, m_action(action)
, m_swipe(new SwipeGesture)
{
Q_UNUSED(swipeModifier)
m_swipe->setDirection(toSwipeDirection(direction));
m_swipe->setMinimumFingerCount(4);
m_swipe->setMaximumFingerCount(4);
QObject::connect(m_swipe.data(), &SwipeGesture::triggered, m_action, &QAction::trigger, Qt::QueuedConnection);
}
InternalGlobalShortcut::~InternalGlobalShortcut()
{
}
void InternalGlobalShortcut::invoke()
{
// using QueuedConnection so that we finish the even processing first
QMetaObject::invokeMethod(m_action, "trigger", Qt::QueuedConnection);
}
const Shortcut& GlobalShortcut::shortcut() const
{
return m_shortcut;
}
SwipeGesture* GlobalShortcut::swipeGesture() const
{
return m_gesture.get();
}
GlobalShortcutsManager::GlobalShortcutsManager(QObject *parent)
: QObject(parent)
, m_gestureRecognizer(new GestureRecognizer(this))
{
}
template <typename T>
void clearShortcuts(T &shortcuts)
{
for (auto it = shortcuts.begin(); it != shortcuts.end(); ++it) {
qDeleteAll((*it));
}
}
GlobalShortcutsManager::~GlobalShortcutsManager()
{
clearShortcuts(m_pointerShortcuts);
clearShortcuts(m_axisShortcuts);
clearShortcuts(m_swipeShortcuts);
}
void GlobalShortcutsManager::init()
@ -155,80 +90,47 @@ void GlobalShortcutsManager::init()
}
}
template <typename T>
void handleDestroyedAction(QObject *object, T &shortcuts)
void GlobalShortcutsManager::objectDeleted(QObject *object)
{
for (auto it = shortcuts.begin(); it != shortcuts.end(); ++it) {
auto &list = it.value();
auto it2 = list.begin();
while (it2 != list.end()) {
if (InternalGlobalShortcut *shortcut = dynamic_cast<InternalGlobalShortcut*>(it2.value())) {
if (shortcut->action() == object) {
it2 = list.erase(it2);
delete shortcut;
continue;
}
}
++it2;
auto it = m_shortcuts.begin();
while (it != m_shortcuts.end()) {
if (it->action() == object) {
it = m_shortcuts.erase(it);
} else {
++it;
}
}
}
void GlobalShortcutsManager::objectDeleted(QObject *object)
bool GlobalShortcutsManager::addIfNotExists(GlobalShortcut sc)
{
handleDestroyedAction(object, m_pointerShortcuts);
handleDestroyedAction(object, m_axisShortcuts);
handleDestroyedAction(object, m_swipeShortcuts);
}
template <typename T, typename R>
GlobalShortcut *addShortcut(T &shortcuts, QAction *action, Qt::KeyboardModifiers modifiers, R value)
{
GlobalShortcut *cut = new InternalGlobalShortcut(modifiers, value, action);
auto it = shortcuts.find(modifiers);
if (it != shortcuts.end()) {
// TODO: check if shortcut already exists
(*it).insert(value, cut);
} else {
QHash<R, GlobalShortcut*> s;
s.insert(value, cut);
shortcuts.insert(modifiers, s);
for (const auto& cs : m_shortcuts) {
if (sc.shortcut() == cs.shortcut()) {
return false;
}
}
return cut;
if (std::holds_alternative<FourFingerSwipeShortcut>(sc.shortcut())) {
m_gestureRecognizer->registerGesture(sc.swipeGesture());
}
connect(sc.action(), &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted);
m_shortcuts.push_back(std::move(sc));
return true;
}
void GlobalShortcutsManager::registerPointerShortcut(QAction *action, Qt::KeyboardModifiers modifiers, Qt::MouseButtons pointerButtons)
{
addShortcut(m_pointerShortcuts, action, modifiers, pointerButtons);
connect(action, &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted);
addIfNotExists(GlobalShortcut(PointerButtonShortcut{ modifiers, pointerButtons }, action));
}
void GlobalShortcutsManager::registerAxisShortcut(QAction *action, Qt::KeyboardModifiers modifiers, PointerAxisDirection axis)
{
addShortcut(m_axisShortcuts, action, modifiers, axis);
connect(action, &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted);
addIfNotExists(GlobalShortcut(PointerAxisShortcut{ modifiers, axis }, action));
}
void GlobalShortcutsManager::registerTouchpadSwipe(QAction *action, SwipeDirection direction)
{
auto shortcut = addShortcut(m_swipeShortcuts, action, Qt::NoModifier, direction);
connect(action, &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted);
m_gestureRecognizer->registerGesture(static_cast<InternalGlobalShortcut*>(shortcut)->swipeGesture());
}
template <typename T, typename U>
bool processShortcut(Qt::KeyboardModifiers mods, T key, U &shortcuts)
{
auto it = shortcuts.find(mods);
if (it == shortcuts.end()) {
return false;
}
auto it2 = (*it).find(key);
if (it2 == (*it).end()) {
return false;
}
it2.value()->invoke();
return true;
addIfNotExists(GlobalShortcut(FourFingerSwipeShortcut{ direction }, action));
}
bool GlobalShortcutsManager::processKey(Qt::KeyboardModifiers mods, int keyQt)
@ -267,14 +169,29 @@ bool GlobalShortcutsManager::processKey(Qt::KeyboardModifiers mods, int keyQt)
return false;
}
template<typename ShortcutKind, typename... Args>
bool match(QVector<GlobalShortcut> &shortcuts, Args... args)
{
for (auto& sc : shortcuts) {
if (std::holds_alternative<ShortcutKind>(sc.shortcut())) {
if (std::get<ShortcutKind>(sc.shortcut()) == ShortcutKind{args...}) {
sc.invoke();
return true;
}
}
}
return false;
}
//TODO(C++20): use ranges for a nicer way of filtering by shortcut type
bool GlobalShortcutsManager::processPointerPressed(Qt::KeyboardModifiers mods, Qt::MouseButtons pointerButtons)
{
return processShortcut(mods, pointerButtons, m_pointerShortcuts);
return match<PointerButtonShortcut>(m_shortcuts, mods, pointerButtons);
}
bool GlobalShortcutsManager::processAxis(Qt::KeyboardModifiers mods, PointerAxisDirection axis)
{
return processShortcut(mods, axis, m_axisShortcuts);
return match<PointerAxisShortcut>(m_shortcuts, mods, axis);
}
void GlobalShortcutsManager::processSwipeStart(uint fingerCount)

View file

@ -12,6 +12,7 @@
#include <kwinglobals.h>
// Qt
#include <QKeySequence>
#include <QSharedPointer>
class QAction;
class KGlobalAccelD;
@ -97,85 +98,66 @@ public:
private:
void objectDeleted(QObject *object);
QHash<Qt::KeyboardModifiers, QHash<Qt::MouseButtons, GlobalShortcut*> > m_pointerShortcuts;
QHash<Qt::KeyboardModifiers, QHash<PointerAxisDirection, GlobalShortcut*> > m_axisShortcuts;
QHash<Qt::KeyboardModifiers, QHash<SwipeDirection, GlobalShortcut*> > m_swipeShortcuts;
bool addIfNotExists(GlobalShortcut sc);
QVector<GlobalShortcut> m_shortcuts;
KGlobalAccelD *m_kglobalAccel = nullptr;
KGlobalAccelInterface *m_kglobalAccelInterface = nullptr;
GestureRecognizer *m_gestureRecognizer;
};
struct KeyboardShortcut
{
QKeySequence sequence;
bool operator==(const KeyboardShortcut& rhs) const {
return sequence == rhs.sequence;
}
};
struct PointerButtonShortcut
{
Qt::KeyboardModifiers pointerModifiers;
Qt::MouseButtons pointerButtons;
bool operator==(const PointerButtonShortcut& rhs) const {
return pointerModifiers == rhs.pointerModifiers && pointerButtons == rhs.pointerButtons;
}
};
struct PointerAxisShortcut
{
Qt::KeyboardModifiers axisModifiers;
PointerAxisDirection axisDirection;
bool operator==(const PointerAxisShortcut& rhs) const {
return axisModifiers == rhs.axisModifiers && axisDirection == rhs.axisDirection;
}
};
struct FourFingerSwipeShortcut
{
SwipeDirection swipeDirection;
bool operator==(const FourFingerSwipeShortcut& rhs) const {
return swipeDirection == rhs.swipeDirection;
}
};
using Shortcut = std::variant<KeyboardShortcut,PointerButtonShortcut,PointerAxisShortcut,FourFingerSwipeShortcut>;
class GlobalShortcut
{
public:
virtual ~GlobalShortcut();
const QKeySequence &shortcut() const;
Qt::KeyboardModifiers pointerButtonModifiers() const;
Qt::MouseButtons pointerButtons() const;
SwipeDirection swipeDirection() const {
return m_swipeDirection;
}
virtual void invoke() = 0;
protected:
GlobalShortcut(const QKeySequence &shortcut);
GlobalShortcut(Qt::KeyboardModifiers pointerButtonModifiers, Qt::MouseButtons pointerButtons);
GlobalShortcut(Qt::KeyboardModifiers axisModifiers);
GlobalShortcut(SwipeDirection direction);
private:
QKeySequence m_shortcut;
Qt::KeyboardModifiers m_pointerModifiers;
Qt::MouseButtons m_pointerButtons;
SwipeDirection m_swipeDirection = SwipeDirection::Invalid;;
};
class InternalGlobalShortcut : public GlobalShortcut
{
public:
InternalGlobalShortcut(Qt::KeyboardModifiers modifiers, const QKeySequence &shortcut, QAction *action);
InternalGlobalShortcut(Qt::KeyboardModifiers pointerButtonModifiers, Qt::MouseButtons pointerButtons, QAction *action);
InternalGlobalShortcut(Qt::KeyboardModifiers axisModifiers, PointerAxisDirection axis, QAction *action);
InternalGlobalShortcut(Qt::KeyboardModifiers swipeModifier, SwipeDirection direction, QAction *action);
~InternalGlobalShortcut() override;
void invoke() override;
GlobalShortcut(Shortcut&& shortcut, QAction *action);
~GlobalShortcut();
void invoke() const;
QAction *action() const;
const Shortcut& shortcut() const;
SwipeGesture* swipeGesture() const;
SwipeGesture *swipeGesture() const {
return m_swipe.data();
}
private:
QAction *m_action;
QScopedPointer<SwipeGesture> m_swipe;
QSharedPointer<SwipeGesture> m_gesture;
Shortcut m_shortcut = {};
QAction *m_action = nullptr;
};
inline
QAction *InternalGlobalShortcut::action() const
{
return m_action;
}
inline
const QKeySequence &GlobalShortcut::shortcut() const
{
return m_shortcut;
}
inline
Qt::KeyboardModifiers GlobalShortcut::pointerButtonModifiers() const
{
return m_pointerModifiers;
}
inline
Qt::MouseButtons GlobalShortcut::pointerButtons() const
{
return m_pointerButtons;
}
} // namespace
#endif