feat/object-selection-panels #4

Merged
gaozheng merged 18 commits from feat/object-selection-panels into main 2026-06-10 21:33:30 +08:00
2 changed files with 33 additions and 4 deletions
Showing only changes of commit 3daaad3de3 - Show all commits

View File

@ -1,5 +1,7 @@
#include "WorkbenchNavController.hpp" #include "WorkbenchNavController.hpp"
#include <QMetaObject>
#include "dto/NavDto.hpp" #include "dto/NavDto.hpp"
namespace geopro::controller { namespace geopro::controller {
@ -10,8 +12,8 @@ using data::Workspace;
WorkbenchNavController::WorkbenchNavController(data::IProjectRepository& repo, QObject* parent) WorkbenchNavController::WorkbenchNavController(data::IProjectRepository& repo, QObject* parent)
: QObject(parent), repo_(repo) {} : QObject(parent), repo_(repo) {}
namespace {
// RAII进入公共导航操作时置忙驱动等待光标任何返回路径都复位——保证 busyChanged 配平。 // RAII进入公共导航操作时置忙驱动等待光标任何返回路径都复位——保证 busyChanged 配平。
// 命名(非匿名)以匹配 controller 的 friend 声明,从而在析构时排空挂起的勾选请求。
struct BusyGuard { struct BusyGuard {
WorkbenchNavController* self; WorkbenchNavController* self;
bool* busy; bool* busy;
@ -20,11 +22,16 @@ struct BusyGuard {
emit self->busyChanged(true); emit self->busyChanged(true);
} }
~BusyGuard() { ~BusyGuard() {
WorkbenchNavController* ctrl = self; // 取本地副本lambda 不能捕获成员
*busy = false; *busy = false;
emit self->busyChanged(false); emit ctrl->busyChanged(false);
// 触发源是延迟合并发射,可能落在嵌套事件循环里:用队列调用在调用栈/嵌套循环展开后再排空,
// 那时 busy_ 已可靠为 false重放才会真正执行lambda 捕获的 ctrl 生命周期与应用一致,安全)。
if (ctrl->checkedTmsPending_)
QMetaObject::invokeMethod(
ctrl, [ctrl] { ctrl->drainPendingCheckedTms(); }, Qt::QueuedConnection);
} }
}; };
} // namespace
void WorkbenchNavController::start() { void WorkbenchNavController::start() {
if (busy_) return; if (busy_) return;
@ -51,6 +58,10 @@ void WorkbenchNavController::loadProjectsAndStructure() {
} }
lastProjects_ = ps.value.rows; lastProjects_ = ps.value.rows;
tmExceptionCache_.clear(); tmExceptionCache_.clear();
currentParentId_.clear(); // 切项目/工作空间重置选中态spec §6
currentParentConfType_ = 0;
checkedTmsPending_ = false; // 丢弃跨项目的陈旧挂起重放
pendingCheckedTms_.clear();
QString curP; QString curP;
if (!ps.value.rows.empty()) { if (!ps.value.rows.empty()) {
const auto& first = ps.value.rows.front(); const auto& first = ps.value.rows.front();
@ -107,6 +118,10 @@ void WorkbenchNavController::switchProject(const QString& projectId) {
} }
lastStructNodes_ = st.value; lastStructNodes_ = st.value;
tmExceptionCache_.clear(); tmExceptionCache_.clear();
currentParentId_.clear(); // 切项目/工作空间重置选中态spec §6
currentParentConfType_ = 0;
checkedTmsPending_ = false; // 丢弃跨项目的陈旧挂起重放
pendingCheckedTms_.clear();
emit structureLoaded(QString::fromStdString(currentProjectName_), st.value); emit structureLoaded(QString::fromStdString(currentProjectName_), st.value);
} }
@ -166,7 +181,11 @@ void WorkbenchNavController::loadMoreFiles() {
} }
void WorkbenchNavController::setCheckedTms(const QStringList& tmObjectIds) { void WorkbenchNavController::setCheckedTms(const QStringList& tmObjectIds) {
if (busy_) return; if (busy_) { // 触发源是延迟合并发射,可能落在别的同步操作的嵌套事件循环里:
pendingCheckedTms_ = tmObjectIds; // 不丢弃,记下最新一次请求,待空闲重放
checkedTmsPending_ = true;
return;
}
BusyGuard guard(this, &busy_); BusyGuard guard(this, &busy_);
auto nameOf = [this](const std::string& id) -> std::string { auto nameOf = [this](const std::string& id) -> std::string {
for (const auto& n : lastStructNodes_) for (const auto& n : lastStructNodes_)
@ -198,6 +217,12 @@ void WorkbenchNavController::setCheckedTms(const QStringList& tmObjectIds) {
emit exceptionTreeLoaded(groups, total); emit exceptionTreeLoaded(groups, total);
} }
void WorkbenchNavController::drainPendingCheckedTms() {
if (busy_ || !checkedTmsPending_) return;
checkedTmsPending_ = false; // 先清标志再重放,避免重入自旋
setCheckedTms(pendingCheckedTms_); // 此时 busy_=false会正常执行
}
void WorkbenchNavController::selectDataset(const QString& dsObjectId) { void WorkbenchNavController::selectDataset(const QString& dsObjectId) {
if (dsObjectId.isEmpty() || busy_) return; if (dsObjectId.isEmpty() || busy_) return;
BusyGuard guard(this, &busy_); BusyGuard guard(this, &busy_);

View File

@ -45,10 +45,14 @@ signals:
void loadFailed(const QString& stage, const QString& message); void loadFailed(const QString& stage, const QString& message);
private: private:
friend struct BusyGuard; // 允许在 guard 析构时排空挂起的勾选请求
void loadProjectsAndStructure(); // start + switchWorkspace 共用 void loadProjectsAndStructure(); // start + switchWorkspace 共用
void drainPendingCheckedTms(); // 空闲后重放最近一次被挂起的勾选集
data::IProjectRepository& repo_; data::IProjectRepository& repo_;
bool busy_ = false; bool busy_ = false;
bool checkedTmsPending_ = false;
QStringList pendingCheckedTms_;
std::vector<data::ProjectSummary> lastProjects_; std::vector<data::ProjectSummary> lastProjects_;
std::string currentWorkspaceId_, currentProjectId_, currentProjectName_, currentCrsCode_; std::string currentWorkspaceId_, currentProjectId_, currentProjectName_, currentCrsCode_;
std::string currentParentId_; std::string currentParentId_;