ButtonC.st
author claus
Tue, 09 May 1995 03:57:16 +0200
changeset 125 3ffa271732f7
parent 121 4e63bbdb266a
child 126 40228f4fd66b
permissions -rw-r--r--
.

"
 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'
!

!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/Attic/ButtonC.st,v 1.8 1995-05-09 01:54:58 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'!

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
!

beTriggerOnDown
    "make the receiver act on button press"

    isTriggerOnDown := true
!

active:aBoolean
    active := aBoolean
!

isTriggerOnDown
    "return true, if I trigger on press
     (in contrast to triggering on up, which is the default)"

    ^ isTriggerOnDown
!

pressed:aBoolean
    pressed := aBoolean
!

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
    ]
!

beToggle
    "make the receiver act like a toggle"

    isTriggerOnDown := true.
    isToggle := true
!

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

    pressActionBlock := aBlock
!

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

    releaseActionBlock := aBlock
!

autoRepeat
    "turn on autorepeat"

    autoRepeat := true.
    repeatBlock := [self repeat]
!

entered:aBoolean
    entered := aBoolean
!

beTriggerOnUp
    "make the receiver act on button release"

    isTriggerOnDown := false
!

enable
    "enable the button"

    enabled ifFalse:[
	enabled := true.
	view redraw
    ]
!

toggleNoAction
    "toggle, but do NOT perform any action"

    pressed ifTrue:[
	view turnOff.
	pressed := false.
    ] ifFalse:[
	view turnOn.
	pressed := true.
    ].
!

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
!

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
!

disable
    "disable the button"

    enabled ifTrue:[
	enabled := false.
	view redraw
    ]
! !

!ButtonController methodsFor:'event handling'!

buttonPress:button x:x y:y
    |sym action|

    (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 synchronizeOutput.
	    ].

	    self performAction.

	    autoRepeat ifTrue:[
		Processor addTimedBlock:repeatBlock afterSeconds:initialDelay
	    ]
	]
    ]
!

buttonRelease:button x:x y:y
    "button was released - if enabled, perform releaseaction"

    |sym|

    (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 synchronizeOutput.
		].

		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.
! !