home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-10-25 | 9.8 KB | 270 lines | [TEXT/MPS ] |
- {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]}
- { USynchScroller.p }
- { Copyright © 1989-1990 by Apple Computer Inc. All rights reserved. }
-
- {[f-]}
- (*
- T H E O R Y O F O P E R A T I O N
-
- This is Larry Tesler's excellent unit: USynchScroller, that supports smoother
- synchronized scrolling. It addresses two problems.
-
- Problem 1: In programs like Calc and DoubleVision, when the main view scrolls,
- the side and/or top views scroll by the same amount. However, each view scrolls
- using a separate ScrollRect, causing a jumpy appearance, especially when redraw
- time is significant.
-
- Problem 2: Packaging the synchronous scrolling functionality of (the old) Calc as a
- building block would be difficult. In addition to defining a special subclass
- of TScroller for the main view's scroller, Calc used to override methods of the side
- views themselves, as well as methods of various command objects.
-
- Solution: Combine ideas from both Calc and DoubleVision. USynchScroller
- defines a subclass of TScroller:
- TSynchScroller
- and two subclasses of TSynchScroller:
- TPrimaryScroller
- TSecondaryScroller
- A primary scroller can control a list of secondary scrollers. The scroll bars
- belong exclusively to the primary scroller.
-
- The extent and scrollLimit of a secondary scroller must match those of the
- primary scroller in the pertinent axis. There are other constraints as well--
- most or all of which are obvious--that are documented herein.
-
- The unit overrides the following methods of TScroller:
- In TSynchScroller:
- SetScrollLimits
- In TPrimaryScroller and TSecondaryScroller:
- DoScroll
- Fields
- In TPrimaryScroller only:
- ScrollDraw
-
- It also implements a method, TPrimaryScroller.AddSecondaryScroller, to connect the
- primary scroller bidirectionally with each of its secondary scrollers. Once connected,
- the following behavior transpires to perform scrolling, whether invoked by the
- scroll bars, function keys, or autoscrolling:
-
- When addressed to the primary scroller:
- -> TPrimaryScroller.ScrollBy
- -> TPrimaryScroller.DoScroll
- -> TPrimaryScroller.ScrollDraw
- If it wants to scroll more than a view-full:
- ForceRedraw three times;
- Else if scrolling is unidirectional (the usual case):
- ScrollRect once with an enlarged rectangle;
- Else if it is bidirectional:
- ScrollRect three times.
- Then update.
-
- When addressed to the secondary scroller:
- -> TSecondaryScroller.ScrollBy
- -> TSecondaryScroller.DoScroll
- Delegates to TPrimaryScroller
- -> TPrimaryScroller.ScrollBy (see above)
-
- One complication is that resizing (and potentially other activities) can tell
- all three scrollers to ScrollTo without redrawing. If this were allowed to
- happen, some state would be altered twice, and the secondary scrollers would
- fail to update properly when resized larger after being scrolled to the limit.
-
- All such activities bottleneck through SetScrollLimits. Upon reflection, we realize
- that SetScrollLimits has no business affecting any peer scroller. There is an override
- of it in class TSynchScroller to temporarily set an instance variable, fLimiting,
- that tells both overrides of DoScroll to exhibit inherited behavior only. (??? there must
- be a better way!!!)
-
- The unit is not very difficult to use. The following comments from
- USynchScroller.p illustrate the changes one would make to a typical program.
-
- How to use this unit:
- In the .r file resource 'view':
- 'WIND', 'SCLP',
- { 17, 32 }, { 200, 300 }, sizeRelSuperView, sizeRelSuperView, shown, enabled,
- Scroller {
- "TPrimaryScroller",
- VertScrollBar, HorzScrollBar,
- 0, 0, 16, 16,
- VertConstrain, HorzConstrain,
- {-16, -16, 0, 0 }}, // offset more to make bars even longer
-
- 'WIND', 'SCLV',
- { 0, 32 }, { 17, 300 }, sizeFixed, sizeRelSuperView, shown, enabled,
- Scroller {
- "TSecondaryScroller",
- noVertScrollBar, noHorzScrollBar,
- 0, 0, 16, 0,
- noVertConstrain, noHorzConstrain,
- noInset },
-
- 'WIND', 'SCLH',
- { 17, 0 }, { 200, 32 }, sizeRelSuperView, sizeFixed, shown, enabled,
- Scroller {
- "TSecondaryScroller",
- noVertScrollBar, noHorzScrollBar,
- 0, 0, 0, 16,
- noVertConstrain, noHorzConstrain,
- noInset },
- In the U--.p file:
- USES …, USynchScroller
- In the U--.inc1.p file DoMakeViews:
- VAR
- aMainScroller: TPrimaryScroller;
- aLeftScroller: TSecondaryScroller;
- aTopScroller: TSecondaryScroller;
- …
- aMainScroller := TPrimaryScroller(aMainView.GetScroller(TRUE));
- aLeftScroller := TSecondaryScroller(aLeftView.GetScroller(TRUE));
- aTopScroller := TSecondaryScroller(aTopView.GetScroller(TRUE));
- aMainScroller.Controls(aLeftScroller, aTopScroller);
- In the M--.p file:
- USES …, USynchScroller
- …
- InitUSynchScroller;
- Code is generated in the following code segments:
- AInit
- ARes
- AFields
- *)
- {[f+]}
- UNIT USynchScroller;
-
- INTERFACE
-
- USES
- { • MacApp }
- UMacApp;
-
- { • Building Blocks }
-
- { • Required for this unit's interface }
-
- { • Implementation Use }
-
- CONST
- kVDependent = 1; { Dependent scroller is dependent on
- vertical translation }
- kNotVDependent = 0; { Dependent scroller is NOT dependent on
- vertical translation }
- kHDependent = 1; { Dependent scroller is dependent on
- horizontal translation }
- kNotHDependent = 0; { Dependent scroller is NOT dependent on
- horizontal translation }
-
- TYPE
-
- TSynchScroller = OBJECT (TScroller) { The common superclass of TPrimaryScroller
- and TSecondaryScroller. Merely overrides
- SetScrollLimits to assure that resizing is
- reliable. }
-
- fLimiting: BOOLEAN; { Pushed and popped by SetScrollLimits to
- tell DoScroll not to affect peer scrollers
- }
-
- PROCEDURE TSynchScroller.IRes(itsDocument: TDocument;
- itsSuperview: TView;
- VAR itsParams: Ptr); OVERRIDE;
- { Initialize from templates }
-
- PROCEDURE TSynchScroller.SetScrollLimits(scrollLimit: VPoint;
- drawScrollBars: BOOLEAN); OVERRIDE;
- { Avoid affecting any peer scroller }
-
- PROCEDURE TSynchScroller.Fields(PROCEDURE
- DoToField(fieldName: Str255;
- fieldAddr: Ptr;
- fieldType: INTEGER)); OVERRIDE;
- END; { TSynchScroller }
-
- {--------------------------------------------------------------------------------------------------}
- {$IFC qHasForward}
- TSecondaryScroller = OBJECT; FORWARD;
- {$EndC}
-
- TPrimaryScroller = OBJECT (TSynchScroller) { A TPrimaryScroller has a list of
- secondary scrollers (if non-NIL): When
- it scrolls it makes the scrollers on the
- list scroll too in the very same
- ScrollRect toolbox call if possible. If
- some of the secondary scrollers can't be
- scrolled in the same ScrollRect call
- because they only scroll in one
- direction then they will be scrolled
- separately as necessary. }
-
- fSecondaryScrollers: TList; { The list of secondary scrollers, if any }
-
- PROCEDURE TPrimaryScroller.IRes(itsDocument: TDocument;
- itsSuperview: TView;
- VAR itsParams: Ptr); OVERRIDE;
- { Initialize from templates }
-
- PROCEDURE TPrimaryScroller.Free; OVERRIDE;
- { Frees its list }
-
- PROCEDURE TPrimaryScroller.AddSecondaryScroller(itsSecondaryScroller:
- TSecondaryScroller;
- itsHDependency,
- itsVDependency: VCoordinate);
- { Become associated with the secondary scroller(s). The V and H dependencies could
- someday be scale factors but for now are kVDependent and kHDependent for now. }
-
- PROCEDURE TPrimaryScroller.RemoveSecondaryScroller(itsSecondaryScroller:
- TSecondaryScroller);
- { Remove the secondary scroller. }
-
- PROCEDURE TPrimaryScroller.DoScroll(delta: VPoint;
- redraw: BOOLEAN); OVERRIDE;
- { Assure that fTranslation is always updated in the secondary scrollers }
-
- PROCEDURE TPrimaryScroller.ScrollDraw(delta: VPoint;
- invalidate: BOOLEAN); OVERRIDE;
- { Scroll the secondary scrollers as well (unless called via SetScrollLimits) }
-
- PROCEDURE TPrimaryScroller.Fields(PROCEDURE
- DoToField(fieldName: Str255;
- fieldAddr: Ptr;
- fieldType: INTEGER)); OVERRIDE;
-
- END; { TPrimaryScroller }
-
- {--------------------------------------------------------------------------------------------------}
- TSecondaryScroller = OBJECT (TSynchScroller) { A TSecondaryScroller is controlled by a
- TPrimaryScroller (see above). When it is
- told to scroll, it delegates the job to
- the TPrimaryScroller. Constraints (none
- are checked by this unit at this time):
- 1. It and its subview must have the same
- length in the desired synch-scroll
- directions. 2. Commands for dragging
- within it must have it as their
- fScroller. 3. No scroll bars! }
-
- fPrimaryScroller: TPrimaryScroller; { the primary scroller }
- fDeltaFactor: Point; { kVDependent and kHDependent (or not) }
-
- PROCEDURE TSecondaryScroller.DoScroll(delta: VPoint;
- redraw: BOOLEAN); OVERRIDE;
- { Polarize and delegate scroll attempts to the primary scroller
- (unless called via SetScrollLimits) }
-
- PROCEDURE TSecondaryScroller.Fields(PROCEDURE
- DoToField(fieldName: Str255;
- fieldAddr: Ptr;
- fieldType: INTEGER)); OVERRIDE;
-
- END; { TSecondaryScroller }
-
- {--------------------------------------------------------------------------------------------------}
-
- PROCEDURE InitUSynchScroller;
- { Call from your Mxxx.p to perform required initialization. }
-
- IMPLEMENTATION
-
- {$I USynchScroller.inc1.p}
-
- END.
-