2026-04-25 16:11:01 +08:00
|
|
|
#include "Windowing/Content/EditorWindowFrameOrchestrator.h"
|
2026-04-19 15:52:28 +08:00
|
|
|
|
2026-04-21 00:57:14 +08:00
|
|
|
#include "Composition/EditorContext.h"
|
2026-04-22 20:32:56 +08:00
|
|
|
#include "Composition/EditorShellRuntime.h"
|
2026-04-19 15:52:28 +08:00
|
|
|
#include "Composition/EditorShellVariant.h"
|
2026-04-25 16:11:01 +08:00
|
|
|
#include "Support/EnvironmentFlags.h"
|
|
|
|
|
#include "Windowing/Content/EditorWindowContentStyle.h"
|
|
|
|
|
#include "Windowing/Utility/EditorUtilityWindowRequestSink.h"
|
|
|
|
|
#include "Windowing/Workspace/WindowWorkspaceTransferQueue.h"
|
2026-04-19 15:52:28 +08:00
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
#include "Windowing/Workspace/UIEditorDetachedWindowPolicy.h"
|
|
|
|
|
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
|
2026-04-22 20:32:56 +08:00
|
|
|
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
2026-04-19 15:52:28 +08:00
|
|
|
#include <XCEngine/UI/DrawData.h>
|
|
|
|
|
|
|
|
|
|
#include <sstream>
|
2026-04-25 16:11:01 +08:00
|
|
|
#include <utility>
|
2026-04-19 15:52:28 +08:00
|
|
|
|
2026-04-21 00:57:14 +08:00
|
|
|
namespace XCEngine::UI::Editor::App {
|
2026-04-19 15:52:28 +08:00
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
using namespace EditorWindowContentStyle;
|
2026-04-21 20:49:18 +08:00
|
|
|
using ::XCEngine::UI::UIDrawData;
|
2026-04-19 15:52:28 +08:00
|
|
|
using ::XCEngine::UI::UIDrawList;
|
|
|
|
|
using ::XCEngine::UI::UIInputEvent;
|
|
|
|
|
using ::XCEngine::UI::UIInputEventType;
|
|
|
|
|
using ::XCEngine::UI::UIPoint;
|
2026-04-25 16:11:01 +08:00
|
|
|
namespace WindowingDomain = ::XCEngine::UI::Editor::Windowing::Domain;
|
2026-04-19 15:52:28 +08:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
bool IsVerboseRuntimeTraceEnabled() {
|
2026-04-25 16:11:01 +08:00
|
|
|
static const bool s_enabled = IsEnvironmentFlagEnabled("XCUIEDITOR_VERBOSE_TRACE");
|
2026-04-19 15:52:28 +08:00
|
|
|
return s_enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string DescribeInputEventType(const UIInputEvent& event) {
|
|
|
|
|
switch (event.type) {
|
|
|
|
|
case UIInputEventType::PointerMove: return "PointerMove";
|
|
|
|
|
case UIInputEventType::PointerEnter: return "PointerEnter";
|
|
|
|
|
case UIInputEventType::PointerLeave: return "PointerLeave";
|
|
|
|
|
case UIInputEventType::PointerButtonDown: return "PointerDown";
|
|
|
|
|
case UIInputEventType::PointerButtonUp: return "PointerUp";
|
|
|
|
|
case UIInputEventType::PointerWheel: return "PointerWheel";
|
|
|
|
|
case UIInputEventType::KeyDown: return "KeyDown";
|
|
|
|
|
case UIInputEventType::KeyUp: return "KeyUp";
|
|
|
|
|
case UIInputEventType::Character: return "Character";
|
|
|
|
|
case UIInputEventType::FocusGained: return "FocusGained";
|
|
|
|
|
case UIInputEventType::FocusLost: return "FocusLost";
|
|
|
|
|
default: return "Unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
WindowingDomain::WindowCursorType ToWindowCursorType(ProjectPanel::CursorKind cursorType) {
|
|
|
|
|
switch (cursorType) {
|
|
|
|
|
case ProjectPanel::CursorKind::ResizeEW:
|
|
|
|
|
return WindowingDomain::WindowCursorType::ResizeEW;
|
|
|
|
|
case ProjectPanel::CursorKind::Arrow:
|
|
|
|
|
default:
|
|
|
|
|
return WindowingDomain::WindowCursorType::Arrow;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WindowingDomain::WindowCursorType ToWindowCursorType(
|
|
|
|
|
Widgets::UIEditorDockHostCursorKind cursorType) {
|
|
|
|
|
switch (cursorType) {
|
|
|
|
|
case Widgets::UIEditorDockHostCursorKind::ResizeEW:
|
|
|
|
|
return WindowingDomain::WindowCursorType::ResizeEW;
|
|
|
|
|
case Widgets::UIEditorDockHostCursorKind::ResizeNS:
|
|
|
|
|
return WindowingDomain::WindowCursorType::ResizeNS;
|
|
|
|
|
case Widgets::UIEditorDockHostCursorKind::Arrow:
|
|
|
|
|
default:
|
|
|
|
|
return WindowingDomain::WindowCursorType::Arrow;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WindowingDomain::WindowCaptureDemand BuildCaptureDemand(
|
|
|
|
|
const EditorShellRuntime& shellRuntime) {
|
|
|
|
|
return WindowingDomain::WindowCaptureDemand{
|
|
|
|
|
.shellInteractive = shellRuntime.HasShellInteractiveCapture(),
|
|
|
|
|
.hostedContent = shellRuntime.HasHostedContentCapture(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WindowingDomain::WindowCursorType ResolveContentCursorType(
|
|
|
|
|
const EditorShellRuntime& shellRuntime) {
|
|
|
|
|
const WindowingDomain::WindowCursorType hostedCursorType =
|
|
|
|
|
ToWindowCursorType(shellRuntime.GetHostedContentCursorType());
|
|
|
|
|
if (hostedCursorType != WindowingDomain::WindowCursorType::Arrow) {
|
|
|
|
|
return hostedCursorType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ToWindowCursorType(shellRuntime.GetDockCursorType());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QueueWorkspaceTransferRequest(
|
|
|
|
|
WindowWorkspaceTransferQueue& workspaceTransferQueue,
|
|
|
|
|
WindowWorkspaceTransferRequest::Type type,
|
|
|
|
|
std::string_view sourceWindowId,
|
|
|
|
|
std::string sourceNodeId,
|
|
|
|
|
std::string panelId,
|
|
|
|
|
std::int32_t cursorScreenX,
|
|
|
|
|
std::int32_t cursorScreenY) {
|
|
|
|
|
WindowWorkspaceTransferRequest request = {};
|
|
|
|
|
request.type = type;
|
|
|
|
|
request.sourceWindowId = std::string(sourceWindowId);
|
|
|
|
|
request.sourceNodeId = std::move(sourceNodeId);
|
|
|
|
|
request.panelId = std::move(panelId);
|
|
|
|
|
request.screenX = cursorScreenX;
|
|
|
|
|
request.screenY = cursorScreenY;
|
|
|
|
|
request.hasScreenPoint = true;
|
|
|
|
|
workspaceTransferQueue.QueueWorkspaceTransferRequest(request);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 15:52:28 +08:00
|
|
|
} // namespace
|
|
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
EditorWindowContentUpdateResult EditorWindowFrameOrchestrator::UpdateAndAppend(
|
2026-04-19 15:52:28 +08:00
|
|
|
EditorContext& editorContext,
|
2026-04-25 16:11:01 +08:00
|
|
|
std::string_view sourceWindowId,
|
|
|
|
|
EditorUtilityWindowResultState& utilityWindowResultState,
|
|
|
|
|
EditorUtilityWindowRequestSink& utilityRequestSink,
|
|
|
|
|
WindowWorkspaceTransferQueue& workspaceTransferQueue,
|
2026-04-22 20:32:56 +08:00
|
|
|
UIEditorWorkspaceController& workspaceController,
|
|
|
|
|
EditorShellRuntime& shellRuntime,
|
2026-04-19 15:52:28 +08:00
|
|
|
const ::XCEngine::UI::UIRect& workspaceBounds,
|
2026-04-22 20:32:56 +08:00
|
|
|
const std::vector<UIInputEvent>& frameEvents,
|
|
|
|
|
std::string_view captureStatusText,
|
|
|
|
|
bool primary,
|
|
|
|
|
bool globalTabDragActive,
|
|
|
|
|
bool useDetachedTitleBarTabStrip,
|
2026-04-25 16:11:01 +08:00
|
|
|
std::int32_t cursorScreenX,
|
|
|
|
|
std::int32_t cursorScreenY,
|
|
|
|
|
bool hasCursorScreenPoint,
|
2026-04-22 20:32:56 +08:00
|
|
|
UIDrawData& drawData) const {
|
|
|
|
|
LogInputTrace(
|
|
|
|
|
editorContext,
|
|
|
|
|
workspaceController,
|
|
|
|
|
shellRuntime.GetShellInteractionState(),
|
|
|
|
|
frameEvents);
|
2026-04-19 15:52:28 +08:00
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
utilityRequestSink.ConfigureFrameSource(
|
|
|
|
|
sourceWindowId,
|
|
|
|
|
cursorScreenX,
|
|
|
|
|
cursorScreenY,
|
|
|
|
|
hasCursorScreenPoint);
|
2026-04-19 15:52:28 +08:00
|
|
|
shellRuntime.Update(
|
|
|
|
|
editorContext,
|
2026-04-25 16:11:01 +08:00
|
|
|
utilityWindowResultState,
|
|
|
|
|
utilityRequestSink,
|
2026-04-19 15:52:28 +08:00
|
|
|
workspaceController,
|
|
|
|
|
workspaceBounds,
|
|
|
|
|
frameEvents,
|
|
|
|
|
captureStatusText,
|
|
|
|
|
primary ? EditorShellVariant::Primary : EditorShellVariant::DetachedWindow,
|
|
|
|
|
useDetachedTitleBarTabStrip,
|
2026-04-21 00:57:14 +08:00
|
|
|
useDetachedTitleBarTabStrip ? kBorderlessTitleBarHeightDips : 0.0f,
|
|
|
|
|
primary ? 0.0f : kBorderlessTitleBarHeightDips);
|
2026-04-19 15:52:28 +08:00
|
|
|
const UIEditorShellInteractionFrame& shellFrame = shellRuntime.GetShellFrame();
|
|
|
|
|
const UIEditorDockHostInteractionState& dockHostInteractionState =
|
|
|
|
|
shellRuntime.GetShellInteractionState().workspaceInteractionState.dockHostInteractionState;
|
|
|
|
|
|
|
|
|
|
LogFrameInteractionTrace(workspaceController, frameEvents, shellFrame);
|
2026-04-25 16:11:01 +08:00
|
|
|
EditorWindowContentUpdateResult updateResult = {};
|
|
|
|
|
updateResult.contentOutput.captureDemand = BuildCaptureDemand(shellRuntime);
|
|
|
|
|
updateResult.contentOutput.cursorType = ResolveContentCursorType(shellRuntime);
|
|
|
|
|
updateResult.contentOutput.titleBarMode =
|
|
|
|
|
HasUIEditorSingleVisibleRootTab(workspaceController)
|
|
|
|
|
? WindowingDomain::WindowTitleBarMode::DetachedTabStrip
|
|
|
|
|
: WindowingDomain::WindowTitleBarMode::SystemChrome;
|
|
|
|
|
AppendWorkspaceTransferRequests(
|
|
|
|
|
sourceWindowId,
|
|
|
|
|
globalTabDragActive,
|
|
|
|
|
dockHostInteractionState,
|
|
|
|
|
shellFrame,
|
|
|
|
|
cursorScreenX,
|
|
|
|
|
cursorScreenY,
|
|
|
|
|
hasCursorScreenPoint,
|
|
|
|
|
workspaceTransferQueue);
|
2026-04-19 15:52:28 +08:00
|
|
|
for (const WorkspaceTraceEntry& entry : shellRuntime.GetTraceEntries()) {
|
2026-04-25 16:11:01 +08:00
|
|
|
AppendUIEditorRuntimeTrace(entry.channel, entry.message);
|
2026-04-19 15:52:28 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-21 20:49:18 +08:00
|
|
|
shellRuntime.Append(drawData);
|
2026-04-25 16:11:01 +08:00
|
|
|
return updateResult;
|
2026-04-19 15:52:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EditorWindowFrameOrchestrator::AppendInvalidFrame(
|
|
|
|
|
EditorContext& editorContext,
|
|
|
|
|
UIDrawList& drawList) const {
|
|
|
|
|
drawList.AddText(
|
|
|
|
|
UIPoint(28.0f, 28.0f),
|
|
|
|
|
"Editor shell asset invalid.",
|
|
|
|
|
kShellTextColor,
|
|
|
|
|
16.0f);
|
|
|
|
|
drawList.AddText(
|
|
|
|
|
UIPoint(28.0f, 54.0f),
|
|
|
|
|
editorContext.GetValidationMessage().empty()
|
|
|
|
|
? std::string("Unknown validation error.")
|
|
|
|
|
: editorContext.GetValidationMessage(),
|
|
|
|
|
kShellMutedTextColor,
|
|
|
|
|
12.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string EditorWindowFrameOrchestrator::DescribeInputEvents(
|
|
|
|
|
const std::vector<UIInputEvent>& events) const {
|
|
|
|
|
std::ostringstream stream = {};
|
|
|
|
|
stream << "events=[";
|
|
|
|
|
for (std::size_t index = 0; index < events.size(); ++index) {
|
|
|
|
|
if (index > 0u) {
|
|
|
|
|
stream << " | ";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UIInputEvent& event = events[index];
|
|
|
|
|
stream << DescribeInputEventType(event)
|
|
|
|
|
<< '@'
|
|
|
|
|
<< static_cast<int>(event.position.x)
|
|
|
|
|
<< ','
|
|
|
|
|
<< static_cast<int>(event.position.y);
|
|
|
|
|
}
|
|
|
|
|
stream << ']';
|
|
|
|
|
return stream.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EditorWindowFrameOrchestrator::LogInputTrace(
|
|
|
|
|
EditorContext& editorContext,
|
2026-04-22 20:32:56 +08:00
|
|
|
const UIEditorWorkspaceController& workspaceController,
|
|
|
|
|
const UIEditorShellInteractionState& shellInteractionState,
|
2026-04-19 15:52:28 +08:00
|
|
|
const std::vector<UIInputEvent>& frameEvents) const {
|
|
|
|
|
if (frameEvents.empty() || !IsVerboseRuntimeTraceEnabled()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
AppendUIEditorRuntimeTrace(
|
2026-04-19 15:52:28 +08:00
|
|
|
"input",
|
|
|
|
|
DescribeInputEvents(frameEvents) + " | " +
|
|
|
|
|
editorContext.DescribeWorkspaceState(
|
2026-04-22 20:32:56 +08:00
|
|
|
workspaceController,
|
|
|
|
|
shellInteractionState));
|
2026-04-19 15:52:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EditorWindowFrameOrchestrator::LogFrameInteractionTrace(
|
|
|
|
|
const UIEditorWorkspaceController& workspaceController,
|
|
|
|
|
const std::vector<UIInputEvent>& frameEvents,
|
|
|
|
|
const UIEditorShellInteractionFrame& shellFrame) const {
|
|
|
|
|
if (!IsVerboseRuntimeTraceEnabled() ||
|
|
|
|
|
(frameEvents.empty() &&
|
|
|
|
|
!shellFrame.result.workspaceResult.dockHostResult.layoutChanged &&
|
|
|
|
|
!shellFrame.result.workspaceResult.dockHostResult.commandExecuted)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::ostringstream frameTrace = {};
|
|
|
|
|
frameTrace << "result consumed="
|
|
|
|
|
<< (shellFrame.result.consumed ? "true" : "false")
|
|
|
|
|
<< " layoutChanged="
|
|
|
|
|
<< (shellFrame.result.workspaceResult.dockHostResult.layoutChanged ? "true"
|
|
|
|
|
: "false")
|
|
|
|
|
<< " commandExecuted="
|
|
|
|
|
<< (shellFrame.result.workspaceResult.dockHostResult.commandExecuted ? "true"
|
|
|
|
|
: "false")
|
|
|
|
|
<< " active="
|
|
|
|
|
<< workspaceController.GetWorkspace().activePanelId
|
|
|
|
|
<< " message="
|
|
|
|
|
<< shellFrame.result.workspaceResult.dockHostResult.layoutResult.message;
|
2026-04-25 16:11:01 +08:00
|
|
|
AppendUIEditorRuntimeTrace("frame", frameTrace.str());
|
2026-04-19 15:52:28 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
void EditorWindowFrameOrchestrator::AppendWorkspaceTransferRequests(
|
|
|
|
|
std::string_view sourceWindowId,
|
2026-04-19 15:52:28 +08:00
|
|
|
bool globalTabDragActive,
|
|
|
|
|
const UIEditorDockHostInteractionState& dockHostInteractionState,
|
2026-04-25 16:11:01 +08:00
|
|
|
const UIEditorShellInteractionFrame& shellFrame,
|
|
|
|
|
std::int32_t cursorScreenX,
|
|
|
|
|
std::int32_t cursorScreenY,
|
|
|
|
|
bool hasCursorScreenPoint,
|
|
|
|
|
WindowWorkspaceTransferQueue& workspaceTransferQueue) const {
|
2026-04-19 15:52:28 +08:00
|
|
|
if (!globalTabDragActive &&
|
|
|
|
|
!dockHostInteractionState.activeTabDragNodeId.empty() &&
|
|
|
|
|
!dockHostInteractionState.activeTabDragPanelId.empty() &&
|
2026-04-25 16:11:01 +08:00
|
|
|
hasCursorScreenPoint) {
|
|
|
|
|
QueueWorkspaceTransferRequest(
|
|
|
|
|
workspaceTransferQueue,
|
|
|
|
|
WindowWorkspaceTransferRequest::Type::StartGlobalTabDrag,
|
|
|
|
|
sourceWindowId,
|
2026-04-19 15:52:28 +08:00
|
|
|
dockHostInteractionState.activeTabDragNodeId,
|
|
|
|
|
dockHostInteractionState.activeTabDragPanelId,
|
2026-04-25 16:11:01 +08:00
|
|
|
cursorScreenX,
|
|
|
|
|
cursorScreenY);
|
2026-04-19 15:52:28 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
if (shellFrame.result.workspaceResult.dockHostResult.detachRequested &&
|
|
|
|
|
hasCursorScreenPoint) {
|
|
|
|
|
QueueWorkspaceTransferRequest(
|
|
|
|
|
workspaceTransferQueue,
|
|
|
|
|
WindowWorkspaceTransferRequest::Type::DetachPanel,
|
|
|
|
|
sourceWindowId,
|
2026-04-19 15:52:28 +08:00
|
|
|
shellFrame.result.workspaceResult.dockHostResult.detachedNodeId,
|
|
|
|
|
shellFrame.result.workspaceResult.dockHostResult.detachedPanelId,
|
2026-04-25 16:11:01 +08:00
|
|
|
cursorScreenX,
|
|
|
|
|
cursorScreenY);
|
2026-04-19 15:52:28 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 00:57:14 +08:00
|
|
|
} // namespace XCEngine::UI::Editor::App
|
|
|
|
|
|
2026-04-25 16:11:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|