44 |
44 |
45 #include "private/qdeclarativeevents_p_p.h" |
45 #include "private/qdeclarativeevents_p_p.h" |
46 |
46 |
47 #include <QGraphicsSceneMouseEvent> |
47 #include <QGraphicsSceneMouseEvent> |
48 |
48 |
|
49 #include <float.h> |
|
50 |
49 QT_BEGIN_NAMESPACE |
51 QT_BEGIN_NAMESPACE |
50 static const int PressAndHoldDelay = 800; |
52 static const int PressAndHoldDelay = 800; |
51 |
53 |
52 QDeclarativeDrag::QDeclarativeDrag(QObject *parent) |
54 QDeclarativeDrag::QDeclarativeDrag(QObject *parent) |
53 : QObject(parent), _target(0), _axis(XandYAxis), _xmin(0), _xmax(0), _ymin(0), _ymax(0), |
55 : QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), |
54 _active(false) |
56 _active(false), _filterChildren(false) |
55 { |
57 { |
56 } |
58 } |
57 |
59 |
58 QDeclarativeDrag::~QDeclarativeDrag() |
60 QDeclarativeDrag::~QDeclarativeDrag() |
59 { |
61 { |
156 return; |
158 return; |
157 _active = drag; |
159 _active = drag; |
158 emit activeChanged(); |
160 emit activeChanged(); |
159 } |
161 } |
160 |
162 |
|
163 bool QDeclarativeDrag::filterChildren() const |
|
164 { |
|
165 return _filterChildren; |
|
166 } |
|
167 |
|
168 void QDeclarativeDrag::setFilterChildren(bool filter) |
|
169 { |
|
170 if (_filterChildren == filter) |
|
171 return; |
|
172 _filterChildren = filter; |
|
173 emit filterChildrenChanged(); |
|
174 } |
|
175 |
161 QDeclarativeMouseAreaPrivate::~QDeclarativeMouseAreaPrivate() |
176 QDeclarativeMouseAreaPrivate::~QDeclarativeMouseAreaPrivate() |
162 { |
177 { |
163 delete drag; |
178 delete drag; |
164 } |
179 } |
165 |
|
166 |
180 |
167 /*! |
181 /*! |
168 \qmlclass MouseArea QDeclarativeMouseArea |
182 \qmlclass MouseArea QDeclarativeMouseArea |
169 \since 4.7 |
183 \since 4.7 |
170 \brief The MouseArea item enables simple mouse handling. |
184 \brief The MouseArea item enables simple mouse handling. |
171 \inherits Item |
185 \inherits Item |
172 |
186 |
173 A MouseArea is typically used in conjunction with a visible item, |
187 A MouseArea is typically used in conjunction with a visible item, |
174 where the MouseArea effectively 'proxies' mouse handling for that |
188 where the MouseArea effectively 'proxies' mouse handling for that |
175 item. For example, we can put a MouseArea in a Rectangle that changes |
189 item. For example, we can put a MouseArea in a \l Rectangle that changes |
176 the Rectangle color to red when clicked: |
190 the \l Rectangle color to red when clicked: |
177 \snippet doc/src/snippets/declarative/mouseregion.qml 0 |
191 |
|
192 \snippet doc/src/snippets/declarative/mousearea.qml import |
|
193 \codeline |
|
194 \snippet doc/src/snippets/declarative/mousearea.qml intro |
178 |
195 |
179 Many MouseArea signals pass a \l {MouseEvent}{mouse} parameter that contains |
196 Many MouseArea signals pass a \l {MouseEvent}{mouse} parameter that contains |
180 additional information about the mouse event, such as the position, button, |
197 additional information about the mouse event, such as the position, button, |
181 and any key modifiers. |
198 and any key modifiers. |
182 |
199 |
183 Below we have the previous |
200 Here is an extension of the previous example that produces a different |
184 example extended so as to give a different color when you right click. |
201 color when the area is right clicked: |
185 \snippet doc/src/snippets/declarative/mouseregion.qml 1 |
202 |
|
203 \snippet doc/src/snippets/declarative/mousearea.qml intro-extended |
186 |
204 |
187 For basic key handling, see the \l {Keys}{Keys attached property}. |
205 For basic key handling, see the \l {Keys}{Keys attached property}. |
188 |
206 |
189 MouseArea is an invisible item: it is never painted. |
207 MouseArea is an invisible item: it is never painted. |
190 |
208 |
191 \sa MouseEvent |
209 \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example} |
192 */ |
210 */ |
193 |
211 |
194 /*! |
212 /*! |
195 \qmlsignal MouseArea::onEntered() |
213 \qmlsignal MouseArea::onEntered() |
196 |
214 |
236 This handler is called when there is a click. A click is defined as a press followed by a release, |
254 This handler is called when there is a click. A click is defined as a press followed by a release, |
237 both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and |
255 both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and |
238 releasing is also considered a click). |
256 releasing is also considered a click). |
239 |
257 |
240 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
258 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
241 position of the release of the click, and whether the click wasHeld. |
259 position of the release of the click, and whether the click was held. |
242 |
260 |
243 The \e accepted property of the MouseEvent parameter is ignored in this handler. |
261 The \e accepted property of the MouseEvent parameter is ignored in this handler. |
244 */ |
262 */ |
245 |
263 |
246 /*! |
264 /*! |
260 /*! |
278 /*! |
261 \qmlsignal MouseArea::onReleased(mouse) |
279 \qmlsignal MouseArea::onReleased(mouse) |
262 |
280 |
263 This handler is called when there is a release. |
281 This handler is called when there is a release. |
264 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
282 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
265 position of the release of the click, and whether the click wasHeld. |
283 position of the release of the click, and whether the click was held. |
266 |
284 |
267 The \e accepted property of the MouseEvent parameter is ignored in this handler. |
285 The \e accepted property of the MouseEvent parameter is ignored in this handler. |
268 */ |
286 */ |
269 |
287 |
270 /*! |
288 /*! |
280 /*! |
298 /*! |
281 \qmlsignal MouseArea::onDoubleClicked(mouse) |
299 \qmlsignal MouseArea::onDoubleClicked(mouse) |
282 |
300 |
283 This handler is called when there is a double-click (a press followed by a release followed by a press). |
301 This handler is called when there is a double-click (a press followed by a release followed by a press). |
284 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
302 The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y |
285 position of the release of the click, and whether the click wasHeld. |
303 position of the release of the click, and whether the click was held. |
286 |
304 |
287 The \e accepted property of the MouseEvent parameter is ignored in this handler. |
305 The \e accepted property of the MouseEvent parameter is ignored in this handler. |
288 */ |
306 */ |
289 |
307 |
290 /*! |
308 /*! |
291 \qmlsignal MouseArea::onCanceled() |
309 \qmlsignal MouseArea::onCanceled() |
292 |
310 |
293 This handler is called when the mouse events are canceled, either because the event was not accepted or |
311 This handler is called when mouse events have been canceled, either because an event was not accepted, or |
294 another element stole the mouse event handling. This signal is for advanced users, it's useful in case there |
312 because another element stole the mouse event handling. This signal is for advanced use: it is useful when |
295 is more than one mouse areas handling input, or when there is a mouse area inside a flickable. In the latter |
313 there is more than one MouseArea that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter |
296 case, if you do some logic on pressed and then start dragging, the flickable will steal the mouse handling |
314 case, if you execute some logic on the pressed signal and then start dragging, the \l Flickable will steal the mouse handling |
297 from the mouse area. In these cases, to reset the logic when there is no mouse handling anymore, you should |
315 from the MouseArea. In these cases, to reset the logic when the MouseArea has lost the mouse handling to the |
298 use onCanceled, in addition to onReleased. |
316 \l Flickable, \c onCanceled should be used in addition to onReleased. |
299 */ |
317 */ |
300 |
318 |
301 /*! |
319 /*! |
302 \internal |
320 \internal |
303 \class QDeclarativeMouseArea |
321 \class QDeclarativeMouseArea |
304 \brief The QDeclarativeMouseArea class provides a simple mouse handling abstraction for use within Qml. |
322 \brief The QDeclarativeMouseArea class provides a simple mouse handling abstraction for use within QML. |
305 |
323 |
306 All QDeclarativeItem derived classes can do mouse handling but the QDeclarativeMouseArea class exposes mouse |
324 All QDeclarativeItem derived classes can do mouse handling but the QDeclarativeMouseArea class exposes mouse |
307 handling data as properties and tracks flicking and dragging of the mouse. |
325 handling data as properties and tracks flicking and dragging of the mouse. |
308 |
326 |
309 A QDeclarativeMouseArea object can be instantiated in Qml using the tag \l MouseArea. |
327 A QDeclarativeMouseArea object can be instantiated in QML using the tag \l MouseArea. |
310 */ |
328 */ |
311 QDeclarativeMouseArea::QDeclarativeMouseArea(QDeclarativeItem *parent) |
329 QDeclarativeMouseArea::QDeclarativeMouseArea(QDeclarativeItem *parent) |
312 : QDeclarativeItem(*(new QDeclarativeMouseAreaPrivate), parent) |
330 : QDeclarativeItem(*(new QDeclarativeMouseAreaPrivate), parent) |
313 { |
331 { |
314 Q_D(QDeclarativeMouseArea); |
332 Q_D(QDeclarativeMouseArea); |
326 |
344 |
327 If the hoverEnabled property is false then these properties will only be valid |
345 If the hoverEnabled property is false then these properties will only be valid |
328 while a button is pressed, and will remain valid as long as the button is held |
346 while a button is pressed, and will remain valid as long as the button is held |
329 even if the mouse is moved outside the area. |
347 even if the mouse is moved outside the area. |
330 |
348 |
331 If hoverEnabled is true then these properties will be valid: |
349 If hoverEnabled is true then these properties will be valid when: |
332 \list |
350 \list |
333 \i when no button is pressed, but the mouse is within the MouseArea (containsMouse is true). |
351 \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true). |
334 \i if a button is pressed and held, even if it has since moved out of the area. |
352 \i a button is pressed and held, even if it has since moved out of the area. |
335 \endlist |
353 \endlist |
336 |
354 |
337 The coordinates are relative to the MouseArea. |
355 The coordinates are relative to the MouseArea. |
338 */ |
356 */ |
339 qreal QDeclarativeMouseArea::mouseX() const |
357 qreal QDeclarativeMouseArea::mouseX() const |
415 if (d->drag) |
423 if (d->drag) |
416 d->drag->setActive(false); |
424 d->drag->setActive(false); |
417 setHovered(true); |
425 setHovered(true); |
418 d->startScene = event->scenePos(); |
426 d->startScene = event->scenePos(); |
419 // we should only start timer if pressAndHold is connected to. |
427 // we should only start timer if pressAndHold is connected to. |
420 if (d->isConnected("pressAndHold(QDeclarativeMouseEvent*)")) |
428 if (d->isPressAndHoldConnected()) |
421 d->pressAndHoldTimer.start(PressAndHoldDelay, this); |
429 d->pressAndHoldTimer.start(PressAndHoldDelay, this); |
422 setKeepMouseGrab(false); |
430 setKeepMouseGrab(false); |
423 event->setAccepted(setPressed(true)); |
431 event->setAccepted(setPressed(true)); |
424 } |
432 } |
425 } |
433 } |
448 d->startY = drag()->target()->y(); |
456 d->startY = drag()->target()->y(); |
449 } |
457 } |
450 |
458 |
451 QPointF startLocalPos; |
459 QPointF startLocalPos; |
452 QPointF curLocalPos; |
460 QPointF curLocalPos; |
453 if (drag()->target()->parent()) { |
461 if (drag()->target()->parentItem()) { |
454 startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene); |
462 startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene); |
455 curLocalPos = drag()->target()->parentItem()->mapFromScene(event->scenePos()); |
463 curLocalPos = drag()->target()->parentItem()->mapFromScene(event->scenePos()); |
456 } else { |
464 } else { |
457 startLocalPos = d->startScene; |
465 startLocalPos = d->startScene; |
458 curLocalPos = event->scenePos(); |
466 curLocalPos = event->scenePos(); |
459 } |
467 } |
460 |
468 |
461 const int dragThreshold = QApplication::startDragDistance(); |
469 const int dragThreshold = QApplication::startDragDistance(); |
462 qreal dx = qAbs(curLocalPos.x() - startLocalPos.x()); |
470 qreal dx = qAbs(curLocalPos.x() - startLocalPos.x()); |
463 qreal dy = qAbs(curLocalPos.y() - startLocalPos.y()); |
471 qreal dy = qAbs(curLocalPos.y() - startLocalPos.y()); |
464 if ((d->dragX && !(dx < dragThreshold)) || (d->dragY && !(dy < dragThreshold))) |
472 if ((d->dragX && !(dx < dragThreshold)) || (d->dragY && !(dy < dragThreshold))) { |
465 d->drag->setActive(true); |
473 d->drag->setActive(true); |
|
474 d->stealMouse = true; |
|
475 } |
466 if (!keepMouseGrab()) { |
476 if (!keepMouseGrab()) { |
467 if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold) |
477 if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold) |
468 || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold) |
478 || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold) |
469 || (d->dragX && d->dragY)) { |
479 || (d->dragX && d->dragY)) { |
470 setKeepMouseGrab(true); |
480 setKeepMouseGrab(true); |
498 |
508 |
499 |
509 |
500 void QDeclarativeMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) |
510 void QDeclarativeMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) |
501 { |
511 { |
502 Q_D(QDeclarativeMouseArea); |
512 Q_D(QDeclarativeMouseArea); |
|
513 d->stealMouse = false; |
503 if (!d->absorb) { |
514 if (!d->absorb) { |
504 QDeclarativeItem::mouseReleaseEvent(event); |
515 QDeclarativeItem::mouseReleaseEvent(event); |
505 } else { |
516 } else { |
506 d->saveEvent(event); |
517 d->saveEvent(event); |
507 setPressed(false); |
518 setPressed(false); |
508 if (d->drag) |
519 if (d->drag) |
509 d->drag->setActive(false); |
520 d->drag->setActive(false); |
510 // If we don't accept hover, we need to reset containsMouse. |
521 // If we don't accept hover, we need to reset containsMouse. |
511 if (!acceptHoverEvents()) |
522 if (!acceptHoverEvents()) |
512 setHovered(false); |
523 setHovered(false); |
|
524 QGraphicsScene *s = scene(); |
|
525 if (s && s->mouseGrabberItem() == this) |
|
526 ungrabMouse(); |
513 setKeepMouseGrab(false); |
527 setKeepMouseGrab(false); |
514 } |
528 } |
515 } |
529 } |
516 |
530 |
517 void QDeclarativeMouseArea::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) |
531 void QDeclarativeMouseArea::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) |
582 } |
596 } |
583 } |
597 } |
584 return rv; |
598 return rv; |
585 } |
599 } |
586 |
600 |
|
601 bool QDeclarativeMouseArea::sendMouseEvent(QGraphicsSceneMouseEvent *event) |
|
602 { |
|
603 Q_D(QDeclarativeMouseArea); |
|
604 QGraphicsSceneMouseEvent mouseEvent(event->type()); |
|
605 QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); |
|
606 |
|
607 QGraphicsScene *s = scene(); |
|
608 QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0; |
|
609 bool stealThisEvent = d->stealMouse; |
|
610 if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) { |
|
611 mouseEvent.setAccepted(false); |
|
612 for (int i = 0x1; i <= 0x10; i <<= 1) { |
|
613 if (event->buttons() & i) { |
|
614 Qt::MouseButton button = Qt::MouseButton(i); |
|
615 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button))); |
|
616 } |
|
617 } |
|
618 mouseEvent.setScenePos(event->scenePos()); |
|
619 mouseEvent.setLastScenePos(event->lastScenePos()); |
|
620 mouseEvent.setPos(mapFromScene(event->scenePos())); |
|
621 mouseEvent.setLastPos(mapFromScene(event->lastScenePos())); |
|
622 |
|
623 switch(mouseEvent.type()) { |
|
624 case QEvent::GraphicsSceneMouseMove: |
|
625 mouseMoveEvent(&mouseEvent); |
|
626 break; |
|
627 case QEvent::GraphicsSceneMousePress: |
|
628 mousePressEvent(&mouseEvent); |
|
629 break; |
|
630 case QEvent::GraphicsSceneMouseRelease: |
|
631 mouseReleaseEvent(&mouseEvent); |
|
632 break; |
|
633 default: |
|
634 break; |
|
635 } |
|
636 grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()); |
|
637 if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) |
|
638 grabMouse(); |
|
639 |
|
640 return stealThisEvent; |
|
641 } |
|
642 if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) { |
|
643 d->stealMouse = false; |
|
644 ungrabMouse(); |
|
645 } |
|
646 return false; |
|
647 } |
|
648 |
|
649 bool QDeclarativeMouseArea::sceneEventFilter(QGraphicsItem *i, QEvent *e) |
|
650 { |
|
651 Q_D(QDeclarativeMouseArea); |
|
652 if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren()) |
|
653 return QDeclarativeItem::sceneEventFilter(i, e); |
|
654 switch (e->type()) { |
|
655 case QEvent::GraphicsSceneMousePress: |
|
656 case QEvent::GraphicsSceneMouseMove: |
|
657 case QEvent::GraphicsSceneMouseRelease: |
|
658 return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e)); |
|
659 default: |
|
660 break; |
|
661 } |
|
662 |
|
663 return QDeclarativeItem::sceneEventFilter(i, e); |
|
664 } |
|
665 |
587 void QDeclarativeMouseArea::timerEvent(QTimerEvent *event) |
666 void QDeclarativeMouseArea::timerEvent(QTimerEvent *event) |
588 { |
667 { |
589 Q_D(QDeclarativeMouseArea); |
668 Q_D(QDeclarativeMouseArea); |
590 if (event->timerId() == d->pressAndHoldTimer.timerId()) { |
669 if (event->timerId() == d->pressAndHoldTimer.timerId()) { |
591 d->pressAndHoldTimer.stop(); |
670 d->pressAndHoldTimer.stop(); |
762 \qmlproperty enumeration MouseArea::drag.axis |
841 \qmlproperty enumeration MouseArea::drag.axis |
763 \qmlproperty real MouseArea::drag.minimumX |
842 \qmlproperty real MouseArea::drag.minimumX |
764 \qmlproperty real MouseArea::drag.maximumX |
843 \qmlproperty real MouseArea::drag.maximumX |
765 \qmlproperty real MouseArea::drag.minimumY |
844 \qmlproperty real MouseArea::drag.minimumY |
766 \qmlproperty real MouseArea::drag.maximumY |
845 \qmlproperty real MouseArea::drag.maximumY |
767 |
846 \qmlproperty bool MouseArea::drag.filterChildren |
768 drag provides a convenient way to make an item draggable. |
847 |
|
848 \c drag provides a convenient way to make an item draggable. |
769 |
849 |
770 \list |
850 \list |
771 \i \c target specifies the item to drag. |
851 \i \c drag.target specifies the id of the item to drag. |
772 \i \c active specifies if the target item is being currently dragged. |
852 \i \c drag.active specifies if the target item is currently being dragged. |
773 \i \c axis specifies whether dragging can be done horizontally (Drag.XAxis), vertically (Drag.YAxis), or both (Drag.XandYAxis) |
853 \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis) |
774 \i the minimum and maximum properties limit how far the target can be dragged along the corresponding axes. |
854 \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes. |
775 \endlist |
855 \endlist |
776 |
856 |
777 The following example uses drag to reduce the opacity of an image as it moves to the right: |
857 The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity |
778 \snippet doc/src/snippets/declarative/drag.qml 0 |
858 of the rectangle is reduced when it is dragged to the right. |
|
859 |
|
860 \snippet doc/src/snippets/declarative/mousearea.qml drag |
|
861 |
|
862 \note Items cannot be dragged if they are anchored for the requested |
|
863 \c drag.axis. For example, if \c anchors.left or \c anchors.right was set |
|
864 for \c rect in the above example, it cannot be dragged along the X-axis. |
|
865 This can be avoided by settng the anchor value to \c undefined in |
|
866 an \l onPressed handler. |
|
867 |
|
868 If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This |
|
869 enables a parent MouseArea to handle drags, for example, while descendants handle clicks: |
|
870 |
|
871 \snippet doc/src/snippets/declarative/mouseareadragfilter.qml dragfilter |
|
872 |
779 */ |
873 */ |
780 |
874 |
781 QT_END_NAMESPACE |
875 QT_END_NAMESPACE |