feat: implement viewport toolbar overlay, Collapse/Filter Console list, and split Build Settings with Scenes In Build drag-drop

This commit is contained in:
ayuan9957 2026-06-08 10:29:12 +08:00
parent 5b4095f049
commit 998edb23e7
2 changed files with 408 additions and 35 deletions

View File

@ -83,6 +83,12 @@ static std::filesystem::path PendingRenamePath_{};
static std::array<char, 256> RenameBuffer_{}; static std::array<char, 256> RenameBuffer_{};
static std::array<char, 256> NewFolderBuffer_{}; static std::array<char, 256> NewFolderBuffer_{};
static float IconSize_ = 64.0f; static float IconSize_ = 64.0f;
static bool ConsoleCollapse_ = true;
static bool ConsoleShowInfo_ = true;
static bool ConsoleShowWarning_ = true;
static bool ConsoleShowError_ = true;
static int ConsoleSelectedEntryIdx_ = -1;
static std::vector<std::pair<std::string, bool>> BuildScenes_{};
static HANDLE PlayerProcessHandle_ = NULL; static HANDLE PlayerProcessHandle_ = NULL;
static HANDLE PlayerStdoutRead_ = NULL; static HANDLE PlayerStdoutRead_ = NULL;
@ -5115,18 +5121,168 @@ public:
bool IsOpenByDefault() const override { return true; } bool IsOpenByDefault() const override { return true; }
void DrawPanel(MetaCoreEditorContext& editorContext) override { void DrawPanel(MetaCoreEditorContext& editorContext) override {
for (const MetaCoreLogEntry& entry : editorContext.GetLogService().GetEntries()) { auto& logService = editorContext.GetLogService();
ImVec4 textColor = ImVec4(0.85F, 0.85F, 0.88F, 1.0F);
const char* icon = "(i)"; // 1. 顶部工具栏 (Toolbar)
if (entry.Level == MetaCoreLogLevel::Warning) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 2.0f));
textColor = ImVec4(0.95F, 0.75F, 0.25F, 1.0F);
icon = "(!)"; if (ImGui::Button("Clear")) {
} else if (entry.Level == MetaCoreLogLevel::Error) { logService.Clear();
textColor = ImVec4(0.95F, 0.35F, 0.30F, 1.0F); ConsoleSelectedEntryIdx_ = -1;
icon = "(X)";
}
ImGui::TextColored(textColor, "%s [%s] %s", icon, entry.Category.c_str(), entry.Message.c_str());
} }
ImGui::SameLine();
ImGui::Checkbox("Collapse", &ConsoleCollapse_);
// 计算各级别日志计数
int infoCount = 0;
int warnCount = 0;
int errCount = 0;
for (const auto& entry : logService.GetEntries()) {
if (entry.Level == MetaCoreLogLevel::Info) infoCount++;
else if (entry.Level == MetaCoreLogLevel::Warning) warnCount++;
else if (entry.Level == MetaCoreLogLevel::Error) errCount++;
}
// 日志分级过滤 (Info / Warning / Error)
char infoLabel[32];
char warnLabel[32];
char errLabel[32];
std::snprintf(infoLabel, sizeof(infoLabel), " %d", infoCount);
std::snprintf(warnLabel, sizeof(warnLabel), "⚠️ %d", warnCount);
std::snprintf(errLabel, sizeof(errLabel), "🛑 %d", errCount);
ImGui::SameLine(ImGui::GetContentRegionMax().x - 170.0f);
auto drawToggle = [](const char* label, bool* value, const ImVec4& activeColor) {
if (*value) {
ImGui::PushStyleColor(ImGuiCol_Button, activeColor);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(activeColor.x * 1.1f, activeColor.y * 1.1f, activeColor.z * 1.1f, activeColor.w));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(activeColor.x * 0.9f, activeColor.y * 0.9f, activeColor.z * 0.9f, activeColor.w));
} else {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.2f, 0.2f, 0.4f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.3f, 0.6f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.15f, 0.15f, 0.15f, 0.6f));
}
if (ImGui::Button(label, ImVec2(50.0f, 20.0f))) {
*value = !*value;
}
ImGui::PopStyleColor(3);
};
drawToggle(infoLabel, &ConsoleShowInfo_, ImVec4(0.173f, 0.365f, 0.529f, 0.6f));
ImGui::SameLine(0, 2);
drawToggle(warnLabel, &ConsoleShowWarning_, ImVec4(0.85f, 0.65f, 0.15f, 0.6f));
ImGui::SameLine(0, 2);
drawToggle(errLabel, &ConsoleShowError_, ImVec4(0.75f, 0.25f, 0.25f, 0.6f));
ImGui::PopStyleVar();
ImGui::Separator();
// 2. 日志列表区域
// 为下方详细文本保留 85px 高度
float availHeight = ImGui::GetContentRegionAvail().y - 85.0f;
if (availHeight < 50.0f) {
availHeight = 50.0f;
}
if (ImGui::BeginChild("LogList", ImVec2(0, availHeight), true)) {
struct DisplayEntry {
size_t OriginalIndex = 0;
MetaCoreLogLevel Level = MetaCoreLogLevel::Info;
std::string Category;
std::string Message;
int Count = 1;
};
std::vector<DisplayEntry> displayList;
const auto& entries = logService.GetEntries();
for (size_t i = 0; i < entries.size(); ++i) {
const auto& entry = entries[i];
if (entry.Level == MetaCoreLogLevel::Info && !ConsoleShowInfo_) continue;
if (entry.Level == MetaCoreLogLevel::Warning && !ConsoleShowWarning_) continue;
if (entry.Level == MetaCoreLogLevel::Error && !ConsoleShowError_) continue;
if (ConsoleCollapse_ && !displayList.empty() &&
displayList.back().Level == entry.Level &&
displayList.back().Category == entry.Category &&
displayList.back().Message == entry.Message) {
displayList.back().Count++;
} else {
DisplayEntry de{};
de.OriginalIndex = i;
de.Level = entry.Level;
de.Category = entry.Category;
de.Message = entry.Message;
de.Count = 1;
displayList.push_back(de);
}
}
for (size_t i = 0; i < displayList.size(); ++i) {
const auto& de = displayList[i];
ImGui::PushID(static_cast<int>(i));
// 斑马线交替背景
ImVec4 rowBg = (i % 2 == 0) ? ImVec4(0.22f, 0.22f, 0.22f, 0.4f) : ImVec4(0.18f, 0.18f, 0.18f, 0.4f);
if (ConsoleSelectedEntryIdx_ == static_cast<int>(de.OriginalIndex)) {
rowBg = ImVec4(0.173f, 0.365f, 0.529f, 0.7f);
}
ImGui::PushStyleColor(ImGuiCol_Header, rowBg);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(rowBg.x * 1.15f, rowBg.y * 1.15f, rowBg.z * 1.15f, rowBg.w + 0.15f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(rowBg.x * 0.9f, rowBg.y * 0.9f, rowBg.z * 0.9f, rowBg.w + 0.2f));
const char* iconStr = "";
if (de.Level == MetaCoreLogLevel::Warning) {
iconStr = "⚠️";
} else if (de.Level == MetaCoreLogLevel::Error) {
iconStr = "🛑";
}
char rowLabel[512];
std::snprintf(rowLabel, sizeof(rowLabel), "%s [%s] %s", iconStr, de.Category.c_str(), de.Message.c_str());
bool isSelected = (ConsoleSelectedEntryIdx_ == static_cast<int>(de.OriginalIndex));
if (ImGui::Selectable(rowLabel, isSelected, ImGuiSelectableFlags_SpanAllColumns)) {
ConsoleSelectedEntryIdx_ = static_cast<int>(de.OriginalIndex);
}
ImGui::PopStyleColor(3);
if (ConsoleCollapse_ && de.Count > 1) {
ImGui::SameLine(ImGui::GetContentRegionMax().x - 45.0f);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.35f, 0.35f, 0.35f, 0.8f));
ImGui::Button(std::to_string(de.Count).c_str(), ImVec2(32.0f, 16.0f));
ImGui::PopStyleColor();
}
ImGui::PopID();
}
}
ImGui::EndChild();
ImGui::Separator();
// 3. 详细展示区
if (ImGui::BeginChild("LogDetail", ImVec2(0, 0), true)) {
if (ConsoleSelectedEntryIdx_ >= 0 && ConsoleSelectedEntryIdx_ < static_cast<int>(logService.GetEntries().size())) {
const auto& entry = logService.GetEntries()[ConsoleSelectedEntryIdx_];
ImVec4 txtColor = ImVec4(0.9f, 0.9f, 0.92f, 1.0f);
if (entry.Level == MetaCoreLogLevel::Warning) {
txtColor = ImVec4(0.95f, 0.75f, 0.25f, 1.0f);
} else if (entry.Level == MetaCoreLogLevel::Error) {
txtColor = ImVec4(0.95f, 0.35f, 0.30f, 1.0f);
}
ImGui::TextColored(txtColor, "[%s]", entry.Category.c_str());
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.85f, 0.85f, 0.85f, 1.0f));
ImGui::TextWrapped("%s", entry.Message.c_str());
ImGui::PopStyleColor();
} else {
ImGui::TextDisabled("选择任意日志项以查看详细信息。");
}
}
ImGui::EndChild();
} }
}; };
@ -5756,42 +5912,156 @@ public:
const MetaCoreProjectDescriptor& project = assetDatabaseService->GetProjectDescriptor(); const MetaCoreProjectDescriptor& project = assetDatabaseService->GetProjectDescriptor();
BindProjectDefaults(project); BindProjectDefaults(project);
ImGui::Text("Project: %s", project.Name.c_str()); // 1. Scenes In Build 场景构建列表
ImGui::Text("Root: %s", project.RootPath.string().c_str()); ImGui::Text("Scenes In Build");
ImGui::Text("Project Startup Scene: %s", project.StartupScenePath.generic_string().c_str()); ImGui::TextDisabled("可以将 .mcscene 场景资产从项目面板拖拽至下方列表中添加");
ImGui::Separator();
// 自动初始化场景列表
if (BuildScenes_.empty()) {
for (const auto& a : assetDatabaseService->GetAssetRegistry()) {
if (a.Type == "scene") {
BuildScenes_.push_back({ a.RelativePath.generic_string(), true });
}
}
}
ImGui::InputText("Startup Scene", StartupScenePathBuffer_.data(), StartupScenePathBuffer_.size()); // 用 Child Window 包裹展现边框列表
ImGui::InputText("Startup UI", StartupUiPathBuffer_.data(), StartupUiPathBuffer_.size()); if (ImGui::BeginChild("ScenesInBuildList", ImVec2(0, 110.0f), true)) {
ImGui::InputText("Build Profile", BuildProfileNameBuffer_.data(), BuildProfileNameBuffer_.size()); for (size_t i = 0; i < BuildScenes_.size(); ++i) {
ImGui::InputText("Target Platform", TargetPlatformBuffer_.data(), TargetPlatformBuffer_.size()); ImGui::PushID(static_cast<int>(i));
ImGui::InputText("Player Exe", PlayerExecutablePathBuffer_.data(), PlayerExecutablePathBuffer_.size()); ImGui::Checkbox("##enabled", &BuildScenes_[i].second);
ImGui::InputText("Output Dir", OutputDirectoryBuffer_.data(), OutputDirectoryBuffer_.size()); ImGui::SameLine();
ImGui::InputText("Cooked Assets Dir", CookedAssetsDirectoryBuffer_.data(), CookedAssetsDirectoryBuffer_.size()); ImGui::Text("%s", BuildScenes_[i].first.c_str());
ImGui::Checkbox("Use cooked assets", &UseCookedAssetsInPackage_);
ImGui::Separator(); // 右键菜单支持移除场景
ImGui::TextUnformatted("Runtime Data"); if (ImGui::BeginPopupContextItem()) {
ImGui::InputText("Data Sources", DataSourcesPathBuffer_.data(), DataSourcesPathBuffer_.size()); if (ImGui::MenuItem("Remove Scene")) {
ImGui::InputText("Bindings", BindingsPathBuffer_.data(), BindingsPathBuffer_.size()); BuildScenes_.erase(BuildScenes_.begin() + i);
ImGui::InputText("Diagnostics", DiagnosticsPathBuffer_.data(), DiagnosticsPathBuffer_.size()); }
ImGui::Separator(); ImGui::EndPopup();
ImGui::Checkbox("Cook before package", &CookBeforePackage_); }
ImGui::Checkbox("Copy loose Assets/Scenes", &CopyLooseContent_); ImGui::PopID();
ImGui::Checkbox("Copy Runtime config", &CopyRuntimeConfig_); }
// 接收拖放
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(MetaCoreProjectAssetDragDropPayloadType)) {
if (payload->DataSize == sizeof(MetaCoreProjectAssetDragDropPayload)) {
const auto* assetPayload = static_cast<const MetaCoreProjectAssetDragDropPayload*>(payload->Data);
if (assetPayload && std::string(assetPayload->AssetType) == "scene") {
const auto record = assetDatabaseService->FindAssetByGuid(assetPayload->AssetGuid);
if (record.has_value()) {
std::string pStr = record->RelativePath.generic_string();
bool dup = false;
for (const auto& s : BuildScenes_) {
if (s.first == pStr) {
dup = true;
break;
}
}
if (!dup) {
BuildScenes_.push_back({ pStr, true });
}
}
}
}
}
ImGui::EndDragDropTarget();
}
}
ImGui::EndChild();
if (ImGui::Button("Add Open Scenes")) {
for (const auto& a : assetDatabaseService->GetAssetRegistry()) {
if (a.Type == "scene") {
std::string pStr = a.RelativePath.generic_string();
bool dup = false;
for (const auto& s : BuildScenes_) {
if (s.first == pStr) {
dup = true;
break;
}
}
if (!dup) {
BuildScenes_.push_back({ pStr, true });
}
}
}
}
ImGui::Separator();
ImGui::Spacing();
// 2. 双栏布局:左栏平台,右栏打包配置项
ImGui::Columns(2, "BuildSettingsSplit", false);
ImGui::SetColumnWidth(0, 150.0f);
ImGui::Text("Platform");
if (ImGui::BeginChild("PlatformList", ImVec2(0, 180.0f), true)) {
// Unity 风格的高亮单选列表
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.173f, 0.365f, 0.529f, 0.8f));
ImGui::Selectable("💻 PC, Mac & Linux", true);
ImGui::PopStyleColor();
ImGui::Selectable("📱 Android", false);
ImGui::Selectable("🍏 iOS", false);
ImGui::Selectable("🌐 WebGL", false);
}
ImGui::EndChild();
ImGui::NextColumn();
ImGui::Text("Build Configuration");
if (ImGui::BeginChild("ConfigDetails", ImVec2(0, 180.0f), false)) {
ImGui::InputText("Startup Scene", StartupScenePathBuffer_.data(), StartupScenePathBuffer_.size());
ImGui::InputText("Startup UI", StartupUiPathBuffer_.data(), StartupUiPathBuffer_.size());
ImGui::InputText("Build Profile", BuildProfileNameBuffer_.data(), BuildProfileNameBuffer_.size());
ImGui::InputText("Target Platform", TargetPlatformBuffer_.data(), TargetPlatformBuffer_.size());
ImGui::InputText("Player Exe", PlayerExecutablePathBuffer_.data(), PlayerExecutablePathBuffer_.size());
ImGui::InputText("Output Dir", OutputDirectoryBuffer_.data(), OutputDirectoryBuffer_.size());
ImGui::InputText("Cooked Assets Dir", CookedAssetsDirectoryBuffer_.data(), CookedAssetsDirectoryBuffer_.size());
ImGui::Checkbox("Use cooked assets", &UseCookedAssetsInPackage_);
ImGui::Separator();
ImGui::TextDisabled("Runtime Config Paths");
ImGui::InputText("Data Sources", DataSourcesPathBuffer_.data(), DataSourcesPathBuffer_.size());
ImGui::InputText("Bindings", BindingsPathBuffer_.data(), BindingsPathBuffer_.size());
ImGui::InputText("Diagnostics", DiagnosticsPathBuffer_.data(), DiagnosticsPathBuffer_.size());
ImGui::Separator();
ImGui::Checkbox("Cook before package", &CookBeforePackage_);
ImGui::Checkbox("Copy loose Assets/Scenes", &CopyLooseContent_);
ImGui::Checkbox("Copy Runtime config", &CopyRuntimeConfig_);
}
ImGui::EndChild();
ImGui::Columns(1);
ImGui::Separator();
ImGui::Spacing();
// 3. 底部主按钮 (右对齐)
const bool canBuild = buildService != nullptr; const bool canBuild = buildService != nullptr;
if (!canBuild) { if (!canBuild) {
ImGui::BeginDisabled(); ImGui::BeginDisabled();
} }
if (ImGui::Button("Save Build Settings", ImVec2(-1.0F, 28.0F))) {
// 保存配置
if (ImGui::Button("Save Settings", ImVec2(120.0f, 26.0f))) {
(void)SaveBuildSettings(editorContext, project, true); (void)SaveBuildSettings(editorContext, project, true);
} }
if (ImGui::Button("Build Player Package", ImVec2(-1.0F, 28.0F))) { ImGui::SameLine();
// 打包 (Build)
if (ImGui::Button("Build", ImVec2(100.0f, 26.0f))) {
BuildPackage(editorContext, *buildService, false); BuildPackage(editorContext, *buildService, false);
} }
if (ImGui::Button("Build Cooked-Only Package", ImVec2(-1.0F, 28.0F))) { ImGui::SameLine();
// 烹饪并打包 (Build Cooked)
if (ImGui::Button("Build Cooked", ImVec2(120.0f, 26.0f))) {
BuildPackage(editorContext, *buildService, true); BuildPackage(editorContext, *buildService, true);
} }
if (!canBuild) { if (!canBuild) {
ImGui::EndDisabled(); ImGui::EndDisabled();
} }

View File

@ -1276,6 +1276,109 @@ void MetaCoreEditorApp::DrawSceneViewWindow() {
ImGui::Dummy(viewportSize); ImGui::Dummy(viewportSize);
} }
// 场景视口浮动快捷工具条 (Toolbar Overlay)
// 浮动在视口左上角采用半透明暗色背景1:1 复刻 Unity 视口功能控制体验
ImGui::SetCursorScreenPos(ImVec2(viewportPos.x + 10.0f, viewportPos.y + 10.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.12f, 0.12f, 0.85f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 4.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6.0f, 4.0f));
if (ImGui::BeginChild("ViewportToolbar", ImVec2(410.0f, 28.0f), false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
// A. Gizmo 轴操作切换 (T: 位移, R: 旋转, S: 缩放)
auto currentOp = EditorContext_->GetGizmoOperation();
auto drawToolbarButton = [](const char* label, bool active) -> bool {
if (active) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.173f, 0.365f, 0.529f, 1.0f)); // 选中亮蓝色
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.22f, 0.45f, 0.65f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.12f, 0.28f, 0.42f, 1.0f));
} else {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); // 平时透明
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.12f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.20f));
}
bool clicked = ImGui::Button(label, ImVec2(24.0f, 20.0f));
ImGui::PopStyleColor(3);
return clicked;
};
if (drawToolbarButton("T", currentOp == MetaCoreGizmoOperation::Translate)) {
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Translate);
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("移动工具 (W)");
ImGui::SameLine(0, 2);
if (drawToolbarButton("R", currentOp == MetaCoreGizmoOperation::Rotate)) {
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Rotate);
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("旋转工具 (E)");
ImGui::SameLine(0, 2);
if (drawToolbarButton("S", currentOp == MetaCoreGizmoOperation::Scale)) {
EditorContext_->SetGizmoOperation(MetaCoreGizmoOperation::Scale);
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("缩放工具 (R)");
ImGui::SameLine(0, 6);
ImGui::TextDisabled("|");
ImGui::SameLine(0, 6);
// B. 坐标系切换 (Local本地 / Global世界)
auto currentMode = EditorContext_->GetGizmoMode();
const char* modeLabel = (currentMode == MetaCoreGizmoMode::Local) ? "Local" : "Global";
if (ImGui::Button(modeLabel, ImVec2(50.0f, 20.0f))) {
EditorContext_->SetGizmoMode(currentMode == MetaCoreGizmoMode::Local ? MetaCoreGizmoMode::Global : MetaCoreGizmoMode::Local);
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("切换本地/全局坐标系");
ImGui::SameLine(0, 6);
ImGui::TextDisabled("|");
ImGui::SameLine(0, 6);
// C. 枢轴模式切换 (Pivot枢轴 / Center中心)
auto currentPivot = EditorContext_->GetGizmoPivotMode();
const char* pivotLabel = (currentPivot == MetaCoreGizmoPivotMode::Pivot) ? "Pivot" : "Center";
if (ImGui::Button(pivotLabel, ImVec2(55.0f, 20.0f))) {
EditorContext_->SetGizmoPivotMode(currentPivot == MetaCoreGizmoPivotMode::Pivot ? MetaCoreGizmoPivotMode::Center : MetaCoreGizmoPivotMode::Pivot);
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("切换 Pivot/Center 枢轴中心");
ImGui::SameLine(0, 6);
ImGui::TextDisabled("|");
ImGui::SameLine(0, 6);
// D. 网格显示/隐藏控制 (G)
bool showGrid = EditorContext_->GetShowViewportGrid();
if (drawToolbarButton("G", showGrid)) {
EditorContext_->SetShowViewportGrid(!showGrid);
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip(showGrid ? "隐藏网格" : "显示网格");
ImGui::SameLine(0, 4);
// E. Gizmo 吸附开启/隐藏控制 (U)
bool snapEnabled = EditorContext_->GetGizmoSnapSettings().Enabled;
if (drawToolbarButton("U", snapEnabled)) {
EditorContext_->SetGizmoSnapEnabled(!snapEnabled);
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip(snapEnabled ? "关闭吸附" : "开启吸附");
// F. 视口摄像机一键复位 (Reset Cam)
ImGui::SameLine(0, 6);
ImGui::TextDisabled("|");
ImGui::SameLine(0, 6);
if (ImGui::Button("Reset Cam", ImVec2(75.0f, 20.0f))) {
EditorContext_->GetCameraController().FocusGameObject({}); // 传入空 GameObject 表示复位摄像机到初始中心位置
}
if (ImGui::IsItemHovered()) ImGui::SetTooltip("复位视口摄像机");
}
ImGui::EndChild();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
// 绘制完工具条后,将游标坐标复位,以避免影响后续子节点与 DragDrop 的正常绘制
ImGui::SetCursorScreenPos(viewportPos);
if (ImGui::BeginDragDropTarget()) { if (ImGui::BeginDragDropTarget()) {
(void)MetaCoreHandleProjectAssetDrop(*EditorContext_, std::nullopt); (void)MetaCoreHandleProjectAssetDrop(*EditorContext_, std::nullopt);
ImGui::EndDragDropTarget(); ImGui::EndDragDropTarget();