{\rtf1\mac\deff2 {\fonttbl{\f0\fswiss Chicago;}{\f2\froman New York;}{\f3\fswiss Geneva;}{\f4\fmodern Monaco;}{\f13\fnil Zapf Dingbats;}{\f14\fnil Bookman;}{\f15\fnil N Helvetica Narrow;}{\f16\fnil Palatino;}{\f18\fnil Zapf Chancery;}{\f20\froman Times;} {\f21\fswiss Helvetica;}{\f22\fmodern Courier;}{\f23\ftech Symbol;}{\f33\fnil Avant Garde;}{\f34\fnil New Century Schlbk;}{\f55\fnil Code 3 of 9;}{\f1904\fnil AppleIcon;}{\f2029\fnil Nadianne;}{\f2052\fnil Zeal;}{\f12899\fnil AppleGaramond LtIt;} {\f12900\fnil AppleGaramond BkIt;}{\f12901\fnil AppleGaramond BdIt;}{\f12902\fnil AppleGaramond Lt;}{\f12903\fnil AppleGaramond Bk;}{\f12904\fnil AppleGaramond Bd;}}{\colortbl\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blu e255; \red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\ green255\blue0;\red255\green255\blue255;}{\stylesheet{\tx585\tx720 \f20 \sbasedon222\snext0 Normal;}{\s1\tx360 \f20\fs20 \sbasedon222\snext1 Normal;}{\s2\tx360 \b\f20\fs60 \sbasedon222\snext2 Title;}{\s3\tx360 \i\f20\fs20 \sbasedon1\snext3 Byline;}{\s4\tx360 \b\f20\fs20 \sbasedon3\snext4 Head1;}{\s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx 1800\tx1980\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 \sbasedon1\snext5 Code;}{\s6\fi-180\li360\tx360 \f20\fs20 \sbasedon222\snext6 BulletList;}}\margl720\margr720\margt720\margb720\deftab360\ftnbj\fracwi dth \sectd \linemod0\cols1 \pard\plain \s2\tx360 \b\f20\fs60 {\plain \b\f20 \par }\pard\plain \s3\tx360 \i\f20\fs20 {\b\fs28 Blowing Smoke\par }\pard \s3\tx360 {\plain \i\f20 \par }\pard \s3\keep\tx360 {\plain \i\f20 Michael S. Engber \par Copyright \'a9 1993 - Michael S. Engber\par }\pard \s3\tx360 {\plain \i\f20 \par }\pard\plain \tx585\tx720 \f20 This article was published in the November 1993 issue of PIE Developers magazine. For information about PIE Developers, contact Creative Digital Systems at CDS.SEM@APPLELINK.APPLE.COM or 415.621.4252.\par \pard \tx585\tx720 \par \pard \tx585\tx720 The system view types clParagrah and clEdit automatically handle scrubbing gestures using the ever popular poof sound and dissipating smoke. This article explains how you can use the scrub effect in your own views. This article assumes that the reader has some experience using NTK to write Newton applications. The culmination of this article is a method, SmokeIt, that you define in your application\rquote s base view. You pass this method a bounds frame, in global coordinates, describing the rec tangle you want \ldblquote smoked.\rdblquote \par \pard\plain \s4\tx360 \b\f20\fs20 {\plain \b\f20 \par }\pard \s4\keep\tx360 {\plain \b\f20 Poof Sound\par }\pard\plain \tx360\tx585\tx720 \f20 The first problem to solve is how to get the graphics and sound data. Fortunately, we can use the same bitmaps and sound the system uses. The sound is available as ROM_poof and the three bitmaps in the animation sequence are ROM_Cloud1, ROM_Cloud2, and ROM _Cloud3. These pre-defined constants and many others are provided by NTK. In the initial release of NTK, all the constant definitions are in the \ldblquote GlobalData\rdblquote text file. In future releases of NTK, the actual definitions will be in the various platform files, but a listing of them can be found in the \ldblquote NTK Definitions\rdblquote text file.\par \pard \tx360\tx585\tx720 \tab Connect the NTK Inspector and execute the following code:\par \par \pard\plain \s5\li180\keep\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\ tx1800\tx1980\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 PlaySound(ROM_poof);\par }\pard \s5\li180\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx180 0\tx1980\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \par }\pard\plain \tx360\tx585\tx720 \f20 You will hear the familiar poof. We have the sound problem solved. PlaySound plays the sound asynchronously, so all that\rquote s remains is to immediately follow the call to PlaySound with animation code.\par \pard \tx360\tx585\tx720 \par \pard\plain \s4\keep\tx360 \b\f20\fs20 {\plain \b\f20 Cloud Animation\par }\pard\plain \tx360\tx585\tx720 \f20 In principle, it sounds easy. Just draw ROM_Cloud1, ROM_Cloud2, and ROM_Cloud3 in rapid succession. I t ried a number of different techniques before finding one that closely resembled the way the system does it. Speed was not the problem. In fact, delays need to be inserted between drawing the clouds or they go by too quickly to be seen. We\rquote ll start by examining the bitmaps we need to draw. If we use NTK\rquote s Inspector to examine ROM_Cloud1, we see the following:\par \pard \tx360\tx585\tx720 \par \pard\plain \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 \{\line \tab mask: ,\line \tab bits: ,\line \tab bounds: \{left: 0, top: 0, right: 178, bottom: 109\}\}\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \par }\pard\plain \tx360\tx585\tx720 \f20 Bitmaps are represented as frames with a bits slot containing the raw bitmap data and bounds slot specifying the rectangle in which to draw the (scaled) bitmap. Bitmaps can optionally have a mask slot containing raw bitmap data that is used to determine which pixels from the bits data are actu ally drawn. The mask allows you to draw non-rectangular images. Only bits pixels whose corresponding mask pixels are \ldblquote on\rdblquote actually get drawn.\par \pard \tx360\tx585\tx720 \tab Bitmaps are used in three ways in the Newton:\par \pard\plain \s6\fi-180\li360\tx360 \f20\fs20 \par \pard \s6\fi-180\li360\keep\tx360 \bullet \tab They can be used in graphic objects that are created with MakeShape and drawn with DrawShape; \par \pard \s6\fi-180\li360\keep\tx360 \bullet \tab They can be used in the icon slots of clPicture views, in which case the view system takes care of drawing them; and \par \pard \s6\fi-180\li360\keep\tx360 \bullet \tab They can be passed to the CopyBits drawing function.\par \pard\plain \tx360\tx585\tx720 \f20 \par \pard \tx360\tx585\tx720 Unfortunately, the first two methods won\rquote t work for us. when you pass a bitmap to MakeShape the resulting graphic object does not retain the mask data. Since our cloud bitmaps have masks, we can\rquote t use MakeShape and DrawShape. clPicture views use the mask data. We could create a dummy clPicture view and a nimate it by changing its icon slot using SetValue. However, when we closed the dummy clPicture view, the entire view rectangle would be erased instead of just the area covered by clouds. A small point, but it\rquote s definitely a different look and feel from the way the system does scrubbing.\par \pard \tx360\tx585\tx720 \tab CopyBits uses the mask data if you specify modeMask as the transfer mode, and it doesn\rquote t require allocating any dummy views. That makes CopyBits the best choice of the three methods, but life is never so easy. We need to b e able to draw the clouds scaled. Unlike its namesake in the Macintosh ToolBox, CopyBits only lets you specify the top-left corner of where you want to draw, not the entire rectangle. However, CopyBits does use the bounds slot of the bitmap. We can adjust the bounds slot to achieve suitable scaling. Of course, the three cloud bitmaps are in ROM, so we can\rquote t change them directly. Instead, we clone them and change the bounds of the clones. Below is the relevant code extracted from the SmokeIt function:\par \pard \tx360\tx585\tx720 \par \pard\plain \s5\keep\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800 \tx1980\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 local bm1 := Clone(ROM_cloud1);\line local bm2 := Clone(ROM_cloud2);\line local bm3 := Clone(ROM_cloud3);\line \line bm1.bounds := bm2.bounds := bm3.bounds := \tab \tab \tab \tab \tab smokeBounds;\line \par }\pard\plain \tx360\tx585\tx720 \f20 \tab Since bitmaps are fairly large objects (we can see that the bits and mask fields occupy 2*2632 = 5264 bytes), you might be concerned that cloning consumes a lot of memory. Remember that we\rquote re using Clone, not DeepClone. Clone only copies the top level of a data structure. The only memory allocated is a new, three-slot frame. The bits and mask fields of the clone are still references to the same objects referenced by the original slots. \par \pard \tx360\tx585\tx720 \tab Below is the code from the single animation step which draws ROM_Cloud1. First we draw the cloud, then we delay a bit, and finally we erase the cloud. These steps are repeated for each cloud.\par \pard \tx360\tx585\tx720 \par \pard\plain \s5\keep\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800 \tx1980\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 GetRoot():CopyBits(bm1,left,top,modeMask);\line Sleep(2);\line GetRoot():CopyBits(bm1,left,top,modeBic);\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \par }\pard\plain \tx360\tx585\tx720 \f20 Just for completeness, let\rquote s consider how the two drawing techniques we abandoned would handle scaling: Graphic objects created with MakeSha pe can be scaled using the ScaleShape function; clPicture views scale their pictures if you have their content set to full horizontal and full vertical justification.\par \pard \tx360\tx585\tx720 \par \pard\plain \s4\keep\tx360 \b\f20\fs20 {\plain \b\f20 Forcing A Screen Update\par }\pard\plain \tx360\tx585\tx720 \f20 The net result on the screen from each animation step is to erase the area covered by the cloud. After the animation we need to ensure that the screen updates properly. Presumably SmokeIt\rquote s caller takes care of making the scrubbed item disappear, by deleting the view for instance. However, smoke clouds usually occupy a larger area than the item being scrubbed. This means SmokeIt must ensure that the entire smokeBounds rectangle gets re-drawn.\par \pard \tx360\tx585\tx720 \tab If we know that smokeBounds is confined to a single view, we could simply send that view a Dirty message. Since we\rquote re writing SmokeIt as a general purpose routine, however, we can\rquote t rely on having this information. Alternately, we could send a Dirty message to the root view, but this turns out to be unacceptably slow. What\rquote s needed is something like the InvalRect call from the Maci ntosh ToolBox, but no such call is available. \par \pard \tx360\tx585\tx720 \tab We can achieve the same effect by creating a dummy view, opening it, and then closing it. Here is the relevant code from SmokeIt:\par \pard \tx360\tx585\tx720 \par \pard\plain \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 local danQuayle := BuildContext(\{\tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab viewClass:clView,\line \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab viewFlags:vFloating,\line \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab viewBounds:smokeBounds\});\line danQuayle:Open();\line danQuayle:Close();\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \par }\pard\plain \tx360\tx585\tx720 \f20 BuildContext takes a template as an argument and returns the view it constructed from that template. The view\rquote s parent is the root view. The template is not added to the root view\rquote s viewChildren array \endash you can liken the view to a free agent. BuildContext should not be treated as a general purpose method for creating views dynamically. It\rquote s only useful in special situations like the one at hand.\par \tab The template passed to BuildContext is the bare minimum amount of information you need to create a view. The only part worth discussing is the viewFlags. If vFloating isn\rquote t specified, the dummy view appears behind any floating views that happen to be around. Hence, those floating views wouldn\rquote t be forced to update when the dummy view is closed.\par \pard \tx360\tx585\tx720 \par \pard\plain \s4\keep\tx360 \b\f20\fs20 {\plain \b\f20 SmokeIt\par }\pard\plain \tx360\tx585\tx720 \f20 The source code to SmokeIt is listed on the nest page. Copy this source into a script slot named SmokeIt in your application\rquote s base view. That way it will be available to all the views in your application. The smokeBounds argument is a bounds frame. That i s, a frame with a left, top, bottom, and right slot. It needs to be specified in global coordinates. A typical usage might be to delete a view named deadMan. You might do this with code like: \par \pard \tx360\tx585\tx720 \par \pard\plain \s5\keep\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800 \tx1980\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 :SmokeIt(deadMan:GlobalBox());\line deadMan:Close();\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \par }\pard\plain \tx360\tx585\tx720 \f20 In practice, you\rquote ll find that you want the rectangle to be somewhat larger than the item being deleted so that the clouds completely obscure it. I find that expanding 25% in each direction is adequate.\par \tab Some useful functions and methods for creating bounds frames are SetBounds, RelBounds, StrokeBounds, :GlobalBox, and :LocalBox. They are all documented in the {\i Newton Programmer\rquote s Guide.}\par \pard \tx360\tx585\tx720 \par \pard\plain \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 func(smokeBounds)\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 begin\line \line local top := smokeBounds.top;\line local left := smokeBounds.left;\line \line local bm1 := Clone(ROM_cloud1);\line local bm2 := Clone(ROM_cloud2);\line local bm3 := Clone(ROM_cloud3);\line \line //CopyBits draws bitmaps scaled to their bounds\line bm1.bounds := bm2.bounds := bm3.bounds := smokeBounds;\line \line //expla ined below\line local danQuayle := BuildContext(\{\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab \tab \tab \tab \tab viewClass:clView,\par \tab \tab \tab \tab \tab \tab \tab viewFlags:vFloating,\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab \tab \tab \tab \tab viewBounds:smokeBounds\});\line \line PlaySound(ROM_poof);\line \line GetRoot():CopyBits(bm1,left,top,modeMask);\line Sleep(2);\line GetRoot():CopyBits(bm1,left,top,modeBic);\line \line GetRoot():CopyBits(bm2,left,top,modeMask);\line Sleep(2);\line GetRoot():CopyBits(bm2,left,top,modeBic);\line \line GetRoot():CopyBits(bm3,left,top,modeMask);\line Sleep(1);\line GetRoot():CopyBits(bm3,left,top,modeBic);\line \line //\tab \tab force update (a la InvalRect in Mac ToolBox)\line // If you knew what view(s) to dirty you could \par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 //\tab \tab just use :Dirty() to force the update. This \par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 //\tab \tab code is general purpose, so it doesn\rquote t \line //\tab \tab know (and GetRoot():Dirty() is way slow.)\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \line danQuayle:Open();\line danQuayle:Close();\line \line end\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \par }\pard \s5\sl240\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx180 0\tx1980\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \b\i\f23 p}{\plain \f22 \par }\pard\plain \s4\tx360 \b\f20\fs20 {\plain \b\f20 \par }}