#include "ui_core/overlay_lifetime.h" #include namespace pp::ui { UiOverlayLifetime::UiOverlayLifetime(NodeLifetimeTree& tree, NodeHandle root) noexcept : tree_(tree) , root_(root) { } pp::foundation::Result UiOverlayLifetime::open_popup() { return open_overlay(root_, UiOverlayKind::popup, true, false); } pp::foundation::Result UiOverlayLifetime::open_child_popup(NodeHandle parent_popup) { if (!tracked_alive(parent_popup)) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("child popup requires a live tracked parent popup")); } return open_overlay(parent_popup, UiOverlayKind::popup, true, false); } pp::foundation::Result UiOverlayLifetime::open_dialog(bool modal) { return open_overlay(root_, UiOverlayKind::dialog, modal, modal); } pp::foundation::Status UiOverlayLifetime::close(NodeHandle overlay) noexcept { if (!tracked_alive(overlay)) { return pp::foundation::Status::invalid_argument("overlay close requires a live tracked overlay"); } const auto status = tree_.destroy_subtree(overlay); if (!status.ok()) { return status; } prune_dead_entries(); restore_capture(UiCaptureKind::pointer); restore_capture(UiCaptureKind::keyboard); return pp::foundation::Status::success(); } void UiOverlayLifetime::clear_for_layout_reload() noexcept { tree_.clear(); entries_.clear(); } bool UiOverlayLifetime::tracks(NodeHandle overlay) const noexcept { return tracked_alive(overlay); } std::size_t UiOverlayLifetime::overlay_count() const noexcept { std::size_t count = 0; for (const auto& entry : entries_) { if (tree_.contains(entry.node)) { ++count; } } return count; } pp::foundation::Result UiOverlayLifetime::top_overlay() const noexcept { for (auto index = entries_.size(); index > 0U; --index) { const auto& entry = entries_[index - 1U]; if (tree_.contains(entry.node)) { return pp::foundation::Result::success(entry.node); } } return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("overlay stack is empty")); } pp::foundation::Result UiOverlayLifetime::open_overlay( NodeHandle parent, UiOverlayKind kind, bool captures_pointer, bool captures_keyboard) { if (!tree_.contains(root_)) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("overlay root is not live")); } if (!tree_.contains(parent)) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("overlay parent is not live")); } auto node = tree_.create_child(parent); if (!node) { return node; } if (captures_pointer) { const auto status = tree_.capture(UiCaptureKind::pointer, node.value()); if (!status.ok()) { (void)tree_.destroy_subtree(node.value()); return pp::foundation::Result::failure(status); } } if (captures_keyboard) { const auto status = tree_.capture(UiCaptureKind::keyboard, node.value()); if (!status.ok()) { (void)tree_.destroy_subtree(node.value()); return pp::foundation::Result::failure(status); } } entries_.push_back(UiOverlayEntry { .node = node.value(), .parent = parent, .kind = kind, .captures_pointer = captures_pointer, .captures_keyboard = captures_keyboard, }); return node; } void UiOverlayLifetime::prune_dead_entries() noexcept { entries_.erase( std::remove_if( entries_.begin(), entries_.end(), [this](const UiOverlayEntry& entry) { return !tree_.contains(entry.node); }), entries_.end()); } void UiOverlayLifetime::restore_capture(UiCaptureKind kind) noexcept { for (auto index = entries_.size(); index > 0U; --index) { const auto& entry = entries_[index - 1U]; const auto captures = kind == UiCaptureKind::pointer ? entry.captures_pointer : entry.captures_keyboard; if (captures && tree_.contains(entry.node)) { (void)tree_.capture(kind, entry.node); return; } } } bool UiOverlayLifetime::tracked_alive(NodeHandle overlay) const noexcept { if (!tree_.contains(overlay)) { return false; } return std::any_of( entries_.begin(), entries_.end(), [overlay](const UiOverlayEntry& entry) { return entry.node == overlay; }); } }