diff --git a/src/bindx/Bind.hx b/src/bindx/Bind.hx index 547f3db..3cb7d63 100644 --- a/src/bindx/Bind.hx +++ b/src/bindx/Bind.hx @@ -10,10 +10,11 @@ using bindx.MetaUtils; #end using Lambda; +@:access(bindx.BindMacros) class Bind { - @:noUsing macro static public function bindx(field:Expr, listener:Expr):Expr { - return bind(field, listener, true); + @:noUsing macro static public function bind(field:Expr, listener:Expr):Expr { + return _bind(field, listener, true); } @:noUsing macro static public function bindTo(field:Expr, target:Expr):Expr { @@ -21,17 +22,25 @@ class Bind { return BindMacros.bindingSignalProvider.getClassFieldBindToExpr(fieldData.e, fieldData.field, target); } - @:noUsing macro static public function unbindx(field:Expr, listener:Expr):Expr { - return bind(field, listener, false); + @:noUsing macro static public function unbind(field:Expr, ?listener:Expr):Expr { + return _bind(field, listener, false); } @:noUsing macro static public function notify(field:Expr, ?oldValue:Expr, ?newValue:Expr):Expr { var fieldData = checkField(field); return BindMacros.bindingSignalProvider.getClassFieldChangedExpr(fieldData.e, fieldData.field, oldValue, newValue); } + + @:noUsing macro static public function disposeBindings(object:ExprOf):Expr { + var type = Context.typeof(object).follow(); + if (!isBindable(type.getClass())) { + Context.error('\'${object.toString()}\' must be bindx.IBindable', object.pos); + } + return BindMacros.bindingSignalProvider.getDisposeBindingsExpr(object, type); + } #if macro - static function bind(field:Expr, listener:Expr, doBind:Bool):Expr { + static function _bind(field:Expr, listener:Expr, doBind:Bool):Expr { var fieldData = checkField(field); return if (fieldData != null) { if (doBind) BindMacros.bindingSignalProvider.getClassFieldBindExpr(fieldData.e, fieldData.field, listener); @@ -71,10 +80,11 @@ class Bind { } static inline function isBindable(classType:ClassType) { - return classType.interfaces.exists(function (it) { + var res = classType.interfaces.exists(function (it) { var t = it.t.get(); return t.module == "bindx.IBindable" && t.name == "IBindable"; }); + return res || classType.superClass == null ? res : isBindable(classType.superClass.t.get()); } #end } \ No newline at end of file diff --git a/src/bindx/BindMacros.hx b/src/bindx/BindMacros.hx index f12f4e4..e5dce7b 100644 --- a/src/bindx/BindMacros.hx +++ b/src/bindx/BindMacros.hx @@ -25,12 +25,7 @@ class BindMacros { static var processed:Array = []; - static public var bindingSignalProvider:IBindingSignalProvider; - - static public function setBindingSignalProvider(value:IBindingSignalProvider) { - bindingSignalProvider = value; - return macro {}; - } + static var bindingSignalProvider:IBindingSignalProvider; macro static public function buildIBindable():Array { var type = Context.getLocalType(); @@ -67,7 +62,7 @@ class BindMacros { var inlineSetter = meta.findParam(INLINE_SETTER); if (forceParam.isNotNullAndTrue()) { if (inlineSetter != null) - Context.warning('\'$INLINE_SETTER\' ingored. \'$FORCE\' mode', inlineSetter.pos); + Context.warning('\'$INLINE_SETTER\' ignored. \'$FORCE\' mode', inlineSetter.pos); res.push(field); return; } @@ -96,7 +91,7 @@ class BindMacros { case FProp(get, set, type, expr): if (inlineSetter != null) - Context.warning('$INLINE_SETTER ingored. Setter already exist', inlineSetter.pos); + Context.warning('$INLINE_SETTER ignored. Setter already exist', inlineSetter.pos); var fieldName = field.name; var setter = fields.find(function (it) return it.name == 'set_$fieldName'); if (setter == null) return; diff --git a/src/bindx/BindSignal.hx b/src/bindx/BindSignal.hx index a6af0a4..2ef0b12 100644 --- a/src/bindx/BindSignal.hx +++ b/src/bindx/BindSignal.hx @@ -1,18 +1,18 @@ package bindx; +import bindx.BindSignal.BindSignalProvider; +import bindx.BindSignal.Signal; import haxe.macro.Expr; import haxe.macro.Type; import haxe.macro.Context; +import haxe.rtti.Meta; using bindx.MetaUtils; +using haxe.macro.Tools; +using Lambda; class BindSignalProvider implements IBindingSignalProvider { - macro static public function register() { - bindx.BindMacros.setBindingSignalProvider(new BindSignalProvider()); - return macro {}; - } - #if macro static inline var SIGNAL_POSTFIX = "Changed"; @@ -25,6 +25,7 @@ class BindSignalProvider implements IBindingSignalProvider { * default value: false */ static inline var INLINE_SIGNAL_GETTER = "inlineSignalGetter"; + static inline var BIND_SIGNAL_META = "BindSignal"; public function new() {} @@ -72,7 +73,10 @@ class BindSignalProvider implements IBindingSignalProvider { public function getClassFieldUnbindExpr(expr:Expr, field:ClassField, listener:Expr):Expr { var signalName = signalName(field.name); - return macro $expr.$signalName.remove($listener); + return if (!listener.expr.match(EConst(CIdent("null")))) + macro $expr.$signalName.remove($listener); + else + macro $expr.$signalName.removeAll(); } public function getClassFieldChangedExpr(expr:Expr, field:ClassField, oldValue:Expr, newValue:Expr):Expr { @@ -82,6 +86,19 @@ class BindSignalProvider implements IBindingSignalProvider { } return dispatchSignal(expr, field.name, args, hasLazy(field.bindableMeta())); } + + public function getDisposeBindingsExpr(expr:ExprOf, type:Type):Expr { + return macro { + var meta = haxe.rtti.Meta.getFields($p{type.toString().split(".")}); + if (meta != null) for (m in std.Reflect.fields(meta)) { + var data:Dynamic = std.Reflect.field(meta, m); + if (std.Reflect.hasField(data, $v{BIND_SIGNAL_META})) { + var signal:bindx.BindSignal.Signal = cast Reflect.field($expr, m); + if (signal != null) signal.dispose(); + } + } + } + } function generateSignal(field:Field, type:ComplexType, builder:Expr, res:Array) { var signalName = signalName(field.name); @@ -93,14 +110,15 @@ class BindSignalProvider implements IBindingSignalProvider { res.push({ name: signalPrivateName, kind: FVar(type, null), - pos: field.pos + pos: field.pos, + meta: [ { name:BIND_SIGNAL_META, pos:field.pos } ] }); res.push({ name: signalName, kind: FProp("get", "never", type, null), pos: field.pos, - access: [APublic] + access: [APrivate], }); var getter = macro function foo() { @@ -126,7 +144,8 @@ class BindSignalProvider implements IBindingSignalProvider { name: signalName, kind: FProp("default", "null", type, builder), pos: field.pos, - access: [APublic] + access: [APrivate], + meta: [ { name:BIND_SIGNAL_META, pos:field.pos } ] }); } } @@ -203,7 +222,7 @@ class Signal { @:expose inline function checkLock() { if (lock > 0) { listeners = listeners.copy(); - lock --; + lock = 0; } } } \ No newline at end of file diff --git a/src/bindx/IBindingSignalProvider.hx b/src/bindx/IBindingSignalProvider.hx index d9f3bae..eed6abd 100644 --- a/src/bindx/IBindingSignalProvider.hx +++ b/src/bindx/IBindingSignalProvider.hx @@ -12,5 +12,6 @@ interface IBindingSignalProvider { function getClassFieldBindToExpr(expr:Expr, field:ClassField, target:Expr):Expr; function getClassFieldUnbindExpr(expr:Expr, field:ClassField, listener:Expr):Expr; function getClassFieldChangedExpr(expr:Expr, field:ClassField, oldValue:Expr, newValue:Expr):Expr; + function getDisposeBindingsExpr(expr:ExprOf, type:Type):Expr; #end } diff --git a/test/BaseTest.hx b/test/BaseTest.hx index 246b47e..8c667e8 100644 --- a/test/BaseTest.hx +++ b/test/BaseTest.hx @@ -1,5 +1,6 @@ package ; +import bindx.Bind; import haxe.unit.TestCase; class BaseTest extends TestCase { @@ -12,13 +13,13 @@ class BaseTest extends TestCase { var b = new Bindable1(); b.str = "a"; var callNum = 0; - b.strChanged.add(function (from, to) { + Bind.bind(b.str, function (from, to) { assertEquals(from, "a"); assertEquals(to, "b"); callNum ++; }); - bindx.Bind.bindx(b.str, function (from, to) { + bindx.Bind.bind(b.str, function (from, to) { assertEquals(from, "a"); assertEquals(to, "b"); callNum ++; @@ -37,13 +38,13 @@ class BaseTest extends TestCase { callNum ++; } - b.strChanged.add(listener); - bindx.Bind.bindx(b.str, listener); + bindx.Bind.bind(b.str, listener); + bindx.Bind.bind(b.str, listener); b.str = ""; assertEquals(callNum, 1); - b.strChanged.add(listener); - bindx.Bind.unbindx(b.str, listener); + bindx.Bind.bind(b.str, listener); + bindx.Bind.unbind(b.str, listener); b.str = "1"; assertEquals(callNum, 1); } @@ -53,19 +54,19 @@ class BaseTest extends TestCase { b.str = null; var callNum = 0; - bindx.Bind.bindx(b.str, function (_, _) callNum++); - bindx.Bind.bindx(b.str, function (_, _) callNum++); + bindx.Bind.bind(b.str, function (_, _) callNum++); + bindx.Bind.bind(b.str, function (_, _) callNum++); b.str = ""; assertEquals(callNum, 2); - b.strChanged.removeAll(); + bindx.Bind.unbind(b.str); b.str = "1"; assertEquals(callNum, 2); - - b.strChanged.dispose(); + + Bind.disposeBindings(b); var addError = false; try { - b.strChanged.add(function (_, _) {}); + Bind.bind(b.str, function (_, _) {}); } catch (e:Dynamic) { addError = true; } @@ -81,8 +82,9 @@ class BaseTest extends TestCase { assertEquals(to, "2"); callNum ++; } - b.strChanged.add(listener); - b.strChanged.dispatch("1", "2"); + + Bind.bind(b.str, listener); + bindx.Bind.notify(b.str, "1", "2"); assertEquals(callNum, 1); bindx.Bind.notify(b.str, "1", "2"); @@ -93,7 +95,7 @@ class BaseTest extends TestCase { var b = new Bindable1(); b.str = null; var callNum = 0; - b.bindChanged.add(function () callNum++); + Bind.bind(b.bind, function () callNum++); b.i = 10; assertEquals(callNum, 1); diff --git a/test/InheritanceTest.hx b/test/InheritanceTest.hx index 6866173..de51c86 100644 --- a/test/InheritanceTest.hx +++ b/test/InheritanceTest.hx @@ -1,6 +1,7 @@ package ; import bindx.IBindable; +import bindx.Bind; class InheritanceTest extends haxe.unit.TestCase { public function new() { @@ -12,7 +13,7 @@ class InheritanceTest extends haxe.unit.TestCase { c.i = 0; c.s = "0"; var iChanged = 0; - c.iChanged.add(function (from, to) { + Bind.bind(c.i, function (from, to) { assertEquals(from, 0); assertEquals(to, 1); iChanged ++; @@ -21,7 +22,7 @@ class InheritanceTest extends haxe.unit.TestCase { assertEquals(iChanged, 1); var sChanged = 0; - c.sChanged.add(function (from, to) { + Bind.bind(c.s, function (from, to) { assertEquals(from, "0"); assertEquals(to, "1"); sChanged ++; diff --git a/test/TestProperty.hx b/test/TestProperty.hx index bb00e80..83bdd56 100644 --- a/test/TestProperty.hx +++ b/test/TestProperty.hx @@ -1,5 +1,7 @@ package ; +import bindx.Bind; + class TestProperty extends haxe.unit.TestCase { public function new() { super(); @@ -10,7 +12,7 @@ class TestProperty extends haxe.unit.TestCase { p.s = "1"; var callNum = 0; - p.sChanged.add(function (from, to) { + Bind.bind(p.s, function (from, to) { assertEquals(from, "1"); assertEquals(to, ""); callNum ++; @@ -18,9 +20,9 @@ class TestProperty extends haxe.unit.TestCase { p.s = null; - p.sChanged.removeAll(); + Bind.unbind(p.s); - p.sChanged.add(function (from, to) { + Bind.bind(p.s, function (from, to) { assertEquals(from, ""); assertEquals(to, "1"); callNum ++;