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]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:
Put ilist[4]
-- void
put ilist[0]
-- void
-- 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] endNotice 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] -- VoidThe 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.


I'm Hanford Lemoore. My parking skills are unparalleled.






February 1st, 2007 at 2:09 am
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…
February 1st, 2007 at 2:51 am
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.
February 1st, 2007 at 4:25 am
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]
June 21st, 2007 at 8:30 am
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
July 2nd, 2007 at 6:11 pm
Test.