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

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