CPSC 225, Spring 2019
Lab 10: Stream API / E-Card Designer
We have been looking at the stream API in class. The first part of the lab asks you do a few short exercises using that API.
In the second, and main, part of the lab, you will work with a GUI program that lets the user place shapes, text, and images in a window. To make things more definite, we think of the picture that is crearted as an "e-card", that is, a kind of greating card that could be emailed to someone. In next week's lab, you will add the ability to work with files to this program.
You will need copies of the files SimpleStreams.java, ECardDesigner.java, and SimpleDialogs.java. All the work for this lab will be done in the first two of those three files. In addition, you will need copies of the folders images and textures. The Java files and the folders should be copied into the src folder in your project. Note that you should copy the entire folders, not their contents, into your project. Make sure that they are inside the src folder. You can find copies of both the the Java files and the folders in /classes/cs225/lab10-files. There is also a downaloadable .zip file, lab10-files.zip that contains all of the files that you need.
The due dates for the two parts of the lab have been changed: SimpleStreams.java is due at the start of next week's lab, on April 9. You should submit that file into your homework folder by that time. The completed ECardDesigner project, including working with files, will be due in two weeks. Because of the test on Monday, April 15, it will be due on Thursday, April 18 later.
Your program is due by the start of lab next week, on April 9. You should copy the files SimpleStreams.java and ECardDesigner.java into a folder named lab10 inside your homework folder. It is not necessary to submit a copy of SimpleDialog.java. And you only need to submit copies of the images and texture folders if you have modified the contents of those folders. Note that the lab is due by the start of lab next week. Next week's lab will continue the "eCard" project, and you will have the option of using my solution to this week's lab instead of your own. At the start of the lab, I will make my own solution to the eCard program available. Your program must be submitted by that time!
Stream API Exercises
The main() routine in the file SimpleStreams.java contains seven numbered exercises on the stream API. The exercises are given in comments. You can put your solution to each exercise after the comment that contains the exercise. You should be able to finish all seven exercises fairly quickly. Four of the exercises use an array of Names, where Name is a class that is defined in the same file. The class and the array are defined below main().
Recall that to use the stream API, you need to make a stream, possibly apply some intermediate operations, and finally apply a terminal operation. Some of the intermediate operations that we have seen include filter(), map(), and mapToInt(). Some of the terminal operations that we have seen include forEach(), sum(), count(), max(), and min().
(At the end of main() is a commented-out example that uses the more complicated terminal operation collect(). If you want to see what it does, you can un-comment it.)
In the second part of the lab, you will work on a GUI program, ECardDesigner.java. In the process, you will learn about several new GUI classes and techniques. Much of the new material was discussed in class yesterday. The program is already 400 lines long. Next week, you will continue to work on the eCard project by adding support for files.
When you run the original program, you will see a large area containing three items: an image, a shape, and a text. You can drag the three items around (but they will always stay in the same back-to-front order). These items are created as examples at the end of the start() method. They should NOT be included in the program that you turn in. The program also has a menu bar, with a fully functioning "File" menu and "Background" menu. The "Edit" and "Add Item" menu have commands, but the commands do not work; you will implement them. At the bottom of the window is a two-row toolbar containing several controls; you will make use of the values from these controls when you add an item to the eCard.
As mentioned in class, JavaFX has some standard "dialog boxes" for user interaction, but they are not very convenient to use. The class "SimpleDialog" has static methods for showing some common kinds of the dialog and getting the user's input from the dialog, if any. The eCard program currently uses only SimpleDialogs.colorChooser(), which is used for the custom background color command. You will want to use SimpleDialogs.message() to show error messages to the user, and you might want to use SimpleDialogs.prompt() to ask the user a question and get a response.
Your first task is to implement the commands in the "Add Item" menu. The code for this goes into the handleMenu() method. Be sure to understand that the parameter, command, to that menu is the text from the menu item that the user has selected.
There are three kinds of items: images, text, and shapes. An image item is an object of type ImageView. A text item is an object of type Text. And a shape item is an object of type Rectangle or Oval. The Rectangle class is used both for plain rectangles and for roundrects. These are all subclasses of Node. (If you would like to know more about these classes, see the JavaFX API, but everything that you really need to know is given here or in the example code at the end of the start() method.)
To add an item, you need to create the object that represents the item, configure it (in the case of a text item or shape), and make it draggable (by passing it to the makeDraggable() method). You also need to add it to items, an ArrayList<Node> that is already defined in the program. This list keeps track of the items that are in the eCard.
The "Add Text" command should add a text item to the card. The data that you need to create and configure the text time comes from the top row of the toolbar. There are global variables representing each of the controls in the toolbar. You can read the values of the controls in the first row to determine the properties of the Text object. The actual text to be displayed, for example, can be obtained by calling textInput.getText(). The hard part is creating the Font that will be used for the text. The most general factory method for making fonts has the form
Font font = Font.font( family, weight, posture, size );
The first parameter is a string giving the name of the font family. It can be null to use the default font. Unfortunately, other values are not standardized. Some names that might work on most computers include "Times New Roman", "Arial", and "Courier New". If the specified family does not exist, it is not an error; the default font will be used. The second parameter is a constant of type FontWeight which should be FontWeight.BOLD for bold text or FontWeight.NORMAL for non-bold. The third parameter is a constant of type FontPosture which should be FontPosture.ITALIC for italic text or FontPosture.REGULAR for non-italic. The last parameter is an int specifying the size of the text, in points (which are more-or-less the same as pixels). You can get the value from textSize.getValue(). (The value returned by textSize.getValue() is a String; you will need to convert it to an int.)
The next three commands from the "Add Item" menu add shapes. Use an Ellipse for "Add Oval" and a Rectangle for "Add Rect" and "Add RoundRect". The data that you need to create and configure the shape comes from the second row of controls in the toolbar, which are represented by another set of global variables. Note that it is an error if the strings in the widthInput and heightInput boxes do not represent legal integers.
The remaining commands in the "Add Item" menu are file names of image resource files that can be found in the images folder that you added to the project. As in the example code at the end of the start() method, you need to create an Image object from the resource file, and then create an ImageView object from the Image. Note that you need to add the folder name to the file name; for example, for the file name "earthrise.jpg", the full resource file name is "images/earthrise.jpg". No configuration is needed for image items. The file names in the menu come from the global array named imageFileNames. You should check out how the similar array, textureFileNames, is already used in the handleMenu() method. You can implement the image item commands in a similar way.
Don't forget to add new items to both the ArrayList of items and to the StackPane that holds the content of the eCard!
Your second task is to work on the "Edit" menu. You should implement the "Remove Front Item" command in the "Edit" menu. That item is disabled when the program first starts, and it should be disabled whenever the items array is empty. However, when an item is added, it should become enabled. You should enable/disable the menu item at appropriate times. The code for removing the front item, assuming that the size of the list of items is greater than zero, is
Node node = items.remove(items.size() - 1); content.getChildren().remove(node);
You should also add a second command named "Clear" to the "Edit" menu, and implement it. You will need to work in both the handleMenu() method and in the getMenuBar() method. The command should remove all items from the eCard. Optionally, it might also reset the background of the eCard to white. Like "Remove Front Item", this command should be enabled only when there are actually items in the eCard.
For the last point of a full-credit grade, you should also do one of the following (and for extra credit, you can do more than one:
- Add another ColorPicker to the shape controls for setting the color of the stroke drawn around the shape.
- Add a ComboBox to the shape controls for setting the width of the stroke drawn around the shape. Consider using the value "None" to mean that the shape should not be stroked.
- Add a "Scale Image" command to the "Edit" menu. The command should be enabled only if an ImageView is the front item. (If you don't know about the instaceof operator, ask!) You can use SimpleDialogs.prompt() to ask the user to enter a scale factor, which should be converted to type double. Typically, a scale factor might be in the range of about 0.2 to 3.0. You should make sure that the value entered by the user makes sense.
To implement the "Scale Image" command, you can use the following code, which uses setFitWidth() to set the size of the scaled image and adds some commands recommended by the JavaFX API for scaling an ImageView. Here, img is the ImageView object representing the image item from the eCard:
img.setFitWidth(img.getImage().getWidth()*scaleFactor); img.setPreserveRatio(true); img.setSmooth(true); img.setCache(true);
You might also consider adding other editing capabilities. You can talk to me about ideas if you are interested.