|
|
|
|
@ -162,8 +162,9 @@ ObjectTreePanel::ObjectTreePanel(QWidget* parent) : QWidget(parent) {
|
|
|
|
|
QObject::connect(tree_, &QTreeWidget::itemClicked, this, [this](QTreeWidgetItem* item, int) {
|
|
|
|
|
if (pressOnCheckbox_) { // 点的是复选框:只切换勾选态,不当作「选中」(不重载数据集列表)
|
|
|
|
|
pressOnCheckbox_ = false;
|
|
|
|
|
// GS 复选框点击:基于点击前聚合(dsOn + 子 TM 均未被本次点击改写)翻转全开/全关(spec §6)。
|
|
|
|
|
// Qt 因 ItemIsUserCheckable 已临时 toggle 了 GS 自身 checkState,但随后 recomputeGsState 会覆盖回正确三态。
|
|
|
|
|
// GS 复选框点击:翻转全开/全关(spec §6)。Qt 因 ItemIsUserCheckable 在 itemClicked 发射前
|
|
|
|
|
// 已临时 toggle 了 GS 自身 checkState——故此处**故意不读 item->checkState(0)**(它已是 toggle 后的脏值),
|
|
|
|
|
// 而是读 kRoleGsDsOn + 子 TM(二者均未被本次点击改写)重建"点击前"聚合判断方向;末尾 recomputeGsState 覆盖回正确三态。
|
|
|
|
|
if (item->data(0, kRoleConfType).toInt() == kConfTypeGs && !item->data(0, kRoleIsRoot).toBool()) {
|
|
|
|
|
const bool dsOn = item->data(0, kRoleGsDsOn).toBool();
|
|
|
|
|
int total = 0, checked = 0;
|
|
|
|
|
@ -198,16 +199,7 @@ ObjectTreePanel::ObjectTreePanel(QWidget* parent) : QWidget(parent) {
|
|
|
|
|
checkPending_ = true;
|
|
|
|
|
QTimer::singleShot(0, this, [this]() {
|
|
|
|
|
checkPending_ = false;
|
|
|
|
|
std::function<void(QTreeWidgetItem*)> recompAll = [&](QTreeWidgetItem* node) {
|
|
|
|
|
for (int i = 0; i < node->childCount(); ++i) {
|
|
|
|
|
QTreeWidgetItem* c = node->child(i);
|
|
|
|
|
if (c->data(0, kRoleConfType).toInt() == kConfTypeGs &&
|
|
|
|
|
!c->data(0, kRoleIsRoot).toBool())
|
|
|
|
|
recomputeGsState(c); // 内部 SignalBlocker,不会再触发 itemChanged
|
|
|
|
|
recompAll(c);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
recompAll(tree_->invisibleRootItem());
|
|
|
|
|
recomputeAllGsStates(); // 按子 TM 重算各 GS 三态(内部 SignalBlocker,不再触发 itemChanged)
|
|
|
|
|
emitCheckedSources();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
@ -326,6 +318,19 @@ void ObjectTreePanel::recomputeGsState(QTreeWidgetItem* gs) {
|
|
|
|
|
: Qt::Unchecked);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ObjectTreePanel::recomputeAllGsStates() {
|
|
|
|
|
if (!tree_) return;
|
|
|
|
|
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() == kConfTypeGs && !c->data(0, kRoleIsRoot).toBool())
|
|
|
|
|
recomputeGsState(c);
|
|
|
|
|
walk(c);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
walk(tree_->invisibleRootItem());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ObjectTreePanel::setAllChildTmChecked(QTreeWidgetItem* gs, bool checked) {
|
|
|
|
|
if (!gs) return;
|
|
|
|
|
const Qt::CheckState st = checked ? Qt::Checked : Qt::Unchecked;
|
|
|
|
|
@ -382,8 +387,8 @@ void ObjectTreePanel::emitCheckedSources() {
|
|
|
|
|
emit checkedTmsChanged(tmIds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── 快速筛选:遍历所有 TM 叶子,对其 setCheckState。批量改用 SignalBlocker 屏蔽逐项 itemChanged,
|
|
|
|
|
// 末尾手动触发一次 itemChanged 让既有 0ms 合并逻辑收集并发射 checkedTmsChanged。──
|
|
|
|
|
// ── 快速筛选:遍历所有 TM 叶子 setCheckState(SignalBlocker 屏蔽逐项 itemChanged),
|
|
|
|
|
// 末尾直接 recomputeAllGsStates + emitCheckedSources 同步 GS 三态并发射勾选源。──
|
|
|
|
|
void ObjectTreePanel::setAllTmsChecked(bool checked) {
|
|
|
|
|
if (!tree_) return;
|
|
|
|
|
const Qt::CheckState st = checked ? Qt::Checked : Qt::Unchecked;
|
|
|
|
|
@ -398,7 +403,8 @@ void ObjectTreePanel::setAllTmsChecked(bool checked) {
|
|
|
|
|
const QSignalBlocker block(tree_);
|
|
|
|
|
walk(tree_->invisibleRootItem());
|
|
|
|
|
}
|
|
|
|
|
emit tree_->itemChanged(nullptr, 0); // 触发既有合并发射
|
|
|
|
|
recomputeAllGsStates(); // 子 TM 批量变更后同步各 GS 三态
|
|
|
|
|
emitCheckedSources();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ObjectTreePanel::invertTmChecks() {
|
|
|
|
|
@ -415,7 +421,8 @@ void ObjectTreePanel::invertTmChecks() {
|
|
|
|
|
const QSignalBlocker block(tree_);
|
|
|
|
|
walk(tree_->invisibleRootItem());
|
|
|
|
|
}
|
|
|
|
|
emit tree_->itemChanged(nullptr, 0);
|
|
|
|
|
recomputeAllGsStates();
|
|
|
|
|
emitCheckedSources();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString ObjectTreePanel::currentParentForNew() const {
|
|
|
|
|
|