eXept Software AG Logo

Smalltalk/X Webserver

Documentation of class 'Button':

Home

Documentation
www.exept.de
Everywhere
for:
[back]

Class: Button


Inheritance:

   Object
   |
   +--GraphicsMedium
      |
      +--DisplaySurface
         |
         +--SimpleView
            |
            +--View
               |
               +--Label
                  |
                  +--Button
                     |
                     +--ArrowButton
                     |
                     +--LabeledBooleanView
                     |
                     +--LinkButton
                     |
                     +--PopUpList
                     |
                     +--Toggle
                     |
                     +--ViewWithAcceptAndCancelBar::AcceptAndCancelBar::ButtonWithHelpText

Package:
stx:libwidg
Category:
Views-Interactors
Version:
rev: 1.199 date: 2023/11/11 15:21:35
user: stefan
file: Button.st directory: libwidg
module: stx stc-classLibrary: libwidg

Description:


Buttons are Labels which do something when pressed/released.
Since they inherit from Label, read the documentation on Labels to 
see how color, layout, font, image or labelString are changed.

Buttons (and also instances of subclasses, such as toggles, checkToggles
and radioButtons) perform some action when pressed, and optionally when
released. Due to historic reasons, ST/X buttons support two mechanisms:

Actionblock operation:
  the button has an actionBlock (actually, it has two: a pressAction and
  a releaseAction) which is evaluated, when the button is pressed.
  The user of a button can set these action blocks via the #action:,
  #pressAction: or #releaseAction: messages.

Model-View interaction:
  buttons with a model will send change-messages to the model and also react 
  to changes in the model.
  You have to define the buttons labelMessage and an aspectSymbol.
  When changed, the model should send 'self changed:<aspect>' if it wants the
  label to change and return a string from the labelSymbol-message.
  By default, the labelMsg is nil, therefore no update of the label is done.
  (this behavior is inherited from label, see documentation there)

  When pressed, the button sends a changeMessage to the model.
  If the changeMsg is for a one-argument message, the current
  press-state is passed as argument (i.e. true if pressed, false if released).
  By default, the change-Message is #value: (for ST-80 compatibility).

    button model:aModel.
    button aspect:aspectSymbol.
    button change:changeSymbol.
    button labelMessage:labelSymbol.

    model sends #changed:aspectSymbol
    ---> button will redraw its label from value returned by model>>labelSymbol

    button changes state:
    ---> button sends changeSymbol / changeSymbol:state to the model

  By default (as inherited), the labelMsg is nil; therefore, buttons
  do not try to acquire a new labelString from the model.
  If you want this behavior, you must set labelMsg and aspectMsg
  as appropriate.

labels
  Originally, labels only had a label- and disabledLabel attributes (both
  had to be explicitly set via setter methods).
  Later the model- and labelChannel interfaces were added; model interface
  for compatbility with other smalltalks, where a single multi-aspect model
  might be used, the channels were added to support visualWorks style of multiple
  single-aspect holders. This final version is also what the UIBuilder is working
  against. The other mechanisms are still present, but disabled if a newer mechanism
  is used (i.e. if a labelChannel has been given, the label and disabledLabel instvars
  are ignored).
  All of this is here to provide backward compatibility for existing customers
  and st/x programs (which is a good thing to have, b.t.w).


See examples.


[Instance variables:]

  activeLogo              <StringOrImage> logo to show when active (pressed)
  passiveLogo             <StringOrImage> logo to show when passive (released)
                                          default is nil for both, so the normal logo is used
                                          (see superclass: Label)
  onLevel                 <Integer>       level when pressed (3D only) (default: depends on style)
  offLevel                <Integer>       level when released (3D only) (default: depends on style)
  disabledFgColor         <Color>         color used to draw logo when disabled (default: depends on style)
  activeFgColor           <Color>         color to draw logo when pressed (default: depends on style)
  activeBgColor           <Color>         bg color when pressed (default: depends on style)
  enteredFgColor          <Color>         fg color to draw logo when cursor entered (default: depends on style)
  enteredBgColor          <Color>         bg color when cursor entered (default: depends on style)

  isReturnButton          <Boolean>       true if this button is also activated by the
                                          return key - if true, it will draw a return-bitmap 
                                          in addition to the logo (default: false)

  defaultable             <Boolean>       true, if this button can become a returnButton.
                                          (computes its default extent differently)

  shadowForm              <Form>          form to display in addition to buttons label (returnbutton only)
  lightForm               <Form>          light part of shadowForm (returnbutton only)

  formColor               <Color>         color to draw form with (returnbutton only)
  formShadowColor         <Color>         color for shadowing the form (3D only & return)
  formLightColor          <Color>         color for lighting the form (3D only)


