1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "qquickuniversalstyle_p.h"
38 
39 #include <QtCore/qdebug.h>
40 #include <QtCore/qsettings.h>
41 #include <QtQml/qqmlinfo.h>
42 #include <QtQuickControls2/private/qquickstyle_p.h>
43 
44 QT_BEGIN_NAMESPACE
45 
qquickuniversal_light_color(QQuickUniversalStyle::SystemColor role)46 static QRgb qquickuniversal_light_color(QQuickUniversalStyle::SystemColor role)
47 {
48     static const QRgb colors[] = {
49         0xFFFFFFFF, // SystemAltHighColor
50         0x33FFFFFF, // SystemAltLowColor
51         0x99FFFFFF, // SystemAltMediumColor
52         0xCCFFFFFF, // SystemAltMediumHighColor
53         0x66FFFFFF, // SystemAltMediumLowColor
54         0xFF000000, // SystemBaseHighColor
55         0x33000000, // SystemBaseLowColor
56         0x99000000, // SystemBaseMediumColor
57         0xCC000000, // SystemBaseMediumHighColor
58         0x66000000, // SystemBaseMediumLowColor
59         0xFF171717, // SystemChromeAltLowColor
60         0xFF000000, // SystemChromeBlackHighColor
61         0x33000000, // SystemChromeBlackLowColor
62         0x66000000, // SystemChromeBlackMediumLowColor
63         0xCC000000, // SystemChromeBlackMediumColor
64         0xFFCCCCCC, // SystemChromeDisabledHighColor
65         0xFF7A7A7A, // SystemChromeDisabledLowColor
66         0xFFCCCCCC, // SystemChromeHighColor
67         0xFFF2F2F2, // SystemChromeLowColor
68         0xFFE6E6E6, // SystemChromeMediumColor
69         0xFFF2F2F2, // SystemChromeMediumLowColor
70         0xFFFFFFFF, // SystemChromeWhiteColor
71         0x19000000, // SystemListLowColor
72         0x33000000  // SystemListMediumColor
73     };
74     return colors[role];
75 }
76 
qquickuniversal_dark_color(QQuickUniversalStyle::SystemColor role)77 static QRgb qquickuniversal_dark_color(QQuickUniversalStyle::SystemColor role)
78 {
79     static const QRgb colors[] = {
80         0xFF000000, // SystemAltHighColor
81         0x33000000, // SystemAltLowColor
82         0x99000000, // SystemAltMediumColor
83         0xCC000000, // SystemAltMediumHighColor
84         0x66000000, // SystemAltMediumLowColor
85         0xFFFFFFFF, // SystemBaseHighColor
86         0x33FFFFFF, // SystemBaseLowColor
87         0x99FFFFFF, // SystemBaseMediumColor
88         0xCCFFFFFF, // SystemBaseMediumHighColor
89         0x66FFFFFF, // SystemBaseMediumLowColor
90         0xFFF2F2F2, // SystemChromeAltLowColor
91         0xFF000000, // SystemChromeBlackHighColor
92         0x33000000, // SystemChromeBlackLowColor
93         0x66000000, // SystemChromeBlackMediumLowColor
94         0xCC000000, // SystemChromeBlackMediumColor
95         0xFF333333, // SystemChromeDisabledHighColor
96         0xFF858585, // SystemChromeDisabledLowColor
97         0xFF767676, // SystemChromeHighColor
98         0xFF171717, // SystemChromeLowColor
99         0xFF1F1F1F, // SystemChromeMediumColor
100         0xFF2B2B2B, // SystemChromeMediumLowColor
101         0xFFFFFFFF, // SystemChromeWhiteColor
102         0x19FFFFFF, // SystemListLowColor
103         0x33FFFFFF  // SystemListMediumColor
104     };
105     return colors[role];
106 }
107 
qquickuniversal_accent_color(QQuickUniversalStyle::Color accent)108 static QRgb qquickuniversal_accent_color(QQuickUniversalStyle::Color accent)
109 {
110     static const QRgb colors[] = {
111         0xFFA4C400, // Lime
112         0xFF60A917, // Green
113         0xFF008A00, // Emerald
114         0xFF00ABA9, // Teal
115         0xFF1BA1E2, // Cyan
116         0xFF3E65FF, // Cobalt
117         0xFF6A00FF, // Indigo
118         0xFFAA00FF, // Violet
119         0xFFF472D0, // Pink
120         0xFFD80073, // Magenta
121         0xFFA20025, // Crimson
122         0xFFE51400, // Red
123         0xFFFA6800, // Orange
124         0xFFF0A30A, // Amber
125         0xFFE3C800, // Yellow
126         0xFF825A2C, // Brown
127         0xFF6D8764, // Olive
128         0xFF647687, // Steel
129         0xFF76608A, // Mauve
130         0xFF87794E  // Taupe
131     };
132     return colors[accent];
133 }
134 
qquickuniversal_effective_theme(QQuickUniversalStyle::Theme theme)135 static QQuickUniversalStyle::Theme qquickuniversal_effective_theme(QQuickUniversalStyle::Theme theme)
136 {
137     if (theme == QQuickUniversalStyle::System)
138         theme = QQuickStylePrivate::isDarkSystemTheme() ? QQuickUniversalStyle::Dark : QQuickUniversalStyle::Light;
139     return theme;
140 }
141 
142 // If no value was inherited from a parent or explicitly set, the "global" values are used.
143 // The initial, default values of the globals are hard-coded here, but the environment
144 // variables and .conf file override them if specified.
145 static QQuickUniversalStyle::Theme GlobalTheme = QQuickUniversalStyle::Light;
146 static QRgb GlobalAccent = qquickuniversal_accent_color(QQuickUniversalStyle::Cobalt);
147 static QRgb GlobalForeground = qquickuniversal_light_color(QQuickUniversalStyle::BaseHigh);
148 static QRgb GlobalBackground = qquickuniversal_light_color(QQuickUniversalStyle::AltHigh);
149 // These represent whether a global foreground/background was set.
150 // Each style's m_hasForeground/m_hasBackground are initialized to these values.
151 static bool HasGlobalForeground = false;
152 static bool HasGlobalBackground = false;
153 
QQuickUniversalStyle(QObject * parent)154 QQuickUniversalStyle::QQuickUniversalStyle(QObject *parent) : QQuickAttachedObject(parent),
155     m_hasForeground(HasGlobalForeground), m_hasBackground(HasGlobalBackground), m_theme(GlobalTheme),
156     m_accent(GlobalAccent), m_foreground(GlobalForeground), m_background(GlobalBackground)
157 {
158     init();
159 }
160 
qmlAttachedProperties(QObject * object)161 QQuickUniversalStyle *QQuickUniversalStyle::qmlAttachedProperties(QObject *object)
162 {
163     return new QQuickUniversalStyle(object);
164 }
165 
theme() const166 QQuickUniversalStyle::Theme QQuickUniversalStyle::theme() const
167 {
168     return m_theme;
169 }
170 
setTheme(Theme theme)171 void QQuickUniversalStyle::setTheme(Theme theme)
172 {
173     theme = qquickuniversal_effective_theme(theme);
174     m_explicitTheme = true;
175     if (m_theme == theme)
176         return;
177 
178     m_theme = theme;
179     propagateTheme();
180     emit themeChanged();
181     emit paletteChanged();
182     emit foregroundChanged();
183     emit backgroundChanged();
184 }
185 
inheritTheme(Theme theme)186 void QQuickUniversalStyle::inheritTheme(Theme theme)
187 {
188     if (m_explicitTheme || m_theme == theme)
189         return;
190 
191     m_theme = theme;
192     propagateTheme();
193     emit themeChanged();
194     emit paletteChanged();
195     emit foregroundChanged();
196     emit backgroundChanged();
197 }
198 
propagateTheme()199 void QQuickUniversalStyle::propagateTheme()
200 {
201     const auto styles = attachedChildren();
202     for (QQuickAttachedObject *child : styles) {
203         QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(child);
204         if (universal)
205             universal->inheritTheme(m_theme);
206     }
207 }
208 
resetTheme()209 void QQuickUniversalStyle::resetTheme()
210 {
211     if (!m_explicitTheme)
212         return;
213 
214     m_explicitTheme = false;
215     QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(attachedParent());
216     inheritTheme(universal ? universal->theme() : GlobalTheme);
217 }
218 
accent() const219 QVariant QQuickUniversalStyle::accent() const
220 {
221     return QColor::fromRgba(m_accent);
222 }
223 
setAccent(const QVariant & var)224 void QQuickUniversalStyle::setAccent(const QVariant &var)
225 {
226     QRgb accent = 0;
227     if (!variantToRgba(var, "accent", &accent))
228         return;
229 
230     m_explicitAccent = true;
231     if (m_accent == accent)
232         return;
233 
234     m_accent = accent;
235     propagateAccent();
236     emit accentChanged();
237 }
238 
inheritAccent(QRgb accent)239 void QQuickUniversalStyle::inheritAccent(QRgb accent)
240 {
241     if (m_explicitAccent || m_accent == accent)
242         return;
243 
244     m_accent = accent;
245     propagateAccent();
246     emit accentChanged();
247 }
248 
propagateAccent()249 void QQuickUniversalStyle::propagateAccent()
250 {
251     const auto styles = attachedChildren();
252     for (QQuickAttachedObject *child : styles) {
253         QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(child);
254         if (universal)
255             universal->inheritAccent(m_accent);
256     }
257 }
258 
resetAccent()259 void QQuickUniversalStyle::resetAccent()
260 {
261     if (!m_explicitAccent)
262         return;
263 
264     m_explicitAccent = false;
265     QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(attachedParent());
266     inheritAccent(universal ? universal->m_accent : GlobalAccent);
267 }
268 
foreground() const269 QVariant QQuickUniversalStyle::foreground() const
270 {
271     if (m_hasForeground)
272         return QColor::fromRgba(m_foreground);
273     return baseHighColor();
274 }
275 
setForeground(const QVariant & var)276 void QQuickUniversalStyle::setForeground(const QVariant &var)
277 {
278     QRgb foreground = 0;
279     if (!variantToRgba(var, "foreground", &foreground))
280         return;
281 
282     m_hasForeground = true;
283     m_explicitForeground = true;
284     if (m_foreground == foreground)
285         return;
286 
287     m_foreground = foreground;
288     propagateForeground();
289     emit foregroundChanged();
290 }
291 
inheritForeground(QRgb foreground,bool has)292 void QQuickUniversalStyle::inheritForeground(QRgb foreground, bool has)
293 {
294     if (m_explicitForeground || m_foreground == foreground)
295         return;
296 
297     m_hasForeground = has;
298     m_foreground = foreground;
299     propagateForeground();
300     emit foregroundChanged();
301 }
302 
propagateForeground()303 void QQuickUniversalStyle::propagateForeground()
304 {
305     const auto styles = attachedChildren();
306     for (QQuickAttachedObject *child : styles) {
307         QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(child);
308         if (universal)
309             universal->inheritForeground(m_foreground, m_hasForeground);
310     }
311 }
312 
resetForeground()313 void QQuickUniversalStyle::resetForeground()
314 {
315     if (!m_explicitForeground)
316         return;
317 
318     m_hasForeground = false;
319     m_explicitForeground = false;
320     QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(attachedParent());
321     inheritForeground(universal ? universal->m_foreground : GlobalForeground, universal ? universal->m_hasForeground : false);
322 }
323 
background() const324 QVariant QQuickUniversalStyle::background() const
325 {
326     if (m_hasBackground)
327         return QColor::fromRgba(m_background);
328     return altHighColor();
329 }
330 
setBackground(const QVariant & var)331 void QQuickUniversalStyle::setBackground(const QVariant &var)
332 {
333     QRgb background = 0;
334     if (!variantToRgba(var, "background", &background))
335         return;
336 
337     m_hasBackground = true;
338     m_explicitBackground = true;
339     if (m_background == background)
340         return;
341 
342     m_background = background;
343     propagateBackground();
344     emit backgroundChanged();
345 }
346 
inheritBackground(QRgb background,bool has)347 void QQuickUniversalStyle::inheritBackground(QRgb background, bool has)
348 {
349     if (m_explicitBackground || m_background == background)
350         return;
351 
352     m_hasBackground = has;
353     m_background = background;
354     propagateBackground();
355     emit backgroundChanged();
356 }
357 
propagateBackground()358 void QQuickUniversalStyle::propagateBackground()
359 {
360     const auto styles = attachedChildren();
361     for (QQuickAttachedObject *child : styles) {
362         QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(child);
363         if (universal)
364             universal->inheritBackground(m_background, m_hasBackground);
365     }
366 }
367 
resetBackground()368 void QQuickUniversalStyle::resetBackground()
369 {
370     if (!m_explicitBackground)
371         return;
372 
373     m_hasBackground = false;
374     m_explicitBackground = false;
375     QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(attachedParent());
376     inheritBackground(universal ? universal->m_background : GlobalBackground, universal ? universal->m_hasBackground : false);
377 }
378 
color(Color color) const379 QColor QQuickUniversalStyle::color(Color color) const
380 {
381     return qquickuniversal_accent_color(color);
382 }
383 
altHighColor() const384 QColor QQuickUniversalStyle::altHighColor() const
385 {
386     return systemColor(AltHigh);
387 }
388 
altLowColor() const389 QColor QQuickUniversalStyle::altLowColor() const
390 {
391     return systemColor(AltLow);
392 }
393 
altMediumColor() const394 QColor QQuickUniversalStyle::altMediumColor() const
395 {
396     return systemColor(AltMedium);
397 }
398 
altMediumHighColor() const399 QColor QQuickUniversalStyle::altMediumHighColor() const
400 {
401     return systemColor(AltMediumHigh);
402 }
403 
altMediumLowColor() const404 QColor QQuickUniversalStyle::altMediumLowColor() const
405 {
406     return systemColor(AltMediumLow);
407 }
408 
baseHighColor() const409 QColor QQuickUniversalStyle::baseHighColor() const
410 {
411     return systemColor(BaseHigh);
412 }
413 
baseLowColor() const414 QColor QQuickUniversalStyle::baseLowColor() const
415 {
416     return systemColor(BaseLow);
417 }
418 
baseMediumColor() const419 QColor QQuickUniversalStyle::baseMediumColor() const
420 {
421     return systemColor(BaseMedium);
422 }
423 
baseMediumHighColor() const424 QColor QQuickUniversalStyle::baseMediumHighColor() const
425 {
426     return systemColor(BaseMediumHigh);
427 }
428 
baseMediumLowColor() const429 QColor QQuickUniversalStyle::baseMediumLowColor() const
430 {
431     return systemColor(BaseMediumLow);
432 }
433 
chromeAltLowColor() const434 QColor QQuickUniversalStyle::chromeAltLowColor() const
435 {
436     return systemColor(ChromeAltLow);
437 }
438 
chromeBlackHighColor() const439 QColor QQuickUniversalStyle::chromeBlackHighColor() const
440 {
441     return systemColor(ChromeBlackHigh);
442 }
443 
chromeBlackLowColor() const444 QColor QQuickUniversalStyle::chromeBlackLowColor() const
445 {
446     return systemColor(ChromeBlackLow);
447 }
448 
chromeBlackMediumLowColor() const449 QColor QQuickUniversalStyle::chromeBlackMediumLowColor() const
450 {
451     return systemColor(ChromeBlackMediumLow);
452 }
453 
chromeBlackMediumColor() const454 QColor QQuickUniversalStyle::chromeBlackMediumColor() const
455 {
456     return systemColor(ChromeBlackMedium);
457 }
458 
chromeDisabledHighColor() const459 QColor QQuickUniversalStyle::chromeDisabledHighColor() const
460 {
461     return systemColor(ChromeDisabledHigh);
462 }
463 
chromeDisabledLowColor() const464 QColor QQuickUniversalStyle::chromeDisabledLowColor() const
465 {
466     return systemColor(ChromeDisabledLow);
467 }
468 
chromeHighColor() const469 QColor QQuickUniversalStyle::chromeHighColor() const
470 {
471     return systemColor(ChromeHigh);
472 }
473 
chromeLowColor() const474 QColor QQuickUniversalStyle::chromeLowColor() const
475 {
476     return systemColor(ChromeLow);
477 }
478 
chromeMediumColor() const479 QColor QQuickUniversalStyle::chromeMediumColor() const
480 {
481     return systemColor(ChromeMedium);
482 }
483 
chromeMediumLowColor() const484 QColor QQuickUniversalStyle::chromeMediumLowColor() const
485 {
486     return systemColor(ChromeMediumLow);
487 }
488 
chromeWhiteColor() const489 QColor QQuickUniversalStyle::chromeWhiteColor() const
490 {
491     return systemColor(ChromeWhite);
492 }
493 
listLowColor() const494 QColor QQuickUniversalStyle::listLowColor() const
495 {
496     return systemColor(ListLow);
497 }
498 
listMediumColor() const499 QColor QQuickUniversalStyle::listMediumColor() const
500 {
501     return systemColor(ListMedium);
502 }
503 
systemColor(SystemColor role) const504 QColor QQuickUniversalStyle::systemColor(SystemColor role) const
505 {
506     return QColor::fromRgba(m_theme == QQuickUniversalStyle::Dark ? qquickuniversal_dark_color(role) : qquickuniversal_light_color(role));
507 }
508 
attachedParentChange(QQuickAttachedObject * newParent,QQuickAttachedObject * oldParent)509 void QQuickUniversalStyle::attachedParentChange(QQuickAttachedObject *newParent, QQuickAttachedObject *oldParent)
510 {
511     Q_UNUSED(oldParent);
512     QQuickUniversalStyle *universal = qobject_cast<QQuickUniversalStyle *>(newParent);
513     if (universal) {
514         inheritTheme(universal->theme());
515         inheritAccent(universal->m_accent);
516         inheritForeground(universal->m_foreground, universal->m_hasForeground);
517         inheritBackground(universal->m_background, universal->m_hasBackground);
518     }
519 }
520 
521 template <typename Enum>
toEnumValue(const QByteArray & value,bool * ok)522 static Enum toEnumValue(const QByteArray &value, bool *ok)
523 {
524     QMetaEnum enumeration = QMetaEnum::fromType<Enum>();
525     return static_cast<Enum>(enumeration.keyToValue(value, ok));
526 }
527 
resolveSetting(const QByteArray & env,const QSharedPointer<QSettings> & settings,const QString & name)528 static QByteArray resolveSetting(const QByteArray &env, const QSharedPointer<QSettings> &settings, const QString &name)
529 {
530     QByteArray value = qgetenv(env);
531 #if QT_CONFIG(settings)
532     if (value.isNull() && !settings.isNull())
533         value = settings->value(name).toByteArray();
534 #endif
535     return value;
536 }
537 
initGlobals()538 void QQuickUniversalStyle::initGlobals()
539 {
540     QSharedPointer<QSettings> settings = QQuickStylePrivate::settings(QStringLiteral("Universal"));
541 
542     bool ok = false;
543     QByteArray themeValue = resolveSetting("QT_QUICK_CONTROLS_UNIVERSAL_THEME", settings, QStringLiteral("Theme"));
544     Theme themeEnum = toEnumValue<Theme>(themeValue, &ok);
545     if (ok)
546         GlobalTheme = qquickuniversal_effective_theme(themeEnum);
547     else if (!themeValue.isEmpty())
548         qWarning().nospace().noquote() << "Universal: unknown theme value: " << themeValue;
549 
550     QByteArray accentValue = resolveSetting("QT_QUICK_CONTROLS_UNIVERSAL_ACCENT", settings, QStringLiteral("Accent"));
551     Color accentEnum = toEnumValue<Color>(accentValue, &ok);
552     if (ok) {
553         GlobalAccent = qquickuniversal_accent_color(accentEnum);
554     } else if (!accentValue.isEmpty()) {
555         QColor color(accentValue.constData());
556         if (color.isValid())
557             GlobalAccent = color.rgba();
558         else
559             qWarning().nospace().noquote() << "Universal: unknown accent value: " << accentValue;
560     }
561 
562     QByteArray foregroundValue = resolveSetting("QT_QUICK_CONTROLS_UNIVERSAL_FOREGROUND", settings, QStringLiteral("Foreground"));
563     Color foregroundEnum = toEnumValue<Color>(foregroundValue, &ok);
564     if (ok) {
565         GlobalForeground = qquickuniversal_accent_color(foregroundEnum);
566         HasGlobalForeground = true;
567     } else if (!foregroundValue.isEmpty()) {
568         QColor color(foregroundValue.constData());
569         if (color.isValid()) {
570             GlobalForeground = color.rgba();
571             HasGlobalForeground = true;
572         } else {
573             qWarning().nospace().noquote() << "Universal: unknown foreground value: " << foregroundValue;
574         }
575     }
576 
577     QByteArray backgroundValue = resolveSetting("QT_QUICK_CONTROLS_UNIVERSAL_BACKGROUND", settings, QStringLiteral("Background"));
578     Color backgroundEnum = toEnumValue<Color>(backgroundValue, &ok);
579     if (ok) {
580         GlobalBackground = qquickuniversal_accent_color(backgroundEnum);
581         HasGlobalBackground = true;
582     } else if (!backgroundValue.isEmpty()) {
583         QColor color(backgroundValue.constData());
584         if (color.isValid()) {
585             GlobalBackground = color.rgba();
586             HasGlobalBackground = true;
587         } else {
588             qWarning().nospace().noquote() << "Universal: unknown background value: " << backgroundValue;
589         }
590     }
591 }
592 
variantToRgba(const QVariant & var,const char * name,QRgb * rgba) const593 bool QQuickUniversalStyle::variantToRgba(const QVariant &var, const char *name, QRgb *rgba) const
594 {
595     if (var.type() == QVariant::Int) {
596         int val = var.toInt();
597         if (val < Lime || val > Taupe) {
598             qmlWarning(parent()) << "unknown Universal." << name << " value: " << val;
599             return false;
600         }
601         *rgba = qquickuniversal_accent_color(static_cast<Color>(val));
602     } else {
603         int val = QMetaEnum::fromType<Color>().keyToValue(var.toByteArray());
604         if (val != -1) {
605             *rgba = qquickuniversal_accent_color(static_cast<Color>(val));
606         } else {
607             QColor color(var.toString());
608             if (!color.isValid()) {
609                 qmlWarning(parent()) << "unknown Universal." << name << " value: " << var.toString();
610                 return false;
611             }
612             *rgba = color.rgba();
613         }
614     }
615     return true;
616 }
617 
618 QT_END_NAMESPACE
619