223 lines
7.1 KiB
C++
223 lines
7.1 KiB
C++
#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
|