Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgtk3theme.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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// Qt-Security score:significant reason:default
4
5#include "qgtk3theme.h"
7#include <QVariant>
8#include <QGuiApplication>
9#include <qpa/qwindowsysteminterface.h>
10
11#undef signals
12#include <gtk/gtk.h>
13
14#if QT_CONFIG(xcb_xlib)
15#include <X11/Xlib.h>
16#endif
17
19
20using namespace Qt::StringLiterals;
21
22const char *QGtk3Theme::name = "gtk3";
23
24template <typename T>
25static T gtkSetting(const gchar *propertyName)
26{
27 GtkSettings *settings = gtk_settings_get_default();
28 T value;
29 g_object_get(settings, propertyName, &value, NULL);
30 return value;
31}
32
33static QString gtkSetting(const gchar *propertyName)
34{
35 gchararray value = gtkSetting<gchararray>(propertyName);
36 QString str = QString::fromUtf8(value);
37 g_free(value);
38 return str;
39}
40
41void gtkMessageHandler(const gchar *log_domain,
42 GLogLevelFlags log_level,
43 const gchar *message,
44 gpointer unused_data) {
45 /* Silence false-positive Gtk warnings (we are using Xlib to set
46 * the WM_TRANSIENT_FOR hint).
47 */
48 if (g_strcmp0(message, "GtkDialog mapped without a transient parent. "
49 "This is discouraged.") != 0) {
50 /* For other messages, call the default handler. */
51 g_log_default_handler(log_domain, log_level, message, unused_data);
52 }
53}
54
56{
57 // Ensure gtk uses the same windowing system, but let it
58 // fallback in case GDK_BACKEND environment variable
59 // filters the preferred one out
60 if (QGuiApplication::platformName().startsWith("wayland"_L1))
61 gdk_set_allowed_backends("wayland,x11");
62 else if (QGuiApplication::platformName() == "xcb"_L1)
63 gdk_set_allowed_backends("x11,wayland");
64
65#if QT_CONFIG(xcb_xlib)
66 // gtk_init will reset the Xlib error handler, and that causes
67 // Qt applications to quit on X errors. Therefore, we need to manually restore it.
68 int (*oldErrorHandler)(Display *, XErrorEvent *) = XSetErrorHandler(nullptr);
69#endif
70
71 gtk_init(nullptr, nullptr);
72
73#if QT_CONFIG(xcb_xlib)
74 XSetErrorHandler(oldErrorHandler);
75#endif
76
77 /* Initialize some types here so that Gtk+ does not crash when reading
78 * the treemodel for GtkFontChooser.
79 */
80 g_type_ensure(PANGO_TYPE_FONT_FAMILY);
81 g_type_ensure(PANGO_TYPE_FONT_FACE);
82
83 /* Use our custom log handler. */
84 g_log_set_handler("Gtk", G_LOG_LEVEL_MESSAGE, gtkMessageHandler, nullptr);
85
86#define SETTING_CONNECT(setting) g_signal_connect(settings, "notify::" setting, G_CALLBACK(notifyThemeChanged), nullptr)
87 auto notifyThemeChanged = [] {
88 QWindowSystemInterface::handleThemeChange();
89 };
90
91 GtkSettings *settings = gtk_settings_get_default();
92 SETTING_CONNECT("gtk-cursor-blink");
93 SETTING_CONNECT("gtk-cursor-blink-time");
94 SETTING_CONNECT("gtk-double-click-distance");
95 SETTING_CONNECT("gtk-double-click-time");
96 SETTING_CONNECT("gtk-long-press-time");
97 SETTING_CONNECT("gtk-entry-password-hint-timeout");
98 SETTING_CONNECT("gtk-dnd-drag-threshold");
99 SETTING_CONNECT("gtk-icon-theme-name");
100 SETTING_CONNECT("gtk-fallback-icon-theme");
101 SETTING_CONNECT("gtk-font-name");
102 SETTING_CONNECT("gtk-application-prefer-dark-theme");
103 SETTING_CONNECT("gtk-theme-name");
104 SETTING_CONNECT("gtk-cursor-theme-name");
105 SETTING_CONNECT("gtk-cursor-theme-size");
106#undef SETTING_CONNECT
107
108 m_storage.reset(new QGtk3Storage);
109}
110
112{
113 const char *gtk_long_press_time = "gtk-long-press-time";
114 static bool found = g_object_class_find_property(G_OBJECT_GET_CLASS(gtk_settings_get_default()), gtk_long_press_time);
115 if (!found)
116 return QVariant();
117 return QVariant(gtkSetting<guint>(gtk_long_press_time)); // Since 3.14, apparently we support >= 3.6
118}
119
120QVariant QGtk3Theme::themeHint(QPlatformTheme::ThemeHint hint) const
121{
122 switch (hint) {
123 case QPlatformTheme::CursorFlashTime:
124 if (gtkSetting<gboolean>("gtk-cursor-blink"))
125 return QVariant(gtkSetting<gint>("gtk-cursor-blink-time"));
126 else
127 return 0;
128 case QPlatformTheme::MouseDoubleClickDistance:
129 return QVariant(gtkSetting<gint>("gtk-double-click-distance"));
130 case QPlatformTheme::MouseDoubleClickInterval:
131 return QVariant(gtkSetting<gint>("gtk-double-click-time"));
132 case QPlatformTheme::MousePressAndHoldInterval: {
133 QVariant v = gtkGetLongPressTime();
134 if (!v.isValid())
135 v = QGnomeTheme::themeHint(hint);
136 return v;
137 }
138 case QPlatformTheme::PasswordMaskDelay:
139 return QVariant(gtkSetting<guint>("gtk-entry-password-hint-timeout"));
140 case QPlatformTheme::StartDragDistance:
141 return QVariant(gtkSetting<gint>("gtk-dnd-drag-threshold"));
142 case QPlatformTheme::SystemIconThemeName:
143 return QVariant(gtkSetting("gtk-icon-theme-name"));
144 case QPlatformTheme::SystemIconFallbackThemeName:
145 return QVariant(gtkSetting("gtk-fallback-icon-theme"));
146 case QPlatformTheme::MouseCursorTheme:
147 return QVariant(gtkSetting("gtk-cursor-theme-name"));
148 case QPlatformTheme::MouseCursorSize: {
149 int s = gtkSetting<gint>("gtk-cursor-theme-size");
150 if (s > 0)
151 return QVariant(QSize(s, s));
152 return QGnomeTheme::themeHint(hint);
153 }
154 default:
155 return QGnomeTheme::themeHint(hint);
156 }
157}
158
160{
161 QString cfgFontName = gtkSetting("gtk-font-name");
162 if (!cfgFontName.isEmpty())
163 return cfgFontName;
164 return QGnomeTheme::gtkFontName();
165}
166
168{
169 Q_ASSERT(m_storage);
170
171 Q_D(const QGnomeTheme);
172 const Qt::ColorScheme colorScheme = d->colorScheme();
173 const bool hasRequestedColorScheme = d->hasRequestedColorScheme();
174
175#ifdef QT_DEBUG
176 if (hasRequestedColorScheme && colorScheme != m_storage->colorScheme()) {
177 qCDebug(lcQGtk3Interface) << "Requested color scheme" << colorScheme
178 << "differs from theme color scheme" << m_storage->colorScheme();
179 }
180#endif
181
182 return hasRequestedColorScheme ? colorScheme : m_storage->colorScheme();
183}
184
185void QGtk3Theme::requestColorScheme(Qt::ColorScheme scheme)
186{
187 const Qt::ColorScheme oldColorScheme = colorScheme();
188 QGnomeTheme::requestColorScheme(scheme);
189 if (oldColorScheme == colorScheme())
190 return;
191 qCDebug(lcQGtk3Interface) << scheme << "has been requested. Theme supports color scheme:"
192 << m_storage->colorScheme();
193 m_storage->handleThemeChange();
194 QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents);
195}
196
197bool QGtk3Theme::usePlatformNativeDialog(DialogType type) const
198{
199 switch (type) {
200 case ColorDialog:
201 return true;
202 case FileDialog:
203 return useNativeFileDialog();
204 case FontDialog:
205 return true;
206 default:
207 return false;
208 }
209}
210
212{
213 switch (type) {
214 case ColorDialog:
215 return new QGtk3ColorDialogHelper;
216 case FileDialog:
217 if (!useNativeFileDialog())
218 return nullptr;
219 return new QGtk3FileDialogHelper;
220 case FontDialog:
221 return new QGtk3FontDialogHelper;
222 default:
223 return nullptr;
224 }
225}
226
227bool QGtk3Theme::useNativeFileDialog()
228{
229 /* Require GTK3 >= 3.15.5 to avoid running into this bug:
230 * https://bugzilla.gnome.org/show_bug.cgi?id=725164
231 *
232 * While this bug only occurs when using widget-based file dialogs
233 * (native GTK3 dialogs are fine) we have to disable platform file
234 * dialogs entirely since we can't avoid creation of a platform
235 * dialog helper.
236 */
237 return gtk_check_version(3, 15, 5) == nullptr;
238}
239
240const QPalette *QGtk3Theme::palette(Palette type) const
241{
242 Q_ASSERT(m_storage);
243
244 Q_D(const QGnomeTheme);
245 const Qt::ColorScheme colorScheme = d->colorScheme();
246 const bool hasRequestedColorScheme = d->hasRequestedColorScheme();
247
248#ifdef QT_DEBUG
249 if (hasRequestedColorScheme && colorScheme != m_storage->colorScheme()) {
250 qCDebug(lcQGtk3Interface) << "Current KDE theme doesn't support requested color scheme"
251 << colorScheme << "Falling back to fusion palette.";
252 return QPlatformTheme::palette(type);
253 }
254#endif
255
256 return (hasRequestedColorScheme && colorScheme != m_storage->colorScheme())
257 ? QPlatformTheme::palette(type)
258 : m_storage->palette(type);
259}
260
261QPixmap QGtk3Theme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
262{
263 Q_ASSERT(m_storage);
264 return m_storage->standardPixmap(sp, size);
265}
266
267const QFont *QGtk3Theme::font(Font type) const
268{
269 Q_ASSERT(m_storage);
270 return m_storage->font(type);
271}
272
273QIcon QGtk3Theme::fileIcon(const QFileInfo &fileInfo,
274 QPlatformTheme::IconOptions iconOptions) const
275{
276 Q_UNUSED(iconOptions);
277 Q_ASSERT(m_storage);
278 return m_storage->fileIcon(fileInfo);
279}
280
281#if QT_CONFIG(dbus)
282void QGtk3Theme::updateColorScheme(Qt::ColorScheme newColorScheme)
283{
284 if (newColorScheme == colorScheme())
285 QGnomeTheme::updateColorScheme(newColorScheme);
286 else
287 m_storage->handleThemeChange();
288}
289#endif // QT_CONFIG(dbus)
290
291QT_END_NAMESPACE
QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions={ }) const override
Return an icon for fileInfo, observing iconOptions.
Qt::ColorScheme colorScheme() const override
bool usePlatformNativeDialog(DialogType type) const override
virtual QVariant themeHint(ThemeHint hint) const override
void requestColorScheme(Qt::ColorScheme scheme) override
QPlatformDialogHelper * createPlatformDialogHelper(DialogType type) const override
static const char * name
Definition qgtk3theme.h:34
const QPalette * palette(Palette type=SystemPalette) const override
Return a color palette for type type.
virtual QString gtkFontName() const override
QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override
Return a pixmap for standardPixmap, at the given size.
const QFont * font(Font type=SystemFont) const override
void gtkMessageHandler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data)
static QString gtkSetting(const gchar *propertyName)
static QVariant gtkGetLongPressTime()
static T gtkSetting(const gchar *propertyName)
#define SETTING_CONNECT(setting)