gled-ui 2.33.0

gled's egui interface, shared by the desktop app and the browser
Documentation
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(),
        }
    }
}