Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickhoverhandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <private/qquicksinglepointhandler_p_p.h>
7#include <private/qquickdeliveryagent_p.h>
8#include <private/qquickitem_p.h>
9
11
12Q_STATIC_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
13
14/*!
15 \qmltype HoverHandler
16 \nativetype QQuickHoverHandler
17 \inherits SinglePointHandler
18 \inqmlmodule QtQuick
19 \ingroup qtquick-input-handlers
20 \brief Handler for mouse and tablet hover.
21
22 HoverHandler detects a hovering mouse or tablet stylus cursor.
23
24 A binding to the \l hovered property is the easiest way to react when the
25 cursor enters or leaves the \l {PointerHandler::parent}{parent} Item.
26 The \l {SinglePointHandler::point}{point} property provides more detail,
27 including the cursor position. The
28 \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices},
29 \l {PointerDeviceHandler::acceptedPointerTypes}{acceptedPointerTypes},
30 and \l {PointerDeviceHandler::acceptedModifiers}{acceptedModifiers}
31 properties can be used to narrow the behavior to detect hovering of
32 specific kinds of devices or while holding a modifier key.
33
34 The \l cursorShape property allows changing the cursor whenever
35 \l hovered changes to \c true.
36
37 \sa MouseArea, PointHandler, {Qt Quick Examples - Pointer Handlers}
38*/
39
40class QQuickHoverHandlerPrivate : public QQuickSinglePointHandlerPrivate
41{
42 Q_DECLARE_PUBLIC(QQuickHoverHandler)
43
44public:
45 void onEnabledChanged() override;
46 void onParentChanged(QQuickItem *oldParent, QQuickItem *newParent) override;
47
48 void updateHasHoverInChild(QQuickItem *item, bool hasHover);
49};
50
51void QQuickHoverHandlerPrivate::onEnabledChanged()
52{
53 Q_Q(QQuickHoverHandler);
54
55 if (auto parent = q->parentItem())
56 updateHasHoverInChild(parent, enabled);
57 if (!enabled)
58 q->setHovered(false);
59
60 QQuickSinglePointHandlerPrivate::onEnabledChanged();
61}
62
63void QQuickHoverHandlerPrivate::onParentChanged(QQuickItem *oldParent, QQuickItem *newParent)
64{
65 if (oldParent)
66 updateHasHoverInChild(oldParent, false);
67 if (newParent)
68 updateHasHoverInChild(newParent, true);
69
70 QQuickSinglePointHandlerPrivate::onParentChanged(oldParent, newParent);
71}
72
73void QQuickHoverHandlerPrivate::updateHasHoverInChild(QQuickItem *item, bool hasHover)
74{
75 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
76 itemPriv->setHasHoverInChild(hasHover);
77 // The DA needs to resolve which items and handlers should now be hovered or unhovered.
78 // Marking the parent item dirty ensures that flushFrameSynchronousEvents() will be called from the render loop,
79 // even if this change is not in response to a mouse event and no item has already marked itself dirty.
80 itemPriv->dirty(QQuickItemPrivate::Content);
81}
82
83QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
84 : QQuickSinglePointHandler(*(new QQuickHoverHandlerPrivate), parent)
85{
86 Q_D(QQuickHoverHandler);
87 // Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state
88 d->acceptedButtons = Qt::NoButton;
89 if (parent)
90 d->updateHasHoverInChild(parent, true);
91}
92
93QQuickHoverHandler::~QQuickHoverHandler()
94{
95 Q_D(QQuickHoverHandler);
96 if (auto parent = parentItem())
97 d->updateHasHoverInChild(parent, false);
98}
99
100/*!
101 \qmlproperty bool QtQuick::HoverHandler::blocking
102 \since 6.3
103
104 Whether this handler prevents other items or handlers behind it from
105 being hovered at the same time. This property is \c false by default.
106*/
107void QQuickHoverHandler::setBlocking(bool blocking)
108{
109 if (m_blocking == blocking)
110 return;
111
112 m_blocking = blocking;
113 emit blockingChanged();
114}
115
116bool QQuickHoverHandler::event(QEvent *event)
117{
118 switch (event->type())
119 {
120 case QEvent::HoverLeave:
121 setHovered(false);
122 setActive(false);
123 break;
124 default:
125 return QQuickSinglePointHandler::event(event);
126 break;
127 }
128
129 return true;
130}
131
132void QQuickHoverHandler::componentComplete()
133{
134 Q_D(QQuickHoverHandler);
135 QQuickSinglePointHandler::componentComplete();
136
137 if (d->enabled) {
138 if (auto parent = parentItem())
139 d->updateHasHoverInChild(parent, true);
140 }
141}
142
143bool QQuickHoverHandler::wantsPointerEvent(QPointerEvent *event)
144{
145 // No state change should occur if a button is being pressed or released.
146 if (event->isSinglePointEvent() && static_cast<QSinglePointEvent *>(event)->button())
147 return false;
148 auto &point = event->point(0);
149 const bool inside = parentContains(point);
150 if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(event, point) && inside) {
151 // assume this is a mouse or tablet event, so there's only one point
152 setPointId(point.id());
153 return true;
154 }
155
156 // Some hover events come from QQuickWindow::tabletEvent(). In between,
157 // some hover events come from QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(),
158 // but those look like mouse events. If a particular HoverHandler instance
159 // is filtering for tablet events only (e.g. by setting
160 // acceptedDevices:PointerDevice.Stylus), those events should not cause
161 // the hovered property to transition to false prematurely.
162 // If a QQuickPointerTabletEvent caused the hovered property to become true,
163 // then only another QQuickPointerTabletEvent can make it become false.
164 // But after kCursorOverrideTimeout ms, QQuickItemPrivate::effectiveCursorHandler()
165 // will ignore it, just in case there is no QQuickPointerTabletEvent to unset it.
166 // For example, a tablet proximity leave event could occur, but we don't deliver it to the window.
167 if (!inside || !(m_hoveredTablet && QQuickDeliveryAgentPrivate::isMouseEvent(event)))
168 setHovered(false);
169
170 return false;
171}
172
173void QQuickHoverHandler::handleEventPoint(QPointerEvent *ev, QEventPoint &point)
174{
175 bool hovered = true;
176 if (point.state() == QEventPoint::Released &&
177 ev->pointingDevice()->pointerType() == QPointingDevice::PointerType::Finger)
178 hovered = false;
179 else if (QQuickDeliveryAgentPrivate::isTabletEvent(ev))
180 m_hoveredTablet = true;
181 setHovered(hovered);
182}
183
184/*!
185 \qmlproperty bool QtQuick::HoverHandler::hovered
186 \readonly
187
188 Holds true whenever any pointing device cursor (mouse or tablet) is within
189 the bounds of the \c parent Item, extended by the
190 \l {PointerHandler::margin}{margin}, if any.
191*/
192void QQuickHoverHandler::setHovered(bool hovered)
193{
194 if (m_hovered != hovered) {
195 qCDebug(lcHoverHandler) << objectName() << "hovered" << m_hovered << "->" << hovered;
196 m_hovered = hovered;
197 if (!hovered)
198 m_hoveredTablet = false;
199 emit hoveredChanged();
200 }
201}
202
203/*!
204 \internal
205 \qmlproperty flags QtQuick::HoverHandler::acceptedButtons
206
207 This property is not used in HoverHandler.
208*/
209
210/*!
211 \qmlproperty flags QtQuick::HoverHandler::acceptedDevices
212
213 The types of pointing devices that can activate the pointer handler.
214
215 By default, this property is set to
216 \l{QInputDevice::DeviceType}{PointerDevice.AllDevices}.
217 If you set it to an OR combination of device types, it will ignore pointer
218 events from the non-matching devices.
219
220 For example, an item could be made to respond to mouse hover in one way,
221 and stylus hover in another way, with two handlers:
222
223 \snippet pointerHandlers/hoverMouseOrStylus.qml 0
224
225 The available device types are as follows:
226
227 \value PointerDevice.Mouse A mouse.
228 \value PointerDevice.TouchScreen A touchscreen.
229 \value PointerDevice.TouchPad A touchpad or trackpad.
230 \value PointerDevice.Stylus A stylus on a graphics tablet.
231 \value PointerDevice.Airbrush An airbrush on a graphics tablet.
232 \value PointerDevice.Puck A digitizer with crosshairs, on a graphics tablet.
233 \value PointerDevice.AllDevices Any type of pointing device.
234
235 \note Not all platforms are yet able to distinguish mouse and touchpad; and
236 on those that do, you often want to make mouse and touchpad behavior the same.
237
238 \sa QInputDevice::DeviceType
239*/
240
241/*!
242 \qmlproperty flags QtQuick::HoverHandler::acceptedPointerTypes
243
244 The types of pointing instruments (generic, stylus, eraser, and so on)
245 that can activate the pointer handler.
246
247 By default, this property is set to
248 \l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
249 If you set it to an OR combination of device types, it will ignore events
250 from non-matching events.
251
252 For example, you could provide feedback by changing the cursor depending on
253 whether a stylus or eraser is hovering over a graphics tablet:
254
255 \snippet pointerHandlers/hoverStylusOrEraser.qml 0
256
257 The available pointer types are as follows:
258
259 \value PointerDevice.Generic A mouse or a device that emulates a mouse.
260 \value PointerDevice.Finger A finger on a touchscreen (hover detection is unlikely).
261 \value PointerDevice.Pen A stylus on a graphics tablet.
262 \value PointerDevice.Eraser An eraser on a graphics tablet.
263 \value PointerDevice.Cursor A digitizer with crosshairs, on a graphics tablet.
264 \value PointerDevice.AllPointerTypes Any type of pointing device.
265
266 \sa QPointingDevice::PointerType
267*/
268
269/*!
270 \qmlproperty flags QtQuick::HoverHandler::acceptedModifiers
271
272 If this property is set, a hover event is handled only if the given keyboard
273 modifiers are pressed. The event is ignored without the modifiers.
274
275 This property is set to \c Qt.KeyboardModifierMask by default, resulting
276 in handling hover events regardless of any modifier keys.
277
278 For example, an \l[QML]{Item} could have two handlers of the same type, one
279 of which is enabled only if the required keyboard modifiers are pressed:
280
281 \snippet pointerHandlers/hoverModifiers.qml 0
282
283 The available modifiers are as follows:
284
285 \value Qt.NoModifier No modifier key is allowed.
286 \value Qt.ShiftModifier A Shift key on the keyboard must be pressed.
287 \value Qt.ControlModifier A Ctrl key on the keyboard must be pressed.
288 \value Qt.AltModifier An Alt key on the keyboard must be pressed.
289 \value Qt.MetaModifier A Meta key on the keyboard must be pressed.
290 \value Qt.KeypadModifier A keypad button must be pressed.
291 \value Qt.GroupSwitchModifier A Mode_switch key on the keyboard must be pressed.
292 X11 only (unless activated on Windows by a command line argument).
293 \value Qt.KeyboardModifierMask The handler ignores modifier keys.
294
295 \sa Qt::KeyboardModifier
296*/
297
298/*!
299 \since 5.15
300 \qmlproperty Qt::CursorShape QtQuick::HoverHandler::cursorShape
301 This property holds the cursor shape that will appear whenever
302 \l hovered is \c true and no other handler is overriding it.
303
304 The available cursor shapes are:
305 \list
306 \li Qt.ArrowCursor
307 \li Qt.UpArrowCursor
308 \li Qt.CrossCursor
309 \li Qt.WaitCursor
310 \li Qt.IBeamCursor
311 \li Qt.SizeVerCursor
312 \li Qt.SizeHorCursor
313 \li Qt.SizeBDiagCursor
314 \li Qt.SizeFDiagCursor
315 \li Qt.SizeAllCursor
316 \li Qt.BlankCursor
317 \li Qt.SplitVCursor
318 \li Qt.SplitHCursor
319 \li Qt.PointingHandCursor
320 \li Qt.ForbiddenCursor
321 \li Qt.WhatsThisCursor
322 \li Qt.BusyCursor
323 \li Qt.OpenHandCursor
324 \li Qt.ClosedHandCursor
325 \li Qt.DragCopyCursor
326 \li Qt.DragMoveCursor
327 \li Qt.DragLinkCursor
328 \endlist
329
330 The default value of this property is not set, which allows any active
331 handler on the same \e parent item to determine the cursor shape.
332 This property can be reset to the initial condition by setting it to
333 \c undefined.
334
335 If any handler with defined \c cursorShape is
336 \l {PointerHandler::active}{active}, that cursor will appear.
337 Else if the HoverHandler has a defined \c cursorShape, that cursor will appear.
338 Otherwise, the \l {QQuickItem::cursor()}{cursor} of \e parent item will appear.
339
340 \note When this property has not been set, or has been set to \c undefined,
341 if you read the value it will return \c Qt.ArrowCursor.
342
343 \sa Qt::CursorShape, QQuickItem::cursor()
344*/
345
346/*!
347 \internal
348 \qmlproperty flags HoverHandler::dragThreshold
349
350 This property is not used in HoverHandler.
351*/
352
353QT_END_NAMESPACE
354
355#include "moc_qquickhoverhandler_p.cpp"