Object Info Particle Services Globals Table of Contents

Panels

Availability  LightWave® 6.0
Component  Layout, Modeler
Header  lwpanel.h

The Panels global supplies a set of routines for creating user interface windows from within plug-ins. Also see the related raster and context menu globals.

LWPanels (or the newer XPanels system) gives you a way to create interfaces for your plug-ins that have the LightWave® look and feel, using a single code base for all of the platforms LightWave® supports.

Creating a non-trivial user interface is a complex task that demands an understanding of both real-time, event-driven programming and of human factors (the ergonomics of the mind). Good design marries function and aesthetics, while a good implementation seeks a balance between responsiveness and power.

This page can't hope to teach any of that, of course, but it's worth mentioning that there's more to this process than the mere building blocks presented here.

If you've programmed interfaces for Microsoft Windows or Apple MacOS, you're familiar with a design method that uses resource files to define dialogs "ahead of time," or statically. When your code runs, you make an operating system call to load your dialog template and use it to create a dialog. Events are either sent to a single, central callback or are pulled by your code from an event queue.

LWPanels doesn't use dialog templates. Dialogs, or panels, are built "on the fly" by calling functions that add and position a panel's controls.. Panels are defined by a sequence of function calls rather than a list of directives in a resource file. And events may be sent to many different callbacks. You tell LWPanels where to send them.

If you're accustomed to designing dialogs in a visual environment, the LWPanels approach may take some getting used to.

Other aspects of a panel's life cycle are very similar to those for Windows or MacOS dialogs. You initialize the values of controls before the panel is displayed, and you can read back those values at any time, but in particular after the panel is closed. Interactive controls like sliders generate events while the user is modifying them, and you can respond to those events by changing the values or the appearance of other controls. You can draw on a panel, and blit bitmaps onto it.

If you haven't written an interface in another environment, your first reading of this page is likely to be overwhelming. (In fact, that may be true regardless of your previous experience.) Try looking at some of the SDK samples, particularly the ones mentioned by name at the end of this page, to get an initial idea of what's going on, and then refer back to the documentation for an explanation of anything you don't immediately understand.

Handlers whose interfaces use LWPanels will almost always create and display their panels from within the callback you put in the options field of the LWInterface structure. (Don't use the panel field of that structure; that's for xpanels.)

Global Call

   LWPanelFuncs *panf;
   panf = global( LWPANELFUNCS_GLOBAL, GFUSE_TRANSIENT );

The global function returns a pointer to an LWPanelFuncs.

   typedef struct st_LWPanelFuncs {
      LWPanelID   (*create)      (char *, void *);
      void        (*destroy)     (LWPanelID);
      int         (*open)        (LWPanelID, int flags);
      int         (*handle)      (LWPanelID, int);
      void        (*draw)        (LWPanelID, DrMode);
      void        (*close)       (LWPanelID);
      void        (*get)         (LWPanelID, pTag, void *);
      void        (*set)         (LWPanelID, pTag, void *);
      LWControl * (*addControl)  (LWPanelID, char *type,
                                    LWPanControlDesc *, char *label);
      LWControl * (*nextControl) (LWPanelID, LWControlID);
      DrawFuncs   *drawFuncs;
      void        *user_data;
      GlobalFunc  *globalFun;
   } LWPanelFuncs;
panel = create( title, panf )
Create a panel. This allocates resources for the panel but doesn't display it.

destroy( panel )
Free the resources allocated by create and addControl. The panel ID is no longer valid after this is called. Panels should not be destroyed while open.

result = open( panel, flags )
Display the panel. The panel and its controls must already be created, positioned, sized, initialized and ready to go. The flags are a combination of the following.

