1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use crate::canvas::undo::UndoStack;
use crate::fixture_editor::state::PatchEdit;
use gled_model::asset::project::Project;
use gled_model::patching::Patch;
/// The patch the editor works on, beside the one the show is running.
///
/// Patching is not a live operation: a half-wired chain or a fixture in mid
/// drag would reach the stage. Changes therefore stay here until they are
/// applied, and are dropped when the window closes.
pub(super) struct Draft {
pub(super) patch: Patch,
/// The show's patch when this draft was last applied or taken, so what
/// changed is known.
pub(super) applied: Patch,
/// The changes to the draft. Applying keeps them: undoing past the
/// applied patch is a change to apply again.
pub(super) undo: UndoStack<Patch>,
/// Whether `patch` differs from `applied`. Every change goes through an
/// edit (`Draft::edit`), so the patches are compared only after one that
/// changed something, not on every frame.
unapplied: bool,
}
/// What the toolbar shows of a draft.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct DraftStatus {
pub(super) unapplied: bool,
pub(super) can_undo: bool,
pub(super) can_redo: bool,
}
impl Draft {
pub(super) fn taken_from(patch: &Patch) -> Self {
Self {
patch: patch.clone(),
applied: patch.clone(),
undo: UndoStack::default(),
unapplied: false,
}
}
/// Lets `change` edit the draft. If it changed anything — an undo back
/// to the applied patch too — the draft is compared with the applied
/// patch again.
pub(super) fn edit<R>(&mut self, change: impl FnOnce(&mut PatchEdit<'_>) -> R) -> R {
let mut edit = PatchEdit::new(&mut self.patch, &mut self.undo);
let result = change(&mut edit);
if edit.changed() {
self.unapplied = self.patch != self.applied;
}
result
}
/// Hands the draft to the show: `project` takes it.
pub(super) fn apply_to(&mut self, project: &mut Project) {
project.patch = self.patch.clone();
self.applied = self.patch.clone();
self.unapplied = false;
}
pub(super) fn unapplied(&self) -> bool {
self.unapplied
}
pub(super) fn status(&self) -> DraftStatus {
DraftStatus {
unapplied: self.unapplied,
can_undo: self.undo.can_undo(),
can_redo: self.undo.can_redo(),
}
}
}