Qt 6.x
The Qt SDK
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 const auto modifiers = m_tapHandler->point().modifiers();
182 return;
183
184 updateActiveState(false);
185 });
186
188 if (!m_tapHandler->isPressed())
189 return;
191 return;
192
194 const auto modifiers = m_tapHandler->point().modifiers();
196 return;
197
199 // Extend the existing selection towards the pressed cell
200 if (!m_active)
201 return;
204 updateActiveState(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
216 return;
220 updateActiveState(true);
221 } else if (modifiers == Qt::NoModifier) {
222 // Don't select any cell
223 updateActiveState(false);
224 }
225 });
226
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
240 // Extend the existing selection towards the pressed cell
241 if (!m_active)
242 return;
245 updateActiveState(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).
251 return;
255 updateActiveState(true);
256 } else if (modifiers == Qt::NoModifier) {
257 // Select a single cell
260 return;
264 updateActiveState(true);
265 }
266 });
267
270 const QPointF startPos = m_dragHandler->centroid().pressPosition();
271 const QPointF dragPos = m_dragHandler->centroid().position();
272 const auto modifiers = m_dragHandler->centroid().modifiers();
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.
280 if (!m_active)
281 return;
282 } else {
283 if (!m_selectable->startSelection(startPos))
284 return;
286 }
288 m_draggedHandle = nullptr;
290 updateActiveState(true);
292 } else {
295 updateDraggingState(false);
296 }
297 });
298
300 if (!m_dragging)
301 return;
306 });
307}
308
310{
313 return;
314
316 if (!dist.isNull())
318}
319
321{
322 const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
323 if (m_topLeftHandle) {
324 const QPointF localPos = m_topLeftHandle->mapFromItem(handlerTarget, pos);
325 if (m_topLeftHandle->contains(localPos))
326 return m_topLeftHandle.data();
327 }
328
330 const QPointF localPos = m_bottomRightHandle->mapFromItem(handlerTarget, pos);
331 if (m_bottomRightHandle->contains(localPos))
332 return m_bottomRightHandle.data();
333 }
334
335 return nullptr;
336}
337
339{
340 if (dragging != m_dragging) {
341 m_dragging = dragging;
342 emit q_func()->draggingChanged();
343 }
344
345 if (auto attached = getAttachedObject(m_draggedHandle))
346 attached->setDragging(dragging);
347}
348
350{
351 if (active == m_active)
352 return;
353
354 m_active = active;
355
356 if (const auto tableview = qobject_cast<QQuickTableView *>(m_target)) {
357 if (active) {
358 // If the position of rows and columns changes, we'll need to reposition the handles
359 connect(tableview, &QQuickTableView::layoutChanged, this, &QQuickSelectionRectanglePrivate::updateHandles);
360 } else {
361 disconnect(tableview, &QQuickTableView::layoutChanged, this, &QQuickSelectionRectanglePrivate::updateHandles);
362 }
363 }
364
365 emit q_func()->activeChanged();
366}
367
369{
371
372 // Incubate the handle
375 const auto handlerTarget = m_selectable->selectionPointerHandlerTarget();
376 handleItem->setParentItem(handlerTarget);
377 if (auto attached = getAttachedObject(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);
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
405
406 QObject::connect(dragHandler, &QQuickDragHandler::activeChanged, [this, corner, handleItem, dragHandler]() {
407 if (dragHandler->active()) {
408 const QPointF localPos = dragHandler->centroid().position();
409 const QPointF pos = handleItem->mapToItem(handleItem->parentItem(), 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(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(false);
425#if QT_CONFIG(cursor)
426 QGuiApplication::restoreOverrideCursor();
427#endif
428 }
429 });
430
431 QObject::connect(dragHandler, &QQuickDragHandler::centroidChanged, [this, corner, handleItem, dragHandler]() {
432 if (!m_dragging)
433 return;
434
435 const QPointF localPos = dragHandler->centroid().position();
436 const QPointF pos = handleItem->mapToItem(handleItem->parentItem(), localPos);
437 if (corner == Qt::TopLeftCorner)
439 else
441
444 });
445
446 return handleItem;
447}
448
450{
452
455
458
459 if (m_topLeftHandle) {
462 }
463
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
471{
472 // To support QuickSelectionRectangle::Auto, we need to listen for changes to the target
473 if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
475 }
476}
477
479{
481
482 const bool enabled = q->isEnabled();
484
486 if (qobject_cast<QQuickScrollView *>(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.
492 } else if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
493 if (enabled && !flickable->isInteractive()) {
496 } else {
499 }
500 } else {
504 }
509 } else {
512 }
513}
514
516{
517 QObject *attachedObject = qmlAttachedPropertiesObject<QQuickSelectionRectangle>(object);
519}
520
521// --------------------------------------------------------
522
525{
527 d->m_tapHandler->setParent(this);
528 d->m_dragHandler->setParent(this);
529
531 d->m_scrollTimer.stop();
532 d->updateSelectionMode();
533 d->updateDraggingState(false);
534 d->updateActiveState(false);
535 });
536}
537
539{
540 return d_func()->m_target;
541}
542
544{
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(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(d->m_target.data()));
561 if (!d->m_selectable)
562 qmlWarning(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
574}
575
577{
578 return d_func()->m_active;
579}
580
582{
583 return d_func()->m_dragging;
584}
585
587{
588 return d_func()->m_selectionMode;
589}
590
592{
594 if (d->m_selectionMode == selectionMode)
595 return;
596
597 d->m_selectionMode = selectionMode;
598
599 if (d->m_target)
600 d->updateSelectionMode();
601
603}
604
606{
607 return d_func()->m_topLeftHandleDelegate;
608}
609
611{
613 if (d->m_topLeftHandleDelegate == topLeftHandle)
614 return;
615
616 d->m_topLeftHandleDelegate = topLeftHandle;
618}
619
621{
622 return d_func()->m_bottomRightHandleDelegate;
623}
624
626{
628 if (d->m_bottomRightHandleDelegate == bottomRightHandle)
629 return;
630
631 d->m_bottomRightHandleDelegate = bottomRightHandle;
633}
634
636{
638}
639
641 : QObject(parent)
642{
643}
644
646{
647 return m_control;
648}
649
651{
652 if (m_control == control)
653 return;
654
655 m_control = control;
657}
658
660{
661 return m_dragging;
662}
663
665{
666 if (m_dragging == dragging)
667 return;
668
669 m_dragging = dragging;
671}
672
674
675#include "moc_qquickselectionrectangle_p.cpp"
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:153
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:298
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal & ry() noexcept
Returns a reference to the y coordinate of this point.
Definition qpoint.h:358
constexpr qreal & rx() noexcept
Returns a reference to the x coordinate of this point.
Definition qpoint.h:353
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:64
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...
Q_INVOKABLE QPointF mapToItem(const QQuickItem *item, const QPointF &point) const
Maps the given point in this item's coordinate system to the equivalent point within item's coordinat...
void setParentItem(QQuickItem *parent)
qreal z
\qmlproperty real QtQuick::Item::z
Definition qquickitem.h:75
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:76
QQuickItem * parentItem() const
void enabledChanged()
qreal height
This property holds the height of this item.
Definition qquickitem.h:77
void setZ(qreal)
void setX(qreal)
void setY(qreal)
void setAcceptedDevices(QInputDevice::DeviceTypes acceptedDevices)
\qmlproperty flags PointerDeviceHandler::acceptedDevices
void setGrabPermissions(GrabPermissions grabPermissions)
void setParentItem(QQuickItem *p)
void setEnabled(bool enabled)
void setTarget(QQuickItem *target)
virtual void normalizeSelection()=0
virtual bool startSelection(const QPointF &pos)=0
virtual QQuickItem * selectionPointerHandlerTarget() const =0
virtual void setSelectionEndPos(const QPointF &pos)=0
virtual void clearSelection()=0
virtual QRectF selectionRectangle() const =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)
void setGesturePolicy(GesturePolicy gesturePolicy)
\qmlproperty enumeration QtQuick::TapHandler::gesturePolicy
void tapped(QEventPoint eventPoint, Qt::MouseButton)
\inmodule QtCore\reentrant
Definition qrect.h:483
QRectF normalized() const noexcept
Returns a normalized rectangle; i.e., a rectangle that has a non-negative width and height.
Definition qrect.cpp:1514
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:207
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:321
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:324
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:208
bool isActive() const
Returns true if the timer is running (pending); otherwise returns false.
Definition qtimer.cpp:156
void stop()
Stops the timer.
Definition qtimer.cpp:226
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:483
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
std::uniform_real_distribution dist(1, 2.5)
[2]
myObject disconnect()
[26]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent