egui: add redo support to Undoer (#3478)
* Closes #3447 * Closes #3448 Better implementation than #3448. (by accident since I did not see that PR)
This commit is contained in:
parent
c0b14f4d4e
commit
5201c04512
|
|
@ -57,15 +57,22 @@ pub struct Undoer<State> {
|
|||
/// The latest undo point may (often) be the current state.
|
||||
undos: VecDeque<State>,
|
||||
|
||||
/// Stores redos immediately after a sequence of undos.
|
||||
/// Gets cleared every time the state changes.
|
||||
/// Does not need to be a deque, because there can only be up to undos.len() redos,
|
||||
/// which is already limited to settings.max_undos.
|
||||
redos: Vec<State>,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
flux: Option<Flux<State>>,
|
||||
}
|
||||
|
||||
impl<State> std::fmt::Debug for Undoer<State> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { undos, .. } = self;
|
||||
let Self { undos, redos, .. } = self;
|
||||
f.debug_struct("Undoer")
|
||||
.field("undo count", &undos.len())
|
||||
.field("redo count", &redos.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
@ -91,6 +98,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn has_redo(&self, current_state: &State) -> bool {
|
||||
!self.redos.is_empty() && self.undos.back() == Some(current_state)
|
||||
}
|
||||
|
||||
/// Return true if the state is currently changing
|
||||
pub fn is_in_flux(&self) -> bool {
|
||||
self.flux.is_some()
|
||||
|
|
@ -101,7 +112,9 @@ where
|
|||
self.flux = None;
|
||||
|
||||
if self.undos.back() == Some(current_state) {
|
||||
self.undos.pop_back();
|
||||
self.redos.push(self.undos.pop_back().unwrap());
|
||||
} else {
|
||||
self.redos.push(current_state.clone());
|
||||
}
|
||||
|
||||
// Note: we keep the undo point intact.
|
||||
|
|
@ -111,9 +124,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn redo(&mut self, current_state: &State) -> Option<&State> {
|
||||
if !self.undos.is_empty() && self.undos.back() != Some(current_state) {
|
||||
// state changed since the last undo, redos should be cleared.
|
||||
self.redos.clear();
|
||||
None
|
||||
} else if let Some(state) = self.redos.pop() {
|
||||
self.undos.push_back(state);
|
||||
self.undos.back()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an undo point if, and only if, there has been a change since the latest undo point.
|
||||
///
|
||||
/// * `time`: current time in seconds.
|
||||
pub fn add_undo(&mut self, current_state: &State) {
|
||||
if self.undos.back() != Some(current_state) {
|
||||
self.undos.push_back(current_state.clone());
|
||||
|
|
@ -139,6 +163,8 @@ where
|
|||
if latest_undo == current_state {
|
||||
self.flux = None;
|
||||
} else {
|
||||
self.redos.clear();
|
||||
|
||||
match self.flux.as_mut() {
|
||||
None => {
|
||||
self.flux = Some(Flux {
|
||||
|
|
|
|||
|
|
@ -986,8 +986,7 @@ fn events(
|
|||
pressed: true,
|
||||
modifiers,
|
||||
..
|
||||
} if modifiers.command && !modifiers.shift => {
|
||||
// TODO(emilk): redo
|
||||
} if modifiers.matches(Modifiers::COMMAND) => {
|
||||
if let Some((undo_ccursor_range, undo_txt)) = state
|
||||
.undoer
|
||||
.lock()
|
||||
|
|
@ -999,6 +998,25 @@ fn events(
|
|||
None
|
||||
}
|
||||
}
|
||||
Event::Key {
|
||||
key,
|
||||
pressed: true,
|
||||
modifiers,
|
||||
..
|
||||
} if (modifiers.matches(Modifiers::COMMAND) && *key == Key::Y)
|
||||
|| (modifiers.matches(Modifiers::SHIFT | Modifiers::COMMAND) && *key == Key::Z) =>
|
||||
{
|
||||
if let Some((redo_ccursor_range, redo_txt)) = state
|
||||
.undoer
|
||||
.lock()
|
||||
.redo(&(cursor_range.as_ccursor_range(), text.as_str().to_owned()))
|
||||
{
|
||||
text.replace(redo_txt);
|
||||
Some(*redo_ccursor_range)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Event::Key {
|
||||
key,
|
||||
|
|
|
|||
Loading…
Reference in New Issue