ScrollableView.st
author claus
Sat, 18 Mar 1995 06:16:33 +0100
changeset 104 ca75c90df7a9
parent 93 f608d94e31ff
child 105 3d064ba4a0cc
permissions -rw-r--r--
Initial revision

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

View subclass:#ScrollableView
       instanceVariableNames:'scrolledView scrollBar helpView innerMargin
			      scrollBarPosition lockUpdates'
       classVariableNames:''
       poolDictionaries:''
       category:'Views-Basic'
!

ScrollableView comment:'
COPYRIGHT (c) 1989 by Claus Gittinger
	      All Rights Reserved

$Header: /cvs/stx/stx/libwidg/ScrollableView.st,v 1.11 1995-02-22 03:38:03 claus Exp $
'!

!ScrollableView class methodsFor:'documentation'!

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

version
"
$Header: /cvs/stx/stx/libwidg/ScrollableView.st,v 1.11 1995-02-22 03:38:03 claus Exp $
"
!

documentation
"
    a view containing a scrollbar and some other (slave-)view.
    This view wraps scrollbars around the view to be scrolled.
    The scrollbars are setup to send scrollUp/scrollDown/scrollVerticalTo
    and scrollLeft/scrollRight/scrollHorizontalTo- messages whenever moved.
    The view itself has to omplement these (there is a default implementation
    in the common View class for this.

    For the scrollbars to know about the full (maximum) size, the view
    must implement #heightOfContents and/or #widthOfContents.
    The values returned by these are used to compute the fractio which is
    visible (i.e. the scrollers thumb heights).

    There are two ways to create a ScrollableView:
    if the type of the view to be scrolled is known in advance,
    use:
	v := ScrollableView for:<ViewClass> in:someSuperView.

    otherwise, create the scrollableView empty with:
	v := ScrollableView in:someSuperView.
    and define the view later with:
	v scrolledView:aViewToBeScrolled

    It is also possible to change the scrolledView multiple times,
    for example if different views are needed to display different types
    of data (see example2).

    If you want to scroll views (instead of contents), you need a companion
    class (ViewScroller). See the documentation there.

    If you need horizontal scrolling too, use an instance of HVScrollableView.
"
!

examples
"
    example1 (simple scrolled text):

	|top scr txt|

	top := StandardSystemView label:'scroll example1'.
	top extent:200@100.
	scr := ScrollableView for:EditTextView in:top.
	scr origin:0.0@0.0 corner:1.0@1.0.
	txt := scr scrolledView.

	txt list:#('line1'
		   'line2'
		   'line3'
		   'line4'
		   'line5'
		   'line7'
		   'line8'
		   'line9'
		   'line10'
		  ).
	top open


    example2 (changing the scrolledView later):

	|top scr txtView1 txtView2|

	top := StandardSystemView label:'scroll example2'.
	top extent:200@100.
	scr := ScrollableView in:top.
	scr origin:0.0@0.0 corner:1.0@1.0.
	top open.

	(Delay forSeconds:5) wait.

	txtView1 := EditTextView new.
	txtView1 list:#(
			'wait 5 seconds to see the other text'
			'line2'
			'line3'
			'line4'
			'line5'
			'line7'
			'line8'
			'line9'
			'line10'
		  ).
	scr scrolledView:txtView1.

	(Delay forSeconds:5) wait.

	txtView2 := EditTextView new.
	txtView2 list:#('this is the other text'
			'alternative line2'
			'alternative line3'
			'alternative line4'
			'alternative line5'
			'alternative line6').
	scr scrolledView:txtView2.
"
! !

!ScrollableView class methodsFor:'instance creation'!

miniScroller:mini
    "return a new scrolling view. The subview will be created later.
     The view will have full scrollbars if mini is false, 
     miniscrollers if true."

    ^ self for:nil miniScrollerH:mini miniScrollerV:mini 
!

