Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAndroidScaling.cpp
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: GPL-3.0-or-later
3 */
4#include "KisAndroidScaling.h"
5
7#include <KisAndroidUtils.h>
8#include <KisApplication.h>
9#include <kis_config.h>
10
11#include <QGuiApplication>
12
13#include <qpa/qplatformscreen.h>
14
15#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
16#include <QJniObject>
17#else
18#include <QAndroidJniObject>
19using QJniObject = QAndroidJniObject;
20#endif
21
23 : QObject(app)
24{
25 KIS_ASSERT(app);
26
28 QScreen *screen = QGuiApplication::primaryScreen();
29 if (screen) {
31 m_initialPrimaryScreenDevicePixelRatio = screen->devicePixelRatio();
32 }
33 }
34
35 qreal scale = getValidInitialScale(cfg);
36 if (scale >= 1.0) {
38 } else {
39 // Reset the configuration, it wasn't valid or the screen changed.
40 cfg.setAndroidScalingLastInitialScale(cfg.androidScalingLastInitialScale(true));
41 cfg.setAndroidScalingTargetScale(cfg.androidScalingTargetScale(true));
42 cfg.setAndroidScalingAskOnStartup(cfg.androidScalingAskOnStartup(true));
43 }
44
45 // Queued connections are used in the following to make sure they run on
46 // the main thread. The signals may come from the Android UI thread.
47 connect(this,
49 this,
51 Qt::QueuedConnection);
52 connect(this,
54 this,
56 Qt::QueuedConnection);
57 connect(this,
59 this,
61 Qt::QueuedConnection);
62
63 KisAndroidDonations *androidDonations = app->androidDonations();
64 KIS_SAFE_ASSERT_RECOVER_NOOP(androidDonations);
65 if (androidDonations) {
66 connect(androidDonations,
68 this,
70 Qt::QueuedConnection);
71 }
72}
73
75{
76 KisApplication *app = qobject_cast<KisApplication *>(KisApplication::instance());
77 if (app) {
78 return app->androidScaling();
79 } else {
80 return nullptr;
81 }
82}
83
88
95
97{
98 KisConfig cfg(false);
100 QScreen *screen = m_initialPrimaryScreen.data();
101 if (screen) {
102 cfg.setAndroidScalingLastInitialScale(m_initialPrimaryScreenDevicePixelRatio);
103 cfg.setAndroidScalingTargetScale(screen->devicePixelRatio());
104 cfg.setAndroidScalingAskOnStartup(askOnStartup);
105 }
106 }
107}
108
113
115{
116 // Detects whether we have scaling available in this session. Compare
117 // with the startup handling of the scaling setting in main.cc. We do
118 // *not* check the config value here, it only applies after a restart.
119#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
120 return !qEnvironmentVariableIsSet("QT_ENABLE_HIGHDPI_SCALING")
121 && qgetenv("QT_ENABLE_HIGHDPI_SCALING") != QByteArrayLiteral("0");
122#else
123 return QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling)
124 && !QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling);
125#endif
126}
127
129{
130 if (m_initialPrimaryScreen.isNull()) {
131 return 0.0;
132 }
133
134 // If we don't have a previous screen scale or the default screen scale
135 // changed, the configuration is invalid and we fall back to the default.
136 qreal lastInitialScale = cfg.androidScalingLastInitialScale();
137 if (lastInitialScale < 1.0 || !qFuzzyCompare(lastInitialScale, m_initialPrimaryScreenDevicePixelRatio)) {
138 return 0.0;
139 }
140
141 return cfg.androidScalingTargetScale();
142}
143
145{
147 QScreen *screen = m_initialPrimaryScreen.data();
148 if (screen) {
149 QPlatformScreen *platformScreen = screen->handle();
150 if (platformScreen) {
151 qreal actualScale = qMax(1.0, scale);
152 qreal densityAdjustment = actualScale / m_initialPrimaryScreenDevicePixelRatio;
153 if (!qFuzzyCompare(densityAdjustment, platformScreen->densityAdjustment())) {
154 platformScreen->setDensityAdjustment(densityAdjustment);
156 }
157 }
158 }
159 }
160}
161
163{
164 // We want to see the dialog if scaling is supported, the dialog isn't
165 // already up and either the user manually summoned it or they want to
166 // see the dialog on startup. The user can only decide to remember the
167 // scaling setting after they decided on a target scale at least once
168 // before. This is to make sure that they actually used the application
169 // at that scale and can make an informed decision about not wanting to
170 // see the dialog again instead of despairing at choosing a wrong scale
171 // and being unable to find the dialog again.
173 return;
174 }
175
176 QScreen *screen = m_initialPrimaryScreen.data();
177 if (!screen) {
178 return;
179 }
180
181 KisConfig cfg(true);
182 bool haveTargetScale = cfg.androidScalingTargetScale() >= 1.0;
183 bool askOnStartup = cfg.androidScalingAskOnStartup();
184 if (!startup || askOnStartup) {
185 QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
186 "activity",
187 "()Landroid/app/Activity;");
188 KisAndroidUtils::clearJniException(QStringLiteral("getting activity in KisAndroidScalingDialog"));
189
190 if (activity.isValid()) {
191 activity.callMethod<void>("showScalingDialog",
192 "(DDZZ)V",
193 jdouble(m_initialPrimaryScreen->devicePixelRatio()),
195 jboolean(askOnStartup),
196 jboolean(!startup || haveTargetScale));
197 KisAndroidUtils::clearJniException(QStringLiteral("showing scaling dialog in KisAndroidScalingDialog"));
198 }
199 }
200}
201
202extern "C" JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_setPrimaryScreenScale(JNIEnv *env,
203 jobject obj,
204 jdouble scale)
205{
206 Q_UNUSED(env);
207 Q_UNUSED(obj);
209 if (androidScaling) {
210 Q_EMIT androidScaling->sigJniSetPrimaryScreenScale(scale);
211 }
212}
213
214extern "C" JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_savePrimaryScreenScale(JNIEnv *env,
215 jobject obj,
216 jboolean askOnStartup)
217{
218 Q_UNUSED(env);
219 Q_UNUSED(obj);
221 if (androidScaling) {
222 Q_EMIT androidScaling->sigJniSaveInterfaceScale(askOnStartup);
223 }
224}
225
226extern "C" JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_onScalingDialogShown(JNIEnv *env, jobject obj)
227{
228 Q_UNUSED(env);
229 Q_UNUSED(obj);
231 if (androidScaling) {
232 Q_EMIT androidScaling->sigJniScalingDialogActive(true);
233 }
234}
235
236extern "C" JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_onScalingDialogDismissed(JNIEnv *env, jobject obj)
237{
238 Q_UNUSED(env);
239 Q_UNUSED(obj);
241 if (androidScaling) {
242 Q_EMIT androidScaling->sigJniScalingDialogActive(false);
243 }
244}
JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_savePrimaryScreenScale(JNIEnv *env, jobject obj, jboolean askOnStartup)
JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_onScalingDialogDismissed(JNIEnv *env, jobject obj)
JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_onScalingDialogShown(JNIEnv *env, jobject obj)
JNIEXPORT void JNICALL Java_org_krita_android_JNIWrappers_setPrimaryScreenScale(JNIEnv *env, jobject obj, jdouble scale)
void sigSplashDialogDismissed()
void sigInterfaceScaleChanged()
static KisAndroidScaling * instance()
qreal getValidInitialScale(KisConfig &cfg) const
QPointer< QScreen > m_initialPrimaryScreen
qreal m_initialPrimaryScreenDevicePixelRatio
void sigJniScalingDialogActive(bool active)
KisAndroidScaling(KisConfig &cfg, KisApplication *app)
void sigJniSaveInterfaceScale(bool askOnStartup)
void sigJniSetPrimaryScreenScale(qreal scale)
void setPrimaryScreenScale(qreal scale)
void slotJniSaveInterfaceScale(bool askOnStartup)
void slotJniScalingDialogActive(bool active)
void slotJniSetPrimaryScreenScale(qreal scale)
static bool isHighDpiScalingEnabled()
void maybeShowDialog(bool startup)
Base class for the Krita app.
static bool qFuzzyCompare(half p1, half p2)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
void clearJniException(const QString &location)