12#include <initializer_list>
36 std::shared_ptr<const PropertyArray>
storage;
43 explicit operator bool() const noexcept {
return static_cast<bool>(
storage); }
47 std::shared_ptr<const PropertyObject>
storage;
54 explicit operator bool() const noexcept {
return static_cast<bool>(
storage); }
57bool operator==(
const PropertyArrayValue& a,
const PropertyArrayValue& b);
58bool operator!=(
const PropertyArrayValue& a,
const PropertyArrayValue& b);
59bool operator==(
const PropertyObjectValue& a,
const PropertyObjectValue& b);
60bool operator!=(
const PropertyObjectValue& a,
const PropertyObjectValue& b);
85 using Member = std::pair<std::string, PropertyValue>;
87 std::map<std::string, PropertyValue, std::less<>>
values;
93 for (
const auto& [key, value] : v)
94 values.emplace(key, value);
99 return a.values == b.values;
107 return a.values == b.values;
115 if (a.storage == b.storage)
return true;
116 if (!a.storage || !b.storage)
return false;
117 return *a.storage == *b.storage;
125 if (a.storage == b.storage)
return true;
126 if (!a.storage || !b.storage)
return false;
127 return *a.storage == *b.storage;
136 std::make_shared<const PropertyArray>(
PropertyArray{std::move(values)})};
141 std::make_shared<const PropertyArray>(
PropertyArray{values})};
146 std::make_shared<const PropertyObject>(
PropertyObject{std::move(values)})};
150 std::initializer_list<PropertyObject::Member> values) {
156 if (
auto* ptr = std::get_if<PropertyArrayValue>(&value))
162 if (
auto* ptr = std::get_if<PropertyObjectValue>(&value))
182 std::function<void(
StateTree& parent,
StateTree& child,
int old_index,
int new_index)>;
293class StateTree :
public std::enable_shared_from_this<StateTree> {
295 using Ptr = std::shared_ptr<StateTree>;
310 const std::string&
type_name()
const {
return type_name_; }
334 bool get_bool(std::string_view name,
bool default_val =
false)
const;
335 int64_t
get_int(std::string_view name, int64_t default_val = 0)
const;
336 double get_double(std::string_view name,
double default_val = 0.0)
const;
337 std::string
get_string(std::string_view name, std::string_view default_val =
"")
const;
341 bool has(std::string_view name)
const;
406 static bool move_child(StateTree& from_parent,
int from_index, StateTree& to_parent,
412 StateTree& to_parent,
int to_index);
417 int child_count()
const {
return static_cast<int>(children_.size()); }
457 StateTree*
parent()
const {
return parent_; }
479 std::string_view name = {});
579 template <
typename Fn>
struct Slot {
585 std::string type_name_;
586 std::map<std::string, PropertyValue, std::less<>> properties_;
587 std::vector<Ptr> children_;
588 StateTree* parent_ =
nullptr;
590 std::vector<Slot<TreeListener>> listeners_;
591 std::vector<Slot<ChildListener>> child_added_listeners_;
592 std::vector<Slot<ChildListener>> child_removed_listeners_;
593 std::vector<Slot<ChildMovedListener>> child_moved_listeners_;
594 std::vector<Slot<ParentChangedListener>> parent_changed_listeners_;
595 std::vector<Slot<AncestorChangedListener>> ancestor_changed_listeners_;
596 int next_listener_id_ = 0;
600 std::shared_ptr<TxState> tx_;
604 template <
typename Fn>
605 int add_slot(std::vector<Slot<Fn>>& slots, Fn fn,
ListenerDelivery delivery);
606 template <
typename Fn>
static void remove_slot(std::vector<Slot<Fn>>& slots,
int id);
607 template <
typename Fn,
typename... Args>
608 static void dispatch(std::vector<Slot<Fn>>& slots, Pass pass, Args&... args);
610 std::shared_ptr<TxState> active_tx()
const;
611 bool flush_in_progress()
const;
612 static TreeEditResult check_tx(
const StateTree* a,
const StateTree* b);
614 void apply_property(std::string_view name, std::optional<PropertyValue> value);
615 static void relocate(
const Ptr&
child, StateTree* to,
int to_index,
bool reorder_event);
616 static void place(
const Ptr&
child, StateTree* to,
int to_index);
617 static void replay(
const Edit& edit,
bool forward);
618 static void replay_log(
const std::vector<Edit>& log,
const Ptr& owner,
bool forward);
619 static void emit(Pending pending, TxState* tx);
620 static void deliver(
const Pending& pending, Pass pass);
621 static void finish(std::shared_ptr<TxState> state);
626 bool is_ancestor_of(
const StateTree* node)
const;
666 Transaction(std::shared_ptr<TxState>
state,
size_t edit_mark,
size_t pending_mark)
667 : state_(
std::move(
state)), edit_mark_(edit_mark), pending_mark_(pending_mark),
671 std::shared_ptr<TxState> state_;
672 size_t edit_mark_ = 0;
673 size_t pending_mark_ = 0;
674 bool active_ =
false;
709 return tx_.is_active();
712 return first_error_.has_value();
721 : tx_(
std::move(tx)), validator_(
std::move(validator)), reentrant_(reentrant) {}
723 TreeEditResult guard(std::initializer_list<const StateTree*> nodes)
const;
728 std::optional<TreeEditError> first_error_;
729 bool reentrant_ =
false;
769 std::weak_ptr<StateTree> source;
770 int prop_listener_id;
771 int child_added_listener_id;
772 int child_removed_listener_id;
773 int child_moved_listener_id;
778 std::vector<WiringEntry> wiring_;
779 bool attached_ =
true;
793 const T&
get()
const {
return value_; }
794 operator const T&()
const {
return value_; }
797 if (value_ != new_value) {
798 T old = std::move(value_);
799 value_ = std::move(new_value);
808 if (listeners_.size() == 1) {
809 auto fn = listeners_.front().second;
810 if (fn) fn(old, value_);
813 auto snapshot = listeners_;
814 for (
auto& [
id, fn] : snapshot) {
816 bool still_registered = std::any_of(
817 listeners_.begin(), listeners_.end(),
818 [
id =
id](
const auto& e) { return e.first == id; });
819 if (still_registered) fn(old, value_);
825 set(std::move(new_value));
833 listeners_.push_back({id, std::move(fn)});
839 std::remove_if(listeners_.begin(), listeners_.end(),
840 [
id](
auto& p) { return p.first == id; }),
846 std::vector<std::pair<int, ChangeListener>> listeners_;
Result<T, E> — either holds a T or an E, never both.
Definition result.hpp:76
ObservableValue(T initial)
Definition state_tree.hpp:791
int add_listener(ChangeListener fn)
Definition state_tree.hpp:831
ObservableValue & operator=(T new_value)
Definition state_tree.hpp:824
const T & get() const
Definition state_tree.hpp:793
void set(T new_value)
Definition state_tree.hpp:796
void remove_listener(int id)
Definition state_tree.hpp:837
std::function< void(const T &old_val, const T &new_val)> ChangeListener
Definition state_tree.hpp:829
ObservableValue()=default
Owns the listener wiring created by StateTree::clone_synced().
Definition state_tree.hpp:736
void detach()
Drop the listener subscriptions early.
const StateTree::Ptr & clone() const
The cloned tree.
Definition state_tree.hpp:746
bool is_attached() const
Whether the subscription is still active.
Definition state_tree.hpp:753
friend class StateTree
Definition state_tree.hpp:756
SyncedClone(SyncedClone &&) noexcept
RAII transaction on a StateTree.
Definition state_tree.hpp:636
Transaction(const Transaction &)=delete
Transaction & operator=(const Transaction &)=delete
bool is_active() const
Whether this object still holds an open transaction.
Definition state_tree.hpp:659
bool abort()
Revert every edit made since this transaction began, in reverse order, drop their pending notificatio...
void commit()
End this transaction and keep its edits.
friend class StateTree
Definition state_tree.hpp:664
Transaction()=default
An inert transaction (is_active() is false).
Transaction & operator=(Transaction &&other) noexcept
Commits the transaction currently held, then takes over other.
Transaction(Transaction &&other) noexcept
A transaction whose edits are checked and return TreeEditResult instead of being silently ignored.
Definition state_tree.hpp:685
ValidatedTransaction(const ValidatedTransaction &)=delete
const std::optional< TreeEditError > & first_error() const
Definition state_tree.hpp:714
ValidatedTransaction & operator=(ValidatedTransaction &&other) noexcept
bool abort()
Revert every edit and end the transaction. Returns false when inactive.
bool has_error() const
Definition state_tree.hpp:711
ValidatedTransaction & operator=(const ValidatedTransaction &)=delete
ValidatedTransaction(ValidatedTransaction &&other) noexcept=default
TreeEditResult remove(StateTree &node, std::string_view name)
TreeEditResult commit()
Commit when every edit succeeded; otherwise abort and return the first error.
bool is_active() const
Definition state_tree.hpp:708
TreeEditResult set(StateTree &node, std::string_view name, PropertyValue value)
friend class StateTree
Definition state_tree.hpp:719
TreeEditResult move_child(StateTree &from_parent, int from_index, StateTree &to_parent, int to_index)
TreeEditResult insert_child(StateTree &parent, int index, Ptr child)
TreeEditResult add_child(StateTree &parent, Ptr child)
TreeEditResult remove_child(StateTree &parent, int index)
Reactive tree node — each node has a type name, properties, and children.
Definition state_tree.hpp:293
int child_count() const
Number of children.
Definition state_tree.hpp:417
double get_double(std::string_view name, double default_val=0.0) const
Transaction begin_transaction(UndoManager *undo, std::string_view name)
Open (or join) a transaction that records inverse edits so the whole transaction becomes one named st...
void remove_listener(int id)
bool in_transaction() const
Whether a transaction is open that covers edits made through this node.
void for_each_descendant(const std::function< void(StateTree &)> &fn)
Visit every descendant (not this node) depth-first, parents before children.
static Ptr from_json(std::string_view json)
Deserialize from JSON string.
Ptr find_descendant(const std::function< bool(const StateTree &)> &predicate) const
First descendant (depth-first, parents before children, not this node) for which predicate returns tr...
bool get_bool(std::string_view name, bool default_val=false) const
Typed getters with defaults.
void remove_all_children()
Remove every child.
void remove_parent_changed_listener(int id)
TreeEditResult remove_child_checked(int index)
Checked remove_child(int).
void remove_child_added_listener(int id)
TreeEditResult insert_child_checked(int index, Ptr child)
Checked insert_child(): an out-of-range index is an error, not clamped.
Transaction begin_transaction()
Open (or join) a transaction on this node's tree.
StateTree * parent() const
Parent (may be null for root).
Definition state_tree.hpp:457
int add_child_removed_listener(ChildListener listener, ListenerDelivery delivery=ListenerDelivery::kCoalesced)
int add_child_moved_listener(ChildMovedListener listener, ListenerDelivery delivery=ListenerDelivery::kCoalesced)
Listen for reorders within this node (move_child with one parent).
int64_t get_int(std::string_view name, int64_t default_val=0) const
void add_child(Ptr child)
Add a child node (notifies child-added listeners).
PropertyValue get(std::string_view name) const
Get a property value (returns monostate if not set).
static Ptr create(std::string type_name)
Create a tree node with a type name.
Definition state_tree.hpp:302
int index_of(const StateTree *node) const
Index of node among this node's children, or -1.
bool has(std::string_view name) const
Whether a property exists.
std::vector< Ptr > find_children(std::string_view type_name) const
Find all children with matching type name.
Ptr find_child(std::string_view type_name) const
Find first child with matching type name.
Ptr child(int index) const
Get child by index.
void remove_child_moved_listener(int id)
int add_ancestor_changed_listener(AncestorChangedListener listener, ListenerDelivery delivery=ListenerDelivery::kCoalesced)
Listen for any ancestor link above this node changing.
Ptr deep_copy() const
Deep-copy this subtree (including properties and recursive children).
void remove_child_removed_listener(int id)
static bool move_child(StateTree &from_parent, int from_index, StateTree &to_parent, int to_index)
Move the child at from_index of from_parent to to_index of to_parent.
TreeEditResult set_checked(std::string_view name, PropertyValue value, const PropertyValidator &validator={})
Set a property, returning an error value instead of applying an invalid edit: an empty name,...
std::shared_ptr< StateTree > Ptr
Definition state_tree.hpp:295
int add_listener(TreeListener listener, ListenerDelivery delivery=ListenerDelivery::kCoalesced)
Listen for property changes.
int add_parent_changed_listener(ParentChangedListener listener, ListenerDelivery delivery=ListenerDelivery::kCoalesced)
Listen for this node's parent changing.
TreeEditResult add_child_checked(Ptr child)
Checked add_child(): returns an error instead of ignoring the edit.
static TreeEditResult move_child_checked(StateTree &from_parent, int from_index, StateTree &to_parent, int to_index)
Checked move_child().
std::vector< std::string > property_names() const
Get all property names (snapshot copy).
void remove_child(int index)
Remove a child by index (notifies child-removed listeners).
void remove(std::string_view name)
Remove a property.
void remove_ancestor_changed_listener(int id)
TreeEditResult remove_checked(std::string_view name)
Remove a property; kMissingProperty if it does not exist.
bool is_equivalent_to(const StateTree &other) const
Structural equality: same type name, equal properties (exact variant match, so int64 3 differs from d...
const std::string & type_name() const
Type name (e.g., "preset", "oscillator", "filter").
Definition state_tree.hpp:310
void set(std::string_view name, PropertyValue value)
Set a property value (notifies listeners).
std::string get_string(std::string_view name, std::string_view default_val="") const
ValidatedTransaction begin_validated_transaction(PropertyValidator validator={}, UndoManager *undo=nullptr, std::string_view name={})
Open (or join) a transaction whose edits are checked and return TreeEditResult.
void remove_child(StateTree *child)
Remove a specific child.
std::string to_json() const
Serialize to JSON string.
int add_child_added_listener(ChildListener listener, ListenerDelivery delivery=ListenerDelivery::kCoalesced)
Listen for child added/removed.
SyncedClone clone_synced()
void insert_child(int index, Ptr child)
Insert a child at a specific index (clamped to the valid range).
Manages undo/redo history with configurable depth.
Definition undo_manager.hpp:70
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
std::function< std::optional< std::string >( const StateTree &node, std::string_view name, const PropertyValue &value)> PropertyValidator
Property validator used by checked edits.
Definition state_tree.hpp:234
ListenerDelivery
When a listener is told about edits made inside a StateTree::Transaction.
Definition state_tree.hpp:203
@ kImmediate
Definition state_tree.hpp:205
@ kCoalesced
Definition state_tree.hpp:204
bool operator==(const PropertyArrayValue &a, const PropertyArrayValue &b)
Definition state_tree.hpp:114
runtime::Result< std::monostate, TreeEditError > TreeEditResult
Result of a checked StateTree edit: success carries no value.
Definition state_tree.hpp:230
TreeEditErrorCode
Why a checked StateTree edit was refused.
Definition state_tree.hpp:210
@ kInvalidName
Empty property name.
Definition state_tree.hpp:211
@ kNotInTransaction
Node does not belong to this transaction's tree.
Definition state_tree.hpp:218
@ kReentrantTransaction
Transaction was begun from a listener during a commit burst.
Definition state_tree.hpp:219
@ kTransactionConflict
Edit spans two trees with different open transactions.
Definition state_tree.hpp:216
@ kWouldCreateCycle
Edit would make a node its own descendant.
Definition state_tree.hpp:215
@ kNullChild
Child pointer was null.
Definition state_tree.hpp:213
@ kIndexOutOfRange
Child index outside the valid range.
Definition state_tree.hpp:214
@ kTransactionClosed
Transaction already committed or aborted.
Definition state_tree.hpp:220
@ kRejectedByValidator
A PropertyValidator refused the value.
Definition state_tree.hpp:217
@ kMissingProperty
remove of a property that does not exist.
Definition state_tree.hpp:212
std::function< void(StateTree &node, StateTree *old_parent)> ParentChangedListener
Parent-changed listener — node's parent pointer changed.
Definition state_tree.hpp:187
PropertyValue make_property_array(std::vector< PropertyValue > values)
Definition state_tree.hpp:134
std::function< void(StateTree &node, std::string_view property, const PropertyValue &old_val, const PropertyValue &new_val)> TreeListener
Listener callback — receives the tree node, property name, old value, new value.
Definition state_tree.hpp:171
std::function< void(StateTree &node)> AncestorChangedListener
Ancestor-changed listener — some ancestor link above node changed (the node itself or one of its ance...
Definition state_tree.hpp:191
const PropertyArray * get_property_array(const PropertyValue &value)
Definition state_tree.hpp:155
std::function< void(StateTree &parent, StateTree &child, int old_index, int new_index)> ChildMovedListener
Reorder listener — a child moved within the same parent from old_index to new_index (both positions a...
Definition state_tree.hpp:181
std::function< void(StateTree &parent, StateTree &child, int index)> ChildListener
Child listener — receives parent, child, index.
Definition state_tree.hpp:177
PropertyValue make_property_object(std::map< std::string, PropertyValue, std::less<> > values)
Definition state_tree.hpp:144
const PropertyObject * get_property_object(const PropertyValue &value)
Definition state_tree.hpp:161
std::variant< std::monostate, bool, int64_t, double, std::string, PropertyArrayValue, PropertyObjectValue > PropertyValue
Property value — can hold scalar values plus immutable recursive arrays and string-keyed objects.
Definition state_tree.hpp:66
bool operator!=(const PropertyArrayValue &a, const PropertyArrayValue &b)
Definition state_tree.hpp:120
Result<T, E> — success / failure union for fallible operations.
Definition state_tree.hpp:35
const PropertyArray * get() const noexcept
Definition state_tree.hpp:42
std::shared_ptr< const PropertyArray > storage
Definition state_tree.hpp:36
PropertyArrayValue()=default
PropertyArrayValue(std::shared_ptr< const PropertyArray > s)
Definition state_tree.hpp:39
Definition state_tree.hpp:76
PropertyArray(std::vector< PropertyValue > v)
Definition state_tree.hpp:80
PropertyArray(std::initializer_list< PropertyValue > v)
Definition state_tree.hpp:81
std::vector< PropertyValue > values
Definition state_tree.hpp:77
Definition state_tree.hpp:46
PropertyObjectValue()=default
const PropertyObject * get() const noexcept
Definition state_tree.hpp:53
std::shared_ptr< const PropertyObject > storage
Definition state_tree.hpp:47
PropertyObjectValue(std::shared_ptr< const PropertyObject > s)
Definition state_tree.hpp:50
Definition state_tree.hpp:84
PropertyObject(std::initializer_list< Member > v)
Definition state_tree.hpp:92
PropertyObject(std::map< std::string, PropertyValue, std::less<> > v)
Definition state_tree.hpp:90
std::map< std::string, PropertyValue, std::less<> > values
Definition state_tree.hpp:87
std::pair< std::string, PropertyValue > Member
Definition state_tree.hpp:85
Error value for a refused StateTree edit.
Definition state_tree.hpp:224
TreeEditErrorCode code
Definition state_tree.hpp:225
std::string message
Definition state_tree.hpp:226