Scroller.st
changeset 38 4b9b70b2cc87
parent 24 966098a893f8
child 59 450ce95a72a4
--- a/Scroller.st	Sun Aug 07 15:22:53 1994 +0200
+++ b/Scroller.st	Sun Aug 07 15:23:42 1994 +0200
@@ -21,63 +21,90 @@
                               thumbSoftEdge
                               thumbHalfShadowColor thumbHalfLightColor
                               thumbFrameSizeDifference
-                              tallyLevel tallyMarks'
+                              tallyLevel tallyMarks
+                              fixThumbHeight'
        classVariableNames:   'HandleShadowForm HandleLightForm'
        poolDictionaries:''
        category:'Views-Interactors'
 !
 
 Scroller comment:'
-
 COPYRIGHT (c) 1989 by Claus Gittinger
               All Rights Reserved
 
-$Header: /cvs/stx/stx/libwidg/Scroller.st,v 1.6 1994-01-13 00:18:10 claus Exp $
-
-written spring/summer 89 by claus
+$Header: /cvs/stx/stx/libwidg/Scroller.st,v 1.7 1994-08-07 13:23:20 claus Exp $
 '!
 
 !Scroller 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/Scroller.st,v 1.7 1994-08-07 13:23:20 claus Exp $
+"
+!
+
 documentation
 "