PANF_BLOCKING
When this is set, the panel is modal, meaning that it will be the only LightWave® window that can receive user input. The open function will not return until the panel has been closed. Without this flag, the panel is non-modal and the open function returns immediately.
PANF_CANCEL
Add a Cancel button at the bottom of the panel.
PANF_FRAME
Add operating system-specific decoration to the panel window. This flag currently has no effect.
PANF_MOUSETRAP
The panel wants mouse input, which will be passed to panel callbacks.
PANF_PASSALLKEYS
The Enter and Escape keys normally close a panel. This flag allows the panel's keyboard callback to handle them instead.
PANF_ABORT
Changes the label of the Cancel button to "Abort". This should be used with the PANF_CANCEL flag.
PANF_NOBUTT
Display no buttons (no Continue or Continue/Cancel) at the bottom of the panel.
PANF_RESIZE
Allow resizing of the panel window. When this is set, the panel will accept calls to the set function with PAN_W and PAN_H tags.

result = handle( panel, flag )
Process user input for non-modal panels. When the panel is non-modal (opened without the PANF_BLOCKING flag), open returns immediately. In order to allow user input processing to occur, the plug-in yields control by calling handle. If flag is 0, handle returns as soon as the event queue is empty. It returns 0 if the panel is still open, or -1 if the user has closed it. If flag is EVNT_BLOCKING, handle won't return until the user closes the panel.

draw( panel, drmode )
Redraw the panel. LWPanels performs its own drawing and then calls your panel draw callback, if you've set one. Any of the drawing modes described later for controls are also valid here, but in most cases you'll use DR_REFRESH.

close( panel )
Close a non-modal panel. Typically users will close your panels, so you won't need to call this. A closed panel can be reopened later.

get( panel, ptag, value )
set( panel, ptag, value )
Set and retrieve various panel attributes. The value is the panel attribute cast as a void *. Many of the attributes are pointers to callback functions, which are described below. The ptag identifies the attribute and can be one of the following.

PAN_X, PAN_Y, PAN_W, PAN_H
Panel position and size in pixels.
PAN_TITLE
The panel title passed to create.
PAN_PANFUN (get)
The LWPanelFuncs pointer passed to create.
PAN_FLAGS (get)
The flags passed to the open function.
PAN_USERDATA
Your data pointer. This is passed as the second argument to all of the panel callbacks.
PAN_MOUSEX, PAN_MOUSEY
The position of the mouse at the time of the most recent event, relative to the upper left corner of the panel.
PAN_QUALIFIERS (get)
An integer containing bit flags that provide additional information about the most recent mouse event. These are the same qualifier bits that are passed to mouse callbacks.
PAN_MOUSEBUTTON, PAN_MOUSEMOVE, PAN_MOUSEWHEEL
Your mouse event callbacks.
PAN_USERKEYS, PAN_USERKEYUPS
Your keyboard input callbacks.
PAN_USERDRAW
Your panel draw callback.
PAN_USERACTIVATE, PAN_USEROPEN, PAN_USERCLOSE
Callbacks that LWPanels calls when the panel is activated (receives input focus from the operating system), opened and closed, respectively.
PAN_VERSION (get)
The LWPanels API version. Compare this to LWPANELS_API_VERSION, which is defined in lwpanel.h.
PAN_RESULT (set )
Set this to pass results when closing panels manually.
PAN_HOSTDISPLAY (get)
A pointer to a HostDisplayInfo for the panel.
PAN_TO_FRONT (set)
Move the panel to the top of the window z-order.

control = addControl( panel, type, ctrldesc, label )
Add a control to a panel. Call this after the panel has been created but before it's opened. In practice, you'll seldom call this function explicitly. For each control type, lwpanel.h supplies a macro that calls addControl with the proper arguments for that control. Returns a pointer to an LWControl structure, described below. By default, each control is positioned beneath the previous one, and the panel autosizes to fit all of the controls. Controls can be moved after they're created, but internally they remain in the order in which they're created, which for example affects the drawing order.

control = nextControl( panel, control )
Enumerate the controls that have been added to a panel. Get the first control in the list by passing NULL as the second argument.

drawFuncs
A pointer to a DrawFuncs structure, described below.

user_data
A place to store whatever you like.

globalFun
Set this to the GlobalFunc passed to your activation function.

Panel Callbacks

The LWPanelFuncs set function allows you to install a number of panel callbacks that LWPanels will call when certain events occur. You aren't required to install any, so only use them if you need them. All panel callbacks receive as their second argument the value you set for PAN_USERDATA.

