Adobe Director: Type overloading using ancestors

Warning! This is a heavily geeky Adobe Director post! This is something I’ve messed around with in Director for a while now, and I recently brought it up on Direct-L. A lot of people there hadn’t heard about it so I thought I’d blog about it here.

An object’s ancestor property allows you to attach a secondary object that will get events that the original object doesn’t get. For example you can attach a “car” child object as an ancestor to a “Probe” child object. Once you’ve done that, any events sent to “Probe” that the probe script doesn’t process will be passed on to “car”. If you’re confused, look it up in the help docs, because it’s only going to get , um, confusinger.

I discovered a while back that you can assign other types to a child object’s ancestor property and have the resulting object act like the ancestor’s ilk. And by “ilk” I mean lists, property lists, rect(), point(), image objects … you name it and it probably works.

Here’s an example. Let’s say I wanted a list that returned void whenever an out of bounds index was referenced. From a usage standpoint I’d want it to look like this:
ilist = [1, 2, 3]
Put ilist[4]
-- void
put ilist[0]
-- void
So that’s how we want it to work. But currently if you try this with Director, you’ll get errors if you try to access invalid pointers. However, using this trick we can build an overloaded list that will work the way we want to. Here’s some code that goes in a parent script:

-- Parent Script: "BetterList"

property ancestor

on new me
  ancestor = []
  return me
end

on getat me, iposition
  if iposition <1 or iposition > ancestor.count then return void
  return ancestor[iposition]
end
Notice how all this really does is assign an empty list to the ancestor, and a getat function that checks to see if the requested index is out of bounds. Now, if we do this:
ilist = new(script "betterlist")
ilist[1] = "hello"
ilist[2] = "there"

put ilist[1]
-- "hello"

put ilist[3]
-- Void
The ilist variable operates as a normal list: we can address it with brackets, and we can add items to the list like normal. However, when calling an out of bounds index, the list returns void. This is because the GetAt function (which bracket syntax is shorthand for) is being processed not by the list, but by my script. However, all other functions are unused by the child object and are therefore passed to the ancestor. On Direct-L Valentin Schmidt took my script and modified it to create a way to make a list that starts at 0 instead of one. This could be very useful when porting code from other languages where arrays start with zero:
--- parent script "zero_list"
property ancestor

on new me
 l = []
 repeat with i = 2 to the paramcount
   l.append(param(i))
 end repeat
 ancestor = l
 return me
end

on getAt me, pos
 if pos < 0 or iposition >= ancestor.count then return void
 return ancestor[pos+1]
end

on setAt me, pos, val
 ancestor[pos+1] = val
end

--- test
l = script("zero_list").new(1,2,3)
put l[0]
-- 1
l[0] = 23
put l[0]
-- 23

How useful is this hidden functionality? Only time will tell. It has worked with every data type I’ve tried. Drop me a comment if you’ve used this before or have any insight as to what can be done with it.


5 Responses to “Adobe Director: Type overloading using ancestors”

  1. ahoeben Says:

    If you change the ‘new’ handler slightly, you can set initial list values upon script initialisation:

    on new me, aList

    if not listP(aList) then aList = []
    ancestor = aList

    return me
    end

    usage:
    myList = new(script(“BetterList”),[11,22,33])
    put myList[2]
    -- 22

    While this technique is very usefull to add ‘methods’ to the list datatype (such as an ‘addOnce’ method to only add an item to a list if it is not already in the list, or an ‘append’ to append another list to this list, etc), you also loose a lot of list functionality:— you can no longer ‘put’ the list— you can no longer do arithmetics on the full list (eg multiply all list items by 2)
    I think that’s the reason why acestors aren’t used more to extend datatypes…

  2. Hanford Says:

    Interesting about losing those aspects of list functionality. You can, of course write your own “put”, like this:

    on put me
    trace(ancestor)
    end

    I like the change you made to the “new” handler. Valentin does something similar, but in my original example I wanted it to be as simple as possible.

  3. ahoeben Says:

    The ‘on put me’ approach does not seem to work. Your reply got me thinking though, and the solution is as simple as:

    myList = new(script(“BetterList”),[11,22,33])

    put myList.ancestor
    -- [11,22,33]

    put myList.ancestor * 4
    -- [44,88,132]

    put myList.ancestor + [1,2,3]
    -- [12,24,36]

  4. Phylobates Says:

    I’m been using director for years and you think you got it pretty much sussed when a little gem of knowledge like that pops up.. interesting. Tried with “rect” and it works beautifully. I’m sure I’ll find some use for it…

    -- New rect object in script customRect
    on new me

    me.ancestor = rect(0,0,0,0)
    return me
    end

    on diagonalLength me

    return sqrt((me.width*me.width) + (me.height * me.height))
    end

    -- and lo and behold..

    myrect = new script(“customRect”)
    myrect.top = 50
    myrect.right = 50
    put myrect.diagonalLength()
    -- 71

  5. Hanford Says:

    Test.

Leave a Reply

For security, enter the word TURING below:
Comments RSS feed