diff --git a/src/quicktemplates2/qquicksplitview.cpp b/src/quicktemplates2/qquicksplitview.cpp index a3566c56..e62c999f 100644 --- a/src/quicktemplates2/qquicksplitview.cpp +++ b/src/quicktemplates2/qquicksplitview.cpp @@ -916,42 +916,6 @@ void QQuickSplitViewPrivate::updateHandleVisibilities() } } -void QQuickSplitViewPrivate::updateHoveredHandle(QQuickItem *hoveredItem) -{ - Q_Q(QQuickSplitView); - qCDebug(qlcQQuickSplitViewMouse) << "updating hovered handle after" << hoveredItem << "was hovered"; - - const int oldHoveredHandleIndex = m_hoveredHandleIndex; - m_hoveredHandleIndex = m_handleItems.indexOf(hoveredItem); - if (m_hoveredHandleIndex == oldHoveredHandleIndex) - return; - - // First, clear the hovered flag of any previously-hovered handle. - if (oldHoveredHandleIndex != -1) { - QQuickItem *oldHoveredHandle = m_handleItems.at(oldHoveredHandleIndex); - QQuickSplitHandleAttached *oldHoveredHandleAttached = qobject_cast( - qmlAttachedPropertiesObject(oldHoveredHandle, true)); - QQuickSplitHandleAttachedPrivate::get(oldHoveredHandleAttached)->setHovered(false); - qCDebug(qlcQQuickSplitViewMouse) << "handle item at index" << oldHoveredHandleIndex << "is no longer hovered"; - } - - if (m_hoveredHandleIndex != -1) { - QQuickSplitHandleAttached *handleAttached = qobject_cast( - qmlAttachedPropertiesObject(hoveredItem, true)); - QQuickSplitHandleAttachedPrivate::get(handleAttached)->setHovered(true); - qCDebug(qlcQQuickSplitViewMouse) << "handle item at index" << m_hoveredHandleIndex << "is now hovered"; - } else { - qCDebug(qlcQQuickSplitViewMouse) << "either there is no hovered item or" << hoveredItem << "is not a handle"; - } - -#if QT_CONFIG(cursor) - if (m_hoveredHandleIndex != -1) - q->setCursor(m_orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor); - else - q->setCursor(Qt::ArrowCursor); -#endif -} - void QQuickSplitViewPrivate::setResizing(bool resizing) { Q_Q(QQuickSplitView); @@ -981,7 +945,7 @@ void QQuickSplitViewPrivate::handlePress(const QPointF &point) Q_Q(QQuickSplitView); QQuickContainerPrivate::handlePress(point); - QQuickItem *pressedItem = q->childAt(point.x(), point.y()); + QQuickItem *pressedItem = handleAt(point); const int pressedHandleIndex = m_handleItems.indexOf(pressedItem); if (pressedHandleIndex != -1) { m_pressedHandleIndex = pressedHandleIndex; @@ -1062,6 +1026,13 @@ void QQuickSplitViewPrivate::handleRelease(const QPointF &point) m_leftOrTopItemSizeBeforePress = 0.0; m_rightOrBottomItemSizeBeforePress = 0.0; q->setKeepMouseGrab(false); + +#if QT_CONFIG(cursor) + if (m_cursorAlreadyOverridden) { + QGuiApplication::restoreOverrideCursor(); + m_cursorAlreadyOverridden = false; + } +#endif } void QQuickSplitViewPrivate::itemVisibilityChanged(QQuickItem *item) @@ -1117,6 +1088,8 @@ QQuickSplitView::QQuickSplitView(QQuickItem *parent) setAcceptedMouseButtons(Qt::LeftButton); setFiltersChildMouseEvents(true); + + setAcceptHoverEvents(true); } QQuickSplitView::QQuickSplitView(QQuickSplitViewPrivate &dd, QQuickItem *parent) @@ -1127,6 +1100,8 @@ QQuickSplitView::QQuickSplitView(QQuickSplitViewPrivate &dd, QQuickItem *parent) setAcceptedMouseButtons(Qt::LeftButton); setFiltersChildMouseEvents(true); + + setAcceptHoverEvents(true); } QQuickSplitView::~QQuickSplitView() @@ -1237,6 +1212,27 @@ void QQuickSplitView::setHandle(QQmlComponent *handle) emit handleChanged(); } +/*! + \qmlproperty bool QtQuick.Controls::SplitView::handleOversize + \readonly + + This property adds extra space for handle input events +*/ +int QQuickSplitView::handleOversize() const +{ + Q_D(const QQuickSplitView); + return d->m_handleOversize; +} + +void QQuickSplitView::setHandleOversize(int size) +{ + Q_D(QQuickSplitView); + if (d->m_handleOversize != size) { + d->m_handleOversize = size; + emit handleOversizeChanged(); + } +} + bool QQuickSplitView::isContent(QQuickItem *item) const { Q_D(const QQuickSplitView); @@ -1249,6 +1245,92 @@ bool QQuickSplitView::isContent(QQuickItem *item) const return !d->m_handleItems.contains(item); } +QQuickItem* QQuickSplitViewPrivate::handleAt(const QPointF& testPoint) +{ + Q_Q(QQuickSplitView); + + const QVector children = m_handleItems; + + for (int i = children.count()-1; i >= 0; --i) { + QQuickItem *child = children.at(i); + // Map coordinates to the child element's coordinate space + QPointF point = q->mapToItem(child, testPoint); + + qreal oversizeX = m_orientation == Qt::Horizontal ? m_handleOversize : 0; + qreal oversizeY = m_orientation == Qt::Horizontal ? 0 : m_handleOversize; + + if (child->isVisible() + && point.x() + oversizeX >= 0 + && child->width() + oversizeX > point.x() + && point.y() + oversizeY >= 0 + && child->height() + oversizeY > point.y()) + return child; + } + return nullptr; +} + +void QQuickSplitViewPrivate::hoverMoveHandler(const QPointF& point) +{ + QQuickItem *hoveredItem = handleAt(point); + if (!hoveredItem) { + // No handle is hovered. + hoverLeaveHandler(); + } else { + // A child item of ours is hovered. + + // First, clear the hovered flag of any previously-hovered handle. + if (m_hoveredHandleIndex != -1) { + QQuickItem *oldHoveredHandle = m_handleItems.at(m_hoveredHandleIndex); + QQuickSplitHandleAttached *handleAttached = qobject_cast( + qmlAttachedPropertiesObject(oldHoveredHandle, true)); + QQuickSplitHandleAttachedPrivate::get(handleAttached)->setHovered(false); + } + + // Now check if the newly hovered item is actually a handle. + const int hoveredHandleIndex = m_handleItems.indexOf(hoveredItem); + if (hoveredHandleIndex == -1) + return; + + // It's a handle, so it's now hovered. + m_hoveredHandleIndex = hoveredHandleIndex; + + QQuickSplitHandleAttached *handleAttached = qobject_cast( + qmlAttachedPropertiesObject(hoveredItem, true)); + QQuickSplitHandleAttachedPrivate::get(handleAttached)->setHovered(true); + +#if QT_CONFIG(cursor) + if (!m_cursorAlreadyOverridden) { + QGuiApplication::setOverrideCursor(m_orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor); + m_cursorAlreadyOverridden = true; + } +#endif + + qCDebug(qlcQQuickSplitViewMouse) << "handle item at index" << m_hoveredHandleIndex << "is now hovered"; + } +} + +void QQuickSplitViewPrivate::hoverLeaveHandler() +{ + if (m_hoveredHandleIndex != -1) { + // The previously-hovered handle is no longer hovered. + QQuickItem *oldHoveredHandle = m_handleItems.at(m_hoveredHandleIndex); + QQuickSplitHandleAttached *handleAttached = qobject_cast( + qmlAttachedPropertiesObject(oldHoveredHandle, true)); + QQuickSplitHandleAttachedPrivate::get(handleAttached)->setHovered(false); + } + + qCDebug(qlcQQuickSplitViewMouse) << "handle item at index" << m_hoveredHandleIndex << "is no longer hovered"; + + m_hoveredHandleIndex = -1; + +#if QT_CONFIG(cursor) + if (m_cursorAlreadyOverridden) { + QGuiApplication::restoreOverrideCursor(); + m_cursorAlreadyOverridden = false; + } +#endif +} + QQuickSplitViewAttached *QQuickSplitView::qmlAttachedProperties(QObject *object) { return new QQuickSplitViewAttached(object); @@ -1384,27 +1466,72 @@ void QQuickSplitView::componentComplete() d->updatePolish(); } +void QQuickSplitView::mouseUngrabEvent() { + Q_D(QQuickSplitView); + d->hoverLeaveHandler(); + + QQuickContainer::mouseUngrabEvent(); +} + +void QQuickSplitView::hoverLeaveEvent(QHoverEvent *event) +{ + Q_D(QQuickSplitView); + d->hoverLeaveHandler(); + QQuickContainer::hoverLeaveEvent(event); +} + void QQuickSplitView::hoverMoveEvent(QHoverEvent *event) { Q_D(QQuickSplitView); QQuickContainer::hoverMoveEvent(event); - - QQuickItem *hoveredItem = childAt(event->pos().x(), event->pos().y()); - d->updateHoveredHandle(hoveredItem); + d->hoverMoveHandler(event->pos()); } -bool QQuickSplitView::childMouseEventFilter(QQuickItem *item, QEvent *event) -{ +bool QQuickSplitView::childMouseEventFilter(QQuickItem *item, QEvent *event) { + Q_D(QQuickSplitView); - qCDebug(qlcQQuickSplitViewMouse) << "childMouseEventFilter called with" << item << event; - if (event->type() != QEvent::HoverEnter) - return false; - // If a child item received a hover enter event, then it means our handle is no longer hovered. - // Handles should be purely visual and not accept hover events, - // so we should never get hover events for them here. - d->updateHoveredHandle(nullptr); - return false; + if (event->type() == QEvent::HoverMove) { + QHoverEvent* hoverEvent = static_cast(event); + QPointF point = mapFromItem(item, hoverEvent->pos()); + d->hoverMoveHandler(point); + if (d->handleAt(point)) { + return true; + } + } + + else if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent* mouseEvent = static_cast(event); + QPointF point = mapFromItem(item, mouseEvent->pos()); + d->handlePress(point); + if (d->handleAt(point)) { + return true; + } + } + + else if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent* mouseEvent = static_cast(event); + QPointF point = mapFromItem(item, mouseEvent->pos()); + d->handleRelease(point); + if (d->handleAt(point)) { + return true; + } + } + + else if (event->type() == QEvent::MouseMove) { + QMouseEvent* mouseEvent = static_cast(event); + QPointF point = mapFromItem(item, mouseEvent->pos()); + d->handleMove(point); + if (d->handleAt(point) && d->m_pressedHandleIndex != -1) { + return true; + } + } + + if (d->m_pressedHandleIndex != -1) { + return true; + } + + return QQuickContainer::childMouseEventFilter(item, event); } void QQuickSplitView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) diff --git a/src/quicktemplates2/qquicksplitview_p.h b/src/quicktemplates2/qquicksplitview_p.h index 2fa15588..f128d67b 100644 --- a/src/quicktemplates2/qquicksplitview_p.h +++ b/src/quicktemplates2/qquicksplitview_p.h @@ -65,6 +65,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitView : public QQuickContainer Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL) Q_PROPERTY(bool resizing READ isResizing NOTIFY resizingChanged) Q_PROPERTY(QQmlComponent *handle READ handle WRITE setHandle NOTIFY handleChanged FINAL) + Q_PROPERTY(int handleOversize READ handleOversize WRITE setHandleOversize NOTIFY handleOversizeChanged) public: explicit QQuickSplitView(QQuickItem *parent = nullptr); @@ -78,6 +79,9 @@ public: QQmlComponent *handle(); void setHandle(QQmlComponent *handle); + int handleOversize() const; + void setHandleOversize(int size); + bool isContent(QQuickItem *item) const override; static QQuickSplitViewAttached *qmlAttachedProperties(QObject *object); @@ -93,10 +97,13 @@ Q_SIGNALS: void orientationChanged(); void resizingChanged(); void handleChanged(); + void handleOversizeChanged(); protected: QQuickSplitView(QQuickSplitViewPrivate &dd, QQuickItem *parent); + void mouseUngrabEvent() override; + void hoverLeaveEvent(QHoverEvent *event) override; void componentComplete() override; void hoverMoveEvent(QHoverEvent *event) override; bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; diff --git a/src/quicktemplates2/qquicksplitview_p_p.h b/src/quicktemplates2/qquicksplitview_p_p.h index 2430eac1..95b9863d 100644 --- a/src/quicktemplates2/qquicksplitview_p_p.h +++ b/src/quicktemplates2/qquicksplitview_p_p.h @@ -74,9 +74,12 @@ public: void resizeHandle(QQuickItem *handleItem); void resizeHandles(); void updateHandleVisibilities(); - void updateHoveredHandle(QQuickItem *hoveredItem); void setResizing(bool resizing); + QQuickItem* handleAt(const QPointF& testPoint); + void hoverMoveHandler(const QPointF& point); + void hoverLeaveHandler(); + bool isHorizontal() const; qreal accumulatedSize(int firstIndex, int lastIndex) const; @@ -122,6 +125,8 @@ public: bool m_layingOut = false; bool m_ignoreNextLayoutRequest = false; bool m_resizing = false; + bool m_cursorAlreadyOverridden = false; + int m_handleOversize = 0; }; class QQuickSplitViewAttachedPrivate : public QObjectPrivate