|
Class: Button
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
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
copyrightCOPYRIGHT (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.
actionBlocksVersusModelChangesYou 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.
ST-80 instance creation
-
switch
-
ST-80 compatibility: create & return a new radioButton
-
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:)
|
-
trigger
-
ST-80 compatibility: create & return a new button, which
triggers on press.
defaults
-
defaultDisabledForegroundColor
-
-
returnFormOn: aDevice
-
return the form used for the return arrow in non-3D;
cache the one for Display for the next round.
-
returnLightFormOn: aDevice
-
return the form used for the return arrow light pixels (3D only);
cache the one for Display for the next round
-
returnShadowFormOn: aDevice
-
return the form used for the return arrow shadow pixels (3D only);
cache the one for Display for the next round.
-
updateStyleCache
-
extract values from the styleSheet and cache them in class variables
instance creation
-
abortButton
-
since abort-buttons are very common, here is a convenient
method to create one ...
-
abortButtonIn: aView
-
since abort-buttons are very common, here is a convenient
method to create one ...
-
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) **
-
label: aLabel action: aBlock
-
create and return a new Button with text-label, aString
and pressAction, aBlock.
-
label: aLabel action: aBlock in: aView
-
create and return a new Button with text-label, aString
and pressAction, aBlock. Button is placed into aView.
-
okButton
-
since ok-buttons are very common, here is a convenient
method to create one ...
-
okButtonIn: aView
-
since ok-buttons are very common, here is a convenient
method to create one ...
-
on: aModel label: aLabel
-
create and return a new Button with text-label, aString
and a boolean model.
accessing-behavior
-
action
-
return the action
-
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.
-
autoRepeat
-
turn on autorepeat OBSOLETE; use #autoRepeat:
** This is an obsolete interface - do not use it (it may vanish in future versions) **
-
autoRepeat: aBoolean
-
turn on/off autorepeat
-
doubleClickAction
-
return the doubleClickAction; that's the block which gets evaluated
when the button is double-clicked (if non-nil).
Seldom used with buttons
-
doubleClickAction: aBlock
-
define the action to be performed on doubleClick
Seldom used with buttons.
-
enabled
-
return true if the button is enabled
-
enabled: aBoolean
-
enable/disable the button
-
pressAction
-
return the pressAction; that's the block which gets evaluated
when the button is pressed (if non-nil)
-
pressAction: aBlock
-
define the action to be performed on press
-
releaseAction
-
return the releaseAction; that's the block which gets evaluated
when the button is released (if non-nil)
-
releaseAction: aBlock
-
define the action to be performed on release
accessing-channels
-
enableChannel
-
return a valueHolder for enable/disable
-
enableChannel: aValueHolderForBoolean
-
set the valueHolder used for enable/disable
-
pressChannel
-
return the pressChannel
-
pressChannel: aBlock
-
set the pressChannel
-
releaseChannel
-
return the releaseChannel
-
releaseChannel: aBlock
-
set the releaseChannel
accessing-look
-
activeBackgroundColor
-
return the background color to be used when pressed
-
activeBackgroundColor: aColor
-
set the background color to be used when pressed
-
activeForegroundColor
-
return the foreground color to be used when pressed
-
activeForegroundColor: aColor
-
set the foreground color to be used when pressed
-
activeForegroundColor: fgColor backgroundColor: bgColor
-
set both fg and bg colors to be used when pressed
-
activeLevel
-
return the level of the button when pressed
-
activeLevel: aNumber
-
set the level of the button when pressed (i.e. how deep)
-
activeLevel: activeLevelNumber passiveLevel: passiveLevelNumber
-
set the level of the button when pressed (i.e. how deep)
-
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.
-
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.
-
allViewBackground: something if: condition
-
no longer ignored here
-
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).
-
beReturnButton
-
show show a return-key image after the label.
Same as 'isReturnButton:true' for ST-80 compatibility.
-
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.
-
defaultable
-
return true, if the receiver is defaultable
-
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.
-
disabledForegroundColor
-
return the foreground color used when the button is disabled
-
disabledForegroundColor: aColor
-
set the foreground color used when the button is disabled
-
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.
-
edgeStyle: aSymbol
-
set the edgestyle - currently only #soft or nil
-
enterLevel
-
return the level to be used when the mouse
pointer enters the button area
-
enterLevel: aNumber
-
set the level to be used when the mouse
pointer enters the button area
-
enteredBackgroundColor
-
return the background color to be used when the mouse
pointer enters the button area
-
enteredBackgroundColor: aColor
-
set the background color to be used when the mouse
pointer enters the button area
-
enteredForegroundColor
-
return the foreground color to be used when the mouse
pointer enters the button area
-
enteredForegroundColor: aColor
-
set the foreground color to be used when the mouse
pointer enters the button area
-
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.
-
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.
-
isReturnButton: aBoolean
-
show/don't show a return-key image after the label
-
label: aStringOrImageOrForm
-
(comment from inherited method)
set the labelString or image;
adjust extent if not already realized and not fixedSize;
also redraw
-
leaveLevel
-
return the level to be used when the mouse
pointer leaves the button area
-
leaveLevel: aNumber
-
set the level to be used when the mouse
pointer leaves the button area
-
loseDefault
-
ST-80 compatibility - clear isReturnButton attribute
-
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) **
-
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) **
-
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) **
-
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) **
-
passiveLevel
-
return the level of the button when released
-
passiveLevel: aNumber
-
set the level of the button when not pressed (i.e. how high)
-
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.
-
takeDefault
-
ST-80 compatibility - set isReturnButton attribute
-
viewBackground: aColorOrForm
-
on:device
changing state
-
toggle
-
toggle and perform the action
-
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)
-
turnOff
-
turn the button off (if not already off) do NOT perform actions/change notifications
-
turnOffWithAction
-
turn the button off (if not already off) and perform any action/change notification
-
turnOffWithoutRedraw
-
turn the button off - no redraw but perform any action/change notification
-
turnOn
-
turn the button on (if not already on) - do NOT perform any action/notification
-
turnOnWithAction
-
turn the button on (if not already on) and perform any change actions/notifications
-
turnOnWithoutRedraw
-
turn the button on - no redraw but perform any change actions/notifications
focus handling
-
showFocus: explicit
-
the button got the keyboard focus
(either explicit, via tabbing; or implicit, by pointer movement)
- change any display attributes as req'd.
-
showNoFocus: explicit
-
the button lost the keyboard focus
(either explicit, via tabbing; or implicit, by pointer movement)
- change any display attributes as req'd.
-
wantsFocusWithButtonPress
-
no, do not catch the keyboard focus on button click
initialization
-
defaultControllerClass
-
-
fetchDeviceResources
-
fetch device colors, to avoid reallocation at redraw time
-
initBorderStyle
-
-
initCursor
-
set up a hand cursor
-
initEvents
-
(comment from inherited method)
will be sent by create - can be redefined by subclasses to enable
view events
-
initStyle
-
setup viewStyle specifics
-
initialize
-
must be called if redefined
-
reinitialize
-
(comment from inherited method)
this is called right after snapIn
native widget support
-
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)
-
win32nativeWMCommand: commandID
-
private
-
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).
-
getLabelFromLabelChannel
-
-
rawLabelSizeOf: aLogo
-
compute the extent needed to hold the label (plus the return form)
-
shiftLabelWhenPressed
-
queries
-
computePreferredExtent
-
(comment from inherited method)
compute my preferred extent - this is the minimum size I would like to have
-
extraMarginForBorder
-
(comment from inherited method)
some (round) borders may need more space
-
is3D
-
return true, if the receiver is a 3D style view
-
isDefault
-
return true, if this is a default OK button
-
isOn
-
return true, if this button is currently pressed
-
isReturnButton
-
return true, if this is a return button (showing a return-key-icon)
-
isTriggerOnDown
-
return true, if I fire on press (instead of fire-on-release)
-
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
-
drawBottomEdge
-
draw bottom 3D edge into window frame
-
drawEdges
-
draw all of my 3D edges
-
drawLeftEdge
-
draw left 3D edge into window frame
-
drawRightEdge
-
draw right 3D edge into window frame
-
drawTopEdge
-
draw top 3D edge into window frame
-
drawWith: fg and: bg
-
redraw myself with fg/bg. Use super to draw the label, add
the return-arrow here.
-
redraw
-
redraw the button.
That's like redrawing a label, but uses different colors when pressed
or entered.
-
redrawIfPressed
-
redraw the button, but only if visible and currently being pressed
-
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
-
showActive
-
redraw myself as active (i.e. busy)
-
showPassive
-
redraw myself as passive
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.
|