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.