#include "ProductInspectorPanel.h" #include #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::UI::UIColor; using ::XCEngine::UI::UIDrawList; using ::XCEngine::UI::UIPoint; using ::XCEngine::UI::UIRect; constexpr std::string_view kInspectorPanelId = "inspector"; constexpr float kPanelPadding = 10.0f; constexpr float kHeaderHeight = 22.0f; constexpr float kSectionGap = 10.0f; constexpr float kRowHeight = 21.0f; constexpr float kLabelWidth = 88.0f; constexpr float kTitleFontSize = 13.0f; constexpr float kSubtitleFontSize = 11.0f; constexpr float kSectionTitleFontSize = 11.0f; constexpr float kRowFontSize = 11.0f; constexpr UIColor kSurfaceColor(0.205f, 0.205f, 0.205f, 1.0f); constexpr UIColor kSectionHeaderColor(0.225f, 0.225f, 0.225f, 1.0f); constexpr UIColor kSectionBodyColor(0.215f, 0.215f, 0.215f, 1.0f); constexpr UIColor kTitleColor(0.910f, 0.910f, 0.910f, 1.0f); constexpr UIColor kSubtitleColor(0.650f, 0.650f, 0.650f, 1.0f); constexpr UIColor kLabelColor(0.700f, 0.700f, 0.700f, 1.0f); constexpr UIColor kValueColor(0.860f, 0.860f, 0.860f, 1.0f); float ResolveTextTop(float rectY, float rectHeight, float fontSize) { const float lineHeight = fontSize * 1.6f; return rectY + std::floor((rectHeight - lineHeight) * 0.5f); } std::string PathToUtf8String(const std::filesystem::path& path) { const std::u8string value = path.u8string(); return std::string(value.begin(), value.end()); } } // namespace const UIEditorPanelContentHostPanelState* ProductInspectorPanel::FindMountedInspectorPanel( const UIEditorPanelContentHostFrame& contentHostFrame) const { for (const UIEditorPanelContentHostPanelState& panelState : contentHostFrame.panelStates) { if (panelState.panelId == kInspectorPanelId && panelState.mounted) { return &panelState; } } return nullptr; } void ProductInspectorPanel::BuildPresentation(const ProductEditorSession& session) { m_sections.clear(); m_title.clear(); m_subtitle.clear(); m_hasSelection = false; switch (session.selection.kind) { case ProductEditorSelectionKind::HierarchyNode: { m_hasSelection = true; m_title = session.selection.displayName.empty() ? std::string("GameObject") : session.selection.displayName; m_subtitle = "GameObject"; Section identity = {}; identity.title = "Identity"; identity.rows = { { "Type", "GameObject" }, { "Name", m_title }, { "Id", session.selection.itemId } }; m_sections.push_back(std::move(identity)); break; } case ProductEditorSelectionKind::ProjectItem: { m_hasSelection = true; m_title = session.selection.displayName.empty() ? (session.selection.directory ? std::string("Folder") : std::string("Asset")) : session.selection.displayName; m_subtitle = session.selection.directory ? "Folder" : "Asset"; Section identity = {}; identity.title = "Identity"; identity.rows = { { "Type", session.selection.directory ? std::string("Folder") : std::string("Asset") }, { "Name", m_title }, { "Id", session.selection.itemId } }; m_sections.push_back(std::move(identity)); Section location = {}; location.title = "Location"; location.rows = { { "Path", PathToUtf8String(session.selection.absolutePath) } }; m_sections.push_back(std::move(location)); break; } case ProductEditorSelectionKind::None: default: m_title = "Nothing selected"; m_subtitle = "Select a hierarchy item or project asset."; break; } } void ProductInspectorPanel::Update( const ProductEditorSession& session, const UIEditorPanelContentHostFrame& contentHostFrame) { const UIEditorPanelContentHostPanelState* panelState = FindMountedInspectorPanel(contentHostFrame); if (panelState == nullptr) { m_visible = false; m_bounds = {}; m_sections.clear(); m_title.clear(); m_subtitle.clear(); m_hasSelection = false; return; } m_visible = true; m_bounds = panelState->bounds; BuildPresentation(session); } void ProductInspectorPanel::Append(UIDrawList& drawList) const { if (!m_visible || m_bounds.width <= 0.0f || m_bounds.height <= 0.0f) { return; } const UIColor borderColor = ResolveUIEditorDockHostPalette().splitterColor; drawList.AddFilledRect(m_bounds, kSurfaceColor); const float contentX = m_bounds.x + kPanelPadding; const float contentWidth = (std::max)(m_bounds.width - kPanelPadding * 2.0f, 0.0f); float nextY = m_bounds.y + kPanelPadding; const UIRect titleRect(contentX, nextY, contentWidth, 18.0f); drawList.AddText( UIPoint(titleRect.x, ResolveTextTop(titleRect.y, titleRect.height, kTitleFontSize)), m_title, kTitleColor, kTitleFontSize); nextY += titleRect.height; const UIRect subtitleRect(contentX, nextY, contentWidth, 16.0f); drawList.AddText( UIPoint(subtitleRect.x, ResolveTextTop(subtitleRect.y, subtitleRect.height, kSubtitleFontSize)), m_subtitle, kSubtitleColor, kSubtitleFontSize); nextY += subtitleRect.height + kSectionGap; if (!m_hasSelection) { return; } for (const Section& section : m_sections) { const float bodyHeight = static_cast(section.rows.size()) * kRowHeight; const UIRect headerRect(contentX, nextY, contentWidth, kHeaderHeight); const UIRect bodyRect(contentX, headerRect.y + headerRect.height, contentWidth, bodyHeight); drawList.AddFilledRect(headerRect, kSectionHeaderColor); drawList.AddFilledRect(bodyRect, kSectionBodyColor); drawList.AddRectOutline( UIRect(headerRect.x, headerRect.y, headerRect.width, headerRect.height + bodyRect.height), borderColor, 1.0f, 0.0f); drawList.AddText( UIPoint(headerRect.x + 8.0f, ResolveTextTop(headerRect.y, headerRect.height, kSectionTitleFontSize)), section.title, kTitleColor, kSectionTitleFontSize); float rowY = bodyRect.y; for (std::size_t rowIndex = 0u; rowIndex < section.rows.size(); ++rowIndex) { const SectionRow& row = section.rows[rowIndex]; const UIRect rowRect(contentX, rowY, contentWidth, kRowHeight); const UIRect labelRect(rowRect.x + 8.0f, rowRect.y, kLabelWidth, rowRect.height); const UIRect valueRect( labelRect.x + labelRect.width + 8.0f, rowRect.y, (std::max)(rowRect.width - (labelRect.width + 24.0f), 0.0f), rowRect.height); if (rowIndex > 0u) { drawList.AddFilledRect( UIRect(rowRect.x, rowRect.y, rowRect.width, 1.0f), borderColor); } drawList.AddText( UIPoint(labelRect.x, ResolveTextTop(labelRect.y, labelRect.height, kRowFontSize)), row.label, kLabelColor, kRowFontSize); drawList.PushClipRect(valueRect); drawList.AddText( UIPoint(valueRect.x, ResolveTextTop(valueRect.y, valueRect.height, kRowFontSize)), row.value, kValueColor, kRowFontSize); drawList.PopClipRect(); rowY += kRowHeight; } nextY = bodyRect.y + bodyRect.height + kSectionGap; } } } // namespace XCEngine::UI::Editor::App