Stabilize XCEditor shell foundation widgets

This commit is contained in:
2026-04-07 01:42:02 +08:00
parent 998df9013a
commit 3b2a05a098
20 changed files with 4118 additions and 23 deletions

View File

@@ -0,0 +1,102 @@
#pragma once
#include <XCEngine/UI/Input/UIInputPath.h>
#include <XCEngine/UI/Widgets/UIPopupOverlayModel.h>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorMenuPopupState {
std::string popupId = {};
std::string menuId = {};
std::string itemId = {};
[[nodiscard]] bool IsRootPopup() const {
return itemId.empty();
}
};
struct UIEditorMenuSessionMutationResult {
bool changed = false;
std::string openRootMenuId = {};
std::string openedPopupId = {};
std::vector<std::string> closedPopupIds = {};
Widgets::UIPopupDismissReason dismissReason =
Widgets::UIPopupDismissReason::None;
[[nodiscard]] bool HasOpenMenu() const {
return !openRootMenuId.empty();
}
};
class UIEditorMenuSession {
public:
const Widgets::UIPopupOverlayModel& GetPopupOverlayModel() const {
return m_popupOverlayModel;
}
const std::vector<UIEditorMenuPopupState>& GetPopupStates() const {
return m_popupStates;
}
const std::vector<std::string>& GetOpenSubmenuItemIds() const {
return m_openSubmenuItemIds;
}
std::string_view GetOpenRootMenuId() const {
return m_openRootMenuId;
}
[[nodiscard]] bool HasOpenMenu() const {
return !m_openRootMenuId.empty();
}
[[nodiscard]] bool IsMenuOpen(std::string_view menuId) const {
return !m_openRootMenuId.empty() && m_openRootMenuId == menuId;
}
[[nodiscard]] bool IsPopupOpen(std::string_view popupId) const;
const UIEditorMenuPopupState* FindPopupState(std::string_view popupId) const;
void Reset();
UIEditorMenuSessionMutationResult OpenMenuBarRoot(
std::string_view menuId,
Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult HoverMenuBarRoot(
std::string_view menuId,
Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult HoverSubmenu(
std::string_view itemId,
Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult CloseAll(
Widgets::UIPopupDismissReason dismissReason =
Widgets::UIPopupDismissReason::Programmatic);
UIEditorMenuSessionMutationResult DismissFromEscape();
UIEditorMenuSessionMutationResult DismissFromPointerDown(
const UIInputPath& hitPath);
UIEditorMenuSessionMutationResult DismissFromFocusLoss(
const UIInputPath& focusedPath);
private:
UIEditorMenuSessionMutationResult BuildResult(
const Widgets::UIPopupOverlayMutationResult& mutation) const;
void RemoveClosedPopupStates(const std::vector<std::string>& closedPopupIds);
void RebuildDerivedState();
std::string m_openRootMenuId = {};
Widgets::UIPopupOverlayModel m_popupOverlayModel = {};
std::vector<UIEditorMenuPopupState> m_popupStates = {};
std::vector<std::string> m_openSubmenuItemIds = {};
};
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,160 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string_view>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorPanelFrameAction : std::uint8_t {
None = 0,
Pin,
Close
};
enum class UIEditorPanelFrameHitTarget : std::uint8_t {
None = 0,
Header,
Body,
Footer,
PinButton,
CloseButton
};
struct UIEditorPanelFrameState {
bool active = false;
bool hovered = false;
bool focused = false;
bool pinned = false;
bool closable = true;
bool pinnable = true;
bool showFooter = false;
bool pinHovered = false;
bool closeHovered = false;
};
struct UIEditorPanelFrameText {
std::string_view title = {};
std::string_view subtitle = {};
std::string_view footer = {};
};
struct UIEditorPanelFrameMetrics {
float cornerRounding = 10.0f;
float headerHeight = 36.0f;
float footerHeight = 24.0f;
float contentPadding = 12.0f;
float titleInsetX = 14.0f;
float titleInsetY = 9.0f;
float subtitleInsetY = 22.0f;
float footerInsetX = 14.0f;
float footerInsetY = 6.0f;
float actionButtonExtent = 18.0f;
float actionInsetX = 12.0f;
float actionGap = 6.0f;
float baseBorderThickness = 1.0f;
float hoveredBorderThickness = 1.25f;
float activeBorderThickness = 1.5f;
float focusedBorderThickness = 2.0f;
};
struct UIEditorPanelFramePalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.16f, 0.16f, 0.16f, 1.0f);
::XCEngine::UI::UIColor headerColor =
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f);
::XCEngine::UI::UIColor footerColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.30f, 0.30f, 0.30f, 1.0f);
::XCEngine::UI::UIColor hoveredBorderColor =
::XCEngine::UI::UIColor(0.42f, 0.42f, 0.42f, 1.0f);
::XCEngine::UI::UIColor activeBorderColor =
::XCEngine::UI::UIColor(0.58f, 0.58f, 0.58f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(0.71f, 0.71f, 0.71f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor actionButtonColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor actionButtonHoveredColor =
::XCEngine::UI::UIColor(0.34f, 0.34f, 0.34f, 1.0f);
::XCEngine::UI::UIColor actionButtonSelectedColor =
::XCEngine::UI::UIColor(0.48f, 0.48f, 0.48f, 1.0f);
::XCEngine::UI::UIColor actionButtonBorderColor =
::XCEngine::UI::UIColor(0.52f, 0.52f, 0.52f, 1.0f);
::XCEngine::UI::UIColor actionGlyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
};
struct UIEditorPanelFrameLayout {
::XCEngine::UI::UIRect frameRect = {};
::XCEngine::UI::UIRect headerRect = {};
::XCEngine::UI::UIRect bodyRect = {};
::XCEngine::UI::UIRect footerRect = {};
::XCEngine::UI::UIRect pinButtonRect = {};
::XCEngine::UI::UIRect closeButtonRect = {};
bool hasFooter = false;
bool showPinButton = false;
bool showCloseButton = false;
};
bool IsUIEditorPanelFramePointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
bool IsUIEditorPanelFramePinButtonVisible(const UIEditorPanelFrameState& state);
bool IsUIEditorPanelFrameCloseButtonVisible(const UIEditorPanelFrameState& state);
UIEditorPanelFrameLayout BuildUIEditorPanelFrameLayout(
const ::XCEngine::UI::UIRect& frameRect,
const UIEditorPanelFrameState& state,
const UIEditorPanelFrameMetrics& metrics = {});
::XCEngine::UI::UIColor ResolveUIEditorPanelFrameBorderColor(
const UIEditorPanelFrameState& state,
const UIEditorPanelFramePalette& palette = {});
float ResolveUIEditorPanelFrameBorderThickness(
const UIEditorPanelFrameState& state,
const UIEditorPanelFrameMetrics& metrics = {});
UIEditorPanelFrameAction HitTestUIEditorPanelFrameAction(
const UIEditorPanelFrameLayout& layout,
const UIEditorPanelFrameState& state,
const ::XCEngine::UI::UIPoint& point);
UIEditorPanelFrameHitTarget HitTestUIEditorPanelFrame(
const UIEditorPanelFrameLayout& layout,
const UIEditorPanelFrameState& state,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorPanelFrameBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorPanelFrameLayout& layout,
const UIEditorPanelFrameState& state,
const UIEditorPanelFramePalette& palette = {},
const UIEditorPanelFrameMetrics& metrics = {});
void AppendUIEditorPanelFrameForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorPanelFrameLayout& layout,
const UIEditorPanelFrameState& state,
const UIEditorPanelFrameText& text,
const UIEditorPanelFramePalette& palette = {},
const UIEditorPanelFrameMetrics& metrics = {});
void AppendUIEditorPanelFrame(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& frameRect,
const UIEditorPanelFrameState& state,
const UIEditorPanelFrameText& text,
const UIEditorPanelFramePalette& palette = {},
const UIEditorPanelFrameMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,157 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Layout/UITabStripLayout.h>
#include <XCEngine/UI/Widgets/UITabStripModel.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorTabStripInvalidIndex =
::XCEngine::UI::Widgets::UITabStripModel::InvalidIndex;
struct UIEditorTabStripItem {
std::string tabId = {};
std::string title = {};
bool closable = true;
float desiredHeaderLabelWidth = 0.0f;
};
struct UIEditorTabStripState {
std::size_t selectedIndex = UIEditorTabStripInvalidIndex;
std::size_t hoveredIndex = UIEditorTabStripInvalidIndex;
std::size_t closeHoveredIndex = UIEditorTabStripInvalidIndex;
bool focused = false;
};
struct UIEditorTabStripMetrics {
::XCEngine::UI::Layout::UITabStripMetrics layoutMetrics =
::XCEngine::UI::Layout::UITabStripMetrics{ 32.0f, 88.0f, 12.0f, 1.0f };
float estimatedGlyphWidth = 7.0f;
float closeButtonExtent = 14.0f;
float closeButtonGap = 8.0f;
float closeInsetRight = 12.0f;
float closeInsetY = 0.0f;
float labelInsetX = 12.0f;
float labelInsetY = -1.0f;
float baseBorderThickness = 1.0f;
float selectedBorderThickness = 1.5f;
float focusedBorderThickness = 2.0f;
};
struct UIEditorTabStripPalette {
::XCEngine::UI::UIColor stripBackgroundColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor headerBackgroundColor =
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f);
::XCEngine::UI::UIColor contentBackgroundColor =
::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f);
::XCEngine::UI::UIColor stripBorderColor =
::XCEngine::UI::UIColor(0.30f, 0.30f, 0.30f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
::XCEngine::UI::UIColor tabColor =
::XCEngine::UI::UIColor(0.23f, 0.23f, 0.23f, 1.0f);
::XCEngine::UI::UIColor tabHoveredColor =
::XCEngine::UI::UIColor(0.27f, 0.27f, 0.27f, 1.0f);
::XCEngine::UI::UIColor tabSelectedColor =
::XCEngine::UI::UIColor(0.29f, 0.29f, 0.29f, 1.0f);
::XCEngine::UI::UIColor tabBorderColor =
::XCEngine::UI::UIColor(0.32f, 0.32f, 0.32f, 1.0f);
::XCEngine::UI::UIColor tabHoveredBorderColor =
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f);
::XCEngine::UI::UIColor tabSelectedBorderColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(0.76f, 0.76f, 0.76f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor closeButtonColor =
::XCEngine::UI::UIColor(0.25f, 0.25f, 0.25f, 1.0f);
::XCEngine::UI::UIColor closeButtonHoveredColor =
::XCEngine::UI::UIColor(0.36f, 0.36f, 0.36f, 1.0f);
::XCEngine::UI::UIColor closeButtonBorderColor =
::XCEngine::UI::UIColor(0.44f, 0.44f, 0.44f, 1.0f);
::XCEngine::UI::UIColor closeGlyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
};
struct UIEditorTabStripLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect headerRect = {};
::XCEngine::UI::UIRect contentRect = {};
std::vector<::XCEngine::UI::UIRect> tabHeaderRects = {};
std::vector<::XCEngine::UI::UIRect> closeButtonRects = {};
std::vector<bool> showCloseButtons = {};
std::size_t selectedIndex = UIEditorTabStripInvalidIndex;
};
enum class UIEditorTabStripHitTargetKind : std::uint8_t {
None = 0,
HeaderBackground,
Tab,
CloseButton,
Content
};
struct UIEditorTabStripHitTarget {
UIEditorTabStripHitTargetKind kind = UIEditorTabStripHitTargetKind::None;
std::size_t index = UIEditorTabStripInvalidIndex;
};
float ResolveUIEditorTabStripDesiredHeaderLabelWidth(
const UIEditorTabStripItem& item,
const UIEditorTabStripMetrics& metrics = {});
std::size_t ResolveUIEditorTabStripSelectedIndex(
const std::vector<UIEditorTabStripItem>& items,
std::string_view selectedTabId,
std::size_t fallbackIndex = UIEditorTabStripInvalidIndex);
std::size_t ResolveUIEditorTabStripSelectedIndexAfterClose(
std::size_t selectedIndex,
std::size_t closedIndex,
std::size_t itemCountBeforeClose);
UIEditorTabStripLayout BuildUIEditorTabStripLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorTabStripItem>& items,
const UIEditorTabStripState& state,
const UIEditorTabStripMetrics& metrics = {});
UIEditorTabStripHitTarget HitTestUIEditorTabStrip(
const UIEditorTabStripLayout& layout,
const UIEditorTabStripState& state,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorTabStripBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorTabStripLayout& layout,
const UIEditorTabStripState& state,
const UIEditorTabStripPalette& palette = {},
const UIEditorTabStripMetrics& metrics = {});
void AppendUIEditorTabStripForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorTabStripLayout& layout,
const std::vector<UIEditorTabStripItem>& items,
const UIEditorTabStripState& state,
const UIEditorTabStripPalette& palette = {},
const UIEditorTabStripMetrics& metrics = {});
void AppendUIEditorTabStrip(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorTabStripItem>& items,
const UIEditorTabStripState& state,
const UIEditorTabStripPalette& palette = {},
const UIEditorTabStripMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets