Futurebasic/Language/Reference/appearance button

Appearance Button edit

Statement edit

✔ Appearance χ Standard χ Console

Syntax edit

appearance button[#] [-] id&[, [state][, [value][, [min][, [max][, ¬
                     [title$][,[rect][, [type]]]]]]]

Revided edit

Feb 2002 (Release 6)

Description edit

The appearance button statement puts a new control in the current output window, or alters an existing control's characteristics. After you create a button using the appearance button statement, you can use the dialog function to determine whether the user has clicked it. You can use the button close statement if you want to dispose of the button without closing the window.

When you first create a button with a specific ID (in a given window), you must specify all the parameters up to and including type. If you later want to modify that button's characteristics, execute appearance button again with the same ID, and specify one or more of the other parameters (except type, which cannot be altered). The button will be redrawn using the new characteristics that you specified; any parameter that you don't specify will not be altered.

id& a positive or negative integer whose absolute value is in the range 1 through 2147483647. The number you assign must be different from all other scroll bars or buttons in that window. Negative values build invisible buttons. Positive values build visible buttons.
state The state may be:

_grayBtn (0/disabled)
_activebtn (1/default/active)
_markedBtn (2/selected)

value, min, max generally an integer value for the initial, minimum, and maximum values of a control
title$ a string expression. This parameter is not used to set the text of buttons defined with _kControlStaticTextProc or _kControlEditTextProc. See def SetButtonTextString for information on how this may be accomplished.


a rectangle in local window coordinates. You can express it in either of two forms:

Two diagonally opposite corner points.

Long integer expression or pointer variable which points to an 8-byte struct such as a Rect type.

type any of the many types listed in the following text.

New Things To Keep In Mind edit

On button creation, default values supplied for missing parameters (if any) are:

state _activeBtn
value 1
min 0
max 1
title$ null string

You can hide the control with either button -1 or appearance button -1 and you can deactivate the control with either button 1, _grayBtn or appearance button 1, _grayBtn. Buttons are commonly hidden and revealed as tab panes are brought into or removed from view. The same is true of panes that are changed in response to items such as group pop-up placards. To read an appearance button's value, use either x = button( id ) or x = button( id, _FBGetCtlRawValue )

Summary of Appearance Helpers edit

The following utility routines will help access information regarding the new appearance buttons:

actualSize = fn ButtonDataSize( btnID, part, tagName )
def GetButtonData( btnID, part, tagName, maxSize,¬ theData, actualSize )
def SetButtonTextString( btnID, theString )
theString = fn ButtonTextString$( btnID )
def SetButtonFocus( btnID )
def GetButtonTextSelection( btnID, selStart, selEnd )
def SetButtonTextSelection( btnID, selStart, selEnd )

Button Types edit

The Appearance Manager introduces many new control definitions. While it is not our intention to completely document all of Apple's new buttons, a few possible types are shown below with examples on how each might be implemented.

Push Buttons edit

Since the Appearance Manager works in both System 9 and OS X, you will see major differences in how each control is presented. Common push buttons are shown below in both versions.

<img src="a/appearancebutton01.gif" alt="appearancebuttonhtm01" height="496" width="328" border="0"> <img src="a/appearancebutton02.gif" alt="appearancebuttonhtm02" height="473" width="312" border="0">
Push buttons in OS X
Push buttons in System 9

So that you may see how each button was displayed, the following code shows that source used to generate the displays.

appearance button [#] [-] id&[, [state][, [value][,¬
[min][, [max][, [title$][,[rect][, [type]]]]]]]
appearance button  bRef,_activeBtn,0,0,1,¬
appearance button  bRef,_activeBtn,0,0,1,¬
appearance button  bRef,_activeBtn,0,0,1,¬
appearance button  bRef,_activeBtn,0,0,1,¬
// "value" is menu ID
appearance button  bRef,_activeBtn,101,0,1,¬
    _kControlBevelButtonSmallBevelProc + ¬
// max value is cicn ID
appearance button  bRef,_activeBtn,0,0,256,¬
    "_kControlPushButRightIconProc",@r, ¬
appearance button  bRef,_activeBtn,0,0,256,¬
// get rect from pict to determine button size
h = fn GetPicture( 256 )
long if h
  pR;8 = @h..picFrame%
  OffsetRect( pR, -pR.left, -pR.top )
  OffsetRect( pR, r.left, r.top )
