geopro/src/app/PanelHeader.cpp

209 lines
8.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "PanelHeader.hpp"
#include "Theme.hpp"
#include <QButtonGroup>
#include <QColor>
#include <QHBoxLayout>
#include <QLabel>
#include <QSize>
#include <QStackedWidget>
#include <QToolButton>
#include <QVBoxLayout>
#include <QWidget>
namespace geopro::app {
namespace {
// ── 表头图标/尺寸(规范 §4.3:高 36px、标题图标 14px、操作按钮 24×24 含 16px 图标)──
constexpr int kHeaderHeight = 36; // 表头高度§4.3)。标准/Tab/分段表头共用,保持一致。
constexpr int kTitleIcon = 14; // 表头标题图标§4.3「14px 图标」)
constexpr int kActionIcon = 16; // 操作按钮内图标§9「按钮内 16px」
constexpr int kActionButton = 24; // 操作按钮命中区 24×24§4.3 / §12 无障碍 ≥24×24
constexpr int kTabIcon = 19; // Tab 图标
// 表头统一样式(标准表头 + Tab 表头共用)。字号/字重引用 Theme 排版令牌:
// 面板标题=title(15)、徽标=caption(12)、Tab 文本=body(13),加粗统一 semibold。
// #panelBadge 为中性计数徽标;#panelBadgeWarn 为“需注意”变体(语义 warning 色),
// 供异常计数等承载“待复查”含义的徽标使用(调用方改 objectName 即切换)。
// 表头底/标题/徽标 + 页签样式。页签选中态 = 强调色文字 + 2px 强调色下划线,
// 与视图/详情工具条完全一致(全 UI 切换控件统一这一套)。操作按钮(ElaIconButton)自绘 Fluent。
QString headerQss()
{
return QStringLiteral(
"#panelHeader { background:{{bg/panel}}; border-bottom:1px solid {{divider}}; }"
"#panelTitle { color:{{text/primary}}; font-size:%1px; font-weight:%3; }"
"#panelBadge { background:{{bg/hover}}; color:{{text/secondary}}; border-radius:9px;"
" padding:1px 7px; font-size:%2px; font-weight:%3; }"
"#panelBadgeWarn { background:{{status/warning-bg}}; color:{{status/warning}}; border-radius:9px;"
" padding:1px 7px; font-size:%2px; font-weight:%3; }"
"QToolButton#panelAction { border:none; border-radius:%5px; padding:0px; }"
"QToolButton#panelAction:hover { background:{{bg/hover}}; }"
"QToolButton#tabBtn { border:none; border-bottom:2px solid transparent; color:{{text/secondary}};"
" padding:8px 6px; font-size:%4px; }"
"QToolButton#tabBtn:hover { color:{{text/primary}}; }"
"QToolButton#tabBtn:checked { color:{{accent/primary}}; font-weight:%3;"
" border-bottom:2px solid {{accent/primary}}; }")
.arg(scaledPx(type::kTitle)) // %1 标题字号
.arg(scaledPx(type::kCaption)) // %2 徽标字号
.arg(type::kWeightSemibold) // %3 字重(多处)
.arg(scaledPx(type::kBody)) // %4 页签字号
.arg(radius::kSm); // %5 操作按钮悬停底圆角§3.2
}
// 数量徽标(默认隐藏,调用方 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;
}
// 表头操作按钮QToolButton + 项目 glyph 图标,随主题着色;悬停底由 #panelAction QSS 给)。
QWidget* makeActionButton(QWidget* parent, const HeaderAction& a)
{
auto* btn = new QToolButton(parent);
btn->setObjectName(QStringLiteral("panelAction"));
btn->setProperty("glyphId", static_cast<int>(a.first)); // 供调用方按图标定位并连接真实功能
setThemedGlyph(btn, a.first, kActionIcon);
btn->setIconSize(QSize(scaledPx(kActionIcon), scaledPx(kActionIcon)));
// 命中区固定 24×24§4.3 / §12 无障碍16px 图标居中、四周自然留出内距。
btn->setFixedSize(QSize(scaledPx(kActionButton), scaledPx(kActionButton)));
btn->setCursor(Qt::PointingHandCursor);
btn->setToolTip(a.second + QStringLiteral("(占位)"));
btn->setAutoRaise(true);
return btn;
}
} // namespace
QWidget* buildPanelHeader(Glyph icon, const QString& title, const QVector<HeaderAction>& actions)
{
auto* header = new QWidget();
header->setObjectName(QStringLiteral("panelHeader"));
header->setFixedHeight(scaledPx(kHeaderHeight));
geopro::app::applyTokenizedStyleSheet(header, headerQss());
auto* lay = new QHBoxLayout(header);
lay->setContentsMargins(12, 0, 8, 0);
lay->setSpacing(geopro::app::space::kSm);
auto* iconLbl = new QLabel(header);
setThemedGlyph(iconLbl, icon, 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<PanelTab>& tabs, const QVector<HeaderAction>& 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(scaledPx(kHeaderHeight));
geopro::app::applyTokenizedStyleSheet(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;
result.tabGroup = group;
for (int i = 0; i < tabs.size(); ++i) {
const PanelTab& t = tabs[i];
auto* btn = new QToolButton(header); // 页签与工具条统一: QToolButton + 强调色下划线 QSS
btn->setObjectName(QStringLiteral("tabBtn"));
btn->setText(t.title);
setThemedGlyph(btn, t.icon, kTabIcon, kGlyphTextGapPad); // 随主题着色 + 图标→文字6px(§6.7)
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;
}
SegmentedHeader buildSegmentedHeader(const QVector<QString>& segments,
const QVector<HeaderAction>& actions)
{
auto* header = new QWidget();
header->setObjectName(QStringLiteral("panelHeader"));
header->setFixedHeight(scaledPx(kHeaderHeight));
geopro::app::applyTokenizedStyleSheet(header, headerQss());
auto* hlay = new QHBoxLayout(header);
hlay->setContentsMargins(10, 0, 8, 0);
hlay->setSpacing(2);
auto* group = new QButtonGroup(header);
group->setExclusive(true);
SegmentedHeader result;
result.header = header;
for (int i = 0; i < segments.size(); ++i) {
auto* btn = new QToolButton(header); // 与异常/属性页签统一: tabBtn 样式 + 强调色下划线
btn->setObjectName(QStringLiteral("tabBtn"));
btn->setText(segments[i]);
btn->setCheckable(true);
btn->setCursor(Qt::PointingHandCursor);
group->addButton(btn, i);
hlay->addWidget(btn);
hlay->addSpacing(10);
result.buttons.append(btn);
}
hlay->addStretch();
for (const auto& a : actions) hlay->addWidget(makeActionButton(header, a));
if (!result.buttons.isEmpty()) result.buttons[0]->setChecked(true);
return result;
}
} // namespace geopro::app