关键节点
This commit is contained in:
222
editor/src/Menu/UIEditorMenuSession.cpp
Normal file
222
editor/src/Menu/UIEditorMenuSession.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
#include <XCEditor/Menu/UIEditorMenuSession.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
bool AreEquivalentPopupEntries(
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayEntry& lhs,
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayEntry& rhs) {
|
||||
return lhs.popupId == rhs.popupId &&
|
||||
lhs.parentPopupId == rhs.parentPopupId &&
|
||||
lhs.anchorRect.x == rhs.anchorRect.x &&
|
||||
lhs.anchorRect.y == rhs.anchorRect.y &&
|
||||
lhs.anchorRect.width == rhs.anchorRect.width &&
|
||||
lhs.anchorRect.height == rhs.anchorRect.height &&
|
||||
lhs.anchorPath == rhs.anchorPath &&
|
||||
lhs.surfacePath == rhs.surfacePath &&
|
||||
lhs.placement == rhs.placement &&
|
||||
lhs.dismissOnPointerOutside == rhs.dismissOnPointerOutside &&
|
||||
lhs.dismissOnEscape == rhs.dismissOnEscape &&
|
||||
lhs.dismissOnFocusLoss == rhs.dismissOnFocusLoss;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool UIEditorMenuSession::IsPopupOpen(std::string_view popupId) const {
|
||||
return m_popupOverlayModel.FindPopup(popupId) != nullptr;
|
||||
}
|
||||
|
||||
const UIEditorMenuPopupState* UIEditorMenuSession::FindPopupState(
|
||||
std::string_view popupId) const {
|
||||
for (const UIEditorMenuPopupState& state : m_popupStates) {
|
||||
if (state.popupId == popupId) {
|
||||
return &state;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UIEditorMenuSession::Reset() {
|
||||
m_openRootMenuId.clear();
|
||||
m_popupOverlayModel = {};
|
||||
m_popupStates.clear();
|
||||
m_openSubmenuItemIds.clear();
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::OpenMenuBarRoot(
|
||||
std::string_view menuId,
|
||||
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry) {
|
||||
return OpenRootMenu(menuId, std::move(entry));
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::OpenRootMenu(
|
||||
std::string_view menuId,
|
||||
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry) {
|
||||
if (menuId.empty() || entry.popupId.empty()) {
|
||||
return BuildResult({});
|
||||
}
|
||||
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayEntry* rootPopup =
|
||||
m_popupOverlayModel.GetRootPopup();
|
||||
if (rootPopup != nullptr &&
|
||||
m_openRootMenuId == menuId &&
|
||||
AreEquivalentPopupEntries(*rootPopup, entry)) {
|
||||
return BuildResult({});
|
||||
}
|
||||
|
||||
entry.parentPopupId.clear();
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult mutation =
|
||||
m_popupOverlayModel.OpenPopup(std::move(entry));
|
||||
if (mutation.changed) {
|
||||
m_popupStates.clear();
|
||||
|
||||
UIEditorMenuPopupState rootState = {};
|
||||
rootState.popupId = mutation.openedPopupId;
|
||||
rootState.menuId = std::string(menuId);
|
||||
m_popupStates.push_back(std::move(rootState));
|
||||
RebuildDerivedState();
|
||||
}
|
||||
|
||||
return BuildResult(mutation);
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::HoverMenuBarRoot(
|
||||
std::string_view menuId,
|
||||
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry) {
|
||||
if (!HasOpenMenu() || IsMenuOpen(menuId)) {
|
||||
return BuildResult({});
|
||||
}
|
||||
|
||||
return OpenRootMenu(menuId, std::move(entry));
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::HoverSubmenu(
|
||||
std::string_view itemId,
|
||||
::XCEngine::UI::Widgets::UIPopupOverlayEntry entry) {
|
||||
if (!HasOpenMenu() ||
|
||||
itemId.empty() ||
|
||||
entry.popupId.empty() ||
|
||||
entry.parentPopupId.empty() ||
|
||||
m_popupOverlayModel.FindPopup(entry.parentPopupId) == nullptr) {
|
||||
return BuildResult({});
|
||||
}
|
||||
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayEntry* existingPopup =
|
||||
m_popupOverlayModel.FindPopup(entry.popupId);
|
||||
if (existingPopup != nullptr &&
|
||||
existingPopup->parentPopupId == entry.parentPopupId) {
|
||||
return BuildResult({});
|
||||
}
|
||||
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult mutation =
|
||||
m_popupOverlayModel.OpenPopup(std::move(entry));
|
||||
if (mutation.changed) {
|
||||
RemoveClosedPopupStates(mutation.closedPopupIds);
|
||||
|
||||
UIEditorMenuPopupState popupState = {};
|
||||
popupState.popupId = mutation.openedPopupId;
|
||||
popupState.menuId = m_openRootMenuId;
|
||||
popupState.itemId = std::string(itemId);
|
||||
m_popupStates.push_back(std::move(popupState));
|
||||
RebuildDerivedState();
|
||||
}
|
||||
|
||||
return BuildResult(mutation);
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::CloseAll(
|
||||
::XCEngine::UI::Widgets::UIPopupDismissReason dismissReason) {
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult mutation =
|
||||
m_popupOverlayModel.CloseAll(dismissReason);
|
||||
if (mutation.changed) {
|
||||
RemoveClosedPopupStates(mutation.closedPopupIds);
|
||||
RebuildDerivedState();
|
||||
}
|
||||
|
||||
return BuildResult(mutation);
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::DismissFromEscape() {
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult mutation =
|
||||
m_popupOverlayModel.DismissFromEscape();
|
||||
if (mutation.changed) {
|
||||
RemoveClosedPopupStates(mutation.closedPopupIds);
|
||||
RebuildDerivedState();
|
||||
}
|
||||
|
||||
return BuildResult(mutation);
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::DismissFromPointerDown(
|
||||
const UIInputPath& hitPath) {
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult mutation =
|
||||
m_popupOverlayModel.DismissFromPointerDown(hitPath);
|
||||
if (mutation.changed) {
|
||||
RemoveClosedPopupStates(mutation.closedPopupIds);
|
||||
RebuildDerivedState();
|
||||
}
|
||||
|
||||
return BuildResult(mutation);
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::DismissFromFocusLoss(
|
||||
const UIInputPath& focusedPath) {
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult mutation =
|
||||
m_popupOverlayModel.DismissFromFocusLoss(focusedPath);
|
||||
if (mutation.changed) {
|
||||
RemoveClosedPopupStates(mutation.closedPopupIds);
|
||||
RebuildDerivedState();
|
||||
}
|
||||
|
||||
return BuildResult(mutation);
|
||||
}
|
||||
|
||||
UIEditorMenuSessionMutationResult UIEditorMenuSession::BuildResult(
|
||||
const ::XCEngine::UI::Widgets::UIPopupOverlayMutationResult& mutation) const {
|
||||
UIEditorMenuSessionMutationResult result = {};
|
||||
result.changed = mutation.changed;
|
||||
result.openRootMenuId = m_openRootMenuId;
|
||||
result.openedPopupId = mutation.openedPopupId;
|
||||
result.closedPopupIds = mutation.closedPopupIds;
|
||||
result.dismissReason = mutation.dismissReason;
|
||||
return result;
|
||||
}
|
||||
|
||||
void UIEditorMenuSession::RemoveClosedPopupStates(
|
||||
const std::vector<std::string>& closedPopupIds) {
|
||||
if (closedPopupIds.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::erase_if(
|
||||
m_popupStates,
|
||||
[&closedPopupIds](const UIEditorMenuPopupState& state) {
|
||||
return std::find(
|
||||
closedPopupIds.begin(),
|
||||
closedPopupIds.end(),
|
||||
state.popupId) != closedPopupIds.end();
|
||||
});
|
||||
}
|
||||
|
||||
void UIEditorMenuSession::RebuildDerivedState() {
|
||||
m_openSubmenuItemIds.clear();
|
||||
|
||||
if (m_popupStates.empty() || !m_popupOverlayModel.HasOpenPopups()) {
|
||||
m_openRootMenuId.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
m_openRootMenuId = m_popupStates.front().menuId;
|
||||
for (std::size_t index = 1u; index < m_popupStates.size(); ++index) {
|
||||
if (!m_popupStates[index].itemId.empty()) {
|
||||
m_openSubmenuItemIds.push_back(m_popupStates[index].itemId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
Reference in New Issue
Block a user