Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
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
4//
5// W A R N I N G
6// -------------
7//
8// This file is not part of the Qt API. It exists purely as an
9// implementation detail. This header file may change from version to
10// version without notice, or even be removed.
11//
12// We mean it.
13//
14
15
16#include "qgtk3interface_p.h"
17#include "qgtk3storage_p.h"
18#include <QtCore/QMetaEnum>
19#include <QtCore/QFileInfo>
20#include <QtGui/QFontDatabase>
21
23Q_LOGGING_CATEGORY(lcQGtk3Interface, "qt.qpa.gtk");
24
25
26// Callback for gnome event loop has to be static
27static QGtk3Storage *m_storage = nullptr;
28
30{
31 initColorMap();
32
33 if (!s) {
34 qCDebug(lcQGtk3Interface) << "QGtk3Interface instantiated without QGtk3Storage."
35 << "No reaction to runtime theme changes.";
36 return;
37 }
38
39 // Connect to the GTK settings changed signal
40 auto handleThemeChange = [] {
41 if (m_storage)
43 };
44
45 GtkSettings *settings = gtk_settings_get_default();
46 const gboolean success = g_signal_connect(settings, "notify::gtk-theme-name",
47 G_CALLBACK(handleThemeChange), nullptr);
48 if (success == FALSE) {
49 qCDebug(lcQGtk3Interface) << "Connection to theme change signal failed."
50 << "No reaction to runtime theme changes.";
51 } else {
52 m_storage = s;
53 }
54}
55
57{
58 // Ignore theme changes when destructor is reached
59 m_storage = nullptr;
60
61 // QGtkWidgets have to be destroyed manually
62 for (auto v : cache)
63 gtk_widget_destroy(v.second);
64}
65
75{
76#define CASE(x) \
77 if (QLatin1String(QByteArray(state.toLatin1())) == #x ##_L1) \
78 return GTK_STATE_FLAG_ ##x
79
80#define CONVERT\
81 CASE(NORMAL);\
82 CASE(ACTIVE);\
83 CASE(PRELIGHT);\
84 CASE(SELECTED);\
85 CASE(INSENSITIVE);\
86 CASE(INCONSISTENT);\
87 CASE(FOCUSED);\
88 CASE(BACKDROP);\
89 CASE(DIR_LTR);\
90 CASE(DIR_RTL);\
91 CASE(LINK);\
92 CASE(VISITED);\
93 CASE(CHECKED);\
94 CASE(DROP_ACTIVE)
95
96 CONVERT;
97 return -1;
98#undef CASE
99}
100
106{
107#define CASE(x) case GTK_STATE_FLAG_ ##x: return QLatin1String(#x)
108 switch (state) {
109 CONVERT;
110 }
111 Q_UNREACHABLE();
112#undef CASE
113#undef CONVERT
114}
115
120void QGtk3Interface::initColorMap()
121{
122 #define SAVE(src, state, prop, def)\
123 {ColorKey({QGtkColorSource::src, GTK_STATE_FLAG_ ##state}), ColorValue({#prop ##_L1, QGtkColorDefault::def})}
124
125 gtkColorMap = ColorMap {
126 SAVE(Foreground, NORMAL, theme_fg_color, Foreground),
127 SAVE(Foreground, BACKDROP, theme_unfocused_selected_fg_color, Foreground),
128 SAVE(Foreground, INSENSITIVE, insensitive_fg_color, Foreground),
129 SAVE(Foreground, SELECTED, theme_selected_fg_color, Foreground),
130 SAVE(Foreground, ACTIVE, theme_unfocused_fg_color, Foreground),
131 SAVE(Text, NORMAL, theme_text_color, Foreground),
132 SAVE(Text, ACTIVE, theme_unfocused_text_color, Foreground),
133 SAVE(Base, NORMAL, theme_base_color, Background),
134 SAVE(Base, INSENSITIVE, insensitive_base_color, Background),
135 SAVE(Background, NORMAL, theme_bg_color, Background),
136 SAVE(Background, SELECTED, theme_selected_bg_color, Background),
137 SAVE(Background, INSENSITIVE, insensitive_bg_color, Background),
138 SAVE(Background, ACTIVE, theme_unfocused_bg_color, Background),
139 SAVE(Background, BACKDROP, theme_unfocused_selected_bg_color, Background),
140 SAVE(Border, NORMAL, borders, Border),
141 SAVE(Border, ACTIVE, unfocused_borders, Border)
142 };
143#undef SAVE
144
145 qCDebug(lcQGtk3Interface) << "Color map populated from defaults.";
146}
147
160{
161 switch (standardPixmap) {
163 return qt_gtk_get_icon(GTK_STOCK_DELETE);
165 return qt_gtk_get_icon(GTK_STOCK_OK);
167 return qt_gtk_get_icon(GTK_STOCK_CANCEL);
169 return qt_gtk_get_icon(GTK_STOCK_YES);
171 return qt_gtk_get_icon(GTK_STOCK_NO);
173 return qt_gtk_get_icon(GTK_STOCK_OPEN);
175 return qt_gtk_get_icon(GTK_STOCK_CLOSE);
177 return qt_gtk_get_icon(GTK_STOCK_APPLY);
179 return qt_gtk_get_icon(GTK_STOCK_SAVE);
181 return qt_gtk_get_icon(GTK_STOCK_DIALOG_WARNING);
183 return qt_gtk_get_icon(GTK_STOCK_DIALOG_QUESTION);
185 return qt_gtk_get_icon(GTK_STOCK_DIALOG_INFO);
187 return qt_gtk_get_icon(GTK_STOCK_DIALOG_ERROR);
256 return QImage();
257 }
258 Q_UNREACHABLE();
259}
260
265QImage QGtk3Interface::qt_gtk_get_icon(const char* iconName) const
266{
267 GtkIconSet* iconSet = gtk_icon_factory_lookup_default (iconName);
268 GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context(), GTK_ICON_SIZE_DIALOG);
269 return qt_convert_gdk_pixbuf(icon);
270}
271
284QImage QGtk3Interface::qt_convert_gdk_pixbuf(GdkPixbuf *buf) const
285{
286 if (!buf)
287 return QImage();
288
289 const guint8 *gdata = gdk_pixbuf_read_pixels(buf);
290 static_assert(std::is_same<decltype(gdata), const uchar *>::value,
291 "guint8 has diverted from uchar. Code needs fixing.");
292 Q_ASSUME(gdk_pixbuf_get_bits_per_sample(buf) == 8);
293 Q_ASSUME(gdk_pixbuf_get_n_channels(buf) == 4);
294 const uchar *data = static_cast<const uchar *>(gdata);
295
296 const int width = gdk_pixbuf_get_width(buf);
297 const int height = gdk_pixbuf_get_height(buf);
298 const int bpl = gdk_pixbuf_get_rowstride(buf);
299 QImage converted(data, width, height, bpl, QImage::Format_ARGB32);
300 return converted.copy(); // detatch to survive lifetime of buf
301}
302
310GtkWidget *QGtk3Interface::qt_new_gtkWidget(QGtkWidget type) const
311{
312#define CASE(Type)\
313 case QGtkWidget::Type: return Type ##_new();
314#define CASEN(Type)\
315 case QGtkWidget::Type: return Type ##_new(nullptr);
316
317 switch (type) {
321 case QGtkWidget::gtk_button_box: return gtk_button_box_new(GtkOrientation::GTK_ORIENTATION_HORIZONTAL);
327 case QGtkWidget::gtk_popup: return gtk_window_new(GTK_WINDOW_POPUP);
337 case QGtkWidget::gtk_Default: return nullptr;
338 }
339#undef CASE
340#undef CASEN
341 Q_UNREACHABLE();
342}
343
352GdkRGBA QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def) const
353{
354 GdkRGBA color;
355
356#define CASE(def, call)\
357 case QGtkColorDefault::def:\
358 gtk_style_context_get_ ##call(con, state, &color);\
359 break;
360
361 switch (def) {
363 CASE(Background, background_color)
364 CASE(Border, border_color)
365 }
366 return color;
367#undef CASE
368}
369
380QColor QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkStateFlags state) const
381{
382 GdkRGBA col;
383 GtkStyleContext *con = context(widget);
384
385#define CASE(src, def)\
386 case QGtkColorSource::src: {\
387 const ColorKey key = ColorKey({QGtkColorSource::src, state});\
388 if (gtkColorMap.contains(key)) {\
389 const ColorValue val = gtkColorMap.value(key);\
390 if (!gtk_style_context_lookup_color(con, val.propertyName.toUtf8().constData(), &col)) {\
391 col = genericColor(con, state, val.genericSource);\
392 qCDebug(lcQGtk3Interface) << "Property name" << val.propertyName << "not found.\n"\
393 << "Falling back to " << val.genericSource;\
394 }\
395 } else {\
396 col = genericColor(con, state, QGtkColorDefault::def);\
397 qCDebug(lcQGtk3Interface) << "No color source found for" << QGtkColorSource::src\
398 << fromGtkState(state) << "\n Falling back to"\
399 << QGtkColorDefault::def;\
400 }\
401 }\
402 break;
403
404 switch (source) {
410 }
411
412 return fromGdkColor(col);
413#undef CASE
414}
415
425GtkWidget *QGtk3Interface::widget(QGtkWidget type) const
426{
428 return nullptr;
429
430 // Return from cache
431 if (GtkWidget *w = cache.value(type))
432 return w;
433
434 // Create new item and cache it
435 GtkWidget *w = qt_new_gtkWidget(type);
436 cache.insert(type, w);
437 return w;
438}
439
448GtkStyleContext *QGtk3Interface::context(GtkWidget *w) const
449{
450 if (w)
451 return gtk_widget_get_style_context(w);
452
453 return gtk_widget_get_style_context(widget(QGtkWidget::gtk_entry));
454}
455
468{
469 // FIXME: When a color's pixmap can be accessed via the GTK API,
470 // read it and set it in the brush.
471 return QBrush(color(widget(wtype), source, state));
472}
473
479{
480 gchar *theme_name;
481 GtkSettings *settings = gtk_settings_get_default();
482 if (!settings)
483 return QString();
484
485 g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
486 return QLatin1StringView(theme_name);
487}
488
499{
500 const QColor background = color(widget(QGtkWidget::gtk_Default),
502 GTK_STATE_FLAG_ACTIVE);
503 const QColor foreground = color(widget(QGtkWidget::gtk_Default),
505 GTK_STATE_FLAG_ACTIVE);
506
507 if (foreground.lightness() > background.lightness())
509 if (foreground.lightness() < background.lightness())
512}
513
520inline constexpr QGtk3Interface::QGtkWidget QGtk3Interface::toWidgetType(QPlatformTheme::Font type)
521{
522 switch (type) {
551 }
552 Q_UNREACHABLE();
553}
554
559inline constexpr QFont::Style QGtk3Interface::toFontStyle(PangoStyle style)
560{
561 switch (style) {
562 case PANGO_STYLE_ITALIC: return QFont::StyleItalic;
563 case PANGO_STYLE_OBLIQUE: return QFont::StyleOblique;
564 case PANGO_STYLE_NORMAL: return QFont::StyleNormal;
565 }
566 // This is reached when GTK has introduced a new font style
567 Q_UNREACHABLE();
568}
569
577inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
578{
579 // GTK PangoWeight can be directly converted to QFont::Weight
580 // unless one of the enums changes.
581 static_assert(PANGO_WEIGHT_THIN == 100 && PANGO_WEIGHT_ULTRAHEAVY == 1000,
582 "Pango font weight enum changed. Fix conversion.");
583
584 static_assert(QFont::Thin == 100 && QFont::Black == 900,
585 "QFont::Weight enum changed. Fix conversion.");
586
587 return qBound(1, static_cast<int>(weight), 1000);
588}
589
602{
603 GtkStyleContext *con = context(widget(toWidgetType(type)));
604 if (!con)
605 return QFont();
606
607 // explicitly add provider for fixed font
608 GtkCssProvider *cssProvider = nullptr;
610 cssProvider = gtk_css_provider_new();
611 gtk_style_context_add_class (con, GTK_STYLE_CLASS_MONOSPACE);
612 const char *fontSpec = "* {font-family: monospace;}";
613 gtk_css_provider_load_from_data(cssProvider, fontSpec, -1, NULL);
614 gtk_style_context_add_provider(con, GTK_STYLE_PROVIDER(cssProvider),
615 GTK_STYLE_PROVIDER_PRIORITY_USER);
616 }
617
618 // remove monospace provider from style context and unref it
619 QScopeGuard guard([&](){
620 if (cssProvider) {
621 gtk_style_context_remove_provider(con, GTK_STYLE_PROVIDER(cssProvider));
622 g_object_unref(cssProvider);
623 }
624 });
625
626 const PangoFontDescription *gtkFont = gtk_style_context_get_font(con, GTK_STATE_FLAG_NORMAL);
627 if (!gtkFont)
628 return QFont();
629
630 const QString family = QString::fromLatin1(pango_font_description_get_family(gtkFont));
631 if (family.isEmpty())
632 return QFont();
633
634 const int weight = toFontWeight(pango_font_description_get_weight(gtkFont));
635
636 // Creating a QFont() creates a futex lockup on a theme change
637 // QFont doesn't have a constructor with float point size
638 // => create a dummy point size and set it later.
639 QFont font(family, 1, weight);
640 font.setPointSizeF(static_cast<float>(pango_font_description_get_size(gtkFont)/PANGO_SCALE));
641 font.setStyle(toFontStyle(pango_font_description_get_style(gtkFont)));
642
644 font.setFixedPitch(true);
645 if (!QFontInfo(font).fixedPitch()) {
646 qCDebug(lcQGtk3Interface) << "No fixed pitch font found in font family"
647 << font.family() << ". falling back to a default"
648 << "fixed pitch font";
649 font.setFamily("monospace"_L1);
650 }
651 }
652
653 return font;
654}
655
661{
662 GFile *file = g_file_new_for_path(fileInfo.absoluteFilePath().toLatin1().constData());
663 if (!file)
664 return QIcon();
665
666 GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
667 G_FILE_QUERY_INFO_NONE, nullptr, nullptr);
668 if (!info) {
669 g_object_unref(file);
670 return QIcon();
671 }
672
673 GIcon *icon = g_file_info_get_icon(info);
674 if (!icon) {
675 g_object_unref(file);
676 g_object_unref(info);
677 return QIcon();
678 }
679
680 GtkIconTheme *theme = gtk_icon_theme_get_default();
681 GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(theme, icon, GTK_ICON_SIZE_BUTTON,
682 GTK_ICON_LOOKUP_FORCE_SIZE);
683 if (!iconInfo) {
684 g_object_unref(file);
685 g_object_unref(info);
686 g_object_unref(icon);
687 return QIcon();
688 }
689
690 GdkPixbuf *buf = gtk_icon_info_load_icon(iconInfo, nullptr);
691 QImage image = qt_convert_gdk_pixbuf(buf);
692 g_object_unref(file);
693 g_object_unref(info);
694 g_object_unref(icon);
695 g_object_unref(buf);
697}
698
\inmodule QtGui
Definition qbrush.h:30
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
int lightness() const noexcept
Definition qcolor.cpp:1860
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString absoluteFilePath() const
Returns an absolute path including the file name.
std::pair< iterator, bool > insert(const Key &key, const T &value)
Definition qflatmap_p.h:678
T value(const Key &key, const T &defaultValue) const
Definition qflatmap_p.h:636
\reentrant
Definition qfontinfo.h:14
\reentrant
Definition qfont.h:20
void setStyle(Style style)
Sets the style of the font to style.
Definition qfont.cpp:1101
QString family() const
Returns the requested font family name.
Definition qfont.cpp:796
void setFixedPitch(bool)
If enable is true, sets fixed pitch on; otherwise sets fixed pitch off.
Definition qfont.cpp:1327
void setFamily(const QString &)
Sets the family name of the font.
Definition qfont.cpp:814
void setPointSizeF(qreal)
Sets the point size to pointSize.
Definition qfont.cpp:995
@ Thin
Definition qfont.h:61
@ Black
Definition qfont.h:69
Style
This enum describes the different styles of glyphs that are used to display text.
Definition qfont.h:73
@ StyleItalic
Definition qfont.h:75
@ StyleNormal
Definition qfont.h:74
@ StyleOblique
Definition qfont.h:76
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.
QGtk3Interface(QGtk3Storage *)
QImage standardPixmap(QPlatformTheme::StandardPixmap standardPixmap) const
Returns a QImage corresponding to.
QBrush brush(QGtkWidget wtype, QGtkColorSource source, GtkStateFlags state) const
Create a QBrush from a GTK widget.
static const QLatin1String fromGtkState(GtkStateFlags state)
Returns.
const QString themeName() const
Returns the name of the current GTK theme.
static int toGtkState(const QString &state)
Converts a string into the GtkStateFlags enum.
void handleThemeChange()
Handles a theme change at runtime.
The QIcon class provides scalable icons in different modes and states.
Definition qicon.h:20
\inmodule QtGui
Definition qimage.h:37
@ Format_ARGB32
Definition qimage.h:47
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
Definition qpixmap.cpp:1445
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QOpenGLWidget * widget
[1]
else opt state
[0]
Combined button and popup list for selecting options.
ColorScheme
Definition qnamespace.h:49
Definition image.cpp:4
static void * context
#define CASE(Enum, Size)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
struct _GtkWidget GtkWidget
#define CASEN(Type)
#define CONVERT
#define SAVE(src, state, prop, def)
static QGtk3Storage * m_storage
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
GLsizei const GLfloat * v
[13]
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLuint GLuint GLfloat weight
GLint GLsizei width
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLsizei GLsizei GLchar * source
GLdouble s
[6]
Definition qopenglext.h:235
unsigned char uchar
Definition qtypes.h:27
QFile file
[0]
QFileInfo info(fileName)
[8]
QSettings settings("MySoft", "Star Runner")
[0]