[styleSheet parameters:]

  buttonActiveLevel       <Integer>       level when on (ignored in 2D styles)
  buttonPassiveLevel      <Integer>       level when off (ignored in 2D styles)
  buttonBorderWidth       <Integer>       default borderwidth
  buttonEdgeStyle         <Symbol>        style of edges (currently #soft or nil)
  buttonFont              <Font>          font to use for textual labels
  buttonForegroundColor   <Color>         color to draw foreground pixels (i.e. the string)
  buttonBackgroundColor   <Color>         color to draw background pixels
  buttonDisabledForegroundColor <Color>   same when disabled
  buttonDisabledBackgroundColor <Color>   same when disabled
  buttonEnteredForegroundColor  <Color>   same when mouse pointer is in
  buttonEnteredBackgroundColor  <Color>   same when mouse pointer is in
  buttonActiveForegroundColor   <Color>   same when button is active
  buttonActiveBackgroundColor   <Color>   same when button is active
  buttonShadowColor             <Color>   shadow color for edgaes (ignored in 2D styles)
  buttonLightColor              <Color>   light color for edgaes (ignored in 2D styles)
  buttonHalfShadowColor         <Color>   half shadow color; for soft edges only
  buttonHalfLightColor          <Color>   half light color; for soft edges only

  buttonReturnButtonHasImage    <Boolean> if true, return-buttons draw a return-key image
  buttonReturnButtonHasBorder   <Boolean> if true, return-buttons show a border

copyright

COPYRIGHT (c) 1989 by Claus Gittinger All Rights Reserved This software is furnished under a license and may be used only in accordance with the terms of that license and with the inclusion of the above copyright notice. This software may not be provided or otherwise made available to, or used by, any other person. No title to or ownership of the software is hereby transferred.

actionBlocksVersusModelChanges

You may ask yourself, why ST/X supports two different ways to trigger a buttons action (also true for sliders, selectionInListViews, toggles etc.) and when to use which mechanism. There are two reasons: 1) history - historically, ST/X started with only actionBlocks (aka callbacks) Basically, these provide the most general functionality (you can of course send change-messages from those blocks) Change-message notification was added later, to make porting of existing (VisualWorks) applications easier. 2) for many (simple) applications, it is often easier, to simply define an action block. This is especially true, if the button triggers some action (such as ok-/abort-buttons) and does NOT change some model's internal value. (other Smalltalks provide extra adapter-models for this, in which you can set actionBlocks in much the same way). In general: use model-change-messages, iff multiple views are possibly open on the same domain model, and changes can occur from multiple places. Or if a change has to be forwarded to multiple other objects (possible not foreseen). For simplicity, better use actionBlocks for trigger-like actions (such as closing a view, ok/abort etc.) iff there is only a single entity which is interested in that button press.

Class protocol:

ST-80 instance creation
o  switch
ST-80 compatibility: create & return a new radioButton

o  toggle
ST-80 compatibility: create & return a new toggle.

