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 39 additions and 12 deletions
Showing only changes of commit 68296a5d36 - Show all commits

View File

@ -2,9 +2,12 @@
#include <QLabel>
#include <QSignalBlocker>
#include <QStringList>
#include <QTimer>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include <functional>
#include "Glyphs.hpp"
#include "Theme.hpp"
@ -13,17 +16,22 @@
namespace geopro::app {
namespace {
// TM 节点把 tmObjectId 存在该角色GS/项目根节点为空。
constexpr int kRoleTmId = Qt::UserRole + 2;
constexpr int kRoleObjId = Qt::UserRole + 2; // 节点对象 idGS/TM 都存)
constexpr int kRoleConfType = Qt::UserRole + 3; // 1=GS 2=TM
void addNodes(QTreeWidgetItem* parent, const std::vector<data::dto::StructTreeNode>& nodes) {
for (const auto& n : nodes) {
auto* item = new QTreeWidgetItem(parent);
item->setText(0, QString::fromStdString(n.node.name));
item->setData(0, kRoleObjId, QString::fromStdString(n.node.id));
if (n.isTm) {
item->setData(0, kRoleTmId, QString::fromStdString(n.node.id));
item->setData(0, kRoleConfType, 2);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(0, Qt::Unchecked); // 真实数据渲染下一轮接入,默认不勾
item->setCheckState(0, Qt::Unchecked);
} else {
item->setData(0, kRoleConfType, 1); // GS
item->setFlags(item->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate);
item->setCheckState(0, Qt::Unchecked);
}
addNodes(item, n.children);
}
@ -49,12 +57,28 @@ ObjectTreePanel::ObjectTreePanel(QWidget* parent) : QWidget(parent) {
lay->addWidget(hint_);
QObject::connect(tree_, &QTreeWidget::itemClicked, this, [this](QTreeWidgetItem* item, int) {
const QString tmId = item->data(0, kRoleTmId).toString();
if (!tmId.isEmpty()) emit tmClicked(tmId);
const QString id = item->data(0, kRoleObjId).toString();
const int confType = item->data(0, kRoleConfType).toInt();
if (!id.isEmpty() && confType != 0) emit objectClicked(id, confType);
});
// 勾选变化GS 级联会触发多次 itemChanged用 0ms 单发合并成一次「收集勾选叶子并发射」。
QObject::connect(tree_, &QTreeWidget::itemChanged, this, [this](QTreeWidgetItem*, int) {
if (checkPending_) return;
checkPending_ = true;
QTimer::singleShot(0, this, [this]() {
checkPending_ = false;
QStringList tmIds;
std::function<void(QTreeWidgetItem*)> walk = [&](QTreeWidgetItem* node) {
for (int i = 0; i < node->childCount(); ++i) {
QTreeWidgetItem* c = node->child(i);
if (c->data(0, kRoleConfType).toInt() == 2 && c->checkState(0) == Qt::Checked)
tmIds << c->data(0, kRoleObjId).toString();
walk(c);
}
};
walk(tree_->invisibleRootItem());
emit checkedTmsChanged(tmIds);
});
QObject::connect(tree_, &QTreeWidget::itemChanged, this, [this](QTreeWidgetItem* item, int) {
const QString tmId = item->data(0, kRoleTmId).toString();
if (!tmId.isEmpty()) emit tmCheckToggled(tmId, item->checkState(0) == Qt::Checked);
});
}

View File

@ -1,4 +1,5 @@
#pragma once
#include <QStringList>
#include <QWidget>
#include <vector>
#include "repo/RepoTypes.hpp"
@ -19,13 +20,15 @@ public:
void showMessage(const QString& message); // 错误/空状态占位
signals:
void tmClicked(const QString& tmObjectId);
// 前瞻钩子:勾选驱动中央渲染留待下一轮接真实 DS本轮暂无消费者
void tmCheckToggled(const QString& tmObjectId, bool checked);
// confType: 1=GS 2=TM。单击行驱动数据列表 + 对象属性)。
void objectClicked(const QString& objectId, int confType);
// 当前全部被勾选的 TM 叶子 id已合并发射
void checkedTmsChanged(const QStringList& tmObjectIds);
private:
QTreeWidget* tree_ = nullptr; // Qt 原生标准树(复选框/箭头由 Fusion 绘制,清晰可控)
QLabel* hint_ = nullptr;
bool checkPending_ = false; // 勾选合并发射防重入
};
} // namespace geopro::app