refactor/pure-qt-ui #3

Merged
gaozheng merged 56 commits from refactor/pure-qt-ui into main 2026-06-10 18:41:53 +08:00
2 changed files with 42 additions and 29 deletions
Showing only changes of commit 3ccb8df4ed - Show all commits

View File

@ -3,12 +3,15 @@
#include <QAbstractButton> #include <QAbstractButton>
#include <QActionGroup> #include <QActionGroup>
#include <QColor> #include <QColor>
#include <QFont>
#include <QFrame> #include <QFrame>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QIcon>
#include <QLabel> #include <QLabel>
#include <QMenu> #include <QMenu>
#include <QMenuBar> #include <QMenuBar>
#include <QPushButton> #include <QPainter>
#include <QPixmap>
#include <QSize> #include <QSize>
#include <QStringList> #include <QStringList>
#include <QToolButton> #include <QToolButton>
@ -50,6 +53,29 @@ QWidget* makeIconButton(QWidget* parent, Glyph icon, const QString& tip)
return btn; return btn;
} }
// 圆形头像图标:强调色填充 + 白色缩写。2x 绘制保证高 DPI 清晰。
QPixmap renderAvatar(const QString& initials, int px, const QColor& bg, const QColor& fg)
{
constexpr int kScale = 2;
const int s = px * kScale;
QPixmap pm(s, s);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing, true);
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(0, 0, s, s);
QFont f = p.font();
f.setPixelSize(static_cast<int>(s * 0.4));
f.setBold(true);
p.setFont(f);
p.setPen(fg);
p.drawText(QRect(0, 0, s, s), Qt::AlignCenter, initials);
p.end();
pm.setDevicePixelRatio(kScale);
return pm;
}
// ── 四个菜单(结构对齐需求;叶子项当前为静态占位,后续接真实页面)── // ── 四个菜单(结构对齐需求;叶子项当前为静态占位,后续接真实页面)──
QMenu* buildViewMenu(QWidget* p) QMenu* buildViewMenu(QWidget* p)
{ {
@ -146,7 +172,8 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) {
" subcontrol-position: right center; subcontrol-origin: padding; right:8px; }" " subcontrol-position: right center; subcontrol-origin: padding; right:8px; }"
"QToolButton#iconBtn { border:none; border-radius:8px; padding:8px; }" "QToolButton#iconBtn { border:none; border-radius:8px; padding:8px; }"
"QToolButton#iconBtn:hover { background:{{bg/hover}}; }" "QToolButton#iconBtn:hover { background:{{bg/hover}}; }"
"#userBtn { border:none; border-radius:8px; padding:2px 4px; text-align:left; }" "#userBtn { border:none; border-radius:8px; padding:4px 10px 4px 6px;"
" color:{{text/primary}}; font-size:%3px; }"
"#userBtn:hover { background:{{bg/hover}}; }" "#userBtn:hover { background:{{bg/hover}}; }"
"#userBtn::menu-indicator { image:none; }" "#userBtn::menu-indicator { image:none; }"
"#avatar { background:{{accent/primary}}; color:{{text/on-primary}}; border-radius:17px; font-weight:%2;" "#avatar { background:{{accent/primary}}; color:{{text/on-primary}}; border-radius:17px; font-weight:%2;"
@ -205,37 +232,21 @@ TopBar::TopBar(QWidget* parent) : QWidget(parent) {
lay->addWidget(makeDivider(this)); lay->addWidget(makeDivider(this));
lay->addSpacing(12); lay->addSpacing(12);
// 用户区:头像 + 姓名 + 职务 同一行整块可点击QPushButton + 子布局,子控件鼠标穿透→点击命中按钮)。 // 用户区:头像(圆形图标) + 姓名·职务 同一行,整块可点击 → 退出登录菜单。
auto* userBtn = new QPushButton(this); // 用 QToolButton(图标+文字),而非 QPushButton+子布局——后者按空文本算尺寸会把内容挤成一团。
auto* userBtn = new QToolButton(this);
userBtn->setObjectName(QStringLiteral("userBtn")); userBtn->setObjectName(QStringLiteral("userBtn"));
userBtn->setIcon(QIcon(renderAvatar(QStringLiteral("ZL"), 30,
geopro::app::tokenColor("accent/primary"), Qt::white)));
userBtn->setIconSize(QSize(30, 30));
userBtn->setText(QStringLiteral("张磊 · 高级工程师"));
userBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
userBtn->setPopupMode(QToolButton::InstantPopup);
userBtn->setCursor(Qt::PointingHandCursor); userBtn->setCursor(Qt::PointingHandCursor);
userBtn->setFlat(true);
auto* userLay = new QHBoxLayout(userBtn);
userLay->setContentsMargins(6, 2, 10, 2);
userLay->setSpacing(8);
auto* avatar = new QLabel(QStringLiteral("ZL"), userBtn);
avatar->setObjectName(QStringLiteral("avatar"));
avatar->setFixedSize(34, 34);
avatar->setAlignment(Qt::AlignCenter);
avatar->setAttribute(Qt::WA_TransparentForMouseEvents);
userLay->addWidget(avatar);
auto* userName = new QLabel(QStringLiteral("张磊"), userBtn);
userName->setObjectName(QStringLiteral("userName"));
userName->setAttribute(Qt::WA_TransparentForMouseEvents);
userLay->addWidget(userName);
auto* userRole = new QLabel(QStringLiteral("高级工程师"), userBtn);
userRole->setObjectName(QStringLiteral("userRole"));
userRole->setAttribute(Qt::WA_TransparentForMouseEvents);
userLay->addWidget(userRole);
auto* userMenu = new QMenu(userBtn); auto* userMenu = new QMenu(userBtn);
QObject::connect(userMenu->addAction(QStringLiteral("退出登录")), &QAction::triggered, this, QObject::connect(userMenu->addAction(QStringLiteral("退出登录")), &QAction::triggered, this,
[this] { emit logoutRequested(); }); [this] { emit logoutRequested(); });
userBtn->setMenu(userMenu); userBtn->setMenu(userMenu);
lay->addWidget(userBtn); lay->addWidget(userBtn);
} }

View File

@ -999,7 +999,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
const QSettings settings; const QSettings settings;
const QByteArray geo = settings.value(QStringLiteral("ui/geometry")).toByteArray(); const QByteArray geo = settings.value(QStringLiteral("ui/geometry")).toByteArray();
if (!geo.isEmpty()) window.restoreGeometry(geo); if (!geo.isEmpty()) window.restoreGeometry(geo);
const QByteArray dockState = settings.value(QStringLiteral("ui/dockState")).toByteArray(); // 注意ADS 按 dock 唯一名作键。改过 dock 名后旧布局会失配 → bump 此键丢弃旧布局,
// 回落到下方 addDockWidget 的默认排布(再改 dock 名时同样要 bump 版本号)。
const QByteArray dockState = settings.value(QStringLiteral("ui/dockState_v2")).toByteArray();
if (!dockState.isEmpty()) { if (!dockState.isEmpty()) {
dockManager->restoreState(dockState); dockManager->restoreState(dockState);
// restoreState 重建停靠区会重新显示 ADS 标题栏,再隐藏一次保持“无双标题”。 // restoreState 重建停靠区会重新显示 ADS 标题栏,再隐藏一次保持“无双标题”。
@ -1010,7 +1012,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
QObject::connect(qApp, &QCoreApplication::aboutToQuit, dockManager, [dockManager, &window]() { QObject::connect(qApp, &QCoreApplication::aboutToQuit, dockManager, [dockManager, &window]() {
QSettings settings; QSettings settings;
settings.setValue(QStringLiteral("ui/geometry"), window.saveGeometry()); settings.setValue(QStringLiteral("ui/geometry"), window.saveGeometry());
settings.setValue(QStringLiteral("ui/dockState"), dockManager->saveState()); settings.setValue(QStringLiteral("ui/dockState_v2"), dockManager->saveState());
}); });
} }