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
qgtk3interface.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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//
6// W A R N I N G
7// -------------
8//
9// This file is not part of the Qt API. It exists purely as an
10// implementation detail. This header file may change from version to
11// version without notice, or even be removed.
12//
13// We mean it.
14//
15
16
18#include "qgtk3storage_p.h"
19#include <QtCore/QMetaEnum>
20#include <QtCore/QFileInfo>
21#include <QtGui/QFontDatabase>
22
24Q_LOGGING_CATEGORY(lcQGtk3Interface, "qt.qpa.gtk");
25
26
27// Callback for gnome event loop has to be static
28static QGtk3Storage *m_storage = nullptr;
29
30QGtk3Interface::QGtk3Interface(QGtk3Storage *s)
31{
32 initColorMap();
33
34 if (!s) {
35 qCDebug(lcQGtk3Interface) << "QGtk3Interface instantiated without QGtk3Storage."
36 << "No reaction to runtime theme changes.";
37 return;
38 }
39
40 // Connect to the GTK settings changed signal
41 auto handleThemeChange = [] {
42 if (m_storage)
44 };
45
46 GtkSettings *settings = gtk_settings_get_default();
47 const gboolean success = g_signal_connect(settings, "notify::gtk-theme-name",
48 G_CALLBACK(handleThemeChange), nullptr);
49 if (success == FALSE) {
50 qCDebug(lcQGtk3Interface) << "Connection to theme change signal failed."
51 << "No reaction to runtime theme changes.";
52 } else {
53 m_storage = s;
54 }
55}
56
58{
59 // Ignore theme changes when destructor is reached
60 m_storage = nullptr;
61
62 // QGtkWidgets have to be destroyed manually
63 for (auto v : cache)
64 gtk_widget_destroy(v.second);
65}
66
67/*!
68 \internal
69 \brief Converts a string into the GtkStateFlags enum.
70
71 Converts a string formatted GTK color \param state into an enum value.
72 Returns an integer corresponding to GtkStateFlags.
73 Returns -1 if \param state does not correspond to a valid enum key.
74 */
75int QGtk3Interface::toGtkState(const QString &state)
76{
77#define CASE(x)
78 if (QLatin1String(QByteArray(state.toLatin1())) == #x ##_L1)
79 return GTK_STATE_FLAG_ ##x
80
81#define CONVERT
82 CASE(NORMAL);
83 CASE(ACTIVE);
84 CASE(PRELIGHT);
85 CASE(SELECTED);
86 CASE(INSENSITIVE);
87 CASE(INCONSISTENT);
88 CASE(FOCUSED);
89 CASE(BACKDROP);
90 CASE(DIR_LTR);
91 CASE(DIR_RTL);
92 CASE(LINK);
93 CASE(VISITED);
94 CASE(CHECKED);
95 CASE(DROP_ACTIVE)
96
97 CONVERT;
98 return -1;
99#undef CASE
100}
101
102/*!
103 \internal
104 \brief Returns \param state converted into a string.
105 */
106const QLatin1String QGtk3Interface::fromGtkState(GtkStateFlags state)
107{
108#define CASE(x) case GTK_STATE_FLAG_ ##x: return QLatin1String(#x)
109 switch (state) {
110 CONVERT;
111 }
112 Q_UNREACHABLE();
113#undef CASE
114#undef CONVERT
115}
116
117/*!
118 \internal
119 \brief Populates the internal map used to find a GTK color's source and fallback generic color.
120 */
121void QGtk3Interface::initColorMap()
122{
123 #define SAVE(src, state, prop, def)
124 {ColorKey({QGtkColorSource::src, GTK_STATE_FLAG_ ##state}), ColorValue({#prop ##_L1, QGtkColorDefault::def})}
125
126 gtkColorMap = ColorMap {
127 SAVE(Foreground, NORMAL, theme_fg_color, Foreground),
128 SAVE(Foreground, BACKDROP, theme_unfocused_selected_fg_color, Foreground),
129 SAVE(Foreground, INSENSITIVE, insensitive_fg_color, Foreground),
130 SAVE(Foreground, SELECTED, theme_selected_fg_color, Foreground),
131 SAVE(Foreground, ACTIVE, theme_unfocused_fg_color, Foreground),
132 SAVE(Text, NORMAL, theme_text_color, Foreground),
133 SAVE(Text, ACTIVE, theme_unfocused_text_color, Foreground),
134 SAVE(Base, NORMAL, theme_base_color, Background),
135 SAVE(Base, INSENSITIVE, insensitive_base_color, Background),
136 SAVE(Background, NORMAL, theme_bg_color, Background),
137 SAVE(Background, SELECTED, theme_selected_bg_color, Background),
138 SAVE(Background, INSENSITIVE, insensitive_bg_color, Background),
139 SAVE(Background, ACTIVE, theme_unfocused_bg_color, Background),
140 SAVE(Background, BACKDROP, theme_unfocused_selected_bg_color, Background),
141 SAVE(Border, NORMAL, borders, Border),
142 SAVE(Border, ACTIVE, unfocused_borders, Border)
143 };
144#undef SAVE
145
146 qCDebug(lcQGtk3Interface) << "Color map populated from defaults.";
147}
148
149/*!
150 \internal
151 \brief Returns a QImage corresponding to \param standardPixmap.
152
153 A QImage (not a QPixmap) is returned so it can be cached and re-scaled in case the pixmap is
154 requested multiple times with different resolutions.
155
156 \note Rather than defaulting to a QImage(), all QPlatformTheme::StandardPixmap enum values have
157 been mentioned explicitly.
158 That way they can be covered more easily in case additional icons are provided by GTK.
159 */
160QImage QGtk3Interface::standardPixmap(QPlatformTheme::StandardPixmap standardPixmap) const
161{
162 switch (standardPixmap) {
163 case QPlatformTheme::DialogDiscardButton:
164 return qt_gtk_get_icon(GTK_STOCK_DELETE);
165 case QPlatformTheme::DialogOkButton:
166 return qt_gtk_get_icon(GTK_STOCK_OK);
167 case QPlatformTheme::DialogCancelButton:
168 return qt_gtk_get_icon(GTK_STOCK_CANCEL);
169 case QPlatformTheme::DialogYesButton:
170 return qt_gtk_get_icon(GTK_STOCK_YES);
171 case QPlatformTheme::DialogNoButton:
172 return qt_gtk_get_icon(GTK_STOCK_NO);
173 case QPlatformTheme::DialogOpenButton:
174 return qt_gtk_get_icon(GTK_STOCK_OPEN);
175 case QPlatformTheme::DialogCloseButton:
176 return qt_gtk_get_icon(GTK_STOCK_CLOSE);
177 case QPlatformTheme::DialogApplyButton:
178 return qt_gtk_get_icon(GTK_STOCK_APPLY);
179 case QPlatformTheme::DialogSaveButton:
180 return qt_gtk_get_icon(GTK_STOCK_SAVE);
181 case QPlatformTheme::MessageBoxWarning:
182 return qt_gtk_get_icon(GTK_STOCK_DIALOG_WARNING);
183 case QPlatformTheme::MessageBoxQuestion:
184 return qt_gtk_get_icon(GTK_STOCK_DIALOG_QUESTION);
185 case QPlatformTheme::MessageBoxInformation:
186 return qt_gtk_get_icon(GTK_STOCK_DIALOG_INFO);
187 case QPlatformTheme::MessageBoxCritical:
188 return qt_gtk_get_icon(GTK_STOCK_DIALOG_ERROR);
189 case QPlatformTheme::CustomBase:
190 case QPlatformTheme::TitleBarMenuButton:
191 case QPlatformTheme::TitleBarMinButton:
192 case QPlatformTheme::TitleBarMaxButton:
193 case QPlatformTheme::TitleBarCloseButton:
194 case QPlatformTheme::TitleBarNormalButton:
195 case QPlatformTheme::TitleBarShadeButton:
196 case QPlatformTheme::TitleBarUnshadeButton:
197 case QPlatformTheme::TitleBarContextHelpButton:
198 case QPlatformTheme::DockWidgetCloseButton:
199 case QPlatformTheme::DesktopIcon:
200 case QPlatformTheme::TrashIcon:
201 case QPlatformTheme::ComputerIcon:
202 case QPlatformTheme::DriveFDIcon:
203 case QPlatformTheme::DriveHDIcon:
204 case QPlatformTheme::DriveCDIcon:
205 case QPlatformTheme::DriveDVDIcon:
206 case QPlatformTheme::DriveNetIcon:
207 case QPlatformTheme::DirOpenIcon:
208 case QPlatformTheme::DirClosedIcon:
209 case QPlatformTheme::DirLinkIcon:
210 case QPlatformTheme::DirLinkOpenIcon:
211 case QPlatformTheme::FileIcon:
212 case QPlatformTheme::FileLinkIcon:
213 case QPlatformTheme::ToolBarHorizontalExtensionButton:
214 case QPlatformTheme::ToolBarVerticalExtensionButton:
215 case QPlatformTheme::FileDialogStart:
216 case QPlatformTheme::FileDialogEnd:
217 case QPlatformTheme::FileDialogToParent:
218 case QPlatformTheme::FileDialogNewFolder:
219 case QPlatformTheme::FileDialogDetailedView:
220 case QPlatformTheme::FileDialogInfoView:
221 case QPlatformTheme::FileDialogContentsView:
222 case QPlatformTheme::FileDialogListView:
223 case QPlatformTheme::FileDialogBack:
224 case QPlatformTheme::DirIcon:
225 case QPlatformTheme::DialogHelpButton:
226 case QPlatformTheme::DialogResetButton:
227 case QPlatformTheme::ArrowUp:
228 case QPlatformTheme::ArrowDown:
229 case QPlatformTheme::ArrowLeft:
230 case QPlatformTheme::ArrowRight:
231 case QPlatformTheme::ArrowBack:
232 case QPlatformTheme::ArrowForward:
233 case QPlatformTheme::DirHomeIcon:
234 case QPlatformTheme::CommandLink:
235 case QPlatformTheme::VistaShield:
236 case QPlatformTheme::BrowserReload:
237 case QPlatformTheme::BrowserStop:
238 case QPlatformTheme::MediaPlay:
239 case QPlatformTheme::MediaStop:
240 case QPlatformTheme::MediaPause:
241 case QPlatformTheme::MediaSkipForward:
242 case QPlatformTheme::MediaSkipBackward:
243 case QPlatformTheme::MediaSeekForward:
244 case QPlatformTheme::MediaSeekBackward:
245 case QPlatformTheme::MediaVolume:
246 case QPlatformTheme::MediaVolumeMuted:
247 case QPlatformTheme::LineEditClearButton:
248 case QPlatformTheme::DialogYesToAllButton:
249 case QPlatformTheme::DialogNoToAllButton:
250 case QPlatformTheme::DialogSaveAllButton:
251 case QPlatformTheme::DialogAbortButton:
252 case QPlatformTheme::DialogRetryButton:
253 case QPlatformTheme::DialogIgnoreButton:
254 case QPlatformTheme::RestoreDefaultsButton:
255 case QPlatformTheme::TabCloseButton:
256 case QPlatformTheme::NStandardPixmap:
257 return QImage();
258 }
259 Q_UNREACHABLE();
260}
261
262/*!
263 \internal
264 \brief Returns a QImage for a given GTK \param iconName.
265 */
266QImage QGtk3Interface::qt_gtk_get_icon(const char* iconName) const
267{
268 GtkIconSet* iconSet = gtk_icon_factory_lookup_default (iconName);
269 GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context(), GTK_ICON_SIZE_DIALOG);
270 return qt_convert_gdk_pixbuf(icon);
271}
272
273/*!
274 \internal
275 \brief Returns a QImage converted from the GDK pixel buffer \param buf.
276
277 The ability to convert GdkPixbuf to QImage relies on the following assumptions:
278 \list
279 \li QImage uses uchar as a data container (unasserted)
280 \li the types guint8 and uchar are identical (statically asserted)
281 \li GDK pixel buffer uses 8 bits per sample (assumed at runtime)
282 \li GDK pixel buffer has 4 channels (assumed at runtime)
283 \endlist
284 */
285QImage QGtk3Interface::qt_convert_gdk_pixbuf(GdkPixbuf *buf) const
286{
287 if (!buf)
288 return QImage();
289
290 const guint8 *gdata = gdk_pixbuf_read_pixels(buf);
291 static_assert(std::is_same<decltype(gdata), const uchar *>::value,
292 "guint8 has diverted from uchar. Code needs fixing.");
293 Q_ASSERT(gdk_pixbuf_get_bits_per_sample(buf) == 8);
294 Q_ASSERT(gdk_pixbuf_get_n_channels(buf) == 4);
295 const uchar *data = static_cast<const uchar *>(gdata);
296
297 const int width = gdk_pixbuf_get_width(buf);
298 const int height = gdk_pixbuf_get_height(buf);
299 const int bpl = gdk_pixbuf_get_rowstride(buf);
300 QImage converted(data, width, height, bpl, QImage::Format_RGBA8888);
301
302 // convert to more optimal format and detach to survive lifetime of buf
303 return converted.convertToFormat(QImage::Format_ARGB32_Premultiplied);
304}
305
306/*!
307 \internal
308 \brief Instantiate a new GTK widget.
309
310 Returns a pointer to a new GTK widget of \param type, allocated on the heap.
311 Returns nullptr of gtk_Default has is passed.
312 */
313GtkWidget *QGtk3Interface::qt_new_gtkWidget(QGtkWidget type) const
314{
315#define CASE(Type)
316 case QGtkWidget::Type: return Type ##_new();
317#define CASEN(Type)
318 case QGtkWidget::Type: return Type ##_new(nullptr);
319
320 switch (type) {
321 CASE(gtk_menu_bar)
322 CASE(gtk_menu)
323 CASE(gtk_button)
324 case QGtkWidget::gtk_button_box: return gtk_button_box_new(GtkOrientation::GTK_ORIENTATION_HORIZONTAL);
325 CASE(gtk_check_button)
326 CASEN(gtk_radio_button)
327 CASEN(gtk_frame)
328 CASE(gtk_statusbar)
329 CASE(gtk_entry)
330 case QGtkWidget::gtk_popup: return gtk_window_new(GTK_WINDOW_POPUP);
331 CASE(gtk_notebook)
332 CASE(gtk_toolbar)
333 CASE(gtk_tree_view)
334 CASE(gtk_combo_box)
335 CASE(gtk_combo_box_text)
336 CASE(gtk_progress_bar)
337 CASE(gtk_fixed)
338 CASE(gtk_separator_menu_item)
339 CASE(gtk_offscreen_window)
340 case QGtkWidget::gtk_Default: return nullptr;
341 }
342#undef CASE
343#undef CASEN
344 Q_UNREACHABLE();
345}
346
347/*!
348 \internal
349 \brief Read a GTK widget's color from a generic color getter.
350
351 This method returns a generic color of \param con, a given GTK style context.
352 The requested color is defined by \param def and the GTK color-state \param state.
353 The return type is GDK color in RGBA format.
354 */
355GdkRGBA QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def) const
356{
357 GdkRGBA color;
358
359#define CASE(def, call)
360 case QGtkColorDefault::def:
361 gtk_style_context_get_ ##call(con, state, &color);
362 break;
363
364 switch (def) {
365 CASE(Foreground, color)
366 CASE(Background, background_color)
367 CASE(Border, border_color)
368 }
369 return color;
370#undef CASE
371}
372
373/*!
374 \internal
375 \brief Read a GTK widget's color from a property.
376
377 Returns a color of GTK-widget \param widget, defined by \param source and \param state.
378 The return type is GDK color in RGBA format.
379
380 \note If no corresponding property can be found for \param source, the method falls back to a
381 suitable generic color.
382 */
383QColor QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkStateFlags state) const
384{
385 GdkRGBA col;
386 GtkStyleContext *con = context(widget);
387
388#define CASE(src, def)
389 case QGtkColorSource::src: {
390 const ColorKey key = ColorKey({QGtkColorSource::src, state});
391 if (gtkColorMap.contains(key)) {
392 const ColorValue val = gtkColorMap.value(key);
393 if (!gtk_style_context_lookup_color(con, val.propertyName.toUtf8().constData(), &col)) {
394 col = genericColor(con, state, val.genericSource);
395 qCDebug(lcQGtk3Interface) << "Property name" << val.propertyName << "not found.\n"
396 << "Falling back to " << val.genericSource;
397 }
398 } else {
399 col = genericColor(con, state, QGtkColorDefault::def);
400 qCDebug(lcQGtk3Interface) << "No color source found for" << QGtkColorSource::src
401 << fromGtkState(state) << "\n Falling back to"
402 << QGtkColorDefault::def;
403 }
404 }
405 break;
406
407 switch (source) {
408 CASE(Foreground, Foreground)
409 CASE(Background, Background)
410 CASE(Text, Foreground)
411 CASE(Base, Background)
412 CASE(Border, Border)
413 }
414
415 return fromGdkColor(col);
416#undef CASE
417}
418
419/*!
420 \internal
421 \brief Get pointer to a GTK widget by \param type.
422
423 Returns the pointer to a GTK widget, specified by \param type.
424 GTK widgets are cached, so that only one instance of each type is created.
425 \note
426 The method returns nullptr for the enum value gtk_Default.
427 */
428GtkWidget *QGtk3Interface::widget(QGtkWidget type) const
429{
430 if (type == QGtkWidget::gtk_Default)
431 return nullptr;
432
433 // Return from cache
434 if (GtkWidget *w = cache.value(type))
435 return w;
436
437 // Create new item and cache it
438 GtkWidget *w = qt_new_gtkWidget(type);
439 cache.insert(type, w);
440 return w;
441}
442
443/*!
444 \internal
445 \brief Access a GTK widget's style context.
446
447 Returns the pointer to the style context of GTK widget \param w.
448
449 \note If \param w is nullptr, the GTK default style context (entry style) is returned.
450 */
451GtkStyleContext *QGtk3Interface::context(GtkWidget *w) const
452{
453 if (w)
454 return gtk_widget_get_style_context(w);
455
456 return gtk_widget_get_style_context(widget(QGtkWidget::gtk_entry));
457}
458
459/*!
460 \internal
461 \brief Create a QBrush from a GTK widget.
462
463 Returns a QBrush corresponding to GTK widget type \param wtype, \param source and \param state.
464
465 Brush height and width is ignored in GTK3, because brush assets (e.g. 9-patches)
466 can't be accessed by the GTK3 API. It's therefore unknown, if the brush relates only to colors,
467 or to a pixmap based style.
468
469 */
470QBrush QGtk3Interface::brush(QGtkWidget wtype, QGtkColorSource source, GtkStateFlags state) const
471{
472 // FIXME: When a color's pixmap can be accessed via the GTK API,
473 // read it and set it in the brush.
474 return QBrush(color(widget(wtype), source, state));
475}
476
477/*!
478 \internal
479 \brief Returns the name of the current GTK theme.
480 */
482{
483 QString name;
484
485 if (GtkSettings *settings = gtk_settings_get_default()) {
486 gchar *theme_name;
487 g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
488 name = QLatin1StringView(theme_name);
489 g_free(theme_name);
490 }
491
492 return name;
493}
494
495/*!
496 \internal
497 \brief Determine color scheme by colors.
498
499 Returns the color scheme of the current GTK theme, heuristically determined by the
500 lightness difference between default background and foreground colors.
501
502 \note Returns Unknown in the unlikely case that both colors have the same lightness.
503 */
505{
506 const QColor background = color(widget(QGtkWidget::gtk_Default),
507 QGtkColorSource::Background,
508 GTK_STATE_FLAG_ACTIVE);
509 const QColor foreground = color(widget(QGtkWidget::gtk_Default),
510 QGtkColorSource::Foreground,
511 GTK_STATE_FLAG_ACTIVE);
512
513 if (foreground.lightness() > background.lightness())
514 return Qt::ColorScheme::Dark;
515 if (foreground.lightness() < background.lightness())
516 return Qt::ColorScheme::Light;
517 return Qt::ColorScheme::Unknown;
518}
519
520/*!
521 \internal
522 \brief Map font type to GTK widget type.
523
524 Returns the GTK widget type corresponding to the given QPlatformTheme::Font \param type.
525 */
526inline constexpr QGtk3Interface::QGtkWidget QGtk3Interface::toWidgetType(QPlatformTheme::Font type)
527{
528 switch (type) {
529 case QPlatformTheme::SystemFont: return QGtkWidget::gtk_Default;
530 case QPlatformTheme::MenuFont: return QGtkWidget::gtk_menu;
531 case QPlatformTheme::MenuBarFont: return QGtkWidget::gtk_menu_bar;
532 case QPlatformTheme::MenuItemFont: return QGtkWidget::gtk_menu;
533 case QPlatformTheme::MessageBoxFont: return QGtkWidget::gtk_popup;
534 case QPlatformTheme::LabelFont: return QGtkWidget::gtk_popup;
535 case QPlatformTheme::TipLabelFont: return QGtkWidget::gtk_Default;
536 case QPlatformTheme::StatusBarFont: return QGtkWidget::gtk_statusbar;
537 case QPlatformTheme::TitleBarFont: return QGtkWidget::gtk_Default;
538 case QPlatformTheme::MdiSubWindowTitleFont: return QGtkWidget::gtk_Default;
539 case QPlatformTheme::DockWidgetTitleFont: return QGtkWidget::gtk_Default;
540 case QPlatformTheme::PushButtonFont: return QGtkWidget::gtk_button;
541 case QPlatformTheme::CheckBoxFont: return QGtkWidget::gtk_check_button;
542 case QPlatformTheme::RadioButtonFont: return QGtkWidget::gtk_radio_button;
543 case QPlatformTheme::ToolButtonFont: return QGtkWidget::gtk_button;
544 case QPlatformTheme::ItemViewFont: return QGtkWidget::gtk_entry;
545 case QPlatformTheme::ListViewFont: return QGtkWidget::gtk_tree_view;
546 case QPlatformTheme::HeaderViewFont: return QGtkWidget::gtk_combo_box;
547 case QPlatformTheme::ListBoxFont: return QGtkWidget::gtk_Default;
548 case QPlatformTheme::ComboMenuItemFont: return QGtkWidget::gtk_combo_box;
549 case QPlatformTheme::ComboLineEditFont: return QGtkWidget::gtk_combo_box_text;
550 case QPlatformTheme::SmallFont: return QGtkWidget::gtk_Default;
551 case QPlatformTheme::MiniFont: return QGtkWidget::gtk_Default;
552 case QPlatformTheme::FixedFont: return QGtkWidget::gtk_Default;
553 case QPlatformTheme::GroupBoxTitleFont: return QGtkWidget::gtk_Default;
554 case QPlatformTheme::TabButtonFont: return QGtkWidget::gtk_button;
555 case QPlatformTheme::EditorFont: return QGtkWidget::gtk_entry;
556 case QPlatformTheme::NFonts: return QGtkWidget::gtk_Default;
557 }
558 Q_UNREACHABLE();
559}
560
561/*!
562 \internal
563 \brief Convert pango \param style to QFont::Style.
564 */
565inline constexpr QFont::Style QGtk3Interface::toFontStyle(PangoStyle style)
566{
567 switch (style) {
568 case PANGO_STYLE_ITALIC: return QFont::StyleItalic;
569 case PANGO_STYLE_OBLIQUE: return QFont::StyleOblique;
570 case PANGO_STYLE_NORMAL: return QFont::StyleNormal;
571 }
572 // This is reached when GTK has introduced a new font style
573 Q_UNREACHABLE();
574}
575
576/*!
577 \internal
578 \brief Convert pango font \param weight to an int, representing font weight in Qt.
579
580 Compatibility of PangoWeight is statically asserted.
581 The minimum (1) and maximum (1000) weight in Qt is respeced.
582 */
583inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
584{
585 // GTK PangoWeight can be directly converted to QFont::Weight
586 // unless one of the enums changes.
587 static_assert(PANGO_WEIGHT_THIN == 100 && PANGO_WEIGHT_ULTRAHEAVY == 1000,
588 "Pango font weight enum changed. Fix conversion.");
589
590 static_assert(QFont::Thin == 100 && QFont::Black == 900,
591 "QFont::Weight enum changed. Fix conversion.");
592
593 return qBound(1, static_cast<int>(weight), 1000);
594}
595
596/*!
597 \internal
598 \brief Return a GTK styled font.
599
600 Returns the QFont corresponding to \param type by reading the corresponding
601 GTK widget type's font.
602
603 \note GTK allows to specify a non fixed font as the system's fixed font.
604 If a fixed font is requested, the method fixes the pitch and falls back to monospace,
605 unless a suitable fixed pitch font is found.
606 */
607QFont QGtk3Interface::font(QPlatformTheme::Font type) const
608{
609 GtkStyleContext *con = context(widget(toWidgetType(type)));
610 if (!con)
611 return QFont();
612
613 // explicitly add provider for fixed font
614 GtkCssProvider *cssProvider = nullptr;
615 if (type == QPlatformTheme::FixedFont) {
616 cssProvider = gtk_css_provider_new();
617 gtk_style_context_add_class (con, GTK_STYLE_CLASS_MONOSPACE);
618 const char *fontSpec = "* {font-family: monospace;}";
619 gtk_css_provider_load_from_data(cssProvider, fontSpec, -1, NULL);
620 gtk_style_context_add_provider(con, GTK_STYLE_PROVIDER(cssProvider),
621 GTK_STYLE_PROVIDER_PRIORITY_USER);
622 }
623
624 // remove monospace provider from style context and unref it
625 QScopeGuard guard([&](){
626 if (cssProvider) {
627 gtk_style_context_remove_provider(con, GTK_STYLE_PROVIDER(cssProvider));
628 g_object_unref(cssProvider);
629 }
630 });
631
632 const PangoFontDescription *gtkFont = gtk_style_context_get_font(con, GTK_STATE_FLAG_NORMAL);
633 if (!gtkFont)
634 return QFont();
635
636 const QString family = QString::fromLatin1(pango_font_description_get_family(gtkFont));
637 if (family.isEmpty())
638 return QFont();
639
640 const int weight = toFontWeight(pango_font_description_get_weight(gtkFont));
641
642 // Creating a QFont() creates a futex lockup on a theme change
643 // QFont doesn't have a constructor with float point size
644 // => create a dummy point size and set it later.
645 QFont font(family, 1, weight);
646 font.setPointSizeF(static_cast<float>(pango_font_description_get_size(gtkFont)/PANGO_SCALE));
647 font.setStyle(toFontStyle(pango_font_description_get_style(gtkFont)));
648
649 if (type == QPlatformTheme::FixedFont) {
650 font.setFixedPitch(true);
651 if (!QFontInfo(font).fixedPitch()) {
652 qCDebug(lcQGtk3Interface) << "No fixed pitch font found in font family"
653 << font.family() << ". falling back to a default"
654 << "fixed pitch font";
655 font.setFamily("monospace"_L1);
656 }
657 }
658
659 return font;
660}
661
662/*!
663 \internal
664 \brief Returns a GTK styled file icon for \param fileInfo.
665 */
666QIcon QGtk3Interface::fileIcon(const QFileInfo &fileInfo) const
667{
668 GFile *file = g_file_new_for_path(fileInfo.absoluteFilePath().toLatin1().constData());
669 if (!file)
670 return QIcon();
671
672 GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_ICON,
673 G_FILE_QUERY_INFO_NONE, nullptr, nullptr);
674 if (!info) {
675 g_object_unref(file);
676 return QIcon();
677 }
678
679 GIcon *icon = g_file_info_get_icon(info);
680 if (!icon) {
681 g_object_unref(file);
682 g_object_unref(info);
683 return QIcon();
684 }
685
686 GtkIconTheme *theme = gtk_icon_theme_get_default();
687 GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(theme, icon, 16,
688 GTK_ICON_LOOKUP_FORCE_SIZE);
689 if (!iconInfo) {
690 g_object_unref(file);
691 g_object_unref(info);
692 return QIcon();
693 }
694
695 GdkPixbuf *buf = gtk_icon_info_load_icon(iconInfo, nullptr);
696 QImage image = qt_convert_gdk_pixbuf(buf);
697 g_object_unref(file);
698 g_object_unref(info);
699 g_object_unref(buf);
700 return QIcon(QPixmap::fromImage(image));
701}
702
703QT_END_NAMESPACE
The QGtk3Interface class centralizes communication with the GTK3 library.
QIcon fileIcon(const QFileInfo &fileInfo) const
Returns a GTK styled file icon for.
QFont font(QPlatformTheme::Font type) const
Return a GTK styled font.
Qt::ColorScheme colorSchemeByColors() const
Determine color scheme by colors.
QImage standardPixmap(QPlatformTheme::StandardPixmap standardPixmap) const
Returns a QImage corresponding to.
QString themeName() const
Returns the name of the current GTK theme.
void handleThemeChange()
Handles a theme change at runtime.
#define CASE(E, member)
#define CASEN(Type)
static QGtk3Storage * m_storage
#define CONVERT
#define SAVE(src, state, prop, def)