142 lines
4.8 KiB
C++
142 lines
4.8 KiB
C++
#pragma once
|
|
|
|
#include "foundation/result.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <array>
|
|
#include <vector>
|
|
|
|
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<void(NodeHandle)>;
|
|
|
|
[[nodiscard]] pp::foundation::Result<NodeHandle> create_root();
|
|
[[nodiscard]] pp::foundation::Result<NodeHandle> create_child(NodeHandle parent);
|
|
|
|
[[nodiscard]] bool contains(NodeHandle node) const noexcept;
|
|
[[nodiscard]] pp::foundation::Result<NodeHandle> parent_of(NodeHandle node) const noexcept;
|
|
[[nodiscard]] pp::foundation::Result<std::size_t> child_count(NodeHandle node) const noexcept;
|
|
[[nodiscard]] pp::foundation::Result<NodeHandle> 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<NodeHandle> captured_node(UiCaptureKind kind) const noexcept;
|
|
|
|
[[nodiscard]] pp::foundation::Result<UiConnection> connect(NodeHandle node, Callback callback);
|
|
[[nodiscard]] pp::foundation::Result<ScopedUiConnection> scoped_connect(NodeHandle node, Callback callback);
|
|
[[nodiscard]] pp::foundation::Status disconnect(UiConnection connection) noexcept;
|
|
[[nodiscard]] pp::foundation::Result<std::size_t> 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<NodeHandle> children;
|
|
std::vector<UiConnection> 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<NodeHandle> 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<NodeSlot> nodes_;
|
|
std::vector<std::uint32_t> free_nodes_;
|
|
std::vector<ConnectionSlot> connections_;
|
|
std::vector<std::uint32_t> free_connections_;
|
|
std::array<NodeHandle, 2> captures_ {};
|
|
};
|
|
|
|
}
|