Add editor panel host lifecycle contract

This commit is contained in:
2026-04-07 13:24:56 +08:00
parent 6bf61ad8e2
commit b2ab516228
10 changed files with 1224 additions and 0 deletions

View File

@@ -18,6 +18,7 @@ add_library(XCUIEditorLib STATIC
src/Core/UIEditorDockHostInteraction.cpp
src/Core/UIEditorMenuModel.cpp
src/Core/UIEditorMenuSession.cpp
src/Core/UIEditorPanelHostLifecycle.cpp
src/Core/UIEditorPanelRegistry.cpp
src/Core/UIEditorShellCompose.cpp
src/Core/UIEditorShellInteraction.cpp

View File

@@ -0,0 +1,68 @@
#pragma once
#include <XCEditor/Core/UIEditorPanelRegistry.h>
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor {
enum class UIEditorPanelHostLifecycleEventKind : std::uint8_t {
Attached = 0,
Detached,
Shown,
Hidden,
Activated,
Deactivated,
FocusGained,
FocusLost
};
std::string_view GetUIEditorPanelHostLifecycleEventKindName(
UIEditorPanelHostLifecycleEventKind kind);
struct UIEditorPanelHostState {
std::string panelId = {};
bool attached = false;
bool visible = false;
bool active = false;
bool focused = false;
};
struct UIEditorPanelHostLifecycleEvent {
UIEditorPanelHostLifecycleEventKind kind = UIEditorPanelHostLifecycleEventKind::Attached;
std::string panelId = {};
};
struct UIEditorPanelHostLifecycleState {
std::vector<UIEditorPanelHostState> panelStates = {};
};
struct UIEditorPanelHostLifecycleRequest {
std::string focusedPanelId = {};
};
struct UIEditorPanelHostLifecycleFrame {
std::vector<UIEditorPanelHostState> panelStates = {};
std::vector<UIEditorPanelHostLifecycleEvent> events = {};
};
const UIEditorPanelHostState* FindUIEditorPanelHostState(
const UIEditorPanelHostLifecycleState& state,
std::string_view panelId);
const UIEditorPanelHostState* FindUIEditorPanelHostState(
const UIEditorPanelHostLifecycleFrame& frame,
std::string_view panelId);
UIEditorPanelHostLifecycleFrame UpdateUIEditorPanelHostLifecycle(
UIEditorPanelHostLifecycleState& state,
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const UIEditorPanelHostLifecycleRequest& request = {});
} // namespace XCEngine::UI::Editor

View File

@@ -0,0 +1,207 @@
#include <XCEditor/Core/UIEditorPanelHostLifecycle.h>
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace XCEngine::UI::Editor {
namespace {
using HostStateMap = std::unordered_map<std::string_view, const UIEditorPanelHostState*>;
HostStateMap BuildHostStateMap(const std::vector<UIEditorPanelHostState>& states) {
HostStateMap map = {};
map.reserve(states.size());
for (const UIEditorPanelHostState& state : states) {
map.emplace(state.panelId, &state);
}
return map;
}
const UIEditorPanelHostState* FindHostState(
const std::vector<UIEditorPanelHostState>& states,
std::string_view panelId) {
for (const UIEditorPanelHostState& state : states) {
if (state.panelId == panelId) {
return &state;
}
}
return nullptr;
}
void AppendLeaveEvents(
std::vector<UIEditorPanelHostLifecycleEvent>& events,
const std::vector<UIEditorPanelHostState>& previousStates,
const HostStateMap& currentStateMap) {
auto appendCategory = [&](auto predicate, UIEditorPanelHostLifecycleEventKind kind) {
for (const UIEditorPanelHostState& previous : previousStates) {
const UIEditorPanelHostState emptyState = {};
const UIEditorPanelHostState& current =
currentStateMap.contains(previous.panelId)
? *currentStateMap.at(previous.panelId)
: emptyState;
if (predicate(previous, current)) {
events.push_back({ kind, previous.panelId });
}
}
};
appendCategory(
[](const UIEditorPanelHostState& previous, const UIEditorPanelHostState& current) {
return previous.focused && !current.focused;
},
UIEditorPanelHostLifecycleEventKind::FocusLost);
appendCategory(
[](const UIEditorPanelHostState& previous, const UIEditorPanelHostState& current) {
return previous.active && !current.active;
},
UIEditorPanelHostLifecycleEventKind::Deactivated);
appendCategory(
[](const UIEditorPanelHostState& previous, const UIEditorPanelHostState& current) {
return previous.visible && !current.visible;
},
UIEditorPanelHostLifecycleEventKind::Hidden);
appendCategory(
[](const UIEditorPanelHostState& previous, const UIEditorPanelHostState& current) {
return previous.attached && !current.attached;
},
UIEditorPanelHostLifecycleEventKind::Detached);
}
void AppendEnterEvents(
std::vector<UIEditorPanelHostLifecycleEvent>& events,
const std::vector<UIEditorPanelHostState>& currentStates,
const HostStateMap& previousStateMap) {
auto appendCategory = [&](auto predicate, UIEditorPanelHostLifecycleEventKind kind) {
for (const UIEditorPanelHostState& current : currentStates) {
const UIEditorPanelHostState emptyState = {};
const UIEditorPanelHostState& previous =
previousStateMap.contains(current.panelId)
? *previousStateMap.at(current.panelId)
: emptyState;
if (predicate(previous, current)) {
events.push_back({ kind, current.panelId });
}
}
};
appendCategory(
[](const UIEditorPanelHostState& previous, const UIEditorPanelHostState& current) {
return !previous.attached && current.attached;
},
UIEditorPanelHostLifecycleEventKind::Attached);
appendCategory(
[](const UIEditorPanelHostState& previous, const UIEditorPanelHostState& current) {
return !previous.visible && current.visible;
},
UIEditorPanelHostLifecycleEventKind::Shown);
appendCategory(
[](const UIEditorPanelHostState& previous, const UIEditorPanelHostState& current) {
return !previous.active && current.active;
},
UIEditorPanelHostLifecycleEventKind::Activated);
appendCategory(
[](const UIEditorPanelHostState& previous, const UIEditorPanelHostState& current) {
return !previous.focused && current.focused;
},
UIEditorPanelHostLifecycleEventKind::FocusGained);
}
std::vector<UIEditorPanelHostState> BuildResolvedPanelHostStates(
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const UIEditorPanelHostLifecycleRequest& request) {
const std::vector<UIEditorWorkspaceVisiblePanel> visiblePanels =
CollectUIEditorWorkspaceVisiblePanels(workspace, session);
std::unordered_set<std::string_view> visiblePanelIds = {};
visiblePanelIds.reserve(visiblePanels.size());
for (const UIEditorWorkspaceVisiblePanel& panel : visiblePanels) {
visiblePanelIds.insert(panel.panelId);
}
std::vector<UIEditorPanelHostState> states = {};
states.reserve(panelRegistry.panels.size());
for (const UIEditorPanelDescriptor& descriptor : panelRegistry.panels) {
if (!ContainsUIEditorWorkspacePanel(workspace, descriptor.panelId)) {
continue;
}
const UIEditorPanelSessionState* sessionState =
FindUIEditorPanelSessionState(session, descriptor.panelId);
if (sessionState == nullptr) {
continue;
}
UIEditorPanelHostState state = {};
state.panelId = descriptor.panelId;
state.attached = sessionState->open;
state.visible = state.attached && visiblePanelIds.contains(state.panelId);
state.active = state.visible && workspace.activePanelId == state.panelId;
state.focused = state.active && request.focusedPanelId == state.panelId;
states.push_back(std::move(state));
}
return states;
}
} // namespace
std::string_view GetUIEditorPanelHostLifecycleEventKindName(
UIEditorPanelHostLifecycleEventKind kind) {
switch (kind) {
case UIEditorPanelHostLifecycleEventKind::Attached:
return "Attached";
case UIEditorPanelHostLifecycleEventKind::Detached:
return "Detached";
case UIEditorPanelHostLifecycleEventKind::Shown:
return "Shown";
case UIEditorPanelHostLifecycleEventKind::Hidden:
return "Hidden";
case UIEditorPanelHostLifecycleEventKind::Activated:
return "Activated";
case UIEditorPanelHostLifecycleEventKind::Deactivated:
return "Deactivated";
case UIEditorPanelHostLifecycleEventKind::FocusGained:
return "FocusGained";
case UIEditorPanelHostLifecycleEventKind::FocusLost:
return "FocusLost";
}
return "Unknown";
}
const UIEditorPanelHostState* FindUIEditorPanelHostState(
const UIEditorPanelHostLifecycleState& state,
std::string_view panelId) {
return FindHostState(state.panelStates, panelId);
}
const UIEditorPanelHostState* FindUIEditorPanelHostState(
const UIEditorPanelHostLifecycleFrame& frame,
std::string_view panelId) {
return FindHostState(frame.panelStates, panelId);
}
UIEditorPanelHostLifecycleFrame UpdateUIEditorPanelHostLifecycle(
UIEditorPanelHostLifecycleState& state,
const UIEditorPanelRegistry& panelRegistry,
const UIEditorWorkspaceModel& workspace,
const UIEditorWorkspaceSession& session,
const UIEditorPanelHostLifecycleRequest& request) {
UIEditorPanelHostLifecycleFrame frame = {};
frame.panelStates =
BuildResolvedPanelHostStates(panelRegistry, workspace, session, request);
const HostStateMap previousStateMap = BuildHostStateMap(state.panelStates);
const HostStateMap currentStateMap = BuildHostStateMap(frame.panelStates);
AppendLeaveEvents(frame.events, state.panelStates, currentStateMap);
AppendEnterEvents(frame.events, frame.panelStates, previousStateMap);
state.panelStates = frame.panelStates;
return frame;
}
} // namespace XCEngine::UI::Editor