| #ifndef _RIVE_SEMANTIC_NODE_HPP_ | |
| #define _RIVE_SEMANTIC_NODE_HPP_ | |
| #include "rive/math/aabb.hpp" | |
| #include "rive/refcnt.hpp" | |
| #include <cstdint> | |
| #include <string> | |
| #include <vector> | |
| namespace rive | |
| { | |
| class Artboard; | |
| class Core; | |
| class SemanticManager; | |
| // Ref-counted tree node holding all data needed by platform accessibility. | |
| // Created lazily by SemanticData::semanticNode() and registered with | |
| // SemanticManager via addChild(). The manager flattens these into | |
| // SemanticsDiffNode structs for platform consumption. | |
| // | |
| // Ids are scoped to the owning SemanticManager: a node is constructed | |
| // with id == 0 (unassigned) and receives a manager-local id on first | |
| // addChild(). This avoids collisions when multiple independent | |
| // SemanticManagers (e.g. two unrelated artboards in the same process) | |
| // outlive a single process-global counter. | |
| // | |
| // Callers that want a deterministic id (tests, serialization round-trips) | |
| // may construct with an explicit id; SemanticManager will preserve it and | |
| // bump its watermark to avoid future collisions. | |
| // | |
| // Access model: | |
| // - Authored-value mutators (role, label, value, hint, headingLevel, | |
| // stateFlags, traitFlags, bounds) are public: SemanticData writes | |
| // them from its property-change handlers, Artboard sets them on | |
| // boundary nodes, tests use them to hand-build trees. | |
| // - Construction-time back-refs (coreOwner, semanticData, isBoundaryNode) | |
| // are public: set once at node creation, never again. | |
| // - Structural mutators (parent, mutable children, manager, | |
| // id assignment) live behind `friend class SemanticManager`. The | |
| // manager is the only code allowed to wire the tree or rebind a | |
| // node to a different manager. | |
| class SemanticNode : public RefCnt<SemanticNode> | |
| { | |
| public: | |
| explicit SemanticNode(uint32_t id = 0) : m_id(id) {} | |
| uint32_t id() const { return m_id; } | |
| SemanticNode* parent() const { return m_parent; } | |
| const std::vector<rcp<SemanticNode>>& children() const | |
| { | |
| return m_children; | |
| } | |
| void role(uint32_t value) { m_role = value; } | |
| uint32_t role() const { return m_role; } | |
| void stateFlags(uint32_t value) { m_stateFlags = value; } | |
| uint32_t stateFlags() const { return m_stateFlags; } | |
| void label(const std::string& value) { m_label = value; } | |
| const std::string& label() const { return m_label; } | |
| void value(const std::string& v) { m_value = v; } | |
| const std::string& value() const { return m_value; } | |
| void hint(const std::string& v) { m_hint = v; } | |
| const std::string& hint() const { return m_hint; } | |
| void headingLevel(uint32_t v) { m_headingLevel = v; } | |
| uint32_t headingLevel() const { return m_headingLevel; } | |
| AABB bounds() const { return m_bounds; } | |
| void bounds(const AABB& value) { m_bounds = value; } | |
| void traitFlags(uint32_t value) { m_traitFlags = value; } | |
| uint32_t traitFlags() const { return m_traitFlags; } | |
| Core* coreOwner() const { return m_coreOwner; } | |
| void coreOwner(Core* value) { m_coreOwner = value; } | |
| SemanticManager* manager() const { return m_manager; } | |
| /// Boundary nodes are structural-only nodes inserted at artboard | |
| /// boundaries. They are skipped during flattening (children reparented | |
| /// upward) but participate in tree management for O(1) subtree | |
| /// collapse/uncollapse and targeted bounds dirtying. | |
| bool isBoundaryNode() const { return m_isBoundaryNode; } | |
| void isBoundaryNode(bool value) { m_isBoundaryNode = value; } | |
| /// For boundary nodes, the nested Artboard whose region this boundary | |
| /// represents. The boundary's bounds are the artboard's rect mapped | |
| /// through its rootTransform into root space. | |
| Artboard* boundaryArtboard() const { return m_boundaryArtboard; } | |
| void boundaryArtboard(Artboard* value) { m_boundaryArtboard = value; } | |
| /// Back-pointer to the SemanticData that owns this node. Null for | |
| /// boundary nodes. Used for direct collapse without walking the | |
| /// component hierarchy. | |
| class SemanticData* semanticData() const { return m_semanticData; } | |
| void semanticData(class SemanticData* value) { m_semanticData = value; } | |
| private: | |
| // SemanticManager is the only class allowed to rewire the tree, | |
| // assign/reassign ids, or rebind a node to a different manager. | |
| friend class SemanticManager; | |
| void id(uint32_t value) { m_id = value; } | |
| void parent(SemanticNode* value) { m_parent = value; } | |
| std::vector<rcp<SemanticNode>>& mutableChildren() { return m_children; } | |
| void manager(SemanticManager* m) { m_manager = m; } | |
| uint32_t m_id; | |
| SemanticNode* m_parent = nullptr; | |
| std::vector<rcp<SemanticNode>> m_children; | |
| uint32_t m_role = 0; | |
| uint32_t m_stateFlags = 0; | |
| std::string m_label = ""; | |
| std::string m_value = ""; | |
| std::string m_hint = ""; | |
| uint32_t m_headingLevel = 0; | |
| // Default to "no bounds" rather than (0,0,0,0) which looks like a | |
| // valid position at the origin. forExpansion() has minX > maxX so | |
| // isEmptyOrNaN() returns true. | |
| AABB m_bounds = AABB::forExpansion(); | |
| uint32_t m_traitFlags = 0; | |
| Core* m_coreOwner = nullptr; | |
| SemanticManager* m_manager = nullptr; | |
| bool m_isBoundaryNode = false; | |
| class SemanticData* m_semanticData = nullptr; | |
| Artboard* m_boundaryArtboard = nullptr; | |
| }; | |
| } // namespace rive | |
| #endif |