diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 8efaa927..9c6bc50d 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -103,6 +103,7 @@ void MainWindow::InitUI() { connect(modelBrowser_, &ModelBrowser::WorkSpaceChange, propertyBrowser_, &PropertyBrowser::OnWorkSpaceChange); connect(modelBrowser_, &ModelBrowser::EntityChange, propertyBrowser_, &PropertyBrowser::OnEntityChange); + connect(modelBrowser_, &ModelBrowser::FileEntryChange, propertyBrowser_, &PropertyBrowser::OnFileEntryChange); qtOsgViewWidget_ = new OsgWidget; qtOsgViewWidget_->Initialize(); diff --git a/src/ui/ModelBrowser.cpp b/src/ui/ModelBrowser.cpp index 3c65d8d0..8bf5d1b4 100644 --- a/src/ui/ModelBrowser.cpp +++ b/src/ui/ModelBrowser.cpp @@ -46,4 +46,5 @@ void ModelBrowser::InitUI() { //ModelTreeWidget* treeWidget = modelBrowserPannal_->GetModelTreeWidget(); connect(treeWidget_, &ModelTreeWidget::WorkSpaceChange, this, &ModelBrowser::WorkSpaceChange); connect(treeWidget_, &ModelTreeWidget::EntityChange, this, &ModelBrowser::EntityChange); + connect(treeWidget_, &ModelTreeWidget::FileEntryChange, this, &ModelBrowser::FileEntryChange); } diff --git a/src/ui/ModelBrowser.h b/src/ui/ModelBrowser.h index f8a89656..80203b6f 100644 --- a/src/ui/ModelBrowser.h +++ b/src/ui/ModelBrowser.h @@ -14,6 +14,7 @@ public: Q_SIGNALS: void WorkSpaceChange(const QVariant& workSpace); void EntityChange(const QVariant& workSpace); + void FileEntryChange(const QVariant& fileEntry); private: void InitUI(); diff --git a/src/ui/ModelBrowser/EntityConstent.h b/src/ui/ModelBrowser/EntityConstent.h index bf9b3d6b..ecbc6bda 100644 --- a/src/ui/ModelBrowser/EntityConstent.h +++ b/src/ui/ModelBrowser/EntityConstent.h @@ -5,5 +5,6 @@ enum ItemRol { E_Workspace = Qt::ItemDataRole::UserRole + 1, E_Entity, - E_UUid + E_UUid, + E_FileEntry }; diff --git a/src/ui/ModelBrowser/ModelTreeWidget.cpp b/src/ui/ModelBrowser/ModelTreeWidget.cpp index 3582f4ca..9841d666 100644 --- a/src/ui/ModelBrowser/ModelTreeWidget.cpp +++ b/src/ui/ModelBrowser/ModelTreeWidget.cpp @@ -42,6 +42,7 @@ void ModelTreeWidget::OnWorkspaceChange(WorkSpace* workSpace) { connect(workSpace, &WorkSpace::EntityAdded, this, &ModelTreeWidget::OnEntityAdded); connect(workSpace, &WorkSpace::EntityRemoved, this, &ModelTreeWidget::OnEntityRemoved); + connect(workSpace, &WorkSpace::FilesChanged, this, &ModelTreeWidget::OnWorkspaceFilesChanged); const QString& name = currentWorkSpace_->GetName(); root_ = new QTreeWidgetItem(this); @@ -56,6 +57,12 @@ void ModelTreeWidget::OnWorkspaceChange(WorkSpace* workSpace) { for (const auto& entity : entities) { AddEntity(root_, entity); } + + // Create and populate curve entries branch + curvesRoot_ = new QTreeWidgetItem(root_); + curvesRoot_->setText(0, tr("Curves")); + curvesRoot_->setData(0, E_UUid, QString("CurvesRoot")); + PopulateCurveEntries(); } void ModelTreeWidget::OnEntityAdded(Entity* entity) { @@ -163,22 +170,17 @@ void ModelTreeWidget::OnWorkItemChanged(WorkSpaceItem* item) { } void ModelTreeWidget::OnWorkItemCliecked(QTreeWidgetItem* item, int column) { + // Prefer entity, then file entry, then workspace QVariant value = item->data(column, E_Entity); - bool isEntity = true; - if (!value.isValid()) { - value = item->data(column, E_Workspace); - isEntity = false; - } - if (!value.isValid()) { - LOG_WARN("unknown data rol"); - return; - } + if (value.isValid()) { emit EntityChange(value); return; } - if (isEntity) { - emit EntityChange(value); - } else { - emit WorkSpaceChange(value); - } + value = item->data(column, E_FileEntry); + if (value.isValid()) { emit FileEntryChange(value); return; } + + value = item->data(column, E_Workspace); + if (value.isValid()) { emit WorkSpaceChange(value); return; } + + LOG_WARN("unknown data rol"); } void ModelTreeWidget::OnItemDoubleClicked(QTreeWidgetItem* item, int column) { @@ -393,3 +395,32 @@ void ModelTreeWidget::AddEntity(class QTreeWidgetItem* parent, Entity* entity) { AddEntity(item, child); } } + +void ModelTreeWidget::PopulateCurveEntries() { + if (!currentWorkSpace_ || !curvesRoot_) return; + + // Clear existing curve children + while (curvesRoot_->childCount() > 0) { + QTreeWidgetItem* child = curvesRoot_->child(0); + curvesRoot_->removeChild(child); + delete child; + } + + const auto entries = currentWorkSpace_->GetFileEntries(FileEntryType::Curve); + for (const auto& entry : entries) { + if (!entry) continue; + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, entry->GetName()); + QVariant v; + v.setValue(entry.get()); + item->setData(0, E_FileEntry, v); + item->setData(0, E_UUid, QString("Curve:%1").arg(entry->GetFileName())); + curvesRoot_->addChild(item); + } +} + +void ModelTreeWidget::OnWorkspaceFilesChanged(FileEntryType type, std::shared_ptr /*fileEntry*/) { + if (type == FileEntryType::Curve) { + PopulateCurveEntries(); + } +} diff --git a/src/ui/ModelBrowser/ModelTreeWidget.h b/src/ui/ModelBrowser/ModelTreeWidget.h index ab0aaa71..b0618651 100644 --- a/src/ui/ModelBrowser/ModelTreeWidget.h +++ b/src/ui/ModelBrowser/ModelTreeWidget.h @@ -6,6 +6,7 @@ #include "entities/Entity.h" #include "workspace/WorkSpace.h" #include "workspace/WorkSpaceItem.h" +#include "workspace/FileEntry.h" class ModelTreeWidget : public QTreeWidget { Q_OBJECT @@ -22,6 +23,7 @@ public: Q_SIGNALS: void WorkSpaceChange(const QVariant& workSpace); void EntityChange(const QVariant& entity); + void FileEntryChange(const QVariant& fileEntry); protected: void contextMenuEvent(QContextMenuEvent* event) override; @@ -30,6 +32,8 @@ protected: void OnWorkItemCliecked(QTreeWidgetItem* item, int column); void OnItemDoubleClicked(QTreeWidgetItem* item, int column); + void OnWorkspaceFilesChanged(enum class FileEntryType type, std::shared_ptr fileEntry); + void OnAddMeshEntity(const QString& mesh, const QString& name); class SceneComponent* OnAddComponent(const QString& typeName, class Entity* entity); @@ -47,8 +51,10 @@ protected: private: //void initUI(); void AddEntity(class QTreeWidgetItem* parent, Entity* entity); + void PopulateCurveEntries(); private: WorkSpace* currentWorkSpace_{ nullptr }; QTreeWidgetItem* root_{ nullptr }; + QTreeWidgetItem* curvesRoot_{ nullptr }; }; \ No newline at end of file diff --git a/src/ui/PropertyBrowser.cpp b/src/ui/PropertyBrowser.cpp index 3967acb1..f773d71b 100644 --- a/src/ui/PropertyBrowser.cpp +++ b/src/ui/PropertyBrowser.cpp @@ -43,6 +43,7 @@ void PropertyBrowser::AttachDock(DockWidget* dockWidget) { } void PropertyBrowser::OnWorkSpaceChange(const QVariant& value) { + inFileEntryView_ = false; WorkSpace* workspace = value.value(); if (nullptr == workspace) { LOG_WARN("workspace is nullptr"); @@ -70,25 +71,11 @@ void PropertyBrowser::OnWorkSpaceChange(const QVariant& value) { QObject::connect(currentWorkspace_, &WorkSpace::FilesChanged, this, &PropertyBrowser::OnWorkspaceFilesChanged); - // Mount all curve entries, each as its own top-level group - auto curves = currentWorkspace_->GetFileEntries(FileEntryType::Curve); - for (const auto& entry : curves) { - if (!entry) continue; - auto curve = entry->AsCurve(); - if (!curve) continue; - - // Use filename as stable unique id, and include display name in group title - const QString id = QString("CurveEntry:%1").arg(entry->GetFileName()); - const QString title = QString("CurveEntry - %1").arg(entry->GetName()); - - QtProperty* cprop = curveEntryManager_->addProperty(title); - QCurveEntryAttribute attr(curve); - curveEntryManager_->setValue(cprop, attr); - addProperty(cprop, id); - } + // No longer mount file entries under workspace; file entries are handled separately } void PropertyBrowser::OnEntityChange(const QVariant& value) { + inFileEntryView_ = false; Entity* entity = value.value(); if (nullptr == entity) { LOG_WARN("engity is nullptr"); @@ -136,6 +123,43 @@ void PropertyBrowser::OnEntityChange(const QVariant& value) { } } +void PropertyBrowser::OnFileEntryChange(const QVariant& value) { + FileEntry* entry = value.value(); + if (!entry) { + LOG_WARN("file entry is nullptr"); + return; + } + + // Enter single FileEntry view mode and clear existing panels + inFileEntryView_ = true; + browser_->clear(); + propertyToId_.clear(); + idToProperty_.clear(); + idToExpanded_.clear(); + + // Compute our id and ensure the property exists (create if missing) + const QString id = QString("CurveEntry:%1").arg(entry->GetFileName()); + auto curve = entry->AsCurve(); + if (!curve) { + LOG_WARN("file entry not a curve: %s", entry->GetFileName().toStdString().c_str()); + return; + } + const QString title = QString("CurveEntry - %1").arg(entry->GetName()); + QtProperty* prop = curveEntryManager_->addProperty(title); + QCurveEntryAttribute attr(curve); + curveEntryManager_->setValue(prop, attr); + addProperty(prop, id); + + // Focus the corresponding group: expand and select it + if (prop) { + QtBrowserItem* item = browser_->topLevelItem(prop); + if (item) { + browser_->setExpanded(item, true); + browser_->setCurrentItem(item); + } + } +} + void PropertyBrowser::InitUI() { QBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -208,6 +232,8 @@ void PropertyBrowser::InitPropertyManager() { void PropertyBrowser::OnWorkspaceFilesChanged(FileEntryType type, std::shared_ptr fileEntry) { if (!currentWorkspace_) return; + // In single FileEntry view, skip workspace-driven group refresh to keep panel clean + if (inFileEntryView_) return; // Refresh workspace group { auto it = idToProperty_.find(tr("WorkSpace")); @@ -217,52 +243,6 @@ void PropertyBrowser::OnWorkspaceFilesChanged(FileEntryType type, std::shared_pt workSpaceManager_->setValue(property, worksapceAttribute); } } - - // Refresh curve entry groups when curve files mutate - if (type == FileEntryType::Curve) { - const auto curves = currentWorkspace_->GetFileEntries(FileEntryType::Curve); - - // Desired ids for current curves - QSet desiredIds; - for (const auto& entry : curves) { - if (!entry) continue; - desiredIds.insert(QString("CurveEntry:%1").arg(entry->GetFileName())); - } - - // Add or update properties for all current curves - for (const auto& entry : curves) { - if (!entry) continue; - auto curve = entry->AsCurve(); - if (!curve) continue; - - const QString id = QString("CurveEntry:%1").arg(entry->GetFileName()); - const QString title = QString("CurveEntry - %1").arg(entry->GetName()); - QCurveEntryAttribute attr(curve); - - auto it = idToProperty_.find(id); - if (it != idToProperty_.end()) { - // Update existing group - curveEntryManager_->setValue(it.value(), attr); - } else { - // Create new group for this curve - QtProperty* prop = curveEntryManager_->addProperty(title); - curveEntryManager_->setValue(prop, attr); - addProperty(prop, id); - } - } - - // Remove groups for curves that no longer exist - QList existingIds = idToProperty_.keys(); - for (const auto& id : existingIds) { - if (id.startsWith("CurveEntry:") && !desiredIds.contains(id)) { - QtProperty* prop = idToProperty_[id]; - browser_->removeProperty(prop); - propertyToId_.remove(prop); - idToExpanded_.remove(id); - idToProperty_.remove(id); - } - } - } } void PropertyBrowser::InitComponentPropertyManager() { diff --git a/src/ui/PropertyBrowser.h b/src/ui/PropertyBrowser.h index e1c22475..f830dc3e 100644 --- a/src/ui/PropertyBrowser.h +++ b/src/ui/PropertyBrowser.h @@ -18,6 +18,7 @@ public: void OnWorkSpaceChange(const QVariant& value); void OnEntityChange(const QVariant& value); + void OnFileEntryChange(const QVariant& value); void OnWorkspaceFilesChanged(enum class FileEntryType type, std::shared_ptr fileEntry); void Test(); @@ -55,5 +56,8 @@ private: // Track current workspace for real-time refresh class WorkSpace* currentWorkspace_{ nullptr }; + + // When true, we are showing a single FileEntry view (not workspace-wide groups) + bool inFileEntryView_{ false }; }; diff --git a/src/ui/PropertyBrowser/qtworkspaceattribute.cpp b/src/ui/PropertyBrowser/qtworkspaceattribute.cpp index 9a2967d9..9bf10406 100644 --- a/src/ui/PropertyBrowser/qtworkspaceattribute.cpp +++ b/src/ui/PropertyBrowser/qtworkspaceattribute.cpp @@ -10,6 +10,7 @@ #include "entities/EntitiesManager.h" #include "utils/Transform.h" #include "utils/OsgUtils.h" +#include "workspace/WorkSpaceManager.h" QWorkspaceAttribute::QWorkspaceAttribute(class WorkSpace* workspace) @@ -138,9 +139,8 @@ void QWorkspaceAttribute::SetFileEntryPath(FileEntryType type, int index, const // Update the path of the specific entry entries[index]->SetPath(path); - - // Trigger files changed signal - workspace_->SetFileEntryCount(type, static_cast(entries.size())); + // Trigger files changed signal properly + workspace_->NotifyFileEntryUpdated(type); } QString QWorkspaceAttribute::GetFileEntryAbsPath(FileEntryType type, int index) const { @@ -659,6 +659,7 @@ QCurveEntryAttribute& QCurveEntryAttribute::operator=(const QCurveEntryAttribute void QCurveEntryAttribute::SetName(const QString& name) { if (!entry_) return; entry_->SetName(name); + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } QString QCurveEntryAttribute::GetName() const { @@ -669,6 +670,7 @@ QString QCurveEntryAttribute::GetName() const { void QCurveEntryAttribute::SetChartType(ChartType type) { if (!entry_) return; entry_->SetChartType(type); + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } ChartType QCurveEntryAttribute::GetChartType() const { @@ -703,6 +705,7 @@ void QCurveEntryAttribute::SetCurveCount(int count) { entry_->RemoveCurveProperty(i); } } + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } QString QCurveEntryAttribute::GetCurveName(int index) const { @@ -721,6 +724,7 @@ void QCurveEntryAttribute::SetCurveName(int index, const QString& name) { // overwrite by remove/add pattern entry_->RemoveCurveProperty(index); entry_->AddCurveProperty(cp); + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } QColor QCurveEntryAttribute::GetCurveColor(int index) const { @@ -738,6 +742,7 @@ void QCurveEntryAttribute::SetCurveColor(int index, const QColor& color) { cp.color = color; entry_->RemoveCurveProperty(index); entry_->AddCurveProperty(cp); + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } int QCurveEntryAttribute::GetWaveStart(int index) const { @@ -755,6 +760,7 @@ void QCurveEntryAttribute::SetWaveStart(int index, int start) { cp.data.wave.start = start; entry_->RemoveCurveProperty(index); entry_->AddCurveProperty(cp); + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } int QCurveEntryAttribute::GetWaveStop(int index) const { @@ -772,6 +778,7 @@ void QCurveEntryAttribute::SetWaveStop(int index, int stop) { cp.data.wave.stop = stop; entry_->RemoveCurveProperty(index); entry_->AddCurveProperty(cp); + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } double QCurveEntryAttribute::GetReportX(int index) const { @@ -789,6 +796,7 @@ void QCurveEntryAttribute::SetReportX(int index, double x) { cp.data.report.x = x; entry_->RemoveCurveProperty(index); entry_->AddCurveProperty(cp); + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } double QCurveEntryAttribute::GetReportY(int index) const { @@ -806,4 +814,5 @@ void QCurveEntryAttribute::SetReportY(int index, double y) { cp.data.report.y = y; entry_->RemoveCurveProperty(index); entry_->AddCurveProperty(cp); + if (auto ws = WorkSpaceManager::Get().GetCurrent()) { ws->NotifyFileEntryUpdated(FileEntryType::Curve); } } diff --git a/src/workspace/FileEntry.h b/src/workspace/FileEntry.h index edfa3345..d5cd90af 100644 --- a/src/workspace/FileEntry.h +++ b/src/workspace/FileEntry.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -396,5 +396,9 @@ public: private: ChartProperties chartProperties_; - ImageProperties imageProperties_; -}; \ No newline at end of file + ImageProperties imageProperties_; +}; + +// Enable QVariant support for FileEntry pointers +#include +Q_DECLARE_METATYPE(FileEntry*) \ No newline at end of file diff --git a/src/workspace/WorkSpace.cpp b/src/workspace/WorkSpace.cpp index fa4e221b..1de08c23 100644 --- a/src/workspace/WorkSpace.cpp +++ b/src/workspace/WorkSpace.cpp @@ -379,3 +379,9 @@ void WorkSpace::ExecuteCommands(CommandWhen when) { } cmdMgr_->Execute(this, when); } + +// 统一属性更新通知:不改变数量,仅提升序号并广播 FilesChanged +void WorkSpace::NotifyFileEntryUpdated(FileEntryType type, std::shared_ptr fileEntry) { + ++filesSeq_; + emit FilesChanged(type, fileEntry); +} diff --git a/src/workspace/WorkSpace.h b/src/workspace/WorkSpace.h index a2e5f724..e4994691 100644 --- a/src/workspace/WorkSpace.h +++ b/src/workspace/WorkSpace.h @@ -127,6 +127,9 @@ public: void OnLoaded(); + // 通知:某类型的文件条目属性已更新(不改变数量,仅触发刷新) + void NotifyFileEntryUpdated(FileEntryType type, std::shared_ptr fileEntry = nullptr); + Q_SIGNALS: void EntityAdded(class Entity* entity); void EntityRemoved(class Entity* entity);