-this class implements the scroller for scrollbars.
-it can also be used by itself for scrollbars without step-buttons.
-When moved, a predefined action is performed.
-Beside the obvious 3D rectangle, a scroller may draw a know-form
-(as in NeXT) or little tally marks (as on SGI) in itself.
-These are controlled by the shadowForm, lightForm, tallyLevel and tallyMarks
-instance variables.
+    this class implements the scroller for scrollbars.
+    it can also be used by itself for scrollbars without step-buttons.
+    When moved, a predefined action is performed.
+    Beside the obvious 3D rectangle, a scroller may draw a know-form
+    (as in NeXT) or little tally marks (as on SGI) in itself.
+    These are controlled by the shadowForm, lightForm, tallyLevel and tallyMarks
+    instance variables.
+    The scroller can work synchronous (i.e. every move leads to an immediate evaluation
+    of the action, or asynchronous (i.e. perform action on end-of move).
+    By default, scrollers are synchronous - asynchronous makes sense, if the scroll
+    operation (redraw) is expensive.
 
-Instance variables:
+    Instance variables:
 
-thumbOrigin                 <Number>        origin of thumb (in percent)
-thumbHeight                 <Number>        height of thumb (in percent)
-thumbColor                  <Color>         color of thumb
-thumbFrameColor             <Color>         color of the frame around the thumb
-scrollAction                <Block>         1 arg block to be evaluated when scrolled
-                                            (arg is position in percent)
-moveDirection               <Symbol>        #x or #y
-thumbFrame                  <Rectangle>     frame of thumb in pixels (cached)
-thumbLevel                  <Number>        level of thumb if 3d
-scrolling                   <Boolean>       true during scroll
-pressOffset                 <Number>        temporary (offset into frame when move started)
-synchronousOperation        <Boolean>       true if synchronous (i.e. dont wait till release
-                                            to perform action)
-shadowForm                  <Form>          bitmap of knob if any (shadow part)
-lightForm                   <Form>          bitmap of knob if any (light part)
-inset                       <Integer>       number of pixels to inset thumb from view borders
-thumbShadowColor            <Color>         color do draw dark parts of thumb
-thumblightColor             <Color>         color to draw light parts of thumb
-thumbSoftEdge               <Boolean>       true if edges of thumb are to appear smooth
-thumbHalfShadowColor        <Color>         used to draw smooth edges
-thumbHalfLightColor         <Color>         used to draw smooth edges
-thumbFrameSizeDifference    <Integer>       number of pixels the thumb is larger than 
-                                            it should be
-tallyLevel                  <Integer>       if not zero, specifies if tally-marks should
-                                            go into or out of the display (actually only <0/>0 is checked)
-                                            I dont know of a better word for these ...
-tallyMarks                  <Integer>       number of tally marks
+    thumbOrigin                 <Number>        origin of thumb (in percent)
+    thumbHeight                 <Number>        height of thumb (in percent)
+    thumbColor                  <Color>         color of thumb
+    thumbFrameColor             <Color>         color of the frame around the thumb
+    scrollAction                <Block>         1 arg block to be evaluated when scrolled
+                                                (arg is position in percent)
+    moveDirection               <Symbol>        #x or #y
+    thumbFrame                  <Rectangle>     frame of thumb in pixels (cached)
+    thumbLevel                  <Number>        level of thumb if 3d
+    scrolling                   <Boolean>       true during scroll
+    pressOffset                 <Number>        temporary (offset into frame when move started)
+    synchronousOperation        <Boolean>       true if synchronous (i.e. dont wait till release
+                                                to perform action)
+    shadowForm                  <Form>          bitmap of knob if any (shadow part)
+    lightForm                   <Form>          bitmap of knob if any (light part)
+    inset                       <Integer>       number of pixels to inset thumb from view borders
+    thumbShadowColor            <Color>         color do draw dark parts of thumb
+    thumblightColor             <Color>         color to draw light parts of thumb
+    thumbSoftEdge               <Boolean>       true if edges of thumb are to appear smooth
+    thumbHalfShadowColor        <Color>         used to draw smooth edges
+    thumbHalfLightColor         <Color>         used to draw smooth edges
+    thumbFrameSizeDifference    <Integer>       number of pixels the thumb is larger than 
+                                                it should be (can be negative for mswin-style)
+    tallyLevel                  <Integer>       if not zero, specifies if tally-marks should
+                                                go into or out of the display (actually only <0/>0 is checked)
+                                                I dont know of a better word for these ...
+    tallyMarks                  <Integer>       number of tally marks
+    fixThumbHeight              <Boolean>       perform 'wrong' height computation a la mswindows
+
+    notice: for mswindows style, we force a WRONG thumb-frame
+    computation, to make the thumb have constant size; 
+    if you dont like that (I do not :-), set fixThumbHeight to false (in initStyle).
 "
 ! !
 
@@ -143,6 +170,7 @@
     "initialize - setup instvars from defaults"
 
     super initialize.
+    self computeInitialExtent.
     moveDirection := #y.
 
     scrolling := false.
@@ -150,10 +178,15 @@
 
     thumbOrigin := 0.
     thumbHeight := 100.
+    thumbFrameSizeDifference := 0.
 
-    inset := 1.
+"/    inset := 1.
 
-    self computeThumbFrame
+"/    self computeThumbFrame
+!
+
+computeInitialExtent
+    ^ self
 !
 
 initStyle
@@ -166,55 +199,69 @@
     thumbSoftEdge := false.
     tallyLevel := 0.
     tallyMarks := 0.
+    fixThumbHeight := false.
+    inset := 0.
 
-    self is3D ifTrue:[
-        thumbSoftEdge := false.
-        inset := 0.
-
-        style == #next ifTrue:[
+    style == #next ifTrue:[
+        self level:0.
+        self borderWidth:1.
+        inset := 1.
+        thumbSoftEdge := true.
+        thumbLevel := 2.
+        thumbColor := Color lightGrey
+    ] ifFalse:[
+        style == #motif ifTrue:[
             self level:0.
-            self borderWidth:1.
             inset := 1.
-            thumbSoftEdge := true.
             thumbLevel := 2.
-            thumbColor := Color lightGrey
+"/            viewBackground := Color lightGrey.
+"/            thumbColor := viewBackground.
+
+            viewBackground := Color darkGrey "grey".
+            thumbColor := Grey "Color lightGrey".
         ] ifFalse:[
-            style == #motif ifTrue:[
-                self level:0.
-                inset := 1.
-                thumbLevel := 2.
-                viewBackground := Color lightGrey.
+            style == #iris ifTrue:[
+                self level:-1.
+                thumbLevel := 3.
+                thumbSoftEdge := true.
                 thumbColor := viewBackground.
+                tallyLevel := 1.
+                tallyMarks := 3.
             ] ifFalse:[
-                style == #iris ifTrue:[
-                    self level:-1.
-                    thumbLevel := 3.
+                style == #mswindows ifTrue:[
+                    inset := 0.
+                    self level:0.
+                    self borderWidth:1.
+                    thumbLevel := 2.
+                    thumbColor := Color lightGrey.
                     thumbSoftEdge := true.
-                    thumbColor := viewBackground.
-                    tallyLevel := 1.
-                    tallyMarks := 3.
+                    viewBackground := Color grey:80.
+                    fixThumbHeight := true.
                 ] ifFalse:[
-                    style == #mswindows ifTrue:[
-                        self level:0.
-                        self borderWidth:1.
-                        thumbLevel := 2.
-                        thumbColor := Color lightGrey.
-                        thumbSoftEdge := true.
-                        viewBackground := Color grey:80.
+                    (style == #st80) ifTrue:[
+                        self level:1.
+                        thumbLevel := 0.
+                        thumbColor := Black.
+                        viewBackground := Grey.
+                        inset := 3.
                     ] ifFalse:[
-                        self level:-1.
-                        thumbLevel := 2.
-                        thumbColor := Color lightGrey
+                        (style ~~ #normal) ifTrue:[
+                            self level:-1.
+                            thumbLevel := 2.
+                            thumbColor := Color lightGrey
+                        ] ifFalse:[
+                            thumbColor := White.
+                            inset := 1
+                        ]
                     ]
                 ]
             ]
