Wire LayoutLab to XCUI editor collection primitives
This commit is contained in:
@@ -64,6 +64,7 @@ Current gap:
|
||||
- `XCUI Demo` now consumes the shared `UITextInputController` path for text editing instead of carrying a private key-handling state machine.
|
||||
- `LayoutLab` now includes a `ScrollView` prototype and a more editor-like three-column authored layout.
|
||||
- `LayoutLab` now also covers editor-facing widget prototypes: `TreeView`, `TreeItem`, `ListView`, `ListItem`, `PropertySection`, and `FieldRow`.
|
||||
- `LayoutLab` now consumes the shared `UIEditorCollectionPrimitives` helper layer for collection-widget tag classification, clipping flags, and default metric resolution instead of keeping that taxonomy private to the sandbox runtime.
|
||||
- Panel diagnostics were expanded to clearly separate preview/runtime/input state and native vs legacy paths.
|
||||
- The editor bridge layer now has smoke coverage for swapchain after-UI rendering hooks and SRV-backed ImGui texture descriptor registration.
|
||||
- `Application` no longer owns the ImGui backend directly; window presentation now routes through `IWindowUICompositor` with an `ImGuiWindowUICompositor` implementation, which currently delegates to `IEditorHostCompositor` / `ImGuiHostCompositor`.
|
||||
@@ -143,6 +144,7 @@ Current gap:
|
||||
- `ImGuiTransitionBackend` moved behind `ImGuiXCUIHostedPreviewPresenter`
|
||||
- generic preview frame submission no longer carries an ImGui draw-list pointer
|
||||
- panel/runtime callers still preserve the same legacy and native-preview behavior
|
||||
- `LayoutLab` now resolves editor collection widget taxonomy and metrics through shared `UIEditorCollectionPrimitives` helpers instead of duplicating the same tag and metric rules inside the sandbox runtime.
|
||||
- `new_editor` panel/shell diagnostics improvements for hosted preview state.
|
||||
- XCUI asset document loading changed to prefer direct source compilation before `ResourceManager` fallback for the sandbox path, fixing the LayoutLab crash.
|
||||
- `UIDocumentCompiler.cpp` repaired enough to restore full local builds after the duplicated schema-helper regression.
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <XCEngine/Resources/UI/UIDocumentCompiler.h>
|
||||
#include <XCEngine/UI/Style/Theme.h>
|
||||
#include <XCEngine/UI/Types.h>
|
||||
#include <XCEngine/UI/Widgets/UIEditorCollectionPrimitives.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
@@ -42,6 +43,7 @@ using XCEngine::UI::UIDrawList;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
namespace Style = XCEngine::UI::Style;
|
||||
namespace UIWidgets = XCEngine::UI::Widgets;
|
||||
|
||||
constexpr std::size_t kInvalidIndex = static_cast<std::size_t>(-1);
|
||||
constexpr char kViewRelativePath[] = "new_editor/resources/xcui_layout_lab_view.xcui";
|
||||
@@ -284,34 +286,6 @@ bool IsStretch(const std::string& value) {
|
||||
return value.empty() || value == "stretch";
|
||||
}
|
||||
|
||||
bool IsScrollViewTag(const std::string& tagName) {
|
||||
return tagName == "ScrollView";
|
||||
}
|
||||
|
||||
bool IsTreeViewTag(const std::string& tagName) {
|
||||
return tagName == "TreeView";
|
||||
}
|
||||
|
||||
bool IsTreeItemTag(const std::string& tagName) {
|
||||
return tagName == "TreeItem";
|
||||
}
|
||||
|
||||
bool IsListViewTag(const std::string& tagName) {
|
||||
return tagName == "ListView";
|
||||
}
|
||||
|
||||
bool IsListItemTag(const std::string& tagName) {
|
||||
return tagName == "ListItem";
|
||||
}
|
||||
|
||||
bool IsPropertySectionTag(const std::string& tagName) {
|
||||
return tagName == "PropertySection";
|
||||
}
|
||||
|
||||
bool IsFieldRowTag(const std::string& tagName) {
|
||||
return tagName == "FieldRow";
|
||||
}
|
||||
|
||||
float ResolveScalar(
|
||||
const std::string& text,
|
||||
float referenceValue,
|
||||
@@ -484,10 +458,11 @@ float ResolvePadding(const LayoutNode& node, const Style::UITheme& theme) {
|
||||
ResolveFloatToken(theme, "space.outer", 18.0f));
|
||||
}
|
||||
|
||||
if (IsTreeViewTag(node.tagName) ||
|
||||
IsListViewTag(node.tagName) ||
|
||||
IsPropertySectionTag(node.tagName)) {
|
||||
return ResolveFloatToken(theme, "space.cardInset", 12.0f);
|
||||
const float primitivePadding = UIWidgets::ResolveUIEditorCollectionPrimitivePadding(
|
||||
UIWidgets::ClassifyUIEditorCollectionPrimitive(node.tagName),
|
||||
theme);
|
||||
if (primitivePadding > 0.0f) {
|
||||
return primitivePadding;
|
||||
}
|
||||
|
||||
return node.tagName == "View"
|
||||
@@ -509,7 +484,9 @@ float ResolveScrollOffset(const LayoutNode& node, const Style::UITheme& theme) {
|
||||
}
|
||||
|
||||
float ResolveListItemHeight(const Style::UITheme& theme) {
|
||||
return ResolveFloatToken(theme, "size.listItemHeight", 60.0f);
|
||||
return UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(
|
||||
UIWidgets::UIEditorCollectionPrimitiveKind::ListItem,
|
||||
theme);
|
||||
}
|
||||
|
||||
float ResolveTreeIndent(const LayoutNode& node, const Style::UITheme& theme) {
|
||||
@@ -518,12 +495,16 @@ float ResolveTreeIndent(const LayoutNode& node, const Style::UITheme& theme) {
|
||||
TryParseFloat(node.indentAttr, indentLevel);
|
||||
}
|
||||
|
||||
return (std::max)(0.0f, indentLevel) *
|
||||
ResolveFloatToken(theme, "size.treeIndent", 18.0f);
|
||||
return UIWidgets::ResolveUIEditorCollectionPrimitiveIndent(
|
||||
UIWidgets::UIEditorCollectionPrimitiveKind::TreeItem,
|
||||
theme,
|
||||
(std::max)(0.0f, indentLevel));
|
||||
}
|
||||
|
||||
float ResolveFieldRowHeight(const Style::UITheme& theme) {
|
||||
return ResolveFloatToken(theme, "size.fieldRowHeight", 32.0f);
|
||||
return UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(
|
||||
UIWidgets::UIEditorCollectionPrimitiveKind::FieldRow,
|
||||
theme);
|
||||
}
|
||||
|
||||
UIRect GetContentRect(const LayoutNode& node, const Style::UITheme& theme) {
|
||||
@@ -531,23 +512,9 @@ UIRect GetContentRect(const LayoutNode& node, const Style::UITheme& theme) {
|
||||
}
|
||||
|
||||
float ResolveDefaultHeight(const LayoutNode& node, const Style::UITheme& theme) {
|
||||
if (IsTreeItemTag(node.tagName)) {
|
||||
return ResolveFloatToken(theme, "size.treeItemHeight", 28.0f);
|
||||
}
|
||||
|
||||
if (IsListItemTag(node.tagName)) {
|
||||
return ResolveListItemHeight(theme);
|
||||
}
|
||||
|
||||
if (IsFieldRowTag(node.tagName)) {
|
||||
return ResolveFieldRowHeight(theme);
|
||||
}
|
||||
|
||||
if (IsPropertySectionTag(node.tagName)) {
|
||||
return ResolveFloatToken(theme, "size.propertySectionHeight", 148.0f);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
return UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(
|
||||
UIWidgets::ClassifyUIEditorCollectionPrimitive(node.tagName),
|
||||
theme);
|
||||
}
|
||||
|
||||
void LayoutNodeTree(RuntimeBuildContext& state, std::size_t nodeIndex);
|
||||
@@ -701,18 +668,18 @@ void LayoutScrollViewChildren(RuntimeBuildContext& state, std::size_t nodeIndex)
|
||||
void LayoutNodeTree(RuntimeBuildContext& state, std::size_t nodeIndex) {
|
||||
const LayoutNode& node = state.nodes[nodeIndex];
|
||||
state.rectsById[node.id] = node.rect;
|
||||
const UIWidgets::UIEditorCollectionPrimitiveKind primitiveKind =
|
||||
UIWidgets::ClassifyUIEditorCollectionPrimitive(node.tagName);
|
||||
|
||||
if (node.tagName == "View" ||
|
||||
node.tagName == "Column" ||
|
||||
IsTreeViewTag(node.tagName) ||
|
||||
IsListViewTag(node.tagName) ||
|
||||
IsPropertySectionTag(node.tagName)) {
|
||||
UIWidgets::UsesUIEditorCollectionPrimitiveColumnLayout(primitiveKind)) {
|
||||
LayoutColumnChildren(state, nodeIndex);
|
||||
} else if (node.tagName == "Row") {
|
||||
LayoutRowChildren(state, nodeIndex);
|
||||
} else if (node.tagName == "Overlay") {
|
||||
LayoutOverlayChildren(state, nodeIndex);
|
||||
} else if (IsScrollViewTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::ScrollView) {
|
||||
LayoutScrollViewChildren(state, nodeIndex);
|
||||
}
|
||||
}
|
||||
@@ -723,6 +690,8 @@ void DrawNode(
|
||||
UIDrawList& drawList,
|
||||
const std::string& hoveredId) {
|
||||
const LayoutNode& node = state.nodes[nodeIndex];
|
||||
const UIWidgets::UIEditorCollectionPrimitiveKind primitiveKind =
|
||||
UIWidgets::ClassifyUIEditorCollectionPrimitive(node.tagName);
|
||||
|
||||
if (node.tagName == "View") {
|
||||
const Color panelColor = ResolveColorToken(
|
||||
@@ -730,9 +699,9 @@ void DrawNode(
|
||||
"color.panel",
|
||||
Color(0.07f, 0.10f, 0.14f, 1.0f));
|
||||
drawList.AddFilledRect(node.rect, ToUIColor(panelColor), 0.0f);
|
||||
} else if (IsScrollViewTag(node.tagName) ||
|
||||
IsTreeViewTag(node.tagName) ||
|
||||
IsListViewTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::ScrollView ||
|
||||
primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::TreeView ||
|
||||
primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::ListView) {
|
||||
const Color surfaceColor = ResolveColorToken(
|
||||
state.theme,
|
||||
"color.scroll.surface",
|
||||
@@ -744,7 +713,7 @@ void DrawNode(
|
||||
const float rounding = ResolveFloatToken(state.theme, "radius.card", 10.0f);
|
||||
drawList.AddFilledRect(node.rect, ToUIColor(surfaceColor), rounding);
|
||||
drawList.AddRectOutline(node.rect, ToUIColor(borderColor), 1.0f, rounding);
|
||||
} else if (IsPropertySectionTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::PropertySection) {
|
||||
const Color sectionColor = ResolveColorToken(
|
||||
state.theme,
|
||||
"color.card",
|
||||
@@ -831,7 +800,8 @@ void DrawNode(
|
||||
2.0f,
|
||||
rounding);
|
||||
}
|
||||
} else if (IsTreeItemTag(node.tagName) || IsListItemTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::TreeItem ||
|
||||
primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::ListItem) {
|
||||
const bool hovered = node.id == hoveredId;
|
||||
const Color rowColor = hovered
|
||||
? ResolveColorToken(state.theme, "color.card.alt", Color(0.20f, 0.27f, 0.34f, 1.0f))
|
||||
@@ -852,11 +822,13 @@ void DrawNode(
|
||||
const float titleFont = ResolveFloatToken(state.theme, "font.body", 13.0f);
|
||||
const float bodyFont = ResolveFloatToken(state.theme, "font.body", 13.0f);
|
||||
const float rounding = ResolveFloatToken(state.theme, "radius.card", 10.0f);
|
||||
const float indent = IsTreeItemTag(node.tagName) ? ResolveTreeIndent(node, state.theme) : 0.0f;
|
||||
const float indent = primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::TreeItem
|
||||
? ResolveTreeIndent(node, state.theme)
|
||||
: 0.0f;
|
||||
|
||||
drawList.AddFilledRect(node.rect, ToUIColor(rowColor), rounding);
|
||||
drawList.AddRectOutline(node.rect, ToUIColor(borderColor), 1.0f, rounding);
|
||||
if (IsTreeItemTag(node.tagName)) {
|
||||
if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::TreeItem) {
|
||||
drawList.AddFilledRect(
|
||||
UIRect(node.rect.x + inset + indent - 8.0f, node.rect.y + 11.0f, 4.0f, 4.0f),
|
||||
ToUIColor(textColor),
|
||||
@@ -874,7 +846,7 @@ void DrawNode(
|
||||
ToUIColor(mutedColor),
|
||||
bodyFont);
|
||||
}
|
||||
} else if (IsFieldRowTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::FieldRow) {
|
||||
const bool hovered = node.id == hoveredId;
|
||||
const Color textColor = ResolveColorToken(
|
||||
state.theme,
|
||||
@@ -903,10 +875,7 @@ void DrawNode(
|
||||
fontSize);
|
||||
}
|
||||
|
||||
const bool clipsChildren =
|
||||
IsScrollViewTag(node.tagName) ||
|
||||
IsTreeViewTag(node.tagName) ||
|
||||
IsListViewTag(node.tagName);
|
||||
const bool clipsChildren = UIWidgets::DoesUIEditorCollectionPrimitiveClipChildren(primitiveKind);
|
||||
if (clipsChildren) {
|
||||
drawList.PushClipRect(GetContentRect(node, state.theme));
|
||||
}
|
||||
@@ -925,9 +894,8 @@ bool IsPointInsideNodeClipping(
|
||||
std::size_t currentIndex = nodeIndex;
|
||||
while (currentIndex != kInvalidIndex) {
|
||||
const LayoutNode& currentNode = state.nodes[currentIndex];
|
||||
if ((IsScrollViewTag(currentNode.tagName) ||
|
||||
IsTreeViewTag(currentNode.tagName) ||
|
||||
IsListViewTag(currentNode.tagName)) &&
|
||||
if (UIWidgets::DoesUIEditorCollectionPrimitiveClipChildren(
|
||||
UIWidgets::ClassifyUIEditorCollectionPrimitive(currentNode.tagName)) &&
|
||||
!ContainsPoint(GetContentRect(currentNode, state.theme), point)) {
|
||||
return false;
|
||||
}
|
||||
@@ -944,11 +912,9 @@ std::size_t HitTest(
|
||||
int bestDepth = -1;
|
||||
for (std::size_t index = 0; index < state.nodes.size(); ++index) {
|
||||
const LayoutNode& node = state.nodes[index];
|
||||
const bool hoverable =
|
||||
node.tagName == "Card" ||
|
||||
IsTreeItemTag(node.tagName) ||
|
||||
IsListItemTag(node.tagName) ||
|
||||
IsFieldRowTag(node.tagName);
|
||||
const bool hoverable = node.tagName == "Card" ||
|
||||
UIWidgets::IsUIEditorCollectionPrimitiveHoverable(
|
||||
UIWidgets::ClassifyUIEditorCollectionPrimitive(node.tagName));
|
||||
if (!hoverable ||
|
||||
!ContainsPoint(node.rect, point) ||
|
||||
!IsPointInsideNodeClipping(state, index, point)) {
|
||||
@@ -1080,25 +1046,27 @@ const XCUILayoutLabFrameResult& XCUILayoutLabRuntime::Update(const XCUILayoutLab
|
||||
state.frameResult.stats.commandCount = state.frameResult.drawData.GetTotalCommandCount();
|
||||
AnalyzeNativeOverlayCompatibility(state.frameResult.drawData, state.frameResult.stats);
|
||||
for (const LayoutNode& node : state.nodes) {
|
||||
const UIWidgets::UIEditorCollectionPrimitiveKind primitiveKind =
|
||||
UIWidgets::ClassifyUIEditorCollectionPrimitive(node.tagName);
|
||||
if (node.tagName == "Row") {
|
||||
++state.frameResult.stats.rowCount;
|
||||
} else if (node.tagName == "Column") {
|
||||
++state.frameResult.stats.columnCount;
|
||||
} else if (node.tagName == "Overlay") {
|
||||
++state.frameResult.stats.overlayCount;
|
||||
} else if (IsScrollViewTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::ScrollView) {
|
||||
++state.frameResult.stats.scrollViewCount;
|
||||
} else if (IsTreeViewTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::TreeView) {
|
||||
++state.frameResult.stats.treeViewCount;
|
||||
} else if (IsTreeItemTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::TreeItem) {
|
||||
++state.frameResult.stats.treeItemCount;
|
||||
} else if (IsListViewTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::ListView) {
|
||||
++state.frameResult.stats.listViewCount;
|
||||
} else if (IsListItemTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::ListItem) {
|
||||
++state.frameResult.stats.listItemCount;
|
||||
} else if (IsPropertySectionTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::PropertySection) {
|
||||
++state.frameResult.stats.propertySectionCount;
|
||||
} else if (IsFieldRowTag(node.tagName)) {
|
||||
} else if (primitiveKind == UIWidgets::UIEditorCollectionPrimitiveKind::FieldRow) {
|
||||
++state.frameResult.stats.fieldRowCount;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user