#include "titlebar.h" #include "ledindicator.h" #include "appcontext.h" #include "car.h" #include #include #include #include "global.h" namespace { // 首页/作业选择“中央卡片区”的左边缘:主区左内边距 24 + 左列 300 + 间距 24 const int kCardLeft = 300; // LedIndicator 的文字起点(圆点区宽度):radius(16)×2 + 左侧留白 18,见 LedIndicator::textLeft() const int kCommTextLeft = 50; // 顶栏左右区宽度。左区要容下最长的一条异常“气体检测仪通信异常” // (9 个全角字 × 43px = 387):274(左侧留白) + 50 + 387 + 10 = 721; // 上限 726——居中标题的左边缘在 750,再宽就压上去了。 const int kSideW = 726; } // namespace TitleBar::TitleBar(AppContext* ctx, QWidget* parent) : QWidget(parent), m_ctx(ctx) { setObjectName("TitleBar"); setFixedHeight(98); setStyleSheet(QString("QWidget#TitleBar{background-color:%1;border-bottom:1px solid %2;}") .arg(C::PANEL, C::BORDER)); // 左侧区(车载端通信状态,固定一行,不轮播) QWidget* leftBox = new QWidget(this); QHBoxLayout* leftLay = new QHBoxLayout(leftBox); leftLay->setSpacing(0); // 只有指示器 + 弹簧,留间距只会白白吃掉文字宽度 m_comm = new LedIndicator("系统通信正常", leftBox); // 高度沿用 LedIndicator 默认的 52:43px 字与 48px 直径的圆点外发光刚好容得下 m_comm->setTextPixelSize(45); // 与右侧“遥控器正常”同字号 m_comm->setBlinkText(true); // 异常时文字跟着圆点一起闪 m_comm->setState(LedIndicator::Green); leftLay->setContentsMargins(kCardLeft - 24 - kCommTextLeft, 0, 0, 0); leftLay->addWidget(m_comm, 0, Qt::AlignLeft | Qt::AlignVCenter); leftLay->addStretch(1); // 右侧区(遥控器状态:链路 + 电量) QWidget* rightBox = new QWidget(this); QHBoxLayout* rightLay = new QHBoxLayout(rightBox); rightLay->setContentsMargins(0, 0, 20, 0); rightLay->setSpacing(30); rightLay->addStretch(1); m_rc = new LedIndicator("遥控器断开", rightBox); m_rc->setFixedHeight(80); m_rc->setTextPixelSize(45); m_rc->setState(LedIndicator::RedBlink); rightLay->addWidget(m_rc); m_rcBat = new QLabel(rightBox); m_rcBat->setAlignment(Qt::AlignCenter); rightLay->addWidget(m_rcBat); setRcBattery(-1); // 左右两侧区等宽(宽度见 kSideW:左区要放得下最长的一条异常) leftBox->setFixedWidth(kSideW); rightBox->setFixedWidth(kSideW); // 标题(居中) m_title = new QLabel("", this); m_title->setAlignment(Qt::AlignCenter); m_title->setStyleSheet(QString("color:%1;font-size:60px;font-weight:700;").arg(C::TEXT)); // 用 QGridLayout 让标题横跨整条顶栏居中,而不是夹在左右两区之间拉伸: // 后者下标题的可用宽度 = 1920 - 24×2 - 两侧区宽×2,左区为了放下通信文字一加宽, // 标题就被挤窄甚至裁字;横跨三列后标题始终居中于屏幕,两侧区多宽都不影响它。 QGridLayout* lay = new QGridLayout(this); lay->setContentsMargins(24, 6, 24, 6); lay->setSpacing(0); lay->addWidget(leftBox, 0, 0, Qt::AlignLeft | Qt::AlignVCenter); lay->addWidget(rightBox, 0, 2, Qt::AlignRight | Qt::AlignVCenter); lay->addWidget(m_title, 0, 0, 1, 3, Qt::AlignCenter); // 车载端通信状态:只在文案变化时由通信层通知,这里不做任何轮播。 connect(m_ctx->comm(), &CarCommunication::commTextChanged, this, &TitleBar::onCommTextChanged); // 每个页面各有自己的顶条实例,构造时通信层可能早就报过状态了;而 commTextChanged // 只在内容变化时才发,所以这里必须先取一次当前值,否则新页面的顶条会一直停在构造时的初值。 updateComm(); // 遥控器链路与电量刷新 connect(m_ctx->rc(), &RcCommunication::linkChanged, this, &TitleBar::onRcLink); connect(m_ctx->rc(), &RcCommunication::frameReceived, this, &TitleBar::onRcFrame); onRcLink(m_ctx->rc()->linkOk()); } void TitleBar::onCommTextChanged() { updateComm(); } // 左区固定显示通信状态:链路断或带 All_Comm_FaultCode 就红色闪烁,其余绿灯。 // 文案由通信层渲染(Telemetry::commText),这里只决定配色。 void TitleBar::updateComm() { const Telemetry& t = m_ctx->telemetry; const bool warn = !t.commOk || t.commCode != 0; m_comm->setText(t.commText); m_comm->setState(warn ? LedIndicator::RedBlink : LedIndicator::Green); m_comm->setTextColor(QColor(warn ? C::DANGER : C::TEXT)); // 只有异常才用红色警示 } void TitleBar::setTitle(const QString& title) { m_title->setText(title); } void TitleBar::onRcLink(bool ok) { if (ok) { m_rc->setText("遥控器正常"); m_rc->setState(LedIndicator::Green); } else { m_rc->setText("遥控器断开"); m_rc->setState(LedIndicator::RedBlink); setRcBattery(-1); } } void TitleBar::setRcBattery(int battery) { // battery < 0 表示链路断开,无电量可显示 const bool valid = battery >= 0; const QString color = !valid ? C::TEXT_SUB : (battery <= 20 ? C::DANGER : C::SUCCESS); m_rcBat->setText(valid ? QString("%1%").arg(battery) : QString("--%")); m_rcBat->setStyleSheet(QString("color:%1;font-size:50px;").arg(color)); } void TitleBar::onRcFrame(const RcFrame& f) { setRcBattery(f.battery); }