1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qquickmousearea_p.h"
43 #include "qquickmousearea_p_p.h"
44 #include "qquickwindow.h"
45 #include "qquickevents_p_p.h"
46 #include "qquickdrag_p.h"
48 #include <private/qqmldata_p.h>
50 #include <QtGui/private/qguiapplication_p.h>
52 #include <QtGui/qevent.h>
53 #include <QtGui/qguiapplication.h>
54 #include <QtGui/qstylehints.h>
60 DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
62 static const int PressAndHoldDelay = 800;
64 #ifndef QT_NO_DRAGANDDROP
66 QQuickDrag::QQuickDrag(QObject *parent)
67 : QObject(parent), _target(0), _axis(XAndYAxis), _xmin(-FLT_MAX),
68 _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false)
72 QQuickDrag::~QQuickDrag()
76 QQuickItem *QQuickDrag::target() const
81 void QQuickDrag::setTarget(QQuickItem *t)
89 void QQuickDrag::resetTarget()
97 QQuickDrag::Axis QQuickDrag::axis() const
102 void QQuickDrag::setAxis(QQuickDrag::Axis a)
110 qreal QQuickDrag::xmin() const
115 void QQuickDrag::setXmin(qreal m)
120 emit minimumXChanged();
123 qreal QQuickDrag::xmax() const
128 void QQuickDrag::setXmax(qreal m)
133 emit maximumXChanged();
136 qreal QQuickDrag::ymin() const
141 void QQuickDrag::setYmin(qreal m)
146 emit minimumYChanged();
149 qreal QQuickDrag::ymax() const
154 void QQuickDrag::setYmax(qreal m)
159 emit maximumYChanged();
162 bool QQuickDrag::active() const
167 void QQuickDrag::setActive(bool drag)
172 emit activeChanged();
175 bool QQuickDrag::filterChildren() const
177 return _filterChildren;
180 void QQuickDrag::setFilterChildren(bool filter)
182 if (_filterChildren == filter)
184 _filterChildren = filter;
185 emit filterChildrenChanged();
188 QQuickDragAttached *QQuickDrag::qmlAttachedProperties(QObject *obj)
190 return new QQuickDragAttached(obj);
193 #endif // QT_NO_DRAGANDDROP
195 QQuickMouseAreaPrivate::QQuickMouseAreaPrivate()
196 : enabled(true), hovered(false), longPress(false),
197 moved(false), stealMouse(false), doubleClick(false), preventStealing(false),
198 propagateComposedEvents(false), pressed(0)
199 #ifndef QT_NO_DRAGANDDROP
208 QQuickMouseAreaPrivate::~QQuickMouseAreaPrivate()
210 #ifndef QT_NO_DRAGANDDROP
218 void QQuickMouseAreaPrivate::init()
220 Q_Q(QQuickMouseArea);
221 q->setAcceptedMouseButtons(Qt::LeftButton);
222 q->setFiltersChildMouseEvents(true);
223 if (qmlVisualTouchDebugging()) {
224 q->setFlag(QQuickItem::ItemHasContents);
228 void QQuickMouseAreaPrivate::saveEvent(QMouseEvent *event)
230 lastPos = event->localPos();
231 lastScenePos = event->windowPos();
232 lastButton = event->button();
233 lastButtons = event->buttons();
234 lastModifiers = event->modifiers();
237 bool QQuickMouseAreaPrivate::isPressAndHoldConnected()
239 Q_Q(QQuickMouseArea);
240 IS_SIGNAL_CONNECTED(q, QQuickMouseArea, pressAndHold, (QQuickMouseEvent *));
243 bool QQuickMouseAreaPrivate::isDoubleClickConnected()
245 Q_Q(QQuickMouseArea);
246 IS_SIGNAL_CONNECTED(q, QQuickMouseArea, doubleClicked, (QQuickMouseEvent *));
249 bool QQuickMouseAreaPrivate::isClickConnected()
251 Q_Q(QQuickMouseArea);
252 IS_SIGNAL_CONNECTED(q, QQuickMouseArea, clicked, (QQuickMouseEvent *));
255 bool QQuickMouseAreaPrivate::isWheelConnected()
257 Q_Q(QQuickMouseArea);
258 IS_SIGNAL_CONNECTED(q, QQuickMouseArea, wheel, (QQuickWheelEvent *));
261 void QQuickMouseAreaPrivate::propagate(QQuickMouseEvent* event, PropagateType t)
263 Q_Q(QQuickMouseArea);
264 if (!propagateComposedEvents)
266 QPointF scenePos = q->mapToScene(QPointF(event->x(), event->y()));
267 propagateHelper(event, window->contentItem(), scenePos, t);
270 bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *item,const QPointF &sp, PropagateType sig)
272 //Based off of QQuickWindow::deliverInitialMousePressEvent
273 //But specific to MouseArea, so doesn't belong in window
274 Q_Q(const QQuickMouseArea);
275 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
277 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
278 QPointF p = item->mapFromScene(sp);
279 if (!item->contains(p))
283 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
284 for (int ii = children.count() - 1; ii >= 0; --ii) {
285 QQuickItem *child = children.at(ii);
286 if (!child->isVisible() || !child->isEnabled())
288 if (propagateHelper(ev, child, sp, sig))
292 QQuickMouseArea* ma = qobject_cast<QQuickMouseArea*>(item);
293 if (ma && ma != q && itemPrivate->acceptedMouseButtons() & ev->button()) {
296 if (!ma->d_func()->isClickConnected())
300 if (!ma->d_func()->isDoubleClickConnected())
304 if (!ma->d_func()->isPressAndHoldConnected())
308 QPointF p = item->mapFromScene(sp);
309 if (item->contains(p)) {
312 ev->setAccepted(true);//It is connected, they have to explicitly ignore to let it slide
314 case Click: emit ma->clicked(ev); break;
315 case DoubleClick: emit ma->doubleClicked(ev); break;
316 case PressAndHold: emit ma->pressAndHold(ev); break;
318 if (ev->isAccepted())
328 \instantiates QQuickMouseArea
329 \inqmlmodule QtQuick 2
330 \ingroup qtquick-input
331 \brief Enables simple mouse handling
334 A MouseArea is an invisible item that is typically used in conjunction with
335 a visible item in order to provide mouse handling for that item.
336 By effectively acting as a proxy, the logic for mouse handling can be
337 contained within a MouseArea item.
339 The \l enabled property is used to enable and disable mouse handling for
340 the proxied item. When disabled, the mouse area becomes transparent to
343 The \l pressed read-only property indicates whether or not the user is
344 holding down a mouse button over the mouse area. This property is often
345 used in bindings between properties in a user interface. The containsMouse
346 read-only property indicates the presence of the mouse cursor over the
347 mouse area but, by default, only when a mouse button is held down; see
348 the containsMouse documentation for details.
350 Information about the mouse position and button clicks are provided via
351 signals for which event handler properties are defined. The most commonly
352 used involved handling mouse presses and clicks: onClicked, onDoubleClicked,
353 onPressed, onReleased and onPressAndHold. It's also possible to handle mouse
354 wheel events via the onWheel signal.
356 If a MouseArea overlaps with the area of other MouseArea items, you can choose
357 to propagate \c clicked, \c doubleClicked and \c pressAndHold events to these
358 other items by setting propagateComposedEvents to true and rejecting events
359 that should be propagated. See the propagateComposedEvents documentation for
362 By default, MouseArea items only report mouse clicks and not changes to the
363 position of the mouse cursor. Setting the hoverEnabled property ensures that
364 handlers defined for onPositionChanged, onEntered and onExited are used and
365 that the containsMouse property is updated even when no mouse buttons are
368 \section1 Example Usage
370 \div {class="float-right"}
371 \inlineimage qml-mousearea-snippet.png
374 The following example uses a MouseArea in a \l Rectangle that changes
375 the \l Rectangle color to red when clicked:
377 \snippet qml/mousearea/mousearea.qml import
379 \snippet qml/mousearea/mousearea.qml intro
382 Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains
383 additional information about the mouse event, such as the position, button,
384 and any key modifiers.
386 Here is an extension of the previous example that produces a different
387 color when the area is right clicked:
389 \snippet qml/mousearea/mousearea.qml intro-extended
391 \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example},
392 {Important Concepts In Qt Quick - User Input}
396 \qmlsignal QtQuick2::MouseArea::onEntered()
398 This handler is called when the mouse enters the mouse area.
400 By default the onEntered handler is only called while a button is
401 pressed. Setting hoverEnabled to true enables handling of
402 onEntered when no mouse button is pressed.
408 \qmlsignal QtQuick2::MouseArea::onExited()
410 This handler is called when the mouse exits the mouse area.
412 By default the onExited handler is only called while a button is
413 pressed. Setting hoverEnabled to true enables handling of
414 onExited when no mouse button is pressed.
416 The example below shows a fairly typical relationship between
417 two MouseAreas, with \c mouseArea2 on top of \c mouseArea1. Moving the
418 mouse into \c mouseArea2 from \c mouseArea1 will cause \c onExited
419 to be called for \c mouseArea1.
422 width: 400; height: 400
430 width: 100; height: 100
431 anchors.centerIn: parent
437 If instead you give the two mouseAreas a parent-child relationship,
438 moving the mouse into \c mouseArea2 from \c mouseArea1 will \b not
439 cause \c onExited to be called for \c mouseArea1. Instead, they will
440 both be considered to be simultaneously hovered.
446 \qmlsignal QtQuick2::MouseArea::onPositionChanged(MouseEvent mouse)
448 This handler is called when the mouse position changes.
450 The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y
451 position, and any buttons currently pressed.
453 The \e accepted property of the MouseEvent parameter is ignored in this handler.
455 By default the onPositionChanged handler is only called while a button is
456 pressed. Setting hoverEnabled to true enables handling of
457 onPositionChanged when no mouse button is pressed.
461 \qmlsignal QtQuick2::MouseArea::onClicked(MouseEvent mouse)
463 This handler is called when there is a click. A click is defined as a press followed by a release,
464 both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and
465 releasing is also considered a click).
467 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
468 position of the release of the click, and whether the click was held.
470 The \e accepted property of the MouseEvent parameter is ignored in this handler.
474 \qmlsignal QtQuick2::MouseArea::onPressed(MouseEvent mouse)
476 This handler is called when there is a press.
477 The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
478 position and which button was pressed.
480 The \e accepted property of the MouseEvent parameter determines whether this MouseArea
481 will handle the press and all future mouse events until release. The default is to accept
482 the event and not allow other MouseArea beneath this one to handle the event. If \e accepted
483 is set to false, no further events will be sent to this MouseArea until the button is next
488 \qmlsignal QtQuick2::MouseArea::onReleased(MouseEvent mouse)
490 This handler is called when there is a release.
491 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
492 position of the release of the click, and whether the click was held.
494 The \e accepted property of the MouseEvent parameter is ignored in this handler.
500 \qmlsignal QtQuick2::MouseArea::onPressAndHold(MouseEvent mouse)
502 This handler is called when there is a long press (currently 800ms).
503 The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
504 position of the press, and which button is pressed.
506 The \e accepted property of the MouseEvent parameter is ignored in this handler.
510 \qmlsignal QtQuick2::MouseArea::onDoubleClicked(MouseEvent mouse)
512 This handler is called when there is a double-click (a press followed by a release followed by a press).
513 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
514 position of the release of the click, and whether the click was held.
516 If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false
517 in the handler, the onPressed/onReleased/onClicked handlers will be called for the second
518 click; otherwise they are suppressed. The accepted property defaults to true.
522 \qmlsignal QtQuick2::MouseArea::onCanceled()
524 This handler is called when mouse events have been canceled, either because an event was not accepted, or
525 because another item stole the mouse event handling.
527 This signal is for advanced use: it is useful when there is more than one MouseArea
528 that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter
529 case, if you execute some logic on the pressed signal and then start dragging, the
530 \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset
531 the logic when the MouseArea has lost the mouse handling to the \l Flickable,
532 \c onCanceled should be used in addition to onReleased.
536 \qmlsignal QtQuick2::MouseArea::onWheel(WheelEvent wheel)
538 This handler is called in response to both mouse wheel and trackpad scroll gestures.
540 The \l {WheelEvent}{wheel} parameter provides information about the event, including the x and y
541 position, any buttons currently pressed, and information about the wheel movement, including
542 angleDelta and pixelDelta.
545 QQuickMouseArea::QQuickMouseArea(QQuickItem *parent)
546 : QQuickItem(*(new QQuickMouseAreaPrivate), parent)
548 Q_D(QQuickMouseArea);
552 QQuickMouseArea::~QQuickMouseArea()
557 \qmlproperty real QtQuick2::MouseArea::mouseX
558 \qmlproperty real QtQuick2::MouseArea::mouseY
559 These properties hold the coordinates of the mouse cursor.
561 If the hoverEnabled property is false then these properties will only be valid
562 while a button is pressed, and will remain valid as long as the button is held
563 down even if the mouse is moved outside the area.
565 By default, this property is false.
567 If hoverEnabled is true then these properties will be valid when:
569 \li no button is pressed, but the mouse is within the MouseArea (containsMouse is true).
570 \li a button is pressed and held, even if it has since moved out of the area.
573 The coordinates are relative to the MouseArea.
575 qreal QQuickMouseArea::mouseX() const
577 Q_D(const QQuickMouseArea);
578 return d->lastPos.x();
581 qreal QQuickMouseArea::mouseY() const
583 Q_D(const QQuickMouseArea);
584 return d->lastPos.y();
588 \qmlproperty bool QtQuick2::MouseArea::enabled
589 This property holds whether the item accepts mouse events.
591 By default, this property is true.
593 bool QQuickMouseArea::isEnabled() const
595 Q_D(const QQuickMouseArea);
599 void QQuickMouseArea::setEnabled(bool a)
601 Q_D(QQuickMouseArea);
602 if (a != d->enabled) {
604 emit enabledChanged();
609 \qmlproperty bool QtQuick2::MouseArea::preventStealing
610 This property holds whether the mouse events may be stolen from this
613 If a MouseArea is placed within an item that filters child mouse
614 events, such as Flickable, the mouse
615 events may be stolen from the MouseArea if a gesture is recognized
616 by the parent item, e.g. a flick gesture. If preventStealing is
617 set to true, no item will steal the mouse events.
619 Note that setting preventStealing to true once an item has started
620 stealing events will have no effect until the next press event.
622 By default this property is false.
624 bool QQuickMouseArea::preventStealing() const
626 Q_D(const QQuickMouseArea);
627 return d->preventStealing;
630 void QQuickMouseArea::setPreventStealing(bool prevent)
632 Q_D(QQuickMouseArea);
633 if (prevent != d->preventStealing) {
634 d->preventStealing = prevent;
635 setKeepMouseGrab(d->preventStealing && d->enabled);
636 emit preventStealingChanged();
642 \qmlproperty bool QtQuick2::MouseArea::propagateComposedEvents
643 This property holds whether composed mouse events will automatically propagate to
644 other MouseAreas that overlap with this MouseArea but are lower in the visual stacking order.
645 By default, this property is false.
647 MouseArea contains several composed events: \c clicked, \c doubleClicked and \c pressAndHold.
648 These are composed of basic mouse events, like \c pressed, and can be propagated differently
649 in comparison to basic events.
651 If propagateComposedEvents is set to true, then composed events will be automatically
652 propagated to other MouseAreas in the same location in the scene. Each event is propagated
653 to the next \l enabled MouseArea beneath it in the stacking order, propagating down this visual
654 hierarchy until a MouseArea accepts the event. Unlike \c pressed events, composed events will
655 not be automatically accepted if no handler is present.
657 For example, below is a yellow \l Rectangle that contains a blue \l Rectangle. The blue
658 rectangle is the top-most item in the hierarchy of the visual stacking order; it will
659 visually rendered above the yellow rectangle. Since the blue rectangle sets
660 propagateComposedEvents to true, and also sets \l MouseEvent::accepted to false for all
661 received \c clicked events, any \c clicked events it receives are propagated to the
662 MouseArea of the yellow rectangle beneath it.
669 width: 100; height: 100
673 onClicked: console.log("clicked yellow")
678 width: 50; height: 50
682 propagateComposedEvents: true
684 console.log("clicked blue")
685 mouse.accepted = false
692 Clicking on the blue rectangle will cause the \c onClicked handler of its child MouseArea to
693 be invoked; the event will then be propagated to the MouseArea of the yellow rectangle, causing
694 its own \c onClicked handler to be invoked.
696 This property greatly simplifies the usecase of when you want to have overlapping MouseAreas
697 handling the composed events together. For example: if you want one MouseArea to handle \c clicked
698 signals and the other to handle \c pressAndHold, or if you want one MouseArea to handle \c clicked most
699 of the time, but pass it through when certain conditions are met.
701 bool QQuickMouseArea::propagateComposedEvents() const
703 Q_D(const QQuickMouseArea);
704 return d->propagateComposedEvents;
707 void QQuickMouseArea::setPropagateComposedEvents(bool prevent)
709 Q_D(QQuickMouseArea);
710 if (prevent != d->propagateComposedEvents) {
711 d->propagateComposedEvents = prevent;
712 setKeepMouseGrab(d->propagateComposedEvents && d->enabled);
713 emit propagateComposedEventsChanged();
718 \qmlproperty MouseButtons QtQuick2::MouseArea::pressedButtons
719 This property holds the mouse buttons currently pressed.
721 It contains a bitwise combination of:
728 The code below displays "right" when the right mouse buttons is pressed:
730 \snippet qml/mousearea/mousearea.qml mousebuttons
732 \note this property only handles buttons specified in \l acceptedButtons.
736 Qt::MouseButtons QQuickMouseArea::pressedButtons() const
738 Q_D(const QQuickMouseArea);
742 void QQuickMouseArea::mousePressEvent(QMouseEvent *event)
744 Q_D(QQuickMouseArea);
746 d->stealMouse = d->preventStealing;
747 if (!d->enabled || !(event->button() & acceptedMouseButtons())) {
748 QQuickItem::mousePressEvent(event);
750 d->longPress = false;
752 #ifndef QT_NO_DRAGANDDROP
754 d->drag->setActive(false);
757 d->startScene = event->windowPos();
758 d->pressAndHoldTimer.start(PressAndHoldDelay, this);
759 setKeepMouseGrab(d->stealMouse);
760 event->setAccepted(setPressed(event->button(), true));
764 void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
766 Q_D(QQuickMouseArea);
767 if (!d->enabled && !d->pressed) {
768 QQuickItem::mouseMoveEvent(event);
774 // ### we should skip this if these signals aren't used
775 // ### can GV handle this for us?
776 const bool isInside = contains(d->lastPos);
777 if (d->hovered && !isInside)
779 else if (!d->hovered && isInside)
782 #ifndef QT_NO_DRAGANDDROP
783 if (d->drag && d->drag->target()) {
785 d->targetStartPos = d->drag->target()->parentItem()
786 ? d->drag->target()->parentItem()->mapToScene(d->drag->target()->position())
787 : d->drag->target()->position();
790 QPointF startLocalPos;
792 if (drag()->target()->parentItem()) {
793 startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene);
794 curLocalPos = drag()->target()->parentItem()->mapFromScene(event->windowPos());
796 startLocalPos = d->startScene;
797 curLocalPos = event->windowPos();
800 qreal dx = qAbs(curLocalPos.x() - startLocalPos.x());
801 qreal dy = qAbs(curLocalPos.y() - startLocalPos.y());
803 if (keepMouseGrab() && d->stealMouse && !d->drag->active())
804 d->drag->setActive(true);
806 QPointF startPos = d->drag->target()->parentItem()
807 ? d->drag->target()->parentItem()->mapFromScene(d->targetStartPos)
810 QPointF dragPos = d->drag->target()->position();
812 bool dragX = drag()->axis() & QQuickDrag::XAxis;
813 bool dragY = drag()->axis() & QQuickDrag::YAxis;
815 if (dragX && d->drag->active()) {
816 qreal x = (curLocalPos.x() - startLocalPos.x()) + startPos.x();
817 if (x < drag()->xmin())
819 else if (x > drag()->xmax())
823 if (dragY && d->drag->active()) {
824 qreal y = (curLocalPos.y() - startLocalPos.y()) + startPos.y();
825 if (y < drag()->ymin())
827 else if (y > drag()->ymax())
831 d->drag->target()->setPosition(dragPos);
833 if (!keepMouseGrab()) {
834 bool xDragged = QQuickWindowPrivate::dragOverThreshold(dx, Qt::XAxis, event);
835 bool yDragged = QQuickWindowPrivate::dragOverThreshold(dy, Qt::YAxis, event);
836 if ((!dragY && !yDragged && dragX && xDragged)
837 || (!dragX && !xDragged && dragY && yDragged)
838 || (dragX && dragY && (xDragged || yDragged))) {
839 setKeepMouseGrab(true);
840 d->stealMouse = true;
848 QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
849 emit mouseXChanged(&me);
850 me.setPosition(d->lastPos);
851 emit mouseYChanged(&me);
852 me.setPosition(d->lastPos);
853 emit positionChanged(&me);
856 void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event)
858 Q_D(QQuickMouseArea);
859 d->stealMouse = false;
860 if (!d->enabled && !d->pressed) {
861 QQuickItem::mouseReleaseEvent(event);
864 setPressed(event->button(), false);
866 // no other buttons are pressed
867 #ifndef QT_NO_DRAGANDDROP
869 d->drag->setActive(false);
871 // If we don't accept hover, we need to reset containsMouse.
872 if (!acceptHoverEvents())
874 QQuickWindow *w = window();
875 if (w && w->mouseGrabberItem() == this)
877 setKeepMouseGrab(false);
880 d->doubleClick = false;
883 void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event)
885 Q_D(QQuickMouseArea);
888 QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
889 me.setAccepted(d->isDoubleClickConnected());
890 emit this->doubleClicked(&me);
891 if (!me.isAccepted())
892 d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick);
893 d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
895 QQuickItem::mouseDoubleClickEvent(event);
898 void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event)
900 Q_D(QQuickMouseArea);
901 if (!d->enabled && !d->pressed) {
902 QQuickItem::hoverEnterEvent(event);
904 d->lastPos = event->posF();
905 d->lastModifiers = event->modifiers();
907 QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
908 emit mouseXChanged(&me);
909 me.setPosition(d->lastPos);
910 emit mouseYChanged(&me);
911 me.setPosition(d->lastPos);
915 void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event)
917 Q_D(QQuickMouseArea);
918 if (!d->enabled && !d->pressed) {
919 QQuickItem::hoverMoveEvent(event);
921 d->lastPos = event->posF();
922 d->lastModifiers = event->modifiers();
923 QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
924 emit mouseXChanged(&me);
925 me.setPosition(d->lastPos);
926 emit mouseYChanged(&me);
927 me.setPosition(d->lastPos);
928 emit positionChanged(&me);
932 void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event)
934 Q_D(QQuickMouseArea);
935 if (!d->enabled && !d->pressed)
936 QQuickItem::hoverLeaveEvent(event);
941 void QQuickMouseArea::wheelEvent(QWheelEvent *event)
943 Q_D(QQuickMouseArea);
945 QQuickItem::wheelEvent(event);
949 QQuickWheelEvent we(event->posF().x(), event->posF().y(), event->angleDelta(),
950 event->pixelDelta(), event->buttons(), event->modifiers());
951 we.setAccepted(d->isWheelConnected());
953 if (!we.isAccepted())
954 QQuickItem::wheelEvent(event);
957 void QQuickMouseArea::ungrabMouse()
959 Q_D(QQuickMouseArea);
961 // if our mouse grab has been removed (probably by Flickable), fix our
964 d->stealMouse = false;
965 setKeepMouseGrab(false);
967 emit pressedChanged();
968 emit pressedButtonsChanged();
971 emit hoveredChanged();
976 void QQuickMouseArea::mouseUngrabEvent()
981 bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event)
983 Q_D(QQuickMouseArea);
984 QPointF localPos = mapFromScene(event->windowPos());
986 QQuickWindow *c = window();
987 QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
988 bool stealThisEvent = d->stealMouse;
989 if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) {
990 QMouseEvent mouseEvent(event->type(), localPos, event->windowPos(), event->screenPos(),
991 event->button(), event->buttons(), event->modifiers());
992 mouseEvent.setAccepted(false);
994 switch (event->type()) {
995 case QEvent::MouseMove:
996 mouseMoveEvent(&mouseEvent);
998 case QEvent::MouseButtonPress:
999 mousePressEvent(&mouseEvent);
1001 case QEvent::MouseButtonRelease:
1002 mouseReleaseEvent(&mouseEvent);
1007 grabber = c->mouseGrabberItem();
1008 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
1011 return stealThisEvent;
1013 if (event->type() == QEvent::MouseButtonRelease) {
1015 d->pressed &= ~event->button();
1016 emit pressedButtonsChanged();
1018 // no other buttons are pressed
1019 d->stealMouse = false;
1020 if (c && c->mouseGrabberItem() == this)
1023 emit pressedChanged();
1026 emit hoveredChanged();
1034 bool QQuickMouseArea::childMouseEventFilter(QQuickItem *i, QEvent *e)
1036 Q_D(QQuickMouseArea);
1038 (!d->enabled || !isVisible()
1039 #ifndef QT_NO_DRAGANDDROP
1040 || !d->drag || !d->drag->filterChildren()
1044 return QQuickItem::childMouseEventFilter(i, e);
1045 switch (e->type()) {
1046 case QEvent::MouseButtonPress:
1047 case QEvent::MouseMove:
1048 case QEvent::MouseButtonRelease:
1049 return sendMouseEvent(static_cast<QMouseEvent *>(e));
1054 return QQuickItem::childMouseEventFilter(i, e);
1057 void QQuickMouseArea::timerEvent(QTimerEvent *event)
1059 Q_D(QQuickMouseArea);
1060 if (event->timerId() == d->pressAndHoldTimer.timerId()) {
1061 d->pressAndHoldTimer.stop();
1062 #ifndef QT_NO_DRAGANDDROP
1063 bool dragged = d->drag && d->drag->active();
1065 bool dragged = false;
1067 if (d->pressed && dragged == false && d->hovered == true) {
1068 d->longPress = true;
1069 QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
1070 me.setAccepted(d->isPressAndHoldConnected());
1071 emit pressAndHold(&me);
1072 if (!me.isAccepted())
1073 d->propagate(&me, QQuickMouseAreaPrivate::PressAndHold);
1074 if (!me.isAccepted()) // no one handled the long press - allow click
1075 d->longPress = false;
1080 void QQuickMouseArea::windowDeactivateEvent()
1083 QQuickItem::windowDeactivateEvent();
1086 void QQuickMouseArea::geometryChanged(const QRectF &newGeometry,
1087 const QRectF &oldGeometry)
1089 Q_D(QQuickMouseArea);
1090 QQuickItem::geometryChanged(newGeometry, oldGeometry);
1092 if (d->lastScenePos.isNull)
1093 d->lastScenePos = mapToScene(d->lastPos);
1094 else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y())
1095 d->lastPos = mapFromScene(d->lastScenePos);
1098 void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value)
1100 Q_D(QQuickMouseArea);
1102 case ItemVisibleHasChanged:
1103 if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) {
1105 QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
1106 d->lastScenePos = d->window->mapFromGlobal(cursorPos.toPoint());
1107 d->lastPos = mapFromScene(d->lastScenePos);
1109 setHovered(!d->hovered);
1116 QQuickItem::itemChange(change, value);
1120 \qmlproperty bool QtQuick2::MouseArea::hoverEnabled
1121 This property holds whether hover events are handled.
1123 By default, mouse events are only handled in response to a button event, or when a button is
1124 pressed. Hover enables handling of all mouse events even when no mouse button is
1127 This property affects the containsMouse property and the onEntered, onExited and
1128 onPositionChanged signals.
1130 bool QQuickMouseArea::hoverEnabled() const
1132 return acceptHoverEvents();
1135 void QQuickMouseArea::setHoverEnabled(bool h)
1137 if (h == acceptHoverEvents())
1140 setAcceptHoverEvents(h);
1141 emit hoverEnabledChanged();
1146 \qmlproperty bool QtQuick2::MouseArea::containsMouse
1147 This property holds whether the mouse is currently inside the mouse area.
1149 \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change.
1150 In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed.
1152 bool QQuickMouseArea::hovered() const
1154 Q_D(const QQuickMouseArea);
1159 \qmlproperty bool QtQuick2::MouseArea::pressed
1160 This property holds whether any of the \l acceptedButtons are currently pressed.
1162 bool QQuickMouseArea::pressed() const
1164 Q_D(const QQuickMouseArea);
1168 void QQuickMouseArea::setHovered(bool h)
1170 Q_D(QQuickMouseArea);
1171 if (d->hovered != h) {
1173 emit hoveredChanged();
1174 d->hovered ? emit entered() : emit exited();
1179 \qmlproperty Qt::MouseButtons QtQuick2::MouseArea::acceptedButtons
1180 This property holds the mouse buttons that the mouse area reacts to.
1182 To specify that the MouseArea will react to multiple buttons,
1183 Qt::MouseButtons flag values are combined using the "|" (or) operator:
1186 MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton }
1189 To indicate that all possible mouse buttons are to be accepted,
1190 the special value 'Qt.AllButtons' may be used:
1193 MouseArea { acceptedButtons: Qt.AllButtons }
1196 The default value is \c Qt.LeftButton.
1198 Qt::MouseButtons QQuickMouseArea::acceptedButtons() const
1200 return acceptedMouseButtons();
1203 void QQuickMouseArea::setAcceptedButtons(Qt::MouseButtons buttons)
1205 if (buttons != acceptedMouseButtons()) {
1206 setAcceptedMouseButtons(buttons);
1207 emit acceptedButtonsChanged();
1211 bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p)
1213 Q_D(QQuickMouseArea);
1215 #ifndef QT_NO_DRAGANDDROP
1216 bool dragged = d->drag && d->drag->active();
1218 bool dragged = false;
1220 bool wasPressed = d->pressed & button;
1221 bool isclick = wasPressed && p == false && dragged == false && d->hovered == true;
1222 Qt::MouseButtons oldPressed = d->pressed;
1224 if (wasPressed != p) {
1225 QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
1227 d->pressed |= button;
1228 if (!d->doubleClick)
1230 me.setPosition(d->lastPos);
1231 emit mouseXChanged(&me);
1232 me.setPosition(d->lastPos);
1233 emit mouseYChanged(&me);
1235 emit pressedChanged();
1236 emit pressedButtonsChanged();
1238 d->pressed &= ~button;
1240 me.setPosition(d->lastPos);
1242 emit pressedChanged();
1243 emit pressedButtonsChanged();
1244 if (isclick && !d->longPress && !d->doubleClick){
1245 me.setAccepted(d->isClickConnected());
1247 if (!me.isAccepted())
1248 d->propagate(&me, QQuickMouseAreaPrivate::Click);
1252 return me.isAccepted();
1259 \qmlproperty Qt::CursorShape QtQuick2::MouseArea::cursorShape
1260 This property holds the cursor shape for this mouse area.
1261 Note that on platforms that do not display a mouse cursor this may have
1264 The available cursor shapes are:
1267 \li Qt.UpArrowCursor
1271 \li Qt.SizeVerCursor
1272 \li Qt.SizeHorCursor
1273 \li Qt.SizeBDiagCursor
1274 \li Qt.SizeFDiagCursor
1275 \li Qt.SizeAllCursor
1279 \li Qt.PointingHandCursor
1280 \li Qt.ForbiddenCursor
1281 \li Qt.WhatsThisCursor
1283 \li Qt.OpenHandCursor
1284 \li Qt.ClosedHandCursor
1285 \li Qt.DragCopyCursor
1286 \li Qt.DragMoveCursor
1287 \li Qt.DragLinkCursor
1290 In order to only set a mouse cursor shape for a region without reacting
1291 to mouse events set the acceptedButtons to none:
1294 MouseArea { cursorShape: Qt.IBeamCursor; acceptedButtons: Qt.NoButton }
1297 The default value is \c Qt.ArrowCursor.
1301 #ifndef QT_NO_CURSOR
1302 Qt::CursorShape QQuickMouseArea::cursorShape() const
1304 return cursor().shape();
1307 void QQuickMouseArea::setCursorShape(Qt::CursorShape shape)
1309 if (cursor().shape() == shape)
1314 emit cursorShapeChanged();
1320 \qmlproperty Item QtQuick2::MouseArea::drag.target
1321 \qmlproperty bool QtQuick2::MouseArea::drag.active
1322 \qmlproperty enumeration QtQuick2::MouseArea::drag.axis
1323 \qmlproperty real QtQuick2::MouseArea::drag.minimumX
1324 \qmlproperty real QtQuick2::MouseArea::drag.maximumX
1325 \qmlproperty real QtQuick2::MouseArea::drag.minimumY
1326 \qmlproperty real QtQuick2::MouseArea::drag.maximumY
1327 \qmlproperty bool QtQuick2::MouseArea::drag.filterChildren
1329 \c drag provides a convenient way to make an item draggable.
1332 \li \c drag.target specifies the id of the item to drag.
1333 \li \c drag.active specifies if the target item is currently being dragged.
1334 \li \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XAndYAxis)
1335 \li \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes.
1338 The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity
1339 of the rectangle is reduced when it is dragged to the right.
1341 \snippet qml/mousearea/mousearea.qml drag
1343 \note Items cannot be dragged if they are anchored for the requested
1344 \c drag.axis. For example, if \c anchors.left or \c anchors.right was set
1345 for \c rect in the above example, it cannot be dragged along the X-axis.
1346 This can be avoided by settng the anchor value to \c undefined in
1347 an \l onPressed handler.
1349 If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This
1350 enables a parent MouseArea to handle drags, for example, while descendants handle clicks:
1352 \snippet qml/mousearea/mouseareadragfilter.qml dragfilter
1356 #ifndef QT_NO_DRAGANDDROP
1357 QQuickDrag *QQuickMouseArea::drag()
1359 Q_D(QQuickMouseArea);
1361 d->drag = new QQuickDrag;
1366 QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1369 Q_D(QQuickMouseArea);
1371 if (!qmlVisualTouchDebugging())
1374 QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
1375 if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode();
1377 rectangle->setRect(QRectF(0, 0, width(), height()));
1378 rectangle->setColor(QColor(255, 0, 0, 50));
1379 rectangle->update();