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 Q_Q(QQuickSelectionRectangle);
181
182 if (!m_tapHandler->isPressed()) {
183 // Deactivate the selection rectangle when the tap handler
184 // is released and there's no selection
185 if (q->active() && !m_selectable->hasSelection())
186 updateActiveState(isActive: false);
187 return;
188 }
189 if (m_effectiveSelectionMode != QQuickSelectionRectangle::Drag)
190 return;
191
192 const QPointF pos = m_tapHandler->point().pressPosition();
193 const auto modifiers = m_tapHandler->point().modifiers();
194 if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier))
195 return;
196
197 // A selection rectangle is not valid when its width or height is 0
198 const auto isSelectionRectValid = [](const QRectF &selectionRect) -> bool {
199 return ((selectionRect.width() != 0) || (selectionRect.height() != 0));
200 };
201
202 if (modifiers & Qt::ShiftModifier) {
203 // Extend the selection towards the pressed cell. If there is no
204 // existing selection, start a new selection from the current item
205 // to the pressed item.
206 if (!m_active) {
207 if (!m_selectable->startSelection(pos, modifiers))
208 return;
209 m_selectable->setSelectionStartPos(QPoint{-1, -1});
210 }
211 m_selectable->setSelectionEndPos(pos);
212 // Don't update and activate the selection handlers when
213 // the size of m_selectable's selection rectangle is 0.
214 if (!isSelectionRectValid(m_selectable->selectionRectangle()))
215 return;
216 updateHandles();
217 updateActiveState(isActive: true);
218 } else if (modifiers & Qt::ControlModifier) {
219 // Select a single cell, but keep the old selection (unless
220 // m_selectable->startSelection(pos. modifiers) returns false, which
221 // it will if selectionMode only allows a single selection).
222 if (handleUnderPos(pos) != nullptr) {
223 // Don't allow press'n'hold to start a new
224 // selection if it started on top of a handle.
225 return;
226 }
227
228 if (!m_selectable->startSelection(pos, modifiers))
229 return;
230 m_selectable->setSelectionStartPos(pos);
231 m_selectable->setSelectionEndPos(pos);
232 // Don't update and activate the selection handlers when
233 // the size of m_selectable's selection rectangle is 0.
234 if (!isSelectionRectValid(m_selectable->selectionRectangle()))
235 return;
236 updateHandles();
237 updateActiveState(isActive: true);
238 }
239 });
240
241 QObject::connect(sender: m_tapHandler, signal: &QQuickTapHandler::longPressed, slot: [this]() {
242 if (m_effectiveSelectionMode != QQuickSelectionRectangle::PressAndHold)
243 return;
244
245 const QPointF pos = m_tapHandler->point().pressPosition();
246 const auto modifiers = m_tapHandler->point().modifiers();
247 if (handleUnderPos(pos) != nullptr) {
248 // Don't allow press'n'hold to start a new
249 // selection if it started on top of a handle.
250 return;
251 }
252
253 // A selection rectangle is not valid when its width or height is 0
254 const auto isSelectionRectValid = [](const QRectF &selectionRect) -> bool {
255 return ((selectionRect.width() != 0) || (selectionRect.height() != 0));
256 };
257
258 if (modifiers == Qt::ShiftModifier) {
259 // Extend the selection towards the pressed cell. If there is no
260 // existing selection, start a new selection from the current item
261 // to the pressed item.
262 if (!m_active) {
263 if (!m_selectable->startSelection(pos, modifiers))
264 return;
265 m_selectable->setSelectionStartPos(QPoint{-1, -1});
266 }
267 m_selectable->setSelectionEndPos(pos);
268 if (!isSelectionRectValid(m_selectable->selectionRectangle()))
269 return;
270 updateHandles();
271 updateActiveState(isActive: true);
272 } else {
273 // Select a single cell. m_selectable->startSelection() will decide
274 // if the existing selection should also be cleared.
275 if (!m_selectable->startSelection(pos, modifiers))
276 return;
277 m_selectable->setSelectionStartPos(pos);
278 m_selectable->setSelectionEndPos(pos);
279 if (!isSelectionRectValid(m_selectable->selectionRectangle()))
280 return;
281 updateHandles();
282 updateActiveState(isActive: true);
283 }
284 });
285
286 QObject::connect(sender: m_dragHandler, signal: &QQuickDragHandler::activeChanged, slot: [this]() {
287 Q_ASSERT(m_effectiveSelectionMode == QQuickSelectionRectangle::Drag);
288 const QPointF startPos = m_dragHandler->centroid().pressPosition();
289 const QPointF dragPos = m_dragHandler->centroid().position();
290 const auto modifiers = m_dragHandler->centroid().modifiers();
291 if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier))
292 return;
293
294 // A selection rectangle is not valid when its width or height is 0
295 const auto isSelectionRectValid = [](const QRectF &selectionRect) -> bool {
296 return ((selectionRect.width() != 0) || (selectionRect.height() != 0));
297 };
298
299 if (m_dragHandler->active()) {
300 // Start a new selection unless there is an active selection
301 // already, and one of the relevant modifiers are being held.
302 // In that case we continue to extend the active selection instead.
303 const bool modifiersHeld = modifiers & (Qt::ControlModifier | Qt::ShiftModifier);
304 if (!m_active || !modifiersHeld) {
305 if (!m_selectable->startSelection(pos: startPos, modifiers))
306 return;
307 m_selectable->setSelectionStartPos(startPos);
308 }
309 m_selectable->setSelectionEndPos(dragPos);
310 m_draggedHandle = nullptr;
311 if (!isSelectionRectValid(m_selectable->selectionRectangle()))
312 return;
313 updateHandles();
314 updateActiveState(isActive: true);
315 updateDraggingState(isDragging: true);
316 } else {
317 m_scrollTimer.stop();
318 m_selectable->normalizeSelection();
319 updateDraggingState(isDragging: false);
320 }
321 });
322
323 QObject::connect(sender: m_dragHandler, signal: &QQuickDragHandler::centroidChanged, slot: [this]() {
324 if (!m_dragging)
325 return;
326 const QPointF pos = m_dragHandler->centroid().position();
327 m_selectable->setSelectionEndPos(pos);
328 updateHandles();
329 scrollTowardsPos(pos);
330 });
331}
332
333void QQuickSelectionRectanglePrivate::scrollTowardsPos(const QPointF &pos)
334{
335 m_scrollToPoint = pos;
336 if (m_scrollTimer.isActive())
337 return;
338
339 const QSizeF dist = m_selectable->scrollTowardsPoint(pos: m_scrollToPoint, step: m_scrollSpeed);
340 if (!dist.isNull())
341 m_scrollTimer.start(msec: 1);
342}
343
344QQuickItem *QQuickSelectionRectanglePrivate::handleUnderPos(const QPointF &pos)
345{
346 const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
347 if (m_topLeftHandle) {
348 const QPointF localPos = m_topLeftHandle->mapFromItem(item: handlerTarget, point: pos);
349 if (m_topLeftHandle->contains(point: localPos))
350 return m_topLeftHandle.data();
351 }
352
353 if (m_bottomRightHandle) {
354 const QPointF localPos = m_bottomRightHandle->mapFromItem(item: handlerTarget, point: pos);
355 if (m_bottomRightHandle->contains(point: localPos))
356 return m_bottomRightHandle.data();
357 }
358
359 return nullptr;
360}
361
362void QQuickSelectionRectanglePrivate::updateDraggingState(bool dragging)
363{
364 if (dragging != m_dragging) {
365 m_dragging = dragging;
366 emit q_func()->draggingChanged();
367 }
368
369 if (auto attached = getAttachedObject(object: m_draggedHandle))
370 attached->setDragging(dragging);
371}
372
373void QQuickSelectionRectanglePrivate::updateActiveState(bool active)
374{
375 if (active == m_active)
376 return;
377
378 m_active = active;
379
380 if (const auto tableview = qobject_cast<QQuickTableView *>(object: m_target)) {
381 if (active) {
382 // If the position of rows and columns changes, we'll need to reposition the handles
383 connect(sender: tableview, signal: &QQuickTableView::layoutChanged, receiverPrivate: this, slot: &QQuickSelectionRectanglePrivate::updateHandles);
384 } else {
385 disconnect(sender: tableview, signal: &QQuickTableView::layoutChanged, receiverPrivate: this, slot: &QQuickSelectionRectanglePrivate::updateHandles);
386 }
387 }
388
389 emit q_func()->activeChanged();
390}
391
392QQuickItem *QQuickSelectionRectanglePrivate::createHandle(QQmlComponent *delegate, Qt::Corner corner)
393{
394 Q_Q(QQuickSelectionRectangle);
395
396 // Incubate the handle
397 QObject *obj = delegate->beginCreate(QQmlEngine::contextForObject(q));
398 QQuickItem *handleItem = qobject_cast<QQuickItem*>(o: obj);
399 const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
400 handleItem->setParentItem(handlerTarget);
401 if (auto attached = getAttachedObject(object: handleItem))
402 attached->setControl(q);
403 delegate->completeCreate();
404 if (handleItem->z() == 0)
405 handleItem->setZ(100);
406
407 // Add pointer handlers to it
408 QQuickDragHandler *dragHandler = new QQuickDragHandler();
409 dragHandler->setTarget(nullptr);
410 dragHandler->setParentItem(handleItem);
411 dragHandler->setGrabPermissions(QQuickPointerHandler::CanTakeOverFromAnything);
412
413 QQuickHoverHandler *hoverHandler = new QQuickHoverHandler();
414 hoverHandler->setTarget(nullptr);
415 hoverHandler->setParentItem(handleItem);
416#if QT_CONFIG(cursor)
417 hoverHandler->setCursorShape(Qt::SizeFDiagCursor);
418#endif
419 hoverHandler->setBlocking(true);
420
421 // Add a dummy TapHandler that blocks the user from being
422 // able to tap on a tap handler underneath the handle.
423 QQuickTapHandler *tapHandler = new QQuickTapHandler();
424 tapHandler->setTarget(nullptr);
425 tapHandler->setParentItem(handleItem);
426 // Set a dummy gesture policy so that the tap handler
427 // will get an exclusive grab already on press
428 tapHandler->setGesturePolicy(QQuickTapHandler::DragWithinBounds);
429
430 QObject::connect(sender: dragHandler, signal: &QQuickDragHandler::activeChanged, slot: [this, corner, handleItem, dragHandler]() {
431 if (dragHandler->active()) {
432 const QPointF localPos = dragHandler->centroid().position();
433 const QPointF pos = handleItem->mapToItem(item: handleItem->parentItem(), point: localPos);
434 if (corner == Qt::TopLeftCorner)
435 m_selectable->setSelectionStartPos(pos);
436 else
437 m_selectable->setSelectionEndPos(pos);
438
439 m_draggedHandle = handleItem;
440 updateHandles();
441 updateDraggingState(dragging: true);
442#if QT_CONFIG(cursor)
443 QGuiApplication::setOverrideCursor(Qt::SizeFDiagCursor);
444#endif
445 } else {
446 m_scrollTimer.stop();
447 m_selectable->normalizeSelection();
448 updateDraggingState(dragging: false);
449#if QT_CONFIG(cursor)
450 QGuiApplication::restoreOverrideCursor();
451#endif
452 }
453 });
454
455 QObject::connect(sender: dragHandler, signal: &QQuickDragHandler::centroidChanged, slot: [this, corner, handleItem, dragHandler]() {
456 if (!m_dragging)
457 return;
458
459 const QPointF localPos = dragHandler->centroid().position();
460 const QPointF pos = handleItem->mapToItem(item: handleItem->parentItem(), point: localPos);
461 if (corner == Qt::TopLeftCorner)
462 m_selectable->setSelectionStartPos(pos);
463 else
464 m_selectable->setSelectionEndPos(pos);
465
466 updateHandles();
467 scrollTowardsPos(pos);
468 });
469
470 return handleItem;
471}
472
473void QQuickSelectionRectanglePrivate::updateHandles()
474{
475 const QRectF rect = m_selectable->selectionRectangle().normalized();
476
477 if (!m_topLeftHandle && m_topLeftHandleDelegate)
478 m_topLeftHandle.reset(other: createHandle(delegate: m_topLeftHandleDelegate, corner: Qt::TopLeftCorner));
479
480 if (!m_bottomRightHandle && m_bottomRightHandleDelegate)
481 m_bottomRightHandle.reset(other: createHandle(delegate: m_bottomRightHandleDelegate, corner: Qt::BottomRightCorner));
482
483 if (m_topLeftHandle) {
484 m_topLeftHandle->setX(rect.x() - (m_topLeftHandle->width() / 2));
485 m_topLeftHandle->setY(rect.y() - (m_topLeftHandle->height() / 2));
486 }
487
488 if (m_bottomRightHandle) {
489 m_bottomRightHandle->setX(rect.x() + rect.width() - (m_bottomRightHandle->width() / 2));
490 m_bottomRightHandle->setY(rect.y() + rect.height() - (m_bottomRightHandle->height() / 2));
491 }
492}
493
494void QQuickSelectionRectanglePrivate::connectToTarget()
495{
496 // To support QuickSelectionRectangle::Auto, we need to listen for changes to the target
497 if (const auto flickable = qobject_cast<QQuickFlickable *>(object: m_target)) {
498 connect(sender: flickable, signal: &QQuickFlickable::interactiveChanged, receiverPrivate: this, slot: &QQuickSelectionRectanglePrivate::updateSelectionMode);
499 }
500
501 // Add a callback function that tells if the selection was
502 // modified outside of the actions taken by SelectionRectangle.
503 m_selectable->setCallback([this](QQuickSelectable::CallBackFlag flag){
504 switch (flag) {
505 case QQuickSelectable::CallBackFlag::CancelSelection:
506 // The selection is either cleared, or can no longer be
507 // represented as a rectangle with two selection handles.
508 updateActiveState(active: false);
509 break;
510 case QQuickSelectable::CallBackFlag::SelectionRectangleChanged:
511 // The selection has changed, but the selection is still
512 // rectangular and without holes.
513 updateHandles();
514 break;
515 default:
516 Q_UNREACHABLE();
517 }
518 });
519}
520
521void QQuickSelectionRectanglePrivate::updateSelectionMode()
522{
523 Q_Q(QQuickSelectionRectangle);
524
525 const bool enabled = q->isEnabled();
526 m_tapHandler->setEnabled(enabled);
527
528 if (m_selectionMode == QQuickSelectionRectangle::Auto) {
529 if (m_target && qobject_cast<QQuickScrollView *>(object: m_target->parentItem())) {
530 // ScrollView allows flicking with touch, but not with mouse. So we do
531 // the same here: you can drag to select with a mouse, but not with touch.
532 m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
533 m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
534 m_dragHandler->setEnabled(enabled);
535 } else if (const auto flickable = qobject_cast<QQuickFlickable *>(object: m_target)) {
536 if (enabled && !flickable->isInteractive()) {
537 m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
538 m_dragHandler->setEnabled(true);
539 } else {
540 m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold;
541 m_dragHandler->setEnabled(false);
542 }
543 } else {
544 m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
545 m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
546 m_dragHandler->setEnabled(enabled);
547 }
548 } else if (m_selectionMode == QQuickSelectionRectangle::Drag) {
549 m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
550 m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::AllDevices);
551 m_dragHandler->setEnabled(enabled);
552 } else {
553 m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold;
554 m_dragHandler->setEnabled(false);
555 }
556}
557
558QQuickSelectionRectangleAttached *QQuickSelectionRectanglePrivate::getAttachedObject(const QObject *object) const
559{
560 QObject *attachedObject = qmlAttachedPropertiesObject<QQuickSelectionRectangle>(obj: object);
561 return static_cast<QQuickSelectionRectangleAttached *>(attachedObject);
562}
563
564// --------------------------------------------------------
565
566QQuickSelectionRectangle::QQuickSelectionRectangle(QQuickItem *parent)
567 : QQuickControl(*(new QQuickSelectionRectanglePrivate), parent)
568{
569 Q_D(QQuickSelectionRectangle);
570 d->m_tapHandler->setParent(this);
571 d->m_dragHandler->setParent(this);
572
573 QObject::connect(sender: this, signal: &QQuickItem::enabledChanged, slot: [=]() {
574 d->m_scrollTimer.stop();
575 d->updateSelectionMode();
576 d->updateDraggingState(dragging: false);
577 d->updateActiveState(active: false);
578 });
579}
580
581QQuickItem *QQuickSelectionRectangle::target() const
582{
583 return d_func()->m_target;
584}
585
586void QQuickSelectionRectangle::setTarget(QQuickItem *target)
587{
588 Q_D(QQuickSelectionRectangle);
589 if (d->m_target == target)
590 return;
591
592 if (d->m_selectable) {
593 d->m_scrollTimer.stop();
594 d->m_tapHandler->setParent(this);
595 d->m_dragHandler->setParent(this);
596 d->m_target->disconnect(receiver: this);
597 d->m_selectable->setCallback(nullptr);
598 }
599
600 d->m_target = target;
601 d->m_selectable = nullptr;
602
603 if (d->m_target) {
604 d->m_selectable = dynamic_cast<QQuickSelectable *>(QObjectPrivate::get(o: d->m_target.data()));
605 if (!d->m_selectable)
606 qmlWarning(me: this) << "the assigned target is not supported by the control";
607 }
608
609 if (d->m_selectable) {
610 const auto handlerTarget = d->m_selectable->selectionPointerHandlerTarget();
611 d->m_dragHandler->setParentItem(handlerTarget);
612 d->m_tapHandler->setParentItem(handlerTarget);
613 d->connectToTarget();
614 d->updateSelectionMode();
615 }
616
617 emit targetChanged();
618}
619
620bool QQuickSelectionRectangle::active()
621{
622 return d_func()->m_active;
623}
624
625bool QQuickSelectionRectangle::dragging()
626{
627 return d_func()->m_dragging;
628}
629
630QQuickSelectionRectangle::SelectionMode QQuickSelectionRectangle::selectionMode() const
631{
632 return d_func()->m_selectionMode;
633}
634
635void QQuickSelectionRectangle::setSelectionMode(QQuickSelectionRectangle::SelectionMode selectionMode)
636{
637 Q_D(QQuickSelectionRectangle);
638 if (d->m_selectionMode == selectionMode)
639 return;
640
641 d->m_selectionMode = selectionMode;
642
643 if (d->m_target)
644 d->updateSelectionMode();
645
646 emit selectionModeChanged();
647}
648
649QQmlComponent *QQuickSelectionRectangle::topLeftHandle() const
650{
651 return d_func()->m_topLeftHandleDelegate;
652}
653
654void QQuickSelectionRectangle::setTopLeftHandle(QQmlComponent *topLeftHandle)
655{
656 Q_D(QQuickSelectionRectangle);
657 if (d->m_topLeftHandleDelegate == topLeftHandle)
658 return;
659
660 d->m_topLeftHandleDelegate = topLeftHandle;
661 emit topLeftHandleChanged();
662}
663
664QQmlComponent *QQuickSelectionRectangle::bottomRightHandle() const
665{
666 return d_func()->m_bottomRightHandleDelegate;
667}
668
669void QQuickSelectionRectangle::setBottomRightHandle(QQmlComponent *bottomRightHandle)
670{
671 Q_D(QQuickSelectionRectangle);
672 if (d->m_bottomRightHandleDelegate == bottomRightHandle)
673 return;
674
675 d->m_bottomRightHandleDelegate = bottomRightHandle;
676 emit bottomRightHandleChanged();
677}
678
679QQuickSelectionRectangleAttached *QQuickSelectionRectangle::qmlAttachedProperties(QObject *obj)
680{
681 return new QQuickSelectionRectangleAttached(obj);
682}
683
684QQuickSelectionRectangleAttached::QQuickSelectionRectangleAttached(QObject *parent)
685 : QObject(parent)
686{
687}
688
689QQuickSelectionRectangle *QQuickSelectionRectangleAttached::control() const
690{
691 return m_control;
692}
693
694void QQuickSelectionRectangleAttached::setControl(QQuickSelectionRectangle *control)
695{
696 if (m_control == control)
697 return;
698
699 m_control = control;
700 emit controlChanged();
701}
702
703bool QQuickSelectionRectangleAttached::dragging() const
704{
705 return m_dragging;
706}
707
708void QQuickSelectionRectangleAttached::setDragging(bool dragging)
709{
710 if (m_dragging == dragging)
711 return;
712
713 m_dragging = dragging;
714 emit draggingChanged();
715}
716
717QT_END_NAMESPACE
718
719#include "moc_qquickselectionrectangle_p.cpp"
720

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