Krita Source Code Documentation
Loading...
Searching...
No Matches
KisQtWidgetsTweaker.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2017 Nikita Vertikov <kitmouse.nikita@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
8
9#include <QBitArray>
10#include <QDockWidget>
11#include <QDoubleSpinBox>
12#include <QEvent>
13#include <QKeyEvent>
14#include <QKeySequence>
15#include <QLineEdit>
16#include <QSpinBox>
17
20#include "KisMainWindow.h"
21
23
24
25namespace {
26
27class ShortcutOverriderBase
28{
29public:
30 enum class DecisionOnShortcutOverride {
31 overrideShortcut,
32 askNext,
33 dontOverrideShortcut
34 };
35
36 constexpr ShortcutOverriderBase() = default;
37 virtual ~ShortcutOverriderBase()
38 {}
39 virtual bool interestedInEvent(QKeyEvent *event) = 0;
40 virtual DecisionOnShortcutOverride handleEvent(QObject *receiver, QKeyEvent *event) = 0;
41 virtual DecisionOnShortcutOverride finishedPhysicalKeyPressHandling()
42 {
43 return DecisionOnShortcutOverride::askNext;
44 }
45};
46
47class LineTextEditingShortcutOverrider : public ShortcutOverriderBase
48{
49public:
50 constexpr LineTextEditingShortcutOverrider() = default;
51
52 virtual bool interestedInEvent(QKeyEvent *event) override
53 {
54 static constexpr QKeySequence::StandardKey actionsForQLineEdit[]{
55 QKeySequence::MoveToNextChar
56 ,QKeySequence::MoveToPreviousChar
57 ,QKeySequence::MoveToStartOfLine
58 ,QKeySequence::MoveToEndOfLine
59 ,QKeySequence::MoveToPreviousWord
60 ,QKeySequence::MoveToNextWord
61 ,QKeySequence::SelectPreviousChar
62 ,QKeySequence::SelectNextChar
63 ,QKeySequence::SelectNextWord
64 ,QKeySequence::SelectPreviousWord
65 ,QKeySequence::SelectStartOfLine
66 ,QKeySequence::SelectEndOfLine
67 ,QKeySequence::SelectAll
68 ,QKeySequence::Deselect
69 ,QKeySequence::Backspace
70 ,QKeySequence::DeleteStartOfWord
71 ,QKeySequence::Delete
72 ,QKeySequence::DeleteEndOfWord
73 ,QKeySequence::DeleteEndOfLine
74 ,QKeySequence::Copy
75 ,QKeySequence::Paste
76 ,QKeySequence::Cut
77 ,QKeySequence::Undo
78 ,QKeySequence::Redo
79 };
80 for (QKeySequence::StandardKey sk : actionsForQLineEdit) {
81 if (event->matches(sk)) {
82 event->accept();
83 return true;
84 }
85 }
86 return false;
87 }
88
89 virtual DecisionOnShortcutOverride handleEvent(QObject *receiver, QKeyEvent *event) override
90 {
91 Q_UNUSED(event);
92
93 if ((qobject_cast<QLineEdit*> (receiver) != nullptr)||
94 (qobject_cast<QSpinBox*> (receiver) != nullptr)||
95 (qobject_cast<QDoubleSpinBox*>(receiver) != nullptr)) {
96
97 return DecisionOnShortcutOverride::overrideShortcut;
98 } else {
99 return DecisionOnShortcutOverride::askNext;
100 }
101 }
102};
103
104class SpinboxShortcutOverrider : public ShortcutOverriderBase
105{
106public:
107 constexpr SpinboxShortcutOverrider() = default;
108
109 virtual bool interestedInEvent(QKeyEvent *event) override
110 {
111 if (event->modifiers() != Qt::NoModifier) {
112 return false;
113 }
114 switch (event->key()) {
115 case Qt::Key_Down:
116 case Qt::Key_Up:
117 case Qt::Key_PageDown:
118 case Qt::Key_PageUp:
119 event->accept();
120 return true;
121 default:
122 return false;
123 }
124 }
125 virtual DecisionOnShortcutOverride handleEvent(QObject *receiver, QKeyEvent *event) override
126 {
127 Q_UNUSED(event);
128
129 if (qobject_cast<QSpinBox*> (receiver) != nullptr||
130 qobject_cast<QDoubleSpinBox*>(receiver) != nullptr) {
131
132 return DecisionOnShortcutOverride::overrideShortcut;
133 } else {
134 return DecisionOnShortcutOverride::askNext;
135 }
136
137 }
138};
139
140class TabShortcutOverrider : public ShortcutOverriderBase
141{
142public:
143 constexpr TabShortcutOverrider() = default;
144
145 virtual bool interestedInEvent(QKeyEvent *event) override
146 {
147 bool tab = event->modifiers() == Qt::NoModifier &&
148 ( event->key() == Qt::Key_Tab ||
149 event->key() == Qt::Key_Backtab);
150 bool shiftTab = event->modifiers() == Qt::ShiftModifier &&
151 event->key() == Qt::Key_Backtab;
152 if (tab || shiftTab) {
153 return true;
154 }else{
155 return false;
156 }
157 }
158 virtual DecisionOnShortcutOverride handleEvent(QObject *receiver, QKeyEvent *event) override
159 {
160 Q_UNUSED(event);
161
162 if (qobject_cast<KisQPainterCanvas*>(receiver) != nullptr||
163 qobject_cast<KisOpenGLCanvas2*> (receiver) != nullptr) {
164
165 return DecisionOnShortcutOverride::dontOverrideShortcut;
166 } else {
167 m_nooverride = true;
168 return DecisionOnShortcutOverride::askNext;
169 }
170 }
171 virtual DecisionOnShortcutOverride finishedPhysicalKeyPressHandling() override
172 {
173 if (m_nooverride){
174 m_nooverride = false;
175 return DecisionOnShortcutOverride::overrideShortcut;
176 }
177 return DecisionOnShortcutOverride::askNext;
178 }
179private:
180 bool m_nooverride = false;
181
182};
183
184
185//for some reason I can't just populate constexpr
186//pointer array using "new"
187LineTextEditingShortcutOverrider overrider0;
188SpinboxShortcutOverrider overrider1;
189TabShortcutOverrider overrider2;
190
191constexpr ShortcutOverriderBase *allShortcutOverriders[] = {
192 &overrider0, &overrider1, &overrider2
193};
194
195constexpr int numOfShortcutOverriders =
196 sizeof(allShortcutOverriders)/
197 sizeof(allShortcutOverriders[0]);
198
199
200
201} //namespace
202
204{
205public:
207 : q(parent)
208 {
209 }
210
212
213 QBitArray interestedHandlers = QBitArray(numOfShortcutOverriders);
214 ShortcutOverriderBase::DecisionOnShortcutOverride decision = ShortcutOverriderBase::DecisionOnShortcutOverride::askNext;
216
217 void newPhysicalKeyPressed(QKeyEvent *event)
218 {
219 for (int i=0; i < numOfShortcutOverriders; ++i) {
220 if (allShortcutOverriders[i]->interestedInEvent(event)) {
221 interestedHandlers.setBit(i);
222 }else{
223 interestedHandlers.clearBit(i);
224 }
225 }
226 decision = ShortcutOverriderBase::DecisionOnShortcutOverride::askNext;
228 }
229};
230
232 :QObject(parent)
233 , d(new KisQtWidgetsTweaker::Private(this))
234{
235
236}
237
242bool KisQtWidgetsTweaker::eventFilter(QObject *receiver, QEvent *event)
243{
244 switch(event->type()) {
245 case QEvent::ShortcutOverride:{
246 //QLineEdit and other widgets lets qt's shortcut system take away it's keyboard events
247 //even is it knows them, such as ctrl+backspace
248 //if there is application-wide shortcut, assigned to it.
249 //The following code fixes it
250 //by accepting ShortcutOverride events.
251
252 //if you press key 'a' and then 'b', qt at first call
253 //all handlers for 'a' key press event, and only after that
254 //for 'b'
255 QKeyEvent *key = static_cast<QKeyEvent*>(event);
258 }
259 for(int i = 0; i < numOfShortcutOverriders; ++i) {
260 if (d->decision != ShortcutOverriderBase::DecisionOnShortcutOverride::askNext) {
261 break;
262 }
263 if (d->interestedHandlers.at(i)) {
264 d->decision = allShortcutOverriders[i]->handleEvent(receiver, key);
265 }
266 }
267 //if nothing said whether shortcutoverride to be accepted
268 //last widget that qt will ask will be kismainwindow or docker
269 if (qobject_cast<KisMainWindow*>(receiver)!=nullptr||
270 receiver->inherits(QDockWidget::staticMetaObject.className())) {
271 for (int i = 0; i < numOfShortcutOverriders; ++i) {
272 if (d->decision != ShortcutOverriderBase::DecisionOnShortcutOverride::askNext) {
273 break;
274 }
275 if (d->interestedHandlers.at(i)) {
276 d->decision = allShortcutOverriders[i]->finishedPhysicalKeyPressHandling();
277 }
278 }
279
281 }
282 bool retval = false;
283 switch (d->decision) {
284 case ShortcutOverriderBase::DecisionOnShortcutOverride::askNext:
285 event->ignore();
286 retval = false;
287 break;
288 case ShortcutOverriderBase::DecisionOnShortcutOverride::dontOverrideShortcut:
289 event->ignore();
290 retval = true;
291 break;
292 case ShortcutOverriderBase::DecisionOnShortcutOverride::overrideShortcut:
293 event->accept();
294 //once shortcutoverride accepted, qt stop asking everyone
295 //about it and proceed to handling next event
297 retval = true;
298 break;
299 }
300
301 return retval || QObject::eventFilter(receiver, event);
302
303 }break;
304
305 //other event types
306 default:
307 break;
308 }
309
310
311 //code for tweaking the behavior of other qt elements will go here
312
313
314 return QObject::eventFilter(receiver, event);
315}
316
317
319{
320 return kqwt_instance;
321}
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
KisQtWidgetsTweaker(QObject *parent=nullptr)
static KisQtWidgetsTweaker * instance()
bool eventFilter(QObject *receiver, QEvent *event) override
const KisQtWidgetsTweaker * q
ShortcutOverriderBase::DecisionOnShortcutOverride decision
Private(KisQtWidgetsTweaker *parent)
void newPhysicalKeyPressed(QKeyEvent *event)