#include "Features/Scene/SceneViewportToolOverlay.h" #include "Host/TextureHost.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 float kButtonExtent = 26.0f; constexpr float kButtonSpacing = 4.0f; constexpr float kPanelPadding = 5.0f; constexpr float kViewportInset = 10.0f; constexpr float kIconInset = 3.0f; constexpr float kPanelCornerRounding = 6.0f; constexpr float kButtonCornerRounding = 4.0f; constexpr float kFallbackFontSize = 10.0f; struct ToolButtonSpec { SceneToolMode mode = SceneToolMode::View; const char* label = ""; const char* inactiveFile = ""; const char* activeFile = ""; }; constexpr std::array kToolButtonSpecs = {{ { SceneToolMode::View, "View", "view_move_tool.png", "view_move_tool_on.png" }, { SceneToolMode::Translate, "Move", "move_tool.png", "move_tool_on.png" }, { SceneToolMode::Rotate, "Rotate", "rotate_tool.png", "rotate_tool_on.png" }, { SceneToolMode::Scale, "Scale", "scale_tool.png", "scale_tool_on.png" }, { SceneToolMode::Transform, "Transform", "transform_tool.png", "transform_tool_on.png" } }}; bool ContainsPoint(const UIRect& rect, const UIPoint& point) { return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height; } UIRect BuildButtonRect(const UIRect& panelRect, std::size_t index) { return UIRect( panelRect.x + kPanelPadding, panelRect.y + kPanelPadding + static_cast(index) * (kButtonExtent + kButtonSpacing), kButtonExtent, kButtonExtent); } } // namespace bool SceneViewportToolOverlay::Initialize( const std::filesystem::path& repoRoot, Host::TextureHost& renderer) { Shutdown(renderer); const std::filesystem::path iconRoot = (repoRoot / "editor" / "resources" / "Icons").lexically_normal(); bool loadedAnyTexture = false; for (std::size_t index = 0; index < kToolButtonSpecs.size(); ++index) { const ToolButtonSpec& path = kToolButtonSpecs[index]; ToolTextureSet& textureSet = m_toolTextures[index]; textureSet = {}; textureSet.mode = path.mode; textureSet.label = path.label; std::string error = {}; loadedAnyTexture = renderer.LoadTextureFromFile( iconRoot / path.inactiveFile, textureSet.inactiveTexture, error) || loadedAnyTexture; error.clear(); loadedAnyTexture = renderer.LoadTextureFromFile( iconRoot / path.activeFile, textureSet.activeTexture, error) || loadedAnyTexture; } return loadedAnyTexture; } void SceneViewportToolOverlay::Shutdown(Host::TextureHost& renderer) { for (ToolTextureSet& textureSet : m_toolTextures) { renderer.ReleaseTexture(textureSet.inactiveTexture); renderer.ReleaseTexture(textureSet.activeTexture); textureSet = {}; } ResetFrame(); } void SceneViewportToolOverlay::ResetFrame() { m_frame = {}; } void SceneViewportToolOverlay::BuildFrame( const UIRect& viewportRect, SceneToolMode activeMode, std::size_t hoveredIndex, std::size_t pressedIndex) { m_frame = {}; if (viewportRect.width <= 1.0f || viewportRect.height <= 1.0f) { return; } m_frame.visible = true; m_frame.clipRect = viewportRect; m_frame.panelRect = UIRect( viewportRect.x + kViewportInset, viewportRect.y + kViewportInset, kPanelPadding * 2.0f + kButtonExtent, kPanelPadding * 2.0f + static_cast(m_frame.buttons.size()) * kButtonExtent + static_cast(m_frame.buttons.size() - 1u) * kButtonSpacing); for (std::size_t index = 0; index < m_frame.buttons.size(); ++index) { const ToolButtonSpec& spec = kToolButtonSpecs[index]; const ToolTextureSet& textureSet = m_toolTextures[index]; SceneViewportToolOverlayButtonFrame& button = m_frame.buttons[index]; button = {}; button.mode = spec.mode; button.label = spec.label; button.rect = BuildButtonRect(m_frame.panelRect, index); button.active = spec.mode == activeMode; button.hovered = index == hoveredIndex; button.pressed = index == pressedIndex; button.texture = button.active ? textureSet.activeTexture : textureSet.inactiveTexture; } } std::size_t SceneViewportToolOverlay::HitTest(const UIPoint& point) const { if (!m_frame.visible || !Contains(point)) { return kSceneViewportToolOverlayInvalidIndex; } for (std::size_t index = 0; index < m_frame.buttons.size(); ++index) { if (ContainsPoint(m_frame.buttons[index].rect, point)) { return index; } } return kSceneViewportToolOverlayInvalidIndex; } bool SceneViewportToolOverlay::Contains(const UIPoint& point) const { return m_frame.visible && ContainsPoint(m_frame.panelRect, point); } const SceneViewportToolOverlayFrame& SceneViewportToolOverlay::GetFrame() const { return m_frame; } void AppendSceneViewportToolOverlay( UIDrawList& drawList, const SceneViewportToolOverlayFrame& frame) { if (!frame.visible) { return; } constexpr UIColor kPanelFill(24.0f / 255.0f, 26.0f / 255.0f, 29.0f / 255.0f, 220.0f / 255.0f); constexpr UIColor kPanelOutline(1.0f, 1.0f, 1.0f, 28.0f / 255.0f); constexpr UIColor kButtonIdle(44.0f / 255.0f, 47.0f / 255.0f, 54.0f / 255.0f, 230.0f / 255.0f); constexpr UIColor kButtonHover(68.0f / 255.0f, 74.0f / 255.0f, 84.0f / 255.0f, 240.0f / 255.0f); constexpr UIColor kButtonPressed(86.0f / 255.0f, 96.0f / 255.0f, 109.0f / 255.0f, 245.0f / 255.0f); constexpr UIColor kButtonActive(78.0f / 255.0f, 102.0f / 255.0f, 126.0f / 255.0f, 245.0f / 255.0f); constexpr UIColor kButtonOutline(1.0f, 1.0f, 1.0f, 24.0f / 255.0f); constexpr UIColor kButtonActiveOutline(1.0f, 1.0f, 1.0f, 48.0f / 255.0f); constexpr UIColor kFallbackText(230.0f / 255.0f, 230.0f / 255.0f, 230.0f / 255.0f, 1.0f); drawList.PushClipRect(frame.clipRect); drawList.AddFilledRect(frame.panelRect, kPanelFill, kPanelCornerRounding); drawList.AddRectOutline(frame.panelRect, kPanelOutline, 1.0f, kPanelCornerRounding); for (const SceneViewportToolOverlayButtonFrame& button : frame.buttons) { UIColor fill = kButtonIdle; if (button.active) { fill = kButtonActive; } if (button.hovered) { fill = button.active ? kButtonActive : kButtonHover; } if (button.pressed) { fill = kButtonPressed; } drawList.AddFilledRect(button.rect, fill, kButtonCornerRounding); drawList.AddRectOutline( button.rect, button.active ? kButtonActiveOutline : kButtonOutline, 1.0f, kButtonCornerRounding); if (button.texture.IsValid()) { const UIRect iconRect( button.rect.x + kIconInset, button.rect.y + kIconInset, button.rect.width - kIconInset * 2.0f, button.rect.height - kIconInset * 2.0f); drawList.AddImage(iconRect, button.texture); continue; } drawList.AddText( UIPoint(button.rect.x + 7.0f, button.rect.y + 9.0f), button.label, kFallbackText, kFallbackFontSize); } drawList.PopClipRect(); } } // namespace XCEngine::UI::Editor::App