Archive XCUI subplan 04

This commit is contained in:
2026-04-04 19:04:28 +08:00
parent 611ca705c8
commit c2cb2e5914
11 changed files with 866 additions and 21 deletions

View 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

View 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

View 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

View 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

View 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