Shiny Language Reference

Language Reference > User Interface > Buttons


setbuttondrawfunc

setbuttondrawfunc(int ID, string FuncName);

 

Sets a function for "callback" to draw a custom button

Note: Sample plugin code for custom drawn buttons is available for download in the Button handler reference.

The SETBUTTONDRAWFUNC is an extremely powerful enhancement of the button user interface element. Whilst the standard button functions provide built in flat and shaded buttons there are cases where complex buttons are required. This statement allows the SSE to draw its own buttons, meaning that a button can have any appearance required including GIF/JPG/BMP/PNG images on the button face (or even used for the entire button itself).

Note: True per-pixel alphablending in PNG images is supported. This allows for smooth, blended edges of buttons. Note that fastdraw must be disabled for PNG alphablending as true blending can only be achieved by recalculating the panel contents.

The SETBUTTONDRAWFUNC is called with the name of a specific function as the second parameter. The function is prototyped as follows:

bool function MyButtonDrawing(int iState, int iWidth, int iHeight)

The name of the function can be anything required, this of course allows for more than one button-drawing functions.

When SETBUTTONDRAWFUNC is called, it immediately prepares Brass for drawing functions. It then calls the function provided as the second parameter. Inside this function you may use any Brass drawing function, exactly as you would in the Render handler.

The specified drawing function is actually called twice. The only change between calls is the value of the iState parameter. The first call to the drawing function sets the value of iState to 0. This indicates you should draw the button in its normal, unpressed state.

When your drawing function returns, Brass cleans up and prepares for drawing again. It then calls your drawing function a second time with the value of iState set to 1. This indicates you should draw the button in its pressed (clicked on) state.

You may use the definitions of BUTTON_NORMAL ( = 0) and BUTTON_PRESSED ( = 1) to test which state you are being asked to draw.

The iWidth and iHeight parameters contain the width and height of the button image. These values are exactly the same as the dimensions you specified for the createbutton statement, they are simply passed as parameters here for convenience, to save you storing the values as global variables.

When you have drawn a state you must return boolean TRUE. Brass checks the return value of your custom draw handler to decide what actions need to be taken. This is to ensure future compatibility. Brass knows what state it asked the plugin to draw, and checks the return value of the handler. If the return value is boolean TRUE, Brass notes that the plugin has successfully drawn the requested state and caches the output. If the return value is boolean FALSE, Brass assumes that the plugin did not draw the requested state. In this case Brass will make a best-guess attempt to draw the control on the plugin's behalf.

This ensures future compatibility for the following reason. Assume that Brass version 1.0 allows for two-state buttons: normal, and clicked. A plugin written for Brass 1.0 will handle the BUTTON_NORMAL and BUTTON_CLICKED states in its custom draw handler, and return boolean TRUE for both. When Brass version 2.0 is released, it allows for three-state buttons: normal, mouse-over, and clicked. A plugin written for Brass 2.0 will now handle BUTTON_NORMAL, BUTTON_CLICKED and BUTTON_MOUSEOVER correctly. However a plugin written for Brass 1.0 that's run in Brass 2.0 will only handle BUTTON_NORMAL and BUTTON_CLICKED. This means when Brass calls the custom draw handler for the version 1.0 plugin, only 2 states will be drawn and the mouseover state will be left blank. This will obviously be visually incorrect.

However, because the version 1.0 plugin returns boolean TRUE for BUTTON_NORMAL and BUTTON_CLICKED, and boolean FALSE for everything else, when Brass called the custom draw handler with BUTTON_MOUSEOVER and boolean FALSE was returned Brass was able to tell that the plugin doesn't support the mouseover state. It can then make a best-guess drawing attempt for the mouseover state (or flag that a mouseover state should not be used).

If you do not return boolean TRUE for states you draw, Brass will destroy your drawing and replace it with a standard button display.

An overview of the process is as follows:

setbuttondrawfunc is called: setbuttondrawfunc(ButtonID, DrawButton);  
   
Brass prepares drawing states  
   
Brass calls the DrawButton function The iState parameter of the DrawButton function is set to 0
   
The DrawButton function begins  
   

The function checks the value of iState

If iState is 0, the unpressed button must be drawn
   

The normal, unpressed button is drawn here

Any Brass drawing function may be used
   

The function returns True

This signifies the requested state has been drawn
   
The DrawButton function ends  
   
Brass prepares new drawing states  
   
Brass calls the DrawButton function The iState parameter of the DrawButton function is set to 1
   
The DrawButton function begins  
   

The function checks the value of iState

If iState is 1, the pressed button must be drawn
   

The pressed button is drawn here

Any Brass drawing function may be used
   

The function returns True

This signifies the requested state has been drawn
   
The DrawButton function ends  
   

 

Although this may seem complex, all of the implementation is managed by Brass.

Implementation note: Buttons may be drawn as complex as you require. Brass calls the button drawing function only when SETBUTTONDRAWFUNC is called, and caches the buttons you draw. Therefore when the display panel must be redrawn, Brass already has the custom button stored in memory and available to draw.

Because of this smart-management system, custom drawn buttons are no slower for use than standard, flat buttons.

It is possible to use the custom draw button system in conjunction with the standard styles. This can be useful if you need to display an icon on a gradient-shaded button. To do this, first call setbuttonstyle with the appropriate parameters to draw the required gradient. Then call SETBUTTONDRAWFUNC and specify your callback drawing function. When your drawing function is called, the button will already have the gradient drawn to it. This allows you to simply make any further drawing actions over the top.

 

Parameters

ID

The control ID of the button you wish to draw manually

FuncName

The name of the function within the plugin that will be called to draw the button states

 

Return Value

None

 

Example Code

The following code is a complete, working plugin. You may paste it directly into SSEdit, compile and run it to demonstrate custom-drawn buttons. This sample draws a red rectangle for the normal state, and turns green when clicked. You can of course use any drawing function in place of fillrect, including using images. Because Brass only calls the custom drawing function when SETBUTTONDRAWFUNC is called (and it is sensible to call SETBUTTONDRAWFUNC during the Init handler), it is perfectly legal to load an image inside the ButtonDraw handler using a local image variable.

function Init()
{
    // Create a button in the standard manner
    int ButtonID = createbutton(1, 50, 100, 100, 30);

    // Tell Brass we want to draw this button ourselves, using the
    // ButtonDraw function.
    setbuttondrawfunc(ButtonID, "ButtonDraw");
}

function ButtonDraw(int iState, int iWidth, int iHeight)
{
    // This function will be called twice, once to draw the normal
    // button state and once to draw the pressed-in state.

    // Check the state we are being asked to draw
    if(iState == BUTTON_NORMAL)
    {
        // We're being asked to draw the normal state. For demo
        // purposes we just fill the button with red
        fillrect(0, 0, iWidth, iHeight, RGB(255, 0, 0));

        // This is legal and the correct way to draw an image.
        // Even though this is a local var, Brass stores the drawn
        // button image so it doesn't matter that ButtonImg here
        // is lost when the function returns.
        image ButtonImg;
        createimage(ButtonImg, "C:\some\legal\file.bmp");
        drawimage(ButtonImg, 10, 10);

        // Tell Brass we drew this state
        return True;
    }

    if(iState == BUTTON_PRESSED)
    {
        // We're being asked to draw the pressed state. For demo
        // purposes we just fill the button with green
        fillrect(0, 0, iWidth, iHeight, RGB(0, 255, 0));

        // Tell Brass we drew this state
        return True;
    }

    // Always return False here, this ensures future compatibility
    return False;
}