// control "value" is pict ID
  appearance button  bRef,_activeBtn,256,0,1,¬
end if

Not all possible push buttons (and their variations) are shown here. For example, the control that displays an arrow to indicate the presence of a menu was built with a small bevel. It would have been created with a large bevel by using _kControlBevelButtonLargeBevelProc + _kControlBevelButtonMenuOnRight

Other button types that you may wish to investigate are:


Using Buttons to Group or Separate edit

FB buttons (which are Control Manager controls) can be grouped together, placed in placards or separated by lines. The following example creates buttons on a plain white background so that you may more easily see the drawing that is implemented by the control definition. We begin with the source code statements used to create the buttons.

appearance button bRef,_activeBtn,0,0,1,¬
appearance button bRef,_activeBtn,0,0,1,¬
appearance button bRef,_activeBtn,1,0,1,¬
appearance button bRef,_activeBtn,1,0,1,¬
// min value is menu ID
appearance button  bRef,_activeBtn,1,101,1,¬
appearance button bRef,_activeBtn,1,101,1,¬
appearance button bRef,_activeBtn,1,0,1,¬
appearance button bRef,_activeBtn,1,0,1,¬
<img src="a/appearancebutton03.gif" alt="appearancebuttonhtm03" height="435" width="346" border="0"> <img src="a/appearancebutton04.gif" alt="appearancebuttonhtm04" height="429" width="349" border="0">
Groups and Separators in OS X

Groups and Separators in System 9

Embedding Buttons edit

Part of the strength of Appearance Manager buttons is that one button may be embedded in another. By disabling or hiding the parent button (called a super control), all embedded controls would automatically be disabled or hidden. Each window has a primary control known as a root control. The following example builds a window with a parent radio group button. Inside of that parent are three radio buttons. We can determine which of the three buttons has been selected by getting the value (via the button() function) of the parent button.

dim r as Rect
dim pR as Rect
dim h as handle
dim bRef as long
dim err as OSErr
// setup
_btnHt = 20
_btnWd = 80
_btnMargin = 8
bRef = 1
<img src="a/appearancebutton05.gif" alt="appearancebuttonhtm05" height="152" width="103" border="0">
// create a window
SetRect( r, 0, 0, _btnWd_btnMargin_btnMargin, 120 )
appearance window 1,,@r
err = fn SetThemeWindowBackground( window( _wndPointer ),¬
   _kThemeActiveDialogBackgroundBrush, _zTrue )
// button #1 is the papa button
// note that the parent button has sufficient space so that
// it holds all embedded buttons within its own rectangle
SetRect( r ,_btnMargin, _btnMargin,¬
   _btnMargin_btnWd, (_btnMargin_btnHt)*3 )
appearance button bRef, _activeBtn, 0, 0, 1,¬
   "", @r, _kControlRadioGroupProc
bRef ++
SetRect( r, _btnMargin, _btnMargin, _btnMargin_btnWd,¬
   _btnMargin_btnHt )
appearance button bRef, _activeBtn, 0, 0, 1,¬
   "Radio 1", @r, _kControlRadioButtonProc
def EmbedButton( bRef, 1 )
bRef ++ : OffsetRect( r, 0, _btnHt_btnMargin )
appearance button bRef, _activeBtn, 0, 0, 1,¬
   "Radio 2", @r, _kControlRadioButtonProc
def EmbedButton( bRef, 1 )
bRef ++ : OffsetRect( r, 0, _btnHt_btnMargin )
appearance button bRef, _activeBtn, 0, 0, 1,¬
   "Radio 3", @r, _kControlRadioButtonProc
def EmbedButton( bRef, 1 )
local fn HandleDialog
  dim as long action,reference
  action = dialog( 0 )
  reference = dialog( action )
  long if action = _btnclick
    MoveTo( 8, 100 )
    print "Current Button "; button( 1 );
  end if
end fn
on dialog fn HandleDialog
until gFBQuit

Check Boxes edit

Other than the obvious differences in physical appearance, check boxes generally follow the same guidelines as they have for many years. One notable exception to this rule is the ability to create a mixed check box. This box contains a dash instead of a check mark to show that part, but not all, of the current selection has a specific feature. This adds a new possible maximum value of 2 (_kControlCheckBoxMixedValue = 2) to the control's range.