panhook( panel, userdata )
This is the form of the callback for PAN_USERACTIVATE, PAN_USEROPEN and PAN_USERCLOSE.

pankey( panel, userdata, key )
The form for PAN_USERKEYS and PAN_USERKEYUPS. For alphanumeric keys, the key code is just the ASCII code. lwpanel.h defines special codes for other keys. A value of '1' should be returned if the key was processed, or '0' if not.

panmouse( panel, userdata, qualifiers, x, y )
For PAN_MOUSEBUTTON, PAN_MOUSEMOVE and PAN_MOUSEWHEEL. The x and y mouse positions are relative to the upper left corner of the panel. For PAN_MOUSEBUTTON and PAN_MOUSEMOVE, the qualifiers are bit flags.
    IQ_CTRL
    IQ_SHIFT
    IQ_ALT
    IQ_CONSTRAIN
    IQ_ADJUST
    MOUSE_LEFT
    MOUSE_MID
    MOUSE_RIGHT
    MOUSE_DOWN
For PAN_MOUSEWHEEL, the qualifiers value is an integer count of the number of clicks made by the mouse wheel. The sign of the value indicates the direction the mouse wheel moved.
pandraw( panel, userdata, drawmode )
This is for PAN_USERDRAW. The drawmode is the same as those used for controls and is described later.

Drawing Functions

The drawFuncs member of LWPanelFuncs is a structure containing functions that allow you to draw on your panel. See also the Raster Functions global for creating and efficiently displaying bitmaps. You can call these at any time, but in most cases you'll want to be synchronized with the redrawing done by LWPanels, and for that you should limit drawing to panel and control draw callbacks.

   typedef struct st_DrawFuncs {
      void (*drawPixel)   (LWPanelID, int color, int x, int y);
      void (*drawRGBPixel)(LWPanelID, int r, int g, int b, int x, int y);
      void (*drawLine)    (LWPanelID, int color, int x1, int y1, int x2,
                             int y2);
      void (*drawBox)     (LWPanelID, int color, int x, int y, int w,
                             int h);
      void (*drawRGBBox)  (LWPanelID, int r, int g, int b, int x, int y,
                             int w, int h);
      void (*drawBorder)  (LWPanelID, int indent, int x, int y, int w,
                             int h);
      int  (*textWidth)   (LWPanelID, char *text);
      void (*drawText)    (LWPanelID, char *text, int color, int x,
                             int y);
      const LWDisplayMetrics *(*dispMetrics)();
   } DrawFuncs;
drawPixel( panel, color, x, y )
drawRGBPixel( panel, r, g, b, x, y )
Draw a pixel. The coordinates are relative to the upper-left corner of the panel. The color is specified as one of the palette colors defined in lwpanel.h or as levels of red, green and blue between 0 and 255.

drawLine( panel, color, x1, y1, x2, y2 )
Draw a line connecting the endpoints.

drawBox( panel, color, x, y, w, h )
drawRGBBox( panel, r, g, b, x, y, w, h )
Draw a solid rectangle.

drawBorder( panel, indent, x, y, w, h )
Draw a rectangular border similar to the ones use to mark the borders of controls. The indent is the thickness of the border. If h is 0, drawBorder creates a horizontal divider.

w = textWidth( panel, str )
Returns the pixel width of the character string. Use this and the font height information in the LWDisplayMetrics structure to find the rectangular extent of a line of text.

drawText( panel, str, color, x, y )
Render a line of text.

dmet = dispMetrics()
Returns an LWDisplayMetrics structure. Except for the screen size and text height, most of this structure is obsolete.
typedef struct st_display_Metrics {
   int  width, height;
   int  pixX, pixY;
   int  maxColors, depth;
   int  textHeight;
   int  textAscent;
} display_Metrics;
#define LWDisplayMetrics display_Metrics
width, height
The size of the screen, in pixels.
pixX, pixY
The pixel aspect ratio. In most cases, pixX == pixY, indicating square pixels.
maxColors, depth
Palette size and bit depth for indexed color displays. depth is 0 for true color displays.
textHeight
The height of the LWPanels font in pixels.
textAscent
Ignore this.

