name: inverse layout: true class: center, middle, inverse --- # Undo reasoning and implementation Jennifer Mankoff CSE 340 Winter 2020 --- layout: false [//]: # (Outline Slide) .left-column[# Today's goals] .right-column[ - Introduce need for Undo by reviewing mental models - Introduce Undo conceptually - Describe Implementation details for assignment ] --- .left-column[ # Every system has at least 3 different models ] .right-column[
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 .left-column-half[  ] -- .right-column-half[  ] .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 .left-column-half[
**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_. ] .right-column-half[  ] --- # Model of Mental Models .left-column50[  ] --- # Model of Mental Models .left-column50[  ] --- # Model of Mental Models .left-column50[  ] --- # Model of Mental Models .left-column50[  ] --- # Model of Mental Models .left-column50[  ] --- # Model of Mental Models .left-column50[  ] ??? - Where are the gulf of evaluation and gulf of execution in this image? - Gulf of execution is the user 'error' region (user requests function the __system DOESNT HAVE__), gulf of evaluation is when the user __doesn't realize the system HAS a functionality__. - How does undo help the user bridge them? -- .right-column50[
**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_. ] --- # Model of Mental Models .left-column50[  ] .right-column50[ What happens when the user does something they think is core but is really not supported? ] ??? (Undo is needed) -- .right-column50[ Undo helps with this ] --- class: center, middle, inverse # How do we support Undo? --- layout:false .left-column-half[ ## Remember this? ] .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 ] .right-column-half[ Callbacks handle *application* response to events - Update Application Model ] --- .left-column-half[ ## Event Dispatch ] .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? --- .left-column[ ## Advantages of an action object - 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? --- .left-column[ ## Undo and Redo] .right-column[ 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 --- .left-column[ ## Undo and Redo] .right-column[ 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 ] --- .left-column[ ## What if an action can't be undone?] .right-column[ Actions that put system into a totally different context Clear both `undo` *and* `redo` stacks! Users may hate you] ??? example? Saving a file --- .left-column[ ## Implementing `undo()` ] .right-column[ System pops action off undo stack Calls `undoAction()` method on it Pushes it on redo stack ] --- .left-column[ ## Why is `undoAction()` hard? ] .right-column[ Two ways to implement: - *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 ] --- .left-column[ ## Why is `undoAction()` hard? ] .right-column[ Two ways to implement: - *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 ] --- .left-column[ ## Implementing `redo()` ] .right-column[ System pops action off redo stack Calls `doAction()` method Pushes it on undo stack ] --- .left-column[ ## More sophisticated forms of Undo ] .right-column[ 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 ] --- .left-column[ ## Flatland: Semantic Undo  ] .right-column[  ] .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 assginment 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 - Use heuristic evaluation to assess an app --- # Discussion of assignment .left-column-half[ ![:youtube Video of assignment, F5FyW3YJ0x4] ] .right-column-half[ ![:youtube Video of assignment, NhUE7GgH-vc] ] --- # Discussion of assginment .left-column[ First time 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
] --- There are five requirements for this assignment: - Requirement 0: Implement `ChangeThicknessAction` - Requirement 1: Implement history - Requirement 2: Add a thickness 0 FAB to the thickness menu - Requirement 3: Integrate colorpicker - Requirement 4: Add a new feature to your app. Make sure it is accessible ---