feat: implement viewport toolbar overlay, Collapse/Filter Console list, and split Build Settings with Scenes In Build drag-drop
This commit is contained in:
parent
5b4095f049
commit
998edb23e7
@ -83,6 +83,12 @@ static std::filesystem::path PendingRenamePath_{};
|
||||
static std::array<char, 256> RenameBuffer_{};
|
||||
static std::array<char, 256> NewFolderBuffer_{};
|
||||
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 PlayerStdoutRead_ = NULL;
|
||||
|
||||
@ -5115,18 +5121,168 @@ public:
|
||||
bool IsOpenByDefault() const override { return true; }
|
||||
|
||||
void DrawPanel(MetaCoreEditorContext& editorContext) override {
|
||||
for (const MetaCoreLogEntry& entry : editorContext.GetLogService().GetEntries()) {
|
||||
ImVec4 textColor = ImVec4(0.85F, 0.85F, 0.88F, 1.0F);
|
||||
const char* icon = "(i)";
|
||||
if (entry.Level == MetaCoreLogLevel::Warning) {
|
||||
textColor = ImVec4(0.95F, 0.75F, 0.25F, 1.0F);
|
||||
icon = "(!)";
|
||||
} else if (entry.Level == MetaCoreLogLevel::Error) {
|
||||
textColor = ImVec4(0.95F, 0.35F, 0.30F, 1.0F);
|
||||
icon = "(X)";
|
||||
}
|
||||
ImGui::TextColored(textColor, "%s [%s] %s", icon, entry.Category.c_str(), entry.Message.c_str());
|
||||
auto& logService = editorContext.GetLogService();
|
||||
|
||||
// 1. 顶部工具栏 (Toolbar)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f, 2.0f));
|
||||
|
||||
if (ImGui::Button("Clear")) {
|
||||
logService.Clear();
|
||||
ConsoleSelectedEntryIdx_ = -1;
|
||||
}
|
||||
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();
|
||||
BindProjectDefaults(project);
|
||||
|
||||
ImGui::Text("Project: %s", project.Name.c_str());
|
||||
ImGui::Text("Root: %s", project.RootPath.string().c_str());
|
||||
ImGui::Text("Project Startup Scene: %s", project.StartupScenePath.generic_string().c_str());
|
||||
ImGui::Separator();
|
||||
// 1. Scenes In Build 场景构建列表
|
||||
ImGui::Text("Scenes In Build");
|
||||
ImGui::TextDisabled("可以将 .mcscene 场景资产从项目面板拖拽至下方列表中添加");
|
||||
|
||||
// 自动初始化场景列表
|
||||
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());
|
||||
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::TextUnformatted("Runtime Data");
|
||||
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_);
|
||||
// 用 Child Window 包裹展现边框列表
|
||||
if (ImGui::BeginChild("ScenesInBuildList", ImVec2(0, 110.0f), true)) {
|
||||
for (size_t i = 0; i < BuildScenes_.size(); ++i) {
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
ImGui::Checkbox("##enabled", &BuildScenes_[i].second);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", BuildScenes_[i].first.c_str());
|
||||
|
||||
// 右键菜单支持移除场景
|
||||
if (ImGui::BeginPopupContextItem()) {
|
||||
if (ImGui::MenuItem("Remove Scene")) {
|
||||
BuildScenes_.erase(BuildScenes_.begin() + i);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
// 接收拖放
|
||||
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;
|
||||
if (!canBuild) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (!canBuild) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
@ -1276,6 +1276,109 @@ void MetaCoreEditorApp::DrawSceneViewWindow() {
|
||||
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()) {
|
||||
(void)MetaCoreHandleProjectAssetDrop(*EditorContext_, std::nullopt);
|
||||
ImGui::EndDragDropTarget();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user