diff --git a/src/lib.rs b/src/lib.rs index 0e55a56..430cfc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,14 +48,14 @@ impl Default for SnapGrid { #[derive(Clone, Debug)] pub struct PointSize(pub f32); -#[derive(Component, Debug)] +#[derive(Component, Debug, Default, Clone)] pub struct ImageData { pub name: String, pub note: String, pub path: String, } -#[derive(Component, Debug)] +#[derive(Component, Debug, Clone)] pub struct ShapeData { pub name: String, pub shape: CreateShape, @@ -64,6 +64,18 @@ pub struct ShapeData { pub center: Option, pub note: String, } +impl ShapeData { + pub fn shallow_copy(&self) -> Self { + Self { + name: Default::default(), + shape: self.shape, + main_shape: self.main_shape, + edges: Default::default(), + center: Default::default(), + note: Default::default() + } + } +} #[derive(Component)] pub struct MainCamera; diff --git a/src/modify.rs b/src/modify.rs index 4c5c7d9..5c59c1b 100644 --- a/src/modify.rs +++ b/src/modify.rs @@ -81,7 +81,7 @@ pub fn modify_sys( Holding::Shape(shape_data, dot) => { if let Ok(sd) = shapes.get_component::(shape_data) { if sd.center == Some(dot) { - undo.push(UndoItem::MoveShape { shape: shape_data, from: *held_from, to: snap.snap_to_grid(mouse_pos) }); + undo.push(UndoItem::MoveItem { shape: shape_data, from: *held_from, to: snap.snap_to_grid(mouse_pos) }); } else { undo.push(UndoItem::MoveDot { @@ -95,7 +95,7 @@ pub fn modify_sys( }, Holding::Image(e, off) => { // ye i know it says MoVeShaPe but really it doesnt matter that much here! - undo.push(UndoItem::MoveShape { shape: e, from: *held_from, to: snap.snap_to_grid(mouse_pos - off) }); + undo.push(UndoItem::MoveItem { shape: e, from: *held_from, to: snap.snap_to_grid(mouse_pos - off) }); }, Holding::None => {}, } diff --git a/src/ui.rs b/src/ui.rs index 263abb9..eafe14b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -9,6 +9,7 @@ pub struct SelectedItem(pub Option); pub fn action_bar_sys( mut coms: Commands, + mut undo: ResMut, assets: Res, mut egui_ctx: ResMut, mut state: ResMut, @@ -43,11 +44,13 @@ pub fn action_bar_sys( let name = String::from(file.file_name().unwrap().to_str().unwrap_or("Image")); let path = file.to_str().unwrap().to_string(); let image: Handle = assets.load(file); - coms.spawn_bundle(SpriteBundle { + let id = coms.spawn_bundle(SpriteBundle { texture: image, ..Default::default() }) - .insert(ImageData { name, note: String::new(), path }); + .insert(ImageData { name, note: String::new(), path }).id(); + + undo.push(UndoItem::CreateImage { entity: id }); } } @@ -207,7 +210,7 @@ pub fn items_tree_sys( pub fn inspector_sys( mut coms: Commands, // For deletion! - mut changes: Local, + mut changes: Local<(f32, String)>, mut undo: ResMut, mut selected: ResMut, // Which item is currently selected mut egui_ctx: ResMut, @@ -218,6 +221,7 @@ pub fn inspector_sys( mut draw_modes: Query<&mut DrawMode, With>, mut visible: Query<&mut Visibility, Or<(With, With)>>, mut images: Query<&mut ImageData, With>, + image_handles: Query<&Handle, With>, ) { if let Some(e) = **selected { let mut open = true; @@ -230,53 +234,96 @@ pub fn inspector_sys( .open(&mut open) .resizable(false) .show(egui_ctx.ctx_mut(), |ui| { - if let Ok(mut sd) = shapes.get_mut(e) { + let mut shape = shapes.get_mut(e); + let mut image = images.get_mut(e); + + if shape.is_err() && image.is_err() { + bevy::log::error!("Selected item is not a shape nor an image, so... weird"); + **selected = None; + return; + } + + let name = if let Ok(ref mut sd) = shape { &mut sd.name } + else if let Ok(ref mut id) = image { &mut id.name } + else { unreachable!() }; + ui.horizontal(|hui| { + hui.label("Name:"); + let te = hui.text_edit_singleline(name); + if te.gained_focus() { + changes.1 = name.clone(); + } + if te.lost_focus() { + let mut new = String::new(); + std::mem::swap(&mut new, &mut changes.1); + undo.push(UndoItem::NameChange { entity: e, from: new }); + } + }); + ui.separator(); + if let Ok(mut t) = transforms.get_mut(e) { ui.horizontal(|hui| { - hui.label("Name:"); - let te = hui.text_edit_singleline(&mut sd.name); - if te.lost_focus() { - bevy::log::info!("stopped editing the name!"); - // TODO have name editing please + hui.label("Translation:"); + let xdrag = hui.add(egui::DragValue::new(&mut t.translation.x)); + if started_edit(&xdrag) { + changes.0 = t.translation.x; + } + else if stopped_edit(&xdrag) { + undo.push(UndoItem::MoveItem { shape: e, from: Vec2::new(changes.0, t.translation.y), to: t.translation.xy() }); + } + let ydrag = hui.add(egui::DragValue::new(&mut t.translation.y)); + if started_edit(&ydrag) { + changes.0 = t.translation.y; + } + else if stopped_edit(&ydrag) { + undo.push(UndoItem::MoveItem { shape: e, from: Vec2::new(t.translation.x , changes.0), to: t.translation.xy() }); + } + + }); + ui.horizontal(|hui| { + hui.label("Rotation:"); + let mut rot = t.rotation.to_euler(EulerRot::XYZ).2.to_degrees(); + let drag = hui.add(egui::DragValue::new(&mut rot).suffix("°")); + if started_edit(&drag) { + changes.0 = rot; + } + if stopped_edit(&drag) { + undo.push(UndoItem::Rotate { entity: e, from: changes.0, to: rot }); + } + if drag.changed() { + t.rotation = Quat::from_rotation_z(rot.to_radians()); + } + hui.label("Z:"); + let z = hui.add(egui::DragValue::new(&mut t.translation.z).clamp_range(0..=i32::MAX)); + if started_edit(&z) { + changes.0 = t.translation.z; + } + if stopped_edit(&z) { + undo.push(UndoItem::ChangeZ { entity: e, from: changes.0, to: t.translation.z }); } }); - ui.separator(); - if let Ok(mut t) = transforms.get_mut(sd.main_shape) { + if image.is_ok() { ui.horizontal(|hui| { - hui.label("Translation:"); - let xdrag = hui.add(egui::DragValue::new(&mut t.translation.x)); - if started_edit(&xdrag) { - *changes = t.translation.x; + hui.label("Scale:"); + let xs = hui.add(egui::DragValue::new(&mut t.scale.x).speed(0.01)); + if started_edit(&xs) { + changes.0 = t.scale.x; } - else if stopped_edit(&xdrag) { - undo.push(UndoItem::MoveShape { shape: e, from: Vec2::new(*changes, t.translation.y), to: t.translation.xy() }); - } - let ydrag = hui.add(egui::DragValue::new(&mut t.translation.y)); - if started_edit(&ydrag) { - *changes = t.translation.y; - } - else if stopped_edit(&ydrag) { - undo.push(UndoItem::MoveShape { shape: e, from: Vec2::new(t.translation.x , *changes), to: t.translation.xy() }); + if stopped_edit(&xs) { + undo.push(UndoItem::ReScale { entity: e, from: Vec2::new(changes.0, t.scale.y), to: t.scale.xy() }); } - }); - ui.horizontal(|hui| { - hui.label("Rotation:"); - let mut rot = t.rotation.to_euler(EulerRot::XYZ).2.to_degrees(); - let drag = hui.add(egui::DragValue::new(&mut rot).suffix("°")); - if started_edit(&drag) { - *changes = rot; + let ys = hui.add(egui::DragValue::new(&mut t.scale.y).speed(0.01)); + if started_edit(&ys) { + changes.0 = t.scale.y; } - if stopped_edit(&drag) { - undo.push(UndoItem::Rotate { entity: e, from: *changes, to: rot }); + if stopped_edit(&ys) { + undo.push(UndoItem::ReScale { entity: e, from: Vec2::new(t.scale.x, changes.0), to: t.scale.xy() }); } - if drag.changed() { - t.rotation = Quat::from_rotation_z(rot.to_radians()); - } - hui.label("Z:"); - hui.add(egui::DragValue::new(&mut t.translation.z).clamp_range(0..=i32::MAX)); + }); } - ui.separator(); + } + ui.separator(); + if let Ok(ref mut sd) = shape { for (i, edge) in sd.edges.iter().enumerate() { ui.horizontal(|hui| { hui.label(format!("Edge {}", i)); @@ -311,9 +358,11 @@ pub fn inspector_sys( }); } ui.separator(); - if let Ok(mut v) = visible.get_mut(e) { - ui.checkbox(&mut v.is_visible, "Visible"); - } + } + if let Ok(mut v) = visible.get_mut(e) { + ui.checkbox(&mut v.is_visible, "Visible"); + } + if let Ok(ref mut sd) = shape { if let Ok(mut dm) = draw_modes.get_mut(e) { ui.separator(); if let DrawMode::Outlined { fill_mode: f, outline_mode: _ } = &mut *dm { @@ -327,51 +376,39 @@ pub fn inspector_sys( } } ui.separator(); - ui.label("Notes"); - ui.text_edit_multiline(&mut sd.note); - - - ui.separator(); - if ui.button("Delete").clicked() { - coms.entity(e).despawn_recursive(); - **selected = None; - } } - else if let Ok(mut name) = images.get_mut(e) { - ui.horizontal(|hui| { - hui.label("Name:"); - hui.text_edit_singleline(&mut name.name); - }); - ui.separator(); - if let Ok(mut t) = transforms.get_mut(e) { - ui.horizontal(|hui| { - hui.label("Translation:"); - hui.add(egui::DragValue::new(&mut t.translation.x)); - hui.add(egui::DragValue::new(&mut t.translation.y)); - - }); - ui.horizontal(|hui| { - hui.label("Rotation:"); - let mut rot = t.rotation.to_euler(EulerRot::XYZ).2.to_degrees(); - if hui.add(egui::DragValue::new(&mut rot).suffix("°")).changed() { - t.rotation = Quat::from_rotation_z(rot.to_radians()); - } - hui.label("Z:"); - hui.add(egui::DragValue::new(&mut t.translation.z).clamp_range(0..=i32::MAX)); - }); - ui.horizontal(|hui| { - hui.label("Scale:"); - hui.add(egui::DragValue::new(&mut t.scale.x).speed(0.01)); - hui.add(egui::DragValue::new(&mut t.scale.y).speed(0.01)); - }); - } + let note = if let Ok(ref mut sd) = shape { &mut sd.note } + else if let Ok(ref mut id) = image { &mut id.note } + else { unreachable!() }; + ui.label("Notes"); + let ne = ui.text_edit_multiline(note); + if ne.gained_focus() { + changes.1 = note.clone(); + } + if ne.lost_focus() { + let mut new = String::new(); + std::mem::swap(&mut new, &mut changes.1); + undo.push(UndoItem::NoteChange { entity: e, from: new }); + } - ui.separator(); - if ui.button("Delete").clicked() { - coms.entity(e).despawn_recursive(); - **selected = None; + ui.separator(); + if ui.button("Delete").clicked() { + if let Ok(mut sd) = shape { + let mut nsd = sd.shallow_copy(); + std::mem::swap(&mut nsd, &mut *sd); + let transform = transforms.get(e).map(|t| *t).unwrap_or_default(); + undo.push(UndoItem::DeleteShape { sd: nsd, transform }); } + else if let Ok(mut id) = image { + 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 }); + } + coms.entity(e).despawn_recursive(); + **selected = None; } }); if !open { diff --git a/src/undo.rs b/src/undo.rs index 2eac0e8..54bb791 100644 --- a/src/undo.rs +++ b/src/undo.rs @@ -1,16 +1,22 @@ use bevy::prelude::*; -use crate::{CreateShape, ShapeData}; +use crate::ui::SelectedItem; +use crate::{CreateShape, ShapeData, ImageData}; #[derive(Debug, Clone)] pub enum UndoItem { Base, CreateShape { entity: Entity }, + DeleteShape { sd: ShapeData, transform: Transform }, + MoveItem { shape: Entity, from: Vec2, to: Vec2 }, MoveDot { shape_data: Entity, dot: Entity, from: Vec2, to: Vec2 }, - MoveShape { shape: Entity, from: Vec2, to: Vec2 }, Rotate { entity: Entity, from: f32, to: f32 }, NameChange { entity: Entity, from: String }, - DeleteShape { name: String, shape: CreateShape, edges: Vec, transform: Transform, color: Color, note: String }, + NoteChange { entity: Entity, from: String }, + ChangeZ { entity: Entity, from: f32, to: f32 }, + ReScale { entity: Entity, from: Vec2, to: Vec2 }, + CreateImage { entity: Entity }, + DeleteImage { id: ImageData, transform: Transform, handle: Handle }, } pub struct UndoStack { items: Vec, @@ -67,9 +73,11 @@ impl UndoStack { pub fn undo_redo_sys( mut coms: Commands, mut stack: ResMut, + mut inspector: ResMut, keyboard: Res>, mut shapes: Query<&mut ShapeData>, mut transforms: Query<&mut Transform>, + mut images: Query<(&mut ImageData, &Handle)>, ) { if keyboard.just_pressed(KeyCode::Z) && keyboard.pressed(KeyCode::LControl) { let item = if keyboard.pressed(KeyCode::LShift) { stack.unpop() } else { stack.pop() }; @@ -79,10 +87,13 @@ pub fn undo_redo_sys( UndoItem::CreateShape { entity } => { bevy::log::error!("NOT IMPLEMENTED : CreateShape"); }, - UndoItem::MoveDot { shape_data, dot, from, to } => { - bevy::log::error!("NOT IMPLEMENTED : MoveDot"); + UndoItem::DeleteShape { + sd, + transform, + } => { + bevy::log::error!("NOT IMPLEMENTED : DeleteShape"); }, - UndoItem::MoveShape { shape, from, to } => { + UndoItem::MoveItem { shape, from, to } => { if let Ok(mut t) = transforms.get_mut(*shape) { t.translation = from.extend(t.translation.z); } @@ -90,6 +101,9 @@ pub fn undo_redo_sys( *from = *to; *to = t; }, + UndoItem::MoveDot { shape_data, dot, from, to } => { + bevy::log::error!("NOT IMPLEMENTED : MoveDot"); + }, UndoItem::Rotate { entity, from, to } => { if let Ok(mut t) = transforms.get_mut(*entity) { t.rotation = Quat::from_rotation_z(from.to_radians()); @@ -102,17 +116,57 @@ pub fn undo_redo_sys( if let Ok(mut sd) = shapes.get_mut(*entity) { std::mem::swap(&mut sd.name, from); } + else if let Ok((mut id, _)) = images.get_mut(*entity) { + std::mem::swap(&mut id.name, from); + } // No need to update the item, so no need to replace }, - UndoItem::DeleteShape { - name, - shape, - edges, - transform, - color, - note - } => { - bevy::log::error!("NOT IMPLEMENTED : DeleteShape"); + UndoItem::ChangeZ { entity, from, to } => { + if let Ok(mut t) = transforms.get_mut(*entity) { + t.translation.z = *from; + } + let t = *from; + *from = *to; + *to = t; + }, + UndoItem::ReScale { entity, from, to } => { + if let Ok(mut t) = transforms.get_mut(*entity) { + t.scale = from.extend(1.0); + } + let t = *from; + *from = *to; + *to = t; + }, + UndoItem::CreateImage { entity } => { + if let Ok((mut id, handle)) = images.get_mut(*entity) { + let transform = transforms.get(*entity).map(|t| *t).unwrap_or(Transform::default()); + let mut nid = ImageData::default(); + std::mem::swap(&mut *id, &mut nid); + + coms.entity(*entity).despawn(); + *item = UndoItem::DeleteImage { id: nid, transform, handle: handle.clone() }; + } + }, + UndoItem::DeleteImage { transform, handle, id } => { + let mut nid = ImageData::default(); + std::mem::swap(id, &mut nid); + + let e = coms.spawn_bundle(SpriteBundle { + texture: handle.clone(), + transform: *transform, + ..Default::default() + }) + .insert(nid).id(); + + *item = UndoItem::CreateImage { entity: e }; + }, + UndoItem::NoteChange { entity, from } => { + if let Ok(mut sd) = shapes.get_mut(*entity) { + std::mem::swap(&mut sd.note, from); + } + else if let Ok((mut id, _)) = images.get_mut(*entity) { + std::mem::swap(&mut id.note, from); + } }, } }