Controls

For each control you add to a panel, the LWPanelFuncs addControl function returns a pointer to an LWControl.

   typedef struct st_LWControl {
      void  (*draw)(LWControlID, DrMode);
      void  (*get) (LWControlID, cTag, LWValue *);
      void  (*set) (LWControlID, cTag, LWValue *);
      void  *priv_data;     
   } LWControl;
draw( control, drawmode )
Draw or redraw the control. LWPanels performs its own drawing and calls your control draw callback, if you've set one for this control. The draw mode is one of the following.

DR_RENDER
Draw the control normally.
DR_GHOST
Draw the control with a disabled or ghosted appearance.
DR_ERASE
Erase the control.
DR_REFRESH
Redraw the control in its current state (normal, ghosted or erased).

get( control, ctag, param )
set( control, ctag, param )
Get and set control attributes, including the value of the control. The param is a pointer to an LWValue. The ctag identifies the attribute and is one of the following.

CTL_VALUE
The value of the control.
CTL_LABEL
The control label passed to addControl.
CTL_X, CTL_Y, CTL_W, CTL_H
The rectangular extent of the control, in pixels. This may include some padding used for control spacing and alignment. X and Y are relative to the upper left corner of the panel.
CTL_HOTX, CTL_HOTY, CTL_HOTW, CTL_HOTH
The extent of the control's "hot" area. Generally this excludes the label and any padding but may include the border decoration.
CTL_LABELWIDTH
The width of the label in pixels. Because of padding, this may differ from W - HOTW.
CTL_MOUSEX, CTL_MOUSEY, CTL_MOUSEBUTTON
The position of the mouse at the time of the most recent control event, relative to the upper left corner of the panel and which mouse button was used in selecting..
CTL_FLAGS
Flags marking the current state of the control. CTLF_DISABLE indicates the control is disabled, or read-only, but still visible. Disabled controls can still trigger callback events, so that, for example, you can display a message explaining why the control's functionality is unavailable. CTLF_INVISIBLE indicates that the control has been erased. CTLF_GHOST is a synonym for CTLF_DISABLED. These flags will affect the draw mode passed to your control draw callbacks.
CTL_USERDATA
Your data pointer for this control. This is passed as the second argument to the control callbacks. Note: For some control types, calling set with this tag (or calling the CON_SETEVENT macro) has important side effects. Even if your userdata is NULL for those controls, you'll want to explicitly set it before opening the panel.
CTL_USERDRAW
Your control draw callback.
CTL_USEREVENT
Your control event callback.
CTL_PANEL, CTL_PANFUN (get)
The panel and LWPanelFuncs.
CTL_RANGEMIN, CTL_RANGEMAX
Slider limits.
CTL_ACTIVATE (set)
Set the input focus to this control. Only valid for edit fields.

priv_data
An opaque pointer to data used internally by LWPanels.

Control Callbacks

The LWControl set function allows you to install callbacks for custom drawing related to the control and for responding to control events. These callbacks receive as their second argument the value you set for CTL_USERDATA.

ctlevent( control, userdata )
This is the form of the callback for CTL_USEREVENT. The context of a control event will vary depending on the type of control. Sliders generate events as the user moves them, and button controls generate an event when they're pressed.

ctldraw( control, userdata, drawmode )
This is for CTL_USERDRAW. The drawmode is one of the modes listed for the draw function. You can draw anywhere on the panel from within this callback.

In addition to these, several control types have type-specific callbacks. These are described in the following sections.

Macros

The lwpanel.h header file defines a set of macros that hide some of the complexity of using the LWPanels system. These macros are an integral part of the LWPanels API.

The macros require that your source declare and initialize a few variables.

   static LWPanControlDesc desc;
   static LWValue
      ival    = { LWT_INTEGER },
      ivecval = { LWT_VINT },
      fval    = { LWT_FLOAT },
      fvecval = { LWT_VFLOAT },
      sval    = { LWT_STRING };

These are used as temporary variables while setting up arguments to the panel functions.

Panel Life Cycle

Three macros are provided for creating, displaying and destroying a panel.

