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 "qquickapplicationwindow_p.h"
5#include "qquickapplicationwindow_p_p.h"
6#include "qquickpopup_p_p.h"
7#include "qquickcontrol_p_p.h"
8#include "qquicktemplatesutils_p.h"
9#include "qquicktoolbar_p.h"
10#include <private/qtquicktemplates2-config_p.h>
11#if QT_CONFIG(quicktemplates2_container)
12#include "qquicktabbar_p.h"
13#include "qquickdialogbuttonbox_p.h"
14#endif
15#include "qquickdeferredexecute_p_p.h"
16#include "qquickdeferredpointer_p_p.h"
17
18#include <QtCore/private/qobject_p.h>
19#include <QtCore/qscopedvaluerollback.h>
20#include <QtQml/private/qqmlpropertytopropertybinding_p.h>
21#include <QtQuick/private/qquickitem_p.h>
22#include <QtQuick/private/qquicksafearea_p.h>
23#include <QtQuick/private/qquickitemchangelistener_p.h>
24#include <QtQuick/private/qquickwindowmodule_p_p.h>
25
26QT_BEGIN_NAMESPACE
27
28using namespace Qt::StringLiterals;
29
30/*!
31 \qmltype ApplicationWindow
32 \inherits Window
33//! \nativetype QQuickApplicationWindow
34 \inqmlmodule QtQuick.Controls
35 \since 5.7
36 \ingroup qtquickcontrols-containers
37 \ingroup qtquickcontrols-focusscopes
38 \brief Styled top-level window with support for a header and footer.
39
40 ApplicationWindow is a \l Window which makes it convenient to add
41 a \l {menuBar}{menu bar}, \l header and \l footer item to the window.
42
43 You can declare ApplicationWindow as the root item of your application,
44 and run it by using \l QQmlApplicationEngine. In this way you can control
45 the window's properties, appearance and layout from QML.
46
47 \image qtquickcontrols-applicationwindow-wireframe.png
48
49 \qml
50 import QtQuick.Controls 2.12
51
52 ApplicationWindow {
53 visible: true
54
55 menuBar: MenuBar {
56 // ...
57 }
58
59 header: ToolBar {
60 // ...
61 }
62
63 footer: TabBar {
64 // ...
65 }
66
67 StackView {
68 anchors.fill: parent
69 }
70 }
71 \endqml
72
73 \note By default, an ApplicationWindow is not visible.
74
75 \section2 Attached ApplicationWindow Properties
76
77 Due to how \l {Scope and Naming Resolution} works in QML, it is possible
78 to reference the \c id of the application root element anywhere in its
79 child QML objects. Even though this approach is fine for many applications
80 and use cases, for a generic QML component it may not be acceptable as it
81 creates a dependency to the surrounding environment.
82
83 ApplicationWindow provides a set of attached properties that can be used
84 to access the window and its building blocks from places where no direct
85 access to the window is available, without creating a dependency to a
86 certain window \c id. A QML component that uses the ApplicationWindow
87 attached properties works in any window regardless of its \c id.
88
89 \section2 Safe Areas
90
91 Since Qt 6.9 ApplicationWindow will automatically add padding to the
92 contentItem for any \l{SafeArea} {safe area margins} reported by the
93 window. This ensures that the contentItem stays inside the safe area
94 of the window, while the background item covers the entire window.
95
96 If you are manually handing safe area margins in the window's contentItem
97 you can override the default via the topPadding, leftPadding, rightPadding
98 and bottomPadding properties:
99
100 \snippet qtquickcontrols-appwindow-safeareas.qml 0
101 \snippet qtquickcontrols-appwindow-safeareas.qml 1
102 \snippet qtquickcontrols-appwindow-safeareas.qml 2
103
104 The \l{header}, \l{footer}, and \l{menuBar} properties do not receive
105 any automatic padding for the safe area margins. However, depending on
106 the style in use, the style may take safe areas into account in its
107 implementation of ToolBar, TabBar, and MenuBar.
108
109 \sa {Customizing ApplicationWindow}, Overlay, Page, {Container Controls},
110 {Focus Management in Qt Quick Controls}
111*/
112
113static const QQuickItemPrivate::ChangeTypes ItemChanges = QQuickItemPrivate::Visibility
114 | QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
115
116static void layoutItem(QQuickItem *item, qreal y, qreal width)
117{
118 if (!item)
119 return;
120
121 item->setY(y);
122 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
123 if (!p->widthValid()) {
124 item->setWidth(width);
125 p->widthValidFlag = false;
126 }
127}
128
129void QQuickApplicationWindowPrivate::updateHasBackgroundFlags()
130{
131 if (!background)
132 return;
133
134 QQuickItemPrivate *backgroundPrivate = QQuickItemPrivate::get(item: background);
135 hasBackgroundWidth = backgroundPrivate->widthValid();
136 hasBackgroundHeight = backgroundPrivate->heightValid();
137}
138
139void QQuickApplicationWindowPrivate::relayout()
140{
141 Q_Q(QQuickApplicationWindow);
142 if (!componentComplete)
143 return;
144
145 // Note: We track whether we are inside relayout, but we do
146 // allow nested relayouts, as those are necessary to compute
147 // the height and position of footers when using safe areas.
148 QScopedValueRollback<bool> guard(insideRelayout, true);
149
150 // Re-evaluate component heights for each use, as they
151 // may change between each use due to recursive layouts.
152 auto menuBarHeight = [this]{ return menuBar && menuBar->isVisible() ? menuBar->height() : 0; };
153 auto headerheight = [this]{ return header && header->isVisible() ? header->height() : 0; };
154 auto footerHeight = [this]{ return footer && footer->isVisible() ? footer->height() : 0; };
155
156 control->setSize(q->size());
157
158 layoutItem(item: menuBar, y: 0, width: q->width());
159 layoutItem(item: header, y: menuBarHeight(), width: q->width());
160 layoutItem(item: footer, y: control->height() - footerHeight(), width: q->width());
161
162 if (background) {
163 if (!hasBackgroundWidth && qFuzzyIsNull(d: background->x()))
164 background->setWidth(q->width());
165 if (!hasBackgroundHeight && qFuzzyIsNull(d: background->y()))
166 background->setHeight(q->height());
167 }
168
169 // Install additional margins on the control, which get reflected
170 // to the content item, but not to the header/footer/menuBar, as
171 // these are siblings of the control
172 auto *controlSafeArea = static_cast<QQuickSafeArea*>(qmlAttachedPropertiesObject<QQuickSafeArea>(obj: control));
173 auto *windowSafeArea = static_cast<QQuickSafeArea*>(qmlAttachedPropertiesObject<QQuickSafeArea>(obj: q));
174 const auto inheritedMargins = windowSafeArea->margins();
175 controlSafeArea->setAdditionalMargins(QMarginsF(
176 0, (menuBarHeight() + headerheight()) - inheritedMargins.top(),
177 0, footerHeight() - inheritedMargins.bottom()));
178}
179
180void QQuickApplicationWindowPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
181{
182 Q_UNUSED(diff);
183
184 if (!insideRelayout && item == background && change.sizeChange()) {
185 // Any time the background is resized (excluding our own resizing),
186 // we should respect it if it's explicit by storing the values of the flags.
187 updateHasBackgroundFlags();
188 }
189
190 relayout();
191}
192
193void QQuickApplicationWindowPrivate::itemVisibilityChanged(QQuickItem *item)
194{
195 Q_UNUSED(item);
196 relayout();
197}
198
199void QQuickApplicationWindowPrivate::itemImplicitWidthChanged(QQuickItem *item)
200{
201 Q_UNUSED(item);
202 relayout();
203}
204
205void QQuickApplicationWindowPrivate::itemImplicitHeightChanged(QQuickItem *item)
206{
207 Q_UNUSED(item);
208 relayout();
209}
210
211void QQuickApplicationWindowPrivate::updateFont(const QFont &f)
212{
213 Q_Q(QQuickApplicationWindow);
214 const bool changed = font != f;
215 font = f;
216
217 QQuickControlPrivate::updateFontRecur(item: q->QQuickWindow::contentItem(), font: f);
218
219 const QList<QQuickPopup *> popups = q->findChildren<QQuickPopup *>();
220 for (QQuickPopup *popup : popups)
221 QQuickControlPrivate::get(control: static_cast<QQuickControl *>(popup->popupItem()))->inheritFont(font: f);
222
223 if (changed)
224 emit q->fontChanged();
225}
226
227void QQuickApplicationWindowPrivate::resolveFont()
228{
229 QFont resolvedFont = font.resolve(QQuickTheme::font(scope: QQuickTheme::System));
230 setFont_helper(resolvedFont);
231}
232
233static QQuickItem *findActiveFocusControl(QQuickWindow *window)
234{
235 auto *appWindow = qobject_cast<QQuickApplicationWindow *>(object: window);
236 auto *appWindowPriv = appWindow ? QQuickApplicationWindowPrivate::get(window: appWindow) : nullptr;
237
238 QQuickItem *item = window->activeFocusItem();
239 while (item) {
240 // The content control is an implementation detail and if
241 // we hit it we've hit the root item, so no controls can exist
242 if (appWindow && item == appWindowPriv->control)
243 return nullptr;
244 if (QQuickTemplatesUtils::isInteractiveControlType(item))
245 return item;
246 item = item->parentItem();
247 }
248 return item;
249}
250
251void QQuickApplicationWindowPrivate::_q_updateActiveFocus()
252{
253 Q_Q(QQuickApplicationWindow);
254 setActiveFocusControl(findActiveFocusControl(window: q));
255}
256
257void QQuickApplicationWindowPrivate::setActiveFocusControl(QQuickItem *control)
258{
259 Q_Q(QQuickApplicationWindow);
260 if (activeFocusControl != control) {
261 activeFocusControl = control;
262 emit q->activeFocusControlChanged();
263 }
264}
265
266void QQuickApplicationWindowPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
267{
268 QQuickItemPrivate::data_append(prop, obj);
269
270 // associate "top-level" popups with the window as soon as they are added to the default property
271 if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(object: obj))
272 QQuickPopupPrivate::get(popup)->setWindow(static_cast<QQuickApplicationWindow *>(prop->data));
273}
274
275void QQuickApplicationWindowPrivate::cancelBackground()
276{
277 Q_Q(QQuickApplicationWindow);
278 quickCancelDeferred(object: q, property: backgroundName());
279}
280
281void QQuickApplicationWindowPrivate::executeBackground(bool complete)
282{
283 Q_Q(QQuickApplicationWindow);
284 if (background.wasExecuted())
285 return;
286
287 if (!background || complete)
288 quickBeginDeferred(object: q, property: backgroundName(), delegate&: background);
289 if (complete) {
290 quickCompleteDeferred(object: q, property: backgroundName(), delegate&: background);
291 // See comment in setBackground for why we do this here.
292 updateHasBackgroundFlags();
293 relayout();
294 }
295}
296
297QQuickApplicationWindow::QQuickApplicationWindow(QWindow *parent)
298 : QQuickWindowQmlImpl(*(new QQuickApplicationWindowPrivate), parent)
299{
300 connect(sender: this, SIGNAL(activeFocusItemChanged()), receiver: this, SLOT(_q_updateActiveFocus()));
301}
302
303QQuickApplicationWindow::~QQuickApplicationWindow()
304{
305 Q_D(QQuickApplicationWindow);
306 d->setActiveFocusControl(nullptr);
307 disconnect(sender: this, SIGNAL(activeFocusItemChanged()), receiver: this, SLOT(_q_updateActiveFocus()));
308 if (d->menuBar)
309 QQuickItemPrivate::get(item: d->menuBar)->removeItemChangeListener(d, types: ItemChanges);
310 if (d->header)
311 QQuickItemPrivate::get(item: d->header)->removeItemChangeListener(d, types: ItemChanges);
312 if (d->footer)
313 QQuickItemPrivate::get(item: d->footer)->removeItemChangeListener(d, types: ItemChanges);
314}
315
316QQuickApplicationWindowAttached *QQuickApplicationWindow::qmlAttachedProperties(QObject *object)
317{
318 return new QQuickApplicationWindowAttached(object);
319}
320
321/*!
322 \qmlproperty Item QtQuick.Controls::ApplicationWindow::background
323
324 This property holds the background item.
325
326 The background item is stacked under the \l {contentItem}{content item},
327 but above the \l {Window::color}{background color} of the window.
328
329 The background item is useful for images and gradients, for example,
330 but the \l {Window::}{color} property is preferable for solid colors,
331 as it doesn't need to create an item.
332
333 \note If the background item has no explicit size specified, it automatically
334 follows the control's size. In most cases, there is no need to specify
335 width or height for a background item.
336
337 \sa {Customizing ApplicationWindow}, contentItem, header, footer
338*/
339QQuickItem *QQuickApplicationWindow::background() const
340{
341 QQuickApplicationWindowPrivate *d = const_cast<QQuickApplicationWindowPrivate *>(d_func());
342 if (!d->background)
343 d->executeBackground();
344 return d->background;
345}
346
347void QQuickApplicationWindow::setBackground(QQuickItem *background)
348{
349 Q_D(QQuickApplicationWindow);
350 if (d->background == background)
351 return;
352
353 if (!d->background.isExecuting())
354 d->cancelBackground();
355
356 if (d->background) {
357 d->hasBackgroundWidth = false;
358 d->hasBackgroundHeight = false;
359 }
360 QQuickControlPrivate::hideOldItem(item: d->background);
361
362 d->background = background;
363
364 if (background) {
365 background->setParentItem(QQuickWindow::contentItem());
366
367 if (qFuzzyIsNull(d: background->z()))
368 background->setZ(-1);
369
370 // If the background hasn't finished executing then we don't know if its width and height
371 // are valid or not, and so relayout would see that they haven't been set yet and override
372 // any bindings the user might have.
373 if (!d->background.isExecuting()) {
374 d->updateHasBackgroundFlags();
375
376 if (isComponentComplete())
377 d->relayout();
378 }
379 }
380 if (!d->background.isExecuting())
381 emit backgroundChanged();
382}
383
384/*!
385 \qmlproperty Item QtQuick.Controls::ApplicationWindow::header
386
387 This property holds the window header item. The header item is positioned at the
388 top of the window, below the menu bar, and resized to the width of the window.
389 The default value is \c null.
390
391 \code
392 ApplicationWindow {
393 header: TabBar {
394 // ...
395 }
396 }
397 \endcode
398
399 \note Assigning a ToolBar, TabBar, or DialogButtonBox as a window header
400 automatically sets the respective \l ToolBar::position, \l TabBar::position,
401 or \l DialogButtonBox::position property to \c Header.
402
403 \sa menuBar, footer, Page::header
404*/
405QQuickItem *QQuickApplicationWindow::header() const
406{
407 Q_D(const QQuickApplicationWindow);
408 return d->header;
409}
410
411void QQuickApplicationWindow::setHeader(QQuickItem *header)
412{
413 Q_D(QQuickApplicationWindow);
414 if (d->header == header)
415 return;
416
417 if (d->header) {
418 QQuickItemPrivate::get(item: d->header)->removeItemChangeListener(d, types: ItemChanges);
419 d->header->setParentItem(nullptr);
420 }
421 d->header = header;
422 if (header) {
423 header->setParentItem(QQuickWindow::contentItem());
424 QQuickItemPrivate *p = QQuickItemPrivate::get(item: header);
425 p->addItemChangeListener(listener: d, types: ItemChanges);
426 if (qFuzzyIsNull(d: header->z()))
427 header->setZ(1);
428 if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(object: header))
429 toolBar->setPosition(QQuickToolBar::Header);
430#if QT_CONFIG(quicktemplates2_container)
431 else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(object: header))
432 tabBar->setPosition(QQuickTabBar::Header);
433 else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(object: header))
434 buttonBox->setPosition(QQuickDialogButtonBox::Header);
435#endif
436
437 header->stackBefore(d->control);
438 }
439 if (isComponentComplete())
440 d->relayout();
441 emit headerChanged();
442}
443
444/*!
445 \qmlproperty Item QtQuick.Controls::ApplicationWindow::footer
446
447 This property holds the window footer item. The footer item is positioned to
448 the bottom, and resized to the width of the window. The default value is \c null.
449
450 \code
451 ApplicationWindow {
452 footer: ToolBar {
453 // ...
454 }
455 }
456 \endcode
457
458 \note Assigning a ToolBar, TabBar, or DialogButtonBox as a window footer
459 automatically sets the respective \l ToolBar::position, \l TabBar::position,
460 or \l DialogButtonBox::position property to \c Footer.
461
462 \sa menuBar, header, Page::footer
463*/
464QQuickItem *QQuickApplicationWindow::footer() const
465{
466 Q_D(const QQuickApplicationWindow);
467 return d->footer;
468}
469
470void QQuickApplicationWindow::setFooter(QQuickItem *footer)
471{
472 Q_D(QQuickApplicationWindow);
473 if (d->footer == footer)
474 return;
475
476 if (d->footer) {
477 QQuickItemPrivate::get(item: d->footer)->removeItemChangeListener(d, types: ItemChanges);
478 d->footer->setParentItem(nullptr);
479 }
480 d->footer = footer;
481 if (footer) {
482 footer->setParentItem(QQuickWindow::contentItem());
483 QQuickItemPrivate *p = QQuickItemPrivate::get(item: footer);
484 p->addItemChangeListener(listener: d, types: ItemChanges);
485 if (qFuzzyIsNull(d: footer->z()))
486 footer->setZ(1);
487 if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(object: footer))
488 toolBar->setPosition(QQuickToolBar::Footer);
489#if QT_CONFIG(quicktemplates2_container)
490 else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(object: footer))
491 tabBar->setPosition(QQuickTabBar::Footer);
492 else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(object: footer))
493 buttonBox->setPosition(QQuickDialogButtonBox::Footer);
494
495 footer->stackAfter(d->control);
496#endif
497 }
498 if (isComponentComplete())
499 d->relayout();
500 emit footerChanged();
501}
502
503/*!
504 \qmlproperty list<QtObject> QtQuick.Controls::ApplicationWindow::contentData
505 \qmldefault
506
507 This default property holds the list of all objects declared as children of
508 the window.
509
510 The data property allows you to freely mix visual children, resources and
511 other windows in an ApplicationWindow.
512
513 If you assign an Item to the contentData list, it becomes a child of the
514 window's contentItem, so that it appears inside the window. The item's
515 parent will be the window's \l contentItem.
516
517 It should not generally be necessary to refer to the contentData property,
518 as it is the default property for ApplicationWindow and thus all child
519 items are automatically assigned to this property.
520
521 \sa contentItem
522*/
523QQmlListProperty<QObject> QQuickApplicationWindowPrivate::contentData()
524{
525 Q_Q(QQuickApplicationWindow);
526 return QQmlListProperty<QObject>(q->contentItem(), q,
527 QQuickApplicationWindowPrivate::contentData_append,
528 QQuickItemPrivate::data_count,
529 QQuickItemPrivate::data_at,
530 QQuickItemPrivate::data_clear);
531}
532
533/*!
534 \qmlproperty Item QtQuick.Controls::ApplicationWindow::contentItem
535 \readonly
536
537 This property holds the window content item.
538
539 The content item is stacked above the \l background item, and under the
540 \l menuBar, \l header, and \l footer items.
541
542 Since Qt 6.9 ApplicationWindow will automatically add padding to the
543 contentItem for any \l{SafeArea} {safe area margins} reported by the
544 window. To override the padding use the individual padding properties.
545
546 \sa background, menuBar, header, footer,
547 topPadding, bottomPadding, leftPadding, rightPadding
548*/
549QQuickItem *QQuickApplicationWindow::contentItem() const
550{
551 Q_D(const QQuickApplicationWindow);
552 return d->control->contentItem();
553}
554
555/*!
556 \qmlproperty real QtQuick.Controls::ApplicationWindow::topPadding
557 \since 6.9
558
559 This property holds the top padding of the window's content item.
560 Unless explicitly set, the value reflects the window's \l{SafeArea}
561 {safe area margins}.
562
563 \sa bottomPadding, leftPadding, rightPadding
564*/
565
566/*!
567 \qmlproperty real QtQuick.Controls::ApplicationWindow::leftPadding
568 \since 6.9
569
570 This property holds the left padding of the window's content item.
571 Unless explicitly set, the value reflects the window's \l{SafeArea}
572 {safe area margins}.
573
574 \sa bottomPadding, topPadding, rightPadding
575*/
576
577/*!
578 \qmlproperty real QtQuick.Controls::ApplicationWindow::rightPadding
579 \since 6.9
580
581 This property holds the right padding of the window's content item.
582 Unless explicitly set, the value reflects the window's \l{SafeArea}
583 {safe area margins}.
584
585 \sa bottomPadding, leftPadding, topPadding
586*/
587
588/*!
589 \qmlproperty real QtQuick.Controls::ApplicationWindow::bottomPadding
590 \since 6.9
591
592 This property holds the bottom padding of the window's content item.
593 Unless explicitly set, the value reflects the window's \l{SafeArea}
594 {safe area margins}.
595
596 \sa topPadding, leftPadding, rightPadding
597*/
598
599/*!
600 \qmlproperty Control QtQuick.Controls::ApplicationWindow::activeFocusControl
601 \readonly
602
603 This property holds the control that currently has active focus, or \c null if there is
604 no control with active focus.
605
606 The difference between \l Window::activeFocusItem and ApplicationWindow::activeFocusControl
607 is that the former may point to a building block of a control, whereas the latter points
608 to the enclosing control. For example, when SpinBox has focus, activeFocusItem points to
609 the editor and activeFocusControl to the SpinBox itself.
610
611 \sa Window::activeFocusItem
612*/
613QQuickItem *QQuickApplicationWindow::activeFocusControl() const
614{
615 Q_D(const QQuickApplicationWindow);
616 return d->activeFocusControl;
617}
618
619/*!
620 \qmlproperty font QtQuick.Controls::ApplicationWindow::font
621
622 This property holds the font currently set for the window.
623
624 The default font depends on the system environment. QGuiApplication maintains a system/theme
625 font which serves as a default for all application windows. You can also set the default font
626 for windows by passing a custom font to QGuiApplication::setFont(), before loading any QML.
627 Finally, the font is matched against Qt's font database to find the best match.
628
629 ApplicationWindow propagates explicit font properties to child controls. If you change a specific
630 property on the window's font, that property propagates to all child controls in the window,
631 overriding any system defaults for that property.
632
633 \sa Control::font
634*/
635QFont QQuickApplicationWindow::font() const
636{
637 Q_D(const QQuickApplicationWindow);
638 return d->font;
639}
640
641void QQuickApplicationWindow::setFont(const QFont &font)
642{
643 Q_D(QQuickApplicationWindow);
644 if (d->font.resolveMask() == font.resolveMask() && d->font == font)
645 return;
646
647 QFont resolvedFont = font.resolve(QQuickTheme::font(scope: QQuickTheme::System));
648 d->setFont_helper(resolvedFont);
649}
650
651void QQuickApplicationWindow::resetFont()
652{
653 setFont(QFont());
654}
655
656/*!
657 \qmlproperty Locale QtQuick.Controls::ApplicationWindow::locale
658
659 This property holds the locale of the window.
660
661 The default locale depends on the system environment. You can set the
662 default locale by calling QLocale::setDefault(), before loading any QML.
663
664 ApplicationWindow propagates the locale to child controls. If you change
665 the window's locale, that locale propagates to all child controls in the
666 window, overriding the system default locale.
667
668 \sa Control::locale
669*/
670QLocale QQuickApplicationWindow::locale() const
671{
672 Q_D(const QQuickApplicationWindow);
673 return d->locale;
674}
675
676void QQuickApplicationWindow::setLocale(const QLocale &locale)
677{
678 Q_D(QQuickApplicationWindow);
679 if (d->locale == locale)
680 return;
681
682 d->locale = locale;
683 QQuickControlPrivate::updateLocaleRecur(item: QQuickWindow::contentItem(), l: locale);
684
685 // TODO: internal QQuickPopupManager that provides reliable access to all QQuickPopup instances
686 const QList<QQuickPopup *> popups = QQuickWindow::contentItem()->findChildren<QQuickPopup *>();
687 for (QQuickPopup *popup : popups)
688 QQuickControlPrivate::get(control: static_cast<QQuickControl *>(popup->popupItem()))->updateLocale(l: locale, e: false); // explicit=false
689
690 emit localeChanged();
691}
692
693void QQuickApplicationWindow::resetLocale()
694{
695 setLocale(QLocale());
696}
697
698/*!
699 \since QtQuick.Controls 2.3 (Qt 5.10)
700 \qmlproperty Item QtQuick.Controls::ApplicationWindow::menuBar
701
702 This property holds the window menu bar. The menu bar is positioned at the
703 top of the window, above the header, and resized to the width of the window.
704 The default value is \c null.
705
706 \code
707 ApplicationWindow {
708 menuBar: MenuBar {
709 // ...
710 }
711 }
712 \endcode
713
714 \sa header, footer, MenuBar
715*/
716QQuickItem *QQuickApplicationWindow::menuBar() const
717{
718 Q_D(const QQuickApplicationWindow);
719 return d->menuBar;
720}
721
722void QQuickApplicationWindow::setMenuBar(QQuickItem *menuBar)
723{
724 Q_D(QQuickApplicationWindow);
725 if (d->menuBar == menuBar)
726 return;
727
728 if (d->menuBar) {
729 QQuickItemPrivate::get(item: d->menuBar)->removeItemChangeListener(d, types: ItemChanges);
730 d->menuBar->setParentItem(nullptr);
731 }
732 d->menuBar = menuBar;
733 if (menuBar) {
734 menuBar->setParentItem(QQuickWindow::contentItem());
735 QQuickItemPrivate *p = QQuickItemPrivate::get(item: menuBar);
736 p->addItemChangeListener(listener: d, types: ItemChanges);
737 if (qFuzzyIsNull(d: menuBar->z()))
738 menuBar->setZ(2);
739
740 if (header())
741 menuBar->stackBefore(header());
742 else
743 menuBar->stackBefore(d->control);
744 }
745 if (isComponentComplete())
746 d->relayout();
747 emit menuBarChanged();
748}
749
750bool QQuickApplicationWindow::isComponentComplete() const
751{
752 Q_D(const QQuickApplicationWindow);
753 return d->componentComplete;
754}
755
756void QQuickApplicationWindow::classBegin()
757{
758 Q_D(QQuickApplicationWindow);
759 d->componentComplete = false;
760 QQuickWindowQmlImpl::classBegin();
761 d->resolveFont();
762
763 /*
764 Create the control up front, rather than lazily, as we have
765 to set the default padding before any user-bindings override
766 them.
767
768 The hierarchy is:
769
770 contentItem (QQuickRootItem)
771 ├── menuBar
772 ├── header
773 ├── control (QQuickControl)
774 │ └── control->contentItem() (QQuickContentItem)
775 ├── footer
776 └── background
777 */
778 d->control = new QQuickControl(QQuickWindow::contentItem());
779 d->control->setObjectName("ApplicationWindowContentControl");
780 auto *contentItem = new QQuickContentItem(this, d->control);
781 // The content item can't be its own focus scope here, as that
782 // will detach focus of items inside the content item from focus
783 // in the menubar, header and footer. Nor can set set the content
784 // item as focused, as that will prevent child items of the content
785 // item from getting the initial focus when they are reparented
786 // into the content item.
787 d->control->setContentItem(contentItem);
788
789 auto *context = qmlContext(this);
790 auto installPropertyBinding = [&](QObject *targetObject, const QString &targetPropertyName,
791 QObject *sourceObject, const QString &sourcePropertyName) {
792 const QQmlProperty targetProperty(targetObject, targetPropertyName);
793 QQmlAnyBinding binding = QQmlPropertyToPropertyBinding::create(
794 engine: context->engine(), source: QQmlProperty(sourceObject, sourcePropertyName), target: targetProperty);
795 binding.installOn(target: targetProperty);
796 };
797
798 // Pick up safe area margins from the control, which reflects both
799 // margins coming from the QWindow, additional margins added by the
800 // user to the QQuickWindow's content item, as well the additional
801 // margins we add to the control directly to account for the header,
802 // footer and menu bar.
803 auto *controlSafeArea = qmlAttachedPropertiesObject<QQuickSafeArea>(obj: d->control);
804 installPropertyBinding(this, "leftPadding"_L1, controlSafeArea, "margins.left"_L1);
805 installPropertyBinding(this, "topPadding"_L1, controlSafeArea, "margins.top"_L1);
806 installPropertyBinding(this, "rightPadding"_L1, controlSafeArea, "margins.right"_L1);
807 installPropertyBinding(this, "bottomPadding"_L1, controlSafeArea, "margins.bottom"_L1);
808
809 // The additional margins for our header, footer and menu bar depend on the window margins
810 auto *windowSafeArea = static_cast<QQuickSafeArea*>(qmlAttachedPropertiesObject<QQuickSafeArea>(obj: this));
811 QObject::connect(sender: windowSafeArea, signal: &QQuickSafeArea::marginsChanged, context: this, slot: [d]{
812 d->relayout();
813 });
814}
815
816void QQuickApplicationWindow::componentComplete()
817{
818 Q_D(QQuickApplicationWindow);
819 d->componentComplete = true;
820 QQuickWindow::contentItem()->setObjectName(QQmlMetaType::prettyTypeName(object: this));
821 d->executeBackground(complete: true);
822 QQuickWindowQmlImpl::componentComplete();
823 d->relayout();
824}
825
826void QQuickApplicationWindow::resizeEvent(QResizeEvent *event)
827{
828 Q_D(QQuickApplicationWindow);
829 QQuickWindowQmlImpl::resizeEvent(event);
830 d->relayout();
831}
832
833class QQuickApplicationWindowAttachedPrivate : public QObjectPrivate
834{
835public:
836 Q_DECLARE_PUBLIC(QQuickApplicationWindowAttached)
837
838 void windowChange(QQuickWindow *wnd);
839 void activeFocusChange();
840
841 QQuickWindow *window = nullptr;
842 QQuickItem *activeFocusControl = nullptr;
843};
844
845void QQuickApplicationWindowAttachedPrivate::windowChange(QQuickWindow *wnd)
846{
847 Q_Q(QQuickApplicationWindowAttached);
848 if (window == wnd)
849 return;
850
851 QQuickApplicationWindow *oldWindow = qobject_cast<QQuickApplicationWindow *>(object: window);
852 if (oldWindow && !QQuickApplicationWindowPrivate::get(window: oldWindow))
853 oldWindow = nullptr; // being deleted (QTBUG-52731)
854
855 if (oldWindow) {
856 disconnect(sender: oldWindow, signal: &QQuickApplicationWindow::activeFocusControlChanged,
857 receiverPrivate: this, slot: &QQuickApplicationWindowAttachedPrivate::activeFocusChange);
858 QObject::disconnect(sender: oldWindow, signal: &QQuickApplicationWindow::menuBarChanged,
859 receiver: q, slot: &QQuickApplicationWindowAttached::menuBarChanged);
860 QObject::disconnect(sender: oldWindow, signal: &QQuickApplicationWindow::headerChanged,
861 receiver: q, slot: &QQuickApplicationWindowAttached::headerChanged);
862 QObject::disconnect(sender: oldWindow, signal: &QQuickApplicationWindow::footerChanged,
863 receiver: q, slot: &QQuickApplicationWindowAttached::footerChanged);
864 } else if (window) {
865 disconnect(sender: window, signal: &QQuickWindow::activeFocusItemChanged,
866 receiverPrivate: this, slot: &QQuickApplicationWindowAttachedPrivate::activeFocusChange);
867 }
868
869 QQuickApplicationWindow *newWindow = qobject_cast<QQuickApplicationWindow *>(object: wnd);
870 if (newWindow) {
871 connect(sender: newWindow, signal: &QQuickApplicationWindow::activeFocusControlChanged,
872 receiverPrivate: this, slot: &QQuickApplicationWindowAttachedPrivate::activeFocusChange);
873 QObject::connect(sender: newWindow, signal: &QQuickApplicationWindow::menuBarChanged,
874 context: q, slot: &QQuickApplicationWindowAttached::menuBarChanged);
875 QObject::connect(sender: newWindow, signal: &QQuickApplicationWindow::headerChanged,
876 context: q, slot: &QQuickApplicationWindowAttached::headerChanged);
877 QObject::connect(sender: newWindow, signal: &QQuickApplicationWindow::footerChanged,
878 context: q, slot: &QQuickApplicationWindowAttached::footerChanged);
879 } else if (wnd) {
880 connect(sender: wnd, signal: &QQuickWindow::activeFocusItemChanged,
881 receiverPrivate: this, slot: &QQuickApplicationWindowAttachedPrivate::activeFocusChange);
882 }
883
884 window = wnd;
885 emit q->windowChanged();
886 emit q->contentItemChanged();
887
888 activeFocusChange();
889 if ((oldWindow && oldWindow->menuBar()) || (newWindow && newWindow->menuBar()))
890 emit q->menuBarChanged();
891 if ((oldWindow && oldWindow->header()) || (newWindow && newWindow->header()))
892 emit q->headerChanged();
893 if ((oldWindow && oldWindow->footer()) || (newWindow && newWindow->footer()))
894 emit q->footerChanged();
895}
896
897void QQuickApplicationWindowAttachedPrivate::activeFocusChange()
898{
899 Q_Q(QQuickApplicationWindowAttached);
900 QQuickItem *control = nullptr;
901 if (QQuickApplicationWindow *appWindow = qobject_cast<QQuickApplicationWindow *>(object: window))
902 control = appWindow->activeFocusControl();
903 else if (window)
904 control = findActiveFocusControl(window);
905 if (activeFocusControl == control)
906 return;
907
908 activeFocusControl = control;
909 emit q->activeFocusControlChanged();
910}
911
912QQuickApplicationWindowAttached::QQuickApplicationWindowAttached(QObject *parent)
913 : QObject(*(new QQuickApplicationWindowAttachedPrivate), parent)
914{
915 Q_D(QQuickApplicationWindowAttached);
916 if (QQuickItem *item = qobject_cast<QQuickItem *>(o: parent)) {
917 d->windowChange(wnd: item->window());
918 QObjectPrivate::connect(sender: item, signal: &QQuickItem::windowChanged, receiverPrivate: d, slot: &QQuickApplicationWindowAttachedPrivate::windowChange);
919 if (!d->window) {
920 QQuickItem *p = item;
921 while (p) {
922 if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(object: p->parent())) {
923 d->windowChange(wnd: popup->window());
924 QObjectPrivate::connect(sender: popup, signal: &QQuickPopup::windowChanged, receiverPrivate: d, slot: &QQuickApplicationWindowAttachedPrivate::windowChange);
925 }
926 p = p->parentItem();
927 }
928 }
929 } else if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(object: parent)) {
930 d->windowChange(wnd: popup->window());
931 QObjectPrivate::connect(sender: popup, signal: &QQuickPopup::windowChanged, receiverPrivate: d, slot: &QQuickApplicationWindowAttachedPrivate::windowChange);
932 }
933}
934
935/*!
936 \qmlattachedproperty ApplicationWindow QtQuick.Controls::ApplicationWindow::window
937 \readonly
938
939 This attached property holds the application window. The property can be attached
940 to any item. The value is \c null if the item is not in an ApplicationWindow.
941
942 \sa {Attached ApplicationWindow Properties}
943*/
944QQuickApplicationWindow *QQuickApplicationWindowAttached::window() const
945{
946 Q_D(const QQuickApplicationWindowAttached);
947 return qobject_cast<QQuickApplicationWindow *>(object: d->window);
948}
949
950/*!
951 \qmlattachedproperty Item QtQuick.Controls::ApplicationWindow::contentItem
952 \readonly
953
954 This attached property holds the window content item. The property can be attached
955 to any item. The value is \c null if the item is not in an ApplicationWindow.
956
957 \sa {Attached ApplicationWindow Properties}
958*/
959QQuickItem *QQuickApplicationWindowAttached::contentItem() const
960{
961 Q_D(const QQuickApplicationWindowAttached);
962 if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(object: d->window))
963 return window->contentItem();
964 return nullptr;
965}
966
967/*!
968 \qmlattachedproperty Control QtQuick.Controls::ApplicationWindow::activeFocusControl
969 \readonly
970
971 This attached property holds the control that currently has active focus, or \c null
972 if there is no control with active focus. The property can be attached to any item.
973 The value is \c null if the item is not in a window, or the window has no active focus.
974
975 \sa Window::activeFocusItem, {Attached ApplicationWindow Properties}
976*/
977QQuickItem *QQuickApplicationWindowAttached::activeFocusControl() const
978{
979 Q_D(const QQuickApplicationWindowAttached);
980 return d->activeFocusControl;
981}
982
983/*!
984 \qmlattachedproperty Item QtQuick.Controls::ApplicationWindow::header
985 \readonly
986
987 This attached property holds the window header item. The property can be attached
988 to any item. The value is \c null if the item is not in an ApplicationWindow, or
989 the window has no header item.
990
991 \sa {Attached ApplicationWindow Properties}
992*/
993QQuickItem *QQuickApplicationWindowAttached::header() const
994{
995 Q_D(const QQuickApplicationWindowAttached);
996 if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(object: d->window))
997 return window->header();
998 return nullptr;
999}
1000
1001/*!
1002 \qmlattachedproperty Item QtQuick.Controls::ApplicationWindow::footer
1003 \readonly
1004
1005 This attached property holds the window footer item. The property can be attached
1006 to any item. The value is \c null if the item is not in an ApplicationWindow, or
1007 the window has no footer item.
1008
1009 \sa {Attached ApplicationWindow Properties}
1010*/
1011QQuickItem *QQuickApplicationWindowAttached::footer() const
1012{
1013 Q_D(const QQuickApplicationWindowAttached);
1014 if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(object: d->window))
1015 return window->footer();
1016 return nullptr;
1017}
1018
1019/*!
1020 \since QtQuick.Controls 2.3 (Qt 5.10)
1021 \qmlattachedproperty Item QtQuick.Controls::ApplicationWindow::menuBar
1022 \readonly
1023
1024 This attached property holds the window menu bar. The property can be attached
1025 to any item. The value is \c null if the item is not in an ApplicationWindow, or
1026 the window has no menu bar.
1027
1028 \sa {Attached ApplicationWindow Properties}
1029*/
1030QQuickItem *QQuickApplicationWindowAttached::menuBar() const
1031{
1032 Q_D(const QQuickApplicationWindowAttached);
1033 if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(object: d->window))
1034 return window->menuBar();
1035 return nullptr;
1036}
1037
1038QT_END_NAMESPACE
1039
1040#include "moc_qquickapplicationwindow_p.cpp"
1041

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