Model UI overlay lifetime in ui core
This commit is contained in:
173
src/ui_core/overlay_lifetime.cpp
Normal file
173
src/ui_core/overlay_lifetime.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "ui_core/overlay_lifetime.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace pp::ui {
|
||||
|
||||
UiOverlayLifetime::UiOverlayLifetime(NodeLifetimeTree& tree, NodeHandle root) noexcept
|
||||
: tree_(tree)
|
||||
, root_(root)
|
||||
{
|
||||
}
|
||||
|
||||
pp::foundation::Result<NodeHandle> UiOverlayLifetime::open_popup()
|
||||
{
|
||||
return open_overlay(root_, UiOverlayKind::popup, true, false);
|
||||
}
|
||||
|
||||
pp::foundation::Result<NodeHandle> UiOverlayLifetime::open_child_popup(NodeHandle parent_popup)
|
||||
{
|
||||
if (!tracked_alive(parent_popup)) {
|
||||
return pp::foundation::Result<NodeHandle>::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<NodeHandle> 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<NodeHandle> 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<NodeHandle>::success(entry.node);
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Result<NodeHandle>::failure(
|
||||
pp::foundation::Status::invalid_argument("overlay stack is empty"));
|
||||
}
|
||||
|
||||
pp::foundation::Result<NodeHandle> UiOverlayLifetime::open_overlay(
|
||||
NodeHandle parent,
|
||||
UiOverlayKind kind,
|
||||
bool captures_pointer,
|
||||
bool captures_keyboard)
|
||||
{
|
||||
if (!tree_.contains(root_)) {
|
||||
return pp::foundation::Result<NodeHandle>::failure(
|
||||
pp::foundation::Status::invalid_argument("overlay root is not live"));
|
||||
}
|
||||
|
||||
if (!tree_.contains(parent)) {
|
||||
return pp::foundation::Result<NodeHandle>::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<NodeHandle>::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<NodeHandle>::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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
54
src/ui_core/overlay_lifetime.h
Normal file
54
src/ui_core/overlay_lifetime.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "foundation/result.h"
|
||||
#include "ui_core/node_lifetime.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace pp::ui {
|
||||
|
||||
enum class UiOverlayKind : std::uint8_t {
|
||||
popup,
|
||||
dialog,
|
||||
};
|
||||
|
||||
struct UiOverlayEntry {
|
||||
NodeHandle node {};
|
||||
NodeHandle parent {};
|
||||
UiOverlayKind kind = UiOverlayKind::popup;
|
||||
bool captures_pointer = false;
|
||||
bool captures_keyboard = false;
|
||||
};
|
||||
|
||||
class UiOverlayLifetime {
|
||||
public:
|
||||
UiOverlayLifetime(NodeLifetimeTree& tree, NodeHandle root) noexcept;
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<NodeHandle> open_popup();
|
||||
[[nodiscard]] pp::foundation::Result<NodeHandle> open_child_popup(NodeHandle parent_popup);
|
||||
[[nodiscard]] pp::foundation::Result<NodeHandle> open_dialog(bool modal);
|
||||
[[nodiscard]] pp::foundation::Status close(NodeHandle overlay) noexcept;
|
||||
void clear_for_layout_reload() noexcept;
|
||||
|
||||
[[nodiscard]] bool tracks(NodeHandle overlay) const noexcept;
|
||||
[[nodiscard]] std::size_t overlay_count() const noexcept;
|
||||
[[nodiscard]] pp::foundation::Result<NodeHandle> top_overlay() const noexcept;
|
||||
|
||||
private:
|
||||
[[nodiscard]] pp::foundation::Result<NodeHandle> open_overlay(
|
||||
NodeHandle parent,
|
||||
UiOverlayKind kind,
|
||||
bool captures_pointer,
|
||||
bool captures_keyboard);
|
||||
void prune_dead_entries() noexcept;
|
||||
void restore_capture(UiCaptureKind kind) noexcept;
|
||||
[[nodiscard]] bool tracked_alive(NodeHandle overlay) const noexcept;
|
||||
|
||||
NodeLifetimeTree& tree_;
|
||||
NodeHandle root_ {};
|
||||
std::vector<UiOverlayEntry> entries_;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user