关键节点

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,242 @@
#pragma once
#include <XCEngine/UI/Types.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor::Collections::DragDropInteraction {
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIPointerButton;
inline constexpr float kDefaultDragThreshold = 4.0f;
struct State {
std::string armedItemId = {};
std::string draggedItemId = {};
std::string dropTargetItemId = {};
UIPoint pressPosition = {};
bool armed = false;
bool dragging = false;
bool validDropTarget = false;
bool requestPointerCapture = false;
bool requestPointerRelease = false;
};
struct PreviewState {
std::string armedItemId = {};
UIPoint pressPosition = {};
bool armed = false;
bool dragging = false;
};
struct ProcessResult {
bool selectionForced = false;
bool dropCommitted = false;
std::string draggedItemId = {};
std::string dropTargetItemId = {};
};
template <typename StateLike>
inline void ResetTransientRequests(StateLike& state) {
state.requestPointerCapture = false;
state.requestPointerRelease = false;
}
template <typename StateLike>
inline bool HasActivePointerCapture(const StateLike& state) {
return state.dragging;
}
inline float ComputeSquaredDistance(const UIPoint& lhs, const UIPoint& rhs) {
const float dx = lhs.x - rhs.x;
const float dy = lhs.y - rhs.y;
return dx * dx + dy * dy;
}
template <typename StateLike>
inline PreviewState BuildPreviewState(const StateLike& state) {
PreviewState preview = {};
preview.armedItemId = state.armedItemId;
preview.pressPosition = state.pressPosition;
preview.armed = state.armed;
preview.dragging = state.dragging;
return preview;
}
template <typename ResolveDraggableItem>
inline bool ProcessPreviewEvent(
PreviewState& preview,
const UIInputEvent& event,
ResolveDraggableItem&& resolveDraggableItem,
float dragThreshold = kDefaultDragThreshold) {
bool suppress = false;
switch (event.type) {
case UIInputEventType::PointerButtonDown:
if (event.pointerButton == UIPointerButton::Left) {
preview.armedItemId = resolveDraggableItem(event.position);
preview.pressPosition = event.position;
preview.armed = !preview.armedItemId.empty();
if (!preview.armed) {
preview.armedItemId.clear();
}
}
if (preview.dragging) {
suppress = true;
}
break;
case UIInputEventType::PointerMove:
if (preview.dragging) {
suppress = true;
break;
}
if (preview.armed &&
ComputeSquaredDistance(event.position, preview.pressPosition) >=
dragThreshold * dragThreshold) {
preview.dragging = !preview.armedItemId.empty();
suppress = preview.dragging;
}
break;
case UIInputEventType::PointerButtonUp:
if (event.pointerButton == UIPointerButton::Left) {
if (preview.dragging) {
suppress = true;
preview.dragging = false;
}
preview.armed = false;
preview.armedItemId.clear();
} else if (preview.dragging) {
suppress = true;
}
break;
case UIInputEventType::PointerLeave:
if (preview.dragging) {
suppress = true;
}
break;
case UIInputEventType::FocusLost:
preview.armed = false;
preview.dragging = false;
preview.armedItemId.clear();
break;
default:
if (preview.dragging &&
(event.type == UIInputEventType::PointerWheel ||
event.type == UIInputEventType::PointerEnter)) {
suppress = true;
}
break;
}
return suppress;
}
template <typename StateLike, typename Callbacks>
inline void ResetInteractionSession(StateLike& state, Callbacks& callbacks) {
state.armed = false;
state.dragging = false;
state.armedItemId.clear();
state.draggedItemId.clear();
callbacks.ResetDropTarget(state);
}
template <typename StateLike, typename Callbacks>
ProcessResult ProcessInputEvents(
StateLike& state,
const std::vector<UIInputEvent>& inputEvents,
Callbacks& callbacks,
float dragThreshold = kDefaultDragThreshold) {
ProcessResult result = {};
for (const UIInputEvent& event : inputEvents) {
switch (event.type) {
case UIInputEventType::PointerButtonDown:
if (event.pointerButton == UIPointerButton::Left) {
state.draggedItemId.clear();
callbacks.ResetDropTarget(state);
state.armedItemId = callbacks.ResolveDraggableItem(event.position);
state.pressPosition = event.position;
state.armed = !state.armedItemId.empty();
if (!state.armed) {
state.armedItemId.clear();
}
}
break;
case UIInputEventType::PointerMove:
if (state.armed &&
!state.dragging &&
ComputeSquaredDistance(event.position, state.pressPosition) >=
dragThreshold * dragThreshold) {
state.dragging = !state.armedItemId.empty();
state.draggedItemId = state.armedItemId;
callbacks.ResetDropTarget(state);
if (state.dragging) {
state.requestPointerCapture = true;
if (!callbacks.IsItemSelected(state.draggedItemId)) {
result.selectionForced = callbacks.SelectDraggedItem(state.draggedItemId);
}
}
}
if (state.dragging) {
callbacks.ResetDropTarget(state);
callbacks.UpdateDropTarget(state, state.draggedItemId, event.position);
}
break;
case UIInputEventType::PointerButtonUp:
if (event.pointerButton != UIPointerButton::Left) {
break;
}
if (state.dragging) {
if (state.validDropTarget) {
result.draggedItemId = state.draggedItemId;
result.dropTargetItemId = state.dropTargetItemId;
result.dropCommitted = callbacks.CommitDrop(state, result);
}
ResetInteractionSession(state, callbacks);
state.requestPointerRelease = true;
} else {
state.armed = false;
state.armedItemId.clear();
}
break;
case UIInputEventType::PointerLeave:
if (state.dragging) {
callbacks.ResetDropTarget(state);
}
break;
case UIInputEventType::FocusLost:
{
const bool requestPointerRelease = state.dragging;
ResetInteractionSession(state, callbacks);
state.pressPosition = {};
state.requestPointerCapture = false;
state.requestPointerRelease = requestPointerRelease;
}
break;
default:
break;
}
}
return result;
}
} // namespace XCEngine::UI::Editor::Collections::DragDropInteraction

View File

@@ -0,0 +1,108 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEditor/Fields/UIEditorTextFieldInteraction.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
struct UIEditorFilterableTreeHostMetrics {
float horizontalPadding = 6.0f;
float topPadding = 6.0f;
float bottomPadding = 6.0f;
float searchFieldHeight = 24.0f;
float searchTreeGap = 6.0f;
UIEditorTextFieldMetrics searchFieldMetrics = {};
};
struct UIEditorFilterableTreeHostPalette {
UIEditorTextFieldPalette searchFieldPalette = {};
::XCEngine::UI::UIColor placeholderColor =
::XCEngine::UI::UIColor(0.46f, 0.46f, 0.46f, 1.0f);
};
struct UIEditorFilterableTreeHostLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect searchFieldRect = {};
::XCEngine::UI::UIRect treeRect = {};
};
} // namespace XCEngine::UI::Editor::Widgets
namespace XCEngine::UI::Editor {
struct UIEditorFilterableTreeHostState {
UIEditorTextFieldInteractionState searchFieldInteractionState = {};
Widgets::UIEditorTextFieldSpec searchFieldSpec = {};
std::string normalizedQuery = {};
};
struct UIEditorFilterableTreeHostResult {
bool queryChanged = false;
bool filteringActive = false;
bool searchFocused = false;
};
struct UIEditorFilterableTreeHostFrame {
Widgets::UIEditorFilterableTreeHostLayout layout = {};
UIEditorTextFieldInteractionFrame searchFieldFrame = {};
const std::vector<Widgets::UIEditorTreeViewItem>* sourceItems = nullptr;
const ::XCEngine::UI::Widgets::UIExpansionModel* sourceExpansionModel = nullptr;
std::vector<Widgets::UIEditorTreeViewItem> filteredItems = {};
::XCEngine::UI::Widgets::UIExpansionModel filteredExpansionModel = {};
UIEditorFilterableTreeHostResult result = {};
};
inline bool IsUIEditorFilterableTreeHostSearchFocused(
const UIEditorFilterableTreeHostState& state) {
return state.searchFieldInteractionState.textFieldState.focused;
}
inline const std::vector<Widgets::UIEditorTreeViewItem>&
ResolveUIEditorFilterableTreeHostItems(
const UIEditorFilterableTreeHostFrame& frame) {
static const std::vector<Widgets::UIEditorTreeViewItem> kEmptyItems = {};
if (frame.result.filteringActive) {
return frame.filteredItems;
}
return frame.sourceItems != nullptr ? *frame.sourceItems : kEmptyItems;
}
inline const ::XCEngine::UI::Widgets::UIExpansionModel&
ResolveUIEditorFilterableTreeHostExpansionModel(
const UIEditorFilterableTreeHostFrame& frame) {
static const ::XCEngine::UI::Widgets::UIExpansionModel kEmptyExpansionModel = {};
if (frame.result.filteringActive) {
return frame.filteredExpansionModel;
}
return frame.sourceExpansionModel != nullptr
? *frame.sourceExpansionModel
: kEmptyExpansionModel;
}
UIEditorFilterableTreeHostFrame UpdateUIEditorFilterableTreeHost(
UIEditorFilterableTreeHostState& state,
const ::XCEngine::UI::UIRect& bounds,
std::string_view searchFieldId,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const std::vector<Widgets::UIEditorTreeViewItem>& sourceItems,
const ::XCEngine::UI::Widgets::UIExpansionModel& sourceExpansionModel,
const Widgets::UIEditorFilterableTreeHostMetrics& metrics = {});
void AppendUIEditorFilterableTreeHostSearchField(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorFilterableTreeHostFrame& frame,
const UIEditorFilterableTreeHostState& state,
std::string_view placeholderText = "Search",
const Widgets::UIEditorFilterableTreeHostPalette& palette = {},
const Widgets::UIEditorFilterableTreeHostMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,73 @@
#pragma once
#include <XCEditor/Collections/UIEditorDragDropInteraction.h>
#include <string_view>
namespace XCEngine::UI::Editor::Collections::GridDragDrop {
using ::XCEngine::UI::UIPoint;
using DragDropInteraction::kDefaultDragThreshold;
using DragDropInteraction::ProcessResult;
using DragDropInteraction::State;
inline void ResetTransientRequests(State& state) {
DragDropInteraction::ResetTransientRequests(state);
}
inline bool HasActivePointerCapture(const State& state) {
return DragDropInteraction::HasActivePointerCapture(state);
}
template <typename Callbacks>
ProcessResult ProcessInputEvents(
State& state,
const std::vector<DragDropInteraction::UIInputEvent>& inputEvents,
Callbacks& callbacks,
float dragThreshold = kDefaultDragThreshold) {
struct AdaptedCallbacks {
Callbacks& callbacks;
std::string ResolveDraggableItem(const UIPoint& point) const {
return callbacks.ResolveDraggableItem(point);
}
void ResetDropTarget(State& state) const {
state.dropTargetItemId.clear();
state.validDropTarget = false;
}
void UpdateDropTarget(
State& state,
std::string_view draggedItemId,
const UIPoint& point) const {
state.dropTargetItemId =
callbacks.ResolveDropTargetItem(draggedItemId, point);
state.validDropTarget =
!state.dropTargetItemId.empty() &&
callbacks.CanDropOnItem(draggedItemId, state.dropTargetItemId);
}
bool IsItemSelected(std::string_view itemId) const {
return callbacks.IsItemSelected(itemId);
}
bool SelectDraggedItem(std::string_view itemId) const {
return callbacks.SelectDraggedItem(itemId);
}
bool CommitDrop(State& state, ProcessResult&) const {
return callbacks.CommitDropOnItem(
state.draggedItemId,
state.dropTargetItemId);
}
} adaptedCallbacks{ callbacks };
return DragDropInteraction::ProcessInputEvents(
state,
inputEvents,
adaptedCallbacks,
dragThreshold);
}
} // namespace XCEngine::UI::Editor::Collections::GridDragDrop

View File

@@ -0,0 +1,63 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Fields/UIEditorTextFieldInteraction.h>
#include <XCEngine/UI/Types.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorInlineRenameSessionState {
bool active = false;
std::string itemId = {};
Widgets::UIEditorTextFieldSpec textFieldSpec = {};
UIEditorTextFieldInteractionState textFieldInteraction = {};
};
struct UIEditorInlineRenameSessionRequest {
bool beginSession = false;
std::string itemId = {};
std::string initialText = {};
::XCEngine::UI::UIRect bounds = {};
};
struct UIEditorInlineRenameSessionResult {
bool consumed = false;
bool sessionStarted = false;
bool sessionCommitted = false;
bool sessionCanceled = false;
bool valueChanged = false;
bool active = false;
std::string itemId = {};
std::string valueBefore = {};
std::string valueAfter = {};
UIEditorTextFieldInteractionResult textFieldResult = {};
};
struct UIEditorInlineRenameSessionFrame {
Widgets::UIEditorTextFieldLayout layout = {};
UIEditorInlineRenameSessionResult result = {};
};
Widgets::UIEditorTextFieldMetrics BuildUIEditorInlineRenameTextFieldMetrics(
const ::XCEngine::UI::UIRect& bounds,
const Widgets::UIEditorTextFieldMetrics& metrics = {});
void AppendUIEditorInlineRenameSession(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorInlineRenameSessionFrame& frame,
const UIEditorInlineRenameSessionState& state,
const Widgets::UIEditorTextFieldPalette& palette = {},
const Widgets::UIEditorTextFieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
UIEditorInlineRenameSessionFrame UpdateUIEditorInlineRenameSession(
UIEditorInlineRenameSessionState& state,
const UIEditorInlineRenameSessionRequest& request,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorTextFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,121 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorListViewInvalidIndex = static_cast<std::size_t>(-1);
enum class UIEditorListViewHitTargetKind : std::uint8_t {
None = 0,
Row
};
struct UIEditorListViewItem {
std::string itemId = {};
std::string primaryText = {};
std::string secondaryText = {};
float desiredHeight = 0.0f;
};
struct UIEditorListViewState {
std::string hoveredItemId = {};
bool focused = false;
};
struct UIEditorListViewMetrics {
float rowHeight = 44.0f;
float rowGap = 2.0f;
float horizontalPadding = 10.0f;
float primaryTextInsetY = 7.0f;
float secondaryTextInsetY = 23.0f;
float singleLineTextInsetY = 13.0f;
float cornerRounding = 6.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
};
struct UIEditorListViewPalette {
::XCEngine::UI::UIColor surfaceColor =
::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 focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor rowSelectedColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor rowSelectedFocusedColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor primaryTextColor =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor secondaryTextColor =
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f);
};
struct UIEditorListViewLayout {
::XCEngine::UI::UIRect bounds = {};
std::vector<std::size_t> itemIndices = {};
std::vector<::XCEngine::UI::UIRect> rowRects = {};
std::vector<::XCEngine::UI::UIRect> primaryTextRects = {};
std::vector<::XCEngine::UI::UIRect> secondaryTextRects = {};
std::vector<bool> hasSecondaryText = {};
};
struct UIEditorListViewHitTarget {
UIEditorListViewHitTargetKind kind = UIEditorListViewHitTargetKind::None;
std::size_t visibleIndex = UIEditorListViewInvalidIndex;
std::size_t itemIndex = UIEditorListViewInvalidIndex;
};
bool IsUIEditorListViewPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
std::size_t FindUIEditorListViewItemIndex(
const std::vector<UIEditorListViewItem>& items,
std::string_view itemId);
UIEditorListViewLayout BuildUIEditorListViewLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorListViewItem>& items,
const UIEditorListViewMetrics& metrics = {});
UIEditorListViewHitTarget HitTestUIEditorListView(
const UIEditorListViewLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorListViewBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorListViewLayout& layout,
const std::vector<UIEditorListViewItem>& items,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const UIEditorListViewState& state,
const UIEditorListViewPalette& palette = {},
const UIEditorListViewMetrics& metrics = {});
void AppendUIEditorListViewForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorListViewLayout& layout,
const std::vector<UIEditorListViewItem>& items,
const UIEditorListViewPalette& palette = {},
const UIEditorListViewMetrics& metrics = {});
void AppendUIEditorListView(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorListViewItem>& items,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const UIEditorListViewState& state,
const UIEditorListViewPalette& palette = {},
const UIEditorListViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,47 @@
#pragma once
#include <XCEditor/Collections/UIEditorListView.h>
#include <XCEngine/UI/Types.h>
#include <XCEngine/UI/Widgets/UIKeyboardNavigationModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorListViewInteractionState {
Widgets::UIEditorListViewState listViewState = {};
::XCEngine::UI::Widgets::UIKeyboardNavigationModel keyboardNavigation = {};
std::string selectionAnchorId = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorListViewInteractionResult {
bool consumed = false;
bool selectionChanged = false;
bool keyboardNavigated = false;
bool secondaryClicked = false;
bool renameRequested = false;
Widgets::UIEditorListViewHitTarget hitTarget = {};
std::string selectedItemId = {};
std::string renameItemId = {};
std::size_t selectedIndex = Widgets::UIEditorListViewInvalidIndex;
};
struct UIEditorListViewInteractionFrame {
Widgets::UIEditorListViewLayout layout = {};
UIEditorListViewInteractionResult result = {};
};
UIEditorListViewInteractionFrame UpdateUIEditorListViewInteraction(
UIEditorListViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorListViewItem>& items,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorListViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,96 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Types.h>
#include <cstdint>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorScrollViewHitTargetKind : std::uint8_t {
None = 0,
Content,
ScrollbarTrack,
ScrollbarThumb
};
struct UIEditorScrollViewState {
bool focused = false;
bool hovered = false;
bool scrollbarHovered = false;
bool draggingScrollbarThumb = false;
};
struct UIEditorScrollViewMetrics {
float scrollbarWidth = 10.0f;
float scrollbarInset = 4.0f;
float minThumbHeight = 28.0f;
float cornerRounding = 8.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
float wheelStep = 48.0f;
};
struct UIEditorScrollViewPalette {
::XCEngine::UI::UIColor surfaceColor =
::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 focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor scrollbarTrackColor =
::XCEngine::UI::UIColor(0.09f, 0.09f, 0.09f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbHoverColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor scrollbarThumbActiveColor =
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f);
};
struct UIEditorScrollViewLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect contentRect = {};
::XCEngine::UI::UIRect scrollbarTrackRect = {};
::XCEngine::UI::UIRect scrollbarThumbRect = {};
float contentHeight = 0.0f;
float verticalOffset = 0.0f;
float maxOffset = 0.0f;
bool hasScrollbar = false;
};
struct UIEditorScrollViewHitTarget {
UIEditorScrollViewHitTargetKind kind = UIEditorScrollViewHitTargetKind::None;
};
bool IsUIEditorScrollViewPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
float ClampUIEditorScrollViewOffset(
const ::XCEngine::UI::UIRect& bounds,
float contentHeight,
float verticalOffset,
const UIEditorScrollViewMetrics& metrics = {});
UIEditorScrollViewLayout BuildUIEditorScrollViewLayout(
const ::XCEngine::UI::UIRect& bounds,
float contentHeight,
float verticalOffset,
const UIEditorScrollViewMetrics& metrics = {});
::XCEngine::UI::UIPoint ResolveUIEditorScrollViewContentOrigin(
const UIEditorScrollViewLayout& layout);
UIEditorScrollViewHitTarget HitTestUIEditorScrollView(
const UIEditorScrollViewLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorScrollViewBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorScrollViewLayout& layout,
const UIEditorScrollViewState& state,
const UIEditorScrollViewPalette& palette = {},
const UIEditorScrollViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,47 @@
#pragma once
#include <XCEditor/Collections/UIEditorScrollView.h>
#include <XCEngine/UI/Types.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorScrollViewInteractionState {
Widgets::UIEditorScrollViewState scrollViewState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
float thumbDragStartPointerY = 0.0f;
float thumbDragStartOffset = 0.0f;
bool hasPointerPosition = false;
};
struct UIEditorScrollViewInteractionResult {
bool consumed = false;
bool offsetChanged = false;
bool focusChanged = false;
bool startedThumbDrag = false;
bool endedThumbDrag = false;
Widgets::UIEditorScrollViewHitTarget hitTarget = {};
float verticalOffset = 0.0f;
};
struct UIEditorScrollViewInteractionFrame {
Widgets::UIEditorScrollViewLayout layout = {};
UIEditorScrollViewInteractionResult result = {};
};
inline bool HasActiveUIEditorScrollViewPointerCapture(
const UIEditorScrollViewInteractionState& state) {
return state.scrollViewState.draggingScrollbarThumb;
}
UIEditorScrollViewInteractionFrame UpdateUIEditorScrollViewInteraction(
UIEditorScrollViewInteractionState& state,
float& verticalOffset,
const ::XCEngine::UI::UIRect& bounds,
float contentHeight,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorScrollViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,135 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#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 = {};
// Pure glyph advance for the rendered title. Header padding is added by layout.
float measuredLabelWidth = 0.0f;
};
struct UIEditorTabStripState {
std::size_t selectedIndex = UIEditorTabStripInvalidIndex;
std::size_t hoveredIndex = UIEditorTabStripInvalidIndex;
bool focused = false;
};
struct UIEditorTabStripMetrics {
::XCEngine::UI::Layout::UITabStripMetrics layoutMetrics =
::XCEngine::UI::Layout::UITabStripMetrics{ 22.0f, 68.0f, 8.0f, 1.0f };
float estimatedGlyphWidth = 6.0f;
float labelInsetX = 8.0f;
float labelInsetY = -0.5f;
float baseBorderThickness = 1.0f;
float selectedBorderThickness = 1.0f;
float dragThreshold = 6.0f;
};
struct UIEditorTabStripPalette {
::XCEngine::UI::UIColor stripBackgroundColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor headerBackgroundColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor contentBackgroundColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor headerContentSeparatorColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor tabColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor tabHoveredColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor tabSelectedColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor tabBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor tabHoveredBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor tabSelectedBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
};
struct UIEditorTabStripLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect headerRect = {};
::XCEngine::UI::UIRect contentRect = {};
std::vector<::XCEngine::UI::UIRect> tabHeaderRects = {};
std::size_t selectedIndex = UIEditorTabStripInvalidIndex;
};
enum class UIEditorTabStripHitTargetKind : std::uint8_t {
None = 0,
HeaderBackground,
Tab,
Content
};
struct UIEditorTabStripHitTarget {
UIEditorTabStripHitTargetKind kind = UIEditorTabStripHitTargetKind::None;
std::size_t index = UIEditorTabStripInvalidIndex;
};
float ResolveUIEditorTabStripMeasuredLabelWidth(
const UIEditorTabStripItem& item,
const UIEditorTabStripMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
std::size_t ResolveUIEditorTabStripSelectedIndex(
const std::vector<UIEditorTabStripItem>& items,
std::string_view selectedTabId,
std::size_t fallbackIndex = UIEditorTabStripInvalidIndex);
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

View File

@@ -0,0 +1,60 @@
#pragma once
#include <XCEditor/Collections/UIEditorTabStrip.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UIDragDropInteraction.h>
#include <XCEngine/UI/Widgets/UITabStripModel.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorTabStripInteractionState {
Widgets::UIEditorTabStripState tabStripState = {};
::XCEngine::UI::Widgets::UITabStripModel navigationModel = {};
::XCEngine::UI::Widgets::UIDragDropState dragState = {};
Widgets::UIEditorTabStripHitTarget pressedTarget = {};
::XCEngine::UI::UIPoint pointerPosition = {};
std::size_t dragSourceIndex = Widgets::UIEditorTabStripInvalidIndex;
bool hasPointerPosition = false;
bool dragCaptureActive = false;
};
struct UIEditorTabStripInteractionResult {
bool consumed = false;
bool selectionChanged = false;
bool keyboardNavigated = false;
bool requestPointerCapture = false;
bool releasePointerCapture = false;
bool dragStarted = false;
bool dragEnded = false;
bool dragCanceled = false;
Widgets::UIEditorTabStripHitTarget hitTarget = {};
std::string selectedTabId = {};
std::size_t selectedIndex = Widgets::UIEditorTabStripInvalidIndex;
std::size_t closedIndex = Widgets::UIEditorTabStripInvalidIndex;
std::string draggedTabId = {};
std::size_t dragSourceIndex = Widgets::UIEditorTabStripInvalidIndex;
};
struct UIEditorTabStripInteractionFrame {
Widgets::UIEditorTabStripLayout layout = {};
UIEditorTabStripInteractionResult result = {};
bool focused = false;
};
// Clears transient pointer-driven state while preserving selection/navigation.
void ClearUIEditorTabStripTransientInteraction(
UIEditorTabStripInteractionState& state);
UIEditorTabStripInteractionFrame UpdateUIEditorTabStripInteraction(
UIEditorTabStripInteractionState& state,
std::string& selectedTabId,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorTabStripItem>& items,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorTabStripMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,398 @@
#pragma once
#include <XCEditor/Collections/UIEditorDragDropInteraction.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEngine/UI/Types.h>
#include <algorithm>
#include <concepts>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Collections::TreeDragDrop {
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
using DragDropInteraction::kDefaultDragThreshold;
using Widgets::HitTestUIEditorTreeView;
using Widgets::UIEditorTreeViewHitTarget;
using Widgets::UIEditorTreeViewHitTargetKind;
using Widgets::UIEditorTreeViewInvalidIndex;
using ::XCEngine::UI::Editor::UIEditorTreeViewDropPreviewPlacement;
struct State : DragDropInteraction::State {
bool dropToRoot = false;
UIEditorTreeViewDropPreviewPlacement dropPlacement =
UIEditorTreeViewDropPreviewPlacement::None;
};
struct ProcessResult : DragDropInteraction::ProcessResult {
bool droppedToRoot = false;
};
inline void ResetTransientRequests(State& state) {
DragDropInteraction::ResetTransientRequests(state);
}
inline bool HasActivePointerCapture(const State& state) {
return DragDropInteraction::HasActivePointerCapture(state);
}
inline 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;
}
template <typename Callbacks>
inline constexpr bool SupportsSiblingInsertion() {
return requires(
Callbacks& callbacks,
std::string_view draggedItemId,
std::string_view targetItemId) {
{ callbacks.CanInsertBeforeItem(draggedItemId, targetItemId) } -> std::convertible_to<bool>;
{ callbacks.CanInsertAfterItem(draggedItemId, targetItemId) } -> std::convertible_to<bool>;
{ callbacks.CommitInsertBeforeItem(draggedItemId, targetItemId) } -> std::convertible_to<bool>;
{ callbacks.CommitInsertAfterItem(draggedItemId, targetItemId) } -> std::convertible_to<bool>;
};
}
inline UIEditorTreeViewDropPreviewPlacement ResolveRowDropPlacement(
const Widgets::UIEditorTreeViewLayout& layout,
const UIEditorTreeViewHitTarget& hitTarget,
const UIPoint& point) {
if (hitTarget.visibleIndex >= layout.rowRects.size()) {
return UIEditorTreeViewDropPreviewPlacement::None;
}
const UIRect& rowRect = layout.rowRects[hitTarget.visibleIndex];
const float edgeBand =
(std::min)((std::max)(rowRect.height * 0.25f, 4.0f), 8.0f);
if (point.y <= rowRect.y + edgeBand) {
return UIEditorTreeViewDropPreviewPlacement::BeforeItem;
}
if (point.y >= rowRect.y + rowRect.height - edgeBand) {
return UIEditorTreeViewDropPreviewPlacement::AfterItem;
}
return UIEditorTreeViewDropPreviewPlacement::OnItem;
}
inline const Widgets::UIEditorTreeViewItem* ResolveGapInsertionItem(
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
const UIPoint& point,
UIEditorTreeViewDropPreviewPlacement& outPlacement) {
outPlacement = UIEditorTreeViewDropPreviewPlacement::None;
if (layout.rowRects.empty()) {
return nullptr;
}
if (point.y < layout.rowRects.front().y) {
outPlacement = UIEditorTreeViewDropPreviewPlacement::BeforeItem;
const std::size_t itemIndex = layout.visibleItemIndices.front();
return itemIndex < items.size() ? &items[itemIndex] : nullptr;
}
for (std::size_t visibleIndex = 0u; visibleIndex + 1u < layout.rowRects.size(); ++visibleIndex) {
const UIRect& currentRow = layout.rowRects[visibleIndex];
const UIRect& nextRow = layout.rowRects[visibleIndex + 1u];
if (point.y > currentRow.y + currentRow.height &&
point.y < nextRow.y) {
outPlacement = UIEditorTreeViewDropPreviewPlacement::BeforeItem;
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex + 1u];
return itemIndex < items.size() ? &items[itemIndex] : nullptr;
}
}
const UIRect& lastRow = layout.rowRects.back();
if (point.y > lastRow.y + lastRow.height) {
outPlacement = UIEditorTreeViewDropPreviewPlacement::AfterItem;
const std::size_t itemIndex = layout.visibleItemIndices.back();
return itemIndex < items.size() ? &items[itemIndex] : nullptr;
}
return nullptr;
}
inline const Widgets::UIEditorTreeViewItem* ResolveHitItem(
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
const UIPoint& point,
UIEditorTreeViewHitTarget* hitTargetOutput = nullptr) {
const UIEditorTreeViewHitTarget hitTarget = HitTestUIEditorTreeView(layout, point);
if (hitTargetOutput != nullptr) {
*hitTargetOutput = hitTarget;
}
if (hitTarget.itemIndex >= items.size()) {
return nullptr;
}
return &items[hitTarget.itemIndex];
}
inline std::size_t FindVisibleIndexForItemId(
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::string_view itemId) {
for (std::size_t visibleIndex = 0u; visibleIndex < layout.visibleItemIndices.size(); ++visibleIndex) {
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex];
if (itemIndex < items.size() && items[itemIndex].itemId == itemId) {
return visibleIndex;
}
}
return UIEditorTreeViewInvalidIndex;
}
inline bool IsSuppressedPointerInputEvent(
const DragDropInteraction::UIInputEvent& event) {
switch (event.type) {
case ::XCEngine::UI::UIInputEventType::PointerMove:
case ::XCEngine::UI::UIInputEventType::PointerButtonDown:
case ::XCEngine::UI::UIInputEventType::PointerButtonUp:
case ::XCEngine::UI::UIInputEventType::PointerWheel:
case ::XCEngine::UI::UIInputEventType::PointerEnter:
return true;
default:
return false;
}
}
inline std::vector<DragDropInteraction::UIInputEvent> FilterPointerInputEvents(
const std::vector<DragDropInteraction::UIInputEvent>& inputEvents,
bool suppressPointerInput) {
if (!suppressPointerInput) {
return inputEvents;
}
std::vector<DragDropInteraction::UIInputEvent> filteredEvents = {};
filteredEvents.reserve(inputEvents.size());
for (const DragDropInteraction::UIInputEvent& event : inputEvents) {
if (!IsSuppressedPointerInputEvent(event)) {
filteredEvents.push_back(event);
}
}
return filteredEvents;
}
inline std::vector<DragDropInteraction::UIInputEvent> BuildInteractionInputEvents(
const State& state,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
const std::vector<DragDropInteraction::UIInputEvent>& rawEvents,
float dragThreshold = kDefaultDragThreshold,
bool suppressPointerInput = false) {
auto resolveDraggableItem =
[&layout, &items](const UIPoint& point) -> std::string {
UIEditorTreeViewHitTarget hitTarget = {};
const Widgets::UIEditorTreeViewItem* hitItem =
ResolveHitItem(layout, items, point, &hitTarget);
if (hitItem != nullptr &&
hitTarget.kind == UIEditorTreeViewHitTargetKind::Row) {
return hitItem->itemId;
}
return {};
};
DragDropInteraction::PreviewState preview =
DragDropInteraction::BuildPreviewState(state);
const std::vector<DragDropInteraction::UIInputEvent> inputEvents =
FilterPointerInputEvents(rawEvents, suppressPointerInput);
std::vector<DragDropInteraction::UIInputEvent> filteredEvents = {};
filteredEvents.reserve(inputEvents.size());
for (const DragDropInteraction::UIInputEvent& event : inputEvents) {
if (!DragDropInteraction::ProcessPreviewEvent(
preview,
event,
resolveDraggableItem,
dragThreshold)) {
filteredEvents.push_back(event);
}
}
return filteredEvents;
}
template <typename Callbacks>
ProcessResult ProcessInputEvents(
State& state,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
const std::vector<DragDropInteraction::UIInputEvent>& inputEvents,
const UIRect& bounds,
Callbacks& callbacks,
float dragThreshold = kDefaultDragThreshold,
bool suppressPointerInput = false) {
struct AdaptedCallbacks {
const Widgets::UIEditorTreeViewLayout& layout;
const std::vector<Widgets::UIEditorTreeViewItem>& items;
const UIRect& bounds;
Callbacks& callbacks;
bool droppedToRoot = false;
std::string ResolveDraggableItem(const UIPoint& point) const {
UIEditorTreeViewHitTarget hitTarget = {};
const Widgets::UIEditorTreeViewItem* hitItem =
ResolveHitItem(layout, items, point, &hitTarget);
if (hitItem != nullptr &&
hitTarget.kind == UIEditorTreeViewHitTargetKind::Row) {
return hitItem->itemId;
}
return {};
}
void ResetDropTarget(State& state) const {
state.dropTargetItemId.clear();
state.dropToRoot = false;
state.dropPlacement = UIEditorTreeViewDropPreviewPlacement::None;
state.validDropTarget = false;
}
void UpdateDropTarget(
State& state,
std::string_view draggedItemId,
const UIPoint& point) const {
UIEditorTreeViewHitTarget hitTarget = {};
const Widgets::UIEditorTreeViewItem* hitItem =
ResolveHitItem(layout, items, point, &hitTarget);
if (hitItem != nullptr &&
(hitTarget.kind == UIEditorTreeViewHitTargetKind::Row ||
hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure)) {
UIEditorTreeViewDropPreviewPlacement placement =
UIEditorTreeViewDropPreviewPlacement::OnItem;
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
placement =
hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure
? UIEditorTreeViewDropPreviewPlacement::OnItem
: ResolveRowDropPlacement(layout, hitTarget, point);
}
state.dropTargetItemId = hitItem->itemId;
state.dropPlacement = placement;
switch (placement) {
case UIEditorTreeViewDropPreviewPlacement::BeforeItem:
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
state.validDropTarget =
callbacks.CanInsertBeforeItem(
draggedItemId,
state.dropTargetItemId);
}
break;
case UIEditorTreeViewDropPreviewPlacement::AfterItem:
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
state.validDropTarget =
callbacks.CanInsertAfterItem(
draggedItemId,
state.dropTargetItemId);
}
break;
case UIEditorTreeViewDropPreviewPlacement::OnItem:
state.validDropTarget =
callbacks.CanDropOnItem(draggedItemId, state.dropTargetItemId);
break;
default:
break;
}
return;
}
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
UIEditorTreeViewDropPreviewPlacement gapPlacement =
UIEditorTreeViewDropPreviewPlacement::None;
const Widgets::UIEditorTreeViewItem* gapItem =
ResolveGapInsertionItem(layout, items, point, gapPlacement);
if (gapItem != nullptr &&
gapPlacement != UIEditorTreeViewDropPreviewPlacement::None) {
state.dropTargetItemId = gapItem->itemId;
state.dropPlacement = gapPlacement;
state.validDropTarget =
gapPlacement == UIEditorTreeViewDropPreviewPlacement::BeforeItem
? callbacks.CanInsertBeforeItem(
draggedItemId,
state.dropTargetItemId)
: callbacks.CanInsertAfterItem(
draggedItemId,
state.dropTargetItemId);
return;
}
}
if (ContainsPoint(bounds, point)) {
state.dropToRoot = true;
state.dropPlacement = UIEditorTreeViewDropPreviewPlacement::Root;
state.validDropTarget = callbacks.CanDropToRoot(draggedItemId);
}
}
bool IsItemSelected(std::string_view itemId) const {
return callbacks.IsItemSelected(itemId);
}
bool SelectDraggedItem(std::string_view itemId) const {
return callbacks.SelectDraggedItem(itemId);
}
bool CommitDrop(State& state, DragDropInteraction::ProcessResult&) {
droppedToRoot = state.dropToRoot;
switch (state.dropPlacement) {
case UIEditorTreeViewDropPreviewPlacement::BeforeItem:
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
return callbacks.CommitInsertBeforeItem(
state.draggedItemId,
state.dropTargetItemId);
}
return false;
case UIEditorTreeViewDropPreviewPlacement::AfterItem:
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
return callbacks.CommitInsertAfterItem(
state.draggedItemId,
state.dropTargetItemId);
}
return false;
case UIEditorTreeViewDropPreviewPlacement::Root:
return callbacks.CommitDropToRoot(state.draggedItemId);
case UIEditorTreeViewDropPreviewPlacement::OnItem:
return callbacks.CommitDropOnItem(
state.draggedItemId,
state.dropTargetItemId);
case UIEditorTreeViewDropPreviewPlacement::None:
default:
return false;
}
}
} adaptedCallbacks{ layout, items, bounds, callbacks };
ProcessResult result = {};
const std::vector<DragDropInteraction::UIInputEvent> filteredInputEvents =
FilterPointerInputEvents(inputEvents, suppressPointerInput);
const DragDropInteraction::ProcessResult interactionResult =
DragDropInteraction::ProcessInputEvents(
state,
filteredInputEvents,
adaptedCallbacks,
dragThreshold);
result.selectionForced = interactionResult.selectionForced;
result.dropCommitted = interactionResult.dropCommitted;
result.draggedItemId = interactionResult.draggedItemId;
result.dropTargetItemId = interactionResult.dropTargetItemId;
result.droppedToRoot = adaptedCallbacks.droppedToRoot;
return result;
}
} // namespace XCEngine::UI::Editor::Collections::TreeDragDrop

