add undo/redo for single tile placement/erasing
This commit is contained in:
parent
6d8bfa03b0
commit
5b6113780a
2 changed files with 144 additions and 22 deletions
130
src/editor.rs
130
src/editor.rs
|
@ -12,7 +12,7 @@ use crate::{
|
|||
draw_scaled_texture, draw_usize, get_free_id, get_scroll,
|
||||
level::Level,
|
||||
marble_engine::{
|
||||
board::Board,
|
||||
board::{Board, ResizeDeltas},
|
||||
pos::{Pos, PosInt},
|
||||
tile::{Claim, Comparison, Direction, MathOp, MirrorType, OpenTile, PTile, Tile, WireType},
|
||||
Machine,
|
||||
|
@ -70,6 +70,14 @@ pub struct Editor {
|
|||
start_time: Instant,
|
||||
pasting_board: Option<Board>,
|
||||
draw_overlay: bool,
|
||||
undo_history: Vec<Action>,
|
||||
undo_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Action {
|
||||
SetTile(ResizeDeltas, Pos, Tile, Tile),
|
||||
SetArea(ResizeDeltas, Pos, Board, Board),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -150,9 +158,66 @@ impl Editor {
|
|||
start_time: Instant::now(),
|
||||
pasting_board: None,
|
||||
draw_overlay: true,
|
||||
undo_history: Vec::new(),
|
||||
undo_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn redo(&mut self) {
|
||||
if self.undo_index >= self.undo_history.len() {
|
||||
return;
|
||||
}
|
||||
let action = self.undo_history[self.undo_index].clone();
|
||||
self.do_action(action);
|
||||
self.undo_index += 1;
|
||||
}
|
||||
|
||||
fn push_action(&mut self, action: Action) {
|
||||
self.do_action(action.clone());
|
||||
self.undo_history.truncate(self.undo_index);
|
||||
self.undo_history.push(action);
|
||||
self.undo_index += 1;
|
||||
}
|
||||
|
||||
fn do_action(&mut self, action: Action) {
|
||||
match action {
|
||||
Action::SetTile(resize_delta, pos, _old, new) => {
|
||||
self.resize_board(resize_delta);
|
||||
self.source_board.set(pos, new);
|
||||
}
|
||||
Action::SetArea(_, _, _, _) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn undo(&mut self) {
|
||||
if self.undo_index == 0 {
|
||||
return;
|
||||
}
|
||||
self.undo_index -= 1;
|
||||
let action = self.undo_history[self.undo_index].clone();
|
||||
match action {
|
||||
Action::SetTile(resize_delta, pos, old, _new) => {
|
||||
self.source_board.set(pos, old);
|
||||
self.undo_resize_board(resize_delta);
|
||||
}
|
||||
Action::SetArea(_, _, _, _) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn resize_board(&mut self, deltas: ResizeDeltas) {
|
||||
self.source_board.grow(&deltas);
|
||||
let tile_size = TILE_TEXTURE_SIZE * self.zoom;
|
||||
self.view_offset.x -= deltas.x_neg as f32 * tile_size;
|
||||
self.view_offset.y -= deltas.y_neg as f32 * tile_size;
|
||||
}
|
||||
|
||||
fn undo_resize_board(&mut self, deltas: ResizeDeltas) {
|
||||
self.source_board.shrink(&deltas);
|
||||
let tile_size = TILE_TEXTURE_SIZE * self.zoom;
|
||||
self.view_offset.x += deltas.x_neg as f32 * tile_size;
|
||||
self.view_offset.y += deltas.y_neg as f32 * tile_size;
|
||||
}
|
||||
|
||||
pub fn get_exit_state(&self) -> ExitState {
|
||||
self.exit_state
|
||||
}
|
||||
|
@ -318,22 +383,39 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_tile(&mut self, mut pos: Pos, tile: Tile) {
|
||||
let tile_size = TILE_TEXTURE_SIZE * self.zoom;
|
||||
pos.x -= BOARD_MARGIN;
|
||||
pos.y -= BOARD_MARGIN;
|
||||
self.grow_board_and_update_view(&mut pos);
|
||||
pos.x += BOARD_MARGIN * 2;
|
||||
pos.y += BOARD_MARGIN * 2;
|
||||
self.grow_board_and_update_view(&mut pos);
|
||||
pos.x -= BOARD_MARGIN;
|
||||
pos.y -= BOARD_MARGIN;
|
||||
self.source_board.set(pos, tile);
|
||||
if tile.is_blank() {
|
||||
let (x, y) = self.source_board.trim_size(BOARD_MARGIN as usize);
|
||||
self.view_offset.x += x as f32 * tile_size;
|
||||
self.view_offset.y += y as f32 * tile_size;
|
||||
fn set_tile(&mut self, pos: Pos, tile: Tile) {
|
||||
let old_tile = self.source_board.get_or_blank(pos);
|
||||
if old_tile == tile {
|
||||
return;
|
||||
}
|
||||
let width = self.source_board.width() as PosInt;
|
||||
let height = self.source_board.height() as PosInt;
|
||||
let resize = ResizeDeltas {
|
||||
x_pos: if (pos.x + BOARD_MARGIN + 1) > width {
|
||||
pos.x + BOARD_MARGIN + 1 - width
|
||||
} else {
|
||||
0
|
||||
} as usize,
|
||||
x_neg: if pos.x < BOARD_MARGIN {
|
||||
BOARD_MARGIN - pos.x
|
||||
} else {
|
||||
0
|
||||
} as usize,
|
||||
y_pos: if (pos.y + BOARD_MARGIN + 1) > height {
|
||||
pos.y + BOARD_MARGIN + 1 - height
|
||||
} else {
|
||||
0
|
||||
} as usize,
|
||||
y_neg: if pos.y < BOARD_MARGIN {
|
||||
BOARD_MARGIN - pos.y
|
||||
} else {
|
||||
0
|
||||
} as usize,
|
||||
};
|
||||
let mut pos = pos;
|
||||
pos.x += resize.x_neg as PosInt;
|
||||
pos.y += resize.y_neg as PosInt;
|
||||
self.push_action(Action::SetTile(resize, pos, old_tile, tile));
|
||||
}
|
||||
|
||||
pub fn update(&mut self, rl: &RaylibHandle) {
|
||||
|
@ -417,12 +499,16 @@ impl Editor {
|
|||
}
|
||||
|
||||
if self.sim_state == SimState::Editing {
|
||||
if rl.is_key_down(KeyboardKey::KEY_LEFT_CONTROL)
|
||||
&& rl.is_key_pressed(KeyboardKey::KEY_V)
|
||||
{
|
||||
if let Ok(text) = rl.get_clipboard_text() {
|
||||
let b = Board::parse(&text);
|
||||
self.pasting_board = Some(b);
|
||||
if rl.is_key_down(KeyboardKey::KEY_LEFT_CONTROL) {
|
||||
if rl.is_key_pressed(KeyboardKey::KEY_V) {
|
||||
if let Ok(text) = rl.get_clipboard_text() {
|
||||
let b = Board::parse(&text);
|
||||
self.pasting_board = Some(b);
|
||||
}
|
||||
} else if rl.is_key_pressed(KeyboardKey::KEY_Z) {
|
||||
self.undo()
|
||||
} else if rl.is_key_pressed(KeyboardKey::KEY_Y) {
|
||||
self.redo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,14 @@ pub struct Board {
|
|||
height: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ResizeDeltas {
|
||||
pub x_pos: usize,
|
||||
pub x_neg: usize,
|
||||
pub y_pos: usize,
|
||||
pub y_neg: usize,
|
||||
}
|
||||
|
||||
impl Board {
|
||||
pub fn parse(source: &str) -> Self {
|
||||
let mut rows = Vec::new();
|
||||
|
@ -226,6 +234,34 @@ impl Board {
|
|||
(offset_x, offset_y)
|
||||
}
|
||||
|
||||
pub fn grow(&mut self, deltas: &ResizeDeltas) {
|
||||
// dbg!(&deltas);
|
||||
let new_width = self.width + deltas.x_neg + deltas.x_pos;
|
||||
let new_height = self.height + deltas.y_neg + deltas.y_pos;
|
||||
let mut new_board = Board::new_empty(new_width, new_height);
|
||||
for x in 0..self.width {
|
||||
for y in 0..self.height {
|
||||
let tile = self.rows[y][x];
|
||||
new_board.rows[y + deltas.y_neg][x + deltas.x_neg] = tile;
|
||||
}
|
||||
}
|
||||
*self = new_board;
|
||||
}
|
||||
|
||||
pub fn shrink(&mut self, deltas: &ResizeDeltas) {
|
||||
// dbg!(&deltas);
|
||||
let new_width = self.width - deltas.x_neg - deltas.x_pos;
|
||||
let new_height = self.height - deltas.y_neg - deltas.y_pos;
|
||||
let mut new_board = Board::new_empty(new_width, new_height);
|
||||
for x in 0..new_width {
|
||||
for y in 0..new_height {
|
||||
let tile = self.rows[y + deltas.y_neg][x + deltas.x_neg];
|
||||
new_board.rows[y][x] = tile;
|
||||
}
|
||||
}
|
||||
*self = new_board;
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue