Krita Source Code Documentation
Loading...
Searching...
No Matches
KisLongPressEventFilter Class Reference

#include <KisLongPressEventFilter.h>

+ Inheritance diagram for KisLongPressEventFilter:

Public Member Functions

bool eventFilter (QObject *watched, QEvent *event) override
 
 KisLongPressEventFilter (QObject *parent=nullptr)
 

Static Public Attributes

static constexpr char ENABLED_PROPERTY [] = "KRITA_LONG_PRESS"
 

Private Member Functions

void cancel ()
 
void flush ()
 
int getKineticScrollDelay (QWidget *target) const
 
bool handleMouseMove (const QMouseEvent *me)
 
bool handleMousePress (QWidget *target, const QMouseEvent *me)
 
bool isWithinDistance (const QPoint &globalPos) const
 
void triggerLongPress ()
 

Static Private Member Functions

static bool isContextMenuTarget (QWidget *target)
 
static bool isLongPressableWidget (QWidget *target)
 
static const QScroller * searchScroller (QWidget *target)
 

Private Attributes

long long m_distanceSquared = 0LL
 
bool m_handlingEvent = false
 
QPoint m_pressGlobalPos
 
QPoint m_pressLocalPos
 
QPointer< QWidget > m_target
 
QTimer * m_timer
 

Static Private Attributes

static constexpr int MINIMUM_DELAY = 100
 
static constexpr int MINIMUM_DISTANCE = 0
 

Detailed Description

Definition at line 13 of file KisLongPressEventFilter.h.

Constructor & Destructor Documentation

◆ KisLongPressEventFilter()

KisLongPressEventFilter::KisLongPressEventFilter ( QObject * parent = nullptr)
explicit

Definition at line 24 of file KisLongPressEventFilter.cpp.

25 : QObject(parent)
26{
27 m_timer = new QTimer(this);
28 m_timer->setTimerType(Qt::CoarseTimer);
29 m_timer->setSingleShot(true);
30 connect(m_timer, &QTimer::timeout, this, &KisLongPressEventFilter::triggerLongPress);
31#ifdef Q_OS_ANDROID
32 m_longPressTimeout =
33 QAndroidJniObject::callStaticMethod<jint>("org/krita/android/MainActivity", "getLongPressTimeout", "()I");
34#endif
35}

References m_timer, and triggerLongPress().

Member Function Documentation

◆ cancel()

void KisLongPressEventFilter::cancel ( )
private

Definition at line 137 of file KisLongPressEventFilter.cpp.

138{
139 m_timer->stop();
140 m_target.clear();
141}

References m_target, and m_timer.

◆ eventFilter()

bool KisLongPressEventFilter::eventFilter ( QObject * watched,
QEvent * event )
override

Definition at line 37 of file KisLongPressEventFilter.cpp.

38{
39 if (!m_handlingEvent) {
40 QScopedValueRollback rollback(m_handlingEvent, true);
41 switch (event->type()) {
42 case QEvent::MouseButtonPress:
43 if (handleMousePress(qobject_cast<QWidget *>(watched), static_cast<QMouseEvent *>(event))) {
44 event->setAccepted(true);
45 return true;
46 }
47 break;
48 case QEvent::MouseMove:
49 if (handleMouseMove(static_cast<QMouseEvent *>(event))) {
50 return true;
51 }
52 break;
53 case QEvent::MouseButtonDblClick:
54 case QEvent::MouseButtonRelease:
55 flush();
56 break;
57 default:
58 break;
59 }
60 }
61 return QObject::eventFilter(watched, event);
62}
bool handleMouseMove(const QMouseEvent *me)
bool handleMousePress(QWidget *target, const QMouseEvent *me)

References flush(), handleMouseMove(), handleMousePress(), and m_handlingEvent.

◆ flush()

void KisLongPressEventFilter::flush ( )
private

Definition at line 122 of file KisLongPressEventFilter.cpp.

123{
124 QWidget *target = m_target.data();
125 cancel();
126 if (target && target->isVisible()) {
127 QMouseEvent event(QEvent::MouseButtonPress,
130 Qt::LeftButton,
131 Qt::LeftButton,
132 Qt::NoModifier);
133 qApp->sendEvent(target, &event);
134 }
135}
KisMagneticGraph::vertex_descriptor target(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)

References cancel(), m_pressGlobalPos, m_pressLocalPos, m_target, and target().

◆ getKineticScrollDelay()

int KisLongPressEventFilter::getKineticScrollDelay ( QWidget * target) const
private

Definition at line 182 of file KisLongPressEventFilter.cpp.

183{
184 if (KisKineticScroller::getConfiguredGestureType() != QScroller::LeftMouseButtonGesture) {
185 return 0;
186 }
187
188 const QScroller *scroller = searchScroller(target);
189 if (!scroller) {
190 return 0;
191 }
192
193 qreal pressDelay = scroller->scrollerProperties().scrollMetric(QScrollerProperties::MousePressEventDelay).toReal();
194 if (pressDelay < 0.0) {
195 return 0;
196 }
197
198 return int(pressDelay * 1000.0);
199}
static const QScroller * searchScroller(QWidget *target)
KRITAWIDGETUTILS_EXPORT QScroller::ScrollerGestureType getConfiguredGestureType()

References KisKineticScroller::getConfiguredGestureType(), searchScroller(), and target().

◆ handleMouseMove()

bool KisLongPressEventFilter::handleMouseMove ( const QMouseEvent * me)
private

Definition at line 105 of file KisLongPressEventFilter.cpp.

106{
107 if (m_timer->isActive()) {
108#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
109 QPoint globalPos = me->globalPos();
110#else
111 QPoint globalPos = me->globalPosition().toPoint();
112#endif
113 if (isWithinDistance(globalPos)) {
114 return true;
115 } else {
116 flush();
117 }
118 }
119 return false;
120}
bool isWithinDistance(const QPoint &globalPos) const

References flush(), isWithinDistance(), and m_timer.

◆ handleMousePress()

bool KisLongPressEventFilter::handleMousePress ( QWidget * target,
const QMouseEvent * me )
private

Definition at line 64 of file KisLongPressEventFilter.cpp.

65{
66 if (me->buttons() == Qt::LeftButton && me->modifiers() == Qt::NoModifier && isContextMenuTarget(target)) {
67 const QStyleHints *sh = qApp->styleHints();
68 long long distance = qMax(MINIMUM_DISTANCE, sh->startDragDistance());
70#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
71 m_pressLocalPos = me->pos();
72 m_pressGlobalPos = me->globalPos();
73#else
74 m_pressLocalPos = me->position().toPoint();
75 m_pressGlobalPos = me->globalPosition().toPoint();
76#endif
77 // Kinetic scrolling may have already delayed the input, so subtract
78 // that from the intended delay to avoid waiting for it twice. This may
79 // end up with a delay of zero, but that's okay. Also, the user may have
80 // already dragged far enough for the cursor to be outside of the
81 // long-press distance (it only checks the axes that are scrollable), in
82 // which case we bail out here.
83 int kineticScrollDelay = getKineticScrollDelay(target);
84 if (kineticScrollDelay == 0 || isWithinDistance(QCursor::pos())) {
86#ifdef Q_OS_ANDROID
87 int longPressInterval = m_longPressTimeout;
88#else
89 int longPressInterval = sh->mousePressAndHoldInterval();
90#endif
91 if (longPressInterval < MINIMUM_DELAY) {
92 longPressInterval = MINIMUM_DELAY;
93 }
94 // Kinetic scrolling may have already delayed the input, so subtract
95 // that from the intended delay to avoid waiting for it twice. This
96 // may end up with a delay of zero, but that's okay.
97 m_timer->start(qMax(0, longPressInterval - kineticScrollDelay));
98 return true;
99 }
100 }
101 cancel();
102 return false;
103}
qreal distance(const QPointF &p1, const QPointF &p2)
int getKineticScrollDelay(QWidget *target) const
static constexpr int MINIMUM_DISTANCE
static constexpr int MINIMUM_DELAY
static bool isContextMenuTarget(QWidget *target)

References cancel(), distance(), getKineticScrollDelay(), isContextMenuTarget(), isWithinDistance(), m_distanceSquared, m_pressGlobalPos, m_pressLocalPos, m_target, m_timer, MINIMUM_DELAY, MINIMUM_DISTANCE, and target().

◆ isContextMenuTarget()

bool KisLongPressEventFilter::isContextMenuTarget ( QWidget * target)
staticprivate

Definition at line 213 of file KisLongPressEventFilter.cpp.

214{
215 while (target && target->isVisible() && isLongPressableWidget(target)) {
216 switch (target->contextMenuPolicy()) {
217 case Qt::NoContextMenu:
218 target = target->parentWidget();
219 break;
220 case Qt::PreventContextMenu:
221 return false;
222 default:
223 return true;
224 }
225 }
226 return false;
227}
static bool isLongPressableWidget(QWidget *target)

References isLongPressableWidget(), and target().

◆ isLongPressableWidget()

bool KisLongPressEventFilter::isLongPressableWidget ( QWidget * target)
staticprivate

Definition at line 229 of file KisLongPressEventFilter.cpp.