miniScrollerH:miniH miniScrollerV:miniV
    "return a new scrolling view. The subview will be created later.
     The view will have full scrollbars if the corresponding miniH/miniV
     is false, miniscrollers if false."

    ^ self for:nil miniScrollerH:miniH miniScrollerV:miniV in:nil
!

in:aView
    "return a new scrolling view to be contained in aView.
     There is no slave view now - this has to be set later via
     the scrolledView: method.
     The view will have full scrollbars."

    ^ self for:nil miniScrollerH:false miniScrollerV:false in:aView
!

for:aViewClass
    "return a new scrolling view scrolling an instance of aViewClass.
     The subview is created here.
     The view will have full scrollbars."

    ^ self for:aViewClass miniScrollerH:false miniScrollerV:false in:nil
!

for:aViewClass in:aView
    "return a new scrolling view scrolling an instance of aViewClass.
     The subview is created here.
     The view will have full scrollbars."

    ^ self for:aViewClass miniScrollerH:false miniScrollerV:false in:aView
!

for:aViewClass miniScrollerH:miniH miniScrollerV:miniV
    "return a new scrolling view scrolling an instance of aViewClass.
     The subview is created here.
     The view will have full scrollbars if the corresponding miniH/miniV
     is false, miniscrollers if false."

    ^ self for:aViewClass miniScrollerH:miniH miniScrollerV:miniV in:nil
!

for:aViewClass miniScroller:mini in:aView
    "return a new scrolling view scrolling an instance of aViewClass.
     The subview is created here.
     The view will have full scrollbars if mini is false, miniscrollers
     if true."

    ^ self for:aViewClass miniScrollerH:mini miniScrollerV:mini in:aView 
!

for:aViewClass miniScrollerH:miniH miniScrollerV:miniV in:aView
    "return a new scrolling view scrolling an instance of aViewClass.
     The subview is created here.
     The view will have full scrollbars if the corresponding miniH/miniV
     is false, miniscrollers if false."

    |newView|

    aView notNil ifTrue:[
	newView := self basicNew.
	newView device:(aView device).
	aView addSubView:newView
    ] ifFalse:[
	"create on Display by default"
	newView := self new.
    ].
    newView initializeFor:aViewClass miniScrollerH:miniH miniScrollerV:miniV.
    ^ newView
! !

!ScrollableView methodsFor:'initialization'!

initialize
    "default setup: full scrollers"

    ^ self initializeFor:nil miniScrollerH:false miniScrollerV:false 
!

