Visibility of Locals, Functions, Structures and User-Interface Items in Rollout Code

The ordering of user-interface items and functions and sub-rollouts in a utility is important when referencing other items and rollouts in handler or local function code. Specifically, you can only reference a user-interface item or rollout or function in a handler or other function if that item or rollout or function has been defined earlier in the utility.

A recommended ordering can help ensure this:

  1. local variables
  2. structs
  3. user-interface items
  4. nested rollouts
  5. functions
  6. event handlers

In some situations, cross-referencing means there's no way to order definitions and still pre-define everything. To remedy this, MAXScript lets you pre-declare local rollouts and functions, so code that comes before the function or rollout definition will see the correct object. You do this by declaring such rollouts and functions as uninitialized locals. In the following example, there are two rollouts, ro1 and ro2 , that each want to reference items within the other. By pre-declaring them as locals in the main utility, this cross-referencing is possible:

EXAMPLE:

( 
    local ro1, ro2 -- pre-declare local rollouts 
    rollout ro1 "Rollout 01" 
    ( 
        checkbox chk_checkbox "Check The Box In Rollout 02" enabled:false 
        on chk_checkbox changed state do ro2.chk_checkbox.checked = state 
    ) 
    rollout ro2 "Rollout 02" 
    ( 
        checkbox chk_checkbox "Enable The Checkbox In Rollout 01" 
        on chk_checkbox changed state do ro1.chk_checkbox.enabled = state 
    ) 
    theFloater = newRolloutFloater "Test Local Visibility" 250 110 
    addRollout ro1 theFloater 
    addRollout ro2 theFloater 
) --end script

As a general rule, it is recommended that all local variable names be explicitly declared as locals at their outermost scope. This will prevent any conflicts from occurring if another script has declared the same variable name as a global variable. While functions, structures definitions, and rollouts will always be local to the utility they are defined in (even if they have the same name as a global variable), declaring these as local will ensure the variables are defined, even if the order in which they are defined is changed. Here's an example showing these declarations:

EXAMPLE:

utility foo "Object Frabulator"
(
    -- local variables
    local target_obj
    -- local functions
    local prop_name
    -- local rollouts
    local setup
    fn prop_name obj name =
    for c in obj.children do c.name = name + "_" + c.name
    checkbutton setup_btn "Setup"
    edittext name_box "New name:"
    -- use a checkbutton to dynamically control presence of panels
    on setup_btn changed state do
    if state then addRollout setup else removeRollout setup
    on foo close do
    removeRollout setup -- always close rollouts
    rollout setup "Setup fraber" -- local panel
    (
        label hello
        pickbutton pick_tgt "Pick object"
        on pick_tgt picked obj do
        (
            target_obj = obj -- access utility local
            name_box.text = obj.name -- access utility item
            prop_name obj obj.name -- call utility local fn
        )
    )
)

If you define a rollout in a struct or plug-in that will access other members or functions in that struct or plug-in, the rollout must also be a member of that struct or plug-in. It cannot be defined in a function of the struct or plug-in, as it will not properly reference the struct's or plug-in's other members or functions.

For example, this will not work, because the rollout is defined in the createRollout() function:

weaponDataCA1 = attributes weaponData
(
    local lpp = undefined
    fn createRollout = 
    (
        rollout ShipRollout "weaponDataCA1" height:200
        (
            spinner s_lpp     "Ship length:"
            on s_lpp changed val do
                lpp = val
        )
        createDialog ShipRollout modal:false
    )
)
weaponDataCA1Dlg1 = createInstance weaponDataCA1
weaponDataCA1Dlg1.createRollout()

However, this will work, because the rollout is defined first, and then referenced in the createRollout() function:

weaponDataCA2 = attributes weaponData
(
    local lpp = undefined
    rollout ShipRollout "weaponDataCA2" height:200
    (
        spinner s_lpp     "Ship length:"
        on s_lpp changed val do
            lpp = val
    )
    fn createRollout = 
    (
        createDialog ShipRollout modal:false
    )
)
weaponDataCADlg2 = createInstance weaponDataCA2
weaponDataCADlg2.createRollout()