Files
XCEngine/new_editor/app/Workspace/EditorWorkspace.cpp

412 lines
15 KiB
C++

#include "ProductEditorWorkspace.h"
#include "Workspace/ProductEditorWorkspaceEventRouter.h"
#include <XCEditor/Shell/UIEditorShellCompose.h>
#include <XCEditor/Foundation/UIEditorTheme.h>
#include <algorithm>
namespace XCEngine::UI::Editor::App {
namespace {
using ::XCEngine::UI::UIColor;
using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using Widgets::UIEditorDockHostHitTargetKind;
bool IsProductViewportPanel(std::string_view panelId) {
return panelId == "scene" || panelId == "game";
}
ProductViewportKind ResolveProductViewportKind(std::string_view panelId) {
return panelId == "game"
? ProductViewportKind::Game
: ProductViewportKind::Scene;
}
void ApplyViewportFrameToPresentation(
const ProductViewportFrame& viewportFrame,
UIEditorWorkspacePanelPresentationModel& presentation) {
presentation.viewportShellModel.frame.texture = viewportFrame.texture;
presentation.viewportShellModel.frame.requestedSize = viewportFrame.requestedSize;
presentation.viewportShellModel.frame.presentedSize = viewportFrame.renderSize;
presentation.viewportShellModel.frame.hasTexture = viewportFrame.hasTexture;
presentation.viewportShellModel.frame.statusText = viewportFrame.statusText;
}
void ApplyViewportFrameToShellModel(
const ProductViewportFrame& viewportFrame,
UIEditorViewportShellModel& shellModel) {
shellModel.frame.texture = viewportFrame.texture;
shellModel.frame.requestedSize = viewportFrame.requestedSize;
shellModel.frame.presentedSize = viewportFrame.renderSize;
shellModel.frame.hasTexture = viewportFrame.hasTexture;
shellModel.frame.statusText = viewportFrame.statusText;
}
UIEditorWorkspacePanelPresentationModel* FindMutableWorkspacePresentation(
std::vector<UIEditorWorkspacePanelPresentationModel>& presentations,
std::string_view panelId) {
for (UIEditorWorkspacePanelPresentationModel& presentation : presentations) {
if (presentation.panelId == panelId) {
return &presentation;
}
}
return nullptr;
}
void ApplyViewportFramesToShellFrame(
UIEditorShellInteractionFrame& shellFrame,
ProductViewportHostService& viewportHostService) {
auto applyToViewportFrames =
[&](std::vector<UIEditorWorkspaceViewportComposeFrame>& viewportFrames) {
for (UIEditorWorkspaceViewportComposeFrame& viewportComposeFrame : viewportFrames) {
if (!IsProductViewportPanel(viewportComposeFrame.panelId)) {
continue;
}
const ProductViewportFrame viewportFrame =
viewportHostService.RequestViewport(
ResolveProductViewportKind(viewportComposeFrame.panelId),
viewportComposeFrame.viewportShellFrame.requestedViewportSize);
ApplyViewportFrameToShellModel(
viewportFrame,
viewportComposeFrame.viewportShellModel);
if (UIEditorWorkspacePanelPresentationModel* presentation =
FindMutableWorkspacePresentation(
shellFrame.model.workspacePresentations,
viewportComposeFrame.panelId);
presentation != nullptr) {
ApplyViewportFrameToPresentation(viewportFrame, *presentation);
}
}
};
applyToViewportFrames(shellFrame.workspaceInteractionFrame.composeFrame.viewportFrames);
applyToViewportFrames(shellFrame.shellFrame.workspaceFrame.viewportFrames);
}
std::vector<UIEditorWorkspacePanelPresentationModel> BuildWorkspacePresentations(
const UIEditorShellInteractionDefinition& definition) {
return definition.workspacePresentations;
}
UIEditorShellComposeModel BuildShellComposeModelFromFrame(
const UIEditorShellInteractionFrame& frame) {
UIEditorShellComposeModel model = {};
model.menuBarItems = frame.request.menuBarItems;
model.toolbarButtons = frame.model.toolbarButtons;
model.statusSegments = frame.model.statusSegments;
model.workspacePresentations = frame.model.workspacePresentations;
return model;
}
void AppendShellPopups(
UIDrawList& drawList,
const UIEditorShellInteractionFrame& frame,
const UIEditorShellInteractionPalette& palette,
const UIEditorShellInteractionMetrics& metrics) {
const std::size_t popupCount =
(std::min)(frame.request.popupRequests.size(), frame.popupFrames.size());
for (std::size_t index = 0; index < popupCount; ++index) {
const UIEditorShellInteractionPopupRequest& popupRequest =
frame.request.popupRequests[index];
const UIEditorShellInteractionPopupFrame& popupFrame =
frame.popupFrames[index];
Widgets::AppendUIEditorMenuPopupBackground(
drawList,
popupRequest.layout,
popupRequest.widgetItems,
popupFrame.popupState,
palette.popupPalette,
metrics.popupMetrics);
Widgets::AppendUIEditorMenuPopupForeground(
drawList,
popupRequest.layout,
popupRequest.widgetItems,
popupFrame.popupState,
palette.popupPalette,
metrics.popupMetrics);
}
}
std::vector<UIInputEvent> FilterShellInputEventsForHostedContentCapture(
const std::vector<UIInputEvent>& inputEvents) {
std::vector<UIInputEvent> filteredEvents = {};
filteredEvents.reserve(inputEvents.size());
for (const UIInputEvent& event : inputEvents) {
switch (event.type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
case UIInputEventType::PointerWheel:
break;
default:
filteredEvents.push_back(event);
break;
}
}
return filteredEvents;
}
bool IsPointerInputEventType(UIInputEventType type) {
switch (type) {
case UIInputEventType::PointerMove:
case UIInputEventType::PointerEnter:
case UIInputEventType::PointerLeave:
case UIInputEventType::PointerButtonDown:
case UIInputEventType::PointerButtonUp:
case UIInputEventType::PointerWheel:
return true;
default:
return false;
}
}
bool ShouldHostedContentYieldPointerStream(
const UIEditorShellInteractionFrame& shellFrame,
bool shellInteractiveCaptureActive) {
if (shellInteractiveCaptureActive ||
shellFrame.result.requestPointerCapture ||
shellFrame.result.releasePointerCapture) {
return true;
}
return shellFrame.result.workspaceResult.dockHostResult.hitTarget.kind ==
UIEditorDockHostHitTargetKind::SplitterHandle;
}
std::vector<UIInputEvent> FilterHostedContentInputEventsForShellOwnership(
const std::vector<UIInputEvent>& inputEvents,
bool shellOwnsPointerStream) {
if (!shellOwnsPointerStream) {
return inputEvents;
}
std::vector<UIInputEvent> filteredEvents = {};
filteredEvents.reserve(inputEvents.size() + 1u);
bool strippedPointerInput = false;
UIInputEvent lastPointerEvent = {};
for (const UIInputEvent& event : inputEvents) {
if (IsPointerInputEventType(event.type)) {
strippedPointerInput = true;
lastPointerEvent = event;
continue;
}
filteredEvents.push_back(event);
}
if (strippedPointerInput) {
UIInputEvent leaveEvent = {};
leaveEvent.type = UIInputEventType::PointerLeave;
leaveEvent.position = lastPointerEvent.position;
leaveEvent.modifiers = lastPointerEvent.modifiers;
filteredEvents.push_back(leaveEvent);
}
return filteredEvents;
}
} // namespace
void ProductEditorWorkspace::Initialize(
const std::filesystem::path& repoRoot,
Host::NativeRenderer& renderer) {
m_builtInIcons.Initialize(renderer);
m_hierarchyPanel.SetBuiltInIcons(&m_builtInIcons);
m_projectPanel.SetBuiltInIcons(&m_builtInIcons);
m_projectPanel.SetTextMeasurer(&renderer);
m_hierarchyPanel.Initialize();
m_projectPanel.Initialize(repoRoot);
}
void ProductEditorWorkspace::AttachViewportWindowRenderer(Host::D3D12WindowRenderer& renderer) {
m_viewportHostService.AttachWindowRenderer(renderer);
}
void ProductEditorWorkspace::DetachViewportWindowRenderer() {
m_viewportHostService.DetachWindowRenderer();
}
void ProductEditorWorkspace::SetViewportSurfacePresentationEnabled(bool enabled) {
m_viewportHostService.SetSurfacePresentationEnabled(enabled);
}
void ProductEditorWorkspace::Shutdown() {
m_shellFrame = {};
m_shellInteractionState = {};
m_traceEntries.clear();
m_viewportHostService.Shutdown();
m_builtInIcons.Shutdown();
}
void ProductEditorWorkspace::ResetInteractionState() {
m_shellFrame = {};
m_shellInteractionState = {};
m_traceEntries.clear();
m_hierarchyPanel.ResetInteractionState();
m_projectPanel.ResetInteractionState();
}
void ProductEditorWorkspace::Update(
ProductEditorContext& context,
UIEditorWorkspaceController& workspaceController,
const ::XCEngine::UI::UIRect& bounds,
const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents,
std::string_view captureText,
ProductEditorShellVariant shellVariant) {
const auto& metrics = ResolveUIEditorShellInteractionMetrics();
context.SyncSessionFromWorkspace(workspaceController);
UIEditorShellInteractionDefinition definition =
context.BuildShellDefinition(workspaceController, captureText, shellVariant);
m_viewportHostService.BeginFrame();
definition.workspacePresentations = BuildWorkspacePresentations(definition);
const std::vector<UIInputEvent> shellEvents =
HasHostedContentCapture()
? FilterShellInputEventsForHostedContentCapture(inputEvents)
: inputEvents;
m_shellFrame = UpdateUIEditorShellInteraction(
m_shellInteractionState,
workspaceController,
bounds,
definition,
shellEvents,
context.GetShellServices(),
metrics);
const bool shellOwnsHostedContentPointerStream =
ShouldHostedContentYieldPointerStream(m_shellFrame, HasShellInteractiveCapture());
const std::vector<UIInputEvent> hostedContentEvents =
FilterHostedContentInputEventsForShellOwnership(
inputEvents,
shellOwnsHostedContentPointerStream);
ApplyViewportFramesToShellFrame(m_shellFrame, m_viewportHostService);
context.SyncSessionFromWorkspace(workspaceController);
context.UpdateStatusFromShellResult(workspaceController, m_shellFrame.result);
const std::string& activePanelId =
workspaceController.GetWorkspace().activePanelId;
m_hierarchyPanel.Update(
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame,
hostedContentEvents,
!m_shellFrame.result.workspaceInputSuppressed,
activePanelId == "hierarchy");
m_projectPanel.Update(
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame,
hostedContentEvents,
!m_shellFrame.result.workspaceInputSuppressed,
activePanelId == "project");
m_traceEntries = ConsumeProductEditorWorkspaceEvents(context, *this);
m_inspectorPanel.Update(
context.GetSession(),
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame);
m_consolePanel.Update(
context.GetSession(),
m_shellFrame.workspaceInteractionFrame.composeFrame.contentHostFrame);
}
void ProductEditorWorkspace::RenderRequestedViewports(
const ::XCEngine::Rendering::RenderContext& renderContext) {
m_viewportHostService.RenderRequestedViewports(renderContext);
}
void ProductEditorWorkspace::Append(UIDrawList& drawList) const {
const auto& metrics = ResolveUIEditorShellInteractionMetrics();
const auto& palette = ResolveUIEditorShellInteractionPalette();
const UIEditorShellComposeModel shellComposeModel =
BuildShellComposeModelFromFrame(m_shellFrame);
AppendUIEditorShellCompose(
drawList,
m_shellFrame.shellFrame,
shellComposeModel,
m_shellInteractionState.composeState,
palette.shellPalette,
metrics.shellMetrics);
m_consolePanel.Append(drawList);
m_hierarchyPanel.Append(drawList);
m_inspectorPanel.Append(drawList);
m_projectPanel.Append(drawList);
AppendShellPopups(drawList, m_shellFrame, palette, metrics);
}
const UIEditorShellInteractionFrame& ProductEditorWorkspace::GetShellFrame() const {
return m_shellFrame;
}
const UIEditorShellInteractionState& ProductEditorWorkspace::GetShellInteractionState() const {
return m_shellInteractionState;
}
const std::vector<ProductEditorWorkspaceTraceEntry>& ProductEditorWorkspace::GetTraceEntries() const {
return m_traceEntries;
}
const std::vector<ProductHierarchyPanel::Event>& ProductEditorWorkspace::GetHierarchyPanelEvents() const {
return m_hierarchyPanel.GetFrameEvents();
}
const std::vector<ProductProjectPanel::Event>& ProductEditorWorkspace::GetProjectPanelEvents() const {
return m_projectPanel.GetFrameEvents();
}
const std::string& ProductEditorWorkspace::GetBuiltInIconError() const {
return m_builtInIcons.GetLastError();
}
ProductProjectPanel::CursorKind ProductEditorWorkspace::GetHostedContentCursorKind() const {
return m_projectPanel.GetCursorKind();
}
Widgets::UIEditorDockHostCursorKind ProductEditorWorkspace::GetDockCursorKind() const {
return Widgets::ResolveUIEditorDockHostCursorKind(
m_shellFrame.workspaceInteractionFrame.dockHostFrame.layout);
}
bool ProductEditorWorkspace::WantsHostPointerCapture() const {
return m_hierarchyPanel.WantsHostPointerCapture() ||
m_projectPanel.WantsHostPointerCapture();
}
bool ProductEditorWorkspace::WantsHostPointerRelease() const {
return (m_hierarchyPanel.WantsHostPointerRelease() ||
m_projectPanel.WantsHostPointerRelease()) &&
!HasHostedContentCapture();
}
bool ProductEditorWorkspace::HasHostedContentCapture() const {
return m_hierarchyPanel.HasActivePointerCapture() ||
m_projectPanel.HasActivePointerCapture();
}
bool ProductEditorWorkspace::HasShellInteractiveCapture() const {
if (m_shellInteractionState.workspaceInteractionState.dockHostInteractionState.splitterDragState.active) {
return true;
}
if (!m_shellInteractionState.workspaceInteractionState.dockHostInteractionState.activeTabDragNodeId.empty()) {
return true;
}
for (const auto& panelState : m_shellInteractionState.workspaceInteractionState.composeState.panelStates) {
if (panelState.viewportShellState.inputBridgeState.captured) {
return true;
}
}
return false;
}
bool ProductEditorWorkspace::HasInteractiveCapture() const {
return HasShellInteractiveCapture() || HasHostedContentCapture();
}
} // namespace XCEngine::UI::Editor::App