Homework 5


This homework is designed to give you some practice with object modeling, composition, and animation. For this assignment you are going to write a shape interface and 4 shape classes. You will then complete an already partially written GUI that uses them. The 4 classes you will write and their API are as follows. You must strictly adhere to this API in order for the partially written GUI that I have provided to you to work. You are free to implement the classes any way you see fit. For example, you do not have to literally declare each property as an instance variable--you could for example use a Java2D Rectangle object to store the position and size properties and then implement methods that return these values individually):

  1. Shape: Shape should be an interface that defines the following methods:

    1. void draw(Graphics g): draws the shape on the screen using that shape's properties
    2. boolean contains(int x, int y): true if the shape contains the point and false otherwise. You should use the shape's bounding box to determine whether or not the shape contains the point.
    3. int getLeft(): returns the value of the left side of the shape
    4. int getTop(): returns the value of the top side of the shape
    5. int getWidth(): returns the width of the shape
    6. int getHeight(): returns the height of the shape

  2. AbstractShape: Your RectShape and TextShape classes will be composed from this class. In other words, rather than inheriting from this class, your RectShape and TextShape classes will declare an instance of AbstractShape and delegate the appropriate part of their implementation to the AbstractShape. This class should not extend any class and it should have the following properties and methods:
    1. Properties
      1. int left, top: The upper, left corner of the shape
      2. int width, height: The size of the shape
      3. Color fillColor: The fill color of the shape
      4. Color lineColor: The boundary color of the shape
      5. boolean filled: True if the shape should be filled and false otherwise
      6. boolean border: True if the shape should have a border and false otherwise
    2. Methods:
      1. boolean contains(int x, int y): true if the shape contains the point and false otherwise. You should use the shape's bounding box to determine whether or not the shape contains the point.
      2. int getLeft(): returns the value of the left property
      3. int getTop(): returns the value of the top property
      4. int getWidth(): returns the value of the width property
      5. int getHeight(): returns the value of the height property
      6. Color getFillColor(): returns the fill color of the shape
      7. Color getLineColor(): returns the boundary color of the shape
      8. boolean getFilled(): returns the value of the filled property
      9. boolean getBorder(): returns the value of the border property
      10. void setLeft(int): sets the value of the left property
      11. void setTop(int): sets the value of the top property
      12. void setWidth(int): sets the value of the width property
      13. void setHeight(int): sets the value of the height property
      14. void setFillColor(Color): sets the fill color of the shape
      15. void setLineColor(Color): sets the boundary color of the shape
      16. void setFilled(boolean): sets the value of the filled property
      17. void setBorder(boolean): sets the value of the border property

  3. RectShape: This class represents a rectangle and is capable of drawing a rectange on the screen. RectShape should implement Shape and should not extend any class. You will be tempted to have a RectShape inherit from an AbstractShape but you should not do so. Instead you should use composition and have a RectShape contain an instance of an AbstractShape. The reason for using composition rather than inheritance is that in a real system, you would want to maintain the flexibility to narrow a RectShape's API in the future, and you could not do this if you inherited from AbstractShape. RectShape should have the following properties and methods:

    1. Properties: It should have all the properties implemented by an AbstractShape
    2. Methods: It should provide all the properties implemented by an AbstractShape plus the following draw method:
      1. void draw(Graphics g): this method should draw a rectangle. The rectangle should be filled with the fill color if the filled property is true, and the rectangle should have a border drawn in the line color if the border property is true.

  4. TextShape: This class represents a piece of text that should be drawn on the screen. It should implement the Shape interface but not extend any class. Like RectShape, it should be composed from an AbstractShape. Notice that its API is narrower than that of an AbstractShape. Specifically it omits setWidth and setHeight, since the width and height properties should be read-only variables, and it also omits the border property, because text should always be rendered. TextShape should define the following properties and methods:
    1. Properties
      1. int width, height: These properties should be read-only and should be calculated from the text shape's string and font.
      2. String label: The text shape's string
      3. Font font: The text shape's font.
      4. Color fillColor: The background fill color of the TextShape
      5. Color lineColor: The color of the text
      6. boolean filled: True if the shape should be filled with the background fill color and false otherwise
    2. Methods
      1. void draw(Graphics g): Draws the text shape, with the upper left corner of the TextShape corresponding to the left and top of the TextShape. For example, if the left and top are (0,0), then I want the TextShape's upper left corner to start at (0,0). I do not want the TextShape's baseline to start at 0, which means your draw method must adjust the value it gives to Java's g.drawString command so that the baseline is drawn in a way that allows the text to appear as I have described.

        If the filled property is true, then the text should be drawn on top of a filled rectangle drawn in the text's fill color. The rectangle should be the smallest rectangle that completely contains the text shape (i.e., its left and top should be equal to the TextShape's left and top and its width and height should be equal to the TextShape's width and height). The TextShape's line color should determine the color in which the text is rendered.

      2. Get/Set methods for each of the properties, except for width and height, which should be read-only and thus only have get methods. Follow the same naming convention used for AbstractShape, where get and set are lowercase and the first character of the property is uppercased (i.e., getFont, getLabel, setFont, setLabel).
      3. int getWidth(), int getHeight(): These methods should use a FontMetric object to calculate the width and height of the current string. You can use the command:
        Toolkit.getDefaultToolkit().getFontMetrics(Font f)
          
        to get a FontMetrics object to measure the width and height of a string that is rendered in a Font f. You will be able to use this FontMetrics object before a TextShape is attached to a window (i.e., added to a CanvasShape, which is in turn added to a JPanel). In general this is poor technique because you should only try to position objects at rendering time, which in Java speak means when paintComponent gets called. You will get a warning about using a deprecated method--that is okay. I am allowing you to use this command for this assignment because you already have a significant task in trying to understand how to do object modeling, and having you defer positioning until rendering time would add unneeded complexity to the assignment.

  5. CanvasShape: This class should extend a JPanel. It should not implement Shape.
    1. Properties
      1. Vector<Shape> shapes: a list of the shapes the canvas is supposed to draw. This vector should store AbstractShapes.
    2. Methods
      1. Vector<Shape> getShapes(): returns a list of the shapes that have been registered with this canvas.
      2. void add(Shape): Adds a shape to the canvas
      3. void paintComponent(Graphics g): iterates through the shapes that have been registered with the CanvasShape and calls each of their draw methods.

    Note that a CanvasShape does not have width and height properties. That is because you can use a JPanel's getPreferredSize() method to define the desired width and height of the CanvasShape.


