(This is a work in progress so i hope that my poorly written ramblings are somewhat helpful even if they are a pain to read)
While maxscript has done a great job integrating the concept of object oriented programing through the “struc” constructor. Implementing rollouts and Ui elements that are fully embedded into their parent objects is not as straight forward as one would hope.
Not to long ago a good friend and coworker of mine, Lukas Lepicovsky, showed me a great way to embed the parent struct into almost any Ui element (via a local variable).
The method involves trading the “on open” event handler of a rollout for a initialization (fn init self) function that can be ran after the dialog has been opened, allowing to store the struct into the rollout, via a local variable (local self).
Let’s take a look at a sample struct with rollout function
Struct mytool
(
/*rollout varible*/
Ro,
/*rollut creator function*/
fn Ui =
(
Rollout ro01 "myrollout"
(
Button btn1
)
Ro01
),
/*ui execution function*/
Fn run =
(
Ro = Ui()
Createdialog Ro
)
)
/*make an instance of the struct*/
Mtool = myTool()
Mtool.run() /*--run the ui*/
In this example the code creates our object and runs our Ui creation function..
This method allows to interact with the rollout trough the struct instances (Mtool) but the rollout is unaware of the object instance.
If we wanted to use functions or access variables embedded in the struct we would have to do it by hard coding the global variable mTool in our rollout code, but we would be unable of having multiple instances of this same Ui open with out sharing the same object instance (bummer
)
In order to make our struct and Ui fully intance-able. we will introduce the concept of self (a variable holding a link to the struct that created the Ui).
Here what this implementation looks like….
Struct mytool
(
Self, /*--self varible will hold a link to the instance*/
Ro, /*-- will hold value*/
TestValue = 0, /*-- this is a test value you will you to test the live link between the object instance and the ui*/
fn Ui = /*--create rollout function*/
(
Rollout ro01 "myrollout"
(
Local self /*--this will hold a link to our struct instance*/
Button btn1 "go"
Fn init pself = self = pself /*--this functions ran after dialog creation will embed a link to the instanced function into the local varialbe self of the rollout*/
On btn1 pressed do print self.TestValue /*--print the testValue in the instanced struct*/
)
Ro01
),
Fn run =
(
Ro = Ui() /*--make rollout*/
Createdialog Ro
Ro.init self /*--embed instanced struct into rollout*/
)
)
Mtool = myTool() /*--create instance*/
Mtool.self = Mtool /*--make instance aware of it's self*/
Mtool.run() /*--run ui*/
So one might ask… why is this better? or why should i go through the trouble of making my ui aware of the object instance it created it?
well this while this type of aprouch might be over kill for simple tools, it facilitates the programing of tools that will be used in a way which will require them to be open multiple times but have different settings.
a good implementation of this might be ui for displaying all the keyframable objects of a character rigg.
in order to write this tool in a way that you can have multiple instances of the ui open pointing a different character riggs the ui needs to be able to store all it’s data in a nice package way that imune to changes by any other script in the application environment.
that’s where the struct comes
and by letting the the ui refrence it’s own struct this programing task becomes much esier…
here’s a simple example of this pardigm using our struct example
/*--first instance*/
Mtool = myTool()
Mtool.self = Mtool
Mtool.run()
Mtool.testValue = 10
/*--second instance*/
Mtool = myTool()
Mtool.self = Mtool
Mtool.run()
Mtool.testValue = 3
this code should open two instances of the same ui… but pressing the go button will print different values based on the instance