Possible check box values now include:


<img src="a/appearancebutton06.gif" alt="appearancebuttonhtm04" height="136" width="207" border="0">

Check Boxes

The buttons in the screen shot above were created using the following lines of code:

appearance button bRef, _activeBtn,¬
   _kControlCheckBoxUncheckedValue, 0,¬
   "Unchecked Check Box", @r, _kControlCheckBoxProc
appearance button bRef, _activeBtn,¬
   _kControlCheckBoxMixedValue, 0,¬
   "Mixed Value Check Box", @r, _kControlCheckBoxProc
appearance button bRef, _activeBtn,¬
   _kControlCheckBoxCheckedValue, 0,¬
   "Checked Check Box", @r, _kControlCheckBoxProc

Note: You cannot use button bRef, state to tick and untick group buttons of type _kControlGroupBoxCheckBoxProc and _kControlGroupBoxSecondaryCheckBoxProc. Use appearance button bRef,,state-1 instead. (button bRef,0 and button bRef,1 will however inactivate and activate the button respectively).

Time and Date Buttons edit

In addition to more common controls, the Appearance Manager can create buttons that manage dates and times. Special data structures are maintained to access the information from these controls, but by following a few simple examples, you can quickly master these skills.

The enhanced button function is useful for extracting complex data from controls. Two specific items come in to play:

ignored = button( btnRef, _FBGetControlDate )
ignored = button( btnRef, _FBGetControlTime )

Referencing either of these functions will fill a global date/time record named gFBControlLongDate and another named gFBControlSeconds. The variable named gFBControlSeconds is a signed 64 bit variable which may be saved in a file or used in any variable where compressed storage is required.

The format for gFBControlLongDate is that of a LongDateRec which follows the layout of the structure below:

begin record LongDateRec
   dim era        as short
   dim year       as short
   dim month      as short
   dim day        as short
   dim hour       as short
   dim minute     as short
   dim second     as short
   dim dayOfWeek  as short
   dim dayOfYear  as short
   dim weekOfYear as short
   dim pm         as short
   dim res1       as short
   dim res2       as short
   dim res3       as short
end record

After calling the button function to examine the contents of a control, you may extract portions of the date/time as follows:

dayOfMonth = gFBControlLongDate.day
thisYear   = gFBControlLongDate.year

Another variable is maintained that holds the text for a specific date/time control. The contents of gFBControlText (a Pascal string) are determined by the second button function parameter. When _FBGetControlDate is used, it is the control's date. When _FBGetControlTime is used, it is the control's time.

<img src="a/appearancebutton07.gif" alt="" height="283" width="442" border="0">
   Date/Time Buttons

In addition to setting specific types when creating a date/time control, you must set an initial value of one of the following:


The specific statements used to create the Time/Date example follow:

appearance button bRef, _activeBtn, 0, 0, 1,, @r,¬
appearance button bRef, _activeBtn, _kControlClockIsLive, 0, 1,,¬
   @r ,_kControlClockTimeSecondsProc
appearance button bRef, _activeBtn, _kControlClockNoFlags,¬
   0, 1,, @r, _kControlClockDateProc
appearance button bRef, _activeBtn, _kControlClockNoFlags,¬
   0, 1,, @r, _kControlClockMonthYearProc
appearance button bRef, _activeBtn, ¬
   0, 1,, @r, _kControlClockTimeSecondsProc

To extract and display the contents of a control, the following statements were created:

err = button( bRef, _FBgetControlTime )
edit field bRef, gFBControlText, @r

Wait States edit

The Appearance Manager provides several methods for telling the user that your application is busy with a task. These include chasing arrows, and finite and indeterminate progress bars.

<img src="a/appearancebutton10.gif" alt="IMAGE imgs/appearancebutton.htm15.gif" height="144" width="120">

Wait States

The chasing arrows control is easy to create and is self maintaining. Each time your program scans for events, the arrows are animated. The following statement creates a chasing arrows control:

appearance button bRef, _activeBtn, 0, 0, 1,, @r,¬

Progress bars are also easy to create, but you need to keep a couple of things in mind. First, the progress bar operates in a range of -32,768 to +32,767. If your task involves a greater number of steps, you will have to calculate a ratio to keep things within range. Second, the progress bar is updated by your program. This is as easy as setting a new value for the button, but it is code that you must write.

The minimum and maximum values for the control become the minimum and maximum values for the progress bar. The initial and current value show the current rate of progress. In the example above, the button was created using the following source:

appearance button bRef, _activeBtn, 50, 0, 100,, @r,¬

The minimum value was zero; maximum was 100. At the time of creation, the control value was 50, so the indicator shows colorization half way across the bar. If we wanted to indicate that the next step had been completed, we would use the following code:

appearance button bRef,, 51

Indeterminate progress bars are more complex. After the button is created, you must set the control's internal data to a new value. The following code shows how:

appearance button bRef, _activeBtn, 1, 0, 1,, @r,¬
dim b   as boolean
dim err as OSErr
b = _true
err = fn SetControlData( bRef, 0,¬
   _kControlProgressBarIndeterminateTag, sizeof( boolean ), @b )

Range Selectors (Sliders and Arrows) edit

<img src="a/appearancebutton08.gif" alt="appearancebuttonhtm02" height="349" width="146" border="0">There are many variations of the slider. Each begins with the simple type constant of _kControlSliderProc. Additional parameters are added to this constant to add features to the control. The following constants are available for slider variations:


To create a slider that uses an upward pointing indicator and has tickmarks, the following type would be used:

_kControlSliderProc + ¬
   _kControlSliderHasTickMarks + ¬

Vertical sliders are created by building the button with a vertical dimension that is greater than the horizontal dimension. The control definition handles the new orientation automatically.

Range Selectors Sliders and Little Arrows

The following source lines show how this display was created:

appearance button bRef, _activeBtn, 1, 1, 10,, @r,¬
appearance button bRef, _activeBtn, 10, 1, 10,, @r,¬
appearance button, _activeBtn, 1, 1, 10,, @r,¬
appearance button bRef, _activeBtn, 1, 1, 10,, @r,¬
appearance button bRef, _activeBtn, 10, 1, 10,, @r,¬
   _kControlSliderProc_kControlSliderHasTickMarks +¬

// vert orientation
appearance button bRef, _activeBtn, 10, 1, 10,, @r,¬
appearance button bRef, _activeBtn, 10, 1, 10,, @r,¬
appearance button bRef, _activeBtn, 10, 1, 10,, @r,¬
   _kControlSliderProc_kControlSliderHasTickMarks +¬

appearance button bRef, _activeBtn, 0, 0, 1,, @r,¬

When sliders are created, the number of tick marks is set by the initial value of the control. After the control is created, the value is reset to the control minimum. Sliders range from a minimum value of -32,768 to a maximum of +32,767. The number of tick marks is something that you need to determine by balancing the size of the control against the range of the control's minimum/maximum value.

The little arrows (shown in the screen shot above) are used to increment and decrement a related visual counter (usually an edit field with a specific range of numbers). The current version of the OS X control definition is intolerant of variations in the value used for the height of this type of control. Our tests show that it must be exactly 22 pixels tall. Other values offset the arrows inside of the beveled area or, in more extreme cases, can place the arrows entirely outside of the beveled area.

Pop-Up Menus edit

There are two distinct types of pop-up menus: beveled, and standard. Both are valid types and the particular use of one over the other is something that should be guided by your individual application and by Apple's Human Interface Guidelines. Beveled buttons are created as follows:

appearance button bRef, _activeBtn, menuID, 0, 1,¬
   @r, _kControlBevelButtonSmallBevelProc + ¬

When bevel-button menus are created, the initial value for the control is the resource ID number of the menu. Three specific button function commands may be used to extract information from the control.

handle       = button( bRef, _FBgetBevelControlMenuHandle )
currentItem  = button( bRef, _FBgetBevelControlMenuVal )
previousItem = button( bRef, _FBgetBevelControlLastMenu )
<img src="a/appearancebutton11.gif" alt="" height="237" width="283">

Pop-Up Menu Buttons

Standard pop-up buttons follow slightly different syntax. When creating, the minimum value specifies the menu resource ID and the maximum value is the width of the title for the menu. Passing in a menu ID of -12345 causes the popup not to try and get the menu from a resource. Instead, you can build the menu and later stuff the menuhandle field in the popup data information (using def SetButtonData( id&, _kControlMenuPart, _kControlPopupButtonMenuHandleTag, sizeof( handle ), @yourMenuHndl )). You can pass -1 in the max parameter to have the control calculate the width of the title on its own instead of guessing and then tweaking to get it right. It adds the appropriate amount of space between the title and the popup. A maximum value of zero means, "Don't show the title."

After creation you might need to change the value, minimum and maximum to the correct settings for your pop-up menu with:

appearance button id&,, value, min, max

The standard pop-up button menu in the above illustration was created with the following code:

appearance button bRef, _activeBtn, 0, 101, -1, "Pop Title:"¬
   ,@r, _kControlPopUpButtonProc

A single button function provides access to the menu handle. Remember: standard and beveled pop-up menus do not use the same button function constants.

menuHandle = button( bRef,  _FBgetControlMenuHandle )

You can retrieve the current pop-up menu item with:

mItem = button( bRef )

List Boxes edit

Lists generally use an auxiliary resource to define their format. The resource type used is 'ldes' and a definition for it can be created using Resourcerer 2.4 or later or by creating a template in any resource editor. You may also format a handle to match the _"ldes" record and save that handle as a resource. The resource ID for the ldes is passed in the 'value' parameter when creating the control. You may pass zero in value which would tell the List Box control to not use a resource. The list will be created with default values, and will use the standard LDEF (0). You can change the list by getting the list handle. You can set the LDEF by using the tag below (_kControlListBoxLDEFTag) in conjunction with def SetButtonData.

A list box resource is defined as follows:

begin record ldes
   dim versionNumber   as short
   dim numberOfRows    as short
   dim numberOfColumns as short
   dim cellHeight      as short
   dim cellWidth       as short
   dim hasVertScroll   as boolean
   dim filler1         as byte
   dim hasHorizScroll  as boolean
   dim filler2         as byte
   dim LDEFresID       as short
   dim hasSizeBox      as boolean
   dim reserved        as byte
end record

The following example creates a list box from a resource with an ID of 256. Then it fills the list with item text.

To simplify this example, Resourcerer was used to create the list box resource.

<img src="a/appearancebutton12.gif" alt="IMAGE imgs/appearancebutton.htm18.gif" height="282" width="329">

Creating an ldes Resource

appearance button bRef, _activeBtn, 256, 0, 1,¬
   "List Box", @r, _kControlListBoxProc
dim cH          as handle        //control handle
dim @bufferSize as long
dim @lH         as handle        //list handle
dim y,t$
dim @celly,cellX                 //cell "point" record
cH = button&( bRef )
err = fn GetControlData( cH, 0,¬
   _kControlListBoxListHandleTag, ¬
   sizeof( handle ),  lH, bufferSize )
cellX = 0
for cellY = 0 to 9
  t$ = "Item #" + mid$( str$( cellY + 1 ), 2 )
  LSetCell( @t$[1], t$[0], celly, lH )
<img src="a/appearancebutton09.gif" alt="" height="148" width="166" border="0">

List Box

Tab Buttons edit

Tab buttons will require more work than other controls. This stems from the fact that tabs are really several controls that act in unison. First is the main tab control. When this is created you specify the number of tabs that will be present by setting the max value of the control. It is generally better to create the tab button invisibly (by using a negative button reference number) then show it by issuing a button statement with the positive version of the reference number.

After the initial shell is built for the tab, you must set the title for each tab using def SetButtonData. Then a user pane is inserted for each tab. These are embedded in the tab shell using def EmbedButton. Buttons that will reside in each user pane are created and embedded in the user pane.

When a dialog event is encountered, the value of the tab shell button corresponds to the position of the clicked tab in the tab list. Your program must loop through each user pane and show or hide them so that the display matches the clicked tab.

Study the following example to see how a working tab button is created. Be sure to note the simple dialog handler that maintains the buttons.

<img src="a/appearancebutton13.gif" alt="" height="233" width="312" border="0">

Tab Buttons

There are many styles of tab buttons:


This example uses _kControlTabSmallProc, but you should experiment with other types to see the results.

