From: Carl Schwan Date: Fri, 12 Nov 2021 20:02:58 +0000 (+0100) Subject: Add WheelHandler to the Search result list too X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~12^2~18^2~96^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=38ac585e7ca2576888df18f182aea7862b276752;p=nextcloud-desktop.git Add WheelHandler to the Search result list too Signed-off-by: Carl Schwan --- diff --git a/src/3rdparty/kirigami/wheelhandler.cpp b/src/3rdparty/kirigami/wheelhandler.cpp new file mode 100644 index 000000000..ce21e51ac --- /dev/null +++ b/src/3rdparty/kirigami/wheelhandler.cpp @@ -0,0 +1,280 @@ +/* + * SPDX-FileCopyrightText: 2019 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "wheelhandler.h" +#include +#include +#include + +class GlobalWheelFilterSingleton +{ +public: + GlobalWheelFilter self; +}; + +Q_GLOBAL_STATIC(GlobalWheelFilterSingleton, privateGlobalWheelFilterSelf) + +GlobalWheelFilter::GlobalWheelFilter(QObject *parent) + : QObject(parent) +{ +} + +GlobalWheelFilter::~GlobalWheelFilter() = default; + +GlobalWheelFilter *GlobalWheelFilter::self() +{ + return &privateGlobalWheelFilterSelf()->self; +} + +void GlobalWheelFilter::setItemHandlerAssociation(QQuickItem *item, WheelHandler *handler) +{ + if (!m_handlersForItem.contains(handler->target())) { + handler->target()->installEventFilter(this); + } + m_handlersForItem.insert(item, handler); + + connect(item, &QObject::destroyed, this, [this](QObject *obj) { + auto item = static_cast(obj); + m_handlersForItem.remove(item); + }); + + connect(handler, &QObject::destroyed, this, [this](QObject *obj) { + auto handler = static_cast(obj); + removeItemHandlerAssociation(handler->target(), handler); + }); +} + +void GlobalWheelFilter::removeItemHandlerAssociation(QQuickItem *item, WheelHandler *handler) +{ + if (!item || !handler) { + return; + } + m_handlersForItem.remove(item, handler); + if (!m_handlersForItem.contains(item)) { + item->removeEventFilter(this); + } +} + +bool GlobalWheelFilter::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::Wheel) { + auto item = qobject_cast(watched); + if (!item || !item->isEnabled()) { + return QObject::eventFilter(watched, event); + } + auto we = static_cast(event); + m_wheelEvent.initializeFromEvent(we); + + bool shouldBlock = false; + bool shouldScrollFlickable = false; + + for (auto *handler : m_handlersForItem.values(item)) { + if (handler->m_blockTargetWheel) { + shouldBlock = true; + } + if (handler->m_scrollFlickableTarget) { + shouldScrollFlickable = true; + } + emit handler->wheel(&m_wheelEvent); + } + + if (shouldScrollFlickable && !m_wheelEvent.isAccepted()) { + manageWheel(item, we); + } + + if (shouldBlock) { + return true; + } + } + return QObject::eventFilter(watched, event); +} + +void GlobalWheelFilter::manageWheel(QQuickItem *target, QWheelEvent *event) +{ + // Duck typing: accept everyhint that has all the properties we need + if (target->metaObject()->indexOfProperty("contentX") == -1 + || target->metaObject()->indexOfProperty("contentY") == -1 + || target->metaObject()->indexOfProperty("contentWidth") == -1 + || target->metaObject()->indexOfProperty("contentHeight") == -1 + || target->metaObject()->indexOfProperty("topMargin") == -1 + || target->metaObject()->indexOfProperty("bottomMargin") == -1 + || target->metaObject()->indexOfProperty("leftMargin") == -1 + || target->metaObject()->indexOfProperty("rightMargin") == -1 + || target->metaObject()->indexOfProperty("originX") == -1 + || target->metaObject()->indexOfProperty("originY") == -1) { + return; + } + + qreal contentWidth = target->property("contentWidth").toReal(); + qreal contentHeight = target->property("contentHeight").toReal(); + qreal contentX = target->property("contentX").toReal(); + qreal contentY = target->property("contentY").toReal(); + qreal topMargin = target->property("topMargin").toReal(); + qreal bottomMargin = target->property("bottomMargin").toReal(); + qreal leftMargin = target->property("leftMaring").toReal(); + qreal rightMargin = target->property("rightMargin").toReal(); + qreal originX = target->property("originX").toReal(); + qreal originY = target->property("originY").toReal(); + + // Scroll Y + if (contentHeight > target->height()) { + + int y = event->pixelDelta().y() != 0 ? event->pixelDelta().y() : event->angleDelta().y() / 8; + + //if we don't have a pixeldelta, apply the configured mouse wheel lines + if (!event->pixelDelta().y()) { + y *= 3; // Magic copied value from Kirigami::Settings + } + + // Scroll one page regardless of delta: + if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) { + if (y > 0) { + y = target->height(); + } else if (y < 0) { + y = -target->height(); + } + } + + qreal minYExtent = topMargin - originY; + qreal maxYExtent = target->height() - (contentHeight + bottomMargin + originY); + + target->setProperty("contentY", qMin(-maxYExtent, qMax(-minYExtent, contentY - y))); + } + + //Scroll X + if (contentWidth > target->width()) { + + int x = event->pixelDelta().x() != 0 ? event->pixelDelta().x() : event->angleDelta().x() / 8; + + // Special case: when can't scroll vertically, scroll horizontally with vertical wheel as well + if (x == 0 && contentHeight <= target->height()) { + x = event->pixelDelta().y() != 0 ? event->pixelDelta().y() : event->angleDelta().y() / 8; + } + + //if we don't have a pixeldelta, apply the configured mouse wheel lines + if (!event->pixelDelta().x()) { + x *= 3; // Magic copied value from Kirigami::Settings + } + + // Scroll one page regardless of delta: + if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) { + if (x > 0) { + x = target->width(); + } else if (x < 0) { + x = -target->width(); + } + } + + qreal minXExtent = leftMargin - originX; + qreal maxXExtent = target->width() - (contentWidth + rightMargin + originX); + + target->setProperty("contentX", qMin(-maxXExtent, qMax(-minXExtent, contentX - x))); + } + + //this is just for making the scrollbar + target->metaObject()->invokeMethod(target, "flick", Q_ARG(double, 0), Q_ARG(double, 1)); + target->metaObject()->invokeMethod(target, "cancelFlick"); +} + + +//////////////////////////// +KirigamiWheelEvent::KirigamiWheelEvent(QObject *parent) + : QObject(parent) +{} + +KirigamiWheelEvent::~KirigamiWheelEvent() = default; + +void KirigamiWheelEvent::initializeFromEvent(QWheelEvent *event) +{ + m_x = event->position().x(); + m_y = event->position().y(); + m_angleDelta = event->angleDelta(); + m_pixelDelta = event->pixelDelta(); + m_buttons = event->buttons(); + m_modifiers = event->modifiers(); + m_accepted = false; + m_inverted = event->inverted(); +} + +qreal KirigamiWheelEvent::x() const +{ + return m_x; +} + +qreal KirigamiWheelEvent::y() const +{ + return m_y; +} + +QPointF KirigamiWheelEvent::angleDelta() const +{ + return m_angleDelta; +} + +QPointF KirigamiWheelEvent::pixelDelta() const +{ + return m_pixelDelta; +} + +int KirigamiWheelEvent::buttons() const +{ + return m_buttons; +} + +int KirigamiWheelEvent::modifiers() const +{ + return m_modifiers; +} + +bool KirigamiWheelEvent::inverted() const +{ + return m_inverted; +} + +bool KirigamiWheelEvent::isAccepted() +{ + return m_accepted; +} + +void KirigamiWheelEvent::setAccepted(bool accepted) +{ + m_accepted = accepted; +} + + +/////////////////////////////// + +WheelHandler::WheelHandler(QObject *parent) + : QObject(parent) +{ +} + +WheelHandler::~WheelHandler() = default; + +QQuickItem *WheelHandler::target() const +{ + return m_target; +} + +void WheelHandler::setTarget(QQuickItem *target) +{ + if (m_target == target) { + return; + } + + if (m_target) { + GlobalWheelFilter::self()->removeItemHandlerAssociation(m_target, this); + } + + m_target = target; + + GlobalWheelFilter::self()->setItemHandlerAssociation(target, this); + + emit targetChanged(); +} + + +#include "moc_wheelhandler.cpp" diff --git a/src/3rdparty/kirigami/wheelhandler.h b/src/3rdparty/kirigami/wheelhandler.h new file mode 100644 index 000000000..114869465 --- /dev/null +++ b/src/3rdparty/kirigami/wheelhandler.h @@ -0,0 +1,213 @@ +/* + * SPDX-FileCopyrightText: 2019 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#pragma once + +#include +#include +#include +#include + +class QWheelEvent; + +class WheelHandler; + +/** + * Describes the mouse wheel event + */ +class KirigamiWheelEvent : public QObject +{ + Q_OBJECT + + /** + * x: real + * + * X coordinate of the mouse pointer + */ + Q_PROPERTY(qreal x READ x CONSTANT) + + /** + * y: real + * + * Y coordinate of the mouse pointer + */ + Q_PROPERTY(qreal y READ y CONSTANT) + + /** + * angleDelta: point + * + * The distance the wheel is rotated in degrees. + * The x and y coordinates indicate the horizontal and vertical wheels respectively. + * A positive value indicates it was rotated up/right, negative, bottom/left + * This value is more likely to be set in traditional mice. + */ + Q_PROPERTY(QPointF angleDelta READ angleDelta CONSTANT) + + /** + * pixelDelta: point + * + * provides the delta in screen pixels available on high resolution trackpads + */ + Q_PROPERTY(QPointF pixelDelta READ pixelDelta CONSTANT) + + /** + * buttons: int + * + * it contains an OR combination of the buttons that were pressed during the wheel, they can be: + * Qt.LeftButton, Qt.MiddleButton, Qt.RightButton + */ + Q_PROPERTY(int buttons READ buttons CONSTANT) + + /** + * modifiers: int + * + * Keyboard mobifiers that were pressed during the wheel event, such as: + * Qt.NoModifier (default, no modifiers) + * Qt.ControlModifier + * Qt.ShiftModifier + * ... + */ + Q_PROPERTY(int modifiers READ modifiers CONSTANT) + + /** + * inverted: bool + * + * Whether the delta values are inverted + * On some platformsthe returned delta are inverted, so positive values would mean bottom/left + */ + Q_PROPERTY(bool inverted READ inverted CONSTANT) + + /** + * accepted: bool + * + * If set, the event shouldn't be managed anymore, + * for instance it can be used to block the handler to manage the scroll of a view on some scenarios + * @code + * // This handler handles automatically the scroll of + * // flickableItem, unless Ctrl is pressed, in this case the + * // app has custom code to handle Ctrl+wheel zooming + * Kirigami.WheelHandler { + * target: flickableItem + * blockTargetWheel: true + * scrollFlickableTarget: true + * onWheel: { + * if (wheel.modifiers & Qt.ControlModifier) { + * wheel.accepted = true; + * // Handle scaling of the view + * } + * } + * } + * @endcode + * + */ + Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + +public: + KirigamiWheelEvent(QObject *parent = nullptr); + ~KirigamiWheelEvent() override; + + void initializeFromEvent(QWheelEvent *event); + + qreal x() const; + qreal y() const; + QPointF angleDelta() const; + QPointF pixelDelta() const; + int buttons() const; + int modifiers() const; + bool inverted() const; + bool isAccepted(); + void setAccepted(bool accepted); + +private: + qreal m_x = 0; + qreal m_y = 0; + QPointF m_angleDelta; + QPointF m_pixelDelta; + Qt::MouseButtons m_buttons = Qt::NoButton; + Qt::KeyboardModifiers m_modifiers = Qt::NoModifier; + bool m_inverted = false; + bool m_accepted = false; +}; + +class GlobalWheelFilter : public QObject +{ + Q_OBJECT + +public: + GlobalWheelFilter(QObject *parent = nullptr); + ~GlobalWheelFilter() override; + + static GlobalWheelFilter *self(); + + void setItemHandlerAssociation(QQuickItem *item, WheelHandler *handler); + void removeItemHandlerAssociation(QQuickItem *item, WheelHandler *handler); + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + void manageWheel(QQuickItem *target, QWheelEvent *wheel); + + QMultiHash m_handlersForItem; + KirigamiWheelEvent m_wheelEvent; +}; + + + +/** + * This class intercepts the mouse wheel events of its target, and gives them to the user code as a signal, which can be used for custom mouse wheel management code. + * The handler can block completely the wheel events from its target, and if it's a Flickable, it can automatically handle scrolling on it + */ +class WheelHandler : public QObject +{ + Q_OBJECT + + /** + * target: Item + * + * The target we want to manage wheel events. + * We will receive wheel() signals every time the user moves + * the mouse wheel (or scrolls with the touchpad) on top + * of that item. + */ + Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged) + + /** + * blockTargetWheel: bool + * + * If true, the target won't receive any wheel event at all (default true) + */ + Q_PROPERTY(bool blockTargetWheel MEMBER m_blockTargetWheel NOTIFY blockTargetWheelChanged) + + /** + * scrollFlickableTarget: bool + * If this property is true and the target is a Flickable, wheel events will cause the Flickable to scroll (default true) + */ + Q_PROPERTY(bool scrollFlickableTarget MEMBER m_scrollFlickableTarget NOTIFY scrollFlickableTargetChanged) + +public: + explicit WheelHandler(QObject *parent = nullptr); + ~WheelHandler() override; + + QQuickItem *target() const; + void setTarget(QQuickItem *target); + +Q_SIGNALS: + void targetChanged(); + void blockTargetWheelChanged(); + void scrollFlickableTargetChanged(); + void wheel(KirigamiWheelEvent *wheel); + +private: + QPointer m_target; + bool m_blockTargetWheel = true; + bool m_scrollFlickableTarget = true; + KirigamiWheelEvent m_wheelEvent; + + friend class GlobalWheelFilter; +}; + + diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index cc65cd6ff..794f44f27 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -134,7 +134,6 @@ set(client_SRCS tray/usermodel.cpp tray/notificationhandler.cpp tray/notificationcache.cpp - tray/wheelhandler.cpp creds/credentialsfactory.cpp creds/httpcredentialsgui.cpp creds/oauth.cpp @@ -207,6 +206,7 @@ set(3rdparty_SRC ../3rdparty/qtsingleapplication/qtsingleapplication.cpp ../3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp ../3rdparty/kmessagewidget/kmessagewidget.cpp + ../3rdparty/kirigami/wheelhandler.cpp ) if(NOT WIN32) @@ -376,6 +376,7 @@ target_include_directories(nextcloudCore PUBLIC ${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator ${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile + ${CMAKE_SOURCE_DIR}/src/3rdparty/kirigami ${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication ${CMAKE_SOURCE_DIR}/src/3rdparty/kmessagewidget ${CMAKE_CURRENT_BINARY_DIR} diff --git a/src/gui/systray.cpp b/src/gui/systray.cpp index 4158809de..475367486 100644 --- a/src/gui/systray.cpp +++ b/src/gui/systray.cpp @@ -19,7 +19,7 @@ #include "common/utility.h" #include "tray/svgimageprovider.h" #include "tray/usermodel.h" -#include "tray/wheelhandler.h" +#include "wheelhandler.h" #include "tray/unifiedsearchresultimageprovider.h" #include "configfile.h" diff --git a/src/gui/tray/ActivityList.qml b/src/gui/tray/ActivityList.qml index 795b3dc77..e1ab4f818 100644 --- a/src/gui/tray/ActivityList.qml +++ b/src/gui/tray/ActivityList.qml @@ -13,6 +13,7 @@ ScrollView { signal activityItemClicked(int index) contentWidth: availableWidth + padding: 1 ScrollBar.horizontal.policy: ScrollBar.AlwaysOff diff --git a/src/gui/tray/Window.qml b/src/gui/tray/Window.qml index 96528a61f..631097879 100644 --- a/src/gui/tray/Window.qml +++ b/src/gui/tray/Window.qml @@ -651,51 +651,61 @@ Window { iconColor: "#afafaf" } - ListView { - id: unifiedSearchResultsListView + ScrollView { + id: controlRoot + padding: 1 + contentWidth: availableWidth + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + + data: WheelHandler { + target: controlRoot.contentItem + } + visible: unifiedSearchResultsListView.count > 0 + anchors.top: trayWindowUnifiedSearchInputContainer.bottom anchors.left: trayWindowBackground.left anchors.right: trayWindowBackground.right anchors.bottom: trayWindowBackground.bottom - spacing: 4 - visible: count > 0 - clip: true - ScrollBar.vertical: ScrollBar { - id: unifiedSearchResultsListViewScrollbar - } - keyNavigationEnabled: true - - reuseItems: true - - Accessible.role: Accessible.List - Accessible.name: qsTr("Unified search results list") - - model: UserModel.currentUser.unifiedSearchResultsListModel - - delegate: UnifiedSearchResultListItem { - width: unifiedSearchResultsListView.width - height: trayWindowBackground.Style.unifiedSearchItemHeight - isSearchInProgress: unifiedSearchResultsListView.model.isSearchInProgress - textLeftMargin: trayWindowBackground.Style.unifiedSearchResultTextLeftMargin - textRightMargin: trayWindowBackground.Style.unifiedSearchResultTextRightMargin - iconWidth: trayWindowBackground.Style.unifiedSearchResulIconWidth - iconLeftMargin: trayWindowBackground.Style.unifiedSearchResulIconLeftMargin - titleFontSize: trayWindowBackground.Style.unifiedSearchResulTitleFontSize - sublineFontSize: trayWindowBackground.Style.unifiedSearchResulSublineFontSize - titleColor: trayWindowBackground.Style.unifiedSearchResulTitleColor - sublineColor: trayWindowBackground.Style.unifiedSearchResulSublineColor - currentFetchMoreInProgressProviderId: unifiedSearchResultsListView.model.currentFetchMoreInProgressProviderId - fetchMoreTriggerClicked: unifiedSearchResultsListView.model.fetchMoreTriggerClicked - resultClicked: unifiedSearchResultsListView.model.resultClicked - ListView.onPooled: isPooled = true - ListView.onReused: isPooled = false - } + ListView { + id: unifiedSearchResultsListView + spacing: 4 + clip: true + + keyNavigationEnabled: true + + reuseItems: true + + Accessible.role: Accessible.List + Accessible.name: qsTr("Unified search results list") + + model: UserModel.currentUser.unifiedSearchResultsListModel + + delegate: UnifiedSearchResultListItem { + width: unifiedSearchResultsListView.width + height: trayWindowBackground.Style.unifiedSearchItemHeight + isSearchInProgress: unifiedSearchResultsListView.model.isSearchInProgress + textLeftMargin: trayWindowBackground.Style.unifiedSearchResultTextLeftMargin + textRightMargin: trayWindowBackground.Style.unifiedSearchResultTextRightMargin + iconWidth: trayWindowBackground.Style.unifiedSearchResulIconWidth + iconLeftMargin: trayWindowBackground.Style.unifiedSearchResulIconLeftMargin + titleFontSize: trayWindowBackground.Style.unifiedSearchResulTitleFontSize + sublineFontSize: trayWindowBackground.Style.unifiedSearchResulSublineFontSize + titleColor: trayWindowBackground.Style.unifiedSearchResulTitleColor + sublineColor: trayWindowBackground.Style.unifiedSearchResulSublineColor + currentFetchMoreInProgressProviderId: unifiedSearchResultsListView.model.currentFetchMoreInProgressProviderId + fetchMoreTriggerClicked: unifiedSearchResultsListView.model.fetchMoreTriggerClicked + resultClicked: unifiedSearchResultsListView.model.resultClicked + ListView.onPooled: isPooled = true + ListView.onReused: isPooled = false + } - section.property: "providerName" - section.criteria: ViewSection.FullString - section.delegate: UnifiedSearchResultSectionItem { - width: unifiedSearchResultsListView.width + section.property: "providerName" + section.criteria: ViewSection.FullString + section.delegate: UnifiedSearchResultSectionItem { + width: unifiedSearchResultsListView.width + } } } diff --git a/src/gui/tray/wheelhandler.cpp b/src/gui/tray/wheelhandler.cpp deleted file mode 100644 index 1e60e5f20..000000000 --- a/src/gui/tray/wheelhandler.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2019 Marco Martin - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#include "wheelhandler.h" -#include -#include -#include - -class GlobalWheelFilterSingleton -{ -public: - GlobalWheelFilter self; -}; - -Q_GLOBAL_STATIC(GlobalWheelFilterSingleton, privateGlobalWheelFilterSelf) - -GlobalWheelFilter::GlobalWheelFilter(QObject *parent) - : QObject(parent) -{ -} - -GlobalWheelFilter::~GlobalWheelFilter() -{} - -GlobalWheelFilter *GlobalWheelFilter::self() -{ - return &privateGlobalWheelFilterSelf()->self; -} - -void GlobalWheelFilter::setItemHandlerAssociation(QQuickItem *item, WheelHandler *handler) -{ - if (!m_handlersForItem.contains(handler->target())) { - handler->target()->installEventFilter(this); - } - m_handlersForItem.insert(item, handler); - - connect(item, &QObject::destroyed, this, [this](QObject *obj) { - QQuickItem *item = static_cast(obj); - m_handlersForItem.remove(item); - }); - - connect(handler, &QObject::destroyed, this, [this](QObject *obj) { - WheelHandler *handler = static_cast(obj); - removeItemHandlerAssociation(handler->target(), handler); - }); -} - -void GlobalWheelFilter::removeItemHandlerAssociation(QQuickItem *item, WheelHandler *handler) -{ - if (!item || !handler) { - return; - } - m_handlersForItem.remove(item, handler); - if (!m_handlersForItem.contains(item)) { - item->removeEventFilter(this); - } -} - -bool GlobalWheelFilter::eventFilter(QObject *watched, QEvent *event) -{ - if (event->type() == QEvent::Wheel) { - QQuickItem *item = qobject_cast(watched); - if (!item || !item->isEnabled()) { - return QObject::eventFilter(watched, event); - } - QWheelEvent *we = static_cast(event); - m_wheelEvent.initializeFromEvent(we); - - bool shouldBlock = false; - bool shouldScrollFlickable = false; - - for (auto *handler : m_handlersForItem.values(item)) { - if (handler->m_blockTargetWheel) { - shouldBlock = true; - } - if (handler->m_scrollFlickableTarget) { - shouldScrollFlickable = true; - } - emit handler->wheel(&m_wheelEvent); - } - - if (shouldScrollFlickable && !m_wheelEvent.isAccepted()) { - manageWheel(item, we); - } - - if (shouldBlock) { - return true; - } - } - return QObject::eventFilter(watched, event); -} - -void GlobalWheelFilter::manageWheel(QQuickItem *target, QWheelEvent *event) -{ - // Duck typing: accept everyhint that has all the properties we need - if (target->metaObject()->indexOfProperty("contentX") == -1 - || target->metaObject()->indexOfProperty("contentY") == -1 - || target->metaObject()->indexOfProperty("contentWidth") == -1 - || target->metaObject()->indexOfProperty("contentHeight") == -1 - || target->metaObject()->indexOfProperty("topMargin") == -1 - || target->metaObject()->indexOfProperty("bottomMargin") == -1 - || target->metaObject()->indexOfProperty("leftMargin") == -1 - || target->metaObject()->indexOfProperty("rightMargin") == -1 - || target->metaObject()->indexOfProperty("originX") == -1 - || target->metaObject()->indexOfProperty("originY") == -1) { - return; - } - - qreal contentWidth = target->property("contentWidth").toReal(); - qreal contentHeight = target->property("contentHeight").toReal(); - qreal contentX = target->property("contentX").toReal(); - qreal contentY = target->property("contentY").toReal(); - qreal topMargin = target->property("topMargin").toReal(); - qreal bottomMargin = target->property("bottomMargin").toReal(); - qreal leftMargin = target->property("leftMaring").toReal(); - qreal rightMargin = target->property("rightMargin").toReal(); - qreal originX = target->property("originX").toReal(); - qreal originY = target->property("originY").toReal(); - - // Scroll Y - if (contentHeight > target->height()) { - - int y = event->pixelDelta().y() != 0 ? event->pixelDelta().y() : event->angleDelta().y() / 8; - - //if we don't have a pixeldelta, apply the configured mouse wheel lines - if (!event->pixelDelta().y()) { - y *= 3; // Magic copied value from Kirigami::Settings - } - - // Scroll one page regardless of delta: - if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) { - if (y > 0) { - y = target->height(); - } else if (y < 0) { - y = -target->height(); - } - } - - qreal minYExtent = topMargin - originY; - qreal maxYExtent = target->height() - (contentHeight + bottomMargin + originY); - - target->setProperty("contentY", qMin(-maxYExtent, qMax(-minYExtent, contentY - y))); - } - - //Scroll X - if (contentWidth > target->width()) { - - int x = event->pixelDelta().x() != 0 ? event->pixelDelta().x() : event->angleDelta().x() / 8; - - // Special case: when can't scroll vertically, scroll horizontally with vertical wheel as well - if (x == 0 && contentHeight <= target->height()) { - x = event->pixelDelta().y() != 0 ? event->pixelDelta().y() : event->angleDelta().y() / 8; - } - - //if we don't have a pixeldelta, apply the configured mouse wheel lines - if (!event->pixelDelta().x()) { - x *= 3; // Magic copied value from Kirigami::Settings - } - - // Scroll one page regardless of delta: - if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) { - if (x > 0) { - x = target->width(); - } else if (x < 0) { - x = -target->width(); - } - } - - qreal minXExtent = leftMargin - originX; - qreal maxXExtent = target->width() - (contentWidth + rightMargin + originX); - - target->setProperty("contentX", qMin(-maxXExtent, qMax(-minXExtent, contentX - x))); - } - - //this is just for making the scrollbar - target->metaObject()->invokeMethod(target, "flick", Q_ARG(double, 0), Q_ARG(double, 1)); - target->metaObject()->invokeMethod(target, "cancelFlick"); -} - - -//////////////////////////// -KirigamiWheelEvent::KirigamiWheelEvent(QObject *parent) - : QObject(parent) -{} - -KirigamiWheelEvent::~KirigamiWheelEvent() -{} - -void KirigamiWheelEvent::initializeFromEvent(QWheelEvent *event) -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - m_x = event->position().x(); - m_y = event->position().y(); -#else - m_x = event->x(); - m_y = event->y(); -#endif - m_angleDelta = event->angleDelta(); - m_pixelDelta = event->pixelDelta(); - m_buttons = event->buttons(); - m_modifiers = event->modifiers(); - m_accepted = false; - m_inverted = event->inverted(); -} - -qreal KirigamiWheelEvent::x() const -{ - return m_x; -} - -qreal KirigamiWheelEvent::y() const -{ - return m_y; -} - -QPointF KirigamiWheelEvent::angleDelta() const -{ - return m_angleDelta; -} - -QPointF KirigamiWheelEvent::pixelDelta() const -{ - return m_pixelDelta; -} - -int KirigamiWheelEvent::buttons() const -{ - return m_buttons; -} - -int KirigamiWheelEvent::modifiers() const -{ - return m_modifiers; -} - -bool KirigamiWheelEvent::inverted() const -{ - return m_inverted; -} - -bool KirigamiWheelEvent::isAccepted() -{ - return m_accepted; -} - -void KirigamiWheelEvent::setAccepted(bool accepted) -{ - m_accepted = accepted; -} - - -/////////////////////////////// - -WheelHandler::WheelHandler(QObject *parent) - : QObject(parent) -{ -} - -WheelHandler::~WheelHandler() -{ -} - -QQuickItem *WheelHandler::target() const -{ - return m_target; -} - -void WheelHandler::setTarget(QQuickItem *target) -{ - if (m_target == target) { - return; - } - - if (m_target) { - GlobalWheelFilter::self()->removeItemHandlerAssociation(m_target, this); - } - - m_target = target; - - GlobalWheelFilter::self()->setItemHandlerAssociation(target, this); - - emit targetChanged(); -} - - -#include "moc_wheelhandler.cpp" diff --git a/src/gui/tray/wheelhandler.h b/src/gui/tray/wheelhandler.h deleted file mode 100644 index 8e81f51e4..000000000 --- a/src/gui/tray/wheelhandler.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2019 Marco Martin - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#pragma once - -#include -#include -#include -#include - -class QWheelEvent; - -class WheelHandler; - -/** - * Describes the mouse wheel event - */ -class KirigamiWheelEvent : public QObject -{ - Q_OBJECT - - /** - * x: real - * - * X coordinate of the mouse pointer - */ - Q_PROPERTY(qreal x READ x CONSTANT) - - /** - * y: real - * - * Y coordinate of the mouse pointer - */ - Q_PROPERTY(qreal y READ y CONSTANT) - - /** - * angleDelta: point - * - * The distance the wheel is rotated in degrees. - * The x and y coordinates indicate the horizontal and vertical wheels respectively. - * A positive value indicates it was rotated up/right, negative, bottom/left - * This value is more likely to be set in traditional mice. - */ - Q_PROPERTY(QPointF angleDelta READ angleDelta CONSTANT) - - /** - * pixelDelta: point - * - * provides the delta in screen pixels available on high resolution trackpads - */ - Q_PROPERTY(QPointF pixelDelta READ pixelDelta CONSTANT) - - /** - * buttons: int - * - * it contains an OR combination of the buttons that were pressed during the wheel, they can be: - * Qt.LeftButton, Qt.MiddleButton, Qt.RightButton - */ - Q_PROPERTY(int buttons READ buttons CONSTANT) - - /** - * modifiers: int - * - * Keyboard mobifiers that were pressed during the wheel event, such as: - * Qt.NoModifier (default, no modifiers) - * Qt.ControlModifier - * Qt.ShiftModifier - * ... - */ - Q_PROPERTY(int modifiers READ modifiers CONSTANT) - - /** - * inverted: bool - * - * Whether the delta values are inverted - * On some platformsthe returned delta are inverted, so positive values would mean bottom/left - */ - Q_PROPERTY(bool inverted READ inverted CONSTANT) - - /** - * accepted: bool - * - * If set, the event shouldn't be managed anymore, - * for instance it can be used to block the handler to manage the scroll of a view on some scenarions - * @code - * // This handler handles automatically the scroll of - * // flickableItem, unless Ctrl is pressed, in this case the - * // app has custom code to handle Ctrl+wheel zooming - * Kirigami.WheelHandler { - * target: flickableItem - * blockTargetWheel: true - * scrollFlickableTarget: true - * onWheel: { - * if (wheel.modifiers & Qt.ControlModifier) { - * wheel.accepted = true; - * // Handle scaling of the view - * } - * } - * } - * @endcode - * - */ - Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) - -public: - KirigamiWheelEvent(QObject *parent = nullptr); - ~KirigamiWheelEvent(); - - void initializeFromEvent(QWheelEvent *event); - - qreal x() const; - qreal y() const; - QPointF angleDelta() const; - QPointF pixelDelta() const; - int buttons() const; - int modifiers() const; - bool inverted() const; - bool isAccepted(); - void setAccepted(bool accepted); - -private: - qreal m_x = 0; - qreal m_y = 0; - QPointF m_angleDelta; - QPointF m_pixelDelta; - Qt::MouseButtons m_buttons = Qt::NoButton; - Qt::KeyboardModifiers m_modifiers = Qt::NoModifier; - bool m_inverted = false; - bool m_accepted = false; -}; - -class GlobalWheelFilter : public QObject -{ - Q_OBJECT - -public: - GlobalWheelFilter(QObject *parent = nullptr); - ~GlobalWheelFilter(); - - static GlobalWheelFilter *self(); - - void setItemHandlerAssociation(QQuickItem *item, WheelHandler *handler); - void removeItemHandlerAssociation(QQuickItem *item, WheelHandler *handler); - -protected: - bool eventFilter(QObject *watched, QEvent *event) override; - -private: - void manageWheel(QQuickItem *target, QWheelEvent *wheel); - - QMultiHash m_handlersForItem; - KirigamiWheelEvent m_wheelEvent; -}; - - - -/** - * This class intercepts the mouse wheel events of its target, and gives them to the user code as a signal, which can be used for custom mouse wheel management code. - * The handler can block completely the wheel events from its target, and if it's a Flickable, it can automatically handle scrolling on it - */ -class WheelHandler : public QObject -{ - Q_OBJECT - - /** - * target: Item - * - * The target we want to manage wheel events. - * We will receive wheel() signals every time the user moves - * the mouse wheel (or scrolls with the touchpad) on top - * of that item. - */ - Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged) - - /** - * blockTargetWheel: bool - * - * If true, the target won't receive any wheel event at all (default true) - */ - Q_PROPERTY(bool blockTargetWheel MEMBER m_blockTargetWheel NOTIFY blockTargetWheelChanged) - - /** - * scrollFlickableTarget: bool - * If this property is true and the target is a Flickable, wheel events will cause the Flickable to scroll (default true) - */ - Q_PROPERTY(bool scrollFlickableTarget MEMBER m_scrollFlickableTarget NOTIFY scrollFlickableTargetChanged) - -public: - explicit WheelHandler(QObject *parent = nullptr); - ~WheelHandler() override; - - QQuickItem *target() const; - void setTarget(QQuickItem *target); - -Q_SIGNALS: - void targetChanged(); - void blockTargetWheelChanged(); - void scrollFlickableTargetChanged(); - void wheel(KirigamiWheelEvent *wheel); - -private: - QPointer m_target; - bool m_blockTargetWheel = true; - bool m_scrollFlickableTarget = true; - KirigamiWheelEvent m_wheelEvent; - - friend class GlobalWheelFilter; -}; - -