1// Copyright (C) 2017 The Qt Company Ltd.
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 "qquickmaterialstyle_p.h"
5#include "qquickmaterialtheme_p.h"
6
7#include <QtCore/qdebug.h>
8#if QT_CONFIG(settings)
9#include <QtCore/qsettings.h>
10#endif
11#include <QtQml/qqmlinfo.h>
12#include <QtQuickControls2/private/qquickstyle_p.h>
13
14#include <algorithm>
15
16QT_BEGIN_NAMESPACE
17
18static const QRgb colors[][14] = {
19 // Red
20 {
21 0xFFFFEBEE, // Shade50
22 0xFFFFCDD2, // Shade100
23 0xFFEF9A9A, // Shade200
24 0xFFE57373, // Shade300
25 0xFFEF5350, // Shade400
26 0xFFF44336, // Shade500
27 0xFFE53935, // Shade600
28 0xFFD32F2F, // Shade700
29 0xFFC62828, // Shade800
30 0xFFB71C1C, // Shade900
31 0xFFFF8A80, // ShadeA100
32 0xFFFF5252, // ShadeA200
33 0xFFFF1744, // ShadeA400
34 0xFFD50000 // ShadeA700
35 },
36 // Pink
37 {
38 0xFFFCE4EC, // Shade50
39 0xFFF8BBD0, // Shade100
40 0xFFF48FB1, // Shade200
41 0xFFF06292, // Shade300
42 0xFFEC407A, // Shade400
43 0xFFE91E63, // Shade500
44 0xFFD81B60, // Shade600
45 0xFFC2185B, // Shade700
46 0xFFAD1457, // Shade800
47 0xFF880E4F, // Shade900
48 0xFFFF80AB, // ShadeA100
49 0xFFFF4081, // ShadeA200
50 0xFFF50057, // ShadeA400
51 0xFFC51162 // ShadeA700
52 },
53 // Purple
54 {
55 0xFFF3E5F5, // Shade50
56 0xFFE1BEE7, // Shade100
57 0xFFCE93D8, // Shade200
58 0xFFBA68C8, // Shade300
59 0xFFAB47BC, // Shade400
60 0xFF9C27B0, // Shade500
61 0xFF8E24AA, // Shade600
62 0xFF7B1FA2, // Shade700
63 0xFF6A1B9A, // Shade800
64 0xFF4A148C, // Shade900
65 0xFFEA80FC, // ShadeA100
66 0xFFE040FB, // ShadeA200
67 0xFFD500F9, // ShadeA400
68 0xFFAA00FF // ShadeA700
69 },
70 // DeepPurple
71 {
72 0xFFEDE7F6, // Shade50
73 0xFFD1C4E9, // Shade100
74 0xFFB39DDB, // Shade200
75 0xFF9575CD, // Shade300
76 0xFF7E57C2, // Shade400
77 0xFF673AB7, // Shade500
78 0xFF5E35B1, // Shade600
79 0xFF512DA8, // Shade700
80 0xFF4527A0, // Shade800
81 0xFF311B92, // Shade900
82 0xFFB388FF, // ShadeA100
83 0xFF7C4DFF, // ShadeA200
84 0xFF651FFF, // ShadeA400
85 0xFF6200EA // ShadeA700
86 },
87 // Indigo
88 {
89 0xFFE8EAF6, // Shade50
90 0xFFC5CAE9, // Shade100
91 0xFF9FA8DA, // Shade200
92 0xFF7986CB, // Shade300
93 0xFF5C6BC0, // Shade400
94 0xFF3F51B5, // Shade500
95 0xFF3949AB, // Shade600
96 0xFF303F9F, // Shade700
97 0xFF283593, // Shade800
98 0xFF1A237E, // Shade900
99 0xFF8C9EFF, // ShadeA100
100 0xFF536DFE, // ShadeA200
101 0xFF3D5AFE, // ShadeA400
102 0xFF304FFE // ShadeA700
103 },
104 // Blue
105 {
106 0xFFE3F2FD, // Shade50
107 0xFFBBDEFB, // Shade100
108 0xFF90CAF9, // Shade200
109 0xFF64B5F6, // Shade300
110 0xFF42A5F5, // Shade400
111 0xFF2196F3, // Shade500
112 0xFF1E88E5, // Shade600
113 0xFF1976D2, // Shade700
114 0xFF1565C0, // Shade800
115 0xFF0D47A1, // Shade900
116 0xFF82B1FF, // ShadeA100
117 0xFF448AFF, // ShadeA200
118 0xFF2979FF, // ShadeA400
119 0xFF2962FF // ShadeA700
120 },
121 // LightBlue
122 {
123 0xFFE1F5FE, // Shade50
124 0xFFB3E5FC, // Shade100
125 0xFF81D4FA, // Shade200
126 0xFF4FC3F7, // Shade300
127 0xFF29B6F6, // Shade400
128 0xFF03A9F4, // Shade500
129 0xFF039BE5, // Shade600
130 0xFF0288D1, // Shade700
131 0xFF0277BD, // Shade800
132 0xFF01579B, // Shade900
133 0xFF80D8FF, // ShadeA100
134 0xFF40C4FF, // ShadeA200
135 0xFF00B0FF, // ShadeA400
136 0xFF0091EA // ShadeA700
137 },
138 // Cyan
139 {
140 0xFFE0F7FA, // Shade50
141 0xFFB2EBF2, // Shade100
142 0xFF80DEEA, // Shade200
143 0xFF4DD0E1, // Shade300
144 0xFF26C6DA, // Shade400
145 0xFF00BCD4, // Shade500
146 0xFF00ACC1, // Shade600
147 0xFF0097A7, // Shade700
148 0xFF00838F, // Shade800
149 0xFF006064, // Shade900
150 0xFF84FFFF, // ShadeA100
151 0xFF18FFFF, // ShadeA200
152 0xFF00E5FF, // ShadeA400
153 0xFF00B8D4 // ShadeA700
154 },
155 // Teal
156 {
157 0xFFE0F2F1, // Shade50
158 0xFFB2DFDB, // Shade100
159 0xFF80CBC4, // Shade200
160 0xFF4DB6AC, // Shade300
161 0xFF26A69A, // Shade400
162 0xFF009688, // Shade500
163 0xFF00897B, // Shade600
164 0xFF00796B, // Shade700
165 0xFF00695C, // Shade800
166 0xFF004D40, // Shade900
167 0xFFA7FFEB, // ShadeA100
168 0xFF64FFDA, // ShadeA200
169 0xFF1DE9B6, // ShadeA400
170 0xFF00BFA5 // ShadeA700
171 },
172 // Green
173 {
174 0xFFE8F5E9, // Shade50
175 0xFFC8E6C9, // Shade100
176 0xFFA5D6A7, // Shade200
177 0xFF81C784, // Shade300
178 0xFF66BB6A, // Shade400
179 0xFF4CAF50, // Shade500
180 0xFF43A047, // Shade600
181 0xFF388E3C, // Shade700
182 0xFF2E7D32, // Shade800
183 0xFF1B5E20, // Shade900
184 0xFFB9F6CA, // ShadeA100
185 0xFF69F0AE, // ShadeA200
186 0xFF00E676, // ShadeA400
187 0xFF00C853 // ShadeA700
188 },
189 // LightGreen
190 {
191 0xFFF1F8E9, // Shade50
192 0xFFDCEDC8, // Shade100
193 0xFFC5E1A5, // Shade200
194 0xFFAED581, // Shade300
195 0xFF9CCC65, // Shade400
196 0xFF8BC34A, // Shade500
197 0xFF7CB342, // Shade600
198 0xFF689F38, // Shade700
199 0xFF558B2F, // Shade800
200 0xFF33691E, // Shade900
201 0xFFCCFF90, // ShadeA100
202 0xFFB2FF59, // ShadeA200
203 0xFF76FF03, // ShadeA400
204 0xFF64DD17 // ShadeA700
205 },
206 // Lime
207 {
208 0xFFF9FBE7, // Shade50
209 0xFFF0F4C3, // Shade100
210 0xFFE6EE9C, // Shade200
211 0xFFDCE775, // Shade300
212 0xFFD4E157, // Shade400
213 0xFFCDDC39, // Shade500
214 0xFFC0CA33, // Shade600
215 0xFFAFB42B, // Shade700
216 0xFF9E9D24, // Shade800
217 0xFF827717, // Shade900
218 0xFFF4FF81, // ShadeA100
219 0xFFEEFF41, // ShadeA200
220 0xFFC6FF00, // ShadeA400
221 0xFFAEEA00 // ShadeA700
222 },
223 // Yellow
224 {
225 0xFFFFFDE7, // Shade50
226 0xFFFFF9C4, // Shade100
227 0xFFFFF59D, // Shade200
228 0xFFFFF176, // Shade300
229 0xFFFFEE58, // Shade400
230 0xFFFFEB3B, // Shade500
231 0xFFFDD835, // Shade600
232 0xFFFBC02D, // Shade700
233 0xFFF9A825, // Shade800
234 0xFFF57F17, // Shade900
235 0xFFFFFF8D, // ShadeA100
236 0xFFFFFF00, // ShadeA200
237 0xFFFFEA00, // ShadeA400
238 0xFFFFD600 // ShadeA700
239 },
240 // Amber
241 {
242 0xFFFFF8E1, // Shade50
243 0xFFFFECB3, // Shade100
244 0xFFFFE082, // Shade200
245 0xFFFFD54F, // Shade300
246 0xFFFFCA28, // Shade400
247 0xFFFFC107, // Shade500
248 0xFFFFB300, // Shade600
249 0xFFFFA000, // Shade700
250 0xFFFF8F00, // Shade800
251 0xFFFF6F00, // Shade900
252 0xFFFFE57F, // ShadeA100
253 0xFFFFD740, // ShadeA200
254 0xFFFFC400, // ShadeA400
255 0xFFFFAB00 // ShadeA700
256 },
257 // Orange
258 {
259 0xFFFFF3E0, // Shade50
260 0xFFFFE0B2, // Shade100
261 0xFFFFCC80, // Shade200
262 0xFFFFB74D, // Shade300
263 0xFFFFA726, // Shade400
264 0xFFFF9800, // Shade500
265 0xFFFB8C00, // Shade600
266 0xFFF57C00, // Shade700
267 0xFFEF6C00, // Shade800
268 0xFFE65100, // Shade900
269 0xFFFFD180, // ShadeA100
270 0xFFFFAB40, // ShadeA200
271 0xFFFF9100, // ShadeA400
272 0xFFFF6D00 // ShadeA700
273 },
274 // DeepOrange
275 {
276 0xFFFBE9E7, // Shade50
277 0xFFFFCCBC, // Shade100
278 0xFFFFAB91, // Shade200
279 0xFFFF8A65, // Shade300
280 0xFFFF7043, // Shade400
281 0xFFFF5722, // Shade500
282 0xFFF4511E, // Shade600
283 0xFFE64A19, // Shade700
284 0xFFD84315, // Shade800
285 0xFFBF360C, // Shade900
286 0xFFFF9E80, // ShadeA100
287 0xFFFF6E40, // ShadeA200
288 0xFFFF3D00, // ShadeA400
289 0xFFDD2C00 // ShadeA700
290 },
291 // Brown
292 {
293 0xFFEFEBE9, // Shade50
294 0xFFD7CCC8, // Shade100
295 0xFFBCAAA4, // Shade200
296 0xFFA1887F, // Shade300
297 0xFF8D6E63, // Shade400
298 0xFF795548, // Shade500
299 0xFF6D4C41, // Shade600
300 0xFF5D4037, // Shade700
301 0xFF4E342E, // Shade800
302 0xFF3E2723, // Shade900
303 0xFF000000, // ShadeA100
304 0xFF000000, // ShadeA200
305 0xFF000000, // ShadeA400
306 0xFF000000 // ShadeA700
307 },
308 // Grey
309 {
310 0xFFFAFAFA, // Shade50
311 0xFFF5F5F5, // Shade100
312 0xFFEEEEEE, // Shade200
313 0xFFE0E0E0, // Shade300
314 0xFFBDBDBD, // Shade400
315 0xFF9E9E9E, // Shade500
316 0xFF757575, // Shade600
317 0xFF616161, // Shade700
318 0xFF424242, // Shade800
319 0xFF212121, // Shade900
320 0xFF000000, // ShadeA100
321 0xFF000000, // ShadeA200
322 0xFF000000, // ShadeA400
323 0xFF000000 // ShadeA700
324 },
325 // BlueGrey
326 {
327 0xFFECEFF1, // Shade50
328 0xFFCFD8DC, // Shade100
329 0xFFB0BEC5, // Shade200
330 0xFF90A4AE, // Shade300
331 0xFF78909C, // Shade400
332 0xFF607D8B, // Shade500
333 0xFF546E7A, // Shade600
334 0xFF455A64, // Shade700
335 0xFF37474F, // Shade800
336 0xFF263238, // Shade900
337 0xFF000000, // ShadeA100
338 0xFF000000, // ShadeA200
339 0xFF000000, // ShadeA400
340 0xFF000000 // ShadeA700
341 }
342};
343
344// If no value was inherited from a parent or explicitly set, the "global" values are used.
345// The initial, default values of the globals are hard-coded here, but the environment
346// variables and .conf file override them if specified.
347static QQuickMaterialStyle::Theme globalTheme = QQuickMaterialStyle::Light;
348static uint globalPrimary = QQuickMaterialStyle::Indigo;
349static uint globalAccent = QQuickMaterialStyle::Pink;
350static uint globalForeground = 0xDD000000; // primaryTextColorLight
351static uint globalBackground = 0xFFFAFAFA; // backgroundColorLight
352// These represent whether a global foreground/background was set.
353// Each style's m_hasForeground/m_hasBackground are initialized to these values.
354static bool hasGlobalForeground = false;
355static bool hasGlobalBackground = false;
356// These represent whether or not the global color value was specified as one of the
357// values that QColor accepts, as opposed to one of the pre-defined colors like Red.
358static bool globalPrimaryCustom = false;
359static bool globalAccentCustom = false;
360static bool globalForegroundCustom = false;
361static bool globalBackgroundCustom = false;
362// This is global because:
363// 1) The theme needs access to it to determine font sizes.
364// 2) There can only be one variant used for the whole application.
365static QQuickMaterialStyle::Variant globalVariant = QQuickMaterialStyle::Normal;
366static const QRgb backgroundColorLight = 0xFFFFFBFE;
367static const QRgb backgroundColorDark = 0xFF1C1B1F;
368static const QRgb dialogColorLight = 0xFFFFFFFF;
369static const QRgb dialogColorDark = 0xFF424242;
370static const QRgb primaryTextColorLight = 0xDD000000;
371static const QRgb primaryTextColorDark = 0xFFFFFFFF;
372static const QRgb secondaryTextColorLight = 0x89000000;
373static const QRgb secondaryTextColorDark = 0xB2FFFFFF;
374static const QRgb hintTextColorLight = 0x60000000;
375static const QRgb hintTextColorDark = 0x4CFFFFFF;
376static const QRgb dividerColorLight = 0x1E000000;
377static const QRgb dividerColorDark = 0x1EFFFFFF;
378static const QRgb iconColorLight = 0x89000000;
379static const QRgb iconColorDark = 0xFFFFFFFF;
380static const QRgb iconDisabledColorLight = 0x42000000;
381static const QRgb iconDisabledColorDark = 0x4CFFFFFF;
382static const QRgb raisedButtonColorLight = 0xFFD6D7D7;
383static const QRgb raisedButtonColorDark = 0x3FCCCCCC;
384static const QRgb raisedButtonDisabledColorLight = dividerColorLight;
385static const QRgb raisedButtonDisabledColorDark = dividerColorDark;
386static const QRgb frameColorLight = hintTextColorLight;
387static const QRgb frameColorDark = hintTextColorDark;
388static const QRgb rippleColorLight = 0x10000000;
389static const QRgb rippleColorDark = 0x20FFFFFF;
390static const QRgb spinBoxDisabledIconColorLight = 0xFFCCCCCC;
391static const QRgb spinBoxDisabledIconColorDark = 0xFF666666;
392static const QRgb sliderDisabledColorLight = 0xFF9E9E9E;
393static const QRgb sliderDisabledColorDark = 0xFF616161;
394/*
395 https://m3.material.io/components/switch/specs#57a434cd-5fcc-4d79-9bff-12b2a9768789
396
397 light / dark
398 surface: #FFFBFE/#1C1B1F
399 on-surface: #1C1B1F/#E6E1E5
400 surface-variant: #E7E0EC/#49454F
401
402 12% = 1E
403 38% = 61
404
405 handle
406
407 unchecked checked
408 disabled #1C1B1F/#E6E1E5 @ 38% (#611C1B1F/#61E6E1E5) #FFFBFE/#1C1B1F @ 100%
409
410 track
411
412 unchecked checked
413 disabled #E7E0EC/#49454F @ 12% (#1EE7E0EC/#1E49454F) #1C1B1F/#E6E1E5 @ 12% (#1E1C1B1F/#1EE6E1E5)
414
415 track outline
416
417 unchecked checked
418 disabled #1C1B1F/#E6E1E5 @ 12% (#1E1C1B1F/#1EE6E1E5) same as track
419*/
420static const QRgb switchUncheckedTrackColorLight = 0xFFE7E0EC;
421static const QRgb switchUncheckedTrackColorDark = 0x49454F;
422static const QRgb switchDisabledUncheckedTrackColorLight = 0x1EE7E0EC;
423static const QRgb switchDisabledUncheckedTrackColorDark = 0x1E49454F;
424static const QRgb switchDisabledUncheckedTrackBorderColorLight = 0x1E1C1B1F;
425static const QRgb switchDisabledUncheckedTrackBorderColorDark = 0x1EE6E1E5;
426static const QRgb switchDisabledCheckedTrackColorLight = 0x1E1C1B1F;
427static const QRgb switchDisabledCheckedTrackColorDark = 0x1EE6E1E5;
428static const QRgb switchDisabledUncheckedIconColorLight = 0x611C1B1F;
429static const QRgb switchDisabledUncheckedIconColorDark = 0x61E6E1E5;
430static const QRgb textFieldFilledContainerColorLight = 0xFFE7E0EC;
431static const QRgb textFieldFilledContainerColorDark = 0xFF49454F;
432
433static QQuickMaterialStyle::Theme effectiveTheme(QQuickMaterialStyle::Theme theme)
434{
435 if (theme == QQuickMaterialStyle::System)
436 theme = QQuickStylePrivate::isDarkSystemTheme() ? QQuickMaterialStyle::Dark : QQuickMaterialStyle::Light;
437 return theme;
438}
439
440QQuickMaterialStyle::QQuickMaterialStyle(QObject *parent) : QQuickAttachedPropertyPropagator(parent),
441 m_customPrimary(globalPrimaryCustom),
442 m_customAccent(globalAccentCustom),
443 m_customForeground(globalForegroundCustom),
444 m_customBackground(globalBackgroundCustom),
445 m_hasForeground(hasGlobalForeground),
446 m_hasBackground(hasGlobalBackground),
447 m_systemTheme(globalTheme == System),
448 m_theme(effectiveTheme(theme: globalTheme)),
449 m_primary(globalPrimary),
450 m_accent(globalAccent),
451 m_foreground(globalForeground),
452 m_background(globalBackground)
453{
454 QQuickAttachedPropertyPropagator::initialize();
455}
456
457QQuickMaterialStyle *QQuickMaterialStyle::qmlAttachedProperties(QObject *object)
458{
459 return new QQuickMaterialStyle(object);
460}
461
462QQuickMaterialStyle::Theme QQuickMaterialStyle::theme() const
463{
464 return m_theme;
465}
466
467void QQuickMaterialStyle::setTheme(Theme theme)
468{
469 m_explicitTheme = true;
470
471 // If theme is System: m_theme is set to system's theme (Dark/Light)
472 // and m_systemTheme is set to true.
473 // If theme is Dark/Light: m_theme is set to the input theme (Dark/Light)
474 // and m_systemTheme is set to false.
475 const bool systemThemeChanged = (m_systemTheme != (theme == System));
476 // Check m_theme and m_systemTheme are changed.
477 if ((m_theme == effectiveTheme(theme)) && !systemThemeChanged)
478 return;
479
480 m_theme = effectiveTheme(theme);
481 m_systemTheme = (theme == System);
482 if (systemThemeChanged) {
483 if (m_systemTheme)
484 QQuickMaterialTheme::registerSystemStyle(style: this);
485 else
486 QQuickMaterialTheme::unregisterSystemStyle(style: this);
487 }
488
489 propagateTheme();
490 themeChange();
491 if (!m_customAccent)
492 accentChange();
493 if (!m_customBackground)
494 backgroundChange();
495 if (!m_customForeground)
496 foregroundChange();
497}
498
499void QQuickMaterialStyle::inheritTheme(Theme theme)
500{
501 const bool systemThemeChanged = (m_systemTheme != (theme == System));
502 const bool themeChanged = systemThemeChanged || (m_theme != effectiveTheme(theme));
503 if (m_explicitTheme || !themeChanged)
504 return;
505
506 m_theme = effectiveTheme(theme);
507 m_systemTheme = (theme == System);
508
509 propagateTheme();
510 themeChange();
511 if (!m_customAccent)
512 accentChange();
513 if (!m_customBackground)
514 backgroundChange();
515 if (!m_customForeground)
516 foregroundChange();
517}
518
519void QQuickMaterialStyle::propagateTheme()
520{
521 const auto styles = attachedChildren();
522 for (QQuickAttachedPropertyPropagator *child : styles) {
523 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: child);
524 if (material)
525 // m_theme is the effective theme, either Dark or Light.
526 // m_systemTheme indicates whether the theme is set by
527 // the system (true) or manually (false).
528 material->inheritTheme(theme: m_systemTheme ? System : m_theme);
529 }
530}
531
532void QQuickMaterialStyle::resetTheme()
533{
534 if (!m_explicitTheme)
535 return;
536
537 m_explicitTheme = false;
538 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: attachedParent());
539 inheritTheme(theme: material ? material->theme() : globalTheme);
540}
541
542void QQuickMaterialStyle::themeChange()
543{
544 emit themeChanged();
545 emit themeOrAccentChanged();
546 emit primaryHighlightedTextColor();
547 emit dialogColorChanged();
548 emit tooltipColorChanged();
549 emit toolBarColorChanged();
550 emit toolTextColorChanged();
551}
552
553QVariant QQuickMaterialStyle::primary() const
554{
555 return primaryColor();
556}
557
558void QQuickMaterialStyle::setPrimary(const QVariant &var)
559{
560 QRgb primary = 0;
561 bool custom = false;
562 if (!variantToRgba(var, name: "primary", rgba: &primary, custom: &custom))
563 return;
564
565 m_explicitPrimary = true;
566 if (m_primary == primary)
567 return;
568
569 m_customPrimary = custom;
570 m_primary = primary;
571 propagatePrimary();
572 primaryChange();
573}
574
575void QQuickMaterialStyle::inheritPrimary(uint primary, bool custom)
576{
577 if (m_explicitPrimary || m_primary == primary)
578 return;
579
580 m_customPrimary = custom;
581 m_primary = primary;
582 propagatePrimary();
583 primaryChange();
584}
585
586void QQuickMaterialStyle::propagatePrimary()
587{
588 const auto styles = attachedChildren();
589 for (QQuickAttachedPropertyPropagator *child : styles) {
590 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: child);
591 if (material)
592 material->inheritPrimary(primary: m_primary, custom: m_customPrimary);
593 }
594}
595
596void QQuickMaterialStyle::resetPrimary()
597{
598 if (!m_explicitPrimary)
599 return;
600
601 m_customPrimary = false;
602 m_explicitPrimary = false;
603 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: attachedParent());
604 if (material)
605 inheritPrimary(primary: material->m_primary, custom: material->m_customPrimary);
606 else
607 inheritPrimary(primary: globalPrimary, custom: false);
608}
609
610void QQuickMaterialStyle::primaryChange()
611{
612 emit primaryChanged();
613 emit toolBarColorChanged();
614 emit toolTextColorChanged();
615}
616
617QVariant QQuickMaterialStyle::accent() const
618{
619 return accentColor();
620}
621
622void QQuickMaterialStyle::setAccent(const QVariant &var)
623{
624 QRgb accent = 0;
625 bool custom = false;
626 if (!variantToRgba(var, name: "accent", rgba: &accent, custom: &custom))
627 return;
628
629 m_explicitAccent = true;
630 if (m_accent == accent)
631 return;
632
633 m_customAccent = custom;
634 m_accent = accent;
635 propagateAccent();
636 accentChange();
637}
638
639void QQuickMaterialStyle::inheritAccent(uint accent, bool custom)
640{
641 if (m_explicitAccent || m_accent == accent)
642 return;
643
644 m_customAccent = custom;
645 m_accent = accent;
646 propagateAccent();
647 accentChange();
648}
649
650void QQuickMaterialStyle::propagateAccent()
651{
652 const auto styles = attachedChildren();
653 for (QQuickAttachedPropertyPropagator *child : styles) {
654 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: child);
655 if (material)
656 material->inheritAccent(accent: m_accent, custom: m_customAccent);
657 }
658}
659
660void QQuickMaterialStyle::resetAccent()
661{
662 if (!m_explicitAccent)
663 return;
664
665 m_customAccent = false;
666 m_explicitAccent = false;
667 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: attachedParent());
668 if (material)
669 inheritAccent(accent: material->m_accent, custom: material->m_customAccent);
670 else
671 inheritAccent(accent: globalAccent, custom: false);
672}
673
674void QQuickMaterialStyle::accentChange()
675{
676 emit accentChanged();
677 emit themeOrAccentChanged();
678}
679
680QVariant QQuickMaterialStyle::foreground() const
681{
682 if (!m_hasForeground) {
683 if (!m_customBackground && m_background == m_primary) {
684 return toolTextColor();
685 }
686 return QColor::fromRgba(rgba: m_theme == Light ? primaryTextColorLight : primaryTextColorDark);
687 }
688 if (m_customForeground)
689 return QColor::fromRgba(rgba: m_foreground);
690 if (m_foreground > BlueGrey)
691 return QColor();
692 return QColor::fromRgba(rgba: colors[m_foreground][Shade500]);
693}
694
695void QQuickMaterialStyle::setForeground(const QVariant &var)
696{
697 QRgb foreground = 0;
698 bool custom = false;
699 if (!variantToRgba(var, name: "foreground", rgba: &foreground, custom: &custom))
700 return;
701
702 // Reset the foreground if the new color matches the current theme's default.
703 // This allows the color to update dynamically when the theme changes.
704 if (!hasGlobalForeground) {
705 const QRgb themeDefault = (m_theme == Light ? primaryTextColorLight : primaryTextColorDark);
706 if (foreground == themeDefault) {
707 resetForeground();
708 return;
709 }
710 }
711
712 m_hasForeground = true;
713 m_explicitForeground = true;
714 if (m_foreground == foreground)
715 return;
716
717 m_customForeground = custom;
718 m_foreground = foreground;
719 propagateForeground();
720 foregroundChange();
721}
722
723void QQuickMaterialStyle::inheritForeground(uint foreground, bool custom, bool has)
724{
725 if (m_explicitForeground || m_foreground == foreground)
726 return;
727
728 m_hasForeground = has;
729 m_customForeground = custom;
730 m_foreground = foreground;
731 propagateForeground();
732 foregroundChange();
733}
734
735void QQuickMaterialStyle::propagateForeground()
736{
737 const auto styles = attachedChildren();
738 for (QQuickAttachedPropertyPropagator *child : styles) {
739 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: child);
740 if (material)
741 material->inheritForeground(foreground: m_foreground, custom: m_customForeground, has: m_hasForeground);
742 }
743}
744
745void QQuickMaterialStyle::resetForeground()
746{
747 if (!m_explicitForeground)
748 return;
749
750 m_hasForeground = false;
751 m_customForeground = false;
752 m_explicitForeground = false;
753 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: attachedParent());
754 inheritForeground(foreground: material ? material->m_foreground : globalForeground, custom: true, has: material ? material->m_hasForeground : false);
755}
756
757void QQuickMaterialStyle::foregroundChange()
758{
759 emit foregroundChanged();
760 emit primaryHighlightedTextColorChanged();
761}
762
763QVariant QQuickMaterialStyle::background() const
764{
765 return backgroundColor();
766}
767
768void QQuickMaterialStyle::setBackground(const QVariant &var)
769{
770 QRgb background = 0;
771 bool custom = false;
772 if (!variantToRgba(var, name: "background", rgba: &background, custom: &custom))
773 return;
774
775 // Reset the background if the new color matches the current theme's default.
776 // This allows the color to update dynamically when the theme changes.
777 if (!hasGlobalBackground) {
778 const QRgb themeDefault = (m_theme == Light ? backgroundColorLight : backgroundColorDark);
779 if (background == themeDefault) {
780 resetBackground();
781 return;
782 }
783 }
784
785 // Do not treat the background as custom if it resolves to the same RGB as the
786 // current primary color. This allows the foreground color to map to
787 // toolTextColor, which is resolved dynamically based on the primary color.
788 if (custom && background == primaryColor().rgb()) {
789 background = m_primary;
790 custom = false;
791 }
792
793 m_hasBackground = true;
794 m_explicitBackground = true;
795 if (m_background == background)
796 return;
797
798 m_customBackground = custom;
799 m_background = background;
800 propagateBackground();
801 backgroundChange();
802
803 // Foreground color depends on the background color if the background is set to primary.
804 if (!m_customBackground && m_background == m_primary)
805 foregroundChange();
806}
807
808void QQuickMaterialStyle::inheritBackground(uint background, bool custom, bool has)
809{
810 if (m_explicitBackground || m_background == background)
811 return;
812
813 m_hasBackground = has;
814 m_customBackground = custom;
815 m_background = background;
816 propagateBackground();
817 backgroundChange();
818 if (!m_customBackground && m_background == m_primary)
819 foregroundChange();
820}
821
822void QQuickMaterialStyle::propagateBackground()
823{
824 const auto styles = attachedChildren();
825 for (QQuickAttachedPropertyPropagator *child : styles) {
826 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: child);
827 if (material)
828 material->inheritBackground(background: m_background, custom: m_customBackground, has: m_hasBackground);
829 }
830}
831
832void QQuickMaterialStyle::resetBackground()
833{
834 if (!m_explicitBackground)
835 return;
836
837 m_hasBackground = false;
838 m_customBackground = false;
839 m_explicitBackground = false;
840 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: attachedParent());
841 inheritBackground(background: material ? material->m_background : globalBackground, custom: true, has: material ? material->m_hasBackground : false);
842}
843
844void QQuickMaterialStyle::backgroundChange()
845{
846 emit backgroundChanged();
847 emit dialogColorChanged();
848 emit tooltipColorChanged();
849 emit toolBarColorChanged();
850}
851
852int QQuickMaterialStyle::elevation() const
853{
854 return m_elevation;
855}
856
857void QQuickMaterialStyle::setElevation(int elevation)
858{
859 if (m_elevation == elevation)
860 return;
861
862 m_elevation = elevation;
863 elevationChange();
864}
865
866void QQuickMaterialStyle::resetElevation()
867{
868 setElevation(0);
869}
870
871void QQuickMaterialStyle::elevationChange()
872{
873 emit elevationChanged();
874}
875
876QQuickMaterialStyle::RoundedScale QQuickMaterialStyle::roundedScale() const
877{
878 return m_roundedScale;
879}
880
881void QQuickMaterialStyle::setRoundedScale(RoundedScale roundedScale)
882{
883 if (m_roundedScale == roundedScale)
884 return;
885
886 m_roundedScale = roundedScale;
887 emit roundedScaleChanged();
888}
889
890void QQuickMaterialStyle::resetRoundedScale()
891{
892 setRoundedScale(RoundedScale::NotRounded);
893}
894
895QQuickMaterialStyle::ContainerStyle QQuickMaterialStyle::containerStyle() const
896{
897 return m_containerStyle;
898}
899
900void QQuickMaterialStyle::setContainerStyle(ContainerStyle containerStyle)
901{
902 if (m_containerStyle == containerStyle)
903 return;
904
905 m_containerStyle = containerStyle;
906 emit containerStyleChanged();
907}
908
909void QQuickMaterialStyle::resetContainerStyle()
910{
911 setContainerStyle(ContainerStyle::Filled);
912}
913
914QColor QQuickMaterialStyle::primaryColor() const
915{
916 if (m_customPrimary)
917 return QColor::fromRgba(rgba: m_primary);
918 if (m_primary > BlueGrey)
919 return QColor();
920 return colors[m_primary][Shade500];
921}
922
923QColor QQuickMaterialStyle::accentColor(Shade shade) const
924{
925 if (m_customAccent)
926 return shade == themeShade() ? QColor::fromRgba(rgba: m_accent)
927 : this->shade(color: QColor::fromRgba(rgba: m_accent), shade);
928 if (m_accent > BlueGrey)
929 return QColor();
930 return colors[m_accent][shade];
931}
932
933QColor QQuickMaterialStyle::accentColor() const
934{
935 return accentColor(shade: themeShade());
936}
937
938QColor QQuickMaterialStyle::backgroundColor(Shade shade) const
939{
940 if (!m_hasBackground)
941 return QColor::fromRgba(rgba: m_theme == Light ? backgroundColorLight : backgroundColorDark);
942 // The application bars (such as ToolBar) set the background color from the primary color,
943 // and in that case, it's better to validate with the custom primary and use it accordingly.
944 if (m_customBackground || (m_customPrimary && m_background == m_primary))
945 return shade == themeShade() ? QColor::fromRgba(rgba: m_background)
946 : this->shade(color: QColor::fromRgba(rgba: m_background), shade);
947 if (m_background > BlueGrey)
948 return QColor();
949 return colors[m_background][shade];
950}
951
952QColor QQuickMaterialStyle::backgroundColor() const
953{
954 return backgroundColor(shade: themeShade());
955}
956
957QColor QQuickMaterialStyle::primaryTextColor() const
958{
959 return QColor::fromRgba(rgba: m_theme == Light ? primaryTextColorLight : primaryTextColorDark);
960}
961
962QColor QQuickMaterialStyle::primaryHighlightedTextColor() const
963{
964 if (m_explicitForeground)
965 return primaryTextColor();
966 return QColor::fromRgba(rgba: primaryTextColorDark);
967}
968
969QColor QQuickMaterialStyle::secondaryTextColor() const
970{
971 return QColor::fromRgba(rgba: m_theme == Light ? secondaryTextColorLight : secondaryTextColorDark);
972}
973
974QColor QQuickMaterialStyle::hintTextColor() const
975{
976 return QColor::fromRgba(rgba: m_theme == Light ? hintTextColorLight : hintTextColorDark);
977}
978
979QColor QQuickMaterialStyle::textSelectionColor() const
980{
981 QColor color = accentColor();
982 color.setAlphaF(0.4f);
983 return color;
984}
985
986QColor QQuickMaterialStyle::dropShadowColor() const
987{
988 return QColor::fromRgba(rgba: 0x40000000);
989}
990
991QColor QQuickMaterialStyle::dividerColor() const
992{
993 return QColor::fromRgba(rgba: m_theme == Light ? dividerColorLight : dividerColorDark);
994}
995
996QColor QQuickMaterialStyle::iconColor() const
997{
998 return QColor::fromRgba(rgba: m_theme == Light ? iconColorLight : iconColorDark);
999}
1000
1001QColor QQuickMaterialStyle::iconDisabledColor() const
1002{
1003 return QColor::fromRgba(rgba: m_theme == Light ? iconDisabledColorLight : iconDisabledColorDark);
1004}
1005
1006QColor QQuickMaterialStyle::buttonColor(Theme theme, const QVariant &background, const QVariant &accent,
1007 bool enabled, bool flat, bool highlighted, bool checked) const
1008{
1009 if (!enabled && !flat) {
1010 return QColor::fromRgba(rgba: m_theme == Light
1011 ? raisedButtonDisabledColorLight : raisedButtonDisabledColorDark);
1012 }
1013
1014 // We don't use theme (and other arguments) here even though we pass it in, as it's
1015 // simpler to just re-use themeShade. We still need the arguments because they allow
1016 // us to be re-called whenever they change.
1017 Shade shade = themeShade();
1018 Q_UNUSED(theme);
1019 Q_UNUSED(background);
1020 Q_UNUSED(accent);
1021
1022 QColor color = Qt::transparent;
1023
1024 if (m_explicitBackground) {
1025 color = backgroundColor(shade);
1026 } else if (highlighted) {
1027 if (m_theme == Light) {
1028 color = accentColor(shade);
1029 if (checked)
1030 color = color.lighter();
1031 } else {
1032 // A highlighted + checked button should become darker.
1033 color = accentColor(shade: checked ? Shade100 : shade);
1034 }
1035 // Flat, highlighted buttons need to have a semi-transparent background,
1036 // otherwise the text won't be visible.
1037 if (flat)
1038 color.setAlphaF(0.25);
1039 } else if (!flat) {
1040 // Even if the elevation is zero, it should still have a background if it's not flat.
1041 color = QColor::fromRgba(rgba: m_theme == Light ? raisedButtonColorLight
1042 : raisedButtonColorDark);
1043 }
1044
1045 return color;
1046}
1047
1048QColor QQuickMaterialStyle::frameColor() const
1049{
1050 return QColor::fromRgba(rgba: m_theme == Light ? frameColorLight : frameColorDark);
1051}
1052
1053QColor QQuickMaterialStyle::rippleColor() const
1054{
1055 return QColor::fromRgba(rgba: m_theme == Light ? rippleColorLight : rippleColorDark);
1056}
1057
1058QColor QQuickMaterialStyle::highlightedRippleColor() const
1059{
1060 QColor pressColor = accentColor();
1061 pressColor.setAlpha(m_theme == Light ? 30 : 50);
1062 return pressColor;
1063}
1064
1065QColor QQuickMaterialStyle::switchUncheckedTrackColor() const
1066{
1067 return QColor::fromRgba(rgba: m_theme == Light ? switchUncheckedTrackColorLight : switchUncheckedTrackColorDark);
1068}
1069
1070QColor QQuickMaterialStyle::switchCheckedTrackColor() const
1071{
1072 return accentColor(shade: m_theme == Light ? themeShade() : Shade100);
1073}
1074
1075QColor QQuickMaterialStyle::switchDisabledUncheckedTrackColor() const
1076{
1077 return QColor::fromRgba(rgba: m_theme == Light
1078 ? switchDisabledUncheckedTrackColorLight : switchDisabledUncheckedTrackColorDark);
1079}
1080
1081QColor QQuickMaterialStyle::switchDisabledCheckedTrackColor() const
1082{
1083 return QColor::fromRgba(rgba: m_theme == Light
1084 ? switchDisabledCheckedTrackColorLight : switchDisabledCheckedTrackColorDark);
1085}
1086
1087QColor QQuickMaterialStyle::switchDisabledUncheckedTrackBorderColor() const
1088{
1089 return QColor::fromRgba(rgba: m_theme == Light
1090 ? switchDisabledUncheckedTrackBorderColorLight : switchDisabledUncheckedTrackBorderColorDark);
1091}
1092
1093QColor QQuickMaterialStyle::switchUncheckedHandleColor() const
1094{
1095 return m_theme == Light ? color(color: Grey, shade: Shade600) : color(color: Grey, shade: Shade400);
1096}
1097
1098QColor QQuickMaterialStyle::switchUncheckedHoveredHandleColor() const
1099{
1100 const QColor color = switchUncheckedHandleColor();
1101 return m_theme == Light ? color.darker(f: 140) : color.lighter(f: 120);
1102}
1103
1104QColor QQuickMaterialStyle::switchCheckedHandleColor() const
1105{
1106 return m_theme == Light ? QColor::fromRgb(rgb: 0xFFFFFF) : accentColor(shade: Shade800);
1107}
1108
1109QColor QQuickMaterialStyle::switchDisabledUncheckedHandleColor() const
1110{
1111 if (m_theme == Light)
1112 return QColor::fromRgba(rgba: 0x611C1B1F);
1113
1114 QColor darkHandleColor = color(color: Grey, shade: Shade800);
1115 darkHandleColor.setAlphaF(0.38f);
1116 return darkHandleColor;
1117}
1118
1119QColor QQuickMaterialStyle::switchDisabledCheckedHandleColor() const
1120{
1121 return QColor::fromRgb(rgb: m_theme == Light ? 0xFFFBFE : 0x1C1B1F);
1122}
1123
1124QColor QQuickMaterialStyle::switchDisabledCheckedIconColor() const
1125{
1126 return QColor::fromRgba(rgba: m_theme == Light ? 0x611C1B1F : 0x61E6E1E5);
1127}
1128
1129QColor QQuickMaterialStyle::switchDisabledUncheckedIconColor() const
1130{
1131 return QColor::fromRgba(rgba: m_theme == Light
1132 ? switchDisabledUncheckedIconColorLight : switchDisabledUncheckedIconColorDark);
1133}
1134
1135QColor QQuickMaterialStyle::scrollBarColor() const
1136{
1137 return QColor::fromRgba(rgba: m_theme == Light ? 0x40000000 : 0x40FFFFFF);
1138}
1139
1140QColor QQuickMaterialStyle::scrollBarHoveredColor() const
1141{
1142 return QColor::fromRgba(rgba: m_theme == Light ? 0x60000000 : 0x60FFFFFF);
1143}
1144
1145QColor QQuickMaterialStyle::scrollBarPressedColor() const
1146{
1147 return QColor::fromRgba(rgba: m_theme == Light ? 0x80000000 : 0x80FFFFFF);
1148}
1149
1150QColor QQuickMaterialStyle::dialogColor() const
1151{
1152 if (m_hasBackground)
1153 return backgroundColor();
1154 return QColor::fromRgba(rgba: m_theme == Light ? dialogColorLight : dialogColorDark);
1155}
1156
1157QColor QQuickMaterialStyle::backgroundDimColor() const
1158{
1159 return QColor::fromRgba(rgba: m_theme == Light ? 0x99303030 : 0x99fafafa);
1160}
1161
1162QColor QQuickMaterialStyle::listHighlightColor() const
1163{
1164 return QColor::fromRgba(rgba: m_theme == Light ? 0x1e000000 : 0x1effffff);
1165}
1166
1167QColor QQuickMaterialStyle::tooltipColor() const
1168{
1169 if (m_explicitBackground)
1170 return backgroundColor();
1171 return color(color: Grey, shade: Shade700);
1172}
1173
1174QColor QQuickMaterialStyle::toolBarColor() const
1175{
1176 if (m_explicitBackground)
1177 return backgroundColor();
1178 return primaryColor();
1179}
1180
1181QColor QQuickMaterialStyle::toolTextColor() const
1182{
1183 if (m_hasForeground || m_customPrimary)
1184 return primaryTextColor();
1185
1186 switch (m_primary) {
1187 case Red:
1188 case Pink:
1189 case Purple:
1190 case DeepPurple:
1191 case Indigo:
1192 case Blue:
1193 case Teal:
1194 case DeepOrange:
1195 case Brown:
1196 case BlueGrey:
1197 return QColor::fromRgba(rgba: primaryTextColorDark);
1198
1199 case LightBlue:
1200 case Cyan:
1201 case Green:
1202 case LightGreen:
1203 case Lime:
1204 case Yellow:
1205 case Amber:
1206 case Orange:
1207 case Grey:
1208 return QColor::fromRgba(rgba: primaryTextColorLight);
1209
1210 default:
1211 break;
1212 }
1213
1214 return primaryTextColor();
1215}
1216
1217QColor QQuickMaterialStyle::spinBoxDisabledIconColor() const
1218{
1219 return QColor::fromRgba(rgba: m_theme == Light ? spinBoxDisabledIconColorLight : spinBoxDisabledIconColorDark);
1220}
1221
1222QColor QQuickMaterialStyle::sliderDisabledColor() const
1223{
1224 return QColor::fromRgba(rgba: m_theme == Light ? sliderDisabledColorLight : sliderDisabledColorDark);
1225}
1226
1227QColor QQuickMaterialStyle::textFieldFilledContainerColor() const
1228{
1229 return QColor::fromRgba(rgba: m_theme == Light ? textFieldFilledContainerColorLight : textFieldFilledContainerColorDark);
1230}
1231
1232QColor QQuickMaterialStyle::color(QQuickMaterialStyle::Color color, QQuickMaterialStyle::Shade shade) const
1233{
1234 int count = sizeof(colors) / sizeof(colors[0]);
1235 if (color < 0 || color >= count)
1236 return QColor();
1237
1238 count = sizeof(colors[0]) / sizeof(colors[0][0]);
1239 if (shade < 0 || shade >= count)
1240 return QColor();
1241
1242 return colors[color][shade];
1243}
1244
1245static QColor lighterShade(const QColor &color, qreal amount)
1246{
1247 QColor hsl = color.toHsl();
1248 hsl.setHslF(h: hsl.hueF(), s: hsl.saturationF(),
1249 l: std::clamp(val: hsl.lightnessF() + amount, lo: qreal(0.0), hi: qreal(1.0)), a: color.alphaF());
1250 return hsl.convertTo(colorSpec: color.spec());
1251}
1252
1253static QColor darkerShade(const QColor &color, qreal amount)
1254{
1255 QColor hsl = color.toHsl();
1256 hsl.setHslF(h: hsl.hueF(), s: hsl.saturationF(),
1257 l: std::clamp(val: hsl.lightnessF() - amount, lo: qreal(0.0), hi: qreal(1.0)), a: color.alphaF());
1258 return hsl.convertTo(colorSpec: color.spec());
1259}
1260
1261QQuickMaterialStyle::Shade QQuickMaterialStyle::themeShade() const
1262{
1263 return m_theme == Light ? Shade500 : Shade200;
1264}
1265
1266/*
1267 * The following lightness values originate from the Material Design Color Generator project.
1268 *
1269 * The MIT License (MIT)
1270 *
1271 * Copyright (c) 2015 mbitson
1272 *
1273 * Permission is hereby granted, free of charge, to any person obtaining a copy
1274 * of this software and associated documentation files (the "Software"), to deal
1275 * in the Software without restriction, including without limitation the rights
1276 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1277 * copies of the Software, and to permit persons to whom the Software is
1278 * furnished to do so, subject to the following conditions:
1279 *
1280 * The above copyright notice and this permission notice shall be included in all
1281 * copies or substantial portions of the Software.
1282 *
1283 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1284 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1285 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1286 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1287 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1288 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1289 * SOFTWARE.
1290 */
1291
1292// Returns the same color, if shade == themeShade()
1293QColor QQuickMaterialStyle::shade(const QColor &color, Shade shade) const
1294{
1295 switch (shade) {
1296 case Shade50:
1297 return lighterShade(color, amount: m_theme == Light ? 0.52 : 0.26);
1298 case Shade100:
1299 return lighterShade(color, amount: m_theme == Light ? 0.37 : 0.11);
1300 case Shade200:
1301 return m_theme == Light ? lighterShade(color, amount: 0.26) : color;
1302 case Shade300:
1303 return m_theme == Light ? lighterShade(color, amount: 0.12) : darkerShade(color, amount: 0.14);
1304 case Shade400:
1305 return m_theme == Light ? lighterShade(color, amount: 0.06) : darkerShade(color, amount: 0.20);
1306 case Shade500:
1307 return m_theme == Light ? color : darkerShade(color, amount: 0.26);
1308 case Shade600:
1309 return darkerShade(color, amount: m_theme == Light ? 0.06 : 0.32);
1310 case Shade700:
1311 return darkerShade(color, amount: m_theme == Light ? 0.12 : 0.38);
1312 case Shade800:
1313 return darkerShade(color, amount: m_theme == Light ? 0.18 : 0.44);
1314 case Shade900:
1315 return darkerShade(color, amount: m_theme == Light ? 0.24 : 0.50);
1316 case ShadeA100:
1317 return lighterShade(color, amount: m_theme == Light ? 0.54 : 0.28);
1318 case ShadeA200:
1319 return lighterShade(color, amount: m_theme == Light ? 0.37 : 0.11);
1320 case ShadeA400:
1321 return m_theme == Light ? lighterShade(color, amount: 0.06) : darkerShade(color, amount: 0.20);
1322 case ShadeA700:
1323 return darkerShade(color, amount: m_theme == Light ? 0.12 : 0.38);
1324 default:
1325 Q_UNREACHABLE_RETURN(QColor());
1326 }
1327}
1328
1329int QQuickMaterialStyle::touchTarget() const
1330{
1331 // https://material.io/guidelines/components/buttons.html#buttons-style
1332 return globalVariant == Dense ? 44 : 48;
1333}
1334
1335int QQuickMaterialStyle::buttonVerticalPadding() const
1336{
1337 return globalVariant == Dense ? 10 : 14;
1338}
1339
1340// https://m3.material.io/components/buttons/specs#256326ad-f934-40e7-b05f-0bcb41aa4382
1341int QQuickMaterialStyle::buttonLeftPadding(bool flat, bool hasIcon) const
1342{
1343 static const int noIconPadding = globalVariant == Dense ? 12 : 24;
1344 static const int iconPadding = globalVariant == Dense ? 8 : 16;
1345 static const int flatPadding = globalVariant == Dense ? 6 : 12;
1346 return !flat ? (!hasIcon ? noIconPadding : iconPadding) : flatPadding;
1347}
1348
1349int QQuickMaterialStyle::buttonRightPadding(bool flat, bool hasIcon, bool hasText) const
1350{
1351 static const int noTextPadding = globalVariant == Dense ? 8 : 16;
1352 static const int textPadding = globalVariant == Dense ? 12 : 24;
1353 static const int flatNoIconPadding = globalVariant == Dense ? 6 : 12;
1354 static const int flatNoTextPadding = globalVariant == Dense ? 6 : 12;
1355 static const int flatTextPadding = globalVariant == Dense ? 8 : 16;
1356 return !flat
1357 ? (!hasText ? noTextPadding : textPadding)
1358 : (!hasIcon ? flatNoIconPadding : (!hasText ? flatNoTextPadding : flatTextPadding));
1359}
1360
1361int QQuickMaterialStyle::buttonHeight() const
1362{
1363 // https://m3.material.io/components/buttons/specs#256326ad-f934-40e7-b05f-0bcb41aa4382
1364 return globalVariant == Dense ? 32 : 40;
1365}
1366
1367int QQuickMaterialStyle::delegateHeight() const
1368{
1369 // https://material.io/guidelines/components/lists.html#lists-specs
1370 return globalVariant == Dense ? 40 : 48;
1371}
1372
1373int QQuickMaterialStyle::dialogButtonBoxHeight() const
1374{
1375 return globalVariant == Dense ? 48 : 52;
1376}
1377
1378int QQuickMaterialStyle::dialogTitleFontPixelSize() const
1379{
1380 return globalVariant == Dense ? 16 : 24;
1381}
1382
1383// https://m3.material.io/components/dialogs/specs#6771d107-624e-47cc-b6d8-2b7b620ba2f1
1384QQuickMaterialStyle::RoundedScale QQuickMaterialStyle::dialogRoundedScale() const
1385{
1386 return globalVariant == Dense
1387 ? QQuickMaterialStyle::RoundedScale::LargeScale
1388 : QQuickMaterialStyle::RoundedScale::ExtraLargeScale;
1389}
1390
1391int QQuickMaterialStyle::frameVerticalPadding() const
1392{
1393 return globalVariant == Dense ? 8 : 12;
1394}
1395
1396int QQuickMaterialStyle::menuItemHeight() const
1397{
1398 // https://material.io/guidelines/components/menus.html#menus-simple-menus
1399 return globalVariant == Dense ? 32 : 48;
1400}
1401
1402int QQuickMaterialStyle::menuItemVerticalPadding() const
1403{
1404 return globalVariant == Dense ? 8 : 12;
1405}
1406
1407int QQuickMaterialStyle::switchIndicatorWidth() const
1408{
1409 return globalVariant == Dense ? 40 : 52;
1410}
1411
1412int QQuickMaterialStyle::switchIndicatorHeight() const
1413{
1414 return globalVariant == Dense ? 22 : 32;
1415}
1416
1417int QQuickMaterialStyle::switchNormalHandleHeight() const
1418{
1419 return globalVariant == Dense ? 10 : 16;
1420}
1421
1422int QQuickMaterialStyle::switchCheckedHandleHeight() const
1423{
1424 return globalVariant == Dense ? 16 : 24;
1425}
1426
1427int QQuickMaterialStyle::switchLargestHandleHeight() const
1428{
1429 return globalVariant == Dense ? 18 : 28;
1430}
1431
1432int QQuickMaterialStyle::switchDelegateVerticalPadding() const
1433{
1434 // SwitchDelegate's indicator is much larger than the others due to the shadow,
1435 // so we must reduce its padding to ensure its implicitHeight is 40 when dense.
1436 return globalVariant == Dense ? 4 : 8;
1437}
1438
1439int QQuickMaterialStyle::textFieldHeight() const
1440{
1441 // filled: https://m3.material.io/components/text-fields/specs#8c032848-e442-46df-b25d-28f1315f234b
1442 // outlined: https://m3.material.io/components/text-fields/specs#605e24f1-1c1f-4c00-b385-4bf50733a5ef
1443 return globalVariant == Dense ? 44 : 56;
1444}
1445int QQuickMaterialStyle::textFieldHorizontalPadding() const
1446{
1447 return globalVariant == Dense ? 12 : 16;
1448}
1449int QQuickMaterialStyle::textFieldVerticalPadding() const
1450{
1451 return globalVariant == Dense ? 4 : 8;
1452}
1453
1454int QQuickMaterialStyle::tooltipHeight() const
1455{
1456 // https://material.io/guidelines/components/tooltips.html
1457 return globalVariant == Dense ? 22 : 32;
1458}
1459
1460QQuickMaterialStyle::Variant QQuickMaterialStyle::variant()
1461{
1462 return globalVariant;
1463}
1464
1465template <typename Enum>
1466static Enum toEnumValue(const QByteArray &value, bool *ok)
1467{
1468 QMetaEnum enumeration = QMetaEnum::fromType<Enum>();
1469 return static_cast<Enum>(enumeration.keyToValue(key: value, ok));
1470}
1471
1472static QByteArray resolveSetting(const QByteArray &env, const QSharedPointer<QSettings> &settings, const QString &name)
1473{
1474 QByteArray value = qgetenv(varName: env);
1475#if QT_CONFIG(settings)
1476 if (value.isNull() && !settings.isNull())
1477 value = settings->value(key: name).toByteArray();
1478#endif
1479 return value;
1480}
1481
1482void QQuickMaterialStyle::initGlobals()
1483{
1484 QSharedPointer<QSettings> settings = QQuickStylePrivate::settings(QStringLiteral("Material"));
1485
1486 bool ok = false;
1487 QByteArray themeValue = resolveSetting(env: "QT_QUICK_CONTROLS_MATERIAL_THEME", settings, QStringLiteral("Theme"));
1488 Theme themeEnum = toEnumValue<Theme>(value: themeValue, ok: &ok);
1489 if (ok)
1490 globalTheme = themeEnum;
1491 else if (!themeValue.isEmpty())
1492 qWarning().nospace().noquote() << "Material: unknown theme value: " << themeValue;
1493
1494 QByteArray variantValue = resolveSetting(env: "QT_QUICK_CONTROLS_MATERIAL_VARIANT", settings, QStringLiteral("Variant"));
1495 Variant variantEnum = toEnumValue<Variant>(value: variantValue, ok: &ok);
1496 if (ok)
1497 globalVariant = variantEnum;
1498 else if (!variantValue.isEmpty())
1499 qWarning().nospace().noquote() << "Material: unknown variant value: " << variantValue;
1500
1501 QByteArray primaryValue = resolveSetting(env: "QT_QUICK_CONTROLS_MATERIAL_PRIMARY", settings, QStringLiteral("Primary"));
1502 Color primaryEnum = toEnumValue<Color>(value: primaryValue, ok: &ok);
1503 if (ok) {
1504 globalPrimaryCustom = false;
1505 globalPrimary = primaryEnum;
1506 } else {
1507 QColor color = QColor::fromString(name: primaryValue);
1508 if (color.isValid()) {
1509 globalPrimaryCustom = true;
1510 globalPrimary = color.rgba();
1511 } else if (!primaryValue.isEmpty()) {
1512 qWarning().nospace().noquote() << "Material: unknown primary value: " << primaryValue;
1513 }
1514 }
1515
1516 QByteArray accentValue = resolveSetting(env: "QT_QUICK_CONTROLS_MATERIAL_ACCENT", settings, QStringLiteral("Accent"));
1517 Color accentEnum = toEnumValue<Color>(value: accentValue, ok: &ok);
1518 if (ok) {
1519 globalAccentCustom = false;
1520 globalAccent = accentEnum;
1521 } else if (!accentValue.isEmpty()) {
1522 QColor color = QColor::fromString(name: accentValue);
1523 if (color.isValid()) {
1524 globalAccentCustom = true;
1525 globalAccent = color.rgba();
1526 } else {
1527 qWarning().nospace().noquote() << "Material: unknown accent value: " << accentValue;
1528 }
1529 }
1530
1531 QByteArray foregroundValue = resolveSetting(env: "QT_QUICK_CONTROLS_MATERIAL_FOREGROUND", settings, QStringLiteral("Foreground"));
1532 Color foregroundEnum = toEnumValue<Color>(value: foregroundValue, ok: &ok);
1533 if (ok) {
1534 globalForegroundCustom = false;
1535 globalForeground = foregroundEnum;
1536 hasGlobalForeground = true;
1537 } else if (!foregroundValue.isEmpty()) {
1538 QColor color = QColor::fromString(name: foregroundValue);
1539 if (color.isValid()) {
1540 globalForegroundCustom = true;
1541 globalForeground = color.rgba();
1542 hasGlobalForeground = true;
1543 } else {
1544 qWarning().nospace().noquote() << "Material: unknown foreground value: " << foregroundValue;
1545 }
1546 }
1547
1548 QByteArray backgroundValue = resolveSetting(env: "QT_QUICK_CONTROLS_MATERIAL_BACKGROUND", settings, QStringLiteral("Background"));
1549 Color backgroundEnum = toEnumValue<Color>(value: backgroundValue, ok: &ok);
1550 if (ok) {
1551 globalBackgroundCustom = false;
1552 globalBackground = backgroundEnum;
1553 hasGlobalBackground = true;
1554 } else if (!backgroundValue.isEmpty()) {
1555 QColor color = QColor::fromString(name: backgroundValue);
1556 if (color.isValid()) {
1557 globalBackgroundCustom = true;
1558 globalBackground = color.rgba();
1559 hasGlobalBackground = true;
1560 } else {
1561 qWarning().nospace().noquote() << "Material: unknown background value: " << backgroundValue;
1562 }
1563 }
1564}
1565
1566void QQuickMaterialStyle::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent)
1567{
1568 Q_UNUSED(oldParent);
1569 QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(object: newParent);
1570 if (material) {
1571 inheritPrimary(primary: material->m_primary, custom: material->m_customPrimary);
1572 inheritAccent(accent: material->m_accent, custom: material->m_customAccent);
1573 inheritForeground(foreground: material->m_foreground, custom: material->m_customForeground, has: material->m_hasForeground);
1574 inheritBackground(background: material->m_background, custom: material->m_customBackground, has: material->m_hasBackground);
1575 inheritTheme(theme: material->theme());
1576 }
1577}
1578
1579bool QQuickMaterialStyle::variantToRgba(const QVariant &var, const char *name, QRgb *rgba, bool *custom) const
1580{
1581 *custom = false;
1582 if (var.metaType().id() == QMetaType::Int) {
1583 int val = var.toInt();
1584 if (val > BlueGrey) {
1585 qmlWarning(me: parent()) << "unknown Material." << name << " value: " << val;
1586 return false;
1587 }
1588 *rgba = val;
1589 } else {
1590 int val = QMetaEnum::fromType<Color>().keyToValue(key: var.toByteArray());
1591 if (val != -1) {
1592 *rgba = val;
1593 } else {
1594 QColor color = QColor::fromString(name: var.toString());
1595 if (!color.isValid()) {
1596 qmlWarning(me: parent()) << "unknown Material." << name << " value: " << var.toString();
1597 return false;
1598 }
1599 *custom = true;
1600 *rgba = color.rgba();
1601 }
1602 }
1603 return true;
1604}
1605
1606QT_END_NAMESPACE
1607
1608#include "moc_qquickmaterialstyle_p.cpp"
1609

source code of qtdeclarative/src/quickcontrols/material/qquickmaterialstyle.cpp