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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/quick/handlers/qquickhoverhandler.cpp