Lab 7: Drawer, Part I

Objectives

Part A. Overview

Applications for making pictures are divided cleanly into drawing programs and painting programs. The drawing program remembers the specific structure of the scene, while the painting program remembers only the pixels of the scene. Both have their advantages: A drawing program allows the user to manipulate the scene's structure, while a painting program gives more control over the exact appearance of the scene.

In this lab we'll develop a simple drawing program, a base on which we'll work later. Our drawing program allows the user to draw rectangles of various colors into a drawing area. Figure 7.1 contains a screen shot.


Figure 7.1: A screen shot of the program.

The program has two menus. The File menu has Quit and Clear options, and the Color menu (with Red, Green, and Blue options) allows the user to change the current color.

To get an idea of how this program will work, type ``drawer 1'' at the Unix command line. Your completed program should work identically to this one. Note that your solution should draw the current rectangle as the user drags the mouse.

Part B. Structure

This program includes four classes.

DrawerContains the main() method for starting the program.
Canvas Represents a window containing a drawing.
DrawingRepresents the contents of a drawing.
RectangleRepresents a single rectangle in the drawing.

The Drawer

class

The Drawer class is the simplest among the classes: Its only function is to describe a main() method. This method simply creates a Canvas object and calls its show() method.

The Canvas

class

A Canvas object represents the window, extending the DrawableFrame class. It tracks the current contents of the screen (using a Drawing object) and the current color selected. The constructor method for Canvas takes no parameters and sets up the window's menus (see the addMenu() and addMenuItem() methods of DrawableFrame).

The Canvas class will override several DrawableFrame methods, including menuItemSelected(), mouseDragging(), and mouseDragged(). The mouseDragging() method will refresh the canvas and then draw the shape that would be drawn if the user were to release the mouse. It does not actually add the shape to the drawing. The mouseDragged() method would add the shape to the drawing and then tell the canvas to redraw itself.

Additionally, the Canvas class should define the following instance methods.

Color getColor()
Returns the currently selected color. (This is for a later lab.)
Drawing getDrawing()
Returns a Drawing object representing the objects in the drawing.
void redraw()
Refreshes the window by clearing it and redrawing everything.

The Drawing

class

A Drawing object represents the contents of a drawing. It should use the ArrayList class to store all these objects. The Drawing class should define the following instance methods.

void add(Rectangle what)
Adds what into the canvas.
void drawAll(Graphics g)
Draws all of the rectangles of the drawing using g.

The Rectangle

class

A Rectangle object represents a single rectangle within the picture. It's actually quite similar to the Block class from Breakout. Its constructor method will take the dimensions of the rectangle.

Rectangle(Color color, int x, int y, int width, int height)
Creates an object representing a rectangle width pixels wide and height pixels tall, beginning with its top left corner at (x,y).

This class should define only one instance method.

void draw(Graphics g)
Draws the rectangle using g.

Part C. Observations

You may be wondering: What's the point of separating Canvas and Drawing? Why not put them together?

People who write many object-oriented programs develop rules of thumb about how to structure their programs based on previous experience. In this case, we're applying the principle that in GUI applications, it's generally a good idea to try to separate the data being represented (in this case, the Drawing) from the graphical representation of it (the Canvas). This principle is actually one instance of a more general fact that, as an object-oriented program grows, you find yourself splitting a class into handling different aspects of its job.

We might begin with single Canvas class, which would simply include an ArrayList directly in the Canvas to hold all the objects on the canvas. Initially, this would make sense, since the complete class to both manage the objects in the drawing and to manage the window is simple. But, with subsequent development, Canvas would grow successively larger, until finally it would grow unwieldy, and we would decide that it's time to divide its responsibilities into representing the drawing (a new Drawing class) and representing the window (the Canvas class). In fact, we'll do something analogous to this in the succeeding lab, when we'll decide that Canvas should really devolve the responsibility for handling mouse motions to a Tool class.