| |||||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||||
Print This Article REALbasic University: Column 087
OOP University: Part ElevenLast time we started the process of turning SimpleDraw into SuperDraw by completing the significant step of moving our drawing objects inside a subclass of a canvas object. Today we'll add new capabilities to our objects and you'll begin to see how powerful object-oriented programming can be.
Selecting/Deselecting Drawing ElementsFirst let's rename the file you created last time SuperDraw.rb, since it won't be SimpleDraw much longer. Now the main capability we're going to add is the ability to select/deselect drawing elements. To do this we'll use our drawCanvasClass object to intercept mouseDown events and then we'll pass those on to our drawing objects. But here's where we start to get clever and use objects the way they're supposed to be used: instead of doing the "is this click inside an object?" calculation within drawCanvasClass, we'll ask the object to tell us if it's been clicked on! We'll also need to add a drawing routine to our objects that will draw selection handles that will display if an object is selected. This is somewhat complicated stuff, and we'll be jumping around a bit, so be prepared. One of the disadvantages of this kind of code is that there's no half-way point: you either get this to work or it doesn't. That means you can't test your code until you've got it all written, which makes writing this stuff tricky. But I'll hold your hand. So let's get started!
Drawing Element ObjectsLet's begin by focusing on adding behavior to our drawing elements. We have three kinds of drawing objects: circleClass, rectClass, and triangleClass (shapeClass doesn't really count since it doesn't draw anything). We're going to want to ad behavior to these objects. Specifically, the ability to draw themselves selected or not selected, as well as report if they were clicked on or not. Because of the OOP principle of inheritance, however, we really only need to focus on shapeClass, the parent object. Any features we add there will be automatically gained by the subobjects! First, add selected as boolean as a new property to shapeClass (double-click on it and choose "Add Property..." from the Edit menu). If this property is true, we know the drawing element is selected. While we're here, let's also a method to toggle this property. Add a new method ("New Method..." from the Edit menu), call it toggleSelection, and give it this code:
Next, add another method, this one drawSelection with parameter g as graphics:
Note that this accepts a graphics object as a parameter, then draws a red square in each of the object's four corners. These are the selection handles. When an object is selected, the handles appear, telling the user that the object is selected. (Eventually these handles will be used to allow the user to resize the object as well.) The size of these selection handles is set by the constant w. Of course this method does us no good unless it's called (executed), so we need to add code to call it. Open the draw method of shapeClass. If you followed the directions in the last lesson, this is pretty much empty, because shapeClass doesn't draw anything. But now we want it to call drawSelection. Since all the drawing objects will use the same selection handle system, we can put this call here:
Now here we run into our first OOP problem. If you remember, we used overriding to have each subobject of shapeClass execute their own draw method. If we put code within shapeClass' draw method, it will never get called because each subobject's draw method is called instead! The obvious workaround for this is to put the call to drawSelection within the draw methods for each subobject. But that's not very OOP, is it? Not only would we need to add identical code to each of four objects, we'd have to add that code for any future objects we create as well. No, there's a much better way, the OOP way. Here's what we'll do: we'll add a new event to shapeClass. All of shapeClasses subobjects will inherit that event, and we'll the draw code into that event for each subobject. Watch. With shapeClass open, go to the Edit menu and choose "New Event..." Give the event the name paint with the parameter g as graphics. Now within shapeClass' draw method, insert the line paint g above the last line. Not counting the comments, the draw method should look like this:
Perfect. Now open circleClass. If you toggle the triangle next to Events, you should see an event called (shockingly) paint. Here's the cool thing: because this is a subclass of shapeClass, which added the event, you can add code to this event! Go to circleClass' draw method and select all the text. Cut it and paste it into the paint event. It should look like this:
Next, delete the draw method from circleClass. (This is an important step! Don't forget it!) Now just repeat those steps for rectClass and triangleClass. Move the code from their draw methods into their paint events and delete their draw methods. If you run SuperDraw, you'll see it works just as before. We haven't broken anything! That's because our code calls our object's draw method. Since our subclasses no longer have a draw method, the default draw method for shapeClass is used. The revised draw method for shapeClass not only tells the subclasses to draw themselves, it also draws selection handles (if appropriate). So right now, with no way to tell an object it's selected, the program works just as before.
Have I Been Clicked?Obviously, our object needs a way to detect if it has been clicked on or not. So let's add a simple method to check for this. Within shapeClass, add new method (Edit menu, "New Method..."). Name it withinMe with parameters g as graphics, x as integer, y as integer and have it return a boolean. The full routine looks like this:
What does this do? Well, it's passed a horizontal and vertical coordinate, the click point. It simply looks to see if this is inside itself. If both the x and y values are within itself, then it returns true. If not, the default value returned is false. That's it! You see now we have a way to ask our object, "Is this click inside you?" and it will tell us yes or no!
Clicking An ObjectBut how do we get the actual click point so we can pass that on to the object? Easy! Our drawCanvasClass object receives mouseDown events from REALbasic, remember? We just need to intercept these and handle them! Open drawCanvasClass. In the mouseDown event, put this code:
I'll explain what this does in a little bit. First, let's see if we can't get this working first. Add another method:
Put this code in the mouseUp event:
Finally, add these properties to drawCanvasClass:
Okay, run the program. Add a few drawing elements and see what happens if you click on them. If you did everything correctly, they should select and deselect. Your window might look something like this: ![]() Isn't that cool? Note that the objects don't care if you're clicking on a circle, triangle, or rectangle: they all work the same. If we added new objects based on shapeClass (and we will), they'll work the same as well! That's it for this week: next time we'll make it so we can move those selected objects around (plus a lot more). If you would like the complete REALbasic project file for this week's tutorial (including resources), you may download it here.
Next WeekWe give SuperDraw some super capabilities.
LettersThis week we've got a couple suggestions for future RBU topics. First Jef writes:
Hi Jef! I haven't specifically covered databases in RBU yet, though I have demonstrated a number of data structures, which are a form of databases. Databases are something I have little experience with, but I'm currently learning and hope to do some lessons on them in 2003. In the meantime, I might suggest (blatant plug coming) a subscription to REALbasic Developer magazine which includes a regular database column. As to your specific situation, I don't know that a database is required. In fact, what you want sounds a great deal like the RBU Glossary program I wrote to generate the RBU Glossary page. The program looks like this when running: ![]() This allows me to add and edit glossary terms and definitions, and when I press the Publish button, it generates an HTML file all a hot-linked index and everything. The actual terms and definitions are saved in a regular text file, one line per term. No database required. If there's interest, I might publish the source for this program at some point in the future. Let me know. Our next letter is from Douglas Beagley, who wants 3D help:
Thanks for the suggestion, Douglas. Unfortunately my knowledge of 3D is even less that of databases! I write about what I know. As I learn about 3D, I'm sure I'll write some columns on it, but don't expect anything soon. Meantime, and I don't want to be endlessly self-promoting here, you also might consider a subscription to REALbasic Developer magazine. Every issue includes a column on 3D by Joe Nastasi (who has some 3D articles on idevgames as well), and these are designed for beginners. We also occasionally publish other 3D articles, like Joe Strout's 3D Math Primer in issue 1.2 or his tutorial on building a first-person shooter in 1.3. Part of the purpose of the magazine is that I can't do everything, as much as I'd love to. Not only don't I have the time and resources, I -- prepare yourself -- don't know everything. So I encourage everyone to take full advantage of the resources that exist. Speaking of that, keep an eye out for an upcoming announcement regarding a new RBU feature that will help in this regard....
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.
| |||||||||||||||||||||||||||||||