davidl@inteloc.intel.com (David Levine) (02/17/88)
This article shows you one way to do a scroll bar entirely in HyperTalk.
First, create your scrolling text field wherever you want it, and give it
a name. (I called mine "test".) Now use Command-Shift-3 to take a screen
snapshot. Using Artisto or some such DA, copy the scroll bar from the
screen dump and paste it over the scroll bar of the scrolling field.
Make it opaque.
Create three Transparent buttons exactly corresponding to the up-arrow
region, gray scrolling region, and down-arrow region. Create a Rectangular
button exactly corresponding to the scroll bar's thumb (the white "elevator"
box). Call this last button "thumb". The sizes of the buttons in pixels
should be as follows:
Arrows: 17 across by 16 high
Scrolling region: 17 across by (height of field - 30) high
Thumb: 15 across by 16 high
Using the appropriate gray, paint over the elevator box in the picture of
the scroll bar. Now you have a scrolling field, with a picture of the
scroll bar (sans thumb) pasted over its scroll bar, and four buttons
corresponding to the active parts of the scroll bar over that.
Now you can start scripting.
Assign the following script to the scrolling field:
on closeField
global contentHeight, fieldHeight, maxScroll, minThumb, maxThumb
-- all updated whenever field contents change
get the number of lines in card field "test"
-- too bad "in me" doesn't work...
multiply it by the textHeight of me
put it into contentHeight
-- height of field contents in pixels
-- ONLY VALID IF FIELD CONTAINS NO SOFT RETURNS
get the rect of me
put (item 4 of it - item 2 of it) into fieldHeight
-- height of field on screen, in pixels
put (contentHeight - fieldHeight) + <Option-Return>
(the textHeight of me - the textSize of me) - 2 into maxScroll
if maxScroll < 0 then put 0 into maxScroll
-- maximum pixels that can scroll off top of field
-- ONLY VALID IF FIELD CONTAINS NO SOFT RETURNS
put (item 2 of the rect of me + 24) into minThumb
-- minimum valid Y coordinate (closest to top of screen)
-- for loc (center) of "thumb" button: 24 is the height
-- of the scroll arrow region plus 1/2 height of thumb
put (item 4 of the rect of me - 24) into maxThumb
-- maximum valid Y coordinate (closest to bottom of screen)
-- for loc (center) of "thumb" button, as for minThumb
send setPos to card button "thumb"
-- reset thumb position based on current field contents
end closeField
Assign the following script to the up-arrow button:
on mouseStillDown
global maxThumb, minThumb, maxScroll -- set by closeField handler
get the scroll of card field "test"
subtract 40 from it
if it < 0 then
put 0 into it
-- don't scroll past top of field
end if
set the scroll of card field "test" to it
send setPos to card button "thumb"
end mouseStillDown
Assign the following script to the down-arrow button:
on mouseStillDown
global maxScroll -- set by closeField handler
get the scroll of card field "test"
add 40 to it
if it > maxScroll then
put maxScroll into it
-- don't scroll past bottom of field
end if
set the scroll of card field "test" to it
send setPos to card button "thumb"
end mouseStillDown
Assign the following script to the button corresponding to the gray
scrolling region:
on mouseUp
global fieldHeight, maxScroll -- set by closeField handler
get the mouseLoc
if item 2 of it < item 2 of the rect of card button "thumb" then
-- user clicked above top of thumb
get the scroll of card field "test"
subtract (fieldHeight - 10) from it
-- scroll by something less than a full field
if it < 0 then
put 0 into it
-- don't scroll past top of field
end if
else if item 2 of it > item 4 of the rect of card button "thumb" then
-- user clicked below bottom of thumb
get the scroll of card field "test"
add (fieldHeight - 10) to it
if it > maxScroll then
put maxScroll into it
-- don't scroll past bottom of field
end if
else
-- user clicked in thumb, don't do anything
get the scroll of card field "test"
end if
set the scroll of card field "test" to it
send setPos to card button "thumb"
end mouseUp
Assign the following script to the button "thumb":
on mouseStillDown
global maxThumb, minThumb, maxScroll -- set by closeField handler
-- this handler lets the user drag the button up and down
-- and sets the scroll of the field accordingly
if maxScroll = 0 then
-- field has nothing to scroll, don't move
set the loc of me to item 1 of the loc of me, minThumb
exit mouseStillDown
end if
get the mouseLoc
put item 1 of the loc of me into item 1 of it
-- only allow vertical motion
if item 2 of it > maxThumb then
-- don't go below bottom of scroll region
put maxThumb into item 2 of it
end if
if item 2 of it < minThumb then
-- don't go above top of scroll region
put minThumb into item 2 of it
end if
set the loc of me to it
get item 2 of the loc of me
subtract minThumb from it
divide it by (maxThumb - minThumb)
multiply it by maxScroll
set the scroll of card field "test" to trunc(it)
--
-- as the Y coordinate of "the loc of me" varies from minThumb
-- to maxThumb, "the scroll of card field test" varies from
-- maxScroll to 0, according to the formula
--
-- thumb - minThumb
-- scroll = ------------------- * maxScroll
-- maxThumb - minThumb
--
end mouseStillDown
on setPos
global maxThumb, minThumb, maxScroll -- set by closeField handler
-- this handler sets the position of the button based on
-- the current scroll of the field
if maxScroll = 0 then
-- field has nothing to scroll, set to top of scroll bar
set the loc of me to item 1 of the loc of me, minThumb
exit setPos
end if
put the scroll of card field "test" into it
multiply it by (maxThumb - minThumb)
divide it by maxScroll
add minThumb to it
put trunc(it) into temp
get the loc of me
put temp into item 2 of it
put item 1 of the loc of me into item 1 of it
--
-- as "the scroll of card field test" varies from maxScroll to 0,
-- the Y coordinate of "the loc of me" varies from minThumb
-- to maxThumb, according to the formula
--
-- scroll * (maxThumb - minThumb)
-- thumb = ------------------------------ + minThumb
-- maxScroll
--
if item 2 of it > maxThumb then
put maxThumb into item 2 of it
-- don't go below bottom of scroll region
end if
if item 2 of it < minThumb then
put minThumb into item 2 of it
-- don't go above top of scroll region
end if
set the loc of me to it
end setPos
Finally, assign the following script to the card:
on openCard
send closeField to card field "test" -- update global variables
end openCard
That's all. You now have a simulation of scroll bars in HyperTalk. You
can extend these scripts to scroll several fields in parallel. You could
also adapt this code to provide horizontal scroll bars and other
nonstandard controls.
A few words of explanation:
Most of the buttons need to know certain characteristics of the field, so
I decided to store those characteristics in global variables. These
globals are all maintained by the field itself: they are automatically
updated whenever the field contents are changed (closeField message). I
put a 'send closeField to card field "test"' in the openCard handler to
be sure these variables are all updated properly before they are needed.
The "thumb" button similarly maintains its own position, using the
user-defined setPos message.
The constant 40 in the up-arrow and down-arrow scripts was chosen to
get a reasonable approximation of the speed of a real scroll bar without
excessive jerkiness. Feel free to mess with this constant for your own
purposes.
The "<Option-Return>" in the closeField handler means an Option-Return
(appears on the Mac screen as a "hook" character).
I think everything else should be fairly self-explanatory.
These scripts are not completely bug-free. For one thing, they don't work
if the field contains any "soft" returns. They can also get seriously
confused if you change the field contents from all fitting in the visible
part of the field at once at once to not fitting, or vice versa. (If this
happens, the "thumb" button can vanish: use "set the loc of card button
thumb to 50,50" to get it back on the screen, then drag it back to where
it's supposed to be.) If you come up with any fixes, let me know.
Now that I've done this, I don't think I'd use it in a for-real stack. For
one thing, it's much slower and jerkier than real scroll bars. The behavior
is also somewhat different from real scroll bars (the arrows don't highlight
when you mouseDown on them; when you drag the thumb, the field scrolls
right away (as opposed to scrolling when you release the mouse)). If I
really had to do something like this, I'd do an XCMD. However, I thought
it might be useful for some purposes (if nothing else, as an example of
HyperTalk programming).
-- David D. Levine, Technical Writer, Intel Corporation
...{decvax,ihnp4,hplabs}!tektronix!ogcvax!inteloa!inteloe!davidl
This article Copyright 1988 by David D. Levine. All rights reserved.
Permission to redistribute this information in electronic or printed
form is granted, provided that such redistribution is not for profit
and this copyright notice is included.