1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQuick module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qquickmousearea_p.h" |
41 | #include "qquickmousearea_p_p.h" |
42 | #include "qquickwindow.h" |
43 | #if QT_CONFIG(quick_draganddrop) |
44 | #include "qquickdrag_p.h" |
45 | #endif |
46 | |
47 | #include <private/qqmldata_p.h> |
48 | #include <private/qsgadaptationlayer_p.h> |
49 | |
50 | #include <QtGui/private/qguiapplication_p.h> |
51 | #include <QtGui/qevent.h> |
52 | #include <QtGui/qstylehints.h> |
53 | |
54 | #include <float.h> |
55 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING) |
59 | |
60 | Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) |
61 | |
62 | QQuickMouseAreaPrivate::QQuickMouseAreaPrivate() |
63 | : enabled(true), scrollGestureEnabled(true), hovered(false), longPress(false), |
64 | moved(false), stealMouse(false), doubleClick(false), preventStealing(false), |
65 | propagateComposedEvents(false), overThreshold(false), |
66 | pressAndHoldInterval(-1) |
67 | #if QT_CONFIG(quick_draganddrop) |
68 | , drag(nullptr) |
69 | #endif |
70 | #if QT_CONFIG(cursor) |
71 | , cursor(nullptr) |
72 | #endif |
73 | { |
74 | } |
75 | |
76 | QQuickMouseAreaPrivate::~QQuickMouseAreaPrivate() |
77 | { |
78 | #if QT_CONFIG(quick_draganddrop) |
79 | delete drag; |
80 | #endif |
81 | #if QT_CONFIG(cursor) |
82 | delete cursor; |
83 | #endif |
84 | } |
85 | |
86 | void QQuickMouseAreaPrivate::init() |
87 | { |
88 | Q_Q(QQuickMouseArea); |
89 | q->setAcceptedMouseButtons(Qt::LeftButton); |
90 | q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch |
91 | q->setFiltersChildMouseEvents(true); |
92 | if (qmlVisualTouchDebugging()) { |
93 | q->setFlag(flag: QQuickItem::ItemHasContents); |
94 | } |
95 | } |
96 | |
97 | void QQuickMouseAreaPrivate::saveEvent(QMouseEvent *event) |
98 | { |
99 | lastPos = event->localPos(); |
100 | lastScenePos = event->windowPos(); |
101 | lastButton = event->button(); |
102 | lastButtons = event->buttons(); |
103 | lastModifiers = event->modifiers(); |
104 | lastFlags = event->flags(); |
105 | } |
106 | |
107 | bool QQuickMouseAreaPrivate::isPressAndHoldConnected() |
108 | { |
109 | Q_Q(QQuickMouseArea); |
110 | IS_SIGNAL_CONNECTED(q, QQuickMouseArea, pressAndHold, (QQuickMouseEvent *)); |
111 | } |
112 | |
113 | bool QQuickMouseAreaPrivate::isDoubleClickConnected() |
114 | { |
115 | Q_Q(QQuickMouseArea); |
116 | IS_SIGNAL_CONNECTED(q, QQuickMouseArea, doubleClicked, (QQuickMouseEvent *)); |
117 | } |
118 | |
119 | bool QQuickMouseAreaPrivate::isClickConnected() |
120 | { |
121 | Q_Q(QQuickMouseArea); |
122 | IS_SIGNAL_CONNECTED(q, QQuickMouseArea, clicked, (QQuickMouseEvent *)); |
123 | } |
124 | |
125 | bool QQuickMouseAreaPrivate::isWheelConnected() |
126 | { |
127 | Q_Q(QQuickMouseArea); |
128 | IS_SIGNAL_CONNECTED(q, QQuickMouseArea, wheel, (QQuickWheelEvent *)); |
129 | } |
130 | |
131 | void QQuickMouseAreaPrivate::propagate(QQuickMouseEvent* event, PropagateType t) |
132 | { |
133 | Q_Q(QQuickMouseArea); |
134 | if (!window || !propagateComposedEvents) |
135 | return; |
136 | QPointF scenePos = q->mapToScene(point: QPointF(event->x(), event->y())); |
137 | propagateHelper(event, window->contentItem(), scenePos, t); |
138 | } |
139 | |
140 | bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *item,const QPointF &sp, PropagateType sig) |
141 | { |
142 | //Based off of QQuickWindow::deliverInitialMousePressEvent |
143 | //But specific to MouseArea, so doesn't belong in window |
144 | Q_Q(const QQuickMouseArea); |
145 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
146 | |
147 | if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { |
148 | QPointF p = item->mapFromScene(point: sp); |
149 | if (!item->contains(point: p)) |
150 | return false; |
151 | } |
152 | |
153 | QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); |
154 | for (int ii = children.count() - 1; ii >= 0; --ii) { |
155 | QQuickItem *child = children.at(i: ii); |
156 | if (!child->isVisible() || !child->isEnabled()) |
157 | continue; |
158 | if (propagateHelper(ev, item: child, sp, sig)) |
159 | return true; |
160 | } |
161 | |
162 | QQuickMouseArea* ma = qobject_cast<QQuickMouseArea*>(object: item); |
163 | if (ma && ma != q && ma->isEnabled() && itemPrivate->acceptedMouseButtons() & ev->button()) { |
164 | switch (sig) { |
165 | case Click: |
166 | if (!ma->d_func()->isClickConnected()) |
167 | return false; |
168 | break; |
169 | case DoubleClick: |
170 | if (!ma->d_func()->isDoubleClickConnected()) |
171 | return false; |
172 | break; |
173 | case PressAndHold: |
174 | if (!ma->d_func()->isPressAndHoldConnected()) |
175 | return false; |
176 | break; |
177 | } |
178 | QPointF p = item->mapFromScene(point: sp); |
179 | if (item->contains(point: p)) { |
180 | ev->setX(p.x()); |
181 | ev->setY(p.y()); |
182 | ev->setAccepted(true);//It is connected, they have to explicitly ignore to let it slide |
183 | switch (sig) { |
184 | case Click: emit ma->clicked(mouse: ev); break; |
185 | case DoubleClick: emit ma->doubleClicked(mouse: ev); break; |
186 | case PressAndHold: emit ma->pressAndHold(mouse: ev); break; |
187 | } |
188 | if (ev->isAccepted()) |
189 | return true; |
190 | } |
191 | } |
192 | return false; |
193 | |
194 | } |
195 | |
196 | /*! |
197 | \qmltype MouseArea |
198 | \instantiates QQuickMouseArea |
199 | \inqmlmodule QtQuick |
200 | \ingroup qtquick-input |
201 | \brief Enables simple mouse handling. |
202 | \inherits Item |
203 | |
204 | A MouseArea is an invisible item that is typically used in conjunction with |
205 | a visible item in order to provide mouse handling for that item. |
206 | By effectively acting as a proxy, the logic for mouse handling can be |
207 | contained within a MouseArea item. |
208 | |
209 | The \l enabled property is used to enable and disable mouse handling for |
210 | the proxied item. When disabled, the mouse area becomes transparent to |
211 | mouse events. |
212 | |
213 | MouseArea is an invisible Item, but it has a visible property. |
214 | When set to false, the mouse area becomes transparent to mouse events. |
215 | |
216 | The \l pressed read-only property indicates whether or not the user is |
217 | holding down a mouse button over the mouse area. This property is often |
218 | used in bindings between properties in a user interface. The containsMouse |
219 | read-only property indicates the presence of the mouse cursor over the |
220 | mouse area but, by default, only when a mouse button is held down; see |
221 | the containsMouse documentation for details. |
222 | |
223 | Information about the mouse position and button clicks are provided via |
224 | signals for which event handler properties are defined. The most commonly |
225 | used involved handling mouse presses and clicks: onClicked, onDoubleClicked, |
226 | onPressed, onReleased and onPressAndHold. It's also possible to handle mouse |
227 | wheel events via the onWheel signal. |
228 | |
229 | If a MouseArea overlaps with the area of other MouseArea items, you can choose |
230 | to propagate \c clicked, \c doubleClicked and \c pressAndHold events to these |
231 | other items by setting propagateComposedEvents to true and rejecting events |
232 | that should be propagated. See the propagateComposedEvents documentation for |
233 | details. |
234 | |
235 | By default, MouseArea items only report mouse clicks and not changes to the |
236 | position of the mouse cursor. Setting the hoverEnabled property ensures that |
237 | handlers defined for onPositionChanged, onEntered and onExited are used and |
238 | that the containsMouse property is updated even when no mouse buttons are |
239 | pressed. |
240 | |
241 | \section1 Example Usage |
242 | |
243 | \div {class="float-right"} |
244 | \inlineimage qml-mousearea-snippet.png |
245 | \enddiv |
246 | |
247 | The following example uses a MouseArea in a \l Rectangle that changes |
248 | the \l Rectangle color to red when clicked: |
249 | |
250 | \snippet qml/mousearea/mousearea.qml import |
251 | \codeline |
252 | \snippet qml/mousearea/mousearea.qml intro |
253 | |
254 | \clearfloat |
255 | Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains |
256 | additional information about the mouse event, such as the position, button, |
257 | and any key modifiers. |
258 | |
259 | Here is an extension of the previous example that produces a different |
260 | color when the area is right clicked: |
261 | |
262 | \snippet qml/mousearea/mousearea.qml intro-extended |
263 | |
264 | \sa MouseEvent, {mousearea}{MouseArea example}, |
265 | {Important Concepts In Qt Quick - User Input} |
266 | */ |
267 | |
268 | /*! |
269 | \qmlsignal QtQuick::MouseArea::entered() |
270 | |
271 | This signal is emitted when the mouse enters the mouse area. |
272 | |
273 | By default this signal is only emitted if a button is currently |
274 | pressed. Set \l hoverEnabled to true to emit this signal |
275 | even when no mouse button is pressed. |
276 | |
277 | \sa hoverEnabled |
278 | */ |
279 | |
280 | /*! |
281 | \qmlsignal QtQuick::MouseArea::exited() |
282 | |
283 | This signal is emitted when the mouse exits the mouse area. |
284 | |
285 | By default this signal is only emitted if a button is currently |
286 | pressed. Set \l hoverEnabled to true to emit this signal |
287 | even when no mouse button is pressed. |
288 | |
289 | The example below shows a fairly typical relationship between |
290 | two MouseAreas, with \c mouseArea2 on top of \c mouseArea1. Moving the |
291 | mouse into \c mouseArea2 from \c mouseArea1 will cause \c mouseArea1 |
292 | to emit the \c exited signal. |
293 | \qml |
294 | Rectangle { |
295 | width: 400; height: 400 |
296 | MouseArea { |
297 | id: mouseArea1 |
298 | anchors.fill: parent |
299 | hoverEnabled: true |
300 | } |
301 | MouseArea { |
302 | id: mouseArea2 |
303 | width: 100; height: 100 |
304 | anchors.centerIn: parent |
305 | hoverEnabled: true |
306 | } |
307 | } |
308 | \endqml |
309 | |
310 | If instead you give the two MouseAreas a parent-child relationship, |
311 | moving the mouse into \c mouseArea2 from \c mouseArea1 will \b not |
312 | cause \c mouseArea1 to emit \c exited. Instead, they will |
313 | both be considered to be simultaneously hovered. |
314 | |
315 | \sa hoverEnabled |
316 | */ |
317 | |
318 | /*! |
319 | \qmlsignal QtQuick::MouseArea::positionChanged(MouseEvent mouse) |
320 | |
321 | This signal is emitted when the mouse position changes. |
322 | |
323 | The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y |
324 | position, and any buttons currently pressed. |
325 | |
326 | By default this signal is only emitted if a button is currently |
327 | pressed. Set \l hoverEnabled to true to emit this signal |
328 | even when no mouse button is pressed. |
329 | |
330 | When handling this signal, changing the \l {MouseEvent::}{accepted} property of the \a mouse |
331 | parameter has no effect. |
332 | */ |
333 | |
334 | /*! |
335 | \qmlsignal QtQuick::MouseArea::clicked(MouseEvent mouse) |
336 | |
337 | This signal is emitted when there is a click. A click is defined as a press followed by a release, |
338 | both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and |
339 | releasing is also considered a click). |
340 | |
341 | The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
342 | position of the release of the click, and whether the click was held. |
343 | |
344 | When handling this signal, changing the \l {MouseEvent::}{accepted} property of the \a mouse |
345 | parameter has no effect, unless the \l propagateComposedEvents property is \c true. |
346 | */ |
347 | |
348 | /*! |
349 | \qmlsignal QtQuick::MouseArea::pressed(MouseEvent mouse) |
350 | |
351 | This signal is emitted when there is a press. |
352 | The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y |
353 | position and which button was pressed. |
354 | |
355 | When handling this signal, use the \l {MouseEvent::}{accepted} property of the \a mouse |
356 | parameter to control whether this MouseArea handles the press and all future mouse events until |
357 | release. The default is to accept the event and not allow other MouseAreas beneath this one to |
358 | handle the event. If \e accepted is set to false, no further events will be sent to this MouseArea |
359 | until the button is next pressed. |
360 | */ |
361 | |
362 | /*! |
363 | \qmlsignal QtQuick::MouseArea::released(MouseEvent mouse) |
364 | |
365 | This signal is emitted when there is a release. |
366 | The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
367 | position of the release of the click, and whether the click was held. |
368 | |
369 | When handling this signal, changing the \l {MouseEvent::}{accepted} property of the \a mouse |
370 | parameter has no effect. |
371 | |
372 | \sa canceled |
373 | */ |
374 | |
375 | /*! |
376 | \qmlsignal QtQuick::MouseArea::pressAndHold(MouseEvent mouse) |
377 | |
378 | This signal is emitted when there is a long press (currently 800ms). |
379 | The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y |
380 | position of the press, and which button is pressed. |
381 | |
382 | When handling this signal, changing the \l {MouseEvent::}{accepted} property of the \a mouse |
383 | parameter has no effect, unless the \l propagateComposedEvents property is \c true. |
384 | */ |
385 | |
386 | /*! |
387 | \qmlsignal QtQuick::MouseArea::doubleClicked(MouseEvent mouse) |
388 | |
389 | This signal is emitted when there is a double-click (a press followed by a release followed by a press). |
390 | The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
391 | position of the release of the click, and whether the click was held. |
392 | |
393 | When handling this signal, if the \l {MouseEvent::}{accepted} property of the \a mouse |
394 | parameter is set to false, the pressed/released/clicked signals will be emitted for the second |
395 | click; otherwise they are suppressed. The \c accepted property defaults to true. |
396 | */ |
397 | |
398 | /*! |
399 | \qmlsignal QtQuick::MouseArea::canceled() |
400 | |
401 | This signal is emitted when mouse events have been canceled, because another item stole the mouse event handling. |
402 | |
403 | This signal is for advanced use: it is useful when there is more than one MouseArea |
404 | that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter |
405 | case, if you execute some logic in the \c onPressed signal handler and then start dragging, the |
406 | \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset |
407 | the logic when the MouseArea has lost the mouse handling to the \l Flickable, |
408 | \c canceled should be handled in addition to \l released. |
409 | */ |
410 | |
411 | /*! |
412 | \qmlsignal QtQuick::MouseArea::wheel(WheelEvent wheel) |
413 | |
414 | This signal is emitted in response to both mouse wheel and trackpad scroll gestures. |
415 | |
416 | The \a wheel parameter provides information about the event, including the x and y |
417 | position, any buttons currently pressed, and information about the wheel movement, including |
418 | angleDelta and pixelDelta. |
419 | */ |
420 | |
421 | QQuickMouseArea::QQuickMouseArea(QQuickItem *parent) |
422 | : QQuickItem(*(new QQuickMouseAreaPrivate), parent) |
423 | { |
424 | Q_D(QQuickMouseArea); |
425 | d->init(); |
426 | #if QT_CONFIG(cursor) |
427 | // Explcitly call setCursor on QQuickItem since |
428 | // it internally keeps a boolean hasCursor that doesn't |
429 | // get set to true unless you call setCursor |
430 | setCursor(Qt::ArrowCursor); |
431 | #endif |
432 | } |
433 | |
434 | QQuickMouseArea::~QQuickMouseArea() |
435 | { |
436 | } |
437 | |
438 | /*! |
439 | \qmlproperty real QtQuick::MouseArea::mouseX |
440 | \qmlproperty real QtQuick::MouseArea::mouseY |
441 | These properties hold the coordinates of the mouse cursor. |
442 | |
443 | If the hoverEnabled property is false then these properties will only be valid |
444 | while a button is pressed, and will remain valid as long as the button is held |
445 | down even if the mouse is moved outside the area. |
446 | |
447 | By default, this property is false. |
448 | |
449 | If hoverEnabled is true then these properties will be valid when: |
450 | \list |
451 | \li no button is pressed, but the mouse is within the MouseArea (containsMouse is true). |
452 | \li a button is pressed and held, even if it has since moved out of the area. |
453 | \endlist |
454 | |
455 | The coordinates are relative to the MouseArea. |
456 | */ |
457 | qreal QQuickMouseArea::mouseX() const |
458 | { |
459 | Q_D(const QQuickMouseArea); |
460 | return d->lastPos.x(); |
461 | } |
462 | |
463 | qreal QQuickMouseArea::mouseY() const |
464 | { |
465 | Q_D(const QQuickMouseArea); |
466 | return d->lastPos.y(); |
467 | } |
468 | |
469 | /*! |
470 | \qmlproperty bool QtQuick::MouseArea::enabled |
471 | This property holds whether the item accepts mouse events. |
472 | |
473 | \note Due to historical reasons, this property is not equivalent to |
474 | Item.enabled. It only affects mouse events, and its effect does not |
475 | propagate to child items. |
476 | |
477 | By default, this property is true. |
478 | */ |
479 | bool QQuickMouseArea::isEnabled() const |
480 | { |
481 | Q_D(const QQuickMouseArea); |
482 | return d->enabled; |
483 | } |
484 | |
485 | void QQuickMouseArea::setEnabled(bool a) |
486 | { |
487 | Q_D(QQuickMouseArea); |
488 | if (a != d->enabled) { |
489 | d->enabled = a; |
490 | emit enabledChanged(); |
491 | } |
492 | } |
493 | |
494 | /*! |
495 | \qmlproperty bool QtQuick::MouseArea::scrollGestureEnabled |
496 | \since 5.5 |
497 | |
498 | This property controls whether this MouseArea responds to scroll gestures |
499 | from non-mouse devices, such as the 2-finger flick gesture on a trackpad. |
500 | If set to false, the \l wheel signal be emitted only when the wheel event |
501 | comes from an actual mouse with a wheel, while scroll gesture events will |
502 | pass through to any other Item that will handle them. For example, the user |
503 | might perform a flick gesture while the cursor is over an item containing a |
504 | MouseArea, intending to interact with a Flickable which is underneath. |
505 | Setting this property to false will allow the PinchArea to handle the mouse |
506 | wheel or the pinch gesture, while the Flickable handles the flick gesture. |
507 | |
508 | By default, this property is true. |
509 | */ |
510 | bool QQuickMouseArea::isScrollGestureEnabled() const |
511 | { |
512 | Q_D(const QQuickMouseArea); |
513 | return d->scrollGestureEnabled; |
514 | } |
515 | |
516 | void QQuickMouseArea::setScrollGestureEnabled(bool e) |
517 | { |
518 | Q_D(QQuickMouseArea); |
519 | if (e != d->scrollGestureEnabled) { |
520 | d->scrollGestureEnabled = e; |
521 | emit scrollGestureEnabledChanged(); |
522 | } |
523 | } |
524 | |
525 | /*! |
526 | \qmlproperty bool QtQuick::MouseArea::preventStealing |
527 | This property holds whether the mouse events may be stolen from this |
528 | MouseArea. |
529 | |
530 | If a MouseArea is placed within an item that filters child mouse |
531 | events, such as Flickable, the mouse |
532 | events may be stolen from the MouseArea if a gesture is recognized |
533 | by the parent item, e.g. a flick gesture. If preventStealing is |
534 | set to true, no item will steal the mouse events. |
535 | |
536 | Note that setting preventStealing to true once an item has started |
537 | stealing events will have no effect until the next press event. |
538 | |
539 | By default this property is false. |
540 | */ |
541 | bool QQuickMouseArea::preventStealing() const |
542 | { |
543 | Q_D(const QQuickMouseArea); |
544 | return d->preventStealing; |
545 | } |
546 | |
547 | void QQuickMouseArea::setPreventStealing(bool prevent) |
548 | { |
549 | Q_D(QQuickMouseArea); |
550 | if (prevent != d->preventStealing) { |
551 | d->preventStealing = prevent; |
552 | setKeepMouseGrab(d->preventStealing && d->enabled); |
553 | emit preventStealingChanged(); |
554 | } |
555 | } |
556 | |
557 | |
558 | /*! |
559 | \qmlproperty bool QtQuick::MouseArea::propagateComposedEvents |
560 | This property holds whether composed mouse events will automatically propagate to |
561 | other MouseAreas that overlap with this MouseArea but are lower in the visual stacking order. |
562 | By default, this property is false. |
563 | |
564 | MouseArea contains several composed events: \c clicked, \c doubleClicked and \c pressAndHold. |
565 | These are composed of basic mouse events, like \c pressed, and can be propagated differently |
566 | in comparison to basic events. |
567 | |
568 | If propagateComposedEvents is set to true, then composed events will be automatically |
569 | propagated to other MouseAreas in the same location in the scene. Each event is propagated |
570 | to the next \l enabled MouseArea beneath it in the stacking order, propagating down this visual |
571 | hierarchy until a MouseArea accepts the event. Unlike \c pressed events, composed events will |
572 | not be automatically accepted if no handler is present. |
573 | |
574 | For example, below is a yellow \l Rectangle that contains a blue \l Rectangle. The blue |
575 | rectangle is the top-most item in the hierarchy of the visual stacking order; it will |
576 | visually rendered above the yellow rectangle. Since the blue rectangle sets |
577 | propagateComposedEvents to true, and also sets \l MouseEvent::accepted to false for all |
578 | received \c clicked events, any \c clicked events it receives are propagated to the |
579 | MouseArea of the yellow rectangle beneath it. |
580 | |
581 | \qml |
582 | import QtQuick 2.0 |
583 | |
584 | Rectangle { |
585 | color: "yellow" |
586 | width: 100; height: 100 |
587 | |
588 | MouseArea { |
589 | anchors.fill: parent |
590 | onClicked: console.log("clicked yellow") |
591 | } |
592 | |
593 | Rectangle { |
594 | color: "blue" |
595 | width: 50; height: 50 |
596 | |
597 | MouseArea { |
598 | anchors.fill: parent |
599 | propagateComposedEvents: true |
600 | onClicked: { |
601 | console.log("clicked blue") |
602 | mouse.accepted = false |
603 | } |
604 | } |
605 | } |
606 | } |
607 | \endqml |
608 | |
609 | Clicking on the blue rectangle will cause the \c onClicked handler of its child MouseArea to |
610 | be invoked; the event will then be propagated to the MouseArea of the yellow rectangle, causing |
611 | its own \c onClicked handler to be invoked. |
612 | |
613 | This property greatly simplifies the usecase of when you want to have overlapping MouseAreas |
614 | handling the composed events together. For example: if you want one MouseArea to handle \c clicked |
615 | signals and the other to handle \c pressAndHold, or if you want one MouseArea to handle \c clicked most |
616 | of the time, but pass it through when certain conditions are met. |
617 | */ |
618 | bool QQuickMouseArea::propagateComposedEvents() const |
619 | { |
620 | Q_D(const QQuickMouseArea); |
621 | return d->propagateComposedEvents; |
622 | } |
623 | |
624 | void QQuickMouseArea::setPropagateComposedEvents(bool prevent) |
625 | { |
626 | Q_D(QQuickMouseArea); |
627 | if (prevent != d->propagateComposedEvents) { |
628 | d->propagateComposedEvents = prevent; |
629 | setKeepMouseGrab(d->propagateComposedEvents && d->enabled); |
630 | emit propagateComposedEventsChanged(); |
631 | } |
632 | } |
633 | |
634 | /*! |
635 | \qmlproperty MouseButtons QtQuick::MouseArea::pressedButtons |
636 | This property holds the mouse buttons currently pressed. |
637 | |
638 | It contains a bitwise combination of: |
639 | \list |
640 | \li Qt.LeftButton |
641 | \li Qt.RightButton |
642 | \li Qt.MiddleButton |
643 | \endlist |
644 | |
645 | The code below displays "right" when the right mouse buttons is pressed: |
646 | |
647 | \snippet qml/mousearea/mousearea.qml mousebuttons |
648 | |
649 | \note this property only handles buttons specified in \l acceptedButtons. |
650 | |
651 | \sa acceptedButtons |
652 | */ |
653 | Qt::MouseButtons QQuickMouseArea::pressedButtons() const |
654 | { |
655 | Q_D(const QQuickMouseArea); |
656 | return d->pressed; |
657 | } |
658 | |
659 | void QQuickMouseArea::mousePressEvent(QMouseEvent *event) |
660 | { |
661 | Q_D(QQuickMouseArea); |
662 | d->moved = false; |
663 | d->stealMouse = d->preventStealing; |
664 | d->overThreshold = false; |
665 | if (!d->enabled || !(event->button() & acceptedMouseButtons())) { |
666 | QQuickItem::mousePressEvent(event); |
667 | } else { |
668 | d->longPress = false; |
669 | d->saveEvent(event); |
670 | #if QT_CONFIG(quick_draganddrop) |
671 | if (d->drag) |
672 | d->drag->setActive(false); |
673 | #endif |
674 | setHovered(true); |
675 | d->startScene = event->windowPos(); |
676 | setKeepMouseGrab(d->stealMouse); |
677 | event->setAccepted(setPressed(button: event->button(), p: true, source: event->source())); |
678 | if (event->isAccepted()) |
679 | d->pressAndHoldTimer.start(msec: pressAndHoldInterval(), obj: this); |
680 | } |
681 | } |
682 | |
683 | void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) |
684 | { |
685 | Q_D(QQuickMouseArea); |
686 | if (!d->enabled && !d->pressed) { |
687 | QQuickItem::mouseMoveEvent(event); |
688 | return; |
689 | } |
690 | |
691 | // ### we should skip this if these signals aren't used |
692 | // ### can GV handle this for us? |
693 | setHovered(contains(point: event->localPos())); |
694 | |
695 | if ((event->buttons() & acceptedMouseButtons()) == 0) { |
696 | QQuickItem::mouseMoveEvent(event); |
697 | return; |
698 | } |
699 | |
700 | d->saveEvent(event); |
701 | |
702 | |
703 | #if QT_CONFIG(quick_draganddrop) |
704 | if (d->drag && d->drag->target()) { |
705 | if (!d->moved) { |
706 | d->targetStartPos = d->drag->target()->parentItem() |
707 | ? d->drag->target()->parentItem()->mapToScene(point: d->drag->target()->position()) |
708 | : d->drag->target()->position(); |
709 | } |
710 | |
711 | QPointF startLocalPos; |
712 | QPointF curLocalPos; |
713 | if (drag()->target()->parentItem()) { |
714 | startLocalPos = drag()->target()->parentItem()->mapFromScene(point: d->startScene); |
715 | curLocalPos = drag()->target()->parentItem()->mapFromScene(point: event->windowPos()); |
716 | } else { |
717 | startLocalPos = d->startScene; |
718 | curLocalPos = event->windowPos(); |
719 | } |
720 | |
721 | if (keepMouseGrab() && d->stealMouse && d->overThreshold && !d->drag->active()) |
722 | d->drag->setActive(true); |
723 | |
724 | QPointF startPos = d->drag->target()->parentItem() |
725 | ? d->drag->target()->parentItem()->mapFromScene(point: d->targetStartPos) |
726 | : d->targetStartPos; |
727 | |
728 | bool dragX = drag()->axis() & QQuickDrag::XAxis; |
729 | bool dragY = drag()->axis() & QQuickDrag::YAxis; |
730 | |
731 | QPointF dragPos = d->drag->target()->position(); |
732 | QPointF boundedDragPos = dragPos; |
733 | if (dragX) { |
734 | dragPos.setX(startPos.x() + curLocalPos.x() - startLocalPos.x()); |
735 | boundedDragPos.setX(qBound( |
736 | min: d->drag->xmin(), |
737 | val: dragPos.x(), |
738 | max: d->drag->xmax())); |
739 | } |
740 | if (dragY) { |
741 | dragPos.setY(startPos.y() + curLocalPos.y() - startLocalPos.y()); |
742 | boundedDragPos.setY(qBound( |
743 | min: d->drag->ymin(), |
744 | val: dragPos.y(), |
745 | max: d->drag->ymax())); |
746 | } |
747 | |
748 | QPointF targetPos = d->drag->target()->position(); |
749 | |
750 | if (d->drag->active()) { |
751 | d->drag->target()->setPosition(boundedDragPos); |
752 | d->lastPos = mapFromScene(point: d->lastScenePos); |
753 | } |
754 | |
755 | bool dragOverThresholdX = QQuickWindowPrivate::dragOverThreshold(d: dragPos.x() - startPos.x(), |
756 | axis: Qt::XAxis, event, startDragThreshold: d->drag->threshold()); |
757 | bool dragOverThresholdY = QQuickWindowPrivate::dragOverThreshold(d: dragPos.y() - startPos.y(), |
758 | axis: Qt::YAxis, event, startDragThreshold: d->drag->threshold()); |
759 | |
760 | if (!d->overThreshold && (((targetPos.x() != boundedDragPos.x()) && dragOverThresholdX) || |
761 | ((targetPos.y() != boundedDragPos.y()) && dragOverThresholdY))) |
762 | { |
763 | d->overThreshold = true; |
764 | if (d->drag->smoothed()) |
765 | d->startScene = event->windowPos(); |
766 | } |
767 | |
768 | if (!keepMouseGrab() && d->overThreshold) { |
769 | setKeepMouseGrab(true); |
770 | d->stealMouse = true; |
771 | } |
772 | |
773 | d->moved = true; |
774 | } |
775 | #endif |
776 | |
777 | QQuickMouseEvent &me = d->quickMouseEvent; |
778 | me.reset(x: d->lastPos.x(), y: d->lastPos.y(), button: d->lastButton, buttons: d->lastButtons, modifiers: d->lastModifiers, isClick: false, wasHeld: d->longPress, flags: event->flags()); |
779 | me.setSource(event->source()); |
780 | emit mouseXChanged(mouse: &me); |
781 | me.setPosition(d->lastPos); |
782 | emit mouseYChanged(mouse: &me); |
783 | me.setPosition(d->lastPos); |
784 | emit positionChanged(mouse: &me); |
785 | } |
786 | |
787 | void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event) |
788 | { |
789 | Q_D(QQuickMouseArea); |
790 | d->stealMouse = false; |
791 | d->overThreshold = false; |
792 | if (!d->enabled && !d->pressed) { |
793 | QQuickItem::mouseReleaseEvent(event); |
794 | } else { |
795 | d->saveEvent(event); |
796 | setPressed(button: event->button(), p: false, source: event->source()); |
797 | if (!d->pressed) { |
798 | // no other buttons are pressed |
799 | #if QT_CONFIG(quick_draganddrop) |
800 | if (d->drag) |
801 | d->drag->setActive(false); |
802 | #endif |
803 | // If we don't accept hover, we need to reset containsMouse. |
804 | if (!acceptHoverEvents()) |
805 | setHovered(false); |
806 | QQuickWindow *w = window(); |
807 | if (w && w->mouseGrabberItem() == this) |
808 | ungrabMouse(); |
809 | setKeepMouseGrab(false); |
810 | } |
811 | } |
812 | d->doubleClick = false; |
813 | } |
814 | |
815 | void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event) |
816 | { |
817 | Q_D(QQuickMouseArea); |
818 | if (d->enabled) { |
819 | d->saveEvent(event); |
820 | QQuickMouseEvent &me = d->quickMouseEvent; |
821 | me.reset(x: d->lastPos.x(), y: d->lastPos.y(), button: d->lastButton, buttons: d->lastButtons, modifiers: d->lastModifiers, isClick: true, |
822 | wasHeld: false, flags: event->flags()); |
823 | me.setSource(event->source()); |
824 | me.setAccepted(d->isDoubleClickConnected()); |
825 | emit this->doubleClicked(mouse: &me); |
826 | if (!me.isAccepted()) |
827 | d->propagate(event: &me, t: QQuickMouseAreaPrivate::DoubleClick); |
828 | d->doubleClick = d->isDoubleClickConnected() || me.isAccepted(); |
829 | } |
830 | QQuickItem::mouseDoubleClickEvent(event); |
831 | } |
832 | |
833 | void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event) |
834 | { |
835 | Q_D(QQuickMouseArea); |
836 | if (!d->enabled && !d->pressed) { |
837 | QQuickItem::hoverEnterEvent(event); |
838 | } else { |
839 | d->lastPos = event->posF(); |
840 | d->lastModifiers = event->modifiers(); |
841 | setHovered(true); |
842 | QQuickMouseEvent &me = d->quickMouseEvent; |
843 | me.reset(x: d->lastPos.x(), y: d->lastPos.y(), button: Qt::NoButton, buttons: Qt::NoButton, modifiers: d->lastModifiers, isClick: false, wasHeld: false); |
844 | emit mouseXChanged(mouse: &me); |
845 | me.setPosition(d->lastPos); |
846 | emit mouseYChanged(mouse: &me); |
847 | me.setPosition(d->lastPos); |
848 | } |
849 | } |
850 | |
851 | void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event) |
852 | { |
853 | Q_D(QQuickMouseArea); |
854 | if (!d->enabled && !d->pressed) { |
855 | QQuickItem::hoverMoveEvent(event); |
856 | } else if (d->lastPos != event->posF()) { |
857 | d->lastPos = event->posF(); |
858 | d->lastModifiers = event->modifiers(); |
859 | QQuickMouseEvent &me = d->quickMouseEvent; |
860 | me.reset(x: d->lastPos.x(), y: d->lastPos.y(), button: Qt::NoButton, buttons: Qt::NoButton, modifiers: d->lastModifiers, isClick: false, wasHeld: false); |
861 | emit mouseXChanged(mouse: &me); |
862 | me.setPosition(d->lastPos); |
863 | emit mouseYChanged(mouse: &me); |
864 | me.setPosition(d->lastPos); |
865 | emit positionChanged(mouse: &me); |
866 | } |
867 | } |
868 | |
869 | void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event) |
870 | { |
871 | Q_D(QQuickMouseArea); |
872 | if (!d->enabled && !d->pressed) |
873 | QQuickItem::hoverLeaveEvent(event); |
874 | else |
875 | setHovered(false); |
876 | } |
877 | |
878 | #if QT_CONFIG(wheelevent) |
879 | void QQuickMouseArea::wheelEvent(QWheelEvent *event) |
880 | { |
881 | Q_D(QQuickMouseArea); |
882 | if (!d->enabled || (!isScrollGestureEnabled() && event->source() != Qt::MouseEventNotSynthesized)) { |
883 | QQuickItem::wheelEvent(event); |
884 | return; |
885 | } |
886 | |
887 | QQuickWheelEvent &we = d->quickWheelEvent; |
888 | we.reset(x: event->position().x(), y: event->position().y(), angleDelta: event->angleDelta(), pixelDelta: event->pixelDelta(), |
889 | buttons: event->buttons(), modifiers: event->modifiers(), inverted: event->inverted()); |
890 | we.setAccepted(d->isWheelConnected()); |
891 | emit wheel(wheel: &we); |
892 | if (!we.isAccepted()) |
893 | QQuickItem::wheelEvent(event); |
894 | } |
895 | #endif |
896 | |
897 | void QQuickMouseArea::ungrabMouse() |
898 | { |
899 | Q_D(QQuickMouseArea); |
900 | if (d->pressed) { |
901 | // if our mouse grab has been removed (probably by Flickable), fix our |
902 | // state |
903 | d->pressed = Qt::NoButton; |
904 | d->stealMouse = false; |
905 | d->doubleClick = false; |
906 | d->overThreshold = false; |
907 | setKeepMouseGrab(false); |
908 | |
909 | #if QT_CONFIG(quick_draganddrop) |
910 | if (d->drag) |
911 | d->drag->setActive(false); |
912 | #endif |
913 | |
914 | emit canceled(); |
915 | emit pressedChanged(); |
916 | emit containsPressChanged(); |
917 | emit pressedButtonsChanged(); |
918 | |
919 | if (d->hovered && !isUnderMouse()) { |
920 | d->hovered = false; |
921 | emit hoveredChanged(); |
922 | } |
923 | } |
924 | } |
925 | |
926 | void QQuickMouseArea::mouseUngrabEvent() |
927 | { |
928 | ungrabMouse(); |
929 | } |
930 | |
931 | void QQuickMouseArea::touchUngrabEvent() |
932 | { |
933 | // allow a Pointer Handler to steal the grab from MouseArea |
934 | ungrabMouse(); |
935 | } |
936 | |
937 | bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event) |
938 | { |
939 | Q_D(QQuickMouseArea); |
940 | QPointF localPos = mapFromScene(point: event->windowPos()); |
941 | |
942 | QQuickWindow *c = window(); |
943 | QQuickItem *grabber = c ? c->mouseGrabberItem() : nullptr; |
944 | bool stealThisEvent = d->stealMouse; |
945 | if ((stealThisEvent || contains(point: localPos)) && (!grabber || !grabber->keepMouseGrab())) { |
946 | QMouseEvent mouseEvent(event->type(), localPos, event->windowPos(), event->screenPos(), |
947 | event->button(), event->buttons(), event->modifiers()); |
948 | mouseEvent.setAccepted(false); |
949 | |
950 | switch (event->type()) { |
951 | case QEvent::MouseMove: |
952 | mouseMoveEvent(event: &mouseEvent); |
953 | break; |
954 | case QEvent::MouseButtonPress: |
955 | mousePressEvent(event: &mouseEvent); |
956 | break; |
957 | case QEvent::MouseButtonRelease: |
958 | mouseReleaseEvent(event: &mouseEvent); |
959 | stealThisEvent = d->stealMouse; |
960 | break; |
961 | default: |
962 | break; |
963 | } |
964 | grabber = c ? c->mouseGrabberItem() : nullptr; |
965 | if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) |
966 | grabMouse(); |
967 | |
968 | return stealThisEvent; |
969 | } |
970 | if (event->type() == QEvent::MouseButtonRelease) { |
971 | if (d->pressed) { |
972 | d->pressed &= ~event->button(); |
973 | emit pressedButtonsChanged(); |
974 | if (!d->pressed) { |
975 | // no other buttons are pressed |
976 | d->stealMouse = false; |
977 | d->overThreshold = false; |
978 | if (c && c->mouseGrabberItem() == this) |
979 | ungrabMouse(); |
980 | emit canceled(); |
981 | emit pressedChanged(); |
982 | emit containsPressChanged(); |
983 | if (d->hovered) { |
984 | d->hovered = false; |
985 | emit hoveredChanged(); |
986 | } |
987 | } |
988 | } |
989 | } |
990 | return false; |
991 | } |
992 | |
993 | bool QQuickMouseArea::childMouseEventFilter(QQuickItem *i, QEvent *e) |
994 | { |
995 | Q_D(QQuickMouseArea); |
996 | if (!d->pressed && |
997 | (!d->enabled || !isVisible() |
998 | #if QT_CONFIG(quick_draganddrop) |
999 | || !d->drag || !d->drag->filterChildren() |
1000 | #endif |
1001 | ) |
1002 | ) |
1003 | return QQuickItem::childMouseEventFilter(i, e); |
1004 | switch (e->type()) { |
1005 | case QEvent::MouseButtonPress: |
1006 | case QEvent::MouseMove: |
1007 | case QEvent::MouseButtonRelease: |
1008 | return sendMouseEvent(event: static_cast<QMouseEvent *>(e)); |
1009 | default: |
1010 | break; |
1011 | } |
1012 | |
1013 | return QQuickItem::childMouseEventFilter(i, e); |
1014 | } |
1015 | |
1016 | void QQuickMouseArea::timerEvent(QTimerEvent *event) |
1017 | { |
1018 | Q_D(QQuickMouseArea); |
1019 | if (event->timerId() == d->pressAndHoldTimer.timerId()) { |
1020 | d->pressAndHoldTimer.stop(); |
1021 | #if QT_CONFIG(quick_draganddrop) |
1022 | bool dragged = d->drag && d->drag->active(); |
1023 | #else |
1024 | bool dragged = false; |
1025 | #endif |
1026 | if (d->pressed && dragged == false && d->hovered == true) { |
1027 | d->longPress = true; |
1028 | QQuickMouseEvent &me = d->quickMouseEvent; |
1029 | me.reset(x: d->lastPos.x(), y: d->lastPos.y(), button: d->lastButton, buttons: d->lastButtons, modifiers: d->lastModifiers, isClick: false, wasHeld: d->longPress, flags: d->lastFlags); |
1030 | me.setSource(Qt::MouseEventSynthesizedByQt); |
1031 | me.setAccepted(d->isPressAndHoldConnected()); |
1032 | emit pressAndHold(mouse: &me); |
1033 | if (!me.isAccepted()) |
1034 | d->propagate(event: &me, t: QQuickMouseAreaPrivate::PressAndHold); |
1035 | if (!me.isAccepted()) // no one handled the long press - allow click |
1036 | d->longPress = false; |
1037 | } |
1038 | } |
1039 | } |
1040 | |
1041 | void QQuickMouseArea::windowDeactivateEvent() |
1042 | { |
1043 | ungrabMouse(); |
1044 | QQuickItem::windowDeactivateEvent(); |
1045 | } |
1046 | |
1047 | void QQuickMouseArea::geometryChanged(const QRectF &newGeometry, |
1048 | const QRectF &oldGeometry) |
1049 | { |
1050 | Q_D(QQuickMouseArea); |
1051 | QQuickItem::geometryChanged(newGeometry, oldGeometry); |
1052 | |
1053 | if (d->lastScenePos.isNull) |
1054 | d->lastScenePos = mapToScene(point: d->lastPos); |
1055 | else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y()) |
1056 | d->lastPos = mapFromScene(point: d->lastScenePos); |
1057 | } |
1058 | |
1059 | void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value) |
1060 | { |
1061 | Q_D(QQuickMouseArea); |
1062 | switch (change) { |
1063 | case ItemVisibleHasChanged: |
1064 | if (d->effectiveEnable && d->enabled && acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) { |
1065 | if (!d->hovered) { |
1066 | QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition; |
1067 | d->lastScenePos = d->window->mapFromGlobal(pos: cursorPos.toPoint()); |
1068 | d->lastPos = mapFromScene(point: d->lastScenePos); |
1069 | } |
1070 | setHovered(!d->hovered); |
1071 | } |
1072 | if (d->pressed && (!isVisible())) { |
1073 | // This happens when the mouse area sets itself disabled or hidden |
1074 | // inside the press handler. In that case we should not keep the internal |
1075 | // state as pressed, since we never became the mouse grabber. |
1076 | ungrabMouse(); |
1077 | } |
1078 | break; |
1079 | default: |
1080 | break; |
1081 | } |
1082 | |
1083 | QQuickItem::itemChange(change, value); |
1084 | } |
1085 | |
1086 | /*! |
1087 | \qmlproperty bool QtQuick::MouseArea::hoverEnabled |
1088 | This property holds whether hover events are handled. |
1089 | |
1090 | By default, mouse events are only handled in response to a button event, or when a button is |
1091 | pressed. Hover enables handling of all mouse events even when no mouse button is |
1092 | pressed. |
1093 | |
1094 | This property affects the containsMouse property and the onEntered, onExited and |
1095 | onPositionChanged signals. |
1096 | */ |
1097 | bool QQuickMouseArea::hoverEnabled() const |
1098 | { |
1099 | return acceptHoverEvents(); |
1100 | } |
1101 | |
1102 | void QQuickMouseArea::setHoverEnabled(bool h) |
1103 | { |
1104 | if (h == acceptHoverEvents()) |
1105 | return; |
1106 | |
1107 | setAcceptHoverEvents(h); |
1108 | emit hoverEnabledChanged(); |
1109 | } |
1110 | |
1111 | |
1112 | /*! |
1113 | \qmlproperty bool QtQuick::MouseArea::containsMouse |
1114 | This property holds whether the mouse is currently inside the mouse area. |
1115 | |
1116 | \warning If hoverEnabled is false, containsMouse will only be valid |
1117 | when the mouse is pressed while the mouse cursor is inside the MouseArea. |
1118 | */ |
1119 | bool QQuickMouseArea::hovered() const |
1120 | { |
1121 | Q_D(const QQuickMouseArea); |
1122 | return d->hovered; |
1123 | } |
1124 | |
1125 | /*! |
1126 | \qmlproperty bool QtQuick::MouseArea::pressed |
1127 | This property holds whether any of the \l acceptedButtons are currently pressed. |
1128 | */ |
1129 | bool QQuickMouseArea::pressed() const |
1130 | { |
1131 | Q_D(const QQuickMouseArea); |
1132 | return d->pressed; |
1133 | } |
1134 | |
1135 | /*! |
1136 | \qmlproperty bool QtQuick::MouseArea::containsPress |
1137 | \since 5.4 |
1138 | This is a convenience property equivalent to \c {pressed && containsMouse}, |
1139 | i.e. it holds whether any of the \l acceptedButtons are currently pressed |
1140 | and the mouse is currently within the MouseArea. |
1141 | |
1142 | This property is particularly useful for highlighting an item while the mouse |
1143 | is pressed within its bounds. |
1144 | |
1145 | \sa pressed, containsMouse |
1146 | */ |
1147 | bool QQuickMouseArea::containsPress() const |
1148 | { |
1149 | Q_D(const QQuickMouseArea); |
1150 | return d->pressed && d->hovered; |
1151 | } |
1152 | |
1153 | void QQuickMouseArea::setHovered(bool h) |
1154 | { |
1155 | Q_D(QQuickMouseArea); |
1156 | if (d->hovered != h) { |
1157 | qCDebug(DBG_HOVER_TRACE) << this << d->hovered << "->" << h; |
1158 | d->hovered = h; |
1159 | emit hoveredChanged(); |
1160 | d->hovered ? emit entered() : emit exited(); |
1161 | if (d->pressed) |
1162 | emit containsPressChanged(); |
1163 | } |
1164 | } |
1165 | |
1166 | /*! |
1167 | \qmlproperty Qt::MouseButtons QtQuick::MouseArea::acceptedButtons |
1168 | This property holds the mouse buttons that the mouse area reacts to. |
1169 | |
1170 | To specify that the MouseArea will react to multiple buttons, |
1171 | Qt::MouseButtons flag values are combined using the "|" (or) operator: |
1172 | |
1173 | \code |
1174 | MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton } |
1175 | \endcode |
1176 | |
1177 | To indicate that all possible mouse buttons are to be accepted, |
1178 | the special value 'Qt.AllButtons' may be used: |
1179 | |
1180 | \code |
1181 | MouseArea { acceptedButtons: Qt.AllButtons } |
1182 | \endcode |
1183 | |
1184 | The default value is \c Qt.LeftButton. |
1185 | */ |
1186 | Qt::MouseButtons QQuickMouseArea::acceptedButtons() const |
1187 | { |
1188 | return acceptedMouseButtons(); |
1189 | } |
1190 | |
1191 | void QQuickMouseArea::setAcceptedButtons(Qt::MouseButtons buttons) |
1192 | { |
1193 | if (buttons != acceptedMouseButtons()) { |
1194 | setAcceptedMouseButtons(buttons); |
1195 | emit acceptedButtonsChanged(); |
1196 | } |
1197 | } |
1198 | |
1199 | bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventSource source) |
1200 | { |
1201 | Q_D(QQuickMouseArea); |
1202 | |
1203 | #if QT_CONFIG(quick_draganddrop) |
1204 | bool dragged = d->drag && d->drag->active(); |
1205 | #else |
1206 | bool dragged = false; |
1207 | #endif |
1208 | bool wasPressed = d->pressed & button; |
1209 | bool isclick = wasPressed && p == false && dragged == false && d->hovered == true; |
1210 | Qt::MouseButtons oldPressed = d->pressed; |
1211 | |
1212 | if (wasPressed != p) { |
1213 | QQuickMouseEvent &me = d->quickMouseEvent; |
1214 | me.reset(x: d->lastPos.x(), y: d->lastPos.y(), button: d->lastButton, buttons: d->lastButtons, modifiers: d->lastModifiers, isClick: isclick, wasHeld: d->longPress, flags: d->lastFlags); |
1215 | me.setSource(source); |
1216 | if (p) { |
1217 | d->pressed |= button; |
1218 | if (!d->doubleClick) |
1219 | emit pressed(mouse: &me); |
1220 | me.setPosition(d->lastPos); |
1221 | emit mouseXChanged(mouse: &me); |
1222 | me.setPosition(d->lastPos); |
1223 | emit mouseYChanged(mouse: &me); |
1224 | |
1225 | if (!me.isAccepted()) { |
1226 | d->pressed = Qt::NoButton; |
1227 | } |
1228 | |
1229 | if (!oldPressed) { |
1230 | emit pressedChanged(); |
1231 | emit containsPressChanged(); |
1232 | } |
1233 | emit pressedButtonsChanged(); |
1234 | } else { |
1235 | d->pressed &= ~button; |
1236 | emit released(mouse: &me); |
1237 | me.setPosition(d->lastPos); |
1238 | if (!d->pressed) { |
1239 | emit pressedChanged(); |
1240 | emit containsPressChanged(); |
1241 | } |
1242 | emit pressedButtonsChanged(); |
1243 | if (isclick && !d->longPress && !d->doubleClick){ |
1244 | me.setAccepted(d->isClickConnected()); |
1245 | emit clicked(mouse: &me); |
1246 | if (!me.isAccepted()) |
1247 | d->propagate(event: &me, t: QQuickMouseAreaPrivate::Click); |
1248 | } |
1249 | } |
1250 | |
1251 | return me.isAccepted(); |
1252 | } |
1253 | return false; |
1254 | } |
1255 | |
1256 | |
1257 | /*! |
1258 | \qmlproperty Qt::CursorShape QtQuick::MouseArea::cursorShape |
1259 | This property holds the cursor shape for this mouse area. |
1260 | Note that on platforms that do not display a mouse cursor this may have |
1261 | no effect. |
1262 | |
1263 | The available cursor shapes are: |
1264 | \list |
1265 | \li Qt.ArrowCursor |
1266 | \li Qt.UpArrowCursor |
1267 | \li Qt.CrossCursor |
1268 | \li Qt.WaitCursor |
1269 | \li Qt.IBeamCursor |
1270 | \li Qt.SizeVerCursor |
1271 | \li Qt.SizeHorCursor |
1272 | \li Qt.SizeBDiagCursor |
1273 | \li Qt.SizeFDiagCursor |
1274 | \li Qt.SizeAllCursor |
1275 | \li Qt.BlankCursor |
1276 | \li Qt.SplitVCursor |
1277 | \li Qt.SplitHCursor |
1278 | \li Qt.PointingHandCursor |
1279 | \li Qt.ForbiddenCursor |
1280 | \li Qt.WhatsThisCursor |
1281 | \li Qt.BusyCursor |
1282 | \li Qt.OpenHandCursor |
1283 | \li Qt.ClosedHandCursor |
1284 | \li Qt.DragCopyCursor |
1285 | \li Qt.DragMoveCursor |
1286 | \li Qt.DragLinkCursor |
1287 | \endlist |
1288 | |
1289 | In order to only set a mouse cursor shape for a region without reacting |
1290 | to mouse events set the acceptedButtons to none: |
1291 | |
1292 | \code |
1293 | MouseArea { cursorShape: Qt.IBeamCursor; acceptedButtons: Qt.NoButton } |
1294 | \endcode |
1295 | |
1296 | The default value is \c Qt.ArrowCursor. |
1297 | \sa Qt::CursorShape |
1298 | */ |
1299 | |
1300 | #if QT_CONFIG(cursor) |
1301 | Qt::CursorShape QQuickMouseArea::cursorShape() const |
1302 | { |
1303 | return cursor().shape(); |
1304 | } |
1305 | |
1306 | void QQuickMouseArea::setCursorShape(Qt::CursorShape shape) |
1307 | { |
1308 | if (cursor().shape() == shape) |
1309 | return; |
1310 | |
1311 | setCursor(shape); |
1312 | |
1313 | emit cursorShapeChanged(); |
1314 | } |
1315 | |
1316 | #endif |
1317 | |
1318 | |
1319 | /*! |
1320 | \qmlproperty int QtQuick::MouseArea::pressAndHoldInterval |
1321 | \since 5.9 |
1322 | |
1323 | This property overrides the elapsed time in milliseconds before |
1324 | \c pressAndHold is emitted. |
1325 | |
1326 | If not explicitly set -- or after reset -- the value follows |
1327 | \c QStyleHints::mousePressAndHoldInterval. |
1328 | |
1329 | Typically it's sufficient to set this property globally using the |
1330 | application style hint. This property should be used when varying intervals |
1331 | are needed for certain MouseAreas. |
1332 | |
1333 | \sa pressAndHold |
1334 | */ |
1335 | int QQuickMouseArea::pressAndHoldInterval() const |
1336 | { |
1337 | Q_D(const QQuickMouseArea); |
1338 | return d->pressAndHoldInterval > -1 ? |
1339 | d->pressAndHoldInterval : QGuiApplication::styleHints()->mousePressAndHoldInterval(); |
1340 | } |
1341 | |
1342 | void QQuickMouseArea::setPressAndHoldInterval(int interval) |
1343 | { |
1344 | Q_D(QQuickMouseArea); |
1345 | if (interval != d->pressAndHoldInterval) { |
1346 | d->pressAndHoldInterval = interval; |
1347 | emit pressAndHoldIntervalChanged(); |
1348 | } |
1349 | } |
1350 | |
1351 | void QQuickMouseArea::resetPressAndHoldInterval() |
1352 | { |
1353 | Q_D(QQuickMouseArea); |
1354 | if (d->pressAndHoldInterval > -1) { |
1355 | d->pressAndHoldInterval = -1; |
1356 | emit pressAndHoldIntervalChanged(); |
1357 | } |
1358 | } |
1359 | |
1360 | /*! |
1361 | \qmlpropertygroup QtQuick::MouseArea::drag |
1362 | \qmlproperty Item QtQuick::MouseArea::drag.target |
1363 | \qmlproperty bool QtQuick::MouseArea::drag.active |
1364 | \qmlproperty enumeration QtQuick::MouseArea::drag.axis |
1365 | \qmlproperty real QtQuick::MouseArea::drag.minimumX |
1366 | \qmlproperty real QtQuick::MouseArea::drag.maximumX |
1367 | \qmlproperty real QtQuick::MouseArea::drag.minimumY |
1368 | \qmlproperty real QtQuick::MouseArea::drag.maximumY |
1369 | \qmlproperty bool QtQuick::MouseArea::drag.filterChildren |
1370 | \qmlproperty real QtQuick::MouseArea::drag.threshold |
1371 | \qmlproperty bool QtQuick::MouseArea::drag.smoothed |
1372 | |
1373 | \c drag provides a convenient way to make an item draggable. |
1374 | |
1375 | \list |
1376 | \li \c drag.target specifies the id of the item to drag. |
1377 | \li \c drag.active specifies if the target item is currently being dragged. |
1378 | \li \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XAndYAxis) |
1379 | \li \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes. |
1380 | \endlist |
1381 | |
1382 | The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity |
1383 | of the rectangle is reduced when it is dragged to the right. |
1384 | |
1385 | \snippet qml/mousearea/mousearea.qml drag |
1386 | |
1387 | \note Items cannot be dragged if they are anchored for the requested |
1388 | \c drag.axis. For example, if \c anchors.left or \c anchors.right was set |
1389 | for \c rect in the above example, it cannot be dragged along the X-axis. |
1390 | This can be avoided by settng the anchor value to \c undefined in |
1391 | an \l {pressed}{onPressed} handler. |
1392 | |
1393 | If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This |
1394 | enables a parent MouseArea to handle drags, for example, while descendants handle clicks: |
1395 | |
1396 | \c drag.threshold determines the threshold in pixels of when the drag operation should |
1397 | start. By default this is bound to a platform dependent value. This property was added in |
1398 | Qt Quick 2.2. |
1399 | |
1400 | If \c drag.smoothed is \c true, the target will be moved only after the drag operation has |
1401 | started. If set to \c false, the target will be moved straight to the current mouse position. |
1402 | By default, this property is \c true. This property was added in Qt Quick 2.4 |
1403 | |
1404 | See the \l Drag attached property and \l DropArea if you want to make a drop. |
1405 | |
1406 | \snippet qml/mousearea/mouseareadragfilter.qml dragfilter |
1407 | |
1408 | */ |
1409 | |
1410 | #if QT_CONFIG(quick_draganddrop) |
1411 | QQuickDrag *QQuickMouseArea::drag() |
1412 | { |
1413 | Q_D(QQuickMouseArea); |
1414 | if (!d->drag) |
1415 | d->drag = new QQuickDrag; |
1416 | return d->drag; |
1417 | } |
1418 | #endif |
1419 | |
1420 | QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) |
1421 | { |
1422 | Q_UNUSED(data); |
1423 | Q_D(QQuickMouseArea); |
1424 | |
1425 | if (!qmlVisualTouchDebugging()) |
1426 | return nullptr; |
1427 | |
1428 | QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode); |
1429 | if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode(); |
1430 | |
1431 | rectangle->setRect(QRectF(0, 0, width(), height())); |
1432 | rectangle->setColor(QColor(255, 0, 0, 50)); |
1433 | rectangle->update(); |
1434 | return rectangle; |
1435 | } |
1436 | |
1437 | QT_END_NAMESPACE |
1438 | |
1439 | #include "moc_qquickmousearea_p.cpp" |
1440 | |