For those that don't want to read this... I'm working on a website to "organize" all this info and make it more presentable.
bmcclure wrote:
Is it possible to reference a container in a container, so that all controls in the subcontainer are destroyed when the parent container is?
Provided I'm understanding the question, this is the default behavior. Whenever a container is destroyed, all objects inside are destroyed (via a dynamic call to %ClassName%_destroy(ClassObject), where ClassName is the class name for the object - set in the %ClassName%_new function (via %ClassName%_initClass) - see the rectangle class for more details. So, when you destroy the subcontainer, all objects in that subcontainer are destroyed. And when you destroy the "top-level" container, all sub-containers, and their sub-containers, ..., and their objects are destroyed automatically - all via dynamic function calls. So, make sure any classes you create have that a %ClassName%_destroy(ClassObject) function to destroy all built-in values, and calls %ClassName%_destroy_private(ClassObject), which the user creates to destroy any user-defined values (see the Rectangle class for detail).
bmcclure wrote:
Is there any inheritance or does each class need every function defined that it will use? I'm assuming the latter at this point, but just wanted to make sure.
Yes, right now, at least, there is no inheritance. However, such an idea is not outside the scope of possibilities. Such method calls will use dynamic function calls, and like normal inheritence, the class would, at creation, specify which class it inherits. However, I'm not sure if it's needed - please elaborate what you need though so I can get a better idea. When I think inheritance, I think of this kind of example. You have a shape object, that has a draw function. You then have other classes, circle, square, etc., that each have their own draw function. In shape, you could store either a shape, circle, square, or any other class that has inherits shape. Then, on this shape object, you would say, Shape_draw(myShape) and it would call the draw function for the object. This latter part CAN be added, but the former is unneeded. Since each object holds it's type, there will never be the need to store a circle in a shape object - since the circle would know it's a circle. If you would like to be able to call Shape_draw(myCircle) and it would call Circle_draw (provided myCircle is a Cirle object), I can add this. The only downside would be it's a dynamic call, but if you knew the object was a cirle, beforehand, you can always call Circle_draw instead. Of course, you would have to create the Circle_draw function. Now, if such a function didn't exist THERE IS a way to detect this (of sorts).
Code:
;myShape/Circle/Square would have a Shape/Circle/Square object
myShape := "Shape"
myCircle := "Circle"
mySquare := "Square"
x := 0
y := 0
;Returns "Shape"
MsgBox, % "Shape_draw returns: " . Shape_draw(myShape, X, Y)
;Returns "Circle"
MsgBox, % "Shape_draw returns: " . Shape_draw(myCircle, X, Y)
;Returns ""
MsgBox, % "Shape_draw returns: " . Shape_draw(mySquare, X, Y)
Shape_draw(Shape, arg1, arg2)
{
;here you would use ClassName := Class_getClass(Shape)
ClassName := Shape
;if ClassName != ThisClass ("Shape in this case")
;prevents calling this function recursively (Forever!!!)
;crashed the code... rightfully so
;then, here would be a check:
callFunction := ClassName != "Shape" && ClassInherits(Shape, "Shape")
if (callFunction && ReturnValue := "?" . %ClassName%_draw(Shape, arg1, arg2))
return subStr(ReturnValue, 2)
MsgBox, % "Called Shape_draw"
return "Shape"
}
Circle_draw(Shape, arg1, arg2)
{
;this function does not have to return a value
;Shape_draw would then just return instead of returning the value
MsgBox, % "Called Circle_draw"
return "Circle"
}
Square_draw(Shape, arg1, arg2)
{
MsgBox, % "Called Square_draw"
return
}
ClassInherits(ClassObject, ClassName)
{
;would see if ClassObject was a "superclass" of ClassName
;e.g. A Circle is a Shape, so if the Class object was a Cicle, and ClassName
;was "Shape" this function would return true
;if ClassObject was a Shape and ClassName was "Circle", then this function
;would return false, since a shape is not a Circle
;when you call Class_setClass in the _new function of your class, you would
;say Circle inherits Shape - in some form. Then, this function would then
;know (by looking at your Circle object) that Cicrle inherits the Shape class functions
;this is just a test, so such "advanced" methods are not shown :(
if (ClassObject = "Circle" and ClassName = "Shape"
|| ClassObject = "Square" and ClassName = "Shape")
return true
return false
}
Now, I'm going to see if I can't find a better way, because this method would require EVERY function to have those first few lines. Because later on (whenever) someone may want to extend your class, these functions would have to allow calling that class. Of course, this is only if you wanted your function to be inherited. You could be a stingy old man/woman and horde all your money and not let future generations inherit from your "fortune", but then you could leave it off. Like I said, I'll see what I can do, but there is no structure, yet, that will allow parsing args in a function call, so I don't believe there is a way to wrap this mess of a call like such, call(ThisFunction, args). Well..., there might be, but what would happend is the call method would parse the args, then have a big if branch (one for each arg count), and pass the args. However, even if this worked fine, I'm not sure if ByRef would work correctly. Because the values would be moved to another variable (a temp value), the address would change, and yeah, no, ByRef won't work. Using the above method, though, it will. So, yeah, it might be hard to remove it. The good is that the structure is a simple cut and paste. There will be a function to check inheritance, so that's good. There's also a function to get the className (for the dynamic call). Only call the function is the object inherits from the base-class. Also, if it didn't inherit, then the function should just return because calling Shape_draw passing a Car object wouldn't make much sense...
Now, the other way. If you had a Car_hasEngine function, and you had a Truck class (which) inherited from Car, you COULD NOT call Truck_hasEngine and expect it to call Car_hasEngine (if no Truck_hasEngine function existed). You could, however, create a Truck_hasEngine function that calls Car_hasEngine and returns its value. This would allow you to reuse the function without recreating it. Also, in this direction, it's not a dynamic function call, just a normal one. Also, you could call Car_hasEngine passing a Truck object (presuming Truck inherits Car) and it would work. Now, all this would add some calling overhead, but that's the cost of functionality.
So, depending what you want from inheritance, I can add the ability to call Shape_draw(myCircle) and it would call call (presuming my circle is a Circle object), Circle_draw. You would, of course, have to write a Cirle_draw function that did what you wanted. You would then need to (I'm work on the details), specify that Circle extends Shape. This would be done as part of the className value. Since this value is stored internally (and only has a single copy for all objects), it increases the base cost a little, but it doesn't cost any more for each new object. Also, I'm sure many could benefit from this, so I shall add it. Of course, if you knew it was a circle before hand, you could call cirle_draw instead (to prevent a need to call the function dynamically).
Quote:
Would you recommend something like a Window class for the container, which then contains an object per window which stores references to each control on the window?
I would recommend an array for the 99 guis (since you know there are 99 and that never changes). Also, this way, when the user interacts with gui1, you would be able to get that Gui object with this call, Array_get(Guis, 1) - where guis is your Array to hold the Guis. Then, you would have a Gui class that would hold whatever gui values you need, as well as a Vector, Cddl, or some other container (if you have ideas I could whip it up), to store the different controls. An array wouldn't work, because you don't know the number of controls on creation - or if you did, then array would work. Also, this way, when Gui1 is destroyed, you only need to call, Array_set(Guis, 1, 0) which will destroy the first gui - the last zero is the null pointer. You would add a gui by calling, Array_set(Guis, 1, Gui1), where Gui1 is the address returned from Gui_new or you could just call Array_set(Guis, 1, Gui_new(args)) if you didn't want to store the Gui address in a variable. Of course, you could always call Array_get(Guis, 1) to get the address for the first gui object. The address would be returned because Gui isn't a wrapper like String and Int (which instead "unwrap" their value and return it - call Array_getAddress to return the address for a wrapper class, which also returns the address for non-wrappers).
Now, some "genaral info".
The container classes accept any classes in them (and can contain multiple classes). Because the class objects store their class name internally, they are able to be destroyed by dynamic call - it calls %ClassName%_Destroy(ClassObject), so make sure that you have such a method defined. This function should do all cleanup for built-in values. If there are user-define values, this function must call %ClassName%_destroy_private, which must be written by the user of the class to clean-up their user-defined values. To clean up those values, the user only needs to call the setter functions with either "" (for strings) or "" or 0 for numerical/object data. The class name can be any combinations of lower-case letters, (a-z), upper-case letters (A-Z), digits (0-9), and the underscore ("_"). The only thing is that the class should only have "_Node" as a suffix if its a "node" class. Node classes should only be used in a container where each "node" in the container is it's own class. For example, Cddl_Node is a node of a Cddl class. A Cddl is returned from Cddl_new and a Cdll_Node is returned from Cddl_addBefore/after. When/if you write a container class, there's stuff to learn for it. I'm working on putting this on a site - so it's more organized and doesn't look so "complicated", because it really isn't - there's just a lot that you can do, and thus there's a lot for me to say.
None of the classes have a "search" feature yet, because I'm not sure how it needs to be implemented. How would you want to be able to find the control from the list of controls? I'm thinking something like a "valueOf" function that would be called dynamically for each object. For example, this valueOf function for your controls would return something to uniquely identify them, so when looking for them in a list of values, it returns the unique value. For menus/menu items, this would be the menu/menu item handle (separators wouldn't be unique), for controls this would be the variable given to the control, or some other value. Since the classes are still in their beginning stages this is the ideal time to add functions to help gain access to the values within a container - any suggestions you have are much welcome.
Disclaimer: Because AHK isn't an OOP language, some OOP stuff isn't possible, and others, while possible, might not be as "pretty" as some would like because it's a "hack".