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: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -2,14 +2,15 @@
#define NOMINMAX
#endif
#include <XCEditor/Core/UIEditorTabStripInteraction.h>
#include <XCEditor/Core/UIEditorPanelRegistry.h>
#include <XCEditor/Core/UIEditorWorkspaceController.h>
#include <XCEditor/Widgets/UIEditorTabStrip.h>
#include "Host/AutoScreenshot.h"
#include "Host/NativeRenderer.h"
#include <XCEngine/Input/InputTypes.h>
#include <XCEngine/UI/DrawData.h>
#include <XCEngine/UI/Widgets/UITabStripModel.h>
#include <windows.h>
#include <windowsx.h>
@@ -27,12 +28,15 @@
namespace {
using XCEngine::Input::KeyCode;
using XCEngine::UI::UIColor;
using XCEngine::UI::UIDrawData;
using XCEngine::UI::UIDrawList;
using XCEngine::UI::UIInputEvent;
using XCEngine::UI::UIInputEventType;
using XCEngine::UI::UIPoint;
using XCEngine::UI::UIPointerButton;
using XCEngine::UI::UIRect;
using XCEngine::UI::Widgets::UITabStripModel;
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceController;
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
@@ -43,6 +47,9 @@ using XCEngine::UI::Editor::Host::AutoScreenshotController;
using XCEngine::UI::Editor::Host::NativeRenderer;
using XCEngine::UI::Editor::UIEditorPanelDescriptor;
using XCEngine::UI::Editor::UIEditorPanelRegistry;
using XCEngine::UI::Editor::UIEditorTabStripInteractionFrame;
using XCEngine::UI::Editor::UIEditorTabStripInteractionResult;
using XCEngine::UI::Editor::UIEditorTabStripInteractionState;
using XCEngine::UI::Editor::UIEditorWorkspaceCommand;
using XCEngine::UI::Editor::UIEditorWorkspaceCommandKind;
using XCEngine::UI::Editor::UIEditorWorkspaceCommandResult;
@@ -50,9 +57,9 @@ using XCEngine::UI::Editor::UIEditorWorkspaceController;
using XCEngine::UI::Editor::UIEditorWorkspaceModel;
using XCEngine::UI::Editor::UIEditorWorkspaceNode;
using XCEngine::UI::Editor::UIEditorWorkspaceNodeKind;
using XCEngine::UI::Editor::UpdateUIEditorTabStripInteraction;
using XCEngine::UI::Editor::Widgets::AppendUIEditorTabStripBackground;
using XCEngine::UI::Editor::Widgets::AppendUIEditorTabStripForeground;
using XCEngine::UI::Editor::Widgets::BuildUIEditorTabStripLayout;
using XCEngine::UI::Editor::Widgets::HitTestUIEditorTabStrip;
using XCEngine::UI::Editor::Widgets::ResolveUIEditorTabStripSelectedIndex;
using XCEngine::UI::Editor::Widgets::UIEditorTabStripHitTarget;
@@ -93,6 +100,39 @@ bool ContainsPoint(const UIRect& rect, float x, float y) {
y <= rect.y + rect.height;
}
std::int32_t MapTabNavigationKey(UINT keyCode) {
switch (keyCode) {
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);
}
}
UIInputEvent MakePointerEvent(
UIInputEventType type,
const UIPoint& position,
UIPointerButton button = UIPointerButton::None) {
UIInputEvent event = {};
event.type = type;
event.position = position;
event.pointerButton = button;
return event;
}
UIInputEvent MakeKeyEvent(std::int32_t keyCode) {
UIInputEvent event = {};
event.type = UIInputEventType::KeyDown;
event.keyCode = keyCode;
return event;
}
void DrawCard(
UIDrawList& drawList,
const UIRect& rect,
@@ -240,9 +280,17 @@ private:
return 0;
}
break;
case WM_LBUTTONDOWN:
if (app != nullptr) {
app->HandleLeftButtonDown(
static_cast<float>(GET_X_LPARAM(lParam)),
static_cast<float>(GET_Y_LPARAM(lParam)));
return 0;
}
break;
case WM_LBUTTONUP:
if (app != nullptr) {
app->HandleClick(
app->HandleLeftButtonUp(
static_cast<float>(GET_X_LPARAM(lParam)),
static_cast<float>(GET_Y_LPARAM(lParam)));
return 0;
@@ -254,7 +302,10 @@ private:
if (wParam == VK_F12) {
app->m_autoScreenshot.RequestCapture("manual_f12");
} else {
app->HandleKeyDown(static_cast<UINT>(wParam));
const std::int32_t keyCode = MapTabNavigationKey(static_cast<UINT>(wParam));
if (keyCode != static_cast<std::int32_t>(KeyCode::None)) {
app->HandleKeyDown(keyCode);
}
}
return 0;
}
@@ -347,13 +398,30 @@ private:
void ResetScenario() {
m_controller =
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
m_tabStripState = {};
m_layout = {};
m_interactionState = {};
m_tabStripFrame = {};
m_tabItems.clear();
m_hoverTarget = {};
m_mousePosition = UIPoint(-1000.0f, -1000.0f);
m_lastResult = "等待操作";
m_navigationModel = {};
}
UIRect GetTabStripRect() const {
RECT clientRect = {};
GetClientRect(m_hwnd, &clientRect);
const float width = static_cast<float>((std::max)(1L, clientRect.right - clientRect.left));
const float height = static_cast<float>((std::max)(1L, clientRect.bottom - clientRect.top));
const float outerPadding = 20.0f;
const float leftColumnWidth = 360.0f;
const UIRect previewCardRect(
leftColumnWidth + outerPadding * 2.0f,
outerPadding,
width - leftColumnWidth - outerPadding * 3.0f,
height - outerPadding * 2.0f);
return UIRect(
previewCardRect.x + 20.0f,
previewCardRect.y + 20.0f,
previewCardRect.width - 40.0f,
previewCardRect.height - 40.0f);
}
void OnResize(UINT width, UINT height) {
@@ -373,114 +441,97 @@ private:
TrackMouseEvent(&event);
m_resetButtonHovered = ContainsPoint(m_resetButtonRect, x, y);
RefreshHoverTarget();
PumpTabStripEvents({ MakePointerEvent(UIInputEventType::PointerMove, m_mousePosition) });
InvalidateRect(m_hwnd, nullptr, FALSE);
}
void HandleMouseLeave() {
m_mousePosition = UIPoint(-1000.0f, -1000.0f);
m_resetButtonHovered = false;
m_hoverTarget = {};
m_tabStripState.hoveredIndex = UIEditorTabStripInvalidIndex;
m_tabStripState.closeHoveredIndex = UIEditorTabStripInvalidIndex;
PumpTabStripEvents({ MakePointerEvent(UIInputEventType::PointerLeave, m_mousePosition) });
InvalidateRect(m_hwnd, nullptr, FALSE);
}
void HandleClick(float x, float y) {
void HandleLeftButtonDown(float x, float y) {
m_mousePosition = UIPoint(x, y);
if (ContainsPoint(m_resetButtonRect, x, y)) {
m_resetButtonHovered = true;
InvalidateRect(m_hwnd, nullptr, FALSE);
return;
}
PumpTabStripEvents({ MakePointerEvent(UIInputEventType::PointerButtonDown, m_mousePosition, UIPointerButton::Left) });
InvalidateRect(m_hwnd, nullptr, FALSE);
}
void HandleLeftButtonUp(float x, float y) {
m_mousePosition = UIPoint(x, y);
if (ContainsPoint(m_resetButtonRect, x, y)) {
ResetScenario();
InvalidateRect(m_hwnd, nullptr, FALSE);
return;
}
const UIEditorTabStripHitTarget hit =
HitTestUIEditorTabStrip(m_layout, m_tabStripState, UIPoint(x, y));
switch (hit.kind) {
case UIEditorTabStripHitTargetKind::CloseButton:
if (hit.index < m_tabItems.size()) {
DispatchCommand(
UIEditorWorkspaceCommandKind::ClosePanel,
m_tabItems[hit.index].tabId,
"Close " + m_tabItems[hit.index].title);
m_tabStripState.focused = true;
}
break;
case UIEditorTabStripHitTargetKind::Tab:
if (hit.index < m_tabItems.size()) {
DispatchCommand(
UIEditorWorkspaceCommandKind::ActivatePanel,
m_tabItems[hit.index].tabId,
"Activate " + m_tabItems[hit.index].title);
m_tabStripState.focused = true;
}
break;
case UIEditorTabStripHitTargetKind::HeaderBackground:
case UIEditorTabStripHitTargetKind::Content:
m_tabStripState.focused = true;
m_lastResult = "TabStrip 获得 focus";
break;
case UIEditorTabStripHitTargetKind::None:
default:
m_tabStripState.focused = false;
m_lastResult = "Focus cleared";
break;
}
RefreshHoverTarget();
const UIEditorTabStripInteractionResult result =
PumpTabStripEvents({ MakePointerEvent(UIInputEventType::PointerButtonUp, m_mousePosition, UIPointerButton::Left) });
ApplyInteractionResult(result, "Mouse");
InvalidateRect(m_hwnd, nullptr, FALSE);
}
void HandleKeyDown(UINT keyCode) {
if (!m_tabStripState.focused) {
return;
}
void HandleKeyDown(std::int32_t keyCode) {
const UIEditorTabStripInteractionResult result =
PumpTabStripEvents({ MakeKeyEvent(keyCode) });
ApplyInteractionResult(result, "Keyboard");
InvalidateRect(m_hwnd, nullptr, FALSE);
}
UIEditorTabStripInteractionResult PumpTabStripEvents(std::vector<UIInputEvent> events) {
RefreshTabItems();
bool handled = true;
bool changed = false;
std::string action = {};
std::string selectedTabId = m_controller.GetWorkspace().activePanelId;
m_tabStripFrame = UpdateUIEditorTabStripInteraction(
m_interactionState,
selectedTabId,
GetTabStripRect(),
m_tabItems,
events);
m_tabStripState = m_interactionState.tabStripState;
m_layout = m_tabStripFrame.layout;
m_hoverTarget = HitTestUIEditorTabStrip(m_layout, m_tabStripState, m_mousePosition);
return m_tabStripFrame.result;
}
switch (keyCode) {
case VK_LEFT:
action = "Keyboard Left";
changed = m_navigationModel.SelectPrevious();
break;
case VK_RIGHT:
action = "Keyboard Right";
changed = m_navigationModel.SelectNext();
break;
case VK_HOME:
action = "Keyboard Home";
changed = m_navigationModel.SelectFirst();
break;
case VK_END:
action = "Keyboard End";
changed = m_navigationModel.SelectLast();
break;
default:
handled = false;
break;
}
if (!handled) {
void ApplyInteractionResult(
const UIEditorTabStripInteractionResult& result,
std::string_view source) {
if (result.closeRequested && !result.closedTabId.empty()) {
DispatchCommand(
UIEditorWorkspaceCommandKind::ClosePanel,
result.closedTabId,
std::string(source) + " Close -> " + result.closedTabId);
PumpTabStripEvents({});
return;
}
if (!changed || !m_navigationModel.HasSelection()) {
m_lastResult = action + " -> NoOp";
InvalidateRect(m_hwnd, nullptr, FALSE);
return;
}
const std::size_t selectedIndex = m_navigationModel.GetSelectedIndex();
if (selectedIndex < m_tabItems.size()) {
if ((result.selectionChanged || result.keyboardNavigated) &&
!result.selectedTabId.empty()) {
DispatchCommand(
UIEditorWorkspaceCommandKind::ActivatePanel,
m_tabItems[selectedIndex].tabId,
action + " -> " + m_tabItems[selectedIndex].title);
result.selectedTabId,
std::string(source) + " Activate -> " + result.selectedTabId);
PumpTabStripEvents({});
return;
}
InvalidateRect(m_hwnd, nullptr, FALSE);
if (result.hitTarget.kind == UIEditorTabStripHitTargetKind::HeaderBackground ||
result.hitTarget.kind == UIEditorTabStripHitTargetKind::Content) {
m_lastResult = "TabStrip 获得 focus";
return;
}
if (result.hitTarget.kind == UIEditorTabStripHitTargetKind::None &&
!m_interactionState.tabStripState.focused) {
m_lastResult = "Focus cleared";
}
}
void DispatchCommand(
@@ -527,31 +578,6 @@ private:
}
}
m_tabStripState.selectedIndex =
ResolveUIEditorTabStripSelectedIndex(m_tabItems, workspace.activePanelId);
m_navigationModel.SetItemCount(m_tabItems.size());
if (m_tabStripState.selectedIndex != UIEditorTabStripInvalidIndex) {
m_navigationModel.SetSelectedIndex(m_tabStripState.selectedIndex);
}
}
void RefreshHoverTarget() {
m_hoverTarget = HitTestUIEditorTabStrip(m_layout, m_tabStripState, m_mousePosition);
m_tabStripState.hoveredIndex = UIEditorTabStripInvalidIndex;
m_tabStripState.closeHoveredIndex = UIEditorTabStripInvalidIndex;
if (m_hoverTarget.kind == UIEditorTabStripHitTargetKind::CloseButton &&
m_hoverTarget.index < m_tabItems.size()) {
m_tabStripState.hoveredIndex = m_hoverTarget.index;
m_tabStripState.closeHoveredIndex = m_hoverTarget.index;
return;
}
if (m_hoverTarget.kind == UIEditorTabStripHitTargetKind::Tab &&
m_hoverTarget.index < m_tabItems.size()) {
m_tabStripState.hoveredIndex = m_hoverTarget.index;
}
}
void RenderFrame() {
@@ -569,15 +595,7 @@ private:
outerPadding,
width - leftColumnWidth - outerPadding * 3.0f,
height - outerPadding * 2.0f);
const UIRect tabStripRect(
previewCardRect.x + 20.0f,
previewCardRect.y + 20.0f,
previewCardRect.width - 40.0f,
previewCardRect.height - 40.0f);
RefreshTabItems();
m_layout = BuildUIEditorTabStripLayout(tabStripRect, m_tabItems, m_tabStripState);
RefreshHoverTarget();
PumpTabStripEvents({});
m_resetButtonRect = UIRect(
stateRect.x + 16.0f,
@@ -723,10 +741,11 @@ private:
std::filesystem::path m_captureRoot = {};
UIPoint m_mousePosition = UIPoint(-1000.0f, -1000.0f);
UIEditorWorkspaceController m_controller = {};
UIEditorTabStripInteractionState m_interactionState = {};
UIEditorTabStripInteractionFrame m_tabStripFrame = {};
UIEditorTabStripState m_tabStripState = {};
UIEditorTabStripLayout m_layout = {};
std::vector<UIEditorTabStripItem> m_tabItems = {};
UITabStripModel m_navigationModel = {};
UIEditorTabStripHitTarget m_hoverTarget = {};
UIRect m_resetButtonRect = {};
bool m_resetButtonHovered = false;