new_editor: stabilize resize lifecycle groundwork
This commit is contained in:
@@ -16,7 +16,7 @@ namespace {
|
||||
bool IsLiveWindow(const EditorWindow* window) {
|
||||
return window != nullptr &&
|
||||
window->GetHwnd() != nullptr &&
|
||||
!window->IsClosing();
|
||||
window->GetLifecycleState() == EditorWindowLifecycleState::Running;
|
||||
}
|
||||
|
||||
LONG ResolveOuterDimension(float value, LONG fallback) {
|
||||
@@ -54,10 +54,10 @@ void EditorUtilityWindowCoordinator::HandleWindowFrameTransferRequests(
|
||||
bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest(
|
||||
EditorWindow& sourceWindow,
|
||||
const EditorWindowOpenUtilityWindowRequest& request) {
|
||||
if (sourceWindow.IsClosing()) {
|
||||
if (!IsLiveWindow(&sourceWindow)) {
|
||||
LogRuntimeTrace(
|
||||
"utility",
|
||||
"open utility window request rejected: source window is closing");
|
||||
"open utility window request rejected: source window is not running");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest(
|
||||
}
|
||||
|
||||
if (EditorWindow* existingWindow = m_hostRuntime.FindWindow(descriptor->windowId);
|
||||
existingWindow != nullptr && existingWindow->GetHwnd() == nullptr &&
|
||||
existingWindow != nullptr && existingWindow->IsDestroyed() &&
|
||||
m_lifecycleCoordinator != nullptr) {
|
||||
m_lifecycleCoordinator->ReapDestroyedWindows();
|
||||
}
|
||||
@@ -87,7 +87,7 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest(
|
||||
existingWindow != nullptr) {
|
||||
LogRuntimeTrace(
|
||||
"utility",
|
||||
"open utility window request rejected: existing utility window is still closing");
|
||||
"open utility window request rejected: existing utility window is not reusable");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ std::string DescribeHostWindows(
|
||||
stream << window->GetWindowId()
|
||||
<< "{hwnd=" << DescribeHwnd(window->GetHwnd())
|
||||
<< ",primary=" << (window->IsPrimary() ? '1' : '0')
|
||||
<< ",closing=" << (window->IsClosing() ? '1' : '0')
|
||||
<< ",state=" << GetEditorWindowLifecycleStateName(window->GetLifecycleState())
|
||||
<< '}';
|
||||
}
|
||||
stream << ']';
|
||||
@@ -159,7 +159,10 @@ void EditorWindowHostRuntime::BindLifecycleCoordinator(
|
||||
}
|
||||
|
||||
void EditorWindowHostRuntime::HandlePendingNativeWindowCreated(HWND hwnd) {
|
||||
if (m_pendingCreateWindow != nullptr && !m_pendingCreateWindow->HasHwnd()) {
|
||||
if (m_pendingCreateWindow != nullptr &&
|
||||
m_pendingCreateWindow->GetLifecycleState() ==
|
||||
EditorWindowLifecycleState::PendingNativeCreate &&
|
||||
!m_pendingCreateWindow->HasHwnd()) {
|
||||
m_pendingCreateWindow->AttachHwnd(hwnd);
|
||||
}
|
||||
}
|
||||
@@ -183,7 +186,7 @@ void EditorWindowHostRuntime::RenderAllWindows(
|
||||
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
|
||||
if (window == nullptr ||
|
||||
window->GetHwnd() == nullptr ||
|
||||
window->IsClosing()) {
|
||||
window->GetLifecycleState() != EditorWindowLifecycleState::Running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -213,7 +216,7 @@ void EditorWindowHostRuntime::RenderAllWindows(
|
||||
for (WindowFrameTransferBatch& batch : transferBatches) {
|
||||
if (batch.sourceWindow == nullptr ||
|
||||
batch.sourceWindow->GetHwnd() == nullptr ||
|
||||
batch.sourceWindow->IsClosing()) {
|
||||
batch.sourceWindow->GetLifecycleState() != EditorWindowLifecycleState::Running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ std::string DescribeHostWindows(
|
||||
stream << window->GetWindowId()
|
||||
<< "{hwnd=" << DescribeHwnd(window->GetHwnd())
|
||||
<< ",primary=" << (window->IsPrimary() ? '1' : '0')
|
||||
<< ",closing=" << (window->IsClosing() ? '1' : '0')
|
||||
<< ",state=" << GetEditorWindowLifecycleStateName(window->GetLifecycleState())
|
||||
<< '}';
|
||||
}
|
||||
stream << ']';
|
||||
@@ -56,6 +56,10 @@ EditorWindowLifecycleCoordinator::EditorWindowLifecycleCoordinator(
|
||||
EditorWindowLifecycleCoordinator::~EditorWindowLifecycleCoordinator() = default;
|
||||
|
||||
void EditorWindowLifecycleCoordinator::PostCloseRequest(EditorWindow& window) {
|
||||
if (window.IsDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HWND hwnd = window.GetHwnd();
|
||||
if (!window.IsClosing()) {
|
||||
window.MarkClosing();
|
||||
@@ -77,13 +81,18 @@ void EditorWindowLifecycleCoordinator::PostCloseRequest(EditorWindow& window) {
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::ExecuteCloseRequest(EditorWindow& window) {
|
||||
if (window.IsDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HWND hwnd = window.GetHwnd();
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"ExecuteCloseRequest begin windowId='" + std::string(window.GetWindowId()) +
|
||||
"' hwnd=" + DescribeHwnd(hwnd) +
|
||||
" primary=" + (window.IsPrimary() ? "1" : "0") +
|
||||
" closingBefore=" + (window.IsClosing() ? "1" : "0") +
|
||||
" lifecycleBefore=" +
|
||||
std::string(GetEditorWindowLifecycleStateName(window.GetLifecycleState())) +
|
||||
" workspace=" + m_workspaceCoordinator.DescribeWindowSet() +
|
||||
" host=" + DescribeHostWindows(m_hostRuntime.GetWindows()));
|
||||
|
||||
@@ -106,6 +115,10 @@ void EditorWindowLifecycleCoordinator::ExecuteCloseRequest(EditorWindow& window)
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::HandleNativeWindowDestroyed(EditorWindow& window) {
|
||||
if (window.IsDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool destroyedPrimary =
|
||||
m_workspaceCoordinator.IsPrimaryWindowId(window.GetWindowId());
|
||||
LogRuntimeTrace(
|
||||
@@ -165,11 +178,12 @@ void EditorWindowLifecycleCoordinator::AbortUnregisteredWindow(EditorWindow& win
|
||||
}
|
||||
|
||||
ShutdownRuntimeIfNeeded(window);
|
||||
window.MarkDestroyed();
|
||||
if (hwnd != nullptr && IsWindow(hwnd)) {
|
||||
DestroyWindow(hwnd);
|
||||
} else {
|
||||
window.MarkDestroyed();
|
||||
}
|
||||
EraseWindow(window);
|
||||
ReapDestroyedWindows();
|
||||
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
@@ -199,7 +213,7 @@ void EditorWindowLifecycleCoordinator::ReapDestroyedWindows() {
|
||||
auto& windows = m_hostRuntime.GetWindows();
|
||||
for (auto it = windows.begin(); it != windows.end();) {
|
||||
EditorWindow* const window = it->get();
|
||||
if (window == nullptr || window->GetHwnd() != nullptr) {
|
||||
if (window == nullptr || !window->IsDestroyed()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
@@ -225,25 +239,6 @@ void EditorWindowLifecycleCoordinator::ShutdownRuntimeIfNeeded(EditorWindow& win
|
||||
}
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::EraseWindow(EditorWindow& window) {
|
||||
auto& windows = m_hostRuntime.GetWindows();
|
||||
const auto it = std::find_if(
|
||||
windows.begin(),
|
||||
windows.end(),
|
||||
[&window](const std::unique_ptr<EditorWindow>& candidate) {
|
||||
return candidate.get() == &window;
|
||||
});
|
||||
if (it == windows.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_hostRuntime.m_pendingCreateWindow == &window) {
|
||||
m_hostRuntime.m_pendingCreateWindow = nullptr;
|
||||
}
|
||||
|
||||
windows.erase(it);
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::LogRuntimeTrace(
|
||||
std::string_view channel,
|
||||
std::string_view message) const {
|
||||
|
||||
@@ -24,7 +24,6 @@ public:
|
||||
|
||||
private:
|
||||
void ShutdownRuntimeIfNeeded(EditorWindow& window);
|
||||
void EraseWindow(EditorWindow& window);
|
||||
void LogRuntimeTrace(std::string_view channel, std::string_view message) const;
|
||||
|
||||
EditorWindowHostRuntime& m_hostRuntime;
|
||||
|
||||
@@ -45,7 +45,7 @@ std::string DescribeHostWindows(const EditorWindowHostRuntime& hostRuntime) {
|
||||
stream << window->GetWindowId()
|
||||
<< "{hwnd=" << DescribeHwnd(window->GetHwnd())
|
||||
<< ",primary=" << (window->IsPrimary() ? '1' : '0')
|
||||
<< ",closing=" << (window->IsClosing() ? '1' : '0')
|
||||
<< ",state=" << GetEditorWindowLifecycleStateName(window->GetLifecycleState())
|
||||
<< '}';
|
||||
}
|
||||
stream << ']';
|
||||
@@ -74,14 +74,36 @@ void EditorWindowMessageDispatcher::DispatchWindowFrameTransferRequests(
|
||||
transferRequests);
|
||||
}
|
||||
|
||||
void EditorWindowMessageDispatcher::RenderAndHandleWindowFrame(const DispatchContext& context) {
|
||||
EditorWindowFrameTransferRequests transferRequests = EditorWindowFrameDriver::DriveImmediateFrame(
|
||||
context.window,
|
||||
context.hostRuntime.GetEditorContext(),
|
||||
context.workspaceCoordinator.IsGlobalTabDragActive());
|
||||
if (transferRequests.HasPendingRequests()) {
|
||||
DispatchWindowFrameTransferRequests(context, transferRequests);
|
||||
void EditorWindowMessageDispatcher::FinalizeImmediateFrame(
|
||||
const DispatchContext& context,
|
||||
const EditorWindowFrameTransferRequests& transferRequests) {
|
||||
context.workspaceCoordinator.RefreshWindowPresentation(context.window);
|
||||
context.utilityCoordinator.RefreshWindowPresentation(context.window);
|
||||
if (!transferRequests.HasPendingRequests()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchWindowFrameTransferRequests(context, transferRequests);
|
||||
}
|
||||
|
||||
void EditorWindowMessageDispatcher::FlushQueuedCompletedImmediateFrame(
|
||||
const DispatchContext& context) {
|
||||
if (!context.window.HasQueuedCompletedImmediateFrame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FinalizeImmediateFrame(
|
||||
context,
|
||||
context.window.ConsumeQueuedCompletedImmediateFrameTransferRequests());
|
||||
}
|
||||
|
||||
void EditorWindowMessageDispatcher::RenderAndHandleWindowFrame(const DispatchContext& context) {
|
||||
FinalizeImmediateFrame(
|
||||
context,
|
||||
EditorWindowFrameDriver::DriveImmediateFrame(
|
||||
context.window,
|
||||
context.hostRuntime.GetEditorContext(),
|
||||
context.workspaceCoordinator.IsGlobalTabDragActive()));
|
||||
}
|
||||
|
||||
bool EditorWindowMessageDispatcher::EnsureTrackingMouseLeave(const DispatchContext& context) {
|
||||
@@ -435,12 +457,11 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowLifecycleMessage(
|
||||
outResult = 0;
|
||||
return true;
|
||||
case WM_PAINT:
|
||||
if (EditorWindowFrameTransferRequests transferRequests = context.window.OnPaintMessage(
|
||||
FinalizeImmediateFrame(
|
||||
context,
|
||||
context.window.OnPaintMessage(
|
||||
context.hostRuntime.GetEditorContext(),
|
||||
context.workspaceCoordinator.IsGlobalTabDragActive());
|
||||
transferRequests.HasPendingRequests()) {
|
||||
DispatchWindowFrameTransferRequests(context, transferRequests);
|
||||
}
|
||||
context.workspaceCoordinator.IsGlobalTabDragActive()));
|
||||
outResult = 0;
|
||||
return true;
|
||||
case WM_ERASEBKGND:
|
||||
@@ -535,9 +556,20 @@ bool EditorWindowMessageDispatcher::TryDispatch(
|
||||
.window = window,
|
||||
};
|
||||
|
||||
return TryDispatchWindowChromeMessage(context, message, wParam, lParam, outResult) ||
|
||||
TryDispatchWindowLifecycleMessage(context, message, wParam, lParam, outResult) ||
|
||||
TryDispatchWindowInputMessage(context, message, wParam, lParam, outResult);
|
||||
bool handled = false;
|
||||
if (TryDispatchWindowChromeMessage(context, message, wParam, lParam, outResult)) {
|
||||
handled = true;
|
||||
} else if (TryDispatchWindowLifecycleMessage(context, message, wParam, lParam, outResult)) {
|
||||
handled = true;
|
||||
} else if (TryDispatchWindowInputMessage(context, message, wParam, lParam, outResult)) {
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
FlushQueuedCompletedImmediateFrame(context);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
|
||||
@@ -36,6 +36,10 @@ public:
|
||||
private:
|
||||
struct DispatchContext;
|
||||
|
||||
static void FinalizeImmediateFrame(
|
||||
const DispatchContext& context,
|
||||
const EditorWindowFrameTransferRequests& transferRequests);
|
||||
static void FlushQueuedCompletedImmediateFrame(const DispatchContext& context);
|
||||
static void RenderAndHandleWindowFrame(const DispatchContext& context);
|
||||
static void DispatchWindowFrameTransferRequests(
|
||||
const DispatchContext& context,
|
||||
|
||||
@@ -61,7 +61,7 @@ bool CanStartGlobalTabDragFromWindow(
|
||||
bool IsLiveInteractiveWindow(const EditorWindow* window) {
|
||||
return window != nullptr &&
|
||||
window->GetHwnd() != nullptr &&
|
||||
!window->IsClosing();
|
||||
window->GetLifecycleState() == EditorWindowLifecycleState::Running;
|
||||
}
|
||||
|
||||
std::string DescribeWindowSetState(const UIEditorWindowWorkspaceSet& windowSet) {
|
||||
@@ -92,7 +92,8 @@ UIEditorWindowWorkspaceSet BuildLiveWindowWorkspaceSet(
|
||||
if (window == nullptr ||
|
||||
window->GetHwnd() == nullptr ||
|
||||
window->GetWindowId() == excludedWindowId ||
|
||||
(!includeClosingWindows && window->IsClosing())) {
|
||||
(!includeClosingWindows &&
|
||||
window->GetLifecycleState() != EditorWindowLifecycleState::Running)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -262,9 +263,27 @@ bool EditorWindowWorkspaceCoordinator::SynchronizeWindowsFromWindowSet(
|
||||
for (const UIEditorWindowWorkspaceState& entry : windowSet.windows) {
|
||||
windowIdsInSet.push_back(entry.windowId);
|
||||
const bool isPrimaryWindow = entry.windowId == windowSet.primaryWindowId;
|
||||
if (EditorWindow* existingWindow = m_hostRuntime.FindWindow(entry.windowId);
|
||||
existingWindow != nullptr) {
|
||||
existingWindow->ClearClosing();
|
||||
EditorWindow* existingWindow = m_hostRuntime.FindWindow(entry.windowId);
|
||||
if (existingWindow != nullptr &&
|
||||
existingWindow->IsDestroyed() &&
|
||||
m_lifecycleCoordinator != nullptr) {
|
||||
m_lifecycleCoordinator->ReapDestroyedWindows();
|
||||
existingWindow = m_hostRuntime.FindWindow(entry.windowId);
|
||||
}
|
||||
|
||||
if (existingWindow != nullptr) {
|
||||
if (existingWindow->GetLifecycleState() != EditorWindowLifecycleState::Running) {
|
||||
LogRuntimeTrace(
|
||||
"window",
|
||||
"workspace synchronization rejected: window '" + entry.windowId +
|
||||
"' is in lifecycle state '" +
|
||||
std::string(
|
||||
GetEditorWindowLifecycleStateName(
|
||||
existingWindow->GetLifecycleState())) +
|
||||
"'");
|
||||
return false;
|
||||
}
|
||||
|
||||
existingWindowSnapshots.push_back(ExistingWindowSnapshot{
|
||||
existingWindow,
|
||||
existingWindow->GetWorkspaceController(),
|
||||
@@ -328,7 +347,8 @@ bool EditorWindowWorkspaceCoordinator::SynchronizeWindowsFromWindowSet(
|
||||
for (const std::unique_ptr<EditorWindow>& window : m_hostRuntime.GetWindows()) {
|
||||
if (window == nullptr ||
|
||||
window->GetHwnd() == nullptr ||
|
||||
window->IsPrimary()) {
|
||||
window->IsPrimary() ||
|
||||
window->GetLifecycleState() != EditorWindowLifecycleState::Running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -503,7 +523,7 @@ void EditorWindowWorkspaceCoordinator::EndGlobalTabDragSession() {
|
||||
if (EditorWindow* ownerWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.panelWindowId);
|
||||
ownerWindow != nullptr) {
|
||||
ownerWindow->ReleasePointerCapture(EditorWindowPointerCaptureOwner::GlobalTabDrag);
|
||||
if (!ownerWindow->IsClosing()) {
|
||||
if (IsLiveInteractiveWindow(ownerWindow)) {
|
||||
ownerWindow->ResetInteractionState();
|
||||
}
|
||||
}
|
||||
@@ -598,9 +618,7 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND h
|
||||
}
|
||||
|
||||
if (EditorWindow* updatedTargetWindow = m_hostRuntime.FindWindow(targetWindow->GetWindowId());
|
||||
updatedTargetWindow != nullptr &&
|
||||
updatedTargetWindow->GetHwnd() != nullptr &&
|
||||
!updatedTargetWindow->IsClosing()) {
|
||||
IsLiveInteractiveWindow(updatedTargetWindow)) {
|
||||
SetForegroundWindow(updatedTargetWindow->GetHwnd());
|
||||
}
|
||||
LogRuntimeTrace(
|
||||
@@ -613,7 +631,7 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND h
|
||||
bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag(
|
||||
EditorWindow& sourceWindow,
|
||||
const EditorWindowPanelTransferRequest& request) {
|
||||
if (sourceWindow.IsClosing()) {
|
||||
if (!IsLiveInteractiveWindow(&sourceWindow)) {
|
||||
LogRuntimeTrace("drag", "failed to start global tab drag: source window is closing");
|
||||
return false;
|
||||
}
|
||||
@@ -712,7 +730,7 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag(
|
||||
bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest(
|
||||
EditorWindow& sourceWindow,
|
||||
const EditorWindowPanelTransferRequest& request) {
|
||||
if (sourceWindow.IsClosing()) {
|
||||
if (!IsLiveInteractiveWindow(&sourceWindow)) {
|
||||
LogRuntimeTrace("detach", "detach request rejected: source window is closing");
|
||||
return false;
|
||||
}
|
||||
@@ -739,9 +757,7 @@ bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest(
|
||||
}
|
||||
|
||||
if (EditorWindow* detachedWindow = m_hostRuntime.FindWindow(result.targetWindowId);
|
||||
detachedWindow != nullptr &&
|
||||
detachedWindow->GetHwnd() != nullptr &&
|
||||
!detachedWindow->IsClosing()) {
|
||||
IsLiveInteractiveWindow(detachedWindow)) {
|
||||
SetForegroundWindow(detachedWindow->GetHwnd());
|
||||
}
|
||||
|
||||
@@ -774,8 +790,7 @@ EditorWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint(
|
||||
if (const HWND hitWindow = WindowFromPoint(screenPoint); hitWindow != nullptr) {
|
||||
const HWND rootWindow = GetAncestor(hitWindow, GA_ROOT);
|
||||
if (EditorWindow* window = m_hostRuntime.FindWindow(rootWindow);
|
||||
window != nullptr &&
|
||||
!window->IsClosing() &&
|
||||
IsLiveInteractiveWindow(window) &&
|
||||
window->GetWindowId() != excludedWindowId) {
|
||||
return window;
|
||||
}
|
||||
@@ -784,8 +799,7 @@ EditorWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint(
|
||||
for (auto it = m_hostRuntime.GetWindows().rbegin(); it != m_hostRuntime.GetWindows().rend(); ++it) {
|
||||
EditorWindow* const window = it->get();
|
||||
if (window == nullptr ||
|
||||
window->GetHwnd() == nullptr ||
|
||||
window->IsClosing() ||
|
||||
!IsLiveInteractiveWindow(window) ||
|
||||
window->GetWindowId() == excludedWindowId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user