https://github.com/epilys/gerb undo system WIP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mod imp { | |
use super::*; | |
#[derive(Debug)] | |
pub struct Event { | |
pub timestamp: u64, | |
pub action: super::Action, | |
} | |
#[derive(Debug, Default)] | |
pub struct UndoDatabase { | |
pub database: RefCell<Vec<Event>>, | |
pub timestamp: RefCell<u64>, | |
pub cursor: RefCell<usize>, | |
} | |
#[glib::object_subclass] | |
impl ObjectSubclass for UndoDatabase { | |
const NAME: &'static str = "UndoDatabase"; | |
type Type = super::UndoDatabase; | |
type ParentType = glib::Object; | |
type Interfaces = (); | |
} | |
impl ObjectImpl for UndoDatabase {} | |
} | |
impl Default for UndoDatabase { | |
fn default() -> Self { | |
Self::new() | |
} | |
} | |
pub struct EventStamp { | |
pub t: std::any::TypeId, | |
//FIXME: add also a weak object ref here | |
pub property: &'static str, | |
pub id: Box<[u8]>, | |
} | |
impl PartialEq for EventStamp { | |
fn eq(&self, other: &Self) -> bool { | |
self.t == other.t && self.property == other.property && self.id == other.id | |
} | |
} | |
impl Eq for EventStamp {} | |
impl UndoDatabase { | |
pub fn new() -> Self { | |
let ret: Self = glib::Object::new::<Self>(&[]).unwrap(); | |
ret | |
} | |
pub fn event(&self, action: Action) { | |
let mut cursor = self.imp().cursor.borrow_mut(); | |
let mut db = self.imp().database.borrow_mut(); | |
let mut timestamp = self.imp().timestamp.borrow_mut(); | |
*timestamp += 1; | |
let timestamp = *timestamp - 1; | |
db.drain(*cursor..); | |
db.push(Event { timestamp, action }); | |
*cursor = db.len(); | |
} | |
pub fn undo(&self) { | |
let mut cursor = self.imp().cursor.borrow_mut(); | |
let mut db = self.imp().database.borrow_mut(); | |
loop { | |
if let Some(last) = db[..*cursor].last_mut() { | |
std::dbg!(&last); | |
(last.action.undo)(); | |
} else { | |
break; | |
} | |
if *cursor == 0 { | |
break; | |
} | |
*cursor -= 1; | |
match std::dbg!((db[..*cursor].last(), db[..*cursor + 1].last())) { | |
(Some(prev), Some(cur)) | |
if std::dbg!( | |
prev.action.stamp == cur.action.stamp | |
&& prev.action.compress | |
&& cur.action.compress | |
) => {} | |
_ => break, | |
} | |
} | |
} | |
pub fn redo(&self) { | |
let mut cursor = self.imp().cursor.borrow_mut(); | |
let mut db = self.imp().database.borrow_mut(); | |
loop { | |
if let Some(last) = db.get_mut(*cursor) { | |
std::dbg!(&last); | |
(last.action.redo)(); | |
} else { | |
break; | |
} | |
*cursor += 1; | |
if *cursor >= db.len() { | |
*cursor = std::cmp::min(db.len(), *cursor); | |
break; | |
} | |
match std::dbg!((db.get(*cursor - 1), db.get(*cursor))) { | |
(Some(prev), Some(cur)) | |
if std::dbg!( | |
prev.action.stamp == cur.action.stamp | |
&& prev.action.compress | |
&& cur.action.compress | |
) => {} | |
_ => break, | |
} | |
} | |
} | |
} | |
pub struct Action { | |
pub stamp: EventStamp, | |
pub compress: bool, | |
pub redo: Box<dyn FnMut()>, | |
pub undo: Box<dyn FnMut()>, | |
} | |
impl std::fmt::Debug for Action { | |
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { | |
fmt.debug_struct("Action") | |
.field("compress", &self.compress) | |
.field( | |
"stamp", | |
&format!( | |
"{:?} {} {:?}", | |
self.stamp.t, self.stamp.property, self.stamp.id | |
), | |
) | |
.finish() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment