Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qandroidplatformtheme.cpp
Go to the documentation of this file.
1// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "androidjnimain.h"
5#include "androidjnimenu.h"
13
14#include <QCoreApplication>
15#include <QDebug>
16#include <QFileInfo>
17#include <QJsonDocument>
18#include <QVariant>
19
20#include <private/qguiapplication_p.h>
21#include <private/qhighdpiscaling_p.h>
23
25
26Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus")
27
28using namespace Qt::StringLiterals;
29
30namespace {
31 const int textStyle_bold = 1;
32 const int textStyle_italic = 2;
33
34 const int typeface_sans = 1;
35 const int typeface_serif = 2;
36 const int typeface_monospace = 3;
37}
38
39static int fontType(const QString &androidControl)
40{
41 if (androidControl == "defaultStyle"_L1)
43 if (androidControl == "textViewStyle"_L1)
45 else if (androidControl == "buttonStyle"_L1)
47 else if (androidControl == "checkboxStyle"_L1)
49 else if (androidControl == "radioButtonStyle"_L1)
51 else if (androidControl == "simple_list_item_single_choice"_L1)
53 else if (androidControl == "simple_spinner_dropdown_item"_L1)
55 else if (androidControl == "spinnerStyle"_L1)
57 else if (androidControl == "simple_list_item"_L1)
59 return -1;
60}
61
62static int paletteType(const QString &androidControl)
63{
64 if (androidControl == "defaultStyle"_L1)
66 if (androidControl == "textViewStyle"_L1)
68 else if (androidControl == "buttonStyle"_L1)
70 else if (androidControl == "checkboxStyle"_L1)
72 else if (androidControl == "radioButtonStyle"_L1)
74 else if (androidControl == "simple_list_item_single_choice"_L1)
76 else if (androidControl == "editTextStyle"_L1)
78 else if (androidControl == "spinnerStyle"_L1)
80 return -1;
81}
82
83static void setPaletteColor(const QVariantMap &object,
86{
87 // QPalette::Active -> ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET
89 role,
90 QRgb(object.value("ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET"_L1).toInt()));
91
92 // QPalette::Inactive -> ENABLED_STATE_SET
94 role,
95 QRgb(object.value("ENABLED_STATE_SET"_L1).toInt()));
96
97 // QPalette::Disabled -> EMPTY_STATE_SET
99 role,
100 QRgb(object.value("EMPTY_STATE_SET"_L1).toInt()));
101
102 palette.setColor(QPalette::Current, role, palette.color(QPalette::Active, role));
103
104 if (role == QPalette::WindowText) {
105 // QPalette::BrightText -> PRESSED
106 // QPalette::Active -> PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET
107 palette.setColor(QPalette::Active,
109 QRgb(object.value("PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET"_L1).toInt()));
110
111 // QPalette::Inactive -> PRESSED_ENABLED_STATE_SET
114 QRgb(object.value("PRESSED_ENABLED_STATE_SET"_L1).toInt()));
115
116 // QPalette::Disabled -> PRESSED_STATE_SET
119 QRgb(object.value("PRESSED_STATE_SET"_L1).toInt()));
120
122
123 // QPalette::HighlightedText -> SELECTED
124 // QPalette::Active -> ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET
125 palette.setColor(QPalette::Active,
127 QRgb(object.value("ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET"_L1).toInt()));
128
129 // QPalette::Inactive -> ENABLED_SELECTED_STATE_SET
132 QRgb(object.value("ENABLED_SELECTED_STATE_SET"_L1).toInt()));
133
134 // QPalette::Disabled -> SELECTED_STATE_SET
137 QRgb(object.value("SELECTED_STATE_SET"_L1).toInt()));
138
139 palette.setColor(QPalette::Current,
142
143 // Same colors for Text
148
149 // And for ButtonText
154 }
155}
156
158{
159 QString stylePath(QLatin1StringView(qgetenv("ANDROID_STYLE_PATH")));
160 const QLatin1Char slashChar('/');
161 if (!stylePath.isEmpty() && !stylePath.endsWith(slashChar))
162 stylePath += slashChar;
163
165 ? QAndroidPlatformTheme::instance()->colorScheme()
167 if (colorScheme == Qt::ColorScheme::Dark)
168 stylePath += "darkUiMode/"_L1;
169
170 Q_ASSERT(!stylePath.isEmpty());
171
172 QFile f(stylePath + "style.json"_L1);
173 if (!f.open(QIODevice::ReadOnly))
174 return QJsonObject();
175
177 QJsonDocument document = QJsonDocument::fromJson(f.readAll(), &error);
178 if (Q_UNLIKELY(document.isNull())) {
179 qCritical() << error.errorString();
180 return QJsonObject();
181 }
182
183 if (Q_UNLIKELY(!document.isObject())) {
184 qCritical("Style.json does not contain a valid style.");
185 return QJsonObject();
186 }
187 return document.object();
188}
189
190static void loadAndroidStyle(QPalette *defaultPalette, std::shared_ptr<AndroidStyle> &style)
191{
192 double pixelDensity = QHighDpiScaling::isActive() ? QtAndroid::pixelDensity() : 1.0;
193 if (style) {
194 style->m_standardPalette = QPalette();
195 style->m_palettes.clear();
196 style->m_fonts.clear();
197 style->m_QWidgetsFonts.clear();
198 } else {
199 style = std::make_shared<AndroidStyle>();
200 }
201
202 style->m_styleData = AndroidStyle::loadStyleData();
203
204 if (style->m_styleData.isEmpty())
205 return;
206
207 {
208 QFont font("Droid Sans Mono"_L1, 14.0 * 100 / 72);
209 style->m_fonts.insert(QPlatformTheme::FixedFont, font);
210 }
211
212 for (QJsonObject::const_iterator objectIterator = style->m_styleData.constBegin();
213 objectIterator != style->m_styleData.constEnd();
214 ++objectIterator) {
215 QString key = objectIterator.key();
216 QJsonValue value = objectIterator.value();
217 if (!value.isObject()) {
218 qWarning("Style.json structure is unrecognized.");
219 continue;
220 }
221 QJsonObject item = value.toObject();
222 QJsonObject::const_iterator attributeIterator = item.find("qtClass"_L1);
223 QByteArray qtClassName;
224 if (attributeIterator != item.constEnd()) {
225 // The item has palette and font information for a specific Qt Class (e.g. QWidget, QPushButton, etc.)
226 qtClassName = attributeIterator.value().toString().toLatin1();
227 }
228 const int ft = fontType(key);
229 if (ft > -1 || !qtClassName.isEmpty()) {
230 // Extract font information
231 QFont font;
232
233 // Font size (in pixels)
234 attributeIterator = item.find("TextAppearance_textSize"_L1);
235 if (attributeIterator != item.constEnd())
236 font.setPixelSize(int(attributeIterator.value().toDouble() / pixelDensity));
237
238 // Font style
239 attributeIterator = item.find("TextAppearance_textStyle"_L1);
240 if (attributeIterator != item.constEnd()) {
241 const int style = int(attributeIterator.value().toDouble());
242 font.setBold(style & textStyle_bold);
243 font.setItalic(style & textStyle_italic);
244 }
245
246 // Font typeface
247 attributeIterator = item.find("TextAppearance_typeface"_L1);
248 if (attributeIterator != item.constEnd()) {
250 switch (int(attributeIterator.value().toDouble())) {
251 case typeface_sans:
252 styleHint = QFont::SansSerif;
253 break;
254 case typeface_serif:
255 styleHint = QFont::Serif;
256 break;
257 case typeface_monospace:
258 styleHint = QFont::Monospace;
259 break;
260 }
262 }
263 if (!qtClassName.isEmpty())
264 style->m_QWidgetsFonts.insert(qtClassName, font);
265
266 if (ft > -1) {
267 style->m_fonts.insert(ft, font);
270 }
271 // Extract font information
272 }
273
274 const int pt = paletteType(key);
275 if (pt > -1 || !qtClassName.isEmpty()) {
276 // Extract palette information
277 QPalette palette = *defaultPalette;
278
279 attributeIterator = item.find("defaultTextColorPrimary"_L1);
280 if (attributeIterator != item.constEnd())
281 palette.setColor(QPalette::WindowText, QRgb(int(attributeIterator.value().toDouble())));
282
283 attributeIterator = item.find("defaultBackgroundColor"_L1);
284 if (attributeIterator != item.constEnd())
285 palette.setColor(QPalette::Window, QRgb(int(attributeIterator.value().toDouble())));
286
287 attributeIterator = item.find("TextAppearance_textColor"_L1);
288 if (attributeIterator != item.constEnd())
289 setPaletteColor(attributeIterator.value().toObject().toVariantMap(), palette, QPalette::WindowText);
290
291 attributeIterator = item.find("TextAppearance_textColorLink"_L1);
292 if (attributeIterator != item.constEnd())
293 setPaletteColor(attributeIterator.value().toObject().toVariantMap(), palette, QPalette::Link);
294
295 attributeIterator = item.find("TextAppearance_textColorHighlight"_L1);
296 if (attributeIterator != item.constEnd())
297 palette.setColor(QPalette::Highlight, QRgb(int(attributeIterator.value().toDouble())));
298
300 *defaultPalette = style->m_standardPalette = palette;
301
302 if (pt > -1)
303 style->m_palettes.insert(pt, palette);
304 // Extract palette information
305 }
306 }
307}
308
309QAndroidPlatformTheme *QAndroidPlatformTheme::m_instance = nullptr;
310
312 QAndroidPlatformNativeInterface *androidPlatformNativeInterface)
313{
314 if (androidPlatformNativeInterface && !m_instance) {
315 m_instance = new QAndroidPlatformTheme(androidPlatformNativeInterface);
316 }
317 return m_instance;
318}
319
320QAndroidPlatformTheme::QAndroidPlatformTheme(QAndroidPlatformNativeInterface *androidPlatformNativeInterface)
321{
322 updateStyle();
323
324 androidPlatformNativeInterface->m_androidStyle = m_androidStyleData;
325
326 // default in case the style has not set a font
327 m_systemFont = QFont("Roboto"_L1, 14.0 * 100 / 72); // keep default size the same after changing from 100 dpi to 72 dpi
328}
329
331{
332 m_instance = nullptr;
333}
334
340
342{
343 QColor windowText = Qt::black;
344 QColor background(229, 229, 229);
345 QColor light = background.lighter(150);
346 QColor mid(background.darker(130));
347 QColor midLight = mid.lighter(110);
348 QColor base(249, 249, 249);
349 QColor disabledBase(background);
350 QColor dark = background.darker(150);
351 QColor darkDisabled = dark.darker(110);
353 QColor highlightedText = Qt::black;
354 QColor disabledText = QColor(190, 190, 190);
355 QColor button(241, 241, 241);
356 QColor shadow(201, 201, 201);
357 QColor highlight(148, 210, 231);
358 QColor disabledShadow = shadow.lighter(150);
359
361 // Colors were prepared based on Theme.DeviceDefault.DayNight
362 windowText = QColor(250, 250, 250);
363 background = QColor(48, 48, 48);
364 light = background.darker(150);
365 mid = background.lighter(130);
366 midLight = mid.darker(110);
367 base = background;
368 disabledBase = background;
369 dark = background.darker(150);
370 darkDisabled = dark.darker(110);
371 text = QColor(250, 250, 250);
372 highlightedText = QColor(250, 250, 250);
373 disabledText = QColor(96, 96, 96);
374 button = QColor(48, 48, 48);
375 shadow = QColor(32, 32, 32);
376 highlight = QColor(102, 178, 204);
377 disabledShadow = shadow.darker(150);
378 }
379
380 m_defaultPalette = QPalette(windowText,background,light,dark,mid,text,base);
381 m_defaultPalette.setBrush(QPalette::Midlight, midLight);
382 m_defaultPalette.setBrush(QPalette::Button, button);
383 m_defaultPalette.setBrush(QPalette::Shadow, shadow);
384 m_defaultPalette.setBrush(QPalette::HighlightedText, highlightedText);
385
386 m_defaultPalette.setBrush(QPalette::Disabled, QPalette::Text, disabledText);
387 m_defaultPalette.setBrush(QPalette::Disabled, QPalette::WindowText, disabledText);
388 m_defaultPalette.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledText);
389 m_defaultPalette.setBrush(QPalette::Disabled, QPalette::Base, disabledBase);
390 m_defaultPalette.setBrush(QPalette::Disabled, QPalette::Dark, darkDisabled);
391 m_defaultPalette.setBrush(QPalette::Disabled, QPalette::Shadow, disabledShadow);
392
393 m_defaultPalette.setBrush(QPalette::Active, QPalette::Highlight, highlight);
394 m_defaultPalette.setBrush(QPalette::Inactive, QPalette::Highlight, highlight);
395 m_defaultPalette.setBrush(QPalette::Disabled, QPalette::Highlight, highlight.lighter(150));
396
397 loadAndroidStyle(&m_defaultPalette, m_androidStyleData);
398}
399
401{
403 qCDebug(lcQpaMenus) << "Created" << menuBar;
404 return menuBar;
405}
406
408{
409 auto *menu = new QAndroidPlatformMenu;
410 qCDebug(lcQpaMenus) << "Created" << menu;
411 return menu;
412}
413
415{
416 auto *menuItem = new QAndroidPlatformMenuItem;
417 qCDebug(lcQpaMenus) << "Created" << menuItem;
418 return menuItem;
419}
420
422{
423 qCDebug(lcQpaMenus) << "Showing platform menu bar";
425}
426
428{
429 if (m_colorSchemeOverride != Qt::ColorScheme::Unknown)
430 return m_colorSchemeOverride;
432}
433
435{
436 m_colorSchemeOverride = scheme;
439 });
440}
441
469
471{
472 if (m_androidStyleData) {
473 auto it = m_androidStyleData->m_palettes.find(paletteType(type));
474 if (it != m_androidStyleData->m_palettes.end())
475 return &(it.value());
476 }
477 return &m_defaultPalette;
478}
479
481{
482 switch (type) {
485
488
489 default:
490 return type;
491 }
492}
493
495{
496 if (m_androidStyleData) {
497 auto it = m_androidStyleData->m_fonts.find(fontType(type));
498 if (it != m_androidStyleData->m_fonts.end())
499 return &(it.value());
500 }
501
503 return &m_systemFont;
504 return 0;
505}
506
508{
509 return new QAndroidPlatformIconEngine(iconName);
510}
511
513{
514 switch (hint) {
515 case StyleNames:
516 if (qEnvironmentVariableIntValue("QT_USE_ANDROID_NATIVE_STYLE")
517 && m_androidStyleData) {
518 return QStringList("android"_L1);
519 }
520 return QStringList("Fusion"_L1);
524 {
525 int minimumDistance = qEnvironmentVariableIntValue("QT_ANDROID_MINIMUM_MOUSE_DOUBLE_CLICK_DISTANCE");
526 int ret = minimumDistance;
527
528 QAndroidPlatformIntegration *platformIntegration
530 QAndroidPlatformScreen *platformScreen = platformIntegration->screen();
531 if (platformScreen != 0) {
532 QScreen *screen = platformScreen->screen();
533 qreal dotsPerInch = screen->physicalDotsPerInch();
534
535 // Allow 15% of an inch between clicks when double clicking
536 int distance = qRound(dotsPerInch * 0.15);
537 if (distance > minimumDistance)
538 ret = distance;
539 }
540
541 if (ret > 0)
542 return ret;
543
545 }
546 default:
548 }
549}
550
552{
553 switch (button) {
555 return QCoreApplication::translate("QAndroidPlatformTheme", "Yes");
557 return QCoreApplication::translate("QAndroidPlatformTheme", "Yes to All");
559 return QCoreApplication::translate("QAndroidPlatformTheme", "No");
561 return QCoreApplication::translate("QAndroidPlatformTheme", "No to All");
562 }
564}
565
567{
568 if (type == MessageDialog)
569 return qEnvironmentVariableIntValue("QT_USE_ANDROID_NATIVE_DIALOGS") == 1;
570 if (type == FileDialog)
571 return true;
572 return false;
573}
574
586
Qt::ColorScheme colorScheme() const override
const QPalette * palette(Palette type=SystemPalette) const override
QVariant themeHint(ThemeHint hint) const override
static QAndroidPlatformTheme * instance(QAndroidPlatformNativeInterface *androidPlatformNativeInterface=nullptr)
void requestColorScheme(Qt::ColorScheme scheme) override
QPlatformMenu * createPlatformMenu() const override
QString standardButtonText(int button) const override
Returns the text of a standard button.
QPlatformDialogHelper * createPlatformDialogHelper(DialogType type) const override
const QFont * font(Font type=SystemFont) const override
bool usePlatformNativeDialog(DialogType type) const override
QPlatformMenuItem * createPlatformMenuItem() const override
QPlatformMenuBar * createPlatformMenuBar() const override
QIconEngine * createIconEngine(const QString &iconName) const override
Factory function for the QIconEngine used by QIcon::fromTheme().
\inmodule QtCore
Definition qbytearray.h:57
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
QColor darker(int f=200) const noexcept
Definition qcolor.cpp:2857
QColor lighter(int f=150) const noexcept
Definition qcolor.cpp:2812
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
\inmodule QtCore
Definition qfile.h:93
\reentrant
Definition qfont.h:22
StyleHint
Style hints are used by the \l{QFont}{font matching} algorithm to find an appropriate default family ...
Definition qfont.h:25
@ AnyStyle
Definition qfont.h:31
@ Monospace
Definition qfont.h:33
@ Serif
Definition qfont.h:27
@ SansSerif
Definition qfont.h:26
void setPixelSize(int)
Sets the font size to pixelSize pixels, with a maxiumum size of an unsigned 16-bit integer.
Definition qfont.cpp:1049
void setBold(bool)
If enable is true sets the font's weight to \l{Weight}{QFont::Bold}; otherwise sets the weight to \l{...
Definition qfont.h:373
void setItalic(bool b)
Sets the style() of the font to QFont::StyleItalic if enable is true; otherwise the style is set to Q...
Definition qfont.h:381
void setStyleHint(StyleHint, StyleStrategy=PreferDefault)
Sets the style hint and strategy to hint and strategy, respectively.
Definition qfont.cpp:1505
@ PreferMatch
Definition qfont.h:44
static QPlatformIntegration * platformIntegration()
static void setFont(const QFont &)
Changes the default application font to font.
static bool isActive()
The QIconEngine class provides an abstract base class for QIcon renderers.
Definition qiconengine.h:15
\inmodule QtCore\reentrant
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
\inmodule QtCore\reentrant
Definition qjsonvalue.h:25
The QPalette class contains color groups for each widget state.
Definition qpalette.h:19
void setBrush(ColorRole cr, const QBrush &brush)
Sets the brush for the given color role to the specified brush for all groups in the palette.
Definition qpalette.h:151
@ Current
Definition qpalette.h:49
@ Inactive
Definition qpalette.h:49
@ Disabled
Definition qpalette.h:49
@ HighlightedText
Definition qpalette.h:53
@ BrightText
Definition qpalette.h:52
@ ButtonText
Definition qpalette.h:52
@ WindowText
Definition qpalette.h:51
@ Highlight
Definition qpalette.h:53
@ Midlight
Definition qpalette.h:51
The QPlatformDialogHelper class allows for platform-specific customization of dialogs.
QScreen * screen() const
virtual QVariant themeHint(ThemeHint hint) const
virtual QString standardButtonText(int button) const
Returns the text of a standard button.
ThemeHint
This enum describes the available theme hints.
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
qreal physicalDotsPerInch
the number of physical dots or pixels per inch
Definition qscreen.h:55
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qvariant.h:65
static void handleThemeChange(QWindow *window=nullptr)
QString text
QPushButton * button
[2]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
double pixelDensity()
Definition qcompare.h:63
ColorScheme
Definition qnamespace.h:50
@ black
Definition qnamespace.h:30
static int fontType(const QString &androidControl)
static void setPaletteColor(const QVariantMap &object, QPalette &palette, QPalette::ColorRole role)
static int paletteType(const QString &androidControl)
static void loadAndroidStyle(QPalette *defaultPalette, std::shared_ptr< AndroidStyle > &style)
#define Q_FALLTHROUGH()
#define Q_UNLIKELY(x)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define qGuiApp
#define qCritical
Definition qlogging.h:167
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
GLuint64 key
GLfloat GLfloat f
GLsizei GLsizei GLfloat distance
GLenum type
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
static QT_BEGIN_NAMESPACE QVariant hint(QPlatformIntegration::StyleHint h)
QScreen * screen
[1]
Definition main.cpp:29
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
double qreal
Definition qtypes.h:187
static const uint base
Definition qurlidna.cpp:20
static int toInt(const QChar &qc, int R)
QGraphicsItem * item
QMenu menu
[5]
QMenuBar * menuBar
[0]
static QJsonObject loadStyleData()
\inmodule QtCore\reentrant
\inmodule QtCore \reentrant
Definition qchar.h:18
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...