fix(tree): 自绘清晰复选框(明暗都可见) + 选中整行连续(去碎片框)

- 多余选中框: 全局 QTreeView::item 去掉 border-radius+margin → 选中是整行连续一条, 不再浮动碎块
- light 复选框看不清: Fusion 原生复选框浅底边框过淡。writeCheckboxIcon 自绘 PNG
  (未选=明显边框空心框, 选中=强调色底+白勾), 明暗各一套, 经 QTreeView::indicator QSS 引用,
  主题切换重绘 → 明暗都清晰
This commit is contained in:
gaozheng 2026-06-10 12:21:01 +08:00
parent e3a1b18efa
commit 934e25be54
4 changed files with 71 additions and 2 deletions

View File

@ -158,6 +158,47 @@ QString writeChevronIcon(bool open, const QColor& color)
return path;
}
QString writeCheckboxIcon(bool checked, const QColor& border, const QColor& fill, const QColor& check,
const QString& tag)
{
constexpr int px = 16;
constexpr int kScale = 3;
QPixmap pm(px * kScale, px * kScale);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing, true);
const double s = px * kScale;
const double inset = s * 0.14;
const QRectF box(inset, inset, s - 2 * inset, s - 2 * inset);
const double r = s * 0.14;
if (checked) {
p.setPen(Qt::NoPen);
p.setBrush(fill);
p.drawRoundedRect(box, r, r);
QPen cpen(check, s * 0.12);
cpen.setCapStyle(Qt::RoundCap);
cpen.setJoinStyle(Qt::RoundJoin);
p.setPen(cpen);
p.drawLine(QPointF(s * 0.30, s * 0.52), QPointF(s * 0.44, s * 0.66));
p.drawLine(QPointF(s * 0.44, s * 0.66), QPointF(s * 0.70, s * 0.34));
} else {
QPen bpen(border, s * 0.085);
p.setPen(bpen);
p.setBrush(fill);
p.drawRoundedRect(box, r, r);
}
p.end();
const QString path = QDir(QDir::tempPath())
.filePath(QStringLiteral("geopro_chk_%1_%2.png")
.arg(tag, checked ? QStringLiteral("on")
: QStringLiteral("off")));
pm.save(path, "PNG");
return path;
}
namespace {
// 当前主题下的 chrome 图标色:取主题主文本色(暗=浅、亮=深),保证两种模式都清晰。
QColor themedIconColor()

View File

@ -46,4 +46,9 @@ void setThemedGlyph(QAbstractButton* button, Glyph type, int px);
// 配合 QTreeView::branch 背景使用,可让选中高亮不覆盖左侧缩进/箭头列且不丢箭头。
QString writeChevronIcon(bool open, const QColor& color);
// 生成清晰复选框 PNG供 QTreeView::indicator 的 QSS `image:url(...)`
// 未选=明显边框空心方框;选中=填充色方框 + 白色对勾。明暗各传一套色tag 区分文件名。
QString writeCheckboxIcon(bool checked, const QColor& border, const QColor& fill, const QColor& check,
const QString& tag);
} // namespace geopro::app

View File

@ -75,8 +75,6 @@ QTreeWidget, QListWidget, QTreeView, QListView {
}
QTreeWidget::item, QListWidget::item, QTreeView::item, QListView::item {
padding: 4px 8px;
border-radius: 6px;
margin: 1px 4px;
}
QTreeWidget::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QListView::item:hover {

View File

@ -6,6 +6,10 @@
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include <ElaDef.h>
#include <ElaTheme.h>
#include "Glyphs.hpp"
#include "Theme.hpp"
#include "dto/NavDto.hpp"
@ -38,6 +42,27 @@ ObjectTreePanel::ObjectTreePanel(QWidget* parent) : QWidget(parent) {
tree_ = new QTreeWidget(this);
tree_->setHeaderHidden(true);
tree_->setIndentation(14); // 收紧缩进
// 清晰复选框:自绘 PNG未选=明显边框空心框,选中=强调色底+白勾),明暗各一套、切换重绘。
// 规避 Fusion 原生复选框在浅底下边框过淡、看不清的问题。
auto applyCheckboxStyle = [this]() {
const bool dark = geopro::app::isDarkTheme();
const QColor border = dark ? QColor(0x8A, 0x92, 0x9C) : QColor(0x8A, 0x93, 0xA3);
const QColor boxBg = dark ? QColor(0x2B, 0x2D, 0x30) : QColor(0xFF, 0xFF, 0xFF);
const QColor accent = eTheme->getThemeColor(
dark ? ElaThemeType::Dark : ElaThemeType::Light, ElaThemeType::PrimaryNormal);
const QString tag = dark ? QStringLiteral("d") : QStringLiteral("l");
const QString off = geopro::app::writeCheckboxIcon(false, border, boxBg, Qt::white, tag);
const QString on = geopro::app::writeCheckboxIcon(true, accent, accent, Qt::white, tag);
tree_->setStyleSheet(QStringLiteral("QTreeView::indicator{ width:16px; height:16px; }"
"QTreeView::indicator:unchecked{ image:url(%1); }"
"QTreeView::indicator:checked{ image:url(%2); }")
.arg(off, on));
};
applyCheckboxStyle();
QObject::connect(eTheme, &ElaTheme::themeModeChanged, tree_,
[applyCheckboxStyle](ElaThemeType::ThemeMode) { applyCheckboxStyle(); });
lay->addWidget(tree_, 1);
hint_ = new QLabel(QStringLiteral("正在加载对象…"), this);