1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickselectionrectangle_p.h"
5#include "qquickselectionrectangle_p_p.h"
6
7#include <QtQml/qqmlinfo.h>
8#include <QtQuick/private/qquickdraghandler_p.h>
9#include <QtQuick/private/qquickhoverhandler_p.h>
10
11#include <QtQuick/private/qquicktableview_p_p.h>
12
13#include "qquickscrollview_p.h"
14
15QT_BEGIN_NAMESPACE
16
17/*!
18 \qmltype SelectionRectangle
19 \inherits Control
20//! \nativetype QQuickSelectionRectangle
21 \inqmlmodule QtQuick.Controls
22 \since 6.2
23 \ingroup utilities
24 \brief Used to select table cells inside a TableView.
25
26 \image qtquickcontrols-selectionrectangle.png
27
28 SelectionRectangle is used for selecting table cells in a TableView. It lets
29 the user start a selection by doing a pointer drag inside the viewport, or by
30 doing a long press on top of a cell.
31
32 For a SelectionRectangle to be able to select cells, TableView must have
33 an ItemSelectionModel assigned. The ItemSelectionModel will store any
34 selections done on the model, and can be used for querying
35 which cells that the user has selected.
36
37 The following example shows how you can make a SelectionRectangle target
38 a TableView:
39
40 \snippet qtquickcontrols-selectionrectangle.qml 0
41
42 \note A SelectionRectangle itself is not shown as part of a selection. Only the
43 delegates (like topLeftHandle and bottomRightHandle) are used.
44 You should also consider \l {Selecting items}{rendering the TableView delegate as selected}.
45
46 \sa TableView, TableView::selectionModel, ItemSelectionModel
47*/
48
49/*!
50 \qmlproperty Item QtQuick.Controls::SelectionRectangle::target
51
52 This property holds the TableView on which the
53 SelectionRectangle should act.
54*/
55
56/*!
57 \qmlproperty bool QtQuick.Controls::SelectionRectangle::active
58 \readonly
59
60 This property is \c true while the user is performing a
61 selection. The selection will be active from the time the
62 the user starts to select, and until the selection is
63 removed again, for example from tapping inside the viewport.
64*/
65
66/*!
67 \qmlproperty bool QtQuick.Controls::SelectionRectangle::dragging
68 \readonly
69
70 This property is \c true whenever the user is doing a pointer drag or
71 a handle drag to adjust the selection rectangle.
72*/
73
74/*!
75 \qmlproperty Component QtQuick.Controls::SelectionRectangle::topLeftHandle
76
77 This property holds the delegate that will be shown on the center of the
78 top-left corner of the selection rectangle. When a handle is
79 provided, the user can drag it to adjust the selection.
80
81 The handle is not hidden by default when a selection is removed.
82 Instead, this is the responsibility of the delegate, to open up for
83 custom fade-out animations. The easiest way to ensure that the handle
84 ends up hidden, is to simply bind \l {Item::}{visible} to the \l active
85 state of the SelectionRectangle:
86
87 \qml
88 SelectionRectangle {
89 topLeftHandle: Rectangle {
90 width: 20
91 height: 20
92 visible: SelectionRectangle.control.active
93 }
94 }
95 \endqml
96
97 Set this property to \c null if you don't want a selection handle on the top-left.
98
99 \sa bottomRightHandle
100*/
101
102/*!
103 \qmlproperty Component QtQuick.Controls::SelectionRectangle::bottomRightHandle
104
105 This property holds the delegate that will be shown on the center of the
106 top-left corner of the selection rectangle. When a handle is
107 provided, the user can drag it to adjust the selection.
108
109 The handle is not hidden by default when a selection is removed.
110 Instead, this is the responsibility of the delegate, to open up for
111 custom fade-out animations. The easiest way to ensure that the handle
112 ends up hidden, is to simply bind \l {Item::}{visible} to the \l active
113 state of the SelectionRectangle:
114
115 \qml
116 SelectionRectangle {
117 bottomRightHandle: Rectangle {
118 width: 20
119 height: 20
120 visible: SelectionRectangle.control.active
121 }
122 }
123 \endqml
124
125 Set this property to \c null if you don't want a selection handle on the bottom-right.
126
127 \sa topLeftHandle
128*/
129
130/*!
131 \qmlproperty enumeration QtQuick.Controls::SelectionRectangle::selectionMode
132
133 This property holds when a selection should start.
134
135 \value SelectionRectangle.Drag A selection will start by doing a pointer drag inside the viewport
136 \value SelectionRectangle.PressAndHold A selection will start by doing a press and hold on top a cell
137 \value SelectionRectangle.Auto SelectionRectangle will choose which mode to use based on the target
138 and the platform. This normally means \c PressAndHold on touch based platforms, and \c Drag on desktop.
139 However, \c Drag will only be used if it doesn't conflict with flicking. This means that
140 TableView will need to be configured with \c interactive set to \c false, or placed
141 inside a ScrollView (where flicking, by default, is off for mouse events), for \c Drag to be chosen.
142
143 The default value is \c Auto.
144*/
145
146/*!
147 \qmlattachedproperty SelectionRectangle QtQuick.Controls::SelectionRectangle::control
148
149 This attached property holds the SelectionRectangle that manages the delegate instance.
150 It is attached to each handle instance.
151*/
152
153/*!
154 \qmlattachedproperty bool QtQuick.Controls::SelectionRectangle::dragging
155
156 This attached property will be \c true if the user is dragging on the handle.
157 It is attached to each handle instance.
158*/
159
160QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate()
161 : QQuickControlPrivate()
162{
163 m_tapHandler = new QQuickTapHandler();
164 m_dragHandler = new QQuickDragHandler();
165 m_dragHandler->setTarget(nullptr);
166
167 QObject::connect(sender: &m_scrollTimer, signal: &QTimer::timeout, slot: [&]{
168 if (m_topLeftHandle && m_draggedHandle == m_topLeftHandle.data())
169 m_selectable->setSelectionStartPos(m_scrollToPoint);
170 else
171 m_selectable->setSelectionEndPos(m_scrollToPoint);
172 updateHandles();
173 const QSizeF dist = m_selectable->scrollTowardsPoint(pos: m_scrollToPoint, step: m_scrollSpeed);
174 m_scrollToPoint.rx() += dist.width() > 0 ? m_scrollSpeed.width() : -m_scrollSpeed.width();
175 m_scrollToPoint.ry() += dist.height() > 0 ? m_scrollSpeed.height() : -m_scrollSpeed.height();
176 m_scrollSpeed = QSizeF(qAbs(t: dist.width() * 0.007), qAbs(t: dist.height() * 0.007));
177 });
178
179 QObject::connect(sender: m_tapHandler, signal: &QQuickTapHandler::pressedChanged, slot: [this]() {
180 if (!m_tapHandler->isPressed())
181 return;
182 if (m_effectiveSelectionMode != QQuickSelectionRectangle::Drag)
183 return;
184
185 const QPointF pos = m_tapHandler->point().pressPosition();
186 const auto modifiers = m_tapHandler->point().modifiers();
187 if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier))
188 return;
189
190 if (modifiers & Qt::ShiftModifier) {
191 // Extend the selection towards the pressed cell. If there is no
192 // existing selection, start a new selection from the current item
193 // to the pressed item.
194 if (!m_active) {
195 if (!m_selectable->startSelection(pos, modifiers))
196 return;
197 m_selectable->setSelectionStartPos(QPoint{-1, -1});
198 }
199 m_selectable->setSelectionEndPos(pos);
200 updateHandles();
201 updateActiveState(isActive: true);
202 } else if (modifiers & Qt::ControlModifier) {
203 // Select a single cell, but keep the old selection (unless
204 // m_selectable->startSelection(pos. modifiers) returns false, which
205 // it will if selectionMode only allows a single selection).
206 if (handleUnderPos(pos) != nullptr) {
207 // Don't allow press'n'hold to start a new
208 // selection if it started on top of a handle.
209 return;
210 }
211
212 if (!m_selectable->startSelection(pos, modifiers))
213 return;
214 m_selectable->setSelectionStartPos(pos);
215 m_selectable->setSelectionEndPos(pos);
216 updateHandles();
217 updateActiveState(isActive: true);
218 }
219 });
220
221 QObject::connect(sender: m_tapHandler, signal: &QQuickTapHandler::longPressed, slot: [this]() {
222 if (m_effectiveSelectionMode != QQuickSelectionRectangle::PressAndHold)
223 return;
224
225 const QPointF pos = m_tapHandler->point().pressPosition();
226 const auto modifiers = m_tapHandler->point().modifiers();
227 if (handleUnderPos(pos) != nullptr) {
228 // Don't allow press'n'hold to start a new
229 // selection if it started on top of a handle.
230 return;
231 }
232
233 if (modifiers == Qt::ShiftModifier) {
234 // Extend the selection towards the pressed cell. If there is no
235 // existing selection, start a new selection from the current item
236 // to the pressed item.
237 if (!m_active) {
238 if (!m_selectable->startSelection(pos, modifiers))
239 return;
240 m_selectable->setSelectionStartPos(QPoint{-1, -1});
241 }
242 m_selectable->setSelectionEndPos(pos);
243 updateHandles();
244 updateActiveState(isActive: true);
245 } else {
246 // Select a single cell. m_selectable->startSelection() will decide
247 // if the existing selection should also be cleared.
248 if (!m_selectable->startSelection(pos, modifiers))
249 return;
250 m_selectable->setSelectionStartPos(pos);
251 m_selectable->setSelectionEndPos(pos);
252 updateHandles();
253 updateActiveState(isActive: true);
254 }
255 });
256
257 QObject::connect(sender: m_dragHandler, signal: &QQuickDragHandler::activeChanged, slot: [this]() {
258 Q_ASSERT(m_effectiveSelectionMode == QQuickSelectionRectangle::Drag);
259 const QPointF startPos = m_dragHandler->centroid().pressPosition();
260 const QPointF dragPos = m_dragHandler->centroid().position();
261 const auto modifiers = m_dragHandler->centroid().modifiers();
262 if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier))
263 return;
264
265 if (m_dragHandler->active()) {
266 // Start a new selection unless there is an active selection
267 // already, and one of the relevant modifiers are being held.
268 // In that case we continue to extend the active selection instead.
269 const bool modifiersHeld = modifiers & (Qt::ControlModifier | Qt::ShiftModifier);
270 if (!m_active || !modifiersHeld) {
271 if (!m_selectable->startSelection(pos: startPos, modifiers))
272 return;
273 m_selectable->setSelectionStartPos(startPos);
274 }
275 m_selectable->setSelectionEndPos(dragPos);
276 m_draggedHandle = nullptr;
277 updateHandles();
278 updateActiveState(isActive: true);
279 updateDraggingState(isDragging: true);
280 } else {
281 m_scrollTimer.stop();
282 m_selectable->normalizeSelection();
283 updateDraggingState(isDragging: false);
284 }
285 });
286
287 QObject::connect(sender: m_dragHandler, signal: &QQuickDragHandler::centroidChanged, slot: [this]() {
288 if (!m_dragging)
289 return;
290 const QPointF pos = m_dragHandler->centroid().position();
291 m_selectable->setSelectionEndPos(pos);
292 updateHandles();
293 scrollTowardsPos(pos);
294 });
295}
296
297void QQuickSelectionRectanglePrivate::scrollTowardsPos(const QPointF &pos)
298{
299 m_scrollToPoint = pos;
300 if (m_scrollTimer.isActive())
301 return;
302
303 const QSizeF dist = m_selectable->scrollTowardsPoint(pos: m_scrollToPoint, step: m_scrollSpeed);
304 if (!dist.isNull())
305 m_scrollTimer.start(msec: 1);
306}
307
308QQuickItem *QQuickSelectionRectanglePrivate::handleUnderPos(const QPointF &pos)
309{
310 const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
311 if (m_topLeftHandle) {
312 const QPointF localPos = m_topLeftHandle->mapFromItem(item: handlerTarget, point: pos);
313 if (m_topLeftHandle->contains(point: localPos))
314 return m_topLeftHandle.data();
315 }
316
317 if (m_bottomRightHandle) {
318 const QPointF localPos = m_bottomRightHandle->mapFromItem(item: handlerTarget, point: pos);
319 if (m_bottomRightHandle->contains(point: localPos))
320 return m_bottomRightHandle.data();
321 }
322
323 return nullptr;
324}
325
326void QQuickSelectionRectanglePrivate::updateDraggingState(bool dragging)
327{
328 if (dragging != m_dragging) {
329 m_dragging = dragging;
330 emit q_func()->draggingChanged();
331 }
332
333 if (auto attached = getAttachedObject(object: m_draggedHandle))
334 attached->setDragging(dragging);
335}
336
337void QQuickSelectionRectanglePrivate::updateActiveState(bool active)
338{
339 if (active == m_active)
340 return;
341
342 m_active = active;
343
344 if (const auto tableview = qobject_cast<QQuickTableView *>(object: m_target)) {
345 if (active) {
346 // If the position of rows and columns changes, we'll need to reposition the handles
347 connect(sender: tableview, signal: &QQuickTableView::layoutChanged, receiverPrivate: this, slot: &QQuickSelectionRectanglePrivate::updateHandles);
348 } else {
349 disconnect(sender: tableview, signal: &QQuickTableView::layoutChanged, receiverPrivate: this, slot: &QQuickSelectionRectanglePrivate::updateHandles);
350 }
351 }
352
353 emit q_func()->activeChanged();
354}
355
356QQuickItem *QQuickSelectionRectanglePrivate::createHandle(QQmlComponent *delegate, Qt::Corner corner)
357{
358 Q_Q(QQuickSelectionRectangle);
359
360 // Incubate the handle
361 QObject *obj = delegate->beginCreate(QQmlEngine::contextForObject(q));
362 QQuickItem *handleItem = qobject_cast<QQuickItem*>(o: obj);
363 const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
364 handleItem->setParentItem(handlerTarget);
365 if (auto attached = getAttachedObject(object: handleItem))
366 attached->setControl(q);
367 delegate->completeCreate();
368 if (handleItem->z() == 0)
369 handleItem->setZ(100);
370
371 // Add pointer handlers to it
372 QQuickDragHandler *dragHandler = new QQuickDragHandler();
373 dragHandler->setTarget(nullptr);
374 dragHandler->setParentItem(handleItem);
375 dragHandler->setGrabPermissions(QQuickPointerHandler::CanTakeOverFromAnything);
376
377 QQuickHoverHandler *hoverHandler = new QQuickHoverHandler();
378 hoverHandler->setTarget(nullptr);
379 hoverHandler->setParentItem(handleItem);
380#if QT_CONFIG(cursor)
381 hoverHandler->setCursorShape(Qt::SizeFDiagCursor);
382#endif
383 hoverHandler->setBlocking(true);
384
385 // Add a dummy TapHandler that blocks the user from being
386 // able to tap on a tap handler underneath the handle.
387 QQuickTapHandler *tapHandler = new QQuickTapHandler();
388 tapHandler->setTarget(nullptr);
389 tapHandler->setParentItem(handleItem);
390 // Set a dummy gesture policy so that the tap handler
391 // will get an exclusive grab already on press
392 tapHandler->setGesturePolicy(QQuickTapHandler::DragWithinBounds);
393
394 QObject::connect(sender: dragHandler, signal: &QQuickDragHandler::activeChanged, slot: [this, corner, handleItem, dragHandler]() {
395 if (dragHandler->active()) {
396 const QPointF localPos = dragHandler->centroid().position();
397 const QPointF pos = handleItem->mapToItem(item: handleItem->parentItem(), point: localPos);
398 if (corner == Qt::TopLeftCorner)
399 m_selectable->setSelectionStartPos(pos);
400 else
401 m_selectable->setSelectionEndPos(pos);
402
403 m_draggedHandle = handleItem;
404 updateHandles();
405 updateDraggingState(dragging: true);
406#if QT_CONFIG(cursor)
407 QGuiApplication::setOverrideCursor(Qt::SizeFDiagCursor);
408#endif
409 } else {
410 m_scrollTimer.stop();
411 m_selectable->normalizeSelection();
412 updateDraggingState(dragging: false);
413#if QT_CONFIG(cursor)
414 QGuiApplication::restoreOverrideCursor();
415#endif
416 }
417 });
418
419 QObject::connect(sender: dragHandler, signal: &QQuickDragHandler::centroidChanged, slot: [this, corner, handleItem, dragHandler]() {
420 if (!m_dragging)
421 return;
422
423 const QPointF localPos = dragHandler->centroid().position();
424 const QPointF pos = handleItem->mapToItem(item: handleItem->parentItem(), point: localPos);
425 if (corner == Qt::TopLeftCorner)
426 m_selectable->setSelectionStartPos(pos);
427 else
428 m_selectable->setSelectionEndPos(pos);
429
430 updateHandles();
431 scrollTowardsPos(pos);
432 });
433
434 return handleItem;
435}
436
437void QQuickSelectionRectanglePrivate::updateHandles()
438{
439 const QRectF rect = m_selectable->selectionRectangle().normalized();
440
441 if (!m_topLeftHandle && m_topLeftHandleDelegate)
442 m_topLeftHandle.reset(other: createHandle(delegate: m_topLeftHandleDelegate, corner: Qt::TopLeftCorner));
443
444 if (!m_bottomRightHandle && m_bottomRightHandleDelegate)
445 m_bottomRightHandle.reset(other: createHandle(delegate: m_bottomRightHandleDelegate, corner: Qt::BottomRightCorner));
446
447 if (m_topLeftHandle) {
448 m_topLeftHandle->setX(rect.x() - (m_topLeftHandle->width() / 2));
449 m_topLeftHandle->setY(rect.y() - (m_topLeftHandle->height() / 2));
450 }
451
452 if (m_bottomRightHandle) {
453 m_bottomRightHandle->setX(rect.x() + rect.width() - (m_bottomRightHandle->width() / 2));
454 m_bottomRightHandle->setY(rect.y() + rect.height() - (m_bottomRightHandle->height() / 2));
455 }
456}
457
458void QQuickSelectionRectanglePrivate::connectToTarget()
459{
460 // To support QuickSelectionRectangle::Auto, we need to listen for changes to the target
461 if (const auto flickable = qobject_cast<QQuickFlickable *>(object: m_target)) {
462 connect(sender: flickable, signal: &QQuickFlickable::interactiveChanged, receiverPrivate: this, slot: &QQuickSelectionRectanglePrivate::updateSelectionMode);
463 }
464
465 // Add a callback function that tells if the selection was
466 // modified outside of the actions taken by SelectionRectangle.
467 m_selectable->setCallback([this](QQuickSelectable::CallBackFlag flag){
468 switch (flag) {
469 case QQuickSelectable::CallBackFlag::CancelSelection:
470 // The selection is either cleared, or can no longer be
471 // represented as a rectangle with two selection handles.
472 updateActiveState(active: false);
473 break;
474 case QQuickSelectable::CallBackFlag::SelectionRectangleChanged:
475 // The selection has changed, but the selection is still
476 // rectangular and without holes.
477 updateHandles();
478 break;
479 default:
480 Q_UNREACHABLE();
481 }
482 });
483}
484
485void QQuickSelectionRectanglePrivate::updateSelectionMode()
486{
487 Q_Q(QQuickSelectionRectangle);
488
489 const bool enabled = q->isEnabled();
490 m_tapHandler->setEnabled(enabled);
491
492 if (m_selectionMode == QQuickSelectionRectangle::Auto) {
493 if (m_target && qobject_cast<QQuickScrollView *>(object: m_target->parentItem())) {
494 // ScrollView allows flicking with touch, but not with mouse. So we do
495 // the same here: you can drag to select with a mouse, but not with touch.
496 m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
497 m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
498 m_dragHandler->setEnabled(enabled);
499 } else if (const auto flickable = qobject_cast<QQuickFlickable *>(object: m_target)) {
500 if (enabled && !flickable->isInteractive()) {
501 m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
502 m_dragHandler->setEnabled(true);
503 } else {
504 m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold;
505 m_dragHandler->setEnabled(false);
506 }
507 } else {
508 m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
509 m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
510 m_dragHandler->setEnabled(enabled);
511 }
512 } else if (m_selectionMode == QQuickSelectionRectangle::Drag) {
513 m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
514 m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::AllDevices);
515 m_dragHandler->setEnabled(enabled);
516 } else {
517 m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold;
518 m_dragHandler->setEnabled(false);
519 }
520}
521
522QQuickSelectionRectangleAttached *QQuickSelectionRectanglePrivate::getAttachedObject(const QObject *object) const
523{
524 QObject *attachedObject = qmlAttachedPropertiesObject<QQuickSelectionRectangle>(obj: object);
525 return static_cast<QQuickSelectionRectangleAttached *>(attachedObject);
526}
527
528// --------------------------------------------------------
529
530QQuickSelectionRectangle::QQuickSelectionRectangle(QQuickItem *parent)
531 : QQuickControl(*(new QQuickSelectionRectanglePrivate), parent)
532{
533 Q_D(QQuickSelectionRectangle);
534 d->m_tapHandler->setParent(this);
535 d->m_dragHandler->setParent(this);
536
537 QObject::connect(sender: this, signal: &QQuickItem::enabledChanged, slot: [=]() {
538 d->m_scrollTimer.stop();
539 d->updateSelectionMode();
540 d->updateDraggingState(dragging: false);
541 d->updateActiveState(active: false);
542 });
543}
544
545QQuickItem *QQuickSelectionRectangle::target() const
546{
547 return d_func()->m_target;
548}
549
550void QQuickSelectionRectangle::setTarget(QQuickItem *target)
551{
552 Q_D(QQuickSelectionRectangle);
553 if (d->m_target == target)
554 return;
555
556 if (d->m_selectable) {
557 d->m_scrollTimer.stop();
558 d->m_tapHandler->setParent(this);
559 d->m_dragHandler->setParent(this);
560 d->m_target->disconnect(receiver: this);
561 d->m_selectable->setCallback(nullptr);
562 }
563
564 d->m_target = target;
565 d->m_selectable = nullptr;
566
567 if (d->m_target) {
568 d->m_selectable = dynamic_cast<QQuickSelectable *>(QObjectPrivate::get(o: d->m_target.data()));
569 if (!d->m_selectable)
570 qmlWarning(me: this) << "the assigned target is not supported by the control";
571 }
572
573 if (d->m_selectable) {
574 const auto handlerTarget = d->m_selectable->selectionPointerHandlerTarget();
575 d->m_dragHandler->setParentItem(handlerTarget);
576 d->m_tapHandler->setParentItem(handlerTarget);
577 d->connectToTarget();
578 d->updateSelectionMode();
579 }
580
581 emit targetChanged();
582}
583
584bool QQuickSelectionRectangle::active()
585{
586 return d_func()->m_active;
587}
588
589bool QQuickSelectionRectangle::dragging()
590{
591 return d_func()->m_dragging;
592}
593
594QQuickSelectionRectangle::SelectionMode QQuickSelectionRectangle::selectionMode() const
595{
596 return d_func()->m_selectionMode;
597}
598
599void QQuickSelectionRectangle::setSelectionMode(QQuickSelectionRectangle::SelectionMode selectionMode)
600{
601 Q_D(QQuickSelectionRectangle);
602 if (d->m_selectionMode == selectionMode)
603 return;
604
605 d->m_selectionMode = selectionMode;
606
607 if (d->m_target)
608 d->updateSelectionMode();
609
610 emit selectionModeChanged();
611}
612
613QQmlComponent *QQuickSelectionRectangle::topLeftHandle() const
614{
615 return d_func()->m_topLeftHandleDelegate;
616}
617
618void QQuickSelectionRectangle::setTopLeftHandle(QQmlComponent *topLeftHandle)
619{
620 Q_D(QQuickSelectionRectangle);
621 if (d->m_topLeftHandleDelegate == topLeftHandle)
622 return;
623
624 d->m_topLeftHandleDelegate = topLeftHandle;
625 emit topLeftHandleChanged();
626}
627
628QQmlComponent *QQuickSelectionRectangle::bottomRightHandle() const
629{
630 return d_func()->m_bottomRightHandleDelegate;
631}
632
633void QQuickSelectionRectangle::setBottomRightHandle(QQmlComponent *bottomRightHandle)
634{
635 Q_D(QQuickSelectionRectangle);
636 if (d->m_bottomRightHandleDelegate == bottomRightHandle)
637 return;
638
639 d->m_bottomRightHandleDelegate = bottomRightHandle;
640 emit bottomRightHandleChanged();
641}
642
643QQuickSelectionRectangleAttached *QQuickSelectionRectangle::qmlAttachedProperties(QObject *obj)
644{
645 return new QQuickSelectionRectangleAttached(obj);
646}
647
648QQuickSelectionRectangleAttached::QQuickSelectionRectangleAttached(QObject *parent)
649 : QObject(parent)
650{
651}
652
653QQuickSelectionRectangle *QQuickSelectionRectangleAttached::control() const
654{
655 return m_control;
656}
657
658void QQuickSelectionRectangleAttached::setControl(QQuickSelectionRectangle *control)
659{
660 if (m_control == control)
661 return;
662
663 m_control = control;
664 emit controlChanged();
665}
666
667bool QQuickSelectionRectangleAttached::dragging() const
668{
669 return m_dragging;
670}
671
672void QQuickSelectionRectangleAttached::setDragging(bool dragging)
673{
674 if (m_dragging == dragging)
675 return;
676
677 m_dragging = dragging;
678 emit draggingChanged();
679}
680
681QT_END_NAMESPACE
682
683#include "moc_qquickselectionrectangle_p.cpp"
684

source code of qtdeclarative/src/quicktemplates/qquickselectionrectangle.cpp