View File

@@ -0,0 +1,233 @@
#pragma once
#include <XCEditor/Collections/UIEditorInlineRenameSession.h>
#include <XCEditor/Collections/UIEditorScrollView.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorTreeViewInvalidIndex = static_cast<std::size_t>(-1);
enum class UIEditorTreeViewHitTargetKind : std::uint8_t {
None = 0,
Row,
Disclosure
};
struct UIEditorTreeViewItem {
std::string itemId = {};
std::string label = {};
std::uint32_t depth = 0u;
bool forceLeaf = false;
float desiredHeight = 0.0f;
::XCEngine::UI::UITextureHandle leadingIcon = {};
};
struct UIEditorTreeViewState {
std::string hoveredItemId = {};
bool focused = false;
UIEditorScrollViewState scrollViewState = {};
};
struct UIEditorTreeViewMetrics {
float rowHeight = 28.0f;
float rowGap = 2.0f;
float horizontalPadding = 8.0f;
float indentWidth = 18.0f;
float disclosureExtent = 12.0f;
float disclosureLabelGap = 6.0f;
float iconExtent = 18.0f;
float iconLabelGap = 2.0f;
float iconInsetY = 0.0f;
float labelInsetY = 6.0f;
float cornerRounding = 6.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
UIEditorScrollViewMetrics scrollViewMetrics = {};
};
struct UIEditorTreeViewPalette {
::XCEngine::UI::UIColor surfaceColor =
::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 focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor rowSelectedColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor rowSelectedFocusedColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor disclosureColor =
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f);
::XCEngine::UI::UIColor textColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
UIEditorScrollViewPalette scrollViewPalette = {};
};
struct UIEditorTreeViewLayout {
::XCEngine::UI::UIRect bounds = {};
UIEditorScrollViewLayout scrollViewLayout = {};
std::vector<std::size_t> visibleItemIndices = {};
std::vector<::XCEngine::UI::UIRect> rowRects = {};
std::vector<::XCEngine::UI::UIRect> disclosureRects = {};
std::vector<::XCEngine::UI::UIRect> iconRects = {};
std::vector<::XCEngine::UI::UIRect> labelRects = {};
std::vector<bool> itemHasChildren = {};
std::vector<bool> itemExpanded = {};
};
struct UIEditorTreeViewHitTarget {
UIEditorTreeViewHitTargetKind kind = UIEditorTreeViewHitTargetKind::None;
std::size_t visibleIndex = UIEditorTreeViewInvalidIndex;
std::size_t itemIndex = UIEditorTreeViewInvalidIndex;
};
bool IsUIEditorTreeViewPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
bool DoesUIEditorTreeViewItemHaveChildren(
const std::vector<UIEditorTreeViewItem>& items,
std::size_t itemIndex);
std::size_t FindUIEditorTreeViewItemIndex(
const std::vector<UIEditorTreeViewItem>& items,
std::string_view itemId);
std::size_t FindUIEditorTreeViewParentItemIndex(
const std::vector<UIEditorTreeViewItem>& items,
std::size_t itemIndex);
std::vector<std::size_t> CollectUIEditorTreeViewVisibleItemIndices(
const std::vector<UIEditorTreeViewItem>& items,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel);
std::size_t FindUIEditorTreeViewFirstVisibleChildItemIndex(
const std::vector<UIEditorTreeViewItem>& items,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
std::size_t itemIndex);
float MeasureUIEditorTreeViewContentHeight(
const std::vector<UIEditorTreeViewItem>& items,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorTreeViewMetrics& metrics = {});
UIEditorTreeViewLayout BuildUIEditorTreeViewLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorTreeViewItem>& items,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorTreeViewMetrics& metrics = {},
float verticalOffset = 0.0f);
UIEditorTreeViewHitTarget HitTestUIEditorTreeView(
const UIEditorTreeViewLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorTreeViewBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorTreeViewLayout& layout,
const std::vector<UIEditorTreeViewItem>& items,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const UIEditorTreeViewState& state,
const UIEditorTreeViewPalette& palette = {},
const UIEditorTreeViewMetrics& metrics = {});
void AppendUIEditorTreeViewForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorTreeViewLayout& layout,
const std::vector<UIEditorTreeViewItem>& items,
const UIEditorTreeViewPalette& palette = {},
const UIEditorTreeViewMetrics& metrics = {});
void AppendUIEditorTreeView(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorTreeViewItem>& items,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorTreeViewState& state,
const UIEditorTreeViewPalette& palette = {},
const UIEditorTreeViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets
namespace XCEngine::UI::Editor {
enum class UIEditorTreeViewDropPreviewPlacement : std::uint8_t {
None = 0,
OnItem,
BeforeItem,
AfterItem,
Root
};
bool HasUIEditorTreeViewValidBounds(
const ::XCEngine::UI::UIRect& bounds);
std::size_t FindUIEditorTreeViewVisibleItemIndex(
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::string_view itemId);
::XCEngine::UI::UIRect BuildUIEditorTreeViewInlineRenameBounds(
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
std::string_view itemId,
const Widgets::UIEditorTextFieldMetrics& hostedMetrics,
float minWidth = 120.0f,
float trailingPadding = 8.0f);
Widgets::UIEditorTextFieldMetrics ResolveUIEditorTreeViewHostedTextFieldMetrics();
Widgets::UIEditorTextFieldMetrics BuildUIEditorTreeViewInlineRenameMetrics(
const ::XCEngine::UI::UIRect& bounds);
Widgets::UIEditorTextFieldPalette ResolveUIEditorTreeViewInlineRenamePalette();
bool TryStartUIEditorTreeViewInlineRenameSession(
UIEditorInlineRenameSessionState& renameState,
UIEditorInlineRenameSessionFrame& renameFrame,
std::string_view itemId,
std::string initialText,
const ::XCEngine::UI::UIRect& bounds);
void UpdateUIEditorTreeViewInlineRenameSession(
UIEditorInlineRenameSessionState& renameState,
UIEditorInlineRenameSessionFrame& renameFrame,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents);
void AppendUIEditorTreeViewDropPreview(
::XCEngine::UI::UIDrawList& drawList,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
bool active,
UIEditorTreeViewDropPreviewPlacement placement,
std::string_view dropTargetItemId,
const ::XCEngine::UI::UIColor& previewColor,
float borderThickness = 1.0f,
float cornerRounding = 0.0f);
void AppendUIEditorTreeViewDropPreview(
::XCEngine::UI::UIDrawList& drawList,
const Widgets::UIEditorTreeViewLayout& layout,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
bool active,
bool dropToRoot,
std::string_view dropTargetItemId,
const ::XCEngine::UI::UIColor& previewColor,
float borderThickness = 1.0f,
float cornerRounding = 0.0f);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,72 @@
#pragma once
#include <XCEditor/Collections/UIEditorScrollViewInteraction.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEngine/UI/Types.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UIKeyboardNavigationModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorHostedTreeViewInputOptions {
bool allowInteraction = false;
bool hasInputFocus = false;
bool captureActive = false;
};
struct UIEditorTreeViewInteractionState {
Widgets::UIEditorTreeViewState treeViewState = {};
UIEditorScrollViewInteractionState scrollViewInteractionState = {};
::XCEngine::UI::Widgets::UIKeyboardNavigationModel keyboardNavigation = {};
std::string selectionAnchorId = {};
::XCEngine::UI::UIPoint pointerPosition = {};
float verticalOffset = 0.0f;
bool hasPointerPosition = false;
};
struct UIEditorTreeViewInteractionResult {
bool consumed = false;
bool selectionChanged = false;
bool expansionChanged = false;
bool keyboardNavigated = false;
bool secondaryClicked = false;
bool renameRequested = false;
Widgets::UIEditorTreeViewHitTarget hitTarget = {};
std::string selectedItemId = {};
std::string renameItemId = {};
std::string toggledItemId = {};
std::size_t selectedVisibleIndex = Widgets::UIEditorTreeViewInvalidIndex;
};
struct UIEditorTreeViewInteractionFrame {
Widgets::UIEditorTreeViewLayout layout = {};
UIEditorTreeViewInteractionResult result = {};
};
inline bool HasActiveUIEditorTreeViewPointerCapture(
const UIEditorTreeViewInteractionState& state) {
return HasActiveUIEditorScrollViewPointerCapture(state.scrollViewInteractionState);
}
std::vector<::XCEngine::UI::UIInputEvent> BuildUIEditorHostedTreeViewInputEvents(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const UIEditorHostedTreeViewInputOptions& options,
bool synthesizeFocusGained = false,
bool synthesizeFocusLost = false);
UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction(
UIEditorTreeViewInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorTreeViewItem>& items,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorTreeViewMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,222 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Workspace/UIEditorWorkspaceModel.h>
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
#include <XCEditor/Panels/UIEditorPanelFrame.h>
#include <XCEditor/Collections/UIEditorTabStrip.h>
#include <XCEngine/UI/Layout/UISplitterLayout.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorDockHostHitTargetKind : std::uint8_t {
None = 0,
SplitterHandle,
TabStripBackground,
Tab,
PanelHeader,
PanelBody,
PanelFooter
};
struct UIEditorDockHostHitTarget {
UIEditorDockHostHitTargetKind kind = UIEditorDockHostHitTargetKind::None;
std::string nodeId = {};
std::string panelId = {};
std::size_t index = UIEditorTabStripInvalidIndex;
};
struct UIEditorDockHostTabStripVisualState {
std::string nodeId = {};
UIEditorTabStripState state = {};
};
struct UIEditorDockHostDropPreviewState {
bool visible = false;
std::string sourceNodeId = {};
std::string sourcePanelId = {};
std::string targetNodeId = {};
UIEditorWorkspaceDockPlacement placement =
UIEditorWorkspaceDockPlacement::Center;
std::size_t insertionIndex = UIEditorTabStripInvalidIndex;
};
struct UIEditorDockHostState {
bool focused = false;
UIEditorDockHostHitTarget hoveredTarget = {};
std::string activeSplitterNodeId = {};
std::vector<UIEditorDockHostTabStripVisualState> tabStripStates = {};
UIEditorDockHostDropPreviewState dropPreview = {};
};
struct UIEditorDockHostMetrics {
::XCEngine::UI::Layout::UISplitterMetrics splitterMetrics =
::XCEngine::UI::Layout::UISplitterMetrics{ 2.0f, 10.0f };
UIEditorTabStripMetrics tabStripMetrics = {};
UIEditorPanelFrameMetrics panelFrameMetrics = {};
::XCEngine::UI::UISize minimumStandalonePanelBodySize =
::XCEngine::UI::UISize(180.0f, 140.0f);
::XCEngine::UI::UISize minimumTabContentBodySize =
::XCEngine::UI::UISize(260.0f, 180.0f);
float splitterHandleRounding = 0.0f;
float placeholderLineGap = 22.0f;
};
struct UIEditorDockHostPalette {
UIEditorTabStripPalette tabStripPalette = {};
UIEditorPanelFramePalette panelFramePalette = {};
::XCEngine::UI::UIColor splitterColor =
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
::XCEngine::UI::UIColor splitterHoveredColor =
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
::XCEngine::UI::UIColor splitterActiveColor =
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
::XCEngine::UI::UIColor placeholderTitleColor =
::XCEngine::UI::UIColor(0.93f, 0.94f, 0.96f, 1.0f);
::XCEngine::UI::UIColor placeholderTextColor =
::XCEngine::UI::UIColor(0.70f, 0.72f, 0.74f, 1.0f);
::XCEngine::UI::UIColor placeholderMutedColor =
::XCEngine::UI::UIColor(0.58f, 0.59f, 0.62f, 1.0f);
::XCEngine::UI::UIColor dropPreviewFillColor =
::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 0.14f);
::XCEngine::UI::UIColor dropPreviewBorderColor =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 0.78f);
};
struct UIEditorDockHostTabItemLayout {
std::string panelId = {};
std::string title = {};
// Cached title measurement used consistently by layout, hit testing and rendering.
float measuredLabelWidth = 0.0f;
bool active = false;
};
struct UIEditorDockHostSplitterLayout {
std::string nodeId = {};
UIEditorWorkspaceSplitAxis axis = UIEditorWorkspaceSplitAxis::Horizontal;
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect handleHitRect = {};
::XCEngine::UI::Layout::UISplitterConstraints constraints = {};
::XCEngine::UI::Layout::UISplitterMetrics metrics = {};
::XCEngine::UI::Layout::UISplitterLayoutResult splitterLayout = {};
bool hovered = false;
bool active = false;
};
struct UIEditorDockHostTabStackLayout {
std::string nodeId = {};
std::string selectedPanelId = {};
::XCEngine::UI::UIRect bounds = {};
std::vector<UIEditorDockHostTabItemLayout> items = {};
UIEditorTabStripState tabStripState = {};
UIEditorTabStripLayout tabStripLayout = {};
UIEditorPanelFrameState contentFrameState = {};
UIEditorPanelFrameLayout contentFrameLayout = {};
};
struct UIEditorDockHostDropPreviewLayout {
bool visible = false;
std::string targetNodeId = {};
UIEditorWorkspaceDockPlacement placement =
UIEditorWorkspaceDockPlacement::Center;
std::size_t insertionIndex = UIEditorTabStripInvalidIndex;
::XCEngine::UI::UIRect previewRect = {};
};
struct UIEditorDockHostLayout {
::XCEngine::UI::UIRect bounds = {};
std::vector<UIEditorDockHostSplitterLayout> splitters = {};
std::vector<UIEditorDockHostTabStackLayout> tabStacks = {};
UIEditorDockHostDropPreviewLayout dropPreview = {};
};
enum class UIEditorDockHostCursorKind : std::uint8_t {
Arrow = 0,
ResizeEW,
ResizeNS
};
// Allows higher-level compose to own panel body presentation while DockHost
// keeps drawing the surrounding chrome/frame.
struct UIEditorDockHostForegroundOptions {
std::vector<std::string> externalBodyPanelIds = {};
bool deferDropPreviewOverlay = false;
};
const UIEditorDockHostSplitterLayout* FindUIEditorDockHostSplitterLayout(
const UIEditorDockHostLayout& layout,
std::string_view nodeId);
UIEditorDockHostCursorKind ResolveUIEditorDockHostCursorKind(
const UIEditorDockHostLayout& layout);
UIEditorDockHostLayout BuildUIEditorDockHostLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const UIEditorDockHostState& state = {},
const UIEditorDockHostMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
UIEditorDockHostHitTarget HitTestUIEditorDockHost(
const UIEditorDockHostLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorDockHostBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorDockHostLayout& layout,
const UIEditorDockHostPalette& palette = {},
const UIEditorDockHostMetrics& metrics = {});
void AppendUIEditorDockHostForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorDockHostLayout& layout,
const UIEditorDockHostForegroundOptions& options = {},
const UIEditorDockHostPalette& palette = {},
const UIEditorDockHostMetrics& metrics = {});
void AppendUIEditorDockHostForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorDockHostLayout& layout,
const UIEditorDockHostPalette& palette,
const UIEditorDockHostMetrics& metrics = {});
void AppendUIEditorDockHostOverlay(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorDockHostLayout& layout,
const UIEditorDockHostPalette& palette = {},
const UIEditorDockHostMetrics& metrics = {});
void AppendUIEditorDockHost(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const UIEditorDockHostState& state = {},
const UIEditorDockHostForegroundOptions& foregroundOptions = {},
const UIEditorDockHostPalette& palette = {},
const UIEditorDockHostMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
void AppendUIEditorDockHost(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const UIEditorDockHostState& state,
const UIEditorDockHostPalette& palette,
const UIEditorDockHostMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,59 @@
#pragma once
#include <XCEditor/Collections/UIEditorTabStripInteraction.h>
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
#include <XCEditor/Docking/UIEditorDockHost.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UISplitterInteraction.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorDockHostTabStripInteractionEntry {
std::string nodeId = {};
UIEditorTabStripInteractionState state = {};
};
struct UIEditorDockHostInteractionState {
Widgets::UIEditorDockHostState dockHostState = {};
::XCEngine::UI::Widgets::UISplitterDragState splitterDragState = {};
std::vector<UIEditorDockHostTabStripInteractionEntry> tabStripInteractions = {};
std::string activeTabDragNodeId = {};
std::string activeTabDragPanelId = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorDockHostInteractionResult {
bool consumed = false;
bool commandExecuted = false;
bool layoutChanged = false;
bool detachRequested = false;
bool requestPointerCapture = false;
bool releasePointerCapture = false;
Widgets::UIEditorDockHostHitTarget hitTarget = {};
std::string activeSplitterNodeId = {};
std::string detachedNodeId = {};
std::string detachedPanelId = {};
UIEditorWorkspaceCommandResult commandResult = {};
UIEditorWorkspaceLayoutOperationResult layoutResult = {};
};
struct UIEditorDockHostInteractionFrame {
Widgets::UIEditorDockHostLayout layout = {};
UIEditorDockHostInteractionResult result = {};
bool focused = false;
};
UIEditorDockHostInteractionFrame UpdateUIEditorDockHostInteraction(
UIEditorDockHostInteractionState& state,
UIEditorWorkspaceController& controller,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorDockHostMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,30 @@
#pragma once
#include <XCEditor/Docking/UIEditorDockHost.h>
#include <XCEngine/UI/Types.h>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor {
struct UIEditorDockHostTabDropTarget {
bool valid = false;
std::string nodeId = {};
UIEditorWorkspaceDockPlacement placement = UIEditorWorkspaceDockPlacement::Center;
std::size_t insertionIndex = Widgets::UIEditorTabStripInvalidIndex;
};
bool TryResolveUIEditorDockHostTabDragHotspot(
const Widgets::UIEditorDockHostLayout& layout,
std::string_view nodeId,
std::string_view panelId,
const ::XCEngine::UI::UIPoint& point,
::XCEngine::UI::UIPoint& outHotspot);
UIEditorDockHostTabDropTarget ResolveUIEditorDockHostTabDropTarget(
const Widgets::UIEditorDockHostLayout& layout,
const ::XCEngine::UI::UIPoint& point);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,189 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorAssetFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
ValueBox,
PickerButton,
ClearButton
};
struct UIEditorAssetFieldSpec {
std::string fieldId = {};
std::string label = {};
std::string assetId = {};
std::string displayName = {};
std::string statusText = {};
std::string emptyText = "None";
::XCEngine::UI::UIColor tint = ::XCEngine::UI::UIColor(0.28f, 0.50f, 0.83f, 1.0f);
bool readOnly = false;
bool showPickerButton = true;
bool allowClear = true;
bool showStatusBadge = true;
};
struct UIEditorAssetFieldState {
UIEditorAssetFieldHitTargetKind hoveredTarget = UIEditorAssetFieldHitTargetKind::None;
UIEditorAssetFieldHitTargetKind activeTarget = UIEditorAssetFieldHitTargetKind::None;
bool focused = false;
};
struct UIEditorAssetFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float valueBoxMinWidth = 116.0f;
float controlInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 6.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float previewSize = 16.0f;
float previewInsetX = 4.0f;
float previewGap = 6.0f;
float previewGlyphFontSize = 10.0f;
float statusBadgeGap = 6.0f;
float statusBadgeMinWidth = 44.0f;
float statusBadgePaddingX = 6.0f;
float statusBadgeHeight = 14.0f;
float statusBadgeFontSize = 10.0f;
float actionButtonGap = 2.0f;
float actionButtonWidth = 20.0f;
float actionGlyphFontSize = 10.0f;
float actionGlyphInsetY = -1.0f;
float cornerRounding = 0.0f;
float valueBoxRounding = 2.0f;
float previewRounding = 2.0f;
float badgeRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorAssetFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.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 rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor valueBoxActiveColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor controlFocusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor emptyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor previewBaseColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor previewEmptyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor previewBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor previewGlyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor statusBadgeColor =
::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f);
::XCEngine::UI::UIColor statusBadgeBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor statusBadgeTextColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor actionButtonHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor actionButtonActiveColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor actionButtonColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor separatorColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor pickerGlyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor clearGlyphColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
};
struct UIEditorAssetFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect valueRect = {};
::XCEngine::UI::UIRect previewRect = {};
::XCEngine::UI::UIRect textRect = {};
::XCEngine::UI::UIRect statusBadgeRect = {};
::XCEngine::UI::UIRect pickerRect = {};
::XCEngine::UI::UIRect clearRect = {};
};
struct UIEditorAssetFieldHitTarget {
UIEditorAssetFieldHitTargetKind kind = UIEditorAssetFieldHitTargetKind::None;
};
bool HasUIEditorAssetFieldValue(const UIEditorAssetFieldSpec& spec);
std::string ResolveUIEditorAssetFieldValueText(const UIEditorAssetFieldSpec& spec);
std::string ResolveUIEditorAssetFieldPreviewGlyph(const UIEditorAssetFieldSpec& spec);
bool IsUIEditorAssetFieldPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
UIEditorAssetFieldLayout BuildUIEditorAssetFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldMetrics& metrics = {});
UIEditorAssetFieldHitTarget HitTestUIEditorAssetField(
const UIEditorAssetFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorAssetFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorAssetFieldLayout& layout,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette = {},
const UIEditorAssetFieldMetrics& metrics = {});
void AppendUIEditorAssetFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorAssetFieldLayout& layout,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette = {},
const UIEditorAssetFieldMetrics& metrics = {});
void AppendUIEditorAssetField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorAssetFieldSpec& spec,
const UIEditorAssetFieldState& state,
const UIEditorAssetFieldPalette& palette = {},
const UIEditorAssetFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,44 @@
#pragma once
#include <XCEditor/Fields/UIEditorAssetField.h>
#include <XCEngine/UI/Types.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorAssetFieldInteractionState {
Widgets::UIEditorAssetFieldState fieldState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorAssetFieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool valueChanged = false;
bool activateRequested = false;
bool pickerRequested = false;
bool clearRequested = false;
Widgets::UIEditorAssetFieldHitTarget hitTarget = {};
std::string assetIdBefore = {};
std::string assetIdAfter = {};
std::string displayNameBefore = {};
std::string displayNameAfter = {};
};
struct UIEditorAssetFieldInteractionFrame {
Widgets::UIEditorAssetFieldLayout layout = {};
UIEditorAssetFieldInteractionResult result = {};
};
UIEditorAssetFieldInteractionFrame UpdateUIEditorAssetFieldInteraction(
UIEditorAssetFieldInteractionState& state,
Widgets::UIEditorAssetFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorAssetFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,117 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorBoolFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
Checkbox
};
struct UIEditorBoolFieldSpec {
std::string fieldId = {};
std::string label = {};
bool value = false;
bool readOnly = false;
};
struct UIEditorBoolFieldState {
UIEditorBoolFieldHitTargetKind hoveredTarget = UIEditorBoolFieldHitTargetKind::None;
bool focused = false;
bool active = false;
};
struct UIEditorBoolFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float checkboxSize = 18.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float checkboxGlyphInsetX = 2.0f;
float checkboxGlyphInsetY = -1.0f;
float checkboxGlyphFontSize = 10.0f;
float cornerRounding = 0.0f;
float checkboxRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorBoolFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor checkboxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor checkboxHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor checkboxReadOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor checkboxBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor checkboxMarkColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
};
struct UIEditorBoolFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect checkboxRect = {};
::XCEngine::UI::UIRect checkmarkRect = {};
};
struct UIEditorBoolFieldHitTarget {
UIEditorBoolFieldHitTargetKind kind = UIEditorBoolFieldHitTargetKind::None;
};
UIEditorBoolFieldLayout BuildUIEditorBoolFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorBoolFieldSpec& spec,
const UIEditorBoolFieldMetrics& metrics = {});
UIEditorBoolFieldHitTarget HitTestUIEditorBoolField(
const UIEditorBoolFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorBoolFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorBoolFieldLayout& layout,
const UIEditorBoolFieldSpec& spec,
const UIEditorBoolFieldState& state,
const UIEditorBoolFieldPalette& palette = {},
const UIEditorBoolFieldMetrics& metrics = {});
void AppendUIEditorBoolFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorBoolFieldLayout& layout,
const UIEditorBoolFieldSpec& spec,
const UIEditorBoolFieldPalette& palette = {},
const UIEditorBoolFieldMetrics& metrics = {});
void AppendUIEditorBoolField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorBoolFieldSpec& spec,
const UIEditorBoolFieldState& state,
const UIEditorBoolFieldPalette& palette = {},
const UIEditorBoolFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,38 @@
#pragma once
#include <XCEditor/Fields/UIEditorBoolField.h>
#include <XCEngine/UI/Types.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorBoolFieldInteractionState {
Widgets::UIEditorBoolFieldState fieldState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorBoolFieldInteractionResult {
bool consumed = false;
bool valueChanged = false;
bool focusedChanged = false;
bool newValue = false;
Widgets::UIEditorBoolFieldHitTarget hitTarget = {};
};
struct UIEditorBoolFieldInteractionFrame {
Widgets::UIEditorBoolFieldLayout layout = {};
UIEditorBoolFieldInteractionResult result = {};
};
UIEditorBoolFieldInteractionFrame UpdateUIEditorBoolFieldInteraction(
UIEditorBoolFieldInteractionState& state,
bool& value,
const ::XCEngine::UI::UIRect& bounds,
const Widgets::UIEditorBoolFieldSpec& spec,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorBoolFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,223 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorColorFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
Swatch,
PopupSurface,
PopupCloseButton,
HueWheel,
SaturationValue,
RedChannel,
GreenChannel,
BlueChannel,
AlphaChannel
};
struct UIEditorColorFieldSpec {
std::string fieldId = {};
std::string label = {};
::XCEngine::UI::UIColor value = {};
bool showAlpha = true;
bool readOnly = false;
bool embeddedEditor = false;
};
struct UIEditorColorFieldState {
UIEditorColorFieldHitTargetKind hoveredTarget = UIEditorColorFieldHitTargetKind::None;
UIEditorColorFieldHitTargetKind activeTarget = UIEditorColorFieldHitTargetKind::None;
bool focused = false;
bool popupOpen = false;
float hue = 0.0f;
bool hueValid = false;
};
struct UIEditorColorFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float swatchWidth = 54.0f;
float swatchInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float popupWidth = 292.0f;
float popupPadding = 10.0f;
float popupGapY = 6.0f;
float popupHeaderHeight = 30.0f;
float popupTopRowHeight = 34.0f;
float popupPreviewWidth = 96.0f;
float popupPreviewHeight = 28.0f;
float popupCloseButtonSize = 18.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
float swatchRounding = 2.0f;
float popupBorderRounding = 3.0f;
float wheelOuterRadius = 110.0f;
float wheelRingThickness = 24.0f;
float saturationValueSize = 114.0f;
float wheelRegionHeight = 220.0f;
float channelRowHeight = 20.0f;
float numericBoxWidth = 62.0f;
float channelLabelWidth = 12.0f;
float hexLabelWidth = 84.0f;
float controlRowSpacing = 8.0f;
float popupFieldInset = 6.0f;
float titleFontSize = 12.0f;
float valueFontSize = 11.0f;
float valueTextInsetX = 6.0f;
float valueTextInsetY = 0.0f;
float checkerSize = 6.0f;
float handleRadius = 8.0f;
float ringHandleRadius = 12.0f;
};
struct UIEditorColorFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.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 rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor swatchBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor swatchHoverBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor swatchReadOnlyOverlayColor =
::XCEngine::UI::UIColor(0.08f, 0.08f, 0.08f, 0.18f);
::XCEngine::UI::UIColor popupColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor popupBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor popupHeaderColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor popupTitleColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor closeButtonColor =
::XCEngine::UI::UIColor(0.20f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor closeButtonHoverColor =
::XCEngine::UI::UIColor(0.28f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor closeGlyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor popupTextColor =
::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f);
::XCEngine::UI::UIColor popupTextMutedColor =
::XCEngine::UI::UIColor(0.66f, 0.66f, 0.66f, 1.0f);
::XCEngine::UI::UIColor previewBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor previewBaseColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor checkerLightColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor checkerDarkColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor sliderBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor numericBoxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor numericBoxBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor numericBoxTextColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor handleFillColor =
::XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f);
::XCEngine::UI::UIColor handleStrokeColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 0.5f);
};
struct UIEditorColorFieldLayout {
::XCEngine::UI::UIRect bounds = {};
bool embeddedEditor = false;
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect swatchRect = {};
::XCEngine::UI::UIRect popupRect = {};
::XCEngine::UI::UIRect popupHeaderRect = {};
::XCEngine::UI::UIRect popupTitleRect = {};
::XCEngine::UI::UIRect popupCloseButtonRect = {};
::XCEngine::UI::UIRect popupBodyRect = {};
::XCEngine::UI::UIRect popupTopRowRect = {};
::XCEngine::UI::UIRect popupPreviewRect = {};
::XCEngine::UI::UIRect popupWheelRect = {};
::XCEngine::UI::UIPoint hueWheelCenter = {};
float hueWheelOuterRadius = 0.0f;
float hueWheelInnerRadius = 0.0f;
::XCEngine::UI::UIRect saturationValueRect = {};
::XCEngine::UI::UIRect redLabelRect = {};
::XCEngine::UI::UIRect redSliderRect = {};
::XCEngine::UI::UIRect redValueRect = {};
::XCEngine::UI::UIRect greenLabelRect = {};
::XCEngine::UI::UIRect greenSliderRect = {};
::XCEngine::UI::UIRect greenValueRect = {};
::XCEngine::UI::UIRect blueLabelRect = {};
::XCEngine::UI::UIRect blueSliderRect = {};
::XCEngine::UI::UIRect blueValueRect = {};
::XCEngine::UI::UIRect alphaLabelRect = {};
::XCEngine::UI::UIRect alphaSliderRect = {};
::XCEngine::UI::UIRect alphaValueRect = {};
::XCEngine::UI::UIRect hexLabelRect = {};
::XCEngine::UI::UIRect hexValueRect = {};
};
struct UIEditorColorFieldHitTarget {
UIEditorColorFieldHitTargetKind kind = UIEditorColorFieldHitTargetKind::None;
};
std::string FormatUIEditorColorFieldRgbaText(
const UIEditorColorFieldSpec& spec);
std::string FormatUIEditorColorFieldHexText(
const UIEditorColorFieldSpec& spec);
UIEditorColorFieldLayout BuildUIEditorColorFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldMetrics& metrics = {},
const ::XCEngine::UI::UIRect& viewportRect = {});
UIEditorColorFieldHitTarget HitTestUIEditorColorField(
const UIEditorColorFieldLayout& layout,
bool popupOpen,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorColorFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorColorFieldLayout& layout,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette = {},
const UIEditorColorFieldMetrics& metrics = {});
void AppendUIEditorColorFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorColorFieldLayout& layout,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette = {},
const UIEditorColorFieldMetrics& metrics = {});
void AppendUIEditorColorField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorColorFieldSpec& spec,
const UIEditorColorFieldState& state,
const UIEditorColorFieldPalette& palette = {},
const UIEditorColorFieldMetrics& metrics = {},
const ::XCEngine::UI::UIRect& viewportRect = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,41 @@
#pragma once
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEngine/UI/Types.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorColorFieldInteractionState {
Widgets::UIEditorColorFieldState colorFieldState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorColorFieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool popupOpened = false;
bool popupClosed = false;
bool colorChanged = false;
Widgets::UIEditorColorFieldHitTarget hitTarget = {};
::XCEngine::UI::UIColor valueBefore = {};
::XCEngine::UI::UIColor valueAfter = {};
};
struct UIEditorColorFieldInteractionFrame {
Widgets::UIEditorColorFieldLayout layout = {};
UIEditorColorFieldInteractionResult result = {};
};
UIEditorColorFieldInteractionFrame UpdateUIEditorColorFieldInteraction(
UIEditorColorFieldInteractionState& state,
Widgets::UIEditorColorFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorColorFieldMetrics& metrics = {},
const ::XCEngine::UI::UIRect& viewportRect = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,110 @@
#pragma once
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor {
inline constexpr std::size_t UIEditorEditableFieldInvalidComponentIndex =
static_cast<std::size_t>(-1);
inline constexpr std::uint64_t
UIEditorEditableFieldDoubleClickIntervalNanoseconds = 500000000ull;
inline constexpr float UIEditorEditableFieldDoubleClickDistanceSquared = 16.0f;
inline constexpr float UIEditorEditableFieldDragActivationDistanceSquared = 9.0f;
struct UIEditorEditableFieldSession {
std::string activeFieldId = {};
std::string lastClickFieldId = {};
::XCEngine::UI::Text::UITextInputState textInputState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
::XCEngine::UI::UIPoint pointerDownPosition = {};
::XCEngine::UI::UIPoint lastClickPosition = {};
std::size_t activeComponentIndex =
UIEditorEditableFieldInvalidComponentIndex;
std::size_t lastClickComponentIndex =
UIEditorEditableFieldInvalidComponentIndex;
std::uint64_t lastClickTimestampNanoseconds = 0u;
std::uint64_t caretBlinkStartNanoseconds = 0u;
double dragStartValue = 0.0;
bool focused = false;
bool editing = false;
bool dragArmed = false;
bool dragActive = false;
bool hasPointerPosition = false;
};
bool ShouldUseUIEditorEditableFieldPointerPosition(
const ::XCEngine::UI::UIInputEvent& event);
void UpdateUIEditorEditableFieldPointerPosition(
UIEditorEditableFieldSession& session,
const ::XCEngine::UI::UIInputEvent& event);
float ComputeUIEditorEditableFieldDistanceSquared(
const ::XCEngine::UI::UIPoint& a,
const ::XCEngine::UI::UIPoint& b);
void RestartUIEditorEditableFieldCaretBlink(
UIEditorEditableFieldSession& session);
bool BeginUIEditorEditableFieldEdit(
UIEditorEditableFieldSession& session,
std::string_view fieldId,
std::size_t componentIndex,
std::string_view baselineText,
bool clearText);
void CommitUIEditorEditableFieldEdit(
UIEditorEditableFieldSession& session);
void CancelUIEditorEditableFieldEdit(
UIEditorEditableFieldSession& session);
void ArmUIEditorEditableFieldDrag(
UIEditorEditableFieldSession& session,
std::string_view fieldId,
std::size_t componentIndex,
double startValue);
bool TryActivateUIEditorEditableFieldDrag(
UIEditorEditableFieldSession& session,
const ::XCEngine::UI::UIInputEvent& event,
float activationDistanceSquared =
UIEditorEditableFieldDragActivationDistanceSquared);
double ResolveUIEditorEditableFieldDragDelta(
const UIEditorEditableFieldSession& session,
double sensitivity);
void EndUIEditorEditableFieldDrag(UIEditorEditableFieldSession& session);
bool IsUIEditorEditableFieldDoubleClick(
const UIEditorEditableFieldSession& session,
std::string_view fieldId,
const ::XCEngine::UI::UIInputEvent& event,
std::size_t componentIndex);
void RecordUIEditorEditableFieldClick(
UIEditorEditableFieldSession& session,
std::string_view fieldId,
const ::XCEngine::UI::UIInputEvent& event,
std::size_t componentIndex);
::XCEngine::UI::Text::UITextInputEditResult HandleUIEditorEditableFieldKeyDown(
UIEditorEditableFieldSession& session,
std::int32_t keyCode,
const ::XCEngine::UI::UIInputModifiers& modifiers);
bool IsUIEditorEditableFieldCharacterInsertable(
std::uint32_t character);
bool InsertUIEditorEditableFieldCharacter(
UIEditorEditableFieldSession& session,
std::uint32_t character);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,131 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorEnumFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
ValueBox,
DropdownArrow
};
struct UIEditorEnumFieldSpec {
std::string fieldId = {};
std::string label = {};
std::vector<std::string> options = {};
std::size_t selectedIndex = 0u;
bool readOnly = false;
};
struct UIEditorEnumFieldState {
UIEditorEnumFieldHitTargetKind hoveredTarget = UIEditorEnumFieldHitTargetKind::None;
bool focused = false;
bool active = false;
bool popupOpen = false;
};
struct UIEditorEnumFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float valueBoxMinWidth = 96.0f;
float controlInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 3.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float dropdownArrowWidth = 20.0f;
float dropdownArrowInsetX = 0.0f;
float dropdownArrowInsetY = -1.0f;
float dropdownArrowFontSize = 10.0f;
float cornerRounding = 0.0f;
float valueBoxRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorEnumFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor arrowColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
};
struct UIEditorEnumFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect valueRect = {};
::XCEngine::UI::UIRect arrowRect = {};
};
struct UIEditorEnumFieldHitTarget {
UIEditorEnumFieldHitTargetKind kind = UIEditorEnumFieldHitTargetKind::None;
};
std::string ResolveUIEditorEnumFieldValueText(const UIEditorEnumFieldSpec& spec);
UIEditorEnumFieldLayout BuildUIEditorEnumFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorEnumFieldSpec& spec,
const UIEditorEnumFieldMetrics& metrics = {});
UIEditorEnumFieldHitTarget HitTestUIEditorEnumField(
const UIEditorEnumFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorEnumFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorEnumFieldLayout& layout,
const UIEditorEnumFieldSpec& spec,
const UIEditorEnumFieldState& state,
const UIEditorEnumFieldPalette& palette = {},
const UIEditorEnumFieldMetrics& metrics = {});
void AppendUIEditorEnumFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorEnumFieldLayout& layout,
const UIEditorEnumFieldSpec& spec,
const UIEditorEnumFieldPalette& palette = {},
const UIEditorEnumFieldMetrics& metrics = {});
void AppendUIEditorEnumField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorEnumFieldSpec& spec,
const UIEditorEnumFieldState& state,
const UIEditorEnumFieldPalette& palette = {},
const UIEditorEnumFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,53 @@
#pragma once
#include <XCEditor/Menu/UIEditorMenuPopup.h>
#include <XCEditor/Fields/UIEditorEnumField.h>
#include <XCEngine/UI/Types.h>
#include <cstddef>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorEnumFieldInteractionState {
Widgets::UIEditorEnumFieldState fieldState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
std::size_t highlightedIndex = Widgets::UIEditorMenuPopupInvalidIndex;
std::size_t pressedPopupIndex = Widgets::UIEditorMenuPopupInvalidIndex;
bool hasPointerPosition = false;
bool popupOpen = false;
};
struct UIEditorEnumFieldInteractionResult {
bool consumed = false;
bool selectionChanged = false;
bool focusedChanged = false;
bool popupOpened = false;
bool popupClosed = false;
std::size_t selectedIndex = 0u;
Widgets::UIEditorEnumFieldHitTarget hitTarget = {};
std::size_t popupItemIndex = Widgets::UIEditorMenuPopupInvalidIndex;
};
struct UIEditorEnumFieldInteractionFrame {
Widgets::UIEditorEnumFieldLayout layout = {};
Widgets::UIEditorMenuPopupLayout popupLayout = {};
Widgets::UIEditorMenuPopupState popupState = {};
std::vector<Widgets::UIEditorMenuPopupItem> popupItems = {};
bool popupOpen = false;
UIEditorEnumFieldInteractionResult result = {};
};
UIEditorEnumFieldInteractionFrame UpdateUIEditorEnumFieldInteraction(
UIEditorEnumFieldInteractionState& state,
std::size_t& selectedIndex,
const ::XCEngine::UI::UIRect& bounds,
const Widgets::UIEditorEnumFieldSpec& spec,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorEnumFieldMetrics& metrics = {},
const Widgets::UIEditorMenuPopupMetrics& popupMetrics = {},
const ::XCEngine::UI::UIRect& viewportRect = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,101 @@
#pragma once
#include <XCEditor/Fields/UIEditorAssetField.h>
#include <XCEditor/Fields/UIEditorBoolField.h>
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEditor/Fields/UIEditorEnumField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorObjectField.h>
#include <XCEditor/Fields/UIEditorPropertyGrid.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <XCEditor/Fields/UIEditorVector4Field.h>
namespace XCEngine::UI::Editor {
const Widgets::UIEditorPropertyGridMetrics& GetUIEditorFixedPropertyGridMetrics();
const Widgets::UIEditorPropertyGridPalette& GetUIEditorFixedPropertyGridPalette();
Widgets::UIEditorBoolFieldMetrics BuildUIEditorPropertyGridBoolFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorBoolFieldMetrics& fallback = {});
Widgets::UIEditorBoolFieldPalette BuildUIEditorPropertyGridBoolFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorBoolFieldPalette& fallback = {});
Widgets::UIEditorNumberFieldMetrics BuildUIEditorPropertyGridNumberFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorNumberFieldMetrics& fallback = {});
Widgets::UIEditorNumberFieldPalette BuildUIEditorPropertyGridNumberFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorNumberFieldPalette& fallback = {});
Widgets::UIEditorTextFieldMetrics BuildUIEditorPropertyGridTextFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorTextFieldMetrics& fallback = {});
Widgets::UIEditorTextFieldPalette BuildUIEditorPropertyGridTextFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorTextFieldPalette& fallback = {});
Widgets::UIEditorVector2FieldMetrics BuildUIEditorPropertyGridVector2FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector2FieldMetrics& fallback = {});
Widgets::UIEditorVector2FieldPalette BuildUIEditorPropertyGridVector2FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector2FieldPalette& fallback = {});
Widgets::UIEditorVector3FieldMetrics BuildUIEditorPropertyGridVector3FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector3FieldMetrics& fallback = {});
Widgets::UIEditorVector3FieldPalette BuildUIEditorPropertyGridVector3FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector3FieldPalette& fallback = {});
Widgets::UIEditorVector4FieldMetrics BuildUIEditorPropertyGridVector4FieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorVector4FieldMetrics& fallback = {});
Widgets::UIEditorVector4FieldPalette BuildUIEditorPropertyGridVector4FieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorVector4FieldPalette& fallback = {});
Widgets::UIEditorEnumFieldMetrics BuildUIEditorPropertyGridEnumFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorEnumFieldMetrics& fallback = {});
Widgets::UIEditorEnumFieldPalette BuildUIEditorPropertyGridEnumFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorEnumFieldPalette& fallback = {});
Widgets::UIEditorColorFieldMetrics BuildUIEditorPropertyGridColorFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorColorFieldMetrics& fallback = {});
Widgets::UIEditorColorFieldPalette BuildUIEditorPropertyGridColorFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorColorFieldPalette& fallback = {});
Widgets::UIEditorObjectFieldMetrics BuildUIEditorPropertyGridObjectFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorObjectFieldMetrics& fallback = {});
Widgets::UIEditorObjectFieldPalette BuildUIEditorPropertyGridObjectFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorObjectFieldPalette& fallback = {});
Widgets::UIEditorAssetFieldMetrics BuildUIEditorPropertyGridAssetFieldMetrics(
const Widgets::UIEditorPropertyGridMetrics& propertyGridMetrics,
const Widgets::UIEditorAssetFieldMetrics& fallback = {});
Widgets::UIEditorAssetFieldPalette BuildUIEditorPropertyGridAssetFieldPalette(
const Widgets::UIEditorPropertyGridPalette& propertyGridPalette,
const Widgets::UIEditorAssetFieldPalette& fallback = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,153 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEngine/UI/DrawData.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorNumberFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
ValueBox
};
struct UIEditorNumberFieldSpec {
std::string fieldId = {};
std::string label = {};
double value = 0.0;
double step = 1.0;
double minValue = 0.0;
double maxValue = 100.0;
bool integerMode = true;
bool readOnly = false;
};
struct UIEditorNumberFieldState {
UIEditorNumberFieldHitTargetKind hoveredTarget = UIEditorNumberFieldHitTargetKind::None;
UIEditorNumberFieldHitTargetKind activeTarget = UIEditorNumberFieldHitTargetKind::None;
bool focused = false;
bool editing = false;
std::size_t caretOffset = 0u;
std::uint64_t caretBlinkStartNanoseconds = 0u;
std::string displayText = {};
};
struct UIEditorNumberFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float valueBoxMinWidth = 96.0f;
float controlInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 5.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float cornerRounding = 0.0f;
float valueBoxRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorNumberFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.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 rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor valueBoxEditingColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor controlFocusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
};
struct UIEditorNumberFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect valueRect = {};
};
struct UIEditorNumberFieldHitTarget {
UIEditorNumberFieldHitTargetKind kind = UIEditorNumberFieldHitTargetKind::None;
};
bool IsUIEditorNumberFieldPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
double NormalizeUIEditorNumberFieldValue(
const UIEditorNumberFieldSpec& spec,
double value);
bool TryParseUIEditorNumberFieldValue(
const UIEditorNumberFieldSpec& spec,
std::string_view text,
double& outValue);
std::string FormatUIEditorNumberFieldValue(const UIEditorNumberFieldSpec& spec);
UIEditorNumberFieldLayout BuildUIEditorNumberFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorNumberFieldSpec& spec,
const UIEditorNumberFieldMetrics& metrics = {});
UIEditorNumberFieldHitTarget HitTestUIEditorNumberField(
const UIEditorNumberFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorNumberFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorNumberFieldLayout& layout,
const UIEditorNumberFieldSpec& spec,
const UIEditorNumberFieldState& state,
const UIEditorNumberFieldPalette& palette = {},
const UIEditorNumberFieldMetrics& metrics = {});
void AppendUIEditorNumberFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorNumberFieldLayout& layout,
const UIEditorNumberFieldSpec& spec,
const UIEditorNumberFieldState& state,
const UIEditorNumberFieldPalette& palette = {},
const UIEditorNumberFieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
void AppendUIEditorNumberField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorNumberFieldSpec& spec,
const UIEditorNumberFieldState& state,
const UIEditorNumberFieldPalette& palette = {},
const UIEditorNumberFieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,44 @@
#pragma once
#include <XCEditor/Fields/UIEditorEditableFieldCore.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorNumberFieldInteractionState {
UIEditorEditableFieldSession session = {};
Widgets::UIEditorNumberFieldState numberFieldState = {};
};
struct UIEditorNumberFieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool valueChanged = false;
bool stepApplied = false;
bool editStarted = false;
bool editCommitted = false;
bool editCommitRejected = false;
bool editCanceled = false;
Widgets::UIEditorNumberFieldHitTarget hitTarget = {};
double valueBefore = 0.0;
double valueAfter = 0.0;
double stepDelta = 0.0;
std::string committedText = {};
};
struct UIEditorNumberFieldInteractionFrame {
Widgets::UIEditorNumberFieldLayout layout = {};
UIEditorNumberFieldInteractionResult result = {};
};
UIEditorNumberFieldInteractionFrame UpdateUIEditorNumberFieldInteraction(
UIEditorNumberFieldInteractionState& state,
Widgets::UIEditorNumberFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorNumberFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,154 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorObjectFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
ValueBox,
ClearButton,
PickerButton
};
struct UIEditorObjectFieldSpec {
std::string fieldId = {};
std::string label = {};
std::string objectName = {};
std::string objectTypeName = {};
std::string emptyText = "(none)";
bool hasValue = false;
bool readOnly = false;
bool showClearButton = true;
bool showPickerButton = true;
};
struct UIEditorObjectFieldState {
UIEditorObjectFieldHitTargetKind hoveredTarget = UIEditorObjectFieldHitTargetKind::None;
UIEditorObjectFieldHitTargetKind activeTarget = UIEditorObjectFieldHitTargetKind::None;
bool focused = false;
};
struct UIEditorObjectFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float valueBoxMinWidth = 96.0f;
float controlInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 5.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float typeTextInsetX = 5.0f;
float typeTextInsetY = 0.0f;
float typeFontSize = 10.0f;
float typeMaxWidth = 96.0f;
float typeMinWidth = 44.0f;
float valueTypeGap = 6.0f;
float buttonWidth = 20.0f;
float buttonGlyphInsetY = -1.0f;
float buttonGlyphFontSize = 10.0f;
float cornerRounding = 0.0f;
float valueBoxRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorObjectFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.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 rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor controlFocusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor buttonColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor buttonHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor buttonActiveColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor buttonGlyphColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor separatorColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor emptyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor typeColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
};
struct UIEditorObjectFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect valueRect = {};
::XCEngine::UI::UIRect typeRect = {};
::XCEngine::UI::UIRect clearButtonRect = {};
::XCEngine::UI::UIRect pickerButtonRect = {};
};
struct UIEditorObjectFieldHitTarget {
UIEditorObjectFieldHitTargetKind kind = UIEditorObjectFieldHitTargetKind::None;
};
std::string ResolveUIEditorObjectFieldDisplayText(const UIEditorObjectFieldSpec& spec);
UIEditorObjectFieldLayout BuildUIEditorObjectFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldMetrics& metrics = {});
UIEditorObjectFieldHitTarget HitTestUIEditorObjectField(
const UIEditorObjectFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorObjectFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorObjectFieldLayout& layout,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldState& state,
const UIEditorObjectFieldPalette& palette = {},
const UIEditorObjectFieldMetrics& metrics = {});
void AppendUIEditorObjectFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorObjectFieldLayout& layout,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldPalette& palette = {},
const UIEditorObjectFieldMetrics& metrics = {});
void AppendUIEditorObjectField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorObjectFieldSpec& spec,
const UIEditorObjectFieldState& state,
const UIEditorObjectFieldPalette& palette = {},
const UIEditorObjectFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,37 @@
#pragma once
#include <XCEditor/Fields/UIEditorObjectField.h>
#include <XCEngine/UI/Types.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorObjectFieldInteractionState {
Widgets::UIEditorObjectFieldState fieldState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool hasPointerPosition = false;
};
struct UIEditorObjectFieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool activateRequested = false;
bool clearRequested = false;
Widgets::UIEditorObjectFieldHitTarget hitTarget = {};
};
struct UIEditorObjectFieldInteractionFrame {
Widgets::UIEditorObjectFieldLayout layout = {};
UIEditorObjectFieldInteractionResult result = {};
};
UIEditorObjectFieldInteractionFrame UpdateUIEditorObjectFieldInteraction(
UIEditorObjectFieldInteractionState& state,
const Widgets::UIEditorObjectFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorObjectFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,357 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UIPropertyEditModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEditor/Fields/UIEditorAssetField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <XCEditor/Fields/UIEditorVector4Field.h>
#include <XCEditor/Menu/UIEditorMenuPopup.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorPropertyGridInvalidIndex = static_cast<std::size_t>(-1);
enum class UIEditorPropertyGridFieldKind : std::uint8_t {
Text = 0,
Bool,
Number,
Enum,
Asset,
Color,
Vector2,
Vector3,
Vector4
};
enum class UIEditorPropertyGridHitTargetKind : std::uint8_t {
None = 0,
SectionHeader,
FieldRow,
ValueBox
};
struct UIEditorPropertyGridFieldLocation {
std::size_t sectionIndex = UIEditorPropertyGridInvalidIndex;
std::size_t fieldIndex = UIEditorPropertyGridInvalidIndex;
constexpr bool IsValid() const {
return sectionIndex != UIEditorPropertyGridInvalidIndex &&
fieldIndex != UIEditorPropertyGridInvalidIndex;
}
};
struct UIEditorPropertyGridNumberFieldValue {
double value = 0.0;
double step = 1.0;
double minValue = 0.0;
double maxValue = 100.0;
bool integerMode = true;
};
struct UIEditorPropertyGridEnumFieldValue {
std::vector<std::string> options = {};
std::size_t selectedIndex = 0u;
};
struct UIEditorPropertyGridAssetFieldValue {
std::string assetId = {};
std::string displayName = {};
std::string statusText = {};
std::string emptyText = "None";
::XCEngine::UI::UIColor tint = ::XCEngine::UI::UIColor(0.28f, 0.50f, 0.83f, 1.0f);
bool showPickerButton = true;
bool allowClear = true;
bool showStatusBadge = true;
};
struct UIEditorPropertyGridColorFieldValue {
::XCEngine::UI::UIColor value = {};
bool showAlpha = true;
};
struct UIEditorPropertyGridVector2FieldValue {
std::array<double, 2u> values = { 0.0, 0.0 };
std::array<std::string, 2u> componentLabels = { std::string("X"), std::string("Y") };
double step = 0.1;
double minValue = -1000000.0;
double maxValue = 1000000.0;
bool integerMode = false;
};
struct UIEditorPropertyGridVector3FieldValue {
std::array<double, 3u> values = { 0.0, 0.0, 0.0 };
std::array<std::string, 3u> componentLabels = {
std::string("X"),
std::string("Y"),
std::string("Z")
};
double step = 0.1;
double minValue = -1000000.0;
double maxValue = 1000000.0;
bool integerMode = false;
};
struct UIEditorPropertyGridVector4FieldValue {
std::array<double, 4u> values = { 0.0, 0.0, 0.0, 0.0 };
std::array<std::string, 4u> componentLabels = {
std::string("X"),
std::string("Y"),
std::string("Z"),
std::string("W")
};
double step = 0.1;
double minValue = -1000000.0;
double maxValue = 1000000.0;
bool integerMode = false;
};
struct UIEditorPropertyGridField {
std::string fieldId = {};
std::string label = {};
std::string valueText = {};
bool readOnly = false;
float desiredHeight = 0.0f;
float controlMinWidth = 0.0f;
UIEditorPropertyGridFieldKind kind = UIEditorPropertyGridFieldKind::Text;
bool boolValue = false;
UIEditorPropertyGridNumberFieldValue numberValue = {};
UIEditorPropertyGridEnumFieldValue enumValue = {};
UIEditorPropertyGridAssetFieldValue assetValue = {};
UIEditorPropertyGridColorFieldValue colorValue = {};
UIEditorPropertyGridVector2FieldValue vector2Value = {};
UIEditorPropertyGridVector3FieldValue vector3Value = {};
UIEditorPropertyGridVector4FieldValue vector4Value = {};
};
struct UIEditorPropertyGridSection {
std::string sectionId = {};
std::string title = {};
std::vector<UIEditorPropertyGridField> fields = {};
float desiredHeaderHeight = 0.0f;
};
struct UIEditorPropertyGridNumberFieldVisualState {
std::string fieldId = {};
UIEditorNumberFieldState state = {};
};
struct UIEditorPropertyGridTextFieldVisualState {
std::string fieldId = {};
UIEditorTextFieldState state = {};
};
struct UIEditorPropertyGridAssetFieldVisualState {
std::string fieldId = {};
UIEditorAssetFieldState state = {};
};
struct UIEditorPropertyGridVector2FieldVisualState {
std::string fieldId = {};
UIEditorVector2FieldState state = {};
};
struct UIEditorPropertyGridVector3FieldVisualState {
std::string fieldId = {};
UIEditorVector3FieldState state = {};
};
struct UIEditorPropertyGridVector4FieldVisualState {
std::string fieldId = {};
UIEditorVector4FieldState state = {};
};
struct UIEditorPropertyGridState {
std::string hoveredSectionId = {};
std::string hoveredFieldId = {};
UIEditorPropertyGridHitTargetKind hoveredHitTarget = UIEditorPropertyGridHitTargetKind::None;
bool focused = false;
std::string pressedFieldId = {};
std::string popupFieldId = {};
std::size_t popupHighlightedIndex = UIEditorPropertyGridInvalidIndex;
std::vector<UIEditorPropertyGridNumberFieldVisualState> numberFieldStates = {};
std::vector<UIEditorPropertyGridTextFieldVisualState> textFieldStates = {};
std::vector<UIEditorPropertyGridAssetFieldVisualState> assetFieldStates = {};
std::vector<UIEditorPropertyGridVector2FieldVisualState> vector2FieldStates = {};
std::vector<UIEditorPropertyGridVector3FieldVisualState> vector3FieldStates = {};
std::vector<UIEditorPropertyGridVector4FieldVisualState> vector4FieldStates = {};
};
struct UIEditorPropertyGridMetrics {
float contentInset = 8.0f;
float sectionGap = 8.0f;
float sectionHeaderHeight = 32.0f;
float fieldRowHeight = 32.0f;
float rowGap = 2.0f;
float horizontalPadding = 12.0f;
float sectionHeaderHorizontalPadding = 6.0f;
float controlColumnStart = 236.0f;
float labelControlGap = 20.0f;
float disclosureExtent = 12.0f;
float disclosureLabelGap = 8.0f;
float sectionTextInsetY = 8.0f;
float sectionFontSize = 12.0f;
float disclosureGlyphInsetX = 2.0f;
float disclosureGlyphInsetY = -1.0f;
float disclosureGlyphFontSize = 12.0f;
float labelTextInsetY = 8.0f;
float labelFontSize = 12.0f;
float valueTextInsetY = 8.0f;
float valueFontSize = 12.0f;
float valueBoxInsetY = 4.0f;
float valueBoxInsetX = 8.0f;
float tagFontSize = 11.0f;
float cornerRounding = 6.0f;
float valueBoxRounding = 5.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 2.0f;
float editOutlineThickness = 1.0f;
};
struct UIEditorPropertyGridPalette {
::XCEngine::UI::UIColor surfaceColor =
::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 focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor sectionHeaderColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor sectionHeaderHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor fieldHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor fieldSelectedColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor fieldSelectedFocusedColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor valueBoxEditingColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
::XCEngine::UI::UIColor valueBoxReadOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor valueBoxBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor valueBoxEditingBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor disclosureColor =
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f);
::XCEngine::UI::UIColor sectionTextColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor labelTextColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueTextColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueTextColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor editTagColor =
::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 1.0f);
};
struct UIEditorPropertyGridLayout {
::XCEngine::UI::UIRect bounds = {};
std::vector<std::size_t> sectionIndices = {};
std::vector<::XCEngine::UI::UIRect> sectionHeaderRects = {};
std::vector<::XCEngine::UI::UIRect> sectionDisclosureRects = {};
std::vector<::XCEngine::UI::UIRect> sectionTitleRects = {};
std::vector<bool> sectionExpanded = {};
std::vector<std::size_t> visibleFieldSectionIndices = {};
std::vector<std::size_t> visibleFieldIndices = {};
std::vector<::XCEngine::UI::UIRect> fieldRowRects = {};
std::vector<::XCEngine::UI::UIRect> fieldLabelRects = {};
std::vector<::XCEngine::UI::UIRect> fieldValueRects = {};
std::vector<bool> fieldReadOnly = {};
};
struct UIEditorPropertyGridHitTarget {
UIEditorPropertyGridHitTargetKind kind = UIEditorPropertyGridHitTargetKind::None;
std::size_t sectionIndex = UIEditorPropertyGridInvalidIndex;
std::size_t fieldIndex = UIEditorPropertyGridInvalidIndex;
std::size_t visibleFieldIndex = UIEditorPropertyGridInvalidIndex;
};
bool IsUIEditorPropertyGridPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
std::size_t FindUIEditorPropertyGridSectionIndex(
const std::vector<UIEditorPropertyGridSection>& sections,
std::string_view sectionId);
UIEditorPropertyGridFieldLocation FindUIEditorPropertyGridFieldLocation(
const std::vector<UIEditorPropertyGridSection>& sections,
std::string_view fieldId);
std::string ResolveUIEditorPropertyGridFieldValueText(
const UIEditorPropertyGridField& field);
std::size_t FindUIEditorPropertyGridVisibleFieldIndex(
const UIEditorPropertyGridLayout& layout,
std::string_view fieldId,
const std::vector<UIEditorPropertyGridSection>& sections);
UIEditorPropertyGridLayout BuildUIEditorPropertyGridLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorPropertyGridMetrics& metrics = {});
UIEditorPropertyGridHitTarget HitTestUIEditorPropertyGrid(
const UIEditorPropertyGridLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorPropertyGridBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorPropertyGridLayout& layout,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridPalette& palette = {},
const UIEditorPropertyGridMetrics& metrics = {});
void AppendUIEditorPropertyGridForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorPropertyGridLayout& layout,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const UIEditorPropertyGridState& state,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridPalette& palette = {},
const UIEditorPropertyGridMetrics& metrics = {},
const UIEditorMenuPopupPalette& popupPalette = {},
const UIEditorMenuPopupMetrics& popupMetrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
void AppendUIEditorPropertyGrid(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridPalette& palette = {},
const UIEditorPropertyGridMetrics& metrics = {},
const UIEditorMenuPopupPalette& popupPalette = {},
const UIEditorMenuPopupMetrics& popupMetrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,82 @@
#pragma once
#include <XCEditor/Fields/UIEditorEditableFieldCore.h>
#include <XCEditor/Fields/UIEditorAssetFieldInteraction.h>
#include <XCEditor/Fields/UIEditorNumberFieldInteraction.h>
#include <XCEditor/Fields/UIEditorPropertyGrid.h>
#include <XCEditor/Fields/UIEditorTextFieldInteraction.h>
#include <XCEditor/Fields/UIEditorVector2FieldInteraction.h>
#include <XCEditor/Fields/UIEditorVector3FieldInteraction.h>
#include <XCEditor/Fields/UIEditorVector4FieldInteraction.h>
#include <XCEngine/UI/Text/UITextInputController.h>
#include <XCEngine/UI/Types.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UIKeyboardNavigationModel.h>
#include <XCEngine/UI/Widgets/UIPropertyEditModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorPropertyGridAssetFieldInteractionEntry {
std::string fieldId = {};
UIEditorAssetFieldInteractionState state = {};
};
struct UIEditorPropertyGridInteractionState {
Widgets::UIEditorPropertyGridState propertyGridState = {};
::XCEngine::UI::Widgets::UIKeyboardNavigationModel keyboardNavigation = {};
UIEditorEditableFieldSession editableFieldSession = {};
::XCEngine::UI::UIPoint pointerPosition = {};
std::size_t pressedPopupIndex = Widgets::UIEditorPropertyGridInvalidIndex;
bool hasPointerPosition = false;
std::vector<UIEditorPropertyGridAssetFieldInteractionEntry> assetFieldInteractionStates = {};
};
struct UIEditorPropertyGridInteractionResult {
bool consumed = false;
bool sectionToggled = false;
bool selectionChanged = false;
bool keyboardNavigated = false;
bool editStarted = false;
bool editValueChanged = false;
bool editCommitted = false;
bool editCommitRejected = false;
bool editCanceled = false;
bool popupOpened = false;
bool popupClosed = false;
bool fieldValueChanged = false;
bool secondaryClicked = false;
bool pickerRequested = false;
bool activateRequested = false;
Widgets::UIEditorPropertyGridHitTarget hitTarget = {};
std::string toggledSectionId = {};
std::string selectedFieldId = {};
std::string activeFieldId = {};
std::string committedFieldId = {};
std::string committedValue = {};
std::string changedFieldId = {};
std::string changedValue = {};
std::string requestedFieldId = {};
};
struct UIEditorPropertyGridInteractionFrame {
Widgets::UIEditorPropertyGridLayout layout = {};
UIEditorPropertyGridInteractionResult result = {};
};
UIEditorPropertyGridInteractionFrame UpdateUIEditorPropertyGridInteraction(
UIEditorPropertyGridInteractionState& state,
::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const ::XCEngine::UI::UIRect& bounds,
std::vector<Widgets::UIEditorPropertyGridSection>& sections,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorPropertyGridMetrics& metrics = {},
const Widgets::UIEditorMenuPopupMetrics& popupMetrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,137 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEngine/UI/DrawData.h>
#include <cstdint>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorTextFieldHitTargetKind : std::uint8_t {
None = 0,
Row,
ValueBox
};
struct UIEditorTextFieldSpec {
std::string fieldId = {};
std::string label = {};
std::string value = {};
bool readOnly = false;
};
struct UIEditorTextFieldState {
UIEditorTextFieldHitTargetKind hoveredTarget = UIEditorTextFieldHitTargetKind::None;
UIEditorTextFieldHitTargetKind activeTarget = UIEditorTextFieldHitTargetKind::None;
bool focused = false;
bool editing = false;
std::size_t caretOffset = 0u;
std::uint64_t caretBlinkStartNanoseconds = 0u;
std::string displayText = {};
};
struct UIEditorTextFieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float valueBoxMinWidth = 96.0f;
float controlInsetY = 1.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 5.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float cornerRounding = 0.0f;
float valueBoxRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorTextFieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.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 rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor valueBoxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor valueBoxHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor valueBoxEditingColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor controlFocusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
};
struct UIEditorTextFieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
::XCEngine::UI::UIRect valueRect = {};
};
struct UIEditorTextFieldHitTarget {
UIEditorTextFieldHitTargetKind kind = UIEditorTextFieldHitTargetKind::None;
};
bool IsUIEditorTextFieldPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
UIEditorTextFieldLayout BuildUIEditorTextFieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorTextFieldSpec& spec,
const UIEditorTextFieldMetrics& metrics = {});
UIEditorTextFieldHitTarget HitTestUIEditorTextField(
const UIEditorTextFieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorTextFieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorTextFieldLayout& layout,
const UIEditorTextFieldSpec& spec,
const UIEditorTextFieldState& state,
const UIEditorTextFieldPalette& palette = {},
const UIEditorTextFieldMetrics& metrics = {});
void AppendUIEditorTextFieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorTextFieldLayout& layout,
const UIEditorTextFieldSpec& spec,
const UIEditorTextFieldState& state,
const UIEditorTextFieldPalette& palette = {},
const UIEditorTextFieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
void AppendUIEditorTextField(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorTextFieldSpec& spec,
const UIEditorTextFieldState& state,
const UIEditorTextFieldPalette& palette = {},
const UIEditorTextFieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,41 @@
#pragma once
#include <XCEditor/Fields/UIEditorEditableFieldCore.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorTextFieldInteractionState {
UIEditorEditableFieldSession session = {};
Widgets::UIEditorTextFieldState textFieldState = {};
};
struct UIEditorTextFieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool valueChanged = false;
bool editStarted = false;
bool editCommitted = false;
bool editCanceled = false;
Widgets::UIEditorTextFieldHitTarget hitTarget = {};
std::string valueBefore = {};
std::string valueAfter = {};
std::string committedText = {};
};
struct UIEditorTextFieldInteractionFrame {
Widgets::UIEditorTextFieldLayout layout = {};
UIEditorTextFieldInteractionResult result = {};
};
UIEditorTextFieldInteractionFrame UpdateUIEditorTextFieldInteraction(
UIEditorTextFieldInteractionState& state,
Widgets::UIEditorTextFieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorTextFieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,178 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEngine/UI/DrawData.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorVector2FieldInvalidComponentIndex = static_cast<std::size_t>(-1);
enum class UIEditorVector2FieldHitTargetKind : std::uint8_t {
None = 0,
Row,
Component
};
struct UIEditorVector2FieldSpec {
std::string fieldId = {};
std::string label = {};
std::array<double, 2u> values = { 0.0, 0.0 };
std::array<std::string, 2u> componentLabels = { std::string("X"), std::string("Y") };
double step = 0.1;
double minValue = -1000000.0;
double maxValue = 1000000.0;
bool integerMode = false;
bool readOnly = false;
};
struct UIEditorVector2FieldState {
UIEditorVector2FieldHitTargetKind hoveredTarget = UIEditorVector2FieldHitTargetKind::None;
UIEditorVector2FieldHitTargetKind activeTarget = UIEditorVector2FieldHitTargetKind::None;
std::size_t hoveredComponentIndex = UIEditorVector2FieldInvalidComponentIndex;
std::size_t activeComponentIndex = UIEditorVector2FieldInvalidComponentIndex;
std::size_t selectedComponentIndex = UIEditorVector2FieldInvalidComponentIndex;
bool focused = false;
bool editing = false;
std::size_t caretOffset = 0u;
std::uint64_t caretBlinkStartNanoseconds = 0u;
std::array<std::string, 2u> displayTexts = { std::string(), std::string() };
};
struct UIEditorVector2FieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float controlInsetY = 1.0f;
float componentGap = 6.0f;
float componentMinWidth = 72.0f;
float componentPrefixWidth = 9.0f;
float componentLabelGap = 4.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 5.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float prefixTextInsetX = 0.0f;
float prefixTextInsetY = -1.0f;
float prefixFontSize = 11.0f;
float cornerRounding = 0.0f;
float componentRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorVector2FieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.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 rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor componentColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor componentHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor componentEditingColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor componentBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor componentFocusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor prefixColor =
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
::XCEngine::UI::UIColor prefixBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor axisXColor =
::XCEngine::UI::UIColor(0.78f, 0.42f, 0.42f, 1.0f);
::XCEngine::UI::UIColor axisYColor =
::XCEngine::UI::UIColor(0.56f, 0.72f, 0.46f, 1.0f);
};
struct UIEditorVector2FieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
std::array<::XCEngine::UI::UIRect, 2u> componentRects = {};
std::array<::XCEngine::UI::UIRect, 2u> componentPrefixRects = {};
std::array<::XCEngine::UI::UIRect, 2u> componentValueRects = {};
};
struct UIEditorVector2FieldHitTarget {
UIEditorVector2FieldHitTargetKind kind = UIEditorVector2FieldHitTargetKind::None;
std::size_t componentIndex = UIEditorVector2FieldInvalidComponentIndex;
};
bool IsUIEditorVector2FieldPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
double NormalizeUIEditorVector2FieldComponentValue(
const UIEditorVector2FieldSpec& spec,
double value);
bool TryParseUIEditorVector2FieldComponentValue(
const UIEditorVector2FieldSpec& spec,
std::string_view text,
double& outValue);
std::string FormatUIEditorVector2FieldComponentValue(
const UIEditorVector2FieldSpec& spec,
std::size_t componentIndex);
UIEditorVector2FieldLayout BuildUIEditorVector2FieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorVector2FieldSpec& spec,
const UIEditorVector2FieldMetrics& metrics = {});
UIEditorVector2FieldHitTarget HitTestUIEditorVector2Field(
const UIEditorVector2FieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorVector2FieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorVector2FieldLayout& layout,
const UIEditorVector2FieldSpec& spec,
const UIEditorVector2FieldState& state,
const UIEditorVector2FieldPalette& palette = {},
const UIEditorVector2FieldMetrics& metrics = {});
void AppendUIEditorVector2FieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorVector2FieldLayout& layout,
const UIEditorVector2FieldSpec& spec,
const UIEditorVector2FieldState& state,
const UIEditorVector2FieldPalette& palette = {},
const UIEditorVector2FieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
void AppendUIEditorVector2Field(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorVector2FieldSpec& spec,
const UIEditorVector2FieldState& state,
const UIEditorVector2FieldPalette& palette = {},
const UIEditorVector2FieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,47 @@
#pragma once
#include <XCEditor/Fields/UIEditorEditableFieldCore.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <array>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorVector2FieldInteractionState {
UIEditorEditableFieldSession session = {};
Widgets::UIEditorVector2FieldState vector2FieldState = {};
};
struct UIEditorVector2FieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool valueChanged = false;
bool stepApplied = false;
bool selectionChanged = false;
bool editStarted = false;
bool editCommitted = false;
bool editCommitRejected = false;
bool editCanceled = false;
Widgets::UIEditorVector2FieldHitTarget hitTarget = {};
std::size_t selectedComponentIndex = Widgets::UIEditorVector2FieldInvalidComponentIndex;
std::size_t changedComponentIndex = Widgets::UIEditorVector2FieldInvalidComponentIndex;
std::array<double, 2u> valuesBefore = { 0.0, 0.0 };
std::array<double, 2u> valuesAfter = { 0.0, 0.0 };
double stepDelta = 0.0;
std::string committedText = {};
};
struct UIEditorVector2FieldInteractionFrame {
Widgets::UIEditorVector2FieldLayout layout = {};
UIEditorVector2FieldInteractionResult result = {};
};
UIEditorVector2FieldInteractionFrame UpdateUIEditorVector2FieldInteraction(
UIEditorVector2FieldInteractionState& state,
Widgets::UIEditorVector2FieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorVector2FieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,180 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEngine/UI/DrawData.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorVector3FieldInvalidComponentIndex = static_cast<std::size_t>(-1);
enum class UIEditorVector3FieldHitTargetKind : std::uint8_t {
None = 0,
Row,
Component
};
struct UIEditorVector3FieldSpec {
std::string fieldId = {};
std::string label = {};
std::array<double, 3u> values = { 0.0, 0.0, 0.0 };
std::array<std::string, 3u> componentLabels = { std::string("X"), std::string("Y"), std::string("Z") };
double step = 0.1;
double minValue = -1000000.0;
double maxValue = 1000000.0;
bool integerMode = false;
bool readOnly = false;
};
struct UIEditorVector3FieldState {
UIEditorVector3FieldHitTargetKind hoveredTarget = UIEditorVector3FieldHitTargetKind::None;
UIEditorVector3FieldHitTargetKind activeTarget = UIEditorVector3FieldHitTargetKind::None;
std::size_t hoveredComponentIndex = UIEditorVector3FieldInvalidComponentIndex;
std::size_t activeComponentIndex = UIEditorVector3FieldInvalidComponentIndex;
std::size_t selectedComponentIndex = UIEditorVector3FieldInvalidComponentIndex;
bool focused = false;
bool editing = false;
std::size_t caretOffset = 0u;
std::uint64_t caretBlinkStartNanoseconds = 0u;
std::array<std::string, 3u> displayTexts = { std::string(), std::string(), std::string() };
};
struct UIEditorVector3FieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float controlInsetY = 1.0f;
float componentGap = 6.0f;
float componentMinWidth = 72.0f;
float componentPrefixWidth = 9.0f;
float componentLabelGap = 4.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 5.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float prefixTextInsetX = 0.0f;
float prefixTextInsetY = -1.0f;
float prefixFontSize = 11.0f;
float cornerRounding = 0.0f;
float componentRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorVector3FieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.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 rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor componentColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor componentHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor componentEditingColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor componentBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor componentFocusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor prefixColor =
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
::XCEngine::UI::UIColor prefixBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor axisXColor =
::XCEngine::UI::UIColor(0.78f, 0.42f, 0.42f, 1.0f);
::XCEngine::UI::UIColor axisYColor =
::XCEngine::UI::UIColor(0.56f, 0.72f, 0.46f, 1.0f);
::XCEngine::UI::UIColor axisZColor =
::XCEngine::UI::UIColor(0.45f, 0.62f, 0.82f, 1.0f);
};
struct UIEditorVector3FieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
std::array<::XCEngine::UI::UIRect, 3u> componentRects = {};
std::array<::XCEngine::UI::UIRect, 3u> componentPrefixRects = {};
std::array<::XCEngine::UI::UIRect, 3u> componentValueRects = {};
};
struct UIEditorVector3FieldHitTarget {
UIEditorVector3FieldHitTargetKind kind = UIEditorVector3FieldHitTargetKind::None;
std::size_t componentIndex = UIEditorVector3FieldInvalidComponentIndex;
};
bool IsUIEditorVector3FieldPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
double NormalizeUIEditorVector3FieldComponentValue(
const UIEditorVector3FieldSpec& spec,
double value);
bool TryParseUIEditorVector3FieldComponentValue(
const UIEditorVector3FieldSpec& spec,
std::string_view text,
double& outValue);
std::string FormatUIEditorVector3FieldComponentValue(
const UIEditorVector3FieldSpec& spec,
std::size_t componentIndex);
UIEditorVector3FieldLayout BuildUIEditorVector3FieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorVector3FieldSpec& spec,
const UIEditorVector3FieldMetrics& metrics = {});
UIEditorVector3FieldHitTarget HitTestUIEditorVector3Field(
const UIEditorVector3FieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorVector3FieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorVector3FieldLayout& layout,
const UIEditorVector3FieldSpec& spec,
const UIEditorVector3FieldState& state,
const UIEditorVector3FieldPalette& palette = {},
const UIEditorVector3FieldMetrics& metrics = {});
void AppendUIEditorVector3FieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorVector3FieldLayout& layout,
const UIEditorVector3FieldSpec& spec,
const UIEditorVector3FieldState& state,
const UIEditorVector3FieldPalette& palette = {},
const UIEditorVector3FieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
void AppendUIEditorVector3Field(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorVector3FieldSpec& spec,
const UIEditorVector3FieldState& state,
const UIEditorVector3FieldPalette& palette = {},
const UIEditorVector3FieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,47 @@
#pragma once
#include <XCEditor/Fields/UIEditorEditableFieldCore.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <array>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorVector3FieldInteractionState {
UIEditorEditableFieldSession session = {};
Widgets::UIEditorVector3FieldState vector3FieldState = {};
};
struct UIEditorVector3FieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool valueChanged = false;
bool stepApplied = false;
bool selectionChanged = false;
bool editStarted = false;
bool editCommitted = false;
bool editCommitRejected = false;
bool editCanceled = false;
Widgets::UIEditorVector3FieldHitTarget hitTarget = {};
std::size_t selectedComponentIndex = Widgets::UIEditorVector3FieldInvalidComponentIndex;
std::size_t changedComponentIndex = Widgets::UIEditorVector3FieldInvalidComponentIndex;
std::array<double, 3u> valuesBefore = { 0.0, 0.0, 0.0 };
std::array<double, 3u> valuesAfter = { 0.0, 0.0, 0.0 };
double stepDelta = 0.0;
std::string committedText = {};
};
struct UIEditorVector3FieldInteractionFrame {
Widgets::UIEditorVector3FieldLayout layout = {};
UIEditorVector3FieldInteractionResult result = {};
};
UIEditorVector3FieldInteractionFrame UpdateUIEditorVector3FieldInteraction(
UIEditorVector3FieldInteractionState& state,
Widgets::UIEditorVector3FieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorVector3FieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,193 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEngine/UI/DrawData.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorVector4FieldInvalidComponentIndex = static_cast<std::size_t>(-1);
enum class UIEditorVector4FieldHitTargetKind : std::uint8_t {
None = 0,
Row,
Component
};
struct UIEditorVector4FieldSpec {
std::string fieldId = {};
std::string label = {};
std::array<double, 4u> values = { 0.0, 0.0, 0.0, 0.0 };
std::array<std::string, 4u> componentLabels = {
std::string("X"),
std::string("Y"),
std::string("Z"),
std::string("W")
};
double step = 0.1;
double minValue = -1000000.0;
double maxValue = 1000000.0;
bool integerMode = false;
bool readOnly = false;
};
struct UIEditorVector4FieldState {
UIEditorVector4FieldHitTargetKind hoveredTarget = UIEditorVector4FieldHitTargetKind::None;
UIEditorVector4FieldHitTargetKind activeTarget = UIEditorVector4FieldHitTargetKind::None;
std::size_t hoveredComponentIndex = UIEditorVector4FieldInvalidComponentIndex;
std::size_t activeComponentIndex = UIEditorVector4FieldInvalidComponentIndex;
std::size_t selectedComponentIndex = UIEditorVector4FieldInvalidComponentIndex;
bool focused = false;
bool editing = false;
std::size_t caretOffset = 0u;
std::uint64_t caretBlinkStartNanoseconds = 0u;
std::array<std::string, 4u> displayTexts = {
std::string(),
std::string(),
std::string(),
std::string()
};
};
struct UIEditorVector4FieldMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float controlInsetY = 1.0f;
float componentGap = 6.0f;
float componentMinWidth = 72.0f;
float componentPrefixWidth = 9.0f;
float componentLabelGap = 4.0f;
float labelTextInsetY = 0.0f;
float labelFontSize = 11.0f;
float valueTextInsetX = 5.0f;
float valueTextInsetY = 0.0f;
float valueFontSize = 12.0f;
float prefixTextInsetX = 0.0f;
float prefixTextInsetY = -1.0f;
float prefixFontSize = 11.0f;
float cornerRounding = 0.0f;
float componentRounding = 2.0f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorVector4FieldPalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.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 rowHoverColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor componentColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor componentHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor componentEditingColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor readOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor componentBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor componentFocusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor prefixColor =
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
::XCEngine::UI::UIColor prefixBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor axisXColor =
::XCEngine::UI::UIColor(0.78f, 0.42f, 0.42f, 1.0f);
::XCEngine::UI::UIColor axisYColor =
::XCEngine::UI::UIColor(0.56f, 0.72f, 0.46f, 1.0f);
::XCEngine::UI::UIColor axisZColor =
::XCEngine::UI::UIColor(0.45f, 0.62f, 0.82f, 1.0f);
::XCEngine::UI::UIColor axisWColor =
::XCEngine::UI::UIColor(0.76f, 0.66f, 0.42f, 1.0f);
};
struct UIEditorVector4FieldLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
std::array<::XCEngine::UI::UIRect, 4u> componentRects = {};
std::array<::XCEngine::UI::UIRect, 4u> componentPrefixRects = {};
std::array<::XCEngine::UI::UIRect, 4u> componentValueRects = {};
};
struct UIEditorVector4FieldHitTarget {
UIEditorVector4FieldHitTargetKind kind = UIEditorVector4FieldHitTargetKind::None;
std::size_t componentIndex = UIEditorVector4FieldInvalidComponentIndex;
};
bool IsUIEditorVector4FieldPointInside(
const ::XCEngine::UI::UIRect& rect,
const ::XCEngine::UI::UIPoint& point);
double NormalizeUIEditorVector4FieldComponentValue(
const UIEditorVector4FieldSpec& spec,
double value);
bool TryParseUIEditorVector4FieldComponentValue(
const UIEditorVector4FieldSpec& spec,
std::string_view text,
double& outValue);
std::string FormatUIEditorVector4FieldComponentValue(
const UIEditorVector4FieldSpec& spec,
std::size_t componentIndex);
UIEditorVector4FieldLayout BuildUIEditorVector4FieldLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorVector4FieldSpec& spec,
const UIEditorVector4FieldMetrics& metrics = {});
UIEditorVector4FieldHitTarget HitTestUIEditorVector4Field(
const UIEditorVector4FieldLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorVector4FieldBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorVector4FieldLayout& layout,
const UIEditorVector4FieldSpec& spec,
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette = {},
const UIEditorVector4FieldMetrics& metrics = {});
void AppendUIEditorVector4FieldForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorVector4FieldLayout& layout,
const UIEditorVector4FieldSpec& spec,
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette = {},
const UIEditorVector4FieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
void AppendUIEditorVector4Field(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorVector4FieldSpec& spec,
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette = {},
const UIEditorVector4FieldMetrics& metrics = {},
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,47 @@
#pragma once
#include <XCEditor/Fields/UIEditorEditableFieldCore.h>
#include <XCEditor/Fields/UIEditorVector4Field.h>
#include <array>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorVector4FieldInteractionState {
UIEditorEditableFieldSession session = {};
Widgets::UIEditorVector4FieldState vector4FieldState = {};
};
struct UIEditorVector4FieldInteractionResult {
bool consumed = false;
bool focusChanged = false;
bool valueChanged = false;
bool stepApplied = false;
bool selectionChanged = false;
bool editStarted = false;
bool editCommitted = false;
bool editCommitRejected = false;
bool editCanceled = false;
Widgets::UIEditorVector4FieldHitTarget hitTarget = {};
std::size_t selectedComponentIndex = Widgets::UIEditorVector4FieldInvalidComponentIndex;
std::size_t changedComponentIndex = Widgets::UIEditorVector4FieldInvalidComponentIndex;
std::array<double, 4u> valuesBefore = { 0.0, 0.0, 0.0, 0.0 };
std::array<double, 4u> valuesAfter = { 0.0, 0.0, 0.0, 0.0 };
double stepDelta = 0.0;
std::string committedText = {};
};
struct UIEditorVector4FieldInteractionFrame {
Widgets::UIEditorVector4FieldLayout layout = {};
UIEditorVector4FieldInteractionResult result = {};
};
UIEditorVector4FieldInteractionFrame UpdateUIEditorVector4FieldInteraction(
UIEditorVector4FieldInteractionState& state,
Widgets::UIEditorVector4FieldSpec& spec,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorVector4FieldMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,107 @@
#pragma once
#include <XCEditor/Foundation/UIEditorCommandRegistry.h>
#include <cstdint>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor {
struct UIEditorHostCommandEvaluationResult {
bool executable = false;
std::string message = {};
};
struct UIEditorHostCommandDispatchResult {
bool commandExecuted = false;
std::string message = {};
};
class UIEditorHostCommandHandler {
public:
virtual ~UIEditorHostCommandHandler() = default;
virtual UIEditorHostCommandEvaluationResult EvaluateHostCommand(
std::string_view commandId) const = 0;
virtual UIEditorHostCommandDispatchResult DispatchHostCommand(
std::string_view commandId) = 0;
};
enum class UIEditorCommandEvaluationCode : std::uint8_t {
None = 0,
InvalidCommandRegistry,
UnknownCommandId,
MissingActivePanel,
MissingHostCommandHandler,
HostCommandDisabled
};
struct UIEditorCommandEvaluationResult {
UIEditorCommandEvaluationCode code = UIEditorCommandEvaluationCode::None;
bool executable = false;
std::string commandId = {};
std::string displayName = {};
UIEditorWorkspaceCommand workspaceCommand = {};
UIEditorWorkspaceCommandResult previewResult = {};
std::string message = {};
UIEditorCommandKind kind = UIEditorCommandKind::Workspace;
[[nodiscard]] bool IsExecutable() const {
return executable;
}
};
enum class UIEditorCommandDispatchStatus : std::uint8_t {
Dispatched = 0,
Rejected
};
struct UIEditorCommandDispatchResult {
UIEditorCommandDispatchStatus status = UIEditorCommandDispatchStatus::Rejected;
bool commandExecuted = false;
std::string commandId = {};
std::string displayName = {};
UIEditorWorkspaceCommand workspaceCommand = {};
UIEditorWorkspaceCommandResult commandResult = {};
std::string message = {};
UIEditorCommandKind kind = UIEditorCommandKind::Workspace;
};
std::string_view GetUIEditorCommandDispatchStatusName(
UIEditorCommandDispatchStatus status);
class UIEditorCommandDispatcher {
public:
UIEditorCommandDispatcher() = default;
explicit UIEditorCommandDispatcher(UIEditorCommandRegistry commandRegistry);
const UIEditorCommandRegistry& GetCommandRegistry() const {
return m_commandRegistry;
}
void SetHostCommandHandler(UIEditorHostCommandHandler* handler) {
m_hostCommandHandler = handler;
}
UIEditorHostCommandHandler* GetHostCommandHandler() const {
return m_hostCommandHandler;
}
UIEditorCommandRegistryValidationResult ValidateConfiguration() const;
UIEditorCommandEvaluationResult Evaluate(
std::string_view commandId,
const UIEditorWorkspaceController& controller) const;
UIEditorCommandDispatchResult Dispatch(
std::string_view commandId,
UIEditorWorkspaceController& controller) const;
private:
UIEditorCommandRegistry m_commandRegistry = {};
UIEditorHostCommandHandler* m_hostCommandHandler = nullptr;
};
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,70 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
enum class UIEditorCommandKind : std::uint8_t {
Workspace = 0,
Host
};
enum class UIEditorCommandPanelSource : std::uint8_t {
None = 0,
FixedPanelId,
ActivePanel
};
struct UIEditorWorkspaceCommandDescriptor {
UIEditorWorkspaceCommandKind kind = UIEditorWorkspaceCommandKind::ActivatePanel;
UIEditorCommandPanelSource panelSource = UIEditorCommandPanelSource::FixedPanelId;
std::string panelId = {};
};
struct UIEditorCommandDescriptor {
std::string commandId = {};
std::string displayName = {};
UIEditorWorkspaceCommandDescriptor workspaceCommand = {};
UIEditorCommandKind kind = UIEditorCommandKind::Workspace;
};
struct UIEditorCommandRegistry {
std::vector<UIEditorCommandDescriptor> commands = {};
};
enum class UIEditorCommandRegistryValidationCode : std::uint8_t {
None = 0,
EmptyCommandId,
EmptyDisplayName,
DuplicateCommandId,
MissingPanelSource,
MissingFixedPanelId,
UnexpectedPanelSource
};
struct UIEditorCommandRegistryValidationResult {
UIEditorCommandRegistryValidationCode code =
UIEditorCommandRegistryValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorCommandRegistryValidationCode::None;
}
};
std::string_view GetUIEditorCommandPanelSourceName(UIEditorCommandPanelSource source);
std::string_view GetUIEditorCommandKindName(UIEditorCommandKind kind);
const UIEditorCommandDescriptor* FindUIEditorCommandDescriptor(
const UIEditorCommandRegistry& registry,
std::string_view commandId);
UIEditorCommandRegistryValidationResult ValidateUIEditorCommandRegistry(
const UIEditorCommandRegistry& registry);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,121 @@
#pragma once
#include <XCEngine/UI/Types.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorPanelInputFilterOptions {
bool allowPointerInBounds = false;
bool allowPointerWhileCaptured = false;
bool allowKeyboardInput = false;
bool allowFocusEvents = false;
bool includePointerLeave = false;
};
inline ::XCEngine::UI::UIInputEvent BuildUIEditorPanelFocusEvent(
::XCEngine::UI::UIInputEventType type) {
::XCEngine::UI::UIInputEvent event = {};
event.type = type;
return event;
}
inline std::vector<::XCEngine::UI::UIInputEvent> FilterUIEditorPanelInputEvents(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const UIEditorPanelInputFilterOptions& options) {
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
if (!options.allowPointerInBounds &&
!options.allowPointerWhileCaptured &&
!options.allowKeyboardInput &&
!options.allowFocusEvents &&
!options.includePointerLeave) {
return {};
}
auto containsPoint = [&bounds](const ::XCEngine::UI::UIPoint& point) {
return point.x >= bounds.x &&
point.x <= bounds.x + bounds.width &&
point.y >= bounds.y &&
point.y <= bounds.y + bounds.height;
};
std::vector<UIInputEvent> filteredEvents = {};
filteredEvents.reserve(inputEvents.size());
for (const UIInputEvent& event : inputEvents) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
case UIInputEventType::PointerWheel:
if (options.allowPointerWhileCaptured ||
(options.allowPointerInBounds && containsPoint(event.position))) {
filteredEvents.push_back(event);
}
break;
case UIInputEventType::PointerLeave:
if (options.includePointerLeave) {
filteredEvents.push_back(event);
}
break;
case UIInputEventType::FocusGained:
case UIInputEventType::FocusLost:
if (options.allowFocusEvents) {
filteredEvents.push_back(event);
}
break;
case UIInputEventType::KeyDown:
case UIInputEventType::KeyUp:
case UIInputEventType::Character:
if (options.allowKeyboardInput) {
filteredEvents.push_back(event);
}
break;
default:
break;
}
}
return filteredEvents;
}
inline std::vector<::XCEngine::UI::UIInputEvent> BuildUIEditorPanelInputEvents(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const UIEditorPanelInputFilterOptions& options,
bool synthesizeFocusGained = false,
bool synthesizeFocusLost = false) {
std::vector<::XCEngine::UI::UIInputEvent> filteredEvents =
FilterUIEditorPanelInputEvents(bounds, inputEvents, options);
if (!synthesizeFocusGained && !synthesizeFocusLost) {
return filteredEvents;
}
std::vector<::XCEngine::UI::UIInputEvent> routedEvents = {};
routedEvents.reserve(
filteredEvents.size() +
static_cast<std::size_t>(synthesizeFocusGained) +
static_cast<std::size_t>(synthesizeFocusLost));
if (synthesizeFocusLost) {
routedEvents.push_back(
BuildUIEditorPanelFocusEvent(::XCEngine::UI::UIInputEventType::FocusLost));
}
if (synthesizeFocusGained) {
routedEvents.push_back(
BuildUIEditorPanelFocusEvent(::XCEngine::UI::UIInputEventType::FocusGained));
}
routedEvents.insert(
routedEvents.end(),
filteredEvents.begin(),
filteredEvents.end());
return routedEvents;
}
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,28 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <string_view>
namespace XCEngine::UI::Editor {
void InitializeUIEditorRuntimeTrace(const std::filesystem::path& logRoot);
void ShutdownUIEditorRuntimeTrace();
void AppendUIEditorRuntimeTrace(
std::string_view channel,
std::string_view message);
void AppendUIEditorCrashTrace(
std::uint32_t exceptionCode,
const void* exceptionAddress);
void AppendUIEditorCrashTrace(
std::uint32_t exceptionCode,
const void* exceptionAddress,
const struct _EXCEPTION_POINTERS* exceptionPointers);
std::filesystem::path GetUIEditorRuntimeTracePath();
std::filesystem::path GetUIEditorCrashTracePath();
bool IsUIEditorRuntimeTraceInitialized();
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,108 @@
#pragma once
#include <XCEditor/Foundation/UIEditorCommandDispatcher.h>
#include <XCEngine/UI/Input/UIShortcutRegistry.h>
#include <cstdint>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor {
enum class UIEditorShortcutManagerValidationCode : std::uint8_t {
None = 0,
InvalidCommandRegistry,
EmptyBindingCommandId,
UnknownCommandId,
MissingScopedOwnerId,
EmptyShortcutKey,
ConflictingBinding
};
struct UIEditorShortcutManagerValidationResult {
UIEditorShortcutManagerValidationCode code =
UIEditorShortcutManagerValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorShortcutManagerValidationCode::None;
}
};
enum class UIEditorShortcutDispatchStatus : std::uint8_t {
NoMatch = 0,
Suppressed,
Dispatched,
Rejected
};
struct UIEditorShortcutDispatchResult {
UIEditorShortcutDispatchStatus status = UIEditorShortcutDispatchStatus::NoMatch;
bool matched = false;
bool commandExecuted = false;
std::string commandId = {};
std::string commandDisplayName = {};
std::string message = {};
XCEngine::UI::UIShortcutScope shortcutScope =
XCEngine::UI::UIShortcutScope::Global;
XCEngine::UI::UIElementId shortcutOwnerId = 0;
UIEditorWorkspaceCommandResult commandResult = {};
};
std::string_view GetUIEditorShortcutDispatchStatusName(
UIEditorShortcutDispatchStatus status);
class UIEditorShortcutManager {
public:
UIEditorShortcutManager() = default;
explicit UIEditorShortcutManager(UIEditorCommandRegistry commandRegistry);
const UIEditorCommandDispatcher& GetCommandDispatcher() const {
return m_commandDispatcher;
}
void SetHostCommandHandler(UIEditorHostCommandHandler* handler) {
m_commandDispatcher.SetHostCommandHandler(handler);
}
UIEditorHostCommandHandler* GetHostCommandHandler() const {
return m_commandDispatcher.GetHostCommandHandler();
}
const UIEditorCommandRegistry& GetCommandRegistry() const {
return m_commandDispatcher.GetCommandRegistry();
}
const XCEngine::UI::UIShortcutRegistry& GetShortcutRegistry() const {
return m_shortcutRegistry;
}
std::uint64_t RegisterBinding(const XCEngine::UI::UIShortcutBinding& binding);
bool UnregisterBinding(std::uint64_t bindingId);
void ClearBindings();
UIEditorShortcutManagerValidationResult ValidateConfiguration() const;
std::string GetPreferredShortcutText(std::string_view commandId) const;
UIEditorShortcutDispatchResult Dispatch(
const XCEngine::UI::UIInputEvent& event,
const XCEngine::UI::UIShortcutContext& shortcutContext,
UIEditorWorkspaceController& controller) const;
private:
UIEditorShortcutDispatchResult BuildDispatchResult(
UIEditorShortcutDispatchStatus status,
std::string commandId,
std::string commandDisplayName,
std::string message,
const XCEngine::UI::UIShortcutMatch* match = nullptr) const;
const XCEngine::UI::UIShortcutBinding* FindPreferredBinding(
std::string_view commandId) const;
UIEditorCommandDispatcher m_commandDispatcher = {};
XCEngine::UI::UIShortcutRegistry m_shortcutRegistry = {};
};
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,39 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <algorithm>
#include <string_view>
namespace XCEngine::UI::Editor {
inline float ClampUIEditorNonNegative(float value) {
return (std::max)(value, 0.0f);
}
inline float EstimateUIEditorSimpleTextWidth(
std::string_view text,
float estimatedGlyphWidth) {
return static_cast<float>(text.size()) * ClampUIEditorNonNegative(estimatedGlyphWidth);
}
inline float ResolveUIEditorMeasuredTextWidth(
std::string_view text,
float measuredWidth,
float fontSize,
float estimatedGlyphWidth,
const UIEditorTextMeasurer* textMeasurer = nullptr) {
if (measuredWidth > 0.0f) {
return measuredWidth;
}
if (textMeasurer != nullptr &&
!text.empty() &&
fontSize > 0.0f) {
return textMeasurer->MeasureTextWidth(UIEditorTextMeasureRequest { text, fontSize });
}
return EstimateUIEditorSimpleTextWidth(text, estimatedGlyphWidth);
}
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,23 @@
#pragma once
#include <string_view>
namespace XCEngine::UI::Editor {
struct UIEditorTextMeasureRequest {
std::string_view text = {};
float fontSize = 0.0f;
};
class UIEditorTextMeasurer {
public:
virtual ~UIEditorTextMeasurer() = default;
virtual float MeasureTextWidth(const UIEditorTextMeasureRequest& request) const = 0;
virtual float MeasureTextAdvance(const UIEditorTextMeasureRequest& request) const {
return MeasureTextWidth(request);
}
};
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,76 @@
#pragma once
#include <XCEditor/Collections/UIEditorListView.h>
#include <XCEditor/Collections/UIEditorScrollView.h>
#include <XCEditor/Collections/UIEditorTabStrip.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEditor/Fields/UIEditorAssetField.h>
#include <XCEditor/Fields/UIEditorBoolField.h>
#include <XCEditor/Fields/UIEditorColorField.h>
#include <XCEditor/Fields/UIEditorEnumField.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Fields/UIEditorObjectField.h>
#include <XCEditor/Fields/UIEditorPropertyGrid.h>
#include <XCEditor/Fields/UIEditorTextField.h>
#include <XCEditor/Fields/UIEditorVector2Field.h>
#include <XCEditor/Fields/UIEditorVector3Field.h>
#include <XCEditor/Fields/UIEditorVector4Field.h>
#include <XCEditor/Docking/UIEditorDockHost.h>
#include <XCEditor/Menu/UIEditorMenuBar.h>
#include <XCEditor/Menu/UIEditorMenuPopup.h>
#include <XCEditor/Panels/UIEditorPanelFrame.h>
#include <XCEditor/Shell/UIEditorShellCompose.h>
#include <XCEditor/Shell/UIEditorShellInteraction.h>
#include <XCEditor/Shell/UIEditorStatusBar.h>
#include <XCEditor/Viewport/UIEditorViewportSlot.h>
namespace XCEngine::UI::Editor {
const Widgets::UIEditorBoolFieldMetrics& ResolveUIEditorBoolFieldMetrics();
const Widgets::UIEditorBoolFieldPalette& ResolveUIEditorBoolFieldPalette();
const Widgets::UIEditorNumberFieldMetrics& ResolveUIEditorNumberFieldMetrics();
const Widgets::UIEditorNumberFieldPalette& ResolveUIEditorNumberFieldPalette();
const Widgets::UIEditorTextFieldMetrics& ResolveUIEditorTextFieldMetrics();
const Widgets::UIEditorTextFieldPalette& ResolveUIEditorTextFieldPalette();
const Widgets::UIEditorVector2FieldMetrics& ResolveUIEditorVector2FieldMetrics();
const Widgets::UIEditorVector2FieldPalette& ResolveUIEditorVector2FieldPalette();
const Widgets::UIEditorVector3FieldMetrics& ResolveUIEditorVector3FieldMetrics();
const Widgets::UIEditorVector3FieldPalette& ResolveUIEditorVector3FieldPalette();
const Widgets::UIEditorVector4FieldMetrics& ResolveUIEditorVector4FieldMetrics();
const Widgets::UIEditorVector4FieldPalette& ResolveUIEditorVector4FieldPalette();
const Widgets::UIEditorEnumFieldMetrics& ResolveUIEditorEnumFieldMetrics();
const Widgets::UIEditorEnumFieldPalette& ResolveUIEditorEnumFieldPalette();
const Widgets::UIEditorColorFieldMetrics& ResolveUIEditorColorFieldMetrics();
const Widgets::UIEditorColorFieldPalette& ResolveUIEditorColorFieldPalette();
const Widgets::UIEditorObjectFieldMetrics& ResolveUIEditorObjectFieldMetrics();
const Widgets::UIEditorObjectFieldPalette& ResolveUIEditorObjectFieldPalette();
const Widgets::UIEditorAssetFieldMetrics& ResolveUIEditorAssetFieldMetrics();
const Widgets::UIEditorAssetFieldPalette& ResolveUIEditorAssetFieldPalette();
const Widgets::UIEditorMenuPopupMetrics& ResolveUIEditorMenuPopupMetrics();
const Widgets::UIEditorMenuPopupPalette& ResolveUIEditorMenuPopupPalette();
const Widgets::UIEditorListViewMetrics& ResolveUIEditorListViewMetrics();
const Widgets::UIEditorListViewPalette& ResolveUIEditorListViewPalette();
const Widgets::UIEditorTreeViewMetrics& ResolveUIEditorTreeViewMetrics();
const Widgets::UIEditorTreeViewPalette& ResolveUIEditorTreeViewPalette();
const Widgets::UIEditorScrollViewMetrics& ResolveUIEditorScrollViewMetrics();
const Widgets::UIEditorScrollViewPalette& ResolveUIEditorScrollViewPalette();
const Widgets::UIEditorTabStripMetrics& ResolveUIEditorTabStripMetrics();
const Widgets::UIEditorTabStripPalette& ResolveUIEditorTabStripPalette();
const Widgets::UIEditorMenuBarMetrics& ResolveUIEditorMenuBarMetrics();
const Widgets::UIEditorMenuBarPalette& ResolveUIEditorMenuBarPalette();
const Widgets::UIEditorStatusBarMetrics& ResolveUIEditorStatusBarMetrics();
const Widgets::UIEditorStatusBarPalette& ResolveUIEditorStatusBarPalette();
const Widgets::UIEditorPanelFrameMetrics& ResolveUIEditorPanelFrameMetrics();
const Widgets::UIEditorPanelFramePalette& ResolveUIEditorPanelFramePalette();
const Widgets::UIEditorDockHostMetrics& ResolveUIEditorDockHostMetrics();
const Widgets::UIEditorDockHostPalette& ResolveUIEditorDockHostPalette();
const Widgets::UIEditorViewportSlotMetrics& ResolveUIEditorViewportSlotMetrics();
const Widgets::UIEditorViewportSlotPalette& ResolveUIEditorViewportSlotPalette();
const UIEditorShellComposeMetrics& ResolveUIEditorShellComposeMetrics();
const UIEditorShellComposePalette& ResolveUIEditorShellComposePalette();
const UIEditorShellInteractionMetrics& ResolveUIEditorShellInteractionMetrics();
const UIEditorShellInteractionPalette& ResolveUIEditorShellInteractionPalette();
const Widgets::UIEditorPropertyGridMetrics& ResolveUIEditorPropertyGridMetrics();
const Widgets::UIEditorPropertyGridPalette& ResolveUIEditorPropertyGridPalette();
} // namespace XCEngine::UI::Editor

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

View File

@@ -0,0 +1,40 @@
#pragma once
#include <XCEditor/Panels/UIEditorPanelContentHost.h>
#include <XCEditor/Panels/UIEditorPanelHostLifecycle.h>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorHostedPanelDispatchEntry {
std::string panelId = {};
UIEditorPanelPresentationKind presentationKind =
UIEditorPanelPresentationKind::Placeholder;
bool mounted = false;
::XCEngine::UI::UIRect bounds = {};
bool allowInteraction = false;
bool attached = false;
bool visible = false;
bool active = false;
bool focused = false;
bool focusGained = false;
bool focusLost = false;
};
struct UIEditorHostedPanelDispatchFrame {
std::vector<UIEditorHostedPanelDispatchEntry> entries = {};
};
const UIEditorHostedPanelDispatchEntry* FindUIEditorHostedPanelDispatchEntry(
const UIEditorHostedPanelDispatchFrame& frame,
std::string_view panelId);
UIEditorHostedPanelDispatchFrame BuildUIEditorHostedPanelDispatchFrame(
const UIEditorPanelContentHostFrame& contentHostFrame,
const UIEditorPanelHostLifecycleFrame& lifecycleFrame,
bool allowInteraction);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,85 @@
#pragma once
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Docking/UIEditorDockHost.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
enum class UIEditorPanelContentHostEventKind : std::uint8_t {
Mounted = 0,
Unmounted,
BoundsChanged
};
std::string_view GetUIEditorPanelContentHostEventKindName(
UIEditorPanelContentHostEventKind kind);
bool IsUIEditorPanelPresentationExternallyHosted(UIEditorPanelPresentationKind kind);
struct UIEditorPanelContentHostMountRequest {
std::string panelId = {};
UIEditorPanelPresentationKind kind = UIEditorPanelPresentationKind::Placeholder;
::XCEngine::UI::UIRect bounds = {};
};
struct UIEditorPanelContentHostRequest {
std::vector<UIEditorPanelContentHostMountRequest> mountRequests = {};
};
struct UIEditorPanelContentHostPanelState {
std::string panelId = {};
UIEditorPanelPresentationKind kind = UIEditorPanelPresentationKind::Placeholder;
bool mounted = false;
::XCEngine::UI::UIRect bounds = {};
};
struct UIEditorPanelContentHostState {
std::vector<UIEditorPanelContentHostPanelState> panelStates = {};
};
struct UIEditorPanelContentHostEvent {
UIEditorPanelContentHostEventKind kind = UIEditorPanelContentHostEventKind::Mounted;
std::string panelId = {};
UIEditorPanelPresentationKind presentationKind = UIEditorPanelPresentationKind::Placeholder;
::XCEngine::UI::UIRect bounds = {};
};
struct UIEditorPanelContentHostFrame {
std::vector<UIEditorPanelContentHostPanelState> panelStates = {};
std::vector<UIEditorPanelContentHostEvent> events = {};
};
const UIEditorPanelContentHostMountRequest* FindUIEditorPanelContentHostMountRequest(
const UIEditorPanelContentHostRequest& request,
std::string_view panelId);
const UIEditorPanelContentHostPanelState* FindUIEditorPanelContentHostPanelState(
const UIEditorPanelContentHostState& state,
std::string_view panelId);
const UIEditorPanelContentHostPanelState* FindUIEditorPanelContentHostPanelState(
const UIEditorPanelContentHostFrame& frame,
std::string_view panelId);
UIEditorPanelContentHostRequest ResolveUIEditorPanelContentHostRequest(
const Widgets::UIEditorDockHostLayout& dockHostLayout,
const UIEditorPanelRegistry& panelRegistry);
UIEditorPanelContentHostFrame BuildUIEditorPanelContentHostFrame(
const UIEditorPanelContentHostRequest& request,
const UIEditorPanelRegistry& panelRegistry);
UIEditorPanelContentHostFrame UpdateUIEditorPanelContentHost(
UIEditorPanelContentHostState& state,
const UIEditorPanelContentHostRequest& request,
const UIEditorPanelRegistry& panelRegistry);
std::vector<std::string> CollectMountedUIEditorPanelContentHostPanelIds(
const UIEditorPanelContentHostFrame& frame);
} // 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 = 0.0f;
float headerHeight = 28.0f;
float footerHeight = 18.0f;
float contentPadding = 8.0f;
float titleInsetX = 9.0f;
float titleInsetY = 6.0f;
float subtitleInsetY = 16.0f;
float footerInsetX = 9.0f;
float footerInsetY = 3.0f;
float actionButtonExtent = 14.0f;
float actionInsetX = 8.0f;
float actionGap = 4.0f;
float baseBorderThickness = 1.0f;
float hoveredBorderThickness = 1.0f;
float activeBorderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorPanelFramePalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor headerColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor footerColor =
::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 hoveredBorderColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor activeBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f);
::XCEngine::UI::UIColor actionButtonColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor actionButtonHoveredColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor actionButtonSelectedColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor actionButtonBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor actionGlyphColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 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,68 @@
#pragma once
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
enum class UIEditorPanelHostLifecycleEventKind : std::uint8_t {
Attached = 0,
Detached,
Shown,
Hidden,
Activated,
Deactivated,
FocusGained,
FocusLost
};
std::string_view GetUIEditorPanelHostLifecycleEventKindName(
UIEditorPanelHostLifecycleEventKind kind);
struct UIEditorPanelHostState {
std::string panelId = {};
bool attached = false;
bool visible = false;
bool active = false;
bool focused = false;
};
struct UIEditorPanelHostLifecycleEvent {
UIEditorPanelHostLifecycleEventKind kind = UIEditorPanelHostLifecycleEventKind::Attached;
std::string panelId = {};
};
struct UIEditorPanelHostLifecycleState {
std::vector<UIEditorPanelHostState> panelStates = {};
};
struct UIEditorPanelHostLifecycleRequest {
std::string focusedPanelId = {};
};
struct UIEditorPanelHostLifecycleFrame {
std::vector<UIEditorPanelHostState> panelStates = {};
std::vector<UIEditorPanelHostLifecycleEvent> events = {};
};
const UIEditorPanelHostState* FindUIEditorPanelHostState(
const UIEditorPanelHostLifecycleState& state,
std::string_view panelId);
const UIEditorPanelHostState* FindUIEditorPanelHostState(
const UIEditorPanelHostLifecycleFrame& frame,
std::string_view panelId);
UIEditorPanelHostLifecycleFrame UpdateUIEditorPanelHostLifecycle(
UIEditorPanelHostLifecycleState& state,
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const UIEditorPanelHostLifecycleRequest& request = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,57 @@
#pragma once
#include <XCEditor/Viewport/UIEditorViewportShell.h>
#include <XCEngine/UI/Types.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
enum class UIEditorPanelPresentationKind : std::uint8_t {
Placeholder = 0,
ViewportShell,
HostedContent
};
struct UIEditorPanelDescriptor {
std::string panelId = {};
std::string defaultTitle = {};
UIEditorPanelPresentationKind presentationKind = UIEditorPanelPresentationKind::Placeholder;
bool placeholder = true;
bool canHide = true;
bool canClose = true;
::XCEngine::UI::UISize minimumDetachedWindowSize = {};
UIEditorViewportShellSpec viewportShellSpec = {};
};
struct UIEditorPanelRegistry {
std::vector<UIEditorPanelDescriptor> panels = {};
};
enum class UIEditorPanelRegistryValidationCode : std::uint8_t {
None = 0,
EmptyPanelId,
EmptyDefaultTitle,
DuplicatePanelId
};
struct UIEditorPanelRegistryValidationResult {
UIEditorPanelRegistryValidationCode code = UIEditorPanelRegistryValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorPanelRegistryValidationCode::None;
}
};
const UIEditorPanelDescriptor* FindUIEditorPanelDescriptor(
const UIEditorPanelRegistry& registry,
std::string_view panelId);
UIEditorPanelRegistryValidationResult ValidateUIEditorPanelRegistry(
const UIEditorPanelRegistry& registry);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,61 @@
#pragma once
#include <XCEditor/Foundation/UIEditorShortcutManager.h>
#include <XCEditor/Menu/UIEditorMenuModel.h>
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Shell/UIEditorShellInteraction.h>
#include <XCEditor/Workspace/UIEditorWorkspaceModel.h>
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <filesystem>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct EditorShellShortcutAsset {
UIEditorCommandRegistry commandRegistry = {};
std::vector<UIShortcutBinding> bindings = {};
};
struct EditorShellAsset {
std::string screenId = {};
std::filesystem::path documentPath = {};
std::filesystem::path captureRootPath = {};
UIEditorPanelRegistry panelRegistry = {};
UIEditorWorkspaceModel workspace = {};
UIEditorWorkspaceSession workspaceSession = {};
UIEditorShellInteractionDefinition shellDefinition = {};
EditorShellShortcutAsset shortcutAsset = {};
};
enum class EditorShellAssetValidationCode : std::uint8_t {
None = 0,
InvalidPanelRegistry = 1,
InvalidWorkspace = 2,
InvalidWorkspaceSession = 3,
InvalidShellMenuModel = 4,
InvalidShortcutConfiguration = 5,
MissingPanelDescriptor = 6,
PanelTitleMismatch = 7,
PanelPlaceholderMismatch = 8,
DuplicateShellPresentationPanelId = 9,
MissingShellPresentationPanelDescriptor = 10,
MissingRequiredShellPresentation = 11,
ShellPresentationKindMismatch = 12
};
struct EditorShellAssetValidationResult {
EditorShellAssetValidationCode code = EditorShellAssetValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == EditorShellAssetValidationCode::None;
}
};
UIEditorShortcutManager BuildEditorShellShortcutManager(const EditorShellAsset& asset);
EditorShellAssetValidationResult ValidateEditorShellAsset(const EditorShellAsset& asset);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,29 @@
#pragma once
#include <XCEditor/Shell/UIEditorShellInteraction.h>
namespace XCEngine::UI {
struct UIInputEvent;
struct UIPoint;
}
namespace XCEngine::UI::Editor {
bool HasActiveUIEditorShellInteractiveCapture(
const UIEditorShellInteractionState& shellInteractionState);
bool ShouldStartImmediateUIEditorShellPointerCapture(
const UIEditorShellInteractionFrame& shellFrame,
const ::XCEngine::UI::UIPoint& clientPoint);
bool ShouldYieldUIEditorHostedContentPointerStream(
const UIEditorShellInteractionFrame& shellFrame,
bool shellInteractiveCaptureActive);
std::vector<::XCEngine::UI::UIInputEvent> FilterUIEditorHostedContentInputEvents(
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
bool shellOwnsPointerStream);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,171 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWorkspaceCompose.h>
#include <XCEditor/Menu/UIEditorMenuBar.h>
#include <XCEditor/Shell/UIEditorStatusBar.h>
namespace XCEngine::UI::Editor::App {
class BuiltInIcons;
}
namespace XCEngine::UI::Editor {
struct UIEditorShellToolbarButton {
std::string buttonId = {};
std::uint8_t iconKind = 0;
bool enabled = true;
};
struct UIEditorShellToolbarLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect groupRect = {};
std::vector<::XCEngine::UI::UIRect> buttonRects = {};
};
struct UIEditorShellToolbarMetrics {
float barHeight = 30.0f;
float groupPaddingX = 6.0f;
float groupPaddingY = 2.0f;
float buttonWidth = 20.0f;
float buttonHeight = 20.0f;
float buttonGap = 6.0f;
float groupCornerRounding = 0.0f;
float buttonCornerRounding = 0.0f;
float borderThickness = 1.0f;
float iconThickness = 1.2f;
};
struct UIEditorShellToolbarPalette {
::XCEngine::UI::UIColor barColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor groupColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor groupBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor buttonColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor buttonBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor iconColor =
::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f);
};
struct UIEditorShellComposeModel {
std::vector<Widgets::UIEditorMenuBarItem> menuBarItems = {};
std::vector<UIEditorShellToolbarButton> toolbarButtons = {};
std::vector<Widgets::UIEditorStatusBarSegment> statusSegments = {};
std::vector<UIEditorWorkspacePanelPresentationModel> workspacePresentations = {};
};
struct UIEditorShellComposeState {
Widgets::UIEditorMenuBarState menuBarState = {};
UIEditorWorkspaceComposeState workspaceState = {};
Widgets::UIEditorStatusBarState statusBarState = {};
};
struct UIEditorShellComposeMetrics {
float outerPadding = 0.0f;
float sectionGap = 0.0f;
float surfaceCornerRounding = 0.0f;
Widgets::UIEditorMenuBarMetrics menuBarMetrics = {};
UIEditorShellToolbarMetrics toolbarMetrics = {};
Widgets::UIEditorDockHostMetrics dockHostMetrics = {};
Widgets::UIEditorViewportSlotMetrics viewportMetrics = {};
Widgets::UIEditorStatusBarMetrics statusBarMetrics = {};
};
struct UIEditorShellComposePalette {
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor surfaceBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
Widgets::UIEditorMenuBarPalette menuBarPalette = {};
UIEditorShellToolbarPalette toolbarPalette = {};
Widgets::UIEditorDockHostPalette dockHostPalette = {};
Widgets::UIEditorViewportSlotPalette viewportPalette = {};
Widgets::UIEditorStatusBarPalette statusBarPalette = {};
};
struct UIEditorShellComposeLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect contentRect = {};
::XCEngine::UI::UIRect menuBarRect = {};
::XCEngine::UI::UIRect toolbarRect = {};
::XCEngine::UI::UIRect workspaceRect = {};
::XCEngine::UI::UIRect statusBarRect = {};
Widgets::UIEditorMenuBarLayout menuBarLayout = {};
UIEditorShellToolbarLayout toolbarLayout = {};
Widgets::UIEditorStatusBarLayout statusBarLayout = {};
};
struct UIEditorShellComposeRequest {
UIEditorShellComposeLayout layout = {};
};
struct UIEditorShellComposeFrame {
UIEditorShellComposeLayout layout = {};
};
UIEditorShellComposeLayout BuildUIEditorShellComposeLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorMenuBarItem>& menuBarItems,
const std::vector<Widgets::UIEditorStatusBarSegment>& statusSegments,
const UIEditorShellComposeMetrics& metrics = {});
UIEditorShellComposeLayout BuildUIEditorShellComposeLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<Widgets::UIEditorMenuBarItem>& menuBarItems,
const std::vector<UIEditorShellToolbarButton>& toolbarButtons,
const std::vector<Widgets::UIEditorStatusBarSegment>& statusSegments,
const UIEditorShellComposeMetrics& metrics = {});
UIEditorShellComposeRequest ResolveUIEditorShellComposeRequest(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorShellComposeModel& model,
const UIEditorShellComposeMetrics& metrics = {});
UIEditorShellComposeFrame UpdateUIEditorShellCompose(
UIEditorShellComposeState& state,
const ::XCEngine::UI::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 = {});
void AppendUIEditorShellCompose(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorShellComposeFrame& frame,
const UIEditorWorkspaceComposeFrame& workspaceFrame,
const UIEditorShellComposeModel& model,
const UIEditorShellComposeState& state,
const UIEditorShellComposePalette& palette = {},
const UIEditorShellComposeMetrics& metrics = {},
const App::BuiltInIcons* builtInIcons = nullptr);
void AppendUIEditorShellComposeBase(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorShellComposeFrame& frame,
const UIEditorShellComposeModel& model,
const UIEditorShellComposeState& state,
const UIEditorShellComposePalette& palette = {},
const UIEditorShellComposeMetrics& metrics = {},
const App::BuiltInIcons* builtInIcons = nullptr);
void AppendUIEditorShellComposeStatusBar(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorShellComposeFrame& frame,
const UIEditorShellComposeModel& model,
const UIEditorShellComposeState& state,
const UIEditorShellComposePalette& palette = {},
const UIEditorShellComposeMetrics& metrics = {});
void AppendUIEditorShellComposeOverlay(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorWorkspaceComposeFrame& workspaceFrame,
const UIEditorShellComposePalette& palette = {},
const UIEditorShellComposeMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,194 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Menu/UIEditorMenuModel.h>
#include <XCEditor/Menu/UIEditorMenuSession.h>
#include <XCEditor/Panels/UIEditorHostedPanelDispatch.h>
#include <XCEditor/Shell/UIEditorShellCompose.h>
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
#include <XCEditor/Workspace/UIEditorWorkspaceInteraction.h>
#include <XCEditor/Menu/UIEditorMenuPopup.h>
#include <XCEngine/UI/DrawData.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorShellInteractionDefinition {
UIEditorMenuModel menuModel = {};
std::vector<UIEditorShellToolbarButton> toolbarButtons = {};
std::vector<Widgets::UIEditorStatusBarSegment> statusSegments = {};
std::vector<UIEditorWorkspacePanelPresentationModel> workspacePresentations = {};
};
struct UIEditorShellInteractionModel {
UIEditorResolvedMenuModel resolvedMenuModel = {};
std::vector<UIEditorShellToolbarButton> toolbarButtons = {};
std::vector<Widgets::UIEditorStatusBarSegment> statusSegments = {};
std::vector<UIEditorWorkspacePanelPresentationModel> workspacePresentations = {};
};
struct UIEditorShellInteractionState {
UIEditorShellComposeState composeState = {};
UIEditorMenuSession menuSession = {};
UIEditorWorkspaceInteractionState workspaceInteractionState = {};
::XCEngine::UI::UIPoint pointerPosition = {};
bool focused = false;
bool hasPointerPosition = false;
};
struct UIEditorShellInteractionMetrics {
UIEditorShellComposeMetrics shellMetrics = {};
Widgets::UIEditorMenuPopupMetrics popupMetrics = {};
};
struct UIEditorShellInteractionPalette {
UIEditorShellComposePalette shellPalette = {};
Widgets::UIEditorMenuPopupPalette popupPalette = {};
};
struct UIEditorShellInteractionServices {
const UIEditorCommandDispatcher* commandDispatcher = nullptr;
const UIEditorShortcutManager* shortcutManager = nullptr;
const UIEditorTextMeasurer* textMeasurer = nullptr;
};
struct UIEditorShellInteractionMenuButtonRequest {
std::string menuId = {};
std::string label = {};
std::string popupId = {};
::XCEngine::UI::UIRect rect = {};
::XCEngine::UI::UIInputPath path = {};
bool enabled = true;
};
struct UIEditorShellInteractionPopupItemRequest {
std::string popupId = {};
std::string menuId = {};
std::string itemId = {};
std::string label = {};
std::string commandId = {};
std::string childPopupId = {};
::XCEngine::UI::UIRect rect = {};
::XCEngine::UI::UIInputPath path = {};
UIEditorMenuItemKind kind = UIEditorMenuItemKind::Command;
bool enabled = false;
bool checked = false;
bool hasSubmenu = false;
};
struct UIEditorShellInteractionPopupRequest {
std::string popupId = {};
std::string menuId = {};
std::string sourceItemId = {};
::XCEngine::UI::Widgets::UIPopupOverlayEntry overlayEntry = {};
::XCEngine::UI::Widgets::UIPopupPlacementResult placement = {};
Widgets::UIEditorMenuPopupLayout layout = {};
std::vector<UIEditorResolvedMenuItem> resolvedItems = {};
std::vector<Widgets::UIEditorMenuPopupItem> widgetItems = {};
std::vector<UIEditorShellInteractionPopupItemRequest> itemRequests = {};
};
struct UIEditorShellInteractionRequest {
UIEditorShellComposeRequest shellRequest = {};
std::vector<Widgets::UIEditorMenuBarItem> menuBarItems = {};
std::vector<UIEditorShellInteractionMenuButtonRequest> menuButtons = {};
std::vector<UIEditorShellInteractionPopupRequest> popupRequests = {};
};
struct UIEditorShellInteractionResult {
bool consumed = false;
bool menuModal = false;
bool workspaceInputSuppressed = false;
bool requestPointerCapture = false;
bool releasePointerCapture = false;
bool viewportInteractionChanged = false;
bool commandTriggered = false;
bool commandDispatched = false;
std::string menuId = {};
std::string popupId = {};
std::string itemId = {};
std::string commandId = {};
std::string viewportPanelId = {};
UIEditorViewportInputBridgeFrame viewportInputFrame = {};
UIEditorMenuSessionMutationResult menuMutation = {};
UIEditorCommandDispatchResult commandDispatchResult = {};
UIEditorWorkspaceInteractionResult workspaceResult = {};
};
struct UIEditorShellInteractionPopupFrame {
std::string popupId = {};
Widgets::UIEditorMenuPopupState popupState = {};
};
struct UIEditorShellInteractionFrame {
UIEditorShellInteractionModel model = {};
UIEditorShellInteractionRequest request = {};
UIEditorShellComposeFrame shellFrame = {};
UIEditorWorkspaceInteractionFrame workspaceInteractionFrame = {};
UIEditorHostedPanelDispatchFrame hostedPanelDispatchFrame = {};
std::vector<UIEditorShellInteractionPopupFrame> popupFrames = {};
UIEditorShellInteractionResult result = {};
std::string openRootMenuId = {};
std::string hoveredMenuId = {};
std::string hoveredPopupId = {};
std::string hoveredItemId = {};
bool focused = false;
};
UIEditorShellInteractionModel ResolveUIEditorShellInteractionModel(
const UIEditorWorkspaceController& controller,
const UIEditorShellInteractionDefinition& definition,
const UIEditorShellInteractionServices& services = {});
UIEditorShellInteractionRequest ResolveUIEditorShellInteractionRequest(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorShellInteractionModel& model,
const UIEditorShellInteractionState& state = {},
const UIEditorShellInteractionMetrics& metrics = {},
const UIEditorShellInteractionServices& services = {});
UIEditorShellInteractionFrame UpdateUIEditorShellInteraction(
UIEditorShellInteractionState& state,
UIEditorWorkspaceController& controller,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorShellInteractionModel& model,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const UIEditorShellInteractionServices& services = {},
const UIEditorShellInteractionMetrics& metrics = {});
UIEditorShellInteractionRequest ResolveUIEditorShellInteractionRequest(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorWorkspaceController& controller,
const UIEditorShellInteractionDefinition& definition,
const UIEditorShellInteractionState& state = {},
const UIEditorShellInteractionMetrics& metrics = {},
const UIEditorShellInteractionServices& services = {});
UIEditorShellInteractionFrame UpdateUIEditorShellInteraction(
UIEditorShellInteractionState& state,
UIEditorWorkspaceController& controller,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorShellInteractionDefinition& definition,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const UIEditorShellInteractionServices& services = {},
const UIEditorShellInteractionMetrics& metrics = {});
void AppendUIEditorShellInteraction(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorShellInteractionFrame& frame,
const UIEditorShellInteractionModel& model,
const UIEditorShellInteractionState& state,
const UIEditorShellInteractionPalette& palette = {},
const UIEditorShellInteractionMetrics& metrics = {});
void AppendUIEditorShellInteraction(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorShellInteractionFrame& frame,
const UIEditorShellInteractionState& state,
const UIEditorShellInteractionPalette& palette = {},
const UIEditorShellInteractionMetrics& metrics = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,154 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.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 UIEditorStatusBarInvalidIndex =
static_cast<std::size_t>(-1);
enum class UIEditorStatusBarSlot : std::uint8_t {
Leading = 0,
Trailing
};
enum class UIEditorStatusBarTextTone : std::uint8_t {
Primary = 0,
Muted,
Accent
};
enum class UIEditorStatusBarHitTargetKind : std::uint8_t {
None = 0,
Background,
Segment,
Separator
};
struct UIEditorStatusBarSegment {
std::string segmentId = {};
std::string label = {};
UIEditorStatusBarSlot slot = UIEditorStatusBarSlot::Leading;
UIEditorStatusBarTextTone tone = UIEditorStatusBarTextTone::Primary;
bool interactive = true;
bool showSeparator = false;
// Pure measured label width. Segment padding is applied only in layout.
float measuredLabelWidth = 0.0f;
};
struct UIEditorStatusBarState {
std::size_t hoveredIndex = UIEditorStatusBarInvalidIndex;
std::size_t activeIndex = UIEditorStatusBarInvalidIndex;
bool focused = false;
};
struct UIEditorStatusBarMetrics {
float barHeight = 22.0f;
float outerPaddingX = 8.0f;
float segmentPaddingX = 8.0f;
float segmentPaddingY = 4.0f;
float segmentGap = 2.0f;
float separatorWidth = 1.0f;
float separatorInsetY = 5.0f;
float slotGapMin = 18.0f;
float cornerRounding = 0.0f;
float labelFontSize = 12.0f;
float labelInsetY = 0.0f;
float estimatedGlyphWidth = 6.5f;
float borderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
};
struct UIEditorStatusBarPalette {
::XCEngine::UI::UIColor surfaceColor =
::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 focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor segmentColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor segmentHoveredColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor segmentActiveColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor segmentBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor separatorColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f);
::XCEngine::UI::UIColor textAccent =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
};
struct UIEditorStatusBarLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect leadingSlotRect = {};
::XCEngine::UI::UIRect trailingSlotRect = {};
std::vector<::XCEngine::UI::UIRect> segmentRects = {};
std::vector<::XCEngine::UI::UIRect> separatorRects = {};
};
struct UIEditorStatusBarHitTarget {
UIEditorStatusBarHitTargetKind kind = UIEditorStatusBarHitTargetKind::None;
std::size_t index = UIEditorStatusBarInvalidIndex;
};
float ResolveUIEditorStatusBarMeasuredLabelWidth(
const UIEditorStatusBarSegment& segment,
const UIEditorStatusBarMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
float ResolveUIEditorStatusBarDesiredSegmentWidth(
const UIEditorStatusBarSegment& segment,
const UIEditorStatusBarMetrics& metrics = {});
UIEditorStatusBarLayout BuildUIEditorStatusBarLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorStatusBarSegment>& segments,
const UIEditorStatusBarMetrics& metrics = {});
UIEditorStatusBarHitTarget HitTestUIEditorStatusBar(
const UIEditorStatusBarLayout& layout,
const ::XCEngine::UI::UIPoint& point);
::XCEngine::UI::UIColor ResolveUIEditorStatusBarTextColor(
UIEditorStatusBarTextTone tone,
const UIEditorStatusBarPalette& palette = {});
void AppendUIEditorStatusBarBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorStatusBarLayout& layout,
const std::vector<UIEditorStatusBarSegment>& segments,
const UIEditorStatusBarState& state,
const UIEditorStatusBarPalette& palette = {},
const UIEditorStatusBarMetrics& metrics = {});
void AppendUIEditorStatusBarForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorStatusBarLayout& layout,
const std::vector<UIEditorStatusBarSegment>& segments,
const UIEditorStatusBarState& state,
const UIEditorStatusBarPalette& palette = {},
const UIEditorStatusBarMetrics& metrics = {});
void AppendUIEditorStatusBar(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorStatusBarSegment>& segments,
const UIEditorStatusBarState& state,
const UIEditorStatusBarPalette& palette = {},
const UIEditorStatusBarMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,29 @@
#pragma once
#include <XCEditor/Foundation/UIEditorShortcutManager.h>
#include <XCEditor/Shell/UIEditorShellAsset.h>
#include <XCEditor/Shell/UIEditorShellInteraction.h>
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
#include <XCEngine/UI/Runtime/UIScreenTypes.h>
namespace XCEngine::UI::Editor {
struct StructuredEditorShellBinding {
::XCEngine::UI::Runtime::UIScreenAsset screenAsset = {};
UIEditorWorkspaceController workspaceController = {};
UIEditorShellInteractionDefinition shellDefinition = {};
UIEditorShortcutManager shortcutManager = {};
EditorShellAssetValidationResult assetValidation = {};
[[nodiscard]] bool IsValid() const {
return assetValidation.IsValid();
}
};
StructuredEditorShellBinding BuildStructuredEditorShellBinding(const EditorShellAsset& asset);
UIEditorShellInteractionServices BuildStructuredEditorShellServices(
const StructuredEditorShellBinding& binding);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,99 @@
#pragma once
#include <XCEngine/UI/Types.h>
#include <cstdint>
#include <unordered_set>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorViewportInputBridgeConfig {
bool focusOnPointerDownInside = true;
bool clearFocusOnPointerDownOutside = true;
bool capturePointerOnPointerDownInside = true;
};
enum class UIEditorViewportInputBridgeFocusMode : std::uint8_t {
SelfManaged = 0,
External
};
struct UIEditorViewportInputBridgeRequest {
UIEditorViewportInputBridgeFocusMode focusMode =
UIEditorViewportInputBridgeFocusMode::SelfManaged;
bool focused = false;
};
struct UIEditorViewportInputBridgeState {
bool hovered = false;
bool focused = false;
bool captured = false;
::XCEngine::UI::UIPointerButton captureButton = ::XCEngine::UI::UIPointerButton::None;
::XCEngine::UI::UIPoint lastScreenPointerPosition = {};
::XCEngine::UI::UIPoint lastLocalPointerPosition = {};
bool hasPointerPosition = false;
::XCEngine::UI::UIInputModifiers modifiers = {};
std::unordered_set<std::int32_t> pressedKeys = {};
std::uint8_t pointerButtonsDownMask = 0;
};
struct UIEditorViewportPointerButtonTransition {
::XCEngine::UI::UIPointerButton button =
::XCEngine::UI::UIPointerButton::None;
bool pressed = false;
bool inside = false;
::XCEngine::UI::UIPoint screenPosition = {};
::XCEngine::UI::UIPoint localPointerPosition = {};
::XCEngine::UI::UIInputModifiers modifiers = {};
};
struct UIEditorViewportInputBridgeFrame {
bool hasPointerPosition = false;
bool hovered = false;
bool focused = false;
bool captured = false;
bool pointerInside = false;
bool pointerMoved = false;
bool pointerPressedInside = false;
bool pointerReleasedInside = false;
bool focusGained = false;
bool focusLost = false;
bool captureStarted = false;
bool captureEnded = false;
::XCEngine::UI::UIPointerButton changedPointerButton = ::XCEngine::UI::UIPointerButton::None;
::XCEngine::UI::UIPoint screenPointerPosition = {};
::XCEngine::UI::UIPoint localPointerPosition = {};
::XCEngine::UI::UIPoint pointerDelta = {};
float wheelDelta = 0.0f;
::XCEngine::UI::UIInputModifiers modifiers = {};
std::vector<std::int32_t> pressedKeyCodes = {};
std::vector<std::int32_t> releasedKeyCodes = {};
std::vector<std::uint32_t> characters = {};
std::vector<UIEditorViewportPointerButtonTransition> pointerButtonTransitions = {};
};
bool IsUIEditorViewportInputBridgeKeyDown(
const UIEditorViewportInputBridgeState& state,
std::int32_t keyCode);
bool IsUIEditorViewportInputBridgePointerButtonDown(
const UIEditorViewportInputBridgeState& state,
::XCEngine::UI::UIPointerButton button);
UIEditorViewportInputBridgeFrame UpdateUIEditorViewportInputBridge(
UIEditorViewportInputBridgeState& state,
const ::XCEngine::UI::UIRect& inputRect,
const std::vector<::XCEngine::UI::UIInputEvent>& events,
const UIEditorViewportInputBridgeConfig& config = {},
const UIEditorViewportInputBridgeRequest& request = {});
UIEditorViewportInputBridgeFrame UpdateUIEditorViewportInputBridge(
UIEditorViewportInputBridgeState& state,
const ::XCEngine::UI::UIRect& interactionRect,
const ::XCEngine::UI::UIRect& localRect,
const std::vector<::XCEngine::UI::UIInputEvent>& events,
const UIEditorViewportInputBridgeConfig& config = {},
const UIEditorViewportInputBridgeRequest& request = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,71 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Viewport/UIEditorViewportInputBridge.h>
#include <XCEditor/Viewport/UIEditorViewportSlot.h>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorViewportShellVisualState {
std::size_t hoveredToolIndex = Widgets::UIEditorViewportSlotInvalidIndex;
std::size_t activeToolIndex = Widgets::UIEditorViewportSlotInvalidIndex;
Widgets::UIEditorStatusBarState statusBarState = {};
};
struct UIEditorViewportShellSpec {
Widgets::UIEditorViewportSlotChrome chrome = {};
std::vector<Widgets::UIEditorViewportSlotToolItem> toolItems = {};
std::vector<Widgets::UIEditorStatusBarSegment> statusSegments = {};
UIEditorViewportInputBridgeConfig inputBridgeConfig = {};
UIEditorViewportShellVisualState visualState = {};
};
struct UIEditorViewportShellModel {
UIEditorViewportShellSpec spec = {};
Widgets::UIEditorViewportSlotFrame frame = {};
};
struct UIEditorViewportShellRequest {
Widgets::UIEditorViewportSlotLayout slotLayout = {};
::XCEngine::UI::UISize requestedViewportSize = {};
};
struct UIEditorViewportShellState {
UIEditorViewportInputBridgeState inputBridgeState = {};
};
struct UIEditorViewportShellFrame {
Widgets::UIEditorViewportSlotLayout slotLayout = {};
Widgets::UIEditorViewportSlotState slotState = {};
UIEditorViewportInputBridgeFrame inputFrame = {};
::XCEngine::UI::UISize requestedViewportSize = {};
};
UIEditorViewportShellModel ResolveUIEditorViewportShellMeasuredModel(
const UIEditorViewportShellModel& model,
const Widgets::UIEditorViewportSlotMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
UIEditorViewportShellRequest ResolveUIEditorViewportShellRequest(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorViewportShellSpec& spec,
const Widgets::UIEditorViewportSlotMetrics& metrics = {});
UIEditorViewportShellFrame UpdateUIEditorViewportShell(
UIEditorViewportShellState& state,
const UIEditorViewportShellRequest& request,
const UIEditorViewportShellModel& model,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const UIEditorViewportInputBridgeRequest& inputRequest = {});
UIEditorViewportShellFrame UpdateUIEditorViewportShell(
UIEditorViewportShellState& state,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorViewportShellModel& model,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorViewportSlotMetrics& metrics = {},
const UIEditorViewportInputBridgeRequest& inputRequest = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,225 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEditor/Shell/UIEditorStatusBar.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorViewportSlotInvalidIndex =
static_cast<std::size_t>(-1);
enum class UIEditorViewportSlotToolSlot : std::uint8_t {
Leading = 0,
Trailing
};
enum class UIEditorViewportSlotHitTargetKind : std::uint8_t {
None = 0,
TopBar,
Title,
ToolItem,
Surface,
BottomBar,
StatusSegment,
StatusSeparator
};
struct UIEditorViewportSlotFrame {
::XCEngine::UI::UITextureHandle texture = {};
::XCEngine::UI::UISize requestedSize = {};
::XCEngine::UI::UISize presentedSize = {};
bool hasTexture = false;
std::string statusText = {};
};
struct UIEditorViewportSlotChrome {
std::string title = {};
std::string subtitle = {};
bool showTopBar = true;
bool showBottomBar = true;
float topBarHeight = 24.0f;
float bottomBarHeight = 22.0f;
};
struct UIEditorViewportSlotToolItem {
std::string itemId = {};
std::string label = {};
UIEditorViewportSlotToolSlot slot = UIEditorViewportSlotToolSlot::Trailing;
bool enabled = true;
bool selected = false;
// Pure measured tool-label width. Tool button padding is applied only in layout.
float measuredLabelWidth = 0.0f;
};
struct UIEditorViewportSlotState {
bool focused = false;
bool surfaceHovered = false;
bool surfaceActive = false;
bool inputCaptured = false;
std::size_t hoveredToolIndex = UIEditorViewportSlotInvalidIndex;
std::size_t activeToolIndex = UIEditorViewportSlotInvalidIndex;
UIEditorStatusBarState statusBarState = {};
};
struct UIEditorViewportSlotMetrics {
float cornerRounding = 0.0f;
float outerBorderThickness = 1.0f;
float focusedBorderThickness = 1.0f;
float topBarPaddingX = 8.0f;
float topBarPaddingY = 0.0f;
float toolPaddingX = 8.0f;
float toolGap = 4.0f;
float toolCornerRounding = 2.0f;
float toolLabelFontSize = 11.0f;
float toolLabelInsetY = 3.0f;
float titleGap = 6.0f;
float titleFontSize = 13.0f;
float titleInsetY = 5.0f;
float subtitleFontSize = 10.0f;
float subtitleInsetY = 14.0f;
float estimatedGlyphWidth = 6.5f;
float surfaceInset = 0.0f;
float surfaceBorderThickness = 1.0f;
float focusedSurfaceBorderThickness = 1.0f;
UIEditorStatusBarMetrics statusBarMetrics = {};
};
struct UIEditorViewportSlotPalette {
::XCEngine::UI::UIColor frameColor =
::XCEngine::UI::UIColor(0.09f, 0.09f, 0.09f, 1.0f);
::XCEngine::UI::UIColor topBarColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.09f, 0.09f, 0.09f, 1.0f);
::XCEngine::UI::UIColor surfaceHoverOverlayColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 0.08f);
::XCEngine::UI::UIColor surfaceActiveOverlayColor =
::XCEngine::UI::UIColor(0.22f, 0.22f, 0.22f, 0.08f);
::XCEngine::UI::UIColor captureOverlayColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 0.05f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor surfaceBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor surfaceHoveredBorderColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor surfaceActiveBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor surfaceCapturedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor toolColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor toolHoveredColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor toolSelectedColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor toolDisabledColor =
::XCEngine::UI::UIColor(0.09f, 0.09f, 0.09f, 1.0f);
::XCEngine::UI::UIColor toolBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f);
::XCEngine::UI::UIColor imageTint =
::XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f);
UIEditorStatusBarPalette statusBarPalette = {};
};
struct UIEditorViewportSlotLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect topBarRect = {};
::XCEngine::UI::UIRect titleRect = {};
::XCEngine::UI::UIRect subtitleRect = {};
::XCEngine::UI::UIRect toolbarLeadingRect = {};
::XCEngine::UI::UIRect toolbarTrailingRect = {};
::XCEngine::UI::UIRect surfaceRect = {};
::XCEngine::UI::UIRect textureRect = {};
::XCEngine::UI::UIRect inputRect = {};
::XCEngine::UI::UIRect bottomBarRect = {};
std::vector<::XCEngine::UI::UIRect> toolItemRects = {};
UIEditorStatusBarLayout statusBarLayout = {};
::XCEngine::UI::UISize requestedSurfaceSize = {};
bool hasTopBar = false;
bool hasBottomBar = false;
};
struct UIEditorViewportSlotHitTarget {
UIEditorViewportSlotHitTargetKind kind = UIEditorViewportSlotHitTargetKind::None;
std::size_t index = UIEditorViewportSlotInvalidIndex;
};
struct UIEditorViewportSlotForegroundAppendOptions {
bool includeSurfaceTexture = true;
};
float ResolveUIEditorViewportSlotMeasuredToolLabelWidth(
const UIEditorViewportSlotToolItem& item,
const UIEditorViewportSlotMetrics& metrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
float ResolveUIEditorViewportSlotDesiredToolWidth(
const UIEditorViewportSlotToolItem& item,
const UIEditorViewportSlotMetrics& metrics = {});
UIEditorViewportSlotLayout BuildUIEditorViewportSlotLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotMetrics& metrics = {});
UIEditorViewportSlotHitTarget HitTestUIEditorViewportSlot(
const UIEditorViewportSlotLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorViewportSlotBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorViewportSlotLayout& layout,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette = {},
const UIEditorViewportSlotMetrics& metrics = {});
void AppendUIEditorViewportSlotForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorViewportSlotLayout& layout,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette = {},
const UIEditorViewportSlotMetrics& metrics = {},
const UIEditorViewportSlotForegroundAppendOptions& options = {});
void AppendUIEditorViewportSlotSurfaceTexture(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorViewportSlotLayout& layout,
const UIEditorViewportSlotFrame& frame,
const UIEditorViewportSlotPalette& palette = {});
void AppendUIEditorViewportSlot(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette = {},
const UIEditorViewportSlotMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <string_view>
namespace XCEngine::UI::Editor::Widgets {
enum class UIEditorCollectionPrimitiveKind : std::uint8_t {
None = 0,
ScrollView,
TreeView,
TreeItem,
ListView,
ListItem,
PropertySection,
FieldRow
};
UIEditorCollectionPrimitiveKind ClassifyUIEditorCollectionPrimitive(std::string_view tagName);
bool IsUIEditorCollectionPrimitiveContainer(UIEditorCollectionPrimitiveKind kind);
bool UsesUIEditorCollectionPrimitiveColumnLayout(UIEditorCollectionPrimitiveKind kind);
bool IsUIEditorCollectionPrimitiveHoverable(UIEditorCollectionPrimitiveKind kind);
bool DoesUIEditorCollectionPrimitiveClipChildren(UIEditorCollectionPrimitiveKind kind);
float ResolveUIEditorCollectionPrimitivePadding(
UIEditorCollectionPrimitiveKind kind);
float ResolveUIEditorCollectionPrimitiveDefaultHeight(
UIEditorCollectionPrimitiveKind kind);
float ResolveUIEditorCollectionPrimitiveIndent(
UIEditorCollectionPrimitiveKind kind,
float indentLevel);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,33 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
struct UIEditorHsvColor {
float hue = 0.0f;
float saturation = 0.0f;
float value = 0.0f;
float alpha = 1.0f;
};
float ClampUIEditorColorUnit(float value);
int ToUIEditorColorByte(float value);
UIEditorHsvColor ConvertUIEditorColorToHsv(
const ::XCEngine::UI::UIColor& color,
float fallbackHue = 0.0f);
::XCEngine::UI::UIColor ConvertUIEditorHsvToColor(const UIEditorHsvColor& hsv);
UIEditorHsvColor ResolveUIEditorDisplayHsv(
const ::XCEngine::UI::UIColor& color,
float rememberedHue,
bool hueValid);
std::string FormatUIEditorColorHex(
const ::XCEngine::UI::UIColor& color,
bool includeAlpha = true);
std::string FormatUIEditorColorChannelsText(
const ::XCEngine::UI::UIColor& color,
bool includeAlpha = true);
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,125 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
namespace XCEngine::UI::Editor::Widgets {
struct UIEditorInspectorFieldStyleTokens {
::XCEngine::UI::UIColor rowHoverColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor rowActiveColor =
::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
::XCEngine::UI::UIColor labelColor =
::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
::XCEngine::UI::UIColor valueColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor readOnlyValueColor =
::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
::XCEngine::UI::UIColor controlColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor controlHoverColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor controlEditingColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
::XCEngine::UI::UIColor controlReadOnlyColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor controlBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor controlFocusedBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor prefixColor =
::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
::XCEngine::UI::UIColor prefixBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor axisXColor =
::XCEngine::UI::UIColor(0.78f, 0.42f, 0.42f, 1.0f);
::XCEngine::UI::UIColor axisYColor =
::XCEngine::UI::UIColor(0.56f, 0.72f, 0.46f, 1.0f);
::XCEngine::UI::UIColor axisZColor =
::XCEngine::UI::UIColor(0.45f, 0.62f, 0.82f, 1.0f);
::XCEngine::UI::UIColor axisWColor =
::XCEngine::UI::UIColor(0.76f, 0.66f, 0.42f, 1.0f);
::XCEngine::UI::UIColor arrowColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor swatchBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor swatchHoverBorderColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor swatchReadOnlyOverlayColor =
::XCEngine::UI::UIColor(0.08f, 0.08f, 0.08f, 0.18f);
::XCEngine::UI::UIColor popupColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor popupBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor popupHeaderColor =
::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
::XCEngine::UI::UIColor popupTitleColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor popupTextColor =
::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f);
::XCEngine::UI::UIColor popupTextMutedColor =
::XCEngine::UI::UIColor(0.66f, 0.66f, 0.66f, 1.0f);
::XCEngine::UI::UIColor previewBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor previewBaseColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor checkerLightColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor checkerDarkColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor sliderBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor numericBoxColor =
::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
::XCEngine::UI::UIColor numericBoxBorderColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor numericBoxTextColor =
::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
::XCEngine::UI::UIColor closeButtonColor =
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f);
::XCEngine::UI::UIColor closeButtonHoverColor =
::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
::XCEngine::UI::UIColor closeGlyphColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
::XCEngine::UI::UIColor handleFillColor =
::XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f);
::XCEngine::UI::UIColor handleStrokeColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 0.5f);
float controlTrailingInset = 9.0f;
float controlMinWidth = 88.0f;
float dropdownArrowWidth = 14.0f;
float vectorComponentMinWidth = 74.0f;
float vectorPrefixWidth = 18.0f;
float vectorPrefixGap = 5.0f;
};
struct UIEditorFieldRowLayoutMetrics {
float rowHeight = 22.0f;
float horizontalPadding = 12.0f;
float labelControlGap = 20.0f;
float controlColumnStart = 236.0f;
float controlMinWidth = 0.0f;
float controlTrailingInset = 8.0f;
float controlInsetY = 1.0f;
};
struct UIEditorFieldRowLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect labelRect = {};
::XCEngine::UI::UIRect controlRect = {};
};
const UIEditorInspectorFieldStyleTokens& GetUIEditorInspectorFieldStyleTokens();
bool AreUIEditorFieldMetricsEqual(float lhs, float rhs);
bool AreUIEditorFieldColorsEqual(
const ::XCEngine::UI::UIColor& lhs,
const ::XCEngine::UI::UIColor& rhs);
UIEditorFieldRowLayout BuildUIEditorFieldRowLayout(
const ::XCEngine::UI::UIRect& bounds,
float minimumControlWidth,
const UIEditorFieldRowLayoutMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,154 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEngine/UI/Text/UITextEditing.h>
#include <XCEngine/UI/DrawData.h>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <string>
namespace XCEngine::UI::Editor::Widgets {
inline float MeasureUIEditorTextLayoutHeight(float fontSize) {
return (std::max)(0.0f, fontSize * 1.35f);
}
inline float ResolveUIEditorTextTop(
const ::XCEngine::UI::UIRect& rect,
float fontSize,
float offsetY = 0.0f) {
const float textHeight = MeasureUIEditorTextLayoutHeight(fontSize);
const float centeredTop =
rect.y + (std::max)(0.0f, std::floor((rect.height - textHeight) * 0.5f));
return centeredTop - 1.0f + offsetY;
}
inline float ResolveUIEditorControlTextTop(
const ::XCEngine::UI::UIRect& rect,
float fontSize,
float offsetY = 0.0f) {
const float textHeight = MeasureUIEditorTextLayoutHeight(fontSize);
const float centeredTop =
rect.y + (std::max)(0.0f, std::floor((rect.height - textHeight) * 0.5f));
return centeredTop + offsetY;
}
inline ::XCEngine::UI::UIRect ResolveUIEditorTextClipRect(
const ::XCEngine::UI::UIRect& rect,
float fontSize) {
const float extraPadding = (std::max)(2.0f, std::ceil(fontSize * 0.35f));
return ::XCEngine::UI::UIRect(
rect.x,
rect.y - 1.0f,
rect.width,
rect.height + extraPadding + 1.0f);
}
inline float MeasureUIEditorTextWidth(
const std::string& text,
float fontSize,
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr) {
if (textMeasurer != nullptr &&
!text.empty() &&
fontSize > 0.0f) {
return textMeasurer->MeasureTextWidth(
::XCEngine::UI::Editor::UIEditorTextMeasureRequest{
text,
fontSize
});
}
return fontSize * 0.56f *
static_cast<float>(::XCEngine::UI::Text::CountUtf8Codepoints(text));
}
inline float MeasureUIEditorTextWidthToCaret(
const std::string& text,
std::size_t caretOffset,
float fontSize,
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr) {
const std::size_t clampedCaretOffset = (std::min)(caretOffset, text.size());
if (textMeasurer != nullptr &&
clampedCaretOffset > 0u &&
fontSize > 0.0f) {
return textMeasurer->MeasureTextAdvance(
::XCEngine::UI::Editor::UIEditorTextMeasureRequest{
std::string_view(text.data(), clampedCaretOffset),
fontSize
});
}
return fontSize * 0.56f *
static_cast<float>(::XCEngine::UI::Text::CountUtf8CodepointsInRange(
text,
0u,
clampedCaretOffset));
}
inline std::uint64_t GetUIEditorTextCaretClockNanoseconds() {
using Clock = std::chrono::steady_clock;
const auto now = Clock::now().time_since_epoch();
return static_cast<std::uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(now).count());
}
inline bool ShouldBlinkUIEditorTextCaret(std::uint64_t blinkStartNanoseconds) {
constexpr std::uint64_t kInitialVisibleDurationNanoseconds = 500000000ull;
constexpr std::uint64_t kBlinkPhaseDurationNanoseconds = 500000000ull;
if (blinkStartNanoseconds == 0u) {
return true;
}
const std::uint64_t nowNanoseconds = GetUIEditorTextCaretClockNanoseconds();
if (nowNanoseconds <= blinkStartNanoseconds) {
return true;
}
const std::uint64_t elapsedNanoseconds = nowNanoseconds - blinkStartNanoseconds;
if (elapsedNanoseconds < kInitialVisibleDurationNanoseconds) {
return true;
}
const std::uint64_t blinkElapsedNanoseconds =
elapsedNanoseconds - kInitialVisibleDurationNanoseconds;
return ((blinkElapsedNanoseconds / kBlinkPhaseDurationNanoseconds) % 2u) == 0u;
}
inline void AppendUIEditorTextCaret(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& rect,
const std::string& text,
std::size_t caretOffset,
std::uint64_t blinkStartNanoseconds,
const ::XCEngine::UI::UIColor& color,
float fontSize,
float insetX,
float insetY = 0.0f,
float thickness = 1.0f,
const ::XCEngine::UI::Editor::UIEditorTextMeasurer* textMeasurer = nullptr) {
if (color.a <= 0.0f || !ShouldBlinkUIEditorTextCaret(blinkStartNanoseconds)) {
return;
}
const float caretX = (std::min)(
rect.x + rect.width - 1.0f,
rect.x + insetX +
MeasureUIEditorTextWidthToCaret(
text,
caretOffset,
fontSize,
textMeasurer));
const float top = ResolveUIEditorControlTextTop(rect, fontSize, insetY);
const float bottom = top + MeasureUIEditorTextLayoutHeight(fontSize);
drawList.AddLine(
::XCEngine::UI::UIPoint(caretX, top),
::XCEngine::UI::UIPoint(caretX, bottom),
color,
thickness);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,25 @@
#pragma once
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor {
const UIEditorPanelDescriptor* ResolveUIEditorSingleVisibleRootPanelDescriptor(
const UIEditorWorkspaceController& controller);
bool HasUIEditorSingleVisibleRootTab(
const UIEditorWorkspaceController& controller);
std::string ResolveUIEditorDetachedWorkspaceTitle(
const UIEditorWorkspaceController& controller,
std::string_view fallbackTitle = {});
::XCEngine::UI::UISize ResolveUIEditorDetachedWorkspaceMinimumOuterSize(
const UIEditorWorkspaceController& controller,
const ::XCEngine::UI::UISize& fallbackSize = ::XCEngine::UI::UISize(640.0f, 360.0f));
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,91 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWindowWorkspaceModel.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
enum class UIEditorWindowWorkspaceOperationStatus : std::uint8_t {
Changed = 0,
NoOp,
Rejected
};
struct UIEditorWindowWorkspaceOperationResult {
UIEditorWindowWorkspaceOperationStatus status =
UIEditorWindowWorkspaceOperationStatus::Rejected;
std::string message = {};
std::string sourceWindowId = {};
std::string targetWindowId = {};
std::string panelId = {};
std::string activeWindowId = {};
std::vector<std::string> windowIds = {};
};
std::string_view GetUIEditorWindowWorkspaceOperationStatusName(
UIEditorWindowWorkspaceOperationStatus status);
class UIEditorWindowWorkspaceController {
public:
UIEditorWindowWorkspaceController() = default;
UIEditorWindowWorkspaceController(
UIEditorPanelRegistry panelRegistry,
UIEditorWindowWorkspaceSet windowSet);
const UIEditorPanelRegistry& GetPanelRegistry() const {
return m_panelRegistry;
}
const UIEditorWindowWorkspaceSet& GetWindowSet() const {
return m_windowSet;
}
UIEditorWindowWorkspaceValidationResult ValidateState() const;
UIEditorWindowWorkspaceOperationResult DetachPanelToNewWindow(
std::string_view sourceWindowId,
std::string_view sourceNodeId,
std::string_view panelId,
std::string_view preferredNewWindowId = {});
UIEditorWindowWorkspaceOperationResult MovePanelToStack(
std::string_view sourceWindowId,
std::string_view sourceNodeId,
std::string_view panelId,
std::string_view targetWindowId,
std::string_view targetNodeId,
std::size_t targetVisibleInsertionIndex);
UIEditorWindowWorkspaceOperationResult DockPanelRelative(
std::string_view sourceWindowId,
std::string_view sourceNodeId,
std::string_view panelId,
std::string_view targetWindowId,
std::string_view targetNodeId,
UIEditorWorkspaceDockPlacement placement,
float splitRatio = 0.5f);
private:
UIEditorWindowWorkspaceOperationResult BuildOperationResult(
UIEditorWindowWorkspaceOperationStatus status,
std::string message,
std::string_view sourceWindowId,
std::string_view targetWindowId,
std::string_view panelId) const;
std::string MakeUniqueWindowId(std::string_view base) const;
UIEditorPanelRegistry m_panelRegistry = {};
UIEditorWindowWorkspaceSet m_windowSet = {};
};
UIEditorWindowWorkspaceController BuildDefaultUIEditorWindowWorkspaceController(
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
std::string primaryWindowId = "main-window");
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,63 @@
#pragma once
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorWindowWorkspaceState {
std::string windowId = {};
UIEditorWorkspaceModel workspace = {};
UIEditorWorkspaceSession session = {};
};
struct UIEditorWindowWorkspaceSet {
std::string primaryWindowId = {};
std::string activeWindowId = {};
std::vector<UIEditorWindowWorkspaceState> windows = {};
};
enum class UIEditorWindowWorkspaceValidationCode : std::uint8_t {
None = 0,
InvalidPanelRegistry,
EmptyWindowId,
DuplicateWindowId,
DuplicatePanelAcrossWindows,
MissingPrimaryWindow,
MissingActiveWindow,
InvalidWorkspace,
InvalidSession
};
struct UIEditorWindowWorkspaceValidationResult {
UIEditorWindowWorkspaceValidationCode code = UIEditorWindowWorkspaceValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorWindowWorkspaceValidationCode::None;
}
};
UIEditorWindowWorkspaceSet BuildDefaultUIEditorWindowWorkspaceSet(
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
std::string primaryWindowId = "main-window");
const UIEditorWindowWorkspaceState* FindUIEditorWindowWorkspaceState(
const UIEditorWindowWorkspaceSet& windowSet,
std::string_view windowId);
UIEditorWindowWorkspaceState* FindMutableUIEditorWindowWorkspaceState(
UIEditorWindowWorkspaceSet& windowSet,
std::string_view windowId);
UIEditorWindowWorkspaceValidationResult ValidateUIEditorWindowWorkspaceSet(
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWindowWorkspaceSet& windowSet);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,145 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Panels/UIEditorPanelContentHost.h>
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Viewport/UIEditorViewportShell.h>
#include <XCEditor/Workspace/UIEditorWorkspaceInputOwner.h>
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
#include <XCEditor/Docking/UIEditorDockHost.h>
#include <XCEngine/UI/DrawData.h>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorWorkspacePanelPresentationModel {
std::string panelId = {};
UIEditorPanelPresentationKind kind = UIEditorPanelPresentationKind::Placeholder;
UIEditorViewportShellModel viewportShellModel = {};
};
struct UIEditorWorkspacePanelPresentationState {
std::string panelId = {};
UIEditorViewportShellState viewportShellState = {};
};
struct UIEditorWorkspaceComposeState {
UIEditorPanelContentHostState contentHostState = {};
std::vector<UIEditorWorkspacePanelPresentationState> panelStates = {};
};
struct UIEditorWorkspaceViewportComposeRequest {
std::string panelId = {};
::XCEngine::UI::UIRect bounds = {};
UIEditorViewportShellModel viewportShellModel = {};
UIEditorViewportShellRequest viewportShellRequest = {};
};
struct UIEditorWorkspaceComposeRequest {
Widgets::UIEditorDockHostLayout dockHostLayout = {};
UIEditorPanelContentHostRequest contentHostRequest = {};
std::vector<UIEditorWorkspaceViewportComposeRequest> viewportRequests = {};
};
struct UIEditorWorkspaceViewportComposeFrame {
std::string panelId = {};
::XCEngine::UI::UIRect bounds = {};
UIEditorViewportShellModel viewportShellModel = {};
UIEditorViewportShellFrame viewportShellFrame = {};
};
struct UIEditorWorkspaceComposeFrame {
Widgets::UIEditorDockHostLayout dockHostLayout = {};
UIEditorPanelContentHostFrame contentHostFrame = {};
std::vector<UIEditorWorkspaceViewportComposeFrame> viewportFrames = {};
};
struct UIEditorWorkspaceComposeAppendOptions {
bool deferDockPreviewOverlay = false;
bool includeViewportTextures = true;
};
const UIEditorWorkspacePanelPresentationModel* FindUIEditorWorkspacePanelPresentationModel(
const std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
std::string_view panelId);
const UIEditorWorkspacePanelPresentationState* FindUIEditorWorkspacePanelPresentationState(
const UIEditorWorkspaceComposeState& state,
std::string_view panelId);
const UIEditorWorkspaceViewportComposeRequest* FindUIEditorWorkspaceViewportPresentationRequest(
const UIEditorWorkspaceComposeRequest& request,
std::string_view panelId);
const UIEditorWorkspaceViewportComposeFrame* FindUIEditorWorkspaceViewportPresentationFrame(
const UIEditorWorkspaceComposeFrame& frame,
std::string_view panelId);
UIEditorWorkspaceComposeRequest ResolveUIEditorWorkspaceComposeRequest(
const Widgets::UIEditorDockHostLayout& dockHostLayout,
const UIEditorPanelRegistry& panelRegistry,
const std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
UIEditorWorkspaceComposeRequest ResolveUIEditorWorkspaceComposeRequest(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
const Widgets::UIEditorDockHostState& dockHostState = {},
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {},
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
UIEditorWorkspaceComposeFrame UpdateUIEditorWorkspaceCompose(
UIEditorWorkspaceComposeState& state,
const UIEditorWorkspaceComposeRequest& request,
const UIEditorPanelRegistry& panelRegistry,
const std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const UIEditorWorkspaceInputOwner* inputOwner = nullptr);
UIEditorWorkspaceComposeFrame UpdateUIEditorWorkspaceCompose(
UIEditorWorkspaceComposeState& state,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorDockHostState& dockHostState = {},
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {},
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {},
const UIEditorWorkspaceInputOwner* inputOwner = nullptr,
const UIEditorTextMeasurer* textMeasurer = nullptr);
std::vector<std::string> CollectUIEditorWorkspaceComposeExternalBodyPanelIds(
const UIEditorWorkspaceComposeFrame& frame);
void AppendUIEditorWorkspaceCompose(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorWorkspaceComposeFrame& frame,
const Widgets::UIEditorDockHostPalette& dockHostPalette = {},
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {},
const Widgets::UIEditorViewportSlotPalette& viewportPalette = {},
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {},
const UIEditorWorkspaceComposeAppendOptions& options = {});
void AppendUIEditorWorkspaceComposeOverlay(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorWorkspaceComposeFrame& frame,
const Widgets::UIEditorDockHostPalette& dockHostPalette = {},
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {});
void AppendUIEditorWorkspaceComposeViewportTextures(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorWorkspaceComposeFrame& frame,
const Widgets::UIEditorViewportSlotPalette& viewportPalette = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,152 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWorkspaceLayoutPersistence.h>
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
enum class UIEditorWorkspaceCommandKind : std::uint8_t {
OpenPanel = 0,
ClosePanel,
ShowPanel,
HidePanel,
ActivatePanel,
ResetWorkspace
};
enum class UIEditorWorkspaceCommandStatus : std::uint8_t {
Changed = 0,
NoOp,
Rejected
};
struct UIEditorWorkspaceCommand {
UIEditorWorkspaceCommandKind kind = UIEditorWorkspaceCommandKind::ActivatePanel;
std::string panelId = {};
};
struct UIEditorWorkspaceCommandResult {
UIEditorWorkspaceCommandKind kind = UIEditorWorkspaceCommandKind::ActivatePanel;
UIEditorWorkspaceCommandStatus status = UIEditorWorkspaceCommandStatus::Rejected;
std::string panelId = {};
std::string message = {};
std::string activePanelId = {};
std::vector<std::string> visiblePanelIds = {};
};
enum class UIEditorWorkspaceControllerValidationCode : std::uint8_t {
None = 0,
InvalidPanelRegistry,
InvalidWorkspace,
InvalidWorkspaceSession
};
struct UIEditorWorkspaceControllerValidationResult {
UIEditorWorkspaceControllerValidationCode code =
UIEditorWorkspaceControllerValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorWorkspaceControllerValidationCode::None;
}
};
std::string_view GetUIEditorWorkspaceCommandKindName(UIEditorWorkspaceCommandKind kind);
std::string_view GetUIEditorWorkspaceCommandStatusName(UIEditorWorkspaceCommandStatus status);
enum class UIEditorWorkspaceLayoutOperationStatus : std::uint8_t {
Changed = 0,
NoOp,
Rejected
};
struct UIEditorWorkspaceLayoutOperationResult {
UIEditorWorkspaceLayoutOperationStatus status =
UIEditorWorkspaceLayoutOperationStatus::Rejected;
std::string message = {};
std::string activePanelId = {};
std::vector<std::string> visiblePanelIds = {};
};
std::string_view GetUIEditorWorkspaceLayoutOperationStatusName(
UIEditorWorkspaceLayoutOperationStatus status);
class UIEditorWorkspaceController {
public:
UIEditorWorkspaceController() = default;
UIEditorWorkspaceController(
UIEditorPanelRegistry panelRegistry,
UIEditorWorkspaceModel workspace,
UIEditorWorkspaceSession session);
const UIEditorPanelRegistry& GetPanelRegistry() const {
return m_panelRegistry;
}
const UIEditorWorkspaceModel& GetWorkspace() const {
return m_workspace;
}
const UIEditorWorkspaceSession& GetSession() const {
return m_session;
}
UIEditorWorkspaceControllerValidationResult ValidateState() const;
UIEditorWorkspaceLayoutSnapshot CaptureLayoutSnapshot() const;
UIEditorWorkspaceLayoutOperationResult RestoreLayoutSnapshot(
const UIEditorWorkspaceLayoutSnapshot& snapshot);
UIEditorWorkspaceLayoutOperationResult RestoreSerializedLayout(
std::string_view serializedLayout);
UIEditorWorkspaceLayoutOperationResult SetSplitRatio(
std::string_view nodeId,
float splitRatio);
UIEditorWorkspaceLayoutOperationResult MoveTabToStack(
std::string_view sourceNodeId,
std::string_view panelId,
std::string_view targetNodeId,
std::size_t targetVisibleInsertionIndex);
UIEditorWorkspaceLayoutOperationResult DockTabRelative(
std::string_view sourceNodeId,
std::string_view panelId,
std::string_view targetNodeId,
UIEditorWorkspaceDockPlacement placement,
float splitRatio = 0.5f);
UIEditorWorkspaceCommandResult Dispatch(const UIEditorWorkspaceCommand& command);
private:
UIEditorWorkspaceCommandResult BuildResult(
const UIEditorWorkspaceCommand& command,
UIEditorWorkspaceCommandStatus status,
std::string message) const;
UIEditorWorkspaceCommandResult FinalizeMutation(
const UIEditorWorkspaceCommand& command,
bool changed,
std::string changedMessage,
std::string unexpectedFailureMessage,
const UIEditorWorkspaceModel& previousWorkspace,
const UIEditorWorkspaceSession& previousSession);
UIEditorWorkspaceLayoutOperationResult BuildLayoutOperationResult(
UIEditorWorkspaceLayoutOperationStatus status,
std::string message) const;
const UIEditorPanelDescriptor* FindPanelDescriptor(std::string_view panelId) const;
UIEditorPanelRegistry m_panelRegistry = {};
UIEditorWorkspaceModel m_baselineWorkspace = {};
UIEditorWorkspaceSession m_baselineSession = {};
UIEditorWorkspaceModel m_workspace = {};
UIEditorWorkspaceSession m_session = {};
};
UIEditorWorkspaceController BuildDefaultUIEditorWorkspaceController(
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,54 @@
#pragma once
#include <XCEditor/Panels/UIEditorPanelContentHost.h>
#include <XCEditor/Docking/UIEditorDockHost.h>
#include <XCEngine/UI/Types.h>
#include <cstdint>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor {
enum class UIEditorWorkspaceInputOwnerKind : std::uint8_t {
None = 0,
DockHost,
HostedPanel,
Viewport
};
struct UIEditorWorkspaceInputOwner {
UIEditorWorkspaceInputOwnerKind kind = UIEditorWorkspaceInputOwnerKind::None;
std::string panelId = {};
};
std::string_view GetUIEditorWorkspaceInputOwnerKindName(
UIEditorWorkspaceInputOwnerKind kind);
bool AreUIEditorWorkspaceInputOwnersEquivalent(
const UIEditorWorkspaceInputOwner& lhs,
const UIEditorWorkspaceInputOwner& rhs);
bool IsUIEditorWorkspaceDockHostInputOwner(
const UIEditorWorkspaceInputOwner& owner);
bool IsUIEditorWorkspaceHostedPanelInputOwner(
const UIEditorWorkspaceInputOwner& owner,
std::string_view panelId = {});
bool IsUIEditorWorkspaceViewportInputOwner(
const UIEditorWorkspaceInputOwner& owner,
std::string_view panelId = {});
UIEditorWorkspaceInputOwner ResolveUIEditorWorkspacePointerInputOwner(
const Widgets::UIEditorDockHostLayout& dockHostLayout,
const UIEditorPanelContentHostFrame& contentHostFrame,
const ::XCEngine::UI::UIPoint& pointerPosition);
UIEditorWorkspaceInputOwner NormalizeUIEditorWorkspaceInputOwner(
UIEditorWorkspaceInputOwner owner,
const Widgets::UIEditorDockHostLayout& dockHostLayout,
const UIEditorPanelContentHostFrame& contentHostFrame);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,57 @@
#pragma once
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
#include <XCEditor/Docking/UIEditorDockHostInteraction.h>
#include <XCEditor/Panels/UIEditorPanelHostLifecycle.h>
#include <XCEditor/Workspace/UIEditorWorkspaceCompose.h>
#include <XCEditor/Workspace/UIEditorWorkspaceInputOwner.h>
#include <XCEngine/UI/Types.h>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorWorkspaceInteractionModel {
std::vector<UIEditorWorkspacePanelPresentationModel> workspacePresentations = {};
};
struct UIEditorWorkspaceInteractionState {
UIEditorDockHostInteractionState dockHostInteractionState = {};
UIEditorPanelHostLifecycleState panelHostLifecycleState = {};
UIEditorWorkspaceComposeState composeState = {};
UIEditorWorkspaceInputOwner inputOwner = {};
};
struct UIEditorWorkspaceInteractionResult {
bool consumed = false;
bool requestPointerCapture = false;
bool releasePointerCapture = false;
bool viewportInteractionChanged = false;
std::string viewportPanelId = {};
UIEditorViewportInputBridgeFrame viewportInputFrame = {};
UIEditorDockHostInteractionResult dockHostResult = {};
};
struct UIEditorWorkspaceInteractionFrame {
UIEditorDockHostInteractionFrame dockHostFrame = {};
UIEditorPanelHostLifecycleFrame panelHostLifecycleFrame = {};
UIEditorWorkspaceComposeFrame composeFrame = {};
UIEditorWorkspaceInteractionResult result = {};
UIEditorWorkspaceInputOwner previousInputOwner = {};
UIEditorWorkspaceInputOwner inputOwner = {};
bool inputOwnerChanged = false;
};
UIEditorWorkspaceInteractionFrame UpdateUIEditorWorkspaceInteraction(
UIEditorWorkspaceInteractionState& state,
UIEditorWorkspaceController& controller,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorWorkspaceInteractionModel& model,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
const Widgets::UIEditorDockHostMetrics& dockHostMetrics = {},
const Widgets::UIEditorViewportSlotMetrics& viewportMetrics = {},
const UIEditorTextMeasurer* textMeasurer = nullptr);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,55 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor {
struct UIEditorWorkspaceLayoutSnapshot {
UIEditorWorkspaceModel workspace = {};
UIEditorWorkspaceSession session = {};
};
enum class UIEditorWorkspaceLayoutLoadCode : std::uint8_t {
None = 0,
InvalidPanelRegistry,
EmptyInput,
InvalidHeader,
UnsupportedVersion,
MissingActiveRecord,
UnexpectedEndOfInput,
InvalidNodeRecord,
InvalidSessionRecord,
InvalidWorkspace,
InvalidWorkspaceSession
};
struct UIEditorWorkspaceLayoutLoadResult {
UIEditorWorkspaceLayoutLoadCode code = UIEditorWorkspaceLayoutLoadCode::None;
std::string message = {};
UIEditorWorkspaceLayoutSnapshot snapshot = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorWorkspaceLayoutLoadCode::None;
}
};
UIEditorWorkspaceLayoutSnapshot BuildUIEditorWorkspaceLayoutSnapshot(
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session);
bool AreUIEditorWorkspaceLayoutSnapshotsEquivalent(
const UIEditorWorkspaceLayoutSnapshot& lhs,
const UIEditorWorkspaceLayoutSnapshot& rhs);
std::string SerializeUIEditorWorkspaceLayoutSnapshot(
const UIEditorWorkspaceLayoutSnapshot& snapshot);
UIEditorWorkspaceLayoutLoadResult DeserializeUIEditorWorkspaceLayoutSnapshot(
const UIEditorPanelRegistry& panelRegistry,
std::string_view serializedLayout);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,94 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorWorkspaceSession;
enum class UIEditorWorkspaceNodeKind : std::uint8_t {
Panel = 0,
TabStack,
Split
};
enum class UIEditorWorkspaceSplitAxis : std::uint8_t {
Horizontal = 0,
Vertical
};
enum class UIEditorWorkspaceDockPlacement : std::uint8_t {
Center = 0,
Left,
Right,
Top,
Bottom
};
struct UIEditorWorkspacePanelState {
std::string panelId = {};
std::string title = {};
bool placeholder = false;
};
struct UIEditorWorkspaceNode {
UIEditorWorkspaceNodeKind kind = UIEditorWorkspaceNodeKind::Panel;
std::string nodeId = {};
UIEditorWorkspaceSplitAxis splitAxis = UIEditorWorkspaceSplitAxis::Horizontal;
float splitRatio = 0.5f;
std::size_t selectedTabIndex = 0u;
UIEditorWorkspacePanelState panel = {};
std::vector<UIEditorWorkspaceNode> children = {};
};
struct UIEditorWorkspaceModel {
UIEditorWorkspaceNode root = {};
std::string activePanelId = {};
};
struct UIEditorWorkspaceVisiblePanel {
std::string panelId = {};
std::string title = {};
bool active = false;
bool placeholder = false;
};
UIEditorWorkspaceNode BuildUIEditorWorkspacePanel(
std::string nodeId,
std::string panelId,
std::string title,
bool placeholder = false);
UIEditorWorkspaceNode BuildUIEditorWorkspaceSingleTabStack(
std::string nodeId,
std::string panelId,
std::string title,
bool placeholder = false);
UIEditorWorkspaceNode BuildUIEditorWorkspaceTabStack(
std::string nodeId,
std::vector<UIEditorWorkspaceNode> panels,
std::size_t selectedTabIndex = 0u);
UIEditorWorkspaceNode BuildUIEditorWorkspaceSplit(
std::string nodeId,
UIEditorWorkspaceSplitAxis axis,
float splitRatio,
UIEditorWorkspaceNode primary,
UIEditorWorkspaceNode secondary);
UIEditorWorkspaceModel CanonicalizeUIEditorWorkspaceModel(
UIEditorWorkspaceModel workspace);
bool AreUIEditorWorkspaceNodesEquivalent(
const UIEditorWorkspaceNode& lhs,
const UIEditorWorkspaceNode& rhs);
bool AreUIEditorWorkspaceModelsEquivalent(
const UIEditorWorkspaceModel& lhs,
const UIEditorWorkspaceModel& rhs);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,60 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWorkspaceModel.h>
#include <cstddef>
#include <string_view>
namespace XCEngine::UI::Editor {
struct UIEditorWorkspaceSession;
bool TryActivateUIEditorWorkspacePanel(
UIEditorWorkspaceModel& workspace,
std::string_view panelId);
bool TrySetUIEditorWorkspaceSplitRatio(
UIEditorWorkspaceModel& workspace,
std::string_view nodeId,
float splitRatio);
bool TryExtractUIEditorWorkspaceVisiblePanelNode(
UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
std::string_view sourceNodeId,
std::string_view panelId,
UIEditorWorkspaceNode& extractedPanel);
bool TryInsertUIEditorWorkspacePanelNodeToStack(
UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
UIEditorWorkspaceNode panelNode,
std::string_view targetNodeId,
std::size_t targetVisibleInsertionIndex);
bool TryDockUIEditorWorkspacePanelNodeRelative(
UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
UIEditorWorkspaceNode panelNode,
std::string_view targetNodeId,
UIEditorWorkspaceDockPlacement placement,
float splitRatio = 0.5f);
bool TryMoveUIEditorWorkspaceTabToStack(
UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
std::string_view sourceNodeId,
std::string_view panelId,
std::string_view targetNodeId,
std::size_t targetVisibleInsertionIndex);
bool TryDockUIEditorWorkspaceTabRelative(
UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
std::string_view sourceNodeId,
std::string_view panelId,
std::string_view targetNodeId,
UIEditorWorkspaceDockPlacement placement,
float splitRatio = 0.5f);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,24 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWorkspaceModel.h>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
std::vector<UIEditorWorkspaceVisiblePanel> CollectUIEditorWorkspaceVisiblePanels(
const UIEditorWorkspaceModel& workspace);
bool ContainsUIEditorWorkspacePanel(
const UIEditorWorkspaceModel& workspace,
std::string_view panelId);
const UIEditorWorkspaceNode* FindUIEditorWorkspaceNode(
const UIEditorWorkspaceModel& workspace,
std::string_view nodeId);
const UIEditorWorkspacePanelState* FindUIEditorWorkspaceActivePanel(
const UIEditorWorkspaceModel& workspace);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,98 @@
#pragma once
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Workspace/UIEditorWorkspaceModel.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
struct UIEditorPanelSessionState {
std::string panelId = {};
bool open = true;
bool visible = true;
};
struct UIEditorWorkspaceSession {
std::vector<UIEditorPanelSessionState> panelStates = {};
};
enum class UIEditorWorkspaceSessionValidationCode : std::uint8_t {
None = 0,
MissingPanelState,
UnknownPanelId,
DuplicatePanelId,
ClosedPanelVisible,
NonHideablePanelHidden,
NonCloseablePanelClosed,
InvalidActivePanelId
};
struct UIEditorWorkspaceSessionValidationResult {
UIEditorWorkspaceSessionValidationCode code = UIEditorWorkspaceSessionValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorWorkspaceSessionValidationCode::None;
}
};
UIEditorWorkspaceSession BuildDefaultUIEditorWorkspaceSession(
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace);
const UIEditorPanelSessionState* FindUIEditorPanelSessionState(
const UIEditorWorkspaceSession& session,
std::string_view panelId);
UIEditorWorkspaceSessionValidationResult ValidateUIEditorWorkspaceSession(
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session);
bool AreUIEditorWorkspaceSessionsEquivalent(
const UIEditorWorkspaceSession& lhs,
const UIEditorWorkspaceSession& rhs);
std::vector<UIEditorWorkspaceVisiblePanel> CollectUIEditorWorkspaceVisiblePanels(
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session);
const UIEditorWorkspacePanelState* FindUIEditorWorkspaceActivePanel(
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session);
bool TryOpenUIEditorWorkspacePanel(
const UIEditorPanelRegistry& panelRegistry,
UIEditorWorkspaceModel& workspace,
UIEditorWorkspaceSession& session,
std::string_view panelId);
bool TryCloseUIEditorWorkspacePanel(
const UIEditorPanelRegistry& panelRegistry,
UIEditorWorkspaceModel& workspace,
UIEditorWorkspaceSession& session,
std::string_view panelId);
bool TryShowUIEditorWorkspacePanel(
const UIEditorPanelRegistry& panelRegistry,
UIEditorWorkspaceModel& workspace,
UIEditorWorkspaceSession& session,
std::string_view panelId);
bool TryHideUIEditorWorkspacePanel(
const UIEditorPanelRegistry& panelRegistry,
UIEditorWorkspaceModel& workspace,
UIEditorWorkspaceSession& session,
std::string_view panelId);
bool TryActivateUIEditorWorkspacePanel(
const UIEditorPanelRegistry& panelRegistry,
UIEditorWorkspaceModel& workspace,
UIEditorWorkspaceSession& session,
std::string_view panelId);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,49 @@
#pragma once
#include <XCEditor/Docking/UIEditorDockHost.h>
#include <XCEditor/Docking/UIEditorDockHostInteraction.h>
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
#include <XCEditor/Workspace/UIEditorWorkspaceLayoutPersistence.h>
#include <XCEngine/UI/Types.h>
#include <string>
#include <string_view>
namespace XCEngine::UI::Editor {
struct UIEditorWorkspaceSplitterDragCorrectionState {
std::string splitNodeId = {};
UIEditorWorkspaceLayoutSnapshot baselineSnapshot = {};
Widgets::UIEditorDockHostLayout baselineDockLayout = {};
};
[[nodiscard]] bool IsUIEditorWorkspaceSplitterDragCorrectionActive(
const UIEditorWorkspaceSplitterDragCorrectionState& state);
void ResetUIEditorWorkspaceSplitterDragCorrectionState(
UIEditorWorkspaceSplitterDragCorrectionState& state);
void BeginUIEditorWorkspaceSplitterDragCorrection(
UIEditorWorkspaceSplitterDragCorrectionState& state,
std::string_view splitNodeId,
const UIEditorWorkspaceLayoutSnapshot& baselineSnapshot,
const Widgets::UIEditorDockHostLayout& baselineDockLayout);
bool TryBuildUIEditorWorkspaceSplitterDragCorrectedSnapshot(
const UIEditorWorkspaceSplitterDragCorrectionState& state,
const ::XCEngine::UI::UIPoint& pointerPosition,
bool hasPointerPosition,
const UIEditorPanelRegistry& panelRegistry,
const Widgets::UIEditorDockHostMetrics& metrics,
UIEditorWorkspaceLayoutSnapshot& outSnapshot);
bool TryApplyUIEditorWorkspaceSplitterDragCorrection(
UIEditorWorkspaceSplitterDragCorrectionState& state,
const UIEditorDockHostInteractionState& dockHostInteractionState,
const UIEditorWorkspaceLayoutSnapshot& preUpdateSnapshot,
const Widgets::UIEditorDockHostLayout& preUpdateDockLayout,
UIEditorWorkspaceController& workspaceController,
const Widgets::UIEditorDockHostMetrics& metrics);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,41 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
namespace XCEngine::UI::Editor {
struct UIEditorWorkspaceExtractedPanel {
UIEditorWorkspaceNode panelNode = {};
UIEditorPanelSessionState sessionState = {};
};
bool TryExtractUIEditorWorkspaceVisiblePanel(
UIEditorWorkspaceModel& workspace,
UIEditorWorkspaceSession& session,
std::string_view sourceNodeId,
std::string_view panelId,
UIEditorWorkspaceExtractedPanel& extractedPanel);
UIEditorWorkspaceModel BuildUIEditorDetachedWorkspaceFromExtractedPanel(
std::string rootNodeId,
UIEditorWorkspaceExtractedPanel extractedPanel);
UIEditorWorkspaceSession BuildUIEditorDetachedWorkspaceSessionFromExtractedPanel(
UIEditorWorkspaceExtractedPanel extractedPanel);
bool TryInsertExtractedUIEditorWorkspacePanelToStack(
UIEditorWorkspaceModel& workspace,
UIEditorWorkspaceSession& session,
UIEditorWorkspaceExtractedPanel extractedPanel,
std::string_view targetNodeId,
std::size_t targetVisibleInsertionIndex);
bool TryDockExtractedUIEditorWorkspacePanelRelative(
UIEditorWorkspaceModel& workspace,
UIEditorWorkspaceSession& session,
UIEditorWorkspaceExtractedPanel extractedPanel,
std::string_view targetNodeId,
UIEditorWorkspaceDockPlacement placement,
float splitRatio = 0.5f);
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,37 @@
#pragma once
#include <XCEditor/Workspace/UIEditorWorkspaceModel.h>
#include <cstdint>
#include <string>
namespace XCEngine::UI::Editor {
enum class UIEditorWorkspaceValidationCode : std::uint8_t {
None = 0,
EmptyNodeId,
DuplicateNodeId,
InvalidSplitChildCount,
InvalidSplitRatio,
EmptyTabStack,
InvalidSelectedTabIndex,
NonPanelTabChild,
EmptyPanelId,
EmptyPanelTitle,
DuplicatePanelId,
InvalidActivePanelId
};
struct UIEditorWorkspaceValidationResult {
UIEditorWorkspaceValidationCode code = UIEditorWorkspaceValidationCode::None;
std::string message = {};
[[nodiscard]] bool IsValid() const {
return code == UIEditorWorkspaceValidationCode::None;
}
};
UIEditorWorkspaceValidationResult ValidateUIEditorWorkspace(
const UIEditorWorkspaceModel& workspace);
} // namespace XCEngine::UI::Editor