aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates')
-rw-r--r--src/quicktemplates/CMakeLists.txt71
-rw-r--r--src/quicktemplates/configure.cmake7
-rw-r--r--src/quicktemplates/qquickabstractbutton.cpp191
-rw-r--r--src/quicktemplates/qquickabstractbutton_p.h6
-rw-r--r--src/quicktemplates/qquickabstractbutton_p_p.h7
-rw-r--r--src/quicktemplates/qquickaction.cpp5
-rw-r--r--src/quicktemplates/qquickaction_p.h4
-rw-r--r--src/quicktemplates/qquickactiongroup.cpp2
-rw-r--r--src/quicktemplates/qquickactiongroup_p.h6
-rw-r--r--src/quicktemplates/qquickapplicationwindow.cpp94
-rw-r--r--src/quicktemplates/qquickapplicationwindow_p.h30
-rw-r--r--src/quicktemplates/qquickbusyindicator_p.h4
-rw-r--r--src/quicktemplates/qquickbutton_p.h4
-rw-r--r--src/quicktemplates/qquickbuttongroup.cpp21
-rw-r--r--src/quicktemplates/qquickbuttongroup_p.h6
-rw-r--r--src/quicktemplates/qquickcalendar_p.h2
-rw-r--r--src/quicktemplates/qquickcalendarmodel.cpp32
-rw-r--r--src/quicktemplates/qquickcalendarmodel_p.h8
-rw-r--r--src/quicktemplates/qquickcheckbox.cpp22
-rw-r--r--src/quicktemplates/qquickcheckbox_p.h9
-rw-r--r--src/quicktemplates/qquickcheckdelegate_p.h4
-rw-r--r--src/quicktemplates/qquickcombobox.cpp38
-rw-r--r--src/quicktemplates/qquickcombobox_p.h7
-rw-r--r--src/quicktemplates/qquickcontainer.cpp13
-rw-r--r--src/quicktemplates/qquickcontainer_p.h6
-rw-r--r--src/quicktemplates/qquickcontainer_p_p.h2
-rw-r--r--src/quicktemplates/qquickcontentitem_p.h2
-rw-r--r--src/quicktemplates/qquickcontrol.cpp141
-rw-r--r--src/quicktemplates/qquickcontrol_p.h36
-rw-r--r--src/quicktemplates/qquickcontrol_p_p.h14
-rw-r--r--src/quicktemplates/qquickdayofweekmodel_p.h2
-rw-r--r--src/quicktemplates/qquickdayofweekrow_p.h2
-rw-r--r--src/quicktemplates/qquickdeferredexecute.cpp3
-rw-r--r--src/quicktemplates/qquickdeferredexecute_p_p.h6
-rw-r--r--src/quicktemplates/qquickdeferredpointer_p_p.h4
-rw-r--r--src/quicktemplates/qquickdelaybutton_p.h4
-rw-r--r--src/quicktemplates/qquickdial.cpp243
-rw-r--r--src/quicktemplates/qquickdial_p.h21
-rw-r--r--src/quicktemplates/qquickdialog.cpp32
-rw-r--r--src/quicktemplates/qquickdialog_p.h8
-rw-r--r--src/quicktemplates/qquickdialog_p_p.h4
-rw-r--r--src/quicktemplates/qquickdialogbuttonbox.cpp14
-rw-r--r--src/quicktemplates/qquickdialogbuttonbox_p.h14
-rw-r--r--src/quicktemplates/qquickdialogbuttonbox_p_p.h2
-rw-r--r--src/quicktemplates/qquickdrawer.cpp106
-rw-r--r--src/quicktemplates/qquickdrawer_p.h4
-rw-r--r--src/quicktemplates/qquickdrawer_p_p.h10
-rw-r--r--src/quicktemplates/qquickframe_p.h4
-rw-r--r--src/quicktemplates/qquickframe_p_p.h2
-rw-r--r--src/quicktemplates/qquickgroupbox.cpp11
-rw-r--r--src/quicktemplates/qquickgroupbox_p.h4
-rw-r--r--src/quicktemplates/qquickheaderview.cpp235
-rw-r--r--src/quicktemplates/qquickheaderview_p.h31
-rw-r--r--src/quicktemplates/qquickheaderview_p_p.h15
-rw-r--r--src/quicktemplates/qquickicon_p.h2
-rw-r--r--src/quicktemplates/qquickindicatorbutton_p.cpp10
-rw-r--r--src/quicktemplates/qquickindicatorbutton_p.h3
-rw-r--r--src/quicktemplates/qquickitemdelegate.cpp5
-rw-r--r--src/quicktemplates/qquickitemdelegate_p.h4
-rw-r--r--src/quicktemplates/qquickitemdelegate_p_p.h2
-rw-r--r--src/quicktemplates/qquicklabel.cpp4
-rw-r--r--src/quicktemplates/qquicklabel_p.h12
-rw-r--r--src/quicktemplates/qquickmenu.cpp760
-rw-r--r--src/quicktemplates/qquickmenu_p.h14
-rw-r--r--src/quicktemplates/qquickmenu_p_p.h42
-rw-r--r--src/quicktemplates/qquickmenubar.cpp566
-rw-r--r--src/quicktemplates/qquickmenubar_p.h9
-rw-r--r--src/quicktemplates/qquickmenubar_p_p.h39
-rw-r--r--src/quicktemplates/qquickmenubaritem.cpp83
-rw-r--r--src/quicktemplates/qquickmenubaritem_p.h10
-rw-r--r--src/quicktemplates/qquickmenubaritem_p_p.h3
-rw-r--r--src/quicktemplates/qquickmenuitem.cpp79
-rw-r--r--src/quicktemplates/qquickmenuitem_p.h19
-rw-r--r--src/quicktemplates/qquickmenuitem_p_p.h1
-rw-r--r--src/quicktemplates/qquickmenuseparator.cpp2
-rw-r--r--src/quicktemplates/qquickmenuseparator_p.h4
-rw-r--r--src/quicktemplates/qquickmonthgrid.cpp21
-rw-r--r--src/quicktemplates/qquickmonthgrid_p.h10
-rw-r--r--src/quicktemplates/qquickmonthmodel.cpp2
-rw-r--r--src/quicktemplates/qquickmonthmodel_p.h4
-rw-r--r--src/quicktemplates/qquicknativeicon.cpp50
-rw-r--r--src/quicktemplates/qquicknativeicon_p.h57
-rw-r--r--src/quicktemplates/qquicknativeiconloader.cpp66
-rw-r--r--src/quicktemplates/qquicknativeiconloader_p.h54
-rw-r--r--src/quicktemplates/qquicknativemenuitem.cpp329
-rw-r--r--src/quicktemplates/qquicknativemenuitem_p.h81
-rw-r--r--src/quicktemplates/qquickoverlay.cpp70
-rw-r--r--src/quicktemplates/qquickoverlay_p.h6
-rw-r--r--src/quicktemplates/qquickoverlay_p_p.h4
-rw-r--r--src/quicktemplates/qquickpage.cpp23
-rw-r--r--src/quicktemplates/qquickpage_p.h7
-rw-r--r--src/quicktemplates/qquickpageindicator.cpp7
-rw-r--r--src/quicktemplates/qquickpageindicator_p.h5
-rw-r--r--src/quicktemplates/qquickpane.cpp30
-rw-r--r--src/quicktemplates/qquickpane_p.h4
-rw-r--r--src/quicktemplates/qquickpane_p_p.h4
-rw-r--r--src/quicktemplates/qquickpopup.cpp618
-rw-r--r--src/quicktemplates/qquickpopup_p.h27
-rw-r--r--src/quicktemplates/qquickpopup_p_p.h34
-rw-r--r--src/quicktemplates/qquickpopupanchors_p.h6
-rw-r--r--src/quicktemplates/qquickpopupitem.cpp21
-rw-r--r--src/quicktemplates/qquickpopupitem_p_p.h8
-rw-r--r--src/quicktemplates/qquickpopuppositioner.cpp82
-rw-r--r--src/quicktemplates/qquickpopuppositioner_p_p.h2
-rw-r--r--src/quicktemplates/qquickpopupwindow.cpp266
-rw-r--r--src/quicktemplates/qquickpopupwindow_p_p.h57
-rw-r--r--src/quicktemplates/qquickpresshandler.cpp7
-rw-r--r--src/quicktemplates/qquickpresshandler_p_p.h4
-rw-r--r--src/quicktemplates/qquickprogressbar.cpp2
-rw-r--r--src/quicktemplates/qquickprogressbar_p.h4
-rw-r--r--src/quicktemplates/qquickradiobutton.cpp2
-rw-r--r--src/quicktemplates/qquickradiobutton_p.h4
-rw-r--r--src/quicktemplates/qquickradiodelegate.cpp2
-rw-r--r--src/quicktemplates/qquickradiodelegate_p.h4
-rw-r--r--src/quicktemplates/qquickrangeslider.cpp62
-rw-r--r--src/quicktemplates/qquickrangeslider_p.h6
-rw-r--r--src/quicktemplates/qquickroundbutton_p.h4
-rw-r--r--src/quicktemplates/qquickscrollbar.cpp84
-rw-r--r--src/quicktemplates/qquickscrollbar_p.h6
-rw-r--r--src/quicktemplates/qquickscrollbar_p_p.h1
-rw-r--r--src/quicktemplates/qquickscrollindicator.cpp25
-rw-r--r--src/quicktemplates/qquickscrollindicator_p.h6
-rw-r--r--src/quicktemplates/qquickscrollview.cpp192
-rw-r--r--src/quicktemplates/qquickscrollview_p.h13
-rw-r--r--src/quicktemplates/qquickselectionrectangle.cpp147
-rw-r--r--src/quicktemplates/qquickselectionrectangle_p.h8
-rw-r--r--src/quicktemplates/qquickselectionrectangle_p_p.h2
-rw-r--r--src/quicktemplates/qquickshortcutcontext.cpp12
-rw-r--r--src/quicktemplates/qquickshortcutcontext_p_p.h2
-rw-r--r--src/quicktemplates/qquickslider.cpp20
-rw-r--r--src/quicktemplates/qquickslider_p.h4
-rw-r--r--src/quicktemplates/qquickspinbox.cpp193
-rw-r--r--src/quicktemplates/qquickspinbox_p.h10
-rw-r--r--src/quicktemplates/qquicksplitview.cpp300
-rw-r--r--src/quicktemplates/qquicksplitview_p.h12
-rw-r--r--src/quicktemplates/qquicksplitview_p_p.h11
-rw-r--r--src/quicktemplates/qquickstackelement.cpp58
-rw-r--r--src/quicktemplates/qquickstackelement_p_p.h16
-rw-r--r--src/quicktemplates/qquickstacktransition_p_p.h4
-rw-r--r--src/quicktemplates/qquickstackview.cpp520
-rw-r--r--src/quicktemplates/qquickstackview_p.cpp123
-rw-r--r--src/quicktemplates/qquickstackview_p.h81
-rw-r--r--src/quicktemplates/qquickstackview_p_p.h27
-rw-r--r--src/quicktemplates/qquickswipe_p.h2
-rw-r--r--src/quicktemplates/qquickswipedelegate.cpp56
-rw-r--r--src/quicktemplates/qquickswipedelegate_p.h5
-rw-r--r--src/quicktemplates/qquickswipedelegate_p_p.h1
-rw-r--r--src/quicktemplates/qquickswipeview.cpp41
-rw-r--r--src/quicktemplates/qquickswipeview_p.h9
-rw-r--r--src/quicktemplates/qquickswitch_p.h4
-rw-r--r--src/quicktemplates/qquickswitchdelegate_p.h4
-rw-r--r--src/quicktemplates/qquicktabbar_p.h8
-rw-r--r--src/quicktemplates/qquicktabbutton.cpp2
-rw-r--r--src/quicktemplates/qquicktabbutton_p.h4
-rw-r--r--src/quicktemplates/qquicktextarea.cpp89
-rw-r--r--src/quicktemplates/qquicktextarea_p.h14
-rw-r--r--src/quicktemplates/qquicktextarea_p_p.h11
-rw-r--r--src/quicktemplates/qquicktextfield.cpp48
-rw-r--r--src/quicktemplates/qquicktextfield_p.h12
-rw-r--r--src/quicktemplates/qquicktextfield_p_p.h3
-rw-r--r--src/quicktemplates/qquicktheme_p.h2
-rw-r--r--src/quicktemplates/qquicktheme_p_p.h2
-rw-r--r--src/quicktemplates/qquicktoolbar_p.h4
-rw-r--r--src/quicktemplates/qquicktoolbutton.cpp2
-rw-r--r--src/quicktemplates/qquicktoolbutton_p.h4
-rw-r--r--src/quicktemplates/qquicktoolseparator_p.h4
-rw-r--r--src/quicktemplates/qquicktooltip.cpp7
-rw-r--r--src/quicktemplates/qquicktooltip_p.h6
-rw-r--r--src/quicktemplates/qquicktreeviewdelegate.cpp8
-rw-r--r--src/quicktemplates/qquicktreeviewdelegate_p.h2
-rw-r--r--src/quicktemplates/qquicktumbler.cpp3
-rw-r--r--src/quicktemplates/qquicktumbler_p.h6
-rw-r--r--src/quicktemplates/qquicktumbler_p_p.h4
-rw-r--r--src/quicktemplates/qquickweeknumbercolumn_p.h2
-rw-r--r--src/quicktemplates/qquickweeknumbermodel_p.h2
-rw-r--r--src/quicktemplates/qtquicktemplates2global_p.h10
176 files changed, 6119 insertions, 1443 deletions
diff --git a/src/quicktemplates/CMakeLists.txt b/src/quicktemplates/CMakeLists.txt
index 57f4897b53..6a98ce2f89 100644
--- a/src/quicktemplates/CMakeLists.txt
+++ b/src/quicktemplates/CMakeLists.txt
@@ -26,9 +26,6 @@ qt_internal_add_qml_module(QuickTemplates2
qquickbuttongroup.cpp qquickbuttongroup_p.h
qquickcheckbox.cpp qquickcheckbox_p.h
qquickcheckdelegate.cpp qquickcheckdelegate_p.h
- qquickcombobox.cpp qquickcombobox_p.h
- qquickcontainer.cpp qquickcontainer_p.h
- qquickcontainer_p_p.h
qquickcontentitem.cpp qquickcontentitem_p.h
qquickcontrol.cpp qquickcontrol_p.h
qquickcontrol_p_p.h
@@ -37,10 +34,6 @@ qt_internal_add_qml_module(QuickTemplates2
qquickdeferredpointer_p_p.h
qquickdelaybutton.cpp qquickdelaybutton_p.h
qquickdial.cpp qquickdial_p.h
- qquickdialog.cpp qquickdialog_p.h
- qquickdialog_p_p.h
- qquickdialogbuttonbox.cpp qquickdialogbuttonbox_p.h
- qquickdialogbuttonbox_p_p.h
qquickdrawer.cpp qquickdrawer_p.h
qquickdrawer_p_p.h
qquickframe.cpp qquickframe_p.h
@@ -52,15 +45,13 @@ qt_internal_add_qml_module(QuickTemplates2
qquickitemdelegate_p_p.h
qquicklabel.cpp qquicklabel_p.h
qquicklabel_p_p.h
- qquickmenu.cpp qquickmenu_p.h
- qquickmenu_p_p.h
- qquickmenubar.cpp qquickmenubar_p.h
- qquickmenubar_p_p.h
- qquickmenubaritem.cpp qquickmenubaritem_p.h
- qquickmenubaritem_p_p.h
- qquickmenuitem.cpp qquickmenuitem_p.h
- qquickmenuitem_p_p.h
qquickmenuseparator.cpp qquickmenuseparator_p.h
+ qquicknativeicon_p.h
+ qquicknativeicon.cpp
+ qquicknativeiconloader_p.h
+ qquicknativeiconloader.cpp
+ qquicknativemenuitem_p.h
+ qquicknativemenuitem.cpp
qquickoverlay.cpp qquickoverlay_p.h
qquickoverlay_p_p.h
qquickpage.cpp qquickpage_p.h
@@ -76,6 +67,8 @@ qt_internal_add_qml_module(QuickTemplates2
qquickpopupitem_p_p.h
qquickpopuppositioner.cpp
qquickpopuppositioner_p_p.h
+ qquickpopupwindow.cpp
+ qquickpopupwindow_p_p.h
qquickpresshandler.cpp
qquickpresshandler_p_p.h
qquickprogressbar.cpp qquickprogressbar_p.h
@@ -91,20 +84,15 @@ qt_internal_add_qml_module(QuickTemplates2
qquickshortcutcontext_p_p.h
qquickslider.cpp qquickslider_p.h
qquickspinbox.cpp qquickspinbox_p.h
- qquicksplitview.cpp qquicksplitview_p.h
qquickstackelement.cpp
qquickstackelement_p_p.h
- qquickstacktransition.cpp
- qquickstacktransition_p_p.h
qquickstackview.cpp qquickstackview_p.cpp qquickstackview_p.h
qquickstackview_p_p.h
qquickswipe_p.h
qquickswipedelegate.cpp qquickswipedelegate_p.h
qquickswipedelegate_p_p.h
- qquickswipeview.cpp qquickswipeview_p.h
qquickswitch.cpp qquickswitch_p.h
qquickswitchdelegate.cpp qquickswitchdelegate_p.h
- qquicktabbar.cpp qquicktabbar_p.h
qquicktabbutton.cpp qquicktabbutton_p.h
qquicktextarea.cpp qquicktextarea_p.h
qquicktextarea_p_p.h
@@ -119,6 +107,8 @@ qt_internal_add_qml_module(QuickTemplates2
qquickvelocitycalculator.cpp
qquickvelocitycalculator_p_p.h
qtquicktemplates2global.cpp qtquicktemplates2global_p.h
+ NO_UNITY_BUILD_SOURCES
+ qquickpopupitem.cpp # redefinition of 'contentItemName' (from qquickcontrol.cpp)
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
@@ -133,9 +123,7 @@ qt_internal_add_qml_module(QuickTemplates2
Qt::Core
Qt::Gui
Qt::Quick
- GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
-)
+ )
qt_internal_extend_target(QuickTemplates2 CONDITION TARGET Qt::QmlModels
LIBRARIES
@@ -151,6 +139,31 @@ qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_accessibility
accessible/qaccessiblequickpage.cpp accessible/qaccessiblequickpage_p.h
)
+qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quicktemplates2_container
+ SOURCES
+ qquickcontainer.cpp qquickcontainer_p.h
+ qquickcontainer_p_p.h
+ qquickdialog.cpp qquickdialog_p.h
+ qquickdialog_p_p.h
+ qquickdialogbuttonbox.cpp qquickdialogbuttonbox_p.h
+ qquickdialogbuttonbox_p_p.h
+ qquickmenubar.cpp qquickmenubar_p.h
+ qquickmenubar_p_p.h
+ qquickmenubaritem.cpp qquickmenubaritem_p.h
+ qquickmenubaritem_p_p.h
+ qquicksplitview.cpp qquicksplitview_p.h
+ qquickswipeview.cpp qquickswipeview_p.h
+ qquicktabbar.cpp qquicktabbar_p.h
+)
+
+qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_qml_object_model
+ SOURCES
+ qquickmenu.cpp qquickmenu_p.h
+ qquickmenu_p_p.h
+ qquickmenuitem.cpp qquickmenuitem_p.h
+ qquickmenuitem_p_p.h
+)
+
qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quick_tableview
SOURCES
qquickheaderview.cpp qquickheaderview_p.h
@@ -182,6 +195,18 @@ qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quicktemplates2_c
qquickweeknumbermodel.cpp qquickweeknumbermodel_p.h
)
+qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quick_viewtransitions
+ SOURCES
+ qquickstacktransition.cpp
+ qquickstacktransition_p_p.h
+)
+
+qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_qml_delegate_model
+ SOURCES
+ qquickcombobox.cpp
+ qquickcombobox_p.h
+)
+
qt_internal_extend_Target(qtquicktemplates2plugin
SOURCES
qtquicktemplates2plugin.cpp
diff --git a/src/quicktemplates/configure.cmake b/src/quicktemplates/configure.cmake
index 3b9cb00fc2..77683e8105 100644
--- a/src/quicktemplates/configure.cmake
+++ b/src/quicktemplates/configure.cmake
@@ -31,6 +31,13 @@ qt_feature("quicktemplates2-calendar" PRIVATE
SECTION "Quick Templates 2"
LABEL "Calendar support"
PURPOSE "Provides calendar types."
+ CONDITION QT_FEATURE_itemmodel
+)
+qt_feature("quicktemplates2-container" PRIVATE
+ SECTION "Quick Templates 2"
+ LABEL "Container controls support"
+ PURPOSE "Provides support for Container and its sub-classes."
+ CONDITION QT_FEATURE_qml_object_model
)
qt_configure_add_summary_section(NAME "Qt Quick Templates 2")
qt_configure_add_summary_entry(ARGS "quicktemplates2-hover")
diff --git a/src/quicktemplates/qquickabstractbutton.cpp b/src/quicktemplates/qquickabstractbutton.cpp
index e30c090379..ff2d9715c1 100644
--- a/src/quicktemplates/qquickabstractbutton.cpp
+++ b/src/quicktemplates/qquickabstractbutton.cpp
@@ -3,6 +3,7 @@
#include "qquickabstractbutton_p.h"
#include "qquickabstractbutton_p_p.h"
+#include "qquickactiongroup_p.h"
#include "qquickbuttongroup_p.h"
#include "qquickaction_p.h"
#include "qquickaction_p_p.h"
@@ -17,6 +18,8 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <QtQml/qqmllist.h>
QT_BEGIN_NAMESPACE
@@ -62,6 +65,8 @@ QT_BEGIN_NAMESPACE
\qmlsignal QtQuick.Controls::AbstractButton::clicked()
This signal is emitted when the button is interactively clicked by the user via touch, mouse, or keyboard.
+
+ \sa click(), animateClick(), {Call a C++ function from QML when a Button is clicked}
*/
/*!
@@ -84,6 +89,30 @@ QT_BEGIN_NAMESPACE
This signal is emitted when the button is interactively double clicked by the user via touch or mouse.
*/
+void QQuickAbstractButtonPrivate::init()
+{
+ Q_Q(QQuickAbstractButton);
+ q->setActiveFocusOnTab(true);
+#ifdef Q_OS_MACOS
+ q->setFocusPolicy(Qt::TabFocus);
+#else
+ q->setFocusPolicy(Qt::StrongFocus);
+#endif
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+#if QT_CONFIG(quicktemplates2_multitouch)
+ q->setAcceptTouchEvents(true);
+#endif
+#if QT_CONFIG(cursor)
+ q->setCursor(Qt::ArrowCursor);
+#endif
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+}
+
+QPointF QQuickAbstractButtonPrivate::centerPressPoint() const
+{
+ return QPointF(qRound(width / 2), qRound(height / 2));
+}
+
void QQuickAbstractButtonPrivate::setPressPoint(const QPointF &point)
{
pressPoint = point;
@@ -362,8 +391,6 @@ void QQuickAbstractButtonPrivate::toggle(bool value)
emit q->toggled();
}
-static inline QString indicatorName() { return QStringLiteral("indicator"); }
-
void QQuickAbstractButtonPrivate::cancelIndicator()
{
Q_Q(QQuickAbstractButton);
@@ -413,7 +440,7 @@ QQuickAbstractButton *QQuickAbstractButtonPrivate::findCheckedButton() const
{
Q_Q(const QQuickAbstractButton);
if (group)
- return qobject_cast<QQuickAbstractButton *>(group->checkedButton());
+ return group->checkedButton();
const QList<QQuickAbstractButton *> buttons = findExclusiveButtons();
// TODO: A singular QRadioButton can be unchecked, which seems logical,
@@ -457,45 +484,29 @@ QList<QQuickAbstractButton *> QQuickAbstractButtonPrivate::findExclusiveButtons(
QQuickAbstractButton::QQuickAbstractButton(QQuickItem *parent)
: QQuickControl(*(new QQuickAbstractButtonPrivate), parent)
{
- setActiveFocusOnTab(true);
-#ifdef Q_OS_MACOS
- setFocusPolicy(Qt::TabFocus);
-#else
- setFocusPolicy(Qt::StrongFocus);
-#endif
- setAcceptedMouseButtons(Qt::LeftButton);
-#if QT_CONFIG(quicktemplates2_multitouch)
- setAcceptTouchEvents(true);
-#endif
-#if QT_CONFIG(cursor)
- setCursor(Qt::ArrowCursor);
-#endif
+ Q_D(QQuickAbstractButton);
+ d->init();
}
QQuickAbstractButton::QQuickAbstractButton(QQuickAbstractButtonPrivate &dd, QQuickItem *parent)
: QQuickControl(dd, parent)
{
- setActiveFocusOnTab(true);
-#ifdef Q_OS_MACOS
- setFocusPolicy(Qt::TabFocus);
-#else
- setFocusPolicy(Qt::StrongFocus);
-#endif
- setAcceptedMouseButtons(Qt::LeftButton);
-#if QT_CONFIG(quicktemplates2_multitouch)
- setAcceptTouchEvents(true);
-#endif
-#if QT_CONFIG(cursor)
- setCursor(Qt::ArrowCursor);
-#endif
+ Q_D(QQuickAbstractButton);
+ d->init();
}
QQuickAbstractButton::~QQuickAbstractButton()
{
Q_D(QQuickAbstractButton);
d->removeImplicitSizeListener(d->indicator);
- if (d->group)
- d->group->removeButton(this);
+ if (d->group) {
+ auto *attached = qobject_cast<QQuickButtonGroupAttached *>(
+ qmlAttachedPropertiesObject<QQuickButtonGroup>(this, false));
+ if (attached)
+ attached->setGroup(nullptr);
+ else
+ d->group->removeButton(this);
+ }
#if QT_CONFIG(shortcut)
d->ungrabShortcut();
#endif
@@ -810,7 +821,7 @@ void QQuickAbstractButton::setIcon(const QQuickIcon &icon)
\header \li Display \li Result
\row \li \c AbstractButton.IconOnly \li \image qtquickcontrols-button-icononly.png
\row \li \c AbstractButton.TextOnly \li \image qtquickcontrols-button-textonly.png
- \row \li \c AbstractButton.TextBesideIcon \li \image qtquickcontrols-button-textbesideicon.png
+ \row \li \c AbstractButton.TextBesideIcon (default) \li \image qtquickcontrols-button-textbesideicon.png
\row \li \c AbstractButton.TextUnderIcon \li \image qtquickcontrols-button-textundericon.png
\endtable
@@ -880,6 +891,12 @@ void QQuickAbstractButton::setAction(QQuickAction *action)
setEnabled(action->isEnabled());
}
+#if QT_CONFIG(accessibility)
+ auto attached = qobject_cast<QQuickAccessibleAttached*>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(this, true));
+ Q_ASSERT(attached);
+ attached->setProxying(qobject_cast<QQuickAccessibleAttached*>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(action, true)));
+#endif
+
d->action = action;
if (oldText != text())
@@ -1042,6 +1059,8 @@ qreal QQuickAbstractButton::implicitIndicatorHeight() const
\qmlmethod void QtQuick.Controls::AbstractButton::toggle()
Toggles the checked state of the button.
+
+ \sa click(), animateClick()
*/
void QQuickAbstractButton::toggle()
{
@@ -1049,6 +1068,81 @@ void QQuickAbstractButton::toggle()
setChecked(!d->checked);
}
+/*!
+ \since Qt 6.8
+ \qmlmethod void QtQuick.Controls::AbstractButton::click()
+
+ Simulates the button being clicked with no delay between press and release.
+
+ All signals associated with a click are emitted as appropriate.
+
+ If the \l focusPolicy includes \c Qt.ClickFocus, \l activeFocus will
+ become \c true.
+
+ This function does nothing if the button is \l {enabled}{disabled}.
+
+ Calling this function again before the button is released resets
+ the release timer.
+
+ \sa animateClick(), pressed(), released(), clicked()
+*/
+void QQuickAbstractButton::click()
+{
+ Q_D(QQuickAbstractButton);
+ if (!isEnabled())
+ return;
+
+ // QQuickItemPrivate::deliverPointerEvent calls setFocusIfNeeded on real clicks,
+ // so we need to do it ourselves.
+ const bool setFocusOnPress = !QGuiApplication::styleHints()->setFocusOnTouchRelease();
+ if (setFocusOnPress && focusPolicy() & Qt::ClickFocus)
+ forceActiveFocus(Qt::MouseFocusReason);
+
+ const QPointF eventPos(d->width / 2, d->height / 2);
+ d->handlePress(eventPos, 0);
+ d->handleRelease(eventPos, 0);
+}
+
+/*!
+ \since Qt 6.8
+ \qmlmethod void QtQuick.Controls::AbstractButton::animateClick()
+
+ Simulates the button being clicked, with a 100 millisecond delay
+ between press and release, animating its visual state in the
+ process.
+
+ All signals associated with a click are emitted as appropriate.
+
+ If the \l focusPolicy includes \c Qt.ClickFocus, \l activeFocus will
+ become \c true.
+
+ This function does nothing if the button is \l {enabled}{disabled}.
+
+ Calling this function again before the button is released resets
+ the release timer.
+
+ \sa click(), pressed(), released(), clicked()
+*/
+void QQuickAbstractButton::animateClick()
+{
+ Q_D(QQuickAbstractButton);
+ if (!isEnabled())
+ return;
+
+ // See comment in click() for why we do this.
+ const bool setFocusOnPress = !QGuiApplication::styleHints()->setFocusOnTouchRelease();
+ if (setFocusOnPress && focusPolicy() & Qt::ClickFocus)
+ forceActiveFocus(Qt::MouseFocusReason);
+
+ // If the timer was already running, kill it so we can restart it.
+ if (d->animateTimer != 0)
+ killTimer(d->animateTimer);
+ else
+ d->handlePress(QPointF(d->width / 2, d->height / 2), 0);
+
+ d->animateTimer = startTimer(100);
+}
+
void QQuickAbstractButton::componentComplete()
{
Q_D(QQuickAbstractButton);
@@ -1084,7 +1178,7 @@ void QQuickAbstractButton::keyPressEvent(QKeyEvent *event)
Q_D(QQuickAbstractButton);
QQuickControl::keyPressEvent(event);
if (d->acceptKeyClick(static_cast<Qt::Key>(event->key()))) {
- d->setPressPoint(QPoint(qRound(width() / 2), qRound(height() / 2)));
+ d->setPressPoint(d->centerPressPoint());
setPressed(true);
if (d->autoRepeat)
@@ -1114,6 +1208,11 @@ void QQuickAbstractButton::keyReleaseEvent(QKeyEvent *event)
void QQuickAbstractButton::mousePressEvent(QMouseEvent *event)
{
+ if (!(event->buttons() & Qt::LeftButton)) {
+ event->ignore();
+ return;
+ }
+
Q_D(QQuickAbstractButton);
d->pressButtons = event->buttons();
QQuickControl::mousePressEvent(event);
@@ -1121,9 +1220,10 @@ void QQuickAbstractButton::mousePressEvent(QMouseEvent *event)
void QQuickAbstractButton::mouseDoubleClickEvent(QMouseEvent *event)
{
+ Q_UNUSED(event);
Q_D(QQuickAbstractButton);
if (d->isDoubleClickConnected()) {
- QQuickControl::mouseDoubleClickEvent(event);
+ // don't call QQuickItem::mouseDoubleClickEvent(): it would ignore()
emit doubleClicked();
d->wasDoubleClick = true;
}
@@ -1143,6 +1243,12 @@ void QQuickAbstractButton::timerEvent(QTimerEvent *event)
emit released();
d->trigger();
emit pressed();
+ } else if (event->timerId() == d->animateTimer) {
+ const bool setFocusOnRelease = QGuiApplication::styleHints()->setFocusOnTouchRelease();
+ if (setFocusOnRelease && focusPolicy() & Qt::ClickFocus)
+ forceActiveFocus(Qt::MouseFocusReason);
+ d->handleRelease(QPointF(d->width / 2, d->height / 2), 0);
+ d->animateTimer = 0;
}
}
@@ -1188,8 +1294,21 @@ void QQuickAbstractButton::buttonChange(ButtonChange change)
void QQuickAbstractButton::nextCheckState()
{
Q_D(QQuickAbstractButton);
- if (d->checkable && (!d->checked || d->findCheckedButton() != this))
- d->toggle(!d->checked);
+ if (!d->checkable)
+ return;
+
+ if (d->checked) {
+ if (d->findCheckedButton() == this)
+ return;
+ if (d->action) {
+ // For non-exclusive groups checkedAction is null
+ if (const auto group = QQuickActionPrivate::get(d->action)->group)
+ if (group->checkedAction() == d->action)
+ return;
+ }
+ }
+
+ d->toggle(!d->checked);
}
#if QT_CONFIG(accessibility)
diff --git a/src/quicktemplates/qquickabstractbutton_p.h b/src/quicktemplates/qquickabstractbutton_p.h
index 8907cfefd8..c9e7407920 100644
--- a/src/quicktemplates/qquickabstractbutton_p.h
+++ b/src/quicktemplates/qquickabstractbutton_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickAction;
class QQuickAbstractButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickAbstractButton : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickAbstractButton : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText RESET resetText NOTIFY textChanged FINAL)
@@ -119,6 +119,8 @@ public:
public Q_SLOTS:
void toggle();
+ Q_REVISION(6, 8) void click();
+ Q_REVISION(6, 8) void animateClick();
Q_SIGNALS:
void pressed();
@@ -188,6 +190,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAbstractButton)
-
#endif // QQUICKABSTRACTBUTTON_P_H
diff --git a/src/quicktemplates/qquickabstractbutton_p_p.h b/src/quicktemplates/qquickabstractbutton_p_p.h
index 9fcb5f5583..0d5eb65940 100644
--- a/src/quicktemplates/qquickabstractbutton_p_p.h
+++ b/src/quicktemplates/qquickabstractbutton_p_p.h
@@ -21,12 +21,14 @@
# include <QtGui/qkeysequence.h>
#endif
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickAction;
class QQuickButtonGroup;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickAbstractButtonPrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickAbstractButtonPrivate : public QQuickControlPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickAbstractButton)
@@ -36,6 +38,7 @@ public:
return button->d_func();
}
+ QPointF centerPressPoint() const;
void setPressPoint(const QPointF &point);
void setMovePoint(const QPointF &point);
@@ -65,6 +68,7 @@ public:
void actionTextChange();
void setText(const QString &text, bool isExplicit);
+ void init();
void updateEffectiveIcon();
@@ -99,6 +103,7 @@ public:
int repeatTimer = 0;
int repeatDelay = AUTO_REPEAT_DELAY;
int repeatInterval = AUTO_REPEAT_INTERVAL;
+ int animateTimer = 0;
#if QT_CONFIG(shortcut)
int shortcutId = 0;
QKeySequence shortcut;
diff --git a/src/quicktemplates/qquickaction.cpp b/src/quicktemplates/qquickaction.cpp
index 466501226d..f49393bdb1 100644
--- a/src/quicktemplates/qquickaction.cpp
+++ b/src/quicktemplates/qquickaction.cpp
@@ -6,6 +6,8 @@
#include "qquickactiongroup_p.h"
#include "qquickshortcutcontext_p_p.h"
+#include <QtCore/qpointer.h>
+#include <QtCore/qloggingcategory.h>
#include <QtGui/qevent.h>
#if QT_CONFIG(shortcut)
# include <QtGui/private/qshortcutmap_p.h>
@@ -15,6 +17,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcAction, "qt.quick.controls.action")
+
/*!
\qmltype Action
\inherits QtObject
@@ -314,6 +318,7 @@ QQuickAction::QQuickAction(QObject *parent)
QQuickAction::~QQuickAction()
{
Q_D(QQuickAction);
+ qCDebug(lcAction) << "destroying" << this << d->text;
if (d->group)
d->group->removeAction(this);
diff --git a/src/quicktemplates/qquickaction_p.h b/src/quicktemplates/qquickaction_p.h
index c4dd818af6..db985f750d 100644
--- a/src/quicktemplates/qquickaction_p.h
+++ b/src/quicktemplates/qquickaction_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickIcon;
class QQuickActionPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickAction : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickAction : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL)
@@ -92,6 +92,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickAction)
-
#endif // QQUICKACTION_P_H
diff --git a/src/quicktemplates/qquickactiongroup.cpp b/src/quicktemplates/qquickactiongroup.cpp
index 5e088f3924..1c41dd5ff0 100644
--- a/src/quicktemplates/qquickactiongroup.cpp
+++ b/src/quicktemplates/qquickactiongroup.cpp
@@ -11,6 +11,8 @@
#include "qquickaction_p.h"
#include "qquickaction_p_p.h"
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
/*!
diff --git a/src/quicktemplates/qquickactiongroup_p.h b/src/quicktemplates/qquickactiongroup_p.h
index 2cd8edbe77..1dd19df1e8 100644
--- a/src/quicktemplates/qquickactiongroup_p.h
+++ b/src/quicktemplates/qquickactiongroup_p.h
@@ -26,7 +26,7 @@ class QQuickActionGroupPrivate;
class QQuickActionGroupAttached;
class QQuickActionGroupAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickActionGroup : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickActionGroup : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickAction *checkedAction READ checkedAction WRITE setCheckedAction NOTIFY checkedActionChanged FINAL)
@@ -73,7 +73,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_updateCurrent())
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickActionGroupAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickActionGroupAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickActionGroup *group READ group WRITE setGroup NOTIFY groupChanged FINAL)
@@ -94,6 +94,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickActionGroup)
-
#endif // QQUICKACTIONGROUP_P_H
diff --git a/src/quicktemplates/qquickapplicationwindow.cpp b/src/quicktemplates/qquickapplicationwindow.cpp
index 582b5dd31a..c10eec6bb8 100644
--- a/src/quicktemplates/qquickapplicationwindow.cpp
+++ b/src/quicktemplates/qquickapplicationwindow.cpp
@@ -8,8 +8,12 @@
#include "qquicktextarea_p.h"
#include "qquicktextfield_p.h"
#include "qquicktoolbar_p.h"
+#include "qquicktooltip_p.h"
+#include <private/qtquicktemplates2-config_p.h>
+#if QT_CONFIG(quicktemplates2_container)
#include "qquicktabbar_p.h"
#include "qquickdialogbuttonbox_p.h"
+#endif
#include "qquickdeferredexecute_p_p.h"
#include "qquickdeferredpointer_p_p.h"
@@ -87,18 +91,13 @@ QT_BEGIN_NAMESPACE
static const QQuickItemPrivate::ChangeTypes ItemChanges = QQuickItemPrivate::Visibility
| QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickApplicationWindowPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickApplicationWindowPrivate
: public QQuickWindowQmlImplPrivate
, public QQuickItemChangeListener
{
Q_DECLARE_PUBLIC(QQuickApplicationWindow)
public:
- QQuickApplicationWindowPrivate()
- {
- complete = true;
- }
-
static QQuickApplicationWindowPrivate *get(QQuickApplicationWindow *window)
{
return window->d_func();
@@ -106,12 +105,14 @@ public:
QQmlListProperty<QObject> contentData();
+ void updateHasBackgroundFlags();
void relayout();
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override;
void itemVisibilityChanged(QQuickItem *item) override;
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ QPalette windowPalette() const override { return defaultPalette(); }
void updateFont(const QFont &f);
inline void setFont_helper(const QFont &f) {
@@ -135,9 +136,15 @@ public:
// Update regular children
QQuickWindowPrivate::updateChildrenPalettes(parentPalette);
- // And cover one special case
- for (auto &&popup : q_func()->findChildren<QQuickPopup *>())
- QQuickPopupPrivate::get(popup)->updateContentPalettes(parentPalette);
+ // And cover special cases
+ for (auto &&child : q_func()->findChildren<QObject *>()) {
+ if (auto *popup = qobject_cast<QQuickPopup *>(child))
+ QQuickPopupPrivate::get(popup)->updateContentPalettes(parentPalette);
+ else if (auto *toolTipAttached = qobject_cast<QQuickToolTipAttached *>(child)) {
+ if (auto *toolTip = toolTipAttached->toolTip())
+ QQuickPopupPrivate::get(toolTip)->updateContentPalettes(parentPalette);
+ }
+ }
}
QQuickDeferredPointer<QQuickItem> background;
@@ -149,6 +156,8 @@ public:
QLocale locale;
QQuickItem *activeFocusControl = nullptr;
bool insideRelayout = false;
+ bool hasBackgroundWidth = false;
+ bool hasBackgroundHeight = false;
};
static void layoutItem(QQuickItem *item, qreal y, qreal width)
@@ -164,10 +173,20 @@ static void layoutItem(QQuickItem *item, qreal y, qreal width)
}
}
+void QQuickApplicationWindowPrivate::updateHasBackgroundFlags()
+{
+ if (!background)
+ return;
+
+ QQuickItemPrivate *backgroundPrivate = QQuickItemPrivate::get(background);
+ hasBackgroundWidth = backgroundPrivate->widthValid();
+ hasBackgroundHeight = backgroundPrivate->heightValid();
+}
+
void QQuickApplicationWindowPrivate::relayout()
{
Q_Q(QQuickApplicationWindow);
- if (!complete || insideRelayout)
+ if (!componentComplete || insideRelayout)
return;
QScopedValueRollback<bool> guard(insideRelayout, true);
@@ -185,23 +204,23 @@ void QQuickApplicationWindowPrivate::relayout()
layoutItem(footer, content->height(), q->width());
if (background) {
- QQuickItemPrivate *p = QQuickItemPrivate::get(background);
- if (!p->widthValid() && qFuzzyIsNull(background->x())) {
+ if (!hasBackgroundWidth && qFuzzyIsNull(background->x()))
background->setWidth(q->width());
- p->widthValidFlag = false;
- }
- if (!p->heightValid() && qFuzzyIsNull(background->y())) {
+ if (!hasBackgroundHeight && qFuzzyIsNull(background->y()))
background->setHeight(q->height());
- p->heightValidFlag = false;
- }
}
}
void QQuickApplicationWindowPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
{
- Q_UNUSED(item);
- Q_UNUSED(change);
Q_UNUSED(diff);
+
+ if (!insideRelayout && item == background && change.sizeChange()) {
+ // Any time the background is resized (excluding our own resizing),
+ // we should respect it if it's explicit by storing the values of the flags.
+ updateHasBackgroundFlags();
+ }
+
relayout();
}
@@ -280,8 +299,6 @@ void QQuickApplicationWindowPrivate::contentData_append(QQmlListProperty<QObject
QQuickPopupPrivate::get(popup)->setWindow(static_cast<QQuickApplicationWindow *>(prop->data));
}
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickApplicationWindowPrivate::cancelBackground()
{
Q_Q(QQuickApplicationWindow);
@@ -296,8 +313,12 @@ void QQuickApplicationWindowPrivate::executeBackground(bool complete)
if (!background || complete)
quickBeginDeferred(q, backgroundName(), background);
- if (complete)
+ if (complete) {
quickCompleteDeferred(q, backgroundName(), background);
+ // See comment in setBackground for why we do this here.
+ updateHasBackgroundFlags();
+ relayout();
+ }
}
QQuickApplicationWindow::QQuickApplicationWindow(QWindow *parent)
@@ -359,14 +380,29 @@ void QQuickApplicationWindow::setBackground(QQuickItem *background)
if (!d->background.isExecuting())
d->cancelBackground();
+ if (d->background) {
+ d->hasBackgroundWidth = false;
+ d->hasBackgroundHeight = false;
+ }
QQuickControlPrivate::hideOldItem(d->background);
+
d->background = background;
+
if (background) {
background->setParentItem(QQuickWindow::contentItem());
+
if (qFuzzyIsNull(background->z()))
background->setZ(-1);
- if (isComponentComplete())
- d->relayout();
+
+ // If the background hasn't finished executing then we don't know if its width and height
+ // are valid or not, and so relayout would see that they haven't been set yet and override
+ // any bindings the user might have.
+ if (!d->background.isExecuting()) {
+ d->updateHasBackgroundFlags();
+
+ if (isComponentComplete())
+ d->relayout();
+ }
}
if (!d->background.isExecuting())
emit backgroundChanged();
@@ -418,10 +454,12 @@ void QQuickApplicationWindow::setHeader(QQuickItem *header)
header->setZ(1);
if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(header))
toolBar->setPosition(QQuickToolBar::Header);
+#if QT_CONFIG(quicktemplates2_container)
else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(header))
tabBar->setPosition(QQuickTabBar::Header);
else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(header))
buttonBox->setPosition(QQuickDialogButtonBox::Header);
+#endif
}
if (isComponentComplete())
d->relayout();
@@ -473,10 +511,12 @@ void QQuickApplicationWindow::setFooter(QQuickItem *footer)
footer->setZ(1);
if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(footer))
toolBar->setPosition(QQuickToolBar::Footer);
+#if QT_CONFIG(quicktemplates2_container)
else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(footer))
tabBar->setPosition(QQuickTabBar::Footer);
else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(footer))
buttonBox->setPosition(QQuickDialogButtonBox::Footer);
+#endif
}
if (isComponentComplete())
d->relayout();
@@ -685,13 +725,13 @@ void QQuickApplicationWindow::setMenuBar(QQuickItem *menuBar)
bool QQuickApplicationWindow::isComponentComplete() const
{
Q_D(const QQuickApplicationWindow);
- return d->complete;
+ return d->componentComplete;
}
void QQuickApplicationWindow::classBegin()
{
Q_D(QQuickApplicationWindow);
- d->complete = false;
+ d->componentComplete = false;
QQuickWindowQmlImpl::classBegin();
d->resolveFont();
}
@@ -699,7 +739,7 @@ void QQuickApplicationWindow::classBegin()
void QQuickApplicationWindow::componentComplete()
{
Q_D(QQuickApplicationWindow);
- d->complete = true;
+ d->componentComplete = true;
d->executeBackground(true);
QQuickWindowQmlImpl::componentComplete();
d->relayout();
diff --git a/src/quicktemplates/qquickapplicationwindow_p.h b/src/quicktemplates/qquickapplicationwindow_p.h
index 283b192b81..23a3a197ac 100644
--- a/src/quicktemplates/qquickapplicationwindow_p.h
+++ b/src/quicktemplates/qquickapplicationwindow_p.h
@@ -27,7 +27,7 @@ class QQuickApplicationWindowPrivate;
class QQuickApplicationWindowAttached;
class QQuickApplicationWindowAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickApplicationWindow : public QQuickWindowQmlImpl
+class Q_QUICKTEMPLATES2_EXPORT QQuickApplicationWindow : public QQuickWindowQmlImpl
{
Q_OBJECT
Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL)
@@ -99,7 +99,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_updateActiveFocus())
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickApplicationWindowAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickApplicationWindowAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickApplicationWindow *window READ window NOTIFY windowChanged FINAL)
@@ -133,32 +133,6 @@ private:
Q_DECLARE_PRIVATE(QQuickApplicationWindowAttached)
};
-struct QWindowForeign2
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QWindow)
- QML_ADDED_IN_VERSION(2, 0)
-};
-
-struct QQuickWindowForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickWindow)
- QML_ADDED_IN_VERSION(2, 0)
-};
-
-struct QQuickWindowQmlImplForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickWindowQmlImpl)
- QML_ADDED_IN_VERSION(2, 2)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickApplicationWindow)
-
#endif // QQUICKAPPLICATIONWINDOW_P_H
diff --git a/src/quicktemplates/qquickbusyindicator_p.h b/src/quicktemplates/qquickbusyindicator_p.h
index 46206b7d78..357d327eb8 100644
--- a/src/quicktemplates/qquickbusyindicator_p.h
+++ b/src/quicktemplates/qquickbusyindicator_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickBusyIndicatorPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickBusyIndicator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickBusyIndicator : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged FINAL)
@@ -53,6 +53,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickBusyIndicator)
-
#endif // QQUICKBUSYINDICATOR_P_H
diff --git a/src/quicktemplates/qquickbutton_p.h b/src/quicktemplates/qquickbutton_p.h
index 1092274ee6..32ebeb563d 100644
--- a/src/quicktemplates/qquickbutton_p.h
+++ b/src/quicktemplates/qquickbutton_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButton : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickButton : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL)
@@ -54,6 +54,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickButton)
-
#endif // QQUICKBUTTON_P_H
diff --git a/src/quicktemplates/qquickbuttongroup.cpp b/src/quicktemplates/qquickbuttongroup.cpp
index 89a64720dc..bb5787a3d2 100644
--- a/src/quicktemplates/qquickbuttongroup.cpp
+++ b/src/quicktemplates/qquickbuttongroup.cpp
@@ -102,7 +102,9 @@ QT_BEGIN_NAMESPACE
\code
ButtonGroup {
buttons: column.children
- onClicked: console.log("clicked:", button.text)
+ onClicked: button => {
+ console.log("clicked:", button.text)
+ }
}
Column {
@@ -143,9 +145,15 @@ public:
void QQuickButtonGroupPrivate::clear()
{
for (QQuickAbstractButton *button : std::as_const(buttons)) {
- QQuickAbstractButtonPrivate::get(button)->group = nullptr;
- QObjectPrivate::disconnect(button, &QQuickAbstractButton::clicked, this, &QQuickButtonGroupPrivate::buttonClicked);
- QObjectPrivate::disconnect(button, &QQuickAbstractButton::checkedChanged, this, &QQuickButtonGroupPrivate::_q_updateCurrent);
+ auto *attached = qobject_cast<QQuickButtonGroupAttached *>(
+ qmlAttachedPropertiesObject<QQuickButtonGroup>(button, false));
+ if (attached) {
+ attached->setGroup(nullptr);
+ } else {
+ QQuickAbstractButtonPrivate::get(button)->group = nullptr;
+ QObjectPrivate::disconnect(button, &QQuickAbstractButton::clicked, this, &QQuickButtonGroupPrivate::buttonClicked);
+ QObjectPrivate::disconnect(button, &QQuickAbstractButton::checkedChanged, this, &QQuickButtonGroupPrivate::_q_updateCurrent);
+ }
}
buttons.clear();
}
@@ -500,11 +508,12 @@ void QQuickButtonGroupAttached::setGroup(QQuickButtonGroup *group)
if (d->group == group)
return;
+ auto *button = qobject_cast<QQuickAbstractButton *>(parent());
if (d->group)
- d->group->removeButton(qobject_cast<QQuickAbstractButton*>(parent()));
+ d->group->removeButton(button);
d->group = group;
if (group)
- group->addButton(qobject_cast<QQuickAbstractButton*>(parent()));
+ group->addButton(button);
emit groupChanged();
}
diff --git a/src/quicktemplates/qquickbuttongroup_p.h b/src/quicktemplates/qquickbuttongroup_p.h
index 6bdd619c1c..a7ac05b01f 100644
--- a/src/quicktemplates/qquickbuttongroup_p.h
+++ b/src/quicktemplates/qquickbuttongroup_p.h
@@ -27,7 +27,7 @@ class QQuickButtonGroupPrivate;
class QQuickButtonGroupAttached;
class QQuickButtonGroupAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButtonGroup : public QObject, public QQmlParserStatus
+class Q_QUICKTEMPLATES2_EXPORT QQuickButtonGroup : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_PROPERTY(QQuickAbstractButton *checkedButton READ checkedButton WRITE setCheckedButton NOTIFY checkedButtonChanged FINAL)
@@ -84,7 +84,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_updateCurrent())
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickButtonGroupAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickButtonGroupAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickButtonGroup *group READ group WRITE setGroup NOTIFY groupChanged FINAL)
@@ -105,6 +105,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickButtonGroup)
-
#endif // QQUICKBUTTONGROUP_P_H
diff --git a/src/quicktemplates/qquickcalendar_p.h b/src/quicktemplates/qquickcalendar_p.h
index 37dac5595a..dbee98a737 100644
--- a/src/quicktemplates/qquickcalendar_p.h
+++ b/src/quicktemplates/qquickcalendar_p.h
@@ -50,6 +50,4 @@ public:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCalendar)
-
#endif // QQUICKCALENDAR_P_H
diff --git a/src/quicktemplates/qquickcalendarmodel.cpp b/src/quicktemplates/qquickcalendarmodel.cpp
index 0bac0969e0..bb3e2a7796 100644
--- a/src/quicktemplates/qquickcalendarmodel.cpp
+++ b/src/quicktemplates/qquickcalendarmodel.cpp
@@ -42,9 +42,9 @@ public:
{
}
- static int getCount(const QDate& from, const QDate &to);
+ static int getCount(QDate from, QDate to);
- void populate(const QDate &from, const QDate &to, bool force = false);
+ void populate(QDate from, QDate to, bool force = false);
bool complete;
QDate from;
@@ -52,24 +52,26 @@ public:
int count;
};
-int QQuickCalendarModelPrivate::getCount(const QDate& from, const QDate &to)
+// Returns the number of months we need to display for both from and to to be shown,
+// or zero if from is in a later month than to, or either is invalid.
+int QQuickCalendarModelPrivate::getCount(QDate from, QDate to)
{
if (!from.isValid() || !to.isValid())
return 0;
- QDate f(from.year(), from.month(), 1);
- QDate t(to.year(), to.month(), to.daysInMonth());
- int days = f.daysTo(t);
- if (days < 0)
+ const QCalendar gregorian;
+ Q_ASSERT(gregorian.isGregorian());
+ const QCalendar::YearMonthDay &f = gregorian.partsFromDate(from);
+ const QCalendar::YearMonthDay &t = gregorian.partsFromDate(to);
+ Q_ASSERT(f.isValid() && t.isValid()); // ... because from and to are valid.
+ if (f.year > t.year || (f.year == t.year && f.month > t.month))
return 0;
- QDate r = QDate(1, 1, 1).addDays(days);
- int years = r.year() - 1;
- int months = r.month() - 1;
- return 12 * years + months + (r.day() / t.day());
+ // Count from's month and every subsequent month until to's:
+ return 1 + t.month + 12 * (t.year - f.year) - f.month;
}
-void QQuickCalendarModelPrivate::populate(const QDate &f, const QDate &t, bool force)
+void QQuickCalendarModelPrivate::populate(QDate f, QDate t, bool force)
{
Q_Q(QQuickCalendarModel);
if (!force && f == from && t == to)
@@ -102,7 +104,7 @@ QDate QQuickCalendarModel::from() const
return d->from;
}
-void QQuickCalendarModel::setFrom(const QDate &from)
+void QQuickCalendarModel::setFrom(QDate from)
{
Q_D(QQuickCalendarModel);
if (d->from != from) {
@@ -124,7 +126,7 @@ QDate QQuickCalendarModel::to() const
return d->to;
}
-void QQuickCalendarModel::setTo(const QDate &to)
+void QQuickCalendarModel::setTo(QDate to)
{
Q_D(QQuickCalendarModel);
if (d->to != to) {
@@ -162,7 +164,7 @@ int QQuickCalendarModel::yearAt(int index) const
Returns the model index of the specified \a date.
*/
-int QQuickCalendarModel::indexOf(const QDate &date) const
+int QQuickCalendarModel::indexOf(QDate date) const
{
Q_D(const QQuickCalendarModel);
return d->getCount(d->from, date) - 1;
diff --git a/src/quicktemplates/qquickcalendarmodel_p.h b/src/quicktemplates/qquickcalendarmodel_p.h
index e9dbf6d075..face2b29cf 100644
--- a/src/quicktemplates/qquickcalendarmodel_p.h
+++ b/src/quicktemplates/qquickcalendarmodel_p.h
@@ -39,14 +39,14 @@ public:
explicit QQuickCalendarModel(QObject *parent = nullptr);
QDate from() const;
- void setFrom(const QDate &from);
+ void setFrom(QDate from);
QDate to() const;
- void setTo(const QDate &to);
+ void setTo(QDate to);
Q_INVOKABLE int monthAt(int index) const;
Q_INVOKABLE int yearAt(int index) const;
- Q_INVOKABLE int indexOf(const QDate &date) const;
+ Q_INVOKABLE int indexOf(QDate date) const;
Q_INVOKABLE int indexOf(int year, int month) const;
enum {
@@ -74,6 +74,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCalendarModel)
-
#endif // QQUICKCALENDARMODEL_P_H
diff --git a/src/quicktemplates/qquickcheckbox.cpp b/src/quicktemplates/qquickcheckbox.cpp
index 21038ccdb9..539e89f2d0 100644
--- a/src/quicktemplates/qquickcheckbox.cpp
+++ b/src/quicktemplates/qquickcheckbox.cpp
@@ -69,8 +69,6 @@ class QQuickCheckBoxPrivate : public QQuickAbstractButtonPrivate
Q_DECLARE_PUBLIC(QQuickCheckBox)
public:
- void setNextCheckState(const QJSValue &callback);
-
QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::CheckBox); }
bool tristate = false;
@@ -78,13 +76,6 @@ public:
QJSValue nextCheckState;
};
-void QQuickCheckBoxPrivate::setNextCheckState(const QJSValue &callback)
-{
- Q_Q(QQuickCheckBox);
- nextCheckState = callback;
- emit q->nextCheckStateChanged();
-}
-
QQuickCheckBox::QQuickCheckBox(QQuickItem *parent)
: QQuickAbstractButton(*(new QQuickCheckBoxPrivate), parent)
{
@@ -150,6 +141,19 @@ void QQuickCheckBox::setCheckState(Qt::CheckState state)
emit checkedChanged();
}
+QJSValue QQuickCheckBox::getNextCheckState() const
+{
+ Q_D(const QQuickCheckBox);
+ return d->nextCheckState;
+}
+
+void QQuickCheckBox::setNextCheckState(const QJSValue &callback)
+{
+ Q_D(QQuickCheckBox);
+ d->nextCheckState = callback;
+ emit nextCheckStateChanged();
+}
+
QFont QQuickCheckBox::defaultFont() const
{
return QQuickTheme::font(QQuickTheme::CheckBox);
diff --git a/src/quicktemplates/qquickcheckbox_p.h b/src/quicktemplates/qquickcheckbox_p.h
index 5adeb44ede..b1a779dacd 100644
--- a/src/quicktemplates/qquickcheckbox_p.h
+++ b/src/quicktemplates/qquickcheckbox_p.h
@@ -21,13 +21,14 @@ QT_BEGIN_NAMESPACE
class QQuickCheckBoxPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickCheckBox : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickCheckBox : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool tristate READ isTristate WRITE setTristate NOTIFY tristateChanged FINAL)
Q_PROPERTY(Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL)
// 2.4 (Qt 5.11)
- Q_PRIVATE_PROPERTY(QQuickCheckBox::d_func(), QJSValue nextCheckState MEMBER nextCheckState WRITE setNextCheckState NOTIFY nextCheckStateChanged FINAL REVISION(2, 4))
+ Q_PROPERTY(QJSValue nextCheckState READ getNextCheckState WRITE setNextCheckState NOTIFY
+ nextCheckStateChanged FINAL REVISION(2, 4))
QML_NAMED_ELEMENT(CheckBox)
QML_ADDED_IN_VERSION(2, 0)
@@ -39,6 +40,8 @@ public:
Qt::CheckState checkState() const;
void setCheckState(Qt::CheckState state);
+ QJSValue getNextCheckState() const;
+ void setNextCheckState(const QJSValue &callback);
Q_SIGNALS:
void tristateChanged();
@@ -59,6 +62,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCheckBox)
-
#endif // QQUICKCHECKBOX_P_H
diff --git a/src/quicktemplates/qquickcheckdelegate_p.h b/src/quicktemplates/qquickcheckdelegate_p.h
index 8c7c3e8c74..8e40ef68e7 100644
--- a/src/quicktemplates/qquickcheckdelegate_p.h
+++ b/src/quicktemplates/qquickcheckdelegate_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickCheckDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickCheckDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickCheckDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(bool tristate READ isTristate WRITE setTristate NOTIFY tristateChanged FINAL)
@@ -63,6 +63,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCheckDelegate)
-
#endif // QQUICKCHECKDELEGATE_P_H
diff --git a/src/quicktemplates/qquickcombobox.cpp b/src/quicktemplates/qquickcombobox.cpp
index 31eb97e31d..c55e988b74 100644
--- a/src/quicktemplates/qquickcombobox.cpp
+++ b/src/quicktemplates/qquickcombobox.cpp
@@ -23,7 +23,9 @@
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquicktextinput_p_p.h>
+#if QT_CONFIG(quick_itemview)
#include <QtQuick/private/qquickitemview_p.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -209,6 +211,7 @@ public:
void hidePopup(bool accept);
void togglePopup(bool accept);
void popupVisibleChanged();
+ void popupDestroyed();
void itemClicked();
void itemHovered();
@@ -253,6 +256,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
void setInputMethodHints(Qt::InputMethodHints hints, bool force = false);
@@ -342,14 +346,18 @@ void QQuickComboBoxPrivate::popupVisibleChanged()
if (isPopupVisible())
QGuiApplication::inputMethod()->reset();
+#if QT_CONFIG(quick_itemview)
QQuickItemView *itemView = popup->findChild<QQuickItemView *>();
if (itemView)
itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
+#endif
updateHighlightedIndex();
+#if QT_CONFIG(quick_itemview)
if (itemView)
itemView->positionViewAtIndex(highlightedIndex, QQuickItemView::Beginning);
+#endif
if (!hasDown) {
q->setDown(pressed || isPopupVisible());
@@ -357,6 +365,13 @@ void QQuickComboBoxPrivate::popupVisibleChanged()
}
}
+void QQuickComboBoxPrivate::popupDestroyed()
+{
+ Q_Q(QQuickComboBox);
+ popup = nullptr;
+ emit q->popupChanged();
+}
+
void QQuickComboBoxPrivate::itemClicked()
{
Q_Q(QQuickComboBox);
@@ -381,8 +396,10 @@ void QQuickComboBoxPrivate::itemHovered()
if (index != -1) {
setHighlightedIndex(index, Highlight);
+#if QT_CONFIG(quick_itemview)
if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
itemView->positionViewAtIndex(index, QQuickItemView::Contain);
+#endif
}
}
@@ -767,8 +784,6 @@ void QQuickComboBoxPrivate::handleUngrab()
q->setPressed(false);
}
-static inline QString indicatorName() { return QStringLiteral("indicator"); }
-
void QQuickComboBoxPrivate::cancelIndicator()
{
Q_Q(QQuickComboBox);
@@ -833,6 +848,16 @@ void QQuickComboBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit q->implicitIndicatorHeightChanged();
}
+void QQuickComboBoxPrivate::itemDestroyed(QQuickItem *item)
+{
+ Q_Q(QQuickComboBox);
+ QQuickControlPrivate::itemDestroyed(item);
+ if (item == indicator) {
+ indicator = nullptr;
+ emit q->indicatorChanged();
+ }
+}
+
qreal QQuickComboBoxPrivate::getContentWidth() const
{
if (componentComplete) {
@@ -929,6 +954,7 @@ QQuickComboBox::QQuickComboBox(QQuickItem *parent)
#endif
Q_D(QQuickComboBox);
d->setInputMethodHints(Qt::ImhNoPredictiveText, true);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
QQuickComboBox::~QQuickComboBox()
@@ -1333,6 +1359,7 @@ void QQuickComboBox::setPopup(QQuickPopup *popup)
d->cancelPopup();
if (d->popup) {
+ QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::destroyed, d, &QQuickComboBoxPrivate::popupDestroyed);
QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
QQuickComboBoxPrivate::hideOldPopup(d->popup);
}
@@ -1340,9 +1367,14 @@ void QQuickComboBox::setPopup(QQuickPopup *popup)
QQuickPopupPrivate::get(popup)->allowVerticalFlip = true;
popup->setClosePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent);
QObjectPrivate::connect(popup, &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
+ // QQuickPopup does not derive from QQuickItemChangeListener, so we cannot use
+ // QQuickItemChangeListener::itemDestroyed so we have to use QObject::destroyed
+ QObjectPrivate::connect(popup, &QQuickPopup::destroyed, d, &QQuickComboBoxPrivate::popupDestroyed);
+#if QT_CONFIG(quick_itemview)
if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
+#endif
}
d->popup = popup;
if (!d->popup.isExecuting())
@@ -2056,7 +2088,7 @@ void QQuickComboBox::keyReleaseEvent(QKeyEvent *event)
if (!isEditable()) {
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
if (buttonPressKeys.contains(key)) {
- if (!isEditable())
+ if (!isEditable() && isPressed())
d->togglePopup(true);
setPressed(false);
event->accept();
diff --git a/src/quicktemplates/qquickcombobox_p.h b/src/quicktemplates/qquickcombobox_p.h
index 303eef21b0..2ef5dc776c 100644
--- a/src/quicktemplates/qquickcombobox_p.h
+++ b/src/quicktemplates/qquickcombobox_p.h
@@ -16,8 +16,11 @@
//
#include <QtCore/qloggingcategory.h>
+#include <QtQmlModels/private/qtqmlmodels-config_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
+QT_REQUIRE_CONFIG(qml_delegate_model);
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcItemManagement)
@@ -27,7 +30,7 @@ class QQuickPopup;
class QQmlInstanceModel;
class QQuickComboBoxPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickComboBox : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickComboBox : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged FINAL)
@@ -240,6 +243,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickComboBox)
-
#endif // QQUICKCOMBOBOX_P_H
diff --git a/src/quicktemplates/qquickcontainer.cpp b/src/quicktemplates/qquickcontainer.cpp
index eefc9161e9..115168083c 100644
--- a/src/quicktemplates/qquickcontainer.cpp
+++ b/src/quicktemplates/qquickcontainer.cpp
@@ -116,7 +116,7 @@ QT_BEGIN_NAMESPACE
Container does not provide any default visualization. It is used to implement
such containers as \l SwipeView and \l TabBar. When implementing a custom
container, the most important part of the API is \l contentModel, which provides
- the contained items in a way that it can be used as a delegate model for item
+ the contained items in a way that it can be used as an object model for item
views and repeaters.
\code
@@ -167,6 +167,7 @@ void QQuickContainerPrivate::init()
QObject::connect(contentModel, &QQmlObjectModel::childrenChanged, q, &QQuickContainer::contentChildrenChanged);
connect(q, &QQuickControl::implicitContentWidthChanged, this, &QQuickContainerPrivate::updateContentWidth);
connect(q, &QQuickControl::implicitContentHeightChanged, this, &QQuickContainerPrivate::updateContentHeight);
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
void QQuickContainerPrivate::cleanup()
@@ -256,7 +257,8 @@ void QQuickContainerPrivate::moveItem(int from, int to, QQuickItem *item)
void QQuickContainerPrivate::removeItem(int index, QQuickItem *item)
{
Q_Q(QQuickContainer);
- if (!q->isContent(item))
+ const bool item_inDestructor = QQuickItemPrivate::get(item)->inDestructor;
+ if (!item_inDestructor && !q->isContent(item))
return;
contentData.removeOne(item);
@@ -271,8 +273,11 @@ void QQuickContainerPrivate::removeItem(int index, QQuickItem *item)
currentChanged = true;
}
- QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes);
- item->setParentItem(nullptr);
+ if (!item_inDestructor) {
+ // already handled by ~QQuickItem
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes);
+ item->setParentItem(nullptr);
+ }
contentModel->remove(index);
--count;
diff --git a/src/quicktemplates/qquickcontainer_p.h b/src/quicktemplates/qquickcontainer_p.h
index 7fdf606571..7a71d5bdf6 100644
--- a/src/quicktemplates/qquickcontainer_p.h
+++ b/src/quicktemplates/qquickcontainer_p.h
@@ -18,11 +18,13 @@
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
#include <QtQml/qqmllist.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickContainerPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickContainer : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickContainer : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged FINAL)
@@ -103,6 +105,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickContainer)
-
#endif // QQUICKCONTAINER_P_H
diff --git a/src/quicktemplates/qquickcontainer_p_p.h b/src/quicktemplates/qquickcontainer_p_p.h
index 84fe62a75d..a486e8cba7 100644
--- a/src/quicktemplates/qquickcontainer_p_p.h
+++ b/src/quicktemplates/qquickcontainer_p_p.h
@@ -21,7 +21,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickContainerPrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickContainerPrivate : public QQuickControlPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickContainer)
diff --git a/src/quicktemplates/qquickcontentitem_p.h b/src/quicktemplates/qquickcontentitem_p.h
index 446489ee70..77f7f14c64 100644
--- a/src/quicktemplates/qquickcontentitem_p.h
+++ b/src/quicktemplates/qquickcontentitem_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickContentItem : public QQuickItem
+class Q_QUICKTEMPLATES2_EXPORT QQuickContentItem : public QQuickItem
{
Q_OBJECT
diff --git a/src/quicktemplates/qquickcontrol.cpp b/src/quicktemplates/qquickcontrol.cpp
index 764b6ca003..667d778180 100644
--- a/src/quicktemplates/qquickcontrol.cpp
+++ b/src/quicktemplates/qquickcontrol.cpp
@@ -107,6 +107,8 @@ Q_LOGGING_CATEGORY(lcItemManagement, "qt.quick.controls.control.itemmanagement")
}
\endcode
+ Wheel events are consumed by controls if \l wheelEnabled is \c true.
+
\sa ApplicationWindow, Container, {Using Qt Quick Controls types in
property declarations}
*/
@@ -150,21 +152,8 @@ bool QQuickControlPrivate::acceptTouch(const QTouchEvent::TouchPoint &point)
}
#endif
-static void setActiveFocus(QQuickControl *control, Qt::FocusReason reason)
-{
- QQuickControlPrivate *d = QQuickControlPrivate::get(control);
- if (d->subFocusItem && d->window && d->flags & QQuickItem::ItemIsFocusScope)
- QQuickWindowPrivate::get(d->window)->clearFocusInScope(control, d->subFocusItem, reason);
- control->forceActiveFocus(reason);
-}
-
bool QQuickControlPrivate::handlePress(const QPointF &, ulong)
{
- Q_Q(QQuickControl);
- if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && !QGuiApplication::styleHints()->setFocusOnTouchRelease()) {
- setActiveFocus(q, Qt::MouseFocusReason);
- return true;
- }
return true;
}
@@ -181,14 +170,8 @@ bool QQuickControlPrivate::handleMove(const QPointF &point, ulong)
bool QQuickControlPrivate::handleRelease(const QPointF &, ulong)
{
- Q_Q(QQuickControl);
- bool accepted = true;
- if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && QGuiApplication::styleHints()->setFocusOnTouchRelease()) {
- setActiveFocus(q, Qt::MouseFocusReason);
- accepted = true;
- }
touchId = -1;
- return accepted;
+ return true;
}
void QQuickControlPrivate::handleUngrab()
@@ -352,12 +335,22 @@ void QQuickControlPrivate::resizeBackground()
bool changeHeight = false;
if (((!p->widthValid() || !extra.isAllocated() || !extra->hasBackgroundWidth) && qFuzzyIsNull(background->x()))
|| (extra.isAllocated() && (extra->hasLeftInset || extra->hasRightInset))) {
- background->setX(getLeftInset());
+ const auto leftInset = getLeftInset();
+ if (!qt_is_nan(leftInset) && p->x.valueBypassingBindings() != leftInset) {
+ // We bypass the binding here to prevent it from being removed
+ p->x.setValueBypassingBindings(leftInset);
+ p->dirty(DirtyType::Position);
+ }
changeWidth = !p->width.hasBinding();
}
if (((!p->heightValid() || !extra.isAllocated() || !extra->hasBackgroundHeight) && qFuzzyIsNull(background->y()))
|| (extra.isAllocated() && (extra->hasTopInset || extra->hasBottomInset))) {
- background->setY(getTopInset());
+ const auto topInset = getTopInset();
+ if (!qt_is_nan(topInset) && p->y.valueBypassingBindings() != topInset) {
+ // We bypass the binding here to prevent it from being removed
+ p->y.setValueBypassingBindings(topInset);
+ p->dirty(DirtyType::Position);
+ }
changeHeight = !p->height.hasBinding();
}
if (changeHeight || changeWidth) {
@@ -404,8 +397,7 @@ void QQuickControlPrivate::setContentItem_helper(QQuickItem *item, bool notify)
QQuickItem *oldContentItem = contentItem;
if (oldContentItem) {
disconnect(oldContentItem, &QQuickItem::baselineOffsetChanged, this, &QQuickControlPrivate::updateBaselineOffset);
- if (oldContentItem)
- QQuickItemPrivate::get(oldContentItem)->removeItemChangeListener(this, QQuickControlPrivate::Focus);
+ QQuickItemPrivate::get(oldContentItem)->removeItemChangeListener(this, QQuickControlPrivate::Focus);
removeImplicitSizeListener(oldContentItem);
}
@@ -751,8 +743,6 @@ void QQuickControlPrivate::executeContentItem(bool complete)
quickCompleteDeferred(q, contentItemName(), contentItem);
}
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickControlPrivate::cancelBackground()
{
Q_Q(QQuickControl);
@@ -802,13 +792,25 @@ void QQuickControlPrivate::hideOldItem(QQuickItem *item)
Named "unhide" because it's used for cases where an item
that was previously hidden by \l hideOldItem() wants to be
shown by a control again, such as a ScrollBar in ScrollView.
+
+ \a visibility controls the visibility of \a item, as there
+ may have been bindings that controlled visibility, such as
+ with a typical ScrollBar.qml implementation:
+
+ \code
+ visible: control.policy !== T.ScrollBar.AlwaysOff
+ \endcode
+
+ In the future we could try to save the binding for the visible
+ property (using e.g. QQmlAnyBinding::takeFrom), but for now we
+ keep it simple and just allow restoring an equivalent literal value.
*/
-void QQuickControlPrivate::unhideOldItem(QQuickControl *control, QQuickItem *item)
+void QQuickControlPrivate::unhideOldItem(QQuickControl *control, QQuickItem *item, UnhideVisibility visibility)
{
Q_ASSERT(item);
qCDebug(lcItemManagement) << "unhiding old item" << item;
- item->setVisible(true);
+ item->setVisible(visibility == UnhideVisibility::Show);
item->setParentItem(control);
#if QT_CONFIG(accessibility)
@@ -906,7 +908,20 @@ void QQuickControlPrivate::itemFocusChanged(QQuickItem *item, Qt::FocusReason re
{
Q_Q(QQuickControl);
if (item == contentItem || item == q)
- q->setFocusReason(reason);
+ setLastFocusChangeReason(reason);
+}
+
+bool QQuickControlPrivate::setLastFocusChangeReason(Qt::FocusReason reason)
+{
+ Q_Q(QQuickControl);
+ Qt::FocusReason oldReason = static_cast<Qt::FocusReason>(focusReason);
+ const auto focusReasonChanged = QQuickItemPrivate::setLastFocusChangeReason(reason);
+ if (focusReasonChanged)
+ emit q->focusReasonChanged();
+ if (isKeyFocusReason(oldReason) != isKeyFocusReason(reason))
+ emit q->visualFocusChanged();
+
+ return focusReasonChanged;
}
QQuickControl::QQuickControl(QQuickItem *parent)
@@ -962,7 +977,7 @@ void QQuickControl::itemChange(QQuickItem::ItemChange change, const QQuickItem::
}
break;
case ItemActiveFocusHasChanged:
- if (isKeyFocusReason(d->focusReason))
+ if (isKeyFocusReason(static_cast<Qt::FocusReason>(d->focusReason)))
emit visualFocusChanged();
break;
default:
@@ -1352,61 +1367,37 @@ bool QQuickControl::isMirrored() const
}
/*!
- \qmlproperty enumeration QtQuick.Controls::Control::focusPolicy
-
- This property determines the way the control accepts focus.
-
- \value Qt.TabFocus The control accepts focus by tabbing.
- \value Qt.ClickFocus The control accepts focus by clicking.
- \value Qt.StrongFocus The control accepts focus by both tabbing and clicking.
- \value Qt.WheelFocus The control accepts focus by tabbing, clicking, and using the mouse wheel.
- \value Qt.NoFocus The control does not accept focus.
-*/
-Qt::FocusPolicy QQuickControl::focusPolicy() const
-{
- Q_D(const QQuickControl);
- uint policy = d->focusPolicy;
- if (activeFocusOnTab())
- policy |= Qt::TabFocus;
- return static_cast<Qt::FocusPolicy>(policy);
-}
+ \qmlproperty enumeration QtQuick.Controls::Control::focusReason
+ \readonly
-void QQuickControl::setFocusPolicy(Qt::FocusPolicy policy)
-{
- Q_D(QQuickControl);
- if (d->focusPolicy == policy)
- return;
+ This property holds the reason of the last focus change.
- d->focusPolicy = policy;
- setActiveFocusOnTab(policy & Qt::TabFocus);
- emit focusPolicyChanged();
-}
+ \note This property does not indicate whether the item has \l {Item::activeFocus}
+ {active focus}, but the reason why the item either gained or lost focus.
-/*!
- \qmlproperty enumeration QtQuick.Controls::Control::focusReason
- \readonly
+ \value Qt.MouseFocusReason A mouse action occurred.
+ \value Qt.TabFocusReason The Tab key was pressed.
+ \value Qt.BacktabFocusReason A Backtab occurred. The input for this may include the Shift or Control keys; e.g. Shift+Tab.
+ \value Qt.ActiveWindowFocusReason The window system made this window either active or inactive.
+ \value Qt.PopupFocusReason The application opened/closed a pop-up that grabbed/released the keyboard focus.
+ \value Qt.ShortcutFocusReason The user typed a label's buddy shortcut
+ \value Qt.MenuBarFocusReason The menu bar took focus.
+ \value Qt.OtherFocusReason Another reason, usually application-specific.
- \include qquickcontrol-focusreason.qdocinc
+ \sa Item::activeFocus
\sa visualFocus
*/
Qt::FocusReason QQuickControl::focusReason() const
{
Q_D(const QQuickControl);
- return d->focusReason;
+ return d->lastFocusChangeReason();
}
void QQuickControl::setFocusReason(Qt::FocusReason reason)
{
Q_D(QQuickControl);
- if (d->focusReason == reason)
- return;
-
- Qt::FocusReason oldReason = d->focusReason;
- d->focusReason = reason;
- emit focusReasonChanged();
- if (isKeyFocusReason(oldReason) != isKeyFocusReason(reason))
- emit visualFocusChanged();
+ d->setLastFocusChangeReason(reason);
}
/*!
@@ -1426,7 +1417,7 @@ void QQuickControl::setFocusReason(Qt::FocusReason reason)
bool QQuickControl::hasVisualFocus() const
{
Q_D(const QQuickControl);
- return d->activeFocus && isKeyFocusReason(d->focusReason);
+ return d->activeFocus && isKeyFocusReason(static_cast<Qt::FocusReason>(d->focusReason));
}
/*!
@@ -1988,13 +1979,11 @@ QFont QQuickControl::defaultFont() const
void QQuickControl::focusInEvent(QFocusEvent *event)
{
QQuickItem::focusInEvent(event);
- setFocusReason(event->reason());
}
void QQuickControl::focusOutEvent(QFocusEvent *event)
{
QQuickItem::focusOutEvent(event);
- setFocusReason(event->reason());
}
#if QT_CONFIG(quicktemplates2_hover)
@@ -2092,9 +2081,6 @@ void QQuickControl::touchUngrabEvent()
void QQuickControl::wheelEvent(QWheelEvent *event)
{
Q_D(QQuickControl);
- if ((d->focusPolicy & Qt::WheelFocus) == Qt::WheelFocus)
- setActiveFocus(this, Qt::MouseFocusReason);
-
event->setAccepted(d->wheelEnabled);
}
#endif
@@ -2175,12 +2161,13 @@ QAccessible::Role QQuickControl::accessibleRole() const
void QQuickControl::accessibilityActiveChanged(bool active)
{
+ Q_D(QQuickControl);
if (!active)
return;
QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(this, true));
Q_ASSERT(accessibleAttached);
- accessibleAttached->setRole(accessibleRole());
+ accessibleAttached->setRole(d->effectiveAccessibleRole());
}
#endif
diff --git a/src/quicktemplates/qquickcontrol_p.h b/src/quicktemplates/qquickcontrol_p.h
index a607a159f3..bf17cf10bc 100644
--- a/src/quicktemplates/qquickcontrol_p.h
+++ b/src/quicktemplates/qquickcontrol_p.h
@@ -25,7 +25,7 @@ QT_BEGIN_NAMESPACE
class QQuickControlPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickControl : public QQuickItem
+class Q_QUICKTEMPLATES2_EXPORT QQuickControl : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont RESET resetFont NOTIFY fontChanged FINAL)
@@ -39,7 +39,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickControl : public QQuickItem
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing RESET resetSpacing NOTIFY spacingChanged FINAL)
Q_PROPERTY(QLocale locale READ locale WRITE setLocale RESET resetLocale NOTIFY localeChanged FINAL)
Q_PROPERTY(bool mirrored READ isMirrored NOTIFY mirroredChanged FINAL)
- Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged FINAL)
+ QT6_ONLY(Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy NOTIFY focusPolicyChanged FINAL))
Q_PROPERTY(Qt::FocusReason focusReason READ focusReason WRITE setFocusReason NOTIFY focusReasonChanged FINAL)
Q_PROPERTY(bool visualFocus READ hasVisualFocus NOTIFY visualFocusChanged FINAL)
Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged FINAL)
@@ -104,9 +104,6 @@ public:
bool isMirrored() const;
- Qt::FocusPolicy focusPolicy() const;
- void setFocusPolicy(Qt::FocusPolicy policy);
-
Qt::FocusReason focusReason() const;
void setFocusReason(Qt::FocusReason reason);
@@ -174,9 +171,8 @@ Q_SIGNALS:
void bottomPaddingChanged();
void spacingChanged();
void localeChanged();
- void mirroredChanged();
- void focusPolicyChanged();
void focusReasonChanged();
+ void mirroredChanged();
void visualFocusChanged();
void hoveredChanged();
void hoverEnabledChanged();
@@ -256,32 +252,6 @@ private:
Q_DECLARE_PRIVATE(QQuickControl)
};
-struct QQuickItemForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickItem)
- QML_ADDED_IN_VERSION(2, 3)
-};
-
-struct QQuickColorGroupForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickColorGroup)
- QML_ADDED_IN_VERSION(6, 0)
-};
-
-struct QQuickPaletteForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickPalette)
- QML_ADDED_IN_VERSION(6, 0)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickControl)
-
#endif // QQUICKCONTROL_P_H
diff --git a/src/quicktemplates/qquickcontrol_p_p.h b/src/quicktemplates/qquickcontrol_p_p.h
index d19c76dfe3..24d0507670 100644
--- a/src/quicktemplates/qquickcontrol_p_p.h
+++ b/src/quicktemplates/qquickcontrol_p_p.h
@@ -35,7 +35,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcItemManagement)
class QQuickAccessibleAttached;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickControlPrivate : public QQuickItemPrivate, public QQuickItemChangeListener
+class Q_QUICKTEMPLATES2_EXPORT QQuickControlPrivate : public QQuickItemPrivate, public QQuickItemChangeListener
#if QT_CONFIG(accessibility)
, public QAccessible::ActivationObserver
#endif
@@ -130,8 +130,14 @@ public:
virtual void cancelBackground();
virtual void executeBackground(bool complete = false);
+ enum class UnhideVisibility {
+ Show,
+ Hide
+ };
+
static void hideOldItem(QQuickItem *item);
- static void unhideOldItem(QQuickControl *control, QQuickItem *item);
+ static void unhideOldItem(QQuickControl *control, QQuickItem *item,
+ UnhideVisibility visibility = UnhideVisibility::Show);
void updateBaselineOffset();
@@ -149,6 +155,8 @@ public:
void itemDestroyed(QQuickItem *item) override;
void itemFocusChanged(QQuickItem *item, Qt::FocusReason reason) override;
+ bool setLastFocusChangeReason(Qt::FocusReason) override;
+
virtual qreal getContentWidth() const;
virtual qreal getContentHeight() const;
@@ -200,8 +208,6 @@ public:
qreal spacing = 0;
QLocale locale;
QFont resolvedFont;
- Qt::FocusPolicy focusPolicy = Qt::NoFocus;
- Qt::FocusReason focusReason = Qt::OtherFocusReason;
QQuickDeferredPointer<QQuickItem> background;
QQuickDeferredPointer<QQuickItem> contentItem;
};
diff --git a/src/quicktemplates/qquickdayofweekmodel_p.h b/src/quicktemplates/qquickdayofweekmodel_p.h
index ba80a43077..f3df4edab8 100644
--- a/src/quicktemplates/qquickdayofweekmodel_p.h
+++ b/src/quicktemplates/qquickdayofweekmodel_p.h
@@ -59,6 +59,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDayOfWeekModel)
-
#endif // QQUICKDAYOFWEEKMODEL_P_H
diff --git a/src/quicktemplates/qquickdayofweekrow_p.h b/src/quicktemplates/qquickdayofweekrow_p.h
index 3b5c062196..7c2a2398fe 100644
--- a/src/quicktemplates/qquickdayofweekrow_p.h
+++ b/src/quicktemplates/qquickdayofweekrow_p.h
@@ -56,6 +56,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDayOfWeekRow)
-
#endif // QQUICKDAYOFWEEKROW_P_H
diff --git a/src/quicktemplates/qquickdeferredexecute.cpp b/src/quicktemplates/qquickdeferredexecute.cpp
index 239b0fa6a6..19870a5636 100644
--- a/src/quicktemplates/qquickdeferredexecute.cpp
+++ b/src/quicktemplates/qquickdeferredexecute.cpp
@@ -30,6 +30,9 @@ static bool beginDeferred(QQmlEnginePrivate *enginePriv, const QQmlProperty &pro
QQmlData *ddata = QQmlData::get(object);
Q_ASSERT(!ddata->deferredData.isEmpty());
+ if (!ddata->propertyCache)
+ ddata->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
+
int propertyIndex = property.index();
int wasInProgress = enginePriv->inProgressCreations;
diff --git a/src/quicktemplates/qquickdeferredexecute_p_p.h b/src/quicktemplates/qquickdeferredexecute_p_p.h
index beecb3636b..31d0f887cf 100644
--- a/src/quicktemplates/qquickdeferredexecute_p_p.h
+++ b/src/quicktemplates/qquickdeferredexecute_p_p.h
@@ -27,9 +27,9 @@ class QString;
class QObject;
namespace QtQuickPrivate {
- Q_QUICKTEMPLATES2_PRIVATE_EXPORT void beginDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate, bool isOwnState);
- Q_QUICKTEMPLATES2_PRIVATE_EXPORT void cancelDeferred(QObject *object, const QString &property);
- Q_QUICKTEMPLATES2_PRIVATE_EXPORT void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate);
+ Q_QUICKTEMPLATES2_EXPORT void beginDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate, bool isOwnState);
+ Q_QUICKTEMPLATES2_EXPORT void cancelDeferred(QObject *object, const QString &property);
+ Q_QUICKTEMPLATES2_EXPORT void completeDeferred(QObject *object, const QString &property, QQuickUntypedDeferredPointer *delegate);
}
template<typename T>
diff --git a/src/quicktemplates/qquickdeferredpointer_p_p.h b/src/quicktemplates/qquickdeferredpointer_p_p.h
index cd5a87e508..e1fa85e2ee 100644
--- a/src/quicktemplates/qquickdeferredpointer_p_p.h
+++ b/src/quicktemplates/qquickdeferredpointer_p_p.h
@@ -113,10 +113,10 @@ class QQuickDeferredPointer : public QQuickUntypedDeferredPointer
{
Q_DISABLE_COPY_MOVE(QQuickDeferredPointer)
public:
- QQuickDeferredPointer() = default;
+ Q_NODISCARD_CTOR QQuickDeferredPointer() = default;
~QQuickDeferredPointer() = default;
- QQuickDeferredPointer(T *v) : QQuickUntypedDeferredPointer(v) {}
+ Q_NODISCARD_CTOR QQuickDeferredPointer(T *v) : QQuickUntypedDeferredPointer(v) {}
QQuickDeferredPointer<T> &operator=(T *o) {
QQuickUntypedDeferredPointer::operator=(o);
return *this;
diff --git a/src/quicktemplates/qquickdelaybutton_p.h b/src/quicktemplates/qquickdelaybutton_p.h
index ea8a8b6720..3e648c4739 100644
--- a/src/quicktemplates/qquickdelaybutton_p.h
+++ b/src/quicktemplates/qquickdelaybutton_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickTransition;
class QQuickDelayButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDelayButton : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickDelayButton : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY delayChanged FINAL)
@@ -62,6 +62,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDelayButton)
-
#endif // QQUICKDELAYBUTTON_P_H
diff --git a/src/quicktemplates/qquickdial.cpp b/src/quicktemplates/qquickdial.cpp
index 4fd3a15de0..3f95bd901d 100644
--- a/src/quicktemplates/qquickdial.cpp
+++ b/src/quicktemplates/qquickdial.cpp
@@ -57,10 +57,30 @@ QT_BEGIN_NAMESPACE
by the user by either touch, mouse, or keys.
*/
-static const qreal startAngleRadians = (M_PI * 2.0) * (4.0 / 6.0);
-static const qreal startAngle = -140;
-static const qreal endAngleRadians = (M_PI * 2.0) * (5.0 / 6.0);
-static const qreal endAngle = 140;
+/*!
+ \qmlsignal QtQuick.Controls::Dial::wrapped(Dial.WrapDirection direction)
+ \since 6.6
+
+ This signal is emitted when the dial wraps around, i.e. goes beyond its
+ maximum value to its minimum value, or vice versa. It is only emitted when
+ \l wrap is \c true.
+ The \a direction argument specifies the direction of the full rotation and
+ will be one of the following arguments:
+
+ \value Dial.Clockwise The dial wrapped in clockwise direction.
+ \value Dial.CounterClockwise The dial wrapped in counterclockwise direction.
+*/
+
+// The user angle is the clockwise angle between the position and the vertical
+// y-axis (12 o clock position).
+// Using radians for logic (atan2(...)) and degree for user interface
+constexpr qreal toUserAngleDeg(qreal logicAngleRad) {
+ // minus to turn clockwise, add 90 deg clockwise
+ return -logicAngleRad / M_PI * 180. + 90;
+}
+
+static const qreal defaultStartAngle = -140;
+static const qreal defaultEndAngle = 140;
class QQuickDialPrivate : public QQuickControlPrivate
{
@@ -74,7 +94,7 @@ public:
qreal linearPositionAt(const QPointF &point) const;
void setPosition(qreal position);
void updatePosition();
- bool isLargeChange(const QPointF &eventPos, qreal proposedPosition) const;
+ bool isLargeChange(qreal proposedPosition) const;
bool isHorizontalOrVertical() const;
bool handlePress(const QPointF &point, ulong timestamp) override;
@@ -87,10 +107,14 @@ public:
void updateAllValuesAreInteger();
+ void maybeEmitWrapAround(qreal pos);
+
qreal from = 0;
qreal to = 1;
qreal value = 0;
qreal position = 0;
+ qreal startAngle = defaultStartAngle;
+ qreal endAngle = defaultEndAngle;
qreal angle = startAngle;
qreal stepSize = 0;
QPointF pressPoint;
@@ -140,13 +164,34 @@ qreal QQuickDialPrivate::circularPositionAt(const QPointF &point) const
{
qreal yy = height / 2.0 - point.y();
qreal xx = point.x() - width / 2.0;
- qreal angle = (xx || yy) ? std::atan2(yy, xx) : 0;
+ qreal alpha = (xx || yy) ? toUserAngleDeg(std::atan2(yy, xx)) : 0;
+
+ // Move around the circle to reach the interval.
+ if (alpha < startAngle && alpha + 360. < endAngle)
+ alpha += 360.;
+ else if (alpha >= endAngle && alpha - 360. >= startAngle)
+ alpha -= 360.;
+
+ // If wrap is on and we are out of the interval [startAngle, endAngle],
+ // we want to jump to the closest border to make it feel nice and responsive
+ if ((alpha < startAngle || alpha > endAngle) && wrap) {
+ if (abs(alpha - startAngle) > abs(endAngle - alpha - 360.))
+ alpha += 360.;
+ else if (abs(alpha - startAngle - 360.) < abs(endAngle - alpha))
+ alpha -= 360.;
+ }
- if (angle < M_PI / -2)
- angle = angle + M_PI * 2;
+ // If wrap is off,
+ // we want to stay as close as possible to the current angle.
+ // This is important to allow easy setting of boundary values (0,1)
+ if (!wrap) {
+ if (abs(angle - alpha) > abs(angle - (alpha + 360.)))
+ alpha += 360.;
+ if (abs(angle - alpha) > abs(angle - (alpha - 360.)))
+ alpha -= 360.;
+ }
- qreal normalizedAngle = (startAngleRadians - angle) / endAngleRadians;
- return normalizedAngle;
+ return (alpha - startAngle) / (endAngle - startAngle);
}
qreal QQuickDialPrivate::linearPositionAt(const QPointF &point) const
@@ -179,12 +224,13 @@ void QQuickDialPrivate::setPosition(qreal pos)
{
Q_Q(QQuickDial);
pos = qBound<qreal>(qreal(0), pos, qreal(1));
- if (qFuzzyCompare(position, pos))
+ const qreal alpha = startAngle + pos * qAbs(endAngle - startAngle);
+ if (qFuzzyCompare(position, pos) && qFuzzyCompare(angle, alpha))
return;
+ angle = alpha;
position = pos;
- angle = startAngle + position * qAbs(endAngle - startAngle);
emit q->positionChanged();
emit q->angleChanged();
@@ -198,9 +244,11 @@ void QQuickDialPrivate::updatePosition()
setPosition(pos);
}
-bool QQuickDialPrivate::isLargeChange(const QPointF &eventPos, qreal proposedPosition) const
+bool QQuickDialPrivate::isLargeChange(qreal proposedPosition) const
{
- return qAbs(proposedPosition - position) >= qreal(0.5) && eventPos.y() >= height / 2;
+ if (endAngle - startAngle < 180.0)
+ return false;
+ return qAbs(proposedPosition - position) > qreal(0.5);
}
bool QQuickDialPrivate::isHorizontalOrVertical() const
@@ -223,11 +271,13 @@ bool QQuickDialPrivate::handleMove(const QPointF &point, ulong timestamp)
Q_Q(QQuickDial);
QQuickControlPrivate::handleMove(point, timestamp);
const qreal oldPos = position;
- qreal pos = positionAt(point);
+ qreal pos = qBound(0.0, positionAt(point), 1.0);
if (snapMode == QQuickDial::SnapAlways)
pos = snapPosition(pos);
- if (wrap || isHorizontalOrVertical() || !isLargeChange(point, pos)) {
+ maybeEmitWrapAround(pos);
+
+ if (wrap || isHorizontalOrVertical() || !isLargeChange(pos)) {
if (live)
q->setValue(valueAt(pos));
else
@@ -248,7 +298,9 @@ bool QQuickDialPrivate::handleRelease(const QPointF &point, ulong timestamp)
if (snapMode != QQuickDial::NoSnap)
pos = snapPosition(pos);
- if (wrap || isHorizontalOrVertical() || !isLargeChange(point, pos))
+ maybeEmitWrapAround(pos);
+
+ if (wrap || isHorizontalOrVertical() || !isLargeChange(pos))
q->setValue(valueAt(pos));
if (!qFuzzyCompare(pos, oldPos))
emit q->moved();
@@ -272,8 +324,6 @@ void QQuickDialPrivate::handleUngrab()
q->setPressed(false);
}
-static inline QString handleName() { return QStringLiteral("handle"); }
-
void QQuickDialPrivate::cancelHandle()
{
Q_Q(QQuickDial);
@@ -303,6 +353,14 @@ void QQuickDialPrivate::updateAllValuesAreInteger()
allValuesAreInteger = areRepresentableAsInteger(to, from, stepSize) && stepSize != 0.0;
}
+void QQuickDialPrivate::maybeEmitWrapAround(qreal pos)
+{
+ Q_Q(QQuickDial);
+
+ if (wrap && isLargeChange(pos))
+ emit q->wrapped((pos < q->position()) ? QQuickDial::Clockwise : QQuickDial::CounterClockwise);
+}
+
QQuickDial::QQuickDial(QQuickItem *parent)
: QQuickControl(*(new QQuickDialPrivate), parent)
{
@@ -314,6 +372,8 @@ QQuickDial::QQuickDial(QQuickItem *parent)
#if QT_CONFIG(cursor)
setCursor(Qt::ArrowCursor);
#endif
+ Q_D(QQuickDial);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
/*!
@@ -422,11 +482,12 @@ qreal QQuickDial::position() const
\qmlproperty real QtQuick.Controls::Dial::angle
\readonly
- This property holds the angle of the handle.
+ This property holds the clockwise angle of the handle in degrees.
- The range is from \c -140 degrees to \c 140 degrees.
+ The angle is zero at the 12 o'clock position and the range is from
+ \l startAngle to \c endAngle.
- \sa position
+ \sa position, startAngle, endAngle
*/
qreal QQuickDial::angle() const
{
@@ -469,6 +530,129 @@ void QQuickDial::setStepSize(qreal step)
emit stepSizeChanged();
}
+
+/*!
+ \qmlproperty real QtQuick.Controls::Dial::startAngle
+ \since 6.6
+
+ This property holds the starting angle of the dial in degrees.
+
+ This is the \l angle the dial will have for its minimum value, i.e. \l from.
+ The \l startAngle has to be smaller than the \l endAngle, larger than -360
+ and larger or equal to the \l endAngle - 360 degrees.
+
+ \sa endAngle, angle
+*/
+qreal QQuickDial::startAngle() const
+{
+ Q_D(const QQuickDial);
+ return d->startAngle;
+}
+
+void QQuickDial::setStartAngle(qreal startAngle)
+{
+ Q_D(QQuickDial);
+ if (!d->componentComplete) {
+ // Binding evaluation order can cause warnings with certain combinations
+ // of start and end angles, so delay the actual setting until after component completion.
+ // Store the requested value in the existing member to avoid the need for an extra one.
+ d->startAngle = startAngle;
+ return;
+ }
+
+ if (qFuzzyCompare(d->startAngle, startAngle))
+ return;
+
+ // do not allow to change direction
+ if (startAngle >= d->endAngle) {
+ qmlWarning(this) << "startAngle (" << startAngle
+ << ") cannot be greater than or equal to endAngle (" << d->endAngle << ")";
+ return;
+ }
+
+ // Keep the interval around 0
+ if (startAngle <= -360.) {
+ qmlWarning(this) << "startAngle (" << startAngle << ") cannot be less than or equal to -360";
+ return;
+ }
+
+ // keep the interval [startAngle, endAngle] unique
+ if (startAngle < d->endAngle - 360.) {
+ qmlWarning(this) << "Difference between startAngle (" << startAngle
+ << ") and endAngle (" << d->endAngle << ") cannot be greater than 360."
+ << " Changing endAngle to avoid overlaps.";
+ d->endAngle = startAngle + 360.;
+ emit endAngleChanged();
+ }
+
+ d->startAngle = startAngle;
+ // changing the startAngle will change the angle
+ // if the value is kept constant
+ d->updatePosition();
+ emit startAngleChanged();
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::Dial::endAngle
+ \since 6.6
+
+ This property holds the end angle of the dial in degrees.
+
+ This is the \l angle the dial will have for its maximum value, i.e. \l to.
+ The \l endAngle has to be bigger than the \l startAngle, smaller than 720
+ and smaller or equal than the \l startAngle + 360 degrees.
+
+ \sa endAngle, angle
+*/
+qreal QQuickDial::endAngle() const
+{
+ Q_D(const QQuickDial);
+ return d->endAngle;
+
+}
+
+void QQuickDial::setEndAngle(qreal endAngle)
+{
+ Q_D(QQuickDial);
+ if (!d->componentComplete) {
+ // Binding evaluation order can cause warnings with certain combinations
+ // of start and end angles, so delay the actual setting until after component completion.
+ // Store the requested value in the existing member to avoid the need for an extra one.
+ d->endAngle = endAngle;
+ return;
+ }
+
+ if (qFuzzyCompare(d->endAngle, endAngle))
+ return;
+
+ if (endAngle <= d->startAngle) {
+ qmlWarning(this) << "endAngle (" << endAngle
+ << ") cannot be less than or equal to startAngle (" << d->startAngle << ")";
+ return;
+ }
+
+ // Keep the interval around 0
+ if (endAngle >= 720.) {
+ qmlWarning(this) << "endAngle (" << endAngle << ") cannot be greater than or equal to 720";
+ return;
+ }
+
+ // keep the interval [startAngle, endAngle] unique
+ if (endAngle > d->startAngle + 360.) {
+ qmlWarning(this) << "Difference between startAngle (" << d->startAngle
+ << ") and endAngle (" << endAngle << ") cannot be greater than 360."
+ << " Changing startAngle to avoid overlaps.";
+ d->startAngle = endAngle - 360.;
+ emit startAngleChanged();
+ }
+
+ d->endAngle = endAngle;
+ // changing the startAngle will change the angle
+ // if the value is kept constant
+ d->updatePosition();
+ emit endAngleChanged();
+}
+
/*!
\qmlproperty enumeration QtQuick.Controls::Dial::snapMode
@@ -808,6 +992,21 @@ void QQuickDial::componentComplete()
Q_D(QQuickDial);
d->executeHandle(true);
QQuickControl::componentComplete();
+
+ // Set the (delayed) start and end angles, if necessary (see the setters for more info).
+ if (!qFuzzyCompare(d->startAngle, defaultStartAngle)) {
+ const qreal startAngle = d->startAngle;
+ // Temporarily set it to something else so that it sees that it has changed.
+ d->startAngle = defaultStartAngle;
+ setStartAngle(startAngle);
+ }
+
+ if (!qFuzzyCompare(d->endAngle, defaultEndAngle)) {
+ const qreal endAngle = d->endAngle;
+ d->endAngle = defaultEndAngle;
+ setEndAngle(endAngle);
+ }
+
setValue(d->value);
d->updatePosition();
}
diff --git a/src/quicktemplates/qquickdial_p.h b/src/quicktemplates/qquickdial_p.h
index c716b9671b..2008261214 100644
--- a/src/quicktemplates/qquickdial_p.h
+++ b/src/quicktemplates/qquickdial_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickDialAttached;
class QQuickDialPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDial : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickDial : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -32,6 +32,8 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDial : public QQuickControl
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL)
Q_PROPERTY(qreal angle READ angle NOTIFY angleChanged FINAL)
+ Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged FINAL REVISION(6, 6))
+ Q_PROPERTY(qreal endAngle READ endAngle WRITE setEndAngle NOTIFY endAngleChanged FINAL REVISION(6, 6))
Q_PROPERTY(qreal stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL)
Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged FINAL)
Q_PROPERTY(bool wrap READ wrap WRITE setWrap NOTIFY wrapChanged FINAL)
@@ -64,6 +66,12 @@ public:
qreal stepSize() const;
void setStepSize(qreal step);
+ qreal startAngle() const;
+ void setStartAngle(qreal startAngle);
+
+ qreal endAngle() const;
+ void setEndAngle(qreal endAngle);
+
enum SnapMode {
NoSnap,
SnapAlways,
@@ -81,6 +89,12 @@ public:
};
Q_ENUM(InputMode)
+ enum WrapDirection {
+ Clockwise,
+ CounterClockwise
+ };
+ Q_ENUM(WrapDirection)
+
bool wrap() const;
void setWrap(bool wrap);
@@ -118,6 +132,9 @@ Q_SIGNALS:
Q_REVISION(2, 2) void liveChanged();
// 2.5 (Qt 5.12)
Q_REVISION(2, 5) void inputModeChanged();
+ Q_REVISION(6, 6) void startAngleChanged();
+ Q_REVISION(6, 6) void endAngleChanged();
+ Q_REVISION(6, 6) void wrapped(WrapDirection);
protected:
void keyPressEvent(QKeyEvent *event) override;
@@ -145,6 +162,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDial)
-
#endif // QQUICKDIAL_P_H
diff --git a/src/quicktemplates/qquickdialog.cpp b/src/quicktemplates/qquickdialog.cpp
index 300ad4301f..7a1c5d513b 100644
--- a/src/quicktemplates/qquickdialog.cpp
+++ b/src/quicktemplates/qquickdialog.cpp
@@ -6,6 +6,7 @@
#include "qquickdialogbuttonbox_p.h"
#include "qquickabstractbutton_p.h"
#include "qquickpopupitem_p_p.h"
+#include "qquickpopupwindow_p_p.h"
QT_BEGIN_NAMESPACE
@@ -25,6 +26,10 @@ QT_BEGIN_NAMESPACE
\image qtquickcontrols-page-wireframe.png
+ The \l {Popup::}{padding} properties only affect the contentItem. Use the
+ \l {Popup::}{spacing} property to affect the space between header,
+ contentItem and footer.
+
By default, Dialogs have \l {QQuickItem::}{focus}.
\section1 Dialog Title and Buttons
@@ -160,6 +165,11 @@ void QQuickDialogPrivate::handleClick(QQuickAbstractButton *button)
}
}
+Qt::WindowFlags QQuickDialogPrivate::popupWindowType() const
+{
+ return Qt::Dialog;
+}
+
QQuickDialog::QQuickDialog(QObject *parent)
: QQuickDialog(*(new QQuickDialogPrivate), parent)
{
@@ -172,8 +182,6 @@ QQuickDialog::QQuickDialog(QQuickDialogPrivate &dd, QObject *parent)
// Dialogs should get active focus when opened so that e.g. Cancel closes them.
setFocus(true);
-
- QObject::connect(d->popupItem, &QQuickPopupItem::titleChanged, this, &QQuickDialog::titleChanged);
QObject::connect(d->popupItem, &QQuickPopupItem::headerChanged, this, &QQuickDialog::headerChanged);
QObject::connect(d->popupItem, &QQuickPopupItem::footerChanged, this, &QQuickDialog::footerChanged);
QObject::connect(d->popupItem, &QQuickPopupItem::implicitHeaderWidthChanged, this, &QQuickDialog::implicitHeaderWidthChanged);
@@ -185,7 +193,6 @@ QQuickDialog::QQuickDialog(QQuickDialogPrivate &dd, QObject *parent)
QQuickDialog::~QQuickDialog()
{
Q_D(QQuickDialog);
- QObject::disconnect(d->popupItem, &QQuickPopupItem::titleChanged, this, &QQuickDialog::titleChanged);
QObject::disconnect(d->popupItem, &QQuickPopupItem::headerChanged, this, &QQuickDialog::headerChanged);
QObject::disconnect(d->popupItem, &QQuickPopupItem::footerChanged, this, &QQuickDialog::footerChanged);
QObject::disconnect(d->popupItem, &QQuickPopupItem::implicitHeaderWidthChanged, this, &QQuickDialog::implicitHeaderWidthChanged);
@@ -214,13 +221,22 @@ QQuickDialog::~QQuickDialog()
QString QQuickDialog::title() const
{
Q_D(const QQuickDialog);
- return d->popupItem->title();
+ return d->m_title;
}
void QQuickDialog::setTitle(const QString &title)
{
Q_D(QQuickDialog);
- d->popupItem->setTitle(title);
+ if (d->m_title == title)
+ return;
+ d->m_title = title;
+
+ if (d->popupWindow)
+ d->popupWindow->setTitle(title);
+ else
+ d->popupItem->setTitle(title);
+
+ emit titleChanged();
}
/*!
@@ -484,6 +500,12 @@ qreal QQuickDialog::implicitFooterHeight() const
return d->popupItem->implicitFooterHeight();
}
+void QQuickDialog::setOpacity(qreal opacity)
+{
+ Q_D(QQuickDialog);
+ QQuickPopup::setOpacity(d->popupWindow ? qreal(!qFuzzyIsNull(opacity)) : opacity);
+}
+
/*!
\qmlmethod void QtQuick.Controls::Dialog::accept()
diff --git a/src/quicktemplates/qquickdialog_p.h b/src/quicktemplates/qquickdialog_p.h
index a350c5d923..c28e687b79 100644
--- a/src/quicktemplates/qquickdialog_p.h
+++ b/src/quicktemplates/qquickdialog_p.h
@@ -19,11 +19,13 @@
#include <QtQuickTemplates2/private/qquickpopup_p.h>
#include <QtGui/qpa/qplatformdialoghelper.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickDialogPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialog : public QQuickPopup
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialog : public QQuickPopup
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
@@ -72,6 +74,8 @@ public:
qreal implicitFooterWidth() const;
qreal implicitFooterHeight() const;
+ void setOpacity(qreal opacity) override;
+
public Q_SLOTS:
virtual void accept();
virtual void reject();
@@ -111,6 +115,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDialog)
-
#endif // QQUICKDIALOG_P_H
diff --git a/src/quicktemplates/qquickdialog_p_p.h b/src/quicktemplates/qquickdialog_p_p.h
index 2d0121892a..9b0e5d177a 100644
--- a/src/quicktemplates/qquickdialog_p_p.h
+++ b/src/quicktemplates/qquickdialog_p_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickAbstractButton;
class QQuickDialogButtonBox;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogPrivate : public QQuickPopupPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialogPrivate : public QQuickPopupPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickDialog)
@@ -40,6 +40,8 @@ public:
virtual void handleReject();
virtual void handleClick(QQuickAbstractButton *button);
+ Qt::WindowFlags popupWindowType() const override;
+
int result = 0;
QString title;
QQuickDialogButtonBox *buttonBox = nullptr;
diff --git a/src/quicktemplates/qquickdialogbuttonbox.cpp b/src/quicktemplates/qquickdialogbuttonbox.cpp
index 989c9b4506..b7c486c472 100644
--- a/src/quicktemplates/qquickdialogbuttonbox.cpp
+++ b/src/quicktemplates/qquickdialogbuttonbox.cpp
@@ -383,10 +383,8 @@ QQuickAbstractButton *QQuickDialogButtonBoxPrivate::createStandardButton(QPlatfo
QQmlContext *creationContext = delegate->creationContext();
if (!creationContext)
creationContext = qmlContext(q);
- QQmlContext *context = new QQmlContext(creationContext, q);
- context->setContextObject(q);
- QObject *object = delegate->beginCreate(context);
+ QObject *object = delegate->beginCreate(creationContext);
QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton*>(object);
if (button) {
QQuickDialogButtonBoxAttached *attached = qobject_cast<QQuickDialogButtonBoxAttached *>(qmlAttachedPropertiesObject<QQuickDialogButtonBox>(button, true));
@@ -457,6 +455,7 @@ QQuickDialogButtonBox::QQuickDialogButtonBox(QQuickItem *parent)
Q_D(QQuickDialogButtonBox);
d->changeTypes |= QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
d->buttonLayout = platformButtonLayout();
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
QQuickDialogButtonBox::~QQuickDialogButtonBox()
@@ -480,7 +479,7 @@ QQuickDialogButtonBox::~QQuickDialogButtonBox()
Possible values:
\value DialogButtonBox.Header The button box is at the top, as a window or page header.
- \value DialogButtonBox.Footer The button box is at the bottom, as a window or page header.
+ \value DialogButtonBox.Footer The button box is at the bottom, as a window or page footer.
The default value is \c Footer.
@@ -515,6 +514,13 @@ void QQuickDialogButtonBox::setPosition(Position position)
\value Qt.AlignTop The buttons are aligned to the top.
\value Qt.AlignVCenter The buttons are vertically centered.
\value Qt.AlignBottom The buttons are aligned to the bottom.
+
+ The default value is \c undefined.
+
+ \note This property assumes a horizontal layout of the buttons. The
+ DialogButtonBox for the \l {iOS Style}{iOS style} uses a vertical layout
+ when there are more than two buttons, and if set to a value other than
+ \c undefined, the layout of its buttons will be done horizontally.
*/
Qt::Alignment QQuickDialogButtonBox::alignment() const
{
diff --git a/src/quicktemplates/qquickdialogbuttonbox_p.h b/src/quicktemplates/qquickdialogbuttonbox_p.h
index 02cdd3bb63..73b0af9953 100644
--- a/src/quicktemplates/qquickdialogbuttonbox_p.h
+++ b/src/quicktemplates/qquickdialogbuttonbox_p.h
@@ -19,6 +19,8 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
#include <QtGui/qpa/qplatformdialoghelper.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQmlComponent;
@@ -26,7 +28,7 @@ class QQuickDialogButtonBoxPrivate;
class QQuickDialogButtonBoxAttached;
class QQuickDialogButtonBoxAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBox : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialogButtonBox : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(Position position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -35,9 +37,9 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBox : public QQuickCont
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL)
// 2.5 (Qt 5.12)
Q_PROPERTY(QPlatformDialogHelper::ButtonLayout buttonLayout READ buttonLayout WRITE setButtonLayout RESET resetButtonLayout NOTIFY buttonLayoutChanged FINAL REVISION(2, 5))
- Q_FLAGS(QPlatformDialogHelper::StandardButtons)
QML_NAMED_ELEMENT(DialogButtonBox)
QML_ATTACHED(QQuickDialogButtonBoxAttached)
+ QML_EXTENDED_NAMESPACE(QPlatformDialogHelper)
QML_ADDED_IN_VERSION(2, 1)
public:
@@ -66,9 +68,6 @@ public:
static QQuickDialogButtonBoxAttached *qmlAttachedProperties(QObject *object);
- // 2.5 (Qt 5.12)
- Q_ENUMS(QPlatformDialogHelper::ButtonLayout)
-
QPlatformDialogHelper::ButtonLayout buttonLayout() const;
void setButtonLayout(QPlatformDialogHelper::ButtonLayout layout);
void resetButtonLayout();
@@ -108,12 +107,11 @@ private:
Q_DECLARE_PRIVATE(QQuickDialogButtonBox)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBoxAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialogButtonBoxAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickDialogButtonBox *buttonBox READ buttonBox NOTIFY buttonBoxChanged FINAL)
Q_PROPERTY(QPlatformDialogHelper::ButtonRole buttonRole READ buttonRole WRITE setButtonRole NOTIFY buttonRoleChanged FINAL)
- Q_ENUMS(QPlatformDialogHelper::ButtonRole)
public:
explicit QQuickDialogButtonBoxAttached(QObject *parent = nullptr);
@@ -134,6 +132,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDialogButtonBox)
-
#endif // QQUICKDIALOGBUTTONBOX_P_H
diff --git a/src/quicktemplates/qquickdialogbuttonbox_p_p.h b/src/quicktemplates/qquickdialogbuttonbox_p_p.h
index f2d9ff6b2d..b4b8af840f 100644
--- a/src/quicktemplates/qquickdialogbuttonbox_p_p.h
+++ b/src/quicktemplates/qquickdialogbuttonbox_p_p.h
@@ -20,7 +20,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDialogButtonBoxPrivate : public QQuickContainerPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickDialogButtonBoxPrivate : public QQuickContainerPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickDialogButtonBox)
diff --git a/src/quicktemplates/qquickdrawer.cpp b/src/quicktemplates/qquickdrawer.cpp
index 54b4eedaaa..678d434629 100644
--- a/src/quicktemplates/qquickdrawer.cpp
+++ b/src/quicktemplates/qquickdrawer.cpp
@@ -12,6 +12,7 @@
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickanimation_p.h>
#include <QtQuick/private/qquicktransition_p.h>
+#include <QtQuickTemplates2/private/qquickoverlay_p.h>
QT_BEGIN_NAMESPACE
@@ -127,7 +128,7 @@ QT_BEGIN_NAMESPACE
Drawer can be configured as a non-closable persistent side panel by
making the Drawer \l {Popup::modal}{non-modal} and \l {interactive}
- {non-interactive}. See the \l {Qt Quick Controls 2 - Side Panel}{Side Panel}
+ {non-interactive}. See the \l {Qt Quick Controls 2 - Gallery}{Gallery}
example for more details.
\note On some platforms, certain edges may be reserved for system
@@ -164,15 +165,25 @@ qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const
if (!window)
return 0;
- switch (edge) {
+ auto size = QSizeF(q->width(), q->height());
+
+ switch (effectiveEdge()) {
case Qt::TopEdge:
- return point.y() / q->height();
+ if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ size.transpose();
+ return point.y() / size.height();
case Qt::LeftEdge:
- return point.x() / q->width();
+ if (edge == Qt::TopEdge || edge == Qt::BottomEdge)
+ size.transpose();
+ return point.x() / size.width();
case Qt::RightEdge:
- return (window->width() - point.x()) / q->width();
+ if (edge == Qt::TopEdge || edge == Qt::BottomEdge)
+ size.transpose();
+ return (window->width() - point.x()) / size.width();
case Qt::BottomEdge:
- return (window->height() - point.y()) / q->height();
+ if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ size.transpose();
+ return (window->height() - point.y()) / size.height();
default:
return 0;
}
@@ -216,22 +227,24 @@ void QQuickDrawerPositioner::reposition()
QQuickPopupPositioner::reposition();
}
-void QQuickDrawerPrivate::showOverlay()
+void QQuickDrawerPrivate::showDimmer()
{
// managed in setPosition()
}
-void QQuickDrawerPrivate::hideOverlay()
+void QQuickDrawerPrivate::hideDimmer()
{
// managed in setPosition()
}
-void QQuickDrawerPrivate::resizeOverlay()
+void QQuickDrawerPrivate::resizeDimmer()
{
if (!dimmer || !window)
return;
- QRectF geometry(0, 0, window->width(), window->height());
+ const QQuickOverlay *overlay = QQuickOverlay::overlay(window);
+
+ QRectF geometry(0, 0, overlay ? overlay->width() : 0, overlay ? overlay->height() : 0);
if (edge == Qt::LeftEdge || edge == Qt::RightEdge) {
geometry.setY(popupItem->y());
@@ -245,17 +258,18 @@ void QQuickDrawerPrivate::resizeOverlay()
dimmer->setSize(geometry.size());
}
-static bool isWithinDragMargin(const QQuickDrawer *drawer, const QPointF &pos)
+bool QQuickDrawerPrivate::isWithinDragMargin(const QPointF &pos) const
{
- switch (drawer->edge()) {
+ Q_Q(const QQuickDrawer);
+ switch (effectiveEdge()) {
case Qt::LeftEdge:
- return pos.x() <= drawer->dragMargin();
+ return pos.x() <= q->dragMargin();
case Qt::RightEdge:
- return pos.x() >= drawer->window()->width() - drawer->dragMargin();
+ return pos.x() >= q->window()->width() - q->dragMargin();
case Qt::TopEdge:
- return pos.y() <= drawer->dragMargin();
+ return pos.y() <= q->dragMargin();
case Qt::BottomEdge:
- return pos.y() >= drawer->window()->height() - drawer->dragMargin();
+ return pos.y() >= q->window()->height() - q->dragMargin();
default:
Q_UNREACHABLE();
break;
@@ -265,14 +279,13 @@ static bool isWithinDragMargin(const QQuickDrawer *drawer, const QPointF &pos)
bool QQuickDrawerPrivate::startDrag(QEvent *event)
{
- Q_Q(QQuickDrawer);
delayedEnterTransition = false;
if (!window || !interactive || dragMargin < 0.0 || qFuzzyIsNull(dragMargin))
return false;
switch (event->type()) {
case QEvent::MouseButtonPress:
- if (QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); isWithinDragMargin(q, mouseEvent->scenePosition())) {
+ if (QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); isWithinDragMargin(mouseEvent->scenePosition())) {
// watch future events and grab the mouse once it has moved
// sufficiently fast or far (in grabMouse).
delayedEnterTransition = true;
@@ -287,7 +300,7 @@ bool QQuickDrawerPrivate::startDrag(QEvent *event)
case QEvent::TouchUpdate: {
auto *touchEvent = static_cast<QTouchEvent *>(event);
for (const QTouchEvent::TouchPoint &point : touchEvent->points()) {
- if (point.state() == QEventPoint::Pressed && isWithinDragMargin(q, point.scenePosition())) {
+ if (point.state() == QEventPoint::Pressed && isWithinDragMargin(point.scenePosition())) {
delayedEnterTransition = true;
touchEvent->addPassiveGrabber(point, popupItem);
handleTouchEvent(window->contentItem(), touchEvent);
@@ -325,10 +338,11 @@ bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event)
// larger threshold to avoid being too eager to steal touch (QTBUG-50045)
const int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5);
bool overThreshold = false;
+ Qt::Edge effEdge = effectiveEdge();
if (position > 0 || dragMargin > 0) {
const bool xOverThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, event, threshold);
const bool yOverThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, event, threshold);
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
overThreshold = xOverThreshold && !yOverThreshold;
else
overThreshold = yOverThreshold && !xOverThreshold;
@@ -336,7 +350,7 @@ bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event)
// Don't be too eager to steal presses outside the drawer (QTBUG-53929)
if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !contains(movePoint)) {
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin;
else
overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin;
@@ -377,10 +391,11 @@ bool QQuickDrawerPrivate::grabTouch(QQuickItem *item, QTouchEvent *event)
// QStyleHints::startDragDistance for dragging. Drawer uses a bit
// larger threshold to avoid being too eager to steal touch (QTBUG-50045)
const int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5);
+ const Qt::Edge effEdge = effectiveEdge();
if (position > 0 || dragMargin > 0) {
const bool xOverThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, &point, threshold);
const bool yOverThreshold = QQuickWindowPrivate::dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, &point, threshold);
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
overThreshold = xOverThreshold && !yOverThreshold;
else
overThreshold = yOverThreshold && !xOverThreshold;
@@ -388,7 +403,7 @@ bool QQuickDrawerPrivate::grabTouch(QQuickItem *item, QTouchEvent *event)
// Don't be too eager to steal presses outside the drawer (QTBUG-53929)
if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !contains(movePoint)) {
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin;
else
overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin;
@@ -421,8 +436,6 @@ static const qreal openCloseVelocityThreshold = 300;
// interactive control.
bool QQuickDrawerPrivate::blockInput(QQuickItem *item, const QPointF &point) const
{
- Q_Q(const QQuickDrawer);
-
// We want all events, if mouse/touch is already grabbed.
if (popupItem->keepMouseGrab() || popupItem->keepTouchGrab())
return true;
@@ -436,7 +449,7 @@ bool QQuickDrawerPrivate::blockInput(QQuickItem *item, const QPointF &point) con
return false;
// Accept all events within drag area.
- if (isWithinDragMargin(q, point))
+ if (isWithinDragMargin(point))
return true;
// Accept all other events if drawer is modal.
@@ -487,9 +500,9 @@ bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point,
}
velocityCalculator.stopMeasuring(point, timestamp);
-
+ Qt::Edge effEdge = effectiveEdge();
qreal velocity = 0;
- if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
+ if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
velocity = velocityCalculator.velocity().x();
else
velocity = velocityCalculator.velocity().y();
@@ -502,7 +515,7 @@ bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point,
// - bottom/right edge: negative velocity opens, positive velocity closes
//
// => invert the velocity for bottom and right edges, for the threshold comparison below
- if (edge == Qt::RightEdge || edge == Qt::BottomEdge)
+ if (effEdge == Qt::RightEdge || effEdge == Qt::BottomEdge)
velocity = -velocity;
if (position > 0.7 || velocity > openCloseVelocityThreshold) {
@@ -510,7 +523,7 @@ bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point,
} else if (position < 0.3 || velocity < -openCloseVelocityThreshold) {
transitionManager.transitionExit();
} else {
- switch (edge) {
+ switch (effEdge) {
case Qt::LeftEdge:
if (point.x() - pressPoint.x() > 0)
transitionManager.transitionEnter();
@@ -583,6 +596,12 @@ bool QQuickDrawerPrivate::prepareExitTransition()
return QQuickPopupPrivate::prepareExitTransition();
}
+QQuickPopup::PopupType QQuickDrawerPrivate::resolvedPopupType() const
+{
+ // For now, a drawer will always be shown in-scene
+ return QQuickPopup::Item;
+}
+
bool QQuickDrawerPrivate::setEdge(Qt::Edge e)
{
Q_Q(QQuickDrawer);
@@ -641,6 +660,31 @@ Qt::Edge QQuickDrawer::edge() const
return d->edge;
}
+Qt::Edge QQuickDrawerPrivate::effectiveEdge() const
+{
+ auto realEdge = edge;
+ qreal rotation = window->contentItem()->rotation();
+ const bool clockwise = rotation > 0;
+ while (qAbs(rotation) >= 90) {
+ rotation -= clockwise ? 90 : -90;
+ switch (realEdge) {
+ case Qt::LeftEdge:
+ realEdge = clockwise ? Qt::TopEdge : Qt::BottomEdge;
+ break;
+ case Qt::TopEdge:
+ realEdge = clockwise ? Qt::RightEdge : Qt::LeftEdge;
+ break;
+ case Qt::RightEdge:
+ realEdge = clockwise ? Qt::BottomEdge : Qt::TopEdge;
+ break;
+ case Qt::BottomEdge:
+ realEdge = clockwise ? Qt::LeftEdge : Qt::RightEdge;
+ break;
+ }
+ }
+ return realEdge;
+}
+
void QQuickDrawer::setEdge(Qt::Edge edge)
{
Q_D(QQuickDrawer);
@@ -799,7 +843,7 @@ void QQuickDrawer::geometryChange(const QRectF &newGeometry, const QRectF &oldGe
{
Q_D(QQuickDrawer);
QQuickPopup::geometryChange(newGeometry, oldGeometry);
- d->resizeOverlay();
+ d->resizeDimmer();
}
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickdrawer_p.h b/src/quicktemplates/qquickdrawer_p.h
index ec42117f20..a8255e992e 100644
--- a/src/quicktemplates/qquickdrawer_p.h
+++ b/src/quicktemplates/qquickdrawer_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickDrawerPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickDrawer : public QQuickPopup
+class Q_QUICKTEMPLATES2_EXPORT QQuickDrawer : public QQuickPopup
{
Q_OBJECT
Q_PROPERTY(Qt::Edge edge READ edge WRITE setEdge NOTIFY edgeChanged FINAL)
@@ -73,6 +73,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDrawer)
-
#endif // QQUICKDRAWER_P_H
diff --git a/src/quicktemplates/qquickdrawer_p_p.h b/src/quicktemplates/qquickdrawer_p_p.h
index 224e557839..18646463b5 100644
--- a/src/quicktemplates/qquickdrawer_p_p.h
+++ b/src/quicktemplates/qquickdrawer_p_p.h
@@ -35,9 +35,9 @@ public:
qreal positionAt(const QPointF &point) const;
QQuickPopupPositioner *getPositioner() override;
- void showOverlay() override;
- void hideOverlay() override;
- void resizeOverlay() override;
+ void showDimmer() override;
+ void hideDimmer() override;
+ void resizeDimmer() override;
bool startDrag(QEvent *event);
bool grabMouse(QQuickItem *item, QMouseEvent *event);
@@ -54,7 +54,11 @@ public:
bool prepareEnterTransition() override;
bool prepareExitTransition() override;
+ QQuickPopup::PopupType resolvedPopupType() const override;
+
bool setEdge(Qt::Edge edge);
+ Qt::Edge effectiveEdge() const;
+ bool isWithinDragMargin(const QPointF &point) const;
Qt::Edge edge = Qt::LeftEdge;
qreal offset = 0;
diff --git a/src/quicktemplates/qquickframe_p.h b/src/quicktemplates/qquickframe_p.h
index 94c98685f4..6500001fa6 100644
--- a/src/quicktemplates/qquickframe_p.h
+++ b/src/quicktemplates/qquickframe_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickFramePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickFrame : public QQuickPane
+class Q_QUICKTEMPLATES2_EXPORT QQuickFrame : public QQuickPane
{
Q_OBJECT
QML_NAMED_ELEMENT(Frame)
@@ -44,6 +44,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickFrame)
-
#endif // QQUICKFRAME_P_H
diff --git a/src/quicktemplates/qquickframe_p_p.h b/src/quicktemplates/qquickframe_p_p.h
index bb7eabf12a..8f882a26e4 100644
--- a/src/quicktemplates/qquickframe_p_p.h
+++ b/src/quicktemplates/qquickframe_p_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickFrame;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickFramePrivate : public QQuickPanePrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickFramePrivate : public QQuickPanePrivate
{
};
diff --git a/src/quicktemplates/qquickgroupbox.cpp b/src/quicktemplates/qquickgroupbox.cpp
index 0fa2d1a6d8..72ce1b42cb 100644
--- a/src/quicktemplates/qquickgroupbox.cpp
+++ b/src/quicktemplates/qquickgroupbox.cpp
@@ -61,6 +61,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::GroupBox); }
@@ -104,6 +105,16 @@ void QQuickGroupBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit q->implicitLabelHeightChanged();
}
+void QQuickGroupBoxPrivate::itemDestroyed(QQuickItem *item)
+{
+ Q_Q(QQuickGroupBox);
+ QQuickFramePrivate::itemDestroyed(item);
+ if (item == label) {
+ label = nullptr;
+ emit q->labelChanged();
+ }
+}
+
QQuickGroupBox::QQuickGroupBox(QQuickItem *parent)
: QQuickFrame(*(new QQuickGroupBoxPrivate), parent)
{
diff --git a/src/quicktemplates/qquickgroupbox_p.h b/src/quicktemplates/qquickgroupbox_p.h
index 5fc6284fea..0b2a5a0e52 100644
--- a/src/quicktemplates/qquickgroupbox_p.h
+++ b/src/quicktemplates/qquickgroupbox_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickGroupBoxPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickGroupBox : public QQuickFrame
+class Q_QUICKTEMPLATES2_EXPORT QQuickGroupBox : public QQuickFrame
{
Q_OBJECT
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
@@ -71,6 +71,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickGroupBox)
-
#endif // QQUICKGROUPBOX_P_H
diff --git a/src/quicktemplates/qquickheaderview.cpp b/src/quicktemplates/qquickheaderview.cpp
index 0d9bc5588c..dedf3a23e1 100644
--- a/src/quicktemplates/qquickheaderview.cpp
+++ b/src/quicktemplates/qquickheaderview.cpp
@@ -11,19 +11,9 @@
\inherits TableView
\brief Provides a horizontal header view to accompany a \l TableView.
- A HorizontalHeaderView provides labeling of the columns of a \l TableView.
- To add a horizontal header to a TableView, bind the
- \l {HorizontalHeaderView::syncView} {syncView} property to the TableView:
+ \include qquickheaderview.qdocinc {detailed-description} {HorizontalHeaderView}
- \snippet qtquickcontrols-headerview-simple.qml horizontal
-
- The header displays data from the {syncView}'s model by default, but can
- also have its own model. If the model is a QAbstractTableModel, then
- the header will display the model's horizontal headerData(); otherwise,
- the model's data().
-
- A HorizontalHeaderView will have
- \l {resizableColumns}{TableView::resizableColumns} set to \c true by default.
+ \sa VerticalHeaderView
*/
/*!
@@ -31,117 +21,61 @@
\inqmlmodule QtQuick.Controls
\ingroup qtquickcontrols-containers
\inherits TableView
- \brief Provides a vertical header view to accompany a \l TableView.
-
- A VerticalHeaderView provides labeling of the rows of a \l TableView.
- To add a vertical header to a TableView, bind the
- \l {VerticalHeaderView::syncView} {syncView} property to the TableView:
+ \brief Offers a vertical header view to accompany a \l TableView.
- \snippet qtquickcontrols-headerview-simple.qml vertical
+ \include qquickheaderview.qdocinc {detailed-description} {VerticalHeaderView}
- The header displays data from the {syncView}'s model by default, but can
- also have its own model. If the model is a QAbstractTableModel, then
- the header will display the model's vertical headerData(); otherwise,
- the model's data().
-
- A VerticalHeaderView will have
- \l {resizableRows}{TableView::resizableRows} set to \c true by default.
+ \sa HorizontalHeaderView
*/
/*!
- \qmlproperty TableView QtQuick::HorizontalHeaderView::syncView
-
- This property holds the TableView to synchronize with.
+ \qmlproperty TableView QtQuick.Controls::HorizontalHeaderView::syncView
- Once this property is bound to another TableView, both header and table
- will synchronize with regard to column widths, column spacing, and flicking
- horizontally.
-
- If the \l model is not explicitly set, then the header will use the syncView's
- model to label the columns.
-
- \sa model TableView
+ \include qquickheaderview.qdocinc {syncView} {horizontally}
*/
/*!
- \qmlproperty TableView QtQuick::VerticalHeaderView::syncView
-
- This property holds the TableView to synchronize with.
-
- Once this property is bound to another TableView, both header and table
- will synchronize with regard to row heights, row spacing, and flicking
- vertically.
-
- If the \l model is not explicitly set, then the header will use the syncView's
- model to label the rows.
+ \qmlproperty TableView QtQuick.Controls::VerticalHeaderView::syncView
- \sa model TableView
+ \include qquickheaderview.qdocinc {syncView} {vertically}
*/
/*!
- \qmlproperty QVariant QtQuick::HorizontalHeaderView::model
+ \qmlproperty QVariant QtQuick.Controls::HorizontalHeaderView::model
- This property holds the model providing data for the horizontal header view.
-
- When model is not explicitly set, the header will use the syncView's
- model once syncView is set.
-
- If model is a QAbstractTableModel, its horizontal headerData() will
- be accessed.
-
- If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
- will be accessed.
-
- Otherwise, the behavior is same as setting TableView::model.
-
- \sa TableView {TableView::model} {model} QAbstractTableModel
+ \include qquickheaderview.qdocinc {model} {horizontal}
*/
/*!
- \qmlproperty QVariant QtQuick::VerticalHeaderView::model
-
- This property holds the model providing data for the vertical header view.
-
- When model is not explicitly set, it will be synchronized with syncView's model
- once syncView is set.
-
- If model is a QAbstractTableModel, its vertical headerData() will
- be accessed.
-
- If model is a QAbstractItemModel other than QAbstractTableModel, model's data()
- will be accessed.
+ \qmlproperty QVariant QtQuick.Controls::VerticalHeaderView::model
- Otherwise, the behavior is same as setting TableView::model.
-
- \sa TableView {TableView::model} {model} QAbstractTableModel
+ \include qquickheaderview.qdocinc {model} {vertical}
*/
/*!
- \qmlproperty QString QtQuick::HorizontalHeaderView::textRole
-
- This property holds the model role used to display text in each header cell.
+ \qmlproperty QString QtQuick.Controls::HorizontalHeaderView::textRole
- When the model has multiple roles, textRole can be set to determine which
- role should be displayed.
+ \include qquickheaderview.qdocinc {textRole}
+*/
- If model is a QAbstractItemModel then it will default to "display"; otherwise
- it is empty.
+/*!
+ \qmlproperty QString QtQuick.Controls::VerticalHeaderView::textRole
- \sa QAbstractItemModel::roleNames()
+ \include qquickheaderview.qdocinc {textRole}
*/
/*!
- \qmlproperty QString QtQuick::VerticalHeaderView::textRole
-
- This property holds the model role used to display text in each header cell.
+ \qmlproperty bool QtQuick.Controls::HorizontalHeaderView::movableColumns
+ \since 6.8
- When the model has multiple roles, textRole can be set to determine which
- role should be displayed.
+ \include qquickheaderview.qdocinc {movableColumns}
+*/
- If model is a QAbstractItemModel then it will default to "display"; otherwise
- it is empty.
+/*!
+ \qmlproperty bool QtQuick.Controls::VerticalHeaderView::movableRows
+ \since 6.8
- \sa QAbstractItemModel::roleNames()
+ \include qquickheaderview.qdocinc {movableRows}
*/
QT_BEGIN_NAMESPACE
@@ -234,36 +168,61 @@ void QQuickHeaderViewBasePrivate::syncModel()
void QQuickHeaderViewBasePrivate::syncSyncView()
{
- Q_Q(QQuickHeaderViewBase);
if (assignedSyncDirection != orientation()) {
qmlWarning(q_func()) << "Setting syncDirection other than Qt::"
<< QVariant::fromValue(orientation()).toString()
<< " is invalid.";
assignedSyncDirection = orientation();
}
- if (assignedSyncView) {
- QBoolBlocker fixupGuard(inUpdateContentSize, true);
- if (orientation() == Qt::Horizontal) {
- q->setLeftMargin(assignedSyncView->leftMargin());
- q->setRightMargin(assignedSyncView->rightMargin());
- } else {
- q->setTopMargin(assignedSyncView->topMargin());
- q->setBottomMargin(assignedSyncView->bottomMargin());
- }
- }
QQuickTableViewPrivate::syncSyncView();
}
+QAbstractItemModel *QQuickHeaderViewBasePrivate::selectionSourceModel()
+{
+ // Our proxy model shares no common model items with HeaderView.model. So
+ // selections done in HeaderView cannot be represented in an ItemSelectionModel
+ // that is shared with the syncView (and for the same reason, the mapping functions
+ // modelIndex(cell) and cellAtIndex(index) have not been overridden either).
+ // Instead, we set the internal proxy model as selection source model.
+ return &m_headerDataProxyModel;
+}
+
+int QQuickHeaderViewBasePrivate::logicalRowIndex(const int visualIndex) const
+{
+ return (m_headerDataProxyModel.orientation() == Qt::Horizontal) ? visualIndex : QQuickTableViewPrivate::logicalRowIndex(visualIndex);
+}
+
+int QQuickHeaderViewBasePrivate::logicalColumnIndex(const int visualIndex) const
+{
+ return (m_headerDataProxyModel.orientation() == Qt::Vertical) ? visualIndex : QQuickTableViewPrivate::logicalColumnIndex(visualIndex);
+}
+
+int QQuickHeaderViewBasePrivate::visualRowIndex(const int logicalIndex) const
+{
+ return (m_headerDataProxyModel.orientation() == Qt::Horizontal) ? logicalIndex : QQuickTableViewPrivate::visualRowIndex(logicalIndex);
+}
+
+int QQuickHeaderViewBasePrivate::visualColumnIndex(const int logicalIndex) const
+{
+ return (m_headerDataProxyModel.orientation() == Qt::Vertical) ? logicalIndex : QQuickTableViewPrivate::visualColumnIndex(logicalIndex);
+}
+
QQuickHeaderViewBase::QQuickHeaderViewBase(Qt::Orientation orient, QQuickItem *parent)
: QQuickTableView(*(new QQuickHeaderViewBasePrivate), parent)
{
- d_func()->setOrientation(orient);
+ Q_D(QQuickHeaderViewBase);
+ d->m_headerDataProxyModel.m_headerView = this;
+ d->setSizePolicy(orient == Qt::Horizontal ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed,
+ orient == Qt::Horizontal ? QLayoutPolicy::Fixed : QLayoutPolicy::Preferred);
+ d->setOrientation(orient);
setSyncDirection(orient);
}
QQuickHeaderViewBase::QQuickHeaderViewBase(QQuickHeaderViewBasePrivate &dd, QQuickItem *parent)
: QQuickTableView(dd, parent)
{
+ Q_D(QQuickHeaderViewBase);
+ d->m_headerDataProxyModel.m_headerView = this;
}
QQuickHeaderViewBase::~QQuickHeaderViewBase()
@@ -384,6 +343,24 @@ bool QHeaderDataProxyModel::hasChildren(const QModelIndex &parent) const
return false;
}
+QHash<int, QByteArray> QHeaderDataProxyModel::roleNames() const
+{
+ using namespace Qt::Literals::StringLiterals;
+
+ auto names = m_model ? m_model->roleNames() : QAbstractItemModel::roleNames();
+ if (m_headerView) {
+ QString textRole = m_headerView->textRole();
+ if (textRole.isEmpty())
+ textRole = u"display"_s;
+ if (!names.values().contains(textRole.toUtf8().constData())) {
+ qmlWarning(m_headerView).nospace() << "The 'textRole' property contains a role that doesn't exist in the model: "
+ << textRole << ". Check your model's roleNames() implementation";
+ }
+ }
+
+ return names;
+}
+
QVariant QHeaderDataProxyModel::variantValue() const
{
return QVariant::fromValue(static_cast<QObject *>(const_cast<QHeaderDataProxyModel *>(this)));
@@ -472,6 +449,30 @@ QQuickHorizontalHeaderView::QQuickHorizontalHeaderView(QQuickItem *parent)
QQuickHorizontalHeaderView::~QQuickHorizontalHeaderView()
{
+ Q_D(QQuickHorizontalHeaderView);
+ d->destroySectionDragHandler();
+}
+
+bool QQuickHorizontalHeaderView::movableColumns() const
+{
+ Q_D(const QQuickHorizontalHeaderView);
+ return d->m_movableColumns;
+}
+
+void QQuickHorizontalHeaderView::setMovableColumns(bool movableColumns)
+{
+ Q_D(QQuickHorizontalHeaderView);
+ if (d->m_movableColumns == movableColumns)
+ return;
+
+ d->m_movableColumns = movableColumns;
+
+ if (d->m_movableColumns)
+ d->initSectionDragHandler(Qt::Horizontal);
+ else
+ d->destroySectionDragHandler();
+
+ emit movableColumnsChanged();
}
QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickItem *parent)
@@ -483,6 +484,30 @@ QQuickVerticalHeaderView::QQuickVerticalHeaderView(QQuickItem *parent)
QQuickVerticalHeaderView::~QQuickVerticalHeaderView()
{
+ Q_D(QQuickVerticalHeaderView);
+ d->destroySectionDragHandler();
+}
+
+bool QQuickVerticalHeaderView::movableRows() const
+{
+ Q_D(const QQuickVerticalHeaderView);
+ return d->m_movableRows ;
+}
+
+void QQuickVerticalHeaderView::setMovableRows(bool movableRows)
+{
+ Q_D(QQuickVerticalHeaderView);
+ if (d->m_movableRows == movableRows)
+ return;
+
+ d->m_movableRows = movableRows;
+
+ if (d->m_movableRows)
+ d->initSectionDragHandler(Qt::Vertical);
+ else
+ d->destroySectionDragHandler();
+
+ emit movableRowsChanged();
}
QQuickHorizontalHeaderViewPrivate::QQuickHorizontalHeaderViewPrivate() = default;
diff --git a/src/quicktemplates/qquickheaderview_p.h b/src/quicktemplates/qquickheaderview_p.h
index 8e4ea10a57..ba123a3c3e 100644
--- a/src/quicktemplates/qquickheaderview_p.h
+++ b/src/quicktemplates/qquickheaderview_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickHeaderViewBase;
class QQuickHeaderViewBasePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickHeaderViewBase : public QQuickTableView
+class Q_QUICKTEMPLATES2_EXPORT QQuickHeaderViewBase : public QQuickTableView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickHeaderViewBase)
@@ -48,10 +48,11 @@ private:
};
class QQuickHorizontalHeaderViewPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickHorizontalHeaderView : public QQuickHeaderViewBase
+class Q_QUICKTEMPLATES2_EXPORT QQuickHorizontalHeaderView : public QQuickHeaderViewBase
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickHorizontalHeaderView)
+ Q_PROPERTY(bool movableColumns READ movableColumns WRITE setMovableColumns NOTIFY movableColumnsChanged REVISION(6, 8) FINAL)
QML_NAMED_ELEMENT(HorizontalHeaderView)
QML_ADDED_IN_VERSION(2, 15)
@@ -59,6 +60,12 @@ public:
QQuickHorizontalHeaderView(QQuickItem *parent = nullptr);
~QQuickHorizontalHeaderView() override;
+ bool movableColumns() const;
+ void setMovableColumns(bool movableColumns);
+
+Q_SIGNALS:
+ Q_REVISION(6, 8) void movableColumnsChanged();
+
protected:
QQuickHorizontalHeaderView(QQuickHorizontalHeaderViewPrivate &dd, QQuickItem *parent);
@@ -67,10 +74,11 @@ private:
};
class QQuickVerticalHeaderViewPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickVerticalHeaderView : public QQuickHeaderViewBase
+class Q_QUICKTEMPLATES2_EXPORT QQuickVerticalHeaderView : public QQuickHeaderViewBase
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickVerticalHeaderView)
+ Q_PROPERTY(bool movableRows READ movableRows WRITE setMovableRows NOTIFY movableRowsChanged REVISION(6, 8) FINAL)
QML_NAMED_ELEMENT(VerticalHeaderView)
QML_ADDED_IN_VERSION(2, 15)
@@ -78,6 +86,12 @@ public:
QQuickVerticalHeaderView(QQuickItem *parent = nullptr);
~QQuickVerticalHeaderView() override;
+ bool movableRows() const;
+ void setMovableRows(bool movableRows);
+
+Q_SIGNALS:
+ Q_REVISION(6, 8) void movableRowsChanged();
+
protected:
QQuickVerticalHeaderView(QQuickVerticalHeaderViewPrivate &dd, QQuickItem *parent);
@@ -85,17 +99,6 @@ private:
Q_DISABLE_COPY(QQuickVerticalHeaderView)
};
-struct QQuickTableViewForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickTableView)
- QML_ADDED_IN_VERSION(2, 14)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickHorizontalHeaderView)
-QML_DECLARE_TYPE(QQuickVerticalHeaderView)
-
#endif // QQUICKHEADERVIEW_P_H
diff --git a/src/quicktemplates/qquickheaderview_p_p.h b/src/quicktemplates/qquickheaderview_p_p.h
index fe33cb0190..63abd58aa3 100644
--- a/src/quicktemplates/qquickheaderview_p_p.h
+++ b/src/quicktemplates/qquickheaderview_p_p.h
@@ -25,7 +25,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QHeaderDataProxyModel : public QAbstractItemModel
+class Q_QUICKTEMPLATES2_EXPORT QHeaderDataProxyModel : public QAbstractItemModel
{
Q_OBJECT
Q_DISABLE_COPY(QHeaderDataProxyModel)
@@ -44,11 +44,14 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
+ QHash<int, QByteArray> roleNames() const override;
inline QVariant variantValue() const;
inline Qt::Orientation orientation() const;
inline void setOrientation(Qt::Orientation o);
+ QQuickHeaderViewBase *m_headerView = nullptr;
+
private:
inline void connectToModel();
inline void disconnectFromModel();
@@ -70,6 +73,7 @@ public:
void setModelImpl(const QVariant &newModel) override;
void syncModel() override;
void syncSyncView() override;
+ QAbstractItemModel *selectionSourceModel() override;
protected:
QHeaderDataProxyModel m_headerDataProxyModel;
@@ -84,6 +88,11 @@ protected:
QStack<SectionSize> m_hiddenSectionSizes;
bool m_modelExplicitlySetByUser = false;
QString m_textRole;
+
+ int logicalRowIndex(const int visualIndex) const final;
+ int logicalColumnIndex(const int visualIndex) const final;
+ int visualRowIndex(const int logicalIndex) const final;
+ int visualColumnIndex(const int logicalIndex) const final;
};
class QQuickHorizontalHeaderViewPrivate : public QQuickHeaderViewBasePrivate
@@ -92,6 +101,8 @@ class QQuickHorizontalHeaderViewPrivate : public QQuickHeaderViewBasePrivate
public:
QQuickHorizontalHeaderViewPrivate();
~QQuickHorizontalHeaderViewPrivate();
+
+ bool m_movableColumns = false;
};
class QQuickVerticalHeaderViewPrivate : public QQuickHeaderViewBasePrivate
@@ -100,6 +111,8 @@ class QQuickVerticalHeaderViewPrivate : public QQuickHeaderViewBasePrivate
public:
QQuickVerticalHeaderViewPrivate();
~QQuickVerticalHeaderViewPrivate();
+
+ bool m_movableRows = false;
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickicon_p.h b/src/quicktemplates/qquickicon_p.h
index 737b28ad89..5f513ea7d3 100644
--- a/src/quicktemplates/qquickicon_p.h
+++ b/src/quicktemplates/qquickicon_p.h
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
class QQuickIconPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickIcon
+class Q_QUICKTEMPLATES2_EXPORT QQuickIcon
{
Q_GADGET
Q_PROPERTY(QString name READ name WRITE setName RESET resetName FINAL)
diff --git a/src/quicktemplates/qquickindicatorbutton_p.cpp b/src/quicktemplates/qquickindicatorbutton_p.cpp
index 1d95a9e631..22c13b9e4e 100644
--- a/src/quicktemplates/qquickindicatorbutton_p.cpp
+++ b/src/quicktemplates/qquickindicatorbutton_p.cpp
@@ -8,8 +8,6 @@ QT_BEGIN_NAMESPACE
class QQuickIndicatorButton;
-static inline QString indicatorName() { return QStringLiteral("indicator"); }
-
void QQuickIndicatorButtonPrivate::cancelIndicator()
{
Q_Q(QQuickIndicatorButton);
@@ -33,6 +31,14 @@ QQuickIndicatorButton::QQuickIndicatorButton(QObject *parent)
{
}
+QQuickIndicatorButton::~QQuickIndicatorButton()
+{
+ Q_D(QQuickIndicatorButton);
+ QQuickControl *parentControl = static_cast<QQuickControl *>(parent());
+ if (parentControl)
+ QQuickControlPrivate::get(parentControl)->removeImplicitSizeListener(d->indicator);
+}
+
bool QQuickIndicatorButton::isPressed() const
{
Q_D(const QQuickIndicatorButton);
diff --git a/src/quicktemplates/qquickindicatorbutton_p.h b/src/quicktemplates/qquickindicatorbutton_p.h
index be5259211d..98b08a6030 100644
--- a/src/quicktemplates/qquickindicatorbutton_p.h
+++ b/src/quicktemplates/qquickindicatorbutton_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickIndicatorButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickIndicatorButton : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickIndicatorButton : public QObject
{
Q_OBJECT
Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL)
@@ -38,6 +38,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickIndicatorButton : public QObject
public:
explicit QQuickIndicatorButton(QObject *parent);
+ ~QQuickIndicatorButton() override;
bool isPressed() const;
void setPressed(bool pressed);
diff --git a/src/quicktemplates/qquickitemdelegate.cpp b/src/quicktemplates/qquickitemdelegate.cpp
index 7b0a1c12ad..d4abad9ce2 100644
--- a/src/quicktemplates/qquickitemdelegate.cpp
+++ b/src/quicktemplates/qquickitemdelegate.cpp
@@ -58,8 +58,11 @@ QQuickItemDelegate::QQuickItemDelegate(QQuickItemDelegatePrivate &dd, QQuickItem
id: listView
model: 10
delegate: ItemDelegate {
- text: modelData
+ text: index
highlighted: ListView.isCurrentItem
+
+ required property int index
+
onClicked: listView.currentIndex = index
}
}
diff --git a/src/quicktemplates/qquickitemdelegate_p.h b/src/quicktemplates/qquickitemdelegate_p.h
index 4716edc849..8236410fb3 100644
--- a/src/quicktemplates/qquickitemdelegate_p.h
+++ b/src/quicktemplates/qquickitemdelegate_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickItemDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickItemDelegate : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickItemDelegate : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL)
@@ -54,6 +54,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickItemDelegate)
-
#endif // QQUICKITEMDELEGATE_P_H
diff --git a/src/quicktemplates/qquickitemdelegate_p_p.h b/src/quicktemplates/qquickitemdelegate_p_p.h
index f1af3daf94..cf223e6b8b 100644
--- a/src/quicktemplates/qquickitemdelegate_p_p.h
+++ b/src/quicktemplates/qquickitemdelegate_p_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickItemDelegatePrivate : public QQuickAbstractButtonPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickItemDelegatePrivate : public QQuickAbstractButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickItemDelegate)
diff --git a/src/quicktemplates/qquicklabel.cpp b/src/quicktemplates/qquicklabel.cpp
index 116884a46f..ee8723d755 100644
--- a/src/quicktemplates/qquicklabel.cpp
+++ b/src/quicktemplates/qquicklabel.cpp
@@ -190,7 +190,7 @@ void QQuickLabelPrivate::accessibilityActiveChanged(bool active)
Q_Q(QQuickLabel);
QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
Q_ASSERT(accessibleAttached);
- accessibleAttached->setRole(accessibleRole());
+ accessibleAttached->setRole(effectiveAccessibleRole());
maybeSetAccessibleName(text);
}
@@ -211,8 +211,6 @@ void QQuickLabelPrivate::maybeSetAccessibleName(const QString &name)
}
#endif
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickLabelPrivate::cancelBackground()
{
Q_Q(QQuickLabel);
diff --git a/src/quicktemplates/qquicklabel_p.h b/src/quicktemplates/qquicklabel_p.h
index 5e564baad7..bef65ee8ef 100644
--- a/src/quicktemplates/qquicklabel_p.h
+++ b/src/quicktemplates/qquicklabel_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickLabelPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickLabel : public QQuickText
+class Q_QUICKTEMPLATES2_EXPORT QQuickLabel : public QQuickText
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override
@@ -93,16 +93,6 @@ private:
Q_DECLARE_PRIVATE(QQuickLabel)
};
-struct QQuickTemplatesTextForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickText)
- QML_ADDED_IN_VERSION(2, 3)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickLabel)
-
#endif // QQUICKLABEL_P_H
diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp
index ea37af1885..9580dea2c0 100644
--- a/src/quicktemplates/qquickmenu.cpp
+++ b/src/quicktemplates/qquickmenu.cpp
@@ -4,18 +4,26 @@
#include "qquickmenu_p.h"
#include "qquickmenu_p_p.h"
#include "qquickmenuitem_p_p.h"
+#include <private/qtquicktemplates2-config_p.h>
+#if QT_CONFIG(quicktemplates2_container)
#include "qquickmenubaritem_p.h"
-#include "qquickmenubar_p.h"
+#include "qquickmenubar_p_p.h"
+#endif
+#include "qquickmenuseparator_p.h"
+#include "qquicknativemenuitem_p.h"
#include "qquickpopupitem_p_p.h"
#include "qquickpopuppositioner_p_p.h"
#include "qquickaction_p.h"
+#include <QtCore/qloggingcategory.h>
#include <QtGui/qevent.h>
#include <QtGui/qcursor.h>
#if QT_CONFIG(shortcut)
#include <QtGui/qkeysequence.h>
#endif
#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/qpa/qplatformtheme.h>
+#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlcomponent.h>
@@ -26,12 +34,16 @@
#include <private/qqmlobjectmodel_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
-#include <QtQuick/private/qquickitemview_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuick/private/qquickrendercontrol_p.h>
#include <QtQuick/private/qquickwindow_p.h>
QT_BEGIN_NAMESPACE
+Q_STATIC_LOGGING_CATEGORY(lcMenu, "qt.quick.controls.menu")
+Q_STATIC_LOGGING_CATEGORY(lcNativeMenus, "qt.quick.controls.nativemenus")
+
// copied from qfusionstyle.cpp
static const int SUBMENU_DELAY = 225;
@@ -45,7 +57,13 @@ static const int SUBMENU_DELAY = 225;
\ingroup qtquickcontrols-popups
\brief Menu popup that can be used as a context menu or popup menu.
- \image qtquickcontrols-menu.png
+ \table
+ \row
+ \li \image qtquickcontrols-menu-native.png
+ \caption Native macOS menu.
+ \li \image qtquickcontrols-menu.png
+ \caption Non-native \l {Material Style}{Material style} menu.
+ \endtable
Menu has two main use cases:
\list
@@ -107,6 +125,16 @@ static const int SUBMENU_DELAY = 225;
}
\endcode
+ If the button should also close the menu when clicked, use the
+ \c Popup.CloseOnPressOutsideParent flag:
+ \code
+ onClicked: menu.visible = !menu.visible
+
+ Menu {
+ // ...
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ \endcode
+
Since QtQuick.Controls 2.3 (Qt 5.10), it is also possible to create sub-menus
and declare Action objects inside Menu:
@@ -172,6 +200,82 @@ static const int SUBMENU_DELAY = 225;
\sa {Customizing Menu}, MenuItem, {Menu Controls}, {Popup Controls},
{Dynamic QML Object Creation from JavaScript}
+
+ \section1 Menu types
+
+ Since Qt 6.8, a menu offers three different implementations, depending on the
+ platform. You can choose which one should be preferred by setting
+ \l popupType. This will let you control if a menu should be shown as a separate
+ window, as an item inside the parent window, or as a native menu. You can read
+ more about these options \l{Popup type}{here.}
+
+ Whether a menu will be able to use the preferred type depends on the platform.
+ \c Popup.Item is supported on all platforms, but \c Popup.Window and \c Popup.Native
+ are normally only supported on desktop platforms. Additionally, if the menu is inside
+ a \l {Native menu bars}{native menubar}, the menu will be native as well. And if
+ the menu is a sub-menu inside another menu, the parent (or root) menu will decide the type.
+
+ \section2 Limitations when using native menus
+
+ When setting \l popupType to \c Popup.Native, there are some limitations and
+ differences compared to using \c Popup.Item and \c Popup.Window.
+
+ \section3 API differences
+
+ When using native menus, only a subset of the Menu API is supported on all platforms:
+
+ \list
+ \li \l {Popup::}{x}
+ \li \l {Popup::}{y}
+ \li \l {Popup::}{visible}
+ \li \l {Popup::}{opened}
+ \li \l title
+ \li \l count
+ \li \l {Popup::}{contentData}
+ \li \l {Popup::}{contentChildren} (visual children will not be visible)
+ \li \l contentModel
+ \li \l {Popup::}{open()}
+ \li \l {Popup::}{popup()}
+ \li \l {Popup::}{close()}
+ \li \l {Popup::}{opened()}
+ \li \l {Popup::}{closed()}
+ \li \l {Popup::}{aboutToShow()}
+ \li \l {Popup::}{aboutToHide()}
+ \endlist
+
+ In addition, showing a popup (using for example \l {Popup::}{open()} or \l {Popup::}{popup()}
+ will, on some platforms, be a blocking call. This means that the call will not return
+ before the menu is closed again, which can affect the logic in your application. This is
+ especially important to take into consideration if your application is targeting multiple
+ platforms, and as such, sometimes run on platforms where native menus are not supported.
+ In that case the popupType will fall back to \c Popup.Item, for example, and calls to
+ \l {Popup::}{open()} will not be blocking.
+
+ Items like \l MenuItem will still react to clicks in the corresponding
+ native menu item by emitting signals, for example, but will be replaced by
+ their native counterpart.
+
+ \section3 Rendering differences
+
+ Native menus are implemented using the available native menu APIs on the platform.
+ Those menus, and all of their contents, will therefore be rendered by the platform, and
+ not by QML. This means that the \l delegate will \e not be used for rendering. It will,
+ however, always be instantiated (but hidden), so that functions such as
+ \l {Component.onCompleted()} execute regardless of platform and \l popupType.
+
+ \section3 Supported platforms
+
+ Native menus are currently supported on the following platforms:
+
+ \list
+ \li Android
+ \li iOS
+ \li Linux (only available as a stand-alone context menu when running with the GTK+ platform theme)
+ \li macOS
+ \li Windows
+ \endlist
+
+ \sa {Popup type}, popupType
*/
/*!
@@ -185,6 +289,8 @@ static const int SUBMENU_DELAY = 225;
The default value is \c true.
+ \include qquickmenu.qdocinc non-native-only-property
+
\sa {Popup::}{activeFocus}
*/
@@ -218,6 +324,282 @@ void QQuickMenuPrivate::init()
contentModel = new QQmlObjectModel(q);
}
+QQuickMenu *QQuickMenuPrivate::rootMenu() const
+{
+ Q_Q(const QQuickMenu);
+ const QQuickMenu *rootMenu = q;
+ const QObject *p = q->parent();
+ while (p) {
+ if (auto menu = qobject_cast<const QQuickMenu *>(p))
+ rootMenu = menu;
+ p = p->parent();
+ }
+
+ return const_cast<QQuickMenu *>(rootMenu);
+}
+
+ QQuickPopup::PopupType QQuickMenuPrivate::resolvedPopupType() const
+{
+ // The resolved popup type is decided by the root
+ // menu (which can be this menu, unless it's a child menu).
+ QQuickMenuPrivate *root_d = QQuickMenuPrivate::get(rootMenu());
+
+ // If the root menu is native, then so should we. We assume here that
+ // the root menu is always shown and created first, before we try to
+ // show and create a child menu.
+ if (root_d->maybeNativeHandle())
+ return QQuickPopup::PopupType::Native;
+
+ return root_d->QQuickPopupPrivate::resolvedPopupType();
+}
+
+bool QQuickMenuPrivate::useNativeMenu() const
+{
+ // If we're inside a MenuBar, it'll decide whether or not we
+ // should be native or not. Otherwise, the root menu (which
+ // might be this menu) will decide.
+ QQuickMenu *root = rootMenu();
+ if (auto menuBar = QQuickMenuPrivate::get(root)->menuBar.get())
+ return QQuickMenuBarPrivate::get(menuBar)->useNativeMenu(q_func());
+ return root->popupType() == QQuickPopup::Native;
+}
+
+QPlatformMenu *QQuickMenuPrivate::nativeHandle()
+{
+ Q_ASSERT(handle || useNativeMenu());
+ if (!handle && !triedToCreateNativeMenu)
+ createNativeMenu();
+ return handle.get();
+}
+
+QPlatformMenu *QQuickMenuPrivate::maybeNativeHandle() const
+{
+ return handle.get();
+}
+
+bool QQuickMenuPrivate::createNativeMenu()
+{
+ Q_ASSERT(!handle);
+ Q_Q(QQuickMenu);
+ qCDebug(lcNativeMenus) << "createNativeMenu called on" << q;
+
+ if (auto menuBar = QQuickMenuPrivate::get(rootMenu())->menuBar) {
+ auto menuBarPrivate = QQuickMenuBarPrivate::get(menuBar);
+ if (menuBarPrivate->useNativeMenuBar()) {
+ qCDebug(lcNativeMenus) << "- creating native menu from native menubar";
+ if (QPlatformMenuBar *menuBarHandle = menuBarPrivate->nativeHandle())
+ handle.reset(menuBarHandle->createMenu());
+ }
+ }
+
+ if (!handle) {
+ QPlatformMenu *parentMenuHandle(parentMenu ? get(parentMenu)->handle.get() : nullptr);
+ if (parentMenu && parentMenuHandle) {
+ qCDebug(lcNativeMenus) << "- creating native sub-menu";
+ handle.reset(parentMenuHandle->createSubMenu());
+ } else {
+ qCDebug(lcNativeMenus) << "- creating native menu";
+ handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformMenu());
+ }
+ }
+
+ triedToCreateNativeMenu = true;
+
+ if (!handle)
+ return false;
+
+ q->connect(handle.get(), &QPlatformMenu::aboutToShow, q, [q, this](){
+ emit q->aboutToShow();
+ visible = true;
+ emit q->visibleChanged();
+ emit q->openedChanged();
+ opened();
+ });
+ q->connect(handle.get(), &QPlatformMenu::aboutToHide, q, [q, this](){
+ qCDebug(lcNativeMenus) << "QPlatformMenu::aboutToHide called; about to call setVisible(false) on Menu";
+ emit q->aboutToHide();
+ visible = false;
+ emit q->visibleChanged();
+ emit q->openedChanged();
+ emit q->closed();
+ });
+
+ recursivelyCreateNativeMenuItems(q);
+ syncWithNativeMenu();
+
+ return true;
+}
+
+QString nativeMenuItemListToString(const QList<QQuickNativeMenuItem *> &nativeItems)
+{
+ if (nativeItems.isEmpty())
+ return QStringLiteral("(Empty)");
+
+ QString str;
+ QTextStream debug(&str);
+ for (const auto *nativeItem : nativeItems)
+ debug << nativeItem->debugText() << ", ";
+ // Remove trailing space and comma.
+ if (!nativeItems.isEmpty())
+ str.chop(2);
+ return str;
+}
+
+void QQuickMenuPrivate::syncWithNativeMenu()
+{
+ Q_Q(QQuickMenu);
+ if (!complete || !handle)
+ return;
+
+ qCDebug(lcNativeMenus).nospace() << "syncWithNativeMenu called on " << q
+ << " (complete: " << complete << " visible: " << visible << ") - "
+ << "syncing " << nativeItems.size() << " item(s)...";
+
+ // TODO: call this function when any of the variables below change
+
+ handle->setText(title);
+ handle->setEnabled(q->isEnabled());
+ handle->setMinimumWidth(q->implicitWidth());
+// nativeHandle->setMenuType(m_type);
+ handle->setFont(q->font());
+
+ // Note: the QQuickMenu::visible property is used to open or close the menu.
+ // This is in contrast to QPlatformMenu::visible, which tells if the menu
+ // should be visible in the menubar or not (if it belongs to one). To control
+ // if a QPlatformMenu should be open, we instead use QPlatformMenu::showPopup()
+ // and dismiss(). As such, we don't want to call handle->setVisible(visible)
+ // from this function since we always want the menu to be visible in the menubar
+ // (if it belongs to one). The currently only way to hide a menu from a menubar is
+ // to instead call MenuBar.removeMenu(menu).
+
+// if (m_menuBar && m_menuBar->handle())
+// m_menuBar->handle()->syncMenu(handle);
+//#if QT_CONFIG(systemtrayicon)
+// else if (m_systemTrayIcon && m_systemTrayIcon->handle())
+// m_systemTrayIcon->handle()->updateMenu(handle);
+//#endif
+
+ for (QQuickNativeMenuItem *item : std::as_const(nativeItems)) {
+ qCDebug(lcNativeMenus) << "- syncing" << item << "action" << item->action()
+ << "sub-menu" << item->subMenu() << item->debugText();
+ item->sync();
+ }
+
+ qCDebug(lcNativeMenus) << "... finished syncing" << q;
+}
+
+void QQuickMenuPrivate::removeNativeMenu()
+{
+ // Remove the native menu, including it's native menu items
+ Q_Q(QQuickMenu);
+ const int qtyItemsToRemove = nativeItems.size();
+ if (qtyItemsToRemove != 0)
+ Q_ASSERT(q->count() == qtyItemsToRemove);
+ for (int i = 0; i < qtyItemsToRemove; ++i)
+ removeNativeItem(0);
+ Q_ASSERT(nativeItems.isEmpty());
+
+ // removeNativeItem will take care of destroying sub-menus and resetting their native data,
+ // but as the root menu, we have to take care of our own.
+ resetNativeData();
+}
+
+void QQuickMenuPrivate::syncWithUseNativeMenu()
+{
+ Q_Q(QQuickMenu);
+ // Users can change AA_DontUseNativeMenuWindows while a menu is visible,
+ // but the changes won't take affect until the menu is re-opened.
+ if (q->isVisible() || parentMenu)
+ return;
+
+ if (maybeNativeHandle() && !useNativeMenu()) {
+ // Switch to a non-native menu by removing the native menu and its native items.
+ // Note that there's nothing to do if a native menu was requested but we failed to create it.
+ removeNativeMenu();
+ } else if (useNativeMenu()) {
+ Q_ASSERT(nativeItems.isEmpty());
+ // Try to create a native menu.
+ nativeHandle();
+ }
+}
+
+/*!
+ \internal
+
+ Recursively destroys native sub-menus of \a menu.
+
+ This function checks if each native item in \c menu has a sub-menu,
+ and if so:
+ \list
+ \li Calls itself with that sub-menu
+ \li Resets the item's data (important to avoid accessing a deleted QQuickAction
+ when printing in QQuickNativeMenuItem's destructor)
+ \li Deletes (eventually) the native item
+ \endlist
+
+ Similar (besides the recursion) to removeNativeItem(), except that
+ we can avoid repeated calls to syncWithNativeMenu().
+*/
+void QQuickMenuPrivate::recursivelyDestroyNativeSubMenus(QQuickMenu *menu)
+{
+ auto *menuPrivate = QQuickMenuPrivate::get(menu);
+ Q_ASSERT(menuPrivate->handle);
+ qCDebug(lcNativeMenus) << "recursivelyDestroyNativeSubMenus called with" << menu << "...";
+
+ while (!menuPrivate->nativeItems.isEmpty()) {
+ std::unique_ptr<QQuickNativeMenuItem> item(menuPrivate->nativeItems.takeFirst());
+ qCDebug(lcNativeMenus) << "- taking and destroying" << item->debugText();
+ if (QQuickMenu *subMenu = item->subMenu())
+ recursivelyDestroyNativeSubMenus(subMenu);
+
+ if (item->handle())
+ menuPrivate->handle->removeMenuItem(item->handle());
+ }
+
+ menuPrivate->resetNativeData();
+
+ qCDebug(lcNativeMenus) << "... finished destroying native sub-menus of" << menu;
+}
+
+static QWindow *effectiveWindow(QWindow *window, QPoint *offset)
+{
+ QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(window);
+ if (quickWindow) {
+ QWindow *renderWindow = QQuickRenderControl::renderWindowFor(quickWindow, offset);
+ if (renderWindow)
+ return renderWindow;
+ }
+ return window;
+}
+
+void QQuickMenuPrivate::setNativeMenuVisible(bool visible)
+{
+ Q_Q(QQuickMenu);
+ qCDebug(lcNativeMenus) << "setNativeMenuVisible called with visible" << visible;
+ if (visible)
+ emit q->aboutToShow();
+ else
+ emit q->aboutToHide();
+
+ this->visible = visible;
+ syncWithNativeMenu();
+
+ QPoint offset;
+ QWindow *window = effectiveWindow(qGuiApp->topLevelWindows().first(), &offset);
+
+ if (visible) {
+ lastDevicePixelRatio = window->devicePixelRatio();
+
+ const QPointF globalPos = parentItem->mapToGlobal(x, y);
+ const QPoint windowPos = window->mapFromGlobal(globalPos.toPoint());
+ QRect targetRect(windowPos, QSize(0, 0));
+ handle->showPopup(window, QHighDpi::toNativePixels(targetRect, window),
+ /*menuItem ? menuItem->handle() : */nullptr);
+ } else {
+ handle->dismiss();
+ }
+}
+
QQuickItem *QQuickMenuPrivate::itemAt(int index) const
{
return qobject_cast<QQuickItem *>(contentModel->get(index));
@@ -225,6 +607,9 @@ QQuickItem *QQuickMenuPrivate::itemAt(int index) const
void QQuickMenuPrivate::insertItem(int index, QQuickItem *item)
{
+ qCDebug(lcMenu) << "insert called with index" << index << "item" << item;
+
+ Q_Q(QQuickMenu);
contentData.append(item);
item->setParentItem(contentItem);
QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-53262
@@ -236,23 +621,90 @@ void QQuickMenuPrivate::insertItem(int index, QQuickItem *item)
QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(item);
if (menuItem) {
- Q_Q(QQuickMenu);
QQuickMenuItemPrivate::get(menuItem)->setMenu(q);
if (QQuickMenu *subMenu = menuItem->subMenu())
QQuickMenuPrivate::get(subMenu)->setParentMenu(q);
QObjectPrivate::connect(menuItem, &QQuickMenuItem::triggered, this, &QQuickMenuPrivate::onItemTriggered);
+ QObjectPrivate::connect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged, this, &QQuickMenuPrivate::updateTextPadding);
+ QObjectPrivate::connect(menuItem, &QQuickMenuItem::visibleChanged, this, &QQuickMenuPrivate::updateTextPadding);
QObjectPrivate::connect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged);
QObjectPrivate::connect(menuItem, &QQuickControl::hoveredChanged, this, &QQuickMenuPrivate::onItemHovered);
}
+
+ if (maybeNativeHandle() && complete)
+ maybeCreateAndInsertNativeItem(index, item);
+
+ if (lcMenu().isDebugEnabled())
+ printContentModelItems();
+
+ updateTextPadding();
+}
+
+void QQuickMenuPrivate::maybeCreateAndInsertNativeItem(int index, QQuickItem *item)
+{
+ Q_Q(QQuickMenu);
+ Q_ASSERT(complete);
+ Q_ASSERT_X(handle, Q_FUNC_INFO, qPrintable(QString::fromLatin1(
+ "Expected %1 to be using a native menu").arg(QDebug::toString(q))));
+ std::unique_ptr<QQuickNativeMenuItem> nativeMenuItem(QQuickNativeMenuItem::createFromNonNativeItem(q, item));
+ if (!nativeMenuItem) {
+ // TODO: fall back to non-native menu
+ qmlWarning(q) << "Native menu failed to create a native menu item for item at index" << index;
+ return;
+ }
+
+ nativeItems.insert(index, nativeMenuItem.get());
+
+ // Having a QQuickNativeMenuItem doesn't mean that we were able to create a native handle:
+ // it could be e.g. a Rectangle. See comment in QQuickNativeMenuItem::createFromNonNativeItem.
+ if (nativeMenuItem->handle()) {
+ QQuickNativeMenuItem *before = nativeItems.value(index + 1);
+ handle->insertMenuItem(nativeMenuItem->handle(), before ? before->handle() : nullptr);
+ qCDebug(lcNativeMenus) << "inserted native menu item at index" << index
+ << "before" << (before ? before->debugText() : QStringLiteral("null"));
+
+ if (nativeMenuItem->subMenu() && QQuickMenuPrivate::get(nativeMenuItem->subMenu())->nativeItems.count()
+ < nativeMenuItem->subMenu()->count()) {
+ // We're inserting a sub-menu item, and it hasn't had native items added yet,
+ // which probably means it's a menu that's been added back in after being removed
+ // with takeMenu(). Sub-menus added for the first time have their native items already
+ // constructed by virtue of contentData_append. Sub-menus that are removed always
+ // have their native items destroyed and removed too.
+ recursivelyCreateNativeMenuItems(nativeMenuItem->subMenu());
+ }
+ }
+
+ nativeMenuItem.release();
+
+ qCDebug(lcNativeMenus) << "nativeItems now contains the following items:"
+ << nativeMenuItemListToString(nativeItems);
}
void QQuickMenuPrivate::moveItem(int from, int to)
{
contentModel->move(from, to);
+
+ if (maybeNativeHandle())
+ nativeItems.move(from, to);
}
-void QQuickMenuPrivate::removeItem(int index, QQuickItem *item)
+/*!
+ \internal
+
+ Removes the specified \a item, potentially destroying it depending on
+ \a destructionPolicy.
+
+ \note the native menu item is destroyed regardless of the destruction
+ policy, because it's an implementation detail and hence is not created by
+ or available to the user.
+*/
+void QQuickMenuPrivate::removeItem(int index, QQuickItem *item, DestructionPolicy destructionPolicy)
{
+ qCDebug(lcMenu) << "removeItem called with index" << index << "item" << item;
+
+ if (maybeNativeHandle())
+ removeNativeItem(index);
+
contentData.removeOne(item);
QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent);
@@ -266,9 +718,79 @@ void QQuickMenuPrivate::removeItem(int index, QQuickItem *item)
if (QQuickMenu *subMenu = menuItem->subMenu())
QQuickMenuPrivate::get(subMenu)->setParentMenu(nullptr);
QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::triggered, this, &QQuickMenuPrivate::onItemTriggered);
+ QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::implicitTextPaddingChanged, this, &QQuickMenuPrivate::updateTextPadding);
+ QObjectPrivate::disconnect(menuItem, &QQuickMenuItem::visibleChanged, this, &QQuickMenuPrivate::updateTextPadding);
QObjectPrivate::disconnect(menuItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::onItemActiveFocusChanged);
QObjectPrivate::disconnect(menuItem, &QQuickControl::hoveredChanged, this, &QQuickMenuPrivate::onItemHovered);
}
+
+ if (destructionPolicy == DestructionPolicy::Destroy)
+ item->deleteLater();
+
+ if (lcMenu().isDebugEnabled())
+ printContentModelItems();
+}
+
+void QQuickMenuPrivate::removeNativeItem(int index)
+{
+ // Either we're still using native menus and are removing item(s), or we've switched
+ // to a non-native menu; either way, we should actually have items to remove before we're called.
+ Q_ASSERT(handle);
+ Q_ASSERT_X(index >= 0 && index < nativeItems.size(), Q_FUNC_INFO, qPrintable(QString::fromLatin1(
+ "index %1 is less than 0 or greater than or equal to %2").arg(index).arg(nativeItems.size())));
+
+ // We can delete the item synchronously because there aren't any external (e.g. QML)
+ // references to it.
+ std::unique_ptr<QQuickNativeMenuItem> nativeItem(nativeItems.takeAt(index));
+ qCDebug(lcNativeMenus) << "removing native item" << nativeItem->debugText() << "at index" << index
+ << "from" << q_func() << "...";
+ if (QQuickMenu *subMenu = nativeItem->subMenu())
+ recursivelyDestroyNativeSubMenus(subMenu);
+
+ if (nativeItem->handle()) {
+ handle->removeMenuItem(nativeItem->handle());
+ syncWithNativeMenu();
+ }
+
+ qCDebug(lcNativeMenus).nospace() << "... after removing item at index " << index
+ << ", nativeItems now contains the following items: " << nativeMenuItemListToString(nativeItems);
+}
+
+void QQuickMenuPrivate::resetNativeData()
+{
+ qCDebug(lcNativeMenus) << "resetNativeData called on" << q_func();
+ handle.reset();
+ triedToCreateNativeMenu = false;
+}
+
+void QQuickMenuPrivate::recursivelyCreateNativeMenuItems(QQuickMenu *menu)
+{
+ auto *menuPrivate = QQuickMenuPrivate::get(menu);
+ // If we're adding a sub-menu, we need to ensure its handle has been created
+ // before trying to create native items for it.
+ if (!menuPrivate->triedToCreateNativeMenu)
+ menuPrivate->createNativeMenu();
+
+ const int qtyItemsToCreate = menuPrivate->contentModel->count();
+ if (menuPrivate->nativeItems.count() == qtyItemsToCreate)
+ return;
+
+ qCDebug(lcNativeMenus) << "recursively creating" << qtyItemsToCreate << "menu item(s) for" << menu;
+ Q_ASSERT(menuPrivate->nativeItems.count() == 0);
+ for (int i = 0; i < qtyItemsToCreate; ++i) {
+ QQuickItem *item = menu->itemAt(i);
+ menuPrivate->maybeCreateAndInsertNativeItem(i, item);
+ auto *menuItem = qobject_cast<QQuickMenuItem *>(item);
+ if (menuItem && menuItem->subMenu())
+ recursivelyCreateNativeMenuItems(menuItem->subMenu());
+ }
+}
+
+void QQuickMenuPrivate::printContentModelItems() const
+{
+ qCDebug(lcMenu) << "contentModel now contains:";
+ for (int i = 0; i < contentModel->count(); ++i)
+ qCDebug(lcMenu) << "-" << itemAt(i);
}
QQuickItem *QQuickMenuPrivate::beginCreateItem()
@@ -403,18 +925,29 @@ QQuickPopupPositioner *QQuickMenuPrivate::getPositioner()
void QQuickMenuPositioner::reposition()
{
QQuickMenu *menu = static_cast<QQuickMenu *>(popup());
- QQuickMenuPrivate *p = QQuickMenuPrivate::get(menu);
- if (p->parentMenu) {
- if (p->cascade) {
- if (p->popupItem->isMirrored())
- menu->setPosition(QPointF(-menu->width() - p->parentMenu->leftPadding() + menu->overlap(), -menu->topPadding()));
- else if (p->parentItem)
- menu->setPosition(QPointF(p->parentItem->width() + p->parentMenu->rightPadding() - menu->overlap(), -menu->topPadding()));
+ QQuickMenuPrivate *menu_d = QQuickMenuPrivate::get(menu);
+
+ if (QQuickMenu *parentMenu = menu_d->parentMenu) {
+ if (menu_d->cascade) {
+ // Align the menu to the frame of the parent menu, minus overlap. The position
+ // should be in the coordinate system of the parentItem.
+ if (menu_d->popupItem->isMirrored()) {
+ menu->setPosition({-menu->width()
+ - parentMenu->leftPadding() + parentMenu->leftInset()
+ + menu->overlap(),
+ menu->topInset() - menu->topPadding()});
+ } else if (menu_d->parentItem) {
+ menu->setPosition({menu_d->parentItem->width()
+ + parentMenu->rightPadding() - parentMenu->rightInset()
+ - menu->overlap(),
+ menu->topInset() - menu->topPadding()});
+ }
} else {
- menu->setPosition(QPointF(p->parentMenu->x() + (p->parentMenu->width() - menu->width()) / 2,
- p->parentMenu->y() + (p->parentMenu->height() - menu->height()) / 2));
+ menu->setPosition(QPointF(parentMenu->x() + (parentMenu->width() - menu->width()) / 2,
+ parentMenu->y() + (parentMenu->height() - menu->height()) / 2));
}
}
+
QQuickPopupPositioner::reposition();
}
@@ -462,6 +995,36 @@ bool QQuickMenuPrivate::blockInput(QQuickItem *item, const QPointF &point) const
return (cascade && parentMenu && contains(point)) || QQuickPopupPrivate::blockInput(item, point);
}
+/*! \internal
+ QQuickPopupWindow::event() calls this to handle the release event of a
+ menu drag-press-release gesture, because the \a eventPoint does not have
+ a grabber within the popup window. This override finds and activates the
+ appropriate menu item, as if it had been pressed and released.
+ Returns true on success, to indicate that handling \a eventPoint is done.
+ */
+bool QQuickMenuPrivate::handleReleaseWithoutGrab(const QEventPoint &eventPoint)
+{
+ if (!contains(eventPoint.scenePosition()))
+ return false;
+
+ QQuickMenuItem *menuItem = nullptr;
+ // Usually, hover events have occurred, and currentIndex is set.
+ // If not, use eventPoint.position() for picking.
+ if (currentIndex < 0) {
+ auto *list = qobject_cast<QQuickListView *>(contentItem);
+ if (!list)
+ return false;
+ menuItem = qobject_cast<QQuickMenuItem *>(list->itemAt(eventPoint.position().x(), eventPoint.position().y()));
+ } else {
+ menuItem = qobject_cast<QQuickMenuItem *>(itemAt(currentIndex));
+ }
+ if (Q_LIKELY(menuItem)) {
+ menuItem->animateClick();
+ return true;
+ }
+ return false;
+}
+
void QQuickMenuPrivate::onItemHovered()
{
Q_Q(QQuickMenu);
@@ -516,6 +1079,30 @@ void QQuickMenuPrivate::onItemActiveFocusChanged()
setCurrentIndex(indexOfItem, control ? control->focusReason() : Qt::OtherFocusReason);
}
+void QQuickMenuPrivate::updateTextPadding()
+{
+ Q_Q(QQuickMenu);
+ if (!complete)
+ return;
+
+ qreal padding = 0;
+ for (int i = 0; i < q->count(); ++i) {
+ if (const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i)))
+ if (menuItem->isVisible())
+ padding = qMax(padding, menuItem->implicitTextPadding());
+ }
+
+ if (padding == textPadding)
+ return;
+
+ textPadding = padding;
+
+ for (int i = 0; i < q->count(); ++i) {
+ if (const auto menuItem = qobject_cast<QQuickMenuItem *>(itemAt(i)))
+ emit menuItem->textPaddingChanged();
+ }
+}
+
QQuickMenu *QQuickMenuPrivate::currentSubMenu() const
{
if (!currentItem)
@@ -571,11 +1158,13 @@ void QQuickMenuPrivate::propagateKeyEvent(QKeyEvent *event)
if (QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem *>(parentItem)) {
if (QQuickMenu *menu = menuItem->menu())
QQuickMenuPrivate::get(menu)->propagateKeyEvent(event);
+#if QT_CONFIG(quicktemplates2_container)
} else if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(parentItem)) {
if (QQuickMenuBar *menuBar = menuBarItem->menuBar()) {
event->accept();
QCoreApplication::sendEvent(menuBar, event);
}
+#endif
}
}
@@ -727,11 +1316,26 @@ QQuickMenu::QQuickMenu(QObject *parent)
QQuickMenu::~QQuickMenu()
{
Q_D(QQuickMenu);
- // We have to do this to ensure that the change listeners are removed.
- // It's too late to do this in ~QQuickMenuPrivate, as contentModel has already
- // been destroyed before that is called.
+ qCDebug(lcNativeMenus) << "destroying" << this
+ << "item count:"
+ << d->contentModel->count()
+ << "native item count:" << d->nativeItems.count();
+ // We have to remove items to ensure that our change listeners on the item
+ // are removed. It's too late to do this in ~QQuickMenuPrivate, as
+ // contentModel has already been destroyed before that is called.
+ // Destruction isn't necessary for the QQuickItems themselves, but it is
+ // required for the native menus (see comment in removeItem()).
while (d->contentModel->count() > 0)
- d->removeItem(0, d->itemAt(0));
+ d->removeItem(0, d->itemAt(0), QQuickMenuPrivate::DestructionPolicy::Destroy);
+
+ if (d->contentItem) {
+ QQuickItemPrivate::get(d->contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
+ QQuickItemPrivate::get(d->contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
+
+ const auto children = d->contentItem->childItems();
+ for (QQuickItem *child : std::as_const(children))
+ QQuickItemPrivate::get(child)->removeItemChangeListener(d, QQuickItemPrivate::SiblingOrder);
+ }
}
/*!
@@ -774,8 +1378,9 @@ void QQuickMenu::insertItem(int index, QQuickItem *item)
if (oldIndex != -1) {
if (oldIndex < index)
--index;
- if (oldIndex != index)
+ if (oldIndex != index) {
d->moveItem(oldIndex, index);
+ }
} else {
d->insertItem(index, item);
}
@@ -815,8 +1420,7 @@ void QQuickMenu::removeItem(QQuickItem *item)
if (index == -1)
return;
- d->removeItem(index, item);
- item->deleteLater();
+ d->removeItem(index, item, QQuickMenuPrivate::DestructionPolicy::Destroy);
}
/*!
@@ -930,6 +1534,7 @@ QQuickMenu *QQuickMenu::takeMenu(int index)
d->removeItem(index, item);
item->deleteLater();
+
return subMenu;
}
@@ -943,11 +1548,18 @@ QQuickMenu *QQuickMenu::takeMenu(int index)
QQuickAction *QQuickMenu::actionAt(int index) const
{
Q_D(const QQuickMenu);
- QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton *>(d->itemAt(index));
- if (!item)
- return nullptr;
+ if (!const_cast<QQuickMenuPrivate *>(d)->maybeNativeHandle()) {
+ QQuickAbstractButton *item = qobject_cast<QQuickAbstractButton *>(d->itemAt(index));
+ if (!item)
+ return nullptr;
+
+ return item->action();
+ } else {
+ if (index < 0 || index >= d->nativeItems.size())
+ return nullptr;
- return item->action();
+ return d->nativeItems.at(index)->action();
+ }
}
/*!
@@ -1026,6 +1638,43 @@ QQuickAction *QQuickMenu::takeAction(int index)
return action;
}
+bool QQuickMenu::isVisible() const
+{
+ Q_D(const QQuickMenu);
+ if (d->maybeNativeHandle())
+ return d->visible;
+ return QQuickPopup::isVisible();
+}
+
+void QQuickMenu::setVisible(bool visible)
+{
+ Q_D(QQuickMenu);
+ if (visible == d->visible)
+ return;
+ if (visible && !parentItem()) {
+ qmlWarning(this) << "cannot show menu: parent is null";
+ return;
+ }
+
+ if (visible && ((d->useNativeMenu() && !d->maybeNativeHandle())
+ || (!d->useNativeMenu() && d->maybeNativeHandle()))) {
+ // We've been made visible, and our actual native state doesn't match our requested state,
+ // which means AA_DontUseNativeMenuWindows was set while we were visible or had a parent.
+ // Try to sync our state again now that we're about to be re-opened.
+ qCDebug(lcNativeMenus) << "setVisible called - useNativeMenu:" << d->useNativeMenu()
+ << "maybeNativeHandle:" << d->maybeNativeHandle();
+ d->syncWithUseNativeMenu();
+ }
+ if (d->maybeNativeHandle()) {
+ d->setNativeMenuVisible(visible);
+ return;
+ }
+
+ // Either the native menu wasn't wanted, or it couldn't be created;
+ // show the non-native menu.
+ QQuickPopup::setVisible(visible);
+}
+
/*!
\qmlproperty model QtQuick.Controls::Menu::contentModel
\readonly
@@ -1095,12 +1744,14 @@ QString QQuickMenu::title() const
return d->title;
}
-void QQuickMenu::setTitle(QString &title)
+void QQuickMenu::setTitle(const QString &title)
{
Q_D(QQuickMenu);
if (title == d->title)
return;
d->title = title;
+ if (d->handle)
+ d->handle->setText(title);
emit titleChanged(title);
}
@@ -1116,7 +1767,9 @@ void QQuickMenu::setTitle(QString &title)
\include qquickicon.qdocinc grouped-properties
- \sa text, display, {Icons in Qt Quick Controls}
+ \include qquickmenu.qdocinc non-native-only-property
+
+ \sa AbstractButton::text, AbstractButton::display, {Icons in Qt Quick Controls}
*/
QQuickIcon QQuickMenu::icon() const
@@ -1147,6 +1800,8 @@ void QQuickMenu::setIcon(const QQuickIcon &icon)
\note Changing the value of the property has no effect while the menu is open.
+ \include qquickmenu.qdocinc non-native-only-property
+
\sa overlap
*/
bool QQuickMenu::cascade() const
@@ -1187,6 +1842,8 @@ void QQuickMenu::resetCascade()
\note Changing the value of the property has no effect while the menu is open.
+ \include qquickmenu.qdocinc non-native-only-property
+
\sa cascade
*/
qreal QQuickMenu::overlap() const
@@ -1219,6 +1876,9 @@ void QQuickMenu::setOverlap(qreal overlap)
}
\endcode
+ \note delegates will only be visible when using a \l {Native Menus}
+ {non-native Menu}.
+
\sa Action
*/
QQmlComponent *QQuickMenu::delegate() const
@@ -1245,6 +1905,8 @@ void QQuickMenu::setDelegate(QQmlComponent *delegate)
Menu items can be highlighted by mouse hover or keyboard navigation.
+ \include qquickmenu.qdocinc non-native-only-property
+
\sa MenuItem::highlighted
*/
int QQuickMenu::currentIndex() const
@@ -1284,10 +1946,10 @@ void QQuickMenu::popup(QQuickItem *menuItem)
#endif
// As a fallback, center the menu over its parent item.
- if (pos.isNull && d->parentItem)
+ if (!pos.isValid() && d->parentItem)
pos = QPointF((d->parentItem->width() - width()) / 2, (d->parentItem->height() - height()) / 2);
- popup(pos.isNull ? QPointF() : pos.value, menuItem);
+ popup(pos.isValid() ? pos.value() : QPointF(), menuItem);
}
void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
@@ -1302,6 +1964,9 @@ void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
if (menuItem)
d->setCurrentIndex(d->contentModel->indexOf(menuItem, nullptr), Qt::PopupFocusReason);
+ else
+ d->setCurrentIndex(-1, Qt::PopupFocusReason);
+
open();
}
@@ -1313,7 +1978,9 @@ void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
Opens the menu at the mouse cursor on desktop platforms that have a mouse cursor
available, and otherwise centers the menu over its \a parent item.
- The menu can be optionally aligned to a specific menu \a item.
+ The menu can be optionally aligned to a specific menu \a item. This item will
+ then become \l {currentIndex}{current.} If no \a item is specified, \l currentIndex
+ will be set to \c -1.
\sa Popup::open()
*/
@@ -1326,7 +1993,9 @@ void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
Opens the menu at the specified position \a pos in the popups coordinate system,
that is, a coordinate relative to its \a parent item.
- The menu can be optionally aligned to a specific menu \a item.
+ The menu can be optionally aligned to a specific menu \a item. This item will
+ then become \l {currentIndex}{current.} If no \a item is specified, \l currentIndex
+ will be set to \c -1.
\sa Popup::open()
*/
@@ -1339,11 +2008,13 @@ void QQuickMenu::popup(const QPointF &pos, QQuickItem *menuItem)
Opens the menu at the specified position \a x, \a y in the popups coordinate system,
that is, a coordinate relative to its \a parent item.
- The menu can be optionally aligned to a specific menu \a item.
+ The menu can be optionally aligned to a specific menu \a item. This item will
+ then become \l {currentIndex}{current.} If no \a item is specified, \l currentIndex
+ will be set to \c -1.
\sa dismiss(), Popup::open()
*/
-void QQuickMenu::popup(QQmlV4Function *args)
+void QQuickMenu::popup(QQmlV4FunctionPtr args)
{
Q_D(QQuickMenu);
const int len = args->length();
@@ -1388,7 +2059,7 @@ void QQuickMenu::popup(QQmlV4Function *args)
pos = QPointF(xArg->asDouble(), yArg->asDouble());
}
- if (pos.isNull && (len >= 2 || (!parentItem && len >= 1))) {
+ if (!pos.isValid() && (len >= 2 || (!parentItem && len >= 1))) {
// point pos
QV4::ScopedValue posArg(scope, (*args)[parentItem ? 1 : 0]);
const QVariant var = QV4::ExecutionEngine::toVariant(posArg, QMetaType {});
@@ -1399,10 +2070,10 @@ void QQuickMenu::popup(QQmlV4Function *args)
if (parentItem)
setParentItem(parentItem);
- if (pos.isNull)
- popup(menuItem);
- else
+ if (pos.isValid())
popup(pos, menuItem);
+ else
+ popup(menuItem);
}
/*!
@@ -1411,9 +2082,10 @@ void QQuickMenu::popup(QQmlV4Function *args)
Closes all menus in the hierarchy that this menu belongs to.
- \note Unlike \l {Popup::}{close()} that only closes a menu and its sub-menus,
- \c dismiss() closes the whole hierarchy of menus, including the parent menus.
- In practice, \c close() is suitable e.g. for implementing navigation in a
+ \note Unlike \l {Popup::}{close()} that only closes a menu and its
+ sub-menus (when using \l {Native Menus}{non-native menus}), \c dismiss()
+ closes the whole hierarchy of menus, including the parent menus. In
+ practice, \c close() is suitable e.g. for implementing navigation in a
hierarchy of menus, and \c dismiss() is the appropriate method for closing
the whole hierarchy of menus.
@@ -1433,6 +2105,8 @@ void QQuickMenu::componentComplete()
Q_D(QQuickMenu);
QQuickPopup::componentComplete();
d->resizeItems();
+ d->updateTextPadding();
+ d->syncWithUseNativeMenu();
}
void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
@@ -1457,12 +2131,16 @@ void QQuickMenu::itemChange(QQuickItem::ItemChange change, const QQuickItem::Ite
Q_D(QQuickMenu);
QQuickPopup::itemChange(change, data);
- if (change == QQuickItem::ItemVisibleHasChanged) {
+ switch (change) {
+ case QQuickItem::ItemVisibleHasChanged:
if (!data.boolValue && d->cascade) {
// Ensure that when the menu isn't visible, there's no current item
// the next time it's opened.
d->setCurrentIndex(-1, Qt::OtherFocusReason);
}
+ break;
+ default:
+ break;
}
}
diff --git a/src/quicktemplates/qquickmenu_p.h b/src/quicktemplates/qquickmenu_p.h
index 351272ed05..ed08537fdb 100644
--- a/src/quicktemplates/qquickmenu_p.h
+++ b/src/quicktemplates/qquickmenu_p.h
@@ -17,10 +17,13 @@
#include <QtQml/qqmllist.h>
#include <QtQml/qqml.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
#include "qquickpopup_p.h"
#include <QtQuickTemplates2/private/qquickicon_p.h>
+QT_REQUIRE_CONFIG(qml_object_model);
+
QT_BEGIN_NAMESPACE
class QQuickAction;
@@ -28,7 +31,7 @@ class QQmlComponent;
class QQuickMenuItem;
class QQuickMenuPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenu : public QQuickPopup
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenu : public QQuickPopup
{
Q_OBJECT
Q_PROPERTY(QVariant contentModel READ contentModel CONSTANT FINAL)
@@ -60,7 +63,7 @@ public:
QQmlListProperty<QObject> contentData();
QString title() const;
- void setTitle(QString &title);
+ void setTitle(const QString &title);
QQuickIcon icon() const;
void setIcon(const QQuickIcon &icon);
@@ -94,10 +97,13 @@ public:
Q_REVISION(2, 3) Q_INVOKABLE void removeAction(QQuickAction *action);
Q_REVISION(2, 3) Q_INVOKABLE QQuickAction *takeAction(int index);
+ bool isVisible() const override;
+ void setVisible(bool visible) override;
+
void popup(QQuickItem *menuItem = nullptr);
void popup(const QPointF &pos, QQuickItem *menuItem = nullptr);
- Q_REVISION(2, 3) Q_INVOKABLE void popup(QQmlV4Function *args);
+ Q_REVISION(2, 3) Q_INVOKABLE void popup(QQmlV4FunctionPtr args);
Q_REVISION(2, 3) Q_INVOKABLE void dismiss();
protected:
@@ -133,6 +139,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenu)
-
#endif // QQUICKMENU_P_H
diff --git a/src/quicktemplates/qquickmenu_p_p.h b/src/quicktemplates/qquickmenu_p_p.h
index 509614d2d1..be607c37c9 100644
--- a/src/quicktemplates/qquickmenu_p_p.h
+++ b/src/quicktemplates/qquickmenu_p_p.h
@@ -18,6 +18,8 @@
#include <QtCore/qlist.h>
#include <QtCore/qpointer.h>
+#include <QtGui/qpa/qplatformmenu.h>
+
#include <QtQuickTemplates2/private/qquickmenu_p.h>
#include <QtQuickTemplates2/private/qquickpopup_p_p.h>
@@ -27,8 +29,10 @@ class QQuickAction;
class QQmlComponent;
class QQmlObjectModel;
class QQuickMenuItem;
+class QQuickNativeMenuItem;
+class QQuickMenuBar;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuPrivate : public QQuickPopupPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuPrivate : public QQuickPopupPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickMenu)
@@ -42,10 +46,33 @@ public:
void init();
+ QPlatformMenu *nativeHandle();
+ QPlatformMenu *maybeNativeHandle() const;
+ QQuickMenu *rootMenu() const;
+ bool useNativeMenu() const;
+ bool createNativeMenu();
+ void removeNativeMenu();
+ void syncWithNativeMenu();
+ void syncWithUseNativeMenu();
+ static void recursivelyDestroyNativeSubMenus(QQuickMenu *menu);
+ void setNativeMenuVisible(bool visible);
+
QQuickItem *itemAt(int index) const;
void insertItem(int index, QQuickItem *item);
+ void maybeCreateAndInsertNativeItem(int index, QQuickItem *item);
void moveItem(int from, int to);
- void removeItem(int index, QQuickItem *item);
+ enum class DestructionPolicy {
+ Destroy,
+ DoNotDestroy
+ };
+ void removeItem(int index, QQuickItem *item,
+ DestructionPolicy destructionPolicy = DestructionPolicy::DoNotDestroy);
+ void removeNativeItem(int index);
+ void resetNativeData();
+
+ static void recursivelyCreateNativeMenuItems(QQuickMenu *menu);
+
+ void printContentModelItems() const;
QQuickItem *beginCreateItem();
void completeCreateItem();
@@ -66,10 +93,12 @@ public:
bool prepareEnterTransition() override;
bool prepareExitTransition() override;
bool blockInput(QQuickItem *item, const QPointF &point) const override;
+ bool handleReleaseWithoutGrab(const QEventPoint &eventPoint) override;
void onItemHovered();
void onItemTriggered();
void onItemActiveFocusChanged();
+ void updateTextPadding();
QQuickMenu *currentSubMenu() const;
void setParentMenu(QQuickMenu *parent);
@@ -92,11 +121,14 @@ public:
static void contentData_clear(QQmlListProperty<QObject> *prop);
QPalette defaultPalette() const override;
+ virtual QQuickPopup::PopupType resolvedPopupType() const override;
bool cascade = false;
+ bool triedToCreateNativeMenu = false;
int hoverTimer = 0;
int currentIndex = -1;
qreal overlap = 0;
+ qreal textPadding = 0;
QPointer<QQuickMenu> parentMenu;
QPointer<QQuickMenuItem> currentItem;
QQuickItem *contentItem = nullptr; // TODO: cleanup
@@ -105,6 +137,12 @@ public:
QQmlComponent *delegate = nullptr;
QString title;
QQuickIcon icon;
+
+ // For native menu support.
+ std::unique_ptr<QPlatformMenu> handle = nullptr;
+ QList<QQuickNativeMenuItem *> nativeItems;
+ QPointer<QQuickMenuBar> menuBar;
+ qreal lastDevicePixelRatio = 0;
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickmenubar.cpp b/src/quicktemplates/qquickmenubar.cpp
index d8ecbb6626..542f2562c1 100644
--- a/src/quicktemplates/qquickmenubar.cpp
+++ b/src/quicktemplates/qquickmenubar.cpp
@@ -42,16 +42,29 @@ QT_BEGIN_NAMESPACE
\l {removeMenu}{remove}, and \l {takeMenu}{take} menus dynamically. The
menus in a menu bar can be accessed using \l menuAt().
+ \section1 Native menu bars
+
+ Since Qt 6.8, a MenuBar is implemented as a native menu bar on \macos. As a
+ result, all Menus, MenuItems and MenuBarItems within a MenuBar will also be native.
+ While this has the advantage that everything will look native, it also comes with the
+ disadvantage that the delegates set on the mentioned controls will not be used
+ for rendering.
+ If a native MenuBar is not wanted, you can set
+ \l {Qt::AA_DontUseNativeMenuBar}{QGuiApplication::setAttribute(Qt::AA_DontUseNativeMenuBar)}
+ to disable it.
+
\sa {Customizing MenuBar}, Menu, MenuBarItem, {Menu Controls},
{Focus Management in Qt Quick Controls}
*/
-QQuickItem *QQuickMenuBarPrivate::beginCreateItem(QQuickMenu *menu)
+Q_LOGGING_CATEGORY(lcMenuBar, "qt.quick.controls.menubar")
+
+static const char* kCreatedFromDelegate = "_qt_createdFromDelegate";
+
+QQuickItem *QQuickMenuBarPrivate::createItemFromDelegate()
{
Q_Q(QQuickMenuBar);
- if (!delegate)
- return nullptr;
-
+ Q_ASSERT(delegate);
QQmlContext *context = delegate->creationContext();
if (!context)
context = qmlContext(q);
@@ -63,45 +76,94 @@ QQuickItem *QQuickMenuBarPrivate::beginCreateItem(QQuickMenu *menu)
return nullptr;
}
- if (QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(item))
- menuBarItem->setMenu(menu);
- item->setParentItem(q);
QQml_setParent_noEvent(item, q);
+ delegate->completeCreate();
return item;
}
-void QQuickMenuBarPrivate::completeCreateItem()
+QQuickMenuBarItem *QQuickMenuBarPrivate::createMenuBarItem(QQuickMenu *menu)
{
- if (!delegate)
- return;
+ Q_Q(QQuickMenuBar);
- delegate->completeCreate();
+ QQuickMenuBarItem *menuBarItem = nullptr;
+ if (delegate) {
+ QQuickItem *item = createItemFromDelegate();
+ menuBarItem = qobject_cast<QQuickMenuBarItem *>(item);
+ if (!menuBarItem) {
+ qmlWarning(q) << "cannot insert menu: the delegate is not a MenuBarItem.";
+ delete item;
+ }
+ }
+
+ if (!menuBarItem) {
+ // When we fail to create a delegate item, create a hidden placeholder
+ // instead. This is needed, since we store the menus inside the container
+ // using MenuBarItem. And without a MenuBarItem, we would therefore lose
+ // the menu, even if the delegate is changed later.
+ qCDebug(lcMenuBar) << "creating hidden placeholder MenuBarItem for:" << menu->title();
+ menuBarItem = new QQuickMenuBarItem(q);
+ menuBarItem->setParentItem(q);
+ menuBarItem->setVisible(false);
+ }
+
+ menuBarItem->setMenu(menu);
+
+ // Tag the menuBarItem, so that we know which container items to change if the
+ // delegate is changed. This is needed since you can add MenuBarItems directly
+ // to the menu bar, which should not change when the delegate changes.
+ menuBarItem->setProperty(kCreatedFromDelegate, true);
+
+ return menuBarItem;
}
-QQuickItem *QQuickMenuBarPrivate::createItem(QQuickMenu *menu)
+void QQuickMenuBarPrivate::openCurrentMenu()
{
- QQuickItem *item = beginCreateItem(menu);
- completeCreateItem();
- return item;
+ if (!currentItem || currentMenuOpen)
+ return;
+ QQuickMenu *menu = currentItem->menu();
+ if (!menu || menu->isOpened())
+ return;
+
+#ifdef Q_OS_MACOS
+ // On macOS, the menu should open underneath the MenuBar
+ Q_Q(QQuickMenuBar);
+ const QPointF posInParentItem = q->mapToItem(currentItem, {currentItem->x(), q->height()});
+#else
+ // On other platforms, it should open underneath the MenuBarItem
+ const QPointF posInParentItem{0, currentItem->y() + currentItem->height()};
+#endif
+
+ // Store explicit if the current menu is logically supposed to be open.
+ // menu->isVisible() is async when using top-level menus, and will not become
+ // "true" before the menu is actually shown by the OS. This will cause us to
+ // lose track of if a menu is (supposed to be) open, if relying on menu->isVisible().
+ currentMenuOpen = true;
+
+ // The position should be the coordinate system of the parent item. Note that
+ // the parentItem() of a menu will be the MenuBarItem (currentItem), and not the
+ // MenuBar (even if parent() usually points to the MenuBar).
+ menu->popup(posInParentItem);
}
-void QQuickMenuBarPrivate::toggleCurrentMenu(bool visible, bool activate)
+void QQuickMenuBarPrivate::closeCurrentMenu()
{
- if (!currentItem || visible == popupMode)
+ if (!currentItem || !currentMenuOpen)
return;
-
+ currentMenuOpen = false;
QQuickMenu *menu = currentItem->menu();
+ QScopedValueRollback triggerRollback(closingCurrentMenu, true);
+ menu->dismiss();
+}
- triggering = true;
- popupMode = visible;
- if (menu)
- menu->setVisible(visible);
- if (!visible)
- currentItem->forceActiveFocus();
- else if (menu && activate)
- menu->setCurrentIndex(0);
- triggering = false;
+void QQuickMenuBarPrivate::activateMenuItem(int index)
+{
+ if (!currentItem)
+ return;
+ QQuickMenu *menu = currentItem->menu();
+ if (!menu)
+ return;
+ menu->setCurrentIndex(index);
}
void QQuickMenuBarPrivate::activateItem(QQuickMenuBarItem *item)
@@ -109,23 +171,20 @@ void QQuickMenuBarPrivate::activateItem(QQuickMenuBarItem *item)
if (currentItem == item)
return;
+ const bool stayOpen = currentMenuOpen;
+
if (currentItem) {
currentItem->setHighlighted(false);
- if (popupMode) {
- if (QQuickMenu *menu = currentItem->menu())
- menu->dismiss();
- }
- }
-
- if (item) {
- item->setHighlighted(true);
- if (popupMode) {
- if (QQuickMenu *menu = item->menu())
- menu->open();
- }
+ closeCurrentMenu();
}
currentItem = item;
+
+ if (currentItem) {
+ currentItem->setHighlighted(true);
+ if (stayOpen)
+ openCurrentMenu();
+ }
}
void QQuickMenuBarPrivate::activateNextItem()
@@ -162,19 +221,34 @@ void QQuickMenuBarPrivate::onItemTriggered()
return;
if (item == currentItem) {
- toggleCurrentMenu(!popupMode, false);
+ if (currentMenuOpen) {
+ closeCurrentMenu();
+ currentItem->forceActiveFocus();
+ } else {
+ openCurrentMenu();
+ }
} else {
- popupMode = true;
activateItem(item);
+ openCurrentMenu();
}
}
-void QQuickMenuBarPrivate::onMenuAboutToHide()
+void QQuickMenuBarPrivate::onMenuAboutToHide(QQuickMenu *menu)
{
- if (triggering || !currentItem || (currentItem->isHovered() && currentItem->isEnabled()) || !currentItem->isHighlighted())
+ if (closingCurrentMenu) {
+ // We only react on a menu closing if it's
+ // initiated from outside of QQuickMenuBar.
+ return;
+ }
+
+ if (!currentItem || currentItem->menu() != menu)
+ return;
+
+ currentMenuOpen = false;
+
+ if (!currentItem->isHighlighted() || currentItem->isHovered())
return;
- popupMode = false;
activateItem(nullptr);
}
@@ -220,14 +294,32 @@ void QQuickMenuBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
void QQuickMenuBarPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
{
- QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
- if (QQuickMenu *menu = qobject_cast<QQuickMenu *>(obj))
- obj = QQuickMenuBarPrivate::get(menuBar)->createItem(menu);
+ auto menuBar = static_cast<QQuickMenuBar *>(prop->object);
+ auto menuBarPriv = QQuickMenuBarPrivate::get(menuBar);
+
+ if (auto *menu = qobject_cast<QQuickMenu *>(obj)) {
+ QQuickMenuBarItem *delegateItem = menuBarPriv->createMenuBarItem(menu);
+ menuBarPriv->insertMenu(menuBar->count(), menu, delegateItem);
+ QQuickContainerPrivate::contentData_append(prop, delegateItem);
+ return;
+ }
+
+ if (auto *menuBarItem = qobject_cast<QQuickMenuBarItem *>(obj)) {
+ menuBarPriv->insertMenu(menuBar->count(), menuBarItem->menu(), menuBarItem);
+ QQuickContainerPrivate::contentData_append(prop, menuBarItem);
+ return;
+ }
+
QQuickContainerPrivate::contentData_append(prop, obj);
}
void QQuickMenuBarPrivate::menus_append(QQmlListProperty<QQuickMenu> *prop, QQuickMenu *obj)
{
+ // This function is only called if the application assigns a list of menus
+ // directly to the 'menus' property. Otherwise, contentData_append is used.
+ // Since the functions belonging to the 'menus' list anyway returns data from
+ // the menuBar, calls such as "menuBar.menus.length" works as expected
+ // regardless of how the menus were added.
QQuickMenuBar *menuBar = static_cast<QQuickMenuBar *>(prop->object);
menuBar->addMenu(obj);
}
@@ -255,6 +347,283 @@ QPalette QQuickMenuBarPrivate::defaultPalette() const
return QQuickTheme::palette(QQuickTheme::MenuBar);
}
+QWindow* QQuickMenuBarPrivate::window() const
+{
+ Q_Q(const QQuickMenuBar);
+ QObject *obj = q->parent();
+ while (obj) {
+ if (QWindow *window = qobject_cast<QWindow *>(obj))
+ return window;
+ QQuickItem *item = qobject_cast<QQuickItem *>(obj);
+ if (item && item->window())
+ return item->window();
+ obj = obj->parent();
+ }
+ return nullptr;
+}
+
+int QQuickMenuBarPrivate::menuIndex(QQuickMenu *menu) const
+{
+ Q_Q(const QQuickMenuBar);
+ for (int i = 0; i < q->count(); ++i) {
+ if (q->menuAt(i) == menu)
+ return i;
+ }
+
+ return -1;
+}
+
+QPlatformMenuBar* QQuickMenuBarPrivate::nativeHandle() const
+{
+ return handle.get();
+}
+
+void QQuickMenuBarPrivate::insertNativeMenu(QQuickMenu *menu)
+{
+ Q_Q(QQuickMenuBar);
+ Q_ASSERT(handle);
+ Q_ASSERT(menu);
+
+ QPlatformMenu *insertBeforeHandle = nullptr;
+
+ // This function assumes that the QQuickMenuBarItem that corresponds to \a menu
+ // has already been added to the container at the correct index. So we search for
+ // it, to determine where to insert it in the native menubar. Since the QPA API
+ // expects a pointer to the QPlatformMenu that comes after it, we need to search
+ // for that one as well, since some MenuBarItems in the container can be hidden.
+ bool foundInContainer = false;
+ for (int i = 0; i < q->count(); ++i) {
+ if (q->menuAt(i) != menu)
+ continue;
+ foundInContainer = true;
+
+ for (int j = i + 1; j < q->count(); ++j) {
+ insertBeforeHandle = QQuickMenuPrivate::get(q->menuAt(j))->maybeNativeHandle();
+ if (insertBeforeHandle)
+ break;
+ }
+
+ break;
+ }
+
+ Q_ASSERT(foundInContainer);
+ QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
+ if (QPlatformMenu *menuHandle = menuPrivate->nativeHandle()) {
+ qCDebug(lcMenuBar) << "insert native menu:" << menu->title() << menuHandle << "before:" << insertBeforeHandle;
+ handle->insertMenu(menuPrivate->nativeHandle(), insertBeforeHandle);
+ } else {
+ qmlWarning(q) << "failed to create native menu for:" << menu->title();
+ }
+}
+
+void QQuickMenuBarPrivate::removeNativeMenu(QQuickMenu *menu)
+{
+ Q_ASSERT(handle);
+ Q_ASSERT(menu);
+
+ QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
+ if (!menuPrivate->maybeNativeHandle())
+ return;
+
+ qCDebug(lcMenuBar) << "remove native menu:" << menu << menu->title();
+ handle->removeMenu(menuPrivate->nativeHandle());
+ menuPrivate->removeNativeMenu();
+}
+
+void QQuickMenuBarPrivate::syncMenuBarItemVisibilty(QQuickMenuBarItem *menuBarItem)
+{
+ if (!handle) {
+ // We only need to update visibility on native menu bar items
+ return;
+ }
+
+ QQuickMenu *menu = menuBarItem->menu();
+ if (!menu)
+ return;
+ QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
+
+ if (menuBarItem->isVisible()) {
+ Q_ASSERT(!menuPrivate->maybeNativeHandle());
+ insertNativeMenu(menu);
+ } else {
+ if (menuPrivate->maybeNativeHandle())
+ removeNativeMenu(menu);
+ }
+}
+
+void QQuickMenuBarPrivate::insertMenu(int index, QQuickMenu *menu, QQuickMenuBarItem *menuBarItem)
+{
+ Q_Q(QQuickMenuBar);
+ if (!menu) {
+ qmlWarning(q) << "cannot insert menu: menu is null.";
+ return;
+ }
+
+ auto menuPrivate = QQuickMenuPrivate::get(menu);
+ menuPrivate->menuBar = q;
+
+ QObject::connect(menuBarItem, &QQuickMenuBarItem::visibleChanged, [this, menuBarItem]{
+ syncMenuBarItemVisibilty(menuBarItem);
+ });
+
+ // Always insert menu into the container, even when using a native
+ // menubar, so that container API such as 'count' and 'itemAt'
+ // continues to work as expected.
+ q->insertItem(index, menuBarItem);
+
+ // Create or remove a native (QPlatformMenu) menu. Note that we should only create
+ // a native menu if it's supposed to be visible in the menu bar.
+ if (menuBarItem->isVisible()) {
+ if (handle)
+ insertNativeMenu(menu);
+ } else {
+ if (menuPrivate->maybeNativeHandle()) {
+ // If the menu was added from an explicit call to addMenu(m), it will have been
+ // created before we enter here. And in that case, QQuickMenuBar::useNativeMenu(m)
+ // was never called, and a QPlatformMenu might have been created for it. In that
+ // case, we remove it again now, since the menu is not supposed to be visible in
+ // the menu bar.
+ menuPrivate->removeNativeMenu();
+ }
+ }
+}
+
+QQuickMenu *QQuickMenuBarPrivate::takeMenu(int index)
+{
+ Q_Q(QQuickMenuBar);
+ QQuickItem *item = q->itemAt(index);
+ Q_ASSERT(item);
+ QQuickMenuBarItem *menuBarItem = qobject_cast<QQuickMenuBarItem *>(item);
+ if (!menuBarItem) {
+ qmlWarning(q) << "cannot take/remove menu: item at index " << index << " is not a MenuBarItem.";
+ return nullptr;
+ }
+ QQuickMenu *menu = menuBarItem->menu();
+ if (!menu) {
+ qmlWarning(q) << "cannot take/remove menu: MenuBarItem.menu at index " << index << " is null.";
+ return nullptr;
+ }
+
+ // Dismiss the menu if it's open. Otherwise, when we now remove it from
+ // the menubar, it will stay open without the user being able to dismiss
+ // it (at least if it's non-native).
+ menu->dismiss();
+
+ if (item == currentItem)
+ activateItem(nullptr);
+
+ if (QQuickMenuPrivate::get(menu)->maybeNativeHandle())
+ removeNativeMenu(menu);
+
+ removeItem(index, item);
+
+ // Delete the MenuBarItem. This will also cause the menu to be deleted by
+ // the garbage collector, unless other QML references are being held to it.
+ // Note: We might consider leaving it to the garbage collector to also
+ // delete the MenuBarItem in the future.
+ item->deleteLater();
+
+ QQuickMenuPrivate::get(menu)->menuBar = nullptr;
+ menuBarItem->disconnect(q);
+
+ return menu;
+}
+
+bool QQuickMenuBarPrivate::useNativeMenuBar() const
+{
+ // We current only use native menu bars on macOS. Especially, the
+ // QPA menu bar for Windows is old and unused, and looks broken and non-native.
+#ifdef Q_OS_MACOS
+ return !QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuBar);
+#else
+ return false;
+#endif
+}
+
+bool QQuickMenuBarPrivate::useNativeMenu(const QQuickMenu *menu) const
+{
+ Q_Q(const QQuickMenuBar);
+ if (!useNativeMenuBar())
+ return false;
+
+ // Since we cannot hide a QPlatformMenu, we have to avoid
+ // creating it if it shouldn't be visible in the menu bar.
+ for (int i = 0; i < q->count(); ++i) {
+ if (q->menuAt(i) == menu)
+ return itemAt(i)->isVisible();
+ }
+
+ return true;
+}
+
+void QQuickMenuBarPrivate::syncNativeMenuBarVisible()
+{
+ Q_Q(QQuickMenuBar);
+ if (!componentComplete)
+ return;
+
+ const bool shouldBeVisible = q->isVisible() && useNativeMenuBar();
+ qCDebug(lcMenuBar) << "syncNativeMenuBarVisible called - q->isVisible()" << q->isVisible()
+ << "useNativeMenuBar()" << useNativeMenuBar() << "handle" << handle.get();
+ if (shouldBeVisible && !handle)
+ createNativeMenuBar();
+ else if (!shouldBeVisible && handle)
+ removeNativeMenuBar();
+}
+
+void QQuickMenuBarPrivate::createNativeMenuBar()
+{
+ Q_Q(QQuickMenuBar);
+ Q_ASSERT(!handle);
+ qCDebug(lcMenuBar) << "creating native menubar";
+
+ handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformMenuBar());
+ if (!handle) {
+ qCDebug(lcMenuBar) << "QPlatformTheme failed to create a QPlatformMenuBar!";
+ return;
+ }
+
+ handle->handleReparent(window());
+ qCDebug(lcMenuBar) << "native menubar parented to window:" << handle->parentWindow();
+
+ // Add all the native menus. We need to do this right-to-left
+ // because of the QPA API (insertBefore).
+ for (int i = q->count() - 1; i >= 0; --i) {
+ if (QQuickMenu *menu = q->menuAt(i)) {
+ if (useNativeMenu(menu))
+ insertNativeMenu(menu);
+ }
+ }
+
+ // Hide the non-native menubar and set it's height to 0. The
+ // latter will cause a relayout to happen in ApplicationWindow
+ // which effectively removes the menubar from the contentItem.
+ setCulled(true);
+ q->setHeight(0);
+}
+
+void QQuickMenuBarPrivate::removeNativeMenuBar()
+{
+ Q_Q(QQuickMenuBar);
+ Q_ASSERT(handle);
+ qCDebug(lcMenuBar) << "removing native menubar";
+
+ // Remove all native menus.
+ for (int i = 0; i < q->count(); ++i) {
+ if (QQuickMenu *menu = q->menuAt(i))
+ removeNativeMenu(menu);
+ }
+
+ // Delete the menubar
+ handle.reset();
+
+ // Show the non-native menubar and reset it's height. The
+ // latter will cause a relayout to happen in ApplicationWindow
+ // which will effectively add the menubar to the contentItem.
+ setCulled(false);
+ q->resetHeight();
+}
+
QQuickMenuBar::QQuickMenuBar(QQuickItem *parent)
: QQuickContainer(*(new QQuickMenuBarPrivate), parent)
{
@@ -264,6 +633,13 @@ QQuickMenuBar::QQuickMenuBar(QQuickItem *parent)
setFocusPolicy(Qt::ClickFocus);
}
+QQuickMenuBar::~QQuickMenuBar()
+{
+ Q_D(QQuickMenuBar);
+ if (d->handle)
+ d->removeNativeMenuBar();
+}
+
/*!
\qmlproperty Component QtQuick.Controls::MenuBar::delegate
@@ -285,6 +661,21 @@ void QQuickMenuBar::setDelegate(QQmlComponent *delegate)
return;
d->delegate = delegate;
+
+ for (int i = count() - 1; i >= 0; --i) {
+ auto item = itemAt(i);
+ if (!item->property(kCreatedFromDelegate).toBool())
+ continue;
+
+ QQuickMenuBarItem *menuBarItem = static_cast<QQuickMenuBarItem *>(item);
+ if (QQuickMenu *menu = menuBarItem->menu()) {
+ removeMenu(menu);
+ d->insertMenu(i, menu, d->createMenuBarItem(menu));
+ } else {
+ removeItem(menuBarItem);
+ }
+ }
+
emit delegateChanged();
}
@@ -310,7 +701,12 @@ QQuickMenu *QQuickMenuBar::menuAt(int index) const
void QQuickMenuBar::addMenu(QQuickMenu *menu)
{
Q_D(QQuickMenuBar);
- addItem(d->createItem(menu));
+ if (d->menuIndex(menu) >= 0) {
+ qmlWarning(this) << "cannot add menu: '" << menu->title() << "' is already in the MenuBar.";
+ return;
+ }
+
+ d->insertMenu(count(), menu, d->createMenuBarItem(menu));
}
/*!
@@ -321,54 +717,52 @@ void QQuickMenuBar::addMenu(QQuickMenu *menu)
void QQuickMenuBar::insertMenu(int index, QQuickMenu *menu)
{
Q_D(QQuickMenuBar);
- insertItem(index, d->createItem(menu));
+ if (d->menuIndex(menu) >= 0) {
+ qmlWarning(this) << "cannot insert menu: '" << menu->title() << "' is already in the MenuBar.";
+ return;
+ }
+
+ d->insertMenu(index, menu, d->createMenuBarItem(menu));
}
/*!
\qmlmethod void QtQuick.Controls::MenuBar::removeMenu(Menu menu)
- Removes and destroys the specified \a menu.
+ Removes specified \a menu. If the menu is \l {QQuickMenu::popup(QQmlV4Function *)}{open},
+ it will first be \l {QQuickMenu::dismiss()}{dismissed.}
+ The \a menu will eventually be deleted by the garbage collector when the
+ application no longer holds any QML references to it.
*/
void QQuickMenuBar::removeMenu(QQuickMenu *menu)
{
Q_D(QQuickMenuBar);
- if (!menu)
+ const int index = d->menuIndex(menu);
+ if (index < 0) {
+ qmlWarning(this) << "cannot remove menu: '" << menu->title() << "' is not in the MenuBar.";
return;
-
- const int count = d->contentModel->count();
- for (int i = 0; i < count; ++i) {
- QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(itemAt(i));
- if (!item || item->menu() != menu)
- continue;
-
- removeItem(item);
- break;
}
- menu->deleteLater();
+ d->takeMenu(index);
}
/*!
\qmlmethod Menu QtQuick.Controls::MenuBar::takeMenu(int index)
- Removes and returns the menu at \a index.
-
- \note The ownership of the item is transferred to the caller.
+ Removes and returns the menu at \a index. If the menu is
+ \l {QQuickMenu::popup(QQmlV4Function *)}{open}, it will first be
+ \l {QQuickMenu::dismiss()}{dismissed.}
+ The menu will eventually be deleted by the garbage collector when the
+ application no longer holds any QML references to it.
*/
QQuickMenu *QQuickMenuBar::takeMenu(int index)
{
Q_D(QQuickMenuBar);
- QQuickMenuBarItem *item = qobject_cast<QQuickMenuBarItem *>(itemAt(index));
- if (!item)
- return nullptr;
-
- QQuickMenu *menu = item->menu();
- if (!menu)
+ if (index < 0 || index > count() - 1) {
+ qmlWarning(this) << "index out of range: " << index;
return nullptr;
+ }
- d->removeItem(index, item);
- item->deleteLater();
- return menu;
+ return d->takeMenu(index);
}
/*!
@@ -488,11 +882,12 @@ void QQuickMenuBar::keyPressEvent(QKeyEvent *event)
switch (event->key()) {
case Qt::Key_Up:
- d->toggleCurrentMenu(false, false);
+ d->closeCurrentMenu();
break;
case Qt::Key_Down:
- d->toggleCurrentMenu(true, true);
+ d->openCurrentMenu();
+ d->activateMenuItem(0);
break;
case Qt::Key_Left:
@@ -502,6 +897,7 @@ void QQuickMenuBar::keyPressEvent(QKeyEvent *event)
else
d->activatePreviousItem();
break;
+ // This is triggered when no popup is open but a menu bar item is highlighted and has focus.
case Qt::Key_Escape:
if (d->currentItem) {
d->activateItem(nullptr);
@@ -516,7 +912,8 @@ void QQuickMenuBar::keyPressEvent(QKeyEvent *event)
if (auto *item = qobject_cast<QQuickMenuBarItem *>(d->itemAt(i))) {
if (item->shortcut() == mnemonic) {
d->activateItem(item);
- d->toggleCurrentMenu(true, true);
+ d->openCurrentMenu();
+ d->activateMenuItem(0);
}
}
}
@@ -549,7 +946,7 @@ void QQuickMenuBar::hoverLeaveEvent(QHoverEvent *event)
{
Q_D(QQuickMenuBar);
QQuickContainer::hoverLeaveEvent(event);
- if (!d->popupMode && d->currentItem)
+ if (!d->currentMenuOpen && d->currentItem)
d->activateItem(nullptr);
}
@@ -572,6 +969,10 @@ void QQuickMenuBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::
d->windowContentItem->installEventFilter(this);
}
break;
+ case ItemVisibleHasChanged:
+ qCDebug(lcMenuBar) << "visibility of" << this << "changed to" << isVisible();
+ d->syncNativeMenuBarVisible();
+ break;
default:
break;
}
@@ -586,7 +987,7 @@ void QQuickMenuBar::itemAdded(int index, QQuickItem *item)
QObjectPrivate::connect(menuBarItem, &QQuickControl::hoveredChanged, d, &QQuickMenuBarPrivate::onItemHovered);
QObjectPrivate::connect(menuBarItem, &QQuickMenuBarItem::triggered, d, &QQuickMenuBarPrivate::onItemTriggered);
if (QQuickMenu *menu = menuBarItem->menu())
- QObjectPrivate::connect(menu, &QQuickPopup::aboutToHide, d, &QQuickMenuBarPrivate::onMenuAboutToHide);
+ connect(menu, &QQuickPopup::aboutToHide, [this, menu]{ d_func()->onMenuAboutToHide(menu); });
}
d->updateImplicitContentSize();
emit menusChanged();
@@ -607,12 +1008,19 @@ void QQuickMenuBar::itemRemoved(int index, QQuickItem *item)
QObjectPrivate::disconnect(menuBarItem, &QQuickControl::hoveredChanged, d, &QQuickMenuBarPrivate::onItemHovered);
QObjectPrivate::disconnect(menuBarItem, &QQuickMenuBarItem::triggered, d, &QQuickMenuBarPrivate::onItemTriggered);
if (QQuickMenu *menu = menuBarItem->menu())
- QObjectPrivate::disconnect(menu, &QQuickPopup::aboutToHide, d, &QQuickMenuBarPrivate::onMenuAboutToHide);
+ menu->disconnect(this);
}
d->updateImplicitContentSize();
emit menusChanged();
}
+void QQuickMenuBar::componentComplete()
+{
+ Q_D(QQuickMenuBar);
+ QQuickContainer::componentComplete();
+ d->syncNativeMenuBarVisible();
+}
+
QFont QQuickMenuBar::defaultFont() const
{
return QQuickTheme::font(QQuickTheme::MenuBar);
diff --git a/src/quicktemplates/qquickmenubar_p.h b/src/quicktemplates/qquickmenubar_p.h
index 2b206a41b1..f053ff6b49 100644
--- a/src/quicktemplates/qquickmenubar_p.h
+++ b/src/quicktemplates/qquickmenubar_p.h
@@ -17,12 +17,14 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickMenu;
class QQuickMenuBarPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBar : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuBar : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL)
@@ -33,6 +35,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBar : public QQuickContainer
public:
explicit QQuickMenuBar(QQuickItem *parent = nullptr);
+ ~QQuickMenuBar() override;
QQmlComponent *delegate() const;
void setDelegate(QQmlComponent *delegate);
@@ -59,6 +62,8 @@ protected:
void itemMoved(int index, QQuickItem *item) override;
void itemRemoved(int index, QQuickItem *item) override;
+ void componentComplete() override;
+
QFont defaultFont() const override;
#if QT_CONFIG(accessibility)
@@ -72,6 +77,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenuBar)
-
#endif // QQUICKMENUBAR_P_H
diff --git a/src/quicktemplates/qquickmenubar_p_p.h b/src/quicktemplates/qquickmenubar_p_p.h
index 2ac3af00ed..98b234e1c8 100644
--- a/src/quicktemplates/qquickmenubar_p_p.h
+++ b/src/quicktemplates/qquickmenubar_p_p.h
@@ -18,12 +18,15 @@
#include <QtQuickTemplates2/private/qquickmenubar_p.h>
#include <QtQuickTemplates2/private/qquickcontainer_p_p.h>
+#include <QtCore/qpointer.h>
+#include <QtGui/qpa/qplatformmenu.h>
+
QT_BEGIN_NAMESPACE
class QQmlComponent;
class QQuickMenuBarItem;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBarPrivate : public QQuickContainerPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuBarPrivate : public QQuickContainerPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickMenuBar)
@@ -36,19 +39,36 @@ public:
QQmlListProperty<QQuickMenu> menus();
QQmlListProperty<QObject> contentData();
- QQuickItem *beginCreateItem(QQuickMenu *menu);
- void completeCreateItem();
+ QQuickItem *createItemFromDelegate();
+ QQuickMenuBarItem *createMenuBarItem(QQuickMenu *menu);
- QQuickItem *createItem(QQuickMenu *menu);
+ void openCurrentMenu();
+ void closeCurrentMenu();
+ void activateMenuItem(int index);
- void toggleCurrentMenu(bool visible, bool activate);
void activateItem(QQuickMenuBarItem *item);
void activateNextItem();
void activatePreviousItem();
void onItemHovered();
void onItemTriggered();
- void onMenuAboutToHide();
+ void onMenuAboutToHide(QQuickMenu *menu);
+
+ void insertMenu(int index, QQuickMenu *menu, QQuickMenuBarItem *delegateItem);
+ QQuickMenu *takeMenu(int index);
+ void insertNativeMenu(QQuickMenu *menu);
+ void removeNativeMenu(QQuickMenu *menu);
+ void syncMenuBarItemVisibilty(QQuickMenuBarItem *menuBarItem);
+
+ QWindow *window() const;
+ int menuIndex(QQuickMenu *menu) const;
+
+ QPlatformMenuBar *nativeHandle() const;
+ bool useNativeMenuBar() const;
+ bool useNativeMenu(const QQuickMenu *menu) const;
+ void syncNativeMenuBarVisible();
+ void createNativeMenuBar();
+ void removeNativeMenuBar();
qreal getContentWidth() const override;
qreal getContentHeight() const override;
@@ -65,12 +85,15 @@ public:
QPalette defaultPalette() const override;
- bool popupMode = false;
- bool triggering = false;
+ bool closingCurrentMenu = false;
bool altPressed = false;
+ bool currentMenuOpen = false;
QQmlComponent *delegate = nullptr;
QPointer<QQuickMenuBarItem> currentItem;
QPointer<QQuickItem> windowContentItem;
+
+private:
+ std::unique_ptr<QPlatformMenuBar> handle;
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickmenubaritem.cpp b/src/quicktemplates/qquickmenubaritem.cpp
index 03e8c60f03..68aff1e62d 100644
--- a/src/quicktemplates/qquickmenubaritem.cpp
+++ b/src/quicktemplates/qquickmenubaritem.cpp
@@ -46,6 +46,43 @@ void QQuickMenuBarItemPrivate::setMenuBar(QQuickMenuBar *newMenuBar)
emit q->menuBarChanged();
}
+bool QQuickMenuBarItemPrivate::handlePress(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickMenuBarItem);
+ const bool handled = QQuickAbstractButtonPrivate::handlePress(point, timestamp);
+ if (!handled)
+ return false;
+
+ const bool wasTouchPress = touchId != -1;
+ if (!wasTouchPress) {
+ // Open the menu when it's a mouse press.
+ emit q->triggered();
+ }
+
+ return true;
+}
+
+bool QQuickMenuBarItemPrivate::handleRelease(const QPointF &point, ulong timestamp)
+{
+ Q_Q(QQuickMenuBarItem);
+ const bool wasTouchPress = touchId != -1;
+ const bool handled = QQuickAbstractButtonPrivate::handleRelease(point, timestamp);
+ if (!handled)
+ return false;
+
+ if (wasDoubleClick || !wasTouchPress) {
+ // Don't open the menu on mouse release, as it should be done on press.
+ return handled;
+ }
+
+ if (wasTouchPress) {
+ // Open the menu.
+ emit q->triggered();
+ }
+
+ return true;
+}
+
QPalette QQuickMenuBarItemPrivate::defaultPalette() const
{
return QQuickTheme::palette(QQuickTheme::MenuBar);
@@ -55,7 +92,7 @@ QQuickMenuBarItem::QQuickMenuBarItem(QQuickItem *parent)
: QQuickAbstractButton(*(new QQuickMenuBarItemPrivate), parent)
{
setFocusPolicy(Qt::NoFocus);
- connect(this, &QQuickAbstractButton::clicked, this, &QQuickMenuBarItem::triggered);
+ d_func()->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Fixed);
}
/*!
@@ -129,6 +166,50 @@ void QQuickMenuBarItem::setHighlighted(bool highlighted)
emit highlightedChanged();
}
+bool QQuickMenuBarItem::event(QEvent *event)
+{
+#if QT_CONFIG(shortcut)
+ Q_D(QQuickMenuBarItem);
+ if (event->type() == QEvent::Shortcut) {
+ auto *shortcutEvent = static_cast<QShortcutEvent *>(event);
+ if (shortcutEvent->shortcutId() == d->shortcutId) {
+ d->trigger();
+ emit triggered();
+ return true;
+ }
+ }
+#endif
+ return QQuickControl::event(event);
+}
+
+void QQuickMenuBarItem::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QQuickMenuBarItem);
+ QQuickControl::keyPressEvent(event);
+ if (d->acceptKeyClick(static_cast<Qt::Key>(event->key()))) {
+ d->setPressPoint(d->centerPressPoint());
+ setPressed(true);
+ emit pressed();
+ event->accept();
+ }
+}
+
+void QQuickMenuBarItem::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QQuickMenuBarItem);
+ QQuickControl::keyReleaseEvent(event);
+ if (d->pressed && d->acceptKeyClick(static_cast<Qt::Key>(event->key()))) {
+ setPressed(false);
+ emit released();
+ d->trigger();
+ // We override these event functions so that we can emit triggered here.
+ // We can't just connect clicked to triggered, because that would cause mouse clicks
+ // to open the menu, when only presses should.
+ emit triggered();
+ event->accept();
+ }
+}
+
void QQuickMenuBarItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickMenuBarItem);
diff --git a/src/quicktemplates/qquickmenubaritem_p.h b/src/quicktemplates/qquickmenubaritem_p.h
index 87faba2e35..c101d1f580 100644
--- a/src/quicktemplates/qquickmenubaritem_p.h
+++ b/src/quicktemplates/qquickmenubaritem_p.h
@@ -17,13 +17,15 @@
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickMenu;
class QQuickMenuBar;
class QQuickMenuBarItemPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuBarItem : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuBarItem : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(QQuickMenuBar *menuBar READ menuBar NOTIFY menuBarChanged FINAL)
@@ -50,6 +52,10 @@ Q_SIGNALS:
void highlightedChanged();
protected:
+ bool event(QEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
QFont defaultFont() const override;
@@ -65,6 +71,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenuBarItem)
-
#endif // QQUICKMENUBARITEM_P_H
diff --git a/src/quicktemplates/qquickmenubaritem_p_p.h b/src/quicktemplates/qquickmenubaritem_p_p.h
index c2dcdd4222..b6f70cc274 100644
--- a/src/quicktemplates/qquickmenubaritem_p_p.h
+++ b/src/quicktemplates/qquickmenubaritem_p_p.h
@@ -35,6 +35,9 @@ public:
void setMenuBar(QQuickMenuBar *menuBar);
+ bool handlePress(const QPointF &point, ulong timestamp) override;
+ bool handleRelease(const QPointF &point, ulong timestamp) override;
+
QPalette defaultPalette() const override;
bool highlighted = false;
diff --git a/src/quicktemplates/qquickmenuitem.cpp b/src/quicktemplates/qquickmenuitem.cpp
index c5b0ea2753..32a0719150 100644
--- a/src/quicktemplates/qquickmenuitem.cpp
+++ b/src/quicktemplates/qquickmenuitem.cpp
@@ -3,7 +3,7 @@
#include "qquickmenuitem_p.h"
#include "qquickmenuitem_p_p.h"
-#include "qquickmenu_p.h"
+#include "qquickmenu_p_p.h"
#include "qquickdeferredexecute_p_p.h"
#include <QtGui/qpa/qplatformtheme.h>
@@ -56,6 +56,44 @@ QT_BEGIN_NAMESPACE
\sa {Customizing Menu}, Menu, {Menu Controls}
*/
+/*!
+ \qmlproperty bool QtQuick.Controls::MenuItem::textPadding
+ \readonly
+ \since 6.8
+
+ This property holds the maximum \l implicitTextPadding found
+ among all the menu items inside the same \l menu.
+
+ This property can be used by the style to ensure that all MenuItems
+ inside the same Menu end up aligned with respect to the \l text.
+
+ A \l Menu can consist of meny different MenuItems, some can be checkable,
+ some can have an icon, and some will just contain text. And very often,
+ a style wants to make sure that the text inside all of them ends up
+ left-aligned (or right-aligned for \l mirrored items).
+ By letting each MenuItem assign its own minimum text padding to
+ \l implicitTextPadding (taking icons and checkmarks into account), but
+ using \l textPadding to actually position the \l text, all MenuItems should
+ end up being aligned
+
+ In order for this to work, all MenuItems should set \l implicitTextPadding
+ to be the minimum space needed from the left edge of the \l contentItem to
+ the text.
+
+ \sa implicitTextPadding
+*/
+
+/*!
+ \qmlproperty bool QtQuick.Controls::MenuItem::implicitTextPadding
+ \since 6.8
+
+ This property holds the minimum space needed from the left edge of the
+ \l contentItem to the text. It's used to calculate a common \l textPadding
+ among all the MenuItems inside a \l Menu.
+
+ \sa textPadding
+*/
+
void QQuickMenuItemPrivate::setMenu(QQuickMenu *newMenu)
{
Q_Q(QQuickMenuItem);
@@ -240,6 +278,26 @@ QFont QQuickMenuItem::defaultFont() const
return QQuickTheme::font(QQuickTheme::Menu);
}
+qreal QQuickMenuItem::implicitTextPadding() const
+{
+ return d_func()->implicitTextPadding;
+}
+
+void QQuickMenuItem::setImplicitTextPadding(qreal newImplicitTextPadding)
+{
+ Q_D(QQuickMenuItem);
+ if (qFuzzyCompare(d->implicitTextPadding, newImplicitTextPadding))
+ return;
+ d->implicitTextPadding = newImplicitTextPadding;
+ emit implicitTextPaddingChanged();
+}
+
+qreal QQuickMenuItem::textPadding() const
+{
+ Q_D(const QQuickMenuItem);
+ return d->menu ? QQuickMenuPrivate::get(d->menu)->textPadding : 0;
+}
+
#if QT_CONFIG(accessibility)
QAccessible::Role QQuickMenuItem::accessibleRole() const
{
@@ -247,6 +305,25 @@ QAccessible::Role QQuickMenuItem::accessibleRole() const
}
#endif
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickMenuItem *menuItem)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ if (!menuItem) {
+ debug << "QQuickMenuItem(nullptr)";
+ return debug;
+ }
+
+ debug << menuItem->metaObject()->className() << '(' << static_cast<const void *>(menuItem);
+ if (!menuItem->objectName().isEmpty())
+ debug << ", name=" << menuItem->objectName();
+ debug << ", text=" << menuItem->text();
+ debug << ')';
+ return debug;
+}
+#endif // QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
#include "moc_qquickmenuitem_p.cpp"
diff --git a/src/quicktemplates/qquickmenuitem_p.h b/src/quicktemplates/qquickmenuitem_p.h
index f38c437dbd..786b6f8ae6 100644
--- a/src/quicktemplates/qquickmenuitem_p.h
+++ b/src/quicktemplates/qquickmenuitem_p.h
@@ -16,13 +16,16 @@
//
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
+#include <QtQmlModels/private/qtqmlmodelsglobal_p.h>
+
+QT_REQUIRE_CONFIG(qml_object_model);
QT_BEGIN_NAMESPACE
class QQuickMenu;
class QQuickMenuItemPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuItem : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuItem : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(bool highlighted READ isHighlighted WRITE setHighlighted NOTIFY highlightedChanged FINAL)
@@ -30,6 +33,8 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuItem : public QQuickAbstractBut
Q_PROPERTY(QQuickItem *arrow READ arrow WRITE setArrow NOTIFY arrowChanged FINAL REVISION(2, 3))
Q_PROPERTY(QQuickMenu *menu READ menu NOTIFY menuChanged FINAL REVISION(2, 3))
Q_PROPERTY(QQuickMenu *subMenu READ subMenu NOTIFY subMenuChanged FINAL REVISION(2, 3))
+ Q_PROPERTY(qreal implicitTextPadding READ implicitTextPadding WRITE setImplicitTextPadding NOTIFY implicitTextPaddingChanged REVISION(6, 8))
+ Q_PROPERTY(qreal textPadding READ textPadding NOTIFY textPaddingChanged REVISION(6, 8))
Q_CLASSINFO("DeferredPropertyNames", "arrow,background,contentItem,indicator")
QML_NAMED_ELEMENT(MenuItem)
QML_ADDED_IN_VERSION(2, 0)
@@ -47,6 +52,10 @@ public:
QQuickMenu *menu() const;
QQuickMenu *subMenu() const;
+ qreal textPadding() const;
+ qreal implicitTextPadding() const;
+ void setImplicitTextPadding(qreal newImplicitTextPadding);
+
Q_SIGNALS:
void triggered();
void highlightedChanged();
@@ -54,6 +63,8 @@ Q_SIGNALS:
Q_REVISION(2, 3) void arrowChanged();
Q_REVISION(2, 3) void menuChanged();
Q_REVISION(2, 3) void subMenuChanged();
+ Q_REVISION(6, 8) void implicitTextPaddingChanged();
+ Q_REVISION(6, 8) void textPaddingChanged();
protected:
void componentComplete() override;
@@ -69,8 +80,10 @@ private:
Q_DECLARE_PRIVATE(QQuickMenuItem)
};
-QT_END_NAMESPACE
+#ifndef QT_NO_DEBUG_STREAM
+Q_QUICKTEMPLATES2_EXPORT QDebug operator<<(QDebug debug, const QQuickMenuItem *menuItem);
+#endif
-QML_DECLARE_TYPE(QQuickMenuItem)
+QT_END_NAMESPACE
#endif // QQUICKMENUITEM_P_H
diff --git a/src/quicktemplates/qquickmenuitem_p_p.h b/src/quicktemplates/qquickmenuitem_p_p.h
index 63bcfa33f6..3ef4981570 100644
--- a/src/quicktemplates/qquickmenuitem_p_p.h
+++ b/src/quicktemplates/qquickmenuitem_p_p.h
@@ -48,6 +48,7 @@ public:
QQuickDeferredPointer<QQuickItem> arrow;
QQuickMenu *menu = nullptr;
QQuickMenu *subMenu = nullptr;
+ qreal implicitTextPadding;
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickmenuseparator.cpp b/src/quicktemplates/qquickmenuseparator.cpp
index d9aa770ca5..0766151d1c 100644
--- a/src/quicktemplates/qquickmenuseparator.cpp
+++ b/src/quicktemplates/qquickmenuseparator.cpp
@@ -33,7 +33,7 @@ QT_BEGIN_NAMESPACE
\sa {Customizing Menu}, Menu, {Separator Controls}
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuSeparatorPrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuSeparatorPrivate : public QQuickControlPrivate
{
Q_DECLARE_PUBLIC(QQuickMenuSeparator)
diff --git a/src/quicktemplates/qquickmenuseparator_p.h b/src/quicktemplates/qquickmenuseparator_p.h
index abf3221461..ec7517ee90 100644
--- a/src/quicktemplates/qquickmenuseparator_p.h
+++ b/src/quicktemplates/qquickmenuseparator_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickMenuSeparator;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickMenuSeparator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickMenuSeparator : public QQuickControl
{
Q_OBJECT
QML_NAMED_ELEMENT(MenuSeparator)
@@ -44,6 +44,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMenuSeparator)
-
#endif // QQUICKMENUSEPARATOR_P_H
diff --git a/src/quicktemplates/qquickmonthgrid.cpp b/src/quicktemplates/qquickmonthgrid.cpp
index a470017676..957c9b8f93 100644
--- a/src/quicktemplates/qquickmonthgrid.cpp
+++ b/src/quicktemplates/qquickmonthgrid.cpp
@@ -92,8 +92,6 @@ public:
bool handleRelease(const QPointF &point, ulong timestamp) override;
void handleUngrab() override;
- static void setContextProperty(QQuickItem *item, const QString &name, const QVariant &value);
-
QString title;
QVariant source;
QDate pressedDate;
@@ -141,7 +139,6 @@ void QQuickMonthGridPrivate::updatePress(const QPointF &pos)
Q_Q(QQuickMonthGrid);
clearPress(false);
pressedItem = cellAt(pos);
- setContextProperty(pressedItem, QStringLiteral("pressed"), true);
pressedDate = dateOf(pressedItem);
if (pressedDate.isValid())
emit q->pressed(pressedDate);
@@ -150,7 +147,6 @@ void QQuickMonthGridPrivate::updatePress(const QPointF &pos)
void QQuickMonthGridPrivate::clearPress(bool clicked)
{
Q_Q(QQuickMonthGrid);
- setContextProperty(pressedItem, QStringLiteral("pressed"), false);
if (pressedDate.isValid()) {
emit q->released(pressedDate);
if (clicked)
@@ -190,16 +186,6 @@ void QQuickMonthGridPrivate::handleUngrab()
clearPress(false);
}
-void QQuickMonthGridPrivate::setContextProperty(QQuickItem *item, const QString &name, const QVariant &value)
-{
- QQmlContext *context = qmlContext(item);
- if (context && context->isValid()) {
- context = context->parentContext();
- if (context && context->isValid())
- context->setContextProperty(name, value);
- }
-}
-
QQuickMonthGrid::QQuickMonthGrid(QQuickItem *parent) :
QQuickControl(*(new QQuickMonthGridPrivate), parent)
{
@@ -357,13 +343,6 @@ void QQuickMonthGrid::componentComplete()
{
Q_D(QQuickMonthGrid);
QQuickControl::componentComplete();
- if (d->contentItem) {
- const auto childItems = d->contentItem->childItems();
- for (QQuickItem *child : childItems) {
- if (!QQuickItemPrivate::get(child)->isTransparentForPositioner())
- d->setContextProperty(child, QStringLiteral("pressed"), false);
- }
- }
d->resizeItems();
}
diff --git a/src/quicktemplates/qquickmonthgrid_p.h b/src/quicktemplates/qquickmonthgrid_p.h
index 9c5ffad030..e7e4421e50 100644
--- a/src/quicktemplates/qquickmonthgrid_p.h
+++ b/src/quicktemplates/qquickmonthgrid_p.h
@@ -58,10 +58,10 @@ Q_SIGNALS:
void titleChanged();
void delegateChanged();
- void pressed(const QDate &date);
- void released(const QDate &date);
- void clicked(const QDate &date);
- void pressAndHold(const QDate &date);
+ void pressed(QDate date);
+ void released(QDate date);
+ void clicked(QDate date);
+ void pressAndHold(QDate date);
protected:
void componentComplete() override;
@@ -79,6 +79,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMonthGrid)
-
#endif // QQUICKMONTHGRID_P_H
diff --git a/src/quicktemplates/qquickmonthmodel.cpp b/src/quicktemplates/qquickmonthmodel.cpp
index 0910ea9be7..60d5e6fce2 100644
--- a/src/quicktemplates/qquickmonthmodel.cpp
+++ b/src/quicktemplates/qquickmonthmodel.cpp
@@ -138,7 +138,7 @@ QDate QQuickMonthModel::dateAt(int index) const
return d->dates.value(index);
}
-int QQuickMonthModel::indexOf(const QDate &date) const
+int QQuickMonthModel::indexOf(QDate date) const
{
Q_D(const QQuickMonthModel);
if (date < d->dates.first() || date > d->dates.last())
diff --git a/src/quicktemplates/qquickmonthmodel_p.h b/src/quicktemplates/qquickmonthmodel_p.h
index 8d1c0fc062..e629a7f8d1 100644
--- a/src/quicktemplates/qquickmonthmodel_p.h
+++ b/src/quicktemplates/qquickmonthmodel_p.h
@@ -50,7 +50,7 @@ public:
void setTitle(const QString &title);
Q_INVOKABLE QDate dateAt(int index) const;
- Q_INVOKABLE int indexOf(const QDate &date) const;
+ Q_INVOKABLE int indexOf(QDate date) const;
enum {
DateRole = Qt::UserRole + 1,
@@ -78,6 +78,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickMonthModel)
-
#endif // QQUICKMONTHMODEL_P_H
diff --git a/src/quicktemplates/qquicknativeicon.cpp b/src/quicktemplates/qquicknativeicon.cpp
new file mode 100644
index 0000000000..dfc8a4cc7e
--- /dev/null
+++ b/src/quicktemplates/qquicknativeicon.cpp
@@ -0,0 +1,50 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquicknativeicon_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QUrl QQuickNativeIcon::source() const
+{
+ return m_source;
+}
+
+void QQuickNativeIcon::setSource(const QUrl& source)
+{
+ m_source = source;
+}
+
+QString QQuickNativeIcon::name() const
+{
+ return m_name;
+}
+
+void QQuickNativeIcon::setName(const QString& name)
+{
+ m_name = name;
+}
+
+bool QQuickNativeIcon::isMask() const
+{
+ return m_mask;
+}
+
+void QQuickNativeIcon::setMask(bool mask)
+{
+ m_mask = mask;
+}
+
+bool QQuickNativeIcon::operator==(const QQuickNativeIcon &other) const
+{
+ return m_source == other.m_source && m_name == other.m_name && m_mask == other.m_mask;
+}
+
+bool QQuickNativeIcon::operator!=(const QQuickNativeIcon &other) const
+{
+ return !(*this == other);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquicknativeicon_p.cpp"
diff --git a/src/quicktemplates/qquicknativeicon_p.h b/src/quicktemplates/qquicknativeicon_p.h
new file mode 100644
index 0000000000..db0625954a
--- /dev/null
+++ b/src/quicktemplates/qquicknativeicon_p.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKNATIVEICON_P_H
+#define QQUICKNATIVEICON_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qurl.h>
+#include <QtCore/qstring.h>
+
+#include <QtQml/qqmlengine.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QObject;
+
+class QQuickNativeIcon
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ Q_PROPERTY(QUrl source READ source WRITE setSource FINAL)
+ Q_PROPERTY(QString name READ name WRITE setName FINAL)
+ Q_PROPERTY(bool mask READ isMask WRITE setMask FINAL)
+
+public:
+ QUrl source() const;
+ void setSource(const QUrl &source);
+
+ QString name() const;
+ void setName(const QString &name);
+
+ bool isMask() const;
+ void setMask(bool mask);
+
+ bool operator==(const QQuickNativeIcon &other) const;
+ bool operator!=(const QQuickNativeIcon &other) const;
+
+private:
+ bool m_mask = false;
+ QUrl m_source;
+ QString m_name;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKNATIVEICON_P_H
diff --git a/src/quicktemplates/qquicknativeiconloader.cpp b/src/quicktemplates/qquicknativeiconloader.cpp
new file mode 100644
index 0000000000..8b5dd54257
--- /dev/null
+++ b/src/quicktemplates/qquicknativeiconloader.cpp
@@ -0,0 +1,66 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquicknativeiconloader_p.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qmetaobject.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+QQuickNativeIconLoader::QQuickNativeIconLoader(int slot, QObject *parent)
+ : m_parent(parent),
+ m_slot(slot),
+ m_enabled(false)
+{
+ Q_ASSERT(slot != -1 && parent);
+}
+
+bool QQuickNativeIconLoader::isEnabled() const
+{
+ return m_enabled;
+}
+
+void QQuickNativeIconLoader::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+ if (m_enabled)
+ loadIcon();
+}
+
+QIcon QQuickNativeIconLoader::toQIcon() const
+{
+ const QIcon fallback = QPixmap::fromImage(image());
+ return QIcon::fromTheme(m_icon.name(), fallback);
+}
+
+QQuickIcon QQuickNativeIconLoader::icon() const
+{
+ return m_icon;
+}
+
+void QQuickNativeIconLoader::setIcon(const QQuickIcon &icon)
+{
+ m_icon = icon;
+ if (m_enabled)
+ loadIcon();
+}
+
+void QQuickNativeIconLoader::loadIcon()
+{
+ if (m_icon.source().isEmpty()) {
+ clear(m_parent);
+ } else {
+ load(qmlEngine(m_parent), m_icon.source());
+ if (m_slot != -1 && isLoading()) {
+ connectFinished(m_parent, m_slot);
+ m_slot = -1;
+ }
+ }
+
+ if (!isLoading())
+ m_parent->metaObject()->method(m_slot).invoke(m_parent);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquicknativeiconloader_p.h b/src/quicktemplates/qquicknativeiconloader_p.h
new file mode 100644
index 0000000000..063178dc47
--- /dev/null
+++ b/src/quicktemplates/qquicknativeiconloader_p.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKNATIVEICONLOADER_P_H
+#define QQUICKNATIVEICONLOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qurl.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qicon.h>
+#include <QtQuick/private/qquickpixmap_p.h>
+#include <QtQuickTemplates2/private/qquickicon_p.h>
+
+#include "qquicknativeicon_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QObject;
+
+class QQuickNativeIconLoader : public QQuickPixmap
+{
+public:
+ QQuickNativeIconLoader(int slot, QObject *parent);
+
+ bool isEnabled() const;
+ void setEnabled(bool enabled);
+
+ QIcon toQIcon() const;
+
+ QQuickIcon icon() const;
+ void setIcon(const QQuickIcon &icon);
+
+private:
+ void loadIcon();
+
+ QObject *m_parent;
+ int m_slot;
+ bool m_enabled;
+ QQuickIcon m_icon;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKNATIVEICONLOADER_P_H
diff --git a/src/quicktemplates/qquicknativemenuitem.cpp b/src/quicktemplates/qquicknativemenuitem.cpp
new file mode 100644
index 0000000000..727fb87aa8
--- /dev/null
+++ b/src/quicktemplates/qquicknativemenuitem.cpp
@@ -0,0 +1,329 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmenuitem_p.h"
+
+#include <QtCore/qloggingcategory.h>
+//#include <QtGui/qicon.h>
+//#if QT_CONFIG(shortcut)
+//#include <QtGui/qkeysequence.h>
+//#endif
+#include <QtGui/qpa/qplatformmenu.h>
+#include <QtGui/qpa/qplatformtheme.h>
+#include <QtGui/private/qguiapplication_p.h>
+//#include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h>
+#include <QtQuickTemplates2/private/qquickabstractbutton_p_p.h>
+#include <QtQuickTemplates2/private/qquickaction_p.h>
+#include <QtQuickTemplates2/private/qquickmenu_p_p.h>
+#include <QtQuickTemplates2/private/qquickmenuseparator_p.h>
+#include <QtQuickTemplates2/private/qquicknativeiconloader_p.h>
+#include <QtQuickTemplates2/private/qquicknativemenuitem_p.h>
+#include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcNativeMenuItem, "qt.quick.controls.nativemenuitem")
+
+/*!
+ \class QQuickNativeMenuItem
+ \brief A native menu item.
+ \since 6.7
+ \internal
+
+ Creates a native menu item from an Action/MenuItem/Menu,
+ and syncs the properties and signals. It can also represent a
+ MenuSeparator.
+
+ \sa Menu, Action
+*/
+
+QQuickNativeMenuItem *QQuickNativeMenuItem::createFromNonNativeItem(
+ QQuickMenu *parentMenu, QQuickItem *nonNativeItem)
+{
+ auto *menuItem = qobject_cast<QQuickMenuItem *>(nonNativeItem);
+ Type type = Type::Unknown;
+ if (menuItem) {
+ if (menuItem->action()) {
+ type = Type::Action;
+ } else if (menuItem->subMenu()) {
+ type = Type::SubMenu;
+ } else {
+ // It's a plain MenuItem, rather than a MenuItem created by us for an Action or Menu.
+ type = Type::MenuItem;
+ }
+ } else if (qobject_cast<QQuickMenuSeparator *>(nonNativeItem)) {
+ type = Type::Separator;
+ }
+
+ std::unique_ptr<QQuickNativeMenuItem> nativeMenuItemPtr(new QQuickNativeMenuItem(
+ parentMenu, nonNativeItem, type));
+ if (type == Type::Unknown) {
+ // It's not a Menu/Action/MenuSeparator that we're dealing with, but we still need
+ // to create the QQuickNativeMenu item for it so that our container has the same
+ // indices as the menu's contentModel.
+ return nativeMenuItemPtr.release();
+ }
+
+ qCDebug(lcNativeMenuItem) << "attemping to create native menu item for"
+ << nativeMenuItemPtr->debugText();
+ auto *parentMenuPrivate = QQuickMenuPrivate::get(parentMenu);
+ nativeMenuItemPtr->m_handle.reset(parentMenuPrivate->handle->createMenuItem());
+ if (!nativeMenuItemPtr->m_handle)
+ nativeMenuItemPtr->m_handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem());
+ if (!nativeMenuItemPtr->m_handle)
+ return nullptr;
+
+ auto *nativeMenuItem = nativeMenuItemPtr.release();
+ switch (type) {
+ case Type::Action:
+ // Ensure that the action is triggered when the user clicks on a native menu item.
+ connect(nativeMenuItem->m_handle.get(), &QPlatformMenuItem::activated,
+ nativeMenuItem->action(), [nativeMenuItem, parentMenu](){
+ nativeMenuItem->action()->trigger(parentMenu);
+ });
+ // Handle programmatic changes in the Action.
+ connect(nativeMenuItem->action(), &QQuickAction::textChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(nativeMenuItem->action(), &QQuickAction::iconChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(nativeMenuItem->action(), &QQuickAction::enabledChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(nativeMenuItem->action(), &QQuickAction::checkedChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(nativeMenuItem->action(), &QQuickAction::checkableChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ break;
+ case Type::SubMenu:
+ nativeMenuItem->m_handle->setMenu(QQuickMenuPrivate::get(
+ nativeMenuItem->subMenu())->handle.get());
+
+ // Handle programmatic changes in the Menu.
+ connect(nativeMenuItem->subMenu(), &QQuickMenu::enabledChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(nativeMenuItem->subMenu(), &QQuickMenu::titleChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ break;
+ case Type::MenuItem:
+ // Ensure that the MenuItem is clicked when the user clicks on a native menu item.
+ connect(nativeMenuItem->m_handle.get(), &QPlatformMenuItem::activated,
+ menuItem, [menuItem](){
+ // This changes the checked state, which we need when syncing but also to ensure that
+ // the user can still use MenuItem's API even though they can't actually interact with it.
+ menuItem->toggle();
+ // The same applies here: allow users to respond to the MenuItem's clicked signal.
+ QQuickAbstractButtonPrivate::get(menuItem)->click();
+ });
+ // Handle programmatic changes in the MenuItem.
+ connect(menuItem, &QQuickMenuItem::textChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(menuItem, &QQuickMenuItem::iconChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(menuItem, &QQuickMenuItem::enabledChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(menuItem, &QQuickMenuItem::checkedChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ connect(menuItem, &QQuickMenuItem::checkableChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
+ break;
+ case Type::Separator:
+ case Type::Unknown:
+ break;
+ }
+
+ return nativeMenuItem;
+}
+
+QQuickNativeMenuItem::QQuickNativeMenuItem(QQuickMenu *parentMenu, QQuickItem *nonNativeItem,
+ QQuickNativeMenuItem::Type type)
+ : QObject(parentMenu)
+ , m_parentMenu(parentMenu)
+ , m_nonNativeItem(nonNativeItem)
+ , m_type(type)
+{
+}
+
+QQuickNativeMenuItem::~QQuickNativeMenuItem()
+{
+ qCDebug(lcNativeMenuItem) << "destroying" << this << debugText();
+}
+
+QQuickAction *QQuickNativeMenuItem::action() const
+{
+ return m_type == Type::Action ? qobject_cast<QQuickMenuItem *>(m_nonNativeItem)->action() : nullptr;
+}
+
+QQuickMenu *QQuickNativeMenuItem::subMenu() const
+{
+ return m_type == Type::SubMenu ? qobject_cast<QQuickMenuItem *>(m_nonNativeItem)->subMenu() : nullptr;
+}
+
+QQuickMenuSeparator *QQuickNativeMenuItem::separator() const
+{
+ return m_type == Type::Separator ? qobject_cast<QQuickMenuSeparator *>(m_nonNativeItem) : nullptr;
+}
+
+QPlatformMenuItem *QQuickNativeMenuItem::handle() const
+{
+ return m_handle.get();
+}
+
+void QQuickNativeMenuItem::sync()
+{
+ if (m_type == Type::Unknown)
+ return;
+
+ if (m_syncing)
+ return;
+
+ QScopedValueRollback recursionGuard(m_syncing, true);
+
+ const auto *action = this->action();
+ const auto *separator = this->separator();
+ auto *subMenu = this->subMenu();
+ auto *menuItem = qobject_cast<QQuickMenuItem *>(m_nonNativeItem);
+
+ // Store the values in variables so that we can use it in the debug output below.
+ const bool enabled = action ? action->isEnabled()
+ : subMenu ? subMenu->isEnabled()
+ : menuItem && menuItem->isEnabled();
+ m_handle->setEnabled(enabled);
+// m_handle->setVisible(isVisible());
+
+ const bool isSeparator = separator != nullptr;
+ m_handle->setIsSeparator(isSeparator);
+
+ const bool checkable = action ? action->isCheckable() : menuItem && menuItem->isCheckable();
+ m_handle->setCheckable(checkable);
+
+ const bool checked = action ? action->isChecked() : menuItem && menuItem->isChecked();
+ m_handle->setChecked(checked);
+
+ m_handle->setRole(QPlatformMenuItem::TextHeuristicRole);
+
+ const QString text = action ? action->text()
+ : subMenu ? subMenu->title()
+ : menuItem ? menuItem->text() : QString();
+ m_handle->setText(text);
+
+// m_handle->setFont(m_font);
+// m_handle->setHasExclusiveGroup(m_group && m_group->isExclusive());
+ m_handle->setHasExclusiveGroup(false);
+
+ const QQuickIcon icon = effectiveIcon();
+ const auto *menuPrivate = QQuickMenuPrivate::get(m_parentMenu);
+ const auto *window = qGuiApp->topLevelWindows().first();
+ // We should reload the icon if the window's DPR has changed, regardless if its properties have changed.
+ // We can't check for ItemDevicePixelRatioHasChanged in QQuickMenu::itemChange,
+ // because that isn't sent when the menu isn't visible, and will never
+ // be sent for native menus. We instead store lastDevicePixelRatio in QQuickMenu
+ // (to avoid storing it for each menu item) and set it whenever it's opened.
+ const bool dprChanged = !qFuzzyCompare(window->devicePixelRatio(), menuPrivate->lastDevicePixelRatio);
+ if (!icon.isEmpty() && (icon != iconLoader()->icon() || dprChanged)) {
+ // This will load the icon, which will call sync() recursively, hence the m_syncing check.
+ reloadIcon();
+ }
+
+ if (subMenu) {
+ // Sync first as dynamically created menus may need to get the handle recreated.
+ auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
+ subMenuPrivate->syncWithNativeMenu();
+ if (subMenuPrivate->handle)
+ m_handle->setMenu(subMenuPrivate->handle.get());
+ }
+
+#if QT_CONFIG(shortcut)
+ if (action)
+ m_handle->setShortcut(action->shortcut());
+#endif
+
+ if (m_parentMenu) {
+ auto *menuPrivate = QQuickMenuPrivate::get(m_parentMenu);
+ if (menuPrivate->handle)
+ menuPrivate->handle->syncMenuItem(m_handle.get());
+ }
+
+ qCDebug(lcNativeMenuItem) << "sync called on" << debugText() << "handle" << m_handle.get()
+ << "enabled:" << enabled << "isSeparator" << isSeparator << "checkable" << checkable
+ << "checked" << checked << "text" << text;
+}
+
+QQuickIcon QQuickNativeMenuItem::effectiveIcon() const
+{
+ if (const auto *action = this->action())
+ return action->icon();
+ if (const auto *subMenu = this->subMenu())
+ return subMenu->icon();
+ if (const auto *menuItem = qobject_cast<QQuickMenuItem *>(m_nonNativeItem))
+ return menuItem->icon();
+ return {};
+}
+
+QQuickNativeIconLoader *QQuickNativeMenuItem::iconLoader() const
+{
+ if (!m_iconLoader) {
+ QQuickNativeMenuItem *that = const_cast<QQuickNativeMenuItem *>(this);
+ static int slot = staticMetaObject.indexOfSlot("updateIcon()");
+ m_iconLoader = new QQuickNativeIconLoader(slot, that);
+ // Qt Labs Platform's QQuickMenuItem would call m_iconLoader->setEnabled(m_complete) here,
+ // but since QQuickMenuPrivate::maybeCreateAndInsertNativeItem asserts that the menu's
+ // completed loading, we can just set it to true.
+ m_iconLoader->setEnabled(true);
+ }
+ return m_iconLoader;
+}
+
+void QQuickNativeMenuItem::reloadIcon()
+{
+ iconLoader()->setIcon(effectiveIcon());
+ m_handle->setIcon(iconLoader()->toQIcon());
+}
+
+void QQuickNativeMenuItem::updateIcon()
+{
+ sync();
+}
+
+void QQuickNativeMenuItem::addShortcut()
+{
+#if QT_CONFIG(shortcut)
+ const auto *action = this->action();
+ const QKeySequence sequence = action ? action->shortcut() : QKeySequence();
+ if (!sequence.isEmpty() && action->isEnabled()) {
+ m_shortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, sequence,
+ Qt::WindowShortcut, QQuickShortcutContext::matcher);
+ } else {
+ m_shortcutId = -1;
+ }
+#endif
+}
+
+void QQuickNativeMenuItem::removeShortcut()
+{
+#if QT_CONFIG(shortcut)
+ if (m_shortcutId == -1)
+ return;
+
+ QKeySequence sequence;
+ switch (m_type) {
+ case Type::Action:
+ sequence = action()->shortcut();
+ break;
+ default:
+ // TODO
+ break;
+ }
+
+ QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(m_shortcutId, this, sequence);
+#endif
+}
+
+QString QQuickNativeMenuItem::debugText() const
+{
+ switch (m_type) {
+ case Type::Action:
+ return QString::fromLatin1("Action(text = %1)").arg(action()->text());
+ case Type::SubMenu:
+ return QString::fromLatin1("Sub-menu(title = %1)").arg(subMenu()->title());
+ case Type::MenuItem:
+ return QString::fromLatin1("MenuItem(text = %1)").arg(
+ qobject_cast<QQuickMenuItem *>(m_nonNativeItem)->text());
+ case Type::Separator:
+ return QStringLiteral("Separator");
+ case Type::Unknown:
+ return QStringLiteral("(Unknown)");
+ }
+
+ Q_UNREACHABLE();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquicknativemenuitem_p.cpp"
diff --git a/src/quicktemplates/qquicknativemenuitem_p.h b/src/quicktemplates/qquicknativemenuitem_p.h
new file mode 100644
index 0000000000..421d84df29
--- /dev/null
+++ b/src/quicktemplates/qquicknativemenuitem_p.h
@@ -0,0 +1,81 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKNATIVEMENUITEM_P_H
+#define QQUICKNATIVEMENUITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h>
+#include <QtQuickTemplates2/private/qquickicon_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAction;
+class QQuickNativeIconLoader;
+class QQuickMenu;
+class QQuickMenuSeparator;
+class QPlatformMenuItem;
+
+class Q_QUICKTEMPLATES2_EXPORT QQuickNativeMenuItem : public QObject
+{
+ Q_OBJECT
+
+public:
+ static QQuickNativeMenuItem *createFromNonNativeItem(
+ QQuickMenu *parentMenu, QQuickItem *nonNativeItem);
+ ~QQuickNativeMenuItem();
+
+ QQuickAction *action() const;
+ QQuickMenu *subMenu() const;
+ QQuickMenuSeparator *separator() const;
+ QPlatformMenuItem *handle() const;
+ void sync();
+
+ QQuickIcon effectiveIcon() const;
+ QQuickNativeIconLoader *iconLoader() const;
+ void reloadIcon();
+
+ QString debugText() const;
+
+private Q_SLOTS:
+ void updateIcon();
+
+private:
+ enum class Type {
+ Unknown,
+ // It's an Action or a MenuItem with an Action.
+ Action,
+ // It's a MenuItem without an Action.
+ MenuItem,
+ Separator,
+ SubMenu
+ };
+
+ explicit QQuickNativeMenuItem(QQuickMenu *parentMenu, QQuickItem *nonNativeItem, Type type);
+
+ void addShortcut();
+ void removeShortcut();
+
+ QQuickMenu *m_parentMenu = nullptr;
+ QQuickItem *m_nonNativeItem = nullptr;
+ Type m_type = Type::Unknown;
+ mutable QQuickNativeIconLoader *m_iconLoader = nullptr;
+ std::unique_ptr<QPlatformMenuItem> m_handle = nullptr;
+ int m_shortcutId = -1;
+ bool m_syncing = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKNATIVEMENUITEM_P_H
diff --git a/src/quicktemplates/qquickoverlay.cpp b/src/quicktemplates/qquickoverlay.cpp
index bbbde24602..36fa669d22 100644
--- a/src/quicktemplates/qquickoverlay.cpp
+++ b/src/quicktemplates/qquickoverlay.cpp
@@ -9,6 +9,7 @@
#include "qquickdrawer_p.h"
#include "qquickdrawer_p_p.h"
#include "qquickapplicationwindow_p.h"
+#include <QtGui/qpainterpath.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlproperty.h>
#include <QtQml/qqmlcomponent.h>
@@ -66,6 +67,11 @@ void QQuickOverlayPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChang
updateGeometry();
}
+void QQuickOverlayPrivate::itemRotationChanged(QQuickItem *)
+{
+ updateGeometry();
+}
+
bool QQuickOverlayPrivate::startDrag(QEvent *event, const QPointF &pos)
{
Q_Q(QQuickOverlay);
@@ -256,38 +262,15 @@ void QQuickOverlayPrivate::setMouseGrabberPopup(QQuickPopup *popup)
void QQuickOverlayPrivate::updateGeometry()
{
Q_Q(QQuickOverlay);
- if (!window)
+ if (!window || !window->contentItem())
return;
- QPointF pos;
- QSizeF size = window->size();
- qreal rotation = 0;
-
- switch (window->contentOrientation()) {
- case Qt::PrimaryOrientation:
- case Qt::PortraitOrientation:
- size = window->size();
- break;
- case Qt::LandscapeOrientation:
- rotation = 90;
- pos = QPointF((size.width() - size.height()) / 2, -(size.width() - size.height()) / 2);
- size.transpose();
- break;
- case Qt::InvertedPortraitOrientation:
- rotation = 180;
- break;
- case Qt::InvertedLandscapeOrientation:
- rotation = 270;
- pos = QPointF((size.width() - size.height()) / 2, -(size.width() - size.height()) / 2);
- size.transpose();
- break;
- default:
- break;
- }
+ const QSizeF size = window->contentItem()->size();
+ const QPointF pos(-(size.width() - window->size().width()) / 2,
+ -(size.height() - window->size().height()) / 2);
q->setSize(size);
q->setPosition(pos);
- q->setRotation(rotation);
}
QQuickOverlay::QQuickOverlay(QQuickItem *parent)
@@ -307,7 +290,8 @@ QQuickOverlay::QQuickOverlay(QQuickItem *parent)
QQuickItemPrivate::get(parent)->addItemChangeListener(d, QQuickItemPrivate::Geometry);
if (QQuickWindow *window = parent->window()) {
window->installEventFilter(this);
- QObjectPrivate::connect(window, &QWindow::contentOrientationChanged, d, &QQuickOverlayPrivate::updateGeometry);
+ if (QQuickItem *contentItem = window->contentItem())
+ QQuickItemPrivate::get(contentItem)->addItemChangeListener(d, QQuickItemPrivate::Rotation);
}
}
}
@@ -315,8 +299,13 @@ QQuickOverlay::QQuickOverlay(QQuickItem *parent)
QQuickOverlay::~QQuickOverlay()
{
Q_D(QQuickOverlay);
- if (QQuickItem *parent = parentItem())
+ if (QQuickItem *parent = parentItem()) {
QQuickItemPrivate::get(parent)->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
+ if (QQuickWindow *window = parent->window()) {
+ if (QQuickItem *contentItem = window->contentItem())
+ QQuickItemPrivate::get(contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Rotation);
+ }
+ }
}
QQmlComponent *QQuickOverlay::modal() const
@@ -392,7 +381,7 @@ void QQuickOverlay::geometryChange(const QRectF &newGeometry, const QRectF &oldG
Q_D(QQuickOverlay);
QQuickItem::geometryChange(newGeometry, oldGeometry);
for (QQuickPopup *popup : std::as_const(d->allPopups))
- QQuickPopupPrivate::get(popup)->resizeOverlay();
+ QQuickPopupPrivate::get(popup)->resizeDimmer();
}
void QQuickOverlay::mousePressEvent(QMouseEvent *event)
@@ -522,6 +511,12 @@ bool QQuickOverlay::eventFilter(QObject *object, QEvent *event)
// Make sure to accept the touch event in order to receive the consequent
// touch events, to be able to close non-modal popups on release outside.
event->accept();
+ // Since we eat the event, QQuickWindow::event never sees it to clean up the
+ // grabber states. So we have to do so explicitly.
+ if (QQuickWindow *window = parentItem() ? parentItem()->window() : nullptr) {
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
+ d->clearGrabbers(static_cast<QPointerEvent *>(event));
+ }
return true;
#endif
@@ -567,7 +562,18 @@ bool QQuickOverlay::eventFilter(QObject *object, QEvent *event)
if (targetItems.isEmpty())
break;
+ QQuickItem * const dimmerItem = property("_q_dimmerItem").value<QQuickItem *>();
QQuickItem * const topItem = targetItems.first();
+
+ QQuickItem *item = topItem;
+ while ((item = item->parentItem())) {
+ if (qobject_cast<QQuickPopupItem *>(item))
+ break;
+ }
+
+ if (!item && dimmerItem != topItem && isAncestorOf(topItem))
+ break;
+
const auto popups = d->stackingOrderPopups();
// Eat the event if receiver topItem is not a child of a popup before
// the first modal popup.
@@ -575,8 +581,8 @@ bool QQuickOverlay::eventFilter(QObject *object, QEvent *event)
const QQuickItem *popupItem = popup->popupItem();
if (!popupItem)
continue;
- // if we reach a popup that contains the item, deliver the event
- if (popupItem->isAncestorOf(topItem))
+ // if current popup item matches with any popup in stack, deliver the event
+ if (popupItem == item)
break;
// if the popup doesn't contain the item but is modal, eat the event
if (popup->overlayEvent(topItem, we))
diff --git a/src/quicktemplates/qquickoverlay_p.h b/src/quicktemplates/qquickoverlay_p.h
index 47f7e62615..16f0ba09db 100644
--- a/src/quicktemplates/qquickoverlay_p.h
+++ b/src/quicktemplates/qquickoverlay_p.h
@@ -25,7 +25,7 @@ class QQuickOverlayPrivate;
class QQuickOverlayAttached;
class QQuickOverlayAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickOverlay : public QQuickItem
+class Q_QUICKTEMPLATES2_EXPORT QQuickOverlay : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQmlComponent *modal READ modal WRITE setModal NOTIFY modalChanged FINAL)
@@ -76,7 +76,7 @@ private:
Q_DECLARE_PRIVATE(QQuickOverlay)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickOverlayAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickOverlayAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickOverlay *overlay READ overlay NOTIFY overlayChanged FINAL)
@@ -108,6 +108,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickOverlay)
-
#endif // QQUICKOVERLAY_P_H
diff --git a/src/quicktemplates/qquickoverlay_p_p.h b/src/quicktemplates/qquickoverlay_p_p.h
index 38c214804f..3d96540d45 100644
--- a/src/quicktemplates/qquickoverlay_p_p.h
+++ b/src/quicktemplates/qquickoverlay_p_p.h
@@ -20,6 +20,8 @@
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickPopup;
@@ -54,6 +56,7 @@ public:
QList<QQuickPopup *> stackingOrderDrawers() const;
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override;
+ void itemRotationChanged(QQuickItem *item) override;
void updateGeometry();
@@ -64,6 +67,7 @@ public:
// QQuickDrawer by the time removePopup is called.
QList<QQuickPopup *> allDrawers;
QPointer<QQuickPopup> mouseGrabberPopup;
+ QPointer<QQuickItem> lastActiveFocusItem;
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickpage.cpp b/src/quicktemplates/qquickpage.cpp
index 28348c2518..9f19f5bae3 100644
--- a/src/quicktemplates/qquickpage.cpp
+++ b/src/quicktemplates/qquickpage.cpp
@@ -3,9 +3,11 @@
#include "qquickpage_p.h"
#include "qquickpage_p_p.h"
-#include "qquicktabbar_p.h"
#include "qquicktoolbar_p.h"
+#if QT_CONFIG(quicktemplates2_container)
+#include "qquicktabbar_p.h"
#include "qquickdialogbuttonbox_p.h"
+#endif
QT_BEGIN_NAMESPACE
@@ -75,23 +77,27 @@ namespace {
Footer
};
+Q_STATIC_ASSERT(int(Header) == int(QQuickToolBar::Header));
+Q_STATIC_ASSERT(int(Footer) == int(QQuickToolBar::Footer));
+
+#if QT_CONFIG(quicktemplates2_container)
Q_STATIC_ASSERT(int(Header) == int(QQuickTabBar::Header));
Q_STATIC_ASSERT(int(Footer) == int(QQuickTabBar::Footer));
- Q_STATIC_ASSERT(int(Header) == int(QQuickToolBar::Header));
- Q_STATIC_ASSERT(int(Footer) == int(QQuickToolBar::Footer));
-
Q_STATIC_ASSERT(int(Header) == int(QQuickDialogButtonBox::Header));
Q_STATIC_ASSERT(int(Footer) == int(QQuickDialogButtonBox::Footer));
+#endif
static void setPos(QQuickItem *item, Position position)
{
if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(item))
toolBar->setPosition(static_cast<QQuickToolBar::Position>(position));
+#if QT_CONFIG(quicktemplates2_container)
else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(item))
tabBar->setPosition(static_cast<QQuickTabBar::Position>(position));
else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(item))
buttonBox->setPosition(static_cast<QQuickDialogButtonBox::Position>(position));
+#endif
}
}
@@ -110,8 +116,10 @@ void QQuickPagePrivate::relayout()
contentItem->setHeight(q->availableHeight() - hh - fh - hsp - fsp);
}
- if (header)
+ if (header) {
+ header->setY(0);
header->setWidth(q->width());
+ }
if (footer) {
footer->setY(q->height() - footer->height());
@@ -272,6 +280,11 @@ void QQuickPage::setTitle(const QString &title)
emit titleChanged();
}
+void QQuickPage::resetTitle()
+{
+ setTitle(QString());
+}
+
/*!
\qmlproperty Item QtQuick.Controls::Page::header
diff --git a/src/quicktemplates/qquickpage_p.h b/src/quicktemplates/qquickpage_p.h
index b7ce7e9bc9..7eaa443146 100644
--- a/src/quicktemplates/qquickpage_p.h
+++ b/src/quicktemplates/qquickpage_p.h
@@ -22,10 +22,10 @@ QT_BEGIN_NAMESPACE
class QQuickPagePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPage : public QQuickPane
+class Q_QUICKTEMPLATES2_EXPORT QQuickPage : public QQuickPane
{
Q_OBJECT
- Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL)
+ Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged RESET resetTitle FINAL)
Q_PROPERTY(QQuickItem *header READ header WRITE setHeader NOTIFY headerChanged FINAL)
Q_PROPERTY(QQuickItem *footer READ footer WRITE setFooter NOTIFY footerChanged FINAL)
// 2.5 (Qt 5.12)
@@ -42,6 +42,7 @@ public:
QString title() const;
void setTitle(const QString &title);
+ void resetTitle();
QQuickItem *header() const;
void setHeader(QQuickItem *header);
@@ -85,6 +86,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPage)
-
#endif // QQUICKPAGE_P_H
diff --git a/src/quicktemplates/qquickpageindicator.cpp b/src/quicktemplates/qquickpageindicator.cpp
index b0bc5188fe..8b77b79e9f 100644
--- a/src/quicktemplates/qquickpageindicator.cpp
+++ b/src/quicktemplates/qquickpageindicator.cpp
@@ -177,6 +177,13 @@ QQuickPageIndicator::QQuickPageIndicator(QQuickItem *parent)
{
}
+QQuickPageIndicator::~QQuickPageIndicator()
+{
+ Q_D(QQuickPageIndicator);
+ if (d->contentItem)
+ QQuickItemPrivate::get(d->contentItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
+}
+
/*!
\qmlproperty int QtQuick.Controls::PageIndicator::count
diff --git a/src/quicktemplates/qquickpageindicator_p.h b/src/quicktemplates/qquickpageindicator_p.h
index d6c648716c..db4d83fad7 100644
--- a/src/quicktemplates/qquickpageindicator_p.h
+++ b/src/quicktemplates/qquickpageindicator_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQmlComponent;
class QQuickPageIndicatorPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPageIndicator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickPageIndicator : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged FINAL)
@@ -34,6 +34,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPageIndicator : public QQuickContro
public:
explicit QQuickPageIndicator(QQuickItem *parent = nullptr);
+ ~QQuickPageIndicator() override;
int count() const;
void setCount(int count);
@@ -71,6 +72,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPageIndicator)
-
#endif // QQUICKPAGEINDICATOR_P_H
diff --git a/src/quicktemplates/qquickpane.cpp b/src/quicktemplates/qquickpane.cpp
index 9f14843db4..02f32e720e 100644
--- a/src/quicktemplates/qquickpane.cpp
+++ b/src/quicktemplates/qquickpane.cpp
@@ -30,6 +30,10 @@ Q_LOGGING_CATEGORY(lcPane, "qt.quick.controls.pane")
Pane's \l[QtQuickControls2]{Control::}{contentItem}. Items created
dynamically need to be explicitly parented to the \c contentItem.
+ As mentioned in \l {Event Handling}, Pane does not let click and touch
+ events through to items beneath it. If \l [QML] {Control::}{wheelEnabled}
+ is \c true, the same applies to mouse wheel events.
+
\section1 Content Sizing
If only a single item is used within a Pane, it will resize to fit the
@@ -104,6 +108,7 @@ void QQuickPanePrivate::init()
#endif
connect(q, &QQuickControl::implicitContentWidthChanged, this, &QQuickPanePrivate::updateContentWidth);
connect(q, &QQuickControl::implicitContentHeightChanged, this, &QQuickPanePrivate::updateContentHeight);
+ setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
QList<QQuickItem *> QQuickPanePrivate::contentChildItems() const
@@ -139,14 +144,25 @@ void QQuickPanePrivate::itemImplicitHeightChanged(QQuickItem *item)
updateImplicitContentHeight();
}
+void QQuickPanePrivate::itemDestroyed(QQuickItem *item)
+{
+ // Do this check before calling the base class implementation, as that clears contentItem.
+ if (item == firstChild)
+ firstChild = nullptr;
+
+ QQuickControlPrivate::itemDestroyed(item);
+}
+
void QQuickPanePrivate::contentChildrenChange()
{
Q_Q(QQuickPane);
- QQuickItem *newFirstChild = contentChildItems().value(0);
+
+ QQuickItem *newFirstChild = getFirstChild();
+
if (newFirstChild != firstChild) {
if (firstChild)
removeImplicitSizeListener(firstChild);
- if (newFirstChild)
+ if (newFirstChild && newFirstChild != contentItem)
addImplicitSizeListener(newFirstChild);
firstChild = newFirstChild;
}
@@ -171,6 +187,16 @@ qreal QQuickPanePrivate::getContentWidth() const
return 0;
}
+QQuickItem* QQuickPanePrivate::getFirstChild() const
+{
+ // The first child varies depending on how the content item is declared.
+ // If it's declared as a child of the Pane, it will be parented to the
+ // default QQuickContentItem. If it's assigned to the contentItem property
+ // directly, QQuickControl::contentItem will be used.
+ return (qobject_cast<QQuickContentItem *>(contentItem)
+ ? contentChildItems().value(0) : contentItem.data());
+}
+
qreal QQuickPanePrivate::getContentHeight() const
{
if (!contentItem)
diff --git a/src/quicktemplates/qquickpane_p.h b/src/quicktemplates/qquickpane_p.h
index d059e629d4..a2a5cbbff6 100644
--- a/src/quicktemplates/qquickpane_p.h
+++ b/src/quicktemplates/qquickpane_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickPanePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPane : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickPane : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth RESET resetContentWidth NOTIFY contentWidthChanged FINAL)
@@ -69,6 +69,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPane)
-
#endif // QQUICKPANE_P_H
diff --git a/src/quicktemplates/qquickpane_p_p.h b/src/quicktemplates/qquickpane_p_p.h
index 7a8e68a068..71da80090a 100644
--- a/src/quicktemplates/qquickpane_p_p.h
+++ b/src/quicktemplates/qquickpane_p_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickPane;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPanePrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickPanePrivate : public QQuickControlPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickPane)
@@ -31,6 +31,7 @@ public:
virtual QQmlListProperty<QObject> contentData();
virtual QQmlListProperty<QQuickItem> contentChildren();
virtual QList<QQuickItem *> contentChildItems() const;
+ virtual QQuickItem *getFirstChild() const;
QQuickItem *getContentItem() override;
@@ -39,6 +40,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
void contentChildrenChange();
diff --git a/src/quicktemplates/qquickpopup.cpp b/src/quicktemplates/qquickpopup.cpp
index 5b26789951..1af14d37ce 100644
--- a/src/quicktemplates/qquickpopup.cpp
+++ b/src/quicktemplates/qquickpopup.cpp
@@ -5,22 +5,28 @@
#include "qquickpopup_p_p.h"
#include "qquickpopupanchors_p.h"
#include "qquickpopupitem_p_p.h"
+#include "qquickpopupwindow_p_p.h"
#include "qquickpopuppositioner_p_p.h"
#include "qquickapplicationwindow_p.h"
#include "qquickoverlay_p_p.h"
#include "qquickcontrol_p_p.h"
+#if QT_CONFIG(quicktemplates2_container)
#include "qquickdialog_p.h"
+#endif
#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlinfo.h>
#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <QtQuick/private/qquicktransition_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <qpa/qplatformintegration.h>
+#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcDimmer, "qt.quick.controls.popup.dimmer")
-Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
+Q_LOGGING_CATEGORY(lcQuickPopup, "qt.quick.controls.popup")
/*!
\qmltype Popup
@@ -36,8 +42,8 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
used with \l Window or \l ApplicationWindow.
\qml
- import QtQuick.Window 2.2
- import QtQuick.Controls 2.12
+ import QtQuick.Window
+ import QtQuick.Controls
ApplicationWindow {
id: window
@@ -63,10 +69,6 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
}
\endqml
- In order to ensure that a popup is displayed above other items in the
- scene, it is recommended to use ApplicationWindow. ApplicationWindow also
- provides background dimming effects.
-
Popup does not provide a layout of its own, but requires you to position
its contents, for instance by creating a \l RowLayout or a \l ColumnLayout.
@@ -118,6 +120,57 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
}
\endcode
+ \section1 Popup type
+
+ Since Qt 6.8, some popups, such as \l Menu, offer three different implementations,
+ depending on the platform. You can choose which one you prefer by setting \l popupType.
+
+ Whether a popup will be able to use the preferred type depends on the platform.
+ \c Popup.Item is supported on all platforms, but \c Popup.Window and \c Popup.Native
+ are normally only supported on desktop platforms. Additionally, if a popup is a
+ \l Menu inside a \l {Native menu bars}{native menubar}, the menu will be native as
+ well. And if the menu is a sub-menu inside another menu, the parent (or root) menu
+ will decide the type.
+
+ \section2 Showing a popup as an item
+
+ By setting \l popupType to \c Popup.Item, the popup will \e not be shown as a separate
+ window, but as an item inside the same scene as the parent. This item is parented
+ to that scene's \l{Overlay::overlay}{overlay}, and styled to look like an actual window.
+
+ This option is especially useful on platforms that doesn't support multiple windows.
+ This was also the only option before Qt 6.8.
+
+ In order to ensure that a popup is displayed above other items in the
+ scene, it is recommended to use ApplicationWindow. ApplicationWindow also
+ provides background dimming effects.
+
+ \section2 Showing a popup as a separate window
+
+ By setting \l popupType to \c Popup.Window, the popup will be shown inside a top-level
+ \l {QQuickWindow}{window} configured with the \l Qt::Popup flag. Using a window to show a
+ popup has the advantage that the popup will float on top of the parent window, and
+ can be placed outside of its geometry. The popup will otherwise look the same as when
+ using \c Popup.Item, that is, it will use the same QML delegates and styling as
+ when using \c Popup.Item.
+
+ \note If the platform doesn't support \c Popup.Window, \c Popup.Item will be used as fallback.
+
+ \section2 Showing a native popup
+
+ By setting \l popupType to \c Popup.Native, the popup will be shown using a platform
+ native popup window. This window, and all its contents, will be rendered by the
+ platform, and not by QML. This means that the QML delegates assigned to the popup
+ will \e not be used for rendering. If you for example
+ use this option on a \l Menu, it will be implemented using platform-specific
+ menu APIs. This will normally make the popup look and feel more native than for example
+ \c Popup.Window, but at the same time, suffer from platform limitations and differences
+ related to appearance and behavior. Such limitations are documented in more detail
+ in the subclasses that are affected, such as for a
+ \l {Limitations when using native menus}{Menu}).
+
+ \note If the platform doesn't support \c Popup.Native, \c Popup.Window will be used as fallback.
+
\section1 Popup Sizing
If only a single item is used within a Popup, it will resize to fit the
@@ -169,6 +222,36 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
}
\endcode
+ \note When using \l {Popup Items}, the popup's \l{contentItem}{content item} gets parented to the
+ \l{Overlay::overlay}{overlay}, and does not live within the popup's parent.
+ Because of that, a \l{Item::scale}{scale} applied to the tree in which
+ the popup lives does not apply to the visual popup. To make the popup
+ of e.g. a \l{ComboBox} follow the scale of the combobox, apply the same scale
+ to the \l{Overlay::overlay}{overlay} as well:
+
+ \code
+ Window {
+ property double scaleFactor: 2.0
+
+ Scale {
+ id: scale
+ xScale: scaleFactor
+ yScale: scaleFactor
+ }
+ Item {
+ id: scaledContent
+ transform: scale
+
+ ComboBox {
+ id: combobox
+ // ...
+ }
+ }
+
+ Overlay.overlay.transform: scale
+ }
+ \endcode
+
\section1 Popup Positioning
Similar to items in Qt Quick, Popup's \l x and \l y coordinates are
@@ -186,6 +269,47 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
To ensure that the popup is positioned within the bounds of the enclosing
window, the \l margins property can be set to a non-negative value.
+ \section1 Showing Non-Child Items in Front of Popup
+
+ In cases where \l {Showing a popup as an item}{popup windows} are not being used,
+ Popup sets its contentItem's \l{qtquick-visualcanvas-visualparent.html}{visual parent}
+ to be the window's \l{Overlay::overlay}{overlay}, in order to ensure that
+ the popup appears in front of everything else in the scene.
+ In some cases, it might be useful to put an item in front of a popup,
+ such as a \l [QML QtVirtualKeyboard] {InputPanel} {virtual keyboard}.
+ This can be done by setting the item's parent to the overlay,
+ and giving the item a positive z value. The same result can also be
+ achieved by waiting until the popup is opened, before re-parenting the item
+ to the overlay.
+
+ \omit
+ This shouldn't be a snippet, since we don't want VKB to be a dependency to controls.
+ \endomit
+ \qml
+ Popup {
+ id: popup
+ visible: true
+ anchors.centerIn: parent
+ margins: 10
+ closePolicy: Popup.CloseOnEscape
+ ColumnLayout {
+ TextField {
+ placeholderText: qsTr("Username")
+ }
+ TextField {
+ placeholderText: qsTr("Password")
+ echoMode: TextInput.Password
+ }
+ }
+ }
+ InputPanel {
+ parent: Overlay.overlay
+ width: parent.width
+ y: popup.y + popup.topMargin + (window.activeFocusItem?.y ?? 0) + (window.activeFocusItem?.height ?? 0)
+ z: 1
+ }
+ \endqml
+
\section1 Popup Transitions
Since Qt 5.15.3 the following properties are restored to their original values from before
@@ -220,6 +344,50 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
\endlist
\sa {Popup Controls}, {Customizing Popup}, ApplicationWindow
+
+ \section1 Property Propagation
+
+ Popup inherits fonts, palettes and attached properties through its parent
+ window, not its \l {Visual Parent}{object or visual parent}:
+
+ \snippet qtquickcontrols-popup-property-propagation.qml file
+
+ \image qtquickcontrols-basic-popup-property-propagation.png
+
+ In addition, popups do not propagate their properties to child popups. This
+ behavior is modelled on Qt Widgets, where a \c Qt::Popup widget is a
+ top-level window. Top-level windows do not propagate their properties to
+ child windows.
+
+ Certain derived types like ComboBox are typically implemented in such a way
+ that the popup is considered an integral part of the control, and as such,
+ may inherit things like attached properties. For example, in the
+ \l {Material Style}{Material style} ComboBox, the theme and other attached
+ properties are explicitly inherited by the Popup from the ComboBox itself:
+
+ \code
+ popup: T.Popup {
+ // ...
+
+ Material.theme: control.Material.theme
+ Material.accent: control.Material.accent
+ Material.primary: control.Material.primary
+ }
+ \endcode
+
+ So, to ensure that a child popup has the same property values as its parent
+ popup, explicitly set those properties:
+
+ \code
+ Popup {
+ id: parentPopup
+ // ...
+
+ Popup {
+ palette: parentPopup.palette
+ }
+ }
+ \endcode
*/
/*!
@@ -254,6 +422,18 @@ Q_LOGGING_CATEGORY(lcPopup, "qt.quick.controls.popup")
\sa closed()
*/
+QQuickItem *QQuickPopup::findParentItem() const
+{
+ QObject *obj = parent();
+ while (obj) {
+ QQuickItem *item = qobject_cast<QQuickItem *>(obj);
+ if (item)
+ return item;
+ obj = obj->parent();
+ }
+ return nullptr;
+}
+
const QQuickPopup::ClosePolicy QQuickPopupPrivate::DefaultClosePolicy = QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutside;
QQuickPopupPrivate::QQuickPopupPrivate()
@@ -266,7 +446,6 @@ void QQuickPopupPrivate::init()
Q_Q(QQuickPopup);
popupItem = new QQuickPopupItem(q);
popupItem->setVisible(false);
- q->setParentItem(qobject_cast<QQuickItem *>(parent));
QObject::connect(popupItem, &QQuickControl::paddingChanged, q, &QQuickPopup::paddingChanged);
QObject::connect(popupItem, &QQuickControl::backgroundChanged, q, &QQuickPopup::backgroundChanged);
QObject::connect(popupItem, &QQuickControl::contentItemChanged, q, &QQuickPopup::contentItemChanged);
@@ -279,9 +458,11 @@ void QQuickPopupPrivate::init()
void QQuickPopupPrivate::closeOrReject()
{
Q_Q(QQuickPopup);
+#if QT_CONFIG(quicktemplates2_container)
if (QQuickDialog *dialog = qobject_cast<QQuickDialog*>(q))
dialog->reject();
else
+#endif
q->close();
touchId = -1;
}
@@ -340,7 +521,7 @@ bool QQuickPopupPrivate::blockInput(QQuickItem *item, const QPointF &point) cons
// a) outside a non-modal popup,
// b) to popup children/content, or
// b) outside a modal popups's background dimming
- return modal && !popupItem->isAncestorOf(item) && (!dimmer || dimmer->contains(dimmer->mapFromScene(point)));
+ return modal && ((popupItem != item) && !popupItem->isAncestorOf(item)) && (!dimmer || dimmer->contains(dimmer->mapFromScene(point)));
}
bool QQuickPopupPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp)
@@ -348,7 +529,16 @@ bool QQuickPopupPrivate::handlePress(QQuickItem *item, const QPointF &point, ulo
Q_UNUSED(timestamp);
pressPoint = point;
outsidePressed = !contains(point);
- outsideParentPressed = outsidePressed && parentItem && !parentItem->contains(parentItem->mapFromScene(point));
+
+ if (outsidePressed && parentItem) {
+ // Note that the parentItem (e.g a menuBarItem, in case of a MenuBar) will
+ // live inside another window when using popup windows. We therefore need to
+ // map to and from global.
+ const QPointF globalPoint = item->mapToGlobal(point);
+ const QPointF localPoint = parentItem->mapFromGlobal(globalPoint);
+ outsideParentPressed = !parentItem->contains(localPoint);
+ }
+
tryClose(point, QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent);
return blockInput(item, point);
}
@@ -410,6 +600,54 @@ bool QQuickPopupPrivate::handleHoverEvent(QQuickItem *item, QHoverEvent *event)
}
}
+QPointF QQuickPopupPrivate::dropShadowOffset() const
+{
+ // If the popupWindowType has Qt::FramelessWindowHint set, it means
+ // that the background delegate is responsible for drawing the window
+ // frame. And to make room for a drop-shadow in that case, the delegate
+ // can be shifted into the popup using insets, to allow the shadow to
+ // be drawn between the edge of the window and the edge of the frame.
+ // This function will report the size of that shadow, to ensure that we
+ // place the popup such that the top-left corner of the background
+ // frame ends up at the requested position, rather then the top-left
+ // corner of the drop-shadow. If the insets are negative, the shadow
+ // is drawn on the outside of the popup, and the corner of the frame
+ // should already be at 0, 0.
+ if (popupWindowType() & Qt::FramelessWindowHint) {
+ return {
+ popupItem->leftInset() > 0 ? popupItem->leftInset() : 0,
+ popupItem->topInset() > 0 ? popupItem->topInset() : 0
+ };
+ }
+
+ return {0, 0};
+}
+
+void QQuickPopupPrivate::setEffectivePosFromWindowPos(const QPointF &windowPos)
+{
+ // Popup operates internally with three different positions; requested
+ // position, effective position, and window position. The first is the
+ // position requested by the application, and the second is where the popup
+ // is actually placed. The reason for placing it on a different position than
+ // the one requested, is to keep it inside the window (in case of Popup.Item),
+ // or the screen (in case of Popup.Window).
+ // Additionally, since a popup can set Qt::FramelessWindowHint and draw the
+ // window frame from the background delegate, the effective position in that
+ // case is adjusted to be the top-left corner of the background delegate, rather
+ // than the top-left corner of the window. This allowes the background delegate
+ // to render a drop-shadow between the edge of the window and the background frame.
+ // Finally, the window position is the actual position of the window, including
+ // any drop-shadow effects. This posision can be calculated by taking
+ // the effective position and subtract the dropShadowOffset().
+ Q_Q(QQuickPopup);
+ const QPointF oldEffectivePos = effectivePos;
+ effectivePos = windowPos + dropShadowOffset();
+ if (!qFuzzyCompare(oldEffectivePos.x(), effectivePos.x()))
+ emit q->xChanged();
+ if (!qFuzzyCompare(oldEffectivePos.y(), effectivePos.y()))
+ emit q->yChanged();
+}
+
#if QT_CONFIG(quicktemplates2_multitouch)
bool QQuickPopupPrivate::handleTouchEvent(QQuickItem *item, QTouchEvent *event)
{
@@ -458,37 +696,21 @@ bool QQuickPopupPrivate::prepareEnterTransition()
return false;
if (transitionState != EnterTransition) {
- QQuickOverlay *overlay = QQuickOverlay::overlay(window);
- const auto popupStack = QQuickOverlayPrivate::get(overlay)->stackingOrderPopups();
- popupItem->setParentItem(overlay);
- // if there is a stack of popups, and the current top popup item belongs to an
- // ancestor of this popup, then make sure that this popup's item is at the top
- // of the stack.
- const QQuickPopup *topPopup = popupStack.isEmpty() ? nullptr : popupStack.first();
- const QObject *ancestor = q;
- while (ancestor && topPopup) {
- if (ancestor == topPopup)
- break;
- ancestor = ancestor->parent();
- }
- if (topPopup && topPopup != q && ancestor) {
- QQuickItem *topPopupItem = popupStack.first()->popupItem();
- popupItem->stackAfter(topPopupItem);
- // If the popup doesn't have an explicit z value set, set it to be at least as
- // high as the current top popup item so that later opened popups are on top.
- if (!hasZ)
- popupItem->setZ(qMax(topPopupItem->z(), popupItem->z()));
- }
+ visible = true;
+ adjustPopupItemParentAndWindow();
if (dim)
createOverlay();
- showOverlay();
+ showDimmer();
emit q->aboutToShow();
- visible = true;
transitionState = EnterTransition;
- popupItem->setVisible(true);
getPositioner()->setParentItem(parentItem);
emit q->visibleChanged();
+ QQuickOverlay *overlay = QQuickOverlay::overlay(window);
+ auto *overlayPrivate = QQuickOverlayPrivate::get(overlay);
+ if (overlayPrivate->lastActiveFocusItem.isNull())
+ overlayPrivate->lastActiveFocusItem = window->activeFocusItem();
+
if (focus)
popupItem->setFocus(true, Qt::PopupFocusReason);
}
@@ -501,6 +723,8 @@ bool QQuickPopupPrivate::prepareExitTransition()
if (transitionState == ExitTransition && transitionManager.isRunning())
return false;
+ Q_ASSERT(popupItem);
+
// We need to cache the original scale and opacity values so we can reset it after
// the exit transition is done so they have the original values again
prevScale = popupItem->scale();
@@ -509,12 +733,15 @@ bool QQuickPopupPrivate::prepareExitTransition()
if (transitionState != ExitTransition) {
// The setFocus(false) call below removes any active focus before we're
// able to check it in finalizeExitTransition.
- if (!hadActiveFocusBeforeExitTransition)
- hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus();
+ if (!hadActiveFocusBeforeExitTransition) {
+ const auto *da = QQuickItemPrivate::get(popupItem)->deliveryAgentPrivate();
+ hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus() || (da && da->focusTargetItem() == popupItem);
+ }
+
if (focus)
popupItem->setFocus(false, Qt::PopupFocusReason);
transitionState = ExitTransition;
- hideOverlay();
+ hideDimmer();
emit q->aboutToHide();
emit q->openedChanged();
}
@@ -525,7 +752,7 @@ void QQuickPopupPrivate::finalizeEnterTransition()
{
Q_Q(QQuickPopup);
transitionState = NoTransition;
- getPositioner()->reposition();
+ reposition();
emit q->openedChanged();
opened();
}
@@ -534,11 +761,11 @@ void QQuickPopupPrivate::finalizeExitTransition()
{
Q_Q(QQuickPopup);
getPositioner()->setParentItem(nullptr);
- if (popupItem) {
+ if (popupItem && !popupWindow) {
popupItem->setParentItem(nullptr);
popupItem->setVisible(false);
}
- destroyOverlay();
+ destroyDimmer();
if (hadActiveFocusBeforeExitTransition && window) {
// restore focus to the next popup in chain, or to the window content if there are no other popups open
@@ -556,15 +783,21 @@ void QQuickPopupPrivate::finalizeExitTransition()
if (nextFocusPopup) {
nextFocusPopup->forceActiveFocus(Qt::PopupFocusReason);
} else {
- QQuickApplicationWindow *applicationWindow = qobject_cast<QQuickApplicationWindow*>(window);
- if (applicationWindow)
- applicationWindow->contentItem()->setFocus(true, Qt::PopupFocusReason);
- else
- window->contentItem()->setFocus(true, Qt::PopupFocusReason);
+ auto *appWindow = qobject_cast<QQuickApplicationWindow*>(window);
+ auto *contentItem = appWindow ? appWindow->contentItem() : window->contentItem();
+ auto *overlay = QQuickOverlay::overlay(window);
+ auto *overlayPrivate = QQuickOverlayPrivate::get(overlay);
+ if (!contentItem->scopedFocusItem()
+ && !overlayPrivate->lastActiveFocusItem.isNull()) {
+ overlayPrivate->lastActiveFocusItem->setFocus(true, Qt::OtherFocusReason);
+ } else {
+ contentItem->setFocus(true, Qt::PopupFocusReason);
+ }
+ overlayPrivate->lastActiveFocusItem = nullptr;
}
}
-
visible = false;
+ adjustPopupItemParentAndWindow();
transitionState = NoTransition;
hadActiveFocusBeforeExitTransition = false;
emit q->visibleChanged();
@@ -581,6 +814,11 @@ void QQuickPopupPrivate::opened()
emit q->opened();
}
+Qt::WindowFlags QQuickPopupPrivate::popupWindowType() const
+{
+ return Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint;
+}
+
QMarginsF QQuickPopupPrivate::getMargins() const
{
Q_Q(const QQuickPopup);
@@ -736,6 +974,86 @@ QPalette QQuickPopupPrivate::defaultPalette() const
return QQuickTheme::palette(QQuickTheme::System);
}
+QQuickPopup::PopupType QQuickPopupPrivate::resolvedPopupType() const
+{
+ // Whether or not the resolved popup type ends up the same as the preferred popup type
+ // depends on platform capabilities, the popup subclass, and sometimes also the location
+ // of the popup in the parent hierarchy (menus). This function can therefore be overridden
+ // to return the actual popup type that should be used, based on the knowledge the popup
+ // has just before it's about to be shown.
+
+ // PopupType::Native is not directly supported by QQuickPopup (only by subclasses).
+ // So for that case, we fall back to use PopupType::Window, if supported.
+ if (m_popupType == QQuickPopup::PopupType::Window
+ || m_popupType == QQuickPopup::PopupType::Native) {
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::Capability::MultipleWindows))
+ return QQuickPopup::PopupType::Window;
+ }
+
+ return QQuickPopup::PopupType::Item;
+}
+
+bool QQuickPopupPrivate::usePopupWindow() const
+{
+ return resolvedPopupType() == QQuickPopup::PopupType::Window;
+}
+
+void QQuickPopupPrivate::adjustPopupItemParentAndWindow()
+{
+ Q_Q(QQuickPopup);
+ QQuickOverlay *overlay = QQuickOverlay::overlay(window);
+
+ if (visible && popupWindowDirty) {
+ popupItem->setParentItem(overlay);
+ if (popupWindow) {
+ popupWindow->deleteLater();
+ popupWindow = nullptr;
+ }
+ popupWindowDirty = false;
+ }
+
+ if (usePopupWindow()) {
+ if (!popupWindow) {
+ popupWindow = new QQuickPopupWindow(q, window);
+ popupWindow->setWidth(popupItem->width());
+ popupWindow->setHeight(popupItem->height());
+ popupWindow->setModality(modal ? Qt::ApplicationModal : Qt::NonModal);
+ popupItem->resetTitle();
+ popupWindow->setTitle(m_title);
+ popupItem->setParentItem(popupWindow->contentItem());
+ popupItem->forceActiveFocus(Qt::PopupFocusReason);
+ }
+ popupItem->setVisible(true);
+ popupWindow->setVisible(visible);
+ } else {
+ if (visible) {
+ popupItem->setParentItem(overlay);
+ const auto popupStack = QQuickOverlayPrivate::get(overlay)->stackingOrderPopups();
+ // if there is a stack of popups, and the current top popup item belongs to an
+ // ancestor of this popup, then make sure that this popup's item is at the top
+ // of the stack.
+ const QQuickPopup *topPopup = popupStack.isEmpty() ? nullptr : popupStack.first();
+ const QObject *ancestor = q;
+ while (ancestor && topPopup) {
+ if (ancestor == topPopup)
+ break;
+ ancestor = ancestor->parent();
+ }
+ if (topPopup && topPopup != q && ancestor) {
+ QQuickItem *topPopupItem = popupStack.first()->popupItem();
+ popupItem->stackAfter(topPopupItem);
+ // If the popup doesn't have an explicit z value set, set it to be at least as
+ // high as the current top popup item so that later opened popups are on top.
+ if (!hasZ)
+ popupItem->setZ(qMax(topPopupItem->z(), popupItem->z()));
+ }
+ }
+
+ popupItem->setTitle(m_title);
+ popupItem->setVisible(visible);
+ }
+}
+
static QQuickItem *createDimmer(QQmlComponent *component, QQuickPopup *popup, QQuickItem *parent)
{
QQuickItem *item = nullptr;
@@ -752,7 +1070,6 @@ static QQuickItem *createDimmer(QQmlComponent *component, QQuickPopup *popup, QQ
item = new QQuickItem;
if (item) {
- item->setOpacity(popup->isVisible() ? 1.0 : 0.0);
item->setParentItem(parent);
item->stackBefore(popup->popupItem());
item->setZ(popup->z());
@@ -794,12 +1111,24 @@ void QQuickPopupPrivate::createOverlay()
if (!component)
component = modal ? overlay->modal() : overlay->modeless();
- if (!dimmer)
+ if (!dimmer) {
dimmer = createDimmer(component, q, overlay);
- resizeOverlay();
+ if (!dimmer)
+ return;
+ // We cannot update explicitDimmerOpacity when dimmer's opacity changes,
+ // as it is expected to do so when we fade the dimmer in and out in
+ // show/hideDimmer, and any binding of the dimmer's opacity will be
+ // implicitly broken anyway.
+ explicitDimmerOpacity = dimmer->opacity();
+ // initially fully transparent, showDimmer fades the dimmer in.
+ dimmer->setOpacity(0);
+ if (q->isVisible())
+ showDimmer();
+ }
+ resizeDimmer();
}
-void QQuickPopupPrivate::destroyOverlay()
+void QQuickPopupPrivate::destroyDimmer()
{
if (dimmer) {
qCDebug(lcDimmer) << "destroying dimmer" << dimmer;
@@ -815,7 +1144,7 @@ void QQuickPopupPrivate::destroyOverlay()
void QQuickPopupPrivate::toggleOverlay()
{
- destroyOverlay();
+ destroyDimmer();
if (dim)
createOverlay();
}
@@ -829,27 +1158,29 @@ void QQuickPopupPrivate::updateContentPalettes(const QPalette& parentPalette)
QQuickItemPrivate::get(popupItem)->updateChildrenPalettes(parentPalette);
}
-void QQuickPopupPrivate::showOverlay()
+void QQuickPopupPrivate::showDimmer()
{
// use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors
if (dim && dimmer)
- QQmlProperty::write(dimmer, QStringLiteral("opacity"), 1.0);
+ QQmlProperty::write(dimmer, QStringLiteral("opacity"), explicitDimmerOpacity);
}
-void QQuickPopupPrivate::hideOverlay()
+void QQuickPopupPrivate::hideDimmer()
{
// use QQmlProperty instead of QQuickItem::setOpacity() to trigger QML Behaviors
if (dim && dimmer)
QQmlProperty::write(dimmer, QStringLiteral("opacity"), 0.0);
}
-void QQuickPopupPrivate::resizeOverlay()
+void QQuickPopupPrivate::resizeDimmer()
{
if (!dimmer)
return;
- qreal w = window ? window->width() : 0;
- qreal h = window ? window->height() : 0;
+ const QQuickOverlay *overlay = QQuickOverlay::overlay(window);
+
+ qreal w = overlay ? overlay->width() : 0;
+ qreal h = overlay ? overlay->height() : 0;
dimmer->setSize(QSizeF(w, h));
}
@@ -896,6 +1227,8 @@ QQuickPopup::QQuickPopup(QObject *parent)
{
Q_D(QQuickPopup);
d->init();
+ // By default, allow popup to move beyond window edges
+ d->relaxEdgeConstraint = true;
}
QQuickPopup::QQuickPopup(QQuickPopupPrivate &dd, QObject *parent)
@@ -909,6 +1242,13 @@ QQuickPopup::~QQuickPopup()
{
Q_D(QQuickPopup);
d->inDestructor = true;
+
+ QQuickItem *currentContentItem = d->popupItem->d_func()->contentItem.data();
+ if (currentContentItem) {
+ disconnect(currentContentItem, &QQuickItem::childrenChanged,
+ this, &QQuickPopup::contentChildrenChanged);
+ }
+
setParentItem(nullptr);
// If the popup is destroyed before the exit transition finishes,
@@ -956,8 +1296,7 @@ void QQuickPopup::close()
*/
qreal QQuickPopup::x() const
{
- Q_D(const QQuickPopup);
- return d->effectiveX;
+ return d_func()->effectivePos.x();
}
void QQuickPopup::setX(qreal x)
@@ -975,8 +1314,7 @@ void QQuickPopup::setX(qreal x)
*/
qreal QQuickPopup::y() const
{
- Q_D(const QQuickPopup);
- return d->effectiveY;
+ return d_func()->effectivePos.y();
}
void QQuickPopup::setY(qreal y)
@@ -987,8 +1325,7 @@ void QQuickPopup::setY(qreal y)
QPointF QQuickPopup::position() const
{
- Q_D(const QQuickPopup);
- return QPointF(d->effectiveX, d->effectiveY);
+ return d_func()->effectivePos;
}
void QQuickPopup::setPosition(const QPointF &pos)
@@ -1024,6 +1361,9 @@ void QQuickPopup::setPosition(const QPointF &pos)
of an already open popup, then it will be stacked on top of its parent.
This ensures that children are never hidden under their parents.
+ If the popup has its own window, the z-value will determine the window
+ stacking order instead.
+
The default z-value is \c 0.
\sa x, y
@@ -1038,9 +1378,13 @@ void QQuickPopup::setZ(qreal z)
{
Q_D(QQuickPopup);
d->hasZ = true;
- if (qFuzzyCompare(z, d->popupItem->z()))
+ bool previousZ = d->popupWindow ? d->popupWindow->z() : d->popupItem->z();
+ if (qFuzzyCompare(z, previousZ))
return;
- d->popupItem->setZ(z);
+ if (d->popupWindow)
+ d->popupWindow->setZ(z);
+ else
+ d->popupItem->setZ(z);
emit zChanged();
}
@@ -1066,7 +1410,16 @@ void QQuickPopup::setWidth(qreal width)
{
Q_D(QQuickPopup);
d->hasWidth = true;
- d->popupItem->setWidth(width);
+
+ // QQuickPopupWindow::setWidth() triggers a window resize event.
+ // This will cause QQuickPopupWindow::resizeEvent() to resize
+ // the popupItem. QQuickPopupItem::geometryChanged() calls QQuickPopup::geometryChange(),
+ // which emits widthChanged().
+
+ if (d->popupWindow)
+ d->popupWindow->setWidth(width);
+ else
+ d->popupItem->setWidth(width);
}
void QQuickPopup::resetWidth()
@@ -1096,7 +1449,16 @@ void QQuickPopup::setHeight(qreal height)
{
Q_D(QQuickPopup);
d->hasHeight = true;
- d->popupItem->setHeight(height);
+
+ // QQuickPopupWindow::setHeight() triggers a window resize event.
+ // This will cause QQuickPopupWindow::resizeEvent() to resize
+ // the popupItem. QQuickPopupItem::geometryChanged() calls QQuickPopup::geometryChange(),
+ // which emits heightChanged().
+
+ if (d->popupWindow)
+ d->popupWindow->setHeight(height);
+ else
+ d->popupItem->setHeight(height);
}
void QQuickPopup::resetHeight()
@@ -1682,8 +2044,18 @@ void QQuickPopup::setParentItem(QQuickItem *parent)
if (parent) {
QObjectPrivate::connect(parent, &QQuickItem::windowChanged, d, &QQuickPopupPrivate::setWindow);
QQuickItemPrivate::get(d->parentItem)->addItemChangeListener(d, QQuickItemPrivate::Destroyed);
- } else if (!d->inDestructor) {
- // NOTE: if setParentItem is called from the dtor, this bypasses virtual dispatch and calls QQuickPopup::close() directly
+ } else if (d->inDestructor) {
+ d->destroyDimmer();
+ } else {
+ // Reset transition manager state when its parent window destroyed
+ if (!d->window && d->transitionManager.isRunning()) {
+ if (d->transitionState == QQuickPopupPrivate::EnterTransition)
+ d->finalizeEnterTransition();
+ else if (d->transitionState == QQuickPopupPrivate::ExitTransition)
+ d->finalizeExitTransition();
+ }
+ // NOTE: if setParentItem is called from the dtor, this bypasses virtual dispatch and calls
+ // QQuickPopup::close() directly
close();
}
d->setWindow(parent ? parent->window() : nullptr);
@@ -1695,7 +2067,7 @@ void QQuickPopup::resetParentItem()
if (QQuickWindow *window = qobject_cast<QQuickWindow *>(parent()))
setParentItem(window->contentItem());
else
- setParentItem(qobject_cast<QQuickItem *>(parent()));
+ setParentItem(findParentItem());
}
/*!
@@ -1760,8 +2132,18 @@ void QQuickPopup::setContentItem(QQuickItem *item)
{
Q_D(QQuickPopup);
// See comment in setBackground for why we do this.
- QQuickControlPrivate::warnIfCustomizationNotSupported(this, item, QStringLiteral("background"));
+ QQuickControlPrivate::warnIfCustomizationNotSupported(this, item, QStringLiteral("contentItem"));
+ QQuickItem *oldContentItem = d->complete ? d->popupItem->d_func()->contentItem.data()
+ : nullptr;
+ if (oldContentItem)
+ disconnect(oldContentItem, &QQuickItem::childrenChanged, this, &QQuickPopup::contentChildrenChanged);
d->popupItem->setContentItem(item);
+ if (d->complete) {
+ QQuickItem *newContentItem = d->popupItem->d_func()->contentItem.data();
+ connect(newContentItem, &QQuickItem::childrenChanged, this, &QQuickPopup::contentChildrenChanged);
+ if (oldContentItem != newContentItem)
+ emit contentChildrenChanged();
+ }
}
/*!
@@ -1816,17 +2198,18 @@ QQmlListProperty<QQuickItem> QQuickPopupPrivate::contentChildren()
\qmlproperty bool QtQuick.Controls::Popup::clip
This property holds whether clipping is enabled. The default value is \c false.
+ Clipping only works when the popup isn't in its own window.
*/
bool QQuickPopup::clip() const
{
Q_D(const QQuickPopup);
- return d->popupItem->clip();
+ return d->popupItem->clip() && !d->usePopupWindow();
}
void QQuickPopup::setClip(bool clip)
{
Q_D(QQuickPopup);
- if (clip == d->popupItem->clip())
+ if (clip == d->popupItem->clip() || d->usePopupWindow())
return;
d->popupItem->setClip(clip);
emit clipChanged();
@@ -1906,6 +2289,7 @@ void QQuickPopup::setModal(bool modal)
if (d->modal == modal)
return;
d->modal = modal;
+ d->popupWindowDirty = true;
if (d->complete && d->visible)
d->toggleOverlay();
emit modalChanged();
@@ -1977,14 +2361,15 @@ void QQuickPopup::setVisible(bool visible)
if (d->visible == visible && d->transitionState != QQuickPopupPrivate::ExitTransition)
return;
- if (d->complete) {
- if (visible)
- d->transitionManager.transitionEnter();
- else
- d->transitionManager.transitionExit();
- } else {
+ if (!d->complete || (visible && !d->window)) {
d->visible = visible;
+ return;
}
+
+ if (visible)
+ d->transitionManager.transitionEnter();
+ else
+ d->transitionManager.transitionExit();
}
/*!
@@ -2435,6 +2820,57 @@ void QQuickPopup::resetBottomInset()
d->popupItem->resetBottomInset();
}
+
+/*!
+ \qmlproperty enumeration QtQuick.Controls::Popup::popupType
+ \since 6.8
+
+ This property determines the type of popup that is preferred.
+
+ Available options:
+ \value Item The popup will be embedded into the \l{Popup Items}{same scene as the parent}, without the use of a separate window.
+ \value Window The popup will be presented in a \l {Popup Windows}{separate window}. If the platform doesn't support multiple windows,
+ \c Popup.Item will be used instead.
+ \value Native The popup will be native to the platform. If the platform doesn't support native popups,
+ \c Popup.Window will be used instead.
+
+ Whether a popup will be able to use the preferred type depends on the platform.
+ \c Popup.Item is supported on all platforms, but \c Popup.Window and \c Popup.Native
+ are normally only supported on desktop platforms. Additionally, if a popup is a
+ \l Menu inside a \l {Native menu bars}{native menubar}, the menu will be native as
+ well. And if the menu is a sub-menu inside another menu, the parent (or root) menu
+ will decide the type.
+
+ \note The default value is platform dependent, and can change in future versions of Qt.
+ Therefore, if you want to make sure that for example \c Popup.Item is always used on all
+ platforms, you should set it explicitly.
+
+ You can set this property to \c undefined, to restore its value back to its default type.
+
+ \sa {Popup type}
+*/
+QQuickPopup::PopupType QQuickPopup::popupType() const
+{
+ Q_D(const QQuickPopup);
+ return d->m_popupType;
+}
+
+void QQuickPopup::setPopupType(PopupType popupType)
+{
+ Q_D(QQuickPopup);
+ if (d->m_popupType == popupType)
+ return;
+
+ d->m_popupType = popupType;
+
+ emit popupTypeChanged();
+}
+
+void QQuickPopup::resetPopupType()
+{
+ setPopupType(PopupType::Item);
+}
+
/*!
\since QtQuick.Controls 2.3 (Qt 5.10)
\qmlproperty palette QtQuick.Controls::Popup::palette
@@ -2505,7 +2941,7 @@ void QQuickPopup::classBegin()
void QQuickPopup::componentComplete()
{
Q_D(QQuickPopup);
- qCDebug(lcPopup) << "componentComplete" << this;
+ qCDebug(lcQuickPopup) << "componentComplete" << this;
if (!parentItem())
resetParentItem();
@@ -2514,6 +2950,11 @@ void QQuickPopup::componentComplete()
d->complete = true;
d->popupItem->componentComplete();
+
+ if (auto currentContentItem = d->popupItem->d_func()->contentItem.data()) {
+ connect(currentContentItem, &QQuickItem::childrenChanged,
+ this, &QQuickPopup::contentChildrenChanged);
+ }
}
bool QQuickPopup::isComponentComplete() const
@@ -2660,7 +3101,7 @@ void QQuickPopup::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
void QQuickPopup::contentSizeChange(const QSizeF &newSize, const QSizeF &oldSize)
{
- qCDebug(lcPopup) << "contentSizeChange called on" << this << "with newSize" << newSize << "oldSize" << oldSize;
+ qCDebug(lcQuickPopup) << "contentSizeChange called on" << this << "with newSize" << newSize << "oldSize" << oldSize;
if (!qFuzzyCompare(newSize.width(), oldSize.width()))
emit contentWidthChanged();
if (!qFuzzyCompare(newSize.height(), oldSize.height()))
@@ -2677,7 +3118,7 @@ void QQuickPopup::fontChange(const QFont &newFont, const QFont &oldFont)
void QQuickPopup::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickPopup);
- qCDebug(lcPopup) << "geometryChange called on" << this << "with newGeometry" << newGeometry << "oldGeometry" << oldGeometry;
+ qCDebug(lcQuickPopup) << "geometryChange called on" << this << "with newGeometry" << newGeometry << "oldGeometry" << oldGeometry;
d->reposition();
if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) {
emit widthChanged();
@@ -2769,6 +3210,19 @@ QFont QQuickPopup::defaultFont() const
}
#if QT_CONFIG(accessibility)
+QAccessible::Role QQuickPopup::effectiveAccessibleRole() const
+{
+ auto *attached = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(this, false);
+
+ auto role = QAccessible::NoRole;
+ if (auto *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(attached))
+ role = accessibleAttached->role();
+ if (role == QAccessible::NoRole)
+ role = accessibleRole();
+
+ return role;
+}
+
QAccessible::Role QQuickPopup::accessibleRole() const
{
return QAccessible::Dialog;
diff --git a/src/quicktemplates/qquickpopup_p.h b/src/quicktemplates/qquickpopup_p.h
index ee9003d627..1f388d8adb 100644
--- a/src/quicktemplates/qquickpopup_p.h
+++ b/src/quicktemplates/qquickpopup_p.h
@@ -38,7 +38,7 @@ class QQuickPopupAnchors;
class QQuickPopupPrivate;
class QQuickTransition;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopup : public QObject, public QQmlParserStatus
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopup : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
@@ -101,6 +101,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopup : public QObject, public QQml
Q_PROPERTY(qreal leftInset READ leftInset WRITE setLeftInset RESET resetLeftInset NOTIFY leftInsetChanged FINAL REVISION(2, 5))
Q_PROPERTY(qreal rightInset READ rightInset WRITE setRightInset RESET resetRightInset NOTIFY rightInsetChanged FINAL REVISION(2, 5))
Q_PROPERTY(qreal bottomInset READ bottomInset WRITE setBottomInset RESET resetBottomInset NOTIFY bottomInsetChanged FINAL REVISION(2, 5))
+ Q_PROPERTY(PopupType popupType READ popupType WRITE setPopupType RESET resetPopupType NOTIFY popupTypeChanged FINAL REVISION(6, 8))
Q_CLASSINFO("DeferredPropertyNames", "background,contentItem")
Q_CLASSINFO("DefaultProperty", "contentData")
QML_NAMED_ELEMENT(Popup)
@@ -222,11 +223,11 @@ public:
void setDim(bool dim);
void resetDim();
- bool isVisible() const;
+ virtual bool isVisible() const;
virtual void setVisible(bool visible);
qreal opacity() const;
- void setOpacity(qreal opacity);
+ virtual void setOpacity(qreal opacity);
qreal scale() const;
void setScale(qreal scale);
@@ -311,6 +312,17 @@ public:
void setBottomInset(qreal inset);
void resetBottomInset();
+ enum PopupType {
+ Item,
+ Window,
+ Native
+ };
+ Q_ENUM(PopupType)
+
+ PopupType popupType() const;
+ void setPopupType(PopupType);
+ void resetPopupType();
+
public Q_SLOTS:
void open();
void close();
@@ -378,6 +390,7 @@ Q_SIGNALS:
Q_REVISION(2, 5) void leftInsetChanged();
Q_REVISION(2, 5) void rightInsetChanged();
Q_REVISION(2, 5) void bottomInsetChanged();
+ Q_REVISION(6, 8) void popupTypeChanged();
protected:
QQuickPopup(QQuickPopupPrivate &dd, QObject *parent);
@@ -419,7 +432,10 @@ protected:
virtual QFont defaultFont() const;
#if QT_CONFIG(accessibility)
+ QAccessible::Role effectiveAccessibleRole() const;
+private:
virtual QAccessible::Role accessibleRole() const;
+protected:
virtual void accessibilityActiveChanged(bool active);
#endif
@@ -430,8 +446,11 @@ protected:
bool setAccessibleProperty(const char *propertyName, const QVariant &value);
private:
+ QQuickItem *findParentItem() const;
+
Q_DISABLE_COPY(QQuickPopup)
Q_DECLARE_PRIVATE(QQuickPopup)
+ friend class QQuickPopupWindow;
friend class QQuickPopupItem;
friend class QQuickOverlay;
friend class QQuickOverlayPrivate;
@@ -441,6 +460,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPopup::ClosePolicy)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPopup)
-
#endif // QQUICKPOPUP_P_H
diff --git a/src/quicktemplates/qquickpopup_p_p.h b/src/quicktemplates/qquickpopup_p_p.h
index eabe2df001..48d6fc32c2 100644
--- a/src/quicktemplates/qquickpopup_p_p.h
+++ b/src/quicktemplates/qquickpopup_p_p.h
@@ -25,6 +25,8 @@
#include <QtQuick/private/qquicktransitionmanager_p_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickTransition;
@@ -32,10 +34,11 @@ class QQuickTransitionManager;
class QQuickPopup;
class QQuickPopupAnchors;
class QQuickPopupItem;
+class QQuickPopupWindow;
class QQuickPopupPrivate;
class QQuickPopupPositioner;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupTransitionManager : public QQuickTransitionManager
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupTransitionManager : public QQuickTransitionManager
{
public:
QQuickPopupTransitionManager(QQuickPopupPrivate *popup);
@@ -50,7 +53,7 @@ private:
QQuickPopupPrivate *popup = nullptr;
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupPrivate
: public QObjectPrivate
, public QQuickItemChangeListener
, public QQuickPaletteProviderPrivateBase<QQuickPopup, QQuickPopupPrivate>
@@ -82,6 +85,7 @@ public:
virtual bool handlePress(QQuickItem* item, const QPointF &point, ulong timestamp);
virtual bool handleMove(QQuickItem* item, const QPointF &point, ulong timestamp);
virtual bool handleRelease(QQuickItem* item, const QPointF &point, ulong timestamp);
+ virtual bool handleReleaseWithoutGrab(const QEventPoint &) { return false; }
virtual void handleUngrab();
bool handleMouseEvent(QQuickItem *item, QMouseEvent *event);
@@ -90,15 +94,22 @@ public:
bool handleTouchEvent(QQuickItem *item, QTouchEvent *event);
#endif
+ QPointF dropShadowOffset() const;
+ void setEffectivePosFromWindowPos(const QPointF &windowPos);
void reposition();
+ bool usePopupWindow() const;
+ void adjustPopupItemParentAndWindow();
void createOverlay();
- void destroyOverlay();
+ void destroyDimmer();
void toggleOverlay();
void updateContentPalettes(const QPalette& parentPalette);
- virtual void showOverlay();
- virtual void hideOverlay();
- virtual void resizeOverlay();
+
+ virtual QQuickPopup::PopupType resolvedPopupType() const;
+
+ virtual void showDimmer();
+ virtual void hideDimmer();
+ virtual void resizeDimmer();
virtual bool prepareEnterTransition();
virtual bool prepareExitTransition();
@@ -107,6 +118,8 @@ public:
virtual void opened();
+ virtual Qt::WindowFlags popupWindowType() const;
+
QMarginsF getMargins() const;
void setTopMargin(qreal value, bool reset = false);
@@ -154,11 +167,12 @@ public:
bool outsidePressed = false;
bool outsideParentPressed = false;
bool inDestructor = false;
+ bool relaxEdgeConstraint = false;
+ bool popupWindowDirty = false;
int touchId = -1;
qreal x = 0;
qreal y = 0;
- qreal effectiveX = 0;
- qreal effectiveY = 0;
+ QPointF effectivePos;
qreal margins = -1;
qreal topMargin = 0;
qreal leftMargin = 0;
@@ -173,13 +187,17 @@ public:
QQuickTransition *enter = nullptr;
QQuickTransition *exit = nullptr;
QQuickPopupItem *popupItem = nullptr;
+ QQuickPopupWindow *popupWindow = nullptr;
QQuickPopupPositioner *positioner = nullptr;
QList<QQuickStateAction> enterActions;
QList<QQuickStateAction> exitActions;
QQuickPopupTransitionManager transitionManager;
QQuickPopupAnchors *anchors = nullptr;
+ qreal explicitDimmerOpacity = 0;
qreal prevOpacity = 0;
qreal prevScale = 0;
+ QString m_title;
+ QQuickPopup::PopupType m_popupType = QQuickPopup::Item;
friend class QQuickPopupTransitionManager;
};
diff --git a/src/quicktemplates/qquickpopupanchors_p.h b/src/quicktemplates/qquickpopupanchors_p.h
index 589e2d6c8b..db94939673 100644
--- a/src/quicktemplates/qquickpopupanchors_p.h
+++ b/src/quicktemplates/qquickpopupanchors_p.h
@@ -26,10 +26,10 @@ class QQuickItem;
class QQuickPopupAnchorsPrivate;
class QQuickPopup;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupAnchors : public QObject, public QQuickItemChangeListener
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupAnchors : public QObject, public QQuickItemChangeListener
{
Q_OBJECT
- Q_PROPERTY(QQuickItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged)
+ Q_PROPERTY(QQuickItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged FINAL)
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 5)
@@ -53,6 +53,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickPopupAnchors)
-
#endif // QQUICKPOPUPANCHORS_P_H
diff --git a/src/quicktemplates/qquickpopupitem.cpp b/src/quicktemplates/qquickpopupitem.cpp
index 157046ca51..e60e4c5d77 100644
--- a/src/quicktemplates/qquickpopupitem.cpp
+++ b/src/quicktemplates/qquickpopupitem.cpp
@@ -22,6 +22,11 @@ QQuickPopupItemPrivate::QQuickPopupItemPrivate(QQuickPopup *popup)
isTabFence = true;
}
+QQuickPopupItemPrivate *QQuickPopupItemPrivate::get(QQuickPopupItem *popupItem)
+{
+ return popupItem->d_func();
+}
+
void QQuickPopupItemPrivate::implicitWidthChanged()
{
qCDebug(lcPopupItem).nospace() << "implicitWidthChanged called on " << q_func() << "; new implicitWidth is " << implicitWidth;
@@ -71,8 +76,6 @@ void QQuickPopupItemPrivate::executeContentItem(bool complete)
quickCompleteDeferred(popup, contentItemName(), contentItem);
}
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickPopupItemPrivate::cancelBackground()
{
quickCancelDeferred(popup, backgroundName());
@@ -143,6 +146,18 @@ QPalette QQuickPopupItemPrivate::parentPalette(const QPalette &fallbackPalette)
return QQuickPopupPrivate::get(popup)->parentPalette(fallbackPalette);
}
+bool QQuickPopupItem::contains(const QPointF &point) const
+{
+ Q_D(const QQuickPopupItem);
+ // A popup will often contain a drop shadow. And when determining if a point
+ // is inside the popup, we want to exclude that shadow from the test, and only
+ // consider the background rect.
+ const QRectF backgroundRect = boundingRect().adjusted(
+ d->popup->leftInset(), d->popup->topInset(),
+ -d->popup->rightInset(), -d->popup->bottomInset());
+ return backgroundRect.contains(point);
+}
+
void QQuickPopupItem::updatePolish()
{
Q_D(QQuickPopupItem);
@@ -310,7 +325,7 @@ QFont QQuickPopupItem::defaultFont() const
QAccessible::Role QQuickPopupItem::accessibleRole() const
{
Q_D(const QQuickPopupItem);
- return d->popup->accessibleRole();
+ return d->popup->effectiveAccessibleRole();
}
void QQuickPopupItem::accessibilityActiveChanged(bool active)
diff --git a/src/quicktemplates/qquickpopupitem_p_p.h b/src/quicktemplates/qquickpopupitem_p_p.h
index 77b7695421..cb0d2709cd 100644
--- a/src/quicktemplates/qquickpopupitem_p_p.h
+++ b/src/quicktemplates/qquickpopupitem_p_p.h
@@ -23,13 +23,15 @@ QT_BEGIN_NAMESPACE
class QQuickPopup;
class QQuickPopupItemPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupItem : public QQuickPage
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupItem : public QQuickPage
{
Q_OBJECT
public:
explicit QQuickPopupItem(QQuickPopup *popup);
+ bool contains(const QPointF &point) const override;
+
protected:
void updatePolish() override;
@@ -74,13 +76,15 @@ private:
friend class QQuickPopup;
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupItemPrivate : public QQuickPagePrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupItemPrivate : public QQuickPagePrivate
{
Q_DECLARE_PUBLIC(QQuickPopupItem)
public:
QQuickPopupItemPrivate(QQuickPopup *popup);
+ static QQuickPopupItemPrivate *get(QQuickPopupItem *popupItem);
+
void implicitWidthChanged() override;
void implicitHeightChanged() override;
diff --git a/src/quicktemplates/qquickpopuppositioner.cpp b/src/quicktemplates/qquickpopuppositioner.cpp
index aecbc7373c..a7e1029a77 100644
--- a/src/quicktemplates/qquickpopuppositioner.cpp
+++ b/src/quicktemplates/qquickpopuppositioner.cpp
@@ -5,6 +5,7 @@
#include "qquickpopuppositioner_p_p.h"
#include "qquickpopupanchors_p.h"
#include "qquickpopupitem_p_p.h"
+#include "qquickpopupwindow_p_p.h"
#include "qquickpopup_p_p.h"
#include <QtCore/qloggingcategory.h>
@@ -72,7 +73,40 @@ void QQuickPopupPositioner::setParentItem(QQuickItem *parent)
void QQuickPopupPositioner::reposition()
{
- QQuickItem *popupItem = m_popup->popupItem();
+ auto p = QQuickPopupPrivate::get(popup());
+ QQuickPopupItem *popupItem = static_cast<QQuickPopupItem *>(m_popup->popupItem());
+
+ if (p->usePopupWindow()) {
+ QPointF requestedPos(p->x, p->y);
+ // Shift the window position a bit back, so that the top-left of the
+ // background frame ends up at the requested position.
+ QPointF windowPos = requestedPos - p->dropShadowOffset();
+
+ if (!p->popupWindow || !p->parentItem) {
+ // If we don't have a popupWindow, set a temporary effective pos. Otherwise
+ // wait for a callback to QQuickPopupWindow::handlePopupPositionChangeFromWindowSystem()
+ // from setting p->popupWindow->setPosition() below.
+ p->setEffectivePosFromWindowPos(windowPos);
+ return;
+ }
+
+ const QQuickItem *centerInParent = p->anchors ? p->getAnchors()->centerIn() : nullptr;
+ const QQuickOverlay *centerInOverlay = qobject_cast<const QQuickOverlay *>(centerInParent);
+
+ if (centerInParent == p->parentItem || centerInOverlay) {
+ windowPos = centerInOverlay ? QPoint(qRound(centerInOverlay->width() / 2.0), qRound(centerInOverlay->height() / 2.0))
+ : QPoint(qRound(p->parentItem->width() / 2.0), qRound(p->parentItem->height() / 2.0));
+ windowPos -= QPoint(qRound(p->popupItem->width() / 2.0), qRound(p->popupItem->height() / 2.0));
+
+ } else if (centerInParent)
+ qmlWarning(popup()) << "Popup can only be centered within its immediate parent or Overlay.overlay";
+
+ const QPointF globalCoords = p->parentItem->mapToGlobal(windowPos.x(), windowPos.y());
+ p->popupWindow->setPosition(globalCoords.x(), globalCoords.y());
+ p->popupItem->setPosition({0, 0});
+ return;
+ }
+
if (!popupItem->isVisible())
return;
@@ -90,14 +124,13 @@ void QQuickPopupPositioner::reposition()
bool widthAdjusted = false;
bool heightAdjusted = false;
- QQuickPopupPrivate *p = QQuickPopupPrivate::get(m_popup);
const QQuickItem *centerInParent = p->anchors ? p->getAnchors()->centerIn() : nullptr;
const QQuickOverlay *centerInOverlay = qobject_cast<const QQuickOverlay*>(centerInParent);
QRectF rect(!centerInParent ? p->allowHorizontalMove ? p->x : popupItem->x() : 0,
!centerInParent ? p->allowVerticalMove ? p->y : popupItem->y() : 0,
- !p->hasWidth && iw > 0 ? iw : w,
- !p->hasHeight && ih > 0 ? ih : h);
+ !p->hasWidth && iw > 0 ? iw : w, !p->hasHeight && ih > 0 ? ih : h);
+ bool relaxEdgeConstraint = p->relaxEdgeConstraint;
if (m_parentItem) {
// m_parentItem is the parent that the popup should open in,
// and popupItem()->parentItem() is the overlay, so the mapToItem() calls below
@@ -110,6 +143,8 @@ void QQuickPopupPositioner::reposition()
if (centerInOverlay) {
rect.moveCenter(QPointF(qRound(centerInOverlay->width() / 2.0), qRound(centerInOverlay->height() / 2.0)));
+ // Popup cannot be moved outside window bounds when its centered with overlay
+ relaxEdgeConstraint = false;
} else {
const QPointF parentItemCenter = QPointF(qRound(m_parentItem->width() / 2), qRound(m_parentItem->height() / 2));
rect.moveCenter(m_parentItem->mapToItem(popupItem->parentItem(), parentItemCenter));
@@ -124,8 +159,6 @@ void QQuickPopupPositioner::reposition()
qMax<qreal>(0.0, margins.top()),
p->window->width() - qMax<qreal>(0.0, margins.left()) - qMax<qreal>(0.0, margins.right()),
p->window->height() - qMax<qreal>(0.0, margins.top()) - qMax<qreal>(0.0, margins.bottom()));
- if (p->window->contentOrientation() == Qt::LandscapeOrientation || p->window->contentOrientation() == Qt::InvertedLandscapeOrientation)
- bounds = bounds.transposed();
// if the popup doesn't fit horizontally inside the window, try flipping it around (left <-> right)
if (p->allowHorizontalFlip && (rect.left() < bounds.left() || rect.right() > bounds.right())) {
@@ -170,12 +203,17 @@ void QQuickPopupPositioner::reposition()
}
// as a last resort, adjust the width to fit the window
+ // Negative margins don't require resize as popup not pushed within
+ // the boundary. But otherwise, retain existing behavior of resizing
+ // for items, such as menus, which enables flip.
if (p->allowHorizontalResize) {
- if (rect.left() < bounds.left()) {
+ if ((margins.left() >= 0 || !relaxEdgeConstraint)
+ && (rect.left() < bounds.left())) {
rect.setLeft(bounds.left());
widthAdjusted = true;
}
- if (rect.right() > bounds.right()) {
+ if ((margins.right() >= 0 || !relaxEdgeConstraint)
+ && (rect.right() > bounds.right())) {
rect.setRight(bounds.right());
widthAdjusted = true;
}
@@ -198,12 +236,17 @@ void QQuickPopupPositioner::reposition()
}
// as a last resort, adjust the height to fit the window
+ // Negative margins don't require resize as popup not pushed within
+ // the boundary. But otherwise, retain existing behavior of resizing
+ // for items, such as menus, which enables flip.
if (p->allowVerticalResize) {
- if (rect.top() < bounds.top()) {
+ if ((margins.top() >= 0 || !relaxEdgeConstraint)
+ && (rect.top() < bounds.top())) {
rect.setTop(bounds.top());
heightAdjusted = true;
}
- if (rect.bottom() > bounds.bottom()) {
+ if ((margins.bottom() >= 0 || !relaxEdgeConstraint)
+ && (rect.bottom() > bounds.bottom())) {
rect.setBottom(bounds.bottom());
heightAdjusted = true;
}
@@ -219,21 +262,20 @@ void QQuickPopupPositioner::reposition()
m_positioning = true;
- popupItem->setPosition(rect.topLeft());
+ // Shift the "window" a bit back, so that the top-left of the
+ // background frame ends up at the requested position.
+ const QPointF windowPos = rect.topLeft() - p->dropShadowOffset();
+ popupItem->setPosition(windowPos);
// If the popup was assigned a parent, rect will be in scene coordinates,
// so we need to map its top left back to item coordinates.
// However, if centering within the overlay, the coordinates will be relative
// to the window, so we don't need to do anything.
- const QPointF effectivePos = m_parentItem && !centerInOverlay ? m_parentItem->mapFromScene(rect.topLeft()) : rect.topLeft();
- if (!qFuzzyCompare(p->effectiveX, effectivePos.x())) {
- p->effectiveX = effectivePos.x();
- emit m_popup->xChanged();
- }
- if (!qFuzzyCompare(p->effectiveY, effectivePos.y())) {
- p->effectiveY = effectivePos.y();
- emit m_popup->yChanged();
- }
+ // The same applies to popups that are in their own dedicated window.
+ if (m_parentItem && !centerInOverlay)
+ p->setEffectivePosFromWindowPos(m_parentItem->mapFromScene(windowPos));
+ else
+ p->setEffectivePosFromWindowPos(windowPos);
if (!p->hasWidth && widthAdjusted && rect.width() > 0) {
popupItem->setWidth(rect.width() / m_popupScale);
diff --git a/src/quicktemplates/qquickpopuppositioner_p_p.h b/src/quicktemplates/qquickpopuppositioner_p_p.h
index c8e20b4b28..1ec8234469 100644
--- a/src/quicktemplates/qquickpopuppositioner_p_p.h
+++ b/src/quicktemplates/qquickpopuppositioner_p_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QQuickItem;
class QQuickPopup;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickPopupPositioner : public QQuickItemChangeListener
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupPositioner : public QQuickItemChangeListener
{
public:
explicit QQuickPopupPositioner(QQuickPopup *popup);
diff --git a/src/quicktemplates/qquickpopupwindow.cpp b/src/quicktemplates/qquickpopupwindow.cpp
new file mode 100644
index 0000000000..f773c5fdeb
--- /dev/null
+++ b/src/quicktemplates/qquickpopupwindow.cpp
@@ -0,0 +1,266 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickpopupwindow_p_p.h"
+#include "qquickcombobox_p.h"
+#include "qquickpopup_p.h"
+#include "qquickpopup_p_p.h"
+#include "qquickmenu_p_p.h"
+#include "qquickmenubar_p_p.h"
+#include "qquickpopupitem_p_p.h"
+#include <QtGui/private/qguiapplication_p.h>
+
+#include <QtCore/qloggingcategory.h>
+#include <QtGui/private/qeventpoint_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickwindowmodule_p.h>
+#include <QtQuick/private/qquickwindowmodule_p_p.h>
+#include <qpa/qplatformwindow_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPopupWindow, "qt.quick.controls.popup.window")
+
+class QQuickPopupWindowPrivate : public QQuickWindowQmlImplPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPopupWindow)
+
+public:
+ QPointer<QQuickItem> m_popupItem;
+ QPointer<QQuickPopup> m_popup;
+
+ void forwardEventToParentMenuOrMenuBar(QEvent *event);
+};
+
+QQuickPopupWindow::QQuickPopupWindow(QQuickPopup *popup, QWindow *parent)
+ : QQuickWindowQmlImpl(*(new QQuickPopupWindowPrivate), nullptr)
+{
+ Q_D(QQuickPopupWindow);
+
+ d->m_popup = popup;
+ d->m_popupItem = popup->popupItem();
+ setTransientParent(parent);
+
+ connect(d->m_popup, &QQuickPopup::windowChanged, this, &QQuickPopupWindow::windowChanged);
+ connect(d->m_popup, &QQuickPopup::implicitWidthChanged, this, &QQuickPopupWindow::implicitWidthChanged);
+ connect(d->m_popup, &QQuickPopup::implicitHeightChanged, this, &QQuickPopupWindow::implicitHeightChanged);
+ connect(d->m_popup->window(), &QWindow::xChanged, this, &QQuickPopupWindow::parentWindowXChanged);
+ connect(d->m_popup->window(), &QWindow::yChanged, this, &QQuickPopupWindow::parentWindowYChanged);
+
+ setWidth(d->m_popupItem->implicitWidth());
+ setHeight(d->m_popupItem->implicitHeight());
+
+ const auto flags = QQuickPopupPrivate::get(popup)->popupWindowType();
+
+ // For popup windows, we'll need to draw everything, in order to have enough control over the styling.
+ if (flags & Qt::Popup)
+ setColor(QColorConstants::Transparent);
+
+ setFlags(flags);
+
+ qCDebug(lcPopupWindow) << "Created popup window with flags: " << flags;
+}
+
+QQuickPopupWindow::~QQuickPopupWindow()
+{
+ Q_D(QQuickPopupWindow);
+ disconnect(d->m_popup, &QQuickPopup::windowChanged, this, &QQuickPopupWindow::windowChanged);
+ disconnect(d->m_popup, &QQuickPopup::implicitWidthChanged, this, &QQuickPopupWindow::implicitWidthChanged);
+ disconnect(d->m_popup, &QQuickPopup::implicitHeightChanged, this, &QQuickPopupWindow::implicitHeightChanged);
+ disconnect(d->m_popup->window(), &QWindow::xChanged, this, &QQuickPopupWindow::parentWindowXChanged);
+ disconnect(d->m_popup->window(), &QWindow::yChanged, this, &QQuickPopupWindow::parentWindowYChanged);
+}
+
+QQuickPopup *QQuickPopupWindow::popup() const
+{
+ Q_D(const QQuickPopupWindow);
+ return d->m_popup;
+}
+
+void QQuickPopupWindow::hideEvent(QHideEvent *e)
+{
+ Q_D(QQuickPopupWindow);
+ QQuickWindow::hideEvent(e);
+ if (QQuickPopup *popup = d->m_popup) {
+ QQuickPopupPrivate::get(popup)->visible = false;
+ emit popup->visibleChanged();
+ }
+}
+
+void QQuickPopupWindow::moveEvent(QMoveEvent *e)
+{
+ handlePopupPositionChangeFromWindowSystem(e->pos());
+}
+
+void QQuickPopupWindow::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QQuickPopupWindow);
+ QQuickWindowQmlImpl::resizeEvent(e);
+
+ if (!d->m_popupItem)
+ return;
+
+ qCDebug(lcPopupWindow) << "A window system event changed the popup's size to be " << e->size();
+ d->m_popupItem->setWidth(e->size().width());
+ d->m_popupItem->setHeight(e->size().height());
+}
+
+/*! \internal
+ We want to handle menus specially, compared to other popups. For menus, we
+ want all open parent menus and sub menus that belong together to almost
+ act as a single popup WRT hover event delivery. This will allow the user to
+ hover and highlight MenuItems inside all of them, not just the leaf menu.
+ This function will therefore find the menu, or menu bar, under the event's
+ position, and forward the event to it. But note that this forwarding will
+ happen in parallel with normal event delivery (we don't eat the events), as
+ we don't want to break the delivery to e.g grabbers.
+ */
+void QQuickPopupWindowPrivate::forwardEventToParentMenuOrMenuBar(QEvent *event)
+{
+ Q_Q(QQuickPopupWindow);
+
+ if (!event->isPointerEvent())
+ return;
+ auto menu = qobject_cast<QQuickMenu *>(q->popup());
+ if (!menu)
+ return;
+
+ auto *pe = static_cast<QPointerEvent *>(event);
+ const QPointF globalPos = pe->points().first().globalPosition();
+
+ // If there is a Menu or a MenuBar under the mouse, resolve its window.
+ QQuickPopupWindow *targetPopupWindow = nullptr;
+ QQuickWindow *targetWindow = nullptr;
+
+ QObject *menuParent = menu;
+ while (menuParent) {
+ if (auto parentMenu = qobject_cast<QQuickMenu *>(menuParent)) {
+ QQuickPopupWindow *popupWindow = QQuickMenuPrivate::get(parentMenu)->popupWindow;
+ auto popup_d = QQuickPopupPrivate::get(popupWindow->popup());
+ QPointF scenePos = popupWindow->contentItem()->mapFromGlobal(globalPos);
+ if (popup_d->contains(scenePos)) {
+ targetPopupWindow = popupWindow;
+ targetWindow = popupWindow;
+ break;
+ }
+ } else if (auto menuBar = qobject_cast<QQuickMenuBar *>(menuParent)) {
+ const QPointF menuBarPos = menuBar->mapFromGlobal(globalPos);
+ if (menuBar->contains(menuBarPos))
+ targetWindow = menuBar->window();
+ break;
+ }
+
+ menuParent = menuParent->parent();
+ }
+
+ if (!targetPopupWindow) {
+ if (pe->isBeginEvent()) {
+ // A QQuickPopupWindow can be bigger than the Popup itself, to make room
+ // for a drop-shadow. Close all popups if the user clicks either on the
+ // shadow or outside the window.
+ QGuiApplicationPrivate::closeAllPopups();
+ return;
+ }
+ }
+
+ if (!targetWindow)
+ return;
+
+ if (pe->isUpdateEvent()){
+ // Forward move events to the target window
+ const auto scenePos = pe->point(0).scenePosition();
+ const auto translatedScenePos = targetWindow->mapFromGlobal(globalPos);
+ QMutableEventPoint::setScenePosition(pe->point(0), translatedScenePos);
+ auto *grabber = pe->exclusiveGrabber(pe->point(0));
+
+ if (grabber) {
+ // Temporarily disable the grabber, to stop the delivery agent inside
+ // targetWindow from forwarding the event to an item outside the menu
+ // or menubar. This is especially important to support a press on e.g
+ // a MenuBarItem, followed by a drag-and-release on top of a MenuItem.
+ pe->setExclusiveGrabber(pe->point(0), nullptr);
+ }
+
+ qCDebug(lcPopupWindow) << "forwarding" << pe << "to popup menu:" << targetWindow;
+ QQuickWindowPrivate::get(targetWindow)->deliveryAgent->event(pe);
+
+ // Restore the event before we return
+ QMutableEventPoint::setScenePosition(pe->point(0), scenePos);
+ if (grabber)
+ pe->setExclusiveGrabber(pe->point(0), grabber);
+ } else if (pe->isEndEvent()) {
+ // To support opening a Menu on press (e.g on a MenuBarItem), followed by
+ // a drag and release on a MenuItem inside the Menu, we ask the Menu to
+ // perform a click on the active MenuItem, if any.
+ if (targetPopupWindow)
+ QQuickPopupPrivate::get(targetPopupWindow->popup())->handleReleaseWithoutGrab(pe->point(0));
+ }
+}
+
+bool QQuickPopupWindow::event(QEvent *e)
+{
+ Q_D(QQuickPopupWindow);
+ d->forwardEventToParentMenuOrMenuBar(e);
+
+ return QQuickWindowQmlImpl::event(e);
+}
+
+void QQuickPopupWindow::windowChanged(QWindow *window)
+{
+ if (window) {
+ connect(window, &QWindow::xChanged, this, &QQuickPopupWindow::parentWindowXChanged);
+ connect(window, &QWindow::yChanged, this, &QQuickPopupWindow::parentWindowYChanged);
+ }
+}
+
+QPoint QQuickPopupWindow::global2Local(const QPoint &pos) const
+{
+ Q_D(const QQuickPopupWindow);
+ QQuickPopup *popup = d->m_popup;
+ Q_ASSERT(popup);
+ const QPoint scenePos = popup->window()->mapFromGlobal(pos);
+ // Popup's coordinates are relative to the nearest parent item.
+ return popup->parentItem() ? popup->parentItem()->mapFromScene(scenePos).toPoint() : scenePos;
+}
+
+void QQuickPopupWindow::parentWindowXChanged(int newX)
+{
+ const auto popupLocalPos = global2Local({x(), y()});
+ handlePopupPositionChangeFromWindowSystem({newX + popupLocalPos.x(), y()});
+}
+
+void QQuickPopupWindow::parentWindowYChanged(int newY)
+{
+ const auto popupLocalPos = global2Local({x(), y()});
+ handlePopupPositionChangeFromWindowSystem({x(), newY + popupLocalPos.y()});
+}
+
+void QQuickPopupWindow::handlePopupPositionChangeFromWindowSystem(const QPoint &pos)
+{
+ Q_D(QQuickPopupWindow);
+ QQuickPopup *popup = d->m_popup;
+ if (!popup)
+ return;
+ QQuickPopupPrivate *popupPrivate = QQuickPopupPrivate::get(popup);
+
+ const auto windowPos = global2Local(pos);
+ qCDebug(lcPopupWindow) << "A window system event changed the popup's position to be " << windowPos;
+ popupPrivate->setEffectivePosFromWindowPos(windowPos);
+}
+
+void QQuickPopupWindow::implicitWidthChanged()
+{
+ Q_D(const QQuickPopupWindow);
+ if (auto popup = d->m_popup)
+ setWidth(popup->implicitWidth());
+}
+
+void QQuickPopupWindow::implicitHeightChanged()
+{
+ Q_D(const QQuickPopupWindow);
+ if (auto popup = d->m_popup)
+ setHeight(popup->implicitHeight());
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/quicktemplates/qquickpopupwindow_p_p.h b/src/quicktemplates/qquickpopupwindow_p_p.h
new file mode 100644
index 0000000000..0b9842c059
--- /dev/null
+++ b/src/quicktemplates/qquickpopupwindow_p_p.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKPOPUPWINDOW_P_P_H
+#define QQUICKPOPUPWINDOW_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qquickwindowmodule_p.h>
+#include <QtQuickTemplates2/private/qtquicktemplates2global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPopup;
+class QQuickPopupWindowPrivate;
+
+class Q_QUICKTEMPLATES2_EXPORT QQuickPopupWindow : public QQuickWindowQmlImpl
+{
+ Q_OBJECT
+ QML_ANONYMOUS
+
+public:
+ explicit QQuickPopupWindow(QQuickPopup *popup, QWindow *parent = nullptr);
+ ~QQuickPopupWindow();
+ QQuickPopup *popup() const;
+
+protected:
+ void hideEvent(QHideEvent *e) override;
+ void moveEvent(QMoveEvent *e) override;
+ void resizeEvent(QResizeEvent *e) override;
+ bool event(QEvent *e) override;
+
+private:
+ void windowChanged(QWindow *window);
+ QPoint global2Local(const QPoint& pos) const;
+ void parentWindowXChanged(int newX);
+ void parentWindowYChanged(int newY);
+ void handlePopupPositionChangeFromWindowSystem(const QPoint &pos);
+ void implicitWidthChanged();
+ void implicitHeightChanged();
+
+ Q_DISABLE_COPY(QQuickPopupWindow)
+ Q_DECLARE_PRIVATE(QQuickPopupWindow)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOPUPWINDOW_P_P_H
diff --git a/src/quicktemplates/qquickpresshandler.cpp b/src/quicktemplates/qquickpresshandler.cpp
index d752bb36f1..fc569a81ca 100644
--- a/src/quicktemplates/qquickpresshandler.cpp
+++ b/src/quicktemplates/qquickpresshandler.cpp
@@ -19,7 +19,7 @@ void QQuickPressHandler::mousePressEvent(QMouseEvent *event)
pressPos = event->position();
if (Qt::LeftButton == (event->buttons() & Qt::LeftButton)) {
timer.start(QGuiApplication::styleHints()->mousePressAndHoldInterval(), control);
- delayedMousePressEvent = new QMouseEvent(event->type(), event->position().toPoint(), event->globalPosition().toPoint(),
+ delayedMousePressEvent = std::make_unique<QMouseEvent>(event->type(), event->position().toPoint(), event->globalPosition().toPoint(),
event->button(), event->buttons(), event->modifiers(), event->pointingDevice());
} else {
timer.stop();
@@ -86,10 +86,7 @@ QT_WARNING_POP
void QQuickPressHandler::clearDelayedMouseEvent()
{
- if (delayedMousePressEvent) {
- delete delayedMousePressEvent;
- delayedMousePressEvent = 0;
- }
+ delayedMousePressEvent.reset();
}
bool QQuickPressHandler::isActive()
diff --git a/src/quicktemplates/qquickpresshandler_p_p.h b/src/quicktemplates/qquickpresshandler_p_p.h
index adbed66a30..72d78aae66 100644
--- a/src/quicktemplates/qquickpresshandler_p_p.h
+++ b/src/quicktemplates/qquickpresshandler_p_p.h
@@ -19,6 +19,8 @@
#include <QtCore/qbasictimer.h>
#include <QtCore/private/qglobal_p.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QQuickItem;
@@ -44,7 +46,7 @@ struct QQuickPressHandler
int pressAndHoldSignalIndex = -1;
int pressedSignalIndex = -1;
int releasedSignalIndex = -1;
- QMouseEvent *delayedMousePressEvent = nullptr;
+ std::unique_ptr<QMouseEvent> delayedMousePressEvent = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickprogressbar.cpp b/src/quicktemplates/qquickprogressbar.cpp
index c42a62b4fe..77be68873c 100644
--- a/src/quicktemplates/qquickprogressbar.cpp
+++ b/src/quicktemplates/qquickprogressbar.cpp
@@ -65,6 +65,8 @@ public:
QQuickProgressBar::QQuickProgressBar(QQuickItem *parent)
: QQuickControl(*(new QQuickProgressBarPrivate), parent)
{
+ Q_D(QQuickProgressBar);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
/*!
diff --git a/src/quicktemplates/qquickprogressbar_p.h b/src/quicktemplates/qquickprogressbar_p.h
index 99e356b525..576515e4ad 100644
--- a/src/quicktemplates/qquickprogressbar_p.h
+++ b/src/quicktemplates/qquickprogressbar_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickProgressBarPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickProgressBar : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickProgressBar : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -74,6 +74,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickProgressBar)
-
#endif // QQUICKPROGRESSBAR_P_H
diff --git a/src/quicktemplates/qquickradiobutton.cpp b/src/quicktemplates/qquickradiobutton.cpp
index a943da9de6..c49bf3b1cd 100644
--- a/src/quicktemplates/qquickradiobutton.cpp
+++ b/src/quicktemplates/qquickradiobutton.cpp
@@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE
\sa ButtonGroup, {Customizing RadioButton}, {Button Controls}, RadioDelegate
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioButtonPrivate : public QQuickAbstractButtonPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickRadioButtonPrivate : public QQuickAbstractButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickRadioButton)
diff --git a/src/quicktemplates/qquickradiobutton_p.h b/src/quicktemplates/qquickradiobutton_p.h
index 8d70290270..d7f4c74c7b 100644
--- a/src/quicktemplates/qquickradiobutton_p.h
+++ b/src/quicktemplates/qquickradiobutton_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickRadioButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioButton : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickRadioButton : public QQuickAbstractButton
{
Q_OBJECT
QML_NAMED_ELEMENT(RadioButton)
@@ -41,6 +41,4 @@ protected:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRadioButton)
-
#endif // QQUICKRADIOBUTTON_P_H
diff --git a/src/quicktemplates/qquickradiodelegate.cpp b/src/quicktemplates/qquickradiodelegate.cpp
index 2c390ad487..7ec22a6301 100644
--- a/src/quicktemplates/qquickradiodelegate.cpp
+++ b/src/quicktemplates/qquickradiodelegate.cpp
@@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE
\sa {Customizing RadioDelegate}, {Delegate Controls}, RadioButton
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioDelegatePrivate : public QQuickItemDelegatePrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickRadioDelegatePrivate : public QQuickItemDelegatePrivate
{
Q_DECLARE_PUBLIC(QQuickRadioDelegate)
diff --git a/src/quicktemplates/qquickradiodelegate_p.h b/src/quicktemplates/qquickradiodelegate_p.h
index 574c87032c..337711c73f 100644
--- a/src/quicktemplates/qquickradiodelegate_p.h
+++ b/src/quicktemplates/qquickradiodelegate_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickRadioDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRadioDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickRadioDelegate : public QQuickItemDelegate
{
Q_OBJECT
QML_NAMED_ELEMENT(RadioDelegate)
@@ -43,6 +43,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRadioDelegate)
-
#endif // QQUICKRADIODELEGATE_P_H
diff --git a/src/quicktemplates/qquickrangeslider.cpp b/src/quicktemplates/qquickrangeslider.cpp
index ff15a9194e..d97dfdff90 100644
--- a/src/quicktemplates/qquickrangeslider.cpp
+++ b/src/quicktemplates/qquickrangeslider.cpp
@@ -117,8 +117,6 @@ void QQuickRangeSliderNodePrivate::updatePosition(bool ignoreOtherPosition)
setPosition(pos, ignoreOtherPosition);
}
-static inline QString handleName() { return QStringLiteral("handle"); }
-
void QQuickRangeSliderNodePrivate::cancelHandle()
{
Q_Q(QQuickRangeSliderNode);
@@ -227,6 +225,8 @@ void QQuickRangeSliderNode::setHandle(QQuickItem *handle)
if (d->handle == handle)
return;
+ QQuickControlPrivate::warnIfCustomizationNotSupported(d->slider, handle, QStringLiteral("handle"));
+
if (!d->handle.isExecuting())
d->cancelHandle();
@@ -356,6 +356,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
bool live = true;
qreal from = defaultFrom;
@@ -485,8 +486,14 @@ bool QQuickRangeSliderPrivate::handlePress(const QPointF &point, ulong timestamp
if (hitNode) {
hitNode->setPressed(true);
- if (QQuickItem *handle = hitNode->handle())
+ if (QQuickItem *handle = hitNode->handle()) {
handle->setZ(1);
+
+ // A specific handle was hit, so it should get focus, rather than the default
+ // (first handle) that gets focus whenever the RangeSlider itself does - see focusInEvent().
+ if (focusPolicy & Qt::ClickFocus)
+ handle->forceActiveFocus(Qt::MouseFocusReason);
+ }
QQuickRangeSliderNodePrivate::get(hitNode)->touchId = touchId;
}
if (otherNode) {
@@ -528,22 +535,21 @@ bool QQuickRangeSliderPrivate::handleRelease(const QPointF &point, ulong timesta
return true;
QQuickRangeSliderNodePrivate *pressedNodePrivate = QQuickRangeSliderNodePrivate::get(pressedNode);
- if (q->keepMouseGrab() || q->keepTouchGrab()) {
- const qreal oldPos = pressedNode->position();
- qreal pos = positionAt(q, pressedNode->handle(), point);
- if (snapMode != QQuickRangeSlider::NoSnap)
- pos = snapPosition(q, pos);
- qreal val = valueAt(q, pos);
- if (!qFuzzyCompare(val, pressedNode->value()))
- pressedNode->setValue(val);
- else if (snapMode != QQuickRangeSlider::NoSnap)
- pressedNodePrivate->setPosition(pos);
- q->setKeepMouseGrab(false);
- q->setKeepTouchGrab(false);
+ const qreal oldPos = pressedNode->position();
+ qreal pos = positionAt(q, pressedNode->handle(), point);
+ if (snapMode != QQuickRangeSlider::NoSnap)
+ pos = snapPosition(q, pos);
+ qreal val = valueAt(q, pos);
+ if (!qFuzzyCompare(val, pressedNode->value()))
+ pressedNode->setValue(val);
+ else if (snapMode != QQuickRangeSlider::NoSnap)
+ pressedNodePrivate->setPosition(pos);
+ q->setKeepMouseGrab(false);
+ q->setKeepTouchGrab(false);
+
+ if (!qFuzzyCompare(pressedNode->position(), oldPos))
+ emit pressedNode->moved();
- if (!qFuzzyCompare(pressedNode->position(), oldPos))
- emit pressedNode->moved();
- }
pressedNode->setPressed(false);
pressedNodePrivate->touchId = -1;
return true;
@@ -597,14 +603,29 @@ void QQuickRangeSliderPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit second->implicitHandleHeightChanged();
}
+void QQuickRangeSliderPrivate::itemDestroyed(QQuickItem *item)
+{
+ QQuickControlPrivate::itemDestroyed(item);
+ if (item == first->handle())
+ first->setHandle(nullptr);
+ else if (item == second->handle())
+ second->setHandle(nullptr);
+}
+
QQuickRangeSlider::QQuickRangeSlider(QQuickItem *parent)
: QQuickControl(*(new QQuickRangeSliderPrivate), parent)
{
Q_D(QQuickRangeSlider);
d->first = new QQuickRangeSliderNode(0.0, this);
d->second = new QQuickRangeSliderNode(1.0, this);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
setFlag(QQuickItem::ItemIsFocusScope);
+#ifdef Q_OS_MACOS
+ setFocusPolicy(Qt::TabFocus);
+#else
+ setFocusPolicy(Qt::StrongFocus);
+#endif
setAcceptedMouseButtons(Qt::LeftButton);
#if QT_CONFIG(quicktemplates2_multitouch)
setAcceptTouchEvents(true);
@@ -959,6 +980,11 @@ void QQuickRangeSlider::setOrientation(Qt::Orientation orientation)
if (d->orientation == orientation)
return;
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
d->orientation = orientation;
emit orientationChanged();
}
diff --git a/src/quicktemplates/qquickrangeslider_p.h b/src/quicktemplates/qquickrangeslider_p.h
index 577bbe165f..772e4abcda 100644
--- a/src/quicktemplates/qquickrangeslider_p.h
+++ b/src/quicktemplates/qquickrangeslider_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickRangeSliderPrivate;
class QQuickRangeSliderNode;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRangeSlider : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickRangeSlider : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -126,7 +126,7 @@ private:
class QQuickRangeSliderNodePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRangeSliderNode : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickRangeSliderNode : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
@@ -191,6 +191,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRangeSlider)
-
#endif // QQUICKRANGESLIDER_P_H
diff --git a/src/quicktemplates/qquickroundbutton_p.h b/src/quicktemplates/qquickroundbutton_p.h
index bf589b673e..9a30ebe9a8 100644
--- a/src/quicktemplates/qquickroundbutton_p.h
+++ b/src/quicktemplates/qquickroundbutton_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickRoundButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRoundButton : public QQuickButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickRoundButton : public QQuickButton
{
Q_OBJECT
Q_PROPERTY(qreal radius READ radius WRITE setRadius RESET resetRadius NOTIFY radiusChanged FINAL)
@@ -48,6 +48,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickRoundButton)
-
#endif // QQUICKROUNDBUTTON_P_H
diff --git a/src/quicktemplates/qquickscrollbar.cpp b/src/quicktemplates/qquickscrollbar.cpp
index 67fc8116a6..75de0363e9 100644
--- a/src/quicktemplates/qquickscrollbar.cpp
+++ b/src/quicktemplates/qquickscrollbar.cpp
@@ -122,9 +122,9 @@ QT_BEGIN_NAMESPACE
\sa ScrollIndicator, ScrollView, {Customizing ScrollBar}, {Indicator Controls}
*/
-static const QQuickItemPrivate::ChangeTypes changeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
-static const QQuickItemPrivate::ChangeTypes horizontalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitHeight;
-static const QQuickItemPrivate::ChangeTypes verticalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitWidth;
+static const QQuickItemPrivate::ChangeTypes QsbChangeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
+static const QQuickItemPrivate::ChangeTypes QsbHorizontalChangeTypes = QsbChangeTypes | QQuickItemPrivate::ImplicitHeight;
+static const QQuickItemPrivate::ChangeTypes QsbVerticalChangeTypes = QsbChangeTypes | QQuickItemPrivate::ImplicitWidth;
QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const
{
@@ -368,6 +368,7 @@ QQuickScrollBar::QQuickScrollBar(QQuickItem *parent)
Q_D(QQuickScrollBar);
d->decreaseVisual = new QQuickIndicatorButton(this);
d->increaseVisual = new QQuickIndicatorButton(this);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
setKeepMouseGrab(true);
setAcceptedMouseButtons(Qt::LeftButton);
#if QT_CONFIG(quicktemplates2_multitouch)
@@ -409,13 +410,11 @@ void QQuickScrollBar::setSize(qreal size)
size = qBound(0.0, size, 1.0);
if (qFuzzyCompare(d->size, size))
return;
- d->size = size;
- auto oldVisualArea = d->visualArea();
- d->size = qBound(0.0, size, 1.0);
+ const auto oldVisualArea = d->visualArea();
+ d->size = size;
if (d->size + d->position > 1.0) {
- setPosition(1.0 - d->size);
- oldVisualArea = d->visualArea();
+ d->setPosition(1.0 - d->size, false);
}
if (isComponentComplete())
@@ -450,15 +449,22 @@ qreal QQuickScrollBar::position() const
void QQuickScrollBar::setPosition(qreal position)
{
Q_D(QQuickScrollBar);
- if (!qt_is_finite(position) || qFuzzyCompare(d->position, position))
+ d->setPosition(position);
+}
+
+void QQuickScrollBarPrivate::setPosition(qreal newPosition, bool notifyVisualChange)
+{
+ Q_Q(QQuickScrollBar);
+ if (!qt_is_finite(newPosition) || qFuzzyCompare(position, newPosition))
return;
- auto oldVisualArea = d->visualArea();
- d->position = position;
- if (isComponentComplete())
- d->resizeContent();
- emit positionChanged();
- d->visualAreaChange(d->visualArea(), oldVisualArea);
+ auto oldVisualArea = visualArea();
+ position = newPosition;
+ if (q->isComponentComplete())
+ resizeContent();
+ emit q->positionChanged();
+ if (notifyVisualChange)
+ visualAreaChange(visualArea(), oldVisualArea);
}
/*!
@@ -567,6 +573,11 @@ void QQuickScrollBar::setOrientation(Qt::Orientation orientation)
if (d->orientation == orientation)
return;
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
d->orientation = orientation;
if (isComponentComplete())
d->resizeContent();
@@ -656,7 +667,17 @@ void QQuickScrollBar::resetInteractive()
The following example keeps the vertical scroll bar always visible:
- \snippet qtquickcontrols-scrollbar-policy.qml 1
+ \snippet qtquickcontrols-scrollbar-policy-alwayson.qml 1
+
+ Styles may use this property in combination with the \l active property
+ in order to implement transient scroll bars. Transient scroll bars are
+ hidden shortly after the last interaction event (hover or press). This
+ is typically done by animating the opacity of the scroll bar. To override
+ this behavior, set the policy to \c ScrollBar.AlwaysOn or
+ \c ScrollBar.AlwaysOff, depending on the size of the content compared to
+ its view. For example, for a vertical \l ListView:
+
+ \snippet qtquickcontrols-scrollbar-policy-alwayson-when-needed.qml 1
*/
QQuickScrollBar::Policy QQuickScrollBar::policy() const
{
@@ -735,6 +756,7 @@ void QQuickScrollBar::setMinimumSize(qreal minimumSize)
/*!
\since QtQuick.Controls 2.4 (Qt 5.11)
\qmlproperty real QtQuick.Controls::ScrollBar::visualSize
+ \readonly
This property holds the effective visual size of the scroll bar,
which may be limited by the \l {minimumSize}{minimum size}.
@@ -750,6 +772,7 @@ qreal QQuickScrollBar::visualSize() const
/*!
\since QtQuick.Controls 2.4 (Qt 5.11)
\qmlproperty real QtQuick.Controls::ScrollBar::visualPosition
+ \readonly
This property holds the effective visual position of the scroll bar,
which may be limited by the \l {minimumSize}{minimum size}.
@@ -942,11 +965,11 @@ void QQuickScrollBarAttachedPrivate::initHorizontal()
// If a scroll bar was previously hidden (due to e.g. setting a new contentItem
// on a ScrollView), we need to make sure that we un-hide it.
- // We don't bother checking if the item is actually the old one, because
- // if it's not, all of the things the function does (setting parent, visibility, etc.)
- // should be no-ops anyway.
- if (auto control = qobject_cast<QQuickControl*>(q_func()->parent()))
- QQuickControlPrivate::unhideOldItem(control, horizontal);
+ if (auto control = qobject_cast<QQuickControl*>(q_func()->parent())) {
+ const auto visibility = horizontal->policy() != QQuickScrollBar::AlwaysOff
+ ? QQuickControlPrivate::UnhideVisibility::Show : QQuickControlPrivate::UnhideVisibility::Hide;
+ QQuickControlPrivate::unhideOldItem(control, horizontal, visibility);
+ }
layoutHorizontal();
horizontal->setSize(area->property("widthRatio").toReal());
@@ -969,8 +992,11 @@ void QQuickScrollBarAttachedPrivate::initVertical()
if (parent && parent == flickable->parentItem())
vertical->stackAfter(flickable);
- if (auto control = qobject_cast<QQuickControl*>(q_func()->parent()))
- QQuickControlPrivate::unhideOldItem(control, vertical);
+ if (auto control = qobject_cast<QQuickControl*>(q_func()->parent())) {
+ const auto visibility = vertical->policy() != QQuickScrollBar::AlwaysOff
+ ? QQuickControlPrivate::UnhideVisibility::Show : QQuickControlPrivate::UnhideVisibility::Hide;
+ QQuickControlPrivate::unhideOldItem(control, vertical, visibility);
+ }
layoutVertical();
vertical->setSize(area->property("heightRatio").toReal());
@@ -1152,11 +1178,11 @@ QQuickScrollBarAttached::~QQuickScrollBarAttached()
{
Q_D(QQuickScrollBarAttached);
if (d->horizontal) {
- QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsbHorizontalChangeTypes);
d->horizontal = nullptr;
}
if (d->vertical) {
- QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsbVerticalChangeTypes);
d->vertical = nullptr;
}
d->setFlickable(nullptr);
@@ -1189,7 +1215,7 @@ void QQuickScrollBarAttached::setHorizontal(QQuickScrollBar *horizontal)
return;
if (d->horizontal) {
- QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsbHorizontalChangeTypes);
QObjectPrivate::disconnect(d->horizontal, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollHorizontal);
if (d->flickable)
@@ -1203,7 +1229,7 @@ void QQuickScrollBarAttached::setHorizontal(QQuickScrollBar *horizontal)
horizontal->setParentItem(qobject_cast<QQuickItem *>(parent()));
horizontal->setOrientation(Qt::Horizontal);
- QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, QsbHorizontalChangeTypes);
QObjectPrivate::connect(horizontal, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollHorizontal);
if (d->flickable)
@@ -1239,7 +1265,7 @@ void QQuickScrollBarAttached::setVertical(QQuickScrollBar *vertical)
return;
if (d->vertical) {
- QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsbVerticalChangeTypes);
QObjectPrivate::disconnect(d->vertical, &QQuickScrollBar::mirroredChanged, d, &QQuickScrollBarAttachedPrivate::mirrorVertical);
QObjectPrivate::disconnect(d->vertical, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollVertical);
@@ -1254,7 +1280,7 @@ void QQuickScrollBarAttached::setVertical(QQuickScrollBar *vertical)
vertical->setParentItem(qobject_cast<QQuickItem *>(parent()));
vertical->setOrientation(Qt::Vertical);
- QQuickItemPrivate::get(vertical)->addItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(vertical)->addItemChangeListener(d, QsbVerticalChangeTypes);
QObjectPrivate::connect(vertical, &QQuickScrollBar::mirroredChanged, d, &QQuickScrollBarAttachedPrivate::mirrorVertical);
QObjectPrivate::connect(vertical, &QQuickScrollBar::positionChanged, d, &QQuickScrollBarAttachedPrivate::scrollVertical);
diff --git a/src/quicktemplates/qquickscrollbar_p.h b/src/quicktemplates/qquickscrollbar_p.h
index 6e4eade1f8..b43cef1734 100644
--- a/src/quicktemplates/qquickscrollbar_p.h
+++ b/src/quicktemplates/qquickscrollbar_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QQuickScrollBarAttached;
class QQuickScrollBarPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollBar : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollBar : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged FINAL)
@@ -156,7 +156,7 @@ private:
class QQuickScrollBarAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollBarAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollBarAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickScrollBar *horizontal READ horizontal WRITE setHorizontal NOTIFY horizontalChanged FINAL)
@@ -183,6 +183,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScrollBar)
-
#endif // QQUICKSCROLLBAR_P_H
diff --git a/src/quicktemplates/qquickscrollbar_p_p.h b/src/quicktemplates/qquickscrollbar_p_p.h
index cad54882d2..2a834ce71e 100644
--- a/src/quicktemplates/qquickscrollbar_p_p.h
+++ b/src/quicktemplates/qquickscrollbar_p_p.h
@@ -45,6 +45,7 @@ public:
qreal logicalPosition(qreal position) const;
+ void setPosition(qreal position, bool notifyVisualChange = true);
qreal snapPosition(qreal position) const;
qreal positionAt(const QPointF &point) const;
void setInteractive(bool interactive);
diff --git a/src/quicktemplates/qquickscrollindicator.cpp b/src/quicktemplates/qquickscrollindicator.cpp
index 3a691852bc..9928503e86 100644
--- a/src/quicktemplates/qquickscrollindicator.cpp
+++ b/src/quicktemplates/qquickscrollindicator.cpp
@@ -93,9 +93,9 @@ QT_BEGIN_NAMESPACE
\sa ScrollBar, {Customizing ScrollIndicator}, {Indicator Controls}
*/
-static const QQuickItemPrivate::ChangeTypes changeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
-static const QQuickItemPrivate::ChangeTypes horizontalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitHeight;
-static const QQuickItemPrivate::ChangeTypes verticalChangeTypes = changeTypes | QQuickItemPrivate::ImplicitWidth;
+static const QQuickItemPrivate::ChangeTypes QsiChangeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
+static const QQuickItemPrivate::ChangeTypes QsiHorizontalChangeTypes = QsiChangeTypes | QQuickItemPrivate::ImplicitHeight;
+static const QQuickItemPrivate::ChangeTypes QsiVerticalChangeTypes = QsiChangeTypes | QQuickItemPrivate::ImplicitWidth;
class QQuickScrollIndicatorPrivate : public QQuickControlPrivate
{
@@ -168,6 +168,8 @@ void QQuickScrollIndicatorPrivate::resizeContent()
QQuickScrollIndicator::QQuickScrollIndicator(QQuickItem *parent)
: QQuickControl(*(new QQuickScrollIndicatorPrivate), parent)
{
+ Q_D(QQuickScrollIndicator);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
}
QQuickScrollIndicatorAttached *QQuickScrollIndicator::qmlAttachedProperties(QObject *object)
@@ -295,6 +297,11 @@ void QQuickScrollIndicator::setOrientation(Qt::Orientation orientation)
if (d->orientation == orientation)
return;
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
d->orientation = orientation;
if (isComponentComplete())
d->resizeContent();
@@ -496,9 +503,9 @@ QQuickScrollIndicatorAttached::~QQuickScrollIndicatorAttached()
Q_D(QQuickScrollIndicatorAttached);
if (d->flickable) {
if (d->horizontal)
- QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsiHorizontalChangeTypes);
if (d->vertical)
- QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d,verticalChangeTypes);
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsiVerticalChangeTypes);
// NOTE: Use removeItemChangeListener(Geometry) instead of updateOrRemoveGeometryChangeListener(Size).
// The latter doesn't remove the listener but only resets its types. Thus, it leaves behind a dangling
// pointer on destruction.
@@ -533,7 +540,7 @@ void QQuickScrollIndicatorAttached::setHorizontal(QQuickScrollIndicator *horizon
return;
if (d->horizontal && d->flickable) {
- QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, QsiHorizontalChangeTypes);
QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateHorizontal);
// TODO: export QQuickFlickableVisibleArea
@@ -549,7 +556,7 @@ void QQuickScrollIndicatorAttached::setHorizontal(QQuickScrollIndicator *horizon
horizontal->setParentItem(d->flickable);
horizontal->setOrientation(Qt::Horizontal);
- QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, horizontalChangeTypes);
+ QQuickItemPrivate::get(horizontal)->addItemChangeListener(d, QsiHorizontalChangeTypes);
QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingHorizontallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateHorizontal);
// TODO: export QQuickFlickableVisibleArea
@@ -591,7 +598,7 @@ void QQuickScrollIndicatorAttached::setVertical(QQuickScrollIndicator *vertical)
return;
if (d->vertical && d->flickable) {
- QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d, QsiVerticalChangeTypes);
QObjectPrivate::disconnect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateVertical);
// TODO: export QQuickFlickableVisibleArea
@@ -607,7 +614,7 @@ void QQuickScrollIndicatorAttached::setVertical(QQuickScrollIndicator *vertical)
vertical->setParentItem(d->flickable);
vertical->setOrientation(Qt::Vertical);
- QQuickItemPrivate::get(vertical)->addItemChangeListener(d, verticalChangeTypes);
+ QQuickItemPrivate::get(vertical)->addItemChangeListener(d, QsiVerticalChangeTypes);
QObjectPrivate::connect(d->flickable, &QQuickFlickable::movingVerticallyChanged, d, &QQuickScrollIndicatorAttachedPrivate::activateVertical);
// TODO: export QQuickFlickableVisibleArea
diff --git a/src/quicktemplates/qquickscrollindicator_p.h b/src/quicktemplates/qquickscrollindicator_p.h
index 1af6407690..e6647c77ab 100644
--- a/src/quicktemplates/qquickscrollindicator_p.h
+++ b/src/quicktemplates/qquickscrollindicator_p.h
@@ -23,7 +23,7 @@ class QQuickFlickable;
class QQuickScrollIndicatorAttached;
class QQuickScrollIndicatorPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollIndicator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollIndicator : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged FINAL)
@@ -96,7 +96,7 @@ private:
class QQuickScrollIndicatorAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollIndicatorAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollIndicatorAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickScrollIndicator *horizontal READ horizontal WRITE setHorizontal NOTIFY horizontalChanged FINAL)
@@ -123,6 +123,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScrollIndicator)
-
#endif // QQUICKSCROLLINDICATOR_P_H
diff --git a/src/quicktemplates/qquickscrollview.cpp b/src/quicktemplates/qquickscrollview.cpp
index 8a352b7f61..5f7c7bf2fd 100644
--- a/src/quicktemplates/qquickscrollview.cpp
+++ b/src/quicktemplates/qquickscrollview.cpp
@@ -98,11 +98,17 @@ public:
QQmlListProperty<QObject> contentData() override;
QQmlListProperty<QQuickItem> contentChildren() override;
QList<QQuickItem *> contentChildItems() const override;
+ QQuickItem* getFirstChild() const override;
QQuickItem *getContentItem() override;
- QQuickFlickable *ensureFlickable(bool content);
- bool setFlickable(QQuickFlickable *flickable, bool content);
+ enum class ContentItemFlag {
+ DoNotSet,
+ Set
+ };
+
+ QQuickFlickable *ensureFlickable(ContentItemFlag contentItemFlag);
+ bool setFlickable(QQuickFlickable *flickable, ContentItemFlag contentItemFlag);
void flickableContentWidthChanged();
void flickableContentHeightChanged();
@@ -127,10 +133,17 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
+ void updateScrollBarWidth();
+ void updateScrollBarHeight();
+
+ void disconnectScrollBarSignals(QQuickScrollBarAttachedPrivate *scrollBar);
bool wasTouched = false;
QQuickFlickable *flickable = nullptr;
bool flickableHasExplicitContentWidth = true;
bool flickableHasExplicitContentHeight = true;
+ bool isUpdatingScrollBar = false;
+ qreal effectiveScrollBarWidth = 0;
+ qreal effectiveScrollBarHeight = 0;
};
QList<QQuickItem *> QQuickScrollViewPrivate::contentChildItems() const
@@ -145,15 +158,23 @@ QQuickItem *QQuickScrollViewPrivate::getContentItem()
{
if (!contentItem)
executeContentItem();
- return ensureFlickable(false);
+ // This function is called by QQuickControl::contentItem() to lazily create
+ // a contentItem, so we don't need to try to set it again.
+ return ensureFlickable(ContentItemFlag::DoNotSet);
}
-QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(bool content)
+QQuickItem* QQuickScrollViewPrivate::getFirstChild() const
+{
+ return contentChildItems().value(0);
+}
+
+QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(ContentItemFlag contentItemFlag)
{
Q_Q(QQuickScrollView);
if (!flickable) {
flickableHasExplicitContentWidth = false;
flickableHasExplicitContentHeight = false;
+ // Pass ourselves as the Flickable's parent item.
auto flickable = new QQuickFlickable(q);
// We almost always want to clip the flickable so that flickable
// contents doesn't show up outside the scrollview. The only time
@@ -163,12 +184,64 @@ QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(bool content)
// child inside the scrollview, and control clipping on it explicit.
flickable->setClip(true);
flickable->setPixelAligned(true);
- setFlickable(flickable, content);
+ setFlickable(flickable, contentItemFlag);
}
return flickable;
}
-bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
+void QQuickScrollViewPrivate::updateScrollBarWidth()
+{
+ Q_Q(QQuickScrollView);
+ qreal oldEffectiveScrollBarWidth = effectiveScrollBarWidth;
+ if (auto *vBar = verticalScrollBar()) {
+ if (vBar->policy() == QQuickScrollBar::AlwaysOff || !vBar->isVisible())
+ effectiveScrollBarWidth = 0;
+ else
+ effectiveScrollBarWidth = vBar->width();
+ }
+ if (effectiveScrollBarWidth != oldEffectiveScrollBarWidth) {
+ if (!isUpdatingScrollBar) {
+ QScopedValueRollback<bool> rollback(isUpdatingScrollBar, true);
+ emit q->effectiveScrollBarWidthChanged();
+ }
+ }
+}
+
+void QQuickScrollViewPrivate::updateScrollBarHeight()
+{
+ Q_Q(QQuickScrollView);
+ qreal oldEffectiveScrollBarHeight = effectiveScrollBarHeight;
+ if (auto *hBar = horizontalScrollBar()) {
+ if (hBar->policy() == QQuickScrollBar::AlwaysOff || !hBar->isVisible())
+ effectiveScrollBarHeight = 0;
+ else
+ effectiveScrollBarHeight = hBar->height();
+ }
+ if (effectiveScrollBarHeight != oldEffectiveScrollBarHeight) {
+ if (!isUpdatingScrollBar) {
+ QScopedValueRollback<bool> rollback(isUpdatingScrollBar, true);
+ emit q->effectiveScrollBarHeightChanged();
+ }
+
+ }
+}
+
+void QQuickScrollViewPrivate::disconnectScrollBarSignals(QQuickScrollBarAttachedPrivate *scrollBar)
+{
+ if (!scrollBar)
+ return;
+
+ if (scrollBar->vertical) {
+ QObjectPrivate::disconnect(scrollBar->vertical, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ QObjectPrivate::disconnect(scrollBar->vertical, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ }
+ if (scrollBar->horizontal) {
+ QObjectPrivate::disconnect(scrollBar->horizontal, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ QObjectPrivate::disconnect(scrollBar->horizontal, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ }
+}
+
+bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, ContentItemFlag contentItemFlag)
{
Q_Q(QQuickScrollView);
if (item == flickable)
@@ -179,8 +252,11 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
if (flickable) {
flickable->removeEventFilter(q);
- if (attached)
- QQuickScrollBarAttachedPrivate::get(attached)->setFlickable(nullptr);
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ scrollBar->setFlickable(nullptr);
+ disconnectScrollBarSignals(scrollBar);
+ }
QObjectPrivate::disconnect(flickable->contentItem(), &QQuickItem::childrenChanged, this, &QQuickPanePrivate::contentChildrenChange);
QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickScrollViewPrivate::flickableContentWidthChanged);
@@ -188,7 +264,7 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
}
flickable = item;
- if (content)
+ if (contentItemFlag == ContentItemFlag::Set)
q->setContentItem(flickable);
if (flickable) {
@@ -202,8 +278,18 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
else
flickableContentHeightChanged();
- if (attached)
- QQuickScrollBarAttachedPrivate::get(attached)->setFlickable(flickable);
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ scrollBar->setFlickable(flickable);
+ if (scrollBar->vertical) {
+ QObjectPrivate::connect(scrollBar->vertical, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ QObjectPrivate::connect(scrollBar->vertical, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ }
+ if (scrollBar->horizontal) {
+ QObjectPrivate::connect(scrollBar->horizontal, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ QObjectPrivate::connect(scrollBar->horizontal, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ }
+ }
QObjectPrivate::connect(flickable->contentItem(), &QQuickItem::childrenChanged, this, &QQuickPanePrivate::contentChildrenChange);
QObjectPrivate::connect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickScrollViewPrivate::flickableContentWidthChanged);
@@ -305,11 +391,14 @@ void QQuickScrollViewPrivate::setScrollBarsInteractive(bool interactive)
void QQuickScrollViewPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
{
QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
- if (!p->flickable && p->setFlickable(qobject_cast<QQuickFlickable *>(obj), true))
+ // If we don't yet have a flickable assigned, and this object is a Flickable,
+ // make it our contentItem.
+ if (!p->flickable && p->setFlickable(qobject_cast<QQuickFlickable *>(obj), ContentItemFlag::Set))
return;
- QQuickFlickable *flickable = p->ensureFlickable(true);
+ QQuickFlickable *flickable = p->ensureFlickable(ContentItemFlag::Set);
Q_ASSERT(flickable);
+ // Add the object that was declared as a child of us as a child object of the Flickable.
QQmlListProperty<QObject> data = flickable->flickableData();
data.append(&data, obj);
}
@@ -348,10 +437,11 @@ void QQuickScrollViewPrivate::contentChildren_append(QQmlListProperty<QQuickItem
{
QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
if (!p->flickable)
- p->setFlickable(qobject_cast<QQuickFlickable *>(item), true);
+ p->setFlickable(qobject_cast<QQuickFlickable *>(item), ContentItemFlag::Set);
- QQuickFlickable *flickable = p->ensureFlickable(true);
+ QQuickFlickable *flickable = p->ensureFlickable(ContentItemFlag::Set);
Q_ASSERT(flickable);
+ // Add the item that was declared as a child of us as a child item of the Flickable's contentItem.
QQmlListProperty<QQuickItem> children = flickable->flickableChildren();
children.append(&children, item);
}
@@ -406,6 +496,48 @@ QQuickScrollView::QQuickScrollView(QQuickItem *parent)
setWheelEnabled(true);
}
+QQuickScrollView::~QQuickScrollView()
+{
+ Q_D(QQuickScrollView);
+ QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(qmlAttachedPropertiesObject<QQuickScrollBar>(this, false));
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ d->disconnectScrollBarSignals(scrollBar);
+ }
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollView::effectiveScrollBarWidth
+ \since 6.6
+
+ This property holds the effective width of the vertical scrollbar.
+ When the scrollbar policy is \c QQuickScrollBar::AlwaysOff or the scrollbar
+ is not visible, this property is \c 0.
+
+ \sa {ScrollBar::policy}
+*/
+qreal QQuickScrollView::effectiveScrollBarWidth()
+{
+ Q_D(QQuickScrollView);
+ return d->effectiveScrollBarWidth;
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollView::effectiveScrollBarHeight
+ \since 6.6
+
+ This property holds the effective height of the horizontal scrollbar.
+ When the scrollbar policy is \c QQuickScrollBar::AlwaysOff or the scrollbar
+ is not visible, this property is \c 0.
+
+ \sa {ScrollBar::policy}
+*/
+qreal QQuickScrollView::effectiveScrollBarHeight()
+{
+ Q_D(QQuickScrollView);
+ return d->effectiveScrollBarHeight;
+}
+
/*!
\qmlproperty list<QtObject> QtQuick.Controls::ScrollView::contentData
\qmldefault
@@ -543,7 +675,7 @@ void QQuickScrollView::componentComplete()
Q_D(QQuickScrollView);
QQuickPane::componentComplete();
if (!d->contentItem)
- d->ensureFlickable(true);
+ d->ensureFlickable(QQuickScrollViewPrivate::ContentItemFlag::Set);
}
void QQuickScrollView::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
@@ -557,7 +689,25 @@ void QQuickScrollView::contentItemChange(QQuickItem *newItem, QQuickItem *oldIte
auto newItemAsFlickable = qobject_cast<QQuickFlickable *>(newItem);
if (newItem && !newItemAsFlickable)
qmlWarning(this) << "ScrollView only supports Flickable types as its contentItem";
- d->setFlickable(newItemAsFlickable, false);
+ // This is called by QQuickControlPrivate::setContentItem_helper, so no need to
+ // try to set it as the contentItem.
+ d->setFlickable(newItemAsFlickable, QQuickScrollViewPrivate::ContentItemFlag::DoNotSet);
+ // We do, however, need to set us as its parent item, as setContentItem_helper will only
+ // do so if the item doesn't already have a parent. If newItem wasn't declared as our
+ // child and was instead imperatively assigned, it may already have a parent item,
+ // which we'll need to override.
+ if (newItem) {
+ newItem->setParentItem(this);
+
+ // Make sure that the scroll bars are stacked in front of the flickable,
+ // otherwise events won't get through to them.
+ QQuickScrollBar *verticalBar = d->verticalScrollBar();
+ if (verticalBar)
+ verticalBar->stackAfter(newItem);
+ QQuickScrollBar *horizontalBar = d->horizontalScrollBar();
+ if (horizontalBar)
+ horizontalBar->stackAfter(newItem);
+ }
}
QQuickPane::contentItemChange(newItem, oldItem);
}
@@ -573,10 +723,14 @@ void QQuickScrollView::contentSizeChange(const QSizeF &newSize, const QSizeF &ol
// exception is if the application has assigned a content size
// directly to the scrollview, which will then win even if the
// application has assigned something else to the flickable.
- if (d->hasContentWidth || !d->flickableHasExplicitContentWidth)
+ if (d->hasContentWidth || !d->flickableHasExplicitContentWidth) {
d->flickable->setContentWidth(newSize.width());
- if (d->hasContentHeight || !d->flickableHasExplicitContentHeight)
+ d->updateScrollBarWidth();
+ }
+ if (d->hasContentHeight || !d->flickableHasExplicitContentHeight) {
d->flickable->setContentHeight(newSize.height());
+ d->updateScrollBarHeight();
+ }
}
}
diff --git a/src/quicktemplates/qquickscrollview_p.h b/src/quicktemplates/qquickscrollview_p.h
index 927de4365c..6cdafd5199 100644
--- a/src/quicktemplates/qquickscrollview_p.h
+++ b/src/quicktemplates/qquickscrollview_p.h
@@ -22,14 +22,19 @@ QT_BEGIN_NAMESPACE
class QQuickScrollViewPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollView : public QQuickPane
+class Q_QUICKTEMPLATES2_EXPORT QQuickScrollView : public QQuickPane
{
Q_OBJECT
QML_NAMED_ELEMENT(ScrollView)
QML_ADDED_IN_VERSION(2, 2)
+ Q_PROPERTY(qreal effectiveScrollBarWidth READ effectiveScrollBarWidth NOTIFY effectiveScrollBarWidthChanged FINAL REVISION(6, 6))
+ Q_PROPERTY(qreal effectiveScrollBarHeight READ effectiveScrollBarHeight NOTIFY effectiveScrollBarHeightChanged FINAL REVISION(6, 6))
public:
explicit QQuickScrollView(QQuickItem *parent = nullptr);
+ ~QQuickScrollView();
+ qreal effectiveScrollBarWidth();
+ qreal effectiveScrollBarHeight();
protected:
bool childMouseEventFilter(QQuickItem *item, QEvent *event) override;
@@ -44,6 +49,10 @@ protected:
QAccessible::Role accessibleRole() const override;
#endif
+Q_SIGNALS:
+ Q_REVISION(6, 6) void effectiveScrollBarWidthChanged();
+ Q_REVISION(6, 6) void effectiveScrollBarHeightChanged();
+
private:
Q_DISABLE_COPY(QQuickScrollView)
Q_DECLARE_PRIVATE(QQuickScrollView)
@@ -51,6 +60,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickScrollView)
-
#endif // QQUICKSCROLLVIEW_P_H
diff --git a/src/quicktemplates/qquickselectionrectangle.cpp b/src/quicktemplates/qquickselectionrectangle.cpp
index a2adc43dcd..5f8dff758e 100644
--- a/src/quicktemplates/qquickselectionrectangle.cpp
+++ b/src/quicktemplates/qquickselectionrectangle.cpp
@@ -81,7 +81,7 @@ QT_BEGIN_NAMESPACE
The handle is not hidden by default when a selection is removed.
Instead, this is the responsibility of the delegate, to open up for
custom fade-out animations. The easiest way to ensure that the handle
- ends up hidden, is to simply bind \l visible to the the \l active
+ ends up hidden, is to simply bind \l {Item::}{visible} to the \l active
state of the SelectionRectangle:
\qml
@@ -109,7 +109,7 @@ QT_BEGIN_NAMESPACE
The handle is not hidden by default when a selection is removed.
Instead, this is the responsibility of the delegate, to open up for
custom fade-out animations. The easiest way to ensure that the handle
- ends up hidden, is to simply bind \l visible to the the \l active
+ ends up hidden, is to simply bind \l {Item::}{visible} to the \l active
state of the SelectionRectangle:
\qml
@@ -144,14 +144,14 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \qmlattachedproperty SelectionRectangle QtQuick::SelectionRectangle::control
+ \qmlattachedproperty SelectionRectangle QtQuick.Controls::SelectionRectangle::control
This attached property holds the SelectionRectangle that manages the delegate instance.
It is attached to each handle instance.
*/
/*!
- \qmlattachedproperty bool QtQuick::SelectionRectangle::dragging
+ \qmlattachedproperty bool QtQuick.Controls::SelectionRectangle::dragging
This attached property will be \c true if the user is dragging on the handle.
It is attached to each handle instance.
@@ -170,56 +170,108 @@ QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate()
else
m_selectable->setSelectionEndPos(m_scrollToPoint);
updateHandles();
- const QSizeF dist = m_selectable->scrollTowardsSelectionPoint(m_scrollToPoint, m_scrollSpeed);
+ const QSizeF dist = m_selectable->scrollTowardsPoint(m_scrollToPoint, m_scrollSpeed);
m_scrollToPoint.rx() += dist.width() > 0 ? m_scrollSpeed.width() : -m_scrollSpeed.width();
m_scrollToPoint.ry() += dist.height() > 0 ? m_scrollSpeed.height() : -m_scrollSpeed.height();
m_scrollSpeed = QSizeF(qAbs(dist.width() * 0.007), qAbs(dist.height() * 0.007));
});
- QObject::connect(m_tapHandler, &QQuickTapHandler::tapped, [this] {
- updateActiveState(false);
- });
+ QObject::connect(m_tapHandler, &QQuickTapHandler::pressedChanged, [this]() {
+ if (!m_tapHandler->isPressed())
+ return;
+ if (m_effectiveSelectionMode != QQuickSelectionRectangle::Drag)
+ return;
- QObject::connect(m_tapHandler, &QQuickTapHandler::longPressed, [this]() {
const QPointF pos = m_tapHandler->point().pressPosition();
const auto modifiers = m_tapHandler->point().modifiers();
+ if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier))
+ return;
+
+ if (modifiers & Qt::ShiftModifier) {
+ // Extend the selection towards the pressed cell. If there is no
+ // existing selection, start a new selection from the current item
+ // to the pressed item.
+ if (!m_active) {
+ if (!m_selectable->startSelection(pos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(QPoint{-1, -1});
+ }
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ } else if (modifiers & Qt::ControlModifier) {
+ // Select a single cell, but keep the old selection (unless
+ // m_selectable->startSelection(pos. modifiers) returns false, which
+ // it will if selectionMode only allows a single selection).
+ if (handleUnderPos(pos) != nullptr) {
+ // Don't allow press'n'hold to start a new
+ // selection if it started on top of a handle.
+ return;
+ }
- if (!m_selectable->startSelection(pos))
+ if (!m_selectable->startSelection(pos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(pos);
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ }
+ });
+
+ QObject::connect(m_tapHandler, &QQuickTapHandler::longPressed, [this]() {
+ if (m_effectiveSelectionMode != QQuickSelectionRectangle::PressAndHold)
return;
+
+ const QPointF pos = m_tapHandler->point().pressPosition();
+ const auto modifiers = m_tapHandler->point().modifiers();
if (handleUnderPos(pos) != nullptr) {
// Don't allow press'n'hold to start a new
// selection if it started on top of a handle.
return;
}
- if (!m_alwaysAcceptPressAndHold) {
- if (m_selectionMode == QQuickSelectionRectangle::Auto) {
- // In Auto mode, we only accept press and hold from touch
- if (m_tapHandler->point().device()->pointerType() != QPointingDevice::PointerType::Finger)
+
+ if (modifiers == Qt::ShiftModifier) {
+ // Extend the selection towards the pressed cell. If there is no
+ // existing selection, start a new selection from the current item
+ // to the pressed item.
+ if (!m_active) {
+ if (!m_selectable->startSelection(pos, modifiers))
return;
- } else if (m_selectionMode != QQuickSelectionRectangle::PressAndHold) {
- return;
+ m_selectable->setSelectionStartPos(QPoint{-1, -1});
}
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
+ } else {
+ // Select a single cell. m_selectable->startSelection() will decide
+ // if the existing selection should also be cleared.
+ if (!m_selectable->startSelection(pos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(pos);
+ m_selectable->setSelectionEndPos(pos);
+ updateHandles();
+ updateActiveState(true);
}
-
- if (!modifiers.testFlag(Qt::ShiftModifier))
- m_selectable->clearSelection();
- m_selectable->setSelectionStartPos(pos);
- m_selectable->setSelectionEndPos(pos);
- updateHandles();
- updateActiveState(true);
});
QObject::connect(m_dragHandler, &QQuickDragHandler::activeChanged, [this]() {
+ Q_ASSERT(m_effectiveSelectionMode == QQuickSelectionRectangle::Drag);
const QPointF startPos = m_dragHandler->centroid().pressPosition();
const QPointF dragPos = m_dragHandler->centroid().position();
const auto modifiers = m_dragHandler->centroid().modifiers();
+ if (modifiers & ~(Qt::ControlModifier | Qt::ShiftModifier))
+ return;
if (m_dragHandler->active()) {
- if (!m_selectable->startSelection(startPos))
- return;
- if (!modifiers.testFlag(Qt::ShiftModifier))
- m_selectable->clearSelection();
- m_selectable->setSelectionStartPos(startPos);
+ // Start a new selection unless there is an active selection
+ // already, and one of the relevant modifiers are being held.
+ // In that case we continue to extend the active selection instead.
+ const bool modifiersHeld = modifiers & (Qt::ControlModifier | Qt::ShiftModifier);
+ if (!m_active || !modifiersHeld) {
+ if (!m_selectable->startSelection(startPos, modifiers))
+ return;
+ m_selectable->setSelectionStartPos(startPos);
+ }
m_selectable->setSelectionEndPos(dragPos);
m_draggedHandle = nullptr;
updateHandles();
@@ -248,7 +300,7 @@ void QQuickSelectionRectanglePrivate::scrollTowardsPos(const QPointF &pos)
if (m_scrollTimer.isActive())
return;
- const QSizeF dist = m_selectable->scrollTowardsSelectionPoint(m_scrollToPoint, m_scrollSpeed);
+ const QSizeF dist = m_selectable->scrollTowardsPoint(m_scrollToPoint, m_scrollSpeed);
if (!dist.isNull())
m_scrollTimer.start(1);
}
@@ -409,6 +461,25 @@ void QQuickSelectionRectanglePrivate::connectToTarget()
if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
connect(flickable, &QQuickFlickable::interactiveChanged, this, &QQuickSelectionRectanglePrivate::updateSelectionMode);
}
+
+ // Add a callback function that tells if the selection was
+ // modified outside of the actions taken by SelectionRectangle.
+ m_selectable->setCallback([this](QQuickSelectable::CallBackFlag flag){
+ switch (flag) {
+ case QQuickSelectable::CallBackFlag::CancelSelection:
+ // The selection is either cleared, or can no longer be
+ // represented as a rectangle with two selection handles.
+ updateActiveState(false);
+ break;
+ case QQuickSelectable::CallBackFlag::SelectionRectangleChanged:
+ // The selection has changed, but the selection is still
+ // rectangular and without holes.
+ updateHandles();
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ });
}
void QQuickSelectionRectanglePrivate::updateSelectionMode()
@@ -419,26 +490,33 @@ void QQuickSelectionRectanglePrivate::updateSelectionMode()
m_tapHandler->setEnabled(enabled);
if (m_selectionMode == QQuickSelectionRectangle::Auto) {
- if (qobject_cast<QQuickScrollView *>(m_target->parentItem())) {
+ if (m_target && qobject_cast<QQuickScrollView *>(m_target->parentItem())) {
// ScrollView allows flicking with touch, but not with mouse. So we do
// the same here: you can drag to select with a mouse, but not with touch.
+ m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
m_dragHandler->setEnabled(enabled);
} else if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
- m_dragHandler->setEnabled(enabled && !flickable->isInteractive());
+ if (enabled && !flickable->isInteractive()) {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
+ m_dragHandler->setEnabled(true);
+ } else {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold;
+ m_dragHandler->setEnabled(false);
+ }
} else {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::Mouse);
m_dragHandler->setEnabled(enabled);
}
} else if (m_selectionMode == QQuickSelectionRectangle::Drag) {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
m_dragHandler->setAcceptedDevices(QInputDevice::DeviceType::AllDevices);
m_dragHandler->setEnabled(enabled);
} else {
+ m_effectiveSelectionMode = QQuickSelectionRectangle::PressAndHold;
m_dragHandler->setEnabled(false);
}
-
- // If you can't select using a drag, we always accept a PressAndHold
- m_alwaysAcceptPressAndHold = !m_dragHandler->enabled();
}
QQuickSelectionRectangleAttached *QQuickSelectionRectanglePrivate::getAttachedObject(const QObject *object) const
@@ -480,6 +558,7 @@ void QQuickSelectionRectangle::setTarget(QQuickItem *target)
d->m_tapHandler->setParent(this);
d->m_dragHandler->setParent(this);
d->m_target->disconnect(this);
+ d->m_selectable->setCallback(nullptr);
}
d->m_target = target;
diff --git a/src/quicktemplates/qquickselectionrectangle_p.h b/src/quicktemplates/qquickselectionrectangle_p.h
index 2cf3c22062..5a1cf7e041 100644
--- a/src/quicktemplates/qquickselectionrectangle_p.h
+++ b/src/quicktemplates/qquickselectionrectangle_p.h
@@ -18,13 +18,15 @@
#include <QtQuick/qquickitem.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQuickSelectionRectanglePrivate;
class QQuickSelectable;
class QQuickSelectionRectangleAttached;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSelectionRectangle : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickSelectionRectangle : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged FINAL)
@@ -77,7 +79,7 @@ private:
Q_DECLARE_PRIVATE(QQuickSelectionRectangle)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSelectionRectangleAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSelectionRectangleAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickSelectionRectangle *control READ control NOTIFY controlChanged FINAL)
@@ -105,6 +107,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSelectionRectangle)
-
#endif // QQUICKSELECTIONRECTANGLE_P_H
diff --git a/src/quicktemplates/qquickselectionrectangle_p_p.h b/src/quicktemplates/qquickselectionrectangle_p_p.h
index 4144550aa7..d053b8bf42 100644
--- a/src/quicktemplates/qquickselectionrectangle_p_p.h
+++ b/src/quicktemplates/qquickselectionrectangle_p_p.h
@@ -66,7 +66,7 @@ public:
QSizeF m_scrollSpeed = QSizeF(1, 1);
QQuickSelectionRectangle::SelectionMode m_selectionMode = QQuickSelectionRectangle::Auto;
- bool m_alwaysAcceptPressAndHold = false;
+ QQuickSelectionRectangle::SelectionMode m_effectiveSelectionMode = QQuickSelectionRectangle::Drag;
bool m_enabled = true;
bool m_active = false;
diff --git a/src/quicktemplates/qquickshortcutcontext.cpp b/src/quicktemplates/qquickshortcutcontext.cpp
index 2d07eb44f9..503eb270a7 100644
--- a/src/quicktemplates/qquickshortcutcontext.cpp
+++ b/src/quicktemplates/qquickshortcutcontext.cpp
@@ -4,8 +4,11 @@
#include "qquickshortcutcontext_p_p.h"
#include "qquickoverlay_p_p.h"
#include "qquicktooltip_p.h"
+#include <QtQmlModels/private/qtqmlmodels-config_p.h>
+#if QT_CONFIG(qml_object_model)
#include "qquickmenu_p.h"
#include "qquickmenu_p_p.h"
+#endif
#include "qquickpopup_p.h"
#include <QtCore/qloggingcategory.h>
@@ -27,6 +30,9 @@ static bool isBlockedByPopup(QQuickItem *item)
if (qobject_cast<QQuickToolTip *>(popup))
continue; // ignore tooltips (QTBUG-60492)
if (popup->isModal() || popup->closePolicy() & QQuickPopup::CloseOnEscape) {
+ qCDebug(lcContextMatcher) << popup << "is modal or has a CloseOnEscape policy;"
+ << "if the following are both true," << item << "will be blocked by it:"
+ << (item != popup->popupItem()) << !popup->popupItem()->isAncestorOf(item);
return item != popup->popupItem() && !popup->popupItem()->isAncestorOf(item);
}
}
@@ -50,6 +56,7 @@ bool QQuickShortcutContext::matcher(QObject *obj, Qt::ShortcutContext context)
obj = popup->window();
item = popup->popupItem();
+#if QT_CONFIG(qml_object_model)
if (!obj) {
// The popup has no associated window (yet). However, sub-menus,
// unlike top-level menus, will not have an associated window
@@ -61,15 +68,16 @@ bool QQuickShortcutContext::matcher(QObject *obj, Qt::ShortcutContext context)
obj = parentMenu->window();
}
}
+#endif
break;
}
obj = obj->parent();
}
if (QWindow *renderWindow = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow *>(obj)))
obj = renderWindow;
- qCDebug(lcContextMatcher) << "obj" << obj << "focusWindow" << QGuiApplication::focusWindow()
+ qCDebug(lcContextMatcher) << "obj" << obj << "item" << item << "focusWindow" << QGuiApplication::focusWindow()
<< "!isBlockedByPopup(item)" << !isBlockedByPopup(item);
- return obj && obj == QGuiApplication::focusWindow() && !isBlockedByPopup(item);
+ return obj && qobject_cast<QWindow*>(obj)->isActive() && !isBlockedByPopup(item);
default:
return false;
}
diff --git a/src/quicktemplates/qquickshortcutcontext_p_p.h b/src/quicktemplates/qquickshortcutcontext_p_p.h
index efeb541dda..4dcd96d54f 100644
--- a/src/quicktemplates/qquickshortcutcontext_p_p.h
+++ b/src/quicktemplates/qquickshortcutcontext_p_p.h
@@ -22,7 +22,7 @@ QT_BEGIN_NAMESPACE
class QObject;
-struct Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickShortcutContext
+struct Q_QUICKTEMPLATES2_EXPORT QQuickShortcutContext
{
static bool matcher(QObject *object, Qt::ShortcutContext context);
};
diff --git a/src/quicktemplates/qquickslider.cpp b/src/quicktemplates/qquickslider.cpp
index 70317cd231..04b5589524 100644
--- a/src/quicktemplates/qquickslider.cpp
+++ b/src/quicktemplates/qquickslider.cpp
@@ -74,6 +74,7 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
qreal from = 0;
qreal to = 1;
@@ -202,8 +203,6 @@ void QQuickSliderPrivate::handleUngrab()
q->setPressed(false);
}
-static inline QString handleName() { return QStringLiteral("handle"); }
-
void QQuickSliderPrivate::cancelHandle()
{
Q_Q(QQuickSlider);
@@ -238,9 +237,21 @@ void QQuickSliderPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit q->implicitHandleHeightChanged();
}
+void QQuickSliderPrivate::itemDestroyed(QQuickItem *item)
+{
+ Q_Q(QQuickSlider);
+ QQuickControlPrivate::itemDestroyed(item);
+ if (item == handle) {
+ handle = nullptr;
+ emit q->handleChanged();
+ }
+}
+
QQuickSlider::QQuickSlider(QQuickItem *parent)
: QQuickControl(*(new QQuickSliderPrivate), parent)
{
+ Q_D(QQuickSlider);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
setActiveFocusOnTab(true);
#ifdef Q_OS_MACOS
setFocusPolicy(Qt::TabFocus);
@@ -524,6 +535,11 @@ void QQuickSlider::setOrientation(Qt::Orientation orientation)
if (d->orientation == orientation)
return;
+ if (orientation == Qt::Horizontal)
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
+ else
+ d->setSizePolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Preferred);
+
d->orientation = orientation;
emit orientationChanged();
}
diff --git a/src/quicktemplates/qquickslider_p.h b/src/quicktemplates/qquickslider_p.h
index 8452d4dad9..4374a6b05d 100644
--- a/src/quicktemplates/qquickslider_p.h
+++ b/src/quicktemplates/qquickslider_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickSliderPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSlider : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickSlider : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -152,6 +152,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSlider)
-
#endif // QQUICKSLIDER_P_H
diff --git a/src/quicktemplates/qquickspinbox.cpp b/src/quicktemplates/qquickspinbox.cpp
index c825724fc8..6c5d47b69d 100644
--- a/src/quicktemplates/qquickspinbox.cpp
+++ b/src/quicktemplates/qquickspinbox.cpp
@@ -2,17 +2,14 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickspinbox_p.h"
-#include "qquickcontrol_p_p.h"
-#include "qquickindicatorbutton_p.h"
-#include "qquickdeferredexecute_p_p.h"
-#include <QtGui/qguiapplication.h>
-#include <QtGui/qstylehints.h>
+#include <private/qquickcontrol_p_p.h>
+#include <private/qquickindicatorbutton_p.h>
+#include <private/qquicktextinput_p.h>
+
+#include <private/qqmlengine_p.h>
#include <QtQml/qqmlinfo.h>
-#include <QtQml/private/qqmllocale_p.h>
-#include <QtQml/private/qqmlengine_p.h>
-#include <QtQuick/private/qquicktextinput_p.h>
QT_BEGIN_NAMESPACE
@@ -60,6 +57,10 @@ static const int AUTO_REPEAT_INTERVAL = 100;
\snippet qtquickcontrols-spinbox-double.qml 1
+ A prefix and suffix can be added using regular expressions:
+
+ \snippet qtquickcontrols-spinbox-prefix.qml 1
+
\sa Tumbler, {Customizing SpinBox}, {Focus Management in Qt Quick Controls}
*/
@@ -88,8 +89,9 @@ public:
int effectiveStepSize() const;
- void updateDisplayText(bool modified = false);
- void setDisplayText(const QString &displayText, bool modified = false);
+ void updateDisplayText();
+ void setDisplayText(const QString &displayText);
+ void contentItemTextChanged();
bool upEnabled() const;
void updateUpEnabled();
@@ -108,10 +110,15 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ void itemDestroyed(QQuickItem *item) override;
+
+ QString evaluateTextFromValue(int val) const;
+ int evaluateValueFromText(const QString &text) const;
QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::SpinBox); }
bool editable = false;
+ bool live = false;
bool wrap = false;
int from = 0;
int to = 99;
@@ -148,20 +155,10 @@ int QQuickSpinBoxPrivate::boundValue(int value, bool wrap) const
void QQuickSpinBoxPrivate::updateValue()
{
- Q_Q(QQuickSpinBox);
if (contentItem) {
QVariant text = contentItem->property("text");
if (text.isValid()) {
- int val = 0;
- QQmlEngine *engine = qmlEngine(q);
- if (engine && valueFromText.isCallable()) {
- QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
- QJSValue loc = QJSValuePrivate::fromReturnedValue(QQmlLocale::wrap(v4, locale));
- val = valueFromText.call(QJSValueList() << text.toString() << loc).toInt();
- } else {
- val = locale.toInt(text.toString());
- }
- setValue(val, /* allowWrap = */ false, /* modified = */ true);
+ setValue(evaluateValueFromText(text.toString()), /* allowWrap = */ false, /* modified = */ true);
}
}
}
@@ -183,7 +180,7 @@ bool QQuickSpinBoxPrivate::setValue(int newValue, bool allowWrap, bool modified)
const bool emitSignals = (value != correctedValue);
value = correctedValue;
- updateDisplayText(modified);
+ updateDisplayText();
updateUpEnabled();
updateDownEnabled();
@@ -217,32 +214,49 @@ int QQuickSpinBoxPrivate::effectiveStepSize() const
return from > to ? -1 * stepSize : stepSize;
}
-void QQuickSpinBoxPrivate::updateDisplayText(bool modified)
+void QQuickSpinBoxPrivate::updateDisplayText()
{
- Q_Q(QQuickSpinBox);
- QString text;
- QQmlEngine *engine = qmlEngine(q);
- if (engine && textFromValue.isCallable()) {
- QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
- QJSValue loc = QJSValuePrivate::fromReturnedValue(QQmlLocale::wrap(v4, locale));
- text = textFromValue.call(QJSValueList() << value << loc).toString();
- } else {
- text = locale.toString(value);
- }
- setDisplayText(text, modified);
+ setDisplayText(evaluateTextFromValue(value));
}
-void QQuickSpinBoxPrivate::setDisplayText(const QString &text, bool modified)
+void QQuickSpinBoxPrivate::setDisplayText(const QString &text)
{
Q_Q(QQuickSpinBox);
- if (!modified && displayText == text)
+ if (displayText == text)
return;
displayText = text;
emit q->displayTextChanged();
}
+void QQuickSpinBoxPrivate::contentItemTextChanged()
+{
+ Q_Q(QQuickSpinBox);
+
+ QQuickTextInput *inputTextItem = qobject_cast<QQuickTextInput *>(q->contentItem());
+ if (!inputTextItem)
+ return;
+ QString text = inputTextItem->text();
+#if QT_CONFIG(validator)
+ if (validator)
+ validator->fixup(text);
+#endif
+
+ if (live) {
+ const int enteredVal = evaluateValueFromText(text);
+ const int correctedValue = boundValue(enteredVal, false);
+ if (correctedValue == enteredVal && correctedValue != value) {
+ // If live is true and the text is valid change the value
+ // setValue will set the displayText for us.
+ q->setValue(correctedValue);
+ return;
+ }
+ }
+ // If live is false or the value is not valid, just set the displayText
+ setDisplayText(text);
+}
+
bool QQuickSpinBoxPrivate::upEnabled() const
{
const QQuickItem *upIndicator = up->indicator();
@@ -352,13 +366,15 @@ bool QQuickSpinBoxPrivate::handleRelease(const QPointF &point, ulong timestamp)
int oldValue = value;
if (up->isPressed()) {
- up->setPressed(false);
if (repeatTimer <= 0 && ui && ui->contains(ui->mapFromItem(q, point)))
q->increase();
+ // Retain pressed state until after increasing is done in case user code binds stepSize
+ // to up/down.pressed.
+ up->setPressed(false);
} else if (down->isPressed()) {
- down->setPressed(false);
if (repeatTimer <= 0 && di && di->contains(di->mapFromItem(q, point)))
q->decrease();
+ down->setPressed(false);
}
if (value != oldValue)
emit q->valueModified();
@@ -397,12 +413,62 @@ void QQuickSpinBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit down->implicitIndicatorHeightChanged();
}
+void QQuickSpinBoxPrivate::itemDestroyed(QQuickItem *item)
+{
+ QQuickControlPrivate::itemDestroyed(item);
+ if (item == up->indicator())
+ up->setIndicator(nullptr);
+ else if (item == down->indicator())
+ down->setIndicator(nullptr);
+}
+
+
+QString QQuickSpinBoxPrivate::evaluateTextFromValue(int val) const
+{
+ Q_Q(const QQuickSpinBox);
+
+ QString text;
+ QQmlEngine *engine = qmlEngine(q);
+ if (engine && textFromValue.isCallable()) {
+ QJSValue loc;
+#if QT_CONFIG(qml_locale)
+ QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
+ loc = QJSValuePrivate::fromReturnedValue(
+ v4->fromData(QMetaType::fromType<QLocale>(), &locale));
+#endif
+ text = textFromValue.call(QJSValueList() << val << loc).toString();
+ } else {
+ text = locale.toString(val);
+ }
+ return text;
+}
+
+int QQuickSpinBoxPrivate::evaluateValueFromText(const QString &text) const
+{
+ Q_Q(const QQuickSpinBox);
+ int value;
+ QQmlEngine *engine = qmlEngine(q);
+ if (engine && valueFromText.isCallable()) {
+ QJSValue loc;
+#if QT_CONFIG(qml_locale)
+ QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine);
+ loc = QJSValuePrivate::fromReturnedValue(
+ v4->fromData(QMetaType::fromType<QLocale>(), &locale));
+#endif
+ value = valueFromText.call(QJSValueList() << text << loc).toInt();
+ } else {
+ value = locale.toInt(text);
+ }
+ return value;
+}
+
QQuickSpinBox::QQuickSpinBox(QQuickItem *parent)
: QQuickControl(*(new QQuickSpinBoxPrivate), parent)
{
Q_D(QQuickSpinBox);
d->up = new QQuickIndicatorButton(this);
d->down = new QQuickIndicatorButton(this);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
setFlag(ItemIsFocusScope);
setFiltersChildMouseEvents(true);
@@ -550,6 +616,41 @@ void QQuickSpinBox::setEditable(bool editable)
emit editableChanged();
}
+/*!
+ \qmlproperty bool QtQuick.Controls::SpinBox::live
+ \since 6.6
+
+ This property holds whether the \l value is updated when the user edits the
+ \l displayText. The default value is \c false. If this property is \c true and
+ the value entered by the user is valid and within the bounds of the spinbox
+ [\l from, \l to], the value of the SpinBox will be set. If this property is
+ \c false or the value entered by the user is outside the boundaries, the
+ value will not be updated until the enter or return keys are pressed, or the
+ input field loses focus.
+
+ \sa editable, displayText
+*/
+bool QQuickSpinBox::isLive() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->live;
+}
+
+void QQuickSpinBox::setLive(bool live)
+{
+ Q_D(QQuickSpinBox);
+ if (d->live == live)
+ return;
+
+ d->live = live;
+
+ //make sure to update the value when changing to live
+ if (live)
+ d->contentItemTextChanged();
+
+ emit liveChanged();
+}
+
#if QT_CONFIG(validator)
/*!
\qmlproperty Validator QtQuick.Controls::SpinBox::validator
@@ -890,16 +991,18 @@ void QQuickSpinBox::keyPressEvent(QKeyEvent *event)
switch (event->key()) {
case Qt::Key_Up:
if (d->upEnabled()) {
- d->increase(true);
+ // Update the pressed state before increasing/decreasing in case user code binds
+ // stepSize to up/down.pressed.
d->up->setPressed(true);
+ d->increase(true);
event->accept();
}
break;
case Qt::Key_Down:
if (d->downEnabled()) {
- d->decrease(true);
d->down->setPressed(true);
+ d->decrease(true);
event->accept();
}
break;
@@ -988,20 +1091,24 @@ void QQuickSpinBox::itemChange(ItemChange change, const ItemChangeData &value)
void QQuickSpinBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
{
Q_D(QQuickSpinBox);
- if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem))
+ if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
disconnect(oldInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickSpinBox::inputMethodComposingChanged);
+ QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d, &QQuickSpinBoxPrivate::contentItemTextChanged);
+ }
if (newItem) {
newItem->setActiveFocusOnTab(true);
if (d->activeFocus)
- newItem->forceActiveFocus(d->focusReason);
+ newItem->forceActiveFocus(static_cast<Qt::FocusReason>(d->focusReason));
#if QT_CONFIG(cursor)
if (d->editable)
newItem->setCursor(Qt::IBeamCursor);
#endif
- if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem))
+ if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
connect(newInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickSpinBox::inputMethodComposingChanged);
+ QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d, &QQuickSpinBoxPrivate::contentItemTextChanged);
+ }
}
}
diff --git a/src/quicktemplates/qquickspinbox_p.h b/src/quicktemplates/qquickspinbox_p.h
index a50e77c171..c9547f405b 100644
--- a/src/quicktemplates/qquickspinbox_p.h
+++ b/src/quicktemplates/qquickspinbox_p.h
@@ -24,7 +24,7 @@ class QValidator;
class QQuickSpinBoxPrivate;
class QQuickIndicatorButton;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSpinBox : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickSpinBox : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(int from READ from WRITE setFrom NOTIFY fromChanged FINAL)
@@ -32,6 +32,8 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSpinBox : public QQuickControl
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged FINAL)
Q_PROPERTY(int stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL)
Q_PROPERTY(bool editable READ isEditable WRITE setEditable NOTIFY editableChanged FINAL)
+ Q_PROPERTY(bool live READ isLive WRITE setLive NOTIFY liveChanged FINAL REVISION(6, 6))
+
#if QT_CONFIG(validator)
Q_PROPERTY(QValidator *validator READ validator WRITE setValidator NOTIFY validatorChanged FINAL)
#endif
@@ -68,6 +70,9 @@ public:
bool isEditable() const;
void setEditable(bool editable);
+ bool isLive() const;
+ void setLive(bool live);
+
#if QT_CONFIG(validator)
QValidator *validator() const;
void setValidator(QValidator *validator);
@@ -105,6 +110,7 @@ Q_SIGNALS:
void valueChanged();
void stepSizeChanged();
void editableChanged();
+ Q_REVISION(6, 6) void liveChanged();
#if QT_CONFIG(validator)
void validatorChanged();
#endif
@@ -151,6 +157,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSpinBox)
-
#endif // QQUICKSPINBOX_P_H
diff --git a/src/quicktemplates/qquicksplitview.cpp b/src/quicktemplates/qquicksplitview.cpp
index 0bf1b79cac..67d8845917 100644
--- a/src/quicktemplates/qquicksplitview.cpp
+++ b/src/quicktemplates/qquicksplitview.cpp
@@ -221,10 +221,13 @@ QT_BEGIN_NAMESPACE
\sa SplitHandle, {Customizing SplitView}, {Container Controls}
*/
-Q_LOGGING_CATEGORY(qlcQQuickSplitView, "qt.quick.controls.splitview")
-Q_LOGGING_CATEGORY(qlcQQuickSplitViewPointer, "qt.quick.controls.splitview.pointer")
-Q_LOGGING_CATEGORY(qlcQQuickSplitViewState, "qt.quick.controls.splitview.state")
+Q_STATIC_LOGGING_CATEGORY(qlcQQuickSplitView, "qt.quick.controls.splitview")
+Q_STATIC_LOGGING_CATEGORY(qlcQQuickSplitViewPointer, "qt.quick.controls.splitview.pointer")
+Q_STATIC_LOGGING_CATEGORY(qlcQQuickSplitViewState, "qt.quick.controls.splitview.state")
+/*
+ Updates m_fillIndex to be between 0 .. (item count - 1).
+*/
void QQuickSplitViewPrivate::updateFillIndex()
{
const int count = contentModel->count();
@@ -232,10 +235,9 @@ void QQuickSplitViewPrivate::updateFillIndex()
qCDebug(qlcQQuickSplitView) << "looking for fillWidth/Height item amongst" << count << "items";
- m_fillIndex = -1;
- int i = 0;
+ int fillIndex = -1;
int lastVisibleIndex = -1;
- for (; i < count; ++i) {
+ for (int i = 0; i < count; ++i) {
QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
if (!item->isVisible())
continue;
@@ -248,19 +250,21 @@ void QQuickSplitViewPrivate::updateFillIndex()
continue;
if ((horizontal && attached->fillWidth()) || (!horizontal && attached->fillHeight())) {
- m_fillIndex = i;
- qCDebug(qlcQQuickSplitView) << "found fillWidth/Height item at index" << m_fillIndex;
+ fillIndex = i;
+ qCDebug(qlcQQuickSplitView) << "found fillWidth/Height item at index" << fillIndex;
break;
}
}
- if (m_fillIndex == -1) {
- // If there was no item with fillWidth/fillHeight set, m_fillIndex will be -1,
- // and we'll set it to the last visible item.
+ if (fillIndex == -1) {
+ // If there was no item with fillWidth/fillHeight set, fillIndex will be -1,
+ // and we'll set m_fillIndex to the last visible item.
// If there was an item with fillWidth/fillHeight set, we were already done and this will be skipped.
- m_fillIndex = lastVisibleIndex != -1 ? lastVisibleIndex : count - 1;
- qCDebug(qlcQQuickSplitView) << "found no fillWidth/Height item; using last item at index" << m_fillIndex;
+ fillIndex = lastVisibleIndex != -1 ? lastVisibleIndex : count - 1;
+ qCDebug(qlcQQuickSplitView) << "found no fillWidth/Height item; using last item at index" << fillIndex;
}
+ // Take new fillIndex into use.
+ m_fillIndex = fillIndex;
}
/*
@@ -325,11 +329,14 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
// The handle shouldn't cross other handles, so use the right edge of
// the first handle to the left as the left edge.
qreal leftEdge = 0;
- if (m_pressedHandleIndex - 1 >= 0) {
- const QQuickItem *leftHandle = m_handleItems.at(m_pressedHandleIndex - 1);
- leftEdge = horizontal
- ? leftHandle->x() + leftHandle->width()
- : leftHandle->y() + leftHandle->height();
+ for (int i = m_pressedHandleIndex - 1; i >= 0; --i) {
+ const QQuickItem *nextHandleToTheLeft = m_handleItems.at(i);
+ if (nextHandleToTheLeft->isVisible()) {
+ leftEdge = horizontal
+ ? nextHandleToTheLeft->x() + nextHandleToTheLeft->width()
+ : nextHandleToTheLeft->y() + nextHandleToTheLeft->height();
+ break;
+ }
}
// The mouse can be clicked anywhere in the handle, and if we don't account for
@@ -344,35 +351,9 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
const qreal newItemSize = newHandlePos - leftEdge;
- // Modify the preferredWidth, otherwise the original implicitWidth/preferredWidth
- // will be used on the next layout (when it's no longer being resized).
- if (!attached) {
- // Force the attached object to be created since we rely on it.
- attached = qobject_cast<QQuickSplitViewAttached*>(
- qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
- }
-
- /*
- Users could conceivably respond to size changes in items by setting attached
- SplitView properties:
-
- onWidthChanged: if (width < 10) secondItem.SplitView.preferredWidth = 100
-
- We handle this by doing another layout after the current layout if the
- attached/implicit size properties are set during this layout. However, we also
- need to set preferredWidth/Height here (for reasons mentioned in the comment above),
- but we don't want this to count as a request for a delayed layout, so we guard against it.
- */
- m_ignoreNextLayoutRequest = true;
-
- if (horizontal)
- attached->setPreferredWidth(newItemSize);
- else
- attached->setPreferredHeight(newItemSize);
-
- // We still need to use requestedWidth in the setWidth() call below,
+ // We still need to use requestedSize in the width/height call below,
// because sizeData has already been calculated and now contains an old
- // effectivePreferredWidth value.
+ // effectivePreferredWidth/Height value.
requestedSize = newItemSize;
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized (dragged) " << item
@@ -405,22 +386,7 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
const qreal newItemSize = rightEdge - (newHandlePos + pressedHandleSize);
- // Modify the preferredWidth, otherwise the original implicitWidth/preferredWidth
- // will be used on the next layout (when it's no longer being resized).
- if (!attached) {
- // Force the attached object to be created since we rely on it.
- attached = qobject_cast<QQuickSplitViewAttached*>(
- qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
- }
-
- m_ignoreNextLayoutRequest = true;
-
- if (horizontal)
- attached->setPreferredWidth(newItemSize);
- else
- attached->setPreferredHeight(newItemSize);
-
- // We still need to use requestedSize in the setWidth()/setHeight() call below,
+ // We still need to use requestedSize in the width/height call below,
// because sizeData has already been calculated and now contains an old
// effectivePreferredWidth/Height value.
requestedSize = newItemSize;
@@ -443,34 +409,43 @@ void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &use
}
if (index != m_fillIndex) {
+ LayoutData layoutData;
if (horizontal) {
- item->setWidth(qBound(
- sizeData.effectiveMinimumWidth,
- requestedSize,
- sizeData.effectiveMaximumWidth));
- item->setHeight(height);
+ layoutData.width = qBound(
+ sizeData.effectiveMinimumWidth,
+ requestedSize,
+ sizeData.effectiveMaximumWidth);
+ layoutData.height = height;
} else {
- item->setWidth(width);
- item->setHeight(qBound(
- sizeData.effectiveMinimumHeight,
- requestedSize,
- sizeData.effectiveMaximumHeight));
+ layoutData.width = width;
+ layoutData.height = qBound(
+ sizeData.effectiveMinimumHeight,
+ requestedSize,
+ sizeData.effectiveMaximumHeight);
}
- qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized split item " << item
- << " (effective"
- << " minW=" << sizeData.effectiveMinimumWidth
- << ", minH=" << sizeData.effectiveMinimumHeight
- << ", prfW=" << sizeData.effectivePreferredWidth
- << ", prfH=" << sizeData.effectivePreferredHeight
- << ", maxW=" << sizeData.effectiveMaximumWidth
- << ", maxH=" << sizeData.effectiveMaximumHeight << ")";
+ // Mark that this item has been manually resized. After this
+ // we can override the preferredWidth & preferredHeight
+ if (isBeingResized)
+ layoutData.wasResizedByHandle = true;
+
+ m_layoutData.insert(item, layoutData);
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": calculated the following size data for split item " << item
+ << ": eminW=" << sizeData.effectiveMinimumWidth
+ << ", eminH=" << sizeData.effectiveMinimumHeight
+ << ", eprfW=" << sizeData.effectivePreferredWidth
+ << ", eprfH=" << sizeData.effectivePreferredHeight
+ << ", emaxW=" << sizeData.effectiveMaximumWidth
+ << ", emaxH=" << sizeData.effectiveMaximumHeight
+ << ", w=" << layoutData.width
+ << ", h=" << layoutData.height << "";
// Keep track of how much space has been used so far.
if (horizontal)
- usedWidth += item->width();
+ usedWidth += layoutData.width;
else
- usedHeight += item->height();
+ usedHeight += layoutData.height;
} else if (indexBeingResizedDueToDrag != m_fillIndex) {
// The fill item is resized afterwards, outside of the loop.
qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": skipping fill item as we resize it last";
@@ -526,20 +501,26 @@ void QQuickSplitViewPrivate::layoutResizeFillItem(QQuickItem *fillItem,
const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
qmlAttachedPropertiesObject<QQuickSplitView>(fillItem, false));
const auto fillSizeData = effectiveSizeData(fillItemPrivate, attached);
+
+ LayoutData layoutData;
if (isHorizontal()) {
- fillItem->setWidth(qBound(
- fillSizeData.effectiveMinimumWidth,
- width - usedWidth,
- fillSizeData.effectiveMaximumWidth));
- fillItem->setHeight(height);
+ layoutData.width = qBound(
+ fillSizeData.effectiveMinimumWidth,
+ width - usedWidth,
+ fillSizeData.effectiveMaximumWidth);
+ layoutData.height = height;
+ usedWidth += layoutData.width;
} else {
- fillItem->setWidth(width);
- fillItem->setHeight(qBound(
- fillSizeData.effectiveMinimumHeight,
- height - usedHeight,
- fillSizeData.effectiveMaximumHeight));
+ layoutData.width = width;
+ layoutData.height = qBound(
+ fillSizeData.effectiveMinimumHeight,
+ height - usedHeight,
+ fillSizeData.effectiveMaximumHeight);
+ usedHeight += layoutData.height;
}
+ m_layoutData.insert(fillItem, layoutData);
+
qCDebug(qlcQQuickSplitView).nospace() << " - " << m_fillIndex
<< ": resized split fill item " << fillItem << " (effective"
<< " minW=" << fillSizeData.effectiveMinimumWidth
@@ -549,6 +530,100 @@ void QQuickSplitViewPrivate::layoutResizeFillItem(QQuickItem *fillItem,
}
/*
+ Limit the sizes if needed and apply them into items.
+*/
+void QQuickSplitViewPrivate::limitAndApplySizes(qreal usedWidth, qreal usedHeight)
+{
+ const int count = contentModel->count();
+ const bool horizontal = isHorizontal();
+
+ const qreal maxSize = horizontal ? width : height;
+ const qreal usedSize = horizontal ? usedWidth : usedHeight;
+ if (usedSize > maxSize) {
+ qCDebug(qlcQQuickSplitView).nospace() << "usedSize " << usedSize << " is greater than maxSize "
+ << maxSize << "; reducing size of non-filled items from right to left / bottom to top";
+
+ // If items don't fit, reduce the size of non-filled items from
+ // right to left / bottom to top. At this point filled item is
+ // already at its minimum size or usedSize wouldn't be > maxSize.
+ qreal delta = usedSize - maxSize;
+ for (int index = count - 1; index >= 0; --index) {
+ if (index == m_fillIndex)
+ continue;
+ QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(index));
+ if (!item->isVisible())
+ continue;
+
+ const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ const auto sizeData = effectiveSizeData(itemPrivate, attached);
+ const qreal maxReduce = horizontal ?
+ m_layoutData[item].width - sizeData.effectiveMinimumWidth :
+ m_layoutData[item].height - sizeData.effectiveMinimumHeight;
+
+ const qreal reduce = std::min(maxReduce, delta);
+ if (horizontal)
+ m_layoutData[item].width -= reduce;
+ else
+ m_layoutData[item].height -= reduce;
+
+ delta -= reduce;
+ if (delta <= 0) {
+ // Now all the items fit, so continue
+ break;
+ }
+ }
+ }
+
+ qCDebug(qlcQQuickSplitView).nospace() << " applying new sizes to " << count << " items (excluding hidden items)";
+
+ // Apply the new sizes into items
+ for (int index = 0; index < count; ++index) {
+ QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(index));
+ if (!item->isVisible())
+ continue;
+
+ QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ LayoutData layoutData = m_layoutData.value(item);
+ if (layoutData.wasResizedByHandle) {
+ // Modify the preferredWidth/Height, otherwise the original implicit/preferred size
+ // will be used on the next layout (when it's no longer being resized).
+ if (!attached) {
+ // Force the attached object to be created since we rely on it.
+ attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
+ }
+ /*
+ Users could conceivably respond to size changes in items by setting attached
+ SplitView properties:
+
+ onWidthChanged: if (width < 10) secondItem.SplitView.preferredWidth = 100
+
+ We handle this by doing another layout after the current layout if the
+ attached/implicit size properties are set during this layout. However, we also
+ need to set preferredWidth/Height here, otherwise the original implicit/preferred sizes
+ will be used on the next layout (when it's no longer being resized).
+ But we don't want this to count as a request for a delayed layout, so we guard against it.
+ */
+ m_ignoreNextLayoutRequest = true;
+ if (horizontal)
+ attached->setPreferredWidth(layoutData.width);
+ else
+ attached->setPreferredHeight(layoutData.height);
+ }
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized item " << item << " from "
+ << item->width() << "x" << item->height() << " to "
+ << layoutData.width << "x" << layoutData.height;
+
+ item->setWidth(layoutData.width);
+ item->setHeight(layoutData.height);
+ }
+}
+
+/*
Positions items by laying them out in a row or column.
Items that aren't visible are skipped.
@@ -621,6 +696,19 @@ void QQuickSplitViewPrivate::requestLayout()
q->polish();
}
+/*
+ Layout steps are (horizontal SplitView as an example):
+ 1) layoutResizeSplitItems: Gives each non-filled item its preferredWidth
+ or if not set, implicitWidth. Sizes are kept between effectiveMinimumWidth
+ and effectiveMaximumWidth and stored into layoutData for now.
+ 2) layoutResizeFillItem: Gives filled item all the remaining space. Size is
+ kept between effectiveMinimumWidth and effectiveMaximumWidth and stored
+ into layoutData for now.
+ 3) limitAndApplySizes: If we have used more space than SplitView item has,
+ start reducing non-filled item sizes from right-to-left. Reduce them up
+ to minimumWidth or until SplitView item width is reached. Finally set the
+ new item sizes from layoutData.
+*/
void QQuickSplitViewPrivate::layout()
{
if (!componentComplete)
@@ -648,13 +736,15 @@ void QQuickSplitViewPrivate::layout()
qCDebug(qlcQQuickSplitView) << "laying out" << count << "split items"
<< (horizontal ? "horizontally" : "vertically") << "in SplitView" << q_func();
+ // Total sizes of items used during the layout operation.
qreal usedWidth = 0;
qreal usedHeight = 0;
int indexBeingResizedDueToDrag = -1;
+ m_layoutData.clear();
qCDebug(qlcQQuickSplitView) << " resizing:";
- // First, resize the items. We need to do this first because otherwise fill
+ // First, resize the non-filled items. We need to do this first because otherwise fill
// items would take up all of the remaining space as soon as they are encountered.
layoutResizeSplitItems(usedWidth, usedHeight, indexBeingResizedDueToDrag);
@@ -666,6 +756,9 @@ void QQuickSplitViewPrivate::layout()
QQuickItem *fillItem = qobject_cast<QQuickItem*>(contentModel->object(m_fillIndex));
layoutResizeFillItem(fillItem, usedWidth, usedHeight, indexBeingResizedDueToDrag);
+ // Reduce the sizes still if needed and apply them into items.
+ limitAndApplySizes(usedWidth, usedHeight);
+
qCDebug(qlcQQuickSplitView) << " positioning:";
// Position the items.
@@ -1129,13 +1222,19 @@ void QQuickSplitView::setOrientation(Qt::Orientation orientation)
return;
d->m_orientation = orientation;
- d->resizeHandles();
+
#if QT_CONFIG(cursor)
for (QQuickItem *handleItem : d->m_handleItems)
d->updateCursorHandle(handleItem);
#endif
- d->requestLayout();
emit orientationChanged();
+
+ // Do this after emitting orientationChanged so that the bindings in QML
+ // update the implicit size in time.
+ d->resizeHandles();
+ // This is queued (via polish) anyway, but to make our intentions clear,
+ // do it afterwards too.
+ d->requestLayout();
}
/*!
@@ -1359,7 +1458,6 @@ void QQuickSplitView::componentComplete()
{
Q_D(QQuickSplitView);
QQuickControl::componentComplete();
- d->resizeHandles();
d->updateFillIndex();
d->updatePolish();
}
@@ -1912,9 +2010,10 @@ void QQuickSplitViewAttached::resetMaximumHeight()
This attached property controls whether the item takes the remaining space
in the split view after all other items have been laid out.
- By default, the last visible child of the split view will have this set,
+ By default, the last visible child of the split view will fill the view,
but it can be changed by explicitly setting \c fillWidth to \c true on
- another item.
+ another item. If multiple items have \c fillWidth set to \c true, the
+ left-most item will fill the view.
The width of a split item with \c fillWidth set to \c true is still
restricted within its \l minimumWidth and \l maximumWidth.
@@ -1947,9 +2046,10 @@ void QQuickSplitViewAttached::setFillWidth(bool fill)
This attached property controls whether the item takes the remaining space
in the split view after all other items have been laid out.
- By default, the last visible child of the split view will have this set,
+ By default, the last visible child of the split view will fill the view,
but it can be changed by explicitly setting \c fillHeight to \c true on
- another item.
+ another item. If multiple items have \c fillHeight set to \c true, the
+ top-most item will fill the view.
The height of a split item with \c fillHeight set to \c true is still
restricted within its \l minimumHeight and \l maximumHeight.
diff --git a/src/quicktemplates/qquicksplitview_p.h b/src/quicktemplates/qquicksplitview_p.h
index 12bba3a856..0828142a4e 100644
--- a/src/quicktemplates/qquicksplitview_p.h
+++ b/src/quicktemplates/qquicksplitview_p.h
@@ -18,6 +18,8 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
#include <QtQml/qqmllist.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickSplitViewPrivate;
@@ -26,7 +28,7 @@ class QQuickSplitViewAttachedPrivate;
class QQuickSplitHandleAttached;
class QQuickSplitHandleAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitView : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickSplitView : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL)
@@ -86,7 +88,7 @@ private:
Q_DECLARE_PRIVATE(QQuickSplitView)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitViewAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSplitViewAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickSplitView *view READ view NOTIFY viewChanged FINAL)
@@ -156,7 +158,7 @@ private:
Q_DECLARE_PRIVATE(QQuickSplitViewAttached)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitHandleAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSplitHandleAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged FINAL)
@@ -185,8 +187,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSplitView)
-
-QML_DECLARE_TYPE(QQuickSplitHandleAttached)
-
#endif // QQUICKSPLITVIEW_P_H
diff --git a/src/quicktemplates/qquicksplitview_p_p.h b/src/quicktemplates/qquicksplitview_p_p.h
index 86316ecfe9..d2b0c35668 100644
--- a/src/quicktemplates/qquicksplitview_p_p.h
+++ b/src/quicktemplates/qquicksplitview_p_p.h
@@ -23,7 +23,7 @@ class QQuickSplitView;
class QQuickSplitViewAttached;
class QQuickSplitHandleAttached;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitViewPrivate : public QQuickContainerPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickSplitViewPrivate : public QQuickContainerPrivate
{
Q_DECLARE_PUBLIC(QQuickSplitView)
@@ -31,6 +31,7 @@ public:
void updateFillIndex();
void layoutResizeSplitItems(qreal &usedWidth, qreal &usedHeight, int &indexBeingResizedDueToDrag);
void layoutResizeFillItem(QQuickItem *fillItem, qreal &usedWidth, qreal &usedHeight, int indexBeingResizedDueToDrag);
+ void limitAndApplySizes(qreal usedWidth, qreal usedHeight);
void layoutPositionItems(const QQuickItem *fillItem);
void requestLayout();
void layout();
@@ -59,6 +60,13 @@ public:
qreal effectiveMaximumHeight;
};
+ // Used during the layout
+ struct LayoutData {
+ qreal width = 0;
+ qreal height = 0;
+ bool wasResizedByHandle = false;
+ };
+
EffectiveSizeData effectiveSizeData(const QQuickItemPrivate *itemPrivate,
const QQuickSplitViewAttached *attached) const;
@@ -80,6 +88,7 @@ public:
Qt::Orientation m_orientation = Qt::Horizontal;
QQmlComponent *m_handle = nullptr;
QList<QQuickItem*> m_handleItems;
+ QHash<QQuickItem*, LayoutData> m_layoutData;
int m_hoveredHandleIndex = -1;
int m_pressedHandleIndex = -1;
int m_nextVisibleIndexAfterPressedHandle = -1;
diff --git a/src/quicktemplates/qquickstackelement.cpp b/src/quicktemplates/qquickstackelement.cpp
index f8001a2980..f6a5ffdd74 100644
--- a/src/quicktemplates/qquickstackelement.cpp
+++ b/src/quicktemplates/qquickstackelement.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickstackelement_p_p.h"
+#include "qquickstackview_p.h"
#include "qquickstackview_p_p.h"
#include <QtQml/qqmlinfo.h>
@@ -15,6 +16,7 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(quick_viewtransitions)
static QQuickStackViewAttached *attachedStackObject(QQuickStackElement *element)
{
QQuickStackViewAttached *attached = qobject_cast<QQuickStackViewAttached *>(qmlAttachedPropertiesObject<QQuickStackView>(element->item, false));
@@ -22,6 +24,7 @@ static QQuickStackViewAttached *attachedStackObject(QQuickStackElement *element)
QQuickStackViewAttachedPrivate::get(attached)->element = element;
return attached;
}
+#endif
class QQuickStackIncubator : public QQmlIncubator
{
@@ -44,18 +47,23 @@ private:
};
QQuickStackElement::QQuickStackElement()
+#if QT_CONFIG(quick_viewtransitions)
: QQuickItemViewTransitionableItem(nullptr)
+#endif
{
}
QQuickStackElement::~QQuickStackElement()
{
+#if QT_CONFIG(quick_viewtransitions)
if (item)
QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed);
+#endif
if (ownComponent)
delete component;
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
if (item) {
if (ownItem) {
@@ -79,6 +87,7 @@ QQuickStackElement::~QQuickStackElement()
if (attached)
emit attached->removed();
+#endif
}
QQuickStackElement *QQuickStackElement::fromString(const QString &str, QQuickStackView *view, QString *error)
@@ -110,9 +119,36 @@ QQuickStackElement *QQuickStackElement::fromObject(QObject *object, QQuickStackV
QQuickStackElement *element = new QQuickStackElement;
element->component = qobject_cast<QQmlComponent *>(object);
+#if QT_CONFIG(quick_viewtransitions)
element->item = qobject_cast<QQuickItem *>(object);
if (element->item)
element->originalParent = element->item->parentItem();
+#endif
+ return element;
+}
+
+QQuickStackElement *QQuickStackElement::fromStackViewArg(QQuickStackView *view, QQuickStackViewArg arg)
+{
+ QQuickStackElement *element = new QQuickStackElement;
+#if QT_CONFIG(quick_viewtransitions)
+ element->item = arg.mItem;
+ if (element->item) {
+ element->originalParent = element->item->parentItem();
+
+ Q_ASSERT(!arg.mComponent);
+ Q_ASSERT(!arg.mUrl.isValid());
+ } else
+#endif
+ if (arg.mComponent) {
+ element->component = arg.mComponent;
+
+ Q_ASSERT(!arg.mUrl.isValid());
+ } else if (arg.mUrl.isValid()) {
+ element->component = new QQmlComponent(qmlEngine(view), arg.mUrl, view);
+ element->ownComponent = true;
+ } else {
+ qFatal("No Item, Component or URL set on arg passed to fromStrictArg");
+ }
return element;
}
@@ -177,7 +213,9 @@ void QQuickStackElement::initialize(RequiredProperties *requiredProperties)
QV4::ScopedValue ipv(scope, properties.value());
QV4::Scoped<QV4::QmlContext> qmlContext(scope, qmlCallingContext.value());
QV4::ScopedValue qmlObject(scope, QV4::QObjectWrapper::wrap(v4, item));
- QQmlComponentPrivate::setInitialProperties(v4, qmlContext, qmlObject, ipv, requiredProperties, item);
+ QQmlComponentPrivate::setInitialProperties(
+ v4, qmlContext, qmlObject, ipv, requiredProperties, item,
+ component ? QQmlComponentPrivate::get(component)->state.creator() : nullptr);
properties.clear();
}
@@ -202,9 +240,11 @@ void QQuickStackElement::setIndex(int value)
return;
index = value;
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
if (attached)
emit attached->indexChanged();
+#endif
}
void QQuickStackElement::setView(QQuickStackView *value)
@@ -213,9 +253,11 @@ void QQuickStackElement::setView(QQuickStackView *value)
return;
view = value;
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
if (attached)
emit attached->viewChanged();
+#endif
}
void QQuickStackElement::setStatus(QQuickStackView::Status value)
@@ -224,6 +266,7 @@ void QQuickStackElement::setStatus(QQuickStackView::Status value)
return;
status = value;
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
if (!attached)
return;
@@ -247,17 +290,25 @@ void QQuickStackElement::setStatus(QQuickStackView::Status value)
}
emit attached->statusChanged();
+#endif
}
void QQuickStackElement::setVisible(bool visible)
{
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackViewAttached *attached = attachedStackObject(this);
- if (!item || (attached && QQuickStackViewAttachedPrivate::get(attached)->explicitVisible))
+#endif
+ if (!item
+#if QT_CONFIG(quick_viewtransitions)
+ || (attached && QQuickStackViewAttachedPrivate::get(attached)->explicitVisible)
+#endif
+ )
return;
item->setVisible(visible);
}
+#if QT_CONFIG(quick_viewtransitions)
void QQuickStackElement::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
{
if (transitioner)
@@ -294,10 +345,13 @@ void QQuickStackElement::completeTransition(QQuickTransition *quickTransition)
{
QQuickItemViewTransitionableItem::completeTransition(quickTransition);
}
+#endif
void QQuickStackElement::itemDestroyed(QQuickItem *)
{
+#if QT_CONFIG(quick_viewtransitions)
item = nullptr;
+#endif
}
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickstackelement_p_p.h b/src/quicktemplates/qquickstackelement_p_p.h
index 00b150805d..5af8149d91 100644
--- a/src/quicktemplates/qquickstackelement_p_p.h
+++ b/src/quicktemplates/qquickstackelement_p_p.h
@@ -17,10 +17,14 @@
#include <QtQuickTemplates2/private/qquickstackview_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
+#if QT_CONFIG(quick_viewtransitions)
#include <QtQuick/private/qquickitemviewtransition_p.h>
+#endif
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQml/private/qv4persistent_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
class QQmlContext;
@@ -28,7 +32,11 @@ class QQmlComponent;
struct QQuickStackTransition;
class RequiredProperties;
-class QQuickStackElement : public QQuickItemViewTransitionableItem, public QQuickItemChangeListener
+class QQuickStackElement :
+#if QT_CONFIG(quick_viewtransitions)
+ public QQuickItemViewTransitionableItem,
+#endif
+ public QQuickItemChangeListener
{
QQuickStackElement();
@@ -37,6 +45,7 @@ public:
static QQuickStackElement *fromString(const QString &str, QQuickStackView *view, QString *error);
static QQuickStackElement *fromObject(QObject *object, QQuickStackView *view, QString *error);
+ static QQuickStackElement *fromStackViewArg(QQuickStackView *view, QQuickStackViewArg arg);
bool load(QQuickStackView *parent);
void incubate(QObject *object, RequiredProperties *requiredProperties);
@@ -47,10 +56,12 @@ public:
void setStatus(QQuickStackView::Status status);
void setVisible(bool visible);
+#if QT_CONFIG(quick_viewtransitions)
void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
void startTransition(QQuickItemViewTransitioner *transitioner, QQuickStackView::Status status);
void completeTransition(QQuickTransition *quickTransition);
+#endif
void itemDestroyed(QQuickItem *item) override;
@@ -67,6 +78,9 @@ public:
QQuickStackView::Status status = QQuickStackView::Inactive;
QV4::PersistentValue properties;
QV4::PersistentValue qmlCallingContext;
+#if !QT_CONFIG(quick_viewtransitions)
+ QQuickItem *item;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/quicktemplates/qquickstacktransition_p_p.h b/src/quicktemplates/qquickstacktransition_p_p.h
index e68f1bb7a1..d42e1981c0 100644
--- a/src/quicktemplates/qquickstacktransition_p_p.h
+++ b/src/quicktemplates/qquickstacktransition_p_p.h
@@ -15,6 +15,10 @@
// We mean it.
//
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_viewtransitions);
+
#include <QtQuickTemplates2/private/qquickstackview_p.h>
#include <QtQuick/private/qquickitemviewtransition_p.h>
diff --git a/src/quicktemplates/qquickstackview.cpp b/src/quicktemplates/qquickstackview.cpp
index d09f55f6a4..2e0109863a 100644
--- a/src/quicktemplates/qquickstackview.cpp
+++ b/src/quicktemplates/qquickstackview.cpp
@@ -4,7 +4,9 @@
#include "qquickstackview_p.h"
#include "qquickstackview_p_p.h"
#include "qquickstackelement_p_p.h"
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickstacktransition_p_p.h"
+#endif
#include <QtCore/qscopedvaluerollback.h>
#include <QtQml/qjsvalue.h>
@@ -16,6 +18,39 @@
QT_BEGIN_NAMESPACE
+QQuickStackViewArg::QQuickStackViewArg(QQuickItem *item)
+ : mItem(item)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(const QUrl &url)
+ : mUrl(url)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(QQmlComponent *component)
+ : mComponent(component)
+{
+}
+
+QQuickStackViewArg::QQuickStackViewArg(const QVariantMap &properties)
+ : mProperties(properties)
+{
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickStackViewArg &arg)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "QQuickStackViewArg("
+ << "mItem=" << arg.mItem
+ << " mComponent=" << arg.mComponent
+ << " mUrl=" << arg.mUrl
+ << ")";
+ return debug;
+}
+#endif
+
/*!
\qmltype StackView
\inherits Control
@@ -349,15 +384,20 @@ QQuickStackView::QQuickStackView(QQuickItem *parent)
: QQuickControl(*(new QQuickStackViewPrivate), parent)
{
setFlag(ItemIsFocusScope);
+
+ Q_D(QQuickStackView);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
}
QQuickStackView::~QQuickStackView()
{
Q_D(QQuickStackView);
+#if QT_CONFIG(quick_viewtransitions)
if (d->transitioner) {
d->transitioner->setChangeListener(nullptr);
delete d->transitioner;
}
+#endif
qDeleteAll(d->removing);
qDeleteAll(d->removed);
qDeleteAll(d->elements);
@@ -517,13 +557,17 @@ QQuickItem *QQuickStackView::find(const QJSValue &callback, LoadBehavior behavio
\value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
\value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
- If no operation is provided, \c PushTransition will be used.
+ If no operation is provided, \c Immediate will be used if the stack is
+ empty, and \c PushTransition otherwise.
\note Items that already exist in the stack are not pushed.
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l pushItem or \l pushItems functions instead.
+
\sa initialItem, {Pushing Items}
*/
-void QQuickStackView::push(QQmlV4Function *args)
+void QQuickStackView::push(QQmlV4FunctionPtr args)
{
Q_D(QQuickStackView);
const QString operationName = QStringLiteral("push");
@@ -543,21 +587,23 @@ void QQuickStackView::push(QQmlV4Function *args)
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
+#if QT_CONFIG(quick_viewtransitions)
Operation operation = d->elements.isEmpty() ? Immediate : PushTransition;
QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
if (lastArg->isInt32())
operation = static_cast<Operation>(lastArg->toInt32());
+#endif
QStringList errors;
QList<QQuickStackElement *> elements = d->parseElements(0, args, &errors);
// Remove any items that are already in the stack, as they can't be in two places at once.
- for (int i = 0; i < elements.size(); ) {
- QQuickStackElement *element = elements.at(i);
- if (element->item && d->findElement(element->item))
- elements.removeAt(i);
- else
- ++i;
- }
+ // not using erase_if, as we have to delete the elements first
+ auto removeIt = std::remove_if(elements.begin(), elements.end(), [&](QQuickStackElement *element) {
+ return element->item && d->findElement(element->item);
+ });
+ for (auto it = removeIt, end = elements.end(); it != end; ++it)
+ delete *it;
+ elements.erase(removeIt, elements.end());
if (!errors.isEmpty() || elements.isEmpty()) {
if (!errors.isEmpty()) {
@@ -570,17 +616,21 @@ void QQuickStackView::push(QQmlV4Function *args)
return;
}
+#if QT_CONFIG(quick_viewtransitions)
QQuickStackElement *exit = nullptr;
if (!d->elements.isEmpty())
exit = d->elements.top();
+#endif
int oldDepth = d->elements.size();
if (d->pushElements(elements)) {
d->depthChange(d->elements.size(), oldDepth);
QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
d->startTransition(QQuickStackTransition::pushEnter(operation, enter, this),
QQuickStackTransition::pushExit(operation, exit, this),
operation == Immediate);
+#endif
d->setCurrentItem(enter);
}
@@ -625,9 +675,13 @@ void QQuickStackView::push(QQmlV4Function *args)
stackView.pop(null)
\endcode
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l popToItem, \l popToIndex or \l popCurrentItem functions
+ instead.
+
\sa clear(), {Popping Items}, {Unwinding Items via Pop}
*/
-void QQuickStackView::pop(QQmlV4Function *args)
+void QQuickStackView::pop(QQmlV4FunctionPtr args)
{
Q_D(QQuickStackView);
const QString operationName = QStringLiteral("pop");
@@ -663,7 +717,7 @@ void QQuickStackView::pop(QQmlV4Function *args)
enter = d->findElement(item);
if (!enter) {
if (item != d->currentItem)
- d->warn(QStringLiteral("unknown argument: ") + value->toQString()); // TODO: safe?
+ d->warn(QStringLiteral("can't find item to pop: ") + value->toQString());
args->setReturnValue(QV4::Encode::null());
d->elements.push(exit); // restore
return;
@@ -671,14 +725,16 @@ void QQuickStackView::pop(QQmlV4Function *args)
}
}
+#if QT_CONFIG(quick_viewtransitions)
Operation operation = PopTransition;
if (argc > 0) {
QV4::ScopedValue lastArg(scope, (*args)[argc - 1]);
if (lastArg->isInt32())
operation = static_cast<Operation>(lastArg->toInt32());
}
+#endif
- QQuickItem *previousItem = nullptr;
+ QPointer<QQuickItem> previousItem;
if (d->popElements(enter)) {
if (exit) {
@@ -687,9 +743,11 @@ void QQuickStackView::pop(QQmlV4Function *args)
previousItem = exit->item;
}
d->depthChange(d->elements.size(), oldDepth);
+#if QT_CONFIG(quick_viewtransitions)
d->startTransition(QQuickStackTransition::popExit(operation, exit, this),
QQuickStackTransition::popEnter(operation, enter, this),
operation == Immediate);
+#endif
d->setCurrentItem(enter);
}
@@ -759,7 +817,8 @@ void QQuickStackView::pop(QQmlV4Function *args)
\value StackView.ReplaceTransition An operation with replace transitions (since QtQuick.Controls 2.1).
\value StackView.PopTransition An operation with pop transitions (since QtQuick.Controls 2.1).
- If no operation is provided, \c ReplaceTransition will be used.
+ If no operation is provided, \c Immediate will be used if the stack is
+ empty, and \c ReplaceTransition otherwise.
The following example illustrates the use of push and pop transitions with replace().
@@ -789,9 +848,12 @@ void QQuickStackView::pop(QQmlV4Function *args)
}
\endcode
+ \note If you are \l {The QML script compiler}{compiling QML}, use the
+ strongly-typed \l replaceCurrentItem functions instead.
+
\sa push(), {Replacing Items}
*/
-void QQuickStackView::replace(QQmlV4Function *args)
+void QQuickStackView::replace(QQmlV4FunctionPtr args)
{
Q_D(QQuickStackView);
const QString operationName = QStringLiteral("replace");
@@ -812,10 +874,12 @@ void QQuickStackView::replace(QQmlV4Function *args)
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
+#if QT_CONFIG(quick_viewtransitions)
Operation operation = d->elements.isEmpty() ? Immediate : ReplaceTransition;
QV4::ScopedValue lastArg(scope, (*args)[args->length() - 1]);
if (lastArg->isInt32())
operation = static_cast<Operation>(lastArg->toInt32());
+#endif
QQuickStackElement *target = nullptr;
QV4::ScopedValue firstArg(scope, (*args)[0]);
@@ -849,9 +913,11 @@ void QQuickStackView::replace(QQmlV4Function *args)
d->removing.insert(exit);
}
QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
d->startTransition(QQuickStackTransition::replaceExit(operation, exit, this),
QQuickStackTransition::replaceEnter(operation, enter, this),
operation == Immediate);
+#endif
d->setCurrentItem(enter);
}
@@ -864,6 +930,422 @@ void QQuickStackView::replace(QQmlV4Function *args)
}
/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItems(items, operation)
+ \since 6.7
+
+ Pushes \a items onto the stack using an optional \a operation, and
+ optionally applies a set of properties on each element. \a items is an array
+ of elements. Each element can be
+ an \l Item, \l Component, or \l [QML] url and can be followed by an optional
+ properties argument (see below). Returns the item that became
+ current (the last item).
+
+ StackView creates an instance automatically if the pushed element is a
+ \l Component or \l [QML] url, and the instance will be destroyed when it is
+ popped off the stack. See \l {Item Ownership} for more information.
+
+ \include qquickstackview.qdocinc optional-properties-after-each-item
+
+ \code
+ stackView.push([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
+
+ // With properties:
+ stackView.pushItems([
+ item, { "color": "red" },
+ rectComponent, { "color": "green" },
+ Qt.resolvedUrl("MyItem.qml"), { "color": "blue" }
+ ])
+
+ // With properties for only some items:
+ stackView.pushItems([
+ item, { "color": "yellow" },
+ rectComponent
+ ])
+ \endcode
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push a single item, use the relevant \c pushItem function:
+ \list
+ \li \l {stackview-pushitem-item-overload}
+ {pushItem}(item, properties, operation)
+ \li \l {stackview-pushitem-component-overload}
+ {pushItem}(component, properties, operation)
+ \li \l {stackview-pushitem-url-overload}
+ {pushItem}(url, properties, operation)
+ \endlist
+
+ \note Items that already exist in the stack are not pushed.
+
+ \sa initialItem, pushItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItems(QList<QQuickStackViewArg> args, Operation operation)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("pushItem");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ return nullptr;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+
+ const QList<QQuickStackElement *> stackElements = d->parseElements(args);
+
+#if QT_CONFIG(quick_viewtransitions)
+ QQuickStackElement *exit = nullptr;
+ if (!d->elements.isEmpty())
+ exit = d->elements.top();
+#endif
+
+ const int oldDepth = d->elements.size();
+ if (d->pushElements(stackElements)) {
+ d->depthChange(d->elements.size(), oldDepth);
+ QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::pushEnter(operation, enter, this),
+ QQuickStackTransition::pushExit(operation, exit, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ return d->currentItem;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(item, properties, operation)
+ \keyword stackview-pushitem-item-overload
+ \since 6.7
+
+ Pushes an \a item onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(QQuickItem *item, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ item, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(component, properties, operation)
+ \overload pushItem()
+ \keyword stackview-pushitem-component-overload
+ \since 6.7
+
+ Pushes a \a component onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(QQmlComponent *component, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ component, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::pushItem(url, properties, operation)
+ \overload pushItem()
+ \keyword stackview-pushitem-url-overload
+ \since 6.7
+
+ Pushes a \a url onto the stack, optionally applying a set of
+ \a properties, using the optional \a operation. Returns the item that
+ became current (the last item).
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PushTransition will be used.
+
+ To push several items onto the stack, use \l pushItems().
+
+ \sa initialItem, {Pushing Items}
+*/
+QQuickItem *QQuickStackView::pushItem(const QUrl &url, const QVariantMap &properties, Operation operation)
+{
+ return pushItems({ url, properties }, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popToItem(item, operation)
+ \since 6.7
+
+ Pops all items down to (but not including) \a item. Returns the last item
+ removed from the stack.
+
+ If \a item is \c null, a warning is produced and \c null is returned.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ \code
+ stackView.popToItem(someItem, StackView.Immediate)
+ \endcode
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popToItem(QQuickItem *item, Operation operation)
+{
+ Q_D(QQuickStackView);
+ return d->popToItem(item, operation, QQuickStackViewPrivate::CurrentItemPolicy::DoNotPop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popToIndex(index, operation)
+ \since 6.7
+
+ Pops all items down to (but not including) \a index. Returns the last item
+ removed from the stack.
+
+ If \a index is out of bounds, a warning is produced and \c null is
+ returned.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ \code
+ stackView.popToIndex(stackView.depth - 2, StackView.Immediate)
+ \endcode
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popToIndex(int index, Operation operation)
+{
+ Q_D(QQuickStackView);
+ if (index < 0 || index >= d->elements.size()) {
+ d->warn(QString::fromLatin1("popToIndex: index %1 is out of bounds (%2 item(s))")
+ .arg(index).arg(d->elements.size()));
+ return nullptr;
+ }
+
+ if (index == d->elements.size() - 1) {
+ // This would pop down to the current item, which is a no-op.
+ return nullptr;
+ }
+
+ QQuickStackElement *element = d->elements.at(index);
+ element->load(this);
+ return d->popToItem(element->item, operation, QQuickStackViewPrivate::CurrentItemPolicy::Pop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::popCurrentItem(operation)
+ \since 6.7
+
+ Pops \l currentItem from the stack. Returns the last item removed from the
+ stack, or \c null if \l depth was \c 1.
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c PopTransition will be used.
+
+ This function is equivalent to \c popToIndex(stackView.currentIndex - 1).
+
+ \sa clear(), {Popping Items}, {Unwinding Items via Pop}
+*/
+QQuickItem *QQuickStackView::popCurrentItem(Operation operation)
+{
+ Q_D(QQuickStackView);
+ if (d->elements.size() == 1) {
+ auto lastItemRemoved = d->elements.last()->item;
+ clear(operation);
+ return lastItemRemoved;
+ }
+ return d->popToItem(d->currentItem, operation, QQuickStackViewPrivate::CurrentItemPolicy::Pop);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(items, operation)
+ \keyword stackview-replacecurrentitem-items-overload
+ \since 6.7
+
+ Pops \l currentItem from the stack and pushes \a items. If the optional
+ \a operation is specified, the relevant transition will be used. Each item
+ can be followed by an optional set of properties that will be applied to
+ that item. Returns the item that became current.
+
+ \include qquickstackview.qdocinc optional-properties-after-each-item
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ \code
+ stackView.replaceCurrentItem([item, rectComponent, Qt.resolvedUrl("MyItem.qml")])
+
+ // With properties:
+ stackView.replaceCurrentItem([
+ item, { "color": "red" },
+ rectComponent, { "color": "green" },
+ Qt.resolvedUrl("MyItem.qml"), { "color": "blue" }
+ ])
+ \endcode
+
+ To push a single item, use the relevant overload:
+ \list
+ \li \l {stackview-replacecurrentitem-item-overload}
+ {replaceCurrentItem}(item, properties, operation)
+ \li \l {stackview-replacecurrentitem-component-overload}
+ {replaceCurrentItem}(component, properties, operation)
+ \li \l {stackview-replacecurrentitem-url-overload}
+ {replaceCurrentItem}(url, properties, operation)
+ \endlist
+
+ \sa push(), {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(const QList<QQuickStackViewArg> &args,
+ Operation operation)
+{
+ Q_D(QQuickStackView);
+ const QString operationName = QStringLiteral("replace");
+ if (d->modifyingElements) {
+ d->warnOfInterruption(operationName);
+ return nullptr;
+ }
+
+ QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+
+ QQuickStackElement *currentElement = !d->elements.isEmpty() ? d->elements.top() : nullptr;
+
+ const QList<QQuickStackElement *> stackElements = d->parseElements(args);
+
+ int oldDepth = d->elements.size();
+ QQuickStackElement* exit = nullptr;
+ if (!d->elements.isEmpty())
+ exit = d->elements.pop();
+
+ const bool successfullyReplaced = exit != currentElement
+ ? d->replaceElements(currentElement, stackElements)
+ : d->pushElements(stackElements);
+ if (successfullyReplaced) {
+ d->depthChange(d->elements.size(), oldDepth);
+ if (exit) {
+ exit->removal = true;
+ d->removing.insert(exit);
+ }
+ QQuickStackElement *enter = d->elements.top();
+#if QT_CONFIG(quick_viewtransitions)
+ d->startTransition(QQuickStackTransition::replaceExit(operation, exit, this),
+ QQuickStackTransition::replaceEnter(operation, enter, this),
+ operation == Immediate);
+#endif
+ d->setCurrentItem(enter);
+ }
+
+ return d->currentItem;
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(item, properties, operation)
+ \overload replaceCurrentItem()
+ \keyword stackview-replacecurrentitem-item-overload
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {item}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(QQuickItem *item, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(item), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(component, properties, operation)
+ \overload replaceCurrentItem()
+ \keyword stackview-replacecurrentitem-component-overload
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {component}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(QQmlComponent *component, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(component), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
+ \qmlmethod Item QtQuick.Controls::StackView::replaceCurrentItem(url, properties, operation)
+ \keyword stackview-replacecurrentitem-url-overload
+ \overload replaceCurrentItem()
+ \since 6.7
+
+ \include qquickstackview.qdocinc {replaceCurrentItem} {url}
+
+ \include qquickstackview.qdocinc pop-ownership
+
+ \include qquickstackview.qdocinc operation-values
+
+ If no operation is provided, \c ReplaceTransition will be used.
+
+ To push several items onto the stack, use
+ \l {stackview-replacecurrentitem-items-overload}
+ {replaceCurrentItem}(items, operation).
+
+ \sa {Replacing Items}
+*/
+QQuickItem *QQuickStackView::replaceCurrentItem(const QUrl &url, const QVariantMap &properties,
+ Operation operation)
+{
+ const QList<QQuickStackViewArg> args = { QQuickStackViewArg(url), QQuickStackViewArg(properties) };
+ return replaceCurrentItem(args, operation);
+}
+
+/*!
\since QtQuick.Controls 2.3 (Qt 5.10)
\qmlproperty bool QtQuick.Controls::StackView::empty
\readonly
@@ -894,6 +1376,9 @@ bool QQuickStackView::isEmpty() const
*/
void QQuickStackView::clear(Operation operation)
{
+#if !QT_CONFIG(quick_viewtransitions)
+ Q_UNUSED(operation)
+#endif
Q_D(QQuickStackView);
if (d->elements.isEmpty())
return;
@@ -904,8 +1389,11 @@ void QQuickStackView::clear(Operation operation)
return;
}
+ const int oldDepth = d->elements.size();
+
QScopedValueRollback<bool> modifyingElements(d->modifyingElements, true);
QScopedValueRollback<QString> operationNameRollback(d->operation, operationName);
+#if QT_CONFIG(quick_viewtransitions)
if (operation != Immediate) {
QQuickStackElement *exit = d->elements.pop();
exit->removal = true;
@@ -913,8 +1401,8 @@ void QQuickStackView::clear(Operation operation)
d->startTransition(QQuickStackTransition::popExit(operation, exit, this),
QQuickStackTransition::popEnter(operation, nullptr, this), false);
}
+#endif
- int oldDepth = d->elements.size();
d->setCurrentItem(nullptr);
qDeleteAll(d->elements);
d->elements.clear();
@@ -945,6 +1433,7 @@ void QQuickStackView::setInitialItem(const QJSValue &item)
d->initialItem = item;
}
+#if QT_CONFIG(quick_viewtransitions)
/*!
\qmlproperty Transition QtQuick.Controls::StackView::popEnter
@@ -1106,6 +1595,7 @@ void QQuickStackView::setReplaceExit(QQuickTransition *exit)
d->transitioner->moveDisplacedTransition = exit;
emit replaceExitChanged();
}
+#endif
void QQuickStackView::componentComplete()
{
diff --git a/src/quicktemplates/qquickstackview_p.cpp b/src/quicktemplates/qquickstackview_p.cpp
index 99127d84b6..0288ff1f4b 100644
--- a/src/quicktemplates/qquickstackview_p.cpp
+++ b/src/quicktemplates/qquickstackview_p.cpp
@@ -3,8 +3,11 @@
#include "qquickstackview_p_p.h"
#include "qquickstackelement_p_p.h"
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickstacktransition_p_p.h"
+#endif
+#include <QtCore/qscopedvaluerollback.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmllist.h>
#include <QtQml/private/qv4qmlcontext_p.h>
@@ -46,7 +49,7 @@ void QQuickStackViewPrivate::setCurrentItem(QQuickStackElement *element)
emit q->currentItemChanged();
}
-static bool initProperties(QQuickStackElement *element, const QV4::Value &props, QQmlV4Function *args)
+static bool initProperties(QQuickStackElement *element, const QV4::Value &props, QQmlV4FunctionPtr args)
{
if (props.isObject()) {
const QV4::QObjectWrapper *wrapper = props.as<QV4::QObjectWrapper>();
@@ -60,7 +63,7 @@ static bool initProperties(QQuickStackElement *element, const QV4::Value &props,
return false;
}
-QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(int from, QQmlV4Function *args, QStringList *errors)
+QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(int from, QQmlV4FunctionPtr args, QStringList *errors)
{
QV4::ExecutionEngine *v4 = args->v4engine();
auto context = v4->callingQmlContext();
@@ -106,6 +109,47 @@ QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(int from, QQml
return elements;
}
+QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(const QList<QQuickStackViewArg> &args)
+{
+ Q_Q(QQuickStackView);
+ QList<QQuickStackElement *> stackElements;
+ for (int i = 0; i < args.size(); ++i) {
+ const QQuickStackViewArg &arg = args.at(i);
+ QVariantMap properties;
+ // Look ahead at the next arg in case it contains properties for this
+ // Item/Component/URL.
+ if (i < args.size() - 1) {
+ const QQuickStackViewArg &nextArg = args.at(i + 1);
+ // If mProperties isn't empty, the user passed properties.
+ // If it is empty, but mItem, mComponent and mUrl also are,
+ // then they passed an empty property map.
+ if (!nextArg.mProperties.isEmpty()
+ || (!nextArg.mItem && !nextArg.mComponent && !nextArg.mUrl.isValid())) {
+ properties = nextArg.mProperties;
+ ++i;
+ }
+ }
+
+ // Remove any items that are already in the stack, as they can't be in two places at once.
+ if (findElement(arg.mItem))
+ continue;
+
+ // We look ahead one index for each Item/Component/URL, so if this arg is
+ // a property map, the user has passed two or more in a row.
+ if (!arg.mProperties.isEmpty()) {
+ qmlWarning(q) << "Properties must come after an Item, Component or URL";
+ return {};
+ }
+
+ QQuickStackElement *element = QQuickStackElement::fromStackViewArg(q, arg);
+ QV4::ExecutionEngine *v4Engine = qmlEngine(q)->handle();
+ element->properties.set(v4Engine, v4Engine->fromVariant(properties));
+ element->qmlCallingContext.set(v4Engine, v4Engine->qmlContext());
+ stackElements.append(element);
+ }
+ return stackElements;
+}
+
QQuickStackElement *QQuickStackViewPrivate::findElement(QQuickItem *item) const
{
if (item) {
@@ -204,6 +248,80 @@ bool QQuickStackViewPrivate::replaceElements(QQuickStackElement *target, const Q
return pushElements(elems);
}
+QQuickItem *QQuickStackViewPrivate::popToItem(QQuickItem *item, QQuickStackView::Operation operation, CurrentItemPolicy currentItemPolicy)
+{
+ const QString operationName = QStringLiteral("pop");
+ if (modifyingElements) {
+ warnOfInterruption(operationName);
+ return nullptr;
+ }
+
+ QScopedValueRollback<bool> modifyingElementsRollback(modifyingElements, true);
+ QScopedValueRollback<QString> operationNameRollback(this->operation, operationName);
+ if (elements.isEmpty()) {
+ warn(QStringLiteral("no items to pop"));
+ return nullptr;
+ }
+
+ if (!item) {
+ warn(QStringLiteral("item cannot be null"));
+ return nullptr;
+ }
+
+ const int oldDepth = elements.size();
+ QQuickStackElement *exit = elements.pop();
+ // top() here will be the item below the previously current item, since we just popped above.
+ QQuickStackElement *enter = elements.top();
+
+ bool nothingToDo = false;
+ if (item != currentItem) {
+ if (!item) {
+ // Popping down to the first item.
+ enter = elements.value(0);
+ } else {
+ // Popping down to an arbitrary item.
+ enter = findElement(item);
+ if (!enter) {
+ warn(QStringLiteral("can't find item to pop: ") + QDebug::toString(item));
+ nothingToDo = true;
+ }
+ }
+ } else {
+ if (currentItemPolicy == CurrentItemPolicy::DoNotPop) {
+ // popToItem was called with the currentItem, which means there are no items
+ // to pop because it's already at the top.
+ nothingToDo = true;
+ }
+ // else: popToItem was called by popCurrentItem, and so we _should_ pop.
+ }
+ if (nothingToDo) {
+ // Restore the element we popped earlier.
+ elements.push(exit);
+ return nullptr;
+ }
+
+ QQuickItem *previousItem = nullptr;
+ if (popElements(enter)) {
+ if (exit) {
+ exit->removal = true;
+ removing.insert(exit);
+ previousItem = exit->item;
+ }
+ depthChange(elements.size(), oldDepth);
+#if QT_CONFIG(quick_viewtransitions)
+ Q_Q(QQuickStackView);
+ startTransition(QQuickStackTransition::popExit(operation, exit, q),
+ QQuickStackTransition::popEnter(operation, enter, q),
+ operation == QQuickStackView::Immediate);
+#else
+ Q_UNUSED(operation);
+#endif
+ setCurrentItem(enter);
+ }
+ return previousItem;
+}
+
+#if QT_CONFIG(quick_viewtransitions)
void QQuickStackViewPrivate::ensureTransitioner()
{
if (!transitioner) {
@@ -301,6 +419,7 @@ void QQuickStackViewPrivate::viewItemTransitionFinished(QQuickItemViewTransition
removing.remove(element);
}
+#endif
void QQuickStackViewPrivate::setBusy(bool b)
{
diff --git a/src/quicktemplates/qquickstackview_p.h b/src/quicktemplates/qquickstackview_p.h
index b36976a395..8606759a7c 100644
--- a/src/quicktemplates/qquickstackview_p.h
+++ b/src/quicktemplates/qquickstackview_p.h
@@ -15,30 +15,67 @@
// We mean it.
//
+#include <QtCore/qdebug.h>
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
QT_BEGIN_NAMESPACE
-class QQmlV4Function;
class QQuickTransition;
class QQuickStackElement;
class QQuickStackViewPrivate;
class QQuickStackViewAttached;
class QQuickStackViewAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickStackView : public QQuickControl
+/*!
+ \internal
+
+ Input from the user that is turned into QQuickStackElements.
+
+ This was added to support the QML-compiler-friendly pushElement[s]
+ functions.
+*/
+class QQuickStackViewArg
+{
+ Q_GADGET
+ QML_CONSTRUCTIBLE_VALUE
+ QML_ANONYMOUS
+
+public:
+ QQuickStackViewArg() = default;
+ Q_INVOKABLE QQuickStackViewArg(QQuickItem *item);
+ Q_INVOKABLE QQuickStackViewArg(const QUrl &url);
+ Q_INVOKABLE QQuickStackViewArg(QQmlComponent *component);
+ Q_INVOKABLE QQuickStackViewArg(const QVariantMap &properties);
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend QDebug operator<<(QDebug debug, const QQuickStackViewArg &arg);
+#endif
+
+private:
+ friend class QQuickStackViewPrivate;
+ friend class QQuickStackElement;
+
+ QQuickItem *mItem = nullptr;
+ QQmlComponent *mComponent = nullptr;
+ QUrl mUrl;
+ QVariantMap mProperties;
+};
+
+class Q_QUICKTEMPLATES2_EXPORT QQuickStackView : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(bool busy READ isBusy NOTIFY busyChanged FINAL)
Q_PROPERTY(int depth READ depth NOTIFY depthChanged FINAL)
Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged FINAL)
Q_PROPERTY(QJSValue initialItem READ initialItem WRITE setInitialItem FINAL)
+#if QT_CONFIG(quick_viewtransitions)
Q_PROPERTY(QQuickTransition *popEnter READ popEnter WRITE setPopEnter NOTIFY popEnterChanged FINAL)
Q_PROPERTY(QQuickTransition *popExit READ popExit WRITE setPopExit NOTIFY popExitChanged FINAL)
Q_PROPERTY(QQuickTransition *pushEnter READ pushEnter WRITE setPushEnter NOTIFY pushEnterChanged FINAL)
Q_PROPERTY(QQuickTransition *pushExit READ pushExit WRITE setPushExit NOTIFY pushExitChanged FINAL)
Q_PROPERTY(QQuickTransition *replaceEnter READ replaceEnter WRITE setReplaceEnter NOTIFY replaceEnterChanged FINAL)
Q_PROPERTY(QQuickTransition *replaceExit READ replaceExit WRITE setReplaceExit NOTIFY replaceExitChanged FINAL)
+#endif
// 2.3 (Qt 5.10)
Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged FINAL REVISION(2, 3))
QML_NAMED_ELEMENT(StackView)
@@ -66,6 +103,7 @@ public:
QJSValue initialItem() const;
void setInitialItem(const QJSValue &item);
+#if QT_CONFIG(quick_viewtransitions)
QQuickTransition *popEnter() const;
void setPopEnter(QQuickTransition *enter);
@@ -83,6 +121,7 @@ public:
QQuickTransition *replaceExit() const;
void setReplaceExit(QQuickTransition *exit);
+#endif
enum LoadBehavior {
DontLoad,
@@ -90,8 +129,8 @@ public:
};
Q_ENUM(LoadBehavior)
- Q_INVOKABLE QQuickItem *get(int index, LoadBehavior behavior = DontLoad);
- Q_INVOKABLE QQuickItem *find(const QJSValue &callback, LoadBehavior behavior = DontLoad);
+ Q_INVOKABLE QQuickItem *get(int index, QQuickStackView::LoadBehavior behavior = DontLoad);
+ Q_INVOKABLE QQuickItem *find(const QJSValue &callback, QQuickStackView::LoadBehavior behavior = DontLoad);
enum Operation {
Transition = -1, // ### Deprecated in Qt 6; remove in Qt 7.
@@ -102,9 +141,31 @@ public:
};
Q_ENUM(Operation)
- Q_INVOKABLE void push(QQmlV4Function *args);
- Q_INVOKABLE void pop(QQmlV4Function *args);
- Q_INVOKABLE void replace(QQmlV4Function *args);
+ Q_INVOKABLE void push(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void pop(QQmlV4FunctionPtr args);
+ Q_INVOKABLE void replace(QQmlV4FunctionPtr args);
+
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *pushItems(QList<QQuickStackViewArg> args,
+ Operation operation = Immediate);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *pushItem(QQuickItem *item, const QVariantMap &properties = {},
+ Operation operation = Immediate);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *pushItem(QQmlComponent *component, const QVariantMap &properties = {},
+ Operation operation = Immediate);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *pushItem(const QUrl &url, const QVariantMap &properties = {},
+ Operation operation = Immediate);
+
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *popToItem(QQuickItem *item, Operation operation = PopTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *popToIndex(int index, Operation operation = PopTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *popCurrentItem(Operation operation = PopTransition);
+
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(const QList<QQuickStackViewArg> &args,
+ Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(QQuickItem *item,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(QQmlComponent *component,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
+ Q_REVISION(6, 7) Q_INVOKABLE QQuickItem *replaceCurrentItem(const QUrl &url,
+ const QVariantMap &properties = {}, Operation operation = ReplaceTransition);
// 2.3 (Qt 5.10)
bool isEmpty() const;
@@ -116,12 +177,14 @@ Q_SIGNALS:
void busyChanged();
void depthChanged();
void currentItemChanged();
+#if QT_CONFIG(quick_viewtransitions)
void popEnterChanged();
void popExitChanged();
void pushEnterChanged();
void pushExitChanged();
void replaceEnterChanged();
void replaceExitChanged();
+#endif
// 2.3 (Qt 5.10)
Q_REVISION(2, 3) void emptyChanged();
@@ -143,7 +206,7 @@ private:
Q_DECLARE_PRIVATE(QQuickStackView)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickStackViewAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickStackViewAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
@@ -185,6 +248,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickStackView)
-
#endif // QQUICKSTACKVIEW_P_H
diff --git a/src/quicktemplates/qquickstackview_p_p.h b/src/quicktemplates/qquickstackview_p_p.h
index 2b86458edc..7d6ee50b1b 100644
--- a/src/quicktemplates/qquickstackview_p_p.h
+++ b/src/quicktemplates/qquickstackview_p_p.h
@@ -17,7 +17,9 @@
#include <QtQuickTemplates2/private/qquickstackview_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
+#if QT_CONFIG(quick_viewtransitions)
#include <QtQuick/private/qquickitemviewtransition_p.h>
+#endif
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQml/private/qv4value_p.h>
#include <QtQml/private/qqmlcontextdata_p.h>
@@ -29,7 +31,10 @@ QT_BEGIN_NAMESPACE
class QQuickStackElement;
struct QQuickStackTransition;
-class QQuickStackViewPrivate : public QQuickControlPrivate, public QQuickItemViewTransitionChangeListener
+class QQuickStackViewPrivate : public QQuickControlPrivate
+#if QT_CONFIG(quick_viewtransitions)
+ , public QQuickItemViewTransitionChangeListener
+#endif
{
Q_DECLARE_PUBLIC(QQuickStackView)
@@ -44,7 +49,8 @@ public:
void setCurrentItem(QQuickStackElement *element);
- QList<QQuickStackElement *> parseElements(int from, QQmlV4Function *args, QStringList *errors);
+ QList<QQuickStackElement *> parseElements(int from, QQmlV4FunctionPtr args, QStringList *errors);
+ QList<QQuickStackElement *> parseElements(const QList<QQuickStackViewArg> &args);
QQuickStackElement *findElement(QQuickItem *item) const;
QQuickStackElement *findElement(const QV4::Value &value) const;
QQuickStackElement *createElement(const QV4::Value &value, const QQmlRefPointer<QQmlContextData> &context, QString *error);
@@ -53,11 +59,19 @@ public:
bool popElements(QQuickStackElement *element);
bool replaceElements(QQuickStackElement *element, const QList<QQuickStackElement *> &elements);
+ enum class CurrentItemPolicy {
+ DoNotPop,
+ Pop
+ };
+ QQuickItem *popToItem(QQuickItem *item, QQuickStackView::Operation operation, CurrentItemPolicy currentItemPolicy);
+
+#if QT_CONFIG(quick_viewtransitions)
void ensureTransitioner();
void startTransition(const QQuickStackTransition &first, const QQuickStackTransition &second, bool immediate);
void completeTransition(QQuickStackElement *element, QQuickTransition *transition, QQuickStackView::Status status);
void viewItemTransitionFinished(QQuickItemViewTransitionableItem *item) override;
+#endif
void setBusy(bool busy);
void depthChange(int newDepth, int oldDepth);
@@ -69,10 +83,15 @@ public:
QSet<QQuickStackElement*> removing;
QList<QQuickStackElement*> removed;
QStack<QQuickStackElement *> elements;
+#if QT_CONFIG(quick_viewtransitions)
QQuickItemViewTransitioner *transitioner = nullptr;
+#endif
};
-class QQuickStackViewAttachedPrivate : public QObjectPrivate, public QQuickItemChangeListener
+class QQuickStackViewAttachedPrivate : public QObjectPrivate
+//#if QT_CONFIG(quick_viewtransitions)
+ , public QQuickItemChangeListener
+//#endif
{
Q_DECLARE_PUBLIC(QQuickStackViewAttached)
@@ -82,7 +101,9 @@ public:
return attached->d_func();
}
+//#if QT_CONFIG(quick_viewtransitions)
void itemParentChanged(QQuickItem *item, QQuickItem *parent) override;
+//#endif
bool explicitVisible = false;
QQuickStackElement *element = nullptr;
diff --git a/src/quicktemplates/qquickswipe_p.h b/src/quicktemplates/qquickswipe_p.h
index 4694948763..44eadcb584 100644
--- a/src/quicktemplates/qquickswipe_p.h
+++ b/src/quicktemplates/qquickswipe_p.h
@@ -26,7 +26,7 @@ class QQuickItem;
class QQuickTransition;
class QQuickSwipePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipe : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipe : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL)
diff --git a/src/quicktemplates/qquickswipedelegate.cpp b/src/quicktemplates/qquickswipedelegate.cpp
index 24ca772b06..36782fee8c 100644
--- a/src/quicktemplates/qquickswipedelegate.cpp
+++ b/src/quicktemplates/qquickswipedelegate.cpp
@@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE
\image qtquickcontrols-swipedelegate-behind.gif
- \sa {Customizing SwipeDelegate}, {Delegate Controls}, {Qt Quick Controls 2 - Swipe to Remove}{Swipe to Remove Example}
+ \sa {Customizing SwipeDelegate}, {Delegate Controls}, {Qt Quick Controls 2 - Gallery}{Gallery Example}
*/
namespace {
@@ -194,6 +194,7 @@ QQuickItem *QQuickSwipePrivate::createDelegateItem(QQmlComponent *component)
if (item) {
item->setParentItem(control);
component->completeCreate();
+ QJSEngine::setObjectOwnership(item, QJSEngine::JavaScriptOwnership);
}
return item;
}
@@ -770,12 +771,18 @@ bool QQuickSwipeDelegatePrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEv
if (!swipePrivate->left && !swipePrivate->right && !swipePrivate->behind)
return false;
+ if (item != q && swipePrivate->complete) {
+ // If the delegate is swiped open, send the event to the exposed item,
+ // in case it's an interactive child (like a Button).
+ const auto posInItem = item->mapToItem(q, event->position().toPoint());
+ forwardMouseEvent(event, item, posInItem);
+ }
+
// Don't handle move events for the control if it wasn't pressed.
if (item == q && !pressed)
return false;
- const QPointF mappedEventPos = item->mapToItem(q, event->position().toPoint());
- const qreal distance = (mappedEventPos - pressPoint).x();
+ const qreal distance = (event->globalPosition() - event->points().first().globalPressPosition()).x();
if (!q->keepMouseGrab()) {
// We used to use the custom threshold that QQuickDrawerPrivate::grabMouse used,
// but since it's larger than what Flickable uses, it results in Flickable
@@ -998,6 +1005,33 @@ QPalette QQuickSwipeDelegatePrivate::defaultPalette() const
return QQuickTheme::palette(QQuickTheme::ListView);
}
+/*! \internal
+ Recursively search right and/or left item tree of swipe delegate for any item that
+ contains the \a event position.
+
+ Returns the first such item found, otherwise \c nullptr.
+*/
+QQuickItem *QQuickSwipeDelegatePrivate::getPressedItem(QQuickItem *childItem, QMouseEvent *event) const
+{
+ if (!childItem || !event)
+ return nullptr;
+
+ QQuickItem *item = nullptr;
+
+ if (childItem->acceptedMouseButtons() &&
+ childItem->contains(childItem->mapFromScene(event->scenePosition()))) {
+ item = childItem;
+ } else {
+ const auto &childItems = childItem->childItems();
+ for (const auto &child: childItems) {
+ if ((item = getPressedItem(child, event)))
+ break;
+ }
+ }
+
+ return item;
+}
+
QQuickSwipeDelegate::QQuickSwipeDelegate(QQuickItem *parent)
: QQuickItemDelegate(*(new QQuickSwipeDelegatePrivate(this)), parent)
{
@@ -1240,17 +1274,11 @@ void QQuickSwipeDelegate::mousePressEvent(QMouseEvent *event)
swipePrivate->velocityCalculator.startMeasuring(event->position().toPoint(), event->timestamp());
if (swipePrivate->complete) {
- auto item = d->swipe.rightItem();
- if (item && item->contains(item->mapFromScene(event->scenePosition()))) {
- d->pressedItem = item;
- d->handleMousePressEvent(item, event);
- } else {
- item = d->swipe.leftItem();
- if (item && item->contains(item->mapFromScene(event->scenePosition()))) {
- d->pressedItem = item;
- d->handleMousePressEvent(item, event);
- }
- }
+ d->pressedItem = d->getPressedItem(d->swipe.rightItem(), event);
+ if (!d->pressedItem)
+ d->pressedItem = d->getPressedItem(d->swipe.leftItem(), event);
+ if (d->pressedItem)
+ d->handleMousePressEvent(d->pressedItem, event);
}
}
diff --git a/src/quicktemplates/qquickswipedelegate_p.h b/src/quicktemplates/qquickswipedelegate_p.h
index 2fadabe09f..fae6c6136d 100644
--- a/src/quicktemplates/qquickswipedelegate_p.h
+++ b/src/quicktemplates/qquickswipedelegate_p.h
@@ -24,7 +24,7 @@ class QQuickSwipeDelegatePrivate;
class QQuickSwipeDelegateAttached;
class QQuickSwipeDelegateAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipeDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(QQuickSwipe *swipe READ swipe CONSTANT FINAL)
@@ -64,7 +64,7 @@ private:
Q_DECLARE_PRIVATE(QQuickSwipeDelegate)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeDelegateAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipeDelegateAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged FINAL)
@@ -86,7 +86,6 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSwipeDelegate)
Q_DECLARE_METATYPE(QQuickSwipeDelegate::Side)
#endif // QQUICKSWIPEDELEGATE_P_H
diff --git a/src/quicktemplates/qquickswipedelegate_p_p.h b/src/quicktemplates/qquickswipedelegate_p_p.h
index 4963bb0ff7..78034badd3 100644
--- a/src/quicktemplates/qquickswipedelegate_p_p.h
+++ b/src/quicktemplates/qquickswipedelegate_p_p.h
@@ -34,6 +34,7 @@ public:
bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event);
void forwardMouseEvent(QMouseEvent *event, QQuickItem *destination, QPointF localPos);
bool attachedObjectsSetPressed(QQuickItem *item, QPointF scenePos, bool pressed, bool cancel = false);
+ QQuickItem *getPressedItem(QQuickItem *childItem, QMouseEvent *event) const;
void resizeContent() override;
void resizeBackground() override;
diff --git a/src/quicktemplates/qquickswipeview.cpp b/src/quicktemplates/qquickswipeview.cpp
index fe647f5764..425f7a5e1a 100644
--- a/src/quicktemplates/qquickswipeview.cpp
+++ b/src/quicktemplates/qquickswipeview.cpp
@@ -77,7 +77,7 @@ class QQuickSwipeViewPrivate : public QQuickContainerPrivate
Q_DECLARE_PUBLIC(QQuickSwipeView)
public:
- void resizeItem(QQuickItem *item);
+ void resizeItem(int index, QQuickItem *item);
void resizeItems();
static QQuickSwipeViewPrivate *get(QQuickSwipeView *view);
@@ -111,25 +111,29 @@ public:
int currentIndex = -1;
};
+void QQuickSwipeViewPrivate::resizeItem(int index, QQuickItem *item)
+{
+ QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
+ // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
+ if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) {
+ qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
+ item->setProperty("_q_QQuickSwipeView_warned", true);
+ }
+ if (orientation == Qt::Horizontal)
+ item->setPosition({index * (contentItem->width() + spacing), 0});
+ else
+ item->setPosition({0, index * (contentItem->height() + spacing)});
+ item->setSize(QSizeF(contentItem->width(), contentItem->height()));
+}
+
void QQuickSwipeViewPrivate::resizeItems()
{
Q_Q(QQuickSwipeView);
const int count = q->count();
for (int i = 0; i < count; ++i) {
QQuickItem *item = itemAt(i);
- if (item) {
- QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
- // TODO: expose QQuickAnchorLine so we can test for other conflicting anchors
- if (anchors && (anchors->fill() || anchors->centerIn()) && !item->property("_q_QQuickSwipeView_warned").toBool()) {
- qmlWarning(item) << "SwipeView has detected conflicting anchors. Unable to layout the item.";
- item->setProperty("_q_QQuickSwipeView_warned", true);
- }
- if (orientation == Qt::Horizontal)
- item->setPosition({i * (contentItem->width() + spacing), 0});
- else
- item->setPosition({0, i * (contentItem->height() + spacing)});
- item->setSize(QSizeF(contentItem->width(), contentItem->height()));
- }
+ if (item)
+ resizeItem(i, item);
}
}
@@ -268,6 +272,13 @@ QQuickSwipeViewAttached *QQuickSwipeView::qmlAttachedProperties(QObject *object)
return new QQuickSwipeViewAttached(object);
}
+void QQuickSwipeView::componentComplete()
+{
+ Q_D(QQuickSwipeView);
+ QQuickContainer::componentComplete();
+ d->resizeItems();
+}
+
void QQuickSwipeView::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickSwipeView);
@@ -279,7 +290,7 @@ void QQuickSwipeView::itemAdded(int index, QQuickItem *item)
{
Q_D(QQuickSwipeView);
if (isComponentComplete())
- item->setSize(QSizeF(d->contentItem->width(), d->contentItem->height()));
+ d->resizeItem(index, item);
QQuickSwipeViewAttached *attached = qobject_cast<QQuickSwipeViewAttached *>(qmlAttachedPropertiesObject<QQuickSwipeView>(item));
if (attached)
QQuickSwipeViewAttachedPrivate::get(attached)->update(this, index);
diff --git a/src/quicktemplates/qquickswipeview_p.h b/src/quicktemplates/qquickswipeview_p.h
index 84e4b51b2a..758de23ffe 100644
--- a/src/quicktemplates/qquickswipeview_p.h
+++ b/src/quicktemplates/qquickswipeview_p.h
@@ -17,12 +17,14 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickSwipeViewAttached;
class QQuickSwipeViewPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeView : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipeView : public QQuickContainer
{
Q_OBJECT
// 2.1 (Qt 5.8)
@@ -60,6 +62,7 @@ Q_SIGNALS:
Q_REVISION(2, 2) void orientationChanged();
protected:
+ void componentComplete() override;
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void itemAdded(int index, QQuickItem *item) override;
void itemMoved(int index, QQuickItem *item) override;
@@ -76,7 +79,7 @@ private:
class QQuickSwipeViewAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipeViewAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwipeViewAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
@@ -112,6 +115,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSwipeView)
-
#endif // QQUICKSWIPEVIEW_P_H
diff --git a/src/quicktemplates/qquickswitch_p.h b/src/quicktemplates/qquickswitch_p.h
index 9044b066b9..4ee45e8d50 100644
--- a/src/quicktemplates/qquickswitch_p.h
+++ b/src/quicktemplates/qquickswitch_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickSwitchPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwitch : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwitch : public QQuickAbstractButton
{
Q_OBJECT
Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -61,6 +61,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSwitch)
-
#endif // QQUICKSWITCH_P_H
diff --git a/src/quicktemplates/qquickswitchdelegate_p.h b/src/quicktemplates/qquickswitchdelegate_p.h
index d6a6c830b3..26823c3fa8 100644
--- a/src/quicktemplates/qquickswitchdelegate_p.h
+++ b/src/quicktemplates/qquickswitchdelegate_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickSwitchDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwitchDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickSwitchDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -61,6 +61,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickSwitchDelegate)
-
#endif // QQUICKSWITCHDELEGATE_P_H
diff --git a/src/quicktemplates/qquicktabbar_p.h b/src/quicktemplates/qquicktabbar_p.h
index f062a01b1a..f0b7ebb12f 100644
--- a/src/quicktemplates/qquicktabbar_p.h
+++ b/src/quicktemplates/qquicktabbar_p.h
@@ -17,13 +17,15 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
+QT_REQUIRE_CONFIG(quicktemplates2_container);
+
QT_BEGIN_NAMESPACE
class QQuickTabBarPrivate;
class QQuickTabBarAttached;
class QQuickTabBarAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabBar : public QQuickContainer
+class Q_QUICKTEMPLATES2_EXPORT QQuickTabBar : public QQuickContainer
{
Q_OBJECT
Q_PROPERTY(Position position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -71,7 +73,7 @@ private:
Q_DECLARE_PRIVATE(QQuickTabBar)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabBarAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickTabBarAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ index NOTIFY indexChanged FINAL)
@@ -97,6 +99,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTabBar)
-
#endif // QQUICKTABBAR_P_H
diff --git a/src/quicktemplates/qquicktabbutton.cpp b/src/quicktemplates/qquicktabbutton.cpp
index 500ee0e81b..1c8402fd23 100644
--- a/src/quicktemplates/qquicktabbutton.cpp
+++ b/src/quicktemplates/qquicktabbutton.cpp
@@ -31,7 +31,7 @@ QT_BEGIN_NAMESPACE
\sa TabBar, {Customizing TabButton}, {Button Controls}, {Navigation Controls}
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabButtonPrivate : public QQuickAbstractButtonPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickTabButtonPrivate : public QQuickAbstractButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickTabButton)
diff --git a/src/quicktemplates/qquicktabbutton_p.h b/src/quicktemplates/qquicktabbutton_p.h
index 7b2fbdb2fe..1ba7ac5f5a 100644
--- a/src/quicktemplates/qquicktabbutton_p.h
+++ b/src/quicktemplates/qquicktabbutton_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickTabButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTabButton : public QQuickAbstractButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickTabButton : public QQuickAbstractButton
{
Q_OBJECT
QML_NAMED_ELEMENT(TabButton)
@@ -43,6 +43,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTabButton)
-
#endif // QQUICKTABBUTTON_P_H
diff --git a/src/quicktemplates/qquicktextarea.cpp b/src/quicktemplates/qquicktextarea.cpp
index d43f78a5a3..a53f4ac542 100644
--- a/src/quicktemplates/qquicktextarea.cpp
+++ b/src/quicktemplates/qquicktextarea.cpp
@@ -121,16 +121,10 @@ using namespace Qt::StringLiterals;
QQuickTextAreaPrivate::QQuickTextAreaPrivate()
{
-#if QT_CONFIG(accessibility)
- QAccessible::installActivationObserver(this);
-#endif
}
QQuickTextAreaPrivate::~QQuickTextAreaPrivate()
{
-#if QT_CONFIG(accessibility)
- QAccessible::removeActivationObserver(this);
-#endif
}
void QQuickTextAreaPrivate::setTopInset(qreal value, bool reset)
@@ -373,8 +367,8 @@ void QQuickTextAreaPrivate::resizeFlickableContent()
if (!flickable)
return;
- flickable->setContentWidth(q->contentWidth() + q->leftPadding() + q->rightPadding());
- flickable->setContentHeight(q->contentHeight() + q->topPadding() + q->bottomPadding());
+ flickable->setContentWidth(q->implicitWidth());
+ flickable->setContentHeight(q->implicitHeight());
}
void QQuickTextAreaPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
@@ -420,37 +414,15 @@ void QQuickTextAreaPrivate::implicitHeightChanged()
emit q->implicitHeightChanged3();
}
-void QQuickTextAreaPrivate::readOnlyChanged(bool isReadOnly)
-{
- Q_UNUSED(isReadOnly);
-#if QT_CONFIG(accessibility)
- if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(q_func()))
- accessibleAttached->set_readOnly(isReadOnly);
-#endif
-}
-
#if QT_CONFIG(accessibility)
void QQuickTextAreaPrivate::accessibilityActiveChanged(bool active)
{
- if (!active)
- return;
-
- Q_Q(QQuickTextArea);
- QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
- Q_ASSERT(accessibleAttached);
- accessibleAttached->setRole(accessibleRole());
- accessibleAttached->set_readOnly(q->isReadOnly());
- accessibleAttached->setDescription(placeholder);
-}
-
-QAccessible::Role QQuickTextAreaPrivate::accessibleRole() const
-{
- return QAccessible::EditableText;
+ QQuickTextEditPrivate::accessibilityActiveChanged(active);
+ if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(q_func()))
+ accessibleAttached->setDescription(placeholder);
}
#endif
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickTextAreaPrivate::cancelBackground()
{
Q_Q(QQuickTextArea);
@@ -500,6 +472,16 @@ QPalette QQuickTextAreaPrivate::defaultPalette() const
return QQuickTheme::palette(QQuickTheme::TextArea);
}
+bool QQuickTextAreaPrivate::setLastFocusChangeReason(Qt::FocusReason reason)
+{
+ Q_Q(QQuickTextArea);
+ const auto focusReasonChanged = QQuickItemPrivate::setLastFocusChangeReason(reason);
+ if (focusReasonChanged)
+ emit q->focusReasonChanged();
+
+ return focusReasonChanged;
+}
+
QQuickTextArea::QQuickTextArea(QQuickItem *parent)
: QQuickTextEdit(*(new QQuickTextAreaPrivate), parent)
{
@@ -509,8 +491,6 @@ QQuickTextArea::QQuickTextArea(QQuickItem *parent)
d->setImplicitResizeEnabled(false);
d->pressHandler.control = this;
- QObjectPrivate::connect(this, &QQuickTextEdit::readOnlyChanged,
- d, &QQuickTextAreaPrivate::readOnlyChanged);
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
if (qEnvironmentVariable("QT_QUICK_CONTROLS_TEXT_SELECTION_BEHAVIOR") == u"old"_s)
QQuickTextEdit::setOldSelectionDefault();
@@ -667,24 +647,35 @@ void QQuickTextArea::setPlaceholderTextColor(const QColor &color)
/*!
\qmlproperty enumeration QtQuick.Controls::TextArea::focusReason
- \include qquickcontrol-focusreason.qdocinc
+ This property holds the reason of the last focus change.
+
+ \note This property does not indicate whether the item has \l {Item::activeFocus}
+ {active focus}, but the reason why the item either gained or lost focus.
+
+ \value Qt.MouseFocusReason A mouse action occurred.
+ \value Qt.TabFocusReason The Tab key was pressed.
+ \value Qt.BacktabFocusReason A Backtab occurred. The input for this may include the Shift or Control keys; e.g. Shift+Tab.
+ \value Qt.ActiveWindowFocusReason The window system made this window either active or inactive.
+ \value Qt.PopupFocusReason The application opened/closed a pop-up that grabbed/released the keyboard focus.
+ \value Qt.ShortcutFocusReason The user typed a label's buddy shortcut
+ \value Qt.MenuBarFocusReason The menu bar took focus.
+ \value Qt.OtherFocusReason Another reason, usually application-specific.
+
+ \note Prefer \l {Item::focusReason} to this property.
*/
Qt::FocusReason QQuickTextArea::focusReason() const
{
Q_D(const QQuickTextArea);
- return d->focusReason;
+ return d->lastFocusChangeReason();
}
void QQuickTextArea::setFocusReason(Qt::FocusReason reason)
{
Q_D(QQuickTextArea);
- if (d->focusReason == reason)
- return;
-
- d->focusReason = reason;
- emit focusReasonChanged();
+ d->setLastFocusChangeReason(reason);
}
+
bool QQuickTextArea::contains(const QPointF &point) const
{
Q_D(const QQuickTextArea);
@@ -928,10 +919,6 @@ void QQuickTextArea::componentComplete()
if (!d->explicitHoverEnabled)
setAcceptHoverEvents(QQuickControlPrivate::calcHoverEnabled(d->parentItem));
#endif
-#if QT_CONFIG(accessibility)
- if (QAccessible::isActive())
- d->accessibilityActiveChanged(true);
-#endif
}
void QQuickTextArea::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
@@ -1014,13 +1001,11 @@ QSGNode *QQuickTextArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
void QQuickTextArea::focusInEvent(QFocusEvent *event)
{
QQuickTextEdit::focusInEvent(event);
- setFocusReason(event->reason());
}
void QQuickTextArea::focusOutEvent(QFocusEvent *event)
{
QQuickTextEdit::focusOutEvent(event);
- setFocusReason(event->reason());
}
#if QT_CONFIG(quicktemplates2_hover)
@@ -1046,7 +1031,7 @@ void QQuickTextArea::mousePressEvent(QMouseEvent *event)
d->pressHandler.mousePressEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
// Calling the base class implementation will result in QQuickTextControl's
@@ -1064,7 +1049,7 @@ void QQuickTextArea::mouseMoveEvent(QMouseEvent *event)
d->pressHandler.mouseMoveEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
QQuickTextEdit::mouseMoveEvent(event);
@@ -1077,7 +1062,7 @@ void QQuickTextArea::mouseReleaseEvent(QMouseEvent *event)
d->pressHandler.mouseReleaseEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
QQuickTextEdit::mouseReleaseEvent(event);
@@ -1088,7 +1073,7 @@ void QQuickTextArea::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_D(QQuickTextArea);
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextEdit::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
QQuickTextEdit::mouseDoubleClickEvent(event);
diff --git a/src/quicktemplates/qquicktextarea_p.h b/src/quicktemplates/qquicktextarea_p.h
index 5bd10af21e..d09f300a9f 100644
--- a/src/quicktemplates/qquicktextarea_p.h
+++ b/src/quicktemplates/qquicktextarea_p.h
@@ -26,7 +26,7 @@ class QQuickText;
class QQuickTextAreaPrivate;
class QQuickTextAreaAttached;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextArea : public QQuickTextEdit
+class Q_QUICKTEMPLATES2_EXPORT QQuickTextArea : public QQuickTextEdit
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override
@@ -155,7 +155,7 @@ private:
class QQuickTextAreaAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextAreaAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickTextAreaAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickTextArea *flickable READ flickable WRITE setFlickable NOTIFY flickableChanged FINAL)
@@ -174,16 +174,6 @@ private:
Q_DECLARE_PRIVATE(QQuickTextAreaAttached)
};
-struct QQuickTextEditForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickTextEdit)
- QML_ADDED_IN_VERSION(2, 3)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextArea)
-
#endif // QQUICKTEXTAREA_P_H
diff --git a/src/quicktemplates/qquicktextarea_p_p.h b/src/quicktemplates/qquicktextarea_p_p.h
index 243b350866..849c22b97c 100644
--- a/src/quicktemplates/qquicktextarea_p_p.h
+++ b/src/quicktemplates/qquicktextarea_p_p.h
@@ -24,18 +24,11 @@
#include <QtQuickTemplates2/private/qquicktextarea_p.h>
-#if QT_CONFIG(accessibility)
-#include <QtGui/qaccessible.h>
-#endif
-
QT_BEGIN_NAMESPACE
class QQuickFlickable;
class QQuickTextAreaPrivate : public QQuickTextEditPrivate, public QQuickItemChangeListener
-#if QT_CONFIG(accessibility)
- , public QAccessible::ActivationObserver
-#endif
{
public:
Q_DECLARE_PUBLIC(QQuickTextArea)
@@ -92,7 +85,6 @@ public:
#if QT_CONFIG(accessibility)
void accessibilityActiveChanged(bool active) override;
- QAccessible::Role accessibleRole() const override;
#endif
void cancelBackground();
@@ -104,6 +96,8 @@ public:
QPalette defaultPalette() const override;
+ bool setLastFocusChangeReason(Qt::FocusReason reason) override;
+
#if QT_CONFIG(quicktemplates2_hover)
bool hovered = false;
bool explicitHoverEnabled = false;
@@ -130,7 +124,6 @@ public:
QQuickDeferredPointer<QQuickItem> background;
QString placeholder;
QColor placeholderColor;
- Qt::FocusReason focusReason = Qt::OtherFocusReason;
QQuickPressHandler pressHandler;
QQuickFlickable *flickable = nullptr;
};
diff --git a/src/quicktemplates/qquicktextfield.cpp b/src/quicktemplates/qquicktextfield.cpp
index bb62c15ed1..7163f2a302 100644
--- a/src/quicktemplates/qquicktextfield.cpp
+++ b/src/quicktemplates/qquicktextfield.cpp
@@ -288,7 +288,7 @@ void QQuickTextFieldPrivate::accessibilityActiveChanged(bool active)
Q_Q(QQuickTextField);
QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
Q_ASSERT(accessibleAttached);
- accessibleAttached->setRole(accessibleRole());
+ accessibleAttached->setRole(effectiveAccessibleRole());
accessibleAttached->set_readOnly(m_readOnly);
accessibleAttached->set_passwordEdit((m_echoMode == QQuickTextField::Password || m_echoMode == QQuickTextField::PasswordEchoOnEdit) ? true : false);
accessibleAttached->setDescription(placeholder);
@@ -300,8 +300,6 @@ QAccessible::Role QQuickTextFieldPrivate::accessibleRole() const
}
#endif
-static inline QString backgroundName() { return QStringLiteral("background"); }
-
void QQuickTextFieldPrivate::cancelBackground()
{
Q_Q(QQuickTextField);
@@ -366,6 +364,16 @@ QPalette QQuickTextFieldPrivate::defaultPalette() const
return QQuickTheme::palette(QQuickTheme::TextField);
}
+bool QQuickTextFieldPrivate::setLastFocusChangeReason(Qt::FocusReason reason)
+{
+ Q_Q(QQuickTextField);
+ const auto focusReasonChanged = QQuickItemPrivate::setLastFocusChangeReason(reason);
+ if (focusReasonChanged)
+ emit q->focusReasonChanged();
+
+ return focusReasonChanged;
+}
+
QQuickTextField::QQuickTextField(QQuickItem *parent)
: QQuickTextInput(*(new QQuickTextFieldPrivate), parent)
{
@@ -525,22 +533,32 @@ void QQuickTextField::setPlaceholderTextColor(const QColor &color)
/*!
\qmlproperty enumeration QtQuick.Controls::TextField::focusReason
- \include qquickcontrol-focusreason.qdocinc
+ This property holds the reason of the last focus change.
+
+ \note This property does not indicate whether the item has \l {Item::activeFocus}
+ {active focus}, but the reason why the item either gained or lost focus.
+
+ \value Qt.MouseFocusReason A mouse action occurred.
+ \value Qt.TabFocusReason The Tab key was pressed.
+ \value Qt.BacktabFocusReason A Backtab occurred. The input for this may include the Shift or Control keys; e.g. Shift+Tab.
+ \value Qt.ActiveWindowFocusReason The window system made this window either active or inactive.
+ \value Qt.PopupFocusReason The application opened/closed a pop-up that grabbed/released the keyboard focus.
+ \value Qt.ShortcutFocusReason The user typed a label's buddy shortcut
+ \value Qt.MenuBarFocusReason The menu bar took focus.
+ \value Qt.OtherFocusReason Another reason, usually application-specific.
+
+ \note Prefer \l {Item::focusReason} to this property.
*/
Qt::FocusReason QQuickTextField::focusReason() const
{
Q_D(const QQuickTextField);
- return d->focusReason;
+ return d->lastFocusChangeReason();
}
void QQuickTextField::setFocusReason(Qt::FocusReason reason)
{
Q_D(QQuickTextField);
- if (d->focusReason == reason)
- return;
-
- d->focusReason = reason;
- emit focusReasonChanged();
+ d->setLastFocusChangeReason(reason);
}
/*!
@@ -839,13 +857,11 @@ QSGNode *QQuickTextField::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
void QQuickTextField::focusInEvent(QFocusEvent *event)
{
QQuickTextInput::focusInEvent(event);
- setFocusReason(event->reason());
}
void QQuickTextField::focusOutEvent(QFocusEvent *event)
{
QQuickTextInput::focusOutEvent(event);
- setFocusReason(event->reason());
}
#if QT_CONFIG(quicktemplates2_hover)
@@ -871,7 +887,7 @@ void QQuickTextField::mousePressEvent(QMouseEvent *event)
d->pressHandler.mousePressEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
if (event->buttons() != Qt::RightButton)
@@ -885,7 +901,7 @@ void QQuickTextField::mouseMoveEvent(QMouseEvent *event)
d->pressHandler.mouseMoveEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event)
@@ -904,7 +920,7 @@ void QQuickTextField::mouseReleaseEvent(QMouseEvent *event)
d->pressHandler.mouseReleaseEvent(event);
if (d->pressHandler.isActive()) {
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
if (event->buttons() != Qt::RightButton)
@@ -916,7 +932,7 @@ void QQuickTextField::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_D(QQuickTextField);
if (d->pressHandler.delayedMousePressEvent) {
- QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent);
+ QQuickTextInput::mousePressEvent(d->pressHandler.delayedMousePressEvent.get());
d->pressHandler.clearDelayedMouseEvent();
}
if (event->buttons() != Qt::RightButton)
diff --git a/src/quicktemplates/qquicktextfield_p.h b/src/quicktemplates/qquicktextfield_p.h
index c5bc9ce0bd..4c38c098ba 100644
--- a/src/quicktemplates/qquicktextfield_p.h
+++ b/src/quicktemplates/qquicktextfield_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickTextFieldPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTextField : public QQuickTextInput
+class Q_QUICKTEMPLATES2_EXPORT QQuickTextField : public QQuickTextInput
{
Q_OBJECT
Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) // override
@@ -146,16 +146,6 @@ private:
Q_DECLARE_PRIVATE(QQuickTextField)
};
-struct QQuickTextFieldForeign
-{
- Q_GADGET
- QML_ANONYMOUS
- QML_FOREIGN(QQuickTextInput)
- QML_ADDED_IN_VERSION(2, 2)
-};
-
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTextField)
-
#endif // QQUICKTEXTFIELD_P_H
diff --git a/src/quicktemplates/qquicktextfield_p_p.h b/src/quicktemplates/qquicktextfield_p_p.h
index 8c6752776d..ac75460b7e 100644
--- a/src/quicktemplates/qquicktextfield_p_p.h
+++ b/src/quicktemplates/qquicktextfield_p_p.h
@@ -94,6 +94,8 @@ public:
QPalette defaultPalette() const override;
+ bool setLastFocusChangeReason(Qt::FocusReason reason) override;
+
#if QT_CONFIG(quicktemplates2_hover)
bool hovered = false;
bool explicitHoverEnabled = false;
@@ -118,7 +120,6 @@ public:
QQuickDeferredPointer<QQuickItem> background;
QString placeholder;
QColor placeholderColor;
- Qt::FocusReason focusReason = Qt::OtherFocusReason;
QQuickPressHandler pressHandler;
};
diff --git a/src/quicktemplates/qquicktheme_p.h b/src/quicktemplates/qquicktheme_p.h
index da6254e9bf..96cbd2c0ad 100644
--- a/src/quicktemplates/qquicktheme_p.h
+++ b/src/quicktemplates/qquicktheme_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickThemePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTheme
+class Q_QUICKTEMPLATES2_EXPORT QQuickTheme
{
public:
QQuickTheme();
diff --git a/src/quicktemplates/qquicktheme_p_p.h b/src/quicktemplates/qquicktheme_p_p.h
index a18957bd0d..f6c9cc39fb 100644
--- a/src/quicktemplates/qquicktheme_p_p.h
+++ b/src/quicktemplates/qquicktheme_p_p.h
@@ -19,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickThemePrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickThemePrivate
{
public:
static QQuickThemePrivate *get(QQuickTheme *theme)
diff --git a/src/quicktemplates/qquicktoolbar_p.h b/src/quicktemplates/qquicktoolbar_p.h
index f896361ea0..3079bc78f0 100644
--- a/src/quicktemplates/qquicktoolbar_p.h
+++ b/src/quicktemplates/qquicktoolbar_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickToolBarPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolBar : public QQuickPane
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolBar : public QQuickPane
{
Q_OBJECT
Q_PROPERTY(Position position READ position WRITE setPosition NOTIFY positionChanged FINAL)
@@ -57,6 +57,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickToolBar)
-
#endif // QQUICKTOOLBAR_P_H
diff --git a/src/quicktemplates/qquicktoolbutton.cpp b/src/quicktemplates/qquicktoolbutton.cpp
index 3acf8782a1..d857b8ab40 100644
--- a/src/quicktemplates/qquicktoolbutton.cpp
+++ b/src/quicktemplates/qquicktoolbutton.cpp
@@ -32,7 +32,7 @@ QT_BEGIN_NAMESPACE
\sa ToolBar, {Customizing ToolButton}, {Button Controls}
*/
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolPrivate : public QQuickButtonPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolPrivate : public QQuickButtonPrivate
{
Q_DECLARE_PUBLIC(QQuickToolButton)
diff --git a/src/quicktemplates/qquicktoolbutton_p.h b/src/quicktemplates/qquicktoolbutton_p.h
index e470ae816d..2ddd9087c6 100644
--- a/src/quicktemplates/qquicktoolbutton_p.h
+++ b/src/quicktemplates/qquicktoolbutton_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickToolButtonPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolButton : public QQuickButton
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolButton : public QQuickButton
{
Q_OBJECT
QML_NAMED_ELEMENT(ToolButton)
@@ -39,6 +39,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickToolButton)
-
#endif // QQUICKTOOLBUTTON_P_H
diff --git a/src/quicktemplates/qquicktoolseparator_p.h b/src/quicktemplates/qquicktoolseparator_p.h
index 85930a3506..04d941566c 100644
--- a/src/quicktemplates/qquicktoolseparator_p.h
+++ b/src/quicktemplates/qquicktoolseparator_p.h
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
class QQuickToolSeparatorPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolSeparator : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolSeparator : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL)
@@ -56,6 +56,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickToolSeparator)
-
#endif // QQUICKTOOLSEPARATOR_P_H
diff --git a/src/quicktemplates/qquicktooltip.cpp b/src/quicktemplates/qquicktooltip.cpp
index 2a78e4d344..0493cd5f4e 100644
--- a/src/quicktemplates/qquicktooltip.cpp
+++ b/src/quicktemplates/qquicktooltip.cpp
@@ -102,6 +102,8 @@ public:
void opened() override;
+ Qt::WindowFlags popupWindowType() const override;
+
QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::ToolTip); }
int delay = 0;
@@ -141,6 +143,11 @@ void QQuickToolTipPrivate::opened()
startTimeout();
}
+Qt::WindowFlags QQuickToolTipPrivate::popupWindowType() const
+{
+ return Qt::ToolTip;
+}
+
QQuickToolTip::QQuickToolTip(QQuickItem *parent)
: QQuickPopup(*(new QQuickToolTipPrivate), parent)
{
diff --git a/src/quicktemplates/qquicktooltip_p.h b/src/quicktemplates/qquicktooltip_p.h
index 979df38a31..1ac89d8efb 100644
--- a/src/quicktemplates/qquicktooltip_p.h
+++ b/src/quicktemplates/qquicktooltip_p.h
@@ -23,7 +23,7 @@ class QQuickToolTipPrivate;
class QQuickToolTipAttached;
class QQuickToolTipAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolTip : public QQuickPopup
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolTip : public QQuickPopup
{
Q_OBJECT
Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY delayChanged FINAL)
@@ -74,7 +74,7 @@ private:
Q_DECLARE_PRIVATE(QQuickToolTip)
};
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickToolTipAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickToolTipAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL)
@@ -117,6 +117,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickToolTip)
-
#endif // QQUICKTOOLTIP_P_H
diff --git a/src/quicktemplates/qquicktreeviewdelegate.cpp b/src/quicktemplates/qquicktreeviewdelegate.cpp
index ba1a26cb34..32d51975be 100644
--- a/src/quicktemplates/qquicktreeviewdelegate.cpp
+++ b/src/quicktemplates/qquicktreeviewdelegate.cpp
@@ -7,6 +7,8 @@
#include <QtQuick/private/qquicktaphandler_p.h>
#include <QtQuick/private/qquicktreeview_p_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
/*!
@@ -99,14 +101,14 @@ QT_BEGIN_NAMESPACE
\note If you want to disable the default behavior that occurs when the
user clicks on the delegate (like changing the current index), you can set
- \l {QQuickTableView::pointerNavigationEnabled}{pointerNavigationEnabled} to \c false.
+ \l {TableView::pointerNavigationEnabled}{pointerNavigationEnabled} to \c false.
\section2 Editing nodes in the tree
TreeViewDelegate has a default \l {TableView::editDelegate}{edit delegate}
assigned. If \l TreeView has \l {TableView::editTriggers}{edit triggers}
set, and the \l {TableView::model}{model} has support for
\l {Editing cells} {editing model items}, then the user can activate any of
- the edit triggers to edit the text of the \l {TableView::current}{current}
+ the edit triggers to edit the text of the \l {TreeViewDelegate::current}{current}
tree node.
The default edit delegate will try to use the \c {Qt.EditRole} to read and
@@ -138,7 +140,7 @@ QT_BEGIN_NAMESPACE
\qmlproperty bool QtQuick.Controls::TreeViewDelegate::isTreeNode
This property is \c true if the delegate item draws a node in the tree.
- Only one column in the view will be used to to draw the tree, and
+ Only one column in the view will be used to draw the tree, and
therefore, only delegate items in that column will have this
property set to \c true.
diff --git a/src/quicktemplates/qquicktreeviewdelegate_p.h b/src/quicktemplates/qquicktreeviewdelegate_p.h
index aa39b17c10..e56057301e 100644
--- a/src/quicktemplates/qquicktreeviewdelegate_p.h
+++ b/src/quicktemplates/qquicktreeviewdelegate_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickTreeViewDelegatePrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTreeViewDelegate : public QQuickItemDelegate
+class Q_QUICKTEMPLATES2_EXPORT QQuickTreeViewDelegate : public QQuickItemDelegate
{
Q_OBJECT
Q_PROPERTY(qreal indentation READ indentation WRITE setIndentation NOTIFY indentationChanged FINAL)
diff --git a/src/quicktemplates/qquicktumbler.cpp b/src/quicktemplates/qquicktumbler.cpp
index e7f6606de3..71766cc698 100644
--- a/src/quicktemplates/qquicktumbler.cpp
+++ b/src/quicktemplates/qquicktumbler.cpp
@@ -259,6 +259,9 @@ QPalette QQuickTumblerPrivate::defaultPalette() const
QQuickTumbler::QQuickTumbler(QQuickItem *parent)
: QQuickControl(*(new QQuickTumblerPrivate), parent)
{
+ Q_D(QQuickTumbler);
+ d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
+
setActiveFocusOnTab(true);
connect(this, SIGNAL(leftPaddingChanged()), this, SLOT(_q_updateItemWidths()));
diff --git a/src/quicktemplates/qquicktumbler_p.h b/src/quicktemplates/qquicktumbler_p.h
index 02ea3049fd..d2f980aad0 100644
--- a/src/quicktemplates/qquicktumbler_p.h
+++ b/src/quicktemplates/qquicktumbler_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QQuickTumblerAttached;
class QQuickTumblerPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumbler : public QQuickControl
+class Q_QUICKTEMPLATES2_EXPORT QQuickTumbler : public QQuickControl
{
Q_OBJECT
Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged FINAL)
@@ -118,7 +118,7 @@ private:
class QQuickTumblerAttachedPrivate;
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumblerAttached : public QObject
+class Q_QUICKTEMPLATES2_EXPORT QQuickTumblerAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickTumbler *tumbler READ tumbler CONSTANT FINAL)
@@ -140,6 +140,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickTumbler)
-
#endif // QQUICKTUMBLER_P_H
diff --git a/src/quicktemplates/qquicktumbler_p_p.h b/src/quicktemplates/qquicktumbler_p_p.h
index e53381c470..70a611bf0f 100644
--- a/src/quicktemplates/qquicktumbler_p_p.h
+++ b/src/quicktemplates/qquicktumbler_p_p.h
@@ -18,9 +18,11 @@
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
#include <QtQuickTemplates2/private/qquicktumbler_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
-class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickTumblerPrivate : public QQuickControlPrivate
+class Q_QUICKTEMPLATES2_EXPORT QQuickTumblerPrivate : public QQuickControlPrivate
{
Q_DECLARE_PUBLIC(QQuickTumbler)
diff --git a/src/quicktemplates/qquickweeknumbercolumn_p.h b/src/quicktemplates/qquickweeknumbercolumn_p.h
index 7ea6255319..6db01a0923 100644
--- a/src/quicktemplates/qquickweeknumbercolumn_p.h
+++ b/src/quicktemplates/qquickweeknumbercolumn_p.h
@@ -66,6 +66,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickWeekNumberColumn)
-
#endif // QQUICKWEEKNUMBERCOLUMN_P_H
diff --git a/src/quicktemplates/qquickweeknumbermodel_p.h b/src/quicktemplates/qquickweeknumbermodel_p.h
index f00fd5a073..ba12f2be16 100644
--- a/src/quicktemplates/qquickweeknumbermodel_p.h
+++ b/src/quicktemplates/qquickweeknumbermodel_p.h
@@ -67,6 +67,4 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickWeekNumberModel)
-
#endif // QQUICKWEEKNUMBERMODEL_P_H
diff --git a/src/quicktemplates/qtquicktemplates2global_p.h b/src/quicktemplates/qtquicktemplates2global_p.h
index bca84663c6..27b5947994 100644
--- a/src/quicktemplates/qtquicktemplates2global_p.h
+++ b/src/quicktemplates/qtquicktemplates2global_p.h
@@ -18,12 +18,16 @@
#include <QtCore/qglobal.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <QtQuickTemplates2/private/qtquicktemplates2-config_p.h>
-#include <QtQuickTemplates2/private/qtquicktemplates2exports_p.h>
+#include <QtQuickTemplates2/qtquicktemplates2exports.h>
QT_BEGIN_NAMESPACE
-Q_QUICKTEMPLATES2_PRIVATE_EXPORT void QQuickTemplates_initializeModule();
-Q_QUICKTEMPLATES2_PRIVATE_EXPORT void qml_register_types_QtQuick_Templates();
+Q_QUICKTEMPLATES2_EXPORT void QQuickTemplates_initializeModule();
+Q_QUICKTEMPLATES2_EXPORT void qml_register_types_QtQuick_Templates();
+
+[[maybe_unused]] static inline QString backgroundName() { return QStringLiteral("background"); }
+[[maybe_unused]] static inline QString handleName() { return QStringLiteral("handle"); }
+[[maybe_unused]] static inline QString indicatorName() { return QStringLiteral("indicator"); }
QT_END_NAMESPACE