feat/vtk-3d-view #7

Merged
gaozheng merged 301 commits from feat/vtk-3d-view into main 2026-06-27 18:43:52 +08:00
2 changed files with 62 additions and 6 deletions
Showing only changes of commit 575529e5a0 - Show all commits

View File

@ -1,14 +1,18 @@
#include "panels/DatasetListPanel.hpp" #include "panels/DatasetListPanel.hpp"
#include <QAbstractItemView> #include <QAbstractItemView>
#include <QApplication>
#include <QColor> #include <QColor>
#include <QHash> #include <QHash>
#include <QKeyEvent>
#include <QListWidget> #include <QListWidget>
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QMouseEvent>
#include <QObject> #include <QObject>
#include <QPainter> #include <QPainter>
#include <QPainterPath> #include <QPainterPath>
#include <QString> #include <QString>
#include <QStyle>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#include <QTreeWidget> #include <QTreeWidget>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
@ -77,6 +81,23 @@ public:
geopro::app::tokenColor("accent/primary")); geopro::app::tokenColor("accent/primary"));
} }
// 可勾选项:左侧画复选框(用当前 style 的指示器),文本整体右移。
int textLeftPad = 14;
const bool checkable = (idx.flags() & Qt::ItemIsUserCheckable);
if (checkable) {
const int box = 16;
QRect checkRect(r.left() + 12, r.top() + (r.height() - box) / 2, box, box);
const auto cs = static_cast<Qt::CheckState>(idx.data(Qt::CheckStateRole).toInt());
QStyleOptionViewItem o(opt);
o.rect = checkRect;
o.state &= ~QStyle::State_HasFocus;
o.state |= (cs == Qt::Checked ? QStyle::State_On : QStyle::State_Off);
const QWidget* w = opt.widget;
QStyle* st = w ? w->style() : QApplication::style();
st->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &o, p, w);
textLeftPad = 12 + box + 8; // 复选框右侧留白后再放文本
}
QString title = disp, meta; QString title = disp, meta;
const int nl = disp.indexOf(QLatin1Char('\n')); const int nl = disp.indexOf(QLatin1Char('\n'));
if (nl >= 0) { if (nl >= 0) {
@ -84,7 +105,7 @@ public:
meta = disp.mid(nl + 1); meta = disp.mid(nl + 1);
} }
const QRect textR = r.adjusted(14, 6, -12, -6); const QRect textR = r.adjusted(textLeftPad, 6, -12, -6);
// 标题 // 标题
QFont tf = opt.font; QFont tf = opt.font;
tf.setPixelSize(geopro::app::scaledPx(13)); tf.setPixelSize(geopro::app::scaledPx(13));
@ -106,6 +127,34 @@ public:
} }
p->restore(); p->restore();
} }
bool editorEvent(QEvent* ev, QAbstractItemModel* model, const QStyleOptionViewItem& opt,
const QModelIndex& idx) override {
if (!(idx.flags() & Qt::ItemIsUserCheckable))
return QStyledItemDelegate::editorEvent(ev, model, opt, idx);
const QRect r = opt.rect.adjusted(4, 2, -4, -2);
const int box = 16;
// 命中区放宽到复选框左侧整段(含一点文本起始),便于点击。
const QRect hit(r.left(), r.top(), 12 + box + 8, r.height());
auto toggle = [&]() {
const auto cur = static_cast<Qt::CheckState>(idx.data(Qt::CheckStateRole).toInt());
model->setData(idx, cur == Qt::Checked ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole);
};
if (ev->type() == QEvent::MouseButtonRelease) {
auto* me = static_cast<QMouseEvent*>(ev);
if (me->button() == Qt::LeftButton && hit.contains(me->pos())) {
toggle();
return true;
}
} else if (ev->type() == QEvent::KeyPress) {
auto* ke = static_cast<QKeyEvent*>(ev);
if (ke->key() == Qt::Key_Space || ke->key() == Qt::Key_Select) {
toggle();
return true;
}
}
return QStyledItemDelegate::editorEvent(ev, model, opt, idx);
}
}; };
} // namespace } // namespace

View File

@ -2,6 +2,7 @@
#include "panels/columns/Column3DDataset.hpp" #include "panels/columns/Column3DDataset.hpp"
#include "panels/columns/Column2DDataset.hpp" #include "panels/columns/Column2DDataset.hpp"
#include "panels/columns/Column3DAnalysis.hpp" #include "panels/columns/Column3DAnalysis.hpp"
#include "Theme.hpp"
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QPushButton> #include <QPushButton>
#include <QTabWidget> #include <QTabWidget>
@ -23,10 +24,16 @@ ColumnDrawer::ColumnDrawer(QWidget* parent)
tabs->addTab(col2D_, QStringLiteral("二维数据集")); tabs->addTab(col2D_, QStringLiteral("二维数据集"));
tabs->addTab(colAnalysis_, QStringLiteral("三维分析")); tabs->addTab(colAnalysis_, QStringLiteral("三维分析"));
// 折叠按钮:固定宽 16px垂直拉伸 // 折叠按钮:固定宽 18px垂直拉伸
toggleBtn_ = new QPushButton(QStringLiteral(""), this); toggleBtn_ = new QPushButton(QStringLiteral(""), this);
toggleBtn_->setFixedWidth(16); toggleBtn_->setFixedWidth(18);
toggleBtn_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); toggleBtn_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
toggleBtn_->setCursor(Qt::PointingHandCursor);
toggleBtn_->setToolTip(QStringLiteral("折叠 / 展开"));
geopro::app::applyTokenizedStyleSheet(toggleBtn_,
QStringLiteral("QPushButton{background:{{bg/panel-subtle}};color:{{text/secondary}};"
"border:none;border-left:1px solid {{border/default}};font-size:12px;}"
"QPushButton:hover{background:{{bg/hover}};color:{{accent/primary}};}"));
connect(toggleBtn_, &QPushButton::clicked, this, &ColumnDrawer::toggleCollapsed); connect(toggleBtn_, &QPushButton::clicked, this, &ColumnDrawer::toggleCollapsed);
// 根布局:[body_ | toggleBtn_],无边距 // 根布局:[body_ | toggleBtn_],无边距
@ -36,8 +43,8 @@ ColumnDrawer::ColumnDrawer(QWidget* parent)
root->addWidget(body_, 1); root->addWidget(body_, 1);
root->addWidget(toggleBtn_, 0); root->addWidget(toggleBtn_, 0);
// 展开时限宽 ~316px (300 body + 16 btn) // 展开时限宽 ~318px (300 body + 18 btn)
setMaximumWidth(316); setMaximumWidth(318);
} }
void ColumnDrawer::toggleCollapsed() void ColumnDrawer::toggleCollapsed()
@ -46,7 +53,7 @@ void ColumnDrawer::toggleCollapsed()
body_->setVisible(!collapsed_); body_->setVisible(!collapsed_);
toggleBtn_->setText(collapsed_ ? QStringLiteral("") : QStringLiteral("")); toggleBtn_->setText(collapsed_ ? QStringLiteral("") : QStringLiteral(""));
// 折叠后只保留按钮宽度;展开恢复上限 // 折叠后只保留按钮宽度;展开恢复上限
setMaximumWidth(collapsed_ ? 16 : 316); setMaximumWidth(collapsed_ ? 18 : 318);
} }
} // namespace geopro::app } // namespace geopro::app