19#include <QtCore/QMetaEnum>
20#include <QtCore/QFileInfo>
21#include <QtGui/QFontDatabase>
24Q_LOGGING_CATEGORY(lcQGtk3Interface,
"qt.qpa.gtk");
35 qCDebug(lcQGtk3Interface) <<
"QGtk3Interface instantiated without QGtk3Storage."
36 <<
"No reaction to runtime theme changes.";
41 auto handleThemeChange = [] {
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.";
64 gtk_widget_destroy(v.second);
68
69
70
71
72
73
74
78 if (QLatin1String(QByteArray(state.toLatin1())) == #x ##_L1)
79 return GTK_STATE_FLAG_ ##x
103
104
105
108#define CASE(x) case GTK_STATE_FLAG_ ##x: return QLatin1String(#x)
118
119
120
123 #define SAVE(src, state, prop, def)
124 {ColorKey({QGtkColorSource::src, GTK_STATE_FLAG_ ##state}), ColorValue({#prop ##_L1, QGtkColorDefault::def})}
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)
146 qCDebug(lcQGtk3Interface) <<
"Color map populated from defaults.";
150
151
152
153
154
155
156
157
158
159
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:
267
268
269
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);
278
279
280
281
282
283
284
285
286
287
288
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);
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);
307 return converted.convertToFormat(QImage::Format_ARGB32_Premultiplied);
311
312
313
314
315
316
320 case QGtkWidget::Type: return Type ##_new();
322 case QGtkWidget::Type: return Type ##_new(nullptr);
329 CASE(gtk_check_button)
330 CASEN(gtk_radio_button)
339 CASE(gtk_combo_box_text)
340 CASE(gtk_progress_bar)
342 CASE(gtk_separator_menu_item)
343 CASE(gtk_offscreen_window)
352
353
354
355
356
357
358
359GdkRGBA
QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def)
const
363#define CASE(def, call)
364 case QGtkColorDefault::def:
365 gtk_style_context_get_ ##call(con, state, &color);
369 CASE(Foreground, color)
370 CASE(Background, background_color)
371 CASE(Border, border_color)
378
379
380
381
382
383
384
385
386
387QColor
QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkStateFlags state)
const
390 GtkStyleContext *con = context(widget);
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;
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;
412 CASE(Foreground, Foreground)
413 CASE(Background, Background)
414 CASE(Text, Foreground)
415 CASE(Base, Background)
419 return fromGdkColor(col);
424
425
426
427
428
429
430
431
438 if (GtkWidget *w = cache.value(type))
442 GtkWidget *w = qt_new_gtkWidget(type);
443 cache.insert(type, w);
448
449
450
451
452
453
454
458 return gtk_widget_get_style_context(w);
460 return gtk_widget_get_style_context(widget(QGtkWidget::gtk_entry));
464
465
466
467
468
469
470
471
472
473
478 return QBrush(color(widget(wtype), source, state));
482
483
484
489 if (GtkSettings *settings = gtk_settings_get_default()) {
491 g_object_get(settings,
"gtk-theme-name", &theme_name,
nullptr);
492 name = QLatin1StringView(theme_name);
500
501
502
503
504
505
506
507
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);
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;
525
526
527
528
529
566
567
568
569inline constexpr QFont::Style
QGtk3Interface::toFontStyle(PangoStyle 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;
581
582
583
584
585
586
587inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
591 static_assert(PANGO_WEIGHT_THIN == 100 && PANGO_WEIGHT_ULTRAHEAVY == 1000,
592 "Pango font weight enum changed. Fix conversion.");
594 static_assert(QFont::Thin == 100 && QFont::Black == 900,
595 "QFont::Weight enum changed. Fix conversion.");
597 return qBound(1,
static_cast<
int>(weight), 1000);
601
602
603
604
605
606
607
608
609
610
613 GtkStyleContext *con = context(widget(toWidgetType(type)));
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);
629 QScopeGuard guard([&](){
631 gtk_style_context_remove_provider(con, GTK_STYLE_PROVIDER(cssProvider));
632 g_object_unref(cssProvider);
636 const PangoFontDescription *gtkFont = gtk_style_context_get_font(con, GTK_STATE_FLAG_NORMAL);
640 const QString family = QString::fromLatin1(pango_font_description_get_family(gtkFont));
641 if (family.isEmpty())
644 const int weight = toFontWeight(pango_font_description_get_weight(gtkFont));
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)));
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);
667
668
669
672 GFile *file = g_file_new_for_path(fileInfo.absoluteFilePath().toLatin1().constData());
676 GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_ICON,
677 G_FILE_QUERY_INFO_NONE,
nullptr,
nullptr);
679 g_object_unref(file);
683 GIcon *icon = g_file_info_get_icon(info);
685 g_object_unref(file);
686 g_object_unref(info);
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);
694 g_object_unref(file);
695 g_object_unref(info);
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);
704 return QIcon(QPixmap::fromImage(image));
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.
static QGtk3Storage * m_storage
#define SAVE(src, state, prop, def)