Sync editor rendering and UI workspace updates

This commit is contained in:
2026-04-09 02:59:36 +08:00
parent 23b23a56be
commit d46bf87970
107 changed files with 10918 additions and 430 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -2,11 +2,12 @@
#define NOMINMAX
#endif
#include <XCEditor/Core/UIEditorTreeViewInteraction.h>
#include <XCEditor/Widgets/UIEditorTreeView.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEditor/Collections/UIEditorTreeViewInteraction.h>
#include "Host/AutoScreenshot.h"
#include "Host/NativeRenderer.h"
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <XCEngine/UI/Widgets/UISelectionModel.h>
@@ -28,6 +29,7 @@
namespace {
using XCEngine::Input::KeyCode;
using XCEngine::UI::UIColor;
using XCEngine::UI::UIDrawData;
using XCEngine::UI::UIDrawList;
@@ -101,6 +103,25 @@ bool ContainsPoint(const UIRect& rect, float x, float y) {
y <= rect.y + rect.height;
}
std::int32_t MapTreeNavigationKey(UINT keyCode) {
switch (keyCode) {
case VK_UP:
return static_cast<std::int32_t>(KeyCode::Up);
case VK_DOWN:
return static_cast<std::int32_t>(KeyCode::Down);
case VK_LEFT:
return static_cast<std::int32_t>(KeyCode::Left);
case VK_RIGHT:
return static_cast<std::int32_t>(KeyCode::Right);
case VK_HOME:
return static_cast<std::int32_t>(KeyCode::Home);
case VK_END:
return static_cast<std::int32_t>(KeyCode::End);
default:
return static_cast<std::int32_t>(KeyCode::None);
}
}
ScenarioLayout BuildScenarioLayout(float width, float height) {
constexpr float margin = 20.0f;
constexpr float leftWidth = 430.0f;
@@ -204,10 +225,11 @@ std::string JoinVisibleItems(
constexpr std::size_t kMaxVisibleLabels = 5u;
std::ostringstream stream = {};
const std::size_t labelCount = (std::min)(layout.visibleItemIndices.size(), kMaxVisibleLabels);
for (std::size_t index = 0; index < labelCount; ++index) {
for (std::size_t index = 0u; index < labelCount; ++index) {
if (index > 0u) {
stream << " | ";
}
const std::size_t itemIndex = layout.visibleItemIndices[index];
if (itemIndex < items.size()) {
stream << items[itemIndex].label;
@@ -225,7 +247,7 @@ std::string DescribeHitTarget(
const UIEditorTreeViewHitTarget& hitTarget,
const std::vector<UIEditorTreeViewItem>& items) {
if (hitTarget.itemIndex >= items.size()) {
return "";
return "(none)";
}
const std::string& label = items[hitTarget.itemIndex].label;
@@ -236,7 +258,7 @@ std::string DescribeHitTarget(
return "row: " + label;
case UIEditorTreeViewHitTargetKind::None:
default:
return "";
return "(none)";
}
}
@@ -251,6 +273,13 @@ UIInputEvent MakePointerEvent(
return event;
}
UIInputEvent MakeKeyEvent(std::int32_t keyCode) {
UIInputEvent event = {};
event.type = UIInputEventType::KeyDown;
event.keyCode = keyCode;
return event;
}
class ScenarioApp {
public:
int Run(HINSTANCE hInstance, int nCmdShow) {
@@ -328,11 +357,19 @@ private:
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (app != nullptr && wParam == VK_F12) {
app->m_autoScreenshot.RequestCapture("manual_f12");
app->m_lastResult = "已请求截图,输出到 captures/latest.png";
InvalidateRect(hwnd, nullptr, FALSE);
return 0;
if (app != nullptr) {
if (wParam == VK_F12) {
app->m_autoScreenshot.RequestCapture("manual_f12");
app->m_lastResult = "已请求截图,输出到 captures/latest.png";
InvalidateRect(hwnd, nullptr, FALSE);
return 0;
}
const std::int32_t keyCode = MapTreeNavigationKey(static_cast<UINT>(wParam));
if (keyCode != static_cast<std::int32_t>(KeyCode::None)) {
app->HandleNavigationKey(keyCode);
return 0;
}
}
break;
@@ -528,6 +565,14 @@ private:
InvalidateRect(m_hwnd, nullptr, FALSE);
}
void HandleNavigationKey(std::int32_t keyCode) {
const bool wasFocused = m_interactionState.treeViewState.focused;
const UIEditorTreeViewInteractionResult result =
PumpTreeEvents({ MakeKeyEvent(keyCode) });
UpdateResultText(result, wasFocused, true);
InvalidateRect(m_hwnd, nullptr, FALSE);
}
void UpdateHoveredAction(const ScenarioLayout& layout, float x, float y) {
const ButtonLayout* button = HitTestAction(layout, x, y);
if (button == nullptr) {
@@ -558,7 +603,7 @@ private:
m_expansionModel,
layout.treeRect,
m_items,
events);
std::move(events));
return m_treeFrame.result;
}
@@ -566,6 +611,21 @@ private:
const UIEditorTreeViewInteractionResult& result,
bool wasFocused,
bool insideTree) {
if (result.keyboardNavigated && result.expansionChanged && !result.toggledItemId.empty()) {
m_lastResult = "键盘切换展开: " + result.toggledItemId;
return;
}
if (result.keyboardNavigated && !result.selectedItemId.empty()) {
m_lastResult = "键盘选择: " + result.selectedItemId;
return;
}
if (result.expansionChanged && result.selectionChanged && !result.selectedItemId.empty()) {
m_lastResult = "折叠后回收 selection: " + result.selectedItemId;
return;
}
if (result.expansionChanged && !result.toggledItemId.empty()) {
m_lastResult = "切换展开: " + result.toggledItemId;
return;
@@ -582,7 +642,7 @@ private:
}
if (insideTree) {
m_lastResult = "点击树内空白: 更新 focus / hover";
m_lastResult = "点击树内空白: 更新 focus / hover";
return;
}
@@ -624,31 +684,31 @@ private:
DrawCard(
drawList,
layout.introRect,
"这个测试在验证什么功能",
"只验证 Editor TreeView 基础控件,不涉及任何业务面板。");
"这个测试在验证什么功能",
"只验证 Editor TreeView 的单选、层级展开/折叠和键盘导航契约,不涉及任何业务面板。");
drawList.AddText(
UIPoint(layout.introRect.x + 16.0f, layout.introRect.y + 72.0f),
"1. 验证行缩进是否正确Scene 的子项右移一层Directional Light 再右移一层",
"1. 点击 row只切换 selectionhover / selected / focused 必须能明确区分",
kTextPrimary,
12.0f);
drawList.AddText(
UIPoint(layout.introRect.x + 16.0f, layout.introRect.y + 94.0f),
"2. 点击 disclosure 只切换展开/折叠,不应误改 selection。",
"2. 点击 disclosure只切换展开/折叠,不应误改 selection。",
kTextPrimary,
12.0f);
drawList.AddText(
UIPoint(layout.introRect.x + 16.0f, layout.introRect.y + 116.0f),
"3. 点击行只切换 selectionhover、selected、focused 三种视觉状态要能区分",
"3. 按 Left / Right验证折叠、展开以及父子层级跳转",
kTextPrimary,
12.0f);
drawList.AddText(
UIPoint(layout.introRect.x + 16.0f, layout.introRect.y + 138.0f),
"4. 点击树外空白后 focus 应清除,但 selection 不应丢失",
"4. 按 Up / Down / Home / End验证可见行导航以及 current 与 selection 同步",
kTextPrimary,
12.0f);
drawList.AddText(
UIPoint(layout.introRect.x + 16.0f, layout.introRect.y + 160.0f),
"5. F12 手动截图;设置 XCUI_AUTO_CAPTURE_ON_STARTUP=1 自动截图。",
"5. 点击树外空白清除 focusF12 手动截图XCUI_AUTO_CAPTURE_ON_STARTUP=1 自动截图。",
kTextPrimary,
12.0f);
@@ -660,7 +720,7 @@ private:
m_hasHoveredAction && m_hoveredAction == button.action);
}
DrawCard(drawList, layout.stateRect, "状态摘要", "重点检查 hit / focus / selection / expanded / visible。");
DrawCard(drawList, layout.stateRect, "状态摘要", "重点检查 hit / focus / selection / current / expanded / visible。");
drawList.AddText(
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 70.0f),
"Hover: " + DescribeHitTarget(currentHit, m_items),
@@ -668,7 +728,7 @@ private:
12.0f);
drawList.AddText(
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 94.0f),
std::string("Focused: ") + (m_interactionState.treeViewState.focused ? "" : ""),
std::string("Focused: ") + (m_interactionState.treeViewState.focused ? "on" : "off"),
kTextPrimary,
12.0f);
drawList.AddText(
@@ -679,17 +739,25 @@ private:
12.0f);
drawList.AddText(
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 142.0f),
"Expanded: " + JoinExpandedItems(m_items, m_expansionModel),
std::string("Current: ") +
(m_interactionState.keyboardNavigation.HasCurrentIndex()
? std::to_string(m_interactionState.keyboardNavigation.GetCurrentIndex())
: std::string("(none)")),
kTextMuted,
12.0f);
drawList.AddText(
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 166.0f),
"Expanded: " + JoinExpandedItems(m_items, m_expansionModel),
kTextMuted,
12.0f);
drawList.AddText(
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 190.0f),
"Visible(" + std::to_string(m_treeFrame.layout.visibleItemIndices.size()) + "): " +
JoinVisibleItems(m_items, m_treeFrame.layout),
kTextMuted,
12.0f);
drawList.AddText(
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 190.0f),
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 214.0f),
"Result: " + m_lastResult,
kTextPrimary,
12.0f);
@@ -701,12 +769,12 @@ private:
? std::string("F12 -> tests/UI/Editor/integration/shell/tree_view_basic/captures/")
: m_autoScreenshot.GetLastCaptureSummary());
drawList.AddText(
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 216.0f),
UIPoint(layout.stateRect.x + 16.0f, layout.stateRect.y + 238.0f),
captureSummary,
kTextWeak,
12.0f);
DrawCard(drawList, layout.previewRect, "TreeView 预览", "这里只放一个 TreeView不混入 Hierarchy/Inspector 等业务内容。");
DrawCard(drawList, layout.previewRect, "TreeView 预览", "这里只放一个 TreeView不混入 Hierarchy / Inspector 等业务内容。");
AppendUIEditorTreeViewBackground(
drawList,
m_treeFrame.layout,