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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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