#pragma once #include "foundation/result.h" #include #include #include #include #include namespace pp::ui { inline constexpr std::uint32_t invalid_node_slot = 0xffffffffU; inline constexpr std::uint32_t invalid_connection_slot = 0xffffffffU; struct NodeHandle { std::uint32_t slot = invalid_node_slot; std::uint32_t generation = 0; [[nodiscard]] constexpr bool valid() const noexcept { return slot != invalid_node_slot && generation != 0U; } }; [[nodiscard]] constexpr bool operator==(NodeHandle left, NodeHandle right) noexcept { return left.slot == right.slot && left.generation == right.generation; } [[nodiscard]] constexpr bool operator!=(NodeHandle left, NodeHandle right) noexcept { return !(left == right); } struct UiConnection { std::uint32_t slot = invalid_connection_slot; std::uint32_t generation = 0; [[nodiscard]] constexpr bool valid() const noexcept { return slot != invalid_connection_slot && generation != 0U; } }; [[nodiscard]] constexpr bool operator==(UiConnection left, UiConnection right) noexcept { return left.slot == right.slot && left.generation == right.generation; } [[nodiscard]] constexpr bool operator!=(UiConnection left, UiConnection right) noexcept { return !(left == right); } enum class UiCaptureKind : std::uint8_t { pointer, keyboard, }; class NodeLifetimeTree; class ScopedUiConnection { public: ScopedUiConnection() noexcept = default; ScopedUiConnection(NodeLifetimeTree& tree, UiConnection connection) noexcept; ScopedUiConnection(const ScopedUiConnection&) = delete; ScopedUiConnection& operator=(const ScopedUiConnection&) = delete; ScopedUiConnection(ScopedUiConnection&& other) noexcept; ScopedUiConnection& operator=(ScopedUiConnection&& other) noexcept; ~ScopedUiConnection(); void reset() noexcept; [[nodiscard]] UiConnection connection() const noexcept; [[nodiscard]] bool connected() const noexcept; private: NodeLifetimeTree* tree_ = nullptr; UiConnection connection_ {}; }; class NodeLifetimeTree { public: using Callback = std::function; [[nodiscard]] pp::foundation::Result create_root(); [[nodiscard]] pp::foundation::Result create_child(NodeHandle parent); [[nodiscard]] bool contains(NodeHandle node) const noexcept; [[nodiscard]] pp::foundation::Result parent_of(NodeHandle node) const noexcept; [[nodiscard]] pp::foundation::Result child_count(NodeHandle node) const noexcept; [[nodiscard]] pp::foundation::Result child_at(NodeHandle node, std::size_t index) const noexcept; [[nodiscard]] pp::foundation::Status destroy_subtree(NodeHandle node) noexcept; void clear() noexcept; [[nodiscard]] pp::foundation::Status capture(UiCaptureKind kind, NodeHandle node) noexcept; [[nodiscard]] pp::foundation::Status release_capture(UiCaptureKind kind, NodeHandle node) noexcept; [[nodiscard]] pp::foundation::Result captured_node(UiCaptureKind kind) const noexcept; [[nodiscard]] pp::foundation::Result connect(NodeHandle node, Callback callback); [[nodiscard]] pp::foundation::Result scoped_connect(NodeHandle node, Callback callback); [[nodiscard]] pp::foundation::Status disconnect(UiConnection connection) noexcept; [[nodiscard]] pp::foundation::Result live_connection_count(NodeHandle node) const noexcept; [[nodiscard]] pp::foundation::Status dispatch(NodeHandle node); private: struct NodeSlot { std::uint32_t generation = 1; bool alive = false; NodeHandle parent {}; std::vector children; std::vector connections; }; struct ConnectionSlot { std::uint32_t generation = 1; bool alive = false; NodeHandle node {}; Callback callback; }; [[nodiscard]] NodeSlot* node_slot(NodeHandle node) noexcept; [[nodiscard]] const NodeSlot* node_slot(NodeHandle node) const noexcept; [[nodiscard]] ConnectionSlot* connection_slot(UiConnection connection) noexcept; [[nodiscard]] const ConnectionSlot* connection_slot(UiConnection connection) const noexcept; [[nodiscard]] pp::foundation::Result allocate_node(NodeHandle parent); [[nodiscard]] std::size_t capture_index(UiCaptureKind kind) const noexcept; void unlink_from_parent(NodeHandle node) noexcept; void release_connection(UiConnection connection) noexcept; void release_captures_for_node(NodeHandle node) noexcept; std::vector nodes_; std::vector free_nodes_; std::vector connections_; std::vector free_connections_; std::array captures_ {}; }; }