关键节点

This commit is contained in:
2026-04-25 16:46:01 +08:00
parent 6002d86a7e
commit ef41c44464
516 changed files with 6175 additions and 12401 deletions

View File

@@ -0,0 +1,129 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEngine/UI/DrawData.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorMenuBarInvalidIndex =
static_cast<std::size_t>(-1);
struct UIEditorMenuBarItem {
std::string menuId = {};
std::string label = {};
bool enabled = true;
// Pure measured label width. Button padding is applied only in layout.
float measuredLabelWidth = 0.0f;
};
struct UIEditorMenuBarState {
std::size_t openIndex = UIEditorMenuBarInvalidIndex;
std::size_t hoveredIndex = UIEditorMenuBarInvalidIndex;
bool focused = false;
};
struct UIEditorMenuBarMetrics {
float barHeight = 24.0f;
float horizontalInset = 0.0f;
float verticalInset = 2.0f;
float buttonGap = 1.0f;
float buttonPaddingX = 7.0f;
float labelFontSize = 13.0f;
float estimatedGlyphWidth = 6.5f;
float labelInsetY = -1.5f;
float barCornerRounding = 0.0f;
float buttonCornerRounding = 0.75f;
float baseBorderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
float openBorderThickness = 1.0f;
};
struct UIEditorMenuBarPalette {
::XCEngine::UI::UIColor barColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor buttonColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor buttonHoveredColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor buttonOpenColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor openBorderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor textDisabled =
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f);
};
struct UIEditorMenuBarLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect contentRect = {};
std::vector<::XCEngine::UI::UIRect> buttonRects = {};
};
enum class UIEditorMenuBarHitTargetKind : std::uint8_t {
None = 0,
BarBackground,
Button
};
struct UIEditorMenuBarHitTarget {
UIEditorMenuBarHitTargetKind kind = UIEditorMenuBarHitTargetKind::None;
std::size_t index = UIEditorMenuBarInvalidIndex;
};
float ResolveUIEditorMenuBarMeasuredLabelWidth(
const UIEditorMenuBarItem& item,
const UIEditorMenuBarMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
float ResolveUIEditorMenuBarDesiredButtonWidth(
const UIEditorMenuBarItem& item,
const UIEditorMenuBarMetrics& metrics = {});
UIEditorMenuBarLayout BuildUIEditorMenuBarLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorMenuBarItem>& items,
const UIEditorMenuBarMetrics& metrics = {});
UIEditorMenuBarHitTarget HitTestUIEditorMenuBar(
const UIEditorMenuBarLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorMenuBarBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorMenuBarLayout& layout,
const std::vector<UIEditorMenuBarItem>& items,
const UIEditorMenuBarState& state,
const UIEditorMenuBarPalette& palette = {},
const UIEditorMenuBarMetrics& metrics = {});
void AppendUIEditorMenuBarForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorMenuBarLayout& layout,
const std::vector<UIEditorMenuBarItem>& items,
const UIEditorMenuBarState& state,
const UIEditorMenuBarPalette& palette = {},
const UIEditorMenuBarMetrics& metrics = {});
void AppendUIEditorMenuBar(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorMenuBarItem>& items,
const UIEditorMenuBarState& state,
const UIEditorMenuBarPalette& palette = {},
const UIEditorMenuBarMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,116 @@
#pragma once
#include <XCEditor/Foundation/UIEditorCommandDispatcher.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
class UIEditorShortcutManager;
enum class UIEditorMenuItemKind : std::uint8_t {
Command = 0,
Separator,
Submenu
};
enum class UIEditorMenuCheckedStateSource : std::uint8_t {
None = 0,
PanelOpen,
PanelVisible,
PanelActive
};
struct UIEditorMenuCheckedStateBinding {
UIEditorMenuCheckedStateSource source = UIEditorMenuCheckedStateSource::None;
std::string panelId = {};
};
struct UIEditorMenuItemDescriptor {
UIEditorMenuItemKind kind = UIEditorMenuItemKind::Command;
std::string itemId = {};
std::string label = {};
std::string commandId = {};
UIEditorMenuCheckedStateBinding checkedState = {};
std::vector<UIEditorMenuItemDescriptor> children = {};
};
struct UIEditorMenuDescriptor {
std::string menuId = {};
std::string label = {};
std::vector<UIEditorMenuItemDescriptor> items = {};
};
struct UIEditorMenuModel {
std::vector<UIEditorMenuDescriptor> menus = {};
};
enum class UIEditorMenuModelValidationCode : std::uint8_t {
None = 0,
InvalidCommandRegistry,
EmptyMenuId,
EmptyMenuLabel,
DuplicateMenuId,
EmptyCommandId,
UnknownCommandId,
MissingItemLabel,
CommandItemHasChildren,
SubmenuMissingLabel,
SubmenuEmptyChildren,
SubmenuHasCommandId,
SeparatorHasCommandId,
SeparatorHasChildren,
UnexpectedCheckedState,
MissingCheckedStatePanelId
};
struct UIEditorMenuModelValidationResult {
UIEditorMenuModelValidationCode code = UIEditorMenuModelValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorMenuModelValidationCode::None;
}
};
struct UIEditorResolvedMenuItem {
UIEditorMenuItemKind kind = UIEditorMenuItemKind::Command;
std::string itemId = {};
std::string label = {};
std::string commandId = {};
std::string commandDisplayName = {};
std::string shortcutText = {};
bool enabled = true;
bool checked = false;
UIEditorWorkspaceCommandStatus previewStatus =
UIEditorWorkspaceCommandStatus::Rejected;
std::string message = {};
std::vector<UIEditorResolvedMenuItem> children = {};
};
struct UIEditorResolvedMenuDescriptor {
std::string menuId = {};
std::string label = {};
std::vector<UIEditorResolvedMenuItem> items = {};
};
struct UIEditorResolvedMenuModel {
std::vector<UIEditorResolvedMenuDescriptor> menus = {};
};
std::string_view GetUIEditorMenuItemKindName(UIEditorMenuItemKind kind);
UIEditorMenuModelValidationResult ValidateUIEditorMenuModel(
const UIEditorMenuModel& model,
const UIEditorCommandRegistry& commandRegistry);
UIEditorResolvedMenuModel BuildUIEditorResolvedMenuModel(
const UIEditorMenuModel& model,
const UIEditorCommandDispatcher& commandDispatcher,
const UIEditorWorkspaceController& controller,
const UIEditorShortcutManager* shortcutManager = nullptr);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,148 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Menu/UIEditorMenuModel.h>
#include <XCEngine/UI/DrawData.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorMenuPopupInvalidIndex =
static_cast<std::size_t>(-1);
struct UIEditorMenuPopupItem {
std::string itemId = {};
::XCEngine::UI::Editor::UIEditorMenuItemKind kind =
::XCEngine::UI::Editor::UIEditorMenuItemKind::Command;
std::string label = {};
std::string shortcutText = {};
bool enabled = true;
bool checked = false;
bool hasSubmenu = false;
// Pure measured text widths. Row/popup sizing is derived from them locally.
float measuredLabelWidth = 0.0f;
float measuredShortcutWidth = 0.0f;
};
struct UIEditorMenuPopupState {
std::size_t hoveredIndex = UIEditorMenuPopupInvalidIndex;
std::size_t submenuOpenIndex = UIEditorMenuPopupInvalidIndex;
bool focused = false;
};
struct UIEditorMenuPopupMetrics {
float contentPaddingX = 2.0f;
float contentPaddingY = 3.0f;
float itemHeight = 24.0f;
float separatorHeight = 7.0f;
float checkColumnWidth = 12.0f;
float shortcutGap = 14.0f;
float submenuIndicatorWidth = 12.0f;
float rowCornerRounding = 2.5f;
float popupCornerRounding = 4.0f;
float labelInsetX = 4.0f;
float labelInsetY = -1.0f;
float labelFontSize = 13.0f;
float shortcutInsetRight = 8.0f;
float estimatedGlyphWidth = 7.0f;
float glyphFontSize = 12.0f;
float separatorThickness = 1.0f;
float borderThickness = 1.0f;
};
struct UIEditorMenuPopupPalette {
::XCEngine::UI::UIColor popupColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor itemHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor itemOpenColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor separatorColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor textDisabled =
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f);
::XCEngine::UI::UIColor glyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
};
struct UIEditorMenuPopupLayout {
::XCEngine::UI::UIRect popupRect = {};
::XCEngine::UI::UIRect contentRect = {};
std::vector<::XCEngine::UI::UIRect> itemRects = {};
};
enum class UIEditorMenuPopupHitTargetKind : std::uint8_t {
None = 0,
PopupSurface,
Item
};
struct UIEditorMenuPopupHitTarget {
UIEditorMenuPopupHitTargetKind kind = UIEditorMenuPopupHitTargetKind::None;
std::size_t index = UIEditorMenuPopupInvalidIndex;
};
float ResolveUIEditorMenuPopupMeasuredLabelWidth(
const UIEditorMenuPopupItem& item,
const UIEditorMenuPopupMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
float ResolveUIEditorMenuPopupMeasuredShortcutWidth(
const UIEditorMenuPopupItem& item,
const UIEditorMenuPopupMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
float ResolveUIEditorMenuPopupDesiredWidth(
const std::vector<UIEditorMenuPopupItem>& items,
const UIEditorMenuPopupMetrics& metrics = {});
float MeasureUIEditorMenuPopupHeight(
const std::vector<UIEditorMenuPopupItem>& items,
const UIEditorMenuPopupMetrics& metrics = {});
UIEditorMenuPopupLayout BuildUIEditorMenuPopupLayout(
const ::XCEngine::UI::UIRect& popupRect,
const std::vector<UIEditorMenuPopupItem>& items,
const UIEditorMenuPopupMetrics& metrics = {});
UIEditorMenuPopupHitTarget HitTestUIEditorMenuPopup(
const UIEditorMenuPopupLayout& layout,
const std::vector<UIEditorMenuPopupItem>& items,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorMenuPopupBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorMenuPopupLayout& layout,
const std::vector<UIEditorMenuPopupItem>& items,
const UIEditorMenuPopupState& state,
const UIEditorMenuPopupPalette& palette = {},
const UIEditorMenuPopupMetrics& metrics = {});
void AppendUIEditorMenuPopupForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorMenuPopupLayout& layout,
const std::vector<UIEditorMenuPopupItem>& items,
const UIEditorMenuPopupState& state,
const UIEditorMenuPopupPalette& palette = {},
const UIEditorMenuPopupMetrics& metrics = {});
void AppendUIEditorMenuPopup(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& popupRect,
const std::vector<UIEditorMenuPopupItem>& items,
const UIEditorMenuPopupState& state,
const UIEditorMenuPopupPalette& palette = {},
const UIEditorMenuPopupMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,106 @@
#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 = {};
::XCEngine::UI::Widgets::UIPopupDismissReason dismissReason =
::XCEngine::UI::Widgets::UIPopupDismissReason::None;
[[nodiscard]] bool HasOpenMenu() const {
return !openRootMenuId.empty();
}
};
class UIEditorMenuSession {
public:
const ::XCEngine::UI::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 OpenRootMenu(
std::string_view menuId,
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult OpenMenuBarRoot(
std::string_view menuId,
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult HoverMenuBarRoot(
std::string_view menuId,
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult HoverSubmenu(
std::string_view itemId,
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry);
UIEditorMenuSessionMutationResult CloseAll(
::XCEngine::UI::Widgets::UIPopupDismissReason dismissReason =
::XCEngine::UI::Widgets::UIPopupDismissReason::Programmatic);
UIEditorMenuSessionMutationResult DismissFromEscape();
UIEditorMenuSessionMutationResult DismissFromPointerDown(
const UIInputPath& hitPath);
UIEditorMenuSessionMutationResult DismissFromFocusLoss(
const UIInputPath& focusedPath);
private:
UIEditorMenuSessionMutationResult BuildResult(
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult& mutation) const;
void RemoveClosedPopupStates(const std::vector<std::string>& closedPopupIds);
void RebuildDerivedState();
std::string m_openRootMenuId = {};
::XCEngine::UI::Widgets::UIPopupOverlayModel m_popupOverlayModel = {};
std::vector<UIEditorMenuPopupState> m_popupStates = {};
std::vector<std::string> m_openSubmenuItemIds = {};
};
} // namespace XCEngine::UI::Editor