diff --git a/app/src/ui/hmi_editor_widget.cpp b/app/src/ui/hmi_editor_widget.cpp index f53f333..728b98f 100644 --- a/app/src/ui/hmi_editor_widget.cpp +++ b/app/src/ui/hmi_editor_widget.cpp @@ -50,10 +50,11 @@ public: // 返回图元自身坐标系中的矩形范围,用于绘制和命中测试 QRectF boundingRect() const override { - return {0, - 0, - static_cast(control_.bounds.width), - static_cast(control_.bounds.height)}; + if (!control_.binding.has_value()) + { + return controlRect(); + } + return controlRect().united(addressRect()); } void paint( @@ -62,10 +63,22 @@ public: QWidget *) override { // 留出一个像素边距,避免描边被图元边界裁剪 - const QRectF rect = boundingRect().adjusted(1, 1, -1, -1); + const QRectF rect = controlRect().adjusted(1, 1, -1, -1); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(QPen(QColor(QStringLiteral("#47545f")), 1)); + if (control_.binding.has_value()) + { + const QFont original_font = painter->font(); + QFont address_font = original_font; + address_font.setPixelSize(11); + painter->setFont(address_font); + painter->setPen(QColor(QStringLiteral("#5f6b73"))); + painter->drawText(addressRect(), Qt::AlignCenter, bindingText()); + painter->setFont(original_font); + painter->setPen(QPen(QColor(QStringLiteral("#47545f")), 1)); + } + switch (control_.type) { case HmiControlType::Button: @@ -136,7 +149,7 @@ public: // 选中框独立于控件类型,提示当前可编辑对象 painter->setBrush(Qt::NoBrush); painter->setPen(QPen(QColor(QStringLiteral("#1677a8")), 2)); - painter->drawRect(boundingRect().adjusted(0, 0, -1, -1)); + painter->drawRect(controlRect().adjusted(0, 0, -1, -1)); } } @@ -225,6 +238,33 @@ protected: } private: + // 控件本体范围保持领域坐标和尺寸语义不变 + QRectF controlRect() const + { + return {0, + 0, + static_cast(control_.bounds.width), + static_cast(control_.bounds.height)}; + } + + // 地址作为控件的附属信息显示在本体上方 + QRectF addressRect() const + { + constexpr qreal address_height = 16.0; + constexpr qreal address_spacing = 2.0; + return {0, + -address_height - address_spacing, + static_cast(control_.bounds.width), + address_height}; + } + + QString bindingText() const + { + return control_.binding.has_value() + ? QString::fromStdString(control_.binding->toString()) + : QString{}; + } + // 根据控件类型和运行数据组合当前应绘制的文字 QString textWithValue() const { @@ -433,7 +473,7 @@ void HmiEditorWidget::fitCurrentPage() return; } fitInView( - scene_->sceneRect().adjusted(-16, -16, 16, 16), + scene_->sceneRect().adjusted(-24, -24, 24, 24), Qt::KeepAspectRatio); } diff --git a/app/tests/main_window_tests.cpp b/app/tests/main_window_tests.cpp index a5b960d..4401430 100644 --- a/app/tests/main_window_tests.cpp +++ b/app/tests/main_window_tests.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -179,6 +181,20 @@ void testModeActionsControlEditingAvailability() "an unbound added control must be marked in the property panel"); require(text_edit->text() == QStringLiteral("按钮"), "property panel must show the control text"); + + const QList unbound_items = hmi_editor->scene()->selectedItems(); + require(unbound_items.size() == 1 && unbound_items.front()->boundingRect().top() == 0, + "an unbound HMI control must not reserve an address label area"); + HmiControl bound_button = *editor_service.findControl(page_id, "button-1"); + bound_button.binding = RegisterAddress{RegisterArea::M, 0}; + require(editor_service.updateControl(page_id, bound_button.id, bound_button).succeeded, + "binding an HMI button to M0 must succeed"); + hmi_editor->reloadPage(); + hmi_editor->selectControl(bound_button.id); + const QList bound_items = hmi_editor->scene()->selectedItems(); + require(bound_items.size() == 1 && bound_items.front()->boundingRect().top() < 0, + "a bound HMI control must include its address label above the control body"); + delete_control_action->trigger(); require(editor_service.findPage(page_id)->controls.empty(), "deleting a selected control must update the HMI page model");