diff --git a/src/app/AxesSettingsPanel.cpp b/src/app/AxesSettingsPanel.cpp index 9d3e0ba..2ba1294 100644 --- a/src/app/AxesSettingsPanel.cpp +++ b/src/app/AxesSettingsPanel.cpp @@ -7,14 +7,43 @@ #include #include #include +#include #include #include +#include +#include #include #include "Theme.hpp" namespace geopro::app { +namespace { +// 点击轨道直接定位到点击处(默认 QSlider 点轨道只按 pageStep 步进;用户要求点哪跳哪)。 +class ClickJumpSlider : public QSlider { +public: + using QSlider::QSlider; + +protected: + void mousePressEvent(QMouseEvent* e) override { + if (e->button() == Qt::LeftButton && orientation() == Qt::Horizontal) { + QStyleOptionSlider opt; + initStyleOption(&opt); + const QRect handle = + style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + if (!handle.contains(e->pos())) { // 点在轨道(非手柄) → 跳到点击位置 + const int span = width() - handle.width(); + const int x = e->pos().x() - handle.width() / 2; + setValue(QStyle::sliderValueFromPosition(minimum(), maximum(), x, span)); + e->accept(); + return; + } + } + QSlider::mousePressEvent(e); // 点手柄 → 正常拖动 + } +}; +} // namespace + AxesSettingsPanel::AxesSettingsPanel(QWidget* parent) : QFrame(parent) { setFrameShape(QFrame::StyledPanel); applyTokenizedStyleSheet( @@ -64,7 +93,7 @@ AxesSettingsPanel::AxesSettingsPanel(QWidget* parent) : QFrame(parent) { auto* scaleLbl = new QLabel(QStringLiteral("放大系数"), this); scaleLbl->setStyleSheet(QStringLiteral("border:none;")); scaleRow->addWidget(scaleLbl); - scaleSlider_ = new QSlider(Qt::Horizontal, this); + scaleSlider_ = new ClickJumpSlider(Qt::Horizontal, this); scaleSlider_->setMinimum(1); scaleSlider_->setMaximum(10); scaleSlider_->setValue(1); diff --git a/src/app/panels/DatasetListPanel.cpp b/src/app/panels/DatasetListPanel.cpp index a97bd96..b396344 100644 --- a/src/app/panels/DatasetListPanel.cpp +++ b/src/app/panels/DatasetListPanel.cpp @@ -84,10 +84,11 @@ public: // 可勾选项:左侧画复选框(用当前 style 的指示器),文本整体右移。容器节点(项目/GS/TM)不画 // 复选框、名称紧跟展开图标(左留白小,与对象树容器一致;勿为对齐子级而预留空复选框列, // 否则容器名与展开图标间出现大段空白,见用户 #2 反馈)。 + const int box = 16; int textLeftPad = 6; const bool checkable = (idx.flags() & Qt::ItemIsUserCheckable); + const bool isContainer = idx.data(kDsDdCodeRole).toString() == QStringLiteral("container"); if (checkable) { - const int box = 16; QRect checkRect(r.left() + 12, r.top() + (r.height() - box) / 2, box, box); const auto cs = static_cast(idx.data(Qt::CheckStateRole).toInt()); QStyleOptionViewItem o(opt); @@ -98,6 +99,11 @@ public: QStyle* st = w ? w->style() : QApplication::style(); st->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &o, p, w); textLeftPad = 12 + box + 8; // 复选框右侧留白后再放文本 + } else if (isContainer) { + // 容器文本左缘对齐子级复选框的左缘(r.left()+12)——使「容器→带框子级」的视觉缩进 = 一个树级 + // (14px),与「带框父→带框子」一致,消除带框子级相对容器缩进过大(用户 #6)。只 +12(非整列宽), + // 故名称仍紧邻展开图标、无 #2 的大留白。 + textLeftPad = 12; } QString title = disp, meta; diff --git a/src/app/panels/ObjectTreePanel.cpp b/src/app/panels/ObjectTreePanel.cpp index c66911d..f260c03 100644 --- a/src/app/panels/ObjectTreePanel.cpp +++ b/src/app/panels/ObjectTreePanel.cpp @@ -86,8 +86,8 @@ public: const int count = idx.data(kRoleChildCount).toInt(); if (count > 0) { QFont f = opt.font; - f.setPointSizeF(-1); - f.setPixelSize(scaledPx(type::kCaption)); + f.setPixelSize(scaledPx(type::kCaption)); // setPixelSize 直接生效,无需先 setPointSizeF(-1) + // (-1 会触发 QFont::setPointSizeF 警告刷屏日志) f.setWeight(QFont::Normal); painter->setFont(f); painter->setPen(tokenColor("text/tertiary")); diff --git a/src/app/panels/columns/CategorySection.cpp b/src/app/panels/columns/CategorySection.cpp index 7ca6a0b..c76e765 100644 --- a/src/app/panels/columns/CategorySection.cpp +++ b/src/app/panels/columns/CategorySection.cpp @@ -169,6 +169,13 @@ bool CategorySection::passesFilters(const DsRow& row) const { } void CategorySection::rebuildList() { + // 增量保留:记住当前已勾选的 ds,重建后复原(仍存在的项保持勾选)。否则每次刷新(勾选对象/建体/ + // 存切片/建异常都会触发)清空全部勾选 → 渲染被重置,体验极差(用户反馈:必须增量更新)。 + std::set wasChecked; + for (QTreeWidgetItemIterator it(list_); *it; ++it) + if ((*it)->checkState(0) == Qt::Checked) + wasChecked.insert((*it)->data(0, kDsIdRole).toString().toStdString()); + std::vector filtered; filtered.reserve(rows_.size()); for (const auto& r : rows_) @@ -219,7 +226,8 @@ void CategorySection::rebuildList() { continue; } (*it)->setFlags((*it)->flags() | Qt::ItemIsUserCheckable); - (*it)->setCheckState(0, Qt::Unchecked); + const std::string id = (*it)->data(0, kDsIdRole).toString().toStdString(); + (*it)->setCheckState(0, wasChecked.count(id) ? Qt::Checked : Qt::Unchecked); // 复原勾选 } } list_->expandAll(); // 展开容器层级(项目根/GS/TM),让体/切片/异常可见 @@ -230,7 +238,7 @@ void CategorySection::rebuildList() { if (!(*hit)->isHidden()) contentH += (*hit)->text(0).contains(QLatin1Char('\n')) ? 52 : 30; list_->setMinimumHeight(contentH); - emitChecked(); // 重建后必为空选,清掉上次渲染勾选 + emitChecked(); // 上抛复原后的勾选集(保持渲染,不再清空 → 控制器据 diff 增量保留已渲染图元) } QStringList CategorySection::checkedDsIds() const { diff --git a/src/app/panels/columns/ColumnDrawer.cpp b/src/app/panels/columns/ColumnDrawer.cpp index 640eb5b..13dd8ad 100644 --- a/src/app/panels/columns/ColumnDrawer.cpp +++ b/src/app/panels/columns/ColumnDrawer.cpp @@ -24,6 +24,7 @@ ColumnDrawer::ColumnDrawer(QWidget* parent, geopro::data::DatasetFieldDictionary body_ = tabs; tabs->addTab(analysisTab_, QStringLiteral("三维分析")); tabs->addTab(col2D_, QStringLiteral("二维分析")); + tabs->tabBar()->setUsesScrollButtons(false); // 永不出左右滚动箭头(两 tab 必能平铺) // 折叠按钮:固定宽 18px,垂直拉伸。 // 用 SVG 图标(makeGlyph)而非 ◀/▶ 文字——三角符(U+25C0/25B6)不在 YaHei,作按钮文字会触发 @@ -61,7 +62,9 @@ void ColumnDrawer::resizeEvent(QResizeEvent* e) if (auto* tabs = qobject_cast(body_)) { const int n = tabs->count(); if (n > 0 && tabs->width() > 0) { - const int w = std::max(40, tabs->width() / n - 6); // 减 margin-right(4)余量 + // 每 tab 内容宽 = 总宽/n - 每 tab 非内容开销(全局 QSS padding 8+16+16=… 约 32 + margin 4)。 + // 稍欠一点宽避免溢出(溢出会触发滚动箭头);setUsesScrollButtons(false) 再兜底。 + const int w = std::max(40, tabs->width() / n - 42); tabs->tabBar()->setStyleSheet(QStringLiteral("QTabBar::tab{width:%1px;}").arg(w)); } }