You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3293 lines
111 KiB
3293 lines
111 KiB
From ffab9df46c5f2cb411765f72ecd6f5dcdb62fbb4 Mon Sep 17 00:00:00 2001
|
|
From: Jan Grulich <jgrulich@redhat.com>
|
|
Date: Thu, 27 Jul 2023 11:54:44 +0200
|
|
Subject: [PATCH 06/15] Re-implement palette, standardPixmap, file icons, fonts
|
|
in QGtk3Theme
|
|
|
|
Read theme colors from GTK3 style context and build platform theme
|
|
palettes in Qt.
|
|
React to runtime theme changes.
|
|
Re-implement methods to retrieve GTK3 styled standardPixmaps, fonts
|
|
and file icons.
|
|
---
|
|
.../5.15.15/QtCore/private/qflatmap_p.h | 2 +
|
|
src/corelib/tools/qflatmap_p.h | 1107 +++++++++++++++++
|
|
src/plugins/platformthemes/gtk3/gtk3.pro | 6 +
|
|
.../platformthemes/gtk3/qgtk3interface.cpp | 558 +++++++++
|
|
.../platformthemes/gtk3/qgtk3interface_p.h | 170 +++
|
|
src/plugins/platformthemes/gtk3/qgtk3json.cpp | 475 +++++++
|
|
src/plugins/platformthemes/gtk3/qgtk3json_p.h | 102 ++
|
|
.../platformthemes/gtk3/qgtk3storage.cpp | 470 +++++++
|
|
.../platformthemes/gtk3/qgtk3storage_p.h | 234 ++++
|
|
.../platformthemes/gtk3/qgtk3theme.cpp | 23 +
|
|
src/plugins/platformthemes/gtk3/qgtk3theme.h | 8 +
|
|
11 files changed, 3155 insertions(+)
|
|
create mode 100644 include/QtCore/5.15.15/QtCore/private/qflatmap_p.h
|
|
create mode 100644 src/corelib/tools/qflatmap_p.h
|
|
create mode 100644 src/plugins/platformthemes/gtk3/qgtk3interface.cpp
|
|
create mode 100644 src/plugins/platformthemes/gtk3/qgtk3interface_p.h
|
|
create mode 100644 src/plugins/platformthemes/gtk3/qgtk3json.cpp
|
|
create mode 100644 src/plugins/platformthemes/gtk3/qgtk3json_p.h
|
|
create mode 100644 src/plugins/platformthemes/gtk3/qgtk3storage.cpp
|
|
create mode 100644 src/plugins/platformthemes/gtk3/qgtk3storage_p.h
|
|
|
|
diff --git a/include/QtCore/5.15.15/QtCore/private/qflatmap_p.h b/include/QtCore/5.15.15/QtCore/private/qflatmap_p.h
|
|
new file mode 100644
|
|
index 0000000000..e629799f72
|
|
--- /dev/null
|
|
+++ b/include/QtCore/5.15.15/QtCore/private/qflatmap_p.h
|
|
@@ -0,0 +1,2 @@
|
|
+#include "../../../../../src/corelib/tools/qflatmap_p.h"
|
|
+
|
|
diff --git a/src/corelib/tools/qflatmap_p.h b/src/corelib/tools/qflatmap_p.h
|
|
new file mode 100644
|
|
index 0000000000..45153e23db
|
|
--- /dev/null
|
|
+++ b/src/corelib/tools/qflatmap_p.h
|
|
@@ -0,0 +1,1107 @@
|
|
+// Copyright (C) 2022 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 QFLATMAP_P_H
|
|
+#define QFLATMAP_P_H
|
|
+
|
|
+//
|
|
+// W A R N I N G
|
|
+// -------------
|
|
+//
|
|
+// This file is not part of the Qt API. It exists for the convenience
|
|
+// of a number of Qt sources files. This header file may change from
|
|
+// version to version without notice, or even be removed.
|
|
+//
|
|
+// We mean it.
|
|
+//
|
|
+
|
|
+#include "qlist.h"
|
|
+#include "private/qglobal_p.h"
|
|
+
|
|
+#include <algorithm>
|
|
+#include <functional>
|
|
+#include <initializer_list>
|
|
+#include <iterator>
|
|
+#include <numeric>
|
|
+#include <type_traits>
|
|
+#include <utility>
|
|
+#include <vector>
|
|
+#include <optional>
|
|
+
|
|
+QT_BEGIN_NAMESPACE
|
|
+
|
|
+/*
|
|
+ QFlatMap provides an associative container backed by sorted sequential
|
|
+ containers. By default, QList is used.
|
|
+
|
|
+ Keys and values are stored in two separate containers. This provides improved
|
|
+ cache locality for key iteration and makes keys() and values() fast
|
|
+ operations.
|
|
+
|
|
+ One can customize the underlying container type by passing the KeyContainer
|
|
+ and MappedContainer template arguments:
|
|
+ QFlatMap<float, int, std::less<float>, std::vector<float>, std::vector<int>>
|
|
+*/
|
|
+
|
|
+// Qt 6.4:
|
|
+// - removed QFlatMap API which was incompatible with STL semantics
|
|
+// - will be released with said API disabled, to catch any out-of-tree users
|
|
+// - also allows opting in to the new API using QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT
|
|
+// Qt 6.5
|
|
+// - will make QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT the default:
|
|
+
|
|
+#ifndef QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT
|
|
+# define QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT
|
|
+#endif
|
|
+
|
|
+namespace Qt {
|
|
+
|
|
+struct OrderedUniqueRange_t {};
|
|
+constexpr OrderedUniqueRange_t OrderedUniqueRange = {};
|
|
+
|
|
+} // namespace Qt
|
|
+
|
|
+template <class Key, class T, class Compare>
|
|
+class QFlatMapValueCompare : protected Compare
|
|
+{
|
|
+public:
|
|
+ QFlatMapValueCompare() = default;
|
|
+ QFlatMapValueCompare(const Compare &key_compare)
|
|
+ : Compare(key_compare)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ using value_type = std::pair<const Key, T>;
|
|
+ static constexpr bool is_comparator_noexcept = noexcept(
|
|
+ std::declval<Compare>()(std::declval<const Key &>(), std::declval<const Key &>()));
|
|
+
|
|
+ bool operator()(const value_type &lhs, const value_type &rhs) const
|
|
+ noexcept(is_comparator_noexcept)
|
|
+ {
|
|
+ return Compare::operator()(lhs.first, rhs.first);
|
|
+ }
|
|
+};
|
|
+
|
|
+namespace qflatmap {
|
|
+namespace detail {
|
|
+template <class T>
|
|
+class QFlatMapMockPointer
|
|
+{
|
|
+ T ref;
|
|
+public:
|
|
+ QFlatMapMockPointer(T r)
|
|
+ : ref(r)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ T *operator->()
|
|
+ {
|
|
+ return &ref;
|
|
+ }
|
|
+};
|
|
+} // namespace detail
|
|
+} // namespace qflatmap
|
|
+
|
|
+template<class Key, class T, class Compare = std::less<Key>, class KeyContainer = QList<Key>,
|
|
+ class MappedContainer = QList<T>>
|
|
+class QFlatMap : private QFlatMapValueCompare<Key, T, Compare>
|
|
+{
|
|
+ static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
|
|
+
|
|
+ template<class U>
|
|
+ using mock_pointer = qflatmap::detail::QFlatMapMockPointer<U>;
|
|
+
|
|
+public:
|
|
+ using key_type = Key;
|
|
+ using mapped_type = T;
|
|
+ using value_compare = QFlatMapValueCompare<Key, T, Compare>;
|
|
+ using value_type = typename value_compare::value_type;
|
|
+ using key_container_type = KeyContainer;
|
|
+ using mapped_container_type = MappedContainer;
|
|
+ using size_type = typename key_container_type::size_type;
|
|
+ using key_compare = Compare;
|
|
+
|
|
+ struct containers
|
|
+ {
|
|
+ key_container_type keys;
|
|
+ mapped_container_type values;
|
|
+ };
|
|
+
|
|
+ class iterator
|
|
+ {
|
|
+ public:
|
|
+ using difference_type = ptrdiff_t;
|
|
+ using value_type = std::pair<const Key, T>;
|
|
+ using reference = std::pair<const Key &, T &>;
|
|
+ using pointer = mock_pointer<reference>;
|
|
+ using iterator_category = std::random_access_iterator_tag;
|
|
+
|
|
+ iterator() = default;
|
|
+
|
|
+ iterator(containers *ac, size_type ai)
|
|
+ : c(ac), i(ai)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ reference operator*() const
|
|
+ {
|
|
+ return { c->keys[i], c->values[i] };
|
|
+ }
|
|
+
|
|
+ pointer operator->() const
|
|
+ {
|
|
+ return { operator*() };
|
|
+ }
|
|
+
|
|
+ bool operator==(const iterator &o) const
|
|
+ {
|
|
+ return c == o.c && i == o.i;
|
|
+ }
|
|
+
|
|
+ bool operator!=(const iterator &o) const
|
|
+ {
|
|
+ return !operator==(o);
|
|
+ }
|
|
+
|
|
+ iterator &operator++()
|
|
+ {
|
|
+ ++i;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ iterator operator++(int)
|
|
+ {
|
|
+
|
|
+ iterator r = *this;
|
|
+ ++*this;
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ iterator &operator--()
|
|
+ {
|
|
+ --i;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ iterator operator--(int)
|
|
+ {
|
|
+ iterator r = *this;
|
|
+ --*this;
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ iterator &operator+=(size_type n)
|
|
+ {
|
|
+ i += n;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ friend iterator operator+(size_type n, const iterator a)
|
|
+ {
|
|
+ iterator ret = a;
|
|
+ return ret += n;
|
|
+ }
|
|
+
|
|
+ friend iterator operator+(const iterator a, size_type n)
|
|
+ {
|
|
+ return n + a;
|
|
+ }
|
|
+
|
|
+ iterator &operator-=(size_type n)
|
|
+ {
|
|
+ i -= n;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ friend iterator operator-(const iterator a, size_type n)
|
|
+ {
|
|
+ iterator ret = a;
|
|
+ return ret -= n;
|
|
+ }
|
|
+
|
|
+ friend difference_type operator-(const iterator b, const iterator a)
|
|
+ {
|
|
+ return b.i - a.i;
|
|
+ }
|
|
+
|
|
+ reference operator[](size_type n) const
|
|
+ {
|
|
+ size_type k = i + n;
|
|
+ return { c->keys[k], c->values[k] };
|
|
+ }
|
|
+
|
|
+ bool operator<(const iterator &other) const
|
|
+ {
|
|
+ return i < other.i;
|
|
+ }
|
|
+
|
|
+ bool operator>(const iterator &other) const
|
|
+ {
|
|
+ return i > other.i;
|
|
+ }
|
|
+
|
|
+ bool operator<=(const iterator &other) const
|
|
+ {
|
|
+ return i <= other.i;
|
|
+ }
|
|
+
|
|
+ bool operator>=(const iterator &other) const
|
|
+ {
|
|
+ return i >= other.i;
|
|
+ }
|
|
+
|
|
+ const Key &key() const { return c->keys[i]; }
|
|
+ T &value() const { return c->values[i]; }
|
|
+
|
|
+ private:
|
|
+ containers *c = nullptr;
|
|
+ size_type i = 0;
|
|
+ friend QFlatMap;
|
|
+ };
|
|
+
|
|
+ class const_iterator
|
|
+ {
|
|
+ public:
|
|
+ using difference_type = ptrdiff_t;
|
|
+ using value_type = std::pair<const Key, const T>;
|
|
+ using reference = std::pair<const Key &, const T &>;
|
|
+ using pointer = mock_pointer<reference>;
|
|
+ using iterator_category = std::random_access_iterator_tag;
|
|
+
|
|
+ const_iterator() = default;
|
|
+
|
|
+ const_iterator(const containers *ac, size_type ai)
|
|
+ : c(ac), i(ai)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ const_iterator(iterator o)
|
|
+ : c(o.c), i(o.i)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ reference operator*() const
|
|
+ {
|
|
+ return { c->keys[i], c->values[i] };
|
|
+ }
|
|
+
|
|
+ pointer operator->() const
|
|
+ {
|
|
+ return { operator*() };
|
|
+ }
|
|
+
|
|
+ bool operator==(const const_iterator &o) const
|
|
+ {
|
|
+ return c == o.c && i == o.i;
|
|
+ }
|
|
+
|
|
+ bool operator!=(const const_iterator &o) const
|
|
+ {
|
|
+ return !operator==(o);
|
|
+ }
|
|
+
|
|
+ const_iterator &operator++()
|
|
+ {
|
|
+ ++i;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ const_iterator operator++(int)
|
|
+ {
|
|
+
|
|
+ const_iterator r = *this;
|
|
+ ++*this;
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ const_iterator &operator--()
|
|
+ {
|
|
+ --i;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ const_iterator operator--(int)
|
|
+ {
|
|
+ const_iterator r = *this;
|
|
+ --*this;
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ const_iterator &operator+=(size_type n)
|
|
+ {
|
|
+ i += n;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ friend const_iterator operator+(size_type n, const const_iterator a)
|
|
+ {
|
|
+ const_iterator ret = a;
|
|
+ return ret += n;
|
|
+ }
|
|
+
|
|
+ friend const_iterator operator+(const const_iterator a, size_type n)
|
|
+ {
|
|
+ return n + a;
|
|
+ }
|
|
+
|
|
+ const_iterator &operator-=(size_type n)
|
|
+ {
|
|
+ i -= n;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ friend const_iterator operator-(const const_iterator a, size_type n)
|
|
+ {
|
|
+ const_iterator ret = a;
|
|
+ return ret -= n;
|
|
+ }
|
|
+
|
|
+ friend difference_type operator-(const const_iterator b, const const_iterator a)
|
|
+ {
|
|
+ return b.i - a.i;
|
|
+ }
|
|
+
|
|
+ reference operator[](size_type n) const
|
|
+ {
|
|
+ size_type k = i + n;
|
|
+ return { c->keys[k], c->values[k] };
|
|
+ }
|
|
+
|
|
+ bool operator<(const const_iterator &other) const
|
|
+ {
|
|
+ return i < other.i;
|
|
+ }
|
|
+
|
|
+ bool operator>(const const_iterator &other) const
|
|
+ {
|
|
+ return i > other.i;
|
|
+ }
|
|
+
|
|
+ bool operator<=(const const_iterator &other) const
|
|
+ {
|
|
+ return i <= other.i;
|
|
+ }
|
|
+
|
|
+ bool operator>=(const const_iterator &other) const
|
|
+ {
|
|
+ return i >= other.i;
|
|
+ }
|
|
+
|
|
+ const Key &key() const { return c->keys[i]; }
|
|
+ const T &value() const { return c->values[i]; }
|
|
+
|
|
+ private:
|
|
+ const containers *c = nullptr;
|
|
+ size_type i = 0;
|
|
+ friend QFlatMap;
|
|
+ };
|
|
+
|
|
+private:
|
|
+ template <class, class = void>
|
|
+ struct is_marked_transparent_type : std::false_type { };
|
|
+
|
|
+ template <class X>
|
|
+ struct is_marked_transparent_type<X, std::void_t<typename X::is_transparent>> : std::true_type { };
|
|
+
|
|
+ template <class X>
|
|
+ using is_marked_transparent = typename std::enable_if<
|
|
+ is_marked_transparent_type<X>::value>::type *;
|
|
+
|
|
+ template <typename It>
|
|
+ using is_compatible_iterator = typename std::enable_if<
|
|
+ std::is_same<value_type, typename std::iterator_traits<It>::value_type>::value>::type *;
|
|
+
|
|
+public:
|
|
+ QFlatMap() = default;
|
|
+
|
|
+#ifdef QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT
|
|
+ explicit QFlatMap(const key_container_type &keys, const mapped_container_type &values)
|
|
+ : c{keys, values}
|
|
+ {
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(key_container_type &&keys, const mapped_container_type &values)
|
|
+ : c{std::move(keys), values}
|
|
+ {
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(const key_container_type &keys, mapped_container_type &&values)
|
|
+ : c{keys, std::move(values)}
|
|
+ {
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(key_container_type &&keys, mapped_container_type &&values)
|
|
+ : c{std::move(keys), std::move(values)}
|
|
+ {
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(std::initializer_list<value_type> lst)
|
|
+ : QFlatMap(lst.begin(), lst.end())
|
|
+ {
|
|
+ }
|
|
+
|
|
+ template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
|
|
+ explicit QFlatMap(InputIt first, InputIt last)
|
|
+ {
|
|
+ initWithRange(first, last);
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, const key_container_type &keys,
|
|
+ const mapped_container_type &values)
|
|
+ : c{keys, values}
|
|
+ {
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, key_container_type &&keys,
|
|
+ const mapped_container_type &values)
|
|
+ : c{std::move(keys), values}
|
|
+ {
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, const key_container_type &keys,
|
|
+ mapped_container_type &&values)
|
|
+ : c{keys, std::move(values)}
|
|
+ {
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, key_container_type &&keys,
|
|
+ mapped_container_type &&values)
|
|
+ : c{std::move(keys), std::move(values)}
|
|
+ {
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, std::initializer_list<value_type> lst)
|
|
+ : QFlatMap(Qt::OrderedUniqueRange, lst.begin(), lst.end())
|
|
+ {
|
|
+ }
|
|
+
|
|
+ template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, InputIt first, InputIt last)
|
|
+ {
|
|
+ initWithRange(first, last);
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(const Compare &compare)
|
|
+ : value_compare(compare)
|
|
+ {
|
|
+ }
|
|
+
|
|
+#ifdef QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT
|
|
+ explicit QFlatMap(const key_container_type &keys, const mapped_container_type &values,
|
|
+ const Compare &compare)
|
|
+ : value_compare(compare), c{keys, values}
|
|
+ {
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(key_container_type &&keys, const mapped_container_type &values,
|
|
+ const Compare &compare)
|
|
+ : value_compare(compare), c{std::move(keys), values}
|
|
+ {
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(const key_container_type &keys, mapped_container_type &&values,
|
|
+ const Compare &compare)
|
|
+ : value_compare(compare), c{keys, std::move(values)}
|
|
+ {
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(key_container_type &&keys, mapped_container_type &&values,
|
|
+ const Compare &compare)
|
|
+ : value_compare(compare), c{std::move(keys), std::move(values)}
|
|
+ {
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(std::initializer_list<value_type> lst, const Compare &compare)
|
|
+ : QFlatMap(lst.begin(), lst.end(), compare)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
|
|
+ explicit QFlatMap(InputIt first, InputIt last, const Compare &compare)
|
|
+ : value_compare(compare)
|
|
+ {
|
|
+ initWithRange(first, last);
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, const key_container_type &keys,
|
|
+ const mapped_container_type &values, const Compare &compare)
|
|
+ : value_compare(compare), c{keys, values}
|
|
+ {
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, key_container_type &&keys,
|
|
+ const mapped_container_type &values, const Compare &compare)
|
|
+ : value_compare(compare), c{std::move(keys), values}
|
|
+ {
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, const key_container_type &keys,
|
|
+ mapped_container_type &&values, const Compare &compare)
|
|
+ : value_compare(compare), c{keys, std::move(values)}
|
|
+ {
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, key_container_type &&keys,
|
|
+ mapped_container_type &&values, const Compare &compare)
|
|
+ : value_compare(compare), c{std::move(keys), std::move(values)}
|
|
+ {
|
|
+ }
|
|
+
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, std::initializer_list<value_type> lst,
|
|
+ const Compare &compare)
|
|
+ : QFlatMap(Qt::OrderedUniqueRange, lst.begin(), lst.end(), compare)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
|
|
+ explicit QFlatMap(Qt::OrderedUniqueRange_t, InputIt first, InputIt last, const Compare &compare)
|
|
+ : value_compare(compare)
|
|
+ {
|
|
+ initWithRange(first, last);
|
|
+ }
|
|
+
|
|
+ size_type count() const noexcept { return c.keys.size(); }
|
|
+ size_type size() const noexcept { return c.keys.size(); }
|
|
+ size_type capacity() const noexcept { return c.keys.capacity(); }
|
|
+ bool isEmpty() const noexcept { return c.keys.empty(); }
|
|
+ bool empty() const noexcept { return c.keys.empty(); }
|
|
+ containers extract() && { return std::move(c); }
|
|
+ const key_container_type &keys() const noexcept { return c.keys; }
|
|
+ const mapped_container_type &values() const noexcept { return c.values; }
|
|
+
|
|
+ void reserve(size_type s)
|
|
+ {
|
|
+ c.keys.reserve(s);
|
|
+ c.values.reserve(s);
|
|
+ }
|
|
+
|
|
+ void clear()
|
|
+ {
|
|
+ c.keys.clear();
|
|
+ c.values.clear();
|
|
+ }
|
|
+
|
|
+ bool remove(const Key &key)
|
|
+ {
|
|
+ return do_remove(find(key));
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ bool remove(const X &key)
|
|
+ {
|
|
+ return do_remove(find(key));
|
|
+ }
|
|
+
|
|
+ iterator erase(iterator it)
|
|
+ {
|
|
+ c.values.erase(toValuesIterator(it));
|
|
+ return fromKeysIterator(c.keys.erase(toKeysIterator(it)));
|
|
+ }
|
|
+
|
|
+ T take(const Key &key)
|
|
+ {
|
|
+ return do_take(find(key));
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ T take(const X &key)
|
|
+ {
|
|
+ return do_take(find(key));
|
|
+ }
|
|
+
|
|
+ bool contains(const Key &key) const
|
|
+ {
|
|
+ return find(key) != end();
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ bool contains(const X &key) const
|
|
+ {
|
|
+ return find(key) != end();
|
|
+ }
|
|
+
|
|
+ T value(const Key &key, const T &defaultValue) const
|
|
+ {
|
|
+ auto it = find(key);
|
|
+ return it == end() ? defaultValue : it.value();
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ T value(const X &key, const T &defaultValue) const
|
|
+ {
|
|
+ auto it = find(key);
|
|
+ return it == end() ? defaultValue : it.value();
|
|
+ }
|
|
+
|
|
+ T value(const Key &key) const
|
|
+ {
|
|
+ auto it = find(key);
|
|
+ return it == end() ? T() : it.value();
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ T value(const X &key) const
|
|
+ {
|
|
+ auto it = find(key);
|
|
+ return it == end() ? T() : it.value();
|
|
+ }
|
|
+
|
|
+ T &operator[](const Key &key)
|
|
+ {
|
|
+ return try_emplace(key).first.value();
|
|
+ }
|
|
+
|
|
+ T &operator[](Key &&key)
|
|
+ {
|
|
+ return try_emplace(std::move(key)).first.value();
|
|
+ }
|
|
+
|
|
+ T operator[](const Key &key) const
|
|
+ {
|
|
+ return value(key);
|
|
+ }
|
|
+
|
|
+#ifdef QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT
|
|
+ std::pair<iterator, bool> insert(const Key &key, const T &value)
|
|
+ {
|
|
+ return try_emplace(key, value);
|
|
+ }
|
|
+
|
|
+ std::pair<iterator, bool> insert(Key &&key, const T &value)
|
|
+ {
|
|
+ return try_emplace(std::move(key), value);
|
|
+ }
|
|
+
|
|
+ std::pair<iterator, bool> insert(const Key &key, T &&value)
|
|
+ {
|
|
+ return try_emplace(key, std::move(value));
|
|
+ }
|
|
+
|
|
+ std::pair<iterator, bool> insert(Key &&key, T &&value)
|
|
+ {
|
|
+ return try_emplace(std::move(key), std::move(value));
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ template <typename...Args>
|
|
+ std::pair<iterator, bool> try_emplace(const Key &key, Args&&...args)
|
|
+ {
|
|
+ auto it = lower_bound(key);
|
|
+ if (it == end() || key_compare::operator()(key, it.key())) {
|
|
+ c.values.insert(toValuesIterator(it), std::forward<Args>(args)...);
|
|
+ return { fromKeysIterator(c.keys.insert(toKeysIterator(it), key)), true };
|
|
+ } else {
|
|
+ return {it, false};
|
|
+ }
|
|
+ }
|
|
+
|
|
+ template <typename...Args>
|
|
+ std::pair<iterator, bool> try_emplace(Key &&key, Args&&...args)
|
|
+ {
|
|
+ auto it = lower_bound(key);
|
|
+ if (it == end() || key_compare::operator()(key, it.key())) {
|
|
+ c.values.insert(toValuesIterator(it), std::forward<Args>(args)...);
|
|
+ return { fromKeysIterator(c.keys.insert(toKeysIterator(it), std::move(key))), true };
|
|
+ } else {
|
|
+ return {it, false};
|
|
+ }
|
|
+ }
|
|
+
|
|
+ template <typename M>
|
|
+ std::pair<iterator, bool> insert_or_assign(const Key &key, M &&obj)
|
|
+ {
|
|
+ auto r = try_emplace(key, std::forward<M>(obj));
|
|
+ if (!r.second)
|
|
+ *toValuesIterator(r.first) = std::forward<M>(obj);
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ template <typename M>
|
|
+ std::pair<iterator, bool> insert_or_assign(Key &&key, M &&obj)
|
|
+ {
|
|
+ auto r = try_emplace(std::move(key), std::forward<M>(obj));
|
|
+ if (!r.second)
|
|
+ *toValuesIterator(r.first) = std::forward<M>(obj);
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+#ifdef QFLATMAP_ENABLE_STL_COMPATIBLE_INSERT
|
|
+ template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
|
|
+ void insert(InputIt first, InputIt last)
|
|
+ {
|
|
+ insertRange(first, last);
|
|
+ }
|
|
+
|
|
+ // ### Merge with the templated version above
|
|
+ // once we can use std::disjunction in is_compatible_iterator.
|
|
+ void insert(const value_type *first, const value_type *last)
|
|
+ {
|
|
+ insertRange(first, last);
|
|
+ }
|
|
+
|
|
+ template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
|
|
+ void insert(Qt::OrderedUniqueRange_t, InputIt first, InputIt last)
|
|
+ {
|
|
+ insertOrderedUniqueRange(first, last);
|
|
+ }
|
|
+
|
|
+ // ### Merge with the templated version above
|
|
+ // once we can use std::disjunction in is_compatible_iterator.
|
|
+ void insert(Qt::OrderedUniqueRange_t, const value_type *first, const value_type *last)
|
|
+ {
|
|
+ insertOrderedUniqueRange(first, last);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ iterator begin() { return { &c, 0 }; }
|
|
+ const_iterator begin() const { return { &c, 0 }; }
|
|
+ const_iterator cbegin() const { return begin(); }
|
|
+ const_iterator constBegin() const { return cbegin(); }
|
|
+ iterator end() { return { &c, c.keys.size() }; }
|
|
+ const_iterator end() const { return { &c, c.keys.size() }; }
|
|
+ const_iterator cend() const { return end(); }
|
|
+ const_iterator constEnd() const { return cend(); }
|
|
+ std::reverse_iterator<iterator> rbegin() { return std::reverse_iterator<iterator>(end()); }
|
|
+ std::reverse_iterator<const_iterator> rbegin() const
|
|
+ {
|
|
+ return std::reverse_iterator<const_iterator>(end());
|
|
+ }
|
|
+ std::reverse_iterator<const_iterator> crbegin() const { return rbegin(); }
|
|
+ std::reverse_iterator<iterator> rend() {
|
|
+ return std::reverse_iterator<iterator>(begin());
|
|
+ }
|
|
+ std::reverse_iterator<const_iterator> rend() const
|
|
+ {
|
|
+ return std::reverse_iterator<const_iterator>(begin());
|
|
+ }
|
|
+ std::reverse_iterator<const_iterator> crend() const { return rend(); }
|
|
+
|
|
+ iterator lower_bound(const Key &key)
|
|
+ {
|
|
+ auto cit = std::as_const(*this).lower_bound(key);
|
|
+ return { &c, cit.i };
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ iterator lower_bound(const X &key)
|
|
+ {
|
|
+ auto cit = std::as_const(*this).lower_bound(key);
|
|
+ return { &c, cit.i };
|
|
+ }
|
|
+
|
|
+ const_iterator lower_bound(const Key &key) const
|
|
+ {
|
|
+ return fromKeysIterator(std::lower_bound(c.keys.begin(), c.keys.end(), key, key_comp()));
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ const_iterator lower_bound(const X &key) const
|
|
+ {
|
|
+ return fromKeysIterator(std::lower_bound(c.keys.begin(), c.keys.end(), key, key_comp()));
|
|
+ }
|
|
+
|
|
+ iterator find(const Key &key)
|
|
+ {
|
|
+ return { &c, std::as_const(*this).find(key).i };
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ iterator find(const X &key)
|
|
+ {
|
|
+ return { &c, std::as_const(*this).find(key).i };
|
|
+ }
|
|
+
|
|
+ const_iterator find(const Key &key) const
|
|
+ {
|
|
+ auto it = lower_bound(key);
|
|
+ if (it != end()) {
|
|
+ if (!key_compare::operator()(key, it.key()))
|
|
+ return it;
|
|
+ it = end();
|
|
+ }
|
|
+ return it;
|
|
+ }
|
|
+
|
|
+ template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
|
|
+ const_iterator find(const X &key) const
|
|
+ {
|
|
+ auto it = lower_bound(key);
|
|
+ if (it != end()) {
|
|
+ if (!key_compare::operator()(key, it.key()))
|
|
+ return it;
|
|
+ it = end();
|
|
+ }
|
|
+ return it;
|
|
+ }
|
|
+
|
|
+ template <typename Predicate>
|
|
+ size_type remove_if(Predicate pred)
|
|
+ {
|
|
+ const auto indirect_call_to_pred = [pred = std::move(pred)](iterator it) {
|
|
+ using Pair = decltype(*it);
|
|
+ using K = decltype(it.key());
|
|
+ using V = decltype(it.value());
|
|
+ using P = Predicate;
|
|
+ if constexpr (std::is_invocable_v<P, K, V>) {
|
|
+ return pred(it.key(), it.value());
|
|
+ } else if constexpr (std::is_invocable_v<P, Pair> && !std::is_invocable_v<P, K>) {
|
|
+ return pred(*it);
|
|
+ } else if constexpr (std::is_invocable_v<P, K> && !std::is_invocable_v<P, Pair>) {
|
|
+ return pred(it.key());
|
|
+ } else {
|
|
+ static_assert(std::false_type(),
|
|
+ "Don't know how to call the predicate.\n"
|
|
+ "Options:\n"
|
|
+ "- pred(*it)\n"
|
|
+ "- pred(it.key(), it.value())\n"
|
|
+ "- pred(it.key())");
|
|
+ }
|
|
+ };
|
|
+
|
|
+ auto first = begin();
|
|
+ const auto last = end();
|
|
+
|
|
+ // find_if prefix loop
|
|
+ while (first != last && !indirect_call_to_pred(first))
|
|
+ ++first;
|
|
+
|
|
+ if (first == last)
|
|
+ return 0; // nothing to do
|
|
+
|
|
+ // we know that we need to remove *first
|
|
+
|
|
+ auto kdest = toKeysIterator(first);
|
|
+ auto vdest = toValuesIterator(first);
|
|
+
|
|
+ ++first;
|
|
+
|
|
+ auto k = std::next(kdest);
|
|
+ auto v = std::next(vdest);
|
|
+
|
|
+ // Main Loop
|
|
+ // - first is used only for indirect_call_to_pred
|
|
+ // - operations are done on k, v
|
|
+ // Loop invariants:
|
|
+ // - first, k, v are pointing to the same element
|
|
+ // - [begin(), first[, [c.keys.begin(), k[, [c.values.begin(), v[: already processed
|
|
+ // - [first, end()[, [k, c.keys.end()[, [v, c.values.end()[: still to be processed
|
|
+ // - [c.keys.begin(), kdest[ and [c.values.begin(), vdest[ are keepers
|
|
+ // - [kdest, k[, [vdest, v[ are considered removed
|
|
+ // - kdest is not c.keys.end()
|
|
+ // - vdest is not v.values.end()
|
|
+ while (first != last) {
|
|
+ if (!indirect_call_to_pred(first)) {
|
|
+ // keep *first, aka {*k, *v}
|
|
+ *kdest = std::move(*k);
|
|
+ *vdest = std::move(*v);
|
|
+ ++kdest;
|
|
+ ++vdest;
|
|
+ }
|
|
+ ++k;
|
|
+ ++v;
|
|
+ ++first;
|
|
+ }
|
|
+
|
|
+ const size_type r = std::distance(kdest, c.keys.end());
|
|
+ c.keys.erase(kdest, c.keys.end());
|
|
+ c.values.erase(vdest, c.values.end());
|
|
+ return r;
|
|
+ }
|
|
+
|
|
+ key_compare key_comp() const noexcept
|
|
+ {
|
|
+ return static_cast<key_compare>(*this);
|
|
+ }
|
|
+
|
|
+ value_compare value_comp() const noexcept
|
|
+ {
|
|
+ return static_cast<value_compare>(*this);
|
|
+ }
|
|
+
|
|
+private:
|
|
+ bool do_remove(iterator it)
|
|
+ {
|
|
+ if (it != end()) {
|
|
+ erase(it);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ T do_take(iterator it)
|
|
+ {
|
|
+ if (it != end()) {
|
|
+ T result = std::move(it.value());
|
|
+ erase(it);
|
|
+ return result;
|
|
+ }
|
|
+ return {};
|
|
+ }
|
|
+
|
|
+ template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
|
|
+ void initWithRange(InputIt first, InputIt last)
|
|
+ {
|
|
+ QtPrivate::reserveIfForwardIterator(this, first, last);
|
|
+ while (first != last) {
|
|
+ c.keys.push_back(first->first);
|
|
+ c.values.push_back(first->second);
|
|
+ ++first;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ iterator fromKeysIterator(typename key_container_type::iterator kit)
|
|
+ {
|
|
+ return { &c, static_cast<size_type>(std::distance(c.keys.begin(), kit)) };
|
|
+ }
|
|
+
|
|
+ const_iterator fromKeysIterator(typename key_container_type::const_iterator kit) const
|
|
+ {
|
|
+ return { &c, static_cast<size_type>(std::distance(c.keys.begin(), kit)) };
|
|
+ }
|
|
+
|
|
+ typename key_container_type::iterator toKeysIterator(iterator it)
|
|
+ {
|
|
+ return c.keys.begin() + it.i;
|
|
+ }
|
|
+
|
|
+ typename mapped_container_type::iterator toValuesIterator(iterator it)
|
|
+ {
|
|
+ return c.values.begin() + it.i;
|
|
+ }
|
|
+
|
|
+ template <class InputIt>
|
|
+ void insertRange(InputIt first, InputIt last)
|
|
+ {
|
|
+ size_type i = c.keys.size();
|
|
+ c.keys.resize(i + std::distance(first, last));
|
|
+ c.values.resize(c.keys.size());
|
|
+ for (; first != last; ++first, ++i) {
|
|
+ c.keys[i] = first->first;
|
|
+ c.values[i] = first->second;
|
|
+ }
|
|
+ ensureOrderedUnique();
|
|
+ }
|
|
+
|
|
+ class IndexedKeyComparator
|
|
+ {
|
|
+ public:
|
|
+ IndexedKeyComparator(const QFlatMap *am)
|
|
+ : m(am)
|
|
+ {
|
|
+ }
|
|
+
|
|
+ bool operator()(size_type i, size_type k) const
|
|
+ {
|
|
+ return m->key_comp()(m->c.keys[i], m->c.keys[k]);
|
|
+ }
|
|
+
|
|
+ private:
|
|
+ const QFlatMap *m;
|
|
+ };
|
|
+
|
|
+ template <class InputIt>
|
|
+ void insertOrderedUniqueRange(InputIt first, InputIt last)
|
|
+ {
|
|
+ const size_type s = c.keys.size();
|
|
+ c.keys.resize(s + std::distance(first, last));
|
|
+ c.values.resize(c.keys.size());
|
|
+ for (size_type i = s; first != last; ++first, ++i) {
|
|
+ c.keys[i] = first->first;
|
|
+ c.values[i] = first->second;
|
|
+ }
|
|
+
|
|
+ std::vector<size_type> p(size_t(c.keys.size()));
|
|
+ std::iota(p.begin(), p.end(), 0);
|
|
+ std::inplace_merge(p.begin(), p.begin() + s, p.end(), IndexedKeyComparator(this));
|
|
+ applyPermutation(p);
|
|
+ makeUnique();
|
|
+ }
|
|
+
|
|
+ void ensureOrderedUnique()
|
|
+ {
|
|
+ std::vector<size_type> p(size_t(c.keys.size()));
|
|
+ std::iota(p.begin(), p.end(), 0);
|
|
+ std::stable_sort(p.begin(), p.end(), IndexedKeyComparator(this));
|
|
+ applyPermutation(p);
|
|
+ makeUnique();
|
|
+ }
|
|
+
|
|
+ void applyPermutation(const std::vector<size_type> &p)
|
|
+ {
|
|
+ const size_type s = c.keys.size();
|
|
+ std::vector<bool> done(s);
|
|
+ for (size_type i = 0; i < s; ++i) {
|
|
+ if (done[i])
|
|
+ continue;
|
|
+ done[i] = true;
|
|
+ size_type j = i;
|
|
+ size_type k = p[i];
|
|
+ while (i != k) {
|
|
+ qSwap(c.keys[j], c.keys[k]);
|
|
+ qSwap(c.values[j], c.values[k]);
|
|
+ done[k] = true;
|
|
+ j = k;
|
|
+ k = p[j];
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void makeUnique()
|
|
+ {
|
|
+ // std::unique, but over two ranges
|
|
+ auto equivalent = [this](const auto &lhs, const auto &rhs) {
|
|
+ return !key_compare::operator()(lhs, rhs) && !key_compare::operator()(rhs, lhs);
|
|
+ };
|
|
+ const auto kb = c.keys.begin();
|
|
+ const auto ke = c.keys.end();
|
|
+ auto k = std::adjacent_find(kb, ke, equivalent);
|
|
+ if (k == ke)
|
|
+ return;
|
|
+
|
|
+ // equivalent keys found, we need to do actual work:
|
|
+ auto v = std::next(c.values.begin(), std::distance(kb, k));
|
|
+
|
|
+ auto kdest = k;
|
|
+ auto vdest = v;
|
|
+
|
|
+ ++k;
|
|
+ ++v;
|
|
+
|
|
+ // Loop Invariants:
|
|
+ //
|
|
+ // - [keys.begin(), kdest] and [values.begin(), vdest] are unique
|
|
+ // - k is not keys.end(), v is not values.end()
|
|
+ // - [next(k), keys.end()[ and [next(v), values.end()[ still need to be checked
|
|
+ while ((++v, ++k) != ke) {
|
|
+ if (!equivalent(*kdest, *k)) {
|
|
+ *++kdest = std::move(*k);
|
|
+ *++vdest = std::move(*v);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ c.keys.erase(std::next(kdest), ke);
|
|
+ c.values.erase(std::next(vdest), c.values.end());
|
|
+ }
|
|
+
|
|
+ containers c;
|
|
+};
|
|
+
|
|
+template<class Key, class T, qsizetype N = 256, class Compare = std::less<Key>>
|
|
+using QVarLengthFlatMap = QFlatMap<Key, T, Compare, QVarLengthArray<Key, N>, QVarLengthArray<T, N>>;
|
|
+
|
|
+QT_END_NAMESPACE
|
|
+
|
|
+#endif // QFLATMAP_P_H
|
|
+
|
|
diff --git a/src/plugins/platformthemes/gtk3/gtk3.pro b/src/plugins/platformthemes/gtk3/gtk3.pro
|
|
index 8d217396d3..c442b24a0a 100644
|
|
--- a/src/plugins/platformthemes/gtk3/gtk3.pro
|
|
+++ b/src/plugins/platformthemes/gtk3/gtk3.pro
|
|
@@ -15,11 +15,17 @@ QMAKE_CXXFLAGS_WARN_ON += -Wno-error=parentheses
|
|
|
|
HEADERS += \
|
|
qgtk3dialoghelpers.h \
|
|
+ qgtk3interface_p.h \
|
|
+ qgtk3json_p.h \
|
|
qgtk3menu.h \
|
|
+ qgtk3storage_p.h \
|
|
qgtk3theme.h
|
|
|
|
SOURCES += \
|
|
main.cpp \
|
|
qgtk3dialoghelpers.cpp \
|
|
+ qgtk3interface.cpp \
|
|
+ qgtk3json.cpp \
|
|
qgtk3menu.cpp \
|
|
+ qgtk3storage.cpp \
|
|
qgtk3theme.cpp
|
|
diff --git a/src/plugins/platformthemes/gtk3/qgtk3interface.cpp b/src/plugins/platformthemes/gtk3/qgtk3interface.cpp
|
|
new file mode 100644
|
|
index 0000000000..d932b250a5
|
|
--- /dev/null
|
|
+++ b/src/plugins/platformthemes/gtk3/qgtk3interface.cpp
|
|
@@ -0,0 +1,558 @@
|
|
+// Copyright (C) 2022 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 "qgtk3interface_p.h"
|
|
+#include "qgtk3storage_p.h"
|
|
+#include <QtCore/QMetaEnum>
|
|
+#include <QtCore/QSettings>
|
|
+#include <QtCore/QFileInfo>
|
|
+#include <QtGui/QFontDatabase>
|
|
+#include <QtGui/QIcon>
|
|
+
|
|
+QT_BEGIN_NAMESPACE
|
|
+Q_LOGGING_CATEGORY(lcQGtk3Interface, "qt.qpa.gtk");
|
|
+
|
|
+
|
|
+// Callback for gnome event loop has to be static
|
|
+static QGtk3Storage *m_storage = nullptr;
|
|
+
|
|
+QGtk3Interface::QGtk3Interface(QGtk3Storage *s)
|
|
+{
|
|
+ initColorMap();
|
|
+
|
|
+ if (!s) {
|
|
+ qCDebug(lcQGtk3Interface) << "QGtk3Interface instantiated without QGtk3Storage."
|
|
+ << "No reaction to runtime theme changes.";
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Connect to the GTK settings changed signal
|
|
+ auto handleThemeChange = [] {
|
|
+ if (m_storage)
|
|
+ m_storage->handleThemeChange();
|
|
+ };
|
|
+
|
|
+ GtkSettings *settings = gtk_settings_get_default();
|
|
+ const gboolean success = g_signal_connect(settings, "notify::gtk-theme-name",
|
|
+ G_CALLBACK(handleThemeChange), nullptr);
|
|
+ if (success == FALSE) {
|
|
+ qCDebug(lcQGtk3Interface) << "Connection to theme change signal failed."
|
|
+ << "No reaction to runtime theme changes.";
|
|
+ } else {
|
|
+ m_storage = s;
|
|
+ }
|
|
+}
|
|
+
|
|
+QGtk3Interface::~QGtk3Interface()
|
|
+{
|
|
+ // Ignore theme changes when destructor is reached
|
|
+ m_storage = nullptr;
|
|
+
|
|
+ // QGtkWidgets have to be destroyed manually
|
|
+ for (auto v : cache)
|
|
+ gtk_widget_destroy(v.second);
|
|
+}
|
|
+
|
|
+int QGtk3Interface::toGtkState(const QString &state)
|
|
+{
|
|
+#define CASE(x) \
|
|
+ if (QLatin1String(QByteArray(state.toLatin1())) == #x) \
|
|
+ return GTK_STATE_FLAG_ ##x
|
|
+
|
|
+#define CONVERT\
|
|
+ CASE(NORMAL);\
|
|
+ CASE(ACTIVE);\
|
|
+ CASE(PRELIGHT);\
|
|
+ CASE(SELECTED);\
|
|
+ CASE(INSENSITIVE);\
|
|
+ CASE(INCONSISTENT);\
|
|
+ CASE(FOCUSED);\
|
|
+ CASE(BACKDROP);\
|
|
+ CASE(DIR_LTR);\
|
|
+ CASE(DIR_RTL);\
|
|
+ CASE(LINK);\
|
|
+ CASE(VISITED);\
|
|
+ CASE(CHECKED);\
|
|
+ CASE(DROP_ACTIVE)
|
|
+
|
|
+ CONVERT;
|
|
+ return -1;
|
|
+#undef CASE
|
|
+}
|
|
+
|
|
+const QLatin1String QGtk3Interface::fromGtkState(GtkStateFlags state)
|
|
+{
|
|
+#define CASE(x) case GTK_STATE_FLAG_ ##x: return QLatin1String(#x)
|
|
+ switch (state) {
|
|
+ CONVERT;
|
|
+ }
|
|
+ Q_UNREACHABLE();
|
|
+#undef CASE
|
|
+#undef CONVERT
|
|
+}
|
|
+
|
|
+void QGtk3Interface::initColorMap()
|
|
+{
|
|
+ // Populate map with default values
|
|
+ #define SAVE(src, state, prop, def)\
|
|
+ {ColorKey({QGtkColorSource::src, GTK_STATE_FLAG_ ##state}), ColorValue({#prop, QGtkColorDefault::def})}
|
|
+
|
|
+ gtkColorMap = ColorMap {
|
|
+ SAVE(Foreground, NORMAL, theme_fg_color, Foreground),
|
|
+ SAVE(Foreground, BACKDROP, theme_unfocused_selected_fg_color, Foreground),
|
|
+ SAVE(Foreground, INSENSITIVE, insensitive_fg_color, Foreground),
|
|
+ SAVE(Foreground, SELECTED, theme_selected_fg_color, Foreground),
|
|
+ SAVE(Foreground, ACTIVE, theme_unfocused_fg_color, Foreground),
|
|
+ SAVE(Text, NORMAL, theme_text_color, Foreground),
|
|
+ SAVE(Text, ACTIVE, theme_unfocused_text_color, Foreground),
|
|
+ SAVE(Base, NORMAL, theme_base_color, Background),
|
|
+ SAVE(Base, INSENSITIVE, insensitive_base_color, Background),
|
|
+ SAVE(Background, NORMAL, theme_bg_color, Background),
|
|
+ SAVE(Background, SELECTED, theme_selected_bg_color, Background),
|
|
+ SAVE(Background, INSENSITIVE, insensitive_bg_color, Background),
|
|
+ SAVE(Background, ACTIVE, theme_unfocused_bg_color, Background),
|
|
+ SAVE(Background, BACKDROP, theme_unfocused_selected_bg_color, Background),
|
|
+ SAVE(Border, NORMAL, borders, Border),
|
|
+ SAVE(Border, ACTIVE, unfocused_borders, Border)
|
|
+ };
|
|
+#undef SAVE
|
|
+
|
|
+ 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
|
|
+QImage QGtk3Interface::standardPixmap(QPlatformTheme::StandardPixmap standardPixmap) const
|
|
+{
|
|
+ switch (standardPixmap) {
|
|
+ case QPlatformTheme::DialogDiscardButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_DELETE);
|
|
+ case QPlatformTheme::DialogOkButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_OK);
|
|
+ case QPlatformTheme::DialogCancelButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_CANCEL);
|
|
+ case QPlatformTheme::DialogYesButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_YES);
|
|
+ case QPlatformTheme::DialogNoButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_NO);
|
|
+ case QPlatformTheme::DialogOpenButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_OPEN);
|
|
+ case QPlatformTheme::DialogCloseButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_CLOSE);
|
|
+ case QPlatformTheme::DialogApplyButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_APPLY);
|
|
+ case QPlatformTheme::DialogSaveButton:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_SAVE);
|
|
+ case QPlatformTheme::MessageBoxWarning:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_DIALOG_WARNING);
|
|
+ case QPlatformTheme::MessageBoxQuestion:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_DIALOG_QUESTION);
|
|
+ case QPlatformTheme::MessageBoxInformation:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_DIALOG_INFO);
|
|
+ case QPlatformTheme::MessageBoxCritical:
|
|
+ return qt_gtk_get_icon(GTK_STOCK_DIALOG_ERROR);
|
|
+ case QPlatformTheme::CustomBase:
|
|
+ case QPlatformTheme::TitleBarMenuButton:
|
|
+ case QPlatformTheme::TitleBarMinButton:
|
|
+ case QPlatformTheme::TitleBarMaxButton:
|
|
+ case QPlatformTheme::TitleBarCloseButton:
|
|
+ case QPlatformTheme::TitleBarNormalButton:
|
|
+ case QPlatformTheme::TitleBarShadeButton:
|
|
+ case QPlatformTheme::TitleBarUnshadeButton:
|
|
+ case QPlatformTheme::TitleBarContextHelpButton:
|
|
+ case QPlatformTheme::DockWidgetCloseButton:
|
|
+ case QPlatformTheme::DesktopIcon:
|
|
+ case QPlatformTheme::TrashIcon:
|
|
+ case QPlatformTheme::ComputerIcon:
|
|
+ case QPlatformTheme::DriveFDIcon:
|
|
+ case QPlatformTheme::DriveHDIcon:
|
|
+ case QPlatformTheme::DriveCDIcon:
|
|
+ case QPlatformTheme::DriveDVDIcon:
|
|
+ case QPlatformTheme::DriveNetIcon:
|
|
+ case QPlatformTheme::DirOpenIcon:
|
|
+ case QPlatformTheme::DirClosedIcon:
|
|
+ case QPlatformTheme::DirLinkIcon:
|
|
+ case QPlatformTheme::DirLinkOpenIcon:
|
|
+ case QPlatformTheme::FileIcon:
|
|
+ case QPlatformTheme::FileLinkIcon:
|
|
+ case QPlatformTheme::ToolBarHorizontalExtensionButton:
|
|
+ case QPlatformTheme::ToolBarVerticalExtensionButton:
|
|
+ case QPlatformTheme::FileDialogStart:
|
|
+ case QPlatformTheme::FileDialogEnd:
|
|
+ case QPlatformTheme::FileDialogToParent:
|
|
+ case QPlatformTheme::FileDialogNewFolder:
|
|
+ case QPlatformTheme::FileDialogDetailedView:
|
|
+ case QPlatformTheme::FileDialogInfoView:
|
|
+ case QPlatformTheme::FileDialogContentsView:
|
|
+ case QPlatformTheme::FileDialogListView:
|
|
+ case QPlatformTheme::FileDialogBack:
|
|
+ case QPlatformTheme::DirIcon:
|
|
+ case QPlatformTheme::DialogHelpButton:
|
|
+ case QPlatformTheme::DialogResetButton:
|
|
+ case QPlatformTheme::ArrowUp:
|
|
+ case QPlatformTheme::ArrowDown:
|
|
+ case QPlatformTheme::ArrowLeft:
|
|
+ case QPlatformTheme::ArrowRight:
|
|
+ case QPlatformTheme::ArrowBack:
|
|
+ case QPlatformTheme::ArrowForward:
|
|
+ case QPlatformTheme::DirHomeIcon:
|
|
+ case QPlatformTheme::CommandLink:
|
|
+ case QPlatformTheme::VistaShield:
|
|
+ case QPlatformTheme::BrowserReload:
|
|
+ case QPlatformTheme::BrowserStop:
|
|
+ case QPlatformTheme::MediaPlay:
|
|
+ case QPlatformTheme::MediaStop:
|
|
+ case QPlatformTheme::MediaPause:
|
|
+ case QPlatformTheme::MediaSkipForward:
|
|
+ case QPlatformTheme::MediaSkipBackward:
|
|
+ case QPlatformTheme::MediaSeekForward:
|
|
+ case QPlatformTheme::MediaSeekBackward:
|
|
+ case QPlatformTheme::MediaVolume:
|
|
+ case QPlatformTheme::MediaVolumeMuted:
|
|
+ case QPlatformTheme::LineEditClearButton:
|
|
+ case QPlatformTheme::DialogYesToAllButton:
|
|
+ case QPlatformTheme::DialogNoToAllButton:
|
|
+ case QPlatformTheme::DialogSaveAllButton:
|
|
+ case QPlatformTheme::DialogAbortButton:
|
|
+ case QPlatformTheme::DialogRetryButton:
|
|
+ case QPlatformTheme::DialogIgnoreButton:
|
|
+ case QPlatformTheme::RestoreDefaultsButton:
|
|
+ case QPlatformTheme::NStandardPixmap:
|
|
+ return QImage();
|
|
+ }
|
|
+ Q_UNREACHABLE();
|
|
+}
|
|
+
|
|
+QImage QGtk3Interface::qt_gtk_get_icon(const char* iconName) const
|
|
+{
|
|
+ GtkIconSet* iconSet = gtk_icon_factory_lookup_default (iconName);
|
|
+ GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context(), GTK_ICON_SIZE_DIALOG);
|
|
+ return qt_convert_gdk_pixbuf(icon);
|
|
+}
|
|
+
|
|
+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);
|
|
+ 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
|
|
+}
|
|
+
|
|
+GtkWidget *QGtk3Interface::qt_new_gtkWidget(QGtkWidget type) const
|
|
+{
|
|
+#define CASE(Type)\
|
|
+ case QGtkWidget::Type: return Type ##_new();
|
|
+#define CASEN(Type)\
|
|
+ case QGtkWidget::Type: return Type ##_new(nullptr);
|
|
+
|
|
+ switch (type) {
|
|
+ CASE(gtk_menu_bar)
|
|
+ CASE(gtk_menu)
|
|
+ CASE(gtk_button)
|
|
+ case QGtkWidget::gtk_button_box: return gtk_button_box_new(GtkOrientation::GTK_ORIENTATION_HORIZONTAL);
|
|
+ CASE(gtk_check_button)
|
|
+ CASEN(gtk_radio_button)
|
|
+ CASEN(gtk_frame)
|
|
+ CASE(gtk_statusbar)
|
|
+ CASE(gtk_entry)
|
|
+ case QGtkWidget::gtk_popup: return gtk_window_new(GTK_WINDOW_POPUP);
|
|
+ CASE(gtk_notebook)
|
|
+ CASE(gtk_toolbar)
|
|
+ CASE(gtk_tree_view)
|
|
+ CASE(gtk_combo_box)
|
|
+ CASE(gtk_combo_box_text)
|
|
+ CASE(gtk_progress_bar)
|
|
+ CASE(gtk_fixed)
|
|
+ CASE(gtk_separator_menu_item)
|
|
+ CASE(gtk_offscreen_window)
|
|
+ case QGtkWidget::gtk_Default: return nullptr;
|
|
+ }
|
|
+#undef CASE
|
|
+#undef CASEN
|
|
+ Q_UNREACHABLE();
|
|
+}
|
|
+
|
|
+GdkRGBA QGtk3Interface::genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def) const
|
|
+{
|
|
+ GdkRGBA color;
|
|
+
|
|
+#define CASE(def, call)\
|
|
+ case QGtkColorDefault::def:\
|
|
+ gtk_style_context_get_ ##call(con, state, &color);\
|
|
+ break;
|
|
+
|
|
+ switch (def) {
|
|
+ CASE(Foreground, color)
|
|
+ CASE(Background, background_color)
|
|
+ CASE(Border, border_color)
|
|
+ }
|
|
+ return color;
|
|
+#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
|
|
+QColor QGtk3Interface::color(GtkWidget *widget, QGtkColorSource source, GtkStateFlags state) const
|
|
+{
|
|
+ GdkRGBA col;
|
|
+ GtkStyleContext *con = context(widget);
|
|
+
|
|
+#define CASE(src, def)\
|
|
+ case QGtkColorSource::src: {\
|
|
+ const ColorKey key = ColorKey({QGtkColorSource::src, state});\
|
|
+ if (gtkColorMap.contains(key)) {\
|
|
+ const ColorValue val = gtkColorMap.value(key);\
|
|
+ if (!gtk_style_context_lookup_color(con, val.propertyName.toUtf8().constData(), &col)) {\
|
|
+ col = genericColor(con, state, val.genericSource);\
|
|
+ qCDebug(lcQGtk3Interface) << "Property name" << val.propertyName << "not found.\n"\
|
|
+ << "Falling back to " << val.genericSource;\
|
|
+ }\
|
|
+ } else {\
|
|
+ col = genericColor(con, state, QGtkColorDefault::def);\
|
|
+ qCDebug(lcQGtk3Interface) << "No color source found for" << QGtkColorSource::src\
|
|
+ << fromGtkState(state) << "\n Falling back to"\
|
|
+ << QGtkColorDefault::def;\
|
|
+ }\
|
|
+ }\
|
|
+ break;
|
|
+
|
|
+ switch (source) {
|
|
+ CASE(Foreground, Foreground)
|
|
+ CASE(Background, Background)
|
|
+ CASE(Text, Foreground)
|
|
+ CASE(Base, Background)
|
|
+ CASE(Border, Border)
|
|
+ }
|
|
+
|
|
+ return fromGdkColor(col);
|
|
+#undef CASE
|
|
+}
|
|
+
|
|
+// Deliver a widget pointer
|
|
+GtkWidget *QGtk3Interface::widget(QGtkWidget type) const
|
|
+{
|
|
+ if (type == QGtkWidget::gtk_Default)
|
|
+ return nullptr;
|
|
+
|
|
+ // Return from cache
|
|
+ if (GtkWidget *w = cache.value(type))
|
|
+ return w;
|
|
+
|
|
+ // Create new item and cache it
|
|
+ GtkWidget *w = qt_new_gtkWidget(type);
|
|
+ cache.insert(type, w);
|
|
+ return w;
|
|
+}
|
|
+
|
|
+// Return widget syle context or default style
|
|
+GtkStyleContext *QGtk3Interface::context(GtkWidget *w) const
|
|
+{
|
|
+ if (w)
|
|
+ return gtk_widget_get_style_context(w);
|
|
+
|
|
+ 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.
|
|
+QBrush QGtk3Interface::brush(QGtkWidget wtype, QGtkColorSource source, GtkStateFlags state) const
|
|
+{
|
|
+ return QBrush(color(widget(wtype), source, state));
|
|
+}
|
|
+
|
|
+const QString QGtk3Interface::themeName() const
|
|
+{
|
|
+ gchar *theme_name;
|
|
+ GtkSettings *settings = gtk_settings_get_default();
|
|
+ if (!settings)
|
|
+ return QString();
|
|
+
|
|
+ g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
|
|
+ return QLatin1String(theme_name);
|
|
+}
|
|
+
|
|
+inline constexpr QGtk3Interface::QGtkWidget QGtk3Interface::toWidgetType(QPlatformTheme::Font type)
|
|
+{
|
|
+ switch (type) {
|
|
+ case QPlatformTheme::SystemFont: return QGtkWidget::gtk_Default;
|
|
+ case QPlatformTheme::MenuFont: return QGtkWidget::gtk_menu;
|
|
+ case QPlatformTheme::MenuBarFont: return QGtkWidget::gtk_menu_bar;
|
|
+ case QPlatformTheme::MenuItemFont: return QGtkWidget::gtk_menu;
|
|
+ case QPlatformTheme::MessageBoxFont: return QGtkWidget::gtk_popup;
|
|
+ case QPlatformTheme::LabelFont: return QGtkWidget::gtk_popup;
|
|
+ case QPlatformTheme::TipLabelFont: return QGtkWidget::gtk_Default;
|
|
+ case QPlatformTheme::StatusBarFont: return QGtkWidget::gtk_statusbar;
|
|
+ case QPlatformTheme::TitleBarFont: return QGtkWidget::gtk_Default;
|
|
+ case QPlatformTheme::MdiSubWindowTitleFont: return QGtkWidget::gtk_Default;
|
|
+ case QPlatformTheme::DockWidgetTitleFont: return QGtkWidget::gtk_Default;
|
|
+ case QPlatformTheme::PushButtonFont: return QGtkWidget::gtk_button;
|
|
+ case QPlatformTheme::CheckBoxFont: return QGtkWidget::gtk_check_button;
|
|
+ case QPlatformTheme::RadioButtonFont: return QGtkWidget::gtk_radio_button;
|
|
+ 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::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::GroupBoxTitleFont: return QGtkWidget::gtk_Default;
|
|
+ case QPlatformTheme::TabButtonFont: return QGtkWidget::gtk_button;
|
|
+ case QPlatformTheme::EditorFont: return QGtkWidget::gtk_entry;
|
|
+ case QPlatformTheme::NFonts: return QGtkWidget::gtk_Default;
|
|
+ }
|
|
+ Q_UNREACHABLE();
|
|
+}
|
|
+
|
|
+inline constexpr QFont::Style QGtk3Interface::toFontStyle(PangoStyle style)
|
|
+{
|
|
+ switch (style) {
|
|
+ case PANGO_STYLE_ITALIC: return QFont::StyleItalic;
|
|
+ case PANGO_STYLE_OBLIQUE: return QFont::StyleOblique;
|
|
+ case PANGO_STYLE_NORMAL: return QFont::StyleNormal;
|
|
+ }
|
|
+ // This is reached when GTK has introduced a new font style
|
|
+ Q_UNREACHABLE();
|
|
+}
|
|
+
|
|
+inline constexpr int QGtk3Interface::toFontWeight(PangoWeight weight)
|
|
+{
|
|
+ // GTK PangoWeight can be directly converted to QFont::Weight
|
|
+ // unless one of the enums changes.
|
|
+ static_assert(PANGO_WEIGHT_THIN == 100 && PANGO_WEIGHT_ULTRAHEAVY == 1000,
|
|
+ "Pango font weight enum changed. Fix conversion.");
|
|
+
|
|
+ return qBound(1, static_cast<int>(weight), 1000);
|
|
+}
|
|
+
|
|
+inline constexpr QFont::Weight QGtk3Interface::toQFontWeight(int weight)
|
|
+{
|
|
+ if (weight <= 100)
|
|
+ return QFont::Thin;
|
|
+ else if (weight <= 200)
|
|
+ return QFont::ExtraLight;
|
|
+ else if (weight <= 300)
|
|
+ return QFont::Light;
|
|
+ else if (weight <= 400)
|
|
+ return QFont::Normal;
|
|
+ else if (weight <= 500)
|
|
+ return QFont::Medium;
|
|
+ else if (weight <= 600)
|
|
+ return QFont::DemiBold;
|
|
+ else if (weight <= 700)
|
|
+ return QFont::Bold;
|
|
+ else if (weight <= 800)
|
|
+ return QFont::ExtraBold;
|
|
+ else
|
|
+ return QFont::Black;
|
|
+}
|
|
+
|
|
+QFont QGtk3Interface::font(QPlatformTheme::Font type) const
|
|
+{
|
|
+ GtkStyleContext *con = context(widget(toWidgetType(type)));
|
|
+ if (!con)
|
|
+ return QFont();
|
|
+
|
|
+ const PangoFontDescription *gtkFont = gtk_style_context_get_font(con, GTK_STATE_FLAG_NORMAL);
|
|
+ if (!gtkFont)
|
|
+ return QFont();
|
|
+
|
|
+ const QString family = QString::fromLatin1(pango_font_description_get_family(gtkFont));
|
|
+ if (family.isEmpty())
|
|
+ return QFont();
|
|
+
|
|
+ const int weight = toFontWeight(pango_font_description_get_weight(gtkFont));
|
|
+
|
|
+ // Creating a QFont() creates a futex lockup on a theme change
|
|
+ // QFont doesn't have a constructor with float point size
|
|
+ // => create a dummy point size and set it later.
|
|
+ QFont font(family, 1, toQFontWeight(weight));
|
|
+ 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()) {
|
|
+ qCDebug(lcQGtk3Interface) << "No fixed pitch font found in font family"
|
|
+ << font.family() << ". falling back to a default"
|
|
+ << "fixed pitch font";
|
|
+ font.setFamily("monospace");
|
|
+ }
|
|
+ }
|
|
+ return font;
|
|
+}
|
|
+
|
|
+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,
|
|
+ G_FILE_QUERY_INFO_NONE, nullptr, nullptr);
|
|
+ if (!info) {
|
|
+ g_object_unref(file);
|
|
+ return QIcon();
|
|
+ }
|
|
+
|
|
+ GIcon *icon = g_file_info_get_icon(info);
|
|
+ if (!icon) {
|
|
+ g_object_unref(file);
|
|
+ g_object_unref(info);
|
|
+ return QIcon();
|
|
+ }
|
|
+
|
|
+ GtkIconTheme *theme = gtk_icon_theme_get_default();
|
|
+ GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(theme, icon, GTK_ICON_SIZE_BUTTON,
|
|
+ GTK_ICON_LOOKUP_FORCE_SIZE);
|
|
+ if (!iconInfo) {
|
|
+ g_object_unref(file);
|
|
+ g_object_unref(info);
|
|
+ g_object_unref(icon);
|
|
+ return QIcon();
|
|
+ }
|
|
+
|
|
+ GdkPixbuf *buf = gtk_icon_info_load_icon(iconInfo, nullptr);
|
|
+ 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));
|
|
+}
|
|
+
|
|
+QT_END_NAMESPACE
|
|
diff --git a/src/plugins/platformthemes/gtk3/qgtk3interface_p.h b/src/plugins/platformthemes/gtk3/qgtk3interface_p.h
|
|
new file mode 100644
|
|
index 0000000000..8997a64e76
|
|
--- /dev/null
|
|
+++ b/src/plugins/platformthemes/gtk3/qgtk3interface_p.h
|
|
@@ -0,0 +1,170 @@
|
|
+// Copyright (C) 2022 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 QGTK3INTERFACE_H
|
|
+#define QGTK3INTERFACE_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/QString>
|
|
+#include <QtCore/QLibrary>
|
|
+#include <QtCore/QCache>
|
|
+#include <private/qflatmap_p.h>
|
|
+#include <QtCore/QObject>
|
|
+#include <QtGui/QPalette>
|
|
+#include <QtWidgets/QWidget>
|
|
+#include <QtCore/QLoggingCategory>
|
|
+#include <QtGui/QPixmap>
|
|
+#include <qpa/qplatformtheme.h>
|
|
+
|
|
+#undef signals // Collides with GTK symbols
|
|
+#include <gtk/gtk.h>
|
|
+#include <gdk/gdk.h>
|
|
+#include <glib.h>
|
|
+
|
|
+QT_BEGIN_NAMESPACE
|
|
+
|
|
+Q_DECLARE_LOGGING_CATEGORY(lcQGtk3Interface);
|
|
+
|
|
+class QGtk3Storage;
|
|
+class QGtk3Interface
|
|
+{
|
|
+ Q_GADGET
|
|
+public:
|
|
+ QGtk3Interface(QGtk3Storage *);
|
|
+ ~QGtk3Interface();
|
|
+
|
|
+ // Enum representing GTK widget types
|
|
+ enum class QGtkWidget {
|
|
+ gtk_menu_bar,
|
|
+ gtk_menu,
|
|
+ gtk_button,
|
|
+ gtk_button_box,
|
|
+ gtk_check_button,
|
|
+ gtk_radio_button,
|
|
+ gtk_frame,
|
|
+ gtk_statusbar,
|
|
+ gtk_entry,
|
|
+ gtk_popup,
|
|
+ gtk_notebook,
|
|
+ gtk_toolbar,
|
|
+ gtk_tree_view,
|
|
+ gtk_combo_box,
|
|
+ gtk_combo_box_text,
|
|
+ gtk_progress_bar,
|
|
+ gtk_fixed,
|
|
+ gtk_separator_menu_item,
|
|
+ gtk_Default,
|
|
+ gtk_offscreen_window
|
|
+ };
|
|
+ Q_ENUM(QGtkWidget)
|
|
+
|
|
+ // Enum representing color sources of a GTK theme
|
|
+ enum class QGtkColorSource {
|
|
+ Foreground,
|
|
+ Background,
|
|
+ Text,
|
|
+ Base,
|
|
+ Border
|
|
+ };
|
|
+ Q_ENUM(QGtkColorSource)
|
|
+
|
|
+ // Enum for default color getter
|
|
+ enum class QGtkColorDefault {
|
|
+ Foreground,
|
|
+ Background,
|
|
+ Border
|
|
+ };
|
|
+ Q_ENUM(QGtkColorDefault)
|
|
+
|
|
+ // Create a brush from GTK widget type, color source and color state
|
|
+ QBrush brush(QGtkWidget wtype, QGtkColorSource source, GtkStateFlags state) const;
|
|
+
|
|
+ // Font & icon getters
|
|
+ QImage standardPixmap(QPlatformTheme::StandardPixmap standardPixmap) const;
|
|
+ QFont font(QPlatformTheme::Font type) const;
|
|
+ QIcon fileIcon(const QFileInfo &fileInfo) const;
|
|
+
|
|
+ // Return current GTK theme name
|
|
+ const QString themeName() const;
|
|
+
|
|
+ // Convert GTK state to/from string
|
|
+ static int toGtkState(const QString &state);
|
|
+ static const QLatin1String fromGtkState(GtkStateFlags state);
|
|
+
|
|
+private:
|
|
+
|
|
+ // Map colors to GTK property names and default to generic color getters
|
|
+ struct ColorKey {
|
|
+ QGtkColorSource colorSource = QGtkColorSource::Background;
|
|
+ GtkStateFlags state = GTK_STATE_FLAG_NORMAL;
|
|
+
|
|
+ // struct becomes key of a map, so operator< is needed
|
|
+ bool operator<(const ColorKey& other) const {
|
|
+ return std::tie(colorSource, state) <
|
|
+ std::tie(other.colorSource, other.state);
|
|
+ }
|
|
+
|
|
+ QDebug operator<<(QDebug dbg)
|
|
+ {
|
|
+ return dbg << "QGtk3Interface::ColorKey(colorSource=" << colorSource << ", GTK state=" << fromGtkState(state) << ")";
|
|
+ }
|
|
+ };
|
|
+
|
|
+ struct ColorValue {
|
|
+ QString propertyName = QString();
|
|
+ QGtkColorDefault genericSource = QGtkColorDefault::Background;
|
|
+
|
|
+ QDebug operator<<(QDebug dbg)
|
|
+ {
|
|
+ return dbg << "QGtk3Interface::ColorValue(propertyName=" << propertyName << ", genericSource=" << genericSource << ")";
|
|
+ }
|
|
+ };
|
|
+
|
|
+ typedef QFlatMap<ColorKey, ColorValue> ColorMap;
|
|
+ ColorMap gtkColorMap;
|
|
+ void initColorMap();
|
|
+
|
|
+ GdkRGBA genericColor(GtkStyleContext *con, GtkStateFlags state, QGtkColorDefault def) const;
|
|
+
|
|
+ // Cache for GTK widgets
|
|
+ mutable QFlatMap<QGtkWidget, GtkWidget *> cache;
|
|
+
|
|
+ // Converters for GTK icon and GDK pixbuf
|
|
+ QImage qt_gtk_get_icon(const char *iconName) const;
|
|
+ QImage qt_convert_gdk_pixbuf(GdkPixbuf *buf) const;
|
|
+
|
|
+ // Create new GTK widget object
|
|
+ GtkWidget *qt_new_gtkWidget(QGtkWidget type) const;
|
|
+
|
|
+ // Deliver GTK Widget from cache or create new
|
|
+ GtkWidget *widget(QGtkWidget type) const;
|
|
+
|
|
+ // Get a GTK widget's style context. Default settings style context if nullptr
|
|
+ GtkStyleContext *context(GtkWidget *widget = nullptr) const;
|
|
+
|
|
+ // Convert GTK color into QColor
|
|
+ static inline QColor fromGdkColor (const GdkRGBA &c)
|
|
+ { return QColor::fromRgbF(c.red, c.green, c.blue, c.alpha); }
|
|
+
|
|
+ // get a QColor of a GTK widget (default settings style if nullptr)
|
|
+ QColor color (GtkWidget *widget, QGtkColorSource source, GtkStateFlags state) const;
|
|
+
|
|
+ // Mappings for GTK fonts
|
|
+ inline static constexpr QGtkWidget toWidgetType(QPlatformTheme::Font);
|
|
+ inline static constexpr QFont::Style toFontStyle(PangoStyle style);
|
|
+ inline static constexpr int toFontWeight(PangoWeight weight);
|
|
+ inline static constexpr QFont::Weight toQFontWeight(int weight);
|
|
+
|
|
+};
|
|
+QT_END_NAMESPACE
|
|
+#endif // QGTK3INTERFACE_H
|
|
diff --git a/src/plugins/platformthemes/gtk3/qgtk3json.cpp b/src/plugins/platformthemes/gtk3/qgtk3json.cpp
|
|
new file mode 100644
|
|
index 0000000000..f4d5b50ec5
|
|
--- /dev/null
|
|
+++ b/src/plugins/platformthemes/gtk3/qgtk3json.cpp
|
|
@@ -0,0 +1,475 @@
|
|
+// Copyright (C) 2022 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 "qgtk3json_p.h"
|
|
+#include <QtCore/QFile>
|
|
+#include <QMetaEnum>
|
|
+
|
|
+QT_BEGIN_NAMESPACE
|
|
+
|
|
+QLatin1String QGtk3Json::fromPalette(QPlatformTheme::Palette palette)
|
|
+{
|
|
+ switch (palette) {
|
|
+ case QPlatformTheme::SystemPalette:
|
|
+ return QLatin1String("SystemPalette");
|
|
+ case QPlatformTheme::ToolTipPalette:
|
|
+ return QLatin1String("ToolTipPalette");
|
|
+ case QPlatformTheme::ToolButtonPalette:
|
|
+ return QLatin1String("ToolButtonPalette");
|
|
+ case QPlatformTheme::ButtonPalette:
|
|
+ return QLatin1String("ButtonPalette");
|
|
+ case QPlatformTheme::CheckBoxPalette:
|
|
+ return QLatin1String("CheckBoxPalette");
|
|
+ case QPlatformTheme::RadioButtonPalette:
|
|
+ return QLatin1String("RadioButtonPalette");
|
|
+ case QPlatformTheme::HeaderPalette:
|
|
+ return QLatin1String("HeaderPalette");
|
|
+ case QPlatformTheme::ComboBoxPalette:
|
|
+ return QLatin1String("ComboBoxPalette");
|
|
+ case QPlatformTheme::ItemViewPalette:
|
|
+ return QLatin1String("ItemViewPalette");
|
|
+ case QPlatformTheme::MessageBoxLabelPalette:
|
|
+ return QLatin1String("MessageBoxLabelPalette");
|
|
+ case QPlatformTheme::TabBarPalette:
|
|
+ return QLatin1String("TabBarPalette");
|
|
+ case QPlatformTheme::LabelPalette:
|
|
+ return QLatin1String("LabelPalette");
|
|
+ case QPlatformTheme::GroupBoxPalette:
|
|
+ return QLatin1String("GroupBoxPalette");
|
|
+ case QPlatformTheme::MenuPalette:
|
|
+ return QLatin1String("MenuPalette");
|
|
+ case QPlatformTheme::MenuBarPalette:
|
|
+ return QLatin1String("MenuBarPalette");
|
|
+ case QPlatformTheme::TextEditPalette:
|
|
+ return QLatin1String("TextEditPalette");
|
|
+ case QPlatformTheme::TextLineEditPalette:
|
|
+ return QLatin1String("TextLineEditPalette");
|
|
+ default:
|
|
+ return QLatin1String();
|
|
+ }
|
|
+ return QLatin1String();
|
|
+}
|
|
+
|
|
+QLatin1String QGtk3Json::fromGtkState(GtkStateFlags state)
|
|
+{
|
|
+ return QGtk3Interface::fromGtkState(state);
|
|
+}
|
|
+
|
|
+QLatin1String fromColor(const QColor &color)
|
|
+{
|
|
+ return QLatin1String(QByteArray(color.name(QColor::HexRgb).toLatin1()));
|
|
+}
|
|
+
|
|
+QLatin1String QGtk3Json::fromColorRole(QPalette::ColorRole role)
|
|
+{
|
|
+ return QLatin1String(QMetaEnum::fromType<QPalette::ColorRole>().valueToKey(static_cast<int>(role)));
|
|
+}
|
|
+
|
|
+QLatin1String QGtk3Json::fromColorGroup(QPalette::ColorGroup group)
|
|
+{
|
|
+ return QLatin1String(QMetaEnum::fromType<QPalette::ColorGroup>().valueToKey(static_cast<int>(group)));
|
|
+}
|
|
+
|
|
+QLatin1String QGtk3Json::fromGdkSource(QGtk3Interface::QGtkColorSource source)
|
|
+{
|
|
+ return QLatin1String(QMetaEnum::fromType<QGtk3Interface::QGtkColorSource>().valueToKey(static_cast<int>(source)));
|
|
+}
|
|
+
|
|
+QLatin1String QGtk3Json::fromWidgetType(QGtk3Interface::QGtkWidget widgetType)
|
|
+{
|
|
+ return QLatin1String(QMetaEnum::fromType<QGtk3Interface::QGtkWidget>().valueToKey(static_cast<int>(widgetType)));
|
|
+}
|
|
+
|
|
+QLatin1String QGtk3Json::fromAppearance(Qt::Appearance app)
|
|
+{
|
|
+ return QLatin1String(QMetaEnum::fromType<Qt::Appearance>().valueToKey(static_cast<int>(app)));
|
|
+}
|
|
+
|
|
+#define CONVERT(type, key, def)\
|
|
+ bool ok;\
|
|
+ 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)
|
|
+{
|
|
+ CONVERT(Qt::Appearance, appearance, Unknown);
|
|
+}
|
|
+
|
|
+QPlatformTheme::Palette QGtk3Json::toPalette(const QString &palette)
|
|
+{
|
|
+ if (palette == QLatin1String("SystemPalette"))
|
|
+ return QPlatformTheme::SystemPalette;
|
|
+ else if (palette == QLatin1String("ToolTipPalette"))
|
|
+ return QPlatformTheme::ToolTipPalette;
|
|
+ else if (palette == QLatin1String("ToolButtonPalette"))
|
|
+ return QPlatformTheme::ToolButtonPalette;
|
|
+ else if (palette == QLatin1String("ButtonPalette"))
|
|
+ return QPlatformTheme::ButtonPalette;
|
|
+ else if (palette == QLatin1String("CheckBoxPalette"))
|
|
+ return QPlatformTheme::CheckBoxPalette;
|
|
+ else if (palette == QLatin1String("RadioButtonPalette"))
|
|
+ return QPlatformTheme::RadioButtonPalette;
|
|
+ else if (palette == QLatin1String("HeaderPalette"))
|
|
+ return QPlatformTheme::HeaderPalette;
|
|
+ else if (palette == QLatin1String("ComboBoxPalette"))
|
|
+ return QPlatformTheme::ComboBoxPalette;
|
|
+ else if (palette == QLatin1String("ItemViewPalette"))
|
|
+ return QPlatformTheme::ItemViewPalette;
|
|
+ else if (palette == QLatin1String("MessageBoxLabelPelette"))
|
|
+ return QPlatformTheme::MessageBoxLabelPelette;
|
|
+ else if (palette == QLatin1String("TabBarPalette"))
|
|
+ return QPlatformTheme::TabBarPalette;
|
|
+ else if (palette == QLatin1String("LabelPalette"))
|
|
+ return QPlatformTheme::LabelPalette;
|
|
+ else if (palette == QLatin1String("GroupBoxPalette"))
|
|
+ return QPlatformTheme::GroupBoxPalette;
|
|
+ else if (palette == QLatin1String("MenuPalette"))
|
|
+ return QPlatformTheme::MenuPalette;
|
|
+ else if (palette == QLatin1String("MenuBarPalette"))
|
|
+ return QPlatformTheme::MenuBarPalette;
|
|
+ else if (palette == QLatin1String("TextEditPalette"))
|
|
+ return QPlatformTheme::TextEditPalette;
|
|
+ else if (palette == QLatin1String("TextLineEditPalette"))
|
|
+ return QPlatformTheme::TextLineEditPalette;
|
|
+
|
|
+ return QPlatformTheme::NPalettes;
|
|
+}
|
|
+
|
|
+GtkStateFlags QGtk3Json::toGtkState(const QString &type)
|
|
+{
|
|
+ int i = QGtk3Interface::toGtkState(type);
|
|
+ if (i < 0)
|
|
+ return GTK_STATE_FLAG_NORMAL;
|
|
+ return static_cast<GtkStateFlags>(i);
|
|
+}
|
|
+
|
|
+QColor toColor(const QStringView &color)
|
|
+{
|
|
+ return QColor(color);
|
|
+}
|
|
+
|
|
+QPalette::ColorRole QGtk3Json::toColorRole(const QString &role)
|
|
+{
|
|
+ CONVERT(QPalette::ColorRole, role, NColorRoles);
|
|
+}
|
|
+
|
|
+QPalette::ColorGroup QGtk3Json::toColorGroup(const QString &group)
|
|
+{
|
|
+ CONVERT(QPalette::ColorGroup, group, NColorGroups);
|
|
+}
|
|
+
|
|
+QGtk3Interface::QGtkColorSource QGtk3Json::toGdkSource(const QString &source)
|
|
+{
|
|
+ CONVERT(QGtk3Interface::QGtkColorSource, source, Background);
|
|
+}
|
|
+
|
|
+QLatin1String QGtk3Json::fromSourceType(QGtk3Storage::SourceType sourceType)
|
|
+{
|
|
+ return QLatin1String(QMetaEnum::fromType<QGtk3Storage::SourceType>().valueToKey(static_cast<int>(sourceType)));
|
|
+}
|
|
+
|
|
+QGtk3Storage::SourceType QGtk3Json::toSourceType(const QString &sourceType)
|
|
+{
|
|
+ CONVERT(QGtk3Storage::SourceType, sourceType, Invalid);
|
|
+}
|
|
+
|
|
+QGtk3Interface::QGtkWidget QGtk3Json::toWidgetType(const QString &widgetType)
|
|
+{
|
|
+ CONVERT(QGtk3Interface::QGtkWidget, widgetType, gtk_offscreen_window);
|
|
+}
|
|
+
|
|
+#undef CONVERT
|
|
+
|
|
+bool QGtk3Json::save(const QGtk3Storage::PaletteMap &map, const QString &fileName,
|
|
+ QJsonDocument::JsonFormat format)
|
|
+{
|
|
+ QJsonDocument doc = save(map);
|
|
+ if (doc.isEmpty()) {
|
|
+ qWarning() << "Nothing to save to" << fileName;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ QFile file(fileName);
|
|
+ if (!file.open(QIODevice::WriteOnly)) {
|
|
+ qWarning() << "Unable to open file" << fileName << "for writing.";
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (!file.write(doc.toJson(format))) {
|
|
+ qWarning() << "Unable to serialize Json document.";
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ file.close();
|
|
+ qInfo() << "Saved mapping data to" << fileName;
|
|
+ return true;
|
|
+}
|
|
+
|
|
+const QJsonDocument QGtk3Json::save(const QGtk3Storage::PaletteMap &map)
|
|
+{
|
|
+ QJsonObject paletteObject;
|
|
+ for (auto paletteIterator = map.constBegin(); paletteIterator != map.constEnd();
|
|
+ ++paletteIterator) {
|
|
+ const QGtk3Storage::BrushMap &bm = paletteIterator.value();
|
|
+ QFlatMap<QPalette::ColorRole, QGtk3Storage::BrushMap> brushMaps;
|
|
+ for (auto brushIterator = bm.constBegin(); brushIterator != bm.constEnd();
|
|
+ ++brushIterator) {
|
|
+ const QPalette::ColorRole role = brushIterator.key().colorRole;
|
|
+ if (brushMaps.contains(role)) {
|
|
+ brushMaps.value(role).insert(brushIterator.key(), brushIterator.value());
|
|
+ } else {
|
|
+ QGtk3Storage::BrushMap newMap;
|
|
+ newMap.insert(brushIterator.key(), brushIterator.value());
|
|
+ brushMaps.insert(role, newMap);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ QJsonObject brushArrayObject;
|
|
+ for (auto brushMapIterator = brushMaps.constBegin();
|
|
+ brushMapIterator != brushMaps.constEnd(); ++brushMapIterator) {
|
|
+
|
|
+ QJsonArray brushArray;
|
|
+ int brushIndex = 0;
|
|
+ const QGtk3Storage::BrushMap &bm = brushMapIterator.value();
|
|
+ for (auto brushIterator = bm.constBegin(); brushIterator != bm.constEnd();
|
|
+ ++brushIterator) {
|
|
+ QJsonObject brushObject;
|
|
+ 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(ceSourceType, fromSourceType(s.sourceType));
|
|
+
|
|
+ QJsonObject sourceObject;
|
|
+ switch (s.sourceType) {
|
|
+ case QGtk3Storage::SourceType::Gtk: {
|
|
+ sourceObject.insert(ceGtkWidget, fromWidgetType(s.gtk3.gtkWidgetType));
|
|
+ sourceObject.insert(ceGdkSource, fromGdkSource(s.gtk3.source));
|
|
+ sourceObject.insert(ceGtkState, fromGtkState(s.gtk3.state));
|
|
+ sourceObject.insert(ceWidth, s.gtk3.width);
|
|
+ sourceObject.insert(ceHeight, s.gtk3.height);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case QGtk3Storage::SourceType::Fixed: {
|
|
+ QJsonObject fixedObject;
|
|
+ fixedObject.insert(ceColor, s.fix.fixedBrush.color().name());
|
|
+ fixedObject.insert(ceWidth, s.fix.fixedBrush.texture().width());
|
|
+ fixedObject.insert(ceHeight, s.fix.fixedBrush.texture().height());
|
|
+ sourceObject.insert(ceBrush, fixedObject);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ 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(ceRed, s.rec.deltaRed);
|
|
+ sourceObject.insert(ceGreen, s.rec.deltaGreen);
|
|
+ sourceObject.insert(ceBlue, s.rec.deltaBlue);
|
|
+ sourceObject.insert(ceWidth, s.rec.width);
|
|
+ sourceObject.insert(ceHeight, s.rec.height);
|
|
+ sourceObject.insert(ceLighter, s.rec.lighter);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case QGtk3Storage::SourceType::Invalid:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ brushObject.insert(ceData, sourceObject);
|
|
+ brushArray.insert(brushIndex, brushObject);
|
|
+ ++brushIndex;
|
|
+ }
|
|
+ brushArrayObject.insert(fromColorRole(brushMapIterator.key()), brushArray);
|
|
+ }
|
|
+ paletteObject.insert(fromPalette(paletteIterator.key()), brushArrayObject);
|
|
+ }
|
|
+
|
|
+ QJsonObject top;
|
|
+ top.insert(cePalettes, paletteObject);
|
|
+ return paletteObject.keys().isEmpty() ? QJsonDocument() : QJsonDocument(top);
|
|
+}
|
|
+
|
|
+bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QString &fileName)
|
|
+{
|
|
+ QFile file(fileName);
|
|
+ if (!file.open(QIODevice::ReadOnly)) {
|
|
+ qCWarning(lcQGtk3Interface) << "Unable to open file:" << fileName;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ QJsonParseError err;
|
|
+ QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &err);
|
|
+ if (err.error != QJsonParseError::NoError) {
|
|
+ qWarning(lcQGtk3Interface) << "Unable to parse Json document from" << fileName
|
|
+ << err.error << err.errorString();
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (Q_LIKELY(load(map, doc))) {
|
|
+ qInfo() << "GTK mapping successfully imported from" << fileName;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ qWarning() << "File" << fileName << "could not be loaded.";
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool QGtk3Json::load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc)
|
|
+{
|
|
+#define GETSTR(obj, key)\
|
|
+ if (!obj.contains(key)) {\
|
|
+ qCDebug(lcQGtk3Interface) << key << "missing for palette" << paletteName\
|
|
+ << ", Brush" << colorRoleName;\
|
|
+ return false;\
|
|
+ }\
|
|
+ value = obj[key].toString()
|
|
+
|
|
+#define GETINT(obj, key, var) GETSTR(obj, key);\
|
|
+ if (!obj[key].isDouble()) {\
|
|
+ qCDebug(lcQGtk3Interface) << key << "type mismatch" << value\
|
|
+ << "is not an integer!"\
|
|
+ << "(Palette" << paletteName << "), Brush" << colorRoleName;\
|
|
+ return false;\
|
|
+ }\
|
|
+ const int var = obj[key].toInt()
|
|
+
|
|
+ map.clear();
|
|
+ const QJsonObject top(doc.object());
|
|
+ if (doc.isEmpty() || top.isEmpty() || !top.contains(cePalettes)) {
|
|
+ qCDebug(lcQGtk3Interface) << "Document does not contain Palettes.";
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ const QStringList &paletteList = top[cePalettes].toObject().keys();
|
|
+ for (const QString &paletteName : paletteList) {
|
|
+ bool ok;
|
|
+ const QPlatformTheme::Palette paletteType = toPalette(paletteName);
|
|
+ if (paletteType == QPlatformTheme::NPalettes) {
|
|
+ qCDebug(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";
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ QGtk3Storage::BrushMap brushes;
|
|
+ const QStringList &colorRoles = paletteObject.keys();
|
|
+ for (const QString &colorRoleName : colorRoles) {
|
|
+ const int intVal = QMetaEnum::fromType<QPalette::ColorRole>().keyToValue(colorRoleName
|
|
+ .toLatin1().constData(), &ok);
|
|
+ if (!ok) {
|
|
+ qCDebug(lcQGtk3Interface) << "Palette" << paletteName
|
|
+ << "contains invalid color role" << colorRoleName;
|
|
+ return false;
|
|
+ }
|
|
+ const QPalette::ColorRole colorRole = static_cast<QPalette::ColorRole>(intVal);
|
|
+ const QJsonArray &brushArray = paletteObject[colorRoleName].toArray();
|
|
+ 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"
|
|
+ << paletteName << ", Brush" << colorRoleName;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ QString value;
|
|
+ GETSTR(brushObject, ceSourceType);
|
|
+ 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);
|
|
+ QGtk3Storage::Source s;
|
|
+
|
|
+ if (!brushObject.contains(ceData) || !brushObject[ceData].isObject()) {
|
|
+ qCDebug(lcQGtk3Interface) << "Source specification missing for palette" << paletteName
|
|
+ << "Brush" << colorRoleName;
|
|
+ return false;
|
|
+ }
|
|
+ const QJsonObject &sourceObject = brushObject[ceData].toObject();
|
|
+
|
|
+ switch (sourceType) {
|
|
+ case QGtk3Storage::SourceType::Gtk: {
|
|
+ GETSTR(sourceObject, ceGdkSource);
|
|
+ const QGtk3Interface::QGtkColorSource gtkSource = toGdkSource(value);
|
|
+ GETSTR(sourceObject, ceGtkState);
|
|
+ const GtkStateFlags gtkState = toGtkState(value);
|
|
+ GETSTR(sourceObject, ceGtkWidget);
|
|
+ const QGtk3Interface::QGtkWidget widgetType = toWidgetType(value);
|
|
+ GETINT(sourceObject, ceHeight, height);
|
|
+ GETINT(sourceObject, ceWidth, width);
|
|
+ s = QGtk3Storage::Source(widgetType, gtkSource, gtkState, width, height);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case QGtk3Storage::SourceType::Fixed: {
|
|
+ if (!sourceObject.contains(ceBrush)) {
|
|
+ qCDebug(lcQGtk3Interface) << "Fixed brush specification missing for palette" << paletteName
|
|
+ << "Brush" << colorRoleName;
|
|
+ return false;
|
|
+ }
|
|
+ const QJsonObject &fixedSource = sourceObject[ceBrush].toObject();
|
|
+ GETINT(fixedSource, ceWidth, width);
|
|
+ GETINT(fixedSource, ceHeight, height);
|
|
+ GETSTR(fixedSource, ceColor);
|
|
+ const QColor color(value);
|
|
+ if (!color.isValid()) {
|
|
+ qCDebug(lcQGtk3Interface) << "Color" << value << "can't be parsed for:" << paletteName
|
|
+ << "Brush" << colorRoleName;
|
|
+ return false;
|
|
+ }
|
|
+ const QBrush fixedBrush = (width < 0 && height < 0)
|
|
+ ? QBrush(color, QPixmap(width, height))
|
|
+ : QBrush(color);
|
|
+ s = QGtk3Storage::Source(fixedBrush);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case QGtk3Storage::SourceType::Modified: {
|
|
+ GETSTR(sourceObject, ceColorGroup);
|
|
+ const QPalette::ColorGroup colorGroup = toColorGroup(value);
|
|
+ GETSTR(sourceObject, ceColorRole);
|
|
+ const QPalette::ColorRole colorRole = toColorRole(value);
|
|
+ GETSTR(sourceObject, ceAppearance);
|
|
+ const Qt::Appearance appearance = toAppearance(value);
|
|
+ GETINT(sourceObject, ceLighter, lighter);
|
|
+ GETINT(sourceObject, ceRed, red);
|
|
+ GETINT(sourceObject, ceBlue, blue);
|
|
+ GETINT(sourceObject, ceGreen, green);
|
|
+ s = QGtk3Storage::Source(colorGroup, colorRole, appearance,
|
|
+ lighter, red, green, blue);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case QGtk3Storage::SourceType::Invalid:
|
|
+ qCDebug(lcQGtk3Interface) << "Invalid source type for palette" << paletteName
|
|
+ << "Brush." << colorRoleName;
|
|
+ return false;
|
|
+ }
|
|
+ brushes.insert(tb, s);
|
|
+ }
|
|
+ }
|
|
+ map.insert(paletteType, brushes);
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+QT_END_NAMESPACE
|
|
+
|
|
diff --git a/src/plugins/platformthemes/gtk3/qgtk3json_p.h b/src/plugins/platformthemes/gtk3/qgtk3json_p.h
|
|
new file mode 100644
|
|
index 0000000000..b3680eb7dc
|
|
--- /dev/null
|
|
+++ b/src/plugins/platformthemes/gtk3/qgtk3json_p.h
|
|
@@ -0,0 +1,102 @@
|
|
+// Copyright (C) 2022 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 QGTK3JSON_P_H
|
|
+#define QGTK3JSON_P_H
|
|
+
|
|
+#include <QtCore/QCache>
|
|
+#include <QtCore/QJsonArray>
|
|
+#include <QtCore/QJsonDocument>
|
|
+#include <QtCore/QJsonObject>
|
|
+#include <QtCore/QMap>
|
|
+#include <QtCore/QString>
|
|
+#include <QtGui/QGuiApplication>
|
|
+#include <QtGui/QPalette>
|
|
+
|
|
+#include <qpa/qplatformtheme.h>
|
|
+#include "qgtk3interface_p.h"
|
|
+#include "qgtk3storage_p.h"
|
|
+
|
|
+#undef signals // Collides with GTK symbols
|
|
+#include <gtk/gtk.h>
|
|
+#include <gdk/gdk.h>
|
|
+#include <glib.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.
|
|
+//
|
|
+
|
|
+QT_BEGIN_NAMESPACE
|
|
+
|
|
+class QGtk3Json
|
|
+{
|
|
+ Q_GADGET
|
|
+private:
|
|
+ QGtk3Json(){};
|
|
+
|
|
+public:
|
|
+ // Convert enums to strings
|
|
+ static QLatin1String fromPalette(QPlatformTheme::Palette palette);
|
|
+ static QLatin1String fromGtkState(GtkStateFlags type);
|
|
+ static QLatin1String fromColor(const QColor &Color);
|
|
+ static QLatin1String fromColorRole(QPalette::ColorRole role);
|
|
+ static QLatin1String fromColorGroup(QPalette::ColorGroup group);
|
|
+ static QLatin1String fromGdkSource(QGtk3Interface::QGtkColorSource source);
|
|
+ static QLatin1String fromSourceType(QGtk3Storage::SourceType sourceType);
|
|
+ static QLatin1String fromWidgetType(QGtk3Interface::QGtkWidget widgetType);
|
|
+ static QLatin1String fromAppearance(Qt::Appearance app);
|
|
+
|
|
+ // Convert strings to enums
|
|
+ static QPlatformTheme::Palette toPalette(const QString &palette);
|
|
+ static GtkStateFlags toGtkState(const QString &type);
|
|
+ static QColor toColor(const QString &Color);
|
|
+ static QPalette::ColorRole toColorRole(const QString &role);
|
|
+ static QPalette::ColorGroup toColorGroup(const QString &group);
|
|
+ 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);
|
|
+
|
|
+ // Json keys
|
|
+ static constexpr QStringView cePalettes = u"QtGtk3Palettes";
|
|
+ static constexpr QStringView cePalette = u"PaletteType";
|
|
+ static constexpr QStringView ceGtkState = u"GtkStateType";
|
|
+ static constexpr QStringView ceGtkWidget = u"GtkWidgetType";
|
|
+ static constexpr QStringView ceColor = u"Color";
|
|
+ static constexpr QStringView ceColorRole = u"ColorRole";
|
|
+ static constexpr QStringView ceColorGroup = u"ColorGroup";
|
|
+ static constexpr QStringView ceGdkSource = u"GdkSource";
|
|
+ static constexpr QStringView ceSourceType = u"SourceType";
|
|
+ static constexpr QStringView ceLighter = u"Lighter";
|
|
+ static constexpr QStringView ceRed = u"DeltaRed";
|
|
+ static constexpr QStringView ceGreen = u"DeltaGreen";
|
|
+ static constexpr QStringView ceBlue = u"DeltaBlue";
|
|
+ static constexpr QStringView ceWidth = u"Width";
|
|
+ static constexpr QStringView ceHeight = u"Height";
|
|
+ static constexpr QStringView ceBrush = u"FixedBrush";
|
|
+ static constexpr QStringView ceData = u"SourceData";
|
|
+ static constexpr QStringView ceBrushes = u"Brushes";
|
|
+ static constexpr QStringView ceAppearance = u"Appearance";
|
|
+
|
|
+ // Save to a file
|
|
+ static bool save(const QGtk3Storage::PaletteMap &map, const QString &fileName,
|
|
+ QJsonDocument::JsonFormat format = QJsonDocument::Indented);
|
|
+
|
|
+ // Save to a Json document
|
|
+ static const QJsonDocument save(const QGtk3Storage::PaletteMap &map);
|
|
+
|
|
+ // Load from a file
|
|
+ static bool load(QGtk3Storage::PaletteMap &map, const QString &fileName);
|
|
+
|
|
+ // Load from a Json document
|
|
+ static bool load(QGtk3Storage::PaletteMap &map, const QJsonDocument &doc);
|
|
+};
|
|
+
|
|
+QT_END_NAMESPACE
|
|
+#endif // QGTK3JSON_P_H
|
|
diff --git a/src/plugins/platformthemes/gtk3/qgtk3storage.cpp b/src/plugins/platformthemes/gtk3/qgtk3storage.cpp
|
|
new file mode 100644
|
|
index 0000000000..0a1fa6ef97
|
|
--- /dev/null
|
|
+++ b/src/plugins/platformthemes/gtk3/qgtk3storage.cpp
|
|
@@ -0,0 +1,470 @@
|
|
+// Copyright (C) 2022 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 "qgtk3json_p.h"
|
|
+#include "qgtk3storage_p.h"
|
|
+#include <qpa/qwindowsysteminterface.h>
|
|
+
|
|
+QT_BEGIN_NAMESPACE
|
|
+
|
|
+QGtk3Storage::QGtk3Storage()
|
|
+{
|
|
+ m_interface.reset(new QGtk3Interface(this));
|
|
+ populateMap();
|
|
+}
|
|
+
|
|
+// Set a brush from a source and resolve recursions
|
|
+QBrush QGtk3Storage::brush(const Source &source, const BrushMap &map) const
|
|
+{
|
|
+ switch (source.sourceType) {
|
|
+ case SourceType::Gtk:
|
|
+ return m_interface ? QBrush(m_interface->brush(source.gtk3.gtkWidgetType,
|
|
+ source.gtk3.source, source.gtk3.state))
|
|
+ : QBrush();
|
|
+
|
|
+ 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);
|
|
+
|
|
+ if (!recSource.isValid() || (recSource.sourceType == SourceType::Modified))
|
|
+ return QBrush();
|
|
+
|
|
+ // Set brush and alter color
|
|
+ QBrush b = brush(recSource, map);
|
|
+ if (source.rec.width > 0 && source.rec.height > 0)
|
|
+ b.setTexture(QPixmap(source.rec.width, source.rec.height));
|
|
+ QColor c = b.color().lighter(source.rec.lighter);
|
|
+ c = QColor((c.red() + source.rec.deltaRed),
|
|
+ (c.green() + source.rec.deltaGreen),
|
|
+ (c.blue() + source.rec.deltaBlue));
|
|
+ b.setColor(c);
|
|
+ return b;
|
|
+ }
|
|
+
|
|
+ case SourceType::Fixed:
|
|
+ return source.fix.fixedBrush;
|
|
+
|
|
+ case SourceType::Invalid:
|
|
+ return QBrush();
|
|
+ }
|
|
+
|
|
+ // needed because of the scope after recursive
|
|
+ Q_UNREACHABLE();
|
|
+}
|
|
+
|
|
+// Find source for a recursion and take dark/light/unknown into consideration
|
|
+QGtk3Storage::Source QGtk3Storage::brush(const TargetBrush &b, const BrushMap &map) const
|
|
+{
|
|
+#define FIND(brush) if (map.contains(brush))\
|
|
+ return map.value(brush)
|
|
+
|
|
+ // 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));
|
|
+ }
|
|
+
|
|
+ // Color group All can always be found
|
|
+ if (b.colorGroup != QPalette::All)
|
|
+ return brush(TargetBrush(QPalette::All, b.colorRole, b.appearance), map);
|
|
+
|
|
+ // Brush not found
|
|
+ return Source();
|
|
+#undef FIND
|
|
+}
|
|
+
|
|
+// Create a simple standard palette
|
|
+QPalette QGtk3Storage::standardPalette()
|
|
+{
|
|
+ QColor backgroundColor(0xd4, 0xd0, 0xc8);
|
|
+ QColor lightColor(backgroundColor.lighter());
|
|
+ QColor darkColor(backgroundColor.darker());
|
|
+ const QBrush darkBrush(darkColor);
|
|
+ QColor midColor(Qt::gray);
|
|
+ QPalette palette(Qt::black, backgroundColor, lightColor, darkColor,
|
|
+ midColor, Qt::black, Qt::white);
|
|
+ palette.setBrush(QPalette::Disabled, QPalette::WindowText, darkBrush);
|
|
+ palette.setBrush(QPalette::Disabled, QPalette::Text, darkBrush);
|
|
+ palette.setBrush(QPalette::Disabled, QPalette::ButtonText, darkBrush);
|
|
+ palette.setBrush(QPalette::Disabled, QPalette::Base, QBrush(backgroundColor));
|
|
+ return palette;
|
|
+}
|
|
+
|
|
+// Deliver a palette styled according to the current GTK Theme
|
|
+const QPalette *QGtk3Storage::palette(QPlatformTheme::Palette type) const
|
|
+{
|
|
+ if (type >= QPlatformTheme::NPalettes)
|
|
+ return nullptr;
|
|
+
|
|
+ if (m_paletteCache[type].has_value()) {
|
|
+ qCDebug(lcQGtk3Interface) << "Returning palette from cache:"
|
|
+ << QGtk3Json::fromPalette(type);
|
|
+
|
|
+ return &m_paletteCache[type].value();
|
|
+ }
|
|
+
|
|
+ // Read system palette as a baseline first
|
|
+ if (!m_paletteCache[QPlatformTheme::SystemPalette].has_value() && type != QPlatformTheme::SystemPalette)
|
|
+ palette();
|
|
+
|
|
+ // Fall back to system palette for unknown types
|
|
+ if (!m_palettes.contains(type) && type != QPlatformTheme::SystemPalette) {
|
|
+ qCDebug(lcQGtk3Interface) << "Returning system palette for unknown type"
|
|
+ << QGtk3Json::fromPalette(type);
|
|
+ return palette();
|
|
+ }
|
|
+
|
|
+ BrushMap brushes = m_palettes.value(type);
|
|
+
|
|
+ // Standard palette is base for system palette. System palette is base for all others.
|
|
+ QPalette p = QPalette( type == QPlatformTheme::SystemPalette ? standardPalette()
|
|
+ : m_paletteCache[QPlatformTheme::SystemPalette].value());
|
|
+
|
|
+ qCDebug(lcQGtk3Interface) << "Creating palette:" << QGtk3Json::fromPalette(type);
|
|
+ for (auto i = brushes.begin(); i != brushes.end(); ++i) {
|
|
+ Source source = i.value();
|
|
+
|
|
+ // Brush is set if
|
|
+ // - theme and source appearance match
|
|
+ // - or either of them is unknown
|
|
+ const auto appSource = i.key().appearance;
|
|
+ const auto appTheme = appearance();
|
|
+ const bool setBrush = (appSource == appTheme) ||
|
|
+ (appSource == Qt::Appearance::Unknown) ||
|
|
+ (appTheme == Qt::Appearance::Unknown);
|
|
+
|
|
+ if (setBrush) {
|
|
+ p.setBrush(i.key().colorGroup, i.key().colorRole, brush(source, brushes));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ m_paletteCache[type].emplace(p);
|
|
+ if (type == QPlatformTheme::SystemPalette)
|
|
+ qCDebug(lcQGtk3Interface) << "System Palette defined" << themeName() << appearance() << p;
|
|
+
|
|
+ return &m_paletteCache[type].value();
|
|
+}
|
|
+
|
|
+const QFont *QGtk3Storage::font(QPlatformTheme::Font type) const
|
|
+{
|
|
+ if (m_fontCache[type].has_value())
|
|
+ return &m_fontCache[type].value();
|
|
+
|
|
+ m_fontCache[type].emplace(m_interface->font(type));
|
|
+ return &m_fontCache[type].value();
|
|
+}
|
|
+
|
|
+QPixmap QGtk3Storage::standardPixmap(QPlatformTheme::StandardPixmap standardPixmap,
|
|
+ const QSizeF &size) const
|
|
+{
|
|
+ if (m_pixmapCache.contains(standardPixmap))
|
|
+ return QPixmap::fromImage(m_pixmapCache.object(standardPixmap)->scaled(size.toSize()));
|
|
+
|
|
+ if (!m_interface)
|
|
+ return QPixmap();
|
|
+
|
|
+ QImage image = m_interface->standardPixmap(standardPixmap);
|
|
+ if (image.isNull())
|
|
+ return QPixmap();
|
|
+
|
|
+ m_pixmapCache.insert(standardPixmap, new QImage(image));
|
|
+ return QPixmap::fromImage(image.scaled(size.toSize()));
|
|
+}
|
|
+
|
|
+QIcon QGtk3Storage::fileIcon(const QFileInfo &fileInfo) const
|
|
+{
|
|
+ return m_interface ? m_interface->fileIcon(fileInfo) : QIcon();
|
|
+}
|
|
+
|
|
+void QGtk3Storage::clear()
|
|
+{
|
|
+ m_appearance = Qt::Appearance::Unknown;
|
|
+ m_palettes.clear();
|
|
+ for (auto &cache : m_paletteCache)
|
|
+ cache.reset();
|
|
+
|
|
+ for (auto &cache : m_fontCache)
|
|
+ cache.reset();
|
|
+}
|
|
+
|
|
+void QGtk3Storage::handleThemeChange()
|
|
+{
|
|
+ clear();
|
|
+ populateMap();
|
|
+ QWindowSystemInterface::handleThemeChange(nullptr);
|
|
+}
|
|
+
|
|
+void QGtk3Storage::populateMap()
|
|
+{
|
|
+ static QString m_themeName;
|
|
+
|
|
+ // Distiguish initialization, theme change or call without theme change
|
|
+ const QString newThemeName = themeName();
|
|
+ if (m_themeName == newThemeName)
|
|
+ return;
|
|
+
|
|
+ clear();
|
|
+
|
|
+ // Derive appearance from theme name
|
|
+ m_appearance = newThemeName.contains("dark", Qt::CaseInsensitive)
|
|
+ ? Qt::Appearance::Dark : Qt::Appearance::Light;
|
|
+
|
|
+ if (m_themeName.isEmpty()) {
|
|
+ qCDebug(lcQGtk3Interface) << "GTK theme initialized:" << newThemeName << m_appearance;
|
|
+ } else {
|
|
+ qCDebug(lcQGtk3Interface) << "GTK theme changed to:" << newThemeName << m_appearance;
|
|
+ }
|
|
+ m_themeName = newThemeName;
|
|
+
|
|
+ // create standard mapping or load from Json file?
|
|
+ const QString jsonInput = qEnvironmentVariable("QT_GUI_GTK_JSON");
|
|
+ if (!jsonInput.isEmpty()) {
|
|
+ if (load(jsonInput)) {
|
|
+ return;
|
|
+ } else {
|
|
+ qWarning() << "Falling back to standard GTK mapping.";
|
|
+ }
|
|
+ }
|
|
+
|
|
+ createMapping();
|
|
+
|
|
+ const QString jsonOutput = qEnvironmentVariable("QT_GUI_GTK_JSON_SAVE");
|
|
+ if (!jsonOutput.isEmpty() && !save(jsonOutput))
|
|
+ qWarning() << "File" << jsonOutput << "could not be saved.\n";
|
|
+}
|
|
+
|
|
+const QGtk3Storage::PaletteMap QGtk3Storage::savePalettes() const
|
|
+{
|
|
+ const QString hard = qEnvironmentVariable("QT_GUI_GTK_JSON_HARDCODED");
|
|
+ if (!hard.contains("true", Qt::CaseInsensitive))
|
|
+ return m_palettes;
|
|
+
|
|
+ // Json output is supposed to be readable without GTK connection
|
|
+ // convert palette map into hard coded brushes
|
|
+ PaletteMap map = m_palettes;
|
|
+ for (auto paletteIterator = map.begin(); paletteIterator != map.end();
|
|
+ ++paletteIterator) {
|
|
+ QGtk3Storage::BrushMap &bm = paletteIterator.value();
|
|
+ for (auto brushIterator = bm.begin(); brushIterator != bm.end();
|
|
+ ++brushIterator) {
|
|
+ QGtk3Storage::Source &s = brushIterator.value();
|
|
+ switch (s.sourceType) {
|
|
+
|
|
+ // Read the brush and convert it into a fixed brush
|
|
+ case SourceType::Gtk: {
|
|
+ const QBrush fixedBrush = brush(s, bm);
|
|
+ s.fix.fixedBrush = fixedBrush;
|
|
+ s.sourceType = SourceType::Fixed;
|
|
+ }
|
|
+ break;
|
|
+ case SourceType::Fixed:
|
|
+ case SourceType::Modified:
|
|
+ case SourceType::Invalid:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return map;
|
|
+}
|
|
+
|
|
+bool QGtk3Storage::save(const QString &filename, QJsonDocument::JsonFormat f) const
|
|
+{
|
|
+ return QGtk3Json::save(savePalettes(), filename, f);
|
|
+}
|
|
+
|
|
+QJsonDocument QGtk3Storage::save() const
|
|
+{
|
|
+ return QGtk3Json::save(savePalettes());
|
|
+}
|
|
+
|
|
+bool QGtk3Storage::load(const QString &filename)
|
|
+{
|
|
+ return QGtk3Json::load(m_palettes, filename);
|
|
+}
|
|
+
|
|
+void QGtk3Storage::createMapping()
|
|
+{
|
|
+ // Hard code standard mapping
|
|
+ BrushMap map;
|
|
+ Source source;
|
|
+
|
|
+ // Define a GTK source
|
|
+#define GTK(wtype, colorSource, state)\
|
|
+ source = Source(QGtk3Interface::QGtkWidget::gtk_ ##wtype,\
|
|
+ QGtk3Interface::QGtkColorSource::colorSource, GTK_STATE_FLAG_ ##state)
|
|
+
|
|
+ // Define a modified source
|
|
+#define LIGHTER(group, role, lighter)\
|
|
+ source = Source(QPalette::group, QPalette::role,\
|
|
+ Qt::Appearance::Unknown, lighter)
|
|
+#define MODIFY(group, role, red, green, blue)\
|
|
+ source = Source(QPalette::group, QPalette::role,\
|
|
+ Qt::Appearance::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
|
|
+#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);
|
|
+#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
|
|
+#define SAVE(palette) m_palettes.insert(QPlatformTheme::palette, map)
|
|
+ // Clear brushes to start next palette
|
|
+#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.
|
|
+ */
|
|
+
|
|
+
|
|
+ // 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;
|
|
+
|
|
+#undef GTK
|
|
+#undef REC
|
|
+#undef FIX
|
|
+#undef ADD
|
|
+#undef ADD_2
|
|
+#undef ADD_3
|
|
+#undef ADD_X
|
|
+#undef SAVE
|
|
+#undef LOAD
|
|
+}
|
|
+
|
|
+QT_END_NAMESPACE
|
|
diff --git a/src/plugins/platformthemes/gtk3/qgtk3storage_p.h b/src/plugins/platformthemes/gtk3/qgtk3storage_p.h
|
|
new file mode 100644
|
|
index 0000000000..57f6aeea96
|
|
--- /dev/null
|
|
+++ b/src/plugins/platformthemes/gtk3/qgtk3storage_p.h
|
|
@@ -0,0 +1,234 @@
|
|
+// Copyright (C) 2022 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 QGTK3STORAGE_P_H
|
|
+#define QGTK3STORAGE_P_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 "qgtk3interface_p.h"
|
|
+
|
|
+#include <QtCore/QJsonDocument>
|
|
+#include <QtCore/QCache>
|
|
+#include <QtCore/QString>
|
|
+#include <QtGui/QGuiApplication>
|
|
+#include <QtGui/QPalette>
|
|
+
|
|
+#include <qpa/qplatformtheme.h>
|
|
+#include <private/qflatmap_p.h>
|
|
+
|
|
+QT_BEGIN_NAMESPACE
|
|
+class QGtk3Storage
|
|
+{
|
|
+ Q_GADGET
|
|
+public:
|
|
+ QGtk3Storage();
|
|
+
|
|
+ enum class SourceType {
|
|
+ Gtk,
|
|
+ Fixed,
|
|
+ Modified,
|
|
+ Invalid
|
|
+ };
|
|
+ Q_ENUM(SourceType)
|
|
+
|
|
+ // Standard GTK source: Populate a brush from GTK
|
|
+ struct Gtk3Source {
|
|
+ QGtk3Interface::QGtkWidget gtkWidgetType;
|
|
+ QGtk3Interface::QGtkColorSource source;
|
|
+ GtkStateFlags state;
|
|
+ int width = -1;
|
|
+ int height = -1;
|
|
+ QDebug operator<<(QDebug dbg)
|
|
+ {
|
|
+ return dbg << "QGtkStorage::Gtk3Source(gtkwidgetType=" << gtkWidgetType << ", source="
|
|
+ << source << ", state=" << state << ", width=" << width << ", height="
|
|
+ << height << ")";
|
|
+ }
|
|
+ };
|
|
+
|
|
+ // Recursive source: Populate a brush by altering another source
|
|
+ struct RecursiveSource {
|
|
+ QPalette::ColorGroup colorGroup;
|
|
+ QPalette::ColorRole colorRole;
|
|
+ Qt::Appearance appearance;
|
|
+ int lighter = 100;
|
|
+ int deltaRed = 0;
|
|
+ int deltaGreen = 0;
|
|
+ int deltaBlue = 0;
|
|
+ int width = -1;
|
|
+ int height = -1;
|
|
+ QDebug operator<<(QDebug dbg)
|
|
+ {
|
|
+ return dbg << "QGtkStorage::RecursiceSource(colorGroup=" << colorGroup << ", colorRole="
|
|
+ << colorRole << ", appearance=" << appearance << ", lighter=" << lighter
|
|
+ << ", deltaRed="<< deltaRed << "deltaBlue =" << deltaBlue << "deltaGreen="
|
|
+ << deltaGreen << ", width=" << width << ", height=" << height << ")";
|
|
+ }
|
|
+ };
|
|
+
|
|
+ // Fixed source: Populate a brush with fixed values rather than reading GTK
|
|
+ struct FixedSource {
|
|
+ QBrush fixedBrush;
|
|
+ QDebug operator<<(QDebug dbg)
|
|
+ {
|
|
+ return dbg << "QGtkStorage::FixedSource(" << fixedBrush << ")";
|
|
+ }
|
|
+ };
|
|
+
|
|
+ // Data source for brushes
|
|
+ struct Source {
|
|
+ SourceType sourceType = SourceType::Invalid;
|
|
+ Gtk3Source gtk3;
|
|
+ RecursiveSource rec;
|
|
+ FixedSource fix;
|
|
+
|
|
+ // GTK constructor
|
|
+ Source(QGtk3Interface::QGtkWidget wtype, QGtk3Interface::QGtkColorSource csource,
|
|
+ GtkStateFlags cstate, int bwidth = -1, int bheight = -1) : sourceType(SourceType::Gtk)
|
|
+ {
|
|
+ gtk3.gtkWidgetType = wtype;
|
|
+ gtk3.source = csource;
|
|
+ gtk3.state = cstate;
|
|
+ gtk3.width = bwidth;
|
|
+ gtk3.height = bheight;
|
|
+ }
|
|
+
|
|
+ // Recursive constructor for darker/lighter colors
|
|
+ Source(QPalette::ColorGroup group, QPalette::ColorRole role,
|
|
+ Qt::Appearance app, int p_lighter = 100)
|
|
+ : sourceType(SourceType::Modified)
|
|
+ {
|
|
+ rec.colorGroup = group;
|
|
+ rec.colorRole = role;
|
|
+ rec.appearance = app;
|
|
+ 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)
|
|
+ : sourceType(SourceType::Modified)
|
|
+ {
|
|
+ rec.colorGroup = group;
|
|
+ rec.colorRole = role;
|
|
+ rec.appearance = app;
|
|
+ rec.deltaRed = p_red;
|
|
+ rec.deltaGreen = p_green;
|
|
+ rec.deltaBlue = p_blue;
|
|
+ }
|
|
+
|
|
+ // Recursive constructor for all: color modification and darker/lighter
|
|
+ Source(QPalette::ColorGroup group, QPalette::ColorRole role,
|
|
+ Qt::Appearance app, 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.lighter = p_lighter;
|
|
+ rec.deltaRed = p_red;
|
|
+ rec.deltaGreen = p_green;
|
|
+ rec.deltaBlue = p_blue;
|
|
+ }
|
|
+
|
|
+ // Fixed Source constructor
|
|
+ Source(const QBrush &brush) : sourceType(SourceType::Fixed)
|
|
+ {
|
|
+ fix.fixedBrush = brush;
|
|
+ };
|
|
+
|
|
+ // Invalid constructor and getter
|
|
+ Source() : sourceType(SourceType::Invalid) {};
|
|
+ bool isValid() const { return sourceType != SourceType::Invalid; }
|
|
+
|
|
+ // Debug
|
|
+ QDebug operator<<(QDebug dbg)
|
|
+ {
|
|
+ return dbg << "QGtk3Storage::Source(sourceType=" << sourceType << ")";
|
|
+ }
|
|
+ };
|
|
+
|
|
+ // Struct with key attributes to identify a brush: color group, color role and appearance
|
|
+ struct TargetBrush {
|
|
+ QPalette::ColorGroup colorGroup;
|
|
+ QPalette::ColorRole colorRole;
|
|
+ Qt::Appearance appearance;
|
|
+
|
|
+ // Generic constructor
|
|
+ TargetBrush(QPalette::ColorGroup group, QPalette::ColorRole role,
|
|
+ Qt::Appearance app = Qt::Appearance::Unknown) :
|
|
+ colorGroup(group), colorRole(role), appearance(app) {};
|
|
+
|
|
+ // 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) {};
|
|
+
|
|
+ // 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);
|
|
+ }
|
|
+ };
|
|
+
|
|
+ // Mapping a palette's brushes to their GTK sources
|
|
+ typedef QFlatMap<TargetBrush, Source> BrushMap;
|
|
+
|
|
+ // Storage of palettes and their GTK sources
|
|
+ typedef QFlatMap<QPlatformTheme::Palette, BrushMap> PaletteMap;
|
|
+
|
|
+ // 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; };
|
|
+ static QPalette standardPalette();
|
|
+ const QString themeName() const { return m_interface ? m_interface->themeName() : QString(); };
|
|
+ const QFont *font(QPlatformTheme::Font type) const;
|
|
+ QIcon fileIcon(const QFileInfo &fileInfo) const;
|
|
+
|
|
+ // Initialization
|
|
+ void populateMap();
|
|
+ void handleThemeChange();
|
|
+
|
|
+private:
|
|
+ // Storage for palettes and their brushes
|
|
+ PaletteMap m_palettes;
|
|
+
|
|
+ std::unique_ptr<QGtk3Interface> m_interface;
|
|
+
|
|
+
|
|
+ Qt::Appearance m_appearance = Qt::Appearance::Unknown;
|
|
+
|
|
+ // Caches for Pixmaps, fonts and palettes
|
|
+ mutable QCache<QPlatformTheme::StandardPixmap, QImage> m_pixmapCache;
|
|
+ mutable std::array<std::optional<QPalette>, QPlatformTheme::Palette::NPalettes> m_paletteCache;
|
|
+ mutable std::array<std::optional<QFont>, QPlatformTheme::NFonts> m_fontCache;
|
|
+
|
|
+ // Search brush with a given GTK3 source
|
|
+ QBrush brush(const Source &source, const BrushMap &map) const;
|
|
+
|
|
+ // Get GTK3 source for a target brush
|
|
+ Source brush (const TargetBrush &brush, const BrushMap &map) const;
|
|
+
|
|
+ // clear cache, palettes and appearance
|
|
+ void clear();
|
|
+
|
|
+ // Data creation, import & export
|
|
+ void createMapping ();
|
|
+ const PaletteMap savePalettes() const;
|
|
+ bool save(const QString &filename, const QJsonDocument::JsonFormat f = QJsonDocument::Indented) const;
|
|
+ QJsonDocument save() const;
|
|
+ bool load(const QString &filename);
|
|
+};
|
|
+
|
|
+QT_END_NAMESPACE
|
|
+#endif // QGTK3STORAGE_H
|
|
diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
|
|
index aceacda4b8..ee6e0f3dd9 100644
|
|
--- a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
|
|
+++ b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
|
|
@@ -135,6 +135,8 @@ QGtk3Theme::QGtk3Theme()
|
|
qputenv("XCURSOR_THEME", cursorTheme.toUtf8());
|
|
}
|
|
}
|
|
+
|
|
+ m_storage.reset(new QGtk3Storage);
|
|
}
|
|
|
|
static inline QVariant gtkGetLongPressTime()
|
|
@@ -235,4 +237,25 @@ bool QGtk3Theme::useNativeFileDialog()
|
|
return gtk_check_version(3, 15, 5) == nullptr;
|
|
}
|
|
|
|
+const QPalette *QGtk3Theme::palette(Palette type) const
|
|
+{
|
|
+ return m_storage ? m_storage->palette(type) : QPlatformTheme::palette(type);
|
|
+}
|
|
+
|
|
+QPixmap QGtk3Theme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
|
|
+{
|
|
+ return m_storage ? m_storage->standardPixmap(sp, size) : QPlatformTheme::standardPixmap(sp, size);
|
|
+}
|
|
+
|
|
+const QFont *QGtk3Theme::font(Font type) const
|
|
+{
|
|
+ return m_storage ? m_storage->font(type) : QGnomeTheme::font(type);
|
|
+}
|
|
+
|
|
+QIcon QGtk3Theme::fileIcon(const QFileInfo &fileInfo,
|
|
+ QPlatformTheme::IconOptions iconOptions) const
|
|
+{
|
|
+ return m_storage ? m_storage->fileIcon(fileInfo) : QGnomeTheme::fileIcon(fileInfo, iconOptions);
|
|
+}
|
|
+
|
|
QT_END_NAMESPACE
|
|
diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.h b/src/plugins/platformthemes/gtk3/qgtk3theme.h
|
|
index 54296d2ff1..99e896c020 100644
|
|
--- a/src/plugins/platformthemes/gtk3/qgtk3theme.h
|
|
+++ b/src/plugins/platformthemes/gtk3/qgtk3theme.h
|
|
@@ -41,6 +41,7 @@
|
|
#define QGTK3THEME_H
|
|
|
|
#include <private/qgenericunixthemes_p.h>
|
|
+#include "qgtk3storage_p.h"
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
@@ -58,9 +59,16 @@ public:
|
|
QPlatformMenu* createPlatformMenu() const override;
|
|
QPlatformMenuItem* createPlatformMenuItem() const override;
|
|
|
|
+ const QPalette *palette(Palette type = SystemPalette) const override;
|
|
+ const QFont *font(Font type = SystemFont) const override;
|
|
+ QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;
|
|
+ QIcon fileIcon(const QFileInfo &fileInfo,
|
|
+ QPlatformTheme::IconOptions iconOptions = { }) const override;
|
|
+
|
|
static const char *name;
|
|
private:
|
|
static bool useNativeFileDialog();
|
|
+ std::unique_ptr<QGtk3Storage> m_storage;
|
|
};
|
|
|
|
QT_END_NAMESPACE
|
|
--
|
|
2.41.0
|
|
|