#include #include namespace XCEngine::UI::Editor { namespace { using ::XCEngine::UI::UIColor; using ::XCEngine::UI::UIPoint; using ::XCEngine::UI::UIRect; using Widgets::AppendUIEditorMenuBarBackground; using Widgets::AppendUIEditorMenuBarForeground; using Widgets::AppendUIEditorStatusBarBackground; using Widgets::AppendUIEditorStatusBarForeground; using Widgets::BuildUIEditorMenuBarLayout; using Widgets::BuildUIEditorStatusBarLayout; float ClampNonNegative(float value) { return (std::max)(value, 0.0f); } UIRect InsetRect(const UIRect& rect, float inset) { const float clampedInset = ClampNonNegative(inset); const float insetX = (std::min)(clampedInset, rect.width * 0.5f); const float insetY = (std::min)(clampedInset, rect.height * 0.5f); return UIRect( rect.x + insetX, rect.y + insetY, (std::max)(0.0f, rect.width - insetX * 2.0f), (std::max)(0.0f, rect.height - insetY * 2.0f)); } UIEditorShellToolbarLayout BuildUIEditorShellToolbarLayout( const UIRect& bounds, const std::vector& buttons, const UIEditorShellToolbarMetrics& metrics) { UIEditorShellToolbarLayout layout = {}; layout.bounds = bounds; if (buttons.empty() || bounds.width <= 0.0f || bounds.height <= 0.0f) { return layout; } const float buttonWidth = ClampNonNegative(metrics.buttonWidth); const float buttonHeight = ClampNonNegative(metrics.buttonHeight); const float buttonGap = ClampNonNegative(metrics.buttonGap); const float groupPaddingX = ClampNonNegative(metrics.groupPaddingX); const float groupPaddingY = ClampNonNegative(metrics.groupPaddingY); const float totalButtonWidth = buttonWidth * static_cast(buttons.size()) + buttonGap * static_cast((std::max)(std::ptrdiff_t(0), static_cast(buttons.size()) - 1)); const float groupWidth = totalButtonWidth + groupPaddingX * 2.0f; const float groupHeight = (std::min)(bounds.height, buttonHeight + groupPaddingY * 2.0f); layout.groupRect = UIRect( bounds.x + (std::max)(0.0f, (bounds.width - groupWidth) * 0.5f), bounds.y + (std::max)(0.0f, (bounds.height - groupHeight) * 0.5f), (std::min)(groupWidth, bounds.width), groupHeight); const float buttonY = layout.groupRect.y + (std::max)(0.0f, (layout.groupRect.height - buttonHeight) * 0.5f); float buttonX = layout.groupRect.x + groupPaddingX; layout.buttonRects.reserve(buttons.size()); for (std::size_t index = 0; index < buttons.size(); ++index) { layout.buttonRects.push_back(UIRect(buttonX, buttonY, buttonWidth, buttonHeight)); buttonX += buttonWidth + buttonGap; } return layout; } void AppendPlayGlyph( ::XCEngine::UI::UIDrawList& drawList, const UIRect& rect, const UIColor& color, float thickness) { const UIPoint a(rect.x + rect.width * 0.38f, rect.y + rect.height * 0.27f); const UIPoint b(rect.x + rect.width * 0.38f, rect.y + rect.height * 0.73f); const UIPoint c(rect.x + rect.width * 0.70f, rect.y + rect.height * 0.50f); drawList.AddLine(a, b, color, thickness); drawList.AddLine(a, c, color, thickness); drawList.AddLine(b, c, color, thickness); } void AppendPauseGlyph( ::XCEngine::UI::UIDrawList& drawList, const UIRect& rect, const UIColor& color, float thickness) { const float top = rect.y + rect.height * 0.26f; const float bottom = rect.y + rect.height * 0.74f; const float left = rect.x + rect.width * 0.40f; const float right = rect.x + rect.width * 0.60f; drawList.AddLine(UIPoint(left, top), UIPoint(left, bottom), color, thickness); drawList.AddLine(UIPoint(right, top), UIPoint(right, bottom), color, thickness); } void AppendStepGlyph( ::XCEngine::UI::UIDrawList& drawList, const UIRect& rect, const UIColor& color, float thickness) { const UIPoint a(rect.x + rect.width * 0.30f, rect.y + rect.height * 0.27f); const UIPoint b(rect.x + rect.width * 0.30f, rect.y + rect.height * 0.73f); const UIPoint c(rect.x + rect.width * 0.58f, rect.y + rect.height * 0.50f); const float barX = rect.x + rect.width * 0.70f; drawList.AddLine(a, b, color, thickness); drawList.AddLine(a, c, color, thickness); drawList.AddLine(b, c, color, thickness); drawList.AddLine( UIPoint(barX, rect.y + rect.height * 0.25f), UIPoint(barX, rect.y + rect.height * 0.75f), color, thickness); } void AppendToolbarGlyph( ::XCEngine::UI::UIDrawList& drawList, const UIRect& rect, UIEditorShellToolbarGlyph glyph, const UIColor& color, float thickness) { switch (glyph) { case UIEditorShellToolbarGlyph::Play: AppendPlayGlyph(drawList, rect, color, thickness); break; case UIEditorShellToolbarGlyph::Pause: AppendPauseGlyph(drawList, rect, color, thickness); break; case UIEditorShellToolbarGlyph::Step: AppendStepGlyph(drawList, rect, color, thickness); break; default: break; } } void AppendUIEditorShellToolbar( ::XCEngine::UI::UIDrawList& drawList, const UIEditorShellToolbarLayout& layout, const std::vector& buttons, const UIEditorShellToolbarPalette& palette, const UIEditorShellToolbarMetrics& metrics) { if (layout.bounds.width <= 0.0f || layout.bounds.height <= 0.0f) { return; } drawList.AddFilledRect(layout.bounds, palette.barColor); drawList.AddLine( UIPoint(layout.bounds.x, layout.bounds.y + layout.bounds.height - 1.0f), UIPoint(layout.bounds.x + layout.bounds.width, layout.bounds.y + layout.bounds.height - 1.0f), palette.groupBorderColor, metrics.borderThickness); if (buttons.empty() || layout.groupRect.width <= 0.0f || layout.groupRect.height <= 0.0f) { return; } drawList.AddFilledRect( layout.groupRect, palette.groupColor, metrics.groupCornerRounding); drawList.AddRectOutline( layout.groupRect, palette.groupBorderColor, metrics.borderThickness, metrics.groupCornerRounding); const std::size_t buttonCount = (std::min)(buttons.size(), layout.buttonRects.size()); for (std::size_t index = 0; index < buttonCount; ++index) { const UIRect& buttonRect = layout.buttonRects[index]; drawList.AddFilledRect( buttonRect, palette.buttonColor, metrics.buttonCornerRounding); drawList.AddRectOutline( buttonRect, palette.buttonBorderColor, metrics.borderThickness, metrics.buttonCornerRounding); AppendToolbarGlyph( drawList, buttonRect, buttons[index].glyph, palette.iconColor, metrics.iconThickness); } } } // namespace UIEditorShellComposeLayout BuildUIEditorShellComposeLayout( const UIRect& bounds, const std::vector& menuBarItems, const std::vector& toolbarButtons, const std::vector& statusSegments, const UIEditorShellComposeMetrics& metrics) { UIEditorShellComposeLayout layout = {}; layout.bounds = UIRect( bounds.x, bounds.y, ClampNonNegative(bounds.width), ClampNonNegative(bounds.height)); layout.contentRect = InsetRect(layout.bounds, metrics.outerPadding); const float menuBarHeight = ClampNonNegative(metrics.menuBarMetrics.barHeight); const float toolbarHeight = ClampNonNegative(metrics.toolbarMetrics.barHeight); const float statusBarHeight = ClampNonNegative(metrics.statusBarMetrics.barHeight); const float sectionGap = ClampNonNegative(metrics.sectionGap); const bool hasMenuBar = menuBarHeight > 0.0f && !menuBarItems.empty(); const bool hasToolbar = toolbarHeight > 0.0f && !toolbarButtons.empty(); const bool hasStatusBar = statusBarHeight > 0.0f && !statusSegments.empty(); float gapCount = 0.0f; if (hasMenuBar) { gapCount += 1.0f; } if (hasToolbar) { gapCount += 1.0f; } if (hasStatusBar) { gapCount += 1.0f; } const float availableHeight = layout.contentRect.height; const float consumedHeight = (hasMenuBar ? menuBarHeight : 0.0f) + (hasToolbar ? toolbarHeight : 0.0f) + (hasStatusBar ? statusBarHeight : 0.0f) + sectionGap * gapCount; const float workspaceHeight = (std::max)(0.0f, availableHeight - consumedHeight); float cursorY = layout.contentRect.y; if (hasMenuBar) { layout.menuBarRect = UIRect( layout.contentRect.x, cursorY, layout.contentRect.width, menuBarHeight); cursorY += menuBarHeight + sectionGap; } if (hasToolbar) { layout.toolbarRect = UIRect( layout.contentRect.x, cursorY, layout.contentRect.width, toolbarHeight); cursorY += toolbarHeight + sectionGap; } layout.workspaceRect = UIRect( layout.contentRect.x, cursorY, layout.contentRect.width, workspaceHeight); if (hasStatusBar) { layout.statusBarRect = UIRect( layout.contentRect.x, layout.contentRect.y + layout.contentRect.height - statusBarHeight, layout.contentRect.width, statusBarHeight); } layout.menuBarLayout = BuildUIEditorMenuBarLayout(layout.menuBarRect, menuBarItems, metrics.menuBarMetrics); layout.toolbarLayout = BuildUIEditorShellToolbarLayout(layout.toolbarRect, toolbarButtons, metrics.toolbarMetrics); layout.statusBarLayout = BuildUIEditorStatusBarLayout(layout.statusBarRect, statusSegments, metrics.statusBarMetrics); return layout; } UIEditorShellComposeRequest ResolveUIEditorShellComposeRequest( const UIRect& bounds, const UIEditorPanelRegistry& panelRegistry, const UIEditorWorkspaceModel& workspace, const UIEditorWorkspaceSession& session, const UIEditorShellComposeModel& model, const Widgets::UIEditorDockHostState& dockHostState, const UIEditorShellComposeState& state, const UIEditorShellComposeMetrics& metrics) { UIEditorShellComposeRequest request = {}; request.layout = BuildUIEditorShellComposeLayout( bounds, model.menuBarItems, model.toolbarButtons, model.statusSegments, metrics); request.workspaceRequest = ResolveUIEditorWorkspaceComposeRequest( request.layout.workspaceRect, panelRegistry, workspace, session, model.workspacePresentations, dockHostState, metrics.dockHostMetrics, metrics.viewportMetrics); request.layout.menuBarLayout = BuildUIEditorMenuBarLayout( request.layout.menuBarRect, model.menuBarItems, metrics.menuBarMetrics); request.layout.toolbarLayout = BuildUIEditorShellToolbarLayout( request.layout.toolbarRect, model.toolbarButtons, metrics.toolbarMetrics); request.layout.statusBarLayout = BuildUIEditorStatusBarLayout( request.layout.statusBarRect, model.statusSegments, metrics.statusBarMetrics); (void)state; return request; } UIEditorShellComposeFrame UpdateUIEditorShellCompose( UIEditorShellComposeState& state, const UIRect& bounds, const UIEditorPanelRegistry& panelRegistry, const UIEditorWorkspaceModel& workspace, const UIEditorWorkspaceSession& session, const UIEditorShellComposeModel& model, const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, const Widgets::UIEditorDockHostState& dockHostState, const UIEditorShellComposeMetrics& metrics) { UIEditorShellComposeFrame frame = {}; frame.layout = BuildUIEditorShellComposeLayout( bounds, model.menuBarItems, model.toolbarButtons, model.statusSegments, metrics); frame.workspaceFrame = UpdateUIEditorWorkspaceCompose( state.workspaceState, frame.layout.workspaceRect, panelRegistry, workspace, session, model.workspacePresentations, inputEvents, dockHostState, metrics.dockHostMetrics, metrics.viewportMetrics); frame.layout.menuBarLayout = BuildUIEditorMenuBarLayout( frame.layout.menuBarRect, model.menuBarItems, metrics.menuBarMetrics); frame.layout.toolbarLayout = BuildUIEditorShellToolbarLayout( frame.layout.toolbarRect, model.toolbarButtons, metrics.toolbarMetrics); frame.layout.statusBarLayout = BuildUIEditorStatusBarLayout( frame.layout.statusBarRect, model.statusSegments, metrics.statusBarMetrics); return frame; } void AppendUIEditorShellCompose( ::XCEngine::UI::UIDrawList& drawList, const UIEditorShellComposeFrame& frame, const UIEditorShellComposeModel& model, const UIEditorShellComposeState& state, const UIEditorShellComposePalette& palette, const UIEditorShellComposeMetrics& metrics) { drawList.AddFilledRect( frame.layout.bounds, palette.surfaceColor, metrics.surfaceCornerRounding); drawList.AddRectOutline( frame.layout.bounds, palette.surfaceBorderColor, 1.0f, metrics.surfaceCornerRounding); AppendUIEditorMenuBarBackground( drawList, frame.layout.menuBarLayout, model.menuBarItems, state.menuBarState, palette.menuBarPalette, metrics.menuBarMetrics); AppendUIEditorMenuBarForeground( drawList, frame.layout.menuBarLayout, model.menuBarItems, state.menuBarState, palette.menuBarPalette, metrics.menuBarMetrics); AppendUIEditorShellToolbar( drawList, frame.layout.toolbarLayout, model.toolbarButtons, palette.toolbarPalette, metrics.toolbarMetrics); AppendUIEditorWorkspaceCompose( drawList, frame.workspaceFrame, palette.dockHostPalette, metrics.dockHostMetrics, palette.viewportPalette, metrics.viewportMetrics); AppendUIEditorStatusBarBackground( drawList, frame.layout.statusBarLayout, model.statusSegments, state.statusBarState, palette.statusBarPalette, metrics.statusBarMetrics); AppendUIEditorStatusBarForeground( drawList, frame.layout.statusBarLayout, model.statusSegments, state.statusBarState, palette.statusBarPalette, metrics.statusBarMetrics); } } // namespace XCEngine::UI::Editor