Flip History Strategy Guide

Handling a Non-deterministic Gesture

This chapter shows to implement a more complex gesture, that is an undo step that relates to multiple commits of the document when the end of the gesture cannot be known a priori.

Overview

One common modifications of the document occurs over a gesture. That is, the actual modification of the document integrates multiple commits, but the undo step and publication to the outside world happens once the gesture is said to be "finished".

Sometimes however, the "finished" state of a transaction cannot be determined a priori. This means that the user of the application might expect the last undo to extend over time, based on certain criteria.

Example of such modifications are :

The general idea of the modifications is that it spans over time. In this case the document is modified gradually as the gesture is performed. This allows on each commit to notify the observers so that the document views show an up-to-date of the document, even if this does not make the actual undo step stored in the history.

The undo step itself will be stored at each commit, like the simple undo case. However, this last undo step will be replaced as new actions are performed. For example, if the user moves an object on screen using the keyboard arrows 4 times, then the last undo is replaced with the equivalent full transaction representing the change from the very start. The user would then expect the undo action to revert its 4 actions to be reverted in one-step instead of each action.

Implementation

The following code illustrates such a modification of the document to change gradually the tempo of a song.

Document document (Model::use (), 123456789UL, 'appl', 'gui ');
Carrier carrier (document);
History <HistoryStoreMemory> history (document);
Song & song = document.root <Song> ();
[...]
song.tempo = song.tempo + 10.0;  (1)
auto tx = document.commit ();    (2)
history.add_undo_step (tx);      (3)
[...]
song.tempo = song.tempo + 10.0;  (4)
document.commit ();
auto tx = document.squash ();    (5)
*history.last_undo () = tx;      (6)
[...]
song.tempo = song.tempo + 10.0;  (4)
document.commit ();
auto tx = document.squash ();    (5)
*history.last_undo () = tx;      (6)
[...]
document.push ();                (7)
  1. After a keyboard event, increment the tempo by 10 bpm
  2. Commit the change
  3. Make a new undo step with this transaction
  4. Increment the tempo by 10 bpm
  5. Squash the push stack to make a single transaction out of the multiple commits
  6. Replace the last undo step with the resulting transaction
  7. At some point, the client of the library would advertise this document modification to the outside world at that moment (for example when starting a new action)