Graphic by Keith Ohlfs

CS111, Wellesley College, Fall 1999

Problem Set 9 -- Part B

Due: Friday, December 3 by 4:00 p.m.

[CS111 Home Page] [Syllabus] [Lecture Notes] [Assignments] [Programs] [Documentation] [Software Installation] [FAQ] [CS Dept.] [CWIS]


Homework Problem 3: Fishy GUIs (as opposed to gooey fishies...)

In this problem, the goal is to build GUI controls for the Fish from Problem 2. You will create a control for each of the Fish's instance methods. For example, you will make a button called "Set Body Color" and a pull-down menu (a Choice menu) to select a color, which will change the Fish's body color to whatever color you have selected. At the end you will have a control panel to the left of the area where the fish will be.

Getting started on Problem 3

First things first! Download the ps9b_programs folder from the CS111 download directory. In this folder you will find two files: FishWorld.java and FishWorld.html. Move the two files (not the whole folder!) into your ps9_programs folder from Problem 2. Then, in Symantec Cafe, open up your Fish.proj file (as you should always have your project window open when coding!). Go to the Project menu, and select Add Files. This will give you a popup dialogue box. Select the file FishWorld.java and Add it to your project. You have now incorporated the new files into your project.

Start filling the the first set of methods, and then bring the project up to date. The project will not compile successfully until you have filled in these methods.


Part a:

Because we want to have more than just the Fishtank in our applet window, the Fish will be drawn in an area of the applet window set apart by a Canvas. Canvas, like Applet, is a pre-defined class in Java which you can extend as you like. We will call our Canvas fishtank. In Problem 2, the only thing in our applet window was the fishtank, so we just drew things to the applet window. In Problem 3, we draw to the Canvas in order to keep the Fish in a certain area of the applet window, so that you will be able to add the control panel on the side.

Our overall applet window layout uses a BorderLayout, with the control panel in the West and the Canvas in the Center. The init() method sets up this layout for you. makeControlPanel() creates the control panel as described below, and makeFishtank() creates the Canvas where the Fish will be drawn..

The start() method starts up the applet. In it, we want to create a new Fish using a method called makeFish and set the FishWorld instance variable currentFish to be this new Fish. You should hand makeFish the Graphics context of your Canvas so that it can create the Fish and give it the correct Graphics context.

Before even worrying about getting things to be clickable in your control panel, just get the control panel to look right. This involves creating the appropriate Panels, Buttons, TextFields, and Choice menus. Remember to specify your Layout for each Panel, and then to put things into it in the right order.

This is done by calling a series of make methods to create each Button or TextField, or whatever, to look the way you want them to look.

The overall control panel uses a grid layout, with a row for each button, and all of them in a single column. You'll notice that some of the rows have multiple items in them (a button and a textfield, for instance). Basically, any time you want more than one thing in a specific part of your grid, you need to create a panel to arrange them together first. So for that button and textfield example, you would need to create a panel which has a button and a textfield in it, and then put the panel into your overall grid layout.

For example, if you wanted a single button in the first part of your grid, you would do something like:

	myControls.add (makeMyButton());

in your method that builds your control panel. This adds your button to the control panel called MyControls by calling the method makeMyButton to create the button.

The method makeMyButton() then creates the button, puts the labelling text (the words which appear in the button) into it, and returns the completed button. In fact, the method which makes the control panel is itself one of these make methods: it returns the completed control panel to the init() method in your FishWorld class.

You should create your control panel for your FishWorld applet to look like the one shown above. Your Choice menus contain all the color choices for the body and tail of the Fish. You may include any of the usual colors that you want. The TextFields are all length 5.


Part b:

Once you get things to look right, then you can begin to add functionality to each of your buttons. Your buttons should react as follows:

Button Action
Set Body Color calls Fish method setBodyColor using the current selectedBodyColor instance variable
Set Tail Color calls Fish method setTailColor using the current selectedTailColor instance variable
Set Position reads in the numbers entered in the two TextFields to create a new Point, and then uses the Fish method setPosition to put the Fish at the new Point
Resize reads in the number entered in the TextField and hands it to the Fish method resize as the new Fish length
Swim Up and Down reads in the number entered in the TextField and hands it to the Fish method swim as the amplitude of swim
Reset paints over the Canvas with a white Rectangle and then calls the start() method to restore the default Fish

Start by writing your action method. The action method takes two parameters: an Event and an Object. It looks something like this:

	public boolean action (Event event, Object arg) {
	}

In this method, each possible location for the event (a mouse click, in the cases with which we are concerned) is listed as a conditional, and the appropriate responding action is called. We need to define our conditionals to check if the event's target location was over each GUI element. For instance:

	if (event.target == myButton) {
		//do the right thing
	} else if (event.target == myNextButton) {
		//do the right thing
	}

and so on. The final else should call the following line:

	return super.action(event, arg);

so that if the action doesn't fit any of the ones we care about, then it will be handed up to another level to be dealt with there. Once all of the conditionals have been exhausted, we return true as the last step in the action method. This confirms that the event has been handled successfully, in the case that it was an event that we care about.

In most cases, more than one thing needs to happen in response to the Event. In these cases, the corresponding if clause contains a single call to a method which handles the response. This method contains all of the steps necessary to respond to the Event. The idea here is to keep the main action method clean, with each response only taking up a single line of code within this method.

Note that, in this problem, there are a couple of cases in which we can respond directly to the event without needing to create a separate method to handle the response.

Some particulars about the Color Choice menus:

For the cases in which the event is over the color Choice menus, the responding methods will need the Object arg from the action method's parameters. This arg will contain the value of the selection in the form of the String which is displayed as the menu item. In particular, the String "Color.blue" might be this value, if that is the selected color. The responding method needs to check to see if arg matches each of these Strings, and then set the instance variable (selectedBodyColor or selectedTailColor) to be the corresponding Color.

The responding method for this set of actions should also set the background color of the Choice menu to reflect the new selected color.

Some particulars about using the TextFields:

True to the name, TextFields are areas where text can be entered by your user. The problem with this, in our case, is that we need integers from the user. We assume that the user is intelligent and will enter numbers into our TextFields, but we still need to convert that text into Integers for the methods which need ints. To do this, we use the Integer method parseInt, which we used in some of our List problems earlier this semester.

In this problem, we apply Integer.parseInt to the result of getText(), a method of TextField. Thus, each time we need an integer from a TextField, we use something like this:

	int myInt = Integer.parseInt(myTextField.getText());

Some particulars about the resetAction method:

The resetAction() method basically needs to clear the Canvas so that the GUI can begin anew. We do this by first "erasing" the Canvas by drawing a white rectangle over its entirety. For this, we need the exact dimensions of our Canvas. Fortunately, a method exists which gives us exactly this information.

We introduce a class called Dimension which contains width and height. (You do not need to define the Dimension class. It already exists.) The Canvas method size() returns a Dimension object, from which we can extract the exact dimensions of our Canvas. The syntax looks like:

	Dimension d = myCanvas.size();

We can then use d.width and d.height to draw our white rectangle.

Remember that, in order to draw to the Canvas, we must have its Graphics context. We can get the Graphics context by using:

	Graphics myContext = myCanvas.getGraphics();

With the Canvas "erased", we can then call the start() method to restart the applet from its beginning.


Once you are done, your default Fish will react to the swim button like this:

 

This is what the applet window looks like after you've changed all the values to the ones shown (and clicked on the corresponding buttons to get it to actually change), and then clicked the swim button.