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 case QPlatformTheme::TabScrollLeftButton:
258 case QPlatformTheme::TabScrollRightButton:
259 case QPlatformTheme::TabScrollUpButton:
260 case QPlatformTheme::TabScrollDownButton:
261 return QImage();
262 }
263 Q_UNREACHABLE();
264}
265
266/*!
267 \internal
268 \brief Returns a QImage for a given GTK \param iconName.
269 */
270QImage QGtk3Interface::qt_gtk_get_icon(const char* iconName) const
271{
272 GtkIconSet* iconSet = gtk_icon_factory_lookup_default (iconName);
273 GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context(), GTK_ICON_SIZE_DIALOG);
274 return qt_convert_gdk_pixbuf(icon);
275}
276
277/*!
278 \internal
279 \brief Returns a QImage converted from the GDK pixel buffer \param buf.
280
281 The ability to convert GdkPixbuf to QImage relies on the following assumptions:
282 \list
283 \li QImage uses uchar as a data container (unasserted)
284 \li the types guint8 and uchar are identical (statically asserted)
285 \li GDK pixel buffer uses 8 bits per sample (assumed at runtime)
286 \li GDK pixel buffer has 4 channels (assumed at runtime)
287 \endlist
288 */
289QImage QGtk3Interface::qt_convert_gdk_pixbuf(GdkPixbuf *buf) const
290{
291 if (!buf)
292 return QImage();
293
294 const guint8 *gdata = gdk_pixbuf_read_pixels(buf);
295 static_assert(std::is_same<decltype(gdata), const uchar *>::value,
296 "guint8 has diverted from uchar. Code needs fixing.");
297 Q_ASSERT(gdk_pixbuf_get_bits_per_sample(buf) == 8);
298 Q_ASSERT(gdk_pixbuf_get_n_channels(buf) == 4);
299 const uchar *data = static_cast<const uchar *>(gdata);
300
301 const int width = gdk_pixbuf_get_width(buf);
302 const int height = gdk_pixbuf_get_height(buf);
303 const int bpl = gdk_pixbuf_get_rowstride(buf);
304 QImage converted(data, width, height, bpl, QImage::Format_RGBA8888);
305
306 // convert to more optimal format and detach to survive lifetime of buf
307 return converted.convertToFormat(QImage::Format_ARGB32_Premultiplied);
308}
309
310/*!
311 \internal
312 \brief Instantiate a new GTK widget.
313
314 Returns a pointer to a new GTK widget of \param type, allocated on the heap.
315 Returns nullptr of gtk_Default has is passed.
316 */
317GtkWidget *QGtk3Interface::qt_new_gtkWidget(QGtkWidget type) const
318{
319#define CASE(Type)
320 case QGtkWidget::Type: return Type ##_new();
321#define CASEN(Type)
322 case QGtkWidget::Type: return Type ##_new(nullptr);
323
324 switch (type) {
325 CASE(gtk_menu_bar)
326 CASE(gtk_menu)
327 CASE(gtk_button)
328 case QGtkWidget::gtk_button_box: return gtk_button_box_new(GtkOrientation::GTK_ORIENTATION_HORIZONTAL);
329 CASE(gtk_check_button)
330 CASEN(gtk_radio_button)
331 CASEN(gtk_frame)
332 CASE(gtk_statusbar)
333 CASE(gtk_entry)
334 case QGtkWidget::gtk_popup: return gtk_window_new(GTK_WINDOW_POPUP);
335 CASE(gtk_notebook)
336 CASE(gtk_toolbar)
337 CASE(gtk_tree_view)
338 CASE(gtk_combo_box)
339 CASE(gtk_combo_box_text)
340 CASE(gtk_progress_bar)
341 CASE(gtk_fixed)
342 CASE(gtk_separator_menu_item)
343 CASE(gtk_offscreen_window)
344 case QGtkWidget::gtk_Default: return nullptr;
345 }
346#undef CASE
347#undef CASEN
348 Q_UNREACHABLE();
349}
350
351/*!
352 \internal
353 \brief Read a GTK widget's color from a generic color getter.
354
355 This method returns a generic color of \param con, a given GTK style context.
356 The requested color is defined by \param def and the GTK color-state \param state.
357 The return type is GDK color in RGBA format.
358 */
359GdkRGBA QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def) const
360{
361 GdkRGBA color;
362
363#define CASE(def, call)
364 case QGtkColorDefault::def:
365 gtk_style_context_get_ ##call(con, state, &color);
366 break;
367
368 switch (def) {
369 CASE(Foreground, color)
370 CASE(Background, background_color)
371 CASE(Border, border_color)
372 }
373 return color;
374#undef CASE
375}
376
377/*!
378 \internal
379 \brief Read a GTK widget's color from a property.
380
381 Returns a color of GTK-widget \param widget, defined by \param source and \param state.
382 The return type is GDK color in RGBA format.
383
384 \note If no corresponding property can be found for \param source, the method falls back to a
385 suitable generic color.
386 */
387QColor QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkStateFlags state) const
388{
389 GdkRGBA col;
390 GtkStyleContext *con = context(widget);
391
392#define CASE(src, def)
393 case QGtkColorSource::src: {
394 const ColorKey key = ColorKey({QGtkColorSource::src, state});
395 if (gtkColorMap.contains(key)) {
396 const ColorValue val = gtkColorMap.value(key);
397 if (!gtk_style_context_lookup_color(con, val.propertyName.toUtf8().constData(), &col)) {
398 col = genericColor(con, state, val.genericSource);
399 qCDebug(lcQGtk3Interface) << "Property name" << val.propertyName << "not found.\n"
400 << "Falling back to " << val.genericSource;
401 }
402 } else {
403 col = genericColor(con, state, QGtkColorDefault::def);
404 qCDebug(lcQGtk3Interface) << "No color source found for" << QGtkColorSource::src
405 << fromGtkState(state) << "\n Falling back to"
406 << QGtkColorDefault::def;
407 }
408 }
409 break;
410
411 switch (source) {
412 CASE(Foreground, Foreground)
413 CASE(Background, Background)
414 CASE(Text, Foreground)
415 CASE(Base, Background)
416 CASE(Border, Border)
417 }
418
419 return fromGdkColor(col);
420#undef CASE
421}
422
423/*!
424 \internal
425 \brief Get pointer to a GTK widget by \param type.
426
427 Returns the pointer to a GTK widget, specified by \param type.
428 GTK widgets are cached, so that only one instance of each type is created.
429 \note
430 The method returns nullptr for the enum value gtk_Default.
431 */
432GtkWidget *QGtk3Interface::widget(QGtkWidget type) const
433{
434 if (type == QGtkWidget::gtk_Default)
435 return nullptr;
436
437 // Return from cache
438 if (GtkWidget *w = cache.value(type))
439 return w;
440
441 // Create new item and cache it
442 GtkWidget *w = qt_new_gtkWidget(type);
443 cache.insert(type, w);
444 return w;
445}
446
447/*!
448 \internal
449 \brief Access a GTK widget's style context.
450
451 Returns the pointer to the style context of GTK widget \param w.
452
453 \note If \param w is nullptr, the GTK default style context (entry style) is returned.
454 */
455GtkStyleContext *QGtk3Interface::context(GtkWidget *w) const
456{
457 if (w)
458 return gtk_widget_get_style_context(w);
459
460 return gtk_widget_get_style_context(widget(QGtkWidget::gtk_entry));
461}
462
463/*!
464 \internal
465 \brief Create a QBrush from a GTK widget.
466
467 Returns a QBrush corresponding to GTK widget type \param wtype, \param source and \param state.
468
469 Brush height and width is ignored in GTK3, because brush assets (e.g. 9-patches)
470 can't be accessed by the GTK3 API. It's therefore unknown, if the brush relates only to colors,
471 or to a pixmap based style.
472
473 */
474QBrush QGtk3Interface::brush(QGtkWidget wtype, QGtkColorSource source, GtkStateFlags state) const
475{
476 // FIXME: When a color's pixmap can be accessed via the GTK API,
477 // read it and set it in the brush.
478 return QBrush(color(widget(wtype), source, state));
479}
480
481/*!
482 \internal
483 \brief Returns the name of the current GTK theme.
484 */
486{
487 QString name;
488
489 if (GtkSettings *settings = gtk_settings_get_default()) {
490 gchar *theme_name;
491 g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
492 name = QLatin1StringView(theme_name);
493 g_free(theme_name);
494 }
495
496 return name;
497}
498
499/*!
500 \internal
501 \brief Determine color scheme by colors.
502
503 Returns the color scheme of the current GTK theme, heuristically determined by the
504 lightness difference between default background and foreground colors.
505
506 \note Returns Unknown in the unlikely case that both colors have the same lightness.
507 */
509{
510 const QColor background = color(widget(QGtkWidget::gtk_Default),
511 QGtkColorSource::Background,
512 GTK_STATE_FLAG_ACTIVE);
513 const QColor foreground = color(widget(QGtkWidget::gtk_Default),
514 QGtkColorSource::Foreground,
515 GTK_STATE_FLAG_ACTIVE);
516
517 if (foreground.lightness() > background.lightness())
518 return Qt::ColorScheme::Dark;
519 if (foreground.lightness() < background.lightness())
520 return Qt::ColorScheme::Light;
521 return Qt::ColorScheme::Unknown;
522}
523
524/*!
525 \internal
526 \brief Map font type to GTK widget type.
527
528 Returns the GTK widget type corresponding to the given QPlatformTheme::Font \param type.
529 */
530inline constexpr QGtk3Interface::QGtkWidget QGtk3Interface::toWidgetType(QPlatformTheme::Font type)
531{
532 switch (type) {
533 case QPlatformTheme::SystemFont: return QGtkWidget::gtk_Default;
534 case QPlatformTheme::MenuFont: return QGtkWidget::gtk_menu;
535 case QPlatformTheme::MenuBarFont: return QGtkWidget::gtk_menu_bar;
536 case QPlatformTheme::MenuItemFont: return QGtkWidget::gtk_menu;
537 case QPlatformTheme::MessageBoxFont: return QGtkWidget::gtk_popup;
538 case QPlatformTheme::LabelFont: return QGtkWidget::gtk_popup;
539 case QPlatformTheme::TipLabelFont: return QGtkWidget::gtk_Default;
540 case QPlatformTheme::StatusBarFont: return QGtkWidget::gtk_statusbar;
541 case QPlatformTheme::TitleBarFont: return QGtkWidget::gtk_Default;
542 case QPlatformTheme::MdiSubWindowTitleFont: return QGtkWidget::gtk_Default;
543 case QPlatformTheme::DockWidgetTitleFont: return QGtkWidget::gtk_Default;
544 case QPlatformTheme::PushButtonFont: return QGtkWidget::gtk_button;
545 case QPlatformTheme::CheckBoxFont: return QGtkWidget::gtk_check_button;
546 case QPlatformTheme::RadioButtonFont: return QGtkWidget::gtk_radio_button;
547 case QPlatformTheme::ToolButtonFont: return QGtkWidget::gtk_button;
548 case QPlatformTheme::ItemViewFont: return QGtkWidget::gtk_entry;
549 case QPlatformTheme::ListViewFont: return QGtkWidget::gtk_tree_view;
550 case QPlatformTheme::HeaderViewFont: return QGtkWidget::gtk_combo_box;
551 case QPlatformTheme::ListBoxFont: return QGtkWidget::gtk_Default;
552 case QPlatformTheme::ComboMenuItemFont: return QGtkWidget::gtk_combo_box;
553 case QPlatformTheme::ComboLineEditFont: return QGtkWidget::gtk_combo_box_text;
554 case QPlatformTheme::SmallFont: return QGtkWidget::gtk_Default;
555 case QPlatformTheme::MiniFont: return QGtkWidget::gtk_Default;
556 case QPlatformTheme::FixedFont: return QGtkWidget::gtk_Default;
557 case QPlatformTheme::GroupBoxTitleFont: return QGtkWidget::gtk_Default;
558 case QPlatformTheme::TabButtonFont: return QGtkWidget::gtk_button;
559 case QPlatformTheme::EditorFont: return QGtkWidget::gtk_entry;
560 case QPlatformTheme::NFonts: return QGtkWidget::gtk_Default;
561 }
562 Q_UNREACHABLE();
563}
564
565/*!
566 \internal
567 \brief Convert pango \param style to QFont::Style.
568 */
569inline constexpr QFont::Style QGtk3Interface::toFontStyle(PangoStyle style)
570{
571 switch (style) {
572 case PANGO_STYLE_ITALIC: return QFont::StyleItalic;
573 case PANGO_STYLE_OBLIQUE: return QFont::StyleOblique;
574 case PANGO_STYLE_NORMAL: return QFont::StyleNormal;
575 }
576 // This is reached when GTK has introduced a new font style
577 Q_UNREACHABLE();
578}
579
580/*!
581 \internal
582 \brief Convert pango font \param weight to an int, representing font weight in Qt.
583
584 Compatibility of PangoWeight is statically asserted.
585 The minimum (1) and maximum (1000) weight in Qt is respeced.
586 */
587inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
588{
589 // GTK PangoWeight can be directly converted to QFont::Weight
590 // unless one of the enums changes.
591 static_assert(PANGO_WEIGHT_THIN == 100 && PANGO_WEIGHT_ULTRAHEAVY == 1000,
592 "Pango font weight enum changed. Fix conversion.");
593
594 static_assert(QFont::Thin == 100 && QFont::Black == 900,
595 "QFont::Weight enum changed. Fix conversion.");
596
597 return qBound(1, static_cast<int>(weight), 1000);
598}
599
600/*!
601 \internal
602 \brief Return a GTK styled font.
603
604 Returns the QFont corresponding to \param type by reading the corresponding
605 GTK widget type's font.
606
607 \note GTK allows to specify a non fixed font as the system's fixed font.
608 If a fixed font is requested, the method fixes the pitch and falls back to monospace,
609 unless a suitable fixed pitch font is found.
610 */
611QFont QGtk3Interface::font(QPlatformTheme::Font type) const
612{
613 GtkStyleContext *con = context(widget(toWidgetType(type)));
614 if (!con)
615 return QFont();
616
617 // explicitly add provider for fixed font
618 GtkCssProvider *cssProvider = nullptr;
619 if (type == QPlatformTheme::FixedFont) {
620 cssProvider = gtk_css_provider_new();
621 gtk_style_context_add_class (con, GTK_STYLE_CLASS_MONOSPACE);
622 const char *fontSpec = "* {font-family: monospace;}";
623 gtk_css_provider_load_from_data(cssProvider, fontSpec, -1, NULL);
624 gtk_style_context_add_provider(con, GTK_STYLE_PROVIDER(cssProvider),
625 GTK_STYLE_PROVIDER_PRIORITY_USER);
626 }
627
628 // remove monospace provider from style context and unref it
629 QScopeGuard guard([&](){
630 if (cssProvider) {
631 gtk_style_context_remove_provider(con, GTK_STYLE_PROVIDER(cssProvider));
632 g_object_unref(cssProvider);
633 }
634 });
635
636 const PangoFontDescription *gtkFont = gtk_style_context_get_font(con, GTK_STATE_FLAG_NORMAL);
637 if (!gtkFont)
638 return QFont();
639
640 const QString family = QString::fromLatin1(pango_font_description_get_family(gtkFont));
641 if (family.isEmpty())
642 return QFont();
643
644 const int weight = toFontWeight(pango_font_description_get_weight(gtkFont));
645
646 // Creating a QFont() creates a futex lockup on a theme change
647 // QFont doesn't have a constructor with float point size
648 // => create a dummy point size and set it later.
649 QFont font(family, 1, weight);
650 font.setPointSizeF(static_cast<float>(pango_font_description_get_size(gtkFont)/PANGO_SCALE));
651 font.setStyle(toFontStyle(pango_font_description_get_style(gtkFont)));
652
653 if (type == QPlatformTheme::FixedFont) {
654 font.setFixedPitch(true);
655 if (!QFontInfo(font).fixedPitch()) {
656 qCDebug(lcQGtk3Interface) << "No fixed pitch font found in font family"
657 << font.family() << ". falling back to a default"
658 << "fixed pitch font";
659 font.setFamily("monospace"_L1);
660 }
661 }
662
663 return font;
664}
665
666/*!
667 \internal
668 \brief Returns a GTK styled file icon for \param fileInfo.
669 */
670QIcon QGtk3Interface::fileIcon(const QFileInfo &fileInfo) const
671{
672 GFile *file = g_file_new_for_path(fileInfo.absoluteFilePath().toLatin1().constData());
673 if (!file)
674 return QIcon();
675
676 GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_ICON,
677 G_FILE_QUERY_INFO_NONE, nullptr, nullptr);
678 if (!info) {
679 g_object_unref(file);
680 return QIcon();
681 }
682
683 GIcon *icon = g_file_info_get_icon(info);
684 if (!icon) {
685 g_object_unref(file);
686 g_object_unref(info);
687 return QIcon();
688 }
689
690 GtkIconTheme *theme = gtk_icon_theme_get_default();
691 GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(theme, icon, 16,
692 GTK_ICON_LOOKUP_FORCE_SIZE);
693 if (!iconInfo) {
694 g_object_unref(file);
695 g_object_unref(info);
696 return QIcon();
697 }
698
699 GdkPixbuf *buf = gtk_icon_info_load_icon(iconInfo, nullptr);
700 QImage image = qt_convert_gdk_pixbuf(buf);
701 g_object_unref(file);
702 g_object_unref(info);
703 g_object_unref(buf);
704 return QIcon(QPixmap::fromImage(image));
705}
706
707QT_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.
Combined button and popup list for selecting options.
#define CASE(E, member)
#define CASEN(Type)
static QGtk3Storage * m_storage
#define CONVERT
#define SAVE(src, state, prop, def)