.
"
COPYRIGHT (c) 1995 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.
"
'From Smalltalk/X, Version:2.10.5 on 4-may-1995 at 6:17:35 am'!
Controller subclass:#ButtonController
instanceVariableNames:'enabled pressed active entered isTriggerOnDown autoRepeat
repeatBlock initialDelay repeatDelay pressActionBlock
releaseActionBlock isToggle'
classVariableNames:''
poolDictionaries:''
category:'Interface-Support-Controllers'
!
!ButtonController class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 1995 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.
"
!
version
"
$Header: /cvs/stx/stx/libwidg/ButtonController.st,v 1.12 1995-07-03 02:32:19 claus Exp $
"
!
documentation
"
ButtonControllers are used with buttons and handle all user interaction.
These are automatically created when a Button is created, therefore no manual
action is required for creation.
In normal applications, you dont have to care for the controller; access to the
controllers behavior is also possible via messages to the button.
(setting actions, controlling autorepeat etc.)
Instance variables:
enabled <Boolean> pressing is allowed (default: true)
pressed <Boolean> true if currently pressed (read-only)
entered <Boolean> true if the cursor is currently in this view
isTriggerOnDown <Boolean> controls if the action should be executed on
press or on release (default: on release).
isToggle <Boolean> controls if the button should show toggle
behavior (as opposed to one-shot behavior)
pressActionBlock <Block> block to evaluate when pressed (default: noop)
releaseActionBlock <Block> block to evaluate when released (default: noop)
autoRepeat <Boolean> auto-repeats when pressed long enough (default: false)
initialDelay <Number> seconds till first auto-repeat (default: 0.2)
repeatDelay <Number> seconds of repeat intervall (default: 0.025)
repeatBlock <Block> block evaluated for auto-repeat (internal)
active <Boolean> true during action evaluation (internal)
"
! !
!ButtonController class methodsFor:'defaults'!
defaultRepeatDelay
"when autorepeat is enabled, and button is not released,
repeat every repeatDelay seconds"
^ 0.025
!
defaultInitialDelay
"when autorepeat is enabled, and button is not released,
start repeating after initialDelay seconds"
^ 0.2
! !
!ButtonController methodsFor:'accessing-state'!
pressed
"return true, if I am pressed"
^ pressed
!
active
"return true, if I am active;
that is: currently performing my action.
This query can be used to avoid multiple redraws."
^ active
!
enabled
"return true, if I am enabled"
^ enabled
!
entered
"return true, if the mouse pointer is currently in my view"
^ entered
!
entered:aBoolean
entered := aBoolean
!
active:aBoolean
active := aBoolean
!
pressed:aBoolean
pressed := aBoolean
! !
!ButtonController methodsFor:'accessing-behavior'!
beTriggerOnDown
"make the receiver act on button press"
isTriggerOnDown := true
!
beTriggerOnUp
"make the receiver act on button release"
isTriggerOnDown := false
!
triggerOnDown:aBoolean
"set/clear the flag which controls if the action block is to be evaluated
on press or on release.
(see also ST-80 compatibility methods beTriggerOn*)"
isTriggerOnDown := aBoolean
!
isTriggerOnDown
"return true, if I trigger on press
(in contrast to triggering on up, which is the default)"
^ isTriggerOnDown
!
beToggle
"make the receiver act like a toggle"
isTriggerOnDown := true.
isToggle := true
!
autoRepeat
"turn on autorepeat"
autoRepeat := true.
repeatBlock := [self repeat]
!
action:aBlock
"convenient method: depending on the setting the triggerOnDown flag,
either set the press-action clear any release-action or
vice versa, set the release-action and clear the press-action."
isTriggerOnDown ifTrue:[
releaseActionBlock := nil.
pressActionBlock := aBlock
] ifFalse:[
releaseActionBlock := aBlock.
pressActionBlock := nil
]
!
pressAction:aBlock
"define the action to be performed on press"
pressActionBlock := aBlock
!
releaseAction:aBlock
"define the action to be performed on release"
releaseActionBlock := aBlock
!
toggleNoAction
"toggle, but do NOT perform any action"
pressed ifTrue:[
view turnOff.
pressed := false.
] ifFalse:[
view turnOn.
pressed := true.
].
!
toggle
"toggle and perform the action"
enabled ifTrue:[
self toggleNoAction.
self performAction.
view changed:#toggle with:pressed
]
!
pressAction
"return the pressAction; thats the block which gets evaluated
when the button is pressed (if non-nil)"
^ pressActionBlock
!
releaseAction
"return the releaseAction; thats the block which gets evaluated
when the button is relreased (if non-nil)"
^ releaseActionBlock
!
enable
"enable the button"
enabled ifFalse:[
enabled := true.
view redraw
]
!
disable
"disable the button"
enabled ifTrue:[
enabled := false.
view redraw
]
! !
!ButtonController methodsFor:'event handling'!
buttonPress:button x:x y:y
(button == 1 or:[button == #select]) ifFalse:[
^ super buttonPress:button x:x y:y
].
enabled ifTrue:[
isToggle ifTrue:[
self toggle.
^ self
].
pressed ifFalse:[
pressed := true.
view showActive.
(pressActionBlock notNil or:[model notNil]) ifTrue:[
"
force output - so that button is drawn correctly in case
of any long-computation (at high priority)
"
view device flush.
].
self performAction.
autoRepeat ifTrue:[
Processor addTimedBlock:repeatBlock afterSeconds:initialDelay
]
]
]
!
buttonRelease:button x:x y:y
"button was released - if enabled, perform releaseaction"
(button == 1 or:[button == #select]) ifFalse:[
^ super buttonRelease:button x:x y:y
].
isToggle ifTrue:[
^ self
].
pressed ifTrue:[
autoRepeat ifTrue:[
Processor removeTimedBlock:repeatBlock
].
pressed := false.
view showPassive.
enabled ifTrue:[
"
only perform action if released within myself
"
((x >= 0)
and:[x <= view width
and:[y >= 0
and:[y <= view height]]]) ifTrue:[
(releaseActionBlock notNil or:[model notNil]) ifTrue:[
"
force output - so that button is drawn correctly in case
of any long-computation (at high priority)
"
view device flush.
].
self performAction.
]
]
]
!
pointerEnter:state x:x y:y
"mouse pointer entered my view.
Redraw with enteredColors if they differ from the normal colors"
entered := true.
enabled ifTrue:[
pressed ifTrue:[
"
reentered after a leave with mouse-button down;
restart autorepeating and/or if I am a button with
triggerOnDown, show active again.
"
autoRepeat ifTrue:[
Processor addTimedBlock:repeatBlock afterSeconds:initialDelay
].
isTriggerOnDown ifFalse:[
view showActive.
]
] ifFalse:[
view redraw
]
]
!
pointerLeave:state
"mouse pointer left my view.
Redraw with normal colors if they differ from enteredColors"
entered := false.
pressed ifTrue:[
"
leave with mouse-button down;
stop autorepeating and/or if I am a button with
action on release, show passive
"
autoRepeat ifTrue:[
Processor removeTimedBlock:repeatBlock
].
isTriggerOnDown ifFalse:[
view showPassive.
]
] ifFalse:[
enabled ifTrue:[
view redraw
]
]
!
performAction
|action|
"
ST/X style actionBlock evaluation ...
"
pressed ifTrue:[
action := pressActionBlock
] ifFalse:[
action := releaseActionBlock
].
action notNil ifTrue:[
active := true.
action numArgs == 0 ifTrue:[
action value
] ifFalse:[
action value:pressed
].
active := false.
].
"
ST-80 style model notification ...
"
(isToggle
or:[(isTriggerOnDown and:[pressed])
or:[isTriggerOnDown not and:[pressed not]]]) ifTrue:[
"the ST-80 way of doing things"
view notNil ifTrue:[
active := true.
view sendChangeMessageWith:pressed.
active := false.
].
].
!
buttonMultiPress:button x:x y:y
^ self buttonPress:button x:x y:y
!
keyPress:key x:x y:y
"trigger on Return and space, if I am the focusView of my group
(i.e. if I got an explicit focus)"
(key == #Return or:[key == Character space]) ifTrue:[
view hasFocus ifTrue:[
"just simulate a buttonPress/release here."
self buttonPress:1 x:0 y:0.
self buttonRelease:1 x:0 y:0.
^ self.
]
].
view keyPress:key x:x y:y
!
repeat
"this is sent from the autorepeat-block, when the button has been pressed long
enough; it simulates a release-press, by evaluating both release
and press actions."
pressed ifTrue:[
enabled ifTrue:[
active ifFalse:[
active := true.
releaseActionBlock notNil ifTrue:[releaseActionBlock value].
pressActionBlock notNil ifTrue:[pressActionBlock value].
active := false.
autoRepeat ifTrue:[
Processor addTimedBlock:repeatBlock afterSeconds:repeatDelay
]
]
]
]
! !
!ButtonController methodsFor:'initialization'!
initialize
super initialize.
enabled := true.
active := false.
pressed := false.
entered := false.
autoRepeat := false.
initialDelay := self class defaultInitialDelay.
repeatDelay := self class defaultRepeatDelay.
isTriggerOnDown := false.
isToggle := false.
! !