summaryrefslogtreecommitdiffstats
path: root/src/plugins/platformthemes
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platformthemes')
-rw-r--r--src/plugins/platformthemes/CMakeLists.txt1
-rw-r--r--src/plugins/platformthemes/gtk3/CMakeLists.txt22
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp2
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3interface.cpp235
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3interface_p.h50
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3json.cpp46
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3json_p.h6
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3portalinterface.cpp123
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3portalinterface_p.h49
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3storage.cpp512
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3storage_p.h48
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3theme.cpp72
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3theme.h2
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/CMakeLists.txt5
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp28
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp47
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h2
17 files changed, 911 insertions, 339 deletions
diff --git a/src/plugins/platformthemes/CMakeLists.txt b/src/plugins/platformthemes/CMakeLists.txt
index be4adc196b..e5abcd1a11 100644
--- a/src/plugins/platformthemes/CMakeLists.txt
+++ b/src/plugins/platformthemes/CMakeLists.txt
@@ -1,7 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from platformthemes.pro.
if(QT_FEATURE_dbus AND QT_FEATURE_mimetype AND QT_FEATURE_regularexpression AND UNIX AND NOT APPLE)
add_subdirectory(xdgdesktopportal)
diff --git a/src/plugins/platformthemes/gtk3/CMakeLists.txt b/src/plugins/platformthemes/gtk3/CMakeLists.txt
index 2ce2971e91..6d3c7bf3a2 100644
--- a/src/plugins/platformthemes/gtk3/CMakeLists.txt
+++ b/src/plugins/platformthemes/gtk3/CMakeLists.txt
@@ -1,12 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from gtk3.pro.
-
-qt_find_package(GTK3) # special case
+qt_find_package(GTK3)
if(QT_FEATURE_xlib)
- qt_find_package(X11) # special case
+ qt_find_package(X11)
endif()
#####################################################################
@@ -25,9 +23,11 @@ qt_internal_add_plugin(QGtk3ThemePlugin
qgtk3interface.cpp qgtk3interface_p.h
qgtk3storage.cpp qgtk3storage_p.h
qgtk3json.cpp qgtk3json_p.h
+ NO_PCH_SOURCES
+ qgtk3dialoghelpers.cpp # undef QT_NO_FOREACH
DEFINES
GDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_6
- LIBRARIES # special case
+ LIBRARIES
PkgConfig::GTK3
Qt::Core
Qt::CorePrivate
@@ -35,10 +35,14 @@ qt_internal_add_plugin(QGtk3ThemePlugin
Qt::GuiPrivate
)
-qt_internal_extend_target(QGtk3ThemePlugin CONDITION QT_FEATURE_xlib
+qt_internal_extend_target(QGtk3ThemePlugin CONDITION QT_FEATURE_dbus
+ SOURCES
+ qgtk3portalinterface.cpp
LIBRARIES
- X11::X11 # special case
+ Qt::DBus
)
-#### Keys ignored in scope 1:.:.:gtk3.pro:<TRUE>:
-# PLUGIN_EXTENDS = "-"
+qt_internal_extend_target(QGtk3ThemePlugin CONDITION QT_FEATURE_xlib
+ LIBRARIES
+ X11::X11
+)
diff --git a/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp
index b8ba58d30e..08419ec7dc 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp
@@ -1,6 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
#include "qgtk3dialoghelpers.h"
#include "qgtk3theme.h"
diff --git a/src/plugins/platformthemes/gtk3/qgtk3interface.cpp b/src/plugins/platformthemes/gtk3/qgtk3interface.cpp
index d3195863af..a35e211fbf 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3interface.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3interface.cpp
@@ -16,7 +16,6 @@
#include "qgtk3interface_p.h"
#include "qgtk3storage_p.h"
#include <QtCore/QMetaEnum>
-#include <QtCore/QSettings>
#include <QtCore/QFileInfo>
#include <QtGui/QFontDatabase>
@@ -64,6 +63,14 @@ QGtk3Interface::~QGtk3Interface()
gtk_widget_destroy(v.second);
}
+/*!
+ \internal
+ \brief Converts a string into the GtkStateFlags enum.
+
+ Converts a string formatted GTK color \param state into an enum value.
+ Returns an integer corresponding to GtkStateFlags.
+ Returns -1 if \param state does not correspond to a valid enum key.
+ */
int QGtk3Interface::toGtkState(const QString &state)
{
#define CASE(x) \
@@ -91,6 +98,10 @@ int QGtk3Interface::toGtkState(const QString &state)
#undef CASE
}
+/*!
+ \internal
+ \brief Returns \param state converted into a string.
+ */
const QLatin1String QGtk3Interface::fromGtkState(GtkStateFlags state)
{
#define CASE(x) case GTK_STATE_FLAG_ ##x: return QLatin1String(#x)
@@ -102,9 +113,12 @@ const QLatin1String QGtk3Interface::fromGtkState(GtkStateFlags state)
#undef CONVERT
}
+/*!
+ \internal
+ \brief Populates the internal map used to find a GTK color's source and fallback generic color.
+ */
void QGtk3Interface::initColorMap()
{
- // Populate map with default values
#define SAVE(src, state, prop, def)\
{ColorKey({QGtkColorSource::src, GTK_STATE_FLAG_ ##state}), ColorValue({#prop ##_L1, QGtkColorDefault::def})}
@@ -131,8 +145,17 @@ void QGtk3Interface::initColorMap()
qCDebug(lcQGtk3Interface) << "Color map populated from defaults.";
}
-// Return an image rather than an icon or a pixmap:
-// Image can be cached and re-scaled to different sizes if requested multiple times
+/*!
+ \internal
+ \brief Returns a QImage corresponding to \param standardPixmap.
+
+ A QImage (not a QPixmap) is returned so it can be cached and re-scaled in case the pixmap is
+ requested multiple times with different resolutions.
+
+ \note Rather than defaulting to a QImage(), all QPlatformTheme::StandardPixmap enum values have
+ been mentioned explicitly.
+ That way they can be covered more easily in case additional icons are provided by GTK.
+ */
QImage QGtk3Interface::standardPixmap(QPlatformTheme::StandardPixmap standardPixmap) const
{
switch (standardPixmap) {
@@ -235,6 +258,10 @@ QImage QGtk3Interface::standardPixmap(QPlatformTheme::StandardPixmap standardPix
Q_UNREACHABLE();
}
+/*!
+ \internal
+ \brief Returns a QImage for a given GTK \param iconName.
+ */
QImage QGtk3Interface::qt_gtk_get_icon(const char* iconName) const
{
GtkIconSet* iconSet = gtk_icon_factory_lookup_default (iconName);
@@ -242,28 +269,46 @@ QImage QGtk3Interface::qt_gtk_get_icon(const char* iconName) const
return qt_convert_gdk_pixbuf(icon);
}
+/*!
+ \internal
+ \brief Returns a QImage converted from the GDK pixel buffer \param buf.
+
+ The ability to convert GdkPixbuf to QImage relies on the following assumptions:
+ \list
+ \li QImage uses uchar as a data container (unasserted)
+ \li the types guint8 and uchar are identical (statically asserted)
+ \li GDK pixel buffer uses 8 bits per sample (assumed at runtime)
+ \li GDK pixel buffer has 4 channels (assumed at runtime)
+ \endlist
+ */
QImage QGtk3Interface::qt_convert_gdk_pixbuf(GdkPixbuf *buf) const
{
if (!buf)
return QImage();
- // Ability to convert GdkPixbuf to QImage relies on the assumptions, that
- // - QImage uses uchar as a data container
- // - the types guint8 and uchar are identical
const guint8 *gdata = gdk_pixbuf_read_pixels(buf);
static_assert(std::is_same<decltype(gdata), const uchar *>::value,
"guint8 has diverted from uchar. Code needs fixing.");
- Q_ASSUME(gdk_pixbuf_get_bits_per_sample(buf) == 8);
- Q_ASSUME(gdk_pixbuf_get_n_channels(buf) == 4);
+ Q_ASSERT(gdk_pixbuf_get_bits_per_sample(buf) == 8);
+ Q_ASSERT(gdk_pixbuf_get_n_channels(buf) == 4);
const uchar *data = static_cast<const uchar *>(gdata);
const int width = gdk_pixbuf_get_width(buf);
const int height = gdk_pixbuf_get_height(buf);
const int bpl = gdk_pixbuf_get_rowstride(buf);
- QImage converted(data, width, height, bpl, QImage::Format_ARGB32);
- return converted.copy(); // detatch to survive lifetime of buf
+ QImage converted(data, width, height, bpl, QImage::Format_RGBA8888);
+
+ // convert to more optimal format and detach to survive lifetime of buf
+ return converted.convertToFormat(QImage::Format_ARGB32_Premultiplied);
}
+/*!
+ \internal
+ \brief Instantiate a new GTK widget.
+
+ Returns a pointer to a new GTK widget of \param type, allocated on the heap.
+ Returns nullptr of gtk_Default has is passed.
+ */
GtkWidget *QGtk3Interface::qt_new_gtkWidget(QGtkWidget type) const
{
#define CASE(Type)\
@@ -298,6 +343,14 @@ GtkWidget *QGtk3Interface::qt_new_gtkWidget(QGtkWidget type) const
Q_UNREACHABLE();
}
+/*!
+ \internal
+ \brief Read a GTK widget's color from a generic color getter.
+
+ This method returns a generic color of \param con, a given GTK style context.
+ The requested color is defined by \param def and the GTK color-state \param state.
+ The return type is GDK color in RGBA format.
+ */
GdkRGBA QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def) const
{
GdkRGBA color;
@@ -316,9 +369,16 @@ GdkRGBA QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state,
#undef CASE
}
-// Deliver a QColor from a GTK widget, a source type and a GTK widget state
-// Fall back to the generic color getter of source/state if the property name does not exist
-// Fall back to a hard coded generic color getter of source/state are not mapped
+/*!
+ \internal
+ \brief Read a GTK widget's color from a property.
+
+ Returns a color of GTK-widget \param widget, defined by \param source and \param state.
+ The return type is GDK color in RGBA format.
+
+ \note If no corresponding property can be found for \param source, the method falls back to a
+ suitable generic color.
+ */
QColor QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkStateFlags state) const
{
GdkRGBA col;
@@ -355,7 +415,15 @@ QColor QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkState
#undef CASE
}
-// Deliver a widget pointer
+/*!
+ \internal
+ \brief Get pointer to a GTK widget by \param type.
+
+ Returns the pointer to a GTK widget, specified by \param type.
+ GTK widgets are cached, so that only one instance of each type is created.
+ \note
+ The method returns nullptr for the enum value gtk_Default.
+ */
GtkWidget *QGtk3Interface::widget(QGtkWidget type) const
{
if (type == QGtkWidget::gtk_Default)
@@ -371,7 +439,14 @@ GtkWidget *QGtk3Interface::widget(QGtkWidget type) const
return w;
}
-// Return widget syle context or default style
+/*!
+ \internal
+ \brief Access a GTK widget's style context.
+
+ Returns the pointer to the style context of GTK widget \param w.
+
+ \note If \param w is nullptr, the GTK default style context (entry style) is returned.
+ */
GtkStyleContext *QGtk3Interface::context(GtkWidget *w) const
{
if (w)
@@ -380,26 +455,73 @@ GtkStyleContext *QGtk3Interface::context(GtkWidget *w) const
return gtk_widget_get_style_context(widget(QGtkWidget::gtk_entry));
}
-// FIXME
-// Brush assets (e.g. 9-patches) can't be accessed by the GTK API.
-// => brush height and width from GTK will be ignored for the time being,
-// because it is unknown if they relate to a plain brush or an image brush.
+/*!
+ \internal
+ \brief Create a QBrush from a GTK widget.
+
+ Returns a QBrush corresponding to GTK widget type \param wtype, \param source and \param state.
+
+ Brush height and width is ignored in GTK3, because brush assets (e.g. 9-patches)
+ can't be accessed by the GTK3 API. It's therefore unknown, if the brush relates only to colors,
+ or to a pixmap based style.
+
+ */
QBrush QGtk3Interface::brush(QGtkWidget wtype, QGtkColorSource source, GtkStateFlags state) const
{
+ // FIXME: When a color's pixmap can be accessed via the GTK API,
+ // read it and set it in the brush.
return QBrush(color(widget(wtype), source, state));
}
-const QString QGtk3Interface::themeName() const
+/*!
+ \internal
+ \brief Returns the name of the current GTK theme.
+ */
+QString QGtk3Interface::themeName() const
{
- gchar *theme_name;
- GtkSettings *settings = gtk_settings_get_default();
- if (!settings)
- return QString();
+ QString name;
+
+ if (GtkSettings *settings = gtk_settings_get_default()) {
+ gchar *theme_name;
+ g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
+ name = QLatin1StringView(theme_name);
+ g_free(theme_name);
+ }
+
+ return name;
+}
+
+/*!
+ \internal
+ \brief Determine color scheme by colors.
- g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
- return QLatin1StringView(theme_name);
+ Returns the color scheme of the current GTK theme, heuristically determined by the
+ lightness difference between default background and foreground colors.
+
+ \note Returns Unknown in the unlikely case that both colors have the same lightness.
+ */
+Qt::ColorScheme QGtk3Interface::colorSchemeByColors() const
+{
+ const QColor background = color(widget(QGtkWidget::gtk_Default),
+ QGtkColorSource::Background,
+ GTK_STATE_FLAG_ACTIVE);
+ const QColor foreground = color(widget(QGtkWidget::gtk_Default),
+ QGtkColorSource::Foreground,
+ GTK_STATE_FLAG_ACTIVE);
+
+ if (foreground.lightness() > background.lightness())
+ return Qt::ColorScheme::Dark;
+ if (foreground.lightness() < background.lightness())
+ return Qt::ColorScheme::Light;
+ return Qt::ColorScheme::Unknown;
}
+/*!
+ \internal
+ \brief Map font type to GTK widget type.
+
+ Returns the GTK widget type corresponding to the given QPlatformTheme::Font \param type.
+ */
inline constexpr QGtk3Interface::QGtkWidget QGtk3Interface::toWidgetType(QPlatformTheme::Font type)
{
switch (type) {
@@ -420,13 +542,13 @@ inline constexpr QGtk3Interface::QGtkWidget QGtk3Interface::toWidgetType(QPlatfo
case QPlatformTheme::ToolButtonFont: return QGtkWidget::gtk_button;
case QPlatformTheme::ItemViewFont: return QGtkWidget::gtk_entry;
case QPlatformTheme::ListViewFont: return QGtkWidget::gtk_tree_view;
- case QPlatformTheme::HeaderViewFont: return QGtkWidget::gtk_fixed;
+ case QPlatformTheme::HeaderViewFont: return QGtkWidget::gtk_combo_box;
case QPlatformTheme::ListBoxFont: return QGtkWidget::gtk_Default;
case QPlatformTheme::ComboMenuItemFont: return QGtkWidget::gtk_combo_box;
case QPlatformTheme::ComboLineEditFont: return QGtkWidget::gtk_combo_box_text;
case QPlatformTheme::SmallFont: return QGtkWidget::gtk_Default;
case QPlatformTheme::MiniFont: return QGtkWidget::gtk_Default;
- case QPlatformTheme::FixedFont: return QGtkWidget::gtk_fixed;
+ case QPlatformTheme::FixedFont: return QGtkWidget::gtk_Default;
case QPlatformTheme::GroupBoxTitleFont: return QGtkWidget::gtk_Default;
case QPlatformTheme::TabButtonFont: return QGtkWidget::gtk_button;
case QPlatformTheme::EditorFont: return QGtkWidget::gtk_entry;
@@ -435,6 +557,10 @@ inline constexpr QGtk3Interface::QGtkWidget QGtk3Interface::toWidgetType(QPlatfo
Q_UNREACHABLE();
}
+/*!
+ \internal
+ \brief Convert pango \param style to QFont::Style.
+ */
inline constexpr QFont::Style QGtk3Interface::toFontStyle(PangoStyle style)
{
switch (style) {
@@ -446,6 +572,13 @@ inline constexpr QFont::Style QGtk3Interface::toFontStyle(PangoStyle style)
Q_UNREACHABLE();
}
+/*!
+ \internal
+ \brief Convert pango font \param weight to an int, representing font weight in Qt.
+
+ Compatibility of PangoWeight is statically asserted.
+ The minimum (1) and maximum (1000) weight in Qt is respeced.
+ */
inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
{
// GTK PangoWeight can be directly converted to QFont::Weight
@@ -459,12 +592,42 @@ inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
return qBound(1, static_cast<int>(weight), 1000);
}
+/*!
+ \internal
+ \brief Return a GTK styled font.
+
+ Returns the QFont corresponding to \param type by reading the corresponding
+ GTK widget type's font.
+
+ \note GTK allows to specify a non fixed font as the system's fixed font.
+ If a fixed font is requested, the method fixes the pitch and falls back to monospace,
+ unless a suitable fixed pitch font is found.
+ */
QFont QGtk3Interface::font(QPlatformTheme::Font type) const
{
GtkStyleContext *con = context(widget(toWidgetType(type)));
if (!con)
return QFont();
+ // explicitly add provider for fixed font
+ GtkCssProvider *cssProvider = nullptr;
+ if (type == QPlatformTheme::FixedFont) {
+ cssProvider = gtk_css_provider_new();
+ gtk_style_context_add_class (con, GTK_STYLE_CLASS_MONOSPACE);
+ const char *fontSpec = "* {font-family: monospace;}";
+ gtk_css_provider_load_from_data(cssProvider, fontSpec, -1, NULL);
+ gtk_style_context_add_provider(con, GTK_STYLE_PROVIDER(cssProvider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+ }
+
+ // remove monospace provider from style context and unref it
+ QScopeGuard guard([&](){
+ if (cssProvider) {
+ gtk_style_context_remove_provider(con, GTK_STYLE_PROVIDER(cssProvider));
+ g_object_unref(cssProvider);
+ }
+ });
+
const PangoFontDescription *gtkFont = gtk_style_context_get_font(con, GTK_STATE_FLAG_NORMAL);
if (!gtkFont)
return QFont();
@@ -482,9 +645,6 @@ QFont QGtk3Interface::font(QPlatformTheme::Font type) const
font.setPointSizeF(static_cast<float>(pango_font_description_get_size(gtkFont)/PANGO_SCALE));
font.setStyle(toFontStyle(pango_font_description_get_style(gtkFont)));
- // fix pixel pitch if fixed font is requested
- // NOTE: GTK allows to specify a non fixed font as the system's fixed font.
- // => the returned font may still not be a fixed font.
if (type == QPlatformTheme::FixedFont) {
font.setFixedPitch(true);
if (!QFontInfo(font).fixedPitch()) {
@@ -494,16 +654,21 @@ QFont QGtk3Interface::font(QPlatformTheme::Font type) const
font.setFamily("monospace"_L1);
}
}
+
return font;
}
+/*!
+ \internal
+ \brief Returns a GTK styled file icon for \param fileInfo.
+ */
QIcon QGtk3Interface::fileIcon(const QFileInfo &fileInfo) const
{
GFile *file = g_file_new_for_path(fileInfo.absoluteFilePath().toLatin1().constData());
if (!file)
return QIcon();
- GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ GFileInfo *info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_ICON,
G_FILE_QUERY_INFO_NONE, nullptr, nullptr);
if (!info) {
g_object_unref(file);
@@ -518,12 +683,11 @@ QIcon QGtk3Interface::fileIcon(const QFileInfo &fileInfo) const
}
GtkIconTheme *theme = gtk_icon_theme_get_default();
- GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(theme, icon, GTK_ICON_SIZE_BUTTON,
+ GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(theme, icon, 16,
GTK_ICON_LOOKUP_FORCE_SIZE);
if (!iconInfo) {
g_object_unref(file);
g_object_unref(info);
- g_object_unref(icon);
return QIcon();
}
@@ -531,7 +695,6 @@ QIcon QGtk3Interface::fileIcon(const QFileInfo &fileInfo) const
QImage image = qt_convert_gdk_pixbuf(buf);
g_object_unref(file);
g_object_unref(info);
- g_object_unref(icon);
g_object_unref(buf);
return QIcon(QPixmap::fromImage(image));
}
diff --git a/src/plugins/platformthemes/gtk3/qgtk3interface_p.h b/src/plugins/platformthemes/gtk3/qgtk3interface_p.h
index 1a44eed6cb..c43932a4fa 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3interface_p.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3interface_p.h
@@ -16,10 +16,10 @@
//
#include <QtCore/QString>
-#include <QtCore/QLibrary>
#include <QtCore/QCache>
#include <private/qflatmap_p.h>
#include <QtCore/QObject>
+#include <QtGui/QIcon>
#include <QtGui/QPalette>
#include <QtWidgets/QWidget>
#include <QtCore/QLoggingCategory>
@@ -38,6 +38,18 @@ Q_DECLARE_LOGGING_CATEGORY(lcQGtk3Interface);
using namespace Qt::StringLiterals;
class QGtk3Storage;
+
+/*!
+ \internal
+ \brief The QGtk3Interface class centralizes communication with the GTK3 library.
+
+ By encapsulating all GTK version specific syntax and conversions, it makes Qt's GTK theme
+ independent from GTK versions.
+
+ \note
+ Including GTK3 headers requires #undef signals, which disables Qt signal/slot handling.
+ */
+
class QGtk3Interface
{
Q_GADGET
@@ -45,7 +57,13 @@ public:
QGtk3Interface(QGtk3Storage *);
~QGtk3Interface();
- // Enum representing GTK widget types
+ /*!
+ * \internal
+ \enum QGtk3Interface::QGtkWidget
+ \brief Represents GTK widget types used to obtain color information.
+
+ \note The enum value gtk_Default refers to the GTK default style, rather than to a specific widget.
+ */
enum class QGtkWidget {
gtk_menu_bar,
gtk_menu,
@@ -70,7 +88,15 @@ public:
};
Q_ENUM(QGtkWidget)
- // Enum representing color sources of a GTK theme
+ /*!
+ \internal
+ \enum QGtk3Interface::QGtkColorSource
+ \brief The QGtkColorSource enum represents the source of a color within a GTK widgets style context.
+
+ If the current GTK theme provides such a color for a given widget, the color can be read
+ from the style context by passing the enum's key as a property name to the GTK method
+ gtk_style_context_lookup_color. The method will return false, if no color has been found.
+ */
enum class QGtkColorSource {
Foreground,
Background,
@@ -80,7 +106,18 @@ public:
};
Q_ENUM(QGtkColorSource)
- // Enum for default color getter
+ /*!
+ \internal
+ \enum QGtk3Interface::QGtkColorDefault
+ \brief The QGtkColorDefault enum represents generic GTK colors.
+
+ The GTK3 methods gtk_style_context_get_color, gtk_style_context_get_background_color, and
+ gtk_style_context_get_foreground_color always return the respective colors with a widget's
+ style context. Unless set as a property by the current GTK theme, GTK's default colors will
+ be returned.
+ These generic default colors, represented by the GtkColorDefault enum, are used as a
+ back, if a specific color property is requested but not defined in the current GTK theme.
+ */
enum class QGtkColorDefault {
Foreground,
Background,
@@ -97,7 +134,10 @@ public:
QIcon fileIcon(const QFileInfo &fileInfo) const;
// Return current GTK theme name
- const QString themeName() const;
+ QString themeName() const;
+
+ // Derive color scheme from default colors
+ Qt::ColorScheme colorSchemeByColors() const;
// Convert GTK state to/from string
static int toGtkState(const QString &state);
diff --git a/src/plugins/platformthemes/gtk3/qgtk3json.cpp b/src/plugins/platformthemes/gtk3/qgtk3json.cpp
index 039ae5313c..eb81e563be 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3json.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3json.cpp
@@ -53,9 +53,9 @@ QLatin1String QGtk3Json::fromWidgetType(QGtk3Interface::QGtkWidget widgetType)
return QLatin1String(QMetaEnum::fromType<QGtk3Interface::QGtkWidget>().valueToKey(static_cast<int>(widgetType)));
}
-QLatin1String QGtk3Json::fromAppearance(Qt::Appearance app)
+QLatin1String QGtk3Json::fromColorScheme(Qt::ColorScheme app)
{
- return QLatin1String(QMetaEnum::fromType<Qt::Appearance>().valueToKey(static_cast<int>(app)));
+ return QLatin1String(QMetaEnum::fromType<Qt::ColorScheme>().valueToKey(static_cast<int>(app)));
}
#define CONVERT(type, key, def)\
@@ -63,9 +63,9 @@ QLatin1String QGtk3Json::fromAppearance(Qt::Appearance app)
const int intVal = QMetaEnum::fromType<type>().keyToValue(key.toLatin1().constData(), &ok);\
return ok ? static_cast<type>(intVal) : type::def
-Qt::Appearance QGtk3Json::toAppearance(const QString &appearance)
+Qt::ColorScheme QGtk3Json::toColorScheme(const QString &colorScheme)
{
- CONVERT(Qt::Appearance, appearance, Unknown);
+ CONVERT(Qt::ColorScheme, colorScheme, Unknown);
}
QPlatformTheme::Palette QGtk3Json::toPalette(const QString &palette)
@@ -175,7 +175,7 @@ const QJsonDocument QGtk3Json::save(const QGtk3Storage::PaletteMap &map)
const QGtk3Storage::TargetBrush tb = brushIterator.key();
QGtk3Storage::Source s = brushIterator.value();
brushObject.insert(ceColorGroup, fromColorGroup(tb.colorGroup));
- brushObject.insert(ceAppearance, fromAppearance(tb.appearance));
+ brushObject.insert(ceColorScheme, fromColorScheme(tb.colorScheme));
brushObject.insert(ceSourceType, fromSourceType(s.sourceType));
QJsonObject sourceObject;
@@ -201,7 +201,7 @@ const QJsonDocument QGtk3Json::save(const QGtk3Storage::PaletteMap &map)
case QGtk3Storage::SourceType::Modified:{
sourceObject.insert(ceColorGroup, fromColorGroup(s.rec.colorGroup));
sourceObject.insert(ceColorRole, fromColorRole(s.rec.colorRole));
- sourceObject.insert(ceAppearance, fromAppearance(s.rec.appearance));
+ sourceObject.insert(ceColorScheme, fromColorScheme(s.rec.colorScheme));
sourceObject.insert(ceRed, s.rec.deltaRed);
sourceObject.insert(ceGreen, s.rec.deltaGreen);
sourceObject.insert(ceBlue, s.rec.deltaBlue);
@@ -258,7 +258,7 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
{
#define GETSTR(obj, key)\
if (!obj.contains(key)) {\
- qCDebug(lcQGtk3Interface) << key << "missing for palette" << paletteName\
+ qCInfo(lcQGtk3Interface) << key << "missing for palette" << paletteName\
<< ", Brush" << colorRoleName;\
return false;\
}\
@@ -266,7 +266,7 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
#define GETINT(obj, key, var) GETSTR(obj, key);\
if (!obj[key].isDouble()) {\
- qCDebug(lcQGtk3Interface) << key << "type mismatch" << value\
+ qCInfo(lcQGtk3Interface) << key << "type mismatch" << value\
<< "is not an integer!"\
<< "(Palette" << paletteName << "), Brush" << colorRoleName;\
return false;\
@@ -276,7 +276,7 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
map.clear();
const QJsonObject top(doc.object());
if (doc.isEmpty() || top.isEmpty() || !top.contains(cePalettes)) {
- qCDebug(lcQGtk3Interface) << "Document does not contain Palettes.";
+ qCInfo(lcQGtk3Interface) << "Document does not contain Palettes.";
return false;
}
@@ -286,13 +286,13 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
const int intVal = QMetaEnum::fromType<QPlatformTheme::Palette>().keyToValue(paletteName
.toLatin1().constData(), &ok);
if (!ok) {
- qCDebug(lcQGtk3Interface) << "Invalid Palette name:" << paletteName;
+ qCInfo(lcQGtk3Interface) << "Invalid Palette name:" << paletteName;
return false;
}
const QJsonObject &paletteObject = top[cePalettes][paletteName].toObject();
const QStringList &brushList = paletteObject.keys();
if (brushList.isEmpty()) {
- qCDebug(lcQGtk3Interface) << "Palette" << paletteName << "does not contain brushes";
+ qCInfo(lcQGtk3Interface) << "Palette" << paletteName << "does not contain brushes";
return false;
}
@@ -303,7 +303,7 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
const int intVal = QMetaEnum::fromType<QPalette::ColorRole>().keyToValue(colorRoleName
.toLatin1().constData(), &ok);
if (!ok) {
- qCDebug(lcQGtk3Interface) << "Palette" << paletteName
+ qCInfo(lcQGtk3Interface) << "Palette" << paletteName
<< "contains invalid color role" << colorRoleName;
return false;
}
@@ -312,7 +312,7 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
for (int brushIndex = 0; brushIndex < brushArray.size(); ++brushIndex) {
const QJsonObject brushObject = brushArray.at(brushIndex).toObject();
if (brushObject.isEmpty()) {
- qCDebug(lcQGtk3Interface) << "Brush specification missing at for palette"
+ qCInfo(lcQGtk3Interface) << "Brush specification missing at for palette"
<< paletteName << ", Brush" << colorRoleName;
return false;
}
@@ -322,13 +322,13 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
const QGtk3Storage::SourceType sourceType = toSourceType(value);
GETSTR(brushObject, ceColorGroup);
const QPalette::ColorGroup colorGroup = toColorGroup(value);
- GETSTR(brushObject, ceAppearance);
- const Qt::Appearance appearance = toAppearance(value);
- QGtk3Storage::TargetBrush tb(colorGroup, colorRole, appearance);
+ GETSTR(brushObject, ceColorScheme);
+ const Qt::ColorScheme colorScheme = toColorScheme(value);
+ QGtk3Storage::TargetBrush tb(colorGroup, colorRole, colorScheme);
QGtk3Storage::Source s;
if (!brushObject.contains(ceData) || !brushObject[ceData].isObject()) {
- qCDebug(lcQGtk3Interface) << "Source specification missing for palette" << paletteName
+ qCInfo(lcQGtk3Interface) << "Source specification missing for palette" << paletteName
<< "Brush" << colorRoleName;
return false;
}
@@ -350,7 +350,7 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
case QGtk3Storage::SourceType::Fixed: {
if (!sourceObject.contains(ceBrush)) {
- qCDebug(lcQGtk3Interface) << "Fixed brush specification missing for palette" << paletteName
+ qCInfo(lcQGtk3Interface) << "Fixed brush specification missing for palette" << paletteName
<< "Brush" << colorRoleName;
return false;
}
@@ -360,7 +360,7 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
GETSTR(fixedSource, ceColor);
const QColor color(value);
if (!color.isValid()) {
- qCDebug(lcQGtk3Interface) << "Color" << value << "can't be parsed for:" << paletteName
+ qCInfo(lcQGtk3Interface) << "Color" << value << "can't be parsed for:" << paletteName
<< "Brush" << colorRoleName;
return false;
}
@@ -376,19 +376,19 @@ bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
const QPalette::ColorGroup colorGroup = toColorGroup(value);
GETSTR(sourceObject, ceColorRole);
const QPalette::ColorRole colorRole = toColorRole(value);
- GETSTR(sourceObject, ceAppearance);
- const Qt::Appearance appearance = toAppearance(value);
+ GETSTR(sourceObject, ceColorScheme);
+ const Qt::ColorScheme colorScheme = toColorScheme(value);
GETINT(sourceObject, ceLighter, lighter);
GETINT(sourceObject, ceRed, red);
GETINT(sourceObject, ceBlue, blue);
GETINT(sourceObject, ceGreen, green);
- s = QGtk3Storage::Source(colorGroup, colorRole, appearance,
+ s = QGtk3Storage::Source(colorGroup, colorRole, colorScheme,
lighter, red, green, blue);
}
break;
case QGtk3Storage::SourceType::Invalid:
- qCDebug(lcQGtk3Interface) << "Invalid source type for palette" << paletteName
+ qInfo(lcQGtk3Interface) << "Invalid source type for palette" << paletteName
<< "Brush." << colorRoleName;
return false;
}
diff --git a/src/plugins/platformthemes/gtk3/qgtk3json_p.h b/src/plugins/platformthemes/gtk3/qgtk3json_p.h
index b4477eb667..daf280612c 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3json_p.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3json_p.h
@@ -50,7 +50,7 @@ public:
static QLatin1String fromGdkSource(QGtk3Interface::QGtkColorSource source);
static QLatin1String fromSourceType(QGtk3Storage::SourceType sourceType);
static QLatin1String fromWidgetType(QGtk3Interface::QGtkWidget widgetType);
- static QLatin1String fromAppearance(Qt::Appearance app);
+ static QLatin1String fromColorScheme(Qt::ColorScheme colorScheme);
// Convert strings to enums
static QPlatformTheme::Palette toPalette(const QString &palette);
@@ -61,7 +61,7 @@ public:
static QGtk3Interface::QGtkColorSource toGdkSource(const QString &source);
static QGtk3Storage::SourceType toSourceType(const QString &sourceType);
static QGtk3Interface::QGtkWidget toWidgetType(const QString &widgetType);
- static Qt::Appearance toAppearance(const QString &appearance);
+ static Qt::ColorScheme toColorScheme(const QString &colorScheme);
// Json keys
static constexpr QLatin1StringView cePalettes = "QtGtk3Palettes"_L1;
@@ -82,7 +82,7 @@ public:
static constexpr QLatin1StringView ceBrush = "FixedBrush"_L1;
static constexpr QLatin1StringView ceData = "SourceData"_L1;
static constexpr QLatin1StringView ceBrushes = "Brushes"_L1;
- static constexpr QLatin1StringView ceAppearance = "Appearance"_L1;
+ static constexpr QLatin1StringView ceColorScheme = "ColorScheme"_L1;
// Save to a file
static bool save(const QGtk3Storage::PaletteMap &map, const QString &fileName,
diff --git a/src/plugins/platformthemes/gtk3/qgtk3portalinterface.cpp b/src/plugins/platformthemes/gtk3/qgtk3portalinterface.cpp
new file mode 100644
index 0000000000..1ffdda74fa
--- /dev/null
+++ b/src/plugins/platformthemes/gtk3/qgtk3portalinterface.cpp
@@ -0,0 +1,123 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgtk3portalinterface_p.h"
+#include "qgtk3storage_p.h"
+
+#include <QtDBus/QDBusArgument>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusMessage>
+#include <QtDBus/QDBusPendingCall>
+#include <QtDBus/QDBusPendingCallWatcher>
+#include <QtDBus/QDBusPendingReply>
+#include <QtDBus/QDBusVariant>
+#include <QtDBus/QtDBus>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQGtk3PortalInterface, "qt.qpa.gtk");
+
+using namespace Qt::StringLiterals;
+
+static constexpr QLatin1StringView appearanceInterface("org.freedesktop.appearance");
+static constexpr QLatin1StringView colorSchemeKey("color-scheme");
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QMap<QString, QVariantMap> &map)
+{
+ argument.beginMap();
+ map.clear();
+
+ while (!argument.atEnd()) {
+ QString key;
+ QVariantMap value;
+ argument.beginMapEntry();
+ argument >> key >> value;
+ argument.endMapEntry();
+ map.insert(key, value);
+ }
+
+ argument.endMap();
+ return argument;
+}
+
+QGtk3PortalInterface::QGtk3PortalInterface(QGtk3Storage *s)
+ : m_storage(s) {
+ qRegisterMetaType<QDBusVariant>();
+ qDBusRegisterMetaType<QMap<QString, QVariantMap>>();
+
+ queryColorScheme();
+
+ if (!s) {
+ qCDebug(lcQGtk3PortalInterface) << "QGtk3PortalInterface instantiated without QGtk3Storage."
+ << "No reaction to runtime theme changes.";
+ }
+}
+
+Qt::ColorScheme QGtk3PortalInterface::colorScheme() const
+{
+ return m_colorScheme;
+}
+
+void QGtk3PortalInterface::queryColorScheme() {
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ QDBusMessage message = QDBusMessage::createMethodCall(
+ "org.freedesktop.portal.Desktop"_L1,
+ "/org/freedesktop/portal/desktop"_L1,
+ "org.freedesktop.portal.Settings"_L1, "ReadAll"_L1);
+ message << QStringList{ appearanceInterface };
+
+ QDBusPendingCall pendingCall = connection.asyncCall(message);
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
+ QObject::connect(
+ watcher, &QDBusPendingCallWatcher::finished, this,
+ [this](QDBusPendingCallWatcher *watcher) {
+ QDBusPendingReply<QMap<QString, QVariantMap>> reply = *watcher;
+ if (reply.isValid()) {
+ QMap<QString, QVariantMap> settings = reply.value();
+ if (!settings.isEmpty()) {
+ settingChanged(appearanceInterface, colorSchemeKey,
+ QDBusVariant(settings.value(appearanceInterface).value(colorSchemeKey)));
+ }
+ } else {
+ qCDebug(lcQGtk3PortalInterface) << "Failed to query org.freedesktop.portal.Settings: "
+ << reply.error().message();
+ }
+ watcher->deleteLater();
+ });
+
+ QDBusConnection::sessionBus().connect(
+ "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1,
+ "org.freedesktop.portal.Settings"_L1, "SettingChanged"_L1, this,
+ SLOT(settingChanged(QString, QString, QDBusVariant)));
+}
+
+void QGtk3PortalInterface::settingChanged(const QString &group, const QString &key,
+ const QDBusVariant &value)
+{
+ if (group == appearanceInterface && key == colorSchemeKey) {
+ const uint colorScheme = value.variant().toUInt();
+ // From org.freedesktop.portal.Settings.xml
+ // "1" - Prefer dark appearance
+ Qt::ColorScheme newColorScheme = colorScheme == 1 ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light;
+ if (m_colorScheme != newColorScheme) {
+ m_colorScheme = newColorScheme;
+ if (m_storage)
+ m_storage->handleThemeChange();
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgtk3portalinterface_p.cpp"
diff --git a/src/plugins/platformthemes/gtk3/qgtk3portalinterface_p.h b/src/plugins/platformthemes/gtk3/qgtk3portalinterface_p.h
new file mode 100644
index 0000000000..25a5f58ab1
--- /dev/null
+++ b/src/plugins/platformthemes/gtk3/qgtk3portalinterface_p.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QGTK3PORTALINTERFACE_H
+#define QGTK3PORTALINTERFACE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QObject>
+#include <QtCore/QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+class QDBusVariant;
+class QGtk3Storage;
+
+Q_DECLARE_LOGGING_CATEGORY(lcQGtk3PortalInterface);
+
+class QGtk3PortalInterface : public QObject
+{
+ Q_OBJECT
+public:
+ QGtk3PortalInterface(QGtk3Storage *s);
+ ~QGtk3PortalInterface() = default;
+
+ Qt::ColorScheme colorScheme() const;
+
+private Q_SLOTS:
+ void settingChanged(const QString &group, const QString &key,
+ const QDBusVariant &value);
+private:
+ void queryColorScheme();
+
+ Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
+ QGtk3Storage *m_storage = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGTK3PORTALINTERFACE_H
diff --git a/src/plugins/platformthemes/gtk3/qgtk3storage.cpp b/src/plugins/platformthemes/gtk3/qgtk3storage.cpp
index bf694fe3e1..2877b28590 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3storage.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3storage.cpp
@@ -21,10 +21,32 @@ QT_BEGIN_NAMESPACE
QGtk3Storage::QGtk3Storage()
{
m_interface.reset(new QGtk3Interface(this));
+#if QT_CONFIG(dbus)
+ m_portalInterface.reset(new QGtk3PortalInterface(this));
+#endif
populateMap();
}
-// Set a brush from a source and resolve recursions
+/*!
+ \internal
+ \enum QGtk3Storage::SourceType
+ \brief This enum represents the type of a color source.
+
+ \value Gtk Color is read from a GTK widget
+ \value Fixed A fixed brush is specified
+ \value Modified The color is a modification of another color (fixed or read from GTK)
+ \omitvalue Invalid
+ */
+
+/*!
+ \internal
+ \brief Find a brush from a source.
+
+ Returns a QBrush from a given \param source and a \param map of available brushes
+ to search from.
+
+ A null QBrush is returned, if no brush corresponding to the source has been found.
+ */
QBrush QGtk3Storage::brush(const Source &source, const BrushMap &map) const
{
switch (source.sourceType) {
@@ -36,7 +58,7 @@ QBrush QGtk3Storage::brush(const Source &source, const BrushMap &map) const
case SourceType::Modified: {
// don't loop through modified sources, break if modified source not found
Source recSource = brush(TargetBrush(source.rec.colorGroup, source.rec.colorRole,
- source.rec.appearance), map);
+ source.rec.colorScheme), map);
if (!recSource.isValid() || (recSource.sourceType == SourceType::Modified))
return QBrush();
@@ -64,7 +86,14 @@ QBrush QGtk3Storage::brush(const Source &source, const BrushMap &map) const
Q_UNREACHABLE();
}
-// Find source for a recursion and take dark/light/unknown into consideration
+/*!
+ \internal
+ \brief Recurse to find a source brush for modification.
+
+ Returns the source specified by the target brush \param b in the \param map of brushes.
+ Takes dark/light/unknown into consideration.
+ Returns an empty brush if no suitable one can be found.
+ */
QGtk3Storage::Source QGtk3Storage::brush(const TargetBrush &b, const BrushMap &map) const
{
#define FIND(brush) if (map.contains(brush))\
@@ -73,22 +102,31 @@ QGtk3Storage::Source QGtk3Storage::brush(const TargetBrush &b, const BrushMap &m
// Return exact match
FIND(b);
- // unknown appearance can find anything
- if (b.appearance == Qt::Appearance::Unknown) {
- FIND(TargetBrush(b, Qt::Appearance::Dark));
- FIND(TargetBrush(b, Qt::Appearance::Light));
+ // unknown color scheme can find anything
+ if (b.colorScheme == Qt::ColorScheme::Unknown) {
+ FIND(TargetBrush(b, Qt::ColorScheme::Dark));
+ FIND(TargetBrush(b, Qt::ColorScheme::Light));
}
// Color group All can always be found
if (b.colorGroup != QPalette::All)
- return brush(TargetBrush(QPalette::All, b.colorRole, b.appearance), map);
+ return brush(TargetBrush(QPalette::All, b.colorRole, b.colorScheme), map);
// Brush not found
return Source();
#undef FIND
}
-// Create a simple standard palette
+/*!
+ \internal
+ \brief Returns a simple, hard coded base palette.
+
+ Create a hard coded palette with default colors as a fallback for any color that can't be
+ obtained from GTK.
+
+ \note This palette will be used as a default baseline for the system palette, which then
+ will be used as a default baseline for any other palette type.
+ */
QPalette QGtk3Storage::standardPalette()
{
QColor backgroundColor(0xd4, 0xd0, 0xc8);
@@ -105,7 +143,13 @@ QPalette QGtk3Storage::standardPalette()
return palette;
}
-// Deliver a palette styled according to the current GTK Theme
+/*!
+ \internal
+ \brief Return a GTK styled QPalette.
+
+ Returns the pointer to a (cached) QPalette for \param type, with its brushes
+ populated according to the current GTK theme.
+ */
const QPalette *QGtk3Storage::palette(QPlatformTheme::Palette type) const
{
if (type >= QPlatformTheme::NPalettes)
@@ -140,13 +184,13 @@ const QPalette *QGtk3Storage::palette(QPlatformTheme::Palette type) const
Source source = i.value();
// Brush is set if
- // - theme and source appearance match
+ // - theme and source color scheme match
// - or either of them is unknown
- const auto appSource = i.key().appearance;
- const auto appTheme = appearance();
+ const auto appSource = i.key().colorScheme;
+ const auto appTheme = colorScheme();
const bool setBrush = (appSource == appTheme) ||
- (appSource == Qt::Appearance::Unknown) ||
- (appTheme == Qt::Appearance::Unknown);
+ (appSource == Qt::ColorScheme::Unknown) ||
+ (appTheme == Qt::ColorScheme::Unknown);
if (setBrush) {
p.setBrush(i.key().colorGroup, i.key().colorRole, brush(source, brushes));
@@ -155,11 +199,17 @@ const QPalette *QGtk3Storage::palette(QPlatformTheme::Palette type) const
m_paletteCache[type].emplace(p);
if (type == QPlatformTheme::SystemPalette)
- qCDebug(lcQGtk3Interface) << "System Palette defined" << themeName() << appearance() << p;
+ qCDebug(lcQGtk3Interface) << "System Palette defined" << themeName() << colorScheme() << p;
return &m_paletteCache[type].value();
}
+/*!
+ \internal
+ \brief Return a GTK styled font.
+
+ Returns a QFont of \param type, styled according to the current GTK theme.
+*/
const QFont *QGtk3Storage::font(QPlatformTheme::Font type) const
{
if (m_fontCache[type].has_value())
@@ -169,6 +219,13 @@ const QFont *QGtk3Storage::font(QPlatformTheme::Font type) const
return &m_fontCache[type].value();
}
+/*!
+ \internal
+ \brief Return a GTK styled standard pixmap if available.
+
+ Returns a pixmap specified by \param standardPixmap and \param size.
+ Returns an empty pixmap if GTK doesn't support the requested one.
+ */
QPixmap QGtk3Storage::standardPixmap(QPlatformTheme::StandardPixmap standardPixmap,
const QSizeF &size) const
{
@@ -186,14 +243,22 @@ QPixmap QGtk3Storage::standardPixmap(QPlatformTheme::StandardPixmap standardPixm
return QPixmap::fromImage(image.scaled(size.toSize()));
}
+/*!
+ \internal
+ \brief Returns a GTK styled file icon corresponding to \param fileInfo.
+ */
QIcon QGtk3Storage::fileIcon(const QFileInfo &fileInfo) const
{
return m_interface ? m_interface->fileIcon(fileInfo) : QIcon();
}
+/*!
+ \internal
+ \brief Clears all caches.
+ */
void QGtk3Storage::clear()
{
- m_appearance = Qt::Appearance::Unknown;
+ m_colorScheme = Qt::ColorScheme::Unknown;
m_palettes.clear();
for (auto &cache : m_paletteCache)
cache.reset();
@@ -202,33 +267,98 @@ void QGtk3Storage::clear()
cache.reset();
}
+/*!
+ \internal
+ \brief Handles a theme change at runtime.
+
+ Clear all caches, re-populate with current GTK theme and notify the window system interface.
+ This method is a callback for the theme change signal sent from GTK.
+ */
void QGtk3Storage::handleThemeChange()
{
- clear();
populateMap();
QWindowSystemInterface::handleThemeChange();
}
+/*!
+ \internal
+ \brief Populates a map with information about how to locate colors in GTK.
+
+ This method creates a data structure to locate color information for each brush of a QPalette
+ within GTK. The structure can hold mapping information for each QPlatformTheme::Palette
+ enum value. If no specific mapping is stored for an enum value, the system palette is returned
+ instead of a specific one. If no mapping is stored for the system palette, it will fall back to
+ QGtk3Storage::standardPalette.
+
+ The method will populate the data structure with a standard mapping, covering the following
+ palette types:
+ \list
+ \li QPlatformTheme::SystemPalette
+ \li QPlatformTheme::CheckBoxPalette
+ \li QPlatformTheme::RadioButtonPalette
+ \li QPlatformTheme::ComboBoxPalette
+ \li QPlatformTheme::GroupBoxPalette
+ \li QPlatformTheme::MenuPalette
+ \li QPlatformTheme::TextLineEditPalette
+ \endlist
+
+ The method will check the environment variable {{QT_GUI_GTK_JSON_SAVE}}. If it points to a
+ valid path with write access, it will write the standard mapping into a Json file.
+ That Json file can be modified and/or extended.
+ The Json syntax is
+ - "QGtk3Palettes" (top level value)
+ - QPlatformTheme::Palette
+ - QPalette::ColorRole
+ - Qt::ColorScheme
+ - Qt::ColorGroup
+ - Source data
+ - Source Type
+ - [source data]
+
+ If the environment variable {{QT_GUI_GTK_JSON_HARDCODED}} contains the keyword \c true,
+ all sources are converted to fixed sources. In that case, they contain the hard coded HexRGBA
+ values read from GTK.
+
+ The method will also check the environment variable {{QT_GUI_GTK_JSON}}. If it points to a valid
+ Json file with read access, it will be parsed instead of creating a standard mapping.
+ Parsing errors will be printed out with qCInfo if the logging category {{qt.qpa.gtk}} is activated.
+ In case of a parsing error, the method will fall back to creating a standard mapping.
+
+ \note
+ If a Json file contains only fixed brushes (e.g. exported with {{QT_GUI_GTK_JSON_HARDCODED=true}}),
+ no colors will be imported from GTK.
+ */
void QGtk3Storage::populateMap()
{
static QString m_themeName;
// Distiguish initialization, theme change or call without theme change
+ Qt::ColorScheme newColorScheme = Qt::ColorScheme::Unknown;
const QString newThemeName = themeName();
- if (m_themeName == newThemeName)
+
+#if QT_CONFIG(dbus)
+ // Prefer color scheme we get from xdg-desktop-portal as this is what GNOME
+ // relies on these days
+ newColorScheme = m_portalInterface->colorScheme();
+#endif
+
+ if (newColorScheme == Qt::ColorScheme::Unknown) {
+ // Derive color scheme from theme name
+ newColorScheme = newThemeName.contains("dark"_L1, Qt::CaseInsensitive)
+ ? Qt::ColorScheme::Dark : m_interface->colorSchemeByColors();
+ }
+
+ if (m_themeName == newThemeName && m_colorScheme == newColorScheme)
return;
clear();
- // Derive appearance from theme name
- m_appearance = newThemeName.contains("dark"_L1, Qt::CaseInsensitive)
- ? Qt::Appearance::Dark : Qt::Appearance::Light;
-
if (m_themeName.isEmpty()) {
- qCDebug(lcQGtk3Interface) << "GTK theme initialized:" << newThemeName << m_appearance;
+ qCDebug(lcQGtk3Interface) << "GTK theme initialized:" << newThemeName << newColorScheme;
} else {
- qCDebug(lcQGtk3Interface) << "GTK theme changed to:" << newThemeName << m_appearance;
+ qCDebug(lcQGtk3Interface) << "GTK theme changed to:" << newThemeName << newColorScheme;
}
+ m_colorScheme = newColorScheme;
m_themeName = newThemeName;
// create standard mapping or load from Json file?
@@ -248,6 +378,15 @@ void QGtk3Storage::populateMap()
qWarning() << "File" << jsonOutput << "could not be saved.\n";
}
+/*!
+ \internal
+ \brief Return a palette map for saving.
+
+ This method returns the existing palette map, if the environment variable
+ {{QT_GUI_GTK_JSON_HARDCODED}} is not set or does not contain the keyword \c true.
+ If it contains the keyword \c true, it returns a palette map with all brush
+ sources converted to fixed sources.
+ */
const QGtk3Storage::PaletteMap QGtk3Storage::savePalettes() const
{
const QString hard = qEnvironmentVariable("QT_GUI_GTK_JSON_HARDCODED");
@@ -282,21 +421,50 @@ const QGtk3Storage::PaletteMap QGtk3Storage::savePalettes() const
return map;
}
+/*!
+ \internal
+ \brief Saves current palette mapping to a \param filename with Json format \param f.
+
+ Saves the current palette mapping into a QJson file,
+ taking {{QT_GUI_GTK_JSON_HARDCODED}} into consideration.
+ Returns \c true if saving was successful and \c false otherwise.
+ */
bool QGtk3Storage::save(const QString &filename, QJsonDocument::JsonFormat f) const
{
return QGtk3Json::save(savePalettes(), filename, f);
}
+/*!
+ \internal
+ \brief Returns a QJsonDocument with current palette mapping.
+
+ Saves the current palette mapping into a QJsonDocument,
+ taking {{QT_GUI_GTK_JSON_HARDCODED}} into consideration.
+ Returns \c true if saving was successful and \c false otherwise.
+ */
QJsonDocument QGtk3Storage::save() const
{
return QGtk3Json::save(savePalettes());
}
+/*!
+ \internal
+ \brief Loads palette mapping from Json file \param filename.
+
+ Returns \c true if the file was successfully parsed and \c false otherwise.
+ */
bool QGtk3Storage::load(const QString &filename)
{
return QGtk3Json::load(m_palettes, filename);
}
+/*!
+ \internal
+ \brief Creates a standard palette mapping.
+
+ The method creates a hard coded standard mapping, used if no external Json file
+ containing a valid mapping has been specified in the environment variable {{QT_GUI_GTK_JSON}}.
+ */
void QGtk3Storage::createMapping()
{
// Hard code standard mapping
@@ -311,19 +479,19 @@ void QGtk3Storage::createMapping()
// Define a modified source
#define LIGHTER(group, role, lighter)\
source = Source(QPalette::group, QPalette::role,\
- Qt::Appearance::Unknown, lighter)
+ Qt::ColorScheme::Unknown, lighter)
#define MODIFY(group, role, red, green, blue)\
source = Source(QPalette::group, QPalette::role,\
- Qt::Appearance::Unknown, red, green, blue)
+ Qt::ColorScheme::Unknown, red, green, blue)
// Define fixed source
#define FIX(color) source = FixedSource(color);
// Add the source to a target brush
- // Use default Qt::Appearance::Unknown, if no appearance was specified
+ // Use default Qt::ColorScheme::Unknown, if no color scheme was specified
#define ADD_2(group, role) map.insert(TargetBrush(QPalette::group, QPalette::role), source);
#define ADD_3(group, role, app) map.insert(TargetBrush(QPalette::group, QPalette::role,\
- Qt::Appearance::app), source);
+ Qt::ColorScheme::app), source);
#define ADD_X(x, group, role, app, FUNC, ...) FUNC
#define ADD(...) ADD_X(,##__VA_ARGS__, ADD_3(__VA_ARGS__), ADD_2(__VA_ARGS__))
// Save target brushes to a palette type
@@ -332,129 +500,177 @@ void QGtk3Storage::createMapping()
#define CLEAR map.clear()
/*
- * Macro ussage:
- *
- * 1. Define a source
- *
- * GTK(QGtkWidget, QGtkColorSource, GTK_STATE_FLAG)
- * Fetch the color from a GtkWidget, related to a source and a state.
- *
- * LIGHTER(ColorGroup, ColorROle, lighter)
- * Use a color of the same QPalette related to ColorGroup and ColorRole.
- * Make the color lighter (if lighter >100) or darker (if lighter < 100)
- *
- * MODIFY(ColorGroup, ColorRole, red, green, blue)
- * Use a color of the same QPalette related to ColorGroup and ColorRole.
- * Modify it by adding red, green, blue.
- *
- * FIX(const QBrush &)
- * Use a fixed brush without querying GTK
- *
- * 2. Define the target
- *
- * Use ADD(ColorGroup, ColorRole) to use the defined source for the
- * color group / role in the current palette.
- *
- * Use ADD(ColorGroup, ColorRole, Appearance) to use the defined source
- * only for a specific appearance
- *
- * 3. Save mapping
- * Save the defined mappings for a specific palette.
- * If a mapping entry does not cover all color groups and roles of a palette,
- * the system palette will be used for the remaining values.
- * If the system palette does not have all combination of color groups and roles,
- * the remaining ones will be populated by a hard coded fusion-style like palette.
- *
- * 4. Clear mapping
- * Use CLEAR to clear the mapping and begin a new one.
+ Macro usage:
+
+ 1. Define a source
+ GTK(QGtkWidget, QGtkColorSource, GTK_STATE_FLAG)
+ Fetch the color from a GtkWidget, related to a source and a state.
+
+ LIGHTER(ColorGroup, ColorROle, lighter)
+ Use a color of the same QPalette related to ColorGroup and ColorRole.
+ Make the color lighter (if lighter >100) or darker (if lighter < 100)
+
+ MODIFY(ColorGroup, ColorRole, red, green, blue)
+ Use a color of the same QPalette related to ColorGroup and ColorRole.
+ Modify it by adding red, green, blue.
+
+ FIX(const QBrush &)
+ Use a fixed brush without querying GTK
+
+ 2. Define the target
+ Use ADD(ColorGroup, ColorRole) to use the defined source for the
+ color group / role in the current palette.
+
+ Use ADD(ColorGroup, ColorRole, ColorScheme) to use the defined source
+ only for a specific color scheme
+
+ 3. Save mapping
+ Save the defined mappings for a specific palette.
+ If a mapping entry does not cover all color groups and roles of a palette,
+ the system palette will be used for the remaining values.
+ If the system palette does not have all combination of color groups and roles,
+ the remaining ones will be populated by a hard coded fusion-style like palette.
+
+ 4. Clear mapping
+ Use CLEAR to clear the mapping and begin a new one.
*/
// System palette
- // background color and calculate derivates
- GTK(Default, Background, INSENSITIVE);
- ADD(Normal, Window);
- ADD(Normal, Button);
- ADD(Normal, Base);
- ADD(Inactive, Base);
- ADD(Normal, Window); // redundant
- ADD(Inactive, Window);
- LIGHTER(Normal, Window, 125);
- ADD(Normal, Light);
- LIGHTER(Normal, Window, 70);
- ADD(Normal, Shadow);
- LIGHTER(Normal, Window, 80);
- ADD(Normal, Dark);
- GTK(button, Foreground, ACTIVE);
- ADD(Normal, WindowText);
- ADD(Inactive, WindowText);
- LIGHTER(Normal, WindowText, 50);
- ADD(Disabled, Text);
- ADD(Disabled, WindowText);
- //ADD(Normal, ButtonText);
- ADD(Inactive, ButtonText);
- GTK(button, Text, NORMAL);
- ADD(Disabled, ButtonText);
- // special background colors
- GTK(Default, Background, SELECTED);
- ADD(Disabled, Highlight);
- ADD(Normal, Highlight);
- GTK(entry, Foreground, SELECTED);
- ADD(Normal, HighlightedText);
- GTK(entry, Background, ACTIVE);
- ADD(Inactive, HighlightedText);
- // text color and friends
- GTK(entry, Text, NORMAL);
- ADD(Normal, ButtonText);
- ADD(Normal, WindowText);
- ADD(Disabled, WindowText);
- ADD(Disabled, HighlightedText);
- GTK(Default, Text, NORMAL);
- ADD(Normal, Text);
- ADD(Inactive, Text);
- ADD(Normal, HighlightedText);
- LIGHTER(Normal, Base, 93);
- ADD(All, AlternateBase);
- GTK(Default, Foreground, NORMAL);
- ADD(All, ToolTipText);
- MODIFY(Normal, Text, 100, 100, 100);
- ADD(All, PlaceholderText, Light);
- MODIFY(Normal, Text, -100, -100, -100);
- ADD(All, PlaceholderText, Dark);
- SAVE(SystemPalette);
- CLEAR;
-
- // Checkbox and Radio Button
- GTK(button, Text, ACTIVE);
- ADD(Normal, Base, Dark);
- GTK(button, Text, NORMAL);
- ADD(Normal, Base, Light);
- SAVE(CheckBoxPalette);
- SAVE(RadioButtonPalette);
- CLEAR;
-
- // ComboBox, GroupBox, Frame
- GTK(combo_box, Text, NORMAL);
- ADD(Normal, ButtonText, Dark);
- ADD(Normal, Text, Dark);
- GTK(combo_box, Text, ACTIVE);
- ADD(Normal, ButtonText, Light);
- ADD(Normal, Text, Light);
- SAVE(ComboBoxPalette);
- SAVE(GroupBoxPalette);
- CLEAR;
-
- // Menu bar
- GTK(Default, Text, ACTIVE);
- ADD(Normal, ButtonText);
- SAVE(MenuPalette);
- CLEAR;
-
- // LineEdit
- GTK(Default, Background, NORMAL);
- ADD(All, Base);
- SAVE(TextLineEditPalette);
- CLEAR;
+ {
+ // background color and calculate derivates
+ GTK(Default, Background, INSENSITIVE);
+ ADD(All, Window);
+ ADD(All, Button);
+ ADD(All, Base);
+ LIGHTER(Normal, Window, 125);
+ ADD(Normal, Light);
+ ADD(Inactive, Light);
+ LIGHTER(Normal, Window, 70);
+ ADD(Normal, Shadow);
+ LIGHTER(Normal, Window, 80);
+ ADD(Normal, Dark);
+ ADD(Inactive, Dark)
+
+ GTK(button, Foreground, ACTIVE);
+ ADD(Inactive, WindowText);
+ LIGHTER(Normal, WindowText, 50);
+ ADD(Disabled, Text);
+ ADD(Disabled, WindowText);
+ ADD(Disabled, ButtonText);
+
+ GTK(button, Text, NORMAL);
+ ADD(Inactive, ButtonText);
+
+ // special background colors
+ GTK(Default, Background, SELECTED);
+ ADD(Disabled, Highlight);
+ ADD(Normal, Highlight);
+ ADD(Inactive, Highlight);
+
+ GTK(entry, Foreground, SELECTED);
+ ADD(Normal, HighlightedText);
+ ADD(Inactive, HighlightedText);
+
+ // text color and friends
+ GTK(entry, Text, NORMAL);
+ ADD(Normal, ButtonText);
+ ADD(Normal, WindowText);
+ ADD(Disabled, HighlightedText);
+
+ GTK(Default, Text, NORMAL);
+ ADD(Normal, Text);
+ ADD(Inactive, Text);
+ ADD(Normal, HighlightedText);
+ LIGHTER(Normal, Base, 93);
+ ADD(All, AlternateBase);
+
+ GTK(Default, Foreground, NORMAL);
+ MODIFY(Normal, Text, 100, 100, 100);
+ ADD(All, PlaceholderText, Light);
+ MODIFY(Normal, Text, -100, -100, -100);
+ ADD(All, PlaceholderText, Dark);
+
+ // Light, midlight, dark, mid, shadow colors
+ LIGHTER(Normal, Button, 125);
+ ADD(All, Light)
+ LIGHTER(Normal, Button, 113);
+ ADD(All, Midlight)
+ LIGHTER(Normal, Button, 113);
+ ADD(All, Mid)
+ LIGHTER(Normal, Button, 87);
+ ADD(All, Dark)
+ LIGHTER(Normal, Button, 5);
+ ADD(All, Shadow)
+
+ SAVE(SystemPalette);
+ CLEAR;
+ }
+
+ // Label and TabBar Palette
+ {
+ GTK(entry, Text, NORMAL);
+ ADD(Normal, WindowText);
+ ADD(Inactive, WindowText);
+
+ SAVE(LabelPalette);
+ SAVE(TabBarPalette);
+ CLEAR;
+ }
+
+ // Checkbox and RadioButton Palette
+ {
+ GTK(button, Text, ACTIVE);
+ ADD(Normal, Base, Dark);
+ ADD(Inactive, WindowText, Dark);
+
+ GTK(Default, Foreground, NORMAL);
+ ADD(All, Text);
+
+ GTK(Default, Background, NORMAL);
+ ADD(All, Base);
+
+ GTK(button, Text, NORMAL);
+ ADD(Normal, Base, Light);
+ ADD(Inactive, WindowText, Light);
+
+ SAVE(CheckBoxPalette);
+ SAVE(RadioButtonPalette);
+ CLEAR;
+ }
+
+ // ComboBox, GroupBox & Frame Palette
+ {
+ GTK(combo_box, Text, NORMAL);
+ ADD(Normal, ButtonText, Dark);
+ ADD(Normal, Text, Dark);
+ ADD(Inactive, WindowText, Dark);
+
+ GTK(combo_box, Text, ACTIVE);
+ ADD(Normal, ButtonText, Light);
+ ADD(Normal, Text, Light);
+ ADD(Inactive, WindowText, Light);
+
+ SAVE(ComboBoxPalette);
+ SAVE(GroupBoxPalette);
+ CLEAR;
+ }
+
+ // MenuBar Palette
+ {
+ GTK(Default, Text, ACTIVE);
+ ADD(Normal, ButtonText);
+ SAVE(MenuPalette);
+ CLEAR;
+ }
+
+ // LineEdit Palette
+ {
+ GTK(Default, Background, NORMAL);
+ ADD(All, Base);
+ SAVE(TextLineEditPalette);
+ CLEAR;
+ }
#undef GTK
#undef REC
diff --git a/src/plugins/platformthemes/gtk3/qgtk3storage_p.h b/src/plugins/platformthemes/gtk3/qgtk3storage_p.h
index 57f6aeea96..45192263a9 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3storage_p.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3storage_p.h
@@ -16,6 +16,9 @@
//
#include "qgtk3interface_p.h"
+#if QT_CONFIG(dbus)
+#include "qgtk3portalinterface_p.h"
+#endif
#include <QtCore/QJsonDocument>
#include <QtCore/QCache>
@@ -33,6 +36,7 @@ class QGtk3Storage
public:
QGtk3Storage();
+ // Enum documented in cpp file. Please keep it in line with updates made here.
enum class SourceType {
Gtk,
Fixed,
@@ -60,7 +64,7 @@ public:
struct RecursiveSource {
QPalette::ColorGroup colorGroup;
QPalette::ColorRole colorRole;
- Qt::Appearance appearance;
+ Qt::ColorScheme colorScheme;
int lighter = 100;
int deltaRed = 0;
int deltaGreen = 0;
@@ -70,7 +74,7 @@ public:
QDebug operator<<(QDebug dbg)
{
return dbg << "QGtkStorage::RecursiceSource(colorGroup=" << colorGroup << ", colorRole="
- << colorRole << ", appearance=" << appearance << ", lighter=" << lighter
+ << colorRole << ", colorScheme=" << colorScheme << ", lighter=" << lighter
<< ", deltaRed="<< deltaRed << "deltaBlue =" << deltaBlue << "deltaGreen="
<< deltaGreen << ", width=" << width << ", height=" << height << ")";
}
@@ -105,23 +109,23 @@ public:
// Recursive constructor for darker/lighter colors
Source(QPalette::ColorGroup group, QPalette::ColorRole role,
- Qt::Appearance app, int p_lighter = 100)
+ Qt::ColorScheme scheme, int p_lighter = 100)
: sourceType(SourceType::Modified)
{
rec.colorGroup = group;
rec.colorRole = role;
- rec.appearance = app;
+ rec.colorScheme = scheme;
rec.lighter = p_lighter;
}
// Recursive ocnstructor for color modification
Source(QPalette::ColorGroup group, QPalette::ColorRole role,
- Qt::Appearance app, int p_red, int p_green, int p_blue)
+ Qt::ColorScheme scheme, int p_red, int p_green, int p_blue)
: sourceType(SourceType::Modified)
{
rec.colorGroup = group;
rec.colorRole = role;
- rec.appearance = app;
+ rec.colorScheme = scheme;
rec.deltaRed = p_red;
rec.deltaGreen = p_green;
rec.deltaBlue = p_blue;
@@ -129,12 +133,12 @@ public:
// Recursive constructor for all: color modification and darker/lighter
Source(QPalette::ColorGroup group, QPalette::ColorRole role,
- Qt::Appearance app, int p_lighter,
+ Qt::ColorScheme scheme, int p_lighter,
int p_red, int p_green, int p_blue) : sourceType(SourceType::Modified)
{
rec.colorGroup = group;
rec.colorRole = role;
- rec.appearance = app;
+ rec.colorScheme = scheme;
rec.lighter = p_lighter;
rec.deltaRed = p_red;
rec.deltaGreen = p_green;
@@ -158,25 +162,25 @@ public:
}
};
- // Struct with key attributes to identify a brush: color group, color role and appearance
+ // Struct with key attributes to identify a brush: color group, color role and color scheme
struct TargetBrush {
QPalette::ColorGroup colorGroup;
QPalette::ColorRole colorRole;
- Qt::Appearance appearance;
+ Qt::ColorScheme colorScheme;
// Generic constructor
TargetBrush(QPalette::ColorGroup group, QPalette::ColorRole role,
- Qt::Appearance app = Qt::Appearance::Unknown) :
- colorGroup(group), colorRole(role), appearance(app) {};
+ Qt::ColorScheme scheme = Qt::ColorScheme::Unknown) :
+ colorGroup(group), colorRole(role), colorScheme(scheme) {};
- // Copy constructor with appearance modifier for dark/light aware search
- TargetBrush(const TargetBrush &other, Qt::Appearance app) :
- colorGroup(other.colorGroup), colorRole(other.colorRole), appearance(app) {};
+ // Copy constructor with color scheme modifier for dark/light aware search
+ TargetBrush(const TargetBrush &other, Qt::ColorScheme scheme) :
+ colorGroup(other.colorGroup), colorRole(other.colorRole), colorScheme(scheme) {};
// struct becomes key of a map, so operator< is needed
bool operator<(const TargetBrush& other) const {
- return std::tie(colorGroup, colorRole, appearance) <
- std::tie(other.colorGroup, other.colorRole, other.appearance);
+ return std::tie(colorGroup, colorRole, colorScheme) <
+ std::tie(other.colorGroup, other.colorRole, other.colorScheme);
}
};
@@ -189,7 +193,7 @@ public:
// Public getters
const QPalette *palette(QPlatformTheme::Palette = QPlatformTheme::SystemPalette) const;
QPixmap standardPixmap(QPlatformTheme::StandardPixmap standardPixmap, const QSizeF &size) const;
- Qt::Appearance appearance() const { return m_appearance; };
+ Qt::ColorScheme colorScheme() const { return m_colorScheme; };
static QPalette standardPalette();
const QString themeName() const { return m_interface ? m_interface->themeName() : QString(); };
const QFont *font(QPlatformTheme::Font type) const;
@@ -204,9 +208,11 @@ private:
PaletteMap m_palettes;
std::unique_ptr<QGtk3Interface> m_interface;
+#if QT_CONFIG(dbus)
+ std::unique_ptr<QGtk3PortalInterface> m_portalInterface;
+#endif
-
- Qt::Appearance m_appearance = Qt::Appearance::Unknown;
+ Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
// Caches for Pixmaps, fonts and palettes
mutable QCache<QPlatformTheme::StandardPixmap, QImage> m_pixmapCache;
@@ -219,7 +225,7 @@ private:
// Get GTK3 source for a target brush
Source brush (const TargetBrush &brush, const BrushMap &map) const;
- // clear cache, palettes and appearance
+ // clear cache, palettes and color scheme
void clear();
// Data creation, import & export
diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
index c846905786..9d23ba7e48 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
@@ -5,7 +5,6 @@
#include "qgtk3dialoghelpers.h"
#include "qgtk3menu.h"
#include <QVariant>
-#include <QtCore/qregularexpression.h>
#include <QGuiApplication>
#include <qpa/qwindowsysteminterface.h>
@@ -105,20 +104,6 @@ QGtk3Theme::QGtk3Theme()
SETTING_CONNECT("gtk-cursor-theme-size");
#undef SETTING_CONNECT
- /* Set XCURSOR_SIZE and XCURSOR_THEME for Wayland sessions */
- if (QGuiApplication::platformName().startsWith("wayland"_L1)) {
- if (qEnvironmentVariableIsEmpty("XCURSOR_SIZE")) {
- const int cursorSize = gtkSetting<gint>("gtk-cursor-theme-size");
- if (cursorSize > 0)
- qputenv("XCURSOR_SIZE", QString::number(cursorSize).toUtf8());
- }
- if (qEnvironmentVariableIsEmpty("XCURSOR_THEME")) {
- const QString cursorTheme = gtkSetting("gtk-cursor-theme-name");
- if (!cursorTheme.isEmpty())
- qputenv("XCURSOR_THEME", cursorTheme.toUtf8());
- }
- }
-
m_storage.reset(new QGtk3Storage);
}
@@ -175,48 +160,10 @@ QString QGtk3Theme::gtkFontName() const
return QGnomeTheme::gtkFontName();
}
-Qt::Appearance QGtk3Theme::appearance() const
+Qt::ColorScheme QGtk3Theme::colorScheme() const
{
- if (m_storage)
- return m_storage->appearance();
- /*
- https://docs.gtk.org/gtk3/running.html
-
- It's possible to set a theme variant after the theme name when using GTK_THEME:
-
- GTK_THEME=Adwaita:dark
-
- Some themes also have "-dark" as part of their name.
-
- We test this environment variable first because the documentation says
- it's mainly used for easy debugging, so it should be possible to use it
- to override any other settings.
- */
- QString themeName = qEnvironmentVariable("GTK_THEME");
- if (!themeName.isEmpty())
- return themeName.contains("dark"_L1, Qt::CaseInsensitive)
- ? Qt::Appearance::Dark : Qt::Appearance::Light;
-
- /*
- https://docs.gtk.org/gtk3/property.Settings.gtk-application-prefer-dark-theme.html
-
- This setting controls which theme is used when the theme specified by
- gtk-theme-name provides both light and dark variants. We can save a
- regex check by testing this property first.
- */
- const auto preferDark = gtkSetting<gboolean>("gtk-application-prefer-dark-theme");
- if (preferDark)
- return Qt::Appearance::Dark;
-
- /*
- https://docs.gtk.org/gtk3/property.Settings.gtk-theme-name.html
- */
- themeName = gtkSetting("gtk-theme-name");
- if (!themeName.isEmpty())
- return themeName.contains("dark"_L1, Qt::CaseInsensitive)
- ? Qt::Appearance::Dark : Qt::Appearance::Light;
-
- return Qt::Appearance::Unknown;
+ Q_ASSERT(m_storage);
+ return m_storage->colorScheme();
}
bool QGtk3Theme::usePlatformNativeDialog(DialogType type) const
@@ -274,23 +221,28 @@ bool QGtk3Theme::useNativeFileDialog()
const QPalette *QGtk3Theme::palette(Palette type) const
{
- return m_storage ? m_storage->palette(type) : QPlatformTheme::palette(type);
+ Q_ASSERT(m_storage);
+ return m_storage->palette(type);
}
QPixmap QGtk3Theme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
{
- return m_storage ? m_storage->standardPixmap(sp, size) : QPlatformTheme::standardPixmap(sp, size);
+ Q_ASSERT(m_storage);
+ return m_storage->standardPixmap(sp, size);
}
const QFont *QGtk3Theme::font(Font type) const
{
- return m_storage ? m_storage->font(type) : QGnomeTheme::font(type);
+ Q_ASSERT(m_storage);
+ return m_storage->font(type);
}
QIcon QGtk3Theme::fileIcon(const QFileInfo &fileInfo,
QPlatformTheme::IconOptions iconOptions) const
{
- return m_storage ? m_storage->fileIcon(fileInfo) : QGnomeTheme::fileIcon(fileInfo, iconOptions);
+ Q_UNUSED(iconOptions);
+ Q_ASSERT(m_storage);
+ return m_storage->fileIcon(fileInfo);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.h b/src/plugins/platformthemes/gtk3/qgtk3theme.h
index 0071df9cea..2828cc56e6 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3theme.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3theme.h
@@ -18,7 +18,7 @@ public:
virtual QVariant themeHint(ThemeHint hint) const override;
virtual QString gtkFontName() const override;
- Qt::Appearance appearance() const override;
+ Qt::ColorScheme colorScheme() const override;
bool usePlatformNativeDialog(DialogType type) const override;
QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
diff --git a/src/plugins/platformthemes/xdgdesktopportal/CMakeLists.txt b/src/plugins/platformthemes/xdgdesktopportal/CMakeLists.txt
index f3c76ff7d1..6228e83ec7 100644
--- a/src/plugins/platformthemes/xdgdesktopportal/CMakeLists.txt
+++ b/src/plugins/platformthemes/xdgdesktopportal/CMakeLists.txt
@@ -1,8 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from xdgdesktopportal.pro.
-
#####################################################################
## QXdgDesktopPortalThemePlugin Plugin:
#####################################################################
@@ -22,6 +20,3 @@ qt_internal_add_plugin(QXdgDesktopPortalThemePlugin
Qt::Gui
Qt::GuiPrivate
)
-
-#### Keys ignored in scope 1:.:.:xdgdesktopportal.pro:<TRUE>:
-# PLUGIN_EXTENDS = "-"
diff --git a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp
index ec3872f174..1c162be8fc 100644
--- a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp
+++ b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp
@@ -169,19 +169,18 @@ void QXdgDesktopPortalFileDialog::openPortal(Qt::WindowFlags windowFlags, Qt::Wi
options.insert("multiple"_L1, d->multipleFiles);
options.insert("directory"_L1, d->directoryMode);
- if (d->saveFile) {
- if (!d->directory.isEmpty())
- options.insert("current_folder"_L1, QFile::encodeName(d->directory).append('\0'));
-
- if (!d->selectedFiles.isEmpty()) {
- // current_file for the file to be pre-selected, current_name for the file name to be pre-filled
- // current_file accepts absolute path and requires the file to exist
- // while current_name accepts just file name
- QFileInfo selectedFileInfo(d->selectedFiles.first());
- if (selectedFileInfo.exists())
- options.insert("current_file"_L1, QFile::encodeName(d->selectedFiles.first()).append('\0'));
- options.insert("current_name"_L1, selectedFileInfo.fileName());
- }
+ if (!d->directory.isEmpty())
+ options.insert("current_folder"_L1, QFile::encodeName(d->directory).append('\0'));
+
+ if (d->saveFile && !d->selectedFiles.isEmpty()) {
+ // current_file for the file to be pre-selected, current_name for the file name to be
+ // pre-filled current_file accepts absolute path and requires the file to exist while
+ // current_name accepts just file name
+ QFileInfo selectedFileInfo(d->selectedFiles.constFirst());
+ if (selectedFileInfo.exists())
+ options.insert("current_file"_L1,
+ QFile::encodeName(d->selectedFiles.constFirst()).append('\0'));
+ options.insert("current_name"_L1, selectedFileInfo.fileName());
}
// Insert filters
@@ -214,6 +213,9 @@ void QXdgDesktopPortalFileDialog::openPortal(Qt::WindowFlags windowFlags, Qt::Wi
filter.name = mimeType.comment();
filter.filterConditions = filterConditions;
+ if (filter.name.isEmpty())
+ filter.name = mimeTypefilter;
+
filterList << filter;
if (!d->selectedMimeTypeFilter.isEmpty() && d->selectedMimeTypeFilter == mimeTypefilter)
diff --git a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp
index c7b7ec550f..355d3e6cc9 100644
--- a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp
+++ b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp
@@ -20,8 +20,9 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-class QXdgDesktopPortalThemePrivate : public QPlatformThemePrivate
-{
+class QXdgDesktopPortalThemePrivate : public QObject
+ {
+ Q_OBJECT
public:
enum XdgColorschemePref {
None,
@@ -30,7 +31,7 @@ public:
};
QXdgDesktopPortalThemePrivate()
- : QPlatformThemePrivate()
+ : QObject()
{ }
~QXdgDesktopPortalThemePrivate()
@@ -40,7 +41,7 @@ public:
/*! \internal
- Converts the given Freedesktop color scheme setting \a colorschemePref to a Qt::Appearance value.
+ Converts the given Freedesktop color scheme setting \a colorschemePref to a Qt::ColorScheme value.
Specification: https://github.com/flatpak/xdg-desktop-portal/blob/d7a304a00697d7d608821253cd013f3b97ac0fb6/data/org.freedesktop.impl.portal.Settings.xml#L33-L45
Unfortunately the enum numerical values are not defined identically, so we have to convert them.
@@ -53,18 +54,29 @@ public:
1: Prefer dark appearance | 2: Dark
2: Prefer light appearance | 1: Light
*/
- static Qt::Appearance appearanceFromXdgPref(const XdgColorschemePref colorschemePref)
+ static Qt::ColorScheme colorSchemeFromXdgPref(const XdgColorschemePref colorschemePref)
{
switch (colorschemePref) {
- case PreferDark: return Qt::Appearance::Dark;
- case PreferLight: return Qt::Appearance::Light;
- default: return Qt::Appearance::Unknown;
+ case PreferDark: return Qt::ColorScheme::Dark;
+ case PreferLight: return Qt::ColorScheme::Light;
+ default: return Qt::ColorScheme::Unknown;
}
}
+public Q_SLOTS:
+ void settingChanged(const QString &group, const QString &key,
+ const QDBusVariant &value)
+ {
+ if (group == "org.freedesktop.appearance"_L1 && key == "color-scheme"_L1) {
+ colorScheme = colorSchemeFromXdgPref(static_cast<XdgColorschemePref>(value.variant().toUInt()));
+ QWindowSystemInterface::handleThemeChange();
+ }
+ }
+
+public:
QPlatformTheme *baseTheme = nullptr;
uint fileChooserPortalVersion = 0;
- Qt::Appearance appearance = Qt::Appearance::Unknown;
+ Qt::ColorScheme colorScheme = Qt::ColorScheme::Unknown;
};
QXdgDesktopPortalTheme::QXdgDesktopPortalTheme()
@@ -104,7 +116,7 @@ QXdgDesktopPortalTheme::QXdgDesktopPortalTheme()
message << "org.freedesktop.portal.FileChooser"_L1 << "version"_L1;
QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
- QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [d] (QDBusPendingCallWatcher *watcher) {
+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [d] (QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<QVariant> reply = *watcher;
if (reply.isValid()) {
d->fileChooserPortalVersion = reply.value().toUInt();
@@ -124,8 +136,13 @@ QXdgDesktopPortalTheme::QXdgDesktopPortalTheme()
if (reply.isValid()) {
const QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(reply.value());
const QXdgDesktopPortalThemePrivate::XdgColorschemePref xdgPref = static_cast<QXdgDesktopPortalThemePrivate::XdgColorschemePref>(dbusVariant.variant().toUInt());
- d->appearance = QXdgDesktopPortalThemePrivate::appearanceFromXdgPref(xdgPref);
+ d->colorScheme = QXdgDesktopPortalThemePrivate::colorSchemeFromXdgPref(xdgPref);
}
+
+ QDBusConnection::sessionBus().connect(
+ "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1,
+ "org.freedesktop.portal.Settings"_L1, "SettingChanged"_L1, d_ptr.get(),
+ SLOT(settingChanged(QString, QString, QDBusVariant)));
}
QPlatformMenuItem* QXdgDesktopPortalTheme::createPlatformMenuItem() const
@@ -205,10 +222,12 @@ QVariant QXdgDesktopPortalTheme::themeHint(ThemeHint hint) const
return d->baseTheme->themeHint(hint);
}
-Qt::Appearance QXdgDesktopPortalTheme::appearance() const
+Qt::ColorScheme QXdgDesktopPortalTheme::colorScheme() const
{
Q_D(const QXdgDesktopPortalTheme);
- return d->appearance;
+ if (d->colorScheme == Qt::ColorScheme::Unknown)
+ return d->baseTheme->colorScheme();
+ return d->colorScheme;
}
QPixmap QXdgDesktopPortalTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
@@ -245,3 +264,5 @@ QString QXdgDesktopPortalTheme::standardButtonText(int button) const
}
QT_END_NAMESPACE
+
+#include "qxdgdesktopportaltheme.moc"
diff --git a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h
index 8e1cd37932..1ac04c45e6 100644
--- a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h
+++ b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h
@@ -34,7 +34,7 @@ public:
QVariant themeHint(ThemeHint hint) const override;
- Qt::Appearance appearance() const override;
+ Qt::ColorScheme colorScheme() const override;
QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;
QIcon fileIcon(const QFileInfo &fileInfo,