1 | // Copyright (C) 2017 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 | #include "qquickpane_p.h" |
5 | #include "qquickpane_p_p.h" |
6 | #include "qquickcontentitem_p.h" |
7 | |
8 | #include <QtCore/qloggingcategory.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | Q_LOGGING_CATEGORY(lcPane, "qt.quick.controls.pane" ) |
13 | |
14 | /*! |
15 | \qmltype Pane |
16 | \inherits Control |
17 | //! \instantiates QQuickPane |
18 | \inqmlmodule QtQuick.Controls |
19 | \since 5.7 |
20 | \ingroup qtquickcontrols-containers |
21 | \ingroup qtquickcontrols-focusscopes |
22 | \brief Provides a background matching with the application style and theme. |
23 | |
24 | Pane provides a background color that matches with the application style |
25 | and theme. Pane does not provide a layout of its own, but requires you to |
26 | position its contents, for instance by creating a \l RowLayout or a |
27 | \l ColumnLayout. |
28 | |
29 | Items declared as children of a Pane are automatically parented to the |
30 | Pane's \l[QtQuickControls2]{Control::}{contentItem}. Items created |
31 | dynamically need to be explicitly parented to the \c contentItem. |
32 | |
33 | As mentioned in \l {Event Handling}, Pane does not let click and touch |
34 | events through to items beneath it. If \l wheelEnabled is \c true, the |
35 | same applies to mouse wheel events. |
36 | |
37 | \section1 Content Sizing |
38 | |
39 | If only a single item is used within a Pane, it will resize to fit the |
40 | implicit size of its contained item. This makes it particularly suitable |
41 | for use together with layouts. |
42 | |
43 | \image qtquickcontrols-pane.png |
44 | |
45 | \snippet qtquickcontrols-pane.qml 1 |
46 | |
47 | Sometimes there might be two items within the pane: |
48 | |
49 | \code |
50 | Pane { |
51 | SwipeView { |
52 | // ... |
53 | } |
54 | PageIndicator { |
55 | anchors.horizontalCenter: parent.horizontalCenter |
56 | anchors.bottom: parent.bottom |
57 | } |
58 | } |
59 | \endcode |
60 | |
61 | In this case, Pane cannot calculate a sensible implicit size. Since we're |
62 | anchoring the \l PageIndicator over the \l SwipeView, we can simply set the |
63 | content size to the view's implicit size: |
64 | |
65 | \code |
66 | Pane { |
67 | contentWidth: view.implicitWidth |
68 | contentHeight: view.implicitHeight |
69 | |
70 | SwipeView { |
71 | id: view |
72 | // ... |
73 | } |
74 | PageIndicator { |
75 | anchors.horizontalCenter: parent.horizontalCenter |
76 | anchors.bottom: parent.bottom |
77 | } |
78 | } |
79 | \endcode |
80 | |
81 | If the \l[QtQuickControls2]{Control::}{contentItem} has no implicit size |
82 | and only one child, Pane will use the implicit size of that child. For |
83 | example, in the following code, the Pane assumes the size of the Rectangle: |
84 | |
85 | \code |
86 | Pane { |
87 | Item { |
88 | Rectangle { |
89 | implicitWidth: 200 |
90 | implicitHeight: 200 |
91 | color: "salmon" |
92 | } |
93 | } |
94 | } |
95 | \endcode |
96 | |
97 | \sa {Customizing Pane}, {Container Controls}, |
98 | {Focus Management in Qt Quick Controls}, {Event Handling} |
99 | */ |
100 | |
101 | void QQuickPanePrivate::init() |
102 | { |
103 | Q_Q(QQuickPane); |
104 | q->setFlag(flag: QQuickItem::ItemIsFocusScope); |
105 | q->setAcceptedMouseButtons(Qt::AllButtons); |
106 | #if QT_CONFIG(cursor) |
107 | q->setCursor(Qt::ArrowCursor); |
108 | #endif |
109 | connect(sender: q, signal: &QQuickControl::implicitContentWidthChanged, receiverPrivate: this, slot: &QQuickPanePrivate::updateContentWidth); |
110 | connect(sender: q, signal: &QQuickControl::implicitContentHeightChanged, receiverPrivate: this, slot: &QQuickPanePrivate::updateContentHeight); |
111 | } |
112 | |
113 | QList<QQuickItem *> QQuickPanePrivate::contentChildItems() const |
114 | { |
115 | if (!contentItem) |
116 | return QList<QQuickItem *>(); |
117 | |
118 | return contentItem->childItems(); |
119 | } |
120 | |
121 | QQuickItem *QQuickPanePrivate::getContentItem() |
122 | { |
123 | Q_Q(QQuickPane); |
124 | if (QQuickItem *item = QQuickControlPrivate::getContentItem()) |
125 | return item; |
126 | |
127 | return new QQuickContentItem(q); |
128 | } |
129 | |
130 | void QQuickPanePrivate::itemImplicitWidthChanged(QQuickItem *item) |
131 | { |
132 | QQuickControlPrivate::itemImplicitWidthChanged(item); |
133 | |
134 | if (item == firstChild) |
135 | updateImplicitContentWidth(); |
136 | } |
137 | |
138 | void QQuickPanePrivate::itemImplicitHeightChanged(QQuickItem *item) |
139 | { |
140 | QQuickControlPrivate::itemImplicitHeightChanged(item); |
141 | |
142 | if (item == firstChild) |
143 | updateImplicitContentHeight(); |
144 | } |
145 | |
146 | void QQuickPanePrivate::contentChildrenChange() |
147 | { |
148 | Q_Q(QQuickPane); |
149 | QQuickItem *newFirstChild = contentChildItems().value(i: 0); |
150 | if (newFirstChild != firstChild) { |
151 | if (firstChild) |
152 | removeImplicitSizeListener(item: firstChild); |
153 | if (newFirstChild) |
154 | addImplicitSizeListener(item: newFirstChild); |
155 | firstChild = newFirstChild; |
156 | } |
157 | |
158 | updateImplicitContentSize(); |
159 | emit q->contentChildrenChanged(); |
160 | } |
161 | |
162 | qreal QQuickPanePrivate::getContentWidth() const |
163 | { |
164 | if (!contentItem) |
165 | return 0; |
166 | |
167 | const qreal cw = contentItem->implicitWidth(); |
168 | if (!qFuzzyIsNull(d: cw)) |
169 | return cw; |
170 | |
171 | const auto contentChildren = contentChildItems(); |
172 | if (contentChildren.size() == 1) |
173 | return contentChildren.first()->implicitWidth(); |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | qreal QQuickPanePrivate::getContentHeight() const |
179 | { |
180 | if (!contentItem) |
181 | return 0; |
182 | |
183 | const qreal ch = contentItem->implicitHeight(); |
184 | if (!qFuzzyIsNull(d: ch)) |
185 | return ch; |
186 | |
187 | const auto contentChildren = contentChildItems(); |
188 | if (contentChildren.size() == 1) |
189 | return contentChildren.first()->implicitHeight(); |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | void QQuickPanePrivate::updateContentWidth() |
195 | { |
196 | Q_Q(QQuickPane); |
197 | if (hasContentWidth || qFuzzyCompare(p1: contentWidth, p2: implicitContentWidth)) |
198 | return; |
199 | |
200 | const qreal oldContentWidth = contentWidth; |
201 | contentWidth = implicitContentWidth; |
202 | qCDebug(lcPane) << "contentWidth of" << q << "changed to" << contentWidth; |
203 | q->contentSizeChange(newSize: QSizeF(contentWidth, contentHeight), oldSize: QSizeF(oldContentWidth, contentHeight)); |
204 | emit q->contentWidthChanged(); |
205 | } |
206 | |
207 | void QQuickPanePrivate::updateContentHeight() |
208 | { |
209 | Q_Q(QQuickPane); |
210 | if (hasContentHeight || qFuzzyCompare(p1: contentHeight, p2: implicitContentHeight)) |
211 | return; |
212 | |
213 | const qreal oldContentHeight = contentHeight; |
214 | contentHeight = implicitContentHeight; |
215 | qCDebug(lcPane) << "contentHeight of" << q << "changed to" << contentHeight; |
216 | q->contentSizeChange(newSize: QSizeF(contentWidth, contentHeight), oldSize: QSizeF(contentWidth, oldContentHeight)); |
217 | emit q->contentHeightChanged(); |
218 | } |
219 | |
220 | /* |
221 | A pane needs to be opaque to mouse events, so that events don't get |
222 | propagated through to controls covered by the pane. |
223 | */ |
224 | bool QQuickPanePrivate::handlePress(const QPointF &point, ulong timestamp) |
225 | { |
226 | QQuickControlPrivate::handlePress(point, timestamp); |
227 | return true; |
228 | } |
229 | |
230 | QQuickPane::QQuickPane(QQuickItem *parent) |
231 | : QQuickControl(*(new QQuickPanePrivate), parent) |
232 | { |
233 | Q_D(QQuickPane); |
234 | d->init(); |
235 | } |
236 | |
237 | QQuickPane::~QQuickPane() |
238 | { |
239 | Q_D(QQuickPane); |
240 | d->removeImplicitSizeListener(item: d->contentItem); |
241 | d->removeImplicitSizeListener(item: d->firstChild); |
242 | } |
243 | |
244 | QQuickPane::QQuickPane(QQuickPanePrivate &dd, QQuickItem *parent) |
245 | : QQuickControl(dd, parent) |
246 | { |
247 | Q_D(QQuickPane); |
248 | d->init(); |
249 | } |
250 | |
251 | /*! |
252 | \qmlproperty real QtQuick.Controls::Pane::contentWidth |
253 | |
254 | This property holds the content width. It is used for calculating the total |
255 | implicit width of the pane. |
256 | |
257 | For more information, see \l {Content Sizing}. |
258 | |
259 | \sa contentHeight |
260 | */ |
261 | qreal QQuickPane::contentWidth() const |
262 | { |
263 | Q_D(const QQuickPane); |
264 | return d->contentWidth; |
265 | } |
266 | |
267 | void QQuickPane::setContentWidth(qreal width) |
268 | { |
269 | Q_D(QQuickPane); |
270 | d->hasContentWidth = true; |
271 | if (qFuzzyCompare(p1: d->contentWidth, p2: width)) |
272 | return; |
273 | |
274 | const qreal oldWidth = d->contentWidth; |
275 | d->contentWidth = width; |
276 | contentSizeChange(newSize: QSizeF(width, d->contentHeight), oldSize: QSizeF(oldWidth, d->contentHeight)); |
277 | emit contentWidthChanged(); |
278 | } |
279 | |
280 | void QQuickPane::resetContentWidth() |
281 | { |
282 | Q_D(QQuickPane); |
283 | if (!d->hasContentWidth) |
284 | return; |
285 | |
286 | d->hasContentHeight = false; |
287 | d->updateContentWidth(); |
288 | } |
289 | |
290 | /*! |
291 | \qmlproperty real QtQuick.Controls::Pane::contentHeight |
292 | |
293 | This property holds the content height. It is used for calculating the total |
294 | implicit height of the pane. |
295 | |
296 | For more information, see \l {Content Sizing}. |
297 | |
298 | \sa contentWidth |
299 | */ |
300 | qreal QQuickPane::contentHeight() const |
301 | { |
302 | Q_D(const QQuickPane); |
303 | return d->contentHeight; |
304 | } |
305 | |
306 | void QQuickPane::setContentHeight(qreal height) |
307 | { |
308 | Q_D(QQuickPane); |
309 | d->hasContentHeight = true; |
310 | if (qFuzzyCompare(p1: d->contentHeight, p2: height)) |
311 | return; |
312 | |
313 | const qreal oldHeight = d->contentHeight; |
314 | d->contentHeight = height; |
315 | contentSizeChange(newSize: QSizeF(d->contentWidth, height), oldSize: QSizeF(d->contentWidth, oldHeight)); |
316 | emit contentHeightChanged(); |
317 | } |
318 | |
319 | void QQuickPane::resetContentHeight() |
320 | { |
321 | Q_D(QQuickPane); |
322 | if (!d->hasContentHeight) |
323 | return; |
324 | |
325 | d->hasContentHeight = false; |
326 | d->updateContentHeight(); |
327 | } |
328 | |
329 | /*! |
330 | \qmlproperty list<QtObject> QtQuick.Controls::Pane::contentData |
331 | \qmldefault |
332 | |
333 | This property holds the list of content data. |
334 | |
335 | The list contains all objects that have been declared in QML as children |
336 | of the pane. |
337 | |
338 | \note Unlike \c contentChildren, \c contentData does include non-visual QML |
339 | objects. |
340 | |
341 | \sa Item::data, contentChildren |
342 | */ |
343 | QQmlListProperty<QObject> QQuickPanePrivate::contentData() |
344 | { |
345 | Q_Q(QQuickPane); |
346 | return QQmlListProperty<QObject>(q->contentItem(), nullptr, |
347 | QQuickItemPrivate::data_append, |
348 | QQuickItemPrivate::data_count, |
349 | QQuickItemPrivate::data_at, |
350 | QQuickItemPrivate::data_clear); |
351 | } |
352 | |
353 | /*! |
354 | \qmlproperty list<Item> QtQuick.Controls::Pane::contentChildren |
355 | |
356 | This property holds the list of content children. |
357 | |
358 | The list contains all items that have been declared in QML as children |
359 | of the pane. |
360 | |
361 | \note Unlike \c contentData, \c contentChildren does not include non-visual |
362 | QML objects. |
363 | |
364 | \sa Item::children, contentData |
365 | */ |
366 | QQmlListProperty<QQuickItem> QQuickPanePrivate::contentChildren() |
367 | { |
368 | Q_Q(QQuickPane); |
369 | return QQmlListProperty<QQuickItem>(q->contentItem(), nullptr, |
370 | QQuickItemPrivate::children_append, |
371 | QQuickItemPrivate::children_count, |
372 | QQuickItemPrivate::children_at, |
373 | QQuickItemPrivate::children_clear); |
374 | } |
375 | |
376 | void QQuickPane::componentComplete() |
377 | { |
378 | Q_D(QQuickPane); |
379 | QQuickControl::componentComplete(); |
380 | d->updateImplicitContentSize(); |
381 | } |
382 | |
383 | void QQuickPane::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) |
384 | { |
385 | Q_D(QQuickPane); |
386 | QQuickControl::contentItemChange(newItem, oldItem); |
387 | if (oldItem) { |
388 | d->removeImplicitSizeListener(item: oldItem); |
389 | QObjectPrivate::disconnect(sender: oldItem, signal: &QQuickItem::childrenChanged, receiverPrivate: d, slot: &QQuickPanePrivate::contentChildrenChange); |
390 | } |
391 | if (newItem) { |
392 | d->addImplicitSizeListener(item: newItem); |
393 | QObjectPrivate::connect(sender: newItem, signal: &QQuickItem::childrenChanged, receiverPrivate: d, slot: &QQuickPanePrivate::contentChildrenChange); |
394 | } |
395 | d->contentChildrenChange(); |
396 | } |
397 | |
398 | void QQuickPane::contentSizeChange(const QSizeF &newSize, const QSizeF &oldSize) |
399 | { |
400 | Q_UNUSED(newSize); |
401 | Q_UNUSED(oldSize); |
402 | } |
403 | |
404 | #if QT_CONFIG(accessibility) |
405 | QAccessible::Role QQuickPane::accessibleRole() const |
406 | { |
407 | return QAccessible::Pane; |
408 | } |
409 | #endif |
410 | |
411 | QT_END_NAMESPACE |
412 | |
413 | #include "moc_qquickpane_p.cpp" |
414 | |