PAN_CREATE( pf, title )
Calls the LWPanelFuncs create function.

PAN_POST( pf, pan )
Calls the LWPanelFuncs open function with the flags set to PANF_BLOCKING | PANF_CANCEL | PANF_FRAME.

PAN_KILL( pf, pan )
Calls the LWPanelFuncs destroy function.

Panel Attributes

These macros call the LWPanelFuncs get and set functions for specific attributes. An advantage of using them is that you can use constants in your source code. The macro takes care of stuffing the value into an appropriate variable and passing a pointer to that variable. The first two arguments to all of these functions are the LWPanelFuncs pointer and the panel.

x = PAN_GETX( pf, pan )
y = PAN_GETY( pf, pan )
w = PAN_GETW( pf, pan )
h = PAN_GETH( pf, pan )
Get the position and size of the panel in pixels. X and Y are relative to the upper left corner of the screen.

PAN_SETW( pf, pan, w )
PAN_SETH( pf, pan, h )
MOVE_PAN( pf, pan, x, y )
Set the position and size of the panel. Note that the panel automatically adjusts its size as controls are added to it.

PAN_SETDATA( pf, pan, userdata )
PAN_SETDRAW( pf, pan, drawFn )
PAN_SETKEYS( pf, pan, keyFn )
Set the PAN_USERDATA and the panel draw and key event callbacks.

version = PAN_GETVERSION( pf, pan )
Returns the version of LWPanels.

Creating Controls

After creating a panel and before displaying it, your code calls these macros to populate the panel with the elements of your interface. The first three arguments to all of these macros are

  • the LWPanelFuncs returned by the global call
  • the LWPanelID returned by the create call (or the PAN_CREATE macro)
  • a string that will be used to label the control

All of these macros ultimately call the LWPanelFuncs addControl function and return a pointer to LWControl for the newly created control.

Edit fields

c = INT_CTL( pf, pan, label )
c = INTRO_CTL( pf, pan, label )
c = IVEC_CTL( pf, pan, label )
c = IVECRO_CTL( pf, pan, label )
Integer edit fields. RO is read-only, and VEC is a group of three fields.

c = FLOAT_CTL( pf, pan, label )
c = FLOATRO_CTL( pf, pan, label )
c = FVEC_CTL( pf, pan, label )
c = FVECRO_CTL( pf, pan, label )
c = DIST_CTL( pf, pan, label )
c = DVEC_CTL( pf, pan, label )
Floating point edit fields. The distance controls handle unit inputs and conversions.

c = STR_CTL( pf, pan, label, cw )
c = STRRO_CTL( pf, pan, label, cw )
String edit fields. The cw argument is the number of characters that should be visible, or the width of the control in characters. The string itself can be longer or shorter than this. If the fixed width of the integer and floating point controls isn't suitable, you can of course use the string controls and do the numeric conversion yourself.

Buttons

c = BUTTON_CTL( pf, pan, label )
c = WBUTTON_CTL( pf, pan, label, w )
Simple buttons. In order for these to do anything, you'll need to set an event callback that responds when the button is pressed. The WBUTTON version accepts a width in pixels.

c = BOOL_CTL( pf, pan, label )
c = BOOLBUTTON_CTL( pf, pan, label )
c = WBOOLBUTTON_CTL( pf, pan, label, w )
The first of these displays a checkmark, while the other two are buttons that are displayed in selected or unselected states. The underlying value is an integer set to 0 or 1.

Sliders and mouse feedback

c = SLIDER_CTL( pf, pan, label, w, min, max )
c = UNSLIDER_CTL( pf, pan, label, w, min, max )
This kind of slider is a thumb button that moves in or along a horizontal track. It has an associated integer edit field. The UN version is "unbounded," meaning that values outside the min, max range can be entered in the edit field. The width is in pixels.

c = HSLIDER_CTL( pf, pan, label, w, min, max )
c = VSLIDER_CTL( pf, pan, label, h, min, max )
Horizontal and vertical sliders, without an associated edit field. The width or height is in pixels.

