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