1// Copyright (C) 2025 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 "qgnometheme_p.h"
5#include <qpa/qplatformdialoghelper.h>
6#include <qpa/qplatformfontdatabase.h>
7#if QT_CONFIG(dbus)
8# include <private/qdbustrayicon_p.h>
9# include <private/qdbusmenubar_p.h>
10#endif
11#include <qpa/qwindowsysteminterface.h>
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 \class QGnomeTheme
17 \brief QGnomeTheme is a theme implementation for the Gnome desktop.
18 \since 5.0
19 \internal
20 \ingroup qpa
21*/
22const char *QGnomeTheme::name = "gnome";
23
24QGnomeThemePrivate::QGnomeThemePrivate()
25{
26#if QT_CONFIG(dbus)
27 QObject::connect(sender: &m_gnomePortal, signal: &QGnomePortalInterface::themeNameChanged, context: &m_gnomePortal,
28 slot: [this](const QString &themeName) { m_themeName = themeName; });
29#endif // QT_CONFIG(dbus)
30}
31
32QGnomeThemePrivate::~QGnomeThemePrivate()
33{
34 if (systemFont)
35 delete systemFont;
36 if (fixedFont)
37 delete fixedFont;
38}
39
40void QGnomeThemePrivate::configureFonts(const QString &gtkFontName) const
41{
42 Q_ASSERT(!systemFont);
43 const int split = gtkFontName.lastIndexOf(c: QChar::Space);
44 float size = QStringView{gtkFontName}.mid(pos: split + 1).toFloat();
45 QString fontName = gtkFontName.left(n: split);
46
47 systemFont = new QFont(fontName, size);
48 fixedFont = new QFont(QLatin1StringView(QGenericUnixTheme::defaultFixedFontNameC), systemFont->pointSize());
49 fixedFont->setStyleHint(QFont::TypeWriter);
50 qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
51}
52
53Qt::ColorScheme QGnomeThemePrivate::colorScheme() const
54{
55 if (hasRequestedColorScheme())
56 return m_requestedColorScheme;
57
58#if QT_CONFIG(dbus)
59 if (Qt::ColorScheme colorScheme = m_gnomePortal.colorScheme();
60 colorScheme != Qt::ColorScheme::Unknown)
61 return colorScheme;
62
63 // If the color scheme is set to Unknown by mistake or is not set at all,
64 // then maybe the theme name contains a hint about the color scheme.
65 // Let's hope the theme name does not include any accent color name
66 // which contains "dark" or "light" in it (e.g. lightblue). At the moment they don't.
67 if (m_themeName.contains(s: QLatin1StringView("light"), cs: Qt::CaseInsensitive))
68 return Qt::ColorScheme::Light;
69 else if (m_themeName.contains(s: QLatin1StringView("dark"), cs: Qt::CaseInsensitive))
70 return Qt::ColorScheme::Dark;
71#endif // QT_CONFIG(dbus)
72
73 // Fallback to Unknown if no color scheme is set or detected
74 return Qt::ColorScheme::Unknown;
75}
76
77bool QGnomeThemePrivate::hasRequestedColorScheme() const
78{
79 return m_requestedColorScheme != Qt::ColorScheme::Unknown;
80}
81
82QGnomeTheme::QGnomeTheme()
83 : QGenericUnixTheme(new QGnomeThemePrivate())
84{
85#if QT_CONFIG(dbus)
86 Q_D(QGnomeTheme);
87
88 QGnomePortalInterface *portal = &d->m_gnomePortal;
89
90 QObject::connect(sender: portal, signal: &QGnomePortalInterface::colorSchemeChanged, context: portal,
91 slot: [this](Qt::ColorScheme colorScheme) { updateColorScheme(colorScheme); });
92
93 QObject::connect(sender: portal, signal: &QGnomePortalInterface::contrastChanged, context: portal,
94 slot: [this](Qt::ContrastPreference contrast) { updateHighContrast(contrast); });
95#endif // QT_CONFIG(dbus)
96}
97
98QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
99{
100 switch (hint) {
101 case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
102 return QVariant(true);
103 case QPlatformTheme::DialogButtonBoxLayout:
104 return QVariant(QPlatformDialogHelper::GnomeLayout);
105 case QPlatformTheme::SystemIconThemeName:
106 return QVariant(QStringLiteral("Adwaita"));
107 case QPlatformTheme::SystemIconFallbackThemeName:
108 return QVariant(QStringLiteral("gnome"));
109 case QPlatformTheme::IconThemeSearchPaths:
110 return QVariant(xdgIconThemePaths());
111 case QPlatformTheme::IconPixmapSizes:
112 return QVariant::fromValue(value: availableXdgFileIconSizes());
113 case QPlatformTheme::StyleNames: {
114 QStringList styleNames;
115 styleNames << QStringLiteral("Fusion") << QStringLiteral("windows");
116 return QVariant(styleNames);
117 }
118 case QPlatformTheme::KeyboardScheme:
119 return QVariant(int(GnomeKeyboardScheme));
120 case QPlatformTheme::PasswordMaskCharacter:
121 return QVariant(QChar(0x2022));
122 case QPlatformTheme::UiEffects:
123 return QVariant(int(HoverEffect));
124 case QPlatformTheme::ButtonPressKeys:
125 return QVariant::fromValue(
126 value: QList<Qt::Key>({ Qt::Key_Space, Qt::Key_Return, Qt::Key_Enter, Qt::Key_Select }));
127 case QPlatformTheme::PreselectFirstFileInDirectory:
128 return true;
129 case QPlatformTheme::MouseCursorTheme:
130 return QVariant(mouseCursorTheme());
131 case QPlatformTheme::MouseCursorSize:
132 return QVariant(mouseCursorSize());
133 case QPlatformTheme::PreferFileIconFromTheme:
134 return true;
135 default:
136 break;
137 }
138 return QPlatformTheme::themeHint(hint);
139}
140
141QIcon QGnomeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions) const
142{
143#if QT_CONFIG(mimetype)
144 return xdgFileIcon(fileInfo);
145#else
146 Q_UNUSED(fileInfo);
147 return QIcon();
148#endif
149}
150
151const QFont *QGnomeTheme::font(Font type) const
152{
153 Q_D(const QGnomeTheme);
154 if (!d->systemFont)
155 d->configureFonts(gtkFontName: gtkFontName());
156 switch (type) {
157 case QPlatformTheme::SystemFont:
158 return d->systemFont;
159 case QPlatformTheme::FixedFont:
160 return d->fixedFont;
161 default:
162 return nullptr;
163 }
164}
165
166QString QGnomeTheme::gtkFontName() const
167{
168 return QStringLiteral("%1 %2").arg(a: QLatin1StringView(defaultSystemFontNameC))
169 .arg(a: defaultSystemFontSize);
170}
171
172void QGnomeTheme::requestColorScheme(Qt::ColorScheme scheme)
173{
174 Q_D(QGnomeTheme);
175 if (d->m_requestedColorScheme == scheme)
176 return;
177 QPlatformTheme::requestColorScheme(scheme);
178 d->m_requestedColorScheme = scheme;
179 QWindowSystemInterface::handleThemeChange();
180}
181
182Qt::ColorScheme QGnomeTheme::colorScheme() const
183{
184 Q_D(const QGnomeTheme);
185 if (auto colorScheme = d->colorScheme(); colorScheme != Qt::ColorScheme::Unknown)
186 return colorScheme;
187 // If the color scheme is not set or detected, fall back to the default
188 return QPlatformTheme::colorScheme();
189}
190
191#if QT_CONFIG(dbus)
192void QGnomeTheme::updateColorScheme(Qt::ColorScheme colorScheme)
193{
194 Q_UNUSED(colorScheme);
195 QWindowSystemInterface::handleThemeChange();
196}
197
198void QGnomeTheme::updateHighContrast(Qt::ContrastPreference contrast)
199{
200 Q_UNUSED(contrast);
201 QWindowSystemInterface::handleThemeChange();
202}
203
204QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const
205{
206 if (isDBusGlobalMenuAvailable())
207 return new QDBusMenuBar();
208 return nullptr;
209}
210
211Qt::ContrastPreference QGnomeTheme::contrastPreference() const
212{
213 Q_D(const QGnomeTheme);
214 return d->m_gnomePortal.contrastPreference();
215}
216
217# if QT_CONFIG(systemtrayicon)
218QPlatformSystemTrayIcon *QGnomeTheme::createPlatformSystemTrayIcon() const
219{
220 if (shouldUseDBusTray())
221 return new QDBusTrayIcon();
222 return nullptr;
223}
224# endif // QT_CONFIG(systemtrayicon)
225#endif // QT_CONFIG(dbus)
226
227QString QGnomeTheme::standardButtonText(int button) const
228{
229 switch (button) {
230 case QPlatformDialogHelper::Ok:
231 return QCoreApplication::translate(context: "QGnomeTheme", key: "&OK");
232 case QPlatformDialogHelper::Save:
233 return QCoreApplication::translate(context: "QGnomeTheme", key: "&Save");
234 case QPlatformDialogHelper::Cancel:
235 return QCoreApplication::translate(context: "QGnomeTheme", key: "&Cancel");
236 case QPlatformDialogHelper::Close:
237 return QCoreApplication::translate(context: "QGnomeTheme", key: "&Close");
238 case QPlatformDialogHelper::Discard:
239 return QCoreApplication::translate(context: "QGnomeTheme", key: "Close without Saving");
240 default:
241 break;
242 }
243 return QPlatformTheme::standardButtonText(button);
244}
245
246QT_END_NAMESPACE
247

source code of qtbase/src/gui/platform/unix/qgnometheme.cpp