diff --git a/src/ui/ModelBrowser/ModelTreeWidget.cpp b/src/ui/ModelBrowser/ModelTreeWidget.cpp index e1d244f0..8a0dc7c5 100644 --- a/src/ui/ModelBrowser/ModelTreeWidget.cpp +++ b/src/ui/ModelBrowser/ModelTreeWidget.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -144,14 +145,30 @@ void ModelTreeWidget::OnEntityRemoved(Entity* entity) { void ModelTreeWidget::contextMenuEvent(QContextMenuEvent* event) { QTreeWidgetItem* item = itemAt(event->pos()); if (nullptr != item) { - QVariant value = item->data(0, E_Entity); - if (!value.isValid()) { - LOG_WARN("unknown data E_Entity"); + // Try entity menu first + QVariant valueEntity = item->data(0, E_Entity); + if (valueEntity.isValid()) { + Entity* entity = valueEntity.value(); + PopupEntityMenu(event, entity); return; } - Entity* entity = value.value(); - PopupEntityMenu(event, entity); + // Then file entry menu + QVariant valueEntry = item->data(0, E_FileEntry); + if (valueEntry.isValid()) { + auto* entry = valueEntry.value(); + QMenu menu(this); + QAction* deleteAction = new QAction(tr("Delete File"), this); + menu.addAction(deleteAction); + connect(deleteAction, &QAction::triggered, [this, entry]() { + OnDeleteFileEntry(entry); + }); + menu.exec(event->globalPos()); + return; + } + + // Otherwise, show nothing for group/root + return; } else { return; QMenu menu(this); @@ -301,6 +318,37 @@ void ModelTreeWidget::OnDeleteEntity(Entity* entity) { entity->Destory(); } +void ModelTreeWidget::OnDeleteFileEntry(FileEntry* entry) { + if (!entry) { + LOG_WARN("OnDeleteFileEntry: entry is nullptr"); + return; + } + + auto workspace = WorkSpaceManager::Get().GetCurrent(); + if (!workspace) { + LOG_WARN("OnDeleteFileEntry: workspace is nullptr"); + return; + } + + // Confirm deletion of the workspace-managed item (non-destructive to physical file) + auto res = QMessageBox::question(this, tr("Confirm"), tr("Delete this file entry from workspace?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (res != QMessageBox::Yes) { + return; + } + + bool ok = workspace->RemoveFileEntry(entry->GetType(), entry); + if (!ok) { + QMessageBox::warning(this, tr("Prompt"), tr("Delete failed")); + return; + } + + // Clear property panel to avoid showing stale info of deleted item + QVariant v; + v.setValue(workspace); + emit WorkSpaceChange(v); +} + void ModelTreeWidget::PopupEntityMenu(QContextMenuEvent* event, Entity* entity) { QMenu menu(this); diff --git a/src/ui/ModelBrowser/ModelTreeWidget.h b/src/ui/ModelBrowser/ModelTreeWidget.h index 753add93..029edb59 100644 --- a/src/ui/ModelBrowser/ModelTreeWidget.h +++ b/src/ui/ModelBrowser/ModelTreeWidget.h @@ -39,6 +39,7 @@ protected: class SceneComponent* OnAddComponent(const QString& typeName, class Entity* entity); void OnDeleteEntity(class Entity* entity); + void OnDeleteFileEntry(class FileEntry* entry); void PopupEntityMenu(QContextMenuEvent* event, class Entity* entity); diff --git a/src/workspace/WorkSpace.cpp b/src/workspace/WorkSpace.cpp index 33388b03..96877b8e 100644 --- a/src/workspace/WorkSpace.cpp +++ b/src/workspace/WorkSpace.cpp @@ -381,3 +381,66 @@ void WorkSpace::NotifyFileEntryUpdated(FileEntryType type, std::shared_ptrsecond; + auto pos = std::find_if(vec.begin(), vec.end(), [entry](const std::shared_ptr& sp){ return sp.get() == entry; }); + if (pos == vec.end()) { + LOG_WARN("RemoveFileEntry: entry not found in type: {}", FileEntryTypeToString(type)); + return false; + } + vec.erase(pos); + ++filesSeq_; + emit FilesChanged(type, nullptr); + LOG_INFO("Removed file entry of type: {}", FileEntryTypeToString(type)); + return true; +} + +// 按索引删除文件条目 +bool WorkSpace::RemoveFileEntryAt(FileEntryType type, int index) { + auto it = files_.find(type); + if (it == files_.end()) { + LOG_WARN("RemoveFileEntryAt: type not found: {}", FileEntryTypeToString(type)); + return false; + } + auto& vec = it->second; + if (index < 0 || index >= static_cast(vec.size())) { + LOG_WARN("RemoveFileEntryAt: invalid index {} for type {}", index, FileEntryTypeToString(type)); + return false; + } + vec.erase(vec.begin() + index); + ++filesSeq_; + emit FilesChanged(type, nullptr); + LOG_INFO("Removed file entry at index {} for type {}", index, FileEntryTypeToString(type)); + return true; +} + +// 按文件名删除文件条目(匹配工作空间内记录的文件名) +bool WorkSpace::RemoveFileEntryByName(FileEntryType type, const QString& fileName) { + auto it = files_.find(type); + if (it == files_.end()) { + LOG_WARN("RemoveFileEntryByName: type not found: {}", FileEntryTypeToString(type)); + return false; + } + auto& vec = it->second; + auto pos = std::find_if(vec.begin(), vec.end(), [&fileName](const std::shared_ptr& sp){ return sp && sp->GetFileName() == fileName; }); + if (pos == vec.end()) { + LOG_WARN("RemoveFileEntryByName: file not found: {}", fileName.toUtf8().constData()); + return false; + } + vec.erase(pos); + ++filesSeq_; + emit FilesChanged(type, nullptr); + LOG_INFO("Removed file entry by name: {} for type {}", fileName.toUtf8().constData(), FileEntryTypeToString(type)); + return true; +} diff --git a/src/workspace/WorkSpace.h b/src/workspace/WorkSpace.h index e4994691..d87dc4d0 100644 --- a/src/workspace/WorkSpace.h +++ b/src/workspace/WorkSpace.h @@ -68,12 +68,17 @@ public: // New unified file entry management FileEntryResult SetFileEntry(std::shared_ptr fileEntry, bool is_copy = true); - std::vector> GetFileEntries(FileEntryType type) const; + std::vector> GetFileEntries(FileEntryType type) const; // Manage grouped file entries bool SetFileEntryCount(FileEntryType type, int count); QString GetFileEntryAbsPath(FileEntryType type, int index) const; + // Remove file entries + bool RemoveFileEntry(FileEntryType type, class FileEntry* entry); + bool RemoveFileEntryAt(FileEntryType type, int index); + bool RemoveFileEntryByName(FileEntryType type, const QString& fileName); + inline void SetHomeViewpoint(const osgEarth::Viewpoint& viewpoint) { homeViewpoint_ = viewpoint; homeViewpoint_.setHeading(0.0); // Ensure heading is set to 0.0