The Window Classes

The window hierarchy consists of a base Interactor class, with Win and ScrollBar as derived classes. Let's consider these classes in turn, starting at the top of the hierarchy and moving down.

Interactors

What are the common characteristics of all interactors, including both text windows and scroll bars? They all have a size and a relative position, and this information must be accessible. They can check whether a given pair of coordinates falls within their borders, indicating that they should respond to a mouse click. In addition, all interactors have the ability to display themselves and to respond to events. However, since there is no way for a generic Interactor object to paint itself or to respond to events, those member functions should be declared as pure virtual functions.

Recall that Point and Rect classes are available for use. Using these classes, we can write a preliminary interface for Interactor as follows:

class Interactor

{

public:

int width();

int height();

Point origin();

int withinBounds( Point pos );

virtual void paint() = 0;

virtual void handleEvent( Event &action ) = 0;

protected:

Rect area;

};

Because of its pure virtual functions, the Interactor class cannot be instantiated. However, the simple functions withinBounds, width, height, and origin can all be implemented here, since those functions are the same for all interactors.

Text Windows

The Win class is derived from the Interactor class. What additional information does Win maintain, besides that stored by Interactor? As mentioned earlier, a text window must store all the text it can display. An array can be used to hold the text; something dynamically resizeable, such as a linked list, is more flexible, but for this example each window will have a fixed amount of text.

The window might contain one or two scroll bars, or none at all, depending on the length and width of the text being displayed. Should there be separate classes for windows with two scroll bars, windows with one, and windows with none? That seems unnecessary. It's simpler to have a single, flexible window class that can have zero, one, or two scroll bars. Because the scroll bars are optional, they shouldn't be contained as member objects, since that would require a non-scrolling window to store unnecessary objects. Instead, you can use pointers to ScrollBars. Win's constructor can allocate ScrollBar objects if the text is larger than the window and set the pointers to NULL otherwise.

If the window is scrollable, the text document being displayed is larger than the window, so the document's dimensions must also be stored. Similarly, it is also necessary to store the position of the window relative to the document as a whole. A window can optionally have a title, which must be stored as well. A preliminary interface for Win therefore looks like this:

class Win : public Interactor

{

public:

void paint();

void handleEvent( Event &action );

void setchar( Point pos, char newchar );

char retchar( Point pos );

void putstr( Point pos, char *newstr );

int rows();

int columns();

void setTitle( char *newtitle );

private:

ScrollBar *hscroller;

ScrollBar *vscroller;

char *textBuffer;

int textrows, textcolumns;

Point position; // Position of window in text

Point cursorPos;

char *title;

};

This is a lot of information for one class to maintain. You can create a new class, Buffer, that stores the text, and include it as a member of Win. For example:

class Buffer

{

public:

int rows();

int columns();

void setchar( Point pos, char newchar );

char retchar( Point pos );

void putstr( Point pos, char *newstr );

private:

int width, length;

char *textArray;

};

class Win : public Interactor

{

public:

void paint();

void handleEvent( Event &action );

void setTitle( char *newtitle );

private:

ScrollBar *hscroller;

ScrollBar *vscroller;

Buffer canvas;

Point position; // Position of window on text

Point cursorPos;

char *title;

};

This design divides the responsibilities of the previous Win class into two categories: storing the text and displaying the text on screen. Each Win object contains a pointer to its associated Buffer object, which stores the text that the window displays.

Scroll Bars

What additional information does ScrollBar maintain, besides that stored by Interactor? A scroll bar knows whether it is vertical or horizontal, and that information can be represented as a flag. It knows the position of the slider, which can be represented as a number between 1 and 100.

A scroll bar is constrained to be one character wide if it's vertical, or one character high if it's horizontal. It cannot exist separately from a text window. A text window must exist before a scroll bar can be created. The ScrollBar constructor can ensure these conditions.

Is any other member function needed to use a ScrollBar object? If the user scrolls a text window by moving the cursor with the arrow keys, the scroll bar's slider should move. It doesn't seem appropriate to send keyboard events to the scroll bar; the text window itself should handle those. ScrollBar should therefore have a function that sets the slider position, which Win can call. This means the preliminary interface to ScrollBar looks like this:

class ScrollBar : public Interactor

{

public:

void paint();

void handleEvent( Event &action );

void setSlider( int pos );

private:

Win *parentWin;

int sliderPos;

int orientation;

};

How do a scroll bar and its parent interact? When the mouse is clicked on a scroll bar, the text in the window scrolls and the slider moves. A ScrollBar object must tell its associated Win object to perform a scrolling action; it can do this by sending a new type of event, a ScrollEvent, that contains the direction (backward or forwards) and the amount (one line or one page).

Should ScrollBar move its slider by itself? How does it know how far to move the slider? Consider: if the text is four times as long as the window, scrolling down one page means moving the slider one-fourth of the way down the scroll bar. However, if the text is twenty times as long as the window, scrolling down one page means moving the slider one-twentieth of the way down the scroll bar. Scrolling one line at a time is similar. Thus, to calculate the distance the slider should move, ScrollBar would have to know the length of the document and the height of the window (or the corresponding ratio for horizontal scrolling). That would be a duplication of responsibilities, since those values are already stored by Win. Therefore, ScrollBar does not move its slider when the mouse is clicked. It only adjusts the slider when Win calls its setSlider function. Win is responsible for computing the proper position for the slider.