Graphical Interface to Create

You are going to complete a graphical interface that displays a set of colored labeled boxes. Your interface should have the following properties:

  1. The main application window will have a collection of labeled boxes. The labeled boxes should be rectangles with text shapes inscribed in them. The LabeledBox class has already been written for you to show you an example of object modeling. It can be found in ~bvz/gui/hw/hw5/src/LabeledBox.java. The main application window will be found in the lower right side of the application.
  2. There are three control panels surrounding the main application window. These three control panels allow the user to:

    1. change the font of the currently selected labeled box (upper left control panel),
    2. create a new labeled box (lower left control panel), and
    3. animatedly move the currently selected labeled box to a new destination (upper right control panel).

  3. The control panel for creating a new labeled box contains a set of widgets for setting the properties of the new labeled box and a button for creating a new labeled box. Most of this control panel has been written for you and can be found in ~bvz/gui/hw/hw5/src/LabelBoxControls.java. If you search for RectShape, CanvasShape, and LabeledBox, you can find the code that I wrote to create a new labeled box and to change the color of the color chip that you will see in this window. This file contains some useful examples of how you can create a LabeledBox and a RectShape, and how you can set properties in these objects using the API for these objects. The controls that you will see in this region include:

    1. A color chip that displays the color that will be used to fill a new labeled box. When the user clicks with the mouse button over the color chip, the action associated with the mouse button displays a JColorChooser object that allows the user to select a new fill color. The color chip reflects this new fill color. The color chip is a RectShape and it is added to one of your canvas shapes. Since the canvas shape is a JPanel, my code adds the canvas shape containing the color chip to the LabeledBoxControls region in the way I normally would with any Java Swing widget. Also since the canvas shape is exactly as large as the color chip, I can attach a MouseListener to the CanvasShape that listens for mouse events and pops up the JColorChooser object when appropriate.
    2. Text boxes to specify the left and top of the labeled box.
    3. A text box for entering the label
    4. A command button for creating a new labeled rectangle using the information gathered from the other widgets.

  4. The topmost region of the application (i.e., the North region of the window) is divided into a leftmost control panel that has a set of widgets for setting the font of the currently selected labeled box and a rightmost control panel that has a set of widgets for animatedly moving the currently selected labeled box around the main application window.

    1. Font Controls

      1. There are radio buttons for specifying the font size as small, medium, or large. The font sizes corresponding to these sizes should be 8, 12, and 24.
      2. The font controls should be disabled if no labeled box is selected, to prevent the user from making an error.
      3. When a labeled box is selected, the radio button corresponding to the box's font size should be selected. When no box is selected, it is ok to let the last selected radio button remain selected.

    2. Animation Controls

      1. There are text boxes for specifying the destination left and top locations of the labeled box.
      2. There is a slider for specifying the number of seconds that the animation should take. The animation should take from 1-10 seconds. Your animation may take a bit longer because Swing timers will drift.
      3. There is a command button for invoking the animation. When the command button is selected, its actionPerformed method should create and start a Swing timer object that animatedly moves the currently selected labeled box from its current location to its new location. You should use a 25 frames per second animation rate.
      4. The animation controls should be disabled if no labeled box is selected, to prevent the user from making an error.

    3. A labeled box may be selected by clicking on it with the mouse. If another labeled box was previously selected, it should become de-selected. If the mouse is clicked over an empty region of the canvas, then any previously selected labeled box should be de-selected. Selected labeled boxes should be given a blue border, become unfilled, and render their text in white (the text background should still be black). For this assignment you should attach a mouse listener to the Canvas Shape that represents the drawing panel and iterate through the canvas's shapes, querying each one in turn, to determine whether or not they contain the mouse point. If they contain the mouse point, then you should select them.

    4. The control panel for creating a labeled box can alter a number of the properties of a labeled box. The remaining properties should have the following default values:

      1. The text should be rendered in yellow, unless its labeled box is selected, in which case it should be rendered in white.
      2. The text should be a sans-serif font with a plain style. The default font size will be medium (12) but can be changed by the font controls.
      3. The text background should be black
      4. A labeled box's border should be black, unless it is selected, in which case it should be blue.

      My code for creating a labeled box will assign the appropriate default values to a labeled box but when you de-select a labeled box, you will need to restore these default values.


