Drop unnecessary creation contexts from Quick Controls
Bound components can only be instantiated in the context they're declared in. Adding a context in between before instantiating a component makes it impossible to bind the component. We want to use bound components so that we can safely use IDs of other objects from the same context inside the objects created from the component. Pick-to: 6.4 Fixes: QTBUG-106042 Change-Id: I7a0e1ea3e079ccd0f5fe156f07f8bc62149c6c0a Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
parent
2a37ff2f49
commit
fc683799fe
|
@ -489,6 +489,16 @@ bool QQmlComponent::isLoading() const
|
|||
return status() == Loading;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if the component was created in a QML files that specifies
|
||||
\c{pragma ComponentBehavior: Bound}, otherwise returns false.
|
||||
*/
|
||||
bool QQmlComponent::isBound() const
|
||||
{
|
||||
Q_D(const QQmlComponent);
|
||||
return d->isBound();
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlproperty real Component::progress
|
||||
The progress of loading the component, from 0.0 (nothing loaded)
|
||||
|
|
|
@ -63,6 +63,8 @@ public:
|
|||
bool isError() const;
|
||||
bool isLoading() const;
|
||||
|
||||
bool isBound() const;
|
||||
|
||||
QList<QQmlError> errors() const;
|
||||
Q_INVOKABLE QString errorString() const;
|
||||
|
||||
|
|
|
@ -26,3 +26,11 @@ qt_internal_add_module(QuickControlsTestUtilsPrivate
|
|||
Qt::QuickTemplates2Private
|
||||
Qt::QuickTestUtilsPrivate
|
||||
)
|
||||
|
||||
# This is used by both C++ and QML tests, so we need it to be a library and a QML plugin,
|
||||
# hence qt_internal_add_qml_module. We use it in addition to qt_internal_add_module,
|
||||
# because otherwise syncqt complains that there is no "QtQuickControlsTestUtilsPrivate" module.
|
||||
qt_internal_add_qml_module(QuickControlsTestUtilsPrivate
|
||||
URI "Qt.test.controls"
|
||||
VERSION "${PROJECT_VERSION}"
|
||||
)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "controlstestutils_p.h"
|
||||
|
||||
#include <QtTest/qsignalspy.h>
|
||||
#include <QtQml/qqmlcomponent.h>
|
||||
#include <QtQuickControls2/qquickstyle.h>
|
||||
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
|
||||
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
|
||||
|
@ -155,3 +156,16 @@ bool QQuickControlsTestUtils::doubleClickButton(QQuickAbstractButton *button)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Allows creating QQmlComponents in C++, which is useful for tests that need
|
||||
to check if items created from the component have the correct QML context.
|
||||
*/
|
||||
Q_INVOKABLE QQmlComponent *QQuickControlsTestUtils::ComponentCreator::createComponent(const QByteArray &data)
|
||||
{
|
||||
std::unique_ptr<QQmlComponent> component(new QQmlComponent(qmlEngine(this)));
|
||||
component->setData(data, QUrl());
|
||||
if (component->isError())
|
||||
qmlWarning(this) << "Failed to create component from the following data:\n" << data;
|
||||
return component.release();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QQmlComponent;
|
||||
class QQmlEngine;
|
||||
class QQuickApplicationWindow;
|
||||
class QQuickAbstractButton;
|
||||
|
@ -53,6 +54,17 @@ namespace QQuickControlsTestUtils
|
|||
[[nodiscard]] bool verifyButtonClickable(QQuickAbstractButton *button);
|
||||
[[nodiscard]] bool clickButton(QQuickAbstractButton *button);
|
||||
[[nodiscard]] bool doubleClickButton(QQuickAbstractButton *button);
|
||||
|
||||
class ComponentCreator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
Q_MOC_INCLUDE(<QtQml/qqmlcomponent.h>)
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QQmlComponent *createComponent(const QByteArray &data);
|
||||
};
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -35,13 +35,19 @@ QQuickItem *QQuickFolderBreadcrumbBarPrivate::createDelegateItem(QQmlComponent *
|
|||
Q_Q(QQuickFolderBreadcrumbBar);
|
||||
// If we don't use the correct context, it won't be possible to refer to
|
||||
// the control's id from within the delegates.
|
||||
QQmlContext *creationContext = component->creationContext();
|
||||
QQmlContext *context = component->creationContext();
|
||||
// The component might not have been created in QML, in which case
|
||||
// the creation context will be null and we have to create it ourselves.
|
||||
if (!creationContext)
|
||||
creationContext = qmlContext(q);
|
||||
QQmlContext *context = new QQmlContext(creationContext, q);
|
||||
context->setContextObject(q);
|
||||
if (!context)
|
||||
context = qmlContext(q);
|
||||
|
||||
// If we have initial properties we assume that all necessary information is passed via
|
||||
// initial properties.
|
||||
if (!component->isBound() && initialProperties.isEmpty()) {
|
||||
context = new QQmlContext(context, q);
|
||||
context->setContextObject(q);
|
||||
}
|
||||
|
||||
QQuickItem *item = qobject_cast<QQuickItem*>(component->createWithInitialProperties(initialProperties, context));
|
||||
if (item)
|
||||
QQml_setParent_noEvent(item, q);
|
||||
|
|
|
@ -254,11 +254,9 @@ QQuickItem *QQuickMenuPrivate::beginCreateItem()
|
|||
if (!delegate)
|
||||
return nullptr;
|
||||
|
||||
QQmlContext *creationContext = delegate->creationContext();
|
||||
if (!creationContext)
|
||||
creationContext = qmlContext(q);
|
||||
QQmlContext *context = new QQmlContext(creationContext, q);
|
||||
context->setContextObject(q);
|
||||
QQmlContext *context = delegate->creationContext();
|
||||
if (!context)
|
||||
context = qmlContext(q);
|
||||
|
||||
QObject *object = delegate->beginCreate(context);
|
||||
QQuickItem *item = qobject_cast<QQuickItem *>(object);
|
||||
|
|
|
@ -49,17 +49,14 @@ QQuickItem *QQuickMenuBarPrivate::beginCreateItem(QQuickMenu *menu)
|
|||
if (!delegate)
|
||||
return nullptr;
|
||||
|
||||
QQmlContext *creationContext = delegate->creationContext();
|
||||
if (!creationContext)
|
||||
creationContext = qmlContext(q);
|
||||
QQmlContext *context = new QQmlContext(creationContext, q);
|
||||
context->setContextObject(q);
|
||||
QQmlContext *context = delegate->creationContext();
|
||||
if (!context)
|
||||
context = qmlContext(q);
|
||||
|
||||
QObject *object = delegate->beginCreate(context);
|
||||
QQuickItem *item = qobject_cast<QQuickItem *>(object);
|
||||
if (!item) {
|
||||
delete object;
|
||||
delete context;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -715,11 +715,9 @@ static QQuickItem *createDimmer(QQmlComponent *component, QQuickPopup *popup, QQ
|
|||
{
|
||||
QQuickItem *item = nullptr;
|
||||
if (component) {
|
||||
QQmlContext *creationContext = component->creationContext();
|
||||
if (!creationContext)
|
||||
creationContext = qmlContext(popup);
|
||||
QQmlContext *context = new QQmlContext(creationContext, popup);
|
||||
context->setContextObject(popup);
|
||||
QQmlContext *context = component->creationContext();
|
||||
if (!context)
|
||||
context = qmlContext(popup);
|
||||
item = qobject_cast<QQuickItem*>(component->beginCreate(context));
|
||||
}
|
||||
|
||||
|
|
|
@ -700,13 +700,11 @@ void QQuickSplitViewPrivate::createHandleItem(int index)
|
|||
|
||||
// If we don't use the correct context, it won't be possible to refer to
|
||||
// the control's id from within the delegate.
|
||||
QQmlContext *creationContext = m_handle->creationContext();
|
||||
QQmlContext *context = m_handle->creationContext();
|
||||
// The component might not have been created in QML, in which case
|
||||
// the creation context will be null and we have to create it ourselves.
|
||||
if (!creationContext)
|
||||
creationContext = qmlContext(q);
|
||||
QQmlContext *context = new QQmlContext(creationContext, q);
|
||||
context->setContextObject(q);
|
||||
if (!context)
|
||||
context = qmlContext(q);
|
||||
QQuickItem *handleItem = qobject_cast<QQuickItem*>(m_handle->beginCreate(context));
|
||||
if (handleItem) {
|
||||
handleItem->setParent(q);
|
||||
|
|
|
@ -79,8 +79,6 @@ QQuickStackElement::~QQuickStackElement()
|
|||
|
||||
if (attached)
|
||||
emit attached->removed();
|
||||
|
||||
delete context;
|
||||
}
|
||||
|
||||
QQuickStackElement *QQuickStackElement::fromString(const QString &str, QQuickStackView *view, QString *error)
|
||||
|
@ -134,11 +132,9 @@ bool QQuickStackElement::load(QQuickStackView *parent)
|
|||
return true;
|
||||
}
|
||||
|
||||
QQmlContext *creationContext = component->creationContext();
|
||||
if (!creationContext)
|
||||
creationContext = qmlContext(parent);
|
||||
context = new QQmlContext(creationContext, parent);
|
||||
context->setContextObject(parent);
|
||||
QQmlContext *context = component->creationContext();
|
||||
if (!context)
|
||||
context = qmlContext(parent);
|
||||
|
||||
QQuickStackIncubator incubator(this);
|
||||
component->create(incubator, context);
|
||||
|
|
|
@ -61,7 +61,6 @@ public:
|
|||
bool ownComponent = false;
|
||||
bool widthValid = false;
|
||||
bool heightValid = false;
|
||||
QQmlContext *context = nullptr;
|
||||
QQmlComponent *component = nullptr;
|
||||
QQuickStackView *view = nullptr;
|
||||
QPointer<QQuickItem> originalParent;
|
||||
|
|
|
@ -185,13 +185,11 @@ QQuickItem *QQuickSwipePrivate::createDelegateItem(QQmlComponent *component)
|
|||
{
|
||||
// If we don't use the correct context, it won't be possible to refer to
|
||||
// the control's id from within the delegates.
|
||||
QQmlContext *creationContext = component->creationContext();
|
||||
QQmlContext *context = component->creationContext();
|
||||
// The component might not have been created in QML, in which case
|
||||
// the creation context will be null and we have to create it ourselves.
|
||||
if (!creationContext)
|
||||
creationContext = qmlContext(control);
|
||||
QQmlContext *context = new QQmlContext(creationContext, control);
|
||||
context->setContextObject(control);
|
||||
if (!context)
|
||||
context = qmlContext(control);
|
||||
QQuickItem *item = qobject_cast<QQuickItem*>(component->beginCreate(context));
|
||||
if (item) {
|
||||
item->setParentItem(control);
|
||||
|
|
|
@ -6,6 +6,7 @@ import QtTest
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Templates as T
|
||||
import QtQuick.NativeStyle as NativeStyle
|
||||
import Qt.test.controls
|
||||
|
||||
TestCase {
|
||||
id: testCase
|
||||
|
@ -1470,4 +1471,24 @@ TestCase {
|
|||
mouseRelease(title, pressPoint.x, pressPoint.y)
|
||||
compare(title.pressedPosition, Qt.point(0, 0))
|
||||
}
|
||||
|
||||
Component {
|
||||
id: cppDimmerComponent
|
||||
|
||||
Popup {
|
||||
dim: true
|
||||
Overlay.modeless: ComponentCreator.createComponent(
|
||||
"import QtQuick; Rectangle { objectName: \"rect\"; color: \"tomato\" }")
|
||||
}
|
||||
}
|
||||
|
||||
function test_dimmerComponentCreatedInCpp() {
|
||||
let control = createTemporaryObject(cppDimmerComponent, testCase)
|
||||
verify(control)
|
||||
|
||||
control.open()
|
||||
tryCompare(control, "opened", true)
|
||||
let rect = findChild(control.Overlay.overlay, "rect")
|
||||
verify(rect)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import QtQuick.Controls
|
|||
import QtQuick.Window
|
||||
import QtTest
|
||||
import Qt.labs.settings
|
||||
import Qt.test.controls
|
||||
|
||||
TestCase {
|
||||
id: testCase
|
||||
|
@ -2528,4 +2529,43 @@ TestCase {
|
|||
mouseMove(control, control.width - 100, control.height / 2)
|
||||
verify(!targetHandle.SplitHandle.hovered)
|
||||
}
|
||||
|
||||
Component {
|
||||
id: cppHandleSplitViewComponent
|
||||
|
||||
SplitView {
|
||||
anchors.fill: parent
|
||||
handle: ComponentCreator.createComponent(`
|
||||
import QtQuick
|
||||
|
||||
Rectangle {
|
||||
objectName: "handle"
|
||||
implicitWidth: 10
|
||||
implicitHeight: 10
|
||||
color: "tomato"
|
||||
}`)
|
||||
|
||||
Rectangle {
|
||||
objectName: "navajowhite"
|
||||
color: objectName
|
||||
implicitWidth: 100
|
||||
implicitHeight: 100
|
||||
}
|
||||
Rectangle {
|
||||
objectName: "steelblue"
|
||||
color: objectName
|
||||
implicitWidth: 200
|
||||
implicitHeight: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function test_handleComponentCreatedInCpp() {
|
||||
let control = createTemporaryObject(cppHandleSplitViewComponent, testCase)
|
||||
verify(control)
|
||||
|
||||
let handles = findHandles(control)
|
||||
compare(handles.length, 1)
|
||||
compare(handles[0].color, Qt.color("tomato"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import QtQuick
|
||||
import QtTest
|
||||
import QtQuick.Controls
|
||||
import Qt.test.controls
|
||||
|
||||
TestCase {
|
||||
id: testCase
|
||||
|
@ -1225,7 +1226,12 @@ TestCase {
|
|||
|
||||
Item {
|
||||
objectName: "clearUponDestructionItem"
|
||||
Component.onDestruction: container.onDestructionCallback(stackView)
|
||||
onParentChanged: {
|
||||
// We don't actually do this on destruction because destruction is delayed.
|
||||
// Rather, we do it when we get un-parented.
|
||||
if (parent === null)
|
||||
container.onDestructionCallback(stackView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1544,4 +1550,26 @@ TestCase {
|
|||
tryCompare(control, "busy", true)
|
||||
tryCompare(control, "busy", false)
|
||||
}
|
||||
|
||||
Component {
|
||||
id: cppComponent
|
||||
|
||||
StackView {
|
||||
id: stackView
|
||||
anchors.fill: parent
|
||||
initialItem: cppComponent
|
||||
|
||||
property Component cppComponent: ComponentCreator.createComponent("import QtQuick; Rectangle { color: \"navajowhite\" }")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a component created in C++ works with StackView.
|
||||
function test_componentCreatedInCpp() {
|
||||
let control = createTemporaryObject(cppComponent, testCase)
|
||||
verify(control)
|
||||
compare(control.currentItem.color, Qt.color("navajowhite"))
|
||||
|
||||
control.push(control.cppComponent, { color: "tomato" })
|
||||
compare(control.currentItem.color, Qt.color("tomato"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import QtQuick
|
||||
import QtTest
|
||||
import QtQuick.Controls
|
||||
|
||||
import Qt.test.controls
|
||||
|
||||
TestCase {
|
||||
id: testCase
|
||||
|
@ -1726,4 +1726,23 @@ TestCase {
|
|||
compare(control.background.width, 200)
|
||||
compare(control.contentItem.width, 200 - control.leftPadding - control.rightPadding)
|
||||
}
|
||||
|
||||
Component {
|
||||
id: cppDelegateComponent
|
||||
|
||||
SwipeDelegate {
|
||||
text: "SwipeDelegate"
|
||||
width: 150
|
||||
swipe.right: ComponentCreator.createComponent(
|
||||
"import QtQuick; Rectangle { width: 100; height: parent.height; color: \"tomato\" }")
|
||||
}
|
||||
}
|
||||
|
||||
function test_delegateComponentCreatedInCpp() {
|
||||
let control = createTemporaryObject(cppDelegateComponent, testCase)
|
||||
verify(control)
|
||||
|
||||
swipe(control, 0, -1.0)
|
||||
compare(control.swipe.rightItem.color, Qt.color("tomato"))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue