From 85636931af096c4176cc96a661984bd8d877f20e Mon Sep 17 00:00:00 2001 From: gaozheng Date: Fri, 26 Jun 2026 07:49:08 +0800 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=E5=88=86=E6=AE=B5=E6=8A=98=E5=8F=A0?= =?UTF-8?q?=E6=97=B6=E5=90=91=E4=B8=8A=E6=94=B6=E8=B5=B7(=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E5=81=9C=E5=9C=A8=E5=8E=9F=E4=BD=8D=E4=B8=AD=E9=97=B4?= =?UTF-8?q?=E6=A0=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:各段 addWidget(sec,1) 等高平分 stretch,折叠后该段仍占等分高度→段头浮在那格顶部、下方留空, 看着像"停在当前位置中间"(仅面板不出滚动条/内容short于视口时可见)。 修法:据折叠态动态重排 stretch——展开段=1(吸收余量铺满)、折叠段=0(只占段头高);末加尾部弹簧, 全部折叠时置 1 把段头顶到顶部。CategorySection 暴露 isExpanded()+collapsedChanged() 信号驱动重排。 保留"全展开等高铺满"原行为(#7)。 构建:app 链接通过 --- src/app/panels/columns/CategoryAnalysisTab.cpp | 17 +++++++++++++++++ src/app/panels/columns/CategoryAnalysisTab.hpp | 7 +++++++ src/app/panels/columns/CategorySection.cpp | 5 ++++- src/app/panels/columns/CategorySection.hpp | 2 ++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/app/panels/columns/CategoryAnalysisTab.cpp b/src/app/panels/columns/CategoryAnalysisTab.cpp index d110e4b..d4efa73 100644 --- a/src/app/panels/columns/CategoryAnalysisTab.cpp +++ b/src/app/panels/columns/CategoryAnalysisTab.cpp @@ -22,12 +22,16 @@ CategoryAnalysisTab::CategoryAnalysisTab(geopro::data::DatasetFieldDictionary* d auto* content = new QWidget(scroll); auto* col = new QVBoxLayout(content); + col_ = col; col->setContentsMargins(0, space::kSm, 0, space::kSm); // 顶部留白:首段段头不贴顶 col->setSpacing(space::kSm); for (const CategorySpec& spec : categoryConfigs()) { auto* sec = new CategorySection(spec, dict, content); sections_[spec.id] = sec; + ordered_.push_back(sec); + connect(sec, &CategorySection::collapsedChanged, this, + &CategoryAnalysisTab::relayoutSections); // 折叠/展开 → 重排 stretch(向上收) const std::string segId = spec.id; connect(sec, &CategorySection::checkedDatasetsChanged, this, [this, segId](const QStringList& ids) { @@ -54,9 +58,22 @@ CategoryAnalysisTab::CategoryAnalysisTab(geopro::data::DatasetFieldDictionary* d // 某段内容增多时其最小高度(=内容总高)撑大,超出视口则由外层 QScrollArea 统一出纵向滚动条。 col->addWidget(sec, 1); } + // 尾部弹簧(末项):默认 0;全部段折叠时由 relayoutSections 置 1,吸收余量把段头顶到顶部。 + col->addStretch(0); scroll->setWidget(content); } +void CategoryAnalysisTab::relayoutSections() { + if (!col_) return; + int expanded = 0; + for (auto* sec : ordered_) + if (sec->isExpanded()) ++expanded; + // 展开段 stretch=1(吸收余量、铺满);折叠段 stretch=0(只占段头高,下方不再留空)。 + for (auto* sec : ordered_) col_->setStretchFactor(sec, sec->isExpanded() ? 1 : 0); + // 尾部弹簧:仅当全部折叠时=1(把所有段头顶到顶部);有任一展开段时=0(由展开段吸收余量)。 + col_->setStretch(col_->count() - 1, expanded == 0 ? 1 : 0); +} + void CategoryAnalysisTab::setBuckets(const CategoryBuckets& b) { const auto& cfg = categoryConfigs(); for (std::size_t i = 0; i < cfg.size() && i < b.segments.size(); ++i) { diff --git a/src/app/panels/columns/CategoryAnalysisTab.hpp b/src/app/panels/columns/CategoryAnalysisTab.hpp index 1f5afe5..b9a7273 100644 --- a/src/app/panels/columns/CategoryAnalysisTab.hpp +++ b/src/app/panels/columns/CategoryAnalysisTab.hpp @@ -4,6 +4,8 @@ #include #include #include + +class QVBoxLayout; #include "DatasetCategory.hpp" // CategoryBuckets #include "interact/SlicePlaneMath.hpp" // geopro::render::interact::SliceAxis #include "repo/RepoTypes.hpp" @@ -45,8 +47,13 @@ signals: private: void recomputeCheckedUnion(); + // 据各段折叠态重排 stretch:折叠段=0(仅段头高)、展开段=1(吸收余量);全折叠时尾部弹簧吸收→段头顶到顶部。 + // 仅在面板不产生滚动条(内容short于视口)时有可见效果——正是用户反馈的"折叠后停在原位中间"场景。 + void relayoutSections(); std::map sections_; + std::vector ordered_; // 按 categoryConfigs 顺序(relayout 遍历用) + QVBoxLayout* col_ = nullptr; // 段堆叠布局(末项=尾部弹簧) std::map checkedBySeg_; // 各段当前勾选(合并成并集) }; diff --git a/src/app/panels/columns/CategorySection.cpp b/src/app/panels/columns/CategorySection.cpp index a7efac1..f840b47 100644 --- a/src/app/panels/columns/CategorySection.cpp +++ b/src/app/panels/columns/CategorySection.cpp @@ -144,10 +144,13 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset connect(header_, &QToolButton::toggled, this, [this, syncHeader](bool on) { body_->setVisible(on); - syncHeader(); // ▾(展开)/▸(折叠) 切换 + syncHeader(); // ▾(展开)/▸(折叠) 切换 + emit collapsedChanged(); // 外层据此把折叠段 stretch 归 0、展开段吸收余量 → 折叠向上收 }); } +bool CategorySection::isExpanded() const { return header_ && header_->isChecked(); } + void CategorySection::setStructure(const std::vector& nodes) { structure_ = nodes; // 容器分层(项目根/GS/TM→ds)在 Task 12 接入真实结构后据此构建。 } diff --git a/src/app/panels/columns/CategorySection.hpp b/src/app/panels/columns/CategorySection.hpp index 70385cc..f03b4d0 100644 --- a/src/app/panels/columns/CategorySection.hpp +++ b/src/app/panels/columns/CategorySection.hpp @@ -37,9 +37,11 @@ public: QStringList checkedIds() const { return checkedDsIds(); } // 当前勾选 ds(异常显隐同步用) void refreshArrayFilter() { refreshArrayCombo(); } // 装置枚举异步加载后重填下拉 const CategorySpec& spec() const { return spec_; } + bool isExpanded() const; // 段头展开态(供外层按折叠状态重分配 stretch,实现"折叠向上收") signals: void checkedDatasetsChanged(const QStringList& dsIds); // 数据行勾选=渲染 + void collapsedChanged(); // 折叠/展开切换 → 外层 CategoryAnalysisTab 重排各段 stretch void generateVolumeRequested(const QString& dsTypeCode, const QStringList& sourceDsIds); // 段头「+新增三维体」 void detailRequested(const QString& dsId, const QString& ddCode, const QString& name); // 双击/右键=详情 void deleteDatasetRequested(const QString& dsId, const QString& ddCode); // 右键删除(切片/异常)