Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include <QFontDatabase>
24 : #include <QJsonArray>
25 : #include <QJsonDocument>
26 : #include <QJsonObject>
27 : #include <QScreen>
28 : #include <QSettings>
29 : #include <QApplication>
30 :
31 : #include "uiconfiguration.h"
32 : #include "global/configreader.h"
33 :
34 : #include "internal/baseapplication.h"
35 : #include "io/path.h"
36 : #include "io/fileinfo.h"
37 : #include "settings.h"
38 : #include "themeconverter.h"
39 :
40 : #include "imainwindow.h"
41 :
42 : #ifdef Q_OS_WIN
43 : #include <QOperatingSystemVersion>
44 : #endif
45 :
46 : #include "muse_framework_config.h"
47 :
48 : #include "log.h"
49 :
50 : using namespace Qt::Literals;
51 : using namespace muse;
52 : using namespace muse::ui;
53 : using namespace muse::async;
54 :
55 : static const Settings::Key UI_THEMES_KEY("ui", "ui/application/themes");
56 : static const Settings::Key UI_CURRENT_THEME_CODE_KEY("ui", "ui/application/currentThemeCode");
57 : static const Settings::Key UI_CUSTOM_COLORS_KEY("ui", "ui/application/customColors");
58 : static const Settings::Key UI_FOLLOW_SYSTEM_THEME_KEY("ui", "ui/application/followSystemTheme");
59 : static const Settings::Key UI_FONT_FAMILY_KEY("ui", "ui/theme/fontFamily");
60 : static const Settings::Key UI_FONT_SIZE_KEY("ui", "ui/theme/fontSize");
61 : static const Settings::Key UI_ICONS_FONT_FAMILY_KEY("ui", "ui/theme/iconsFontFamily");
62 : static const Settings::Key UI_MUSICAL_FONT_FAMILY_KEY("ui", "ui/theme/musicalFontFamily");
63 : static const Settings::Key UI_MUSICAL_FONT_SIZE_KEY("ui", "ui/theme/musicalFontSize");
64 : static const Settings::Key UI_MUSICAL_TEXT_FONT_FAMILY_KEY("ui", "ui/theme/musicalTextFontFamily");
65 : static const Settings::Key UI_MUSICAL_TEXT_FONT_SIZE_KEY("ui", "ui/theme/musicalTextFontSize");
66 :
67 : static const int FLICKABLE_MAX_VELOCITY = 4000;
68 :
69 : static const int TOOLTIP_DELAY = 500;
70 :
71 : // read custom colors saved by Qt < 6.9
72 : // see: https://github.com/qt/qtbase/blob/v6.2.4/src/gui/kernel/qplatformdialoghelper.cpp#L292-L302
73 0 : static std::vector<Val> readLegacyCustomColors()
74 : {
75 0 : constexpr size_t customColorCount = 16;
76 :
77 0 : QSettings settings(QSettings::UserScope, u"QtProject"_s);
78 0 : std::vector<Val> legacyValues(customColorCount, Val(QColorConstants::White));
79 0 : for (size_t i = 0; i < customColorCount; ++i) {
80 0 : const QVariant value = settings.value(u"Qt/customColors/"_s + QString::number(i));
81 0 : if (value.isValid()) {
82 0 : legacyValues[i] = Val(QColor::fromRgb(value.toUInt()));
83 : }
84 0 : }
85 :
86 0 : return legacyValues;
87 0 : }
88 :
89 0 : void UiConfiguration::init()
90 : {
91 0 : m_config = ConfigReader::read(":/configs/ui.cfg");
92 :
93 0 : settings()->setDefaultValue(UI_CURRENT_THEME_CODE_KEY, Val(LIGHT_THEME_CODE));
94 0 : settings()->setDefaultValue(UI_CUSTOM_COLORS_KEY, Val(readLegacyCustomColors()));
95 0 : settings()->setDefaultValue(UI_FOLLOW_SYSTEM_THEME_KEY, Val(false));
96 0 : settings()->setDefaultValue(UI_FONT_FAMILY_KEY, Val(defaultFontFamily()));
97 0 : settings()->setDefaultValue(UI_FONT_SIZE_KEY, Val(defaultFontSize()));
98 0 : settings()->setDefaultValue(UI_ICONS_FONT_FAMILY_KEY, Val("MusescoreIcon"));
99 0 : settings()->setDefaultValue(UI_MUSICAL_FONT_FAMILY_KEY, Val("Leland"));
100 0 : settings()->setDefaultValue(UI_MUSICAL_FONT_SIZE_KEY, Val(24));
101 0 : settings()->setDefaultValue(UI_MUSICAL_TEXT_FONT_FAMILY_KEY, Val("Leland Text"));
102 0 : settings()->setDefaultValue(UI_MUSICAL_TEXT_FONT_SIZE_KEY, Val(defaultFontSize()));
103 0 : settings()->setDefaultValue(UI_THEMES_KEY, Val(""));
104 :
105 0 : settings()->valueChanged(UI_THEMES_KEY).onReceive(this, [this](const Val&) {
106 0 : updateThemes();
107 0 : notifyAboutCurrentThemeChanged();
108 0 : });
109 :
110 0 : settings()->valueChanged(UI_CURRENT_THEME_CODE_KEY).onReceive(this, [this](const Val&) {
111 0 : notifyAboutCurrentThemeChanged();
112 0 : });
113 :
114 0 : settings()->valueChanged(UI_FOLLOW_SYSTEM_THEME_KEY).onReceive(this, [this](const Val& val) {
115 0 : m_isFollowSystemTheme.set(val.toBool());
116 0 : updateSystemThemeListeningStatus();
117 0 : });
118 :
119 0 : settings()->valueChanged(UI_FONT_FAMILY_KEY).onReceive(this, [this](const Val&) {
120 0 : m_fontChanged.notify();
121 0 : });
122 :
123 0 : settings()->valueChanged(UI_FONT_SIZE_KEY).onReceive(this, [this](const Val&) {
124 0 : m_fontChanged.notify();
125 0 : m_iconsFontChanged.notify();
126 0 : });
127 :
128 0 : settings()->valueChanged(UI_ICONS_FONT_FAMILY_KEY).onReceive(this, [this](const Val&) {
129 0 : m_iconsFontChanged.notify();
130 0 : });
131 :
132 0 : settings()->valueChanged(UI_MUSICAL_FONT_FAMILY_KEY).onReceive(this, [this](const Val&) {
133 0 : m_musicalFontChanged.notify();
134 0 : });
135 :
136 0 : settings()->valueChanged(UI_MUSICAL_FONT_SIZE_KEY).onReceive(this, [this](const Val&) {
137 0 : m_musicalFontChanged.notify();
138 0 : });
139 :
140 0 : platformTheme()->platformThemeChanged().onNotify(this, [this]() {
141 0 : synchThemeWithSystemIfNecessary();
142 0 : });
143 :
144 0 : m_themeWatcher.fileChanged().onReceive(this, [this](const std::string&){
145 0 : initThemes();
146 0 : notifyAboutCurrentThemeChanged();
147 0 : });
148 :
149 0 : correctUserFontIfNeeded();
150 :
151 0 : initThemes();
152 0 : }
153 :
154 0 : void UiConfiguration::deinit()
155 : {
156 0 : platformTheme()->stopListening();
157 0 : m_themeWatcher.stopWatching();
158 0 : }
159 :
160 0 : void UiConfiguration::initThemes()
161 : {
162 0 : m_isFollowSystemTheme.val = settings()->value(UI_FOLLOW_SYSTEM_THEME_KEY).toBool();
163 :
164 0 : updateSystemThemeListeningStatus();
165 :
166 0 : for (const ThemeCode& codeKey : allStandardThemeCodes()) {
167 0 : m_themes.push_back(makeStandardTheme(codeKey));
168 0 : }
169 :
170 0 : updateThemes();
171 0 : updateCurrentTheme();
172 0 : }
173 :
174 0 : void UiConfiguration::correctUserFontIfNeeded()
175 : {
176 0 : QString userFontFamily = QString::fromStdString(fontFamily());
177 0 : if (!QFontDatabase::hasFamily(userFontFamily)) {
178 0 : std::string fallbackFontFamily = defaultFontFamily();
179 0 : LOGI() << "The user font " << userFontFamily << " is missing, we will use the fallback font " << fallbackFontFamily;
180 :
181 0 : setFontFamily(fallbackFontFamily);
182 0 : }
183 0 : }
184 :
185 0 : void UiConfiguration::updateCurrentTheme()
186 : {
187 0 : ThemeCode currentCodeKey = currentThemeCodeKey();
188 :
189 0 : for (size_t i = 0; i < m_themes.size(); ++i) {
190 0 : if (m_themes[i].codeKey == currentCodeKey) {
191 0 : m_currentThemeIndex = i;
192 0 : break;
193 : }
194 : }
195 :
196 0 : platformTheme()->applyPlatformStyleOnAppForTheme(currentCodeKey);
197 0 : }
198 :
199 0 : void UiConfiguration::updateThemes()
200 : {
201 0 : ThemeList modifiedThemes = readThemes();
202 :
203 0 : if (modifiedThemes.empty()) {
204 0 : m_themes.clear();
205 0 : for (const ThemeCode& codeKey : allStandardThemeCodes()) {
206 0 : m_themes.push_back(makeStandardTheme(codeKey));
207 0 : }
208 :
209 0 : return;
210 : }
211 :
212 0 : for (ThemeInfo& theme: m_themes) {
213 0 : auto it = std::find_if(modifiedThemes.begin(), modifiedThemes.end(), [theme](const ThemeInfo& modifiedTheme) {
214 0 : return modifiedTheme.codeKey == theme.codeKey;
215 : });
216 :
217 0 : bool isModified = it != modifiedThemes.end();
218 0 : if (isModified) {
219 0 : for (auto key : it->values.keys()) {
220 0 : if (key != UNKNOWN) {
221 0 : theme.values[key] = it->values[key];
222 : }
223 0 : }
224 : }
225 : }
226 0 : }
227 :
228 0 : bool UiConfiguration::isFollowSystemThemeAvailable() const
229 : {
230 0 : return platformTheme()->isFollowSystemThemeAvailable();
231 : }
232 :
233 0 : ValNt<bool> UiConfiguration::isFollowSystemTheme() const
234 : {
235 0 : return m_isFollowSystemTheme;
236 : }
237 :
238 0 : void UiConfiguration::setFollowSystemTheme(bool follow)
239 : {
240 0 : settings()->setSharedValue(UI_FOLLOW_SYSTEM_THEME_KEY, Val(follow));
241 0 : }
242 :
243 0 : void UiConfiguration::updateSystemThemeListeningStatus()
244 : {
245 0 : if (isFollowSystemTheme().val) {
246 0 : platformTheme()->startListening();
247 0 : synchThemeWithSystemIfNecessary();
248 : } else {
249 0 : platformTheme()->stopListening();
250 : }
251 0 : }
252 :
253 0 : void UiConfiguration::synchThemeWithSystemIfNecessary()
254 : {
255 0 : if (!m_isFollowSystemTheme.val) {
256 0 : return;
257 : }
258 :
259 0 : doSetIsDarkMode(platformTheme()->isSystemThemeDark());
260 : }
261 :
262 0 : void UiConfiguration::notifyAboutCurrentThemeChanged()
263 : {
264 0 : updateCurrentTheme();
265 0 : m_currentThemeChanged.notify();
266 0 : }
267 :
268 0 : static QColor colorFromHex(const QString& hex)
269 : {
270 0 : if (!hex.startsWith('#')) {
271 0 : return QColor();
272 : }
273 :
274 0 : QString str = hex;
275 0 : if (str.length() == 9) { // #RRGGBBAA
276 : // Convert to Qt notation: #AARRGGBB
277 0 : QString alpha = str.mid(7, 2);
278 0 : str.chop(2);
279 0 : str.insert(1, alpha);
280 0 : }
281 :
282 0 : return QColor::fromString(str);
283 0 : }
284 :
285 0 : ThemeInfo UiConfiguration::makeStandardTheme(const ThemeCode& codeKey) const
286 : {
287 0 : ThemeInfo theme;
288 0 : theme.codeKey = codeKey;
289 :
290 0 : io::path_t themeFilePath = globalConfiguration()->appDataPath() + codeKey + ".cfg";
291 :
292 : // Hot reload is disabled in stable builds
293 0 : if (muse::BaseApplication::appUnstable() && io::FileInfo::exists(themeFilePath)) {
294 0 : m_themeWatcher.startWatching(themeFilePath.toStdString());
295 : } else {
296 0 : themeFilePath = ":/configs/" + codeKey + ".cfg";
297 : }
298 :
299 0 : Config config = ConfigReader::read(themeFilePath);
300 :
301 : theme.values = {
302 0 : { BACKGROUND_PRIMARY_COLOR, colorFromHex(config.value("background_primary_color").toQString()) },
303 0 : { BACKGROUND_SECONDARY_COLOR, colorFromHex(config.value("background_secondary_color").toQString()) },
304 0 : { BACKGROUND_TERTIARY_COLOR, colorFromHex(config.value("background_tertiary_color").toQString()) },
305 0 : { BACKGROUND_QUARTERNARY_COLOR, colorFromHex(config.value("background_quarternary_color").toQString()) },
306 0 : { POPUP_BACKGROUND_COLOR, colorFromHex(config.value("popup_background_color").toQString()) },
307 0 : { PROJECT_TAB_COLOR, colorFromHex(config.value("project_tab_color").toQString()) },
308 0 : { TEXT_FIELD_COLOR, colorFromHex(config.value("text_field_color").toQString()) },
309 0 : { ACCENT_COLOR, colorFromHex(config.value("accent_color").toQString()) },
310 0 : { STROKE_COLOR, colorFromHex(config.value("stroke_color").toQString()) },
311 0 : { STROKE_SECONDARY_COLOR, colorFromHex(config.value("stroke_secondary_color").toQString()) },
312 0 : { BUTTON_COLOR, colorFromHex(config.value("button_color").toQString()) },
313 0 : { FONT_PRIMARY_COLOR, colorFromHex(config.value("font_primary_color").toQString()) },
314 0 : { FONT_SECONDARY_COLOR, colorFromHex(config.value("font_secondary_color").toQString()) },
315 0 : { LINK_COLOR, colorFromHex(config.value("link_color").toQString()) },
316 0 : { FOCUS_COLOR, colorFromHex(config.value("focus_color").toQString()) },
317 0 : { WHITE_COLOR, colorFromHex(config.value("white_color").toQString()) },
318 0 : { BLACK_COLOR, colorFromHex(config.value("black_color").toQString()) },
319 0 : { PLAY_COLOR, colorFromHex(config.value("play_color").toQString()) },
320 0 : { RECORD_COLOR, colorFromHex(config.value("record_color").toQString()) },
321 :
322 0 : { BORDER_WIDTH, config.value("border_width").toDouble() },
323 0 : { NAVIGATION_CONTROL_BORDER_WIDTH, config.value("navigation_control_border_width").toDouble() },
324 :
325 0 : { ACCENT_OPACITY_NORMAL, config.value("accent_opacity_normal").toDouble() },
326 0 : { ACCENT_OPACITY_HOVER, config.value("accent_opacity_hover").toDouble() },
327 0 : { ACCENT_OPACITY_HIT, config.value("accent_opacity_hit").toDouble() },
328 :
329 0 : { BUTTON_OPACITY_NORMAL, config.value("button_opacity_normal").toDouble() },
330 0 : { BUTTON_OPACITY_HOVER, config.value("button_opacity_hover").toDouble() },
331 0 : { BUTTON_OPACITY_HIT, config.value("button_opacity_hit").toDouble() },
332 :
333 0 : { ITEM_OPACITY_DISABLED, config.value("item_opacity_disabled").toDouble() }
334 0 : };
335 :
336 0 : for (const auto& [key, val] : config) {
337 0 : if (val.type() == Val::Type::String) {
338 0 : QColor color = colorFromHex(val.toQString());
339 0 : if (color.isValid()) {
340 0 : theme.extra.insert(QString::fromStdString(key), color);
341 : } else {
342 0 : theme.extra.insert(QString::fromStdString(key), val.toQString());
343 : }
344 : } else {
345 0 : theme.extra.insert(QString::fromStdString(key), val.toDouble());
346 : }
347 : }
348 :
349 0 : return theme;
350 0 : }
351 :
352 0 : ThemeList UiConfiguration::readThemes() const
353 : {
354 0 : TRACEFUNC;
355 :
356 0 : ThemeList result;
357 :
358 0 : QByteArray json = QByteArray::fromStdString(settings()->value(UI_THEMES_KEY).toString());
359 0 : if (json.isEmpty()) {
360 0 : return result;
361 : }
362 :
363 0 : QJsonParseError err;
364 0 : QJsonDocument jsodDoc = QJsonDocument::fromJson(json, &err);
365 0 : if (err.error != QJsonParseError::NoError) {
366 0 : LOGE() << "Couldn't read themes: " << err.errorString();
367 0 : return result;
368 : }
369 :
370 0 : if (!jsodDoc.isArray()) {
371 0 : return result;
372 : }
373 :
374 0 : QVariantList objList = jsodDoc.array().toVariantList();
375 :
376 0 : for (const QVariant& themeObj: objList) {
377 0 : result.push_back(ThemeConverter::fromMap(themeObj.toMap()));
378 : }
379 :
380 0 : return result;
381 0 : }
382 :
383 0 : void UiConfiguration::writeThemes(const ThemeList& themes)
384 : {
385 0 : TRACEFUNC;
386 :
387 0 : QJsonArray jsonArray;
388 0 : for (const ThemeInfo& theme : themes) {
389 0 : QVariantMap map = ThemeConverter::toMap(theme);
390 0 : jsonArray << QJsonObject::fromVariantMap(map);
391 0 : }
392 :
393 0 : QJsonDocument jsonDoc(jsonArray);
394 :
395 0 : Val value(jsonDoc.toJson(QJsonDocument::Compact).constData());
396 0 : settings()->setSharedValue(UI_THEMES_KEY, value);
397 0 : }
398 :
399 0 : ThemeList UiConfiguration::themes() const
400 : {
401 0 : return m_themes;
402 : }
403 :
404 0 : QStringList UiConfiguration::possibleAccentColors() const
405 : {
406 : static const QStringList lightAccentColors {
407 : "#F28585",
408 : "#F9906F",
409 : "#F0A663",
410 : "#EDBE6E",
411 : "#DAD153",
412 : "#C0D84A",
413 : "#9DDA60",
414 : "#79D88E",
415 : "#67D2A4",
416 : "#51CAC2",
417 : "#46BCD3",
418 : "#70B8FA",
419 : "#8999FF",
420 : "#B69BFF",
421 : "#D093FB",
422 : "#E39CEB",
423 : "#F298D3",
424 : "#FF92B6"
425 0 : };
426 :
427 : static const QStringList darkAccentColors {
428 : "#D73838",
429 : "#DA5931",
430 : "#CA6100",
431 : "#AD720C",
432 : "#898006",
433 : "#728707",
434 : "#4B8C09",
435 : "#1D9337",
436 : "#0E935B",
437 : "#019086",
438 : "#018DA9",
439 : "#2082DD",
440 : "#5B6FF3",
441 : "#8D64FF",
442 : "#B252F7",
443 : "#D830EA",
444 : "#E03DAA",
445 : "#E9447B"
446 0 : };
447 :
448 0 : if (isDarkMode()) {
449 0 : return darkAccentColors;
450 : }
451 :
452 0 : return lightAccentColors;
453 : }
454 :
455 0 : QStringList UiConfiguration::possibleFontFamilies() const
456 : {
457 0 : QStringList allFonts = QFontDatabase::families();
458 0 : for (const QString& fontFamily : m_nonTextFonts) {
459 0 : allFonts.removeAll(fontFamily);
460 : }
461 0 : return allFonts;
462 0 : }
463 :
464 0 : QStringList UiConfiguration::nonTextFonts() const
465 : {
466 0 : return m_nonTextFonts;
467 : }
468 :
469 0 : void UiConfiguration::setNonTextFonts(const QStringList& fontFamilies)
470 : {
471 0 : m_nonTextFonts = fontFamilies;
472 0 : }
473 :
474 0 : void UiConfiguration::resetThemes()
475 : {
476 0 : m_themes.clear();
477 0 : for (const ThemeCode& codeKey : allStandardThemeCodes()) {
478 0 : m_themes.push_back(makeStandardTheme(codeKey));
479 0 : }
480 :
481 0 : writeThemes(m_themes);
482 :
483 0 : notifyAboutCurrentThemeChanged();
484 0 : }
485 :
486 0 : const ThemeInfo& UiConfiguration::currentTheme() const
487 : {
488 0 : return m_themes[m_currentThemeIndex];
489 : }
490 :
491 0 : ThemeCode UiConfiguration::currentThemeCodeKey() const
492 : {
493 0 : ThemeCode preferredThemeCode = settings()->value(UI_CURRENT_THEME_CODE_KEY).toString();
494 0 : return preferredThemeCode.empty() ? LIGHT_THEME_CODE : preferredThemeCode;
495 0 : }
496 :
497 0 : bool UiConfiguration::isDarkMode() const
498 : {
499 0 : return isDarkTheme(currentThemeCodeKey());
500 : }
501 :
502 0 : void UiConfiguration::setIsDarkMode(bool dark)
503 : {
504 0 : setFollowSystemTheme(false);
505 :
506 0 : doSetIsDarkMode(dark);
507 0 : }
508 :
509 0 : void UiConfiguration::doSetIsDarkMode(bool dark)
510 : {
511 0 : if (isHighContrast()) {
512 0 : doSetCurrentTheme(dark ? HIGH_CONTRAST_BLACK_THEME_CODE : HIGH_CONTRAST_WHITE_THEME_CODE);
513 : } else {
514 0 : doSetCurrentTheme(dark ? DARK_THEME_CODE : LIGHT_THEME_CODE);
515 : }
516 0 : }
517 :
518 0 : bool UiConfiguration::isHighContrast() const
519 : {
520 0 : return isHighContrastTheme(currentThemeCodeKey());
521 : }
522 :
523 0 : void UiConfiguration::setIsHighContrast(bool highContrast)
524 : {
525 0 : if (isDarkMode()) {
526 0 : doSetCurrentTheme(highContrast ? HIGH_CONTRAST_BLACK_THEME_CODE : DARK_THEME_CODE);
527 : } else {
528 0 : doSetCurrentTheme(highContrast ? HIGH_CONTRAST_WHITE_THEME_CODE : LIGHT_THEME_CODE);
529 : }
530 0 : }
531 :
532 0 : void UiConfiguration::setCurrentTheme(const ThemeCode& themeCode)
533 : {
534 0 : setFollowSystemTheme(false);
535 :
536 0 : doSetCurrentTheme(themeCode);
537 0 : }
538 :
539 0 : void UiConfiguration::doSetCurrentTheme(const ThemeCode& themeCode)
540 : {
541 0 : settings()->setSharedValue(UI_CURRENT_THEME_CODE_KEY, Val(themeCode));
542 0 : }
543 :
544 0 : void UiConfiguration::setCurrentThemeStyleValue(ThemeStyleKey key, const Val& val)
545 : {
546 0 : ThemeInfo& currentTheme = m_themes[m_currentThemeIndex];
547 0 : currentTheme.values[key] = val.toQVariant();
548 :
549 0 : ThemeList modifiedThemes = readThemes();
550 :
551 0 : auto it = std::find_if(modifiedThemes.begin(), modifiedThemes.end(), [currentTheme](const ThemeInfo& theme) {
552 0 : return theme.codeKey == currentTheme.codeKey;
553 : });
554 :
555 0 : if (it != modifiedThemes.end()) {
556 0 : modifiedThemes.erase(it);
557 : }
558 :
559 0 : modifiedThemes.push_back(currentTheme);
560 0 : writeThemes(modifiedThemes);
561 0 : }
562 :
563 0 : muse::async::Notification UiConfiguration::currentThemeChanged() const
564 : {
565 0 : return m_currentThemeChanged;
566 : }
567 :
568 0 : std::string UiConfiguration::fontFamily() const
569 : {
570 0 : return settings()->value(UI_FONT_FAMILY_KEY).toString();
571 : }
572 :
573 0 : void UiConfiguration::setFontFamily(const std::string& family)
574 : {
575 0 : settings()->setSharedValue(UI_FONT_FAMILY_KEY, Val(family));
576 0 : }
577 :
578 0 : int UiConfiguration::fontSize(FontSizeType type) const
579 : {
580 0 : int bodyFontSize = settings()->value(UI_FONT_SIZE_KEY).toInt();
581 :
582 : /*
583 : * DEFAULT SIZE:
584 : * body: 12
585 : * body large: 14
586 : * tab: 16
587 : * header: 22
588 : * title: 32
589 : */
590 0 : switch (type) {
591 0 : case FontSizeType::BODY: return bodyFontSize;
592 0 : case FontSizeType::BODY_LARGE: return bodyFontSize + bodyFontSize / 6;
593 0 : case FontSizeType::TAB: return bodyFontSize + bodyFontSize / 3;
594 0 : case FontSizeType::HEADER: return bodyFontSize + bodyFontSize / 1.2;
595 0 : case FontSizeType::TITLE: return bodyFontSize + bodyFontSize / 0.6;
596 : }
597 :
598 0 : return bodyFontSize;
599 : }
600 :
601 0 : void UiConfiguration::setBodyFontSize(int size)
602 : {
603 0 : settings()->setSharedValue(UI_FONT_SIZE_KEY, Val(size));
604 0 : }
605 :
606 0 : muse::async::Notification UiConfiguration::fontChanged() const
607 : {
608 0 : return m_fontChanged;
609 : }
610 :
611 0 : std::string UiConfiguration::iconsFontFamily() const
612 : {
613 0 : return settings()->value(UI_ICONS_FONT_FAMILY_KEY).toString();
614 : }
615 :
616 0 : int UiConfiguration::iconsFontSize(IconSizeType type) const
617 : {
618 0 : int bodyFontSize = fontSize(FontSizeType::BODY) + 4;
619 :
620 0 : switch (type) {
621 0 : case IconSizeType::Regular: return bodyFontSize;
622 0 : case IconSizeType::Toolbar: return bodyFontSize + 2;
623 : }
624 :
625 0 : return bodyFontSize;
626 : }
627 :
628 0 : muse::async::Notification UiConfiguration::iconsFontChanged() const
629 : {
630 0 : return m_iconsFontChanged;
631 : }
632 :
633 0 : io::path_t UiConfiguration::appIconPath() const
634 : {
635 0 : return m_config.value("appIconPath").toPath();
636 : }
637 :
638 0 : std::string UiConfiguration::musicalFontFamily() const
639 : {
640 0 : return settings()->value(UI_MUSICAL_FONT_FAMILY_KEY).toString();
641 : }
642 :
643 0 : int UiConfiguration::musicalFontSize() const
644 : {
645 0 : return settings()->value(UI_MUSICAL_FONT_SIZE_KEY).toInt();
646 : }
647 :
648 0 : muse::async::Notification UiConfiguration::musicalFontChanged() const
649 : {
650 0 : return m_musicalFontChanged;
651 : }
652 :
653 0 : std::string UiConfiguration::musicalTextFontFamily() const
654 : {
655 0 : return settings()->value(UI_MUSICAL_TEXT_FONT_FAMILY_KEY).toString();
656 : }
657 :
658 0 : int UiConfiguration::musicalTextFontSize() const
659 : {
660 0 : return settings()->value(UI_MUSICAL_TEXT_FONT_SIZE_KEY).toInt();
661 : }
662 :
663 0 : Notification UiConfiguration::musicalTextFontChanged() const
664 : {
665 0 : return m_musicalTextFontChanged;
666 : }
667 :
668 0 : std::string UiConfiguration::defaultFontFamily() const
669 : {
670 0 : return QFontDatabase::systemFont(QFontDatabase::GeneralFont).family().toStdString();
671 : }
672 :
673 0 : int UiConfiguration::defaultFontSize() const
674 : {
675 0 : return 12;
676 : }
677 :
678 0 : void UiConfiguration::resetFonts()
679 : {
680 0 : settings()->setSharedValue(UI_FONT_FAMILY_KEY, settings()->defaultValue(UI_FONT_FAMILY_KEY));
681 0 : settings()->setSharedValue(UI_FONT_SIZE_KEY, settings()->defaultValue(UI_FONT_SIZE_KEY));
682 0 : settings()->setSharedValue(UI_ICONS_FONT_FAMILY_KEY, settings()->defaultValue(UI_ICONS_FONT_FAMILY_KEY));
683 0 : settings()->setSharedValue(UI_MUSICAL_FONT_FAMILY_KEY, settings()->defaultValue(UI_MUSICAL_FONT_FAMILY_KEY));
684 0 : settings()->setSharedValue(UI_MUSICAL_FONT_SIZE_KEY, settings()->defaultValue(UI_MUSICAL_FONT_SIZE_KEY));
685 0 : }
686 :
687 0 : void UiConfiguration::setCustomPhysicalDotsPerInch(std::optional<double> dpi)
688 : {
689 0 : m_customDPI = dpi;
690 0 : }
691 :
692 0 : std::optional<double> UiConfiguration::customPhysicalDotsPerInch() const
693 : {
694 0 : return m_customDPI;
695 : }
696 :
697 0 : bool UiConfiguration::isGlobalMenuAvailable() const
698 : {
699 0 : return platformTheme()->isGlobalMenuAvailable();
700 : }
701 :
702 0 : bool UiConfiguration::isSystemDragSupported() const
703 : {
704 : #ifdef MUSE_MODULE_UI_SYSTEMDRAG_SUPPORTED
705 0 : return true;
706 : #else
707 : return false;
708 : #endif
709 : }
710 :
711 0 : void UiConfiguration::applyPlatformStyle(QWindow* window)
712 : {
713 0 : platformTheme()->applyPlatformStyleOnWindowForTheme(window, currentThemeCodeKey());
714 0 : }
715 :
716 0 : int UiConfiguration::flickableMaxVelocity() const
717 : {
718 0 : return FLICKABLE_MAX_VELOCITY;
719 : }
720 :
721 0 : int UiConfiguration::tooltipDelay() const
722 : {
723 0 : return TOOLTIP_DELAY;
724 : }
725 :
726 0 : std::vector<QColor> UiConfiguration::colorDialogCustomColors() const
727 : {
728 0 : const ValList colorVals = settings()->value(UI_CUSTOM_COLORS_KEY).toList();
729 :
730 0 : std::vector<QColor> customColors;
731 0 : customColors.reserve(colorVals.size());
732 0 : for (const auto& colorVal : colorVals) {
733 0 : customColors.push_back(colorVal.toQColor());
734 : }
735 :
736 0 : return customColors;
737 0 : }
738 :
739 0 : void UiConfiguration::setColorDialogCustomColors(const std::vector<QColor>& customColors)
740 : {
741 0 : ValList colorVals;
742 0 : colorVals.reserve(customColors.size());
743 0 : for (const auto& color: customColors) {
744 0 : colorVals.emplace_back(color);
745 : }
746 :
747 0 : settings()->setLocalValue(UI_CUSTOM_COLORS_KEY, Val(colorVals));
748 0 : }
|