What Has Been Done For You

The following classes have already been created for you in ~bvz/gui/hw/hw5/src:

  1. LabeledBoxApplication.java: This class is the top-level class for your application. It creates a JFrame, then creates instances of each of the control panels and the main application panel, adds them to the JFrame, and makes the JFrame visible.
  2. LabeledBox.java: This class creates a labeled box that consists of a RectShape and a TextShape. If you look at the code for the class, you can see which properties have been defined for a labeled box.


What Has Been Partially Done For You

The following classes in ~bvz/gui/hw/hw5/src have been started for you but you need to complete them:

  1. LabelBoxControls.java: This class creates the control panel that allows the user to set the properties for a new labeled box and to create a labeled box. You need to add code to this class to do your own layout of the widgets (search for the comment "Your layout code goes here").

  2. DrawPanel.java: This class creates the main application panel. You will need to add the following functionality:


What You Need To Write From Scratch

The following classes and interfaces you will need to write from scratch:

  1. Shape.java (interface)
  2. AbstractShape.java
  3. RectShape.java
  4. TextShape.java
  5. CanvasShape.java
  6. FontControls.java: The font control panel. The constructor should take a reference to the main drawing panel (see LabeledBoxApplication.java for the constructor call). You will need a reference to the main drawing panel in order to determine the currently selected labeled box. It is okay to create this class with an IDE if you want.
  7. AnimationControls.java: The animation control panel. The constructor should take a reference to the main drawing panel (see LabeledBoxApplication.java for the constructor call). You will need a reference to the main drawing panel in order to determine the currently selected labeled box. It is okay to create this class with an IDE if you want.

What to Submit

Submit a jar file named LabeledBox.jar with the following files:

  1. All of the .java files mentioned above, even the ones I completed for you.
  2. A manifest.txt file that indicates that LabeledBoxApplication is your main class.
  3. All your class files.

We will expect to be able to execute your jar file by typing:

java -jar LabeledBox.jar