2026-04-15 11:13:16 +08:00
|
|
|
#include "Workspace/UIEditorWorkspaceControllerInternal.h"
|
2026-04-06 16:20:46 +08:00
|
|
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
2026-04-15 11:13:16 +08:00
|
|
|
namespace XCEngine::UI::Editor::Detail {
|
2026-04-06 16:20:46 +08:00
|
|
|
|
2026-04-11 17:07:37 +08:00
|
|
|
bool IsPanelOpenAndVisible(
|
|
|
|
|
const UIEditorWorkspaceSession& session,
|
|
|
|
|
std::string_view panelId) {
|
|
|
|
|
const UIEditorPanelSessionState* panelState =
|
|
|
|
|
FindUIEditorPanelSessionState(session, panelId);
|
|
|
|
|
return panelState != nullptr && panelState->open && panelState->visible;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 16:20:46 +08:00
|
|
|
std::vector<std::string> CollectVisiblePanelIds(
|
|
|
|
|
const UIEditorWorkspaceModel& workspace,
|
|
|
|
|
const UIEditorWorkspaceSession& session) {
|
|
|
|
|
const std::vector<UIEditorWorkspaceVisiblePanel> panels =
|
|
|
|
|
CollectUIEditorWorkspaceVisiblePanels(workspace, session);
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> ids = {};
|
|
|
|
|
ids.reserve(panels.size());
|
|
|
|
|
for (const UIEditorWorkspaceVisiblePanel& panel : panels) {
|
|
|
|
|
ids.push_back(panel.panelId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ids;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 17:07:37 +08:00
|
|
|
VisibleTabStackInfo ResolveVisibleTabStackInfo(
|
|
|
|
|
const UIEditorWorkspaceNode& node,
|
|
|
|
|
const UIEditorWorkspaceSession& session,
|
|
|
|
|
std::string_view panelId) {
|
|
|
|
|
VisibleTabStackInfo info = {};
|
|
|
|
|
for (const UIEditorWorkspaceNode& child : node.children) {
|
|
|
|
|
if (child.kind != UIEditorWorkspaceNodeKind::Panel) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool visible = IsPanelOpenAndVisible(session, child.panel.panelId);
|
|
|
|
|
if (child.panel.panelId == panelId) {
|
|
|
|
|
info.panelExists = true;
|
|
|
|
|
info.panelVisible = visible;
|
|
|
|
|
if (visible) {
|
|
|
|
|
info.currentVisibleIndex = info.visibleTabCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (visible) {
|
|
|
|
|
++info.visibleTabCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::size_t CountVisibleTabs(
|
|
|
|
|
const UIEditorWorkspaceNode& node,
|
|
|
|
|
const UIEditorWorkspaceSession& session) {
|
|
|
|
|
if (node.kind != UIEditorWorkspaceNodeKind::TabStack) {
|
|
|
|
|
return 0u;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::size_t visibleCount = 0u;
|
|
|
|
|
for (const UIEditorWorkspaceNode& child : node.children) {
|
|
|
|
|
if (child.kind == UIEditorWorkspaceNodeKind::Panel &&
|
|
|
|
|
IsPanelOpenAndVisible(session, child.panel.panelId)) {
|
|
|
|
|
++visibleCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return visibleCount;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 11:13:16 +08:00
|
|
|
} // namespace XCEngine::UI::Editor::Detail
|
|
|
|
|
|
|
|
|
|
namespace XCEngine::UI::Editor {
|
2026-04-06 16:20:46 +08:00
|
|
|
|
|
|
|
|
std::string_view GetUIEditorWorkspaceCommandKindName(UIEditorWorkspaceCommandKind kind) {
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case UIEditorWorkspaceCommandKind::OpenPanel:
|
|
|
|
|
return "OpenPanel";
|
|
|
|
|
case UIEditorWorkspaceCommandKind::ClosePanel:
|
|
|
|
|
return "ClosePanel";
|
|
|
|
|
case UIEditorWorkspaceCommandKind::ShowPanel:
|
|
|
|
|
return "ShowPanel";
|
|
|
|
|
case UIEditorWorkspaceCommandKind::HidePanel:
|
|
|
|
|
return "HidePanel";
|
|
|
|
|
case UIEditorWorkspaceCommandKind::ActivatePanel:
|
|
|
|
|
return "ActivatePanel";
|
|
|
|
|
case UIEditorWorkspaceCommandKind::ResetWorkspace:
|
|
|
|
|
return "ResetWorkspace";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "Unknown";
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 11:13:16 +08:00
|
|
|
std::string_view GetUIEditorWorkspaceCommandStatusName(
|
|
|
|
|
UIEditorWorkspaceCommandStatus status) {
|
2026-04-06 16:20:46 +08:00
|
|
|
switch (status) {
|
|
|
|
|
case UIEditorWorkspaceCommandStatus::Changed:
|
|
|
|
|
return "Changed";
|
|
|
|
|
case UIEditorWorkspaceCommandStatus::NoOp:
|
|
|
|
|
return "NoOp";
|
|
|
|
|
case UIEditorWorkspaceCommandStatus::Rejected:
|
|
|
|
|
return "Rejected";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "Unknown";
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 16:59:15 +08:00
|
|
|
std::string_view GetUIEditorWorkspaceLayoutOperationStatusName(
|
|
|
|
|
UIEditorWorkspaceLayoutOperationStatus status) {
|
|
|
|
|
switch (status) {
|
|
|
|
|
case UIEditorWorkspaceLayoutOperationStatus::Changed:
|
|
|
|
|
return "Changed";
|
|
|
|
|
case UIEditorWorkspaceLayoutOperationStatus::NoOp:
|
|
|
|
|
return "NoOp";
|
|
|
|
|
case UIEditorWorkspaceLayoutOperationStatus::Rejected:
|
|
|
|
|
return "Rejected";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "Unknown";
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 16:20:46 +08:00
|
|
|
UIEditorWorkspaceController::UIEditorWorkspaceController(
|
|
|
|
|
UIEditorPanelRegistry panelRegistry,
|
|
|
|
|
UIEditorWorkspaceModel workspace,
|
|
|
|
|
UIEditorWorkspaceSession session)
|
|
|
|
|
: m_panelRegistry(std::move(panelRegistry))
|
2026-04-10 21:50:31 +08:00
|
|
|
, m_baselineWorkspace(CanonicalizeUIEditorWorkspaceModel(workspace))
|
2026-04-06 16:20:46 +08:00
|
|
|
, m_baselineSession(session)
|
2026-04-10 21:50:31 +08:00
|
|
|
, m_workspace(m_baselineWorkspace)
|
2026-04-06 16:20:46 +08:00
|
|
|
, m_session(std::move(session)) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UIEditorWorkspaceControllerValidationResult UIEditorWorkspaceController::ValidateState() const {
|
|
|
|
|
const UIEditorPanelRegistryValidationResult registryValidation =
|
|
|
|
|
ValidateUIEditorPanelRegistry(m_panelRegistry);
|
|
|
|
|
if (!registryValidation.IsValid()) {
|
|
|
|
|
UIEditorWorkspaceControllerValidationResult result = {};
|
|
|
|
|
result.code = UIEditorWorkspaceControllerValidationCode::InvalidPanelRegistry;
|
|
|
|
|
result.message = registryValidation.message;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UIEditorWorkspaceValidationResult workspaceValidation =
|
|
|
|
|
ValidateUIEditorWorkspace(m_workspace);
|
|
|
|
|
if (!workspaceValidation.IsValid()) {
|
|
|
|
|
UIEditorWorkspaceControllerValidationResult result = {};
|
|
|
|
|
result.code = UIEditorWorkspaceControllerValidationCode::InvalidWorkspace;
|
|
|
|
|
result.message = workspaceValidation.message;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UIEditorWorkspaceSessionValidationResult sessionValidation =
|
|
|
|
|
ValidateUIEditorWorkspaceSession(m_panelRegistry, m_workspace, m_session);
|
|
|
|
|
if (!sessionValidation.IsValid()) {
|
|
|
|
|
UIEditorWorkspaceControllerValidationResult result = {};
|
|
|
|
|
result.code = UIEditorWorkspaceControllerValidationCode::InvalidWorkspaceSession;
|
|
|
|
|
result.message = sessionValidation.message;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 16:59:15 +08:00
|
|
|
UIEditorWorkspaceLayoutSnapshot UIEditorWorkspaceController::CaptureLayoutSnapshot() const {
|
|
|
|
|
return BuildUIEditorWorkspaceLayoutSnapshot(m_workspace, m_session);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 16:20:46 +08:00
|
|
|
UIEditorWorkspaceCommandResult UIEditorWorkspaceController::BuildResult(
|
|
|
|
|
const UIEditorWorkspaceCommand& command,
|
|
|
|
|
UIEditorWorkspaceCommandStatus status,
|
|
|
|
|
std::string message) const {
|
|
|
|
|
UIEditorWorkspaceCommandResult result = {};
|
|
|
|
|
result.kind = command.kind;
|
|
|
|
|
result.status = status;
|
|
|
|
|
result.panelId = command.panelId;
|
|
|
|
|
result.message = std::move(message);
|
|
|
|
|
result.activePanelId = m_workspace.activePanelId;
|
2026-04-15 11:13:16 +08:00
|
|
|
result.visiblePanelIds = Detail::CollectVisiblePanelIds(m_workspace, m_session);
|
2026-04-06 16:20:46 +08:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 16:59:15 +08:00
|
|
|
UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::BuildLayoutOperationResult(
|
|
|
|
|
UIEditorWorkspaceLayoutOperationStatus status,
|
|
|
|
|
std::string message) const {
|
|
|
|
|
UIEditorWorkspaceLayoutOperationResult result = {};
|
|
|
|
|
result.status = status;
|
|
|
|
|
result.message = std::move(message);
|
|
|
|
|
result.activePanelId = m_workspace.activePanelId;
|
2026-04-15 11:13:16 +08:00
|
|
|
result.visiblePanelIds = Detail::CollectVisiblePanelIds(m_workspace, m_session);
|
2026-04-06 16:59:15 +08:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 16:20:46 +08:00
|
|
|
UIEditorWorkspaceCommandResult UIEditorWorkspaceController::FinalizeMutation(
|
|
|
|
|
const UIEditorWorkspaceCommand& command,
|
|
|
|
|
bool changed,
|
|
|
|
|
std::string changedMessage,
|
|
|
|
|
std::string unexpectedFailureMessage,
|
|
|
|
|
const UIEditorWorkspaceModel& previousWorkspace,
|
|
|
|
|
const UIEditorWorkspaceSession& previousSession) {
|
|
|
|
|
if (!changed) {
|
|
|
|
|
return BuildResult(
|
|
|
|
|
command,
|
|
|
|
|
UIEditorWorkspaceCommandStatus::Rejected,
|
|
|
|
|
std::move(unexpectedFailureMessage));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UIEditorWorkspaceControllerValidationResult validation = ValidateState();
|
|
|
|
|
if (!validation.IsValid()) {
|
|
|
|
|
m_workspace = previousWorkspace;
|
|
|
|
|
m_session = previousSession;
|
|
|
|
|
return BuildResult(
|
|
|
|
|
command,
|
|
|
|
|
UIEditorWorkspaceCommandStatus::Rejected,
|
|
|
|
|
"Command produced invalid workspace state: " + validation.message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BuildResult(
|
|
|
|
|
command,
|
|
|
|
|
UIEditorWorkspaceCommandStatus::Changed,
|
|
|
|
|
std::move(changedMessage));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UIEditorPanelDescriptor* UIEditorWorkspaceController::FindPanelDescriptor(
|
|
|
|
|
std::string_view panelId) const {
|
|
|
|
|
return FindUIEditorPanelDescriptor(m_panelRegistry, panelId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UIEditorWorkspaceController BuildDefaultUIEditorWorkspaceController(
|
|
|
|
|
const UIEditorPanelRegistry& panelRegistry,
|
|
|
|
|
const UIEditorWorkspaceModel& workspace) {
|
2026-04-10 21:50:31 +08:00
|
|
|
const UIEditorWorkspaceModel canonicalWorkspace =
|
|
|
|
|
CanonicalizeUIEditorWorkspaceModel(workspace);
|
2026-04-06 16:20:46 +08:00
|
|
|
return UIEditorWorkspaceController(
|
|
|
|
|
panelRegistry,
|
2026-04-10 21:50:31 +08:00
|
|
|
canonicalWorkspace,
|
|
|
|
|
BuildDefaultUIEditorWorkspaceSession(panelRegistry, canonicalWorkspace));
|
2026-04-06 16:20:46 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-06 20:02:34 +08:00
|
|
|
} // namespace XCEngine::UI::Editor
|