#include "PanelHeader.hpp" #include "Theme.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace geopro::app { namespace { // ── 专业图标/字号尺寸(统一放大)── constexpr int kHeaderHeight = 42; constexpr int kTitleIcon = 20; // 表头标题图标 constexpr int kActionIcon = 19; // 表头操作按钮图标 constexpr int kTabIcon = 19; // Tab 图标 // 表头统一样式(标准表头 + Tab 表头共用)。字号/字重引用 Theme 排版令牌: // 面板标题=title(15)、徽标=caption(12)、Tab 文本=body(13),加粗统一 semibold。 // #panelBadge 为中性计数徽标;#panelBadgeWarn 为“需注意”变体(语义 warning 色), // 供异常计数等承载“待复查”含义的徽标使用(调用方改 objectName 即切换)。 // 表头底/标题/徽标样式。Tab(ElaToolButton)与操作按钮(ElaIconButton)自绘 Fluent, // 不再写它们的 QSS(选中态由 Ela 的 checked 高亮表达,而非自定义下划线)。 QString headerQss() { return QStringLiteral( "#panelHeader { background:#FFFFFF; border-bottom:1px solid #E6EAF1; }" "#panelTitle { color:#1F2A3D; font-size:%1px; font-weight:%3; }" "#panelBadge { background:#EAEEF5; color:#5A6B85; border-radius:9px;" " padding:1px 7px; font-size:%2px; font-weight:%3; }" "#panelBadgeWarn { background:%4; color:%5; border-radius:9px;" " padding:1px 7px; font-size:%2px; font-weight:%3; }") .arg(type::kTitle) .arg(type::kCaption) .arg(type::kWeightSemibold) .arg(QString::fromUtf8(semantic::kWarningFill)) .arg(QString::fromUtf8(semantic::kWarning)); } // 数量徽标(默认隐藏,调用方 setText+setVisible 显示)。 QLabel* makeBadge(QWidget* parent) { auto* badge = new QLabel(parent); badge->setObjectName(QStringLiteral("panelBadge")); badge->setAlignment(Qt::AlignCenter); badge->setMinimumWidth(16); badge->setVisible(false); return badge; } // 表头操作按钮(Fluent ElaIconButton;图标沿用项目 glyph 像素图)。 QWidget* makeActionButton(QWidget* parent, const HeaderAction& a) { auto* btn = new ElaIconButton( makeGlyph(a.first, QColor("#5A6B85"), kActionIcon).pixmap(kActionIcon, kActionIcon), parent); btn->setFixedSize(30, 30); // QPixmap 构造不设尺寸,显式固定防变形 btn->setCursor(Qt::PointingHandCursor); btn->setToolTip(a.second + QStringLiteral("(占位)")); return btn; } } // namespace QWidget* buildPanelHeader(Glyph icon, const QString& title, const QVector& actions) { auto* header = new QWidget(); header->setObjectName(QStringLiteral("panelHeader")); header->setFixedHeight(kHeaderHeight); geopro::app::applyThemedStyleSheet(header, headerQss()); auto* lay = new QHBoxLayout(header); lay->setContentsMargins(12, 0, 8, 0); lay->setSpacing(8); auto* iconLbl = new QLabel(header); iconLbl->setPixmap(makeGlyph(icon, QColor("#44546B"), kTitleIcon).pixmap(kTitleIcon, kTitleIcon)); lay->addWidget(iconLbl); auto* titleLbl = new QLabel(title, header); titleLbl->setObjectName(QStringLiteral("panelTitle")); lay->addWidget(titleLbl); lay->addWidget(makeBadge(header)); // 默认隐藏,调用方可经 findChild 更新 lay->addStretch(); for (const auto& a : actions) lay->addWidget(makeActionButton(header, a)); return header; } TabbedPanel buildTabbedPanel(const QVector& tabs, const QVector& actions) { auto* box = new QWidget(); auto* v = new QVBoxLayout(box); v->setContentsMargins(0, 0, 0, 0); v->setSpacing(0); auto* header = new QWidget(box); header->setObjectName(QStringLiteral("panelHeader")); header->setFixedHeight(kHeaderHeight); geopro::app::applyThemedStyleSheet(header, headerQss()); auto* hlay = new QHBoxLayout(header); hlay->setContentsMargins(10, 0, 8, 0); hlay->setSpacing(2); auto* stack = new QStackedWidget(box); auto* group = new QButtonGroup(box); group->setExclusive(true); TabbedPanel result; result.container = box; for (int i = 0; i < tabs.size(); ++i) { const PanelTab& t = tabs[i]; auto* btn = new ElaToolButton(header); // Fluent tab(选中态由 Ela checked 高亮) btn->setObjectName(QStringLiteral("tabBtn")); btn->setText(t.title); btn->setIcon(makeGlyph(t.icon, QColor("#5A6B85"), kTabIcon)); btn->setIconSize(QSize(kTabIcon, kTabIcon)); btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); btn->setCheckable(true); btn->setCursor(Qt::PointingHandCursor); group->addButton(btn, i); hlay->addWidget(btn); QLabel* badge = nullptr; if (t.hasBadge) { badge = makeBadge(header); hlay->addWidget(badge); } result.badges.append(badge); stack->addWidget(t.content); hlay->addSpacing(10); } hlay->addStretch(); for (const auto& a : actions) hlay->addWidget(makeActionButton(header, a)); QObject::connect(group, &QButtonGroup::idClicked, stack, &QStackedWidget::setCurrentIndex); if (auto* first = group->button(0)) first->setChecked(true); stack->setCurrentIndex(0); v->addWidget(header); v->addWidget(stack, 1); return result; } } // namespace geopro::app