230{
231 QVariant prop = target->property(ENABLED_PROPERTY);
232 if (prop.isValid()) {
233 return prop.toBool();
234 }
235
236 // Several widget types don't have desirable long-press behavior. If they're
237 // our own widgets, we should just fix that in the widget, but we can"t
238 // really sensibly do that to Qt's widgets. Luckily however, those widgets
239 // really shouldn't have context menus in the first place, so we can just
240 // disregard them as long-press targets and be done with it.
241
242 // Buttons get depressed when you click and hold on them and won't pop back
243 // up if a context menu is opened during that. They may also have a menu
244 // attached to them that the user operates like a context menu.
245 if (qobject_cast<QAbstractButton *>(target)) {
246 return false;
247 }
248
249 // Slightly non-obvious, but this thing has many child classes, like text
250 // areas or the canvas, none of which have sensible context menus.
251 if (qobject_cast<QAbstractScrollArea *>(target)) {
252 return false;
253 }
254
255 // Sliders (including scrollbars) may get dragged slowly.
256 if (qobject_cast<QAbstractSlider *>(target)) {
257 return false;
258 }
259
260 // Spinners may get held down to keep a number spinning.
261 if (qobject_cast<QAbstractSpinBox *>(target)) {
262 return false;
263 }
264
265 // Combo boxes get held down to pick an item.
266 if (qobject_cast<QComboBox *>(target)) {
267 return false;
268 }
269
270 // Text editing will have its own long-press handling.
271 if (qobject_cast<QLineEdit *>(target)) {
272 return false;
273 }
274
275 // Menus don't have context menus.
276 if (qobject_cast<QMenu *>(target)) {
277 return false;
278 }
279
280 // Menu bars don't have context menus either.
281 if (qobject_cast<QMenuBar *>(target)) {
282 return false;
283 }
284
285 return true;
286}
static constexpr char ENABLED_PROPERTY[]

References ENABLED_PROPERTY, and target().

◆ isWithinDistance()

bool KisLongPressEventFilter::isWithinDistance ( const QPoint & globalPos) const
private

Definition at line 143 of file KisLongPressEventFilter.cpp.

144{
145 long long x = globalPos.x() - m_pressGlobalPos.x();
146 long long y = globalPos.y() - m_pressGlobalPos.y();
147 return (x * x) + (y * y) <= m_distanceSquared;
148}

References m_distanceSquared, and m_pressGlobalPos.

◆ searchScroller()

const QScroller * KisLongPressEventFilter::searchScroller ( QWidget * target)
staticprivate

Definition at line 201 of file KisLongPressEventFilter.cpp.

202{
203 while (target) {
204 if (QScroller::hasScroller(target)) {
205 return QScroller::scroller(target);
206 } else {
207 target = target->parentWidget();
208 }
209 }
210 return nullptr;
211}

References target().

◆ triggerLongPress()

void KisLongPressEventFilter::triggerLongPress ( )
private

Definition at line 150 of file KisLongPressEventFilter.cpp.

151{
152 QWidget *target = m_target.data();
153 cancel();
155 QScopedValueRollback rollback(m_handlingEvent, true);
156 // First we synchronously send a right click press and release so that
157 // the target widget can update its state correctly. Afterwards we post
158 // the context menu event to it so that'll open. As far as I can tell,
159 // that's what Qt does when you right-click on a widget as well.
160 {
161 QMouseEvent pressEvent(QEvent::MouseButtonPress,
164 Qt::RightButton,
165 Qt::RightButton,
166 Qt::NoModifier);
167 qApp->sendEvent(target, &pressEvent);
168 }
169 {
170 QMouseEvent releaseEvent(QEvent::MouseButtonRelease,
173 Qt::RightButton,
174 Qt::NoButton,
175 Qt::NoModifier);
176 qApp->sendEvent(target, &releaseEvent);
177 }
178 qApp->postEvent(target, new QContextMenuEvent(QContextMenuEvent::Mouse, m_pressLocalPos, m_pressGlobalPos));
179 }
180}

References cancel(), isContextMenuTarget(), m_handlingEvent, m_pressGlobalPos, m_pressLocalPos, m_target, and target().

Member Data Documentation

◆ ENABLED_PROPERTY

constexpr char KisLongPressEventFilter::ENABLED_PROPERTY[] = "KRITA_LONG_PRESS"
staticconstexpr

Definition at line 17 of file KisLongPressEventFilter.h.

◆ m_distanceSquared

long long KisLongPressEventFilter::m_distanceSquared = 0LL
private

Definition at line 43 of file KisLongPressEventFilter.h.

◆ m_handlingEvent

bool KisLongPressEventFilter::m_handlingEvent = false
private

Definition at line 50 of file KisLongPressEventFilter.h.

◆ m_pressGlobalPos

QPoint KisLongPressEventFilter::m_pressGlobalPos
private

Definition at line 45 of file KisLongPressEventFilter.h.

◆ m_pressLocalPos

QPoint KisLongPressEventFilter::m_pressLocalPos
private

Definition at line 44 of file KisLongPressEventFilter.h.

◆ m_target

QPointer<QWidget> KisLongPressEventFilter::m_target
private

Definition at line 46 of file KisLongPressEventFilter.h.

◆ m_timer

QTimer* KisLongPressEventFilter::m_timer
private

Definition at line 42 of file KisLongPressEventFilter.h.

◆ MINIMUM_DELAY

constexpr int KisLongPressEventFilter::MINIMUM_DELAY = 100
staticconstexprprivate

Definition at line 24 of file KisLongPressEventFilter.h.

◆ MINIMUM_DISTANCE

constexpr int KisLongPressEventFilter::MINIMUM_DISTANCE = 0
staticconstexprprivate

Definition at line 25 of file KisLongPressEventFilter.h.


The documentation for this class was generated from the following files: