Interface Creation


Introduction
All Access Based Modules can create interfaces to allow the user to interact with them. All of the controls that your Module will create will need to be placed on a Block. The Block acts as a container for the controls, allowing the user to hide controls that they are not using. Here's an example of a block with some controls on it:
AddItemsBlock.gif
There are quite a few events related to the user's interaction with each control, therefore it is necessary for you to tell messiah which of those events you are interested in, and provide some means of dealing with notifications of those events. An event notification associated with a control is called a Control Notification (CN). When you create each of your controls you will pass a flag that indicates which of the CN's you are interested in. You will also need to supply a Callback to handle the CN messages sent by messiah.
Note:
Control Notifications will not be sent to your Module's access_func(). Instead they are sent to the Callback you pass to messiah when you create the control.
Just like the rest of the Callbacks that you will create in messiah there is a Callback Signature Macro to declare/define the function. You'll use FX_CONCALLBACK() to create your Callback functions for handling CN messages. Any function created with FX_CONCALLBACK() will be referred to as a control_func() for convienence. As an example lets create a control_func() that will be used for a String Control. There are two CN messages that we will be interested in the first is ACC_VALUE() which is sent when the value of the control changes. The other is ACC_UPDATE() which is sent when the control needs to be redrawn and needs to know what value it should display. We'll takle creating controls and telling messiah what CN's we want to be notified of in a moment, for now let's just look at the control_func() needed to handle these two events.
Example:

FX_CONCALLBACK(MyEditControlFnc)
// expands to: FXint MyEditControlFunc( FXcontrol ctl, FXentity ctl_data, FX_Arg *arg, FXint64 entry )
{
        // module this control belongs to
        FXeffect                effect = (FXeffect)ctl_data;

        // data attached to the module
        MyEffectData    *med = fxObjectGetTypeData(effect, FX_NOFLAG);

        switch( entry )
        {
                // Value of the control has changes, retrieve it and store it in our module's internal data
                case ACC_VALUE:
                {
                        fxConGetString( ctl, med->editCtlString, MED_STRING_LEN );
                }
                break;

                // Control needs to be updated and needs us to tell it what its value should be
                case ACC_UPDATE:
                {
                        fxConSetString( ctl, med->editCtlString );
                }
                break;
        }
}
We determine which specific CN message was sent by testing the value of entity as you can see in the switch statement. We use the functions fxConGetString() and fxConSetString() to get and set the value of the control in response to the appropriate CN.
Where to Store the Control's Data
It's important to keep in mind that your Module will only have one instance of its interface. For example, suppose we've created a shiny new Effect Module and defined an interface for it. That one Effect Module might have many separate Effect instances all sharing the same interface code. It's important that when the user selects a different Effect, the interface updates its values to reflect that particular instance. The only real way that can happen is if the values for each of the controls is stored with the Effect instances.
This is true for all Instance Based Modules, the interface needs to reflect the currently selected instance; therefore the data must be stored in the instance (see Data Attachment for more information about attaching data to objects). Our example above was for an Effect Module, so in that case we stored the value in the Effect's state data that we retrieved using fxObjectGetTypeData(). messiah tells us which instance this control is in reference to by passing the ID of that instance. It passes it as a generic FXentity called ctl_data which we can cast to an FXeffect, FXshader or FXaction depending on the type of Instance Based Module this interface belongs to.
If the Module is a Non-Instance Based Module then things are a little easier. Since there is only one Module, there's no conflict and you can store the values globally.
Control Creation
Controls are created in response to the IN_CREATE() AN message sent to your Module. To create a control you must first create a Block upon which the control is to be placed, this is accomplished with the fxCC_Block() function.
Example:

FX_ACCESSFUNC(MyAccessFunc)
{
        switch( level )
        {
        case ACCESS_INTERFACE:
                switch( entry )
                {
                case IN_CREATE:
                {
                        FXcontrol block;

                        // Create a new block
                        block = fxCC_Block("MyBlock", NULL, ACC_NONE, FX_NOFLAG);

                }
                break;

                }// switch entry
        break;
        }// switch level
        
        return ENTRY_OK;
}
That newly created Block will be considered the "current" block, and all subsequent calls to the other fxCC_* functions will place their respective controls on that block.
Let's go ahead and place a Button on the block we just created in the example above, we will also create a control_func() for it that will display a message box saying, what else, "Hello World!".
Example:

// ...
case IN_CREATE:
{
        FXcontrol block, helloButton;

        // create the block to place the button on
        block = fxCC_Block( "Obligatory", NULL, ACC_NONE, FX_NOFLAG );

        // now create the button, passing it the address of our control_func()
        // and the ACC_VALUE flag indicating that we want to recieve that message
        helloButton = fxCC_Button( "Click Me", &HelloButton_cb, ACC_VALUE, 150 );
}
// ...

FX_CONCALLBACK(HelloButton_cb)
{
        switch( entry )
        {
        case ACC_VALUE: // button was clicked
                {
                        fxMessageSend( FX_NULLID, NULL, FX_MESSAGE_POST, "Hello World!", NULL );                        
                }
                break;
        }

        return ENTRY_OK;
}
All of the control creation functions in the messiahAPI take two specific arguments, one is the address of the control_func() that the control will send messages to, and the other is a bit mask indicating which CN's to send to the control_func(). In the example above we passed NULL as the control_func() address to the Block creation function, and the ACC_NONE() value indicating that we don't expect any CN's for that control. We then passed the address of HelloButton_cb to the Button's creation function and ACC_VALUE() indicating that we want to know when the value of the control changes (i.e. when the button is clicked). If we were interested in more than one CN, we would bitwise-OR their identifiers together and pass that value. For example, if we wanted both ACC_VALUE() and ACC_UPDATE() we would use:

helloButton = fxCC_Button( "Click Me", &HelloButton_cb, ACC_VALUE|ACC_UPDATE, 150 );
Per Mode Interfaces
Object Based Modules can have different interfaces depending on the current mode. For example, an Effect Module might display one interface while in Setup Mode and another while in Animate Mode. This will allow you to customize which controls are available to the user based on mode. So in the case of Object Based Modules only, you must tell messiah which of the blocks you have created will be visible in each of these modes. You can make a block visible in more than one mode if you wish, you will just need to set it as visible once for each mode. We'll use the fxInterfaceBlockSet() function to tell messiah where a block should be visible. For instance, to set the block we created above as visible in both Setup Mode and Animate Mode we would use:

fxInterfaceBlockSet( FX_MODE_ANIMATE, NULL, NULL, block );
fxInterfaceBlockSet( FX_MODE_SETUP, NULL, NULL, block );
Don't worry about the NULL's, we'll get to them in a second. If our Module uses more than one block then you would need to call fxInterfaceBlockSet() for each block and for each mode that it is visible in. The order that you call fxInterfaceBlockSet() in will determine the top-to-bottom order of the blocks in the interface.
It is very likely that you will want to use some of the "Standard Blocks" that messiah provides for your Module. These are blocks that the user is already familar with and rather than re-inventing the wheel you might as well take advantage of them. For eample, if your Module has animatable parameters, you'll want the user to be able to adjust the spline settings of the parameter's motion curve. You wouldn't want to have to create all of these controls yourself, so instead you use the FX_MBLOCK_SPLINE() standard block.
MBLOCK_SPLINE.gif
To add this block as part of your Module's interface you will use the following:
        
fxInterfaceBlockSet(FX_MODE_ANIMATE, FX_MODE_ANIMATE, FX_MBLOCK_SPLINE, NULL);
It is also a good idea to place the standard blocks in the same order they appear in messiah's built-in modules and tools , that way they're right where the user expects them to be.
TODO: Control Specifec Info
Block
BlockControl.gif
Area
AreaControl.gif
Button
ButtonControl.gif
Bool
BoolControl.gif
BoolButton
BoolButtonControl.gif
DragButton
DragButtonControl.gif
Choice
ChoiceControl.gif
String
StringControl.gif
Float
FloatControl.gif
Int
IntControl.gif
DragFloat
DragFloatControl.gif
DragInt
DragIntControl.gif
ChannelFloat
ChannelFloatControl.gif
ShaderChannelFloat
ShaderChannelFloatControl.gif
PopupList
PopupListControl.gif
WeightPopup
WeightPopupControl.gif
Separator
SeparatorControl.gif
ObjectPopup
ObjectPopupControl.gif


© 2003 pmG WorldWide, LLC.


www.projectmessiah.com

groups.yahoo.com/pmGmessiah

Last Updated on Thu Jul 10 04:49:36 2003