diff --git a/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp b/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp index fdb0d47..4eb6972 100644 --- a/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp +++ b/Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp @@ -2635,10 +2635,10 @@ private: gameObject.HasComponent() && !gameObject.GetComponent().SourceModelPath.empty(); - // 1:1 对齐 Infernux 装饰样式:模型实例冠以 ◆ 前缀 + // 1:1 对齐 Infernux 装饰样式:模型实例冠以 * 前缀 (使用 ASCII 规避缺失字体导致的问号) std::string hierarchyLabel = gameObject.GetName(); if (isModelInstance) { - hierarchyLabel = "\u25C6 " + hierarchyLabel; + hierarchyLabel = "* " + hierarchyLabel; } // 1:1 对齐选中背景色与模型实例的文字红强调色 @@ -4665,29 +4665,16 @@ public: if (IconSize_ <= 32.0f) { // 列表视图模式 (List View) for (const auto& item : items) { - std::string label; - ImVec4 iconColor(1.0f, 1.0f, 1.0f, 1.0f); - const char* iconStr = "📄"; + // 1. 获取当前行屏幕绝对起点,用于手绘矢量图标 + ImVec2 startPos = ImGui::GetCursorScreenPos(); + + // 留出 18 像素的迷你图标空白位置 + ImGui::Dummy(ImVec2(18.0f, 0.0f)); + ImGui::SameLine(); - if (item.IsDirectory) { - iconStr = "📁"; - iconColor = ImVec4(0.95f, 0.75f, 0.25f, 1.0f); - label = std::string(iconStr) + " " + item.Path.filename().string(); - } else { - if (item.Type == "material") { - iconStr = "🔮"; - iconColor = ImVec4(0.65f, 0.45f, 0.95f, 1.0f); - } else if (item.Type == "model") { - iconStr = "📦"; - iconColor = ImVec4(0.25f, 0.85f, 0.45f, 1.0f); - } else if (item.Type == "scene") { - iconStr = "🎬"; - iconColor = ImVec4(0.95f, 0.35f, 0.35f, 1.0f); - } else if (item.Type == "ui_document") { - iconStr = "📄"; - iconColor = ImVec4(0.25f, 0.75f, 0.95f, 1.0f); - } - label = std::string(iconStr) + " " + item.Path.filename().string() + " [" + item.Type + "]"; + std::string labelText = item.Path.filename().string(); + if (!item.IsDirectory) { + labelText += " [" + item.Type + "]"; } bool isSelected = false; @@ -4704,11 +4691,56 @@ public: } ImGui::PushID(item.IsDirectory ? item.Path.string().c_str() : item.Guid.ToString().c_str()); - const bool clicked = ImGui::Selectable(label.c_str(), isSelected); + const bool clicked = ImGui::Selectable(labelText.c_str(), isSelected); if (isSelected) { ImGui::PopStyleColor(3); } + // 2. 用 ImDrawList 在该行的左侧空白处绘制 10px 精美矢量微型图标 (避免缺失显示为问号) + ImDrawList* drawList = ImGui::GetWindowDrawList(); + float frameHeight = ImGui::GetFrameHeight(); + ImVec2 miniIconCenter(startPos.x + 8.0f, startPos.y + frameHeight * 0.5f); + float miniSize = 10.0f; + + if (item.IsDirectory) { + float w = miniSize; + float h = miniSize * 0.75f; + ImVec2 topLeft(miniIconCenter.x - w * 0.5f, miniIconCenter.y - h * 0.5f + h * 0.1f); + ImVec2 bottomRight(miniIconCenter.x + w * 0.5f, miniIconCenter.y + h * 0.5f); + drawList->AddRectFilled(topLeft, bottomRight, IM_COL32(230, 180, 45, 255), 1.0f); + drawList->AddRectFilled(ImVec2(topLeft.x, topLeft.y - h * 0.2f), ImVec2(topLeft.x + w * 0.4f, topLeft.y + h * 0.1f), IM_COL32(190, 145, 30, 255), 0.5f); + } else if (item.Type == "material") { + drawList->AddCircleFilled(miniIconCenter, miniSize * 0.45f, IM_COL32(140, 80, 210, 255), 12); + ImVec2 highlightCenter(miniIconCenter.x - miniSize * 0.15f, miniIconCenter.y - miniSize * 0.15f); + drawList->AddCircleFilled(highlightCenter, miniSize * 0.15f, IM_COL32(255, 255, 255, 180), 6); + } else if (item.Type == "model") { + float sz = miniSize * 0.45f; + ImVec2 p0(miniIconCenter.x - sz, miniIconCenter.y - sz); + ImVec2 p1(miniIconCenter.x + sz, miniIconCenter.y + sz); + drawList->AddRectFilled(p0, p1, IM_COL32(60, 190, 110, 255), 1.0f); + drawList->AddRect(p0, p1, IM_COL32(120, 255, 170, 255), 1.0f); + } else if (item.Type == "scene") { + drawList->AddCircleFilled(miniIconCenter, miniSize * 0.45f, IM_COL32(230, 80, 80, 255), 12); + float tr = miniSize * 0.2f; + ImVec2 p0(miniIconCenter.x - tr * 0.4f, miniIconCenter.y - tr * 0.7f); + ImVec2 p1(miniIconCenter.x + tr * 0.8f, miniIconCenter.y); + ImVec2 p2(miniIconCenter.x - tr * 0.4f, miniIconCenter.y + tr * 0.7f); + drawList->AddTriangleFilled(p0, p1, p2, IM_COL32(255, 255, 255, 255)); + } else if (item.Type == "prefab") { + float sz = miniSize * 0.45f; + ImVec2 p0(miniIconCenter.x - sz, miniIconCenter.y - sz); + ImVec2 p1(miniIconCenter.x + sz, miniIconCenter.y + sz); + drawList->AddRectFilled(p0, p1, IM_COL32(44, 120, 206, 255), 1.0f); + drawList->AddRect(p0, p1, IM_COL32(134, 210, 255, 255), 1.0f); + } else { + float w = miniSize * 0.7f; + float h = miniSize * 0.9f; + ImVec2 topLeft(miniIconCenter.x - w * 0.5f, miniIconCenter.y - h * 0.5f); + ImVec2 bottomRight(miniIconCenter.x + w * 0.5f, miniIconCenter.y + h * 0.5f); + drawList->AddRectFilled(topLeft, bottomRight, IM_COL32(230, 230, 235, 255), 0.0f); + drawList->AddRect(topLeft, bottomRight, IM_COL32(150, 150, 160, 255), 0.0f); + } + if (clicked) { if (item.IsDirectory) { editorContext.SetSelectedProjectDirectory(item.Path); @@ -4864,6 +4896,12 @@ public: const ImVec2 startPos = ImGui::GetCursorPos(); + // 1. 先用 Dummy 占位,确保 ImGui 已经提交并扩展了 parent 边界,防止 Assert 报错 + ImGui::Dummy(cardSize); + + // 2. 将游标重置回起点,开始在 Dummy 区域里绘制 + ImGui::SetCursorPos(startPos); + if (isSelected) { ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.173F, 0.365F, 0.529F, 0.40F)); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.173F, 0.365F, 0.529F, 0.60F)); @@ -4877,40 +4915,111 @@ public: const bool clicked = ImGui::Selectable("##card", isSelected, 0, cardSize); ImGui::PopStyleColor(3); - const ImVec2 currentPos = ImGui::GetCursorPos(); + // 3. 用 ImDrawList 绘制大尺寸精美矢量图标 + ImDrawList* drawList = ImGui::GetWindowDrawList(); + ImVec2 screenStartPos = ImGui::GetItemRectMin(); + ImVec2 gridIconCenter(screenStartPos.x + cardSize.x * 0.5f, screenStartPos.y + 8.0f + IconSize_ * 0.5f); + float largeSize = IconSize_; - ImGui::SetCursorPos(startPos); - ImGui::SetCursorPosY(startPos.y + 6.0f); - - ImVec4 iconColor(1.0f, 1.0f, 1.0f, 1.0f); - const char* iconStr = "📄"; if (item.IsDirectory) { - iconStr = "📁"; - iconColor = ImVec4(0.95f, 0.75f, 0.25f, 1.0f); + // 绘制带有浅黄色前盖和深黄色后盖层叠效果的双矩形立体文件夹 + float w = largeSize * 0.85f; + float h = largeSize * 0.65f; + ImVec2 topLeft(gridIconCenter.x - w * 0.5f, gridIconCenter.y - h * 0.5f + h * 0.1f); + ImVec2 bottomRight(gridIconCenter.x + w * 0.5f, gridIconCenter.y + h * 0.5f); + // 绘制文件夹后背板 (略暗) + drawList->AddRectFilled(topLeft, bottomRight, IM_COL32(190, 145, 30, 255), 3.0f); + // 绘制文件夹上方标签页 + ImVec2 tabTopLeft(topLeft.x, topLeft.y - h * 0.2f); + ImVec2 tabBottomRight(topLeft.x + w * 0.4f, topLeft.y + h * 0.1f); + drawList->AddRectFilled(tabTopLeft, tabBottomRight, IM_COL32(190, 145, 30, 255), 2.0f); + // 绘制文件夹前挡板 (略亮) + ImVec2 frontTopLeft(topLeft.x, topLeft.y + h * 0.15f); + drawList->AddRectFilled(frontTopLeft, bottomRight, IM_COL32(235, 185, 45, 255), 3.0f); } else if (item.Type == "material") { - iconStr = "🔮"; - iconColor = ImVec4(0.65f, 0.45f, 0.95f, 1.0f); + // 绘制具有左上角高光球体感的紫色材质圆球 + float radius = largeSize * 0.4f; + drawList->AddCircleFilled(gridIconCenter, radius, IM_COL32(140, 80, 210, 255), 36); + ImVec2 highlightCenter(gridIconCenter.x - radius * 0.3f, gridIconCenter.y - radius * 0.3f); + drawList->AddCircleFilled(highlightCenter, radius * 0.35f, IM_COL32(255, 255, 255, 120), 18); + drawList->AddCircleFilled(highlightCenter, radius * 0.15f, IM_COL32(255, 255, 255, 200), 12); } else if (item.Type == "model") { - iconStr = "📦"; - iconColor = ImVec4(0.25f, 0.85f, 0.45f, 1.0f); + // 绘制等角 3D 立体绿箱子 + float halfSize = largeSize * 0.35f; + float dx = halfSize * 0.9f; + float dy = halfSize * 0.45f; + float dz = halfSize * 0.8f; // 高度 + ImVec2 topCenter(gridIconCenter.x, gridIconCenter.y - dz * 0.4f); + ImVec2 p0(topCenter.x, topCenter.y - dy); + ImVec2 p1(topCenter.x + dx, topCenter.y); + ImVec2 p2(topCenter.x, topCenter.y + dy); + ImVec2 p3(topCenter.x - dx, topCenter.y); + ImVec2 b1(p1.x, p1.y + dz); + ImVec2 b2(p2.x, p2.y + dz); + ImVec2 b3(p3.x, p3.y + dz); + // 面填充 + drawList->AddQuadFilled(p0, p1, p2, p3, IM_COL32(90, 220, 140, 255)); + drawList->AddQuadFilled(p3, p2, b2, b3, IM_COL32(60, 190, 110, 255)); + drawList->AddQuadFilled(p2, p1, b1, b2, IM_COL32(45, 165, 90, 255)); + // 绘制边线 + drawList->AddQuad(p0, p1, p2, p3, IM_COL32(120, 255, 170, 255), 1.0f); + drawList->AddLine(p3, b3, IM_COL32(120, 255, 170, 255), 1.0f); + drawList->AddLine(p2, b2, IM_COL32(120, 255, 170, 255), 1.0f); + drawList->AddLine(p1, b1, IM_COL32(120, 255, 170, 255), 1.0f); + drawList->AddLine(b3, b2, IM_COL32(120, 255, 170, 255), 1.0f); + drawList->AddLine(b2, b1, IM_COL32(120, 255, 170, 255), 1.0f); } else if (item.Type == "scene") { - iconStr = "🎬"; - iconColor = ImVec4(0.95f, 0.35f, 0.35f, 1.0f); - } else if (item.Type == "ui_document") { - iconStr = "📄"; - iconColor = ImVec4(0.25f, 0.75f, 0.95f, 1.0f); + // 绘制带播放三角白芯的红色场景圆圈 + float radius = largeSize * 0.4f; + drawList->AddCircleFilled(gridIconCenter, radius, IM_COL32(230, 80, 80, 255), 36); + float tr = radius * 0.4f; + ImVec2 p0(gridIconCenter.x - tr * 0.4f, gridIconCenter.y - tr * 0.7f); + ImVec2 p1(gridIconCenter.x + tr * 0.8f, gridIconCenter.y); + ImVec2 p2(gridIconCenter.x - tr * 0.4f, gridIconCenter.y + tr * 0.7f); + drawList->AddTriangleFilled(p0, p1, p2, IM_COL32(255, 255, 255, 255)); + } else if (item.Type == "prefab") { + // 绘制等角 3D 立体蓝色 Prefab 盒子 + float halfSize = largeSize * 0.35f; + float dx = halfSize * 0.9f; + float dy = halfSize * 0.45f; + float dz = halfSize * 0.8f; + ImVec2 topCenter(gridIconCenter.x, gridIconCenter.y - dz * 0.4f); + ImVec2 p0(topCenter.x, topCenter.y - dy); + ImVec2 p1(topCenter.x + dx, topCenter.y); + ImVec2 p2(topCenter.x, topCenter.y + dy); + ImVec2 p3(topCenter.x - dx, topCenter.y); + ImVec2 b1(p1.x, p1.y + dz); + ImVec2 b2(p2.x, p2.y + dz); + ImVec2 b3(p3.x, p3.y + dz); + // 面填充 + drawList->AddQuadFilled(p0, p1, p2, p3, IM_COL32(74, 150, 236, 255)); + drawList->AddQuadFilled(p3, p2, b2, b3, IM_COL32(44, 120, 206, 255)); + drawList->AddQuadFilled(p2, p1, b1, b2, IM_COL32(29, 95, 176, 255)); + // 绘制边线 + drawList->AddQuad(p0, p1, p2, p3, IM_COL32(134, 210, 255, 255), 1.0f); + drawList->AddLine(p3, b3, IM_COL32(134, 210, 255, 255), 1.0f); + drawList->AddLine(p2, b2, IM_COL32(134, 210, 255, 255), 1.0f); + drawList->AddLine(p1, b1, IM_COL32(134, 210, 255, 255), 1.0f); + drawList->AddLine(b3, b2, IM_COL32(134, 210, 255, 255), 1.0f); + drawList->AddLine(b2, b1, IM_COL32(134, 210, 255, 255), 1.0f); + } else { + // 绘制折页灰色文档纸 + float w = largeSize * 0.55f; + float h = largeSize * 0.75f; + ImVec2 topLeft(gridIconCenter.x - w * 0.5f, gridIconCenter.y - h * 0.5f); + ImVec2 bottomRight(gridIconCenter.x + w * 0.5f, gridIconCenter.y + h * 0.5f); + float foldSize = w * 0.35f; + ImVec2 pFoldTop(bottomRight.x - foldSize, topLeft.y); + ImVec2 pFoldRight(bottomRight.x, topLeft.y + foldSize); + ImVec2 pFoldCorner(bottomRight.x - foldSize, topLeft.y + foldSize); + ImVec2 pts[5] = { topLeft, pFoldTop, pFoldRight, bottomRight, ImVec2(topLeft.x, bottomRight.y) }; + drawList->AddConvexPolyFilled(pts, 5, IM_COL32(230, 230, 235, 255)); + drawList->AddTriangleFilled(pFoldTop, pFoldCorner, pFoldRight, IM_COL32(180, 180, 185, 255)); + drawList->AddPolyline(pts, 5, IM_COL32(150, 150, 160, 255), ImDrawFlags_Closed, 1.0f); } - float scaleFactor = IconSize_ / 32.0f; - if (scaleFactor < 1.0f) scaleFactor = 1.0f; - if (scaleFactor > 3.0f) scaleFactor = 3.0f; - - ImGui::SetWindowFontScale(scaleFactor); - const float textWidth = ImGui::CalcTextSize(iconStr).x; - ImGui::SetCursorPosX(startPos.x + (cardSize.x - textWidth) * 0.5f); - ImGui::TextColored(iconColor, "%s", iconStr); - ImGui::SetWindowFontScale(1.0f); - + // 4. 绘制文件名 + ImGui::SetCursorPos(startPos); ImGui::SetCursorPosY(startPos.y + IconSize_ + 8.0f); std::string displayName = filename; if (ImGui::CalcTextSize(displayName.c_str()).x > cardSize.x - 4.0f) { @@ -4923,7 +5032,8 @@ public: ImGui::SetCursorPosX(startPos.x + (cardSize.x - nameWidth) * 0.5f); ImGui::TextUnformatted(displayName.c_str()); - ImGui::SetCursorPos(currentPos); + // 5. 将游标完美放置到卡片正底端,以防止窗口/父边界越界 Assert 报错 + ImGui::SetCursorPos(ImVec2(startPos.x, startPos.y + cardSize.y)); ImGui::EndGroup(); if (ImGui::IsItemHovered()) { @@ -5148,9 +5258,9 @@ public: 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); + std::snprintf(infoLabel, sizeof(infoLabel), "Info: %d", infoCount); + std::snprintf(warnLabel, sizeof(warnLabel), "Warn: %d", warnCount); + std::snprintf(errLabel, sizeof(errLabel), "Error: %d", errCount); ImGui::SameLine(ImGui::GetContentRegionMax().x - 170.0f); @@ -5232,15 +5342,13 @@ public: 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 = "🛑"; - } + // 1. 获取当前行起点并留出矢量图标位置 + ImVec2 startPos = ImGui::GetCursorScreenPos(); + ImGui::Dummy(ImVec2(18.0f, 0.0f)); + ImGui::SameLine(); char rowLabel[512]; - std::snprintf(rowLabel, sizeof(rowLabel), "%s [%s] %s", iconStr, de.Category.c_str(), de.Message.c_str()); + std::snprintf(rowLabel, sizeof(rowLabel), "[%s] %s", de.Category.c_str(), de.Message.c_str()); bool isSelected = (ConsoleSelectedEntryIdx_ == static_cast(de.OriginalIndex)); if (ImGui::Selectable(rowLabel, isSelected, ImGuiSelectableFlags_SpanAllColumns)) { @@ -5249,6 +5357,31 @@ public: ImGui::PopStyleColor(3); + // 2. 用 ImDrawList 在该行左侧空白处绘制微型矢量图标 + ImDrawList* drawList = ImGui::GetWindowDrawList(); + float frameHeight = ImGui::GetFrameHeight(); + ImVec2 iconCenter(startPos.x + 8.0f, startPos.y + frameHeight * 0.5f); + float iconSize = 10.0f; + + if (de.Level == MetaCoreLogLevel::Info) { + // 蓝色圆圈 + drawList->AddCircleFilled(iconCenter, iconSize * 0.45f, IM_COL32(44, 122, 226, 255), 12); + } else if (de.Level == MetaCoreLogLevel::Warning) { + // 黄色三角形 + float r = iconSize * 0.5f; + ImVec2 p0(iconCenter.x, iconCenter.y - r); + ImVec2 p1(iconCenter.x - r * 0.866f, iconCenter.y + r * 0.5f); + ImVec2 p2(iconCenter.x + r * 0.866f, iconCenter.y + r * 0.5f); + drawList->AddTriangleFilled(p0, p1, p2, IM_COL32(235, 180, 50, 255)); + } else if (de.Level == MetaCoreLogLevel::Error) { + // 红色圆圈 + 白色小叉 + float r = iconSize * 0.45f; + drawList->AddCircleFilled(iconCenter, r, IM_COL32(235, 87, 87, 255), 12); + float xr = r * 0.5f; + drawList->AddLine(ImVec2(iconCenter.x - xr, iconCenter.y - xr), ImVec2(iconCenter.x + xr, iconCenter.y + xr), IM_COL32(255, 255, 255, 255), 1.2f); + drawList->AddLine(ImVec2(iconCenter.x + xr, iconCenter.y - xr), ImVec2(iconCenter.x - xr, iconCenter.y + xr), IM_COL32(255, 255, 255, 255), 1.2f); + } + 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));