1+ package flixel .util ;
2+
3+ #if macro
4+ import haxe .macro .Expr ;
5+ #else
6+ import flixel .util .FlxDestroyUtil .IFlxDestroyable ;
7+
8+ typedef FlxSignal = FlxTypedSignal <Void -> Void >;
9+
10+ @:multiType
11+ abstract FlxTypedSignal <T >(IFlxSignal <T >)
12+ {
13+ public var dispatch (get , never ): T ;
14+
15+ public function new ();
16+
17+ public inline function add (listener : T ): Void
18+ {
19+ this .add (listener );
20+ }
21+
22+ public inline function addOnce (listener : T ): Void
23+ {
24+ this .addOnce (listener );
25+ }
26+
27+ public inline function remove (listener : T ): Void
28+ {
29+ this .remove (listener );
30+ }
31+
32+ public inline function has (listener : T ): Bool
33+ {
34+ return this .has (listener );
35+ }
36+
37+ public inline function removeAll (): Void
38+ {
39+ this .removeAll ();
40+ }
41+
42+ public inline function destroy (): Void
43+ {
44+ this .destroy ();
45+ }
46+
47+ inline function get_dispatch (): T
48+ {
49+ return this .dispatch ;
50+ }
51+
52+ @:to
53+ static inline function toSignal0 (signal : IFlxSignal <Void -> Void >): FlxSignal0
54+ {
55+ return new FlxSignal0 ();
56+ }
57+
58+ @:to
59+ static inline function toSignal1 <T1 >(signal : IFlxSignal <T1 -> Void >): FlxSignal1 <T1 >
60+ {
61+ return new FlxSignal1 ();
62+ }
63+
64+ @:to
65+ static inline function toSignal2 <T1 , T2 >(signal : IFlxSignal <T1 -> T2 -> Void >): FlxSignal2 <T1 , T2 >
66+ {
67+ return new FlxSignal2 ();
68+ }
69+
70+ @:to
71+ static inline function toSignal3 <T1 , T2 , T3 >(signal : IFlxSignal <T1 -> T2 -> T3 -> Void >): FlxSignal3 <T1 , T2 , T3 >
72+ {
73+ return new FlxSignal3 ();
74+ }
75+
76+ @:to
77+ static inline function toSignal4 <T1 , T2 , T3 , T4 >(signal : IFlxSignal <T1 -> T2 -> T3 -> T4 -> Void >): FlxSignal4 <T1 , T2 , T3 , T4 >
78+ {
79+ return new FlxSignal4 ();
80+ }
81+ }
82+
83+ private class FlxSignalHandler <T > implements IFlxDestroyable
84+ {
85+ public var listener : T ;
86+ public var dispatchOnce (default , null ): Bool = false ;
87+
88+ public function new (listener : T , dispatchOnce : Bool )
89+ {
90+ this .listener = listener ;
91+ this .dispatchOnce = dispatchOnce ;
92+ }
93+
94+ public function destroy ()
95+ {
96+ listener = null ;
97+ }
98+ }
99+
100+ private class FlxBaseSignal <T > implements IFlxSignal <T >
101+ {
102+ /**
103+ * Typed function reference used to dispatch this signal.
104+ */
105+ public var dispatch : T ;
106+
107+ var handlers : Array <FlxSignalHandler <T >>;
108+ var pendingRemove : Array <FlxSignalHandler <T >>;
109+ var processingListeners : Bool = false ;
110+
111+ public function new ()
112+ {
113+ handlers = [];
114+ pendingRemove = [];
115+ }
116+
117+ public function add (listener : T )
118+ {
119+ if (listener != null )
120+ registerListener (listener , false );
121+ }
122+
123+ public function addOnce (listener : T ): Void
124+ {
125+ if (listener != null )
126+ registerListener (listener , true );
127+ }
128+
129+ public function remove (listener : T ): Void
130+ {
131+ if (listener != null )
132+ {
133+ var handler = getHandler (listener );
134+ if (handler != null )
135+ {
136+ removeHandler (handler );
137+ }
138+ }
139+ }
140+
141+ inline function removeHandler (handler : FlxSignalHandler <T >): Void
142+ {
143+ if (processingListeners )
144+ pendingRemove .push (handler )
145+ else
146+ {
147+ handlers .remove (handler );
148+ handler .destroy ();
149+ }
150+ }
151+
152+ public function has (listener : T ): Bool
153+ {
154+ if (listener == null )
155+ return false ;
156+ return getHandler (listener ) != null ;
157+ }
158+
159+ public inline function removeAll (): Void
160+ {
161+ FlxDestroyUtil .destroyArray (handlers );
162+ }
163+
164+ public function destroy (): Void
165+ {
166+ removeAll ();
167+ handlers = null ;
168+ pendingRemove = null ;
169+ }
170+
171+ function registerListener (listener : T , dispatchOnce : Bool ): FlxSignalHandler <T >
172+ {
173+ var handler = getHandler (listener );
174+
175+ if (handler == null )
176+ {
177+ handler = new FlxSignalHandler <T >(listener , dispatchOnce );
178+ handlers .push (handler );
179+ return handler ;
180+ }
181+ else
182+ {
183+ // If the listener was previously added, definitely don't add it again.
184+ // But throw an exception if their once values differ.
185+ if (handler .dispatchOnce != dispatchOnce )
186+ throw " You cannot addOnce() then add() the same listener without removing the relationship first." ;
187+ else
188+ return handler ;
189+ }
190+ }
191+
192+ function getHandler (listener : T ): FlxSignalHandler <T >
193+ {
194+ for (handler in handlers )
195+ {
196+ if (#if (neko || hl) // simply comparing the functions doesn't do the trick on these targets
197+ Reflect .compareMethods (handler .listener , listener ) #else handler .listener == listener #end)
198+ {
199+ return handler ; // Listener was already registered.
200+ }
201+ }
202+ return null ; // Listener not yet registered.
203+ }
204+ }
205+
206+ private class FlxSignal0 extends FlxBaseSignal <Void -> Void >
207+ {
208+ public function new ()
209+ {
210+ super ();
211+ this .dispatch = dispatch0 ;
212+ }
213+
214+ public function dispatch0 (): Void
215+ {
216+ Macro .buildDispatch ();
217+ }
218+ }
219+
220+ private class FlxSignal1 <T1 > extends FlxBaseSignal <T1 -> Void >
221+ {
222+ public function new ()
223+ {
224+ super ();
225+ this .dispatch = dispatch1 ;
226+ }
227+
228+ public function dispatch1 (value1 : T1 ): Void
229+ {
230+ Macro .buildDispatch (value1 );
231+ }
232+ }
233+
234+ private class FlxSignal2 <T1 , T2 > extends FlxBaseSignal <T1 -> T2 -> Void >
235+ {
236+ public function new ()
237+ {
238+ super ();
239+ this .dispatch = dispatch2 ;
240+ }
241+
242+ public function dispatch2 (value1 : T1 , value2 : T2 ): Void
243+ {
244+ Macro .buildDispatch (value1 , value2 );
245+ }
246+ }
247+
248+ private class FlxSignal3 <T1 , T2 , T3 > extends FlxBaseSignal <T1 -> T2 -> T3 -> Void >
249+ {
250+ public function new ()
251+ {
252+ super ();
253+ this .dispatch = dispatch3 ;
254+ }
255+
256+ public function dispatch3 (value1 : T1 , value2 : T2 , value3 : T3 ): Void
257+ {
258+ Macro .buildDispatch (value1 , value2 , value3 );
259+ }
260+ }
261+
262+ private class FlxSignal4 <T1 , T2 , T3 , T4 > extends FlxBaseSignal <T1 -> T2 -> T3 -> T4 -> Void >
263+ {
264+ public function new ()
265+ {
266+ super ();
267+ this .dispatch = dispatch4 ;
268+ }
269+
270+ public function dispatch4 (value1 : T1 , value2 : T2 , value3 : T3 , value4 : T4 ): Void
271+ {
272+ Macro .buildDispatch (value1 , value2 , value3 , value4 );
273+ }
274+ }
275+
276+ interface IFlxSignal <T > extends IFlxDestroyable
277+ {
278+ var dispatch : T ;
279+ function add (listener : T ): Void ;
280+ function addOnce (listener : T ): Void ;
281+ function remove (listener : T ): Void ;
282+ function removeAll (): Void ;
283+ function destroy (): Void ;
284+ function has (listener : T ): Bool ;
285+ }
286+ #end
287+
288+ private class Macro
289+ {
290+ public static macro function buildDispatch (exprs : Array <Expr >): Expr
291+ {
292+ return macro
293+ {
294+ processingListeners = true ;
295+ for (handler in handlers )
296+ {
297+ handler .listener ($a {exprs });
298+
299+ if (handler .dispatchOnce )
300+ removeHandler (handler );
301+ }
302+
303+ processingListeners = false ;
304+
305+ for (handler in pendingRemove )
306+ {
307+ removeHandler (handler );
308+ }
309+ if (pendingRemove .length > 0 )
310+ pendingRemove = [];
311+ }
312+ }
313+ }
0 commit comments