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.
|
/// The latest undo point may (often) be the current state.
|
||||||
undos: VecDeque<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))]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
flux: Option<Flux<State>>,
|
flux: Option<Flux<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State> std::fmt::Debug for Undoer<State> {
|
impl<State> std::fmt::Debug for Undoer<State> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let Self { undos, .. } = self;
|
let Self { undos, redos, .. } = self;
|
||||||
f.debug_struct("Undoer")
|
f.debug_struct("Undoer")
|
||||||
.field("undo count", &undos.len())
|
.field("undo count", &undos.len())
|
||||||
|
.field("redo count", &redos.len())
|
||||||
.finish()
|
.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
|
/// Return true if the state is currently changing
|
||||||
pub fn is_in_flux(&self) -> bool {
|
pub fn is_in_flux(&self) -> bool {
|
||||||
self.flux.is_some()
|
self.flux.is_some()
|
||||||
|
|
@ -101,7 +112,9 @@ where
|
||||||
self.flux = None;
|
self.flux = None;
|
||||||
|
|
||||||
if self.undos.back() == Some(current_state) {
|
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.
|
// 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.
|
/// 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) {
|
pub fn add_undo(&mut self, current_state: &State) {
|
||||||
if self.undos.back() != Some(current_state) {
|
if self.undos.back() != Some(current_state) {
|
||||||
self.undos.push_back(current_state.clone());
|
self.undos.push_back(current_state.clone());
|
||||||
|
|
@ -139,6 +163,8 @@ where
|
||||||
if latest_undo == current_state {
|
if latest_undo == current_state {
|
||||||
self.flux = None;
|
self.flux = None;
|
||||||
} else {
|
} else {
|
||||||
|
self.redos.clear();
|
||||||
|
|
||||||
match self.flux.as_mut() {
|
match self.flux.as_mut() {
|
||||||
None => {
|
None => {
|
||||||
self.flux = Some(Flux {
|
self.flux = Some(Flux {
|
||||||
|
|
|
||||||
|
|
@ -986,8 +986,7 @@ fn events(
|
||||||
pressed: true,
|
pressed: true,
|
||||||
modifiers,
|
modifiers,
|
||||||
..
|
..
|
||||||
} if modifiers.command && !modifiers.shift => {
|
} if modifiers.matches(Modifiers::COMMAND) => {
|
||||||
// TODO(emilk): redo
|
|
||||||
if let Some((undo_ccursor_range, undo_txt)) = state
|
if let Some((undo_ccursor_range, undo_txt)) = state
|
||||||
.undoer
|
.undoer
|
||||||
.lock()
|
.lock()
|
||||||
|
|
@ -999,6 +998,25 @@ fn events(
|
||||||
None
|
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 {
|
Event::Key {
|
||||||
key,
|
key,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue