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