diff options
author | Kent Hansen <kent.hansen@nokia.com> | 2011-10-14 10:51:42 +0200 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2011-10-21 11:53:26 +0200 |
commit | bfb3c424abc7c839dc44de0e36024ede99ece736 (patch) | |
tree | b6814d0238bead8518b23c078beb818432de6ce4 /src/declarative/items/qquickmousearea.cpp | |
parent | 2d119a137f1bc0c8b1113cd6c58413a1b89a4c1c (diff) |
Rename Qt Quick-specific classes to QQuick*
The QSG (SceneGraph) prefix is too generic for
Qt Quick(2)-specific classes.
All the classes and files in the declarative/items
directory have been renamed.
In particular, for classes that are currently public,
the renaming is as follows:
QSGView --> QQuickView
QSGCanvas --> QQuickCanvas
QSGItem --> QQuickItem
QSGPaintedItem --> QQuickPaintedItem
The header files have been renamed accordingly
(e.g. qsgview.h --> qquickview.h).
Change-Id: Iac937fff81db20bb639486a793c3aeb5230b038c
Reviewed-by: Kent Hansen <kent.hansen@nokia.com>
Diffstat (limited to 'src/declarative/items/qquickmousearea.cpp')
-rw-r--r-- | src/declarative/items/qquickmousearea.cpp | 1131 |
1 files changed, 1131 insertions, 0 deletions
diff --git a/src/declarative/items/qquickmousearea.cpp b/src/declarative/items/qquickmousearea.cpp new file mode 100644 index 0000000000..19e7fd457b --- /dev/null +++ b/src/declarative/items/qquickmousearea.cpp @@ -0,0 +1,1131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickmousearea_p.h" +#include "qquickmousearea_p_p.h" +#include "qquickcanvas.h" +#include "qquickevents_p_p.h" +#include "qquickdrag_p.h" + +#include <QtGui/qevent.h> +#include <QtGui/qguiapplication.h> +#include <QtGui/qstylehints.h> + +#include <float.h> + +QT_BEGIN_NAMESPACE +static const int PressAndHoldDelay = 800; + +QQuickDrag::QQuickDrag(QObject *parent) +: QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), +_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false) +{ +} + +QQuickDrag::~QQuickDrag() +{ +} + +QQuickItem *QQuickDrag::target() const +{ + return _target; +} + +void QQuickDrag::setTarget(QQuickItem *t) +{ + if (_target == t) + return; + _target = t; + emit targetChanged(); +} + +void QQuickDrag::resetTarget() +{ + if (_target == 0) + return; + _target = 0; + emit targetChanged(); +} + +QQuickDrag::Axis QQuickDrag::axis() const +{ + return _axis; +} + +void QQuickDrag::setAxis(QQuickDrag::Axis a) +{ + if (_axis == a) + return; + _axis = a; + emit axisChanged(); +} + +qreal QQuickDrag::xmin() const +{ + return _xmin; +} + +void QQuickDrag::setXmin(qreal m) +{ + if (_xmin == m) + return; + _xmin = m; + emit minimumXChanged(); +} + +qreal QQuickDrag::xmax() const +{ + return _xmax; +} + +void QQuickDrag::setXmax(qreal m) +{ + if (_xmax == m) + return; + _xmax = m; + emit maximumXChanged(); +} + +qreal QQuickDrag::ymin() const +{ + return _ymin; +} + +void QQuickDrag::setYmin(qreal m) +{ + if (_ymin == m) + return; + _ymin = m; + emit minimumYChanged(); +} + +qreal QQuickDrag::ymax() const +{ + return _ymax; +} + +void QQuickDrag::setYmax(qreal m) +{ + if (_ymax == m) + return; + _ymax = m; + emit maximumYChanged(); +} + +bool QQuickDrag::active() const +{ + return _active; +} + +void QQuickDrag::setActive(bool drag) +{ + if (_active == drag) + return; + _active = drag; + emit activeChanged(); +} + +bool QQuickDrag::filterChildren() const +{ + return _filterChildren; +} + +void QQuickDrag::setFilterChildren(bool filter) +{ + if (_filterChildren == filter) + return; + _filterChildren = filter; + emit filterChildrenChanged(); +} + +QQuickDragAttached *QQuickDrag::qmlAttachedProperties(QObject *obj) +{ + return new QQuickDragAttached(obj); +} + +QQuickMouseAreaPrivate::QQuickMouseAreaPrivate() +: absorb(true), hovered(false), pressed(false), longPress(false), + moved(false), stealMouse(false), doubleClick(false), preventStealing(false), + drag(0) +{ +} + +QQuickMouseAreaPrivate::~QQuickMouseAreaPrivate() +{ + delete drag; +} + +void QQuickMouseAreaPrivate::init() +{ + Q_Q(QQuickMouseArea); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFiltersChildMouseEvents(true); +} + +void QQuickMouseAreaPrivate::saveEvent(QMouseEvent *event) +{ + lastPos = event->localPos(); + lastScenePos = event->windowPos(); + lastButton = event->button(); + lastButtons = event->buttons(); + lastModifiers = event->modifiers(); +} + +bool QQuickMouseAreaPrivate::isPressAndHoldConnected() +{ + Q_Q(QQuickMouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("pressAndHold(QQuickMouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); +} + +bool QQuickMouseAreaPrivate::isDoubleClickConnected() +{ + Q_Q(QQuickMouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("doubleClicked(QQuickMouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); +} + +bool QQuickMouseAreaPrivate::isClickConnected() +{ + Q_Q(QQuickMouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("clicked(QQuickMouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); +} + +void QQuickMouseAreaPrivate::propagate(QQuickMouseEvent* event, PropagateType t) +{ + Q_Q(QQuickMouseArea); + QPointF scenePos = q->mapToScene(QPointF(event->x(), event->y())); + propagateHelper(event, canvas->rootItem(), scenePos, t); +} + +bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *item,const QPointF &sp, PropagateType sig) +{ + //Based off of QQuickCanvas::deliverInitialMousePressEvent + //But specific to MouseArea, so doesn't belong in canvas + Q_Q(const QQuickMouseArea); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(sp); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QQuickItem *child = children.at(ii); + if (!child->isVisible() || !child->isEnabled()) + continue; + if (propagateHelper(ev, child, sp, sig)) + return true; + } + + QQuickMouseArea* ma = qobject_cast<QQuickMouseArea*>(item); + if (ma && ma != q && itemPrivate->acceptedMouseButtons & ev->button()) { + switch (sig) { + case Click: + if (!ma->d_func()->isClickConnected()) + return false; + break; + case DoubleClick: + if (!ma->d_func()->isDoubleClickConnected()) + return false; + break; + case PressAndHold: + if (!ma->d_func()->isPressAndHoldConnected()) + return false; + break; + } + QPointF p = item->mapFromScene(sp); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + ev->setX(p.x()); + ev->setY(p.y()); + ev->setAccepted(true);//It is connected, they have to explicitly ignore to let it slide + switch (sig) { + case Click: emit ma->clicked(ev); break; + case DoubleClick: emit ma->doubleClicked(ev); break; + case PressAndHold: emit ma->pressAndHold(ev); break; + } + if (ev->isAccepted()) + return true; + } + } + return false; + +} + +/*! + \qmlclass MouseArea QQuickMouseArea + \inqmlmodule QtQuick 2 + \ingroup qml-basic-interaction-elements + \brief The MouseArea item enables simple mouse handling. + \inherits Item + + A MouseArea is an invisible item that is typically used in conjunction with + a visible item in order to provide mouse handling for that item. + By effectively acting as a proxy, the logic for mouse handling can be + contained within a MouseArea item. + + For basic key handling, see the \l{Keys}{Keys attached property}. + + The \l enabled property is used to enable and disable mouse handling for + the proxied item. When disabled, the mouse area becomes transparent to + mouse events. + + The \l pressed read-only property indicates whether or not the user is + holding down a mouse button over the mouse area. This property is often + used in bindings between properties in a user interface. The containsMouse + read-only property indicates the presence of the mouse cursor over the + mouse area but, by default, only when a mouse button is held down; see below + for further details. + + Information about the mouse position and button clicks are provided via + signals for which event handler properties are defined. The most commonly + used involved handling mouse presses and clicks: onClicked, onDoubleClicked, + onPressed, onReleased and onPressAndHold. + + By default, MouseArea items only report mouse clicks and not changes to the + position of the mouse cursor. Setting the hoverEnabled property ensures that + handlers defined for onPositionChanged, onEntered and onExited are used and + that the containsMouse property is updated even when no mouse buttons are + pressed. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage qml-mousearea-snippet.png + \enddiv + + The following example uses a MouseArea in a \l Rectangle that changes + the \l Rectangle color to red when clicked: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml import + \codeline + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro + + \clearfloat + Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains + additional information about the mouse event, such as the position, button, + and any key modifiers. + + Here is an extension of the previous example that produces a different + color when the area is right clicked: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro-extended + + Behavioral Change in QtQuick 2.0 + + From QtQuick 2.0, the signals clicked, doubleClicked and pressAndHold have a different interaction + model with regards to the delivery of events to multiple overlapping MouseAreas. These signals will now propagate + to all MouseAreas in the area, in painting order, until accepted by one of them. A signal is accepted by + default if there is a signal handler for it, use mouse.accepted = false; to ignore. This propagation + can send the signal to MouseAreas other than the one which accepted the press event, although that MouseArea + will receive the signal first. + + Note that to get the same behavior as a QtQuick 1.0 MouseArea{} with regard to absorbing all mouse events, you will + now need to add empty signal handlers for these three signals. + + \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example} +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onEntered() + + This handler is called when the mouse enters the mouse area. + + By default the onEntered handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onEntered when no mouse button is pressed. + + \sa hoverEnabled +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onExited() + + This handler is called when the mouse exits the mouse area. + + By default the onExited handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onExited when no mouse button is pressed. + + The example below shows a fairly typical relationship between + two MouseAreas, with \c mouseArea2 on top of \c mouseArea1. Moving the + mouse into \c mouseArea2 from \c mouseArea1 will cause \c onExited + to be called for \c mouseArea1. + \qml + Rectangle { + width: 400; height: 400 + MouseArea { + id: mouseArea1 + anchors.fill: parent + hoverEnabled: true + } + MouseArea { + id: mouseArea2 + width: 100; height: 100 + anchors.centerIn: parent + hoverEnabled: true + } + } + \endqml + + If instead you give the two mouseAreas a parent-child relationship, + moving the mouse into \c mouseArea2 from \c mouseArea1 will \b not + cause \c onExited to be called for \c mouseArea1. Instead, they will + both be considered to be simultaneously hovered. + + \sa hoverEnabled +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onPositionChanged(MouseEvent mouse) + + This handler is called when the mouse position changes. + + The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y + position, and any buttons currently pressed. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. + + By default the onPositionChanged handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onPositionChanged when no mouse button is pressed. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onClicked(MouseEvent mouse) + + This handler is called when there is a click. A click is defined as a press followed by a release, + both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and + releasing is also considered a click). + + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onPressed(MouseEvent mouse) + + This handler is called when there is a press. + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position and which button was pressed. + + The \e accepted property of the MouseEvent parameter determines whether this MouseArea + will handle the press and all future mouse events until release. The default is to accept + the event and not allow other MouseArea beneath this one to handle the event. If \e accepted + is set to false, no further events will be sent to this MouseArea until the button is next + pressed. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onReleased(MouseEvent mouse) + + This handler is called when there is a release. + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. + + \sa onCanceled +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onPressAndHold(MouseEvent mouse) + + This handler is called when there is a long press (currently 800ms). + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position of the press, and which button is pressed. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onDoubleClicked(MouseEvent mouse) + + This handler is called when there is a double-click (a press followed by a release followed by a press). + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false + in the handler, the onPressed/onReleased/onClicked handlers will be called for the second + click; otherwise they are suppressed. The accepted property defaults to true. +*/ + +/*! + \qmlsignal QtQuick2::MouseArea::onCanceled() + + This handler is called when mouse events have been canceled, either because an event was not accepted, or + because another element stole the mouse event handling. + + This signal is for advanced use: it is useful when there is more than one MouseArea + that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter + case, if you execute some logic on the pressed signal and then start dragging, the + \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset + the logic when the MouseArea has lost the mouse handling to the \l Flickable, + \c onCanceled should be used in addition to onReleased. +*/ +QQuickMouseArea::QQuickMouseArea(QQuickItem *parent) + : QQuickItem(*(new QQuickMouseAreaPrivate), parent) +{ + Q_D(QQuickMouseArea); + d->init(); +} + +QQuickMouseArea::~QQuickMouseArea() +{ +} + +/*! + \qmlproperty real QtQuick2::MouseArea::mouseX + \qmlproperty real QtQuick2::MouseArea::mouseY + These properties hold the coordinates of the mouse cursor. + + If the hoverEnabled property is false then these properties will only be valid + while a button is pressed, and will remain valid as long as the button is held + down even if the mouse is moved outside the area. + + By default, this property is false. + + If hoverEnabled is true then these properties will be valid when: + \list + \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true). + \i a button is pressed and held, even if it has since moved out of the area. + \endlist + + The coordinates are relative to the MouseArea. +*/ +qreal QQuickMouseArea::mouseX() const +{ + Q_D(const QQuickMouseArea); + return d->lastPos.x(); +} + +qreal QQuickMouseArea::mouseY() const +{ + Q_D(const QQuickMouseArea); + return d->lastPos.y(); +} + +/*! + \qmlproperty bool QtQuick2::MouseArea::enabled + This property holds whether the item accepts mouse events. + + By default, this property is true. +*/ +bool QQuickMouseArea::isEnabled() const +{ + Q_D(const QQuickMouseArea); + return d->absorb; +} + +void QQuickMouseArea::setEnabled(bool a) +{ + Q_D(QQuickMouseArea); + if (a != d->absorb) { + d->absorb = a; + emit enabledChanged(); + } +} + +/*! + \qmlproperty bool QtQuick2::MouseArea::preventStealing + This property holds whether the mouse events may be stolen from this + MouseArea. + + If a MouseArea is placed within an item that filters child mouse + events, such as Flickable, the mouse + events may be stolen from the MouseArea if a gesture is recognized + by the parent element, e.g. a flick gesture. If preventStealing is + set to true, no element will steal the mouse events. + + Note that setting preventStealing to true once an element has started + stealing events will have no effect until the next press event. + + By default this property is false. +*/ +bool QQuickMouseArea::preventStealing() const +{ + Q_D(const QQuickMouseArea); + return d->preventStealing; +} + +void QQuickMouseArea::setPreventStealing(bool prevent) +{ + Q_D(QQuickMouseArea); + if (prevent != d->preventStealing) { + d->preventStealing = prevent; + setKeepMouseGrab(d->preventStealing && d->absorb); + emit preventStealingChanged(); + } +} + +/*! + \qmlproperty MouseButtons QtQuick2::MouseArea::pressedButtons + This property holds the mouse buttons currently pressed. + + It contains a bitwise combination of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist + + The code below displays "right" when the right mouse buttons is pressed: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml mousebuttons + + \sa acceptedButtons +*/ +Qt::MouseButtons QQuickMouseArea::pressedButtons() const +{ + Q_D(const QQuickMouseArea); + return d->lastButtons; +} + +void QQuickMouseArea::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + d->moved = false; + d->stealMouse = d->preventStealing; + if (!d->absorb) + QQuickItem::mousePressEvent(event); + else { + d->longPress = false; + d->saveEvent(event); + if (d->drag) { + d->dragX = drag()->axis() & QQuickDrag::XAxis; + d->dragY = drag()->axis() & QQuickDrag::YAxis; + } + if (d->drag) + d->drag->setActive(false); + setHovered(true); + d->startScene = event->windowPos(); + d->pressAndHoldTimer.start(PressAndHoldDelay, this); + setKeepMouseGrab(d->stealMouse); + event->setAccepted(setPressed(true)); + + } +} + +void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + if (!d->absorb) { + QQuickItem::mouseMoveEvent(event); + return; + } + + d->saveEvent(event); + + // ### we should skip this if these signals aren't used + // ### can GV handle this for us? + bool contains = boundingRect().contains(d->lastPos); + if (d->hovered && !contains) + setHovered(false); + else if (!d->hovered && contains) + setHovered(true); + + if (d->drag && d->drag->target()) { + if (!d->moved) { + d->targetStartPos = d->drag->target()->parentItem() + ? d->drag->target()->parentItem()->mapToScene(d->drag->target()->pos()) + : d->drag->target()->pos(); + } + + QPointF startLocalPos; + QPointF curLocalPos; + if (drag()->target()->parentItem()) { + startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene); + curLocalPos = drag()->target()->parentItem()->mapFromScene(event->windowPos()); + } else { + startLocalPos = d->startScene; + curLocalPos = event->windowPos(); + } + + const int dragThreshold = qApp->styleHints()->startDragDistance(); + qreal dx = qAbs(curLocalPos.x() - startLocalPos.x()); + qreal dy = qAbs(curLocalPos.y() - startLocalPos.y()); + + if (keepMouseGrab() && d->stealMouse && !d->drag->active()) + d->drag->setActive(true); + + QPointF startPos = d->drag->target()->parentItem() + ? d->drag->target()->parentItem()->mapFromScene(d->targetStartPos) + : d->targetStartPos; + + QPointF dragPos = d->drag->target()->pos(); + + if (d->dragX && d->drag->active()) { + qreal x = (curLocalPos.x() - startLocalPos.x()) + startPos.x(); + if (x < drag()->xmin()) + x = drag()->xmin(); + else if (x > drag()->xmax()) + x = drag()->xmax(); + dragPos.setX(x); + } + if (d->dragY && d->drag->active()) { + qreal y = (curLocalPos.y() - startLocalPos.y()) + startPos.y(); + if (y < drag()->ymin()) + y = drag()->ymin(); + else if (y > drag()->ymax()) + y = drag()->ymax(); + dragPos.setY(y); + } + d->drag->target()->setPos(dragPos); + + if (!keepMouseGrab()) { + if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold) + || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold) + || (d->dragX && d->dragY && (dx > dragThreshold || dy > dragThreshold))) { + setKeepMouseGrab(true); + d->stealMouse = true; + } + } + + d->moved = true; + } + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + emit mouseXChanged(&me); + me.setPosition(d->lastPos); + emit mouseYChanged(&me); + me.setPosition(d->lastPos); + emit positionChanged(&me); +} + +void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + d->stealMouse = false; + if (!d->absorb) { + QQuickItem::mouseReleaseEvent(event); + } else { + d->saveEvent(event); + setPressed(false); + if (d->drag) + d->drag->setActive(false); + // If we don't accept hover, we need to reset containsMouse. + if (!acceptHoverEvents()) + setHovered(false); + QQuickCanvas *c = canvas(); + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + + } + d->doubleClick = false; +} + +void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + if (d->absorb) { + d->saveEvent(event); + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false); + me.setAccepted(d->isDoubleClickConnected()); + emit this->doubleClicked(&me); + if (!me.isAccepted()) + d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick); + d->doubleClick = d->isDoubleClickConnected() || me.isAccepted(); + } + QQuickItem::mouseDoubleClickEvent(event); +} + +void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event) +{ + Q_D(QQuickMouseArea); + if (!d->absorb) { + QQuickItem::hoverEnterEvent(event); + } else { + d->lastPos = event->posF(); + d->lastModifiers = event->modifiers(); + setHovered(true); + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); + emit mouseXChanged(&me); + me.setPosition(d->lastPos); + emit mouseYChanged(&me); + me.setPosition(d->lastPos); + } +} + +void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event) +{ + Q_D(QQuickMouseArea); + if (!d->absorb) { + QQuickItem::hoverMoveEvent(event); + } else { + d->lastPos = event->posF(); + d->lastModifiers = event->modifiers(); + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); + emit mouseXChanged(&me); + me.setPosition(d->lastPos); + emit mouseYChanged(&me); + me.setPosition(d->lastPos); + emit positionChanged(&me); + } +} + +void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event) +{ + Q_D(QQuickMouseArea); + if (!d->absorb) + QQuickItem::hoverLeaveEvent(event); + else + setHovered(false); +} + +void QQuickMouseArea::ungrabMouse() +{ + Q_D(QQuickMouseArea); + if (d->pressed) { + // if our mouse grab has been removed (probably by Flickable), fix our + // state + d->pressed = false; + d->stealMouse = false; + setKeepMouseGrab(false); + emit canceled(); + emit pressedChanged(); + if (d->hovered) { + d->hovered = false; + emit hoveredChanged(); + } + } +} + +void QQuickMouseArea::mouseUngrabEvent() +{ + ungrabMouse(); +} + +bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event) +{ + Q_D(QQuickMouseArea); + QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height())); + + QQuickCanvas *c = canvas(); + QQuickItem *grabber = c ? c->mouseGrabberItem() : 0; + bool stealThisEvent = d->stealMouse; + if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) { + QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + mouseEvent.setAccepted(false); + + switch (event->type()) { + case QEvent::MouseMove: + mouseMoveEvent(&mouseEvent); + break; + case QEvent::MouseButtonPress: + mousePressEvent(&mouseEvent); + break; + case QEvent::MouseButtonRelease: + mouseReleaseEvent(&mouseEvent); + break; + default: + break; + } + grabber = c->mouseGrabberItem(); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return stealThisEvent; + } + if (event->type() == QEvent::MouseButtonRelease) { + if (d->pressed) { + d->pressed = false; + d->stealMouse = false; + if (c && c->mouseGrabberItem() == this) + ungrabMouse(); + emit canceled(); + emit pressedChanged(); + if (d->hovered) { + d->hovered = false; + emit hoveredChanged(); + } + } + } + return false; +} + +bool QQuickMouseArea::childMouseEventFilter(QQuickItem *i, QEvent *e) +{ + Q_D(QQuickMouseArea); + if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren()) + return QQuickItem::childMouseEventFilter(i, e); + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + return sendMouseEvent(static_cast<QMouseEvent *>(e)); + default: + break; + } + + return QQuickItem::childMouseEventFilter(i, e); +} + +void QQuickMouseArea::timerEvent(QTimerEvent *event) +{ + Q_D(QQuickMouseArea); + if (event->timerId() == d->pressAndHoldTimer.timerId()) { + d->pressAndHoldTimer.stop(); + bool dragged = d->drag && d->drag->active(); + if (d->pressed && dragged == false && d->hovered == true) { + d->longPress = true; + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + me.setAccepted(d->isPressAndHoldConnected()); + emit pressAndHold(&me); + if (!me.isAccepted()) + d->propagate(&me, QQuickMouseAreaPrivate::PressAndHold); + if (!me.isAccepted()) // no one handled the long press - allow click + d->longPress = false; + } + } +} + +void QQuickMouseArea::windowDeactivateEvent() +{ + ungrabMouse(); + QQuickItem::windowDeactivateEvent(); +} + +void QQuickMouseArea::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QQuickMouseArea); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + if (d->lastScenePos.isNull) + d->lastScenePos = mapToScene(d->lastPos); + else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y()) + d->lastPos = mapFromScene(d->lastScenePos); +} + +void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickMouseArea); + switch (change) { + case ItemVisibleHasChanged: + if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) + setHovered(!d->hovered); + break; + default: + break; + } + + QQuickItem::itemChange(change, value); +} + +/*! + \qmlproperty bool QtQuick2::MouseArea::hoverEnabled + This property holds whether hover events are handled. + + By default, mouse events are only handled in response to a button event, or when a button is + pressed. Hover enables handling of all mouse events even when no mouse button is + pressed. + + This property affects the containsMouse property and the onEntered, onExited and + onPositionChanged signals. +*/ +bool QQuickMouseArea::hoverEnabled() const +{ + return acceptHoverEvents(); +} + +void QQuickMouseArea::setHoverEnabled(bool h) +{ + if (h == acceptHoverEvents()) + return; + + setAcceptHoverEvents(h); + emit hoverEnabledChanged(); +} + + +/*! + \qmlproperty bool QtQuick2::MouseArea::containsMouse + This property holds whether the mouse is currently inside the mouse area. + + \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change. + In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed. +*/ +bool QQuickMouseArea::hovered() const +{ + Q_D(const QQuickMouseArea); + return d->hovered; +} + +/*! + \qmlproperty bool QtQuick2::MouseArea::pressed + This property holds whether the mouse area is currently pressed. +*/ +bool QQuickMouseArea::pressed() const +{ + Q_D(const QQuickMouseArea); + return d->pressed; +} + +void QQuickMouseArea::setHovered(bool h) +{ + Q_D(QQuickMouseArea); + if (d->hovered != h) { + d->hovered = h; + emit hoveredChanged(); + d->hovered ? emit entered() : emit exited(); + } +} +/*! + \qmlproperty QtQuick2::Qt::MouseButtons MouseArea::acceptedButtons + This property holds the mouse buttons that the mouse area reacts to. + + The available buttons are: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist + + To accept more than one button the flags can be combined with the + "|" (or) operator: + + \code + MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton } + \endcode + + The default value is \c Qt.LeftButton. +*/ +Qt::MouseButtons QQuickMouseArea::acceptedButtons() const +{ + return acceptedMouseButtons(); +} + +void QQuickMouseArea::setAcceptedButtons(Qt::MouseButtons buttons) +{ + if (buttons != acceptedMouseButtons()) { + setAcceptedMouseButtons(buttons); + emit acceptedButtonsChanged(); + } +} + +bool QQuickMouseArea::setPressed(bool p) +{ + Q_D(QQuickMouseArea); + bool dragged = d->drag && d->drag->active(); + bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true; + + if (d->pressed != p) { + d->pressed = p; + QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress); + if (d->pressed) { + if (!d->doubleClick) + emit pressed(&me); + me.setPosition(d->lastPos); + emit mouseXChanged(&me); + me.setPosition(d->lastPos); + emit mouseYChanged(&me); + emit pressedChanged(); + } else { + emit released(&me); + me.setPosition(d->lastPos); + emit pressedChanged(); + if (isclick && !d->longPress && !d->doubleClick){ + me.setAccepted(d->isClickConnected()); + emit clicked(&me); + if (!me.isAccepted()) + d->propagate(&me, QQuickMouseAreaPrivate::Click); + } + } + + return me.isAccepted(); + } + return false; +} + +/*! + \qmlproperty Item QtQuick2::MouseArea::drag.target + \qmlproperty bool QtQuick2::MouseArea::drag.active + \qmlproperty enumeration QtQuick2::MouseArea::drag.axis + \qmlproperty real QtQuick2::MouseArea::drag.minimumX + \qmlproperty real QtQuick2::MouseArea::drag.maximumX + \qmlproperty real QtQuick2::MouseArea::drag.minimumY + \qmlproperty real QtQuick2::MouseArea::drag.maximumY + \qmlproperty bool QtQuick2::MouseArea::drag.filterChildren + + \c drag provides a convenient way to make an item draggable. + + \list + \i \c drag.target specifies the id of the item to drag. + \i \c drag.active specifies if the target item is currently being dragged. + \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis) + \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes. + \endlist + + The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity + of the rectangle is reduced when it is dragged to the right. + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag + + \note Items cannot be dragged if they are anchored for the requested + \c drag.axis. For example, if \c anchors.left or \c anchors.right was set + for \c rect in the above example, it cannot be dragged along the X-axis. + This can be avoided by settng the anchor value to \c undefined in + an \l onPressed handler. + + If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This + enables a parent MouseArea to handle drags, for example, while descendants handle clicks: + + \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter + +*/ + +QQuickDrag *QQuickMouseArea::drag() +{ + Q_D(QQuickMouseArea); + if (!d->drag) + d->drag = new QQuickDrag; + return d->drag; +} + +QT_END_NAMESPACE |