UNDO/REDO FINALLY DONE
This commit is contained in:
parent
718a2c3f06
commit
9652ae1a08
6 changed files with 142 additions and 21 deletions
|
@ -13,7 +13,7 @@ When an item is selected in the items tree window(will be written in gold), clic
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [x] Pick default color for shapes
|
- [x] Pick default color for shapes
|
||||||
- [ ] Undo/Redo history(only missing shape creation/deletion)
|
- [x] Undo/Redo history(ctrl + Z/ctrl + shift + Z)
|
||||||
- [ ] Select item by clicking/double clicking on the relevant shape/image
|
- [ ] Select item by clicking/double clicking on the relevant shape/image
|
||||||
|
|
||||||
## Quality of life todo
|
## Quality of life todo
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub struct ImportItem {
|
||||||
pub data: ExportData,
|
pub data: ExportData,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toml_save_sys(
|
pub fn json_save_sys(
|
||||||
mut imp_exp: ResMut<ShouldImportExport>,
|
mut imp_exp: ResMut<ShouldImportExport>,
|
||||||
shapes: Query<&ShapeData>,
|
shapes: Query<&ShapeData>,
|
||||||
images: Query<(&ImageData, &Transform)>,
|
images: Query<(&ImageData, &Transform)>,
|
||||||
|
@ -107,7 +107,6 @@ pub fn import_sys(
|
||||||
mut imp_exp: ResMut<ShouldImportExport>,
|
mut imp_exp: ResMut<ShouldImportExport>,
|
||||||
assets: Res<AssetServer>,
|
assets: Res<AssetServer>,
|
||||||
p_size: Res<PointSize>,
|
p_size: Res<PointSize>,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
imp_exp.import = false;
|
imp_exp.import = false;
|
||||||
let file = rfd::FileDialog::new()
|
let file = rfd::FileDialog::new()
|
||||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -67,7 +67,7 @@ fn main() {
|
||||||
.add_system(delete_selected_item_on_del_sys.with_run_criteria(|mut ec: ResMut<EguiContext>|
|
.add_system(delete_selected_item_on_del_sys.with_run_criteria(|mut ec: ResMut<EguiContext>|
|
||||||
if ec.ctx_mut().wants_keyboard_input() { ShouldRun::No } else { ShouldRun::Yes }
|
if ec.ctx_mut().wants_keyboard_input() { ShouldRun::No } else { ShouldRun::Yes }
|
||||||
))
|
))
|
||||||
.add_system(export::toml_save_sys.with_run_criteria(|ie: Res<ShouldImportExport>| {
|
.add_system(export::json_save_sys.with_run_criteria(|ie: Res<ShouldImportExport>| {
|
||||||
if ie.export { ShouldRun::Yes } else { ShouldRun::No }
|
if ie.export { ShouldRun::Yes } else { ShouldRun::No }
|
||||||
}))
|
}))
|
||||||
.add_system(export::import_sys.with_run_criteria(|ie: Res<ShouldImportExport>| {
|
.add_system(export::import_sys.with_run_criteria(|ie: Res<ShouldImportExport>| {
|
||||||
|
@ -95,11 +95,35 @@ fn configure_visuals(mut egui_ctx: ResMut<EguiContext>) {
|
||||||
|
|
||||||
fn delete_selected_item_on_del_sys(
|
fn delete_selected_item_on_del_sys(
|
||||||
mut coms: Commands,
|
mut coms: Commands,
|
||||||
|
mut undo: ResMut<undo::UndoStack>,
|
||||||
mut selected: ResMut<SelectedItem>,
|
mut selected: ResMut<SelectedItem>,
|
||||||
keyboard: Res<Input<KeyCode>>,
|
keyboard: Res<Input<KeyCode>>,
|
||||||
|
mut shapes: Query<&mut ShapeData>,
|
||||||
|
mut images: Query<&mut ImageData>,
|
||||||
|
transforms: Query<&Transform>,
|
||||||
|
image_handles: Query<&Handle<Image>>,
|
||||||
|
draw_modes: Query<&DrawMode>,
|
||||||
) {
|
) {
|
||||||
if keyboard.just_pressed(KeyCode::Delete) || keyboard.just_pressed(KeyCode::X) {
|
if keyboard.just_pressed(KeyCode::Delete) || keyboard.just_pressed(KeyCode::X) {
|
||||||
if let Some(e) = selected.0 {
|
if let Some(e) = selected.0 {
|
||||||
|
if let Ok(mut sd) = shapes.get_mut(e) {
|
||||||
|
let mut name = String::new();
|
||||||
|
let mut note = String::new();
|
||||||
|
std::mem::swap(&mut name, &mut sd.name);
|
||||||
|
std::mem::swap(&mut note, &mut sd.note);
|
||||||
|
let edges = sd.edges.iter().map(|e| transforms.get(*e).unwrap().translation.xy()).collect::<Vec<Vec2>>();
|
||||||
|
let transform = transforms.get(e).map(|t| *t).unwrap_or_default();
|
||||||
|
let dm = *draw_modes.get(e).unwrap();
|
||||||
|
undo.push(UndoItem::DeleteShape { transform, edges, name, note, shape: sd.shape, dm, old_entity: e });
|
||||||
|
}
|
||||||
|
else if let Ok(mut id) = images.get_mut(e) {
|
||||||
|
let mut nid = ImageData::default();
|
||||||
|
std::mem::swap(&mut nid, &mut *id);
|
||||||
|
let transform = transforms.get(e).map(|t| *t).unwrap_or_default();
|
||||||
|
let handle = image_handles.get(e).unwrap().clone();
|
||||||
|
undo.push(UndoItem::DeleteImage { id: nid, transform: transform, handle, old_entity: e });
|
||||||
|
}
|
||||||
|
|
||||||
coms.entity(e).despawn_recursive();
|
coms.entity(e).despawn_recursive();
|
||||||
selected.0 = None;
|
selected.0 = None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub fn modify_sys(
|
||||||
else {
|
else {
|
||||||
undo.push(UndoItem::MoveDot {
|
undo.push(UndoItem::MoveDot {
|
||||||
shape_data,
|
shape_data,
|
||||||
dot,
|
dot: sd.edges.iter().position(|e| *e == dot).unwrap_or(0),
|
||||||
from: *held_from,
|
from: *held_from,
|
||||||
to: snap.snap_to_grid(mouse_pos),
|
to: snap.snap_to_grid(mouse_pos),
|
||||||
});
|
});
|
||||||
|
|
14
src/ui.rs
14
src/ui.rs
|
@ -340,10 +340,10 @@ pub fn inspector_sys(
|
||||||
changes.0 = gt_y;
|
changes.0 = gt_y;
|
||||||
}
|
}
|
||||||
if stopped_edit(&rx) {
|
if stopped_edit(&rx) {
|
||||||
undo.push(UndoItem::MoveDot { shape_data: e, dot: *edge, from: Vec2::new(changes.0, gt_y), to: Vec2::new(gt_x, gt_y) });
|
undo.push(UndoItem::MoveDot { shape_data: e, dot: i, from: Vec2::new(changes.0, gt_y), to: Vec2::new(gt_x, gt_y) });
|
||||||
}
|
}
|
||||||
if stopped_edit(&ry) {
|
if stopped_edit(&ry) {
|
||||||
undo.push(UndoItem::MoveDot { shape_data: e, dot: *edge, from: Vec2::new(gt_x, changes.0), to: Vec2::new(gt_x, gt_y) });
|
undo.push(UndoItem::MoveDot { shape_data: e, dot: i, from: Vec2::new(gt_x, changes.0), to: Vec2::new(gt_x, gt_y) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if rx.changed() || ry.changed() {
|
if rx.changed() || ry.changed() {
|
||||||
|
@ -408,10 +408,14 @@ pub fn inspector_sys(
|
||||||
ui.separator();
|
ui.separator();
|
||||||
if ui.button("Delete").clicked() {
|
if ui.button("Delete").clicked() {
|
||||||
if let Ok(mut sd) = shape {
|
if let Ok(mut sd) = shape {
|
||||||
let mut nsd = sd.shallow_copy();
|
let mut name = String::new();
|
||||||
std::mem::swap(&mut nsd, &mut *sd);
|
let mut note = String::new();
|
||||||
|
std::mem::swap(&mut name, &mut sd.name);
|
||||||
|
std::mem::swap(&mut note, &mut sd.note);
|
||||||
|
let edges = sd.edges.iter().map(|e| transforms.get(*e).unwrap().translation.xy()).collect::<Vec<Vec2>>();
|
||||||
let transform = transforms.get(e).map(|t| *t).unwrap_or_default();
|
let transform = transforms.get(e).map(|t| *t).unwrap_or_default();
|
||||||
undo.push(UndoItem::DeleteShape { sd: nsd, transform });
|
let dm = *draw_modes.get(e).unwrap();
|
||||||
|
undo.push(UndoItem::DeleteShape { transform, edges, name, note, shape: sd.shape, dm, old_entity: e });
|
||||||
}
|
}
|
||||||
else if let Ok(mut id) = image {
|
else if let Ok(mut id) = image {
|
||||||
let mut nid = ImageData::default();
|
let mut nid = ImageData::default();
|
||||||
|
|
116
src/undo.rs
116
src/undo.rs
|
@ -1,15 +1,16 @@
|
||||||
use bevy::math::Vec3Swizzles;
|
use bevy::math::Vec3Swizzles;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy_prototype_lyon::prelude::*;
|
||||||
|
|
||||||
use crate::{ShapeData, ImageData};
|
use crate::{ShapeData, ImageData, CreateShape, PointSize};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum UndoItem {
|
pub enum UndoItem {
|
||||||
Base,
|
Base,
|
||||||
CreateShape { entity: Entity },
|
CreateShape { entity: Entity },
|
||||||
DeleteShape { sd: ShapeData, transform: Transform },
|
DeleteShape { transform: Transform, edges: Vec<Vec2>, name: String, note: String, shape: CreateShape, dm: DrawMode, old_entity: Entity },
|
||||||
MoveItem { shape: Entity, from: Vec2, to: Vec2 },
|
MoveItem { shape: Entity, from: Vec2, to: Vec2 },
|
||||||
MoveDot { shape_data: Entity, dot: Entity, from: Vec2, to: Vec2 },
|
MoveDot { shape_data: Entity, dot: usize, from: Vec2, to: Vec2 },
|
||||||
Rotate { entity: Entity, from: f32, to: f32 },
|
Rotate { entity: Entity, from: f32, to: f32 },
|
||||||
NameChange { entity: Entity, from: String },
|
NameChange { entity: Entity, from: String },
|
||||||
NoteChange { entity: Entity, from: String },
|
NoteChange { entity: Entity, from: String },
|
||||||
|
@ -74,25 +75,90 @@ pub fn undo_redo_sys(
|
||||||
mut coms: Commands,
|
mut coms: Commands,
|
||||||
mut stack: ResMut<UndoStack>,
|
mut stack: ResMut<UndoStack>,
|
||||||
keyboard: Res<Input<KeyCode>>,
|
keyboard: Res<Input<KeyCode>>,
|
||||||
|
p_size: Res<PointSize>,
|
||||||
mut shapes: Query<&mut ShapeData>,
|
mut shapes: Query<&mut ShapeData>,
|
||||||
mut transforms: Query<&mut Transform>,
|
mut transforms: Query<&mut Transform>,
|
||||||
global_transforms: Query<&GlobalTransform>,
|
global_transforms: Query<&GlobalTransform>,
|
||||||
mut images: Query<(&mut ImageData, &Handle<Image>)>,
|
mut images: Query<(&mut ImageData, &Handle<Image>)>,
|
||||||
mut paths: Query<&mut bevy_prototype_lyon::prelude::Path>,
|
mut paths: Query<&mut bevy_prototype_lyon::prelude::Path>,
|
||||||
|
draw_modes: Query<&DrawMode>,
|
||||||
) {
|
) {
|
||||||
if keyboard.just_pressed(KeyCode::Z) && keyboard.pressed(KeyCode::LControl) {
|
if keyboard.just_pressed(KeyCode::Z) && keyboard.pressed(KeyCode::LControl) {
|
||||||
let item = if keyboard.pressed(KeyCode::LShift) { stack.unpop() } else { stack.pop() };
|
let item = if keyboard.pressed(KeyCode::LShift) { stack.unpop() } else { stack.pop() };
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
match item {
|
match item {
|
||||||
UndoItem::Base => bevy::log::error!("POP/UNPOP: Got UndoItem::Base as a result!"),
|
UndoItem::Base => bevy::log::error!("POP/UNPOP: Got UndoItem::Base as a result!"),
|
||||||
UndoItem::CreateShape { entity: _ } => {
|
UndoItem::CreateShape { entity } => {
|
||||||
bevy::log::error!("NOT IMPLEMENTED : CreateShape");
|
let transform = transforms.get(*entity).map(|t| *t).unwrap_or_default();
|
||||||
|
let mut sd = shapes.get_mut(*entity).unwrap();
|
||||||
|
let mut name = String::new();
|
||||||
|
let mut note = String::new();
|
||||||
|
std::mem::swap(&mut name, &mut sd.name);
|
||||||
|
std::mem::swap(&mut note, &mut sd.note);
|
||||||
|
let edges = sd.edges.iter().map(|e| transforms.get(*e).unwrap().translation.xy()).collect::<Vec<Vec2>>();
|
||||||
|
let dm = *draw_modes.get(*entity).unwrap();
|
||||||
|
let shape = sd.shape;
|
||||||
|
|
||||||
|
coms.entity(*entity).despawn_recursive();
|
||||||
|
*item = UndoItem::DeleteShape { transform, edges, name, note, shape, dm, old_entity: *entity };
|
||||||
|
|
||||||
},
|
},
|
||||||
UndoItem::DeleteShape {
|
UndoItem::DeleteShape {
|
||||||
sd: _,
|
transform,
|
||||||
transform: _,
|
edges,
|
||||||
|
name,
|
||||||
|
note,
|
||||||
|
shape,
|
||||||
|
dm,
|
||||||
|
old_entity,
|
||||||
} => {
|
} => {
|
||||||
bevy::log::error!("NOT IMPLEMENTED : DeleteShape");
|
let main_shape = spawn_main_shape(&mut coms, *dm, *transform, *shape, edges.clone());
|
||||||
|
|
||||||
|
let dots_dm = DrawMode::Fill(FillMode::color(Color::RED));
|
||||||
|
let dot_shape = shapes::Circle { radius: p_size.0, center: Vec2::ZERO };
|
||||||
|
let center = coms.spawn_bundle(GeometryBuilder::build_as(
|
||||||
|
&dot_shape,
|
||||||
|
dots_dm,
|
||||||
|
Transform::from_xyz(0.0, 0.0, 0.5)
|
||||||
|
)).id();
|
||||||
|
let edges = edges.iter().map(|v| {
|
||||||
|
coms.spawn_bundle(GeometryBuilder::build_as(
|
||||||
|
&dot_shape,
|
||||||
|
dots_dm,
|
||||||
|
Transform::from_translation(v.extend(0.5))
|
||||||
|
)).id()
|
||||||
|
}).collect::<Vec<Entity>>();
|
||||||
|
|
||||||
|
let mut nname = String::new();
|
||||||
|
let mut nnote = String::new();
|
||||||
|
std::mem::swap(&mut nname, name);
|
||||||
|
std::mem::swap(&mut nnote, note);
|
||||||
|
|
||||||
|
let mut main_shape = coms.entity(main_shape);
|
||||||
|
|
||||||
|
edges.iter().for_each(|e| { main_shape.add_child(*e); });
|
||||||
|
let sd = ShapeData { name: nname, shape: *shape, main_shape: main_shape.id(), edges, center: Some(center), note: nnote };
|
||||||
|
main_shape
|
||||||
|
.insert(sd)
|
||||||
|
.add_child(center);
|
||||||
|
|
||||||
|
let oe = *old_entity;
|
||||||
|
*item = UndoItem::CreateShape { entity: main_shape.id() };
|
||||||
|
|
||||||
|
let f = |ne: &mut Entity| { if *ne == oe { *ne = main_shape.id(); }};
|
||||||
|
for i in stack.items.iter_mut() {
|
||||||
|
match i {
|
||||||
|
UndoItem::CreateShape { entity } => f(entity),
|
||||||
|
UndoItem::MoveDot { shape_data, ..} => f(shape_data),
|
||||||
|
UndoItem::MoveItem { shape, .. } => f(shape),
|
||||||
|
UndoItem::Rotate { entity, .. } => f(entity),
|
||||||
|
UndoItem::NameChange { entity, .. } => f(entity),
|
||||||
|
UndoItem::NoteChange { entity, .. } => f(entity),
|
||||||
|
UndoItem::ChangeZ { entity, .. } => f(entity),
|
||||||
|
UndoItem::ReScale { entity, .. } => f(entity),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
UndoItem::MoveItem { shape, from, to } => {
|
UndoItem::MoveItem { shape, from, to } => {
|
||||||
if let Ok(mut t) = transforms.get_mut(*shape) {
|
if let Ok(mut t) = transforms.get_mut(*shape) {
|
||||||
|
@ -103,13 +169,14 @@ pub fn undo_redo_sys(
|
||||||
*to = t;
|
*to = t;
|
||||||
},
|
},
|
||||||
UndoItem::MoveDot { shape_data, dot, from, to } => {
|
UndoItem::MoveDot { shape_data, dot, from, to } => {
|
||||||
let gt = global_transforms.get(*dot).unwrap();
|
|
||||||
|
|
||||||
let sd = shapes.get(*shape_data).unwrap();
|
let sd = shapes.get(*shape_data).unwrap();
|
||||||
|
let dot = sd.edges[*dot];
|
||||||
|
let gt = global_transforms.get(dot).unwrap();
|
||||||
|
|
||||||
let rot = gt.to_scale_rotation_translation().1;
|
let rot = gt.to_scale_rotation_translation().1;
|
||||||
let ang = rot.to_euler(EulerRot::XYZ).2;
|
let ang = rot.to_euler(EulerRot::XYZ).2;
|
||||||
let delta = Mat2::from_angle(-ang) * (*from - gt.translation().xy());
|
let delta = Mat2::from_angle(-ang) * (*from - gt.translation().xy());
|
||||||
if let Ok(mut t) = transforms.get_mut(*dot) {
|
if let Ok(mut t) = transforms.get_mut(dot) {
|
||||||
t.translation += delta.extend(0.0);
|
t.translation += delta.extend(0.0);
|
||||||
}
|
}
|
||||||
// We need to recalculate the center, and update the points to be the new relevant point from the center
|
// We need to recalculate the center, and update the points to be the new relevant point from the center
|
||||||
|
@ -218,3 +285,30 @@ pub fn undo_redo_sys(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_main_shape(coms: &mut Commands, dm: DrawMode, trans: Transform, shape: CreateShape, edges: Vec<Vec2>) -> Entity {
|
||||||
|
match shape {
|
||||||
|
CreateShape::Triangle => {
|
||||||
|
coms.spawn_bundle(GeometryBuilder::build_as(
|
||||||
|
&shapes::Polygon { points: edges, closed: true },
|
||||||
|
dm,
|
||||||
|
trans
|
||||||
|
)).id()
|
||||||
|
},
|
||||||
|
CreateShape::Square => {
|
||||||
|
coms.spawn_bundle(GeometryBuilder::build_as(
|
||||||
|
&shapes::Rectangle { extents: (edges[0] - edges[1]).abs(), ..Default::default() },
|
||||||
|
dm,
|
||||||
|
trans
|
||||||
|
)).id()
|
||||||
|
},
|
||||||
|
CreateShape::Circle => {
|
||||||
|
coms.spawn_bundle(GeometryBuilder::build_as(
|
||||||
|
&shapes::Circle { radius: edges[0].length(), center: Vec2::ZERO },
|
||||||
|
dm,
|
||||||
|
trans
|
||||||
|
)).id()
|
||||||
|
},
|
||||||
|
CreateShape::Capsule => unimplemented!("I should prob get rid of this..."),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue