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:
263
264
265
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);
274
275
276
277
278
279
280
281
282
283
284
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);
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);
303 return converted.convertToFormat(QImage::Format_ARGB32_Premultiplied);
307
308
309
310
311
312
316 case QGtkWidget::Type: return Type ##_new();
318 case QGtkWidget::Type: return Type ##_new(nullptr);
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)
330 case QGtkWidget::gtk_popup:
return gtk_window_new(GTK_WINDOW_POPUP);
335 CASE(gtk_combo_box_text)
336 CASE(gtk_progress_bar)
338 CASE(gtk_separator_menu_item)
339 CASE(gtk_offscreen_window)
348
349
350
351
352
353
354
355GdkRGBA
QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def)
const
359#define CASE(def, call)
360 case QGtkColorDefault::def:
361 gtk_style_context_get_ ##call(con, state, &color);
365 CASE(Foreground, color)
366 CASE(Background, background_color)
367 CASE(Border, border_color)
374
375
376
377
378
379
380
381
382
383QColor
QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkStateFlags state)
const
386 GtkStyleContext *con = context(widget);
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;
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;
408 CASE(Foreground, Foreground)
409 CASE(Background, Background)
410 CASE(Text, Foreground)
411 CASE(Base, Background)
415 return fromGdkColor(col);
420
421
422
423
424
425
426
427
434 if (GtkWidget *w = cache.value(type))
438 GtkWidget *w = qt_new_gtkWidget(type);
439 cache.insert(type, w);
444
445
446
447
448
449
450
454 return gtk_widget_get_style_context(w);
456 return gtk_widget_get_style_context(widget(QGtkWidget::gtk_entry));
460
461
462
463
464
465
466
467
468
469
474 return QBrush(color(widget(wtype), source, state));
478
479
480
485 if (GtkSettings *settings = gtk_settings_get_default()) {
487 g_object_get(settings,
"gtk-theme-name", &theme_name,
nullptr);
488 name = QLatin1StringView(theme_name);
496
497
498
499
500
501
502
503
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);
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;
521
522
523
524
525
562
563
564
565inline constexpr QFont::Style
QGtk3Interface::toFontStyle(PangoStyle 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;
577
578
579
580
581
582
583inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
587 static_assert(PANGO_WEIGHT_THIN == 100 && PANGO_WEIGHT_ULTRAHEAVY == 1000,
588 "Pango font weight enum changed. Fix conversion.");
590 static_assert(QFont::Thin == 100 && QFont::Black == 900,
591 "QFont::Weight enum changed. Fix conversion.");
593 return qBound(1,
static_cast<
int>(weight), 1000);
597
598
599
600
601
602
603
604
605
606
609 GtkStyleContext *con = context(widget(toWidgetType(type)));
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);
625 QScopeGuard guard([&](){
627 gtk_style_context_remove_provider(con, GTK_STYLE_PROVIDER(cssProvider));
628 g_object_unref(cssProvider);
632 const PangoFontDescription *gtkFont = gtk_style_context_get_font(con, GTK_STATE_FLAG_NORMAL);
636 const QString family = QString::fromLatin1(pango_font_description_get_family(gtkFont));
637 if (family.isEmpty())
640 const int weight = toFontWeight(pango_font_description_get_weight(gtkFont));
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)));
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);
663
664
665
668 GFile *file = g_file_new_for_path(fileInfo.absoluteFilePath().toLatin1().constData());
672 GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_ICON,
673 G_FILE_QUERY_INFO_NONE,
nullptr,
nullptr);
675 g_object_unref(file);
679 GIcon *icon = g_file_info_get_icon(info);
681 g_object_unref(file);
682 g_object_unref(info);
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);
690 g_object_unref(file);
691 g_object_unref(info);
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);
700 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.
static QGtk3Storage * m_storage
#define SAVE(src, state, prop, def)