Toast Engine
Loading...
Searching...
No Matches
signals.inl
1#pragma once
2#include "signals.hpp"
3
5#include <tracy/Tracy.hpp>
6
7namespace signals {
8
9template<typename... Args>
10template<typename F>
11 requires(std::is_invocable_r_v<void, F, Args...> || std::is_invocable_r_v<void, F>)
12inline void Signal<Args...>::connect(toast::Node& node, F&& cb) {
13 callback_t wrapper = [f = std::forward<F>(cb)](Args... args) mutable {
14 if constexpr (std::is_invocable_r_v<void, F, Args...>) {
15 f(args...);
16 } else if constexpr (std::is_invocable_r_v<void, F>) {
17 f();
18 }
19 };
20 modify([&](ConnectionList& list) {
21 list.push_back(
22 {.uid = node.uid(),
23 .identifier = "Unnamed",
24 .source = ConnectionSource::cpp,
25 .node = toast::Box<toast::Node>(node),
26 .cb = std::move(wrapper)}
27 );
28 });
29}
30
31template<typename... Args>
32inline void
33 Signal<Args...>::connect(toast::Node& node, std::string_view identifier, ConnectionSource source, bool forwards_args) {
34 callback_t wrapper;
35 if constexpr (std::is_same_v<std::tuple<Args...>, std::tuple<DynamicArgs>>) {
36 wrapper =
37 [iden = std::string(identifier), box = toast::Box<toast::Node>(node), forwards_args](const DynamicArgs& args) mutable {
38 if (forwards_args && !args.values.empty()) {
39 toast::_detail::callNodeScripts(&*box, iden, args.values);
40 } else {
41 box->call(iden);
42 }
43 };
44 } else if (forwards_args) {
45 wrapper = [iden = std::string(identifier), box = toast::Box<toast::Node>(node)](const Args&... args) mutable {
46 box->call(iden, args...);
47 };
48 } else {
49 wrapper = [iden = std::string(identifier), box = toast::Box<toast::Node>(node)](const Args&...) mutable { box->call(iden); };
50 }
51 modify([&](ConnectionList& list) {
52 list.push_back(
53 {.uid = node.uid(),
54 .identifier = std::string(identifier),
55 .source = source,
56 .forwards_args = forwards_args,
57 .node = toast::Box<toast::Node>(node),
58 .cb = std::move(wrapper)}
59 );
60 });
61}
62
63template<typename... Args>
64inline void Signal<Args...>::disconnect(toast::Node& node, std::string_view identifier, ConnectionSource source) {
65 modify([&](ConnectionList& list) {
66 std::erase_if(list, [&](const Connection& listener) {
67 return listener.node == toast::Box<toast::Node>(node) && listener.identifier == identifier && listener.source == source;
68 });
69 });
70}
71
72template<typename... Args>
73inline void Signal<Args...>::clear(ConnectionSource source) {
74 modify([source](ConnectionList& list) {
75 std::erase_if(list, [source](const Connection& listener) { return listener.source == source; });
76 });
77}
78
79template<typename... Args>
80inline auto Signal<Args...>::connections() const -> std::vector<ConnectionInfo> {
81 const auto current = snapshot();
82 std::vector<ConnectionInfo> result;
83 result.reserve(current->size());
84 for (const auto& listener : *current) {
85 result.push_back({listener.uid, listener.identifier, listener.source, listener.forwards_args});
86 }
87 return result;
88}
89
90template<typename... Args>
91inline void Signal<Args...>::fire(const Args&... args) {
92 ZoneScoped;
93 // Handlers may connect, disconnect or fire this signal again
94 const auto current = snapshot();
95 bool has_dead = false;
96 for (const auto& listener : *current) {
97 if (!listener.node) {
98 has_dead = true;
99 continue;
100 }
101 listener.cb(args...);
102 }
103 if (has_dead) {
104 modify([](ConnectionList& list) { std::erase_if(list, [](const Connection& listener) { return !listener.node; }); });
105 }
106}
107
108template<typename... Args>
109template<typename NodeType, auto MemberPtr>
110inline auto Signal<Args...>::get(void* signal) -> std::vector<ConnectionInfo> {
111 if (!signal) {
112 return {};
113 }
114 auto* node = static_cast<NodeType*>(signal);
115 const auto& target_signal = node->*MemberPtr;
116 return target_signal.connections();
117}
118
119template<typename... Args>
120template<typename NodeType, auto MemberPtr>
121inline void Signal<Args...>::connect(
122 void* signal, toast::Node& target, std::string_view identifier, ConnectionSource source, bool forwards_args
123) {
124 if (!signal) {
125 return;
126 }
127 auto& target_signal = static_cast<NodeType*>(signal)->*MemberPtr;
128 const auto connections = target_signal.connections();
129 const bool exists = std::ranges::any_of(connections, [&](const ConnectionInfo& connection) {
130 return connection.target == target.uid() && connection.function == identifier && connection.source == source;
131 });
132 if (!exists) {
133 target_signal.connect(target, identifier, source, forwards_args);
134 }
135}
136
137template<typename... Args>
138template<typename NodeType, auto MemberPtr>
139inline void Signal<Args...>::disconnect(void* signal, toast::Node& target, std::string_view identifier, ConnectionSource source) {
140 if (!signal) {
141 return;
142 }
143 auto& target_signal = static_cast<NodeType*>(signal)->*MemberPtr;
144 target_signal.disconnect(target, identifier, source);
145}
146
147template<typename... Args>
148template<typename NodeType, auto MemberPtr>
149inline void Signal<Args...>::clear(void* signal, ConnectionSource source) {
150 if (!signal) {
151 return;
152 }
153 auto& target_signal = static_cast<NodeType*>(signal)->*MemberPtr;
154 target_signal.clear(source);
155}
156
157template<typename... Args>
158template<typename NodeType, auto MemberPtr>
159inline auto Signal<Args...>::fire(void* signal, std::span<const std::any> args) -> bool {
160 ZoneScoped;
161 if (!signal || args.size() != sizeof...(Args)) {
162 return false;
163 }
164 return [&]<size_t... I>(std::index_sequence<I...>) {
165 if ((std::any_cast<std::decay_t<Args>>(&args[I]) && ...)) {
166 (static_cast<NodeType*>(signal)->*MemberPtr).fire(*std::any_cast<std::decay_t<Args>>(&args[I])...);
167 return true;
168 }
169 return false;
170 }(std::index_sequence_for<Args...> {});
171}
172
173}
auto uid() const noexcept -> const UID &
Stable unique identifier, assigned at construction or deserialization. Never changes.
Definition node.cpp:37
Base object type for the scene graph.
NodeType
Definition node.hpp:62
Definition signal_types.hpp:22
Definition signal_types.hpp:18