Files
panopainter/src/ui_core/node_lifetime.h

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_ {};
};
}