c = MINISLIDER_CTL( pf, pan, label, w, min, max )
c = PERCENT_CTL( pf, pan, label )
c = ANGLE_CTL( pf, pan, label )
c = DISTSLIDER_CTL( pf, pan, label )
A minislider is a small button that captures mouse drag but doesn't move. It has an associated integer edit field. The w argument is the visible width of the edit field in pixels. The PERCENT control is a minislider with a floating point edit field that displays the percent (%) character after the number. ANGLE also has a floating point edit field. The value is displayed to the user in degrees, but plug-ins get and set it in radians. The DISTSLIDER_CTL control is a minislider with a floating point edit field that displays the current unit type after the number and handles the conversion.

c = DRAGBUT_CTL( pf, pan, label, w, h )
c = VDRAGBUT_CTL( pf, pan, label )
c = HDRAGBUT_CTL( pf, pan, label )
Drag buttons are minisliders with no associated edit field.

c = AREA_CTL( pf, pan, label, w, h )
c = DRAGAREA_CTL( pf, pan, label, w, h )
These create a rectangle with a border. AREA controls generate mouse click events, and DRAGAREA controls generate both click and drag events. For AREA, the CTL_MOUSEX and CTL_MOUSEY values contain click coordinates relative to the upper left corner of the control. For DRAGAREA, MOUSEX and MOUSEY are relative to the upper left corner of the panel. Use the GETV_IVEC macro in the DRAGAREA event callback to get an array of three integers containing control-relative mouse coordinates.

Multiple choice

c = HCHOICE_CTL( pf, pan, label, choices )
c = VCHOICE_CTL( pf, pan, label, choices )
An array of mutually exclusive boolean buttons.

c = TABCHOICE_CTL( pf, pan, label, choices )
Similar to HCHOICE and VCHOICE, but drawn to look like file folder tabs. These are generally used to switch between several sets of controls occupying the same space, like flipping to different tabbed notebook pages. You're responsible for erasing and drawing the appropriate sets of controls affected by tabbing.

c = POPUP_CTL( pf, pan, label, choices )
c = WPOPUP_CTL( pf, pan, label, choices, w )
c = POPDOWN_CTL( pf, pan, label, choices )
These create scrolling popup menus. The choices argument is a NULL-terminated string array, and the value of the control is a 0-based index into this array. The W version lets you set the width in pixels. POPUPs display the label to the left of the button and the current selection on the button face, and when opened, the position of the menu window is shifted so that the current selection is under the mouse cursor. POPDOWNs display the label on the button face and always open with the first menu item under the cursor.

c = CUSTPOPUP_CTL( pf, pan, label, w, nameFn, countFn )
Like WPOPUP, but uses callbacks to fill in the menu rather than a static string array. The menu can therefore be different each time the user opens it. The value of the control is a 0-based index into the current list of menu items. The callbacks are
int count( void *userdata )
char *name( void *userdata, int index )

The userdata is the CTL_USERDATA for the control. If you have more than one custom popup that uses the same callbacks, you can use this to distinguish between them. count returns the number of menu items, and name returns an item, given a 0-based index.

c = ITEM_CTL( pf, pan, label, globalFn, itemtype )
c = WITEM_CTL( pf, pan, label, globalFn, itemtype, w )
c = PIKITEM_CTL( pf, pan, label, globalFn, itemtype, w )
These are POPUPs that display a list of scene items. The globalFn is the GlobalFunc passed to your activation function. If you set the LWPanelFuncs globalFun field, you can get it from there. The control value is an LWItemID given as a pointer. In addition to the item types listed on the Item Info page, lwpanel.h defines LWI_IMAGE, for a list of images, and LWI_ANY, for a list of items of all types. Remember to include lwrender.h. PIKITEM behaves like POPDOWN.

c = CHANNEL_CTL( pf, pan, label, w, h );
Displays a tree containing the channels currently in the scene. The control value is an LWChannelID given as a pointer. You'll need lwenvel.h and the Channel Info global to set and make use of this value.

