diff --git a/src/controls/templates/private/ScrollView.qml b/src/controls/templates/private/ScrollView.qml index dcc78b94..c24e0cdf 100644 --- a/src/controls/templates/private/ScrollView.qml +++ b/src/controls/templates/private/ScrollView.qml @@ -1,144 +1,144 @@ /* * Copyright 2016 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.0 import org.kde.kirigami 2.9 as Kirigami MouseArea { id: root default property Item contentItem property Flickable flickableItem clip: true //TODO: horizontalScrollBarPolicy is completely noop just for compatibility right now property int horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff property int verticalScrollBarPolicy: Qt.ScrollBarAsNeeded readonly property Item verticalScrollBar: flickableItem.ScrollBar.vertical ? flickableItem.ScrollBar.vertical : null onVerticalScrollBarPolicyChanged: { if (flickableItem.ScrollBar.vertical) { flickableItem.ScrollBar.vertical.visible = verticalScrollBarPolicy != Qt.ScrollBarAlwaysOff; } scrollBarCreationTimer.restart(); } onHorizontalScrollBarPolicyChanged: { if (flickableItem.ScrollBar.horizontal) { flickableItem.ScrollBar.horizontal.visible = horizontalScrollBarPolicy != Qt.ScrollBarAlwaysOff; } scrollBarCreationTimer.restart(); } onContentItemChanged: { if (contentItem.hasOwnProperty("contentY")) { flickableItem = contentItem; if (typeof(flickableItem.keyNavigationEnabled) != "undefined") { flickableItem.keyNavigationEnabled = true; flickableItem.keyNavigationWraps = false; } contentItem.parent = flickableParent; } else { flickableItem = flickableComponent.createObject(flickableParent); contentItem.parent = flickableItem.contentItem; } flickableItem.interactive = true; flickableItem.anchors.fill = flickableParent; scrollBarCreationTimer.restart(); } Timer { id: scrollBarCreationTimer interval: 0 onTriggered: { //create or destroy the vertical scrollbar if ((!flickableItem.ScrollBar.vertical) && verticalScrollBarPolicy != Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.vertical = verticalScrollComponent.createObject(root); } else if (flickableItem.ScrollBar.vertical && verticalScrollBarPolicy == Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.vertical.destroy(); } //create or destroy the horizontal scrollbar if ((!flickableItem.ScrollBar.horizontal) && horizontalScrollBarPolicy != Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.horizontal = horizontalScrollComponent.createObject(root); } else if (flickableItem.ScrollBar.horizontal && horizontalScrollBarPolicy == Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.horizontal.destroy(); } } } Kirigami.WheelHandler { id: wheelHandler target: root.flickableItem } Item { id: flickableParent anchors { fill: parent - rightMargin: !Kirigami.Settings.tabletMode && flickableItem.ScrollBar.vertical && flickableItem.ScrollBar.vertical.visible ? flickableItem.ScrollBar.vertical.width : 0 + rightMargin: !Kirigami.Settings.hasTransientTouchInput && flickableItem.ScrollBar.vertical && flickableItem.ScrollBar.vertical.visible ? flickableItem.ScrollBar.vertical.width : 0 } } Component { id: flickableComponent Flickable { anchors { fill: parent } contentWidth: root.contentItem ? root.contentItem.width : 0 contentHeight: root.contentItem ? root.contentItem.height : 0 } } Component { id: verticalScrollComponent ScrollBar { z: flickableParent.z + 1 visible: root.contentItem.visible && size < 1 - interactive: !Kirigami.Settings.tabletMode + interactive: !Kirigami.Settings.hasTransientTouchInput //NOTE: use this instead of anchors as crashes on some Qt 5.8 checkouts height: parent.height - anchors.topMargin anchors { topMargin: parent.flickableItem.headerItem ? parent.flickableItem.headerItem.height : 0 right: parent.right top: parent.top } } } Component { id: horizontalScrollComponent ScrollBar { z: flickableParent.z + 1 visible: root.contentItem.visible && size < 1 - interactive: !Kirigami.Settings.tabletMode + interactive: !Kirigami.Settings.hasTransientTouchInput //NOTE: use this instead of anchors as crashes on some Qt 5.8 checkouts height: parent.height - anchors.topMargin anchors { left: parent.left right: parent.right bottom: parent.bottom } } } } diff --git a/src/settings.cpp b/src/settings.cpp index 52ac71cf..cb43d1dc 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,170 +1,223 @@ /* * Copyright 2016 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "settings.h" #include #include #include #include #include #include +#include +#include #include "libkirigami/tabletmodewatcher.h" #ifndef KIRIGAMI_BUILD_TYPE_STATIC #include "../kirigami_version.h" #endif class SettingsSingleton { public: Settings self; }; Q_GLOBAL_STATIC(SettingsSingleton, privateSettingsSelf) Settings::Settings(QObject *parent) - : QObject(parent) + : QObject(parent), + m_hasTouchScreen(false), + m_hasTransientTouchInput(false) { m_tabletModeAvailable = Kirigami::TabletModeWatcher::self()->isTabletModeAvailable(); connect(Kirigami::TabletModeWatcher::self(), &Kirigami::TabletModeWatcher::tabletModeAvailableChanged, this, [this](bool tabletModeAvailable) { setTabletModeAvailable(tabletModeAvailable); }); m_tabletMode = Kirigami::TabletModeWatcher::self()->isTabletMode(); connect(Kirigami::TabletModeWatcher::self(), &Kirigami::TabletModeWatcher::tabletModeChanged, this, [this](bool tabletMode) { setTabletMode(tabletMode); }); #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) m_mobile = true; + m_hasTouchScreen = true; #else //Mostly for debug purposes and for platforms which are always mobile, //such as Plasma Mobile if (qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MOBILE")) { m_mobile = QByteArrayList{"1", "true"}.contains(qgetenv("QT_QUICK_CONTROLS_MOBILE")); } else { m_mobile = false; } + + for (const auto &device : QTouchDevice::devices()) { + if (device->type() == QTouchDevice::TouchScreen) { + m_hasTouchScreen = true; + break; + } + } + if (m_hasTouchScreen) { + connect(qApp, &QGuiApplication::focusWindowChanged, + this, [this](QWindow *win) { + if (win) { + win->installEventFilter(this); + } + }); + } #endif const QString configPath = QStandardPaths::locate(QStandardPaths::ConfigLocation, QStringLiteral("kdeglobals")); if (QFile::exists(configPath)) { QSettings globals(configPath, QSettings::IniFormat); globals.beginGroup(QStringLiteral("KDE")); m_scrollLines = qMax(1, globals.value(QStringLiteral("WheelScrollLines"), 3).toInt()); } else { m_scrollLines = 3; } } Settings::~Settings() { } Settings *Settings::self() { return &privateSettingsSelf()->self; } +bool Settings::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::TouchBegin: + setTransientTouchInput(true); + break; + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::Wheel: + setTransientTouchInput(false); + default: + break; + } + + return false; +} void Settings::setTabletModeAvailable(bool mobileAvailable) { if (mobileAvailable == m_tabletModeAvailable) { return; } m_tabletModeAvailable = mobileAvailable; emit tabletModeAvailableChanged(); } bool Settings::isTabletModeAvailable() const { return m_tabletModeAvailable; } void Settings::setIsMobile(bool mobile) { if (mobile == m_mobile) { return; } m_mobile = mobile; emit isMobileChanged(); } bool Settings::isMobile() const { return m_mobile; } void Settings::setTabletMode(bool tablet) { if (tablet == m_tabletMode) { return; } m_tabletMode = tablet; emit tabletModeChanged(); } bool Settings::tabletMode() const { return m_tabletMode; } +void Settings::setTransientTouchInput(bool touch) +{ + if (touch == m_hasTransientTouchInput) { + return; + } + + m_hasTransientTouchInput = touch; + if (!m_tabletMode) { + emit hasTransientTouchInputChanged(); + } +} + +bool Settings::hasTransientTouchInput() const +{ + return m_hasTransientTouchInput || m_tabletMode; +} + QString Settings::style() const { return m_style; } void Settings::setStyle(const QString &style) { m_style = style; } int Settings::mouseWheelScrollLines() const { return m_scrollLines; } QStringList Settings::information() const { return { #ifndef KIRIGAMI_BUILD_TYPE_STATIC tr("KDE Frameworks %1").arg(QStringLiteral(KIRIGAMI2_VERSION_STRING)), #endif tr("The %1 windowing system").arg(QGuiApplication::platformName()), tr("Qt %2 (built against %3)").arg(QString::fromLocal8Bit(qVersion()), QStringLiteral(QT_VERSION_STR)) }; } QVariant Settings::applicationWindowIcon() const { const QIcon& windowIcon = qApp->windowIcon(); if (windowIcon.isNull()) { return QVariant(); } return windowIcon; } diff --git a/src/settings.h b/src/settings.h index 9613be8a..37c06a37 100644 --- a/src/settings.h +++ b/src/settings.h @@ -1,120 +1,134 @@ /* * Copyright 2016 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SETTINGS_H #define SETTINGS_H #include #include /** * This class contains global kirigami settings about the current device setup * It is exposed to QML as the singleton "Settings" */ class Settings : public QObject { Q_OBJECT /** * True if the system can dynamically enter in tablet mode * (or the device is actually a tablet). * such as transformable laptops that support keyboard detachment */ Q_PROPERTY(bool tabletModeAvailable READ isTabletModeAvailable NOTIFY tabletModeAvailableChanged) /** * True if we are running on a small mobile device such as a mobile phone * This is used when we want to do specific adaptations to our UI for * small screen form factors, such as having bigger touch areas. */ Q_PROPERTY(bool isMobile READ isMobile NOTIFY isMobileChanged) /** * True if the device we are running on is behaving like a tablet: * Note that this doesn't mean exactly a tablet form factor, but * that the preferred input mode for the device is the touch screen * and that pointer and keyboard are either secondary or not available. */ Q_PROPERTY(bool tabletMode READ tabletMode NOTIFY tabletModeChanged) + /** + * True if the user in this moment is interacting with the app with the touch screen + */ + Q_PROPERTY(bool hasTransientTouchInput READ hasTransientTouchInput NOTIFY hasTransientTouchInputChanged) + /** * name of the QtQuickControls2 style we are using, * for instance org.kde.desktop, Plasma, Material, Universal etc */ Q_PROPERTY(QString style READ style CONSTANT) //TODO: make this adapt without file watchers? /** * How many lines of text the mouse wheel should scroll */ Q_PROPERTY(int mouseWheelScrollLines READ mouseWheelScrollLines CONSTANT) /** * @returns runtime information about the libraries in use * * @since 5.52 * @since org.kde.kirigami 2.6 */ Q_PROPERTY(QStringList information READ information CONSTANT) /** * @returns application window icon, basically \QApplication::windowIcon() * * @since 5.62 * @since org.kde.kirigami 2.10 */ Q_PROPERTY(QVariant applicationWindowIcon READ applicationWindowIcon CONSTANT) public: Settings(QObject *parent = nullptr); ~Settings(); void setTabletModeAvailable(bool mobile); bool isTabletModeAvailable() const; void setIsMobile(bool mobile); bool isMobile() const; void setTabletMode(bool tablet); bool tabletMode() const; + void setTransientTouchInput(bool touch); + bool hasTransientTouchInput() const; + QString style() const; void setStyle(const QString &style); int mouseWheelScrollLines() const; QStringList information() const; QVariant applicationWindowIcon() const; static Settings *self(); +protected: + bool eventFilter(QObject *watched, QEvent *event) override; + Q_SIGNALS: void tabletModeAvailableChanged(); void tabletModeChanged(); void isMobileChanged(); + void hasTransientTouchInputChanged(); private: QString m_style; int m_scrollLines = 0; bool m_tabletModeAvailable : 1; bool m_mobile : 1; bool m_tabletMode : 1; + bool m_hasTouchScreen : 1; + bool m_hasTransientTouchInput : 1; }; #endif