From fbcbe4633b86b739626b485e1b09737f50ac1f42 Mon Sep 17 00:00:00 2001 From: suyu <1643689728@qq.com> Date: Thu, 27 Aug 2026 12:00:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E6=A2=AF=E5=BD=A2?= =?UTF-8?q?=E5=9B=BE=E7=BD=91=E7=BB=9C=E6=B3=A8=E9=87=8A=E5=BD=92=E5=B1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/domain/control_logic_model.cpp | 40 ++++++++- app/src/domain/control_logic_model.h | 1 + app/src/services/logic_editor_service.cpp | 85 ++++++++++++++++++-- app/src/services/logic_editor_service.h | 6 +- app/src/ui/logic_editor_widget.cpp | 16 +--- app/src/ui/main_window.cpp | 18 ++--- app/src/ui/main_window.h | 2 +- app/src/ui/main_window.ui | 4 +- app/tests/domain_tests.cpp | 22 +++++ app/tests/logic_editor_service_tests.cpp | 82 ++++++++++++++++++- app/tests/project_management_tests.cpp | 16 ++++ app/tests/runtime_panel_controller_tests.cpp | 18 ++--- 12 files changed, 263 insertions(+), 47 deletions(-) diff --git a/app/src/domain/control_logic_model.cpp b/app/src/domain/control_logic_model.cpp index ba6267f..818daee 100644 --- a/app/src/domain/control_logic_model.cpp +++ b/app/src/domain/control_logic_model.cpp @@ -454,7 +454,7 @@ bool LadderRung::validateStructure(std::string *error) const if (containsLineBreak(comment) || comment.size() > ProjectLimits::kMaximumRungCommentBytes) { - setError(error, "梯形图行注释必须是最多 128 个 UTF-8 字节的单行文本"); + setError(error, "网络注释必须是最多 128 个 UTF-8 字节的单行文本"); return false; } if (cells.empty()) @@ -531,6 +531,33 @@ bool LadderRung::validateForRunning(std::string *error) const return true; } +std::size_t ControlLogic::networkHeadIndex(std::size_t rung_index) const +{ + if (rung_index >= rungs.size()) + { + return rungs.size(); + } + while (rung_index > 0U) + { + const std::string &upper_id = rungs[rung_index - 1U].id; + const std::string &lower_id = rungs[rung_index].id; + const bool connected_to_previous = std::any_of( + verticalConnections.cbegin(), + verticalConnections.cend(), + [&upper_id, &lower_id](const VerticalConnection &connection) + { + return connection.upperRungId == upper_id + && connection.lowerRungId == lower_id; + }); + if (!connected_to_previous) + { + break; + } + --rung_index; + } + return rung_index; +} + bool ControlLogic::validate( const ProjectLimitSettings &limits, std::string *error) const { @@ -641,6 +668,17 @@ bool ControlLogic::validateStructure( return false; } } + for (std::size_t row = 1U; row < rungs.size(); ++row) + { + if (!rungs[row].comment.empty() && networkHeadIndex(row) != row) + { + setError( + error, + "第 " + std::to_string(row + 1U) + + " 行不是网络首行,不能保存网络注释"); + return false; + } + } return true; } diff --git a/app/src/domain/control_logic_model.h b/app/src/domain/control_logic_model.h index 1bc373c..4f53b4f 100644 --- a/app/src/domain/control_logic_model.h +++ b/app/src/domain/control_logic_model.h @@ -175,6 +175,7 @@ struct ControlLogic bool enabled = true; std::vector verticalConnections; + std::size_t networkHeadIndex(std::size_t rung_index) const; bool validate( const ProjectLimitSettings &limits, std::string *error = nullptr) const; diff --git a/app/src/services/logic_editor_service.cpp b/app/src/services/logic_editor_service.cpp index 6d28471..27cc7e5 100644 --- a/app/src/services/logic_editor_service.cpp +++ b/app/src/services/logic_editor_service.cpp @@ -590,6 +590,45 @@ bool LogicEditorService::rungsEqual( || nodesEqual(*left.output, *right.output); } +bool LogicEditorService::mergeNetworkComments( + ControlLogic *logic, std::string *error) +{ + if (logic == nullptr) + { + if (error != nullptr) + { + *error = "未找到控制逻辑"; + } + return false; + } + for (std::size_t row = 1U; row < logic->rungs.size(); ++row) + { + const std::size_t head = logic->networkHeadIndex(row); + if (head == row || logic->rungs[row].comment.empty()) + { + continue; + } + std::string &head_comment = logic->rungs[head].comment; + std::string &branch_comment = logic->rungs[row].comment; + if (!head_comment.empty() && head_comment != branch_comment) + { + if (error != nullptr) + { + *error = "无法合并网络:第 " + std::to_string(head + 1U) + + " 行和第 " + std::to_string(row + 1U) + + " 行存在不同注释,请先统一或清空其中一条"; + } + return false; + } + if (head_comment.empty()) + { + head_comment = branch_comment; + } + branch_comment.clear(); + } + return true; +} + bool LogicEditorService::cellsEqual( const LadderCell &left, const LadderCell &right) { @@ -691,6 +730,19 @@ const LadderRung *LogicEditorService::findRung( return found == logic->rungs.cend() ? nullptr : &*found; } +const LadderRung *LogicEditorService::findNetworkHeadRung( + const std::string &logic_id, const std::string &rung_id) const +{ + const ControlLogic *logic = findLogic(logic_id); + if (logic == nullptr) + { + return nullptr; + } + const std::size_t row = rungIndex(*logic, rung_id); + const std::size_t head = logic->networkHeadIndex(row); + return head == logic->rungs.size() ? nullptr : &logic->rungs[head]; +} + const LadderCell *LogicEditorService::findCell( const std::string &logic_id, const std::string &rung_id, @@ -1217,13 +1269,13 @@ LogicEditorResult LogicEditorService::removeRungs( return {true, LogicEditorError::None, {}, rung_ids.front()}; } -LogicEditorResult LogicEditorService::updateRungComment( +LogicEditorResult LogicEditorService::updateNetworkComment( const std::string &logic_id, const std::string &rung_id, const std::string &comment) { - const LadderRung *rung = findRung(logic_id, rung_id); - if (rung == nullptr) + const LadderRung *head = findNetworkHeadRung(logic_id, rung_id); + if (head == nullptr) { return failure(LogicEditorError::RungNotFound, "未找到梯形图行"); } @@ -1232,17 +1284,18 @@ LogicEditorResult LogicEditorService::updateRungComment( { return failure( LogicEditorError::InvalidOperation, - "行注释必须是最多 128 个 UTF-8 字节的单行文本"); + "网络注释必须是最多 128 个 UTF-8 字节的单行文本"); } - if (rung->comment == comment) + if (head->comment == comment) { - return {true, LogicEditorError::None, {}, rung_id}; + return {true, LogicEditorError::None, {}, head->id}; } + const std::string head_rung_id = head->id; HistoryState before = captureState(); Project &project = project_service_.editProject(); - editableRung(editableLogic(&project, logic_id), rung_id)->comment = comment; + editableRung(editableLogic(&project, logic_id), head_rung_id)->comment = comment; recordHistory(std::move(before)); - return {true, LogicEditorError::None, {}, rung_id}; + return {true, LogicEditorError::None, {}, head_rung_id}; } LogicEditResult LogicEditorService::applyConditionAndAdvance( @@ -1584,6 +1637,11 @@ LogicEditorResult LogicEditorService::setVerticalConnectionRange( } } std::string error; + if (connected && !mergeNetworkComments(editable, &error)) + { + rollbackEdit(std::move(before), modified_before); + return failure(LogicEditorError::InvalidOperation, error); + } if (!editable->validateStructure(project_service_.projectLimits(), &error)) { rollbackEdit(std::move(before), modified_before); @@ -2472,6 +2530,11 @@ LogicClipboardPasteResult LogicEditorService::pasteClipboard( } } std::string error; + if (!mergeNetworkComments(editable, &error)) + { + rollbackEdit(std::move(before), modified_before); + return {failure(LogicEditorError::InvalidOperation, error), {}, {}}; + } if (!editable->validateStructure(project_service_.projectLimits(), &error)) { rollbackEdit(std::move(before), modified_before); @@ -2687,6 +2750,12 @@ LogicClipboardPasteResult LogicEditorService::pasteClipboard( } } std::string error; + if (!fragment.verticalConnections.empty() + && !mergeNetworkComments(editable, &error)) + { + rollbackEdit(std::move(before), modified_before); + return {failure(LogicEditorError::InvalidOperation, error), {}, {}}; + } if (!editable->validateStructure(project_service_.projectLimits(), &error)) { rollbackEdit(std::move(before), modified_before); diff --git a/app/src/services/logic_editor_service.h b/app/src/services/logic_editor_service.h index 839a24f..bf0153e 100644 --- a/app/src/services/logic_editor_service.h +++ b/app/src/services/logic_editor_service.h @@ -165,6 +165,8 @@ public: const ControlLogic *findLogic(const std::string &logic_id) const; const LadderRung *findRung( const std::string &logic_id, const std::string &rung_id) const; + const LadderRung *findNetworkHeadRung( + const std::string &logic_id, const std::string &rung_id) const; const LadderCell *findCell( const std::string &logic_id, const std::string &rung_id, @@ -209,7 +211,7 @@ public: LogicEditorResult removeRungs( const std::string &logic_id, const std::vector &rung_ids); - LogicEditorResult updateRungComment( + LogicEditorResult updateNetworkComment( const std::string &logic_id, const std::string &rung_id, const std::string &comment); @@ -323,6 +325,8 @@ private: const ControlLogic &left, const ControlLogic &right); static bool rungsEqual( const LadderRung &left, const LadderRung &right); + static bool mergeNetworkComments( + ControlLogic *logic, std::string *error); static bool cellsEqual( const LadderCell &left, const LadderCell &right); static bool nodesEqual(const LogicNode &left, const LogicNode &right); diff --git a/app/src/ui/logic_editor_widget.cpp b/app/src/ui/logic_editor_widget.cpp index c529ccc..3e8551e 100644 --- a/app/src/ui/logic_editor_widget.cpp +++ b/app/src/ui/logic_editor_widget.cpp @@ -1055,21 +1055,7 @@ void LogicEditorWidget::rebuildRowLayout(const ControlLogic &logic) qreal next_top = kTop; for (std::size_t row = 0U; row < logic.rungs.size(); ++row) { - bool connected_to_previous = false; - if (row > 0U) - { - const std::string &upper_id = logic.rungs[row - 1U].id; - const std::string &lower_id = logic.rungs[row].id; - connected_to_previous = std::any_of( - logic.verticalConnections.cbegin(), - logic.verticalConnections.cend(), - [&upper_id, &lower_id](const VerticalConnection &connection) - { - return connection.upperRungId == upper_id - && connection.lowerRungId == lower_id; - }); - } - const bool network_head = row == 0U || !connected_to_previous; + const bool network_head = logic.networkHeadIndex(row) == row; if (network_head) { next_top += kCommentBandHeight; diff --git a/app/src/ui/main_window.cpp b/app/src/ui/main_window.cpp index 435147a..66add13 100644 --- a/app/src/ui/main_window.cpp +++ b/app/src/ui/main_window.cpp @@ -878,8 +878,8 @@ void MainWindow::configureActions() }); connect(ui_->deleteLogicAction, &QAction::triggered, this, &MainWindow::deleteSelectedLogicObject); - connect(ui_->editRungCommentAction, &QAction::triggered, - this, &MainWindow::editSelectedRungComment); + connect(ui_->editNetworkCommentAction, &QAction::triggered, + this, &MainWindow::editSelectedNetworkComment); addToolbarMenu( ui_->logicToolBar, tr("更多触点"), @@ -994,7 +994,7 @@ void MainWindow::configureAppearance() ui_->addAddAction->setIcon(makeUiIcon(UiIcon::Add)); ui_->addSubAction->setIcon(makeUiIcon(UiIcon::Subtract)); ui_->addCompareAction->setIcon(makeUiIcon(UiIcon::Compare)); - ui_->editRungCommentAction->setIcon(makeUiIcon(UiIcon::Comment)); + ui_->editNetworkCommentAction->setIcon(makeUiIcon(UiIcon::Comment)); ui_->syntaxCheckAction->setIcon(makeUiIcon(UiIcon::SyntaxCheck)); ui_->doubleCoilCheckAction->setIcon(makeUiIcon(UiIcon::Coil)); ui_->deleteLogicAction->setIcon(makeUiIcon(UiIcon::Delete)); @@ -1835,7 +1835,7 @@ void MainWindow::deleteLogicRung() statusBar()->showMessage(tr("当前行已删除,竖线连接已重新整理"), 3000); } -void MainWindow::editSelectedRungComment() +void MainWindow::editSelectedNetworkComment() { const std::string rung_id = logic_editor_widget_->selectedRungId(); if (rung_id.empty()) @@ -1843,9 +1843,9 @@ void MainWindow::editSelectedRungComment() showProjectResult(tr("网络注释"), tr("请先选择一个梯形图网络"), false); return; } - const LadderRung *rung = logic_editor_service_.findRung( + const LadderRung *head = logic_editor_service_.findNetworkHeadRung( current_logic_id_, rung_id); - if (rung == nullptr) + if (head == nullptr) { return; } @@ -1855,13 +1855,13 @@ void MainWindow::editSelectedRungComment() tr("网络注释"), tr("说明"), QLineEdit::Normal, - fromUtf8(rung->comment), + fromUtf8(head->comment), &accepted); if (!accepted) { return; } - const LogicEditorResult result = logic_editor_service_.updateRungComment( + const LogicEditorResult result = logic_editor_service_.updateNetworkComment( current_logic_id_, rung_id, toUtf8(comment)); if (!result.succeeded) { @@ -2453,7 +2453,7 @@ void MainWindow::updateModeUi(const QString &message) ui_->addAddAction->setEnabled(policy.allowsProjectEditing); ui_->addSubAction->setEnabled(policy.allowsProjectEditing); ui_->addCompareAction->setEnabled(policy.allowsProjectEditing); - ui_->editRungCommentAction->setEnabled(policy.allowsProjectEditing); + ui_->editNetworkCommentAction->setEnabled(policy.allowsProjectEditing); ui_->deleteLogicAction->setEnabled(policy.allowsProjectEditing); ui_->newProjectAction->setEnabled(policy.allowsProjectEditing); ui_->saveProjectAction->setEnabled(policy.allowsProjectEditing); diff --git a/app/src/ui/main_window.h b/app/src/ui/main_window.h index a52a154..1137f96 100644 --- a/app/src/ui/main_window.h +++ b/app/src/ui/main_window.h @@ -197,7 +197,7 @@ private: /** 删除当前选中的梯形图行 */ void deleteLogicRung(); /** 编辑当前网络的注释 */ - void editSelectedRungComment(); + void editSelectedNetworkComment(); /** 规整并检查当前梯形图 */ void runLogicSyntaxCheck(); /** 单独检查当前梯形图中的重复线圈输出 */ diff --git a/app/src/ui/main_window.ui b/app/src/ui/main_window.ui index a469d9d..d96b8d8 100644 --- a/app/src/ui/main_window.ui +++ b/app/src/ui/main_window.ui @@ -364,7 +364,7 @@ - + @@ -1318,7 +1318,7 @@ 在选中的空网格或横线处添加 D 值与常量比较条件 - + 网络注释 diff --git a/app/tests/domain_tests.cpp b/app/tests/domain_tests.cpp index e5ace6c..eccb4e7 100644 --- a/app/tests/domain_tests.cpp +++ b/app/tests/domain_tests.cpp @@ -696,6 +696,28 @@ void testEdgeAndCommentBoundaries() comment_rung.comment = "第一行\r第二行"; require(!comment_rung.validateStructure(), "a rung comment containing CR must be rejected"); + ControlLogic commented_logic; + commented_logic.id = "commented-logic"; + commented_logic.name = "Commented logic"; + LadderRung head; + head.id = "head"; + head.name = "Head"; + head.comment = "Network comment"; + LadderRung branch; + branch.id = "branch"; + branch.name = "Branch"; + branch.comment = "Hidden branch comment"; + commented_logic.rungs = {head, branch}; + commented_logic.verticalConnections = { + {"comment-edge", "head", "branch", 0}}; + require( + commented_logic.networkHeadIndex(1U) == 0U + && !commented_logic.validateStructure(), + "a connected branch row must not persist a hidden network comment"); + commented_logic.rungs[1].comment.clear(); + require(commented_logic.validateStructure(), + "a connected network must be valid when only its head stores the comment"); + Project project = makeValidProject(); project.registerComments = { {RegisterAddress{RegisterArea::M, 0}, "启动按钮"}, diff --git a/app/tests/logic_editor_service_tests.cpp b/app/tests/logic_editor_service_tests.cpp index a5fb9c0..ef85638 100644 --- a/app/tests/logic_editor_service_tests.cpp +++ b/app/tests/logic_editor_service_tests.cpp @@ -192,6 +192,85 @@ void testIndependentVerticalConnectionsAndNetworkSplit() "the model must reject vertical edges between non-adjacent rows"); } +void testNetworkCommentsFollowNetworkHeadsAndMergeAtomically() +{ + Fixture fixture; + const std::string first = fixture.addRung(); + const std::string second = fixture.addRung(); + const std::string third = fixture.addRung(); + + require( + fixture.editor.updateNetworkComment( + fixture.logicId, second, "下方网络注释").succeeded, + "an independent lower network must accept its own comment"); + fixture.editor.clearHistory(); + const LogicEditorResult merged = fixture.editor.setVerticalConnection( + fixture.logicId, first, second, 4, true); + const ControlLogic *logic = fixture.editor.findLogic(fixture.logicId); + require( + merged.succeeded && logic->rungs[0].comment == "下方网络注释" + && logic->rungs[1].comment.empty() + && fixture.editor.findNetworkHeadRung(fixture.logicId, second)->id + == first, + "merging a commented lower network into an empty upper network must move the comment to the new head"); + require( + fixture.editor.undo().succeeded + && fixture.editor.findRung(fixture.logicId, first)->comment.empty() + && fixture.editor.findRung(fixture.logicId, second)->comment + == "下方网络注释" + && fixture.editor.redo().succeeded, + "network merge comment movement must belong to the same undo transaction"); + + fixture.editor.clearHistory(); + const LogicEditorResult edited_from_branch = + fixture.editor.updateNetworkComment( + fixture.logicId, second, "从支路编辑后的注释"); + require( + edited_from_branch.succeeded && edited_from_branch.id == first + && fixture.editor.findRung(fixture.logicId, first)->comment + == "从支路编辑后的注释" + && fixture.editor.findRung(fixture.logicId, second)->comment.empty(), + "editing from a branch row must update only the network head comment"); + + require( + fixture.editor.updateNetworkComment( + fixture.logicId, third, "另一个网络注释").succeeded, + "the third independent network must accept a different comment"); + fixture.editor.clearHistory(); + fixture.projects.restoreModifiedState(false); + const LogicEditorResult conflict = fixture.editor.setVerticalConnection( + fixture.logicId, second, third, 4, true); + logic = fixture.editor.findLogic(fixture.logicId); + require( + !conflict.succeeded + && conflict.message.find("存在不同注释") != std::string::npos + && connectionAt(*logic, second, third, 4) == nullptr + && logic->rungs[0].comment == "从支路编辑后的注释" + && logic->rungs[2].comment == "另一个网络注释" + && !fixture.projects.isModified() && !fixture.editor.canUndo(), + "merging differently commented networks must fail without partial topology or history"); + + require( + fixture.editor.updateNetworkComment( + fixture.logicId, third, "从支路编辑后的注释").succeeded, + "the lower network comment must be editable before a retry"); + fixture.editor.clearHistory(); + require( + fixture.editor.setVerticalConnection( + fixture.logicId, second, third, 4, true).succeeded + && fixture.editor.findRung(fixture.logicId, third)->comment.empty(), + "merging equal network comments must keep one comment at the head"); + const std::string connection_id = connectionAt( + *fixture.editor.findLogic(fixture.logicId), second, third, 4)->id; + require( + fixture.editor.removeVerticalConnections( + fixture.logicId, {connection_id}).succeeded + && fixture.editor.findRung(fixture.logicId, first)->comment + == "从支路编辑后的注释" + && fixture.editor.findRung(fixture.logicId, third)->comment.empty(), + "splitting a network must not duplicate its head comment"); +} + void testInsertRowSplitsVerticalEdges() { Fixture fixture; @@ -1149,7 +1228,7 @@ void testWholeRowClipboardInsertionAndLimit() source_node.succeeded && fixture.editor.setHorizontalWireRange( fixture.logicId, second, 0, 1, true).succeeded - && fixture.editor.updateRungComment( + && fixture.editor.updateNetworkComment( fixture.logicId, first, "整行复制注释").succeeded && fixture.editor.setVerticalConnection( fixture.logicId, first, second, 2, true).succeeded, @@ -1413,6 +1492,7 @@ int main() { testContinuousGridAndIndependentHorizontalWires(); testIndependentVerticalConnectionsAndNetworkSplit(); + testNetworkCommentsFollowNetworkHeadsAndMergeAtomically(); testInsertRowSplitsVerticalEdges(); testDeleteRowMergesOnlyContinuousEdges(); testParallelBranchCreatesConnectedVisualRow(); diff --git a/app/tests/project_management_tests.cpp b/app/tests/project_management_tests.cpp index e3026c2..cfa3785 100644 --- a/app/tests/project_management_tests.cpp +++ b/app/tests/project_management_tests.cpp @@ -128,6 +128,7 @@ Project makeExampleProject() logic.verticalConnections = { {"vertical-left", "rung-1", "rung-2", 0}, {"vertical-right", "rung-1", "rung-2", 1}}; + logic.rungs[1].comment.clear(); project.controlLogics.push_back(std::move(logic)); project.registerComments.push_back({ RegisterAddress{RegisterArea::M, 0}, "Start signal"}); @@ -396,6 +397,21 @@ void testInvalidGridAndConnectionsAreRejected() == ProjectStorageError::InvalidProject, "duplicate edges at one row boundary must be rejected"); + QJsonObject hidden_comment = original; + logic = firstLogic(&hidden_comment); + rungs = logic.value(QStringLiteral("rungs")).toArray(); + rung = rungs.at(1).toObject(); + rung.insert(QStringLiteral("comment"), QStringLiteral("hidden branch comment")); + rungs.replace(1, rung); + logic.insert(QStringLiteral("rungs"), rungs); + replaceFirstLogic(&hidden_comment, logic); + const QString hidden_comment_path = directory.filePath( + "hidden-branch-comment.json"); + writeBytes(hidden_comment_path, QJsonDocument(hidden_comment).toJson()); + require(storage.load(hidden_comment_path.toStdString()).error + == ProjectStorageError::InvalidProject, + "a 2.0 project must reject comments stored on connected branch rows"); + QJsonObject non_adjacent = original; logic = firstLogic(&non_adjacent); edges = logic.value(QStringLiteral("verticalConnections")).toArray(); diff --git a/app/tests/runtime_panel_controller_tests.cpp b/app/tests/runtime_panel_controller_tests.cpp index c8b4d8c..c78a45d 100644 --- a/app/tests/runtime_panel_controller_tests.cpp +++ b/app/tests/runtime_panel_controller_tests.cpp @@ -564,11 +564,14 @@ void testLadderLayoutAndDragDeletion() logic_id, lower, 0, 1, true).succeeded && editor.setVerticalConnection( logic_id, upper, lower, 2, true).succeeded - && editor.updateRungComment( - logic_id, upper, "主网络注释").succeeded - && editor.updateRungComment( - logic_id, lower, "支路注释不应重复").succeeded, + && editor.updateNetworkComment( + logic_id, lower, "主网络注释").succeeded, "layout fixture must create a connected two-row network"); + require( + editor.findNetworkHeadRung(logic_id, lower)->id == upper + && editor.findRung(logic_id, upper)->comment == "主网络注释" + && editor.findRung(logic_id, lower)->comment.empty(), + "editing a branch row must store the comment only on the network head"); editor.clearHistory(); LogicEditorWidget widget(editor); @@ -578,7 +581,6 @@ void testLadderLayoutAndDragDeletion() QCoreApplication::processEvents(QEventLoop::AllEvents); bool found_head_comment = false; - bool found_branch_comment = false; for (QGraphicsItem *item : widget.scene()->items()) { auto *text = dynamic_cast(item); @@ -588,11 +590,9 @@ void testLadderLayoutAndDragDeletion() } found_head_comment = found_head_comment || text->text() == QStringLiteral("主网络注释"); - found_branch_comment = found_branch_comment - || text->text() == QStringLiteral("支路注释不应重复"); } - require(found_head_comment && !found_branch_comment, - "only the network-head comment must be rendered"); + require(found_head_comment, + "the comment edited from a branch must render above the network head"); constexpr qreal left_bus = 60.0; constexpr qreal cell_width = 96.0;