Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
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)
42 m_storage->handleThemeChange();
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_ASSERT(gdk_pixbuf_get_bits_per_sample(buf) == 8);
293 Q_ASSERT(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);
300
301 // convert to more optimal format and detach to survive lifetime of buf
302 return converted.convertToFormat(QImage::Format_ARGB32_Premultiplied);
303}
304
312GtkWidget *QGtk3Interface::qt_new_gtkWidget(QGtkWidget type) const
313{
314#define CASE(Type)\
315 case QGtkWidget::Type: return Type ##_new();
316#define CASEN(Type)\
317 case QGtkWidget::Type: return Type ##_new(nullptr);
318
319 switch (type) {
323 case QGtkWidget::gtk_button_box: return gtk_button_box_new(GtkOrientation::GTK_ORIENTATION_HORIZONTAL);
329 case QGtkWidget::gtk_popup: return gtk_window_new(GTK_WINDOW_POPUP);
339 case QGtkWidget::gtk_Default: return nullptr;
340 }
341#undef CASE
342#undef CASEN
343 Q_UNREACHABLE();
344}
345
354GdkRGBA QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def) const
355{
356 GdkRGBA color;
357
358#define CASE(def, call)\
359 case QGtkColorDefault::def:\
360 gtk_style_context_get_ ##call(con, state, &color);\
361 break;
362
363 switch (def) {
365 CASE(Background, background_color)
366 CASE(Border, border_color)
367 }
368 return color;
369#undef CASE
370}
371
382QColor QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkStateFlags state) const
383{
384 GdkRGBA col;
385 GtkStyleContext *con = context(widget);
386
387#define CASE(src, def)\
388 case QGtkColorSource::src: {\
389 const ColorKey key = ColorKey({QGtkColorSource::src, state});\
390 if (gtkColorMap.contains(key)) {\
391 const ColorValue val = gtkColorMap.value(key);\
392 if (!gtk_style_context_lookup_color(con, val.propertyName.toUtf8().constData(), &col)) {\
393 col = genericColor(con, state, val.genericSource);\
394 qCDebug(lcQGtk3Interface) << "Property name" << val.propertyName << "not found.\n"\
395 << "Falling back to " << val.genericSource;\
396 }\
397 } else {\
398 col = genericColor(con, state, QGtkColorDefault::def);\
399 qCDebug(lcQGtk3Interface) << "No color source found for" << QGtkColorSource::src\
400 << fromGtkState(state) << "\n Falling back to"\
401 << QGtkColorDefault::def;\
402 }\
403 }\
404 break;
405
406 switch (source) {
410 CASE(Base, Background)
412 }
413
414 return fromGdkColor(col);
415#undef CASE
416}
417
427GtkWidget *QGtk3Interface::widget(QGtkWidget type) const
428{
430 return nullptr;
431
432 // Return from cache
433 if (GtkWidget *w = cache.value(type))
434 return w;
435
436 // Create new item and cache it
437 GtkWidget *w = qt_new_gtkWidget(type);
438 cache.insert(type, w);
439 return w;
440}
441
450GtkStyleContext *QGtk3Interface::context(GtkWidget *w) const
451{
452 if (w)
453 return gtk_widget_get_style_context(w);
454
455 return gtk_widget_get_style_context(widget(QGtkWidget::gtk_entry));
456}
457
470{
471 // FIXME: When a color's pixmap can be accessed via the GTK API,
472 // read it and set it in the brush.
473 return QBrush(color(widget(wtype), source, state));
474}
475
481{
483
484 if (GtkSettings *settings = gtk_settings_get_default()) {
485 gchar *theme_name;
486 g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
487 name = QLatin1StringView(theme_name);
488 g_free(theme_name);
489 }
490
491 return name;
492}
493
504{
505 const QColor background = color(widget(QGtkWidget::gtk_Default),
507 GTK_STATE_FLAG_ACTIVE);
508 const QColor foreground = color(widget(QGtkWidget::gtk_Default),
510 GTK_STATE_FLAG_ACTIVE);
511
512 if (foreground.lightness() > background.lightness())
514 if (foreground.lightness() < background.lightness())
517}
518
525inline constexpr QGtk3Interface::QGtkWidget QGtk3Interface::toWidgetType(QPlatformTheme::Font type)
526{
527 switch (type) {
556 }
557 Q_UNREACHABLE();
558}
559
564inline constexpr QFont::Style QGtk3Interface::toFontStyle(PangoStyle style)
565{
566 switch (style) {
567 case PANGO_STYLE_ITALIC: return QFont::StyleItalic;
568 case PANGO_STYLE_OBLIQUE: return QFont::StyleOblique;
569 case PANGO_STYLE_NORMAL: return QFont::StyleNormal;
570 }
571 // This is reached when GTK has introduced a new font style
572 Q_UNREACHABLE();
573}
574
582inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
583{
584 // GTK PangoWeight can be directly converted to QFont::Weight
585 // unless one of the enums changes.
586 static_assert(PANGO_WEIGHT_THIN == 100 && PANGO_WEIGHT_ULTRAHEAVY == 1000,
587 "Pango font weight enum changed. Fix conversion.");
588
589 static_assert(QFont::Thin == 100 && QFont::Black == 900,
590 "QFont::Weight enum changed. Fix conversion.");
591
592 return qBound(1, static_cast<int>(weight), 1000);
593}
594
607{
608 GtkStyleContext *con = context(widget(toWidgetType(type)));
609 if (!con)
610 return QFont();
611
612 // explicitly add provider for fixed font
613 GtkCssProvider *cssProvider = nullptr;
615 cssProvider = gtk_css_provider_new();
616 gtk_style_context_add_class (con, GTK_STYLE_CLASS_MONOSPACE);
617 const char *fontSpec = "* {font-family: monospace;}";
618 gtk_css_provider_load_from_data(cssProvider, fontSpec, -1, NULL);
619 gtk_style_context_add_provider(con, GTK_STYLE_PROVIDER(cssProvider),
620 GTK_STYLE_PROVIDER_PRIORITY_USER);
621 }
622
623 // remove monospace provider from style context and unref it
624 QScopeGuard guard([&](){
625 if (cssProvider) {
626 gtk_style_context_remove_provider(con, GTK_STYLE_PROVIDER(cssProvider));
627 g_object_unref(cssProvider);
628 }
629 });
630
631 const PangoFontDescription *gtkFont = gtk_style_context_get_font(con, GTK_STATE_FLAG_NORMAL);
632 if (!gtkFont)
633 return QFont();
634
635 const QString family = QString::fromLatin1(pango_font_description_get_family(gtkFont));
636 if (family.isEmpty())
637 return QFont();
638
639 const int weight = toFontWeight(pango_font_description_get_weight(gtkFont));
640
641 // Creating a QFont() creates a futex lockup on a theme change
642 // QFont doesn't have a constructor with float point size
643 // => create a dummy point size and set it later.
644 QFont font(family, 1, weight);
645 font.setPointSizeF(static_cast<float>(pango_font_description_get_size(gtkFont)/PANGO_SCALE));
646 font.setStyle(toFontStyle(pango_font_description_get_style(gtkFont)));
647
649 font.setFixedPitch(true);
650 if (!QFontInfo(font).fixedPitch()) {
651 qCDebug(lcQGtk3Interface) << "No fixed pitch font found in font family"
652 << font.family() << ". falling back to a default"
653 << "fixed pitch font";
654 font.setFamily("monospace"_L1);
655 }
656 }
657
658 return font;
659}
660
666{
667 GFile *file = g_file_new_for_path(fileInfo.absoluteFilePath().toLatin1().constData());
668 if (!file)
669 return QIcon();
670
671 GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_ICON,
672 G_FILE_QUERY_INFO_NONE, nullptr, nullptr);
673 if (!info) {
674 g_object_unref(file);
675 return QIcon();
676 }
677
678 GIcon *icon = g_file_info_get_icon(info);
679 if (!icon) {
680 g_object_unref(file);
681 g_object_unref(info);
682 return QIcon();
683 }
684
685 GtkIconTheme *theme = gtk_icon_theme_get_default();
686 GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(theme, icon, 16,
687 GTK_ICON_LOOKUP_FORCE_SIZE);
688 if (!iconInfo) {
689 g_object_unref(file);
690 g_object_unref(info);
691 return QIcon();
692 }
693
694 GdkPixbuf *buf = gtk_icon_info_load_icon(iconInfo, nullptr);
695 QImage image = qt_convert_gdk_pixbuf(buf);
696 g_object_unref(file);
697 g_object_unref(info);
698 g_object_unref(buf);
700}
701
\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:124
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
int lightness() const noexcept
Definition qcolor.cpp:1860
QString absoluteFilePath() const
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:16
\reentrant
Definition qfont.h:22
void setStyle(Style style)
Sets the style of the font to style.
Definition qfont.cpp:1116
QString family() const
Returns the requested font family name.
Definition qfont.cpp:817
void setFixedPitch(bool)
If enable is true, sets fixed pitch on; otherwise sets fixed pitch off.
Definition qfont.cpp:1342
void setFamily(const QString &)
Sets the family name of the font.
Definition qfont.cpp:835
void setPointSizeF(qreal)
Sets the point size to pointSize.
Definition qfont.cpp:1010
@ Thin
Definition qfont.h:64
@ Black
Definition qfont.h:72
Style
This enum describes the different styles of glyphs that are used to display text.
Definition qfont.h:76
@ StyleItalic
Definition qfont.h:78
@ StyleNormal
Definition qfont.h:77
@ StyleOblique
Definition qfont.h:79
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.
QString themeName() const
Returns the name of the current GTK theme.
static const QLatin1String fromGtkState(GtkStateFlags state)
Returns.
static int toGtkState(const QString &state)
Converts a string into the GtkStateFlags enum.
The QIcon class provides scalable icons in different modes and states.
Definition qicon.h:20
\inmodule QtGui
Definition qimage.h:37
@ Format_RGBA8888
Definition qimage.h:59
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
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:1437
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
QOpenGLWidget * widget
[1]
else opt state
[0]
Combined button and popup list for selecting options.
ColorScheme
Definition qnamespace.h:50
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
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLuint GLfloat weight
GLint GLsizei width
GLuint color
[2]
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint name
GLsizei GLsizei GLchar * source
GLdouble s
[6]
Definition qopenglext.h:235
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned char uchar
Definition qtypes.h:32
QFile file
[0]
QSettings settings("MySoft", "Star Runner")
[0]
QHostInfo info
[0]