1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtDeclarative 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativemousearea_p.h"
43 #include "private/qdeclarativemousearea_p_p.h"
44
45 #include "private/qdeclarativeevents_p_p.h"
46
47 #include <QGraphicsSceneMouseEvent>
48
49 #include <float.h>
50
51 QT_BEGIN_NAMESPACE
52 static const int PressAndHoldDelay = 800;
53
QDeclarativeDrag(QObject * parent)54 QDeclarativeDrag::QDeclarativeDrag(QObject *parent)
55 : QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX),
56 _active(false), _filterChildren(false)
57 {
58 }
59
~QDeclarativeDrag()60 QDeclarativeDrag::~QDeclarativeDrag()
61 {
62 }
63
target() const64 QGraphicsObject *QDeclarativeDrag::target() const
65 {
66 return _target;
67 }
68
setTarget(QGraphicsObject * t)69 void QDeclarativeDrag::setTarget(QGraphicsObject *t)
70 {
71 if (_target == t)
72 return;
73 _target = t;
74 emit targetChanged();
75 }
76
resetTarget()77 void QDeclarativeDrag::resetTarget()
78 {
79 if (!_target)
80 return;
81 _target = 0;
82 emit targetChanged();
83 }
84
axis() const85 QDeclarativeDrag::Axis QDeclarativeDrag::axis() const
86 {
87 return _axis;
88 }
89
setAxis(QDeclarativeDrag::Axis a)90 void QDeclarativeDrag::setAxis(QDeclarativeDrag::Axis a)
91 {
92 if (_axis == a)
93 return;
94 _axis = a;
95 emit axisChanged();
96 }
97
xmin() const98 qreal QDeclarativeDrag::xmin() const
99 {
100 return _xmin;
101 }
102
setXmin(qreal m)103 void QDeclarativeDrag::setXmin(qreal m)
104 {
105 if (_xmin == m)
106 return;
107 _xmin = m;
108 emit minimumXChanged();
109 }
110
xmax() const111 qreal QDeclarativeDrag::xmax() const
112 {
113 return _xmax;
114 }
115
setXmax(qreal m)116 void QDeclarativeDrag::setXmax(qreal m)
117 {
118 if (_xmax == m)
119 return;
120 _xmax = m;
121 emit maximumXChanged();
122 }
123
ymin() const124 qreal QDeclarativeDrag::ymin() const
125 {
126 return _ymin;
127 }
128
setYmin(qreal m)129 void QDeclarativeDrag::setYmin(qreal m)
130 {
131 if (_ymin == m)
132 return;
133 _ymin = m;
134 emit minimumYChanged();
135 }
136
ymax() const137 qreal QDeclarativeDrag::ymax() const
138 {
139 return _ymax;
140 }
141
setYmax(qreal m)142 void QDeclarativeDrag::setYmax(qreal m)
143 {
144 if (_ymax == m)
145 return;
146 _ymax = m;
147 emit maximumYChanged();
148 }
149
active() const150 bool QDeclarativeDrag::active() const
151 {
152 return _active;
153 }
154
setActive(bool drag)155 void QDeclarativeDrag::setActive(bool drag)
156 {
157 if (_active == drag)
158 return;
159 _active = drag;
160 emit activeChanged();
161 }
162
filterChildren() const163 bool QDeclarativeDrag::filterChildren() const
164 {
165 return _filterChildren;
166 }
167
setFilterChildren(bool filter)168 void QDeclarativeDrag::setFilterChildren(bool filter)
169 {
170 if (_filterChildren == filter)
171 return;
172 _filterChildren = filter;
173 emit filterChildrenChanged();
174 }
175
~QDeclarativeMouseAreaPrivate()176 QDeclarativeMouseAreaPrivate::~QDeclarativeMouseAreaPrivate()
177 {
178 delete drag;
179 }
180
181 /*!
182 \qmlclass MouseArea QDeclarativeMouseArea
183 \ingroup qml-basic-interaction-elements
184 \since 4.7
185 \brief The MouseArea item enables simple mouse handling.
186 \inherits Item
187
188 A MouseArea is an invisible item that is typically used in conjunction with
189 a visible item in order to provide mouse handling for that item.
190 By effectively acting as a proxy, the logic for mouse handling can be
191 contained within a MouseArea item.
192
193 For basic key handling, see the \l{Keys}{Keys attached property}.
194
195 The \l enabled property is used to enable and disable mouse handling for
196 the proxied item. When disabled, the mouse area becomes transparent to
197 mouse events.
198
199 The \l pressed read-only property indicates whether or not the user is
200 holding down a mouse button over the mouse area. This property is often
201 used in bindings between properties in a user interface. The containsMouse
202 read-only property indicates the presence of the mouse cursor over the
203 mouse area but, by default, only when a mouse button is held down; see below
204 for further details.
205
206 Information about the mouse position and button clicks are provided via
207 signals for which event handler properties are defined. The most commonly
208 used involved handling mouse presses and clicks: onClicked, onDoubleClicked,
209 onPressed, onReleased and onPressAndHold.
210
211 By default, MouseArea items only report mouse clicks and not changes to the
212 position of the mouse cursor. Setting the hoverEnabled property ensures that
213 handlers defined for onPositionChanged, onEntered and onExited are used and
214 that the containsMouse property is updated even when no mouse buttons are
215 pressed.
216
217 \section1 Example Usage
218
219 \div {class="float-right"}
220 \inlineimage qml-mousearea-snippet.png
221 \enddiv
222
223 The following example uses a MouseArea in a \l Rectangle that changes
224 the \l Rectangle color to red when clicked:
225
226 \snippet doc/src/snippets/declarative/mousearea/mousearea.qml import
227 \codeline
228 \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro
229
230 \clearfloat
231 Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains
232 additional information about the mouse event, such as the position, button,
233 and any key modifiers.
234
235 Here is an extension of the previous example that produces a different
236 color when the area is right clicked:
237
238 \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro-extended
239
240 \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example}
241 */
242
243 /*!
244 \qmlsignal MouseArea::onEntered()
245
246 This handler is called when the mouse enters the mouse area.
247
248 By default the onEntered handler is only called while a button is
249 pressed. Setting hoverEnabled to true enables handling of
250 onEntered when no mouse button is pressed.
251
252 \sa hoverEnabled
253 */
254
255 /*!
256 \qmlsignal MouseArea::onExited()
257
258 This handler is called when the mouse exists the mouse area.
259
260 By default the onExited handler is only called while a button is
261 pressed. Setting hoverEnabled to true enables handling of
262 onExited when no mouse button is pressed.
263
264 \sa hoverEnabled
265 */
266
267 /*!
268 \qmlsignal MouseArea::onPositionChanged(MouseEvent mouse)
269
270 This handler is called when the mouse position changes.
271
272 The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y
273 position, and any buttons currently pressed.
274
275 The \e accepted property of the MouseEvent parameter is ignored in this handler.
276
277 By default the onPositionChanged handler is only called while a button is
278 pressed. Setting hoverEnabled to true enables handling of
279 onPositionChanged when no mouse button is pressed.
280 */
281
282 /*!
283 \qmlsignal MouseArea::onClicked(MouseEvent mouse)
284
285 This handler is called when there is a click. A click is defined as a press followed by a release,
286 both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and
287 releasing is also considered a click).
288
289 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
290 position of the release of the click, and whether the click was held.
291
292 The \e accepted property of the MouseEvent parameter is ignored in this handler.
293 */
294
295 /*!
296 \qmlsignal MouseArea::onPressed(MouseEvent mouse)
297
298 This handler is called when there is a press.
299 The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
300 position and which button was pressed.
301
302 The \e accepted property of the MouseEvent parameter determines whether this MouseArea
303 will handle the press and all future mouse events until release. The default is to accept
304 the event and not allow other MouseArea beneath this one to handle the event. If \e accepted
305 is set to false, no further events will be sent to this MouseArea until the button is next
306 pressed.
307 */
308
309 /*!
310 \qmlsignal MouseArea::onReleased(MouseEvent mouse)
311
312 This handler is called when there is a release.
313 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
314 position of the release of the click, and whether the click was held.
315
316 The \e accepted property of the MouseEvent parameter is ignored in this handler.
317
318 \sa onCanceled
319 */
320
321 /*!
322 \qmlsignal MouseArea::onPressAndHold(MouseEvent mouse)
323
324 This handler is called when there is a long press (currently 800ms).
325 The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
326 position of the press, and which button is pressed.
327
328 The \e accepted property of the MouseEvent parameter is ignored in this handler.
329 */
330
331 /*!
332 \qmlsignal MouseArea::onDoubleClicked(MouseEvent mouse)
333
334 This handler is called when there is a double-click (a press followed by a release followed by a press).
335 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
336 position of the release of the click, and whether the click was held.
337
338 If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false
339 in the handler, the onPressed/onReleased/onClicked handlers will be called for the second
340 click; otherwise they are suppressed. The accepted property defaults to true.
341 */
342
343 /*!
344 \qmlsignal MouseArea::onCanceled()
345
346 This handler is called when mouse events have been canceled, either because an event was not accepted, or
347 because another element stole the mouse event handling.
348
349 This signal is for advanced use: it is useful when there is more than one MouseArea
350 that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter
351 case, if you execute some logic on the pressed signal and then start dragging, the
352 \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset
353 the logic when the MouseArea has lost the mouse handling to the \l Flickable,
354 \c onCanceled should be used in addition to onReleased.
355 */
356
QDeclarativeMouseArea(QDeclarativeItem * parent)357 QDeclarativeMouseArea::QDeclarativeMouseArea(QDeclarativeItem *parent)
358 : QDeclarativeItem(*(new QDeclarativeMouseAreaPrivate), parent)
359 {
360 Q_D(QDeclarativeMouseArea);
361 d->init();
362 }
363
~QDeclarativeMouseArea()364 QDeclarativeMouseArea::~QDeclarativeMouseArea()
365 {
366 }
367
368 /*!
369 \qmlproperty real MouseArea::mouseX
370 \qmlproperty real MouseArea::mouseY
371 These properties hold the coordinates of the mouse cursor.
372
373 If the hoverEnabled property is false then these properties will only be valid
374 while a button is pressed, and will remain valid as long as the button is held
375 down even if the mouse is moved outside the area.
376
377 By default, this property is false.
378
379 If hoverEnabled is true then these properties will be valid when:
380 \list
381 \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true).
382 \i a button is pressed and held, even if it has since moved out of the area.
383 \endlist
384
385 The coordinates are relative to the MouseArea.
386 */
mouseX() const387 qreal QDeclarativeMouseArea::mouseX() const
388 {
389 Q_D(const QDeclarativeMouseArea);
390 return d->lastPos.x();
391 }
392
mouseY() const393 qreal QDeclarativeMouseArea::mouseY() const
394 {
395 Q_D(const QDeclarativeMouseArea);
396 return d->lastPos.y();
397 }
398
399 /*!
400 \qmlproperty bool MouseArea::enabled
401 This property holds whether the item accepts mouse events.
402
403 By default, this property is true.
404 */
isEnabled() const405 bool QDeclarativeMouseArea::isEnabled() const
406 {
407 Q_D(const QDeclarativeMouseArea);
408 return d->absorb;
409 }
410
setEnabled(bool a)411 void QDeclarativeMouseArea::setEnabled(bool a)
412 {
413 Q_D(QDeclarativeMouseArea);
414 if (a != d->absorb) {
415 d->absorb = a;
416 emit enabledChanged();
417 }
418 }
419
420 /*!
421 \qmlproperty bool MouseArea::preventStealing
422 \since QtQuick 1.1
423 This property holds whether the mouse events may be stolen from this
424 MouseArea.
425
426 If a MouseArea is placed within an item that filters child mouse
427 events, such as Flickable, the mouse
428 events may be stolen from the MouseArea if a gesture is recognized
429 by the parent element, e.g. a flick gesture. If preventStealing is
430 set to true, no element will steal the mouse events.
431
432 Note that setting preventStealing to true once an element has started
433 stealing events will have no effect until the next press event.
434
435 By default this property is false.
436 */
preventStealing() const437 bool QDeclarativeMouseArea::preventStealing() const
438 {
439 Q_D(const QDeclarativeMouseArea);
440 return d->preventStealing;
441 }
442
setPreventStealing(bool prevent)443 void QDeclarativeMouseArea::setPreventStealing(bool prevent)
444 {
445 Q_D(QDeclarativeMouseArea);
446 if (prevent != d->preventStealing) {
447 d->preventStealing = prevent;
448 setKeepMouseGrab(d->preventStealing && d->absorb);
449 emit preventStealingChanged();
450 }
451 }
452
453 /*!
454 \qmlproperty MouseButtons MouseArea::pressedButtons
455 This property holds the mouse buttons currently pressed.
456
457 It contains a bitwise combination of:
458 \list
459 \o Qt.LeftButton
460 \o Qt.RightButton
461 \o Qt.MiddleButton
462 \endlist
463
464 The code below displays "right" when the right mouse buttons is pressed:
465
466 \snippet doc/src/snippets/declarative/mousearea/mousearea.qml mousebuttons
467
468 \sa acceptedButtons
469 */
pressedButtons() const470 Qt::MouseButtons QDeclarativeMouseArea::pressedButtons() const
471 {
472 Q_D(const QDeclarativeMouseArea);
473 return d->lastButtons;
474 }
475
mousePressEvent(QGraphicsSceneMouseEvent * event)476 void QDeclarativeMouseArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
477 {
478 Q_D(QDeclarativeMouseArea);
479 d->moved = false;
480 d->stealMouse = d->preventStealing;
481 if (!d->absorb)
482 QDeclarativeItem::mousePressEvent(event);
483 else {
484 d->longPress = false;
485 d->saveEvent(event);
486 if (d->drag)
487 d->drag->setActive(false);
488 setHovered(true);
489 d->startScene = event->scenePos();
490 // we should only start timer if pressAndHold is connected to.
491 if (d->isPressAndHoldConnected())
492 d->pressAndHoldTimer.start(PressAndHoldDelay, this);
493 setKeepMouseGrab(d->stealMouse);
494 event->setAccepted(setPressed(true));
495 }
496 }
497
mouseMoveEvent(QGraphicsSceneMouseEvent * event)498 void QDeclarativeMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
499 {
500 Q_D(QDeclarativeMouseArea);
501 if (!d->absorb) {
502 QDeclarativeItem::mouseMoveEvent(event);
503 return;
504 }
505
506 d->saveEvent(event);
507
508 // ### we should skip this if these signals aren't used
509 // ### can GV handle this for us?
510 bool contains = boundingRect().contains(d->lastPos);
511 if (d->hovered && !contains)
512 setHovered(false);
513 else if (!d->hovered && contains)
514 setHovered(true);
515
516 if (d->drag && d->drag->target()) {
517 if (!d->moved) {
518 d->startX = drag()->target()->x();
519 d->startY = drag()->target()->y();
520 }
521
522 QPointF startLocalPos;
523 QPointF curLocalPos;
524 if (drag()->target()->parentItem()) {
525 startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene);
526 curLocalPos = drag()->target()->parentItem()->mapFromScene(event->scenePos());
527 } else {
528 startLocalPos = d->startScene;
529 curLocalPos = event->scenePos();
530 }
531
532 if (keepMouseGrab() && d->stealMouse)
533 d->drag->setActive(true);
534
535 bool dragX = drag()->axis() & QDeclarativeDrag::XAxis;
536 bool dragY = drag()->axis() & QDeclarativeDrag::YAxis;
537
538 const qreal x = dragX
539 ? qBound(d->drag->xmin(), d->startX + curLocalPos.x() - startLocalPos.x(), d->drag->xmax())
540 : d->startX;
541 const qreal y = dragY
542 ? qBound(d->drag->ymin(), d->startY + curLocalPos.y() - startLocalPos.y(), d->drag->ymax())
543 : d->startY;
544
545 if (d->drag->active()) {
546 if (dragX && dragY)
547 d->drag->target()->setPos(x, y);
548 else if (dragX)
549 d->drag->target()->setX(x);
550 else if (dragY)
551 d->drag->target()->setY(y);
552 }
553
554 if (!keepMouseGrab()) {
555 const int dragThreshold = QApplication::startDragDistance();
556
557 if (qAbs(x - d->startX) > dragThreshold || qAbs(y - d->startY) > dragThreshold) {
558 setKeepMouseGrab(true);
559 d->stealMouse = true;
560 }
561 }
562
563 d->moved = true;
564 }
565 QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
566 emit mousePositionChanged(&me);
567 me.setX(d->lastPos.x());
568 me.setY(d->lastPos.y());
569 emit positionChanged(&me);
570 }
571
572
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)573 void QDeclarativeMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
574 {
575 Q_D(QDeclarativeMouseArea);
576 d->stealMouse = false;
577 if (!d->absorb) {
578 QDeclarativeItem::mouseReleaseEvent(event);
579 } else {
580 d->saveEvent(event);
581 setPressed(false);
582 if (d->drag)
583 d->drag->setActive(false);
584 // If we don't accept hover, we need to reset containsMouse.
585 if (!acceptHoverEvents())
586 setHovered(false);
587 QGraphicsScene *s = scene();
588 if (s && s->mouseGrabberItem() == this)
589 ungrabMouse();
590 setKeepMouseGrab(false);
591 }
592 d->doubleClick = false;
593 }
594
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)595 void QDeclarativeMouseArea::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
596 {
597 Q_D(QDeclarativeMouseArea);
598 if (!d->absorb) {
599 QDeclarativeItem::mouseDoubleClickEvent(event);
600 } else {
601 if (d->isDoubleClickConnected())
602 d->doubleClick = true;
603 d->saveEvent(event);
604 QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
605 me.setAccepted(d->isDoubleClickConnected());
606 emit this->doubleClicked(&me);
607 QDeclarativeItem::mouseDoubleClickEvent(event);
608 }
609 }
610
hoverEnterEvent(QGraphicsSceneHoverEvent * event)611 void QDeclarativeMouseArea::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
612 {
613 Q_D(QDeclarativeMouseArea);
614 if (!d->absorb)
615 QDeclarativeItem::hoverEnterEvent(event);
616 else {
617 d->lastPos = event->pos();
618 setHovered(true);
619 QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false);
620 emit mousePositionChanged(&me);
621 }
622 }
623
hoverMoveEvent(QGraphicsSceneHoverEvent * event)624 void QDeclarativeMouseArea::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
625 {
626 Q_D(QDeclarativeMouseArea);
627 if (!d->absorb) {
628 QDeclarativeItem::hoverMoveEvent(event);
629 } else {
630 d->lastPos = event->pos();
631 QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false);
632 emit mousePositionChanged(&me);
633 me.setX(d->lastPos.x());
634 me.setY(d->lastPos.y());
635 emit positionChanged(&me);
636 }
637 }
638
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)639 void QDeclarativeMouseArea::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
640 {
641 Q_D(QDeclarativeMouseArea);
642 if (!d->absorb)
643 QDeclarativeItem::hoverLeaveEvent(event);
644 else
645 setHovered(false);
646 }
647
648 #ifndef QT_NO_CONTEXTMENU
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)649 void QDeclarativeMouseArea::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
650 {
651 bool acceptsContextMenuButton;
652 #if defined(Q_OS_SYMBIAN)
653 // In Symbian a Long Tap on the screen will trigger. See QSymbianControl::HandleLongTapEventL().
654 acceptsContextMenuButton = acceptedButtons() & Qt::LeftButton;
655 #elif defined(Q_WS_WINCE)
656 // ### WinCE can trigger context menu event with a gesture in the left button or a
657 // click with the right button. Since we have no way here to differentiate them when
658 // event happens, accepting either of the them will block the event.
659 acceptsContextMenuButton = acceptedButtons() & (Qt::LeftButton | Qt::RightButton);
660 #else
661 acceptsContextMenuButton = acceptedButtons() & Qt::RightButton;
662 #endif
663
664 if (isEnabled() && event->reason() == QGraphicsSceneContextMenuEvent::Mouse
665 && acceptsContextMenuButton) {
666 // Do not let the context menu event propagate to items behind.
667 return;
668 }
669
670 QDeclarativeItem::contextMenuEvent(event);
671 }
672 #endif // QT_NO_CONTEXTMENU
673
sceneEvent(QEvent * event)674 bool QDeclarativeMouseArea::sceneEvent(QEvent *event)
675 {
676 bool rv = QDeclarativeItem::sceneEvent(event);
677 if (event->type() == QEvent::UngrabMouse) {
678 Q_D(QDeclarativeMouseArea);
679 if (d->pressed) {
680 // if our mouse grab has been removed (probably by Flickable), fix our
681 // state
682 d->pressed = false;
683 d->stealMouse = false;
684 setKeepMouseGrab(false);
685 emit canceled();
686 emit pressedChanged();
687 if (d->hovered) {
688 d->hovered = false;
689 emit hoveredChanged();
690 }
691 }
692 }
693 return rv;
694 }
695
sendMouseEvent(QGraphicsSceneMouseEvent * event)696 bool QDeclarativeMouseArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
697 {
698 Q_D(QDeclarativeMouseArea);
699 QGraphicsSceneMouseEvent mouseEvent(event->type());
700 QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect();
701
702 QGraphicsScene *s = scene();
703 QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0;
704 bool stealThisEvent = d->stealMouse;
705 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
706 mouseEvent.setAccepted(false);
707 for (int i = 0x1; i <= 0x10; i <<= 1) {
708 if (event->buttons() & i) {
709 Qt::MouseButton button = Qt::MouseButton(i);
710 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
711 }
712 }
713 mouseEvent.setScenePos(event->scenePos());
714 mouseEvent.setLastScenePos(event->lastScenePos());
715 mouseEvent.setPos(mapFromScene(event->scenePos()));
716 mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
717
718 switch(mouseEvent.type()) {
719 case QEvent::GraphicsSceneMouseMove:
720 mouseMoveEvent(&mouseEvent);
721 break;
722 case QEvent::GraphicsSceneMousePress:
723 mousePressEvent(&mouseEvent);
724 break;
725 case QEvent::GraphicsSceneMouseRelease:
726 mouseReleaseEvent(&mouseEvent);
727 break;
728 default:
729 break;
730 }
731 grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem());
732 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
733 grabMouse();
734
735 return stealThisEvent;
736 }
737 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
738 if (d->pressed) {
739 d->pressed = false;
740 d->stealMouse = false;
741 if (s && s->mouseGrabberItem() == this)
742 ungrabMouse();
743 emit canceled();
744 emit pressedChanged();
745 if (d->hovered) {
746 d->hovered = false;
747 emit hoveredChanged();
748 }
749 }
750 }
751 return false;
752 }
753
sceneEventFilter(QGraphicsItem * i,QEvent * e)754 bool QDeclarativeMouseArea::sceneEventFilter(QGraphicsItem *i, QEvent *e)
755 {
756 Q_D(QDeclarativeMouseArea);
757 if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren())
758 return QDeclarativeItem::sceneEventFilter(i, e);
759 switch (e->type()) {
760 case QEvent::GraphicsSceneMousePress:
761 case QEvent::GraphicsSceneMouseMove:
762 case QEvent::GraphicsSceneMouseRelease:
763 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
764 default:
765 break;
766 }
767
768 return QDeclarativeItem::sceneEventFilter(i, e);
769 }
770
timerEvent(QTimerEvent * event)771 void QDeclarativeMouseArea::timerEvent(QTimerEvent *event)
772 {
773 Q_D(QDeclarativeMouseArea);
774 if (event->timerId() == d->pressAndHoldTimer.timerId()) {
775 d->pressAndHoldTimer.stop();
776 bool dragged = d->drag && d->drag->active();
777 if (d->pressed && dragged == false && d->hovered == true) {
778 d->longPress = true;
779 QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
780 emit pressAndHold(&me);
781 }
782 }
783 }
784
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)785 void QDeclarativeMouseArea::geometryChanged(const QRectF &newGeometry,
786 const QRectF &oldGeometry)
787 {
788 Q_D(QDeclarativeMouseArea);
789 QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
790
791 if (d->lastScenePos.isNull)
792 d->lastScenePos = mapToScene(d->lastPos);
793 else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y())
794 d->lastPos = mapFromScene(d->lastScenePos);
795 }
796
itemChange(GraphicsItemChange change,const QVariant & value)797 QVariant QDeclarativeMouseArea::itemChange(GraphicsItemChange change,
798 const QVariant &value)
799 {
800 Q_D(QDeclarativeMouseArea);
801 switch (change) {
802 case ItemVisibleHasChanged:
803 if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse()))
804 setHovered(!d->hovered);
805 break;
806 default:
807 break;
808 }
809
810 return QDeclarativeItem::itemChange(change, value);
811 }
812
813 /*!
814 \qmlproperty bool MouseArea::hoverEnabled
815 This property holds whether hover events are handled.
816
817 By default, mouse events are only handled in response to a button event, or when a button is
818 pressed. Hover enables handling of all mouse events even when no mouse button is
819 pressed.
820
821 This property affects the containsMouse property and the onEntered, onExited and
822 onPositionChanged signals.
823 */
hoverEnabled() const824 bool QDeclarativeMouseArea::hoverEnabled() const
825 {
826 return acceptHoverEvents();
827 }
828
setHoverEnabled(bool h)829 void QDeclarativeMouseArea::setHoverEnabled(bool h)
830 {
831 Q_D(QDeclarativeMouseArea);
832 if (h == acceptHoverEvents())
833 return;
834
835 setAcceptHoverEvents(h);
836 emit hoverEnabledChanged();
837 if (d->hovered != isUnderMouse())
838 setHovered(!d->hovered);
839 }
840
841 /*!
842 \qmlproperty bool MouseArea::containsMouse
843 This property holds whether the mouse is currently inside the mouse area.
844
845 \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change.
846 In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed.
847 */
hovered() const848 bool QDeclarativeMouseArea::hovered() const
849 {
850 Q_D(const QDeclarativeMouseArea);
851 return d->hovered;
852 }
853
854 /*!
855 \qmlproperty bool MouseArea::pressed
856 This property holds whether the mouse area is currently pressed.
857 */
pressed() const858 bool QDeclarativeMouseArea::pressed() const
859 {
860 Q_D(const QDeclarativeMouseArea);
861 return d->pressed;
862 }
863
setHovered(bool h)864 void QDeclarativeMouseArea::setHovered(bool h)
865 {
866 Q_D(QDeclarativeMouseArea);
867 if (d->hovered != h) {
868 d->hovered = h;
869 emit hoveredChanged();
870 d->hovered ? emit entered() : emit exited();
871 }
872 }
873
874 /*!
875 \qmlproperty Qt::MouseButtons MouseArea::acceptedButtons
876 This property holds the mouse buttons that the mouse area reacts to.
877
878 The available buttons are:
879 \list
880 \o Qt.LeftButton
881 \o Qt.RightButton
882 \o Qt.MiddleButton
883 \o Qt.XButton1
884 \o Qt.XButton2
885 \endlist
886
887 To accept more than one button the flags can be combined with the
888 "|" (or) operator:
889
890 \code
891 MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton }
892 \endcode
893
894 The default value is \c Qt.LeftButton.
895 */
acceptedButtons() const896 Qt::MouseButtons QDeclarativeMouseArea::acceptedButtons() const
897 {
898 return acceptedMouseButtons();
899 }
900
setAcceptedButtons(Qt::MouseButtons buttons)901 void QDeclarativeMouseArea::setAcceptedButtons(Qt::MouseButtons buttons)
902 {
903 if (buttons != acceptedMouseButtons()) {
904 setAcceptedMouseButtons(buttons);
905 emit acceptedButtonsChanged();
906 }
907 }
908
setPressed(bool p)909 bool QDeclarativeMouseArea::setPressed(bool p)
910 {
911 Q_D(QDeclarativeMouseArea);
912 bool dragged = d->drag && d->drag->active();
913 bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true;
914
915 if (d->pressed != p) {
916 d->pressed = p;
917 QDeclarativeMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
918 if (d->pressed) {
919 if (!d->doubleClick)
920 emit pressed(&me);
921 me.setX(d->lastPos.x());
922 me.setY(d->lastPos.y());
923 emit mousePositionChanged(&me);
924 emit pressedChanged();
925 } else {
926 emit released(&me);
927 me.setX(d->lastPos.x());
928 me.setY(d->lastPos.y());
929 emit pressedChanged();
930 if (isclick && !d->longPress && !d->doubleClick)
931 emit clicked(&me);
932 }
933
934 return me.isAccepted();
935 }
936 return false;
937 }
938
drag()939 QDeclarativeDrag *QDeclarativeMouseArea::drag()
940 {
941 Q_D(QDeclarativeMouseArea);
942 if (!d->drag)
943 d->drag = new QDeclarativeDrag;
944 return d->drag;
945 }
946
947 /*!
948 \qmlproperty Item MouseArea::drag.target
949 \qmlproperty bool MouseArea::drag.active
950 \qmlproperty enumeration MouseArea::drag.axis
951 \qmlproperty real MouseArea::drag.minimumX
952 \qmlproperty real MouseArea::drag.maximumX
953 \qmlproperty real MouseArea::drag.minimumY
954 \qmlproperty real MouseArea::drag.maximumY
955 \qmlproperty bool MouseArea::drag.filterChildren
956
957 \c drag provides a convenient way to make an item draggable.
958
959 \list
960 \i \c drag.target specifies the id of the item to drag.
961 \i \c drag.active specifies if the target item is currently being dragged.
962 \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis)
963 \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes.
964 \endlist
965
966 The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity
967 of the rectangle is reduced when it is dragged to the right.
968
969 \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag
970
971 \note Items cannot be dragged if they are anchored for the requested
972 \c drag.axis. For example, if \c anchors.left or \c anchors.right was set
973 for \c rect in the above example, it cannot be dragged along the X-axis.
974 This can be avoided by settng the anchor value to \c undefined in
975 an \l onPressed handler.
976
977 If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This
978 enables a parent MouseArea to handle drags, for example, while descendants handle clicks:
979
980 \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter
981
982 */
983
984 QT_END_NAMESPACE
985