1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#ifndef QQUICKPALETTEPROVIDERPRIVATEBASE_H
4#define QQUICKPALETTEPROVIDERPRIVATEBASE_H
5
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include <QtQuick/private/qquickpalette_p.h>
18#include <QtQuick/private/qquickabstractpaletteprovider_p.h>
19#include <QtGui/qwindow.h>
20#include <QtQml/private/qlazilyallocated_p.h>
21
22QT_BEGIN_NAMESPACE
23
24class QWindow;
25class QQuickWindow;
26class QQuickWindowPrivate;
27class QQuickItem;
28class QQuickItemPrivate;
29class QQuickPopup;
30class QQuickPopupPrivate;
31
32/*!
33 \internal
34
35 Implements all required operations with palette.
36
37 I -- is interface class (e.g. QQuickItem).
38 Impl -- is implementation class (e.g. QQuickItemPrivate).
39
40 To use this class you need to inherit implementation class from it.
41 */
42template <class I, class Impl>
43class QQuickPaletteProviderPrivateBase : public QQuickAbstractPaletteProvider
44{
45 static_assert(std::is_base_of<QObject, I>{}, "The interface class must inherit QObject");
46
47public:
48 virtual ~QQuickPaletteProviderPrivateBase() = default;
49
50 /*!
51 \internal
52
53 Get current palette.
54
55 \note Palette might be lazily allocated. Signal \p paletteCreated()
56 will be emitted by an object of interface class in this case.
57
58 \note This function doesn't ask an object of interface class to emit
59 paletteChanged() signal in order to avoid problems with
60 property bindigns.
61 */
62 virtual QQuickPalette *palette() const;
63
64 /*!
65 \internal
66
67 Set new palette. Doesn't transfer ownership.
68 */
69 virtual void setPalette(QQuickPalette *p);
70
71 /*!
72 \internal
73
74 Reset palette to the default one.
75 */
76 virtual void resetPalette();
77
78 /*!
79 \internal
80
81 Check if everything is internally allocated and palette exists.
82
83 Use before call \p palette() to avoid unnecessary allocations.
84 */
85 virtual bool providesPalette() const;
86
87 /*!
88 \internal
89
90 The default palette for this component.
91 */
92 QPalette defaultPalette() const override;
93
94 /*!
95 \internal
96
97 The parent palette for this component. Can be null.
98 */
99 QPalette parentPalette(const QPalette &fallbackPalette) const override;
100
101 /*!
102 \internal
103
104 Inherit from \p parentPalette. This function is also called when
105 either parent or window of this item is changed.
106 */
107 void inheritPalette(const QPalette &parentPalette);
108
109 /*!
110 \internal
111
112 Updates children palettes. The default implementation invokes
113 inheritPalette for all visual children.
114
115 This function is also called when palette is changed
116 (signal changed() is emitted).
117 */
118 virtual void updateChildrenPalettes(const QPalette &parentPalette);
119
120protected:
121 void setCurrentColorGroup();
122
123private:
124 using PalettePtr = std::unique_ptr<QQuickPalette>;
125 using Self = QQuickPaletteProviderPrivateBase<I, Impl>;
126
127 void registerPalette(PalettePtr palette);
128
129 bool isValidPalette(const QQuickPalette *palette) const;
130
131 QQuickPalette *windowPalette() const;
132
133
134 void connectItem();
135
136 const I *itemWithPalette() const;
137 I *itemWithPalette();
138
139 QQuickPalette *paletteData() const;
140
141 QPalette toQPalette() const;
142
143private:
144 PalettePtr m_palette;
145};
146
147template<class I, class Impl>
148QQuickPalette *QQuickPaletteProviderPrivateBase<I, Impl>::palette() const
149{
150 if (!providesPalette()) {
151 // It's required to create a new palette without parent,
152 // because this method can be called from the rendering thread
153 const_cast<Self*>(this)->registerPalette(std::make_unique<QQuickPalette>());
154 Q_EMIT const_cast<Self*>(this)->itemWithPalette()->paletteCreated();
155 }
156
157 return paletteData();
158}
159
160template<class I, class Impl>
161bool QQuickPaletteProviderPrivateBase<I, Impl>::isValidPalette(const QQuickPalette *palette) const
162{
163 if (!palette) {
164 qWarning(msg: "Palette cannot be null.");
165 return false;
166 }
167
168 if (providesPalette() && paletteData() == palette) {
169 qWarning(msg: "Self assignment makes no sense.");
170 return false;
171 }
172
173 return true;
174}
175
176template<class I, class Impl>
177void QQuickPaletteProviderPrivateBase<I, Impl>::setPalette(QQuickPalette *p)
178{
179 if (isValidPalette(palette: p)) {
180 palette()->fromQPalette(p->toQPalette());
181 }
182}
183
184template<class I, class Impl>
185void QQuickPaletteProviderPrivateBase<I, Impl>::resetPalette()
186{
187 paletteData()->reset();
188}
189
190template<class I, class Impl>
191bool QQuickPaletteProviderPrivateBase<I, Impl>::providesPalette() const
192{
193 return !!m_palette;
194}
195
196template<class I, class Impl>
197QPalette QQuickPaletteProviderPrivateBase<I, Impl>::defaultPalette() const
198{
199 return QPalette();
200}
201
202template <class Window>
203inline constexpr bool isRootWindow() { return std::is_base_of_v<QWindow, Window>; }
204
205template<class I, class Impl>
206void QQuickPaletteProviderPrivateBase<I, Impl>::registerPalette(PalettePtr palette)
207{
208 if constexpr (!isRootWindow<I>()) {
209 // Connect item only once, before initial data allocation
210 if (!providesPalette()) {
211 connectItem();
212 }
213 }
214
215 m_palette = std::move(palette);
216 m_palette->setPaletteProvider(this);
217 m_palette->inheritPalette(palette: parentPalette(fallbackPalette: defaultPalette()));
218
219 setCurrentColorGroup();
220
221 // In order to avoid extra noise, we should connect
222 // the following signals only after everything is already setup
223 I::connect(paletteData(), &QQuickPalette::changed, itemWithPalette(), &I::paletteChanged);
224 I::connect(paletteData(), &QQuickPalette::changed, [this]{ updateChildrenPalettes(parentPalette: toQPalette()); });
225}
226
227template<class T> struct dependent_false : std::false_type {};
228template<class Impl, class I> decltype(auto) getPrivateImpl(I &t) { return Impl::get(&t); }
229
230template <class T>
231decltype(auto) getPrivate(T &item)
232{
233 if constexpr (std::is_base_of_v<T, QQuickWindow>) {
234 return getPrivateImpl<QQuickWindowPrivate>(item);
235 } else if constexpr (std::is_base_of_v<T, QQuickItem>) {
236 return getPrivateImpl<QQuickItemPrivate>(item);
237 } else {
238 static_assert (dependent_false<T>::value, "Extend please.");
239 }
240}
241
242template<class I, class Impl>
243QQuickPalette *QQuickPaletteProviderPrivateBase<I, Impl>::windowPalette() const
244{
245 if constexpr (!isRootWindow<I>()) {
246 if (auto window = itemWithPalette()->window()) {
247 if (getPrivate(*window)->providesPalette()) {
248 return getPrivate(*window)->palette();
249 }
250 }
251 }
252
253 return nullptr;
254}
255
256template<class I, class Impl>
257QPalette QQuickPaletteProviderPrivateBase<I, Impl>::parentPalette(const QPalette &fallbackPalette) const
258{
259 if constexpr (!isRootWindow<I>()) {
260 // Popups should always inherit from their window, even child popups: QTBUG-115707.
261 if (!std::is_base_of_v<QQuickPopup, I>) {
262 for (auto parentItem = itemWithPalette()->parentItem(); parentItem;
263 parentItem = parentItem->parentItem()) {
264
265 // Don't allocate a new palette here. Use only if it's already pre allocated
266 if (parentItem && getPrivate(*parentItem)->providesPalette()) {
267 return getPrivate(*parentItem)->palette()->toQPalette();
268 }
269 }
270 }
271
272 if (auto wp = windowPalette()) {
273 return wp->toQPalette();
274 }
275 }
276
277 return fallbackPalette;
278}
279
280template<class I>
281const QQuickItem* rootItem(const I &item)
282{
283 if constexpr (isRootWindow<I>()) {
284 return item.contentItem();
285 } else if constexpr (std::is_base_of_v<QQuickPopup, I>) {
286 return nullptr;
287 } else {
288 return &item;
289 }
290}
291
292template<class I, class Impl>
293void QQuickPaletteProviderPrivateBase<I, Impl>::inheritPalette(const QPalette &parentPalette)
294{
295 if (providesPalette()) {
296 // If palette is changed, then this function will be invoked
297 // for all children because of connection with signal changed()
298 palette()->inheritPalette(parentPalette);
299 } else {
300 // Otherwise, just propagate parent palette to all children
301 updateChildrenPalettes(parentPalette);
302 }
303}
304
305template<class I, class Impl>
306void QQuickPaletteProviderPrivateBase<I, Impl>::setCurrentColorGroup()
307{
308 if constexpr (!isRootWindow<I>()) {
309 if (providesPalette()) {
310 const bool enabled = itemWithPalette()->isEnabled();
311 const auto window = itemWithPalette()->window();
312 const bool active = window ? window->isActive() : true;
313 palette()->setCurrentGroup(enabled ? (active ? QPalette::Active : QPalette::Inactive)
314 : QPalette::Disabled);
315 }
316 }
317}
318
319template<class I, class Impl>
320void QQuickPaletteProviderPrivateBase<I, Impl>::updateChildrenPalettes(const QPalette &parentPalette)
321{
322 if constexpr (std::is_same_v<QQuickWindow, I> && std::is_same_v<QQuickWindowPrivate, Impl>) {
323 /* QQuickWindowPrivate instantiates this template, but does not include QQuickItemPrivate
324 * This causes an error with the QQuickItemPrivate::inheritPalette call below on MSVC in
325 * static builds, as QQuickItemPrivate is incomplete. To work around this situation, we do
326 * nothing in this instantiation of updateChildrenPalettes and instead add an override in
327 * QQuickWindowPrivate, which does the correct thing.
328 */
329 Q_UNREACHABLE_RETURN(); // You are not supposed to call this function
330 } else {
331 if (auto root = rootItem(*itemWithPalette())) {
332 for (auto &&child : root->childItems()) {
333 if (Q_LIKELY(child)) {
334 getPrivate(*child)->inheritPalette(parentPalette);
335 }
336 }
337 }
338 }
339}
340
341template<class I, class Impl>
342void QQuickPaletteProviderPrivateBase<I, Impl>::connectItem()
343{
344 Q_ASSERT(itemWithPalette());
345
346 if constexpr (!isRootWindow<I>()) {
347 // Item with palette has the same lifetime as its implementation that inherits this class
348 I::connect(itemWithPalette(), &I::parentChanged , [this]() { inheritPalette(parentPalette: parentPalette(fallbackPalette: defaultPalette())); });
349 I::connect(itemWithPalette(), &I::windowChanged , [this]() { inheritPalette(parentPalette: parentPalette(fallbackPalette: defaultPalette())); });
350 I::connect(itemWithPalette(), &I::enabledChanged, [this]() { setCurrentColorGroup(); });
351 }
352}
353
354template<class I, class Impl>
355const I *QQuickPaletteProviderPrivateBase<I, Impl>::itemWithPalette() const
356{
357 static_assert(std::is_base_of<QObjectData, Impl>{},
358 "The Impl class must inherit QObjectData");
359
360 return static_cast<const I*>(static_cast<const Impl*>(this)->q_ptr);
361}
362
363template<class I, class Impl>
364I *QQuickPaletteProviderPrivateBase<I, Impl>::itemWithPalette()
365{
366 return const_cast<I*>(const_cast<const Self*>(this)->itemWithPalette());
367}
368
369template<class I, class Impl>
370QQuickPalette *QQuickPaletteProviderPrivateBase<I, Impl>::paletteData() const
371{
372 Q_ASSERT(m_palette); return m_palette.get();
373}
374
375template<class I, class Impl>
376QPalette QQuickPaletteProviderPrivateBase<I, Impl>::toQPalette() const
377{
378 return palette()->toQPalette();
379}
380
381QT_END_NAMESPACE
382
383#endif // QQUICKPALETTEPROVIDERPRIVATEBASE_H
384

source code of qtdeclarative/src/quick/items/qquickpaletteproviderprivatebase_p.h