Commit ccca3b2a authored by Janet Blackquill's avatar Janet Blackquill 🌈
Browse files

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.
parent 9f215a06
......@@ -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,105 +19,50 @@
#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
{
QMetaObject::invokeMethod(m_action, "trigger", Qt::QueuedConnection);
}
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()
const Shortcut& GlobalShortcut::shortcut() const
{
return m_shortcut;
}
void InternalGlobalShortcut::invoke()
SwipeGesture* GlobalShortcut::swipeGesture() const
{
// using QueuedConnection so that we finish the even processing first
QMetaObject::invokeMethod(m_action, "trigger", Qt::QueuedConnection);
return m_gesture.get();
}
GlobalShortcutsManager::GlobalShortcutsManager(QObject *parent)
......@@ -125,19 +71,8 @@ GlobalShortcutsManager::GlobalShortcutsManager(QObject *parent)
{
}
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);
}
for (const auto& cs : m_shortcuts) {
if (sc.shortcut() == cs.shortcut()) {
return false;
}
}
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);
if (std::holds_alternative<FourFingerSwipeShortcut>(sc.shortcut())) {
m_gestureRecognizer->registerGesture(sc.swipeGesture());
}
return cut;
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)
......
......@@ -12,6 +12,7 @@
#include <kwinglobals.h>
// Qt
#include <QKeySequence>
#include <QSharedPointer>
class QAction;
class KGlobalAccelD;
......@@ -97,84 +98,65 @@ 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;
};
class GlobalShortcut
struct KeyboardShortcut
{
public:
virtual ~GlobalShortcut();
const QKeySequence &shortcut() const;
Qt::KeyboardModifiers pointerButtonModifiers() const;
Qt::MouseButtons pointerButtons() const;
SwipeDirection swipeDirection() const {
return m_swipeDirection;
QKeySequence sequence;
bool operator==(const KeyboardShortcut& rhs) const {
return sequence == rhs.sequence;
}
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
struct PointerButtonShortcut
{
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;
QAction *action() const;
SwipeGesture *swipeGesture() const {
return m_swipe.data();
Qt::KeyboardModifiers pointerModifiers;
Qt::MouseButtons pointerButtons;
bool operator==(const PointerButtonShortcut& rhs) const {
return pointerModifiers == rhs.pointerModifiers && pointerButtons == rhs.pointerButtons;
}
private:
QAction *m_action;
QScopedPointer<SwipeGesture> m_swipe;
};
inline
QAction *InternalGlobalShortcut::action() const
struct PointerAxisShortcut
{
return m_action;
}
inline
const QKeySequence &GlobalShortcut::shortcut() const
Qt::KeyboardModifiers axisModifiers;
PointerAxisDirection axisDirection;
bool operator==(const PointerAxisShortcut& rhs) const {
return axisModifiers == rhs.axisModifiers && axisDirection == rhs.axisDirection;
}
};
struct FourFingerSwipeShortcut
{
return m_shortcut;
}
SwipeDirection swipeDirection;
bool operator==(const FourFingerSwipeShortcut& rhs) const {
return swipeDirection == rhs.swipeDirection;
}
};
inline
Qt::KeyboardModifiers GlobalShortcut::pointerButtonModifiers() const
{
return m_pointerModifiers;
}
using Shortcut = std::variant<KeyboardShortcut,PointerButtonShortcut,PointerAxisShortcut,FourFingerSwipeShortcut>;
inline
Qt::MouseButtons GlobalShortcut::pointerButtons() const
class GlobalShortcut
{
return m_pointerButtons;
}
public:
GlobalShortcut(Shortcut&& shortcut, QAction *action);
~GlobalShortcut();
void invoke() const;
QAction *action() const;
const Shortcut& shortcut() const;
SwipeGesture* swipeGesture() const;
private:
QSharedPointer<SwipeGesture> m_gesture;
Shortcut m_shortcut = {};
QAction *m_action = nullptr;
};
} // namespace
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment