From 70495410a8a5b63ad25d52c4caca4a935f5de507 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Tue, 21 Nov 2023 17:42:35 +0100 Subject: [PATCH] Maintain z order of shapepaths during update in CurveRenderer The new nodes for any updated shapepath would be appended to the end of the child list, and hence pop to the front of the z order stack. Instead, maintain the order by inserting the new nodes in the old ones' place in the child list. Fixes: QTBUG-119192 Change-Id: I0fa477158648a901b488b08b9fdef6465c312dd0 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quickshapes/qquickshapecurverenderer.cpp | 38 ++++- tests/baseline/scenegraph/data/Ignore | 1 + .../scenegraph/data/shape/OrderedPaths.qml | 141 ++++++++++++++++++ .../scenegraph/data/shape/shape_order.qml | 12 ++ .../data/shape/shape_order_async.qml | 12 ++ 5 files changed, 197 insertions(+), 7 deletions(-) create mode 100644 tests/baseline/scenegraph/data/shape/OrderedPaths.qml create mode 100644 tests/baseline/scenegraph/data/shape/shape_order.qml create mode 100644 tests/baseline/scenegraph/data/shape/shape_order_async.qml diff --git a/src/quickshapes/qquickshapecurverenderer.cpp b/src/quickshapes/qquickshapecurverenderer.cpp index f44e29bc98..e78b9a8a40 100644 --- a/src/quickshapes/qquickshapecurverenderer.cpp +++ b/src/quickshapes/qquickshapecurverenderer.cpp @@ -352,26 +352,49 @@ void QQuickShapeCurveRenderer::updateNode() strokeNode->setColor(pathData.pen.color()); }; - for (PathData &pathData : m_paths) { + NodeList toBeDeleted; + + for (int i = 0; i < m_paths.size(); i++) { + PathData &pathData = m_paths[i]; if (pathData.currentRunner) { if (!pathData.currentRunner->isDone) continue; + // Find insertion point for new nodes + QSGNode *nextNode = nullptr; + int j = i; + do { + const PathData &pd = m_paths[j]; + if (!pd.fillNodes.isEmpty()) + nextNode = pd.fillNodes.first(); + else if (!pathData.strokeNodes.isEmpty()) + nextNode = pd.strokeNodes.first(); + } while (!nextNode && ++j < m_paths.size()); + const PathData &newData = pathData.currentRunner->pathData; if (newData.m_dirty & PathDirty) pathData.path = newData.path; if (newData.m_dirty & FillDirty) { pathData.fillPath = newData.fillPath; - qDeleteAll(pathData.fillNodes); + for (auto *node : std::as_const(newData.fillNodes)) { + if (nextNode) + m_rootNode->insertChildNodeBefore(node, nextNode); + else + m_rootNode->appendChildNode(node); + } + toBeDeleted += pathData.fillNodes; pathData.fillNodes = newData.fillNodes; - for (auto *node : std::as_const(pathData.fillNodes)) - m_rootNode->appendChildNode(node); } if (newData.m_dirty & StrokeDirty) { - qDeleteAll(pathData.strokeNodes); + for (auto *node : std::as_const(newData.strokeNodes)) { + if (nextNode) + m_rootNode->insertChildNodeBefore(node, nextNode); + else + m_rootNode->appendChildNode(node); + } + toBeDeleted += pathData.strokeNodes; pathData.strokeNodes = newData.strokeNodes; - for (auto *node : std::as_const(pathData.strokeNodes)) - m_rootNode->appendChildNode(node); } + if (newData.m_dirty & UniformsDirty) updateUniforms(pathData); @@ -388,6 +411,7 @@ void QQuickShapeCurveRenderer::updateNode() pathData.m_dirty = 0; } } + qDeleteAll(toBeDeleted); // also removes them from m_rootNode's child list } void QQuickShapeCurveRenderer::processPath(PathData *pathData) diff --git a/tests/baseline/scenegraph/data/Ignore b/tests/baseline/scenegraph/data/Ignore index e904d53e8c..909d546391 100644 --- a/tests/baseline/scenegraph/data/Ignore +++ b/tests/baseline/scenegraph/data/Ignore @@ -3,6 +3,7 @@ # These are items to be used in other scenes; lack size borderimages/SimpleBorderImage.qml borderimages/SimpleNoBorder.qml +shape/OrderedPaths.qml # This will not stabilize before the timeout text/text_2500_chinese_characters.qml diff --git a/tests/baseline/scenegraph/data/shape/OrderedPaths.qml b/tests/baseline/scenegraph/data/shape/OrderedPaths.qml new file mode 100644 index 0000000000..688fbfdb59 --- /dev/null +++ b/tests/baseline/scenegraph/data/shape/OrderedPaths.qml @@ -0,0 +1,141 @@ +import QtQuick +import QtQuick.Shapes + +Item { + id: root + property bool async: false + + property int counter: 0 + NumberAnimation { + target: root + property: "counter" + duration: 2000 + from: 0 + to: 15 + running: true + } + + component RectStack : Shape { + asynchronous: root.async + + property alias p1FillColor: p1.fillColor + property alias p2FillColor: p2.fillColor + property alias p3FillColor: p3.fillColor + property alias p4FillColor: p4.fillColor + property alias p1StrokeColor: p1.strokeColor + property alias p2StrokeColor: p2.strokeColor + property alias p3StrokeColor: p3.strokeColor + property alias p4StrokeColor: p4.strokeColor + + ShapePath { + id: p1 + fillColor: "black" + strokeColor: "transparent" + strokeWidth: 5 + startX: 10 + startY: 10 + PathLine { relativeX: 100; relativeY: 0 } + PathLine { relativeX: 0; relativeY: 60 } + PathLine { relativeX: -100; relativeY: 0 } + PathLine { relativeX: 0; relativeY: -60 } + } + ShapePath { + id: p2 + fillColor: "red" + strokeColor: "transparent" + strokeWidth: 5 + startX: 20 + startY: 15 + PathLine { relativeX: 100; relativeY: 0 } + PathLine { relativeX: 0; relativeY: 60 } + PathLine { relativeX: -100; relativeY: 0 } + PathLine { relativeX: 0; relativeY: -60 } + } + ShapePath { + id: p3 + fillColor: "green" + strokeColor: "transparent" + strokeWidth: 5 + startX: 30 + startY: 20 + PathLine { relativeX: 100; relativeY: 0 } + PathLine { relativeX: 0; relativeY: 60 } + PathLine { relativeX: -100; relativeY: 0 } + PathLine { relativeX: 0; relativeY: -60 } + } + ShapePath { + id: p4 + fillColor: "blue" + strokeColor: "transparent" + strokeWidth: 5 + startX: 40 + startY: 25 + PathLine { relativeX: 100; relativeY: 0 } + PathLine { relativeX: 0; relativeY: 60 } + PathLine { relativeX: -100; relativeY: 0 } + PathLine { relativeX: 0; relativeY: -60 } + } + } + + + ListModel { + id: renderers + ListElement { renderer: Shape.GeometryRenderer } + ListElement { renderer: Shape.CurveRenderer } + } + + Row { + spacing: 5 + Repeater { + model: renderers + Column { + spacing: 5 + + RectStack { + preferredRendererType: renderer + } + + RectStack { + preferredRendererType: renderer + p1FillColor: counter % 16 >= 8 ? "black" : "transparent" + p2FillColor: counter % 8 >= 4 ? "red" : "transparent" + p3FillColor: counter % 4 >= 2 ? "green" : "transparent" + p4FillColor: counter % 2 >= 1 ? "blue" : "transparent" + } + + RectStack { + preferredRendererType: renderer + property int shifter: counter < 4 ? counter : counter + 1 + p1FillColor: shifter % 2 == 0 ? "black" : "transparent" + p2FillColor: counter % 2 == 0 ? "red" : "transparent" + p3FillColor: shifter % 2 == 1 ? "green" : "transparent" + p4FillColor: counter % 2 == 1 ? "blue" : "transparent" + } + + RectStack { + preferredRendererType: renderer + p1FillColor: counter % 16 >= 8 ? "black" : "transparent" + p2FillColor: counter % 8 >= 4 ? "red" : "transparent" + p3FillColor: counter % 4 >= 2 ? "green" : "transparent" + p4FillColor: counter % 2 >= 1 ? "blue" : "transparent" + p1StrokeColor: counter % 2 >= 1 ? "transparent" : "lightblue" + p2StrokeColor: counter % 4 >= 2 ? "transparent" : "lightgreen" + p3StrokeColor: counter % 8 >= 4 ? "transparent" : "pink" + p4StrokeColor: counter % 16 >= 8 ? "transparent" : "gray" + } + + RectStack { + preferredRendererType: renderer + p1FillColor: "transparent" + p2FillColor: "transparent" + p3FillColor: "transparent" + p4FillColor: "transparent" + p1StrokeColor: counter % 16 >= 8 ? "lightblue": "transparent" + p2StrokeColor: counter % 8 >= 4 ? "lightgreen": "transparent" + p3StrokeColor: counter % 4 >= 2 ? "pink": "transparent" + p4StrokeColor: counter % 2 >= 1 ? "gray" : "transparent" + } + } + } + } +} diff --git a/tests/baseline/scenegraph/data/shape/shape_order.qml b/tests/baseline/scenegraph/data/shape/shape_order.qml new file mode 100644 index 0000000000..14d2a322f9 --- /dev/null +++ b/tests/baseline/scenegraph/data/shape/shape_order.qml @@ -0,0 +1,12 @@ +import QtQuick + +Item { + width: 320 + height: 480 + + OrderedPaths { + anchors.fill: parent + async: false + } +} + diff --git a/tests/baseline/scenegraph/data/shape/shape_order_async.qml b/tests/baseline/scenegraph/data/shape/shape_order_async.qml new file mode 100644 index 0000000000..5dc96721b3 --- /dev/null +++ b/tests/baseline/scenegraph/data/shape/shape_order_async.qml @@ -0,0 +1,12 @@ +import QtQuick + +Item { + width: 320 + height: 480 + + OrderedPaths { + anchors.fill: parent + async: true + } +} +