Usage example(s):

     Button toggle label:'press me';
		   model:((PluggableAdaptor on:(Point new))
			    getSelector:#x putSelector:#x:)

o  trigger
ST-80 compatibility: create & return a new button, which
triggers on press.

defaults
o  defaultDisabledForegroundColor

o  returnFormOn: aDevice
return the form used for the return arrow in non-3D;
cache the one for Display for the next round.

o  returnLightFormOn: aDevice
return the form used for the return arrow light pixels (3D only);
cache the one for Display for the next round

o  returnShadowFormOn: aDevice
return the form used for the return arrow shadow pixels (3D only);
cache the one for Display for the next round.

o  updateStyleCache
extract values from the styleSheet and cache them in class variables

instance creation
o  abortButton
since abort-buttons are very common, here is a convenient
method to create one ...

o  abortButtonIn: aView
since abort-buttons are very common, here is a convenient
method to create one ...

o  form: aForm action: aBlock in: aView
create and return a new Button with icon-label, aForm
and pressAction, aBlock. Button is placed into aView.
OBSOLETE: this is for backward compatibility;
you can now pass an image or form in the #label:action:in: message.

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  label: aLabel action: aBlock
create and return a new Button with text-label, aString
and pressAction, aBlock.

o  label: aLabel action: aBlock in: aView
create and return a new Button with text-label, aString
and pressAction, aBlock. Button is placed into aView.

o  okButton
since ok-buttons are very common, here is a convenient
method to create one ...

o  okButtonIn: aView
since ok-buttons are very common, here is a convenient
method to create one ...

o  on: aModel label: aLabel
create and return a new Button with text-label, aString
and a boolean model.


Instance protocol:

accessing-behavior
o  action
return the action

o  action: aBlock
convenient method: depending on the setting of controllers triggerOnDown flag,
either set the press-action and clear any release-action or
vice versa, set the release-action and clear the press-action.

o  autoRepeat
turn on autorepeat OBSOLETE; use #autoRepeat:

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  autoRepeat: aBoolean
turn on/off autorepeat

o  doubleClickAction
return the doubleClickAction; that's the block which gets evaluated
when the button is double-clicked (if non-nil).
Seldom used with buttons

o  doubleClickAction: aBlock
define the action to be performed on doubleClick
Seldom used with buttons.

o  enabled
return true if the button is enabled

o  enabled: aBoolean
enable/disable the button

o  pressAction
return the pressAction; that's the block which gets evaluated
when the button is pressed (if non-nil)

o  pressAction: aBlock
define the action to be performed on press

o  releaseAction
return the releaseAction; that's the block which gets evaluated
when the button is released (if non-nil)

o  releaseAction: aBlock
define the action to be performed on release

accessing-channels
o  enableChannel
return a valueHolder for enable/disable

o  enableChannel: aValueHolderForBoolean
set the valueHolder used for enable/disable

o  pressChannel
return the pressChannel

o  pressChannel: aBlock
set the pressChannel

o  releaseChannel
return the releaseChannel

o  releaseChannel: aBlock
set the releaseChannel

accessing-look
o  activeBackgroundColor
return the background color to be used when pressed

o  activeBackgroundColor: aColor
set the background color to be used when pressed

o  activeForegroundColor
return the foreground color to be used when pressed

o  activeForegroundColor: aColor
set the foreground color to be used when pressed

o  activeForegroundColor: fgColor backgroundColor: bgColor
set both fg and bg colors to be used when pressed

o  activeLevel
return the level of the button when pressed

o  activeLevel: aNumber
set the level of the button when pressed (i.e. how deep)

o  activeLevel: activeLevelNumber passiveLevel: passiveLevelNumber
set the level of the button when pressed (i.e. how deep)

o  activeLogo: anImageOrString
define the logo to be displayed while active -
this is optional; the default is to display the same
(logo) in both pressed and released states.

o  activeLogo: activeImageOrString passiveLogo: passiveImageOrString
define the logo to be displayed while active and passive -
this is optional; the default is to display the same
(logo) in both pressed and released states.

o  allViewBackground: something if: condition
no longer ignored here

o  beImageButton
make mySelf an image button - that is turn off all levelChanges
and logo spacing. This should be sent for buttons which use both
passive and active logos, and the 3D effect comes from the bitmaps
(i.e. windows buttons).

o  beReturnButton
show show a return-key image after the label.
Same as 'isReturnButton:true' for ST-80 compatibility.

o  beRoundRadioButton
setup myself for a round radioButton look.
this is a private method; do not use it. It is going to move into the
RadioButton class.

o  defaultable
return true, if the receiver is defaultable

o  defaultable: aBoolean
set/clear the defaultable attribute. If defaultable,
the preferredExtent is computed to include any additional
space required when the receiver is a returnButton.

o  disabledForegroundColor
return the foreground color used when the button is disabled

o  disabledForegroundColor: aColor
set the foreground color used when the button is disabled

o  disabledLogo: anImageOrString
define the logo to be displayed while disabled -
this is optional; the default is to display the same
(logo) in both pressed and released states.
However, the disabled logo is ignored if a labelChannel has
been set - then that value has priority.

o  edgeStyle: aSymbol
set the edgestyle - currently only #soft or nil

o  enterLevel
return the level to be used when the mouse
pointer enters the button area

o  enterLevel: aNumber
set the level to be used when the mouse
pointer enters the button area

o  enteredBackgroundColor
return the background color to be used when the mouse
pointer enters the button area

o  enteredBackgroundColor: aColor
set the background color to be used when the mouse
pointer enters the button area

o  enteredForegroundColor
return the foreground color to be used when the mouse
pointer enters the button area

o  enteredForegroundColor: aColor
set the foreground color to be used when the mouse
pointer enters the button area

o  enteredLogo: anImageOrString
define the logo to be displayed while the mousePointer is in the button -
this is optional; the default is to display the same
(logo) in both entered and normal states.
However, the entered logo is ignored if a labelChannel has
been set - then that value has priority.

o  focusLogo: anImageOrString
define the logo to be displayed while active -
this is optional; the default is to display the same
(logo) in both pressed and released states.
However, the focus logo is ignored if a labelChannel has
been set - then that value has priority.

o  isReturnButton: aBoolean
show/don't show a return-key image after the label

o  label: aStringOrImageOrForm
(comment from inherited method)
set the labelString or image;
adjust extent if not already realized and not fixedSize;
also redraw

o  leaveLevel
return the level to be used when the mouse
pointer leaves the button area

o  leaveLevel: aNumber
set the level to be used when the mouse
pointer leaves the button area

o  loseDefault
ST-80 compatibility - clear isReturnButton attribute

o  offLevel
return the level of the button when released.
Historic leftover; use #passiveLevel.

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  offLevel: aNumber
set the level of the button when released (i.e. how deep).
Historic leftover; use #passiveLevel:.

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  onLevel
return the level of the button when pressed.
Historic leftover; use #activeLevel.

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  onLevel: aNumber
set the level of the button when pressed (i.e. how deep).
Historic leftover; use #activeLevel:.

** This is an obsolete interface - do not use it (it may vanish in future versions) **

o  passiveLevel
return the level of the button when released

o  passiveLevel: aNumber
set the level of the button when not pressed (i.e. how high)

o  passiveLogo: anImageOrString
define the logo to be displayed while inactive -
this is optional; the default is to display the same
(logo) in both pressed and released states.
However, the inactive logo is ignored if a labelChannel has
been set - then that value has priority.

o  takeDefault
ST-80 compatibility - set isReturnButton attribute

o  viewBackground: aColorOrForm
on:device

changing state
o  toggle
toggle and perform the action

o  toggleNoAction
toggle, but do NOT perform any action - can be used to change a toggle
under program control (i.e. turn one toggle off from another one)

o  turnOff
turn the button off (if not already off) do NOT perform actions/change notifications

o  turnOffWithAction
turn the button off (if not already off) and perform any action/change notification

o  turnOffWithoutRedraw
turn the button off - no redraw but perform any action/change notification

o  turnOn
turn the button on (if not already on) - do NOT perform any action/notification

o  turnOnWithAction
turn the button on (if not already on) and perform any change actions/notifications

o  turnOnWithoutRedraw
turn the button on - no redraw but perform any change actions/notifications

focus handling
o  showFocus: explicit
the button got the keyboard focus
(either explicit, via tabbing; or implicit, by pointer movement)
- change any display attributes as req'd.

o  showNoFocus: explicit
the button lost the keyboard focus
(either explicit, via tabbing; or implicit, by pointer movement)
- change any display attributes as req'd.

o  wantsFocusWithButtonPress
no, do not catch the keyboard focus on button click

initialization
o  defaultControllerClass

o  fetchDeviceResources
fetch device colors, to avoid reallocation at redraw time

o  initBorderStyle

o  initCursor
set up a hand cursor

o  initEvents
(comment from inherited method)
will be sent by create - can be redefined by subclasses to enable
view events

o  initStyle
setup viewStyle specifics

o  initialize
must be called if redefined

o  reinitialize
(comment from inherited method)
this is called right after snapIn

native widget support
o  nativeWindowType
return a symbol describing my native window type
(may be used internally by the device as a native window creation hint,
if the device supports native windows)

o  win32nativeWMCommand: commandID

private
o  computeLabelOrigin
compute the origin of the text - if I am a returnButton,
shift it somwehat to the right (we have already allocated the
real estate, since computeLabelSize returned some extra space
before).

o  getLabelFromLabelChannel

o  rawLabelSizeOf: aLogo
compute the extent needed to hold the label (plus the return form)

o  shiftLabelWhenPressed

queries
o  computePreferredExtent
(comment from inherited method)
compute my preferred extent - this is the minimum size I would like to have

o  extraMarginForBorder
(comment from inherited method)
some (round) borders may need more space

o  is3D
return true, if the receiver is a 3D style view

o  isDefault
return true, if this is a default OK button

o  isOn
return true, if this button is currently pressed

o  isReturnButton
return true, if this is a return button (showing a return-key-icon)

o  isTriggerOnDown
return true, if I fire on press (instead of fire-on-release)

o  specClass
returns my spec class (for UI editor)

Usage example(s):

redefined, since the name of my specClass is nonStandard (i.e. not ButtonSpec)

redrawing
o  drawBottomEdge
draw bottom 3D edge into window frame

o  drawEdges
draw all of my 3D edges

o  drawLeftEdge
draw left 3D edge into window frame

o  drawRightEdge
draw right 3D edge into window frame

o  drawTopEdge
draw top 3D edge into window frame

o  drawWith: fg and: bg
redraw myself with fg/bg. Use super to draw the label, add
the return-arrow here.

o  redraw
redraw the button.
That's like redrawing a label, but uses different colors when pressed
or entered.

o  redrawIfPressed
redraw the button, but only if visible and currently being pressed

o  redrawX: x y: y width: w height: h
(comment from inherited method)
redraw part of myself immediately, given logical coordinates
(if transformation is nonNil)
The default here is to redraw everything
- subclasses usually redefine this, adding more intelligence

o  showActive
redraw myself as active (i.e. busy)

o  showPassive
redraw myself as passive


Examples:


You don't have to normally care for all the internals (they allow many variations though). For buttons with a stringLabel, the typical use is: b := Button label:'some logo' in:aView. b action:[ .. things to do, when pressed ... ] and for bitmap/image buttons its: b := Button label:someImage in:aView. b action:[ .. things to do, when pressed ... ] Of course, you can also setup things the ST-80 way, in first creating the button and later add it to some superview: b := Button new. b label:someImage. b action:[ .. things to do, when pressed ... ] ... aSuperView add:b in:<frameRectangle>. Although you can specify most of the look of a button, you should use the defaults in most applications. As you specify more things in your program, the styleSheet settings are more and more ignored. Also, even though it might look fancy, colorful button panels are usually not a good GUI design; they will attract the users attention - possibly to things which are not worth it. Finally, if you use fancy colors, always think of those poor users without color displays - the styleSheet allows those people to adjust things, while hard-coded colors cannot be fixed without changing the code. a simple button with default settings and no action: (you may want to try it with different viewStyle settings in effect):
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button in:top.
      b label:'hello'.

      top open.
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button in:top.
      b label:'press\me' withCRs.

      top open.
give it some action (watch the transcript):
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button in:top.
      b label:'hello'.
      b action:[Transcript flash].

      top open.
there is also a combined instance creation message:
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button label:'hello' action:[Transcript flash] in:top.

      top open.
a return button (in dialogs, you should setup things to have the return-key perform the ok action, and mark the corresponding button as being a returnButton):
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button label:'hello' action:[Transcript flash] in:top.
      b beReturnButton.
      top open.
multiple buttons in a panel:
      |top panel b1 b2 b3|

      top := StandardSystemView new.
      top extent:300@100.

      panel := HorizontalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.

      b1 := Button label:'one'   in:panel.
      b2 := Button label:'two'   in:panel.
      b3 := Button label:'three' action:[Transcript flash] in:panel.

      top open.
enabling/disabling buttons via explicit messages:
      |top panel b1 b2 b3|

      top := StandardSystemView new.
      top extent:300@100.
      panel := HorizontalPanelView origin:0.0@0.0 corner:1.0@1.0 in:top.

      b1 := Button label:'enable'  action:[b3 enable] in:panel.
      b2 := Button label:'disable' action:[b3 disable] in:panel.
      b3 := Button label:'flash'   action:[Transcript flash] in:panel.
      top open.
enabling/disabling buttons via implicit value change of a valueHolder: The two views are only indirectly coupled (via the valueHolder); not knowing about each other.
      |top panel ena t b1 b2 b3 check|

      top := StandardSystemView new.
      top extent:300@100.

      panel := HorizontalPanelView origin:0.0 @ 0.0 corner:1.0 @ 1.0 in:top.

      ena := false asValue.

      t := Toggle label:'enable' in:panel.
      t model:ena.

      b1 := Button label:'button1' in:panel.
      b1 controller enableChannel:ena.

      b2 := Button label:'button2' in:panel.
      b2 controller enableChannel:ena.

      b3 := Button label:'button3' in:panel.
      b3 controller enableChannel:ena.

      top open.

      check := CheckBox model:ena.
      check label:'also enable'.
      check extent:(check preferredExtent + (5@5)).
      check open
changing colors:
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button label:'hello' in:top.
      b action:[Transcript flash].
      b activeForegroundColor:(Color white).
      b activeBackgroundColor:(Color red).

      top open.
changing more colors:
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button label:'hello' in:top.
      b action:[Transcript flash].
      b enteredForegroundColor:(Color red).
      b enteredBackgroundColor:(b backgroundColor).
      b activeForegroundColor:(Color white).
      b activeBackgroundColor:(Color red).

      top open.
button with an image and different colors:
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button in:top.
      b label:(Image fromFile:'bitmaps/SBrowser.xbm').
      b action:[Transcript flash].
      b enteredForegroundColor:(Color green darkened).
      b enteredBackgroundColor:(b backgroundColor).
      b activeForegroundColor:(Color white).
      b activeBackgroundColor:(Color red).

      top open.
changing the image when pressed:
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button in:top.
      b passiveLogo:(Image fromFile:'bitmaps/SBrowser.xbm').
      b activeLogo:(Image fromFile:'bitmaps/CBrowser.xbm').
      b action:[Transcript flash].
      b enteredForegroundColor:(Color red).
      b enteredBackgroundColor:(b backgroundColor).
      b activeForegroundColor:(Color white).
      b activeBackgroundColor:(Color red).

      top open.
well, even that is possible (but you should NEVER do it):
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button in:top.
      b passiveLogo:(Image fromFile:'../../goodies/bitmaps/xpmBitmaps/misc_tools/email.xpm').
      b active:'start'.
      b action:[Transcript flash].
      b enteredForegroundColor:(Color red).
      b enteredBackgroundColor:(b backgroundColor).
      b activeForegroundColor:(Color white).
      b activeBackgroundColor:(Color red).
      top open.
more playing with colors:
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button in:top.
      b label:(Image fromFile:'../../goodies/bitmaps/xbmBitmaps/TicTacToe.xbm').
      b action:[Transcript flash].
      b foregroundColor:(Color red:0 green:80 blue:20) darkened.
      b backgroundColor:(Color grey:10).
      b enteredForegroundColor:(Color red).
      b activeForegroundColor:(Color red).
      b activeBackgroundColor:(Color grey:50).
      top open.
fire on press (buttons in scrollbars do this, while normal buttons fire on release). To give the user a chance to change her mind and leave the button without action, you should not change the default behavior and NOT use triggerOnDown-buttons in most applications:
      |top b|

      top := StandardSystemView new.
      top extent:100@100.

      b := Button label:'hello' in:top.
      b controller beTriggerOnDown.
      b action:[Transcript flash].
      top open.
To implement fun buttons (for games, show-demos etc.), you can access all internal parameters (and not take the viewStyle defaults) as in:
      |b granite light shadow|

      b := Button label:'a nice one ?'.
      granite := Image fromFile:(Button projectDirectory / '../libwidg3/bitmaps/granite_small.tiff').
      light := granite lightened.
      shadow := granite darkened darkened.
      b backgroundColor:granite.
      b foregroundColor:Color white.
      b shadowColor:shadow.
      b lightColor:light.
      b enteredBackgroundColor:light.
      b enteredForegroundColor:Color black.
      b activeLevel:-3; passiveLevel:5.
      b extent:(200 @ 50).
      b open
However, in your application, you may want to read those bitmaps only once during startup and cache them for later reuse in some class variable (reading, lightning & darkening of images is a bit slow) ST/X Buttons allow simulation of the ST-80 MVC way of interacting. To do so, instead (or in addition) to defining actionBlocks, set the buttons model to have this be informed (in addition): Model-View interaction (ST-80 style): (have a look at the models values in the inspector, as the toggles change)
    |bool1 bool2 b1 b2 panel top|

    bool1 := ValueHolder newBoolean.
    bool2 := ValueHolder newBoolean value:true.

    top := StandardSystemView new.
    top extent:200@100.

    panel := HorizontalPanelView 
              origin:0.0 @ 0.0 corner:1.0 @ 50 in:top.

    b1 := Toggle label:'eat me' in:panel.
    b1 model:bool1.

    b2 := Toggle label:'drink me' in:panel.
    b2 model:bool2.

    top open.
    bool1 inspect.
    bool2 inspect.
Using a PluggableAdaptor (ST-80 style): (notice, that this is almost what ST/X buttons did originally, therefore, you may want use actionBlocks right away ...)
      |adaptor1 adaptor2 b1 b2 panel top|

      adaptor1 := PluggableAdaptor new 
                        getBlock:[:m | false] 
                        putBlock:[:m :v | Transcript showCR:'eat']
                        updateBlock:nil.
      adaptor2 := PluggableAdaptor new 
                        getBlock:[:m | false] 
                        putBlock:[:m :v | Transcript showCR:'drink']
                        updateBlock:nil.

      top := StandardSystemView new.
      top extent:200@100.

      panel := HorizontalPanelView 
                origin:0.0 @ 0.0 corner:1.0 @ 50 in:top.

      b1 := Button label:'eat me' in:panel.
      b1 model:adaptor1.

      b2 := Button label:'drink me' in:panel.
      b2 model:adaptor2.

      top open.
as a reminder, the corresponding ST/X setup is:
      |b1 b2 panel top|

      top := StandardSystemView new.
      top extent:200@100.

      panel := HorizontalPanelView 
                origin:0.0 @ 0.0 corner:1.0 @ 50 in:top.

      b1 := Button label:'eat me' in:panel.
      b1 action:[Transcript showCR:'eat'].

      b2 := Button label:'drink me' in:panel.
      b2 action:[Transcript showCR:'drink'].

      top open.
accessing multiple aspects of a complex model (using a plug here to simulate that model ...) (Notice, the next example shall demonstrate multiple aspects; for this kind of functionality, you'd better use actionBlocks in real applications)
      |myModel b1 b2 panel top|

      myModel := Plug new.
      myModel respondTo:#grow with:[top extent:200@300].
      myModel respondTo:#shrink with:[top extent:200@100].

      top := StandardSystemView new.
      top extent:200@100.

      panel := HorizontalPanelView 
                origin:0.0 @ 0.0  
                corner:1.0 @ 50
                in:top.

      b1 := Button label:'eat me' in:panel.
      b1 model:myModel; change:#grow.

      b2 := Button label:'drink me' in:panel.
      b2 model:myModel; change:#shrink.

      top open.
acquiring the label from the model (this functionality is inherited from Label)
      |myModel b1 b2 b3 panel top currentLabel|

      currentLabel := 'foo'.

      myModel := Plug new.
      myModel respondTo:#getLabel with:[currentLabel].
      myModel respondTo:#b0Pressed with:[].
      myModel respondTo:#b1Pressed with:[currentLabel := 'bar'.
                                         myModel changed:#labelAspect].
      myModel respondTo:#b2Pressed with:[currentLabel := 'foo'.
                                         myModel changed:#labelAspect].

      top := StandardSystemView new.
      top extent:300@100.

      panel := HorizontalPanelView 
                origin:0.0 @ 0.0  
                corner:1.0 @ 50
                in:top.

      b1 := Button label:'press me' in:panel.
      b1 model:myModel; change:#b0Pressed; 
         labelMessage:#getLabel; aspect:#labelAspect.

      b2 := Button label:'press for bar' in:panel.
      b2 model:myModel; change:#b1Pressed.

      b3 := Button label:'press for foo' in:panel.
      b3 model:myModel; change:#b2Pressed.

      top open.
the same, using actionBlocks: (notice, that we must disable changeMessages from the first button).
      |myModel b1 b2 b3 panel top currentLabel|

      currentLabel := 'foo'.

      myModel := Plug new.
      myModel respondTo:#getLabel with:[currentLabel].

      top := StandardSystemView new.
      top extent:300@100.

      panel := HorizontalPanelView origin:0.0 @ 0.0 corner:1.0 @ 50 in:top.

      b1 := Button label:'press me' in:panel.
      b1 model:myModel; action:[Transcript showCR:'pressed']; 
         labelMessage:#getLabel; aspect:#labelAspect; change:nil.

      b2 := Button label:'press for bar' in:panel.
      b2 action:[currentLabel := 'bar'. myModel changed:#labelAspect].

      b3 := Button label:'press for foo' in:panel.
      b3 action:[currentLabel := 'foo'. myModel changed:#labelAspect].

      top open.
the same, using a labelChannel:
      |b1 b2 b3 panel top labelHolder|

      labelHolder := 'press me' asValue.

      top := StandardSystemView new.
      top extent:350@100.

      panel := HorizontalPanelView origin:0.0 @ 0.0 corner:1.0 @ 50 in:top.

      b1 := Button label:'none yet ' in:panel.
      b1 labelChannel:labelHolder.
      b1 action:[Transcript showCR:'pressed'].
      b1 sizeFixed:true.

      b2 := Button label:'press for bar' in:panel.
      b2 action:[ labelHolder value:'bar' ].

      b3 := Button label:'press for foo' in:panel.
      b3 action:[ labelHolder value:'foo' ].

      top open.
the same, using a labelChannel:
      |b|
      b := Button new.
      b label:'hello'.
      b renderer:(ButtonRenderer new).
      b open.
enable/disable channels:
      |b1 b2 v p enableHolder|
      enableHolder := false asValue.

      v := StandardSystemView new.
      v extent:100@50.
      p := HorizontalPanelView in:v.
      p origin:0.0@0.0 corner:1.0@1.0.
      
      b1 := Button in:p.
      b1 label:'Toggle'.
      b1 action:[enableHolder value:enableHolder value not].

      b2 := Button in:p.
      b2 label:'Press Me'.
      b2 enableChannel:enableHolder.
      v open.
aspect adaptor (extracting a bit from a value) as channel:
      |b1 v p countHolder|
      countHolder := 0 asValue.

      v := StandardSystemView new.
      v extent:100@50.
      p := HorizontalPanelView in:v.
      p origin:0.0@0.0 corner:1.0@1.0.

      b1 := Button in:p.
      b1 label:'Inc'.
      b1 action:[countHolder value:((countHolder value + 1) bitAnd:2r1111)].
      (3 to:0 by:-1) do:[:bitNr |
          |adapter bI|
          
          bI := Button in:p.
          bI label:bitNr.
          bI disabledB:bitNr.
          adapter := (PluggableAdaptor on:countHolder)
                            getBlock:[:m | (m value bitAt:bitNr+1) == 1]
                            putBlock:[:m :newValue | ]
                            updateBlock:[:m :aspect :param | true].
          bI enableChannel:adapter.
      ].
      v open.
see 'doc/coding-examples' and 'doc/misc/quick_view_intro.doc' for more variations on this theme.

ST/X 7.7.0.0; WebServer 1.702 at 20f6060372b9.unknown:8081; Sat, 21 Dec 2024 16:05:10 GMT