Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
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
16
21
162{
165 m_dragHandler->setTarget(nullptr);
166
170 else
174 m_scrollToPoint.rx() += dist.width() > 0 ? m_scrollSpeed.width() : -m_scrollSpeed.width();
176 m_scrollSpeed = QSizeF(qAbs(dist.width() * 0.007), qAbs(dist.height() * 0.007));
177 });
178
180 if (!m_tapHandler->isPressed())
181 return;
183 return;
184
186 const auto modifiers = m_tapHandler->point().modifiers();
188 return;
189
191 // Extend the selection towards the pressed cell. If there is no
192 // existing selection, start a new selection from the current item
193 // to the pressed item.
194 if (!m_active) {
196 return;
198 }
201 updateActiveState(true);
202 } else if (modifiers & Qt::ControlModifier) {
203 // Select a single cell, but keep the old selection (unless
204 // m_selectable->startSelection(pos. modifiers) returns false, which
205 // it will if selectionMode only allows a single selection).
206 if (handleUnderPos(pos) != nullptr) {
207 // Don't allow press'n'hold to start a new
208 // selection if it started on top of a handle.
209 return;
210 }
211
213 return;
217 updateActiveState(true);
218 }
219 });
220
223 return;
224
225 const QPointF pos = m_tapHandler->point().pressPosition();
226 const auto modifiers = m_tapHandler->point().modifiers();
227 if (handleUnderPos(pos) != nullptr) {
228 // Don't allow press'n'hold to start a new
229 // selection if it started on top of a handle.
230 return;
231 }
232
234 // Extend the selection towards the pressed cell. If there is no
235 // existing selection, start a new selection from the current item
236 // to the pressed item.
237 if (!m_active) {
239 return;
241 }
244 updateActiveState(true);
245 } else if (modifiers == Qt::ControlModifier) {
246 // Select a single cell, but keep the old selection (unless
247 // m_selectable->startSelection(pos, modifiers) returns false, which
248 // it will if selectionMode only allows a single selection).
250 return;
254 updateActiveState(true);
255 } else if (modifiers == Qt::NoModifier) {
256 // Select a single cell
259 return;
263 updateActiveState(true);
264 }
265 });
266
269 const QPointF startPos = m_dragHandler->centroid().pressPosition();
270 const QPointF dragPos = m_dragHandler->centroid().position();
271 const auto modifiers = m_dragHandler->centroid().modifiers();
273 return;
274
275 if (m_dragHandler->active()) {
276 // Start a new selection unless there is an active selection
277 // already, and one of the relevant modifiers are being held.
278 // In that case we continue to extend the active selection instead.
279 const bool modifiersHeld = modifiers & (Qt::ControlModifier | Qt::ShiftModifier);
280 if (!m_active || !modifiersHeld) {
281 if (!m_selectable->startSelection(startPos, modifiers))
282 return;
284 }
286 m_draggedHandle = nullptr;
288 updateActiveState(true);
290 } else {
293 updateDraggingState(false);
294 }
295 });
296
298 if (!m_dragging)
299 return;
304 });
305}
306
317
319{
320 const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
321 if (m_topLeftHandle) {
322 const QPointF localPos = m_topLeftHandle->mapFromItem(handlerTarget, pos);
323 if (m_topLeftHandle->contains(localPos))
324 return m_topLeftHandle.data();
325 }
326
328 const QPointF localPos = m_bottomRightHandle->mapFromItem(handlerTarget, pos);
329 if (m_bottomRightHandle->contains(localPos))
330 return m_bottomRightHandle.data();
331 }
332
333 return nullptr;
334}
335
337{
338 if (dragging != m_dragging) {
339 m_dragging = dragging;
340 emit q_func()->draggingChanged();
341 }
342
343 if (auto attached = getAttachedObject(m_draggedHandle))
344 attached->setDragging(dragging);
345}
346
348{
349 if (active == m_active)
350 return;
351
352 m_active = active;
353
354 if (const auto tableview = qobject_cast<QQuickTableView *>(m_target)) {
355 if (active) {
356 // If the position of rows and columns changes, we'll need to reposition the handles
357 connect(tableview, &QQuickTableView::layoutChanged, this, &QQuickSelectionRectanglePrivate::updateHandles);
358 } else {
359 disconnect(tableview, &QQuickTableView::layoutChanged, this, &QQuickSelectionRectanglePrivate::updateHandles);
360 }
361 }
362
363 emit q_func()->activeChanged();
364}
365
367{
369
370 // Incubate the handle
373 const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
374 handleItem->setParentItem(handlerTarget);
375 if (auto attached = getAttachedObject(handleItem))
376 attached->setControl(q);
377 delegate->completeCreate();
378 if (handleItem->z() == 0)
379 handleItem->setZ(100);
380
381 // Add pointer handlers to it
382 QQuickDragHandler *dragHandler = new QQuickDragHandler();
383 dragHandler->setTarget(nullptr);
384 dragHandler->setParentItem(handleItem);
385 dragHandler->setGrabPermissions(QQuickPointerHandler::CanTakeOverFromAnything);
386
387 QQuickHoverHandler *hoverHandler = new QQuickHoverHandler();
388 hoverHandler->setTarget(nullptr);
389 hoverHandler->setParentItem(handleItem);
390#if QT_CONFIG(cursor)
391 hoverHandler->setCursorShape(Qt::SizeFDiagCursor);
392#endif
393 hoverHandler->setBlocking(true);
394
395 // Add a dummy TapHandler that blocks the user from being
396 // able to tap on a tap handler underneath the handle.
397 QQuickTapHandler *tapHandler = new QQuickTapHandler();
398 tapHandler->setTarget(nullptr);
399 tapHandler->setParentItem(handleItem);
400 // Set a dummy gesture policy so that the tap handler
401 // will get an exclusive grab already on press
402 tapHandler->setGesturePolicy(QQuickTapHandler::DragWithinBounds);
403
404 QObject::connect(dragHandler, &QQuickDragHandler::activeChanged, [this, corner, handleItem, dragHandler]() {
405 if (dragHandler->active()) {
406 const QPointF localPos = dragHandler->centroid().position();
407 const QPointF pos = handleItem->mapToItem(handleItem->parentItem(), localPos);
408 if (corner == Qt::TopLeftCorner)
409 m_selectable->setSelectionStartPos(pos);
410 else
411 m_selectable->setSelectionEndPos(pos);
412
413 m_draggedHandle = handleItem;
414 updateHandles();
415 updateDraggingState(true);
416#if QT_CONFIG(cursor)
417 QGuiApplication::setOverrideCursor(Qt::SizeFDiagCursor);
418#endif
419 } else {
420 m_scrollTimer.stop();
421 m_selectable->normalizeSelection();
422 updateDraggingState(false);
423#if QT_CONFIG(cursor)
424 QGuiApplication::restoreOverrideCursor();
425#endif
426 }
427 });
428
429 QObject::connect(dragHandler, &QQuickDragHandler::centroidChanged, [this, corner, handleItem, dragHandler]() {
430 if (!m_dragging)
431 return;
432
433 const QPointF localPos = dragHandler->centroid().position();
434 const QPointF pos = handleItem->mapToItem(handleItem->parentItem(), localPos);
435 if (corner == Qt::TopLeftCorner)
437 else
439
442 });
443
444 return handleItem;
445}
446
467
469{
470 // To support QuickSelectionRectangle::Auto, we need to listen for changes to the target
471 if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
473 }
474
475 // Add a callback function that tells if the selection was
476 // modified outside of the actions taken by SelectionRectangle.
478 switch (flag) {
480 // The selection is either cleared, or can no longer be
481 // represented as a rectangle with two selection handles.
482 updateActiveState(false);
483 break;
485 // The selection has changed, but the selection is still
486 // rectangular and without holes.
488 break;
489 default:
490 Q_UNREACHABLE();
491 }
492 });
493}
494
496{
498
499 const bool enabled = q->isEnabled();
501
503 if (m_target && qobject_cast<QQuickScrollView *>(m_target->parentItem())) {
504 // ScrollView allows flicking with touch, but not with mouse. So we do
505 // the same here: you can drag to select with a mouse, but not with touch.
509 } else if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
510 if (enabled && !flickable->isInteractive()) {
513 } else {
516 }
517 } else {
521 }
526 } else {
529 }
530}
531
533{
534 QObject *attachedObject = qmlAttachedPropertiesObject<QQuickSelectionRectangle>(object);
536}
537
538// --------------------------------------------------------
539
542{
544 d->m_tapHandler->setParent(this);
545 d->m_dragHandler->setParent(this);
546
548 d->m_scrollTimer.stop();
549 d->updateSelectionMode();
550 d->updateDraggingState(false);
551 d->updateActiveState(false);
552 });
553}
554
556{
557 return d_func()->m_target;
558}
559
561{
563 if (d->m_target == target)
564 return;
565
566 if (d->m_selectable) {
567 d->m_scrollTimer.stop();
568 d->m_tapHandler->setParent(this);
569 d->m_dragHandler->setParent(this);
570 d->m_target->disconnect(this);
571 d->m_selectable->setCallback(nullptr);
572 }
573
574 d->m_target = target;
575 d->m_selectable = nullptr;
576
577 if (d->m_target) {
578 d->m_selectable = dynamic_cast<QQuickSelectable *>(QObjectPrivate::get(d->m_target.data()));
579 if (!d->m_selectable)
580 qmlWarning(this) << "the assigned target is not supported by the control";
581 }
582
583 if (d->m_selectable) {
584 const auto handlerTarget = d->m_selectable->selectionPointerHandlerTarget();
585 d->m_dragHandler->setParentItem(handlerTarget);
586 d->m_tapHandler->setParentItem(handlerTarget);
587 d->connectToTarget();
588 d->updateSelectionMode();
589 }
590
592}
593
595{
596 return d_func()->m_active;
597}
598
600{
601 return d_func()->m_dragging;
602}
603
605{
606 return d_func()->m_selectionMode;
607}
608
610{
612 if (d->m_selectionMode == selectionMode)
613 return;
614
615 d->m_selectionMode = selectionMode;
616
617 if (d->m_target)
618 d->updateSelectionMode();
619
621}
622
624{
625 return d_func()->m_topLeftHandleDelegate;
626}
627
629{
631 if (d->m_topLeftHandleDelegate == topLeftHandle)
632 return;
633
634 d->m_topLeftHandleDelegate = topLeftHandle;
636}
637
639{
640 return d_func()->m_bottomRightHandleDelegate;
641}
642
644{
646 if (d->m_bottomRightHandleDelegate == bottomRightHandle)
647 return;
648
649 d->m_bottomRightHandleDelegate = bottomRightHandle;
651}
652
657
662
667
669{
670 if (m_control == control)
671 return;
672
673 m_control = control;
675}
676
678{
679 return m_dragging;
680}
681
683{
684 if (m_dragging == dragging)
685 return;
686
687 m_dragging = dragging;
689}
690
692
693#include "moc_qquickselectionrectangle_p.cpp"
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:150
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal & ry() noexcept
Returns a reference to the y coordinate of this point.
Definition qpoint.h:368
constexpr qreal & rx() noexcept
Returns a reference to the x coordinate of this point.
Definition qpoint.h:363
\inmodule QtCore\reentrant
Definition qpoint.h:25
The QQmlComponent class encapsulates a QML component definition.
virtual QObject * beginCreate(QQmlContext *)
Create an object instance from this component, within the specified context.
virtual void completeCreate()
This method provides advanced control over component instance creation.
static QQmlContext * contextForObject(const QObject *)
Returns the QQmlContext for the object, or nullptr if no context has been set.
void interactiveChanged()
Qt::KeyboardModifiers modifiers
void setBlocking(bool blocking)
\qmlproperty bool QtQuick::HoverHandler::blocking
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
Q_INVOKABLE QPointF mapFromItem(const QQuickItem *item, const QPointF &point) const
Maps the given point in item's coordinate system to the equivalent point within this item's coordinat...
virtual Q_INVOKABLE bool contains(const QPointF &point) const
\qmlmethod bool QtQuick::Item::contains(point point)
qreal width
This property holds the width of this item.
Definition qquickitem.h:75
QQuickItem * parentItem() const
void enabledChanged()
qreal height
This property holds the height of this item.
Definition qquickitem.h:76
void setX(qreal)
void setY(qreal)
void setAcceptedDevices(QInputDevice::DeviceTypes acceptedDevices)
\qmlproperty flags PointerDeviceHandler::acceptedDevices
void setParentItem(QQuickItem *p)
void setEnabled(bool enabled)
void setTarget(QQuickItem *target)
virtual void normalizeSelection()=0
virtual QQuickItem * selectionPointerHandlerTarget() const =0
virtual void setSelectionEndPos(const QPointF &pos)=0
virtual void setCallback(std::function< void(CallBackFlag)> func)=0
virtual void clearSelection()=0
virtual QRectF selectionRectangle() const =0
virtual bool startSelection(const QPointF &pos, Qt::KeyboardModifiers modifiers)=0
virtual QSizeF scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step)=0
virtual void setSelectionStartPos(const QPointF &pos)=0
void setControl(QQuickSelectionRectangle *control)
QQuickSelectionRectanglePrivate()
Used to select table cells inside a TableView.
QQuickSelectionRectangle::SelectionMode m_selectionMode
QQuickItem * createHandle(QQmlComponent *delegate, Qt::Corner corner)
QScopedPointer< QQuickItem > m_topLeftHandle
QScopedPointer< QQuickItem > m_bottomRightHandle
QQuickItem * handleUnderPos(const QPointF &pos)
QQuickSelectionRectangleAttached * getAttachedObject(const QObject *object) const
QQuickSelectionRectangle::SelectionMode m_effectiveSelectionMode
void setBottomRightHandle(QQmlComponent *bottomRightHandle)
static QQuickSelectionRectangleAttached * qmlAttachedProperties(QObject *obj)
void setTarget(QQuickItem *target)
QQuickSelectionRectangle(QQuickItem *parent=nullptr)
void setTopLeftHandle(QQmlComponent *topLeftHandle)
void setSelectionMode(SelectionMode selectionMode)
\inmodule QtCore\reentrant
Definition qrect.h:484
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
Definition qsize.h:208
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:332
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:335
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
bool isActive() const
Returns true if the timer is running (pending); otherwise returns false.
Definition qtimer.cpp:167
void stop()
Stops the timer.
Definition qtimer.cpp:267
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
EGLImageKHR int int EGLuint64KHR * modifiers
rect
[4]
Combined button and popup list for selecting options.
@ TopLeftCorner
@ BottomRightCorner
@ SizeFDiagCursor
@ ShiftModifier
@ ControlModifier
@ NoModifier
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLenum target
GLhandleARB obj
[2]
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
static QQuickAttachedPropertyPropagator * attachedObject(const QMetaObject *type, QObject *object, bool create=false)
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:492
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
std::uniform_real_distribution dist(1, 2.5)
[2]
myObject disconnect()
[26]