关键节点
This commit is contained in:
@@ -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
|
||||
108
editor/include/XCEditor/Collections/UIEditorFilterableTreeHost.h
Normal file
108
editor/include/XCEditor/Collections/UIEditorFilterableTreeHost.h
Normal 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
|
||||
73
editor/include/XCEditor/Collections/UIEditorGridDragDrop.h
Normal file
73
editor/include/XCEditor/Collections/UIEditorGridDragDrop.h
Normal 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
|
||||
@@ -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
|
||||
121
editor/include/XCEditor/Collections/UIEditorListView.h
Normal file
121
editor/include/XCEditor/Collections/UIEditorListView.h
Normal 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
|
||||
@@ -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
|
||||
96
editor/include/XCEditor/Collections/UIEditorScrollView.h
Normal file
96
editor/include/XCEditor/Collections/UIEditorScrollView.h
Normal 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
|
||||
@@ -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
|
||||
135
editor/include/XCEditor/Collections/UIEditorTabStrip.h
Normal file
135
editor/include/XCEditor/Collections/UIEditorTabStrip.h
Normal 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
|
||||
@@ -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
|
||||
398
editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h
Normal file
398
editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h
Normal 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
|
||||
233
editor/include/XCEditor/Collections/UIEditorTreeView.h
Normal file
233
editor/include/XCEditor/Collections/UIEditorTreeView.h
Normal 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
|
||||
@@ -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
|
||||
222
editor/include/XCEditor/Docking/UIEditorDockHost.h
Normal file
222
editor/include/XCEditor/Docking/UIEditorDockHost.h
Normal 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
|
||||
@@ -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
|
||||
30
editor/include/XCEditor/Docking/UIEditorDockHostTransfer.h
Normal file
30
editor/include/XCEditor/Docking/UIEditorDockHostTransfer.h
Normal 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
|
||||
189
editor/include/XCEditor/Fields/UIEditorAssetField.h
Normal file
189
editor/include/XCEditor/Fields/UIEditorAssetField.h
Normal 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
|
||||
@@ -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
|
||||
117
editor/include/XCEditor/Fields/UIEditorBoolField.h
Normal file
117
editor/include/XCEditor/Fields/UIEditorBoolField.h
Normal 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
|
||||
@@ -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
|
||||
223
editor/include/XCEditor/Fields/UIEditorColorField.h
Normal file
223
editor/include/XCEditor/Fields/UIEditorColorField.h
Normal 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
|
||||
@@ -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
|
||||
110
editor/include/XCEditor/Fields/UIEditorEditableFieldCore.h
Normal file
110
editor/include/XCEditor/Fields/UIEditorEditableFieldCore.h
Normal 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
|
||||
131
editor/include/XCEditor/Fields/UIEditorEnumField.h
Normal file
131
editor/include/XCEditor/Fields/UIEditorEnumField.h
Normal 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
|
||||
@@ -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
|
||||
101
editor/include/XCEditor/Fields/UIEditorFieldStyle.h
Normal file
101
editor/include/XCEditor/Fields/UIEditorFieldStyle.h
Normal 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
|
||||
153
editor/include/XCEditor/Fields/UIEditorNumberField.h
Normal file
153
editor/include/XCEditor/Fields/UIEditorNumberField.h
Normal 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
|
||||
@@ -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
|
||||
154
editor/include/XCEditor/Fields/UIEditorObjectField.h
Normal file
154
editor/include/XCEditor/Fields/UIEditorObjectField.h
Normal 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
|
||||
@@ -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
|
||||
357
editor/include/XCEditor/Fields/UIEditorPropertyGrid.h
Normal file
357
editor/include/XCEditor/Fields/UIEditorPropertyGrid.h
Normal 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
|
||||
@@ -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
|
||||
137
editor/include/XCEditor/Fields/UIEditorTextField.h
Normal file
137
editor/include/XCEditor/Fields/UIEditorTextField.h
Normal 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
|
||||
@@ -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
|
||||
178
editor/include/XCEditor/Fields/UIEditorVector2Field.h
Normal file
178
editor/include/XCEditor/Fields/UIEditorVector2Field.h
Normal 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
|
||||
@@ -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
|
||||
180
editor/include/XCEditor/Fields/UIEditorVector3Field.h
Normal file
180
editor/include/XCEditor/Fields/UIEditorVector3Field.h
Normal 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
|
||||
@@ -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
|
||||
193
editor/include/XCEditor/Fields/UIEditorVector4Field.h
Normal file
193
editor/include/XCEditor/Fields/UIEditorVector4Field.h
Normal 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
|
||||
@@ -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
|
||||
107
editor/include/XCEditor/Foundation/UIEditorCommandDispatcher.h
Normal file
107
editor/include/XCEditor/Foundation/UIEditorCommandDispatcher.h
Normal 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
|
||||
70
editor/include/XCEditor/Foundation/UIEditorCommandRegistry.h
Normal file
70
editor/include/XCEditor/Foundation/UIEditorCommandRegistry.h
Normal 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
|
||||
121
editor/include/XCEditor/Foundation/UIEditorPanelInputFilter.h
Normal file
121
editor/include/XCEditor/Foundation/UIEditorPanelInputFilter.h
Normal 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
|
||||
28
editor/include/XCEditor/Foundation/UIEditorRuntimeTrace.h
Normal file
28
editor/include/XCEditor/Foundation/UIEditorRuntimeTrace.h
Normal 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
|
||||
108
editor/include/XCEditor/Foundation/UIEditorShortcutManager.h
Normal file
108
editor/include/XCEditor/Foundation/UIEditorShortcutManager.h
Normal 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
|
||||
39
editor/include/XCEditor/Foundation/UIEditorTextLayout.h
Normal file
39
editor/include/XCEditor/Foundation/UIEditorTextLayout.h
Normal 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
|
||||
23
editor/include/XCEditor/Foundation/UIEditorTextMeasurement.h
Normal file
23
editor/include/XCEditor/Foundation/UIEditorTextMeasurement.h
Normal 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
|
||||
76
editor/include/XCEditor/Foundation/UIEditorTheme.h
Normal file
76
editor/include/XCEditor/Foundation/UIEditorTheme.h
Normal 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
|
||||
129
editor/include/XCEditor/Menu/UIEditorMenuBar.h
Normal file
129
editor/include/XCEditor/Menu/UIEditorMenuBar.h
Normal 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
|
||||
116
editor/include/XCEditor/Menu/UIEditorMenuModel.h
Normal file
116
editor/include/XCEditor/Menu/UIEditorMenuModel.h
Normal 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
|
||||
148
editor/include/XCEditor/Menu/UIEditorMenuPopup.h
Normal file
148
editor/include/XCEditor/Menu/UIEditorMenuPopup.h
Normal 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
|
||||
106
editor/include/XCEditor/Menu/UIEditorMenuSession.h
Normal file
106
editor/include/XCEditor/Menu/UIEditorMenuSession.h
Normal 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
|
||||
40
editor/include/XCEditor/Panels/UIEditorHostedPanelDispatch.h
Normal file
40
editor/include/XCEditor/Panels/UIEditorHostedPanelDispatch.h
Normal 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
|
||||
85
editor/include/XCEditor/Panels/UIEditorPanelContentHost.h
Normal file
85
editor/include/XCEditor/Panels/UIEditorPanelContentHost.h
Normal 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
|
||||
160
editor/include/XCEditor/Panels/UIEditorPanelFrame.h
Normal file
160
editor/include/XCEditor/Panels/UIEditorPanelFrame.h
Normal 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
|
||||
68
editor/include/XCEditor/Panels/UIEditorPanelHostLifecycle.h
Normal file
68
editor/include/XCEditor/Panels/UIEditorPanelHostLifecycle.h
Normal 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
|
||||
57
editor/include/XCEditor/Panels/UIEditorPanelRegistry.h
Normal file
57
editor/include/XCEditor/Panels/UIEditorPanelRegistry.h
Normal 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
|
||||
61
editor/include/XCEditor/Shell/UIEditorShellAsset.h
Normal file
61
editor/include/XCEditor/Shell/UIEditorShellAsset.h
Normal 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
|
||||
29
editor/include/XCEditor/Shell/UIEditorShellCapturePolicy.h
Normal file
29
editor/include/XCEditor/Shell/UIEditorShellCapturePolicy.h
Normal 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
|
||||
171
editor/include/XCEditor/Shell/UIEditorShellCompose.h
Normal file
171
editor/include/XCEditor/Shell/UIEditorShellCompose.h
Normal 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
|
||||
194
editor/include/XCEditor/Shell/UIEditorShellInteraction.h
Normal file
194
editor/include/XCEditor/Shell/UIEditorShellInteraction.h
Normal 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
|
||||
154
editor/include/XCEditor/Shell/UIEditorStatusBar.h
Normal file
154
editor/include/XCEditor/Shell/UIEditorStatusBar.h
Normal 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
|
||||
29
editor/include/XCEditor/Shell/UIEditorStructuredShell.h
Normal file
29
editor/include/XCEditor/Shell/UIEditorStructuredShell.h
Normal 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
|
||||
@@ -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
|
||||
71
editor/include/XCEditor/Viewport/UIEditorViewportShell.h
Normal file
71
editor/include/XCEditor/Viewport/UIEditorViewportShell.h
Normal 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
|
||||
225
editor/include/XCEditor/Viewport/UIEditorViewportSlot.h
Normal file
225
editor/include/XCEditor/Viewport/UIEditorViewportSlot.h
Normal 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
|
||||
@@ -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
|
||||
33
editor/include/XCEditor/Widgets/UIEditorColorUtils.h
Normal file
33
editor/include/XCEditor/Widgets/UIEditorColorUtils.h
Normal 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
|
||||
125
editor/include/XCEditor/Widgets/UIEditorFieldRowLayout.h
Normal file
125
editor/include/XCEditor/Widgets/UIEditorFieldRowLayout.h
Normal 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
|
||||
154
editor/include/XCEditor/Widgets/UIEditorTextLayout.h
Normal file
154
editor/include/XCEditor/Widgets/UIEditorTextLayout.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
145
editor/include/XCEditor/Workspace/UIEditorWorkspaceCompose.h
Normal file
145
editor/include/XCEditor/Workspace/UIEditorWorkspaceCompose.h
Normal 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
|
||||
152
editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h
Normal file
152
editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
94
editor/include/XCEditor/Workspace/UIEditorWorkspaceModel.h
Normal file
94
editor/include/XCEditor/Workspace/UIEditorWorkspaceModel.h
Normal 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
|
||||
@@ -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
|
||||
24
editor/include/XCEditor/Workspace/UIEditorWorkspaceQueries.h
Normal file
24
editor/include/XCEditor/Workspace/UIEditorWorkspaceQueries.h
Normal 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
|
||||
98
editor/include/XCEditor/Workspace/UIEditorWorkspaceSession.h
Normal file
98
editor/include/XCEditor/Workspace/UIEditorWorkspaceSession.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user