c = LISTBOX_CTL( pf, pan, label, w, ch, nameFn, countFn )
A listbox is a static rectangle containing a menu with a scrollbar. ch is the height of the menu area of the listbox in text lines (the number of visible menu items). The callbacks are of the same form as those for CUSTPOPUP. The value of the control is a 0-based index into the current list. After the control has been created, you must call the CON_SETEVENT macro, or the control set function with the CTL_USERDATA tag, even if your userdata is NULL. LISTBOX controls rely on this to perform some internal initialization.

c = MULTILIST_CTL( pf, pan, label, w, ch, nameFn, countFn, columnFn )
Like a listbox, but divides the text of each item into multiple columns. The countFn callback is the same as those for LISTBOX and CUSTPOPUP. The other two are of the following form.
char *mname( void *userdata, int index, int column )
int colwidth( void *userdata, int index )

The name callback returns a string, given 0-based indexes for the list position and column. The column callback returns the width of each column in pixels. You can have up to ten columns. Return 0 when the column index is greater than or equal to the number of columns you want.

After the control has been created, you must call the CON_SETEVENT macro, or the control set function with the CTL_USERDATA tag, even if your userdata is NULL. MULTILISTBOX controls rely on this to perform some internal initialization.

c = TREE_CTL( pf, pan, label, w, h, infoFn, countFn, childFn )
A tree control is like a listbox, but with the menu items organized hierarchically. Child nodes of the tree can be revealed, hidden and sometimes moved by the user. The value of a tree control is a pointer to a node in your tree data, which can be obtained and set through GET_ADDR() and SET_ADDR(). Trees don't prescribe the internal form of your tree data, but you have to be able to answer the questions about that data asked by the callbacks, which look like this.

void *child( void *userdata, void *node, int i )
Returns a pointer to the i-th child of the node. node is a pointer returned by a previous call to this callback, or NULL for the root.

int count( void *userdata, void *node )
Returns the number of child nodes for a given node.

char *info( void *userdata, void *node, int *flags )
Returns the name of the node. This is what is displayed in the tree control. If flags is non-zero, store the flags value in your node data, and if it's 0, retrieve it from your data and put it into flags.

void move( void *userdata, void *node, void *parent, int i )
Called when the user moves a node. The node becomes the i-th child of the parent node. This isn't in the TREE_CTL macro's argument list. TREE_CTL sets this to NULL, which prevents the user from moving your nodes. If you want to allow the user to move your nodes, use the code in the body of the TREE_CTL macro to create the control, putting your move callback in desc.tree.moveFn.

Color

c = RGB_CTL( pf, pan, label )
c = MINIRGB_CTL( pf, pan, label )
c = MINIHSV_CTL( pf, pan, label )
c = RGBVEC_CTL( pf, pan, label )
RGB (and HSV) levels for all of these controls are integers in the range 0 to 255. You can offer your users more sophisticated color selection by creating a button that calls the current colorpicker.

Files and directories

c = FILE_CTL( pf, pan, label, cw )
c = LOAD_CTL( pf, pan, label, cw )
c = SAVE_CTL( pf, pan, label, cw )
c = DIR_CTL( pf, pan, label, cw )
These combine a string edit field with a button that opens the file dialog. cw is the width of the edit field in characters. FILE is an older control type preserved for backward compatibility.

c = FILEBUTTON_CTL( pf, pan, label, w )
c = LOADBUTTON_CTL( pf, pan, label, w )
c = SAVEBUTTON_CTL( pf, pan, label, w )
c = DIRBUTTON_CTL( pf, pan, label, w )
Just the button for opening the file dialog. The label appears inside the button.

Drawing

c = TEXT_CTL( pf, pan, label, strings )
Use this to put static lines of text on the panel.

c = BORDER_CTL( pf, pan, label, w, h )
For drawing borders. If h is 0, the border is a horizontal divider.

c = CANVAS_CTL( pf, pan, label, w, h )
A bordered rectangle for convenient drawing. The width and height don't include the border, so the rectangle (0, 0, w-1, h-1) (relative to the control's HOTX and HOTY) lies inside the border.

c = OPENGL_CTL( pf, pan, label, width, height )
This creates and initializes an OpenGL window. LWPanels takes care of the platform specific setup for the window. You can draw in this window using standard OpenGL function calls during your event and draw callbacks for the control.

XPanels

c = XPANEL_CTL( pf, pan, label, xpanel )
This creates an xpanel window on your panel. Anything you can put into an xpanel can be put into an xpanel control.

Control Values

These macros call the LWControl get and set functions with the CTL_VALUE attribute, which is how you initialize and read back the values of your controls.

   SET_STR( ctl, buf, buflen )        GET_STR( ctl, buf, buflen )
   SET_INT( ctl, n )                  GET_INT( ctl, n )
   SET_ADDR( ctl, p )                 GET_ADDR( ctl, p )
   SET_FLOAT( ctl, f )                GET_FLOAT( ctl, f )
   SET_IVEC( ctl, x, y, z )           GET_IVEC( ctl, x, y, z )
   SETV_IVEC( ctl, nv )               GETV_IVEC( ctl, nv )
   SET_FVEC( ctl, x, y, z )           GET_FVEC( ctl, x, y, z )
   SETV_FVEC( ctl, fv )               GETV_FVEC( ctl, fv )

Control Attributes

These macros get and set other control attributes.

CON_X( ctl )
CON_Y( ctl )
CON_W( ctl )
CON_H( ctl )
Returns the position and size of the control.

CON_HOTX( ctl )
CON_HOTY( ctl )
CON_HOTW( ctl )
CON_HOTH( ctl )
Returns the position and size of the control's "hot" rectangle.

CON_LW( ctl )
Returns the label width.

CON_PAN( ctl )
CON_PANFUN( ctl )
Returns the panel the control belongs to and the LWPanelFuncs pointer. These have been modified in LW8.2 to support the LWT_POINTER data type for 64-bit compatability.

CON_MOUSEX( ctl )
CON_MOUSEY( ctl )
Returns mouse coordinates for the most recent mouse event.

MOVE_CON( ctl, x, y )
Set the positon of the control relative to the upper left corner of the panel.

CON_SETEVENT( ctl, eventFn, userdata )
Set the control's event function and CTL_USERDATA. Note: For some control types, calling this macro (or the control's set function with the CTL_USERDATA tag) has important side effects. Even if your userdata is NULL for those controls, you'll want to explicitly set it before opening the panel.

ERASE_CON( ctl )
REDRAW_CON( ctl )
GHOST_CON( ctl )
RENDER_CON( ctl )
UNGHOST_CON( ctl )
Draw or redraw the control.

ACTIVATE_CON( ctl )
Activate the control (give the control the input focus). This is only valid for edit field controls.

History

In LightWave® 7.0, LWPANELS_API_VERSION was incremented to 19 and the PANF_NOBUTT and PANF_RESIZE flags were added. In LightWave® 9.6, LWPANELS_API_VERSION was incremented to 20 and the DISTSLIDER_CTL and CTL_MOUSEBUTTON tag and enumerations were added.

Example

At least ten of the SDK samples use the LWPanels system. The panctl sample exercises LWPanels by creating an instance of every supported control type. It also demonstrates event handling for some of the interactive controls. The hello sample is the simplest example. It creates a panel with a single string control. The complete life cycle of this panel is repeated in the following code fragment.

   #include <lwserver.h>
   #include <lwpanel.h>

   LWPanelFuncs *panf;
   LWPanelID panel;
   LWControl *ctl;
   LWPanControlDesc desc;
   LWValue sval = { LWT_STRING };
   char edit[ 80 ] = "This is an edit field.";

   panf = global( LWPANELFUNCS_GLOBAL, GFUSE_TRANSIENT );
   if ( !panf ) return AFUNC_BADGLOBAL;

   panel = PAN_CREATE( panf, "Hello World!" );
   if ( !panel ) return AFUNC_BADGLOBAL;
   ctl = STR_CTL( panf, panel, "Edit Me", 40 );
   SET_STR( ctl, edit, sizeof( edit ));

   if ( panf->open( panel, PANF_BLOCKING | PANF_CANCEL ))
      GET_STR( ctl, edit, sizeof( edit ));

   PAN_KILL( panf, panel );