-        ].
+        ]
+    ].
+    style ~~ #normal ifTrue:[
         device hasGreyscales ifFalse:[
             thumbColor := Color grey
         ].
-    ] ifFalse:[
-        thumbColor := White.
-        inset := 1
     ].
 
     thumbShadowColor := shadowColor.
@@ -231,7 +278,12 @@
     device hasGreyscales ifFalse:[
         thumbShadowColor := Black.
         thumbLightColor := White.
-        viewBackground := Color veryLightGrey "White"
+        style == #motif ifTrue:[
+            thumbColor := White "Color grey".
+            viewBackground := Color veryLightGrey
+        ] ifFalse:[
+            viewBackground := Color veryLightGrey "White"
+        ]
     ].
 
     thumbFrameColor := Black.
@@ -292,6 +344,16 @@
     ^ scrollAction
 !
 
+scrollDownAction:aBlock
+    "ignored -
+     but implemented, so that scroller can be used in place of a scrollbar"
+!
+
+scrollUpAction:aBlock
+    "ignored -
+     but implemented, so that scroller can be used in place of a scrollbar"
+!
+
 thumbOrigin
     "answer the thumbs origin (in percent)"
 
@@ -317,14 +379,19 @@
             realNewOrigin := 0
         ]
     ].
-    (realNewOrigin = thumbOrigin) ifFalse:[
-        oldFrame := thumbFrame.
+    ((realNewOrigin ~= thumbOrigin) or:[thumbFrame isNil]) ifTrue:[
         thumbOrigin := realNewOrigin.
-        self computeThumbFrame.
-        (thumbHeight = 100) ifTrue:[^ self].
 
         shown ifTrue:[
+            oldFrame := thumbFrame.
+            self computeThumbFrame.
+            (thumbHeight = 100) ifTrue:[^ self].
+
             (thumbFrame ~~ oldFrame) ifTrue:[
+                oldFrame isNil ifTrue:[
+                    self drawThumb.
+                    ^ self
+                ].
                 tH := thumbFrame height.
                 tW := thumbFrame width.
                 oldTop := oldFrame top.
@@ -343,11 +410,12 @@
                     ^ self
                 ].
 
+                self catchExpose.
                 self copyFrom:self x:left y:oldTop
                                  toX:left y:thumbTop
                                width:tW height:tH.
 
-                self catchExpose.
+"/                self catchExpose.
 
                 oldTop > thumbTop ifTrue:[
                     delta := oldTop - thumbTop.
@@ -390,16 +458,18 @@
     ] ifFalse:[
         realNewHeight := newHeight
     ].
-    (realNewHeight = thumbHeight) ifFalse:[
-        oldFrame := thumbFrame.
+    ((realNewHeight ~= thumbHeight) or:[thumbFrame isNil]) ifTrue:[
         thumbHeight := realNewHeight.
-        self computeThumbFrame.
         shown ifTrue:[
-            (oldFrame ~~ thumbFrame) ifTrue:[
-                self drawThumbBackgroundInX:(oldFrame left)
-                                          y:(oldFrame top) 
-                                      width:(oldFrame width) 
-                                     height:(oldFrame height).
+            oldFrame := thumbFrame.
+            self computeThumbFrame.
+            (fixThumbHeight or:[oldFrame ~~ thumbFrame]) ifTrue:[
+                oldFrame notNil ifTrue:[
+                    self drawThumbBackgroundInX:(oldFrame left)
+                                              y:(oldFrame top) 
+                                          width:(oldFrame width) 
+                                         height:(oldFrame height).
+                ].
                 self drawThumb
             ]
         ]
@@ -409,7 +479,7 @@
 thumbOrigin:newOrigin thumbHeight:newHeight
     "set both thumbs height and origin (in percent)"
 
-    |realNewOrigin realNewHeight old new same|
+    |realNewOrigin realNewHeight old new changed|
 
     (newHeight > 100) ifTrue:[
         realNewHeight := 100
@@ -425,31 +495,29 @@
         realNewOrigin := 0
     ].
 
-    same := (realNewHeight = thumbHeight).
-    same ifTrue:[
-        same := (realNewOrigin = thumbOrigin)
-    ].
-
-    same ifFalse:[
+    changed := (realNewHeight ~= thumbHeight) or:[realNewOrigin ~= thumbOrigin].
+    (changed or:[thumbFrame isNil]) ifTrue:[
         old := self absFromPercent:thumbOrigin.
         new := self absFromPercent:realNewOrigin.
-        (old == new) ifTrue:[
+        changed := old ~~ new.
+        changed ifFalse:[
             old := self absFromPercent:thumbHeight.
             new := self absFromPercent:realNewHeight.
-            (old == new) ifTrue:[^ self]
+            changed := (old ~~ new)
         ].
-
-        shown ifTrue:[
-            self drawThumbBackgroundInX:(thumbFrame left)
-                                      y:(thumbFrame top) 
-                                  width:(thumbFrame width) 
-                                 height:(thumbFrame height).
-        ].
-        thumbOrigin := realNewOrigin.
-        thumbHeight := realNewHeight.
-        self computeThumbFrame.
-        shown ifTrue:[
-            self drawThumb
+        (changed or:[thumbFrame isNil]) ifTrue:[
+            thumbOrigin := realNewOrigin.
+            thumbHeight := realNewHeight.
+            shown ifTrue:[
+                thumbFrame notNil ifTrue:[
+                    self drawThumbBackgroundInX:(thumbFrame left)
+                                              y:(thumbFrame top) 
+                                          width:(thumbFrame width) 
+                                         height:(thumbFrame height).
+                ].
+                self computeThumbFrame.
+                self drawThumb
+            ]
         ]
     ]
 !
@@ -457,25 +525,37 @@
 setThumbFor:aView
     "get contents and size info from aView and adjust thumb"
 
-    |percentSize percentOrigin totalHeight|
+    |percentSize percentOrigin contentsSize contentsPosition viewsSize|
 
+    "
+     get the content's size
+    "
     aView isNil ifTrue:[
-        totalHeight := 0
+        contentsSize := 0
     ] ifFalse:[
-        totalHeight := aView heightOfContents
+        moveDirection == #y ifTrue:[
+            contentsSize := aView heightOfContents
+        ] ifFalse:[
+            contentsSize := aView widthOfContents
+        ]
     ].
-    (totalHeight = 0) ifTrue:[
+
+    (contentsSize = 0) ifTrue:[
         percentSize := 100.
         percentOrigin := 100
     ] ifFalse:[
-        percentSize := (aView innerHeight) * 100.0 / totalHeight.
-        percentOrigin := (aView yOriginOfContents) * 100.0 / totalHeight.
+        viewsSize := (moveDirection == #y) ifTrue:[aView innerHeight] ifFalse:[aView innerWidth].
+        contentsPosition := (moveDirection == #y) ifTrue:[aView yOriginOfContents] ifFalse:[aView xOriginOfContents].
+
+
+        percentSize := viewsSize * 100.0 / contentsSize.
+        percentOrigin := contentsPosition * 100.0 / contentsSize.
         percentOrigin + percentSize > 100.0 ifTrue:[
             "actually showing stuff below contents of view"
 "
-            totalHeight := aView yOriginOfContents + aView innerHeight.
-            percentSize := (aView innerHeight) * 100.0 / totalHeight.
-            percentOrigin := (aView yOriginOfContents) * 100.0 / totalHeight
+            contentsSize := contentsPosition + aView innerHeight.
+            percentSize := viewsSize * 100.0 / contentsSize.
+            percentOrigin := contentsPosition * 100.0 / contentsSize
 "
         ]
     ].
@@ -493,13 +573,16 @@
 setThumbHeightFor:aView
     "get contents and size info from aView and adjust thumb height"
 
-    |percent totalHeight|
+    |percent totalHeight viewsSize|
 
-    totalHeight := aView heightOfContents.
+    totalHeight := (moveDirection == #y) ifTrue:[aView heightOfContents]
+                                         ifFalse:[aView widthOfContents].
     (totalHeight = 0) ifTrue:[
         percent := 100
     ] ifFalse:[
-        percent := (aView innerHeight) * 100.0 / totalHeight
+        viewsSize := (moveDirection == #y) ifTrue:[aView innerHeight]
+                                           ifFalse:[aView innerWidth].
+        percent := viewsSize * 100.0 / totalHeight
     ].
     self thumbHeight:percent
 !
@@ -507,13 +590,16 @@
 setThumbOriginFor:aView
     "get contents and size info from aView and adjust thumb origin"
 
-    |percent totalHeight|
+    |percent totalHeight contentsPosition|
 
-    totalHeight := aView heightOfContents.
+    totalHeight := (moveDirection == #y) ifTrue:[aView heightOfContents]
+                                         ifFalse:[aView widthOfContents].
     (totalHeight = 0) ifTrue:[
         percent := 100
     ] ifFalse:[
-        percent := (aView yOriginOfContents) * 100.0 / totalHeight
+        contentsPosition := (moveDirection == #y) ifTrue:[aView yOriginOfContents]
+                                                  ifFalse:[aView xOriginOfContents].
+        percent := contentsPosition * 100.0 / totalHeight
     ].
     self thumbOrigin:percent
 !
@@ -522,7 +608,7 @@
     "change the color of the thumb"
 
     thumbColor := aColor on:device.
-    self is3D ifTrue:[
+    (style ~~ #normal) ifTrue:[
         thumbShadowColor := aColor darkened on:device.
         thumbLightColor := aColor lightened on:device.
         thumbHalfShadowColor := thumbShadowColor darkened on:device.
@@ -533,6 +619,12 @@
     ]
 !
 
+thumbColor
+    "return the thumbs color"
+
+    ^ thumbColor
+!
+
 thumbFrame
     "return the area used by the thumbFrame (in device coordinates).
      Allows access to the thumbs physical screen position, for
@@ -553,7 +645,9 @@
     ] ifFalse:[
         fullSize := width
     ].
-    ^ ((percent * (fullSize - (margin * 2))) / 100) rounded
+"/    ^ ((percent * (fullSize - (margin * 2))) / 100) rounded
+"/ 20-apr-94
+    ^ ((percent * (fullSize - thumbFrameSizeDifference- (margin * 2))) / 100) rounded
 !
 
 percentFromAbs:absValue
@@ -574,14 +668,18 @@
 !
 
 computeThumbFrame
-    "compute the thumbs frame (a rectangle) whenever thumb is moved, changed
-     height or the scrollers size has changed"
+    "compute the thumbs frame (a rectangle) whenever thumb is moved, 
+     changed height or the scrollers size has changed.
+     We take care, that the thumb will not become too small (i.e.
+     invisible or uncatchable).
+     Also, for mswindows style, its height/width is constant."
 
-    |np1 np2 ns1 ns2 nh nw ny nx t sz1 sz2|
+    |newPos1 newPos2 newSize1 newSize2 nh nw ny nx 
+     computedSize minSz sz1 sz2|
 
-    np1 := (self absFromPercent:thumbOrigin) + margin.
-    ns1 := self absFromPercent:thumbHeight.
-    thumbFrameSizeDifference := 0.
+    "compute position & size"
+    newPos1 := (self absFromPercent:thumbOrigin) + margin.
+    newSize1 := computedSize := self absFromPercent:thumbHeight.
     (moveDirection == #y) ifTrue:[
         sz1 := height.
         sz2 := width
@@ -589,51 +687,59 @@
         sz1 := width.
         sz2 := height
     ].
-    self is3D ifTrue:[
-        np2 := margin + inset.     
-        ns2 := sz2 - (margin * 2) - (inset * 2).
-        "
-         do not make thumb too small (for handle)
+
+    "
+     do we have to adjust the computed size ?
+    "
+    newPos2 := margin + inset.     
+    newSize2 := sz2 - (2 * newPos2).
+    (style ~~ #normal) ifTrue:[
         "
-        (ns1 < (10 + (2 * thumbLevel))) ifTrue:[
-            t := ns1.
-            ns1 := 10 + (2 * thumbLevel).
-            thumbFrameSizeDifference := ns1 - t
-        ]
+         do not make thumb too small (for handle & to be catchable)
+        "
+        minSz := 10 + (2 * thumbLevel)
     ] ifFalse:[
-        np2 := inset.
-        ns2 := sz2 - (inset * 2).
-
         "
          do not make thumb too small (uncatchable)
         "
-        (ns1 < 4) ifTrue:[
-            t := ns1.
-            ns1 := 4.
-            thumbFrameSizeDifference := ns1 - t
-        ]
+        minSz := 4
+    ].
+
+    (newSize1 < minSz) ifTrue:[
+        newSize1 := minSz.
+        thumbFrameSizeDifference := newSize1 - computedSize
+    ] ifFalse:[
+        thumbFrameSizeDifference := 0.
     ].
+
+    fixThumbHeight ifTrue:[
+        "have a fix-size thumb (i.e. mswindows style)"
+
+        newSize1 := sz2 - (2 * inset).   "make it square"
+        thumbFrameSizeDifference := newSize1 - computedSize.
+    ].
+
     "
-     oops - if height has been increased, we have to adjust
-     the origin
+     oops - if height does not relect real visibible area, we have to adjust the origin
     "
     (thumbFrameSizeDifference == 0) ifFalse:[
-        np1 := ((thumbOrigin * (sz1 - thumbFrameSizeDifference - (margin * 2))) / 100) rounded + margin
+        newPos1 := (self absFromPercent:thumbOrigin) + margin.
+"/        newPos1 := ((thumbOrigin * (sz1 - thumbFrameSizeDifference - (margin * 2))) / 100) rounded + margin
     ].
 
     (moveDirection == #y) ifTrue:[
-        ny := np1.
-        nx := np2.
-        nh := ns1.
-        nw := ns2.
+        ny := newPos1.
+        nx := newPos2.
+        nh := newSize1.
+        nw := newSize2.
         ny + nh + margin > height ifTrue:[
             ny := height - margin - nh
         ]
     ] ifFalse:[
-        nx := np1.
-        ny := np2.
-        nw := ns1.
-        nh := ns2.
+        nx := newPos1.
+        ny := newPos2.
+        nw := newSize1.
+        nh := newSize2.
         nx + nw + margin > width ifTrue:[
             nx := width - margin - nw
         ]
@@ -705,7 +811,7 @@
 
 drawThumbBackgroundInX:x y:y width:w height:h
     "draw part of the thumbs background; defined as a separate
-     method, to allow drawing of arbitrary patterns under thumb."
+     method, to allow drawing of arbitrary patterns under thumb (see ColorSlider)."
 
     self clearRectangleX:x y:y width:w height:h.
 !
@@ -720,16 +826,21 @@
      y "{ Class: SmallInteger }"
      mm xL xR yT yB color1 color2 savEdge|
 
-    ((thumbHeight >= 100) or:[thumbFrame height >= height]) ifTrue:[^ self].
+    (thumbHeight >= 100) ifTrue:[^ self].
+    moveDirection == #y ifTrue:[
+        thumbFrame height >= height ifTrue:[^ self].
+    ] ifFalse:[
+        thumbFrame width >= width ifTrue:[^ self].
+    ].
 
     l := thumbFrame left.
     t := thumbFrame top.
     w := thumbFrame width.
     h := thumbFrame height.
     self paint:thumbColor.
-    self fillRectangleX:l y:t width:w height:h.
+    self fillRectangleX:l y:t width:w-1 height:h.
 
-    self is3D ifFalse:[
+    (style == #normal) ifTrue:[
         self paint:thumbFrameColor.
         self displayRectangle:thumbFrame.
         ^ self
@@ -743,6 +854,15 @@
                 halfShadow:thumbHalfShadowColor halfLight:thumbHalfLightColor.
     softEdge := savEdge.
 
+    style == #mswindows ifTrue:[
+        self paint:thumbFrameColor.
+        moveDirection == #y ifTrue:[
+            self displayRectangleX:l y:t width:w"-1" height:h.
+        ] ifFalse:[
+            self displayRectangleX:l y:t width:w height:h"-1".
+        ]
+    ].
+
     (tallyLevel == 0 or:[tallyMarks == 0]) ifTrue:[
         shadowForm notNil ifTrue:[
             handleX := l + ((w - 8) // 2).
@@ -827,9 +947,30 @@
     ]
 ! !
 
+!Scroller methodsFor:'forced scroll'!
+
+pageUp
+    "page up/left"
+
+    self thumbOrigin:(thumbOrigin - thumbHeight).
+    scrollAction notNil ifTrue:[
+        scrollAction value:thumbOrigin
+    ]
+!
+
+pageDown
+    "page down/right"
+
+    self thumbOrigin:(thumbOrigin + thumbHeight).
+    scrollAction notNil ifTrue:[
+        scrollAction value:thumbOrigin
+    ]
+! !
+
 !Scroller methodsFor:'events'!
 
 redrawX:x y:y width:w height:h
+    thumbFrame isNil ifTrue:[self computeThumbFrame].
     (y > thumbFrame bottom) ifTrue:[
         self drawThumbBackgroundInX:x y:y width:w height:h.
         ^ self
@@ -846,6 +987,7 @@
     "redraw"
 
     shown ifTrue:[
+        thumbFrame isNil ifTrue:[self computeThumbFrame].
         self drawThumbBackgroundInX:0 y:0 width:width height:height.
         self drawThumb
     ]
@@ -854,8 +996,10 @@
 sizeChanged:how
     "size of scroller changed - recompute thumbs frame and redraw it"
 
-    self computeThumbFrame.
-    self redraw
+    shown ifTrue:[
+        self computeThumbFrame.
+        self redraw
+    ]
 !
 
 buttonPress:button x:x y:y
@@ -876,17 +1020,11 @@
 
     (curr < limit1) ifTrue:[
         "page up/left"
-        self thumbOrigin:(thumbOrigin - thumbHeight).
-        scrollAction notNil ifTrue:[
-            scrollAction value:thumbOrigin
-        ]
+        self pageUp
     ] ifFalse:[
         (curr > limit2) ifTrue:[
             "page down/right"
-            self thumbOrigin:(thumbOrigin + thumbHeight).
-            scrollAction notNil ifTrue:[
-                scrollAction value:thumbOrigin
-            ]
+            self pageDown
         ] ifFalse:[
             pressOffset := curr - limit1.
             scrolling := true