Skip to content

Commit 226c545

Browse files
committed
e
1 parent 8e63095 commit 226c545

File tree

5 files changed

+1648
-63
lines changed

5 files changed

+1648
-63
lines changed

source/Main.hx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ class Main extends Sprite
269269
trace(testArray.get("test2"));
270270
trace(testArray["test"]);
271271

272+
272273
// 'You can\'t put variable expressions in COMMENTS, silly!'.NativeComment(true);
273274
// trace('testArray: $testArray');
274275
// 'testArray = $testArray'.NativeTrace(true, false);

source/yutautil/Observable.hx

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package yutautil;
2+
3+
import haxe.macro.Context;
4+
import haxe.macro.Expr;
5+
import haxe.macro.Type;
6+
7+
// Callback types for different kinds of changes
8+
typedef ObjectChangeCallback<T> = (oldValue:T, newValue:T) -> Void;
9+
typedef FieldChangeCallback<T> = (fieldName:String, oldValue:Dynamic, newValue:Dynamic) -> Void;
10+
typedef UsageCallback<T> = (object:T, operation:String) -> Void;
11+
12+
// Internal implementation class for Observable
13+
private class ObservableImpl<T> {
14+
public var value:T;
15+
public var objectChangeCallbacks:Array<ObjectChangeCallback<T>>;
16+
public var fieldChangeCallbacks:Array<FieldChangeCallback<T>>;
17+
public var usageCallbacks:Array<UsageCallback<T>>;
18+
public var isTracking:Bool;
19+
20+
// Store original field values for comparison
21+
public var originalFields:Map<String, Dynamic>;
22+
23+
public function new(value:T) {
24+
this.value = value;
25+
this.objectChangeCallbacks = [];
26+
this.fieldChangeCallbacks = [];
27+
this.usageCallbacks = [];
28+
this.isTracking = true;
29+
this.originalFields = new Map();
30+
31+
// Initialize field tracking
32+
if (value != null) {
33+
captureFields();
34+
}
35+
}
36+
37+
public function captureFields():Void {
38+
if (value == null) return;
39+
40+
// Use reflection to capture current field values
41+
var fields = Reflect.fields(value);
42+
for (field in fields) {
43+
originalFields.set(field, Reflect.field(value, field));
44+
}
45+
}
46+
47+
public function checkFieldChanges():Void {
48+
if (value == null || !isTracking) return;
49+
50+
var fields = Reflect.fields(value);
51+
for (field in fields) {
52+
var currentValue = Reflect.field(value, field);
53+
var originalValue = originalFields.get(field);
54+
55+
if (currentValue != originalValue) {
56+
// Field changed
57+
for (callback in fieldChangeCallbacks) {
58+
callback(field, originalValue, currentValue);
59+
}
60+
originalFields.set(field, currentValue);
61+
}
62+
}
63+
}
64+
65+
public function setValue(newValue:T):Void {
66+
if (value == newValue) return;
67+
68+
var oldValue = value;
69+
value = newValue;
70+
71+
// Trigger object change callbacks
72+
for (callback in objectChangeCallbacks) {
73+
callback(oldValue, newValue);
74+
}
75+
76+
// Update field tracking
77+
if (newValue != null) {
78+
captureFields();
79+
}
80+
}
81+
82+
public function triggerUsage(operation:String):Void {
83+
if (!isTracking) return;
84+
85+
for (callback in usageCallbacks) {
86+
callback(value, operation);
87+
}
88+
89+
// Check for field changes after usage
90+
checkFieldChanges();
91+
}
92+
93+
public function onObjectChange(callback:ObjectChangeCallback<T>):ObservableImpl<T> {
94+
objectChangeCallbacks.push(callback);
95+
return this;
96+
}
97+
98+
public function onFieldChange(callback:FieldChangeCallback<T>):ObservableImpl<T> {
99+
fieldChangeCallbacks.push(callback);
100+
return this;
101+
}
102+
103+
public function onUsage(callback:UsageCallback<T>):ObservableImpl<T> {
104+
usageCallbacks.push(callback);
105+
return this;
106+
}
107+
108+
public function stopTracking():Void {
109+
isTracking = false;
110+
}
111+
112+
public function startTracking():Void {
113+
isTracking = true;
114+
if (value != null) {
115+
captureFields();
116+
}
117+
}
118+
}
119+
120+
// Abstract wrapper for Observable functionality
121+
abstract Observable<T>(ObservableImpl<T>) {
122+
123+
public inline function new(value:T) {
124+
this = new ObservableImpl<T>(value);
125+
}
126+
127+
// Allow implicit conversion from any value to Observable
128+
@:from
129+
public static inline function fromValue<T>(value:T):Observable<T> {
130+
return new Observable<T>(value);
131+
}
132+
133+
// Allow using as the wrapped type
134+
@:to
135+
public inline function toValue():T {
136+
this.triggerUsage("access");
137+
return this.value;
138+
}
139+
140+
// Allow implicit conversion to the wrapped type
141+
@:op(A)
142+
public inline function get():T {
143+
this.triggerUsage("get");
144+
return this.value;
145+
}
146+
147+
// Manual set method (since assignment operator overloading isn't supported)
148+
public inline function set(newValue:T):T {
149+
this.setValue(newValue);
150+
return newValue;
151+
}
152+
153+
// Field access method
154+
public inline function getField(fieldName:String):Dynamic {
155+
this.triggerUsage("field_access." + fieldName);
156+
return Reflect.field(this.value, fieldName);
157+
}
158+
159+
// Field assignment method
160+
public inline function setField(fieldName:String, value:Dynamic):Dynamic {
161+
var oldValue = Reflect.field(this.value, fieldName);
162+
Reflect.setField(this.value, fieldName, value);
163+
164+
// Trigger field change callbacks
165+
for (callback in this.fieldChangeCallbacks) {
166+
callback(fieldName, oldValue, value);
167+
}
168+
this.originalFields.set(fieldName, value);
169+
this.triggerUsage("field_set." + fieldName);
170+
return value;
171+
}
172+
173+
// Array access methods (only for array-like types)
174+
public inline function getAt(index:Int):Dynamic {
175+
this.triggerUsage("array_access");
176+
return cast(this.value, Array<Dynamic>)[index];
177+
}
178+
179+
public inline function setAt(index:Int, value:Dynamic):Dynamic {
180+
this.triggerUsage("array_set");
181+
return cast(this.value, Array<Dynamic>)[index] = value;
182+
}
183+
184+
// Callback registration methods
185+
public inline function onObjectChange(callback:ObjectChangeCallback<T>):Observable<T> {
186+
this.onObjectChange(callback);
187+
return cast this;
188+
}
189+
190+
public inline function onFieldChange(callback:FieldChangeCallback<T>):Observable<T> {
191+
this.onFieldChange(callback);
192+
return cast this;
193+
}
194+
195+
public inline function onUsage(callback:UsageCallback<T>):Observable<T> {
196+
this.onUsage(callback);
197+
return cast this;
198+
}
199+
200+
// Control methods
201+
public inline function stopTracking():Void {
202+
this.stopTracking();
203+
}
204+
205+
public inline function startTracking():Void {
206+
this.startTracking();
207+
}
208+
209+
public inline function checkFieldChanges():Void {
210+
this.checkFieldChanges();
211+
}
212+
213+
// Utility methods
214+
public inline function getValue():T {
215+
return this.value;
216+
}
217+
218+
public inline function isTracking():Bool {
219+
return this.isTracking;
220+
}
221+
}

0 commit comments

Comments
 (0)