Archive XCUI subplan 04
This commit is contained in:
115
engine/include/XCEngine/UI/Core/UIBuildContext.h
Normal file
115
engine/include/XCEngine/UI/Core/UIBuildContext.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include "UIInvalidation.h"
|
||||
#include "UIViewModel.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
|
||||
struct UIBuildElement {
|
||||
UIElementId id = kInvalidUIElementId;
|
||||
UIElementId parentId = kInvalidUIElementId;
|
||||
std::string typeName;
|
||||
std::uint64_t structuralRevision = 0;
|
||||
std::uint64_t localStateRevision = 0;
|
||||
std::uint64_t viewModelRevision = 0;
|
||||
};
|
||||
|
||||
struct UIBuildElementDesc {
|
||||
UIElementId id = kInvalidUIElementId;
|
||||
std::string typeName;
|
||||
std::uint64_t structuralRevision = 0;
|
||||
std::uint64_t localStateRevision = 0;
|
||||
const IUIViewModel* viewModel = nullptr;
|
||||
};
|
||||
|
||||
class UIBuildList {
|
||||
public:
|
||||
void Reset();
|
||||
void Reserve(std::size_t elementCount);
|
||||
void AddElement(const UIBuildElement& element);
|
||||
|
||||
const std::vector<UIBuildElement>& GetElements() const { return m_elements; }
|
||||
bool Empty() const { return m_elements.empty(); }
|
||||
std::size_t GetCount() const { return m_elements.size(); }
|
||||
|
||||
private:
|
||||
std::vector<UIBuildElement> m_elements;
|
||||
};
|
||||
|
||||
class UIBuildContext {
|
||||
public:
|
||||
class ElementScope {
|
||||
public:
|
||||
ElementScope() = default;
|
||||
ElementScope(UIBuildContext* context, bool valid)
|
||||
: m_context(context)
|
||||
, m_valid(valid) {
|
||||
}
|
||||
|
||||
ElementScope(const ElementScope&) = delete;
|
||||
ElementScope& operator=(const ElementScope&) = delete;
|
||||
|
||||
ElementScope(ElementScope&& other) noexcept
|
||||
: m_context(other.m_context)
|
||||
, m_valid(other.m_valid) {
|
||||
other.m_context = nullptr;
|
||||
other.m_valid = false;
|
||||
}
|
||||
|
||||
ElementScope& operator=(ElementScope&& other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Close();
|
||||
m_context = other.m_context;
|
||||
m_valid = other.m_valid;
|
||||
other.m_context = nullptr;
|
||||
other.m_valid = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ElementScope() {
|
||||
Close();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
void Close();
|
||||
|
||||
private:
|
||||
UIBuildContext* m_context = nullptr;
|
||||
bool m_valid = false;
|
||||
};
|
||||
|
||||
explicit UIBuildContext(UIBuildList& buildList);
|
||||
|
||||
bool BeginElement(const UIBuildElementDesc& desc);
|
||||
bool AddLeaf(const UIBuildElementDesc& desc);
|
||||
bool EndElement();
|
||||
|
||||
ElementScope PushElement(const UIBuildElementDesc& desc);
|
||||
|
||||
bool IsValid() const { return m_lastError.empty(); }
|
||||
bool HasOpenElements() const { return !m_stack.empty(); }
|
||||
const std::string& GetLastError() const { return m_lastError; }
|
||||
|
||||
private:
|
||||
bool AppendElement(const UIBuildElementDesc& desc, bool keepOpen);
|
||||
void SetError(const std::string& errorMessage);
|
||||
|
||||
UIBuildList* m_buildList = nullptr;
|
||||
std::vector<UIElementId> m_stack;
|
||||
std::unordered_set<UIElementId> m_seenIds;
|
||||
std::string m_lastError;
|
||||
};
|
||||
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
26
engine/include/XCEngine/UI/Core/UIContext.h
Normal file
26
engine/include/XCEngine/UI/Core/UIContext.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "UIBuildContext.h"
|
||||
#include "UIElementTree.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
|
||||
class UIContext {
|
||||
public:
|
||||
using BuildCallback = std::function<void(UIBuildContext&)>;
|
||||
|
||||
UIElementTreeRebuildResult Rebuild(const BuildCallback& buildCallback);
|
||||
|
||||
UIElementTree& GetElementTree() { return m_tree; }
|
||||
const UIElementTree& GetElementTree() const { return m_tree; }
|
||||
|
||||
private:
|
||||
UIBuildList m_buildList;
|
||||
UIElementTree m_tree;
|
||||
};
|
||||
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
84
engine/include/XCEngine/UI/Core/UIElementTree.h
Normal file
84
engine/include/XCEngine/UI/Core/UIElementTree.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include "UIBuildContext.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
|
||||
enum class UIElementChangeKind : std::uint8_t {
|
||||
None = 0,
|
||||
Created,
|
||||
Updated,
|
||||
Removed
|
||||
};
|
||||
|
||||
struct UIElementNode {
|
||||
UIElementId id = kInvalidUIElementId;
|
||||
UIElementId parentId = kInvalidUIElementId;
|
||||
std::vector<UIElementId> childIds;
|
||||
std::string typeName;
|
||||
std::uint32_t depth = 0;
|
||||
std::uint64_t structuralRevision = 0;
|
||||
std::uint64_t localStateRevision = 0;
|
||||
std::uint64_t viewModelRevision = 0;
|
||||
std::uint64_t lastBuildGeneration = 0;
|
||||
UIDirtyFlags dirtyFlags = UIDirtyFlags::None;
|
||||
|
||||
bool IsDirty() const {
|
||||
return UI::IsDirty(dirtyFlags);
|
||||
}
|
||||
};
|
||||
|
||||
struct UIElementChange {
|
||||
UIElementId id = kInvalidUIElementId;
|
||||
UIElementChangeKind kind = UIElementChangeKind::None;
|
||||
UIDirtyFlags dirtyFlags = UIDirtyFlags::None;
|
||||
};
|
||||
|
||||
struct UIElementTreeRebuildResult {
|
||||
bool succeeded = true;
|
||||
bool treeChanged = false;
|
||||
std::uint64_t generation = 0;
|
||||
std::string errorMessage;
|
||||
std::vector<UIElementChange> changes;
|
||||
std::vector<UIElementId> dirtyRootIds;
|
||||
|
||||
const UIElementChange* FindChange(UIElementId id) const;
|
||||
bool HasChange(UIElementId id) const;
|
||||
};
|
||||
|
||||
class UIElementTree {
|
||||
public:
|
||||
UIElementTree() = default;
|
||||
|
||||
UIElementTreeRebuildResult Rebuild(const UIBuildList& buildList);
|
||||
|
||||
bool HasNode(UIElementId id) const;
|
||||
const UIElementNode* FindNode(UIElementId id) const;
|
||||
UIElementId GetRootId() const { return m_rootId; }
|
||||
std::size_t GetNodeCount() const { return m_nodes.size(); }
|
||||
std::uint64_t GetGeneration() const { return m_generation; }
|
||||
|
||||
void MarkDirty(UIElementId id, UIDirtyFlags flags);
|
||||
std::vector<UIElementId> CollectDirtyRootIds() const;
|
||||
void ClearDirtyFlags(UIElementId id, bool includeSubtree);
|
||||
void ClearAllDirtyFlags();
|
||||
|
||||
private:
|
||||
void MarkDirtyInternal(
|
||||
std::unordered_map<UIElementId, UIElementNode>& nodes,
|
||||
UIElementId id,
|
||||
UIDirtyFlags flags);
|
||||
void ClearDirtyFlagsRecursive(UIElementId id);
|
||||
|
||||
std::unordered_map<UIElementId, UIElementNode> m_nodes;
|
||||
UIElementId m_rootId = kInvalidUIElementId;
|
||||
std::uint64_t m_generation = 0;
|
||||
};
|
||||
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
52
engine/include/XCEngine/UI/Core/UIInvalidation.h
Normal file
52
engine/include/XCEngine/UI/Core/UIInvalidation.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
|
||||
using UIElementId = std::uint64_t;
|
||||
|
||||
constexpr UIElementId kInvalidUIElementId = 0;
|
||||
|
||||
enum class UIDirtyFlags : std::uint8_t {
|
||||
None = 0,
|
||||
Structure = 1 << 0,
|
||||
Layout = 1 << 1,
|
||||
Paint = 1 << 2,
|
||||
LocalState = 1 << 3,
|
||||
ViewModel = 1 << 4
|
||||
};
|
||||
|
||||
inline constexpr UIDirtyFlags operator|(UIDirtyFlags left, UIDirtyFlags right) {
|
||||
return static_cast<UIDirtyFlags>(
|
||||
static_cast<std::uint8_t>(left) |
|
||||
static_cast<std::uint8_t>(right));
|
||||
}
|
||||
|
||||
inline constexpr UIDirtyFlags operator&(UIDirtyFlags left, UIDirtyFlags right) {
|
||||
return static_cast<UIDirtyFlags>(
|
||||
static_cast<std::uint8_t>(left) &
|
||||
static_cast<std::uint8_t>(right));
|
||||
}
|
||||
|
||||
inline constexpr UIDirtyFlags& operator|=(UIDirtyFlags& left, UIDirtyFlags right) {
|
||||
left = left | right;
|
||||
return left;
|
||||
}
|
||||
|
||||
inline constexpr UIDirtyFlags& operator&=(UIDirtyFlags& left, UIDirtyFlags right) {
|
||||
left = left & right;
|
||||
return left;
|
||||
}
|
||||
|
||||
inline constexpr bool HasAnyDirtyFlags(UIDirtyFlags value, UIDirtyFlags flags) {
|
||||
return (value & flags) != UIDirtyFlags::None;
|
||||
}
|
||||
|
||||
inline constexpr bool IsDirty(UIDirtyFlags value) {
|
||||
return value != UIDirtyFlags::None;
|
||||
}
|
||||
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
31
engine/include/XCEngine/UI/Core/UIViewModel.h
Normal file
31
engine/include/XCEngine/UI/Core/UIViewModel.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace UI {
|
||||
|
||||
class IUIViewModel {
|
||||
public:
|
||||
virtual ~IUIViewModel() = default;
|
||||
|
||||
virtual std::uint64_t GetViewModelRevision() const = 0;
|
||||
};
|
||||
|
||||
class RevisionedViewModelBase : public IUIViewModel {
|
||||
public:
|
||||
std::uint64_t GetViewModelRevision() const override {
|
||||
return m_revision;
|
||||
}
|
||||
|
||||
protected:
|
||||
void MarkViewModelChanged() {
|
||||
++m_revision;
|
||||
}
|
||||
|
||||
private:
|
||||
std::uint64_t m_revision = 0;
|
||||
};
|
||||
|
||||
} // namespace UI
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user