name: inverse layout: true class: center, middle, inverse --- # Undo reasoning and implementation Jennifer Mankoff CSE 340 Winter 2021 --- layout: false [//]: # (Outline Slide) # Today's goals - Introduce need for Undo by reviewing mental models - Introduce Undo conceptually - Describe Implementation details for assignment --- # Review: Every system has at least 3 different models
graph TD S[System Image:
Your Implementation ] --> |System Feedback | U[User Model: How the user thinks
the system works] D[Design Model: How you
intend the system to work ]-->S U -->|User Feedback | S
--- # Relating the Human and the Interaction .column[ ![:img Doors with identical handles labeled push and pull, 100%, width](img/undo/doors.jpeg) ] .column[ ![:img Door with what appears to be a push handle labeled pull, 100%, width](img/undo/doors2.jpeg) ] -- .column[ ![:img Don Norman's Human Action Cycle, 100%, width](img/undo/human-action-cycle.png) ] .footnote[[Don Norman, When Three World Collide: A model of the Tangible Interaction Process, 2009](https://www.researchgate.net/publication/221332102_When_three_worlds_collide_A_model_of_the_tangible_interaction_process)] ??? Note to instructors: Need to change image to mermaid --- # Relating the Human and the Interaction .doublecolumn[ **Gulf of Execution** is the user's believe in the functions the system _doesn't have_ - This is the users 'error' region **Gulf of Evaluation** is where the user _doesn't realize the system HAS a functionality_. ] .column[ ![:img Don Norman's Human Action Cycle, 100%, width](img/undo/human-action-cycle.png) ] --- # Design Model What the system designer thinks the system does ("design model") ![:img A box showing the design (white),50%,width](img/interaction/mental1.png) --- # Implementation Model What the system actually does ("system image") ![:img A box showing the design (white) and actual function (blue missing a little bit of the white),50%,width](img/interaction/mental2.png) --- # User's Mental Model What the user knows about the system ("mental model") ![:img A box showing the design (white) and actual function (blue missing a little bit of the white) with a grey circle added in the center labeled "Frequently Used (Well understood) Part of System Functionality",50%,width](img/interaction/mental3.png) --- # User's Mental Model What the user knows about the system ("mental model") ![:img A box showing the design (white) and actual function (blue missing a little bit of the white) with a grey circle added in the center labeled "Frequently Used (Well understood) Part of System Functionality",50%,width](img/interaction/mental4.png) --- # User's Mental Model What the user knows about the system ("mental model") ![:img A box showing the design (white) and actual function (blue missing a little bit of the white) with a grey circle added in the center labeled "Frequently Used (Well understood) Part of System Functionality" a dark blue cloud labeled "Occasionally Used Part of System Functionality" around the user's well understood region,50%,width](img/interaction/mental5.png) --- # User's Mental Model Everything the user thinks they know ("mental model") .left-column50[ ![:img A box showing the design (white) and actual function (blue missing a little bit of the white) with a grey circle added in the center labeled "Frequently Used (Well understood) Part of System Functionality" a dark blue cloud labeled "Occasionally Used Part of System Functionality" around the user's well understood region and another cloud further out with errors (regions outside the blue system box) labeled "Users full model of what the system does",150%,width](img/interaction/mental6.png) ] --- # Relating Gulfs and Mental Models .left-column50[ ![:img A box showing the design (white) and actual function (blue missing a little bit of the white) with a grey circle added in the center labeled "Frequently Used (Well understood) Part of System Functionality" a cloud further out with errors (regions outside the blue system box) and an arrow pointing from the part they really understand to the part outside of the application,100%,width](img/interaction/mental7.png) ] .right-column50[ What happens when the user does something they think is core but is really not supported? ] -- .right-column50[ - Need to undo! ] --- class: center, middle, inverse # How do we support Undo? --- layout:false .left-column-half[ ## Remember this? ![:img Picture of interactor hierarchy connected to an interface and a dotted line indicating application interface, 100%,width](img/undo/callbacks.png)] .right-column-half[ Dispatch Strategies - Bottom-first and top-down positional - Focus-based State Machine describes *within-view* response to events ] --- .left-column-half[ ## Event Dispatch ![:img Picture of interactor hierarchy connected to an interface and a dotted line indicating application interface and a do_action() call happening below the line in response to a button_pressed(), 100%,width](img/undo/callbacks2.png)] .right-column-half[ Callbacks handle *application* response to events - Update Application Model ] --- .left-column-half[ ## Event Dispatch ![:img Picture of interactor hierarchy connected to an interface and a dotted line indicating application interface with do_action() replaced with an actionListener, 100%, width](img/undo/callbacks3.png)] .right-column-half[ Callbacks handle *application* response to events - Update Application Model - Best implemented using custom listeners ] --- # What is `ActionPerformed`? `Higher level` input event (`Command` or `Action` object) - Puts some separation between UI and translation objects - Application (or UI) can ‘listen’ for these events: Key advantage: interactors don’t need to know who/what got notification Same basic flow as simple callbacks --- # What should an Action object do? `doAction()` ??? seems like a lot of work when we could just directly do the action. Major reason for action objects -- `undoAction()` ??? What additional information do we need to undo an action? --- # Advantages of an action object .left-column[ - can be stored on an undo stack - can create a consistent abstraction for reversing an action ] .right-column[
classDiagram AbstractAction <|.. AbstractReversibleAction AbstractReversibleAction <|.. ChangeColorAction AbstractReversibleAction <|.. ChangeThicknessAction AbstractReversibleAction <|.. AbstractReversibleViewAction AbstractReversibleViewAction <|.. StrokeAction class AbstractAction { doAction() } class AbstractReversibleAction { +boolean done +undoAction() } class AbstractReversibleViewAction { +invalidate }
] --- # Where do we store actions? A stack .left-column50[
classDiagram AbstractStackHistory <|.. StackHistory class AbstractStackHistory { addAction(AbstractReversibleAction action) undo() redo() canUndo() canRedo() } class StackHistory { +capacity: "Max stack size" }
] .right-column50[ Why a stack? ] ??? Consider having some volunteers be actions and have them act it out? --- # Undo and Redo 1) new action object created and `doAction()` called 2) Undo stack updated 3) new action object created and `doAction()` called 4) Undo stack updated 5) `undo()` invoked 6) Undo stack reduced and Redo stack increased 7) `undo()` invoked 8) Undo stack reduced and Redo stack increased ??? draw sequence --- # Undo and Redo 9) `redo()` invoked 10) Redo stack decreased and Undo stack increased 11) new action object created and `doAction()` called 12) Redo stack cleared and Undo stack stack increased --- # What if an action can't be undone? Actions that put system into a totally different context Clear both `undo` *and* `redo` stacks! Users may hate you ??? example? Saving a file --- # Implementing undo() System pops action off undo stack Calls `undoAction()` method on it Pushes it on redo stack --- # Why is undoAction() hard? Two ways to implement `undoAction()`: - *Direct Code* (each action object has custom code) - Need parameters of original action - Better store in `doAction()` for later - This is what we will implement --- # Why is undoAction() hard? Two ways to implement `undoAction()`: - *Direct Code* (each action object has custom code) - *Change Records* (Keep a record of the “old value” for everything changed by the application, then put all those values back to undo) - Like some version control systems - More general - Takes more space - `Action` object records `ChangeRecord` (changes which are abstracted into a common data format) - Application has to provide code to restore from change records --- # Implementing redo() System pops action off redo stack Calls `doAction()` method Pushes it on undo stack --- # More sophisticated forms of Undo Explicit visualization of steps Manipulation of action list Delete actions from the middle, reorder, etc. by undoing back to point of change then redoing forward But note: doAction() must be able to work in new context --- # Flatland: Semantic Undo .left-column30[ ![:img Picture of a map with multiple edits including deleting and adding roads,100%,width](img/undo/flatland-roads.png) ] .right-column60[ ![:img Picture of an undo history with a transaction stack that represents causality in a timeline, 80%,width](img/undo/flatland.png) ] .footnote[ Edwards, W. K. ; Igarashi, T. ; LaMarca, A .; Mynatt, E. D. A temporal model for multi-level undo and redo. UIST 2000, Proceedings of 13th Annual ACM Symposium on User Interface Software and Technology; 2000 November 5-8; San Diego, CA. NY: ACM; 2000; 31-40. ] --- # Discussion of assignment Android Goals: - Be able to understand and modify an existing user interface - Learn about floating action buttons - Implement core data structure for Undo HCI Goals - Modify and existing app in a consistent fashion - Make your modifications accessible - Make your modifications usable Open ended portion: Implement something new --- # Discussion of assignment .left-column-half[ ![:youtube Video of assignment, F5FyW3YJ0x4] ] .right-column-half[ ![:youtube Video of assignment, NhUE7GgH-vc] ] --- # Discussion of assignment .left-column[ Like accessibility, you are modifying a fully working program Lots to explore/understand e.g. FAB buttons) ] -- .right-column[
graph TD M[ReversibleDrawingActivity] --> D[DrawingView] M --> FUndo[FAB:Undo] M --> FRedo[FAB:Redo] M --> FColor[FAB:Color] M --> FThick[FAB:Thickness] FColor --> Red[Red] FColor --> Green[Green] FColor --> Blue[Blue] FThick --> Thin[Thin] FThick --> Med[Med] FThick --> Thick[Thick] classDef normal fill:#e6f3ff,stroke:#333,stroke-width:2px; classDef start fill:#d1e0e0,stroke:#333,stroke-width:4px; class M,D,FColor,FThick,Vis start class Red,Green,Blue,Thin,Med,Thick,Hid normal
] --- # Discussion of assignment .left-column[ DrawingView: Holds strokes FABs: - Green ones are always visible - Purple ones are only visible when active ] .right-column[
graph TD M[ReversibleDrawingActivity] --> D[DrawingView] M --> FUndo[FAB:Undo] M --> FRedo[FAB:Redo] M --> FColor[FAB:Color] M --> FThick[FAB:Thickness] FColor --> Red[Red] FColor --> Green[Green] FColor --> Blue[Blue] FThick --> Thin[Thin] FThick --> Med[Med] FThick --> Thick[Thick] classDef normal fill:#e6f3ff,stroke:#333,stroke-width:2px; classDef start fill:#d1e0e0,stroke:#333,stroke-width:4px; class M,D,FColor,FThick,Vis start class Red,Green,Blue,Thin,Med,Thick,Hid normal
] --- # Code deliverables There are five parts for the coding part of this assignment: - Part 1: Implement `ChangeThicknessAction` - Part 2: Implement history - Part 3: Add a thickness 0 FAB to the thickness menu - Part 4: Integrate colorpicker - Part 5: Your own Undo-able thing ---