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_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
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}
112
113QList<QQuickItem *> QQuickPanePrivate::contentChildItems() const
114{
115 if (!contentItem)
116 return QList<QQuickItem *>();
117
118 return contentItem->childItems();
119}
120
121QQuickItem *QQuickPanePrivate::getContentItem()
122{
123 Q_Q(QQuickPane);
124 if (QQuickItem *item = QQuickControlPrivate::getContentItem())
125 return item;
126
127 return new QQuickContentItem(q);
128}
129
130void QQuickPanePrivate::itemImplicitWidthChanged(QQuickItem *item)
131{
132 QQuickControlPrivate::itemImplicitWidthChanged(item);
133
134 if (item == firstChild)
135 updateImplicitContentWidth();
136}
137
138void QQuickPanePrivate::itemImplicitHeightChanged(QQuickItem *item)
139{
140 QQuickControlPrivate::itemImplicitHeightChanged(item);
141
142 if (item == firstChild)
143 updateImplicitContentHeight();
144}
145
146void 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
162qreal 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
178qreal 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
194void 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
207void 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*/
224bool QQuickPanePrivate::handlePress(const QPointF &point, ulong timestamp)
225{
226 QQuickControlPrivate::handlePress(point, timestamp);
227 return true;
228}
229
230QQuickPane::QQuickPane(QQuickItem *parent)
231 : QQuickControl(*(new QQuickPanePrivate), parent)
232{
233 Q_D(QQuickPane);
234 d->init();
235}
236
237QQuickPane::~QQuickPane()
238{
239 Q_D(QQuickPane);
240 d->removeImplicitSizeListener(item: d->contentItem);
241 d->removeImplicitSizeListener(item: d->firstChild);
242}
243
244QQuickPane::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*/
261qreal QQuickPane::contentWidth() const
262{
263 Q_D(const QQuickPane);
264 return d->contentWidth;
265}
266
267void 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
280void 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*/
300qreal QQuickPane::contentHeight() const
301{
302 Q_D(const QQuickPane);
303 return d->contentHeight;
304}
305
306void 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
319void 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*/
343QQmlListProperty<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*/
366QQmlListProperty<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
376void QQuickPane::componentComplete()
377{
378 Q_D(QQuickPane);
379 QQuickControl::componentComplete();
380 d->updateImplicitContentSize();
381}
382
383void 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
398void QQuickPane::contentSizeChange(const QSizeF &newSize, const QSizeF &oldSize)
399{
400 Q_UNUSED(newSize);
401 Q_UNUSED(oldSize);
402}
403
404#if QT_CONFIG(accessibility)
405QAccessible::Role QQuickPane::accessibleRole() const
406{
407 return QAccessible::Pane;
408}
409#endif
410
411QT_END_NAMESPACE
412
413#include "moc_qquickpane_p.cpp"
414

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