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
4#ifndef QQUICKSTYLEITEM_H
5#define QQUICKSTYLEITEM_H
6
7#include <QtCore/qdebug.h>
8#include <QtQml/qqml.h>
9#include <QtQml/qqmlinfo.h>
10#include <QtQuick/private/qquickitem_p.h>
11#include <QtQuickTemplates2/private/qquickcontrol_p.h>
12
13#include "qquicknativestyle.h"
14#include "qquickstyle.h"
15#include "qquickstyleoption.h"
16
17// Work-around for now, to avoid creator getting confused
18// about missing macros. Should eventually be defined
19// in qt declarative somewhere I assume.
20#ifndef QML_NAMED_ELEMENT
21#define QML_NAMED_ELEMENT(NAME)
22#define QML_UNCREATABLE(NAME)
23#endif
24
25#ifdef QT_DEBUG
26#define qqc2Debug() if (m_debugFlags.testFlag(Debug)) qDebug() << __FUNCTION__ << ":"
27#define qqc2Info() if (m_debugFlags.testFlag(Info)) qDebug() << __FUNCTION__ << ":"
28#define qqc2InfoHeading(HEADING) if (m_debugFlags.testFlag(Info)) qDebug() << "--------" << HEADING << "--------"
29#else
30#define qqc2Debug() if (false) qDebug()
31#define qqc2Info() if (false) qDebug()
32#define qqc2InfoHeading(HEADING) if (false) qDebug()
33#endif
34
35QT_BEGIN_NAMESPACE
36
37using namespace QQC2;
38
39class QQuickStyleMargins
40{
41 Q_GADGET
42
43 Q_PROPERTY(int left READ left())
44 Q_PROPERTY(int top READ top())
45 Q_PROPERTY(int right READ right())
46 Q_PROPERTY(int bottom READ bottom())
47
48 QML_NAMED_ELEMENT(stylemargins)
49 QML_UNCREATABLE("")
50
51public:
52 QQuickStyleMargins() {}
53 QQuickStyleMargins(const QQuickStyleMargins &other) : m_margins(other.m_margins) {}
54 QQuickStyleMargins(const QMargins &margins) : m_margins(margins) {}
55 QQuickStyleMargins(const QRect &outer, const QRect &inner)
56 {
57 const int left = inner.left() - outer.left();
58 const int top = inner.top() - outer.top();
59 const int right = outer.right() - inner.right();
60 const int bottom = outer.bottom() - inner.bottom();
61 m_margins = QMargins(left, top, right, bottom);
62 }
63
64 inline void operator=(const QQuickStyleMargins &other) { m_margins = other.m_margins; }
65 inline bool operator==(const QQuickStyleMargins &other) const { return other.m_margins == m_margins; }
66 inline bool operator!=(const QQuickStyleMargins &other) const { return other.m_margins != m_margins; }
67
68 inline int left() const { return m_margins.left(); }
69 inline int right() const { return m_margins.right(); }
70 inline int top() const { return m_margins.top(); }
71 inline int bottom() const { return m_margins.bottom(); }
72
73 QMargins m_margins;
74};
75
76QDebug operator<<(QDebug debug, const QQuickStyleMargins &padding);
77
78struct StyleItemGeometry
79{
80 /*
81 A QQuickStyleItem is responsible for drawing a control, or a part of it.
82
83 'minimumSize' should be the minimum possible size that the item can
84 have _without_ taking content size into consideration (and still render
85 correctly). This will also be the size of the image that the item is drawn
86 to, unless QQuickStyleItem::useNinePatchImage is set to false. In that
87 case, the size of the image will be set to the size of the item instead
88 (which is set from QML, and will typically be the same as the size of the control).
89 The default way to calculate minimumSize is to call style()->sizeFromContents()
90 with an empty content size. This is not always well supported by the legacy QStyle
91 implementation, which means that you might e.g get an empty size in return.
92 For those cases, the correct solution is to go into the specific platform style
93 and change it so that it returns a valid size also for this special case.
94
95 'implicitSize' should reflect the preferred size of the item, taking the
96 given content size (as set from QML) into account. But not all controls
97 have contents (slider), and for many controls, the content/label is instead
98 placed outside the item/background image (radiobutton). In both cases, the
99 size of the item will not include the content size, and implicitSize can
100 usually be set equal to minimumSize instead.
101
102 'contentRect' should be the free space where the contents can be placed. Note that
103 this rect doesn't need to have the same size as the contentSize provided as input
104 to the style item. Instead, QStyle can typically calculate a rect that is bigger, to
105 e.g center the contents inside the control.
106
107 'layoutRect' can be set to shift the position of the whole control so
108 that aligns correctly with the other controls. This is important for
109 controls that draws e.g shadows or focus rings. Such adornments should
110 be painted, but not be included when aligning the controls.
111 */
112
113 QSize minimumSize;
114 QSize implicitSize;
115 QRect contentRect;
116 QRect layoutRect; // If invalid, there are no layout margins!
117 QMargins ninePatchMargins;
118 qreal focusFrameRadius;
119};
120
121QDebug operator<<(QDebug debug, const StyleItemGeometry &cg);
122
123class QQuickStyleItem : public QQuickItem
124{
125 Q_OBJECT
126
127 // Input
128 Q_PROPERTY(QQuickItem *control MEMBER m_control NOTIFY controlChanged)
129 Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth)
130 Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight)
131 Q_PROPERTY(bool useNinePatchImage MEMBER m_useNinePatchImage)
132 Q_PROPERTY(OverrideState overrideState MEMBER m_overrideState)
133
134 // Output
135 Q_PROPERTY(QQuickStyleMargins contentPadding READ contentPadding() NOTIFY contentPaddingChanged)
136 Q_PROPERTY(QQuickStyleMargins layoutMargins READ layoutMargins() NOTIFY layoutMarginsChanged)
137 Q_PROPERTY(QSize minimumSize READ minimumSize() NOTIFY minimumSizeChanged)
138 Q_PROPERTY(int transitionDuration MEMBER m_transitionDuration CONSTANT)
139
140 QML_NAMED_ELEMENT(StyleItem)
141 QML_UNCREATABLE("StyleItem is an abstract base class.")
142
143public:
144 enum DirtyFlag {
145 Nothing = 0,
146 Geometry,
147 Image,
148 Everything = 255
149 };
150 Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag)
151
152 enum OverrideState {
153 None = 0,
154 AlwaysHovered,
155 NeverHovered,
156 AlwaysSunken
157 };
158 Q_ENUM(OverrideState)
159
160
161#ifdef QT_DEBUG
162 enum DebugFlag {
163 NoDebug = 0x000,
164 Debug = 0x001,
165 Info = 0x002,
166 ImageRect = 0x004,
167 ContentRect = 0x008,
168 LayoutRect = 0x010,
169 Unscaled = 0x020,
170 InputContentSize = 0x040,
171 DontUseNinePatchImage = 0x080,
172 NinePatchMargins = 0x100,
173 SaveImage = 0x200,
174 };
175 Q_DECLARE_FLAGS(DebugFlags, DebugFlag)
176 Q_FLAG(DebugFlags)
177#endif
178
179 explicit QQuickStyleItem(QQuickItem *parent = nullptr);
180 ~QQuickStyleItem() override;
181
182 qreal contentWidth();
183 void setContentWidth(qreal contentWidth);
184 qreal contentHeight();
185 void setContentHeight(qreal contentHeight);
186
187 QQuickStyleMargins contentPadding() const;
188 QQuickStyleMargins layoutMargins() const;
189 QSize minimumSize() const;
190 QSize imageSize() const;
191 qreal focusFrameRadius() const;
192
193 Q_INVOKABLE virtual QFont styleFont(QQuickItem *control) const;
194
195 void markGeometryDirty();
196 void markImageDirty();
197
198Q_SIGNALS:
199 void controlChanged();
200 void contentPaddingChanged();
201 void layoutMarginsChanged();
202 void fontChanged();
203 void minimumSizeChanged();
204
205protected:
206 void componentComplete() override;
207 QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData) override;
208 void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
209 void itemChange(ItemChange change, const ItemChangeData &data) override;
210 void updatePolish() override;
211
212 virtual void connectToControl() const;
213 virtual void paintEvent(QPainter *painter) const = 0;
214 virtual StyleItemGeometry calculateGeometry() = 0;
215
216 static QStyle::State controlSize(QQuickItem *item);
217 void initStyleOptionBase(QStyleOption &styleOption) const;
218
219 inline QSize contentSize() const { return QSize(qCeil(v: m_contentSize.width()), qCeil(v: m_contentSize.height())); }
220 inline static QStyle *style() { return QQuickNativeStyle::style(); }
221
222 template <class T> inline const T* control() const {
223#ifdef QT_DEBUG
224 if (!dynamic_cast<T *>(m_control.data())) {
225 qmlWarning(me: this) << "control property is not of correct type";
226 Q_UNREACHABLE();
227 }
228#endif
229 return static_cast<T *>(m_control.data());
230 }
231
232#ifdef QT_DEBUG
233 DebugFlags m_debugFlags = NoDebug;
234#endif
235 OverrideState m_overrideState = None;
236
237private:
238 bool event(QEvent *event) override;
239 inline void updateGeometry();
240 inline void paintControlToImage();
241
242 int dprAlignedSize(const int size) const;
243
244#ifdef QT_DEBUG
245 void addDebugInfo();
246#endif
247
248private:
249 QPointer<QQuickItem> m_control;
250 QImage m_paintedImage;
251 StyleItemGeometry m_styleItemGeometry;
252 QSizeF m_contentSize;
253
254 DirtyFlags m_dirty = Everything;
255 bool m_useNinePatchImage = true;
256 bool m_polishing = false;
257 mutable QQuickWindow *m_connectedWindow = nullptr;
258
259#ifdef Q_OS_MACOS
260 int m_transitionDuration = 150;
261#else
262 int m_transitionDuration = 400;
263#endif
264
265private:
266 friend class QtQuickControls2MacOSStylePlugin;
267};
268
269Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickStyleItem::DirtyFlags)
270
271#ifdef QT_DEBUG
272Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickStyleItem::DebugFlags)
273#endif
274
275QT_END_NAMESPACE
276
277QML_DECLARE_TYPE(QQuickStyleItem)
278
279#endif // QQUICKSTYLEITEM_H
280

source code of qtdeclarative/src/quicknativestyle/items/qquickstyleitem.h