Homework 5: Undo using Command Objects
Assigned: 10/22/2020; Due 10/29/2020 at 1:30pm ET
In this assignment, you will be adding Undo to the graphical editor using the linear undo model, implemented using the Command Object pattern. Since there was a high variance in students' implementation of homework 3, and we didn't want you to have to convert your code into React, we have created a reference implementation of the editor in React, which you are required to use. Note that this version removes the canvas and the delete-all button, since we felt these would not contribute to your learning of undo.
As with previous homeworks, you are allowed to use certain libraries for homework 5, such as those used by our reference implementation of homework 3. In particular, allowed libraries include React, the ones that come with Create-React-App, ReactRouter, ReactStrap, and React Context API (which is a relatively newer API which is becoming a popular/standard way to pass data across components. Note: we recommend you read the https://reactjs.org/docs/context.html documentation to help with understanding the supplied code.). The icons we used on the buttons came from the "react-icons" package. If you implement the extra-credit feature of having a list of command objects on the right, you might find the react-scroll-to-bottom package useful.
However, you may not use libraries that implement the command object pattern or Undo -- you must implement that yourself. If you have another package you would like to use, please ask on Piazza.
The user interface for homework is essentially identical to homework 3, with the added buttons for undo and redo, and removal of the Canvas and delete-all features. We include pictures of what the base looks like, what our reference implementation looks like, with the command list on the right, and we also provide the hw5 video to see the required behaviors for undo.
Michael Liu created an implementation of homework 3 in React, which you are required to download and use. It is in the zip file you will download.
You will download the SSUI-hw5.zip file which will contain the following starter files:
- SSUI-hw5.zip - contains all the starter files and images. Download this file to get started.
- 05631-hw5-base-1.3.0.zip - this is all the code that implements the graphical editor using React. You must start from this code, and add undo handling to it. It also contains the following:
- CommandObject.js and ChangeFillColorCommandObject.js - these files are in the 05631-hw5-base-1.3.0.zip file in the subdirectory: 05631-hw5-base-1.3.0/src/shared/commandObjects/ and provide the structure and an example for how you should implement your command objects.
In addition, we have supplied the following for your reference, also in the zip file:
- HW5-example.mp4 - a movie showing how the homework should look and behave. You can also see it by clicking on it here.
- HW5-base.png - a picture of what the interface starts off like from the base version.
- HW5-command-list.png - a picture of what HW5 might look like after a few commands are executed, and then three commands are undone.
We have supplied a movie to define the required behaviors. As with previous homeworks, you should be careful to meet the specification below for full credit. If something is unclear about how it should work, or you want to deviate from the defined behavior, you might want to first ask on Piazza.
- NOTE: Make sure your implementation of homework 5 does not interfere with our ability to grade your homework 4. You might leave homework 4 alone, and put homework 5 in its own subfolder, linked from the CREATE YOUR OWN link from homework 4. Do not delete your homework 3 folder, but it is fine if nothing links to homework 3 code anymore.
- All editing operations need to be undoable, in the conventional manner. By "conventional", we mean for the undo to be similar to the way that PowerPoint works. The details of the required behaviors are specified below and shown in the hw5 video. Undo should basically restore the drawing as if the undone operation had not happened, including restoring the selection.
- The user must be able to do multiple undo operations in a row, all the way back to the beginning (the linear undo model).
- After any undo, the user must be able to use redo to undo-the-undo, and restore the drawing to the way it was before the undo happened. If there have been multiple undo operations, then redo should work that number of times as well.
- If after the user performs one or more undo operations, and then does an operation that is neither undo nor redo, then the rest of the previously undone operations should be discarded and not available (the standard way that the linear undo model works), and the new operation should go on the undo stack.
- You need to define classes that extend CommandObject for each type of operation (create, delete, move-object, change-fill-color, change-line-color, change-border-width). Each of these should store all of the needed information to undo or redo the operation in the command object itself.
- The buttons for Undo and Redo should be greyed out when those operations are not available. (Undo should only be greyed out when there are no operations at all, and redo should be greyed out whenever there are undone operations that could be redone.)
- The operations that need to be undoable include:
- create - undo makes that object disappear. It should also restore the selection to whatever was selected before the object was selected, if any (which may also cause the palete to change values to match the newly selected object).
- change fill color, change border color - undo makes the color go back to what it was before the change. If the user clicks on a bunch of colors while an object is selected (so it changes colors multiple times), these should all be individually undoable. The object affected by the undo should become selected, and the palette should reflect its current values.
- change border width - even though the object changes continually while the Border Width slider is dragged around, the undo command should undo the whole operation back to before the user started dragging the slider. For example, if the border width of the selected object is 3, then the user clicks on the slider and drags the slider (while holding the mouse button down) to 4, then 5, then 6 up to 10, then down to 9, 8, and so on down to 2 and then lifts the mouse button, the selected object will have had its border take on all of those widths, but the end result is that the border changed from 3 to 2, so only one command object should be created, and it should restore the border back to 3, and redo of that would make the border be 2. (So there is a single undo operation for the change border width.) If the use changes the border width twice in a row, by mousing down on the slide two times, those should be separate commands and separately undoable. As with change-color, the selected object should become the affected object.
- move - undo of moving an object makes the object go back to where it was before the move. As with the border width slider, only the original position (before the move) and the final position (after the mouse button is released) are relevant for undo and redo. The affected object should become selected.
- delete - undo of delete makes the object come back, and become selected, and redo makes it go away again. Note that user can change the selection manually after the undo, and then perform a redo, in which case that redo should still undo the delete of the original object, and not affect the new selection. The object should be selected after an undo of delete.
- Note that normal changing the selection by clicking around on objects is not undoable. That is, no command objects should be allocated for just changing selection or clicking on the background to change there to be no selection. If the selection changes due to undo or redo, then the palette items should change to reflect the values of the object that becomes selected as a result of the undo or redo, just like if the selection was changed by the user.
- Similarly, changing the drawing mode (arrow, line, rectangle or circle), or else changing the current colors or line width when there is no selection, should not affect the undo stack, since nothing changes in the drawing. Therefore, these changes in the palette are not undoable. But as mentionend above, the palette might be affected by undo/redo if the selection changes.
- Aborted operations (operations that are started but aborted using ESC so they don't do anything) should not affect the undo stack (since they didn't actually change anything). Thus aborted operations should not affect undo operations at all.
- Make typing ^Z (or command-Z) and ^Y (or command-shift-Z) work as accelerators for Undo and Redo respectively. [Up to +2 extra credit - +1 each]
- Add the command list panel to the right of the drawing area, as in the HW5-command-list picture and the HW5-example video. For full credit, it should show the current operation, discarded operations should be removed (due to doing a new operation after some undos), and undone operations should be greyed out. [Up to +5 extra credit]
- Add the repeat operation for all editing operations. This should work as specified in lecture 14. In particular:
- The label and icon on the "redo" button should change to be "repeat" when appropriate.
- Repeating the previous change-fill-color, change-border-color, or change-line-width operation should do the same change to the currently selected object, if something is selected, otherwise it is not enabled.
- You should be able to repeat the create command, which will create a copy of the previous object (same size, colors and border width), but offset by 10 pixels in its left and top positions.
- Repeat of delete should delete the currently selected object.
- It is not necessary to make repeat work for move operations.
- The repeat operation should appear on the undo stack so it can be undone in the normal way. If you support the command list panel, it should have a reasonable name, like "repeat of change line border color to #ea3323".
- The repeat button should be greyed out when it cannot be used (if the previous operation was a move, for example).
- [Up to +15 extra credit, out of 100]
- Add 4 "nudge move" commands, one in each direction, that moves the selected object by a few pixels with each press of the keyboard arrow key in that direction, and an undo of the nudge move. For full credit, you should coalesce multiple nudge operations into a single undo, in the same way as PowerPoint operates. [Up to +8 extra credit, out of 100 (+2 points for each direction)]
- Add support for selective undo and selective repeat using the direct inverse model. This requires that you implement the command list panel extra credit item and the repeat extra credit item above, and then also support:
- Selecting a command in the command list panel by clicking on it.
- Two new buttons for "selective undo" and "selective repeat" in the Edit part of the palette, which are enabled when those operations are possible.
- Clicking on the buttons cause the selected command in the command list to be selectively undone or repeated, and then putting that operation onto the undo history (so it appears at the end of the command list). The name of the operation should be something reasonable.
- [Up to +15 extra credit, out of 100] -- This is a challenging one to get right, but you can get up to +35 points if you do all the parts (command list + repeat + selective)
Please have a README file in your subfolder (as in hw3 and hw4), which can be in plain text, Microsoft Word, or pdf format, which should contain:
- Your site's URL on Netlify, and the link to your Github Repo (these will probably be the same as for homeworks 2 and 3, but please put them in your README anyway).
- Any extra credit work you did.
- A discussion of what you found most difficult or confusing to implement in this assignment.
- Anything else of interest in your software design or implementation.
Please upload this README file to Canvas by 1:30pm on 10/29/2020, in addition to having the file in your subfolder.