name: inverse layout: true class: center, middle, inverse --- # Undo reasoning and implementation Lauren Bricker CSE 340 Spring 2020 --- layout: false [//]: # (Outline Slide) # Today's goals - Introduce need for Undo by reviewing mental models - Introduce Undo conceptually - Describe Implementation details for assignment --- .left-column[ # Review: 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[ ![img: Gym doors with few affordances of where you can open them and whether there is an accessible way to use the doors,10%](img/undo/gym-doors.png) ] -- .right-column-half[ ![img: Don Norman's Human Action Cycle,10%](img/undo/norman-d-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 .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[ ![img: Don Norman's Human Action Cycle,10%](img/undo/norman-d-human-action-cycle.png) ] --- # Model of Mental Models .left-column50[ ![:img A box showing the design (white),100%](img/undo/mental1.png) ] --- # Model of Mental Models .left-column50[ ![:img A box showing the design (white) and actual function (blue missing a little bit of the white),100%](img/undo/mental2.png) ] --- # Model of 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",100%](img/undo/mental3.png) ] --- # Model of 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",100%](img/undo/mental4.png) ] --- # Model of 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 dark blue cloud labeled "Occasionally Used Part of System Functionality" around the user's well understood region,100%](img/undo/mental5.png) ] --- # Model of 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 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",100%](img/undo/mental6.png) ] ??? - 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[ ![: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%](img/undo/mental7.png) ] .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? ![:img Picture of interactor hierarchy connected to an interface and a dotted line indicating application interface, 100%](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%](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%](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? --- .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 ![:img Picture of a map with multiple edits including deleting and adding roads,100%](img/undo/flatland-roads.png) ] .right-column[ ![:img Picture of an undo history with a transaction stack that represents causality in a timeline, 80%](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 - 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 assignment .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
] --- # 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: Add a new feature to your app. Make sure it is accessible Later we will learn how to do a Heuristic Evaluation of the application! ---