initializeFor:aViewClass miniScrollerH:miniH miniScrollerV:miniV 
    |negativeOffset twoMargins halfMargin cls isST80 is3D|

    super initialize.

    lockUpdates := false.

    isST80 := StyleSheet name = #st80.

    style == #openwin ifTrue:[self level:0].
    is3D := StyleSheet is3D.

    isST80 ifTrue:[
	innerMargin := 0
    ] ifFalse:[
	is3D ifTrue:[
	    innerMargin := ViewSpacing.
	] ifFalse:[
	    innerMargin := 0    
	]
    ].
    negativeOffset := borderWidth negated.

    "create the scrollbar"

    isST80 ifTrue:[
	cls := ScrollBar
    ] ifFalse:[
	cls := miniV ifTrue:[MiniScroller] ifFalse:[ScrollBar].
    ].

    scrollBar := cls in:self.
    scrollBar thumbOrigin:0 thumbHeight:100.

    "create the subview"
    is3D ifTrue:[
	twoMargins := innerMargin * 2.
	halfMargin := innerMargin // 2.

	aViewClass notNil ifTrue:[
	    scrolledView := aViewClass in:self.
	    style == #openwin ifTrue:[
		scrolledView level:0.
		scrolledView borderWidth:1
	    ] ifFalse:[
		isST80 ifTrue:[
		    scrolledView level:1.
		] ifFalse:[
		    scrolledView level:-1
		]
	    ].
	].
	(scrollBarPosition == #right) ifTrue:[
	    scrollBar origin:[width - scrollBar extent x 
				    - (scrollBar borderWidth * 2)
				    - halfMargin
			      @
			      halfMargin]
		      extent:[scrollBar extent x @ (height - innerMargin)].

	    scrolledView notNil ifTrue:[
		scrolledView origin:halfMargin asPoint
			 extent:[(width - 
				  scrollBar width - 
				  twoMargins) 
				 @ 
				 (height - innerMargin)].
		]
	] ifFalse:[
	    scrollBar origin:halfMargin asPoint
		      extent:[scrollBar extent x @ (height - innerMargin)].

	    scrolledView notNil ifTrue:[
		scrolledView origin:((scrollBar origin x + scrollBar width + innerMargin)
				     @
				     halfMargin)
			     extent:[(width - scrollBar width - twoMargins) 
				     @ 
				     (height - innerMargin)].
	    ]
	].
    ] ifFalse:[
	(scrollBarPosition == #right) ifTrue:[
	    scrollBar origin:[width - scrollBar extent x 
				    - scrollBar borderWidth
			      @
			      negativeOffset]
	] ifFalse:[
	    scrollBar origin:negativeOffset asPoint
	].
	scrollBar extent:[scrollBar extent x @ (height "+ (scrollBar borderWidth * 1)")].

	aViewClass notNil ifTrue:[
	    scrolledView := aViewClass in:self.
	    (scrollBarPosition == #right) ifTrue:[
		scrolledView origin:scrolledView borderWidth negated asPoint
	    ] ifFalse:[
		scrolledView origin:((scrollBar width + 
				      scrollBar borderWidth - 
				      scrolledView borderWidth) 
				    @ 
				    scrolledView borderWidth negated)
	    ].
	    scrolledView extent:[(width - scrollBar width - scrolledView borderWidth) 
				 @ 
				 (height + (scrollBar borderWidth))
				]
	].
    ].
    scrolledView notNil ifTrue:[
	self setScrollActions.
	"
	 pass input to myself (and other subviews) to
	 the scrolled view
	"
	self keyboardHandler:scrolledView.
    ]
!

initStyle
    super initStyle.

    scrollBarPosition := StyleSheet at:'scrollBarPosition' default:#left
!

realize
    super realize.

    "since scrolledview may have done something to its contents
     during init-time we had no chance yet to catch contents-
     changes; do it now
    "
    scrolledView notNil ifTrue:[
	scrollBar setThumbFor:scrolledView
    ]
! !

!ScrollableView methodsFor:'private'!

setScrollActions
    "lock prevents repositioning the scroller to the
     actual (often rounded) position while scrolling,
     and keeps it instead at the pointer position.

     (this avoids run-away scroller when scrolling
      textviews, when the text is aligned line-wise).
      Consider this as a kludge."

    lockUpdates := false.

    scrollBar scrollAction:[:position |
	lockUpdates := true.
	scrolledView scrollVerticalToPercent:position.
	lockUpdates := false
    ].
    scrollBar scrollUpAction:[scrolledView scrollUp].
    scrollBar scrollDownAction:[scrolledView scrollDown].

    scrolledView addDependent:self.
! !

!ScrollableView methodsFor:'accessing'!

scrollBar
    "return the scrollbar"

    ^ scrollBar
!

scrolledView
    "return the scrolled view"

    ^ scrolledView
!

