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
10QT_BEGIN_NAMESPACE
11
12Q_STATIC_LOGGING_CATEGORY(lcPane, "qt.quick.controls.pane")
13
14/*!
15 \qmltype Pane
16 \inherits Control
17//! \nativetype 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 [QML] {Control::}{wheelEnabled}
35 is \c true, the 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
101void 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 setSizePolicy(horizontalPolicy: QLayoutPolicy::Preferred, verticalPolicy: QLayoutPolicy::Preferred);
112}
113
114QList<QQuickItem *> QQuickPanePrivate::contentChildItems() const
115{
116 if (!contentItem)
117 return QList<QQuickItem *>();
118
119 return contentItem->childItems();
120}
121
122QQuickItem *QQuickPanePrivate::getContentItem()
123{
124 Q_Q(QQuickPane);
125 if (QQuickItem *item = QQuickControlPrivate::getContentItem())
126 return item;
127
128 return new QQuickContentItem(q);
129}
130
131void QQuickPanePrivate::itemImplicitWidthChanged(QQuickItem *item)
132{
133 QQuickControlPrivate::itemImplicitWidthChanged(item);
134
135 if (item == firstChild)
136 updateImplicitContentWidth();
137}
138
139void QQuickPanePrivate::itemImplicitHeightChanged(QQuickItem *item)
140{
141 QQuickControlPrivate::itemImplicitHeightChanged(item);
142
143 if (item == firstChild)
144 updateImplicitContentHeight();
145}
146
147void QQuickPanePrivate::itemDestroyed(QQuickItem *item)
148{
149 // Do this check before calling the base class implementation, as that clears contentItem.
150 if (item == firstChild)
151 firstChild = nullptr;
152
153 QQuickControlPrivate::itemDestroyed(item);
154}
155
156void QQuickPanePrivate::contentChildrenChange()
157{
158 Q_Q(QQuickPane);
159
160 QQuickItem *newFirstChild = getFirstChild();
161
162 if (newFirstChild != firstChild) {
163 if (firstChild)
164 removeImplicitSizeListener(item: firstChild);
165 if (newFirstChild && newFirstChild != contentItem)
166 addImplicitSizeListener(item: newFirstChild);
167 firstChild = newFirstChild;
168 }
169
170 updateImplicitContentSize();
171 emit q->contentChildrenChanged();
172}
173
174qreal QQuickPanePrivate::getContentWidth() const
175{
176 if (!contentItem)
177 return 0;
178
179 if (hasContentWidth)
180 return contentWidth;
181
182 const qreal cw = contentItem->implicitWidth();
183 if (!qFuzzyIsNull(d: cw))
184 return cw;
185
186 const auto contentChildren = contentChildItems();
187 if (contentChildren.size() == 1)
188 return contentChildren.first()->implicitWidth();
189
190 return 0;
191}
192
193QQuickItem* QQuickPanePrivate::getFirstChild() const
194{
195 // The first child varies depending on how the content item is declared.
196 // If it's declared as a child of the Pane, it will be parented to the
197 // default QQuickContentItem. If it's assigned to the contentItem property
198 // directly, QQuickControl::contentItem will be used.
199 return (qobject_cast<QQuickContentItem *>(object: contentItem)
200 ? contentChildItems().value(i: 0) : contentItem.data());
201}
202
203qreal QQuickPanePrivate::getContentHeight() const
204{
205 if (!contentItem)
206 return 0;
207
208 if (hasContentHeight)
209 return contentHeight;
210
211 const qreal ch = contentItem->implicitHeight();
212 if (!qFuzzyIsNull(d: ch))
213 return ch;
214
215 const auto contentChildren = contentChildItems();
216 if (contentChildren.size() == 1)
217 return contentChildren.first()->implicitHeight();
218
219 return 0;
220}
221
222void QQuickPanePrivate::updateContentWidth()
223{
224 Q_Q(QQuickPane);
225 if (hasContentWidth || qFuzzyCompare(p1: contentWidth, p2: implicitContentWidth))
226 return;
227
228 const qreal oldContentWidth = contentWidth;
229 contentWidth = implicitContentWidth;
230 qCDebug(lcPane) << "contentWidth of" << q << "changed to" << contentWidth;
231 q->contentSizeChange(newSize: QSizeF(contentWidth, contentHeight), oldSize: QSizeF(oldContentWidth, contentHeight));
232 emit q->contentWidthChanged();
233}
234
235void QQuickPanePrivate::updateContentHeight()
236{
237 Q_Q(QQuickPane);
238 if (hasContentHeight || qFuzzyCompare(p1: contentHeight, p2: implicitContentHeight))
239 return;
240
241 const qreal oldContentHeight = contentHeight;
242 contentHeight = implicitContentHeight;
243 qCDebug(lcPane) << "contentHeight of" << q << "changed to" << contentHeight;
244 q->contentSizeChange(newSize: QSizeF(contentWidth, contentHeight), oldSize: QSizeF(contentWidth, oldContentHeight));
245 emit q->contentHeightChanged();
246}
247
248/*
249 A pane needs to be opaque to mouse events, so that events don't get
250 propagated through to controls covered by the pane.
251*/
252bool QQuickPanePrivate::handlePress(const QPointF &point, ulong timestamp)
253{
254 QQuickControlPrivate::handlePress(point, timestamp);
255 return true;
256}
257
258QQuickPane::QQuickPane(QQuickItem *parent)
259 : QQuickControl(*(new QQuickPanePrivate), parent)
260{
261 Q_D(QQuickPane);
262 d->init();
263}
264
265QQuickPane::~QQuickPane()
266{
267 Q_D(QQuickPane);
268 d->removeImplicitSizeListener(item: d->contentItem);
269 d->removeImplicitSizeListener(item: d->firstChild);
270}
271
272QQuickPane::QQuickPane(QQuickPanePrivate &dd, QQuickItem *parent)
273 : QQuickControl(dd, parent)
274{
275 Q_D(QQuickPane);
276 d->init();
277}
278
279/*!
280 \qmlproperty real QtQuick.Controls::Pane::contentWidth
281
282 This property holds the content width. It is used for calculating the total
283 implicit width of the pane.
284
285 For more information, see \l {Content Sizing}.
286
287 \sa contentHeight
288*/
289qreal QQuickPane::contentWidth() const
290{
291 Q_D(const QQuickPane);
292 return d->contentWidth;
293}
294
295void QQuickPane::setContentWidth(qreal width)
296{
297 Q_D(QQuickPane);
298 d->hasContentWidth = true;
299 if (qFuzzyCompare(p1: d->contentWidth, p2: width))
300 return;
301
302 const qreal oldWidth = d->contentWidth;
303 d->contentWidth = width;
304 contentSizeChange(newSize: QSizeF(width, d->contentHeight), oldSize: QSizeF(oldWidth, d->contentHeight));
305 emit contentWidthChanged();
306}
307
308void QQuickPane::resetContentWidth()
309{
310 Q_D(QQuickPane);
311 if (!d->hasContentWidth)
312 return;
313
314 d->hasContentWidth = false;
315 d->updateImplicitContentWidth();
316}
317
318/*!
319 \qmlproperty real QtQuick.Controls::Pane::contentHeight
320
321 This property holds the content height. It is used for calculating the total
322 implicit height of the pane.
323
324 For more information, see \l {Content Sizing}.
325
326 \sa contentWidth
327*/
328qreal QQuickPane::contentHeight() const
329{
330 Q_D(const QQuickPane);
331 return d->contentHeight;
332}
333
334void QQuickPane::setContentHeight(qreal height)
335{
336 Q_D(QQuickPane);
337 d->hasContentHeight = true;
338 if (qFuzzyCompare(p1: d->contentHeight, p2: height))
339 return;
340
341 const qreal oldHeight = d->contentHeight;
342 d->contentHeight = height;
343 contentSizeChange(newSize: QSizeF(d->contentWidth, height), oldSize: QSizeF(d->contentWidth, oldHeight));
344 emit contentHeightChanged();
345}
346
347void QQuickPane::resetContentHeight()
348{
349 Q_D(QQuickPane);
350 if (!d->hasContentHeight)
351 return;
352
353 d->hasContentHeight = false;
354 d->updateImplicitContentHeight();
355}
356
357/*!
358 \qmlproperty list<QtObject> QtQuick.Controls::Pane::contentData
359 \qmldefault
360
361 This property holds the list of content data.
362
363 The list contains all objects that have been declared in QML as children
364 of the pane.
365
366 \note Unlike \c contentChildren, \c contentData does include non-visual QML
367 objects.
368
369 \sa Item::data, contentChildren
370*/
371QQmlListProperty<QObject> QQuickPanePrivate::contentData()
372{
373 Q_Q(QQuickPane);
374 return QQmlListProperty<QObject>(q->contentItem(), nullptr,
375 QQuickItemPrivate::data_append,
376 QQuickItemPrivate::data_count,
377 QQuickItemPrivate::data_at,
378 QQuickItemPrivate::data_clear);
379}
380
381/*!
382 \qmlproperty list<Item> QtQuick.Controls::Pane::contentChildren
383
384 This property holds the list of content children.
385
386 The list contains all items that have been declared in QML as children
387 of the pane.
388
389 \note Unlike \c contentData, \c contentChildren does not include non-visual
390 QML objects.
391
392 \sa Item::children, contentData
393*/
394QQmlListProperty<QQuickItem> QQuickPanePrivate::contentChildren()
395{
396 Q_Q(QQuickPane);
397 return QQmlListProperty<QQuickItem>(q->contentItem(), nullptr,
398 QQuickItemPrivate::children_append,
399 QQuickItemPrivate::children_count,
400 QQuickItemPrivate::children_at,
401 QQuickItemPrivate::children_clear);
402}
403
404void QQuickPane::componentComplete()
405{
406 Q_D(QQuickPane);
407 QQuickControl::componentComplete();
408 d->updateImplicitContentSize();
409}
410
411void QQuickPane::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
412{
413 Q_D(QQuickPane);
414 QQuickControl::contentItemChange(newItem, oldItem);
415 if (oldItem) {
416 d->removeImplicitSizeListener(item: oldItem);
417 QObjectPrivate::disconnect(sender: oldItem, signal: &QQuickItem::childrenChanged, receiverPrivate: d, slot: &QQuickPanePrivate::contentChildrenChange);
418 }
419 if (newItem) {
420 d->addImplicitSizeListener(item: newItem);
421 QObjectPrivate::connect(sender: newItem, signal: &QQuickItem::childrenChanged, receiverPrivate: d, slot: &QQuickPanePrivate::contentChildrenChange);
422 }
423 d->contentChildrenChange();
424}
425
426void QQuickPane::contentSizeChange(const QSizeF &newSize, const QSizeF &oldSize)
427{
428 Q_UNUSED(newSize);
429 Q_UNUSED(oldSize);
430
431 Q_D(QQuickPane);
432 if (d->hasContentWidth)
433 d->updateImplicitContentWidth();
434 if (d->hasContentHeight)
435 d->updateImplicitContentHeight();
436}
437
438#if QT_CONFIG(accessibility)
439QAccessible::Role QQuickPane::accessibleRole() const
440{
441 return QAccessible::Pane;
442}
443#endif
444
445QT_END_NAMESPACE
446
447#include "moc_qquickpane_p.cpp"
448

source code of qtdeclarative/src/quicktemplates/qquickpane.cpp