typesmouseeventhaxerttiopenfl

Getting child targets for mouse click event in OpenFL


I have a stage with multiple child object of different types. I have an event listener on the stage. I'd like to get the target child object on a mouse click so I can handle clicks on different object types.

Something along the lines of:

stage.addEventListener(MouseEvent.CLICK, onClick);

function onClick(e:Event):Void
{
    var target = e.target
    // if target type is Foo
    //     target.aFooMethod();
    // else if target type is Bar
    //     target.aBarMethod();

}

Whats the correct way of doing this? Tracing e.target seems to print the correct object type but I am unable to call any of the objects methods.

I vaguely remember in actionscript 3 being able to use target.name but return null in this case.


Solution

  • It sounds like your question is mostly about runtime type identification (RTTI) and type casting. Haxe provides a number of utilities in the Type and Reflect classes for dealing with this.

    For your code, it looks like you want specifically:

    if (Std.is(target, Foo)) (cast target:Foo).aFooMethod();
    else if (Std.is(target, Bar)) (cast target:Bar).aBarMethod();
    

    Or perhaps:

    if (Std.is(target, Foo)) {
      var targetAsFoo:Foo = cast target;
      targetAsFoo.aFooMethod();
    } else if (Std.is(target, Bar)) {
      var targetAsBar:Bar = cast target;
      targetAsBar.aBarMethod();
    }
    

    Here is an example demonstrating a number of utilities which could help you, including Std.is, interfaces, Type.getClass, type casting, etc: http://try.haxe.org/#3C192

    The code is as follows:

    class Test {
        static function main() {
     
            function identify(tgt:Dynamic)
            {
                trace("1: "+tgt);
                trace("2: "+Type.typeof(tgt));
                trace("3: "+Type.getClassName(tgt));
                trace("4: "+Type.getClass(Type.getClassName(tgt)));
                trace("5: "+Std.is(tgt, Something));
     
                if (Std.is(tgt, Something)) {
                // Can cast explicitly
                    var casted:Something = cast tgt;
                    trace("Got a Something named: "+casted.name);
                }
     
                if (Std.is(tgt, Something)) {
                // Can cast implicitly
                    var casted:Something = untyped tgt;
                    trace("Got a Something named: "+casted.name);
                }
     
                if (Std.is(tgt, Something)) {
                // Can cast in an inline style, (obj:Type)
                    trace("Got a Something named: "+(tgt:Something).name);
                }
     
                if (Std.is(tgt, IHasAName)) {
                // Can cast to an interface, if you prefer
                    var i_casted:IHasAName = (tgt:IHasAName);
                    trace("Got a IHasAName named: "+i_casted.name);
                }
     
                // Can reflect to see if the name field exists:
                if (Reflect.hasField(tgt, "name")) {
                    trace("tgt has a name: "+Reflect.field(tgt, "name"));
                }
                if (Reflect.hasField(tgt, "length")) {
                    trace("tgt has a length: "+Reflect.field(tgt, "length"));
                }
            }
            
            trace("----------------------------------");
            trace("Calling identify with a Something:");
            trace("----------------------------------");
            var a = new Something("foo", 3);
            identify(a);
     
            trace("----------------------------------");
            trace("Calling identify with a String:");
            trace("----------------------------------");
            var b = "a string";
            identify(b);
     
            trace("----------------------------------");
            trace("Calling identify with anonymous:");
            trace("----------------------------------");
            var c =  { "name" : "anonymous" };
            identify(c);
     
        }
    }
     
    class Something implements IHasAName
    {
        public var name:String;
        public var length:Int;
        public function new(name:String, length:Int)
        {
            this.name = name;
            this.length = length;
        }
    }
     
    interface IHasAName {
        public var name:String;
    }
    

    The output is:

    14:41:24:664   ----------------------------------
    14:41:24:664   Calling identify with a Something:
    14:41:24:664   ----------------------------------
    14:41:24:664   1: { name : foo, length : 3 }
    14:41:24:665   2: TClass({ __name__ : [Something] })
    14:41:24:665   3: null
    14:41:24:665   4: null
    14:41:24:665   5: true
    14:41:24:665   Got a Something named: foo
    14:41:24:665   Got a Something named: foo
    14:41:24:665   Got a Something named: foo
    14:41:24:665   Got a IHasAName named: foo
    14:41:24:665   tgt has a name: foo
    14:41:24:665   tgt has a length: 3
    14:41:24:666   ----------------------------------
    14:41:24:666   Calling identify with a String:
    14:41:24:666   ----------------------------------
    14:41:24:666   1: a string
    14:41:24:666   2: TClass({ __name__ : [String] })
    14:41:24:666   3: null
    14:41:24:666   4: null
    14:41:24:666   5: false
    14:41:24:666   tgt has a length: 8
    14:41:24:667   ----------------------------------
    14:41:24:667   Calling identify with anonymous:
    14:41:24:667   ----------------------------------
    14:41:24:667   1: { name : anonymous }
    14:41:24:667   2: TObject
    14:41:24:667   3: null
    14:41:24:668   4: null
    14:41:24:668   5: false
    14:41:24:668   tgt has a name: anonymous
    

    I vaguely remember in actionscript 3 being able to use target.name but return null in this case.

    In AS3, event.target is of type DisplayObject, and all DisplayObjects have a .name property, which is why you could do that in AS3.

    OpenFL's Event.target is an IEventDispatcher, which isn't specifically associated with display classes.