scrolledView:aView
    "set the view to scroll"

    |halfMargin twoMargins|

    scrolledView notNil ifTrue:[
	scrolledView destroy.
	scrolledView := nil.
    ].
    scrolledView := aView.

    ((style ~~ #normal) and:[style ~~ #mswindows]) ifTrue:[
	"3D look"

	twoMargins := innerMargin * 2.
	halfMargin := innerMargin // 2.

	style == #openwin ifTrue:[
	    scrolledView level:0.
	    scrolledView borderWidth:1
	] ifFalse:[
	    scrolledView level:-1
	].

	(scrollBarPosition == #right) ifTrue:[
	    scrolledView 
		origin:halfMargin asPoint
		extent:[(width - 
			 scrollBar width - 
			 twoMargins) 
			@ 
			(height - innerMargin)
		       ].
	] ifFalse:[
	    scrolledView 
		origin:((scrollBar origin x 
			 + scrollBar width 
			 + innerMargin)
			@
			halfMargin)
		extent:[(width 
			 - scrollBar width 
			 - twoMargins) 
			@ 
			(height - innerMargin)
		       ].
	]
    ] ifFalse:[
	"non 3D look"
	(scrollBarPosition == #right) ifTrue:[
	    scrolledView 
		origin:scrolledView borderWidth negated asPoint
	] ifFalse:[
	    scrolledView 
		origin:((scrollBar width 
			 + scrollBar borderWidth 
			 - scrolledView borderWidth) 
			@ 
			scrolledView borderWidth negated)
	].
	scrolledView 
	    extent:[
		    (width 
		     - scrollBar width 
		     - scrolledView borderWidth) 
		    @ 
		    (height 
		     + (scrollBar borderWidth))
		   ]
    ].

    super addSubView:scrolledView.
    self setScrollActions.
    "
     pass input to myself (and other subviews) to
     the scrolled view
    "
    self keyboardHandler:scrolledView.

    realized ifTrue:[
	self sizeChanged:nil.
	scrolledView realize
    ].
! !

!ScrollableView methodsFor:'slave-view messages'!

leftButtonMenu
    "return scrolledViews leftbuttonmenu"

    scrolledView isNil ifTrue:[^ nil].
    ^ scrolledView leftButtonMenu
!

leftButtonMenu:aMenu
    "pass on leftbuttonmenu to scrolledView"

    scrolledView leftButtonMenu:aMenu
!

middleButtonMenu
    "return scrolledViews middlebuttonmenu"

    scrolledView isNil ifTrue:[^ nil].
    ^ scrolledView middleButtonMenu
!

middleButtonMenu:aMenu
    "pass on middlebuttonmenu to scrolledView"

    scrolledView middleButtonMenu:aMenu
!

rightButtonMenu
    "return scrolledViews rightbuttonmenu"

    scrolledView isNil ifTrue:[^ nil].
    ^ scrolledView rightButtonMenu
!

rightButtonMenu:aMenu
    "pass on rightbuttonmenu to scrolledView"

    scrolledView rightButtonMenu:aMenu
!

doesNotUnderstand:aMessage
    "this is funny: all message we do not understand, are passed
     on to the scrolledView - so we do not have to care for all
     possible messages ...(thanks to the Message class)"

     scrolledView isNil ifFalse:[
	 ^ scrolledView perform:(aMessage selector)
		  withArguments:(aMessage arguments)
     ]
! !

!ScrollableView methodsFor:'forced scroll'!

pageUp
    "page up"

    scrollBar pageUp
!

pageDown
    "page down"

    scrollBar pageDown
! !

!ScrollableView methodsFor:'changes '!

update:something with:argument from:changedObject
    "whenever the scrolledView changes its contents, the scroller must
     be updated"

    changedObject == scrolledView ifTrue:[
	something == #sizeOfContents ifTrue:[
	    scrollBar setThumbFor:scrolledView.
	    ^ self
	].
	something == #originOfContents ifTrue:[
	    lockUpdates ifFalse:[
		scrollBar setThumbOriginFor:scrolledView.
	    ].
	    ^ self
	].
    ].
! !

!ScrollableView methodsFor:'event processing'!

keyPress:key x:x y:y
    "a key was pressed - handle page-keys here"

    (key == #Prior)    ifTrue: [^ self pageUp].
    (key == #Next)     ifTrue: [^ self pageDown].

    super keyPress:key x:x y:y
!

sizeChanged:how
    super sizeChanged:how.
    scrolledView notNil ifTrue:[
	scrollBar setThumbFor:scrolledView
    ]
! !