Я создаю дерево объектов, дерево рисуется с использованием очень простой логики:
Однако этот простой алгоритм не срабатывает при вставке новых объектов в некоторых случаях. Например:
В этом случае узлы 1 и 2 успешно вставляются в узел 0, причем полная высота узла 0 (черная линия слева от каждого узла) является суммой его собственного пользовательского интерфейса плюс оба его дочерних элемента.
Но когда узел 3 вставляется в узел 1, компоновка разрывается — узел 2 не сдвигается вниз, в результате он перекрывается с вновь вставленным узлом, делая его черную линию невидимой.
Когда запускается дополнительный цикл обновления, дерево возвращается в нормальное состояние, поэтому сбой должен быть вызван некоторыми временными свойствами «или синхронизации». Обратите внимание, что одно дополнительное обновление не всегда приводит дерево в порядок, в зависимости от того, где была сделана вставка, ошибка может иногда занять несколько циклов обновления, чтобы «выйти из строя» из-за нарушения выравнивания.
Любые идеи, что может вызвать это ошибочное поведение и / или как это исправить? Что приводит к тому, что код иногда ломается и почему, несмотря на то, что он в конечном итоге работает, требуется один или несколько обновлений, чтобы привести себя в порядок?
РЕДАКТИРОВАТЬ: я был в состоянии изолировать и точно определить, когда выравнивание нарушается — при вставке в узел, который не заканчивается последний узел на своем уровне. Все привязки распространяются должным образом, пока каждый новый узел вставлен в последний и «открытый» узел, например следующего уровня на этом уровне нет, но вставка в любой предыдущий узел нарушает привязки. Таким образом, дополнительные циклы обновления необходимы для поэтапного устранения ошибки на всех уровнях, на которые она влияет. Но я до сих пор не знаю, что вызывает это и как это исправить, только когда это происходит.
РЕДАКТИРОВАТЬ: Исследуя немного больше, кажется, что после того, как новый дочерний элемент вставлен в родительский элемент, и его дочерние элементы перестроены в соответствии с их порядком, childrenRect
Свойство родителя сразу не отражает изменения в размере. Поэтому, когда следующий брат или сестра объекта, в который вставлен объект, позиционируется, он все равно использует старое устаревшее значение для высоты объекта, которое не влияет на вновь вставленный объект, поскольку он не использует его. Поэтому я думаю, что следующий большой вопрос — как это исправить, чтобы привязки происходили так, как они должны, чтобы алгоритм мог работать, как и ожидалось, с правильными данными? Я пытался использовать таймер для задержки операции, но, похоже, это не вопрос времени, а логический порядок, например. количество обновлений, которое требуется в настоящее время для устранения ошибки, равной глубине, с которой она произошла.
РЕДАКТИРОВАТЬ: Я подумал, как «решить», но … не совсем, я до сих пор не знаю, что вызывает это и как этого избежать, но немного более «экономичным» решением, чем обновление всего дерева, пока оно не будет исправлено просто перейдите к родителям и обновляйте только текущую ветку, пока не будет достигнут root. Это экономит много, но я все же предпочел бы иметь правильные значения сразу, так как привязки должны работать, по крайней мере, IMO.
РЕДАКТИРОВАТЬ: Вот код заказа:
for (int i = 0; i < _children.size(); ++i) {
UI * ui = _children.at(i)->_ui;
if (ui) {
if (i) {
UI * prev = _children.at(i-1)->_ui;
ui->setX(prev->x());
ui->setY(prev->height() + prev->y());
} else {
ui->setX(Object::_helper->nodeSize());
ui->setY(_ui->childOffset());
}
}
}
И пример того, как настроен QML:
UI {
id: main
width: childrenRect.width
height: childrenRect.height
Item {
height: childrenRect.height
Rectangle {
width: Helper.nodeSize
height: Helper.nodeSize
color: "red"
Text {
anchors.centerIn: parent
text: main.id
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button == Qt.RightButton)
Helper.gotoXY(main.absolutePosition())
else {
Helper.createObject(1, main)
Helper.updateRoot()
}
}
Rectangle {
height: main.height
width: 10
x: -10
color: "black"}
}
}
}
}
Когда вы думаете об этом, это поведение имеет смысл, нет ничего плохого в привязках, просто в ваших ожиданиях того, на что они способны.
Причина, по которой работает ваше «каскадное обновление», очень проста и указывает на то, почему ваш код не работает и как это исправить.
По сути, когда вы вставляете новый узел, он выталкивает все на «узел», поэтому ваш код не ломается, пока вы вставляете «последний» узел. Но если он не в конце, он немного отодвинет все, но это изменение не будет отражаться до тех пор, пока его родитель не обновит позиции. Если вы вызываете update из корня и вставка находится где-то глубже, ваш первый проход вызовет только первое обновление привязки для высоты, поэтому в следующем цикле обновления его следующий брат и сестра могут быть правильно расположены. Если бы вставка была глубже, было бы больше привязок, которые должны иметь место.
Причина, по которой новый узел вставлен правильно, заключается в том, что он не полагается на свойство, что его собственная вставка не изменится до следующего обновления, которое будет использовать все еще не обновленное значение, а затем оценит привязку, чтобы заставить ее работать в следующем цикле. ,
Я думаю, что самый быстрый способ сделать это — начать с уровня вставки и распространять обновление «наружу» и «вниз», например. обновляйте каждый следующий объект, затем следующих братьев и сестер родителя до братьев и сестер следующего родителя, пока вы не нажмете свой корневой объект. Это будет только перемещать объекты, которые находятся «под» и подвержены влиянию вставки, что должно сократить количество циклов по сравнению с вашим текущим лучшим решением.
Я немного поигрался с QML и JS, и выяснилось, что у меня нет проблем с графическим интерфейсом.
мой демонстрационный проект Умеет добавлять дочерние объекты, а также братьев и сестер в дерево (щелчком левой и правой кнопки мыши на красных полях). Логика может быть не полной, но основы работают очень гладко.
main.qml
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
id: app
width: 800
height: 600
property int lastNodeId: 0
function getId() { return lastNodeId++; }
Level { }
}
Level.qml
import QtQuick 2.0
Item {
id: frame
width: childrenRect.width
height: childrenRect.height
Rectangle {
width: 10
color: "#000000"height: parent.height
x: 0
y: 0
}
Column {
x: 10
id: content
Position { }
}
}
Красная коробка и окружающий предмет для размещения братьев и сестер Position.qml
import QtQuick 2.0
import "component_creation.js" as CC
Item {
id: position0
width: childrenRect.width
height: childrenRect.height
Rectangle {
color: "red"width: 80
height: 30
Text {
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
Component.onCompleted: text = app.getId() // Set once, avoid bindung
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: {
if (mouse.button == Qt.LeftButton)
{
CC.createChild(position0);
}
else if (mouse.button == Qt.RightButton)
{
CC.createSiblingAfter(position0)
}
else if (mouse.button == Qt.MiddleButton)
{
// no clue how to implement
// CC.createSiblingBefore(position0)
}
}
}
}
}
}
И немного JavaScript: component_creation.js
function createChild(parent_item) {
var component = Qt.createComponent("Level.qml");
if (component.status == Component.Ready)
{
var sprite = component.createObject(parent_item, {"x": 70, "y": 30});
if (sprite == null) console.log("Error creating Level object");
}
else
{
console.log("Error loading component:", component.errorString());
}
}
function createSiblingAfter(sibling_position)
{
var component = Qt.createComponent("Position.qml");
if (component.status == Component.Ready)
{
var sprite = component.createObject(sibling_position.parent);
if (sprite == null) console.log("Error creating Position object");
}
else
{
console.log("Error loading component:", component.errorString());
}
}
Надеюсь, это поможет.