2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2023-2024 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2019, 2021 Prince Gupta <jagannatharjun11@gmail.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 * In addition, as a special exception, the copyright holders give permission to
22 * link this program with the OpenSSL project's "OpenSSL" library (or with
23 * modified versions of it that use the same license as the "OpenSSL" library),
24 * and distribute the linked executables. You must obey the GNU General Public
25 * License in all respects for all of the code used other than "OpenSSL". If
26 * you modify file(s), you may extend this exception to your version of the
27 * file(s), but you are not obligated to do so. If you do not wish to do so,
28 * delete this exception statement from your version.
31 #include "uithememanager.h"
33 #include <QApplication>
35 #include <QPixmapCache>
38 #include <QStyleHints>
40 #include "base/global.h"
41 #include "base/logger.h"
42 #include "base/path.h"
43 #include "base/preferences.h"
44 #include "uithemecommon.h"
50 const QPalette palette
= qApp
->palette();
51 const QColor
&color
= palette
.color(QPalette::Active
, QPalette::Base
);
52 return (color
.lightness() < 127);
56 UIThemeManager
*UIThemeManager::m_instance
= nullptr;
58 void UIThemeManager::freeInstance()
64 void UIThemeManager::initInstance()
67 m_instance
= new UIThemeManager
;
70 UIThemeManager::UIThemeManager()
71 : m_useCustomTheme
{Preferences::instance()->useCustomUITheme()}
72 #ifdef QBT_HAS_COLORSCHEME_OPTION
73 , m_colorSchemeSetting
{u
"Appearance/ColorScheme"_s
}
75 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
76 , m_useSystemIcons
{Preferences::instance()->useSystemIcons()}
80 if (const QString styleName
= Preferences::instance()->getStyle(); styleName
.compare(u
"system", Qt::CaseInsensitive
) != 0)
82 if (!QApplication::setStyle(styleName
.isEmpty() ? u
"Fusion"_s
: styleName
))
83 LogMsg(tr("Set app style failed. Unknown style: \"%1\"").arg(styleName
), Log::WARNING
);
87 #ifdef QBT_HAS_COLORSCHEME_OPTION
91 // NOTE: Qt::QueuedConnection can be omitted as soon as support for Qt 6.5 is dropped
92 connect(QApplication::styleHints(), &QStyleHints::colorSchemeChanged
, this, &UIThemeManager::onColorSchemeChanged
, Qt::QueuedConnection
);
96 const Path themePath
= Preferences::instance()->customUIThemePath();
98 if (themePath
.hasExtension(u
".qbtheme"_s
))
100 if (QResource::registerResource(themePath
.data(), u
"/uitheme"_s
))
101 m_themeSource
= std::make_unique
<QRCThemeSource
>();
103 LogMsg(tr("Failed to load UI theme from file: \"%1\"").arg(themePath
.toString()), Log::WARNING
);
105 else if (themePath
.filename() == CONFIG_FILE_NAME
)
107 m_themeSource
= std::make_unique
<FolderThemeSource
>(themePath
.parentPath());
112 m_themeSource
= std::make_unique
<DefaultThemeSource
>();
114 if (m_useCustomTheme
)
121 UIThemeManager
*UIThemeManager::instance()
126 #ifdef QBT_HAS_COLORSCHEME_OPTION
127 ColorScheme
UIThemeManager::colorScheme() const
129 return m_colorSchemeSetting
.get(ColorScheme::System
);
132 void UIThemeManager::setColorScheme(const ColorScheme value
)
134 if (value
== colorScheme())
137 m_colorSchemeSetting
= value
;
140 void UIThemeManager::applyColorScheme() const
142 switch (colorScheme())
144 case ColorScheme::System
:
146 qApp
->styleHints()->unsetColorScheme();
148 case ColorScheme::Light
:
149 qApp
->styleHints()->setColorScheme(Qt::ColorScheme::Light
);
151 case ColorScheme::Dark
:
152 qApp
->styleHints()->setColorScheme(Qt::ColorScheme::Dark
);
158 void UIThemeManager::applyStyleSheet() const
160 qApp
->setStyleSheet(QString::fromUtf8(m_themeSource
->readStyleSheet()));
163 void UIThemeManager::onColorSchemeChanged()
167 // workaround to refresh styled controls once color scheme is changed
168 QApplication::setStyle(QApplication::style()->name());
171 QIcon
UIThemeManager::getIcon(const QString
&iconId
, [[maybe_unused
]] const QString
&fallback
) const
173 const auto colorMode
= isDarkTheme() ? ColorMode::Dark
: ColorMode::Light
;
174 auto &icons
= (colorMode
== ColorMode::Dark
) ? m_darkModeIcons
: m_icons
;
176 const auto iter
= icons
.find(iconId
);
177 if (iter
!= icons
.end())
180 #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
181 // Don't cache system icons because users might change them at run time
182 if (m_useSystemIcons
)
184 auto icon
= QIcon::fromTheme(iconId
);
185 if (icon
.isNull() || icon
.availableSizes().isEmpty())
186 icon
= QIcon::fromTheme(fallback
, QIcon(m_themeSource
->getIconPath(iconId
, colorMode
).data()));
191 const QIcon icon
{m_themeSource
->getIconPath(iconId
, colorMode
).data()};
192 icons
[iconId
] = icon
;
196 QIcon
UIThemeManager::getFlagIcon(const QString
&countryIsoCode
) const
198 if (countryIsoCode
.isEmpty())
201 const QString key
= countryIsoCode
.toLower();
202 const auto iter
= m_flags
.find(key
);
203 if (iter
!= m_flags
.end())
206 const QIcon icon
{u
":/icons/flags/" + key
+ u
".svg"};
211 QPixmap
UIThemeManager::getScaledPixmap(const QString
&iconId
, const int height
) const
213 // (workaround) svg images require the use of `QIcon()` to load and scale losslessly,
214 // otherwise other image classes will convert it to pixmap first and follow-up scaling will become lossy.
216 Q_ASSERT(height
> 0);
218 const QString cacheKey
= iconId
+ u
'@' + QString::number(height
);
221 if (!QPixmapCache::find(cacheKey
, &pixmap
))
223 pixmap
= getIcon(iconId
).pixmap(height
);
224 QPixmapCache::insert(cacheKey
, pixmap
);
230 QColor
UIThemeManager::getColor(const QString
&id
) const
232 const QColor color
= m_themeSource
->getColor(id
, (isDarkTheme() ? ColorMode::Dark
: ColorMode::Light
));
236 void UIThemeManager::applyPalette() const
238 struct ColorDescriptor
241 QPalette::ColorRole colorRole
;
242 QPalette::ColorGroup colorGroup
;
245 const ColorDescriptor paletteColorDescriptors
[] =
247 {u
"Palette.Window"_s
, QPalette::Window
, QPalette::Normal
},
248 {u
"Palette.WindowText"_s
, QPalette::WindowText
, QPalette::Normal
},
249 {u
"Palette.Base"_s
, QPalette::Base
, QPalette::Normal
},
250 {u
"Palette.AlternateBase"_s
, QPalette::AlternateBase
, QPalette::Normal
},
251 {u
"Palette.Text"_s
, QPalette::Text
, QPalette::Normal
},
252 {u
"Palette.ToolTipBase"_s
, QPalette::ToolTipBase
, QPalette::Normal
},
253 {u
"Palette.ToolTipText"_s
, QPalette::ToolTipText
, QPalette::Normal
},
254 {u
"Palette.BrightText"_s
, QPalette::BrightText
, QPalette::Normal
},
255 {u
"Palette.Highlight"_s
, QPalette::Highlight
, QPalette::Normal
},
256 {u
"Palette.HighlightedText"_s
, QPalette::HighlightedText
, QPalette::Normal
},
257 {u
"Palette.Button"_s
, QPalette::Button
, QPalette::Normal
},
258 {u
"Palette.ButtonText"_s
, QPalette::ButtonText
, QPalette::Normal
},
259 {u
"Palette.Link"_s
, QPalette::Link
, QPalette::Normal
},
260 {u
"Palette.LinkVisited"_s
, QPalette::LinkVisited
, QPalette::Normal
},
261 {u
"Palette.Light"_s
, QPalette::Light
, QPalette::Normal
},
262 {u
"Palette.Midlight"_s
, QPalette::Midlight
, QPalette::Normal
},
263 {u
"Palette.Mid"_s
, QPalette::Mid
, QPalette::Normal
},
264 {u
"Palette.Dark"_s
, QPalette::Dark
, QPalette::Normal
},
265 {u
"Palette.Shadow"_s
, QPalette::Shadow
, QPalette::Normal
},
266 {u
"Palette.WindowTextDisabled"_s
, QPalette::WindowText
, QPalette::Disabled
},
267 {u
"Palette.TextDisabled"_s
, QPalette::Text
, QPalette::Disabled
},
268 {u
"Palette.ToolTipTextDisabled"_s
, QPalette::ToolTipText
, QPalette::Disabled
},
269 {u
"Palette.BrightTextDisabled"_s
, QPalette::BrightText
, QPalette::Disabled
},
270 {u
"Palette.HighlightedTextDisabled"_s
, QPalette::HighlightedText
, QPalette::Disabled
},
271 {u
"Palette.ButtonTextDisabled"_s
, QPalette::ButtonText
, QPalette::Disabled
}
274 QPalette palette
= qApp
->palette();
275 for (const ColorDescriptor
&colorDescriptor
: paletteColorDescriptors
)
277 // For backward compatibility, the palette color overrides are read from the section of the "light mode" colors
278 const QColor newColor
= m_themeSource
->getColor(colorDescriptor
.id
, ColorMode::Light
);
279 if (newColor
.isValid())
280 palette
.setColor(colorDescriptor
.colorGroup
, colorDescriptor
.colorRole
, newColor
);
283 qApp
->setPalette(palette
);