| |||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||
Print This Article REALbasic University: Column 053
Zoomer: Part IILast week we started Zoomer, a little experiment to test out REALbasic's DrawInto command. Our goal is to create a virtual magnifying glass that will display one window enlarged inside another. We set up the basics of our project and learned all about the difference between global and local coordinate systems, which are significant for this project. Today we'll finish Zoomer by creating the actual code that will display the zoomed window.
The drawZoom MethodWe created the drawZoom method last time, but left the contents empty. Let's put some code in there now! The first thing we'll do is adjust the size of zoomWindow to make sure it's not bigger than our zoomed in area. Why do this? Well, if we didn't, zoomWindow would attempt to display more of a picture then there actually was. For example, say our zoomed picture is 200 pixels wide but the window is 300. Our drawPicture command tries to fill the entire zoomWindow with the picture. Since the picture isn't big enough, random stuff from memory ends up being drawn as a picture. It doesn't really hurt anything, but it sure looks strange: ![]() While there are other ways to prevent this from happening, I chose to keep things simple and not let the user make zoomWindow bigger than the zoomed area. Since the zoomed area is magnified in zoomWindow, the maximum size of zoomWindow changes based on the zoom percentage set by zoomSlider. (We set the maximum size via zoomWindow's maxWidth and maxHeight properties.) Here's the code for drawZoom:
After adjusting zoomWindow's size (if necessary), we then allocate space for p, our picture object. The size of p is the size of zoomWindow multiplied by zoomAmount. Since 16-bit and 32-bit pictures look almost exactly alike, we go with a 16-bit picture to save memory (that's the 16 in the newPicture line). Once we've got our picture object, we use the drawInto command to draw window1 into the graphics object of p. The simplest way to understand the drawInto command is to think of it like taking a screenshot of the window. In our case, we're storing that screenshot into p -- and later we're drawing p in zoomWindow, except drawing it at a larger size. Note that when we use the drawInto command, we set it to draw at coordinates 0, 0 -- that means the upper left corner of window1 will be drawn at the upper left corner of p. In the case of Zoomer, our goal was to only draw editField1 larger, not window1 in its entirety. That means we must constrain the "view" of zoomWindow so it only displays the enlarged editField. We do this in two ways. First, we've made the size of p relative to the size of editField1 (certainly no larger). Second, we control what portion of p is drawn into zoomWindow by controlling where we begin drawing. To understand this, we need to understand how the drawPicture method works. Here's the method's description in REALbasic's online help: ![]() The first three parameters are simple: the picture object and the coordinates of where to draw that object. The parameters after that are optional. (Remember, stuff between square brackets is optional.) The simplest use of drawPicture would be drawPicture p, 0, 0. But in our case, since we want to manipulate the size of the picture, we need to use those extra parameters, which can be confusing. So let's explore exactly what they mean. The first two extra parameters are the width and height of the destination: in other words, the picture will be resized to fit this width and height. If the destWidth and/or destHeight is smaller than the actual width and height of the picture you are drawing, the picture will be shrunk as it is drawn. If destWidth and/or destHeight are larger than the actual width and height of the picture, the picture will be enlarged. If you pass the actual size of the picture, then it's drawn at actual size (no enlargement or reduction). Just think of these two parameters as percentages in the form: width/height * percent. So a command like this:
would draw p at 50% size. Likewise,
would draw p at 150%. Of course, in reality, REALbasic doesn't understand percentages directly -- you must decimals (like .5 for 50% and 1.5 for 150%). And of course the drawPicture command won't work without the rest of the parameters on the line, so it's important you know how to use those as well. The next parameters are a little hard to visualize at first. These are the sourceX and sourceY parameters. They represent the starting coordinates of where you'll begin drawing from. If these are zero, you'd begin drawing from the upper left corner of p. But if these are greater than zero, you'll start drawing further down and over into the picture, effectively clipping off some of the picture's left and top by not drawing it. It's hard to explain sourceX and sourceY without also explaining sourceWidth and sourceHeight: those indicate how wide and high a chunk of p we'll grab to draw. Between the four parameters, you can grab all of p or any rectangular sub-portion of p and draw it, at any size you want (actual size, larger, or smaller). To grab all of p, just pass 0, 0 as the sourceX and sourceY parameters, and p.width and p.height as the sourceWidth and sourceHeight parameters. To grab a sub-portion of p, pass the left and top coordinates of the rectangular area you are wanting to grab, and the width and height of that rectangle. It's easiest to visualize this with a diagram: ![]() This shows you exactly what we're doing with Zoomer. The faint background picture is p -- all of p. But since zoomWindow is smaller than p, we're only drawing a sub-portion of p. The coordinates of the upper left corner of what you see in zoomWindow is what we pass as the sourceX and sourceY parameters. The width and height of that sub-portion is the width and height of zoomWindow. Calculating those upper left coordinates requires a little math. Remember, we're grabbing a rectangular sub-portion based on where the user's arrow cursor is located. The new upper left coordinates are represented by x1 and y1. The actual location of user's mouse are x and y. As you'll remember from our last lesson, these are in local window coordinates. Because of that, we want to translate these to editField1 coordinates. We do that by subtracting the left/top of editField1 from x/y.
That gives us adjusted coordinates relative to editField1. If we just stopped there, Zoomer would work. In fact, try it: temporarily comment out the if-then code as shown below and try running the program:
What happens is that the sub-portion we draw is not limited to just editField1. Zoomer now zooms the entire window, and even beyond -- showing us garbage stuff from random memory. ![]() This certainly isn't fatal, and it is interesting, but it's not what we're wanting. Since we only want to zoom editField1, we need to adjust x1 and y1 to make sure they're within editField1's range. So all that complex-looking if-then stuff does is make sure x1 isn't less than editField1.left or editField1.width! The elseif portion of the code:
simply gives us a value that's back to the actual (non-magnified) width. Remember, zoomWindow's width represents a zoomed width. If we compared it to editField1.width we'd be comparing apples and oranges. By adjusting it back to actual size (via division of the zoom amount), we compare the actual width amounts of the two. This ensures that we don't let zoomWindow display stuff past the right side of editField1. (I'm just explaining the width portion here, but it's exactly the same code for the height, except we use the height values.) The result of all this is x1 and y1 values that are within range of editField1's position on window1. With this formula, x1 will never be too far left or too far to the right, and the same with y1 never being too high or too low. The final bit of code is the "simple" drawPicture command. Now that we've done all our calculations, we just draw the sub-portion of the picture. We start drawing p at 0, 0, the upper left corner of zoomWindow. We pass zW and zH, our zoomed (enlarged) width and height values, for the width and height parameters. All that work we did with x1 and y1 is now used since we tell drawPicture to start drawing from those coordinates. And finally, we tell it the width and height of the sub-portion of p we're drawing is the width and height of zoomWindow (meaning that zoomWindow will be entirely filled with a zoomed graphic).
That's not so bad, now that you understand it, right? It's a little confusing keep track of all those similar-looking parameters, but REALbasic 4 does help. When you click on the drawPicture text in the Code Editor, it will display a tip window which gives you a brief summary of the command and its parameters: ![]() Once you understand how the command works, the shorthand is usually enough to refresh your memory without having to scroll through the online help for the complete description. Whew! That was a lot of stuff, but we made it through. While this little demo doesn't do anything particularly useful, you hopefully learned some valuable techniques from the lessons. Here's an animation of Zoomer in use: ![]() If you would like the complete REALbasic project file for this week's tutorial (including resources), you may download it here.
Next WeekWe make our own painting program. So to speak.
LettersOur first letter is from William, who asks a very common question about REALbasic.
Just add a property (variable) to any module. A module is a collection of properties, constants, and methods. You get it via the File menu (choose "New Module"). Anything you add to a module is global to your entire program. Think about it: if you add a property to a window, it's within that window, so it's not global. But a module isn't within anything but your project, so it's global. (It makes sense, but REAL Software needs to explain this better.) BTW, you can refer to a window's properties via the object chain. For instance, say you've got Window1 with a boolean property called isDone. Another window or routine could refer to isDone (even though it's not global) by saying Window1.isDone. Sometimes that's helpful and better than using globals. (Remember, globals use memory the entire time your program is running.) Next, we hear from a guy named Squid. At least, that's the name he gave in his email. (I swear I don't make this stuff up!)
Hi Squid! What you want to do is easy: in the Action event of a the button, just type the name of the window you want to open, add a period, and the Show method. Like this: window2.show You can also do: window2.showModal That will make the second window open as a modal dialog (stopping all other action in your program), if it's a dialog and not a document window type. For more on this, look through some of the RBU programs where we handle dialog boxes.
About the Column REALbasic University is a weekly instructional column on programming with REALbasic and is brought to you by REALbasic Developer, the magazine for REALbasic programmers. Each week we answer select reader questions, and we're always open to ideas for future columns. Send your questions to . (Keep your questions simple and specific. General queries like "How do I write my own web browser?" will be neglected.) Your question won't be answered immediately, but will be answered in a future column. (If you don't want your correspondence published, just be sure to indicate that when you write. Otherwise it's fair game.) About the Author See the REALbasic University Archives
REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.
| |||||||||||||||||||||||||||