feat/object-selection-panels #4
|
|
@ -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; // 节点对象 id(GS/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);
|
||||
});
|
||||
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);
|
||||
// 勾选变化: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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue