Sync editor rendering and UI workspace updates
This commit is contained in:
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 |
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user