QmlDesigner: Fix issues with Components and Repeaters

- Fixed state preview rendering by deparenting nodes under
  component wraps.
- Ensure state preview image is always clipped to root item.
  The state preview bounding box broke when Repeater was direct
  child of root and had delegate with rotation.
- Allow dragging items under Repeater to create implicit component.
- Allow dragging Components under Repeater.
- Do not ask for target property when Component is dragged under
  any node.
- Update nodeSource properly when reparenting implicit component
  or removing last child of a Component item.
- Fixed scene update when last child of a repeater is deleted/moved.

Fixes: QDS-5197
Change-Id: Iaaf1745e25db3522ffc1dba7fd1b051da29f5aec
Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Miikka Heikkinen
2021-10-01 12:27:04 +03:00
parent 4036c20f63
commit 2ab6f800b3
11 changed files with 124 additions and 28 deletions

View File

@@ -397,8 +397,19 @@ void NodeInstanceServer::reparentInstances(const QVector<ReparentContainer> &con
if (hasInstanceForId(container.instanceId())) {
ServerNodeInstance instance = instanceForId(container.instanceId());
if (instance.isValid()) {
instance.reparent(instanceForId(container.oldParentInstanceId()), container.oldParentProperty(),
instanceForId(container.newParentInstanceId()), container.newParentProperty());
ServerNodeInstance newParent = instanceForId(container.newParentInstanceId());
PropertyName newParentProperty = container.newParentProperty();
if (!isInformationServer()) {
// Children of the component wraps are left out of the node tree to avoid
// incorrectly rendering them
if (newParent.isComponentWrap()) {
newParent = {};
newParentProperty.clear();
}
}
instance.reparent(instanceForId(container.oldParentInstanceId()),
container.oldParentProperty(),
newParent, newParentProperty);
}
}
}

View File

@@ -415,6 +415,16 @@ bool ObjectNodeInstance::isLockedInEditor() const
return m_isLockedInEditor;
}
bool ObjectNodeInstance::isComponentWrap() const
{
return m_isComponentWrap;
}
void ObjectNodeInstance::setComponentWrap(bool wrap)
{
m_isComponentWrap = wrap;
}
void ObjectNodeInstance::setModifiedFlag(bool b)
{
m_isModified = b;
@@ -732,6 +742,10 @@ QObject *ObjectNodeInstance::createComponentWrap(const QString &nodeSource, cons
QQmlComponent *component = new QQmlComponent(context->engine());
QByteArray data(nodeSource.toUtf8());
if (data.isEmpty()) {
// Add a fake root element as an empty component is not valid and crashes in some cases
data.append("QtObject{}");
}
data.prepend(importCode);
component->setData(data, context->baseUrl().resolved(QUrl("createComponent.qml")));
QObject *object = component;

View File

@@ -202,6 +202,9 @@ public:
virtual void setLockedInEditor(bool b);
bool isLockedInEditor() const;
bool isComponentWrap() const;
void setComponentWrap(bool wrap);
void setModifiedFlag(bool b);
protected:
@@ -234,6 +237,7 @@ private:
bool m_isModified = false;
bool m_isLockedInEditor = false;
bool m_isHiddenInEditor = false;
bool m_isComponentWrap = false;
static QHash<EnumerationName, QVariant> m_enumationValueHash;
};

View File

@@ -199,11 +199,24 @@ void Qt5NodeInstanceServer::markRepeaterParentDirty(qint32 id) const
if (!hasInstanceForId(id))
return;
// If a Repeater instance was moved/removed, the old parent must be marked dirty to rerender it
ServerNodeInstance instance = instanceForId(id);
if (instance.isValid() && instance.isSubclassOf("QQuickRepeater") && instance.hasParent()) {
ServerNodeInstance parentInstance = instance.parent();
if (!instance.isValid())
return;
ServerNodeInstance parentInstance = instance.parent();
if (!parentInstance.isValid())
return;
// If a Repeater instance was moved/removed, the old parent must be marked dirty to rerender it
const QByteArray type("QQuickRepeater");
if (ServerNodeInstance::isSubclassOf(instance.internalObject(), type))
DesignerSupport::addDirty(parentInstance.rootQuickItem(), QQuickDesignerSupport::Content);
// Repeater's parent must also be dirtied when a child of a repeater was moved/removed.
if (ServerNodeInstance::isSubclassOf(parentInstance.internalObject(), type)) {
ServerNodeInstance parentsParent = parentInstance.parent();
if (parentsParent.isValid())
DesignerSupport::addDirty(parentsParent.rootQuickItem(), QQuickDesignerSupport::Content);
}
}

View File

@@ -101,6 +101,10 @@ void Qt5PreviewNodeInstanceServer::changeState(const ChangeStateCommand &/*comma
QImage Qt5PreviewNodeInstanceServer::renderPreviewImage()
{
// Ensure the state preview image is always clipped properly to root item dimensions
if (auto rootItem = qobject_cast<QQuickItem *>(rootNodeInstance().internalObject()))
rootItem->setClip(true);
rootNodeInstance().updateDirtyNodeRecursive();
QRectF boundingRect = rootNodeInstance().boundingRect();

View File

@@ -769,6 +769,9 @@ void QuickItemNodeInstance::reparent(const ObjectNodeInstance::Pointer &oldParen
ObjectNodeInstance::reparent(oldParentInstance, oldParentProperty, newParentInstance, newParentProperty);
if (!newParentInstance)
quickItem()->setParentItem(nullptr);
if (instanceIsValidLayoutable(newParentInstance, newParentProperty)) {
setInLayoutable(true);
setMovable(false);

View File

@@ -150,6 +150,11 @@ bool ServerNodeInstance::holdsGraphical() const
return m_nodeInstance->isQuickItem();
}
bool ServerNodeInstance::isComponentWrap() const
{
return m_nodeInstance->isComponentWrap();
}
void ServerNodeInstance::updateDirtyNodeRecursive()
{
m_nodeInstance->updateAllDirtyNodesRecursive();
@@ -280,6 +285,8 @@ ServerNodeInstance ServerNodeInstance::create(NodeInstanceServer *nodeInstanceSe
instance.internalInstance()->setInstanceId(instanceContainer.instanceId());
instance.internalInstance()->setComponentWrap(componentWrap == WrapAsComponent);
instance.internalInstance()->initialize(instance.m_nodeInstance, instanceContainer.metaFlags());
// Handle hidden state to initialize pickable state

View File

@@ -178,6 +178,8 @@ public:
void updateDirtyNodeRecursive();
bool holdsGraphical() const;
bool isComponentWrap() const;
private: // functions
ServerNodeInstance(const QSharedPointer<Internal::ObjectNodeInstance> &abstractInstance);