dim r       as Rect
dim x       as long
dim bRef    as long
dim infoRec as ControlTabInfoRec
// Names of the individual tabs
_tabCount = 3
dim tabTitles$(_tabCount)
tabTitles$(1) = "One"
tabTitles$(2) = "Two"
tabTitles$(3) = "Three"
// create a window
SetRect( r, 0, 0, 300, 200 )
appearance window 1, "Tabs", @r, _kDocumentWindowClass
def SetWindowBackground( _kThemeActiveDialogBackgroundBrush,¬
  _zTrue )
  Button 100 is the tab 'shell'. In this example, it is made to be the full size
  of the window, less a small margin. Buttons 1, 2, & 3 will be
  the embedded user panes that contain information to be displayed
  for each tab. A tab control is usually built as invisible. This is because
  the information contained in the tabs will be modified
  as the control is being constructed. Making it visible after all
  modifications have been completed provides a cleaner window build.
_tabBtnRef = 100
_btnMargin = 8
InsetRect( r, _btnMargin, _btnMargin )
appearance button -_tabBtnRef, 0, 0, 2, _tabCount,, @r,¬
  Fix the tab to use a small font. This is not a requirement, but it is
  information which many will find useful.
dim cfsRec as ControlFontStyleRec
cfsRec.flags = _kControlUseSizeMask
cfsRec.size = 9
def SetButtonFontStyle(  _tabBtnRef, cfsRec )
  Adapt a rectangle that can be used for the content area of each tab.
InsetRect( r, _btnMargin, _btnMargin )
r.top += 20
// Loop thru the tabs and set up individual panes
for x = 1 to _tabCount
   infoRec.version     = _kControlTabInfoVersionZero
   infoRec.iconSuiteID = 0
   infoRec.Name        = tabTitles$(x)
   def SetButtonData( _tabBtnRef, x, _kControlTabInfoTag, ¬
     sizeof( infoRec ), infoRec )
     Each of these panes is a button that is embedded in the
     tab button. The first    one will be visible. All others will
     be invisible because information from only one tab at a time
     can be viewed.
     Remember: negative button reference numbers make
     invisible buttons.
     Once a new pane button (_kControlUserPaneProc) is created,
     it is embedded into the larger tab button.
   if x != 1 then bRef = -x else bRef = x
   appearance button bRef,,¬
   _kControlSupportsEmbedding,,,, @r,¬
   def EmbedButton( x, _tabBtnRef )
  Now we have a tab shell (_tabBtnRef = 100) and in it we have
  embedded three tab panes (1,2, and 3).
  To demonstrate how these can contain separate info, we will
  place a simple button in each of the three panes.
     Button 10 in pane 1
     Button 20 in pane 2
     Button 30 in pane 3
InsetRect( r, 32, 32 )
r.bottom = r.top + 24
r.right = r.left + 128
appearance button 10, _activeBtn,,,,¬
  "Pane #1", @r, _kControlPushButtonProc
def EmbedButton( 10, 1 )
OffsetRect( r, 8, 8 )
appearance button 20, _activeBtn,,,,¬
  "Pane #2", @r, _kControlPushButtonProc
def EmbedButton( 20, 2 )
OffsetRect( r, 8, 8 )
appearance button 30, _activeBtn,,,,¬
  "Pane #3", @r, _kControlPushButtonProc
def EmbedButton( 30, 3 )
button _tabBtnRef, 1 // make visible
  Only one event (a button click in the tab shell button)
  gets a response from out dialog routine. The value returned
  (1,2, or 3) corresponds to buttons 1,2, or 3 that were
  embedded into the tab parent.
  Our only action is to show (BUTTON j) or hide (BUTTON -j)
  the proper tab pane. All controls embedded in those panes
  will automatically be shown or hidden.
local fn doDialog
  dim as long action, reference, j
  action    = dialog( 0 )
  reference = dialog( action )
  long if action == _btnClick and reference == _tabBtnRef
    for j = 1 to _tabCount
      long if j == button( _tabBtnRef )
        button j
        button -j
      end ifF
  end if
end fn
on dialog fn doDialog
until gFBQuit

Notes edit

No special notes.

See Also edit

button&; button function; button close; scroll button; dialog function; def EmbedButton; def SetButtonData

Language Reference