Krita Source Code Documentation
Loading...
Searching...
No Matches
KisKineticScroller.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2018 Emmet O 'Neill <emmetoneill.pdx@gmail.com>
3 * SPDX-FileCopyrightText: 2018 Eoin O 'Neill <eoinoneill1991@gmail.com>
4 * SPDX-FileCopyrightText: 2021 Alvin Wong <alvin@alvinhc.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
10
11#include <QAbstractItemView>
12#include <QEvent>
13#include <QScrollBar>
14#ifdef Q_OS_ANDROID
15#include <QtAndroid>
16#else
17#include <QApplication>
18#include <QStyleHints>
19#endif
20
21#include <ksharedconfig.h>
22#include <kconfiggroup.h>
23
24namespace {
31 class KisKineticScrollerEventFilter : public QObject
32 {
33 Q_OBJECT
34
35 QAbstractScrollArea *m_scrollArea;
36 QScroller::ScrollerGestureType m_gestureType;
37
38 public:
39 KisKineticScrollerEventFilter(QScroller::ScrollerGestureType gestureType, QAbstractScrollArea *parent)
40 : QObject(parent)
41 , m_scrollArea(parent)
42 , m_gestureType(gestureType)
43 {
44 }
45
46 protected:
47 bool eventFilter(QObject *watched, QEvent *event) override {
48 switch (event->type()) {
49 case QEvent::Enter:
50 QScroller::ungrabGesture(m_scrollArea);
51 break;
52 case QEvent::Leave:
53 QScroller::grabGesture(m_scrollArea, m_gestureType);
54 break;
55 default:
56 break;
57 }
58 return QObject::eventFilter(watched, event);
59 }
60 };
61} /* namespace */
62
63QScroller* KisKineticScroller::createPreconfiguredScroller(QAbstractScrollArea *scrollArea) {
64 KConfigGroup config = KSharedConfig::openConfig()->group("");
65 int sensitivity = config.readEntry("KineticScrollingSensitivity", 75);
66 bool enabled = config.readEntry("KineticScrollingEnabled", true);
67 bool hideScrollBars = config.readEntry("KineticScrollingHideScrollbar", false);
68 float resistanceCoefficient = config.readEntry("KineticScrollingResistanceCoefficient", 10.0f);
69 float dragVelocitySmoothFactor = config.readEntry("KineticScrollingDragVelocitySmoothingFactor", 1.0f);
70 float minimumVelocity = config.readEntry("KineticScrollingMinimumVelocity", 0.0f);
71 float axisLockThresh = config.readEntry("KineticScrollingAxisLockThreshold", 1.0f);
72 float maximumClickThroughVelocity = config.readEntry("KineticScrollingMaxClickThroughVelocity", 0.0f);
73 float flickAccelerationFactor = config.readEntry("KineticScrollingFlickAccelerationFactor", 1.5f);
74 float overshootDragResistanceFactor = config.readEntry("KineticScrollingOvershotDragResistanceFactor", 0.1f);
75 float overshootDragDistanceFactor = config.readEntry("KineticScrollingOvershootDragDistanceFactor", 0.3f);
76 float overshootScrollDistanceFactor = config.readEntry("KineticScrollingOvershootScrollDistanceFactor", 0.1f);
77 float overshootScrollTime = config.readEntry("KineticScrollingOvershootScrollTime", 0.4f);
78 QScroller::ScrollerGestureType gestureType = getConfiguredGestureType();
79
80 if (enabled && scrollArea) {
81 if (hideScrollBars) {
82 scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
83 scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
84 } else if (gestureType != QScroller::TouchGesture) {
85 auto *filter = new KisKineticScrollerEventFilter(gestureType, scrollArea);
86 scrollArea->horizontalScrollBar()->installEventFilter(filter);
87 scrollArea->verticalScrollBar()->installEventFilter(filter);
88 }
89
90 QAbstractItemView *itemView = qobject_cast<QAbstractItemView *>(scrollArea);
91 if (itemView) {
92 itemView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
93 }
94
95 QScroller *scroller = QScroller::scroller(scrollArea);
96 QScroller::grabGesture(scrollArea, gestureType);
97
98 QScrollerProperties properties;
99
100 // DragStartDistance seems to be based on meter per second; though it's
101 // not explicitly documented, other QScroller values are in that metric.
102 // To start kinetic scrolling, with minimal sensitivity, we expect a drag
103 // of 10 mm, with minimum sensitivity any > 0 mm.
104 const float mm = 0.001f;
105 const float resistance = 1.0f - (sensitivity / 100.0f);
106 float mousePressEventDelay = config.readEntry("KineticScrollingMousePressDelay", 1.0f - 0.75f * resistance);
107
108 // If the mouse press event delay is too large, it also ends up delaying
109 // long-presses. Cap the value there for consistency.
110#ifdef Q_OS_ANDROID
111 int maxDelayMs =
112 QAndroidJniObject::callStaticMethod<jint>("org/krita/android/MainActivity", "getLongPressTimeout", "()I");
113#else
114 int maxDelayMs = qApp->styleHints()->mousePressAndHoldInterval();
115#endif
116 float maxDelay = float(maxDelayMs) / 1000.0f;
117 if (mousePressEventDelay > maxDelay) {
118 mousePressEventDelay = maxDelay;
119 }
120
121 properties.setScrollMetric(QScrollerProperties::DragStartDistance, resistance * resistanceCoefficient * mm);
122 properties.setScrollMetric(QScrollerProperties::DragVelocitySmoothingFactor, dragVelocitySmoothFactor);
123 properties.setScrollMetric(QScrollerProperties::MinimumVelocity, minimumVelocity);
124 properties.setScrollMetric(QScrollerProperties::AxisLockThreshold, axisLockThresh);
125 properties.setScrollMetric(QScrollerProperties::MaximumClickThroughVelocity, maximumClickThroughVelocity);
126 properties.setScrollMetric(QScrollerProperties::MousePressEventDelay, mousePressEventDelay);
127 properties.setScrollMetric(QScrollerProperties::AcceleratingFlickSpeedupFactor, flickAccelerationFactor);
128
129 properties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOn);
130 properties.setScrollMetric(QScrollerProperties::OvershootDragResistanceFactor, overshootDragResistanceFactor);
131 properties.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, overshootDragDistanceFactor);
132 properties.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, overshootScrollDistanceFactor);
133 properties.setScrollMetric(QScrollerProperties::OvershootScrollTime, overshootScrollTime);
134
135 scroller->setScrollerProperties(properties);
136
137 return scroller;
138 }
139
140 return nullptr;
141}
142
143QScroller::ScrollerGestureType KisKineticScroller::getConfiguredGestureType() {
144 KConfigGroup config = KSharedConfig::openConfig()->group("");
145#ifdef Q_OS_ANDROID
146 // Use a different default. Shouldn't we use KisConfig::kineticScrollingGesture?
147 int gesture = config.readEntry("KineticScrollingGesture", 1);
148#else
149 int gesture = config.readEntry("KineticScrollingGesture", 0);
150#endif
151
152 switch (gesture) {
153 case 0: {
154 return QScroller::TouchGesture;
155 }
156 case 1: {
157 return QScroller::LeftMouseButtonGesture;
158 }
159 case 2: {
160 return QScroller::MiddleMouseButtonGesture;
161 }
162 case 3: {
163 return QScroller::RightMouseButtonGesture;
164 }
165 default:
166 return QScroller::MiddleMouseButtonGesture;
167 }
168}
169
170void KisKineticScroller::updateCursor(QWidget *source, QScroller::State state) {
171 if( state == QScroller::State::Pressed ) {
172 source->setCursor(Qt::OpenHandCursor);
173 } else if (state == QScroller::State::Dragging) {
174 source->setCursor(Qt::ClosedHandCursor);
175 } else {
176 source->setCursor(Qt::ArrowCursor);
177 }
178}
179
180#include "KisKineticScroller.moc"
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
ChildIterator< value_type, is_const > parent(const ChildIterator< value_type, is_const > &it)
Definition KisForest.h:327
KRITAWIDGETUTILS_EXPORT QScroller::ScrollerGestureType getConfiguredGestureType()
KRITAWIDGETUTILS_EXPORT void updateCursor(QWidget *source, QScroller::State state)
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)