From bdc9e1256ae97d011c9b56aa00fbdc374164e15c Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 01:57:45 -0700 Subject: [PATCH 01/18] Updated roadmap --- Roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Roadmap.md b/Roadmap.md index a41a8a0..a074cdd 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -113,7 +113,7 @@ - [x] Add toggle to hide keyframes (replaces linking/locking) - [x] Add sprite darkening readility mode - [x] Workbench tabs -- [ ] Replace powershell release script with Github action +- [x] Replace powershell release script with Github action - [x] Unit test sheet module ## Tiger 0.5 From 70465eb417b78b4c8b243401277f2cbceb8daee0 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 02:50:26 -0700 Subject: [PATCH 02/18] Updated readme --- README.md | 6 +++--- res/readme/screenshot-0.4.0.png | Bin 0 -> 166512 bytes 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 res/readme/screenshot-0.4.0.png diff --git a/README.md b/README.md index 370f10d..c370a8e 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,17 @@ Tiger is a graphical tool for generating spritesheets and metadata about the animation and hitboxes they contain. -![Tiger](res/readme/screenshot-0.2.0.png?raw=true "Tiger") +![Tiger](res/readme/screenshot-0.4.0.png?raw=true "Tiger") # Key Features - Timeline-editing for authoring animations - Easy to add and position hitboxes -- Support for custom formats when exporting metadata +- Use custom formats when exporting spritesheet metadata - Generated texture atlas for use in-engine - Free and open-source with a permissive license -⚠️ This project is under development. It is already usable and is generating spritesheets for [Project Crystal](https://github.com/agersant/crystal). However, you should come back in a few months if you are looking for a polished experience! +⚠️ This project is under development. # Getting Started diff --git a/res/readme/screenshot-0.4.0.png b/res/readme/screenshot-0.4.0.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad0b7d1bffd69e6d03bb210c6ad6a88298f1cd2 GIT binary patch literal 166512 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 Date: Sun, 26 Jun 2022 02:55:25 -0700 Subject: [PATCH 03/18] Removed duplicate roadmap item --- Roadmap.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Roadmap.md b/Roadmap.md index a074cdd..2b869da 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -103,7 +103,6 @@ - [x] Add option to hide origin - [x] Add option to hide hitboxes - [x] Add option to lock hitboxes -- [x] Add readability mode - [x] Edit hitbox offset and size from details panel - [x] Edit keyframe duration and offset from details panel - [x] Support multiple directions/angles for the same animation From 102a67f7ee49fb3b126244f7660cdda6b2c9c36b Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 03:14:21 -0700 Subject: [PATCH 04/18] Roadmap update --- Roadmap.md | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/Roadmap.md b/Roadmap.md index 2b869da..8ba7abe 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -97,6 +97,7 @@ - [x] UI rewrite (prettier) - [x] In-place rename UX +- [x] Right click menu to rename item - [x] Right click menu to delete item - [x] Filter frames/animations in content panel by text search - [x] Can display frames in thumbnail or list mode @@ -118,15 +119,16 @@ ## Tiger 0.5 - [ ] Angle in liquid template sequence data -- [ ] Nicer looking paths in export settings (no concat) +- [ ] Nicer looking paths in export settings (no ../ concat) - [ ] Consider merging some code between save, save_as and save_all - [ ] Automatically add extensions to exported files - [ ] Default paths for NFD dialogs - [ ] Add menu entry to open recent files -- [ ] Export perf improvements -- [ ] Duplicate animation / keyframe (within same sheet) +- [ ] Copy/paste animations +- [ ] Copy/paste keyframes - [ ] Copy/paste hitboxes -- [ ] Rework content browser: view animations and frames at the same time, possibly with previews? +- [ ] Unit test timeline commands +- [ ] Unit test transient commands ## Tiger 0.6 @@ -134,13 +136,18 @@ - [ ] Add buttons to align frame edges or center to origin - [ ] Draw hitbox names in workbench - [ ] Playback speed controls -- [ ] Time snapping of animation frames -- [ ] Fix bug where when zoomed in a lot, resize handles on hitboxes arent correctly centered +- [ ] Time snapping / quantizing of animation frames +- [ ] Fix bug where when zoomed in a lot, resize handles on hitboxes are not correctly centered ## Tiger 0.7 -- [ ] Right click menu to rename item -- [ ] Export dialog links to documentation +- [ ] Export perf improvements +- [ ] Can zoom workbench with mousewheel +- [ ] Can zoom timeline with mousewheel +- [ ] Can scroll timeline with mousewheel +- [ ] Backend keeps track of frames pane scroll position in undo stack +- [ ] Backend keeps track of animations pane scroll position in undo stack +- [ ] Backend keeps track of timeline scroll position in undo stack ## Tiger 0.8 @@ -152,12 +159,13 @@ - [ ] Timeline scrolling follows playback - [ ] Timeline scrolling follows frame selection (or double click?) -- [ ] Can scroll timeline with mousewheel -- [ ] Backend keeps track of content pane scroll position in undo stack -- [ ] Backend keeps track of timeline scroll position in undo stack - [ ] Fix jank spacing in exported metadata files - [ ] Export complete notification - [ ] Unit test UI +- [ ] Document template format +- [ ] Export dialog links to documentation +- [ ] Auto-updates +- [ ] Splash screen ## Tiger 1.0 @@ -165,10 +173,9 @@ - [ ] Tiger file format uses semver - [ ] Remove support for pre-1.0 versions of Tiger file format - [ ] Provide export templates for some common formats (TBD) -- [ ] Document template format -- [ ] Readme Logo +- [ ] Readme logo - [ ] About dialog -- [ ] App icon +- [ ] App icon (file explorer, taskbar, add/remove programers, installer, title bar) - [ ] Itch.io or other distribution method ## Post 1.0 @@ -181,7 +188,6 @@ - [ ] Import animation data from other software (Asesprite, TBD) - [ ] Sockets (like hitbox but point) - [ ] Events (arbitrary markers on timeline) -- [ ] Copy/paste animation or keyframe (between sheets) -- [ ] Drag and drop frames from OS to content panel +- [ ] Drag and drop frames from OS to content panel - [ ] Drag and drop frames from OS to timeline panel - [ ] Projects? From a6a24fcae950c176d56c47474737cf6067aeb87b Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 15:58:50 -0700 Subject: [PATCH 05/18] Can copy/paste hitboxes --- Roadmap.md | 6 +- src-tauri/Cargo.lock | 9 ++- src-tauri/Cargo.toml | 3 +- src-tauri/src/api.rs | 33 ++++++++ src-tauri/src/main.rs | 2 + src-tauri/src/sheet.rs | 91 ++++++++++++++++------- src-tauri/src/state/document.rs | 23 ++++++ src-tauri/src/state/document/clipboard.rs | 88 ++++++++++++++++++++++ src-tauri/src/state/document/command.rs | 5 ++ src-tauri/src/state/document/content.rs | 2 +- src-tauri/src/state/document/keyframe.rs | 2 +- src-tauri/tauri.conf.json | 4 + src/api/document.ts | 10 +++ src/components/AppBar.vue | 5 +- src/utils/keyboard.ts | 6 ++ 15 files changed, 251 insertions(+), 38 deletions(-) create mode 100644 src-tauri/src/state/document/clipboard.rs diff --git a/Roadmap.md b/Roadmap.md index 8ba7abe..e6250e9 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -113,6 +113,9 @@ - [x] Add toggle to hide keyframes (replaces linking/locking) - [x] Add sprite darkening readility mode - [x] Workbench tabs +- [ ] Copy/paste animations +- [ ] Copy/paste keyframes +- [x] Copy/paste hitboxes - [x] Replace powershell release script with Github action - [x] Unit test sheet module @@ -124,9 +127,6 @@ - [ ] Automatically add extensions to exported files - [ ] Default paths for NFD dialogs - [ ] Add menu entry to open recent files -- [ ] Copy/paste animations -- [ ] Copy/paste keyframes -- [ ] Copy/paste hitboxes - [ ] Unit test timeline commands - [ ] Unit test transient commands diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c0f2afc..49d77e4 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2566,9 +2566,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -2586,9 +2586,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "remove_dir_all" @@ -3334,6 +3334,7 @@ dependencies = [ "liquid", "notify", "pathdiff", + "regex", "serde", "serde_json", "tauri", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e4f3df9..26eab0a 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -19,10 +19,11 @@ image = "0.24" json-patch = "0.2.6" liquid = "0.26" notify = "4.0" +regex = "1.5.6" pathdiff = "0.2.1" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.0.0", features = ["dialog-open", "dialog-save", "protocol-asset", "window-close", "window-maximize", "window-minimize", "window-start-dragging", "window-unmaximize"] } +tauri = { version = "1.0.0", features = ["clipboard", "clipboard-read-text", "clipboard-write-text", "dialog-open", "dialog-save", "protocol-asset", "window-close", "window-maximize", "window-minimize", "window-start-dragging", "window-unmaximize"] } texture_packer = "0.24" thiserror = "1.0" diff --git a/src-tauri/src/api.rs b/src-tauri/src/api.rs index f4cb3b4..32334e0 100644 --- a/src-tauri/src/api.rs +++ b/src-tauri/src/api.rs @@ -2,6 +2,7 @@ use euclid::vec2; use json_patch::Patch; use std::path::PathBuf; use std::time::Duration; +use tauri::ClipboardManager; use crate::dto::{self, ToFileName}; use crate::export::export_sheet; @@ -328,6 +329,38 @@ pub fn redo(app_state: tauri::State<'_, AppState>) -> Result { })) } +#[tauri::command] +pub fn copy( + tauri_app: tauri::AppHandle, + app_state: tauri::State<'_, AppState>, +) -> Result { + Ok(app_state.mutate(|app| { + if let Some(data) = app.current_document().and_then(|d| d.copy()) { + if let Ok(serialized) = serde_json::to_string(&data) { + let mut clipboard = tauri_app.clipboard_manager(); + clipboard.write_text(serialized).ok(); + } + } + })) +} + +#[tauri::command] +pub fn paste( + tauri_app: tauri::AppHandle, + app_state: tauri::State<'_, AppState>, +) -> Result { + Ok(app_state.mutate(|app| { + let clipboard = tauri_app.clipboard_manager(); + if let Ok(Some(serialized)) = clipboard.read_text() { + if let Ok(data) = serde_json::from_str(&serialized) { + if let Some(document) = app.current_document_mut() { + document.process_command(Command::Paste(data)).ok(); + } + } + } + })) +} + #[tauri::command] pub fn set_frames_list_mode( app_state: tauri::State<'_, AppState>, diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index dfad1f8..56911eb 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -42,6 +42,8 @@ fn main() { api::save_as, api::undo, api::redo, + api::copy, + api::paste, api::set_frames_list_mode, api::filter_frames, api::filter_animations, diff --git a/src-tauri/src/sheet.rs b/src-tauri/src/sheet.rs index b511edc..492f872 100644 --- a/src-tauri/src/sheet.rs +++ b/src-tauri/src/sheet.rs @@ -5,6 +5,7 @@ use euclid::rect; #[cfg(test)] use euclid::vec2; use pathdiff::diff_paths; +use regex::Regex; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashSet}; use std::fs::File; @@ -176,15 +177,12 @@ impl Sheet { } } - pub fn create_animation(&mut self) -> (String, &mut Animation) { - let mut name = "New Animation".to_owned(); - let mut index = 2; - while self.has_animation(&name) { - name = format!("New Animation {}", index); - index += 1; - } - let animation = Animation::default(); - self.animations.insert(name.clone(), animation); + pub fn create_animation>( + &mut self, + proposed_name: T, + ) -> (String, &mut Animation) { + let name = generate_unique_name(proposed_name.as_ref(), |n| !self.has_animation(&n)); + self.animations.insert(name.clone(), Animation::default()); (name.clone(), self.animations.get_mut(&name).unwrap()) } @@ -493,13 +491,8 @@ impl Keyframe { self.hitboxes.contains_key(name.as_ref()) } - pub fn create_hitbox(&mut self) -> (String, &mut Hitbox) { - let mut name = "New Hitbox".to_owned(); - let mut index = 2; - while self.has_hitbox(&name) { - name = format!("New Hitbox {}", index); - index += 1; - } + pub fn create_hitbox>(&mut self, proposed_name: T) -> (String, &mut Hitbox) { + let name = generate_unique_name(proposed_name.as_ref(), |n| !self.has_hitbox(n)); self.hitboxes.insert(name.clone(), Hitbox::new()); (name.clone(), self.hitboxes.get_mut(&name).unwrap()) } @@ -666,6 +659,31 @@ impl LiquidExportSettings { } } +fn generate_unique_name bool>(proposed_name: &str, validate: F) -> String { + let name_regex = Regex::new(r"(?P.*?)(?P\d+)$").unwrap(); + if validate(proposed_name) { + return proposed_name.to_owned(); + } + + let (base, mut suffix): (String, usize) = name_regex + .captures(proposed_name) + .map(|c| { + ( + c.name("base").unwrap().as_str().to_owned(), + c.name("suffix").unwrap().as_str().parse().unwrap_or(0), + ) + }) + .unwrap_or_else(|| (proposed_name.to_owned() + " ", 2)); + + loop { + let name = format!("{base}{suffix}"); + suffix += 1; + if validate(&name) { + return name; + } + } +} + #[test] fn can_read_write_sheet_from_disk() { let original = Sheet::read("test-data/sample_sheet_1.tiger").unwrap(); @@ -695,7 +713,7 @@ fn deleting_frame_remove_its_usage() { let mut sheet = Sheet::default(); sheet.add_frame("frame.png"); - let (animation_name, animation) = sheet.create_animation(); + let (animation_name, animation) = sheet.create_animation("Animation"); animation.apply_direction_preset(DirectionPreset::EightDirections); animation .sequence_mut(Direction::East) @@ -746,13 +764,13 @@ fn can_sort_frames() { #[test] fn can_add_and_remove_sheet_animation() { let mut sheet = Sheet::default(); - let (name_1, _animation) = sheet.create_animation(); + let (name_1, _animation) = sheet.create_animation("Animation"); assert!(sheet.has_animation(&name_1)); assert!(sheet.animation(&name_1).is_some()); assert!(sheet.animation_mut(&name_1).is_some()); assert_eq!(sheet.animations_iter().count(), 1); - let (name_2, _animation) = sheet.create_animation(); + let (name_2, _animation) = sheet.create_animation("Animation"); assert!(sheet.has_animation(&name_2)); sheet.delete_animation(&name_1); @@ -765,7 +783,7 @@ fn can_add_and_remove_sheet_animation() { #[test] fn can_rename_sheet_animation() { let mut sheet = Sheet::default(); - let (old_name, _animation) = sheet.create_animation(); + let (old_name, _animation) = sheet.create_animation("Animation"); sheet.rename_animation(&old_name, "updated name").unwrap(); assert!(sheet.animation("updated name").is_some()); assert!(sheet.animation(&old_name).is_none()); @@ -774,16 +792,16 @@ fn can_rename_sheet_animation() { #[test] fn can_rename_sheet_animation_to_same_name() { let mut sheet = Sheet::default(); - let (old_name, _animation) = sheet.create_animation(); + let (old_name, _animation) = sheet.create_animation("Animation"); sheet.rename_animation(&old_name, &old_name).unwrap(); } #[test] fn cannot_rename_sheet_animation_to_existing_name() { let mut sheet = Sheet::default(); - let (old_name, _animation) = sheet.create_animation(); + let (old_name, _animation) = sheet.create_animation("Animation"); sheet.rename_animation(&old_name, "conflict").unwrap(); - let (old_name, _animation) = sheet.create_animation(); + let (old_name, _animation) = sheet.create_animation("Animation"); assert!(sheet.rename_animation(&old_name, "conflict").is_err()); } @@ -947,7 +965,7 @@ fn can_read_write_keyframe_offset() { #[test] fn can_add_and_remove_keyframe_hitbox() { let mut keyframe = Keyframe::new(Path::new("./example/directory/texture.png")); - let (name, _hitbox) = keyframe.create_hitbox(); + let (name, _hitbox) = keyframe.create_hitbox("Hitbox"); assert!(keyframe.has_hitbox(&name)); assert_eq!(keyframe.hitboxes_iter().count(), 1); assert_eq!(keyframe.hitboxes_iter_mut().count(), 1); @@ -961,7 +979,7 @@ fn can_add_and_remove_keyframe_hitbox() { fn can_rename_keyframe_hitbox() { let frame = Path::new("./example/directory/texture.png"); let mut keyframe = Keyframe::new(frame); - let (old_name, _hitbox) = keyframe.create_hitbox(); + let (old_name, _hitbox) = keyframe.create_hitbox("Hitbox"); keyframe.rename_hitbox(&old_name, "updated name").unwrap(); assert!(keyframe.has_hitbox("updated name")); assert!(!keyframe.has_hitbox(&old_name)); @@ -971,10 +989,10 @@ fn can_rename_keyframe_hitbox() { fn can_rename_hitbox_to_existing_name() { let frame = Path::new("./example/directory/texture.png"); let mut keyframe = Keyframe::new(frame); - let (old_name, _hitbox) = keyframe.create_hitbox(); + let (old_name, _hitbox) = keyframe.create_hitbox("Hitbox"); keyframe.rename_hitbox(&old_name, "conflict").unwrap(); - let (old_name, _hitbox) = keyframe.create_hitbox(); + let (old_name, _hitbox) = keyframe.create_hitbox("Hitbox"); assert!(keyframe.rename_hitbox(&old_name, "conflict").is_err()); } @@ -1057,3 +1075,22 @@ fn liquid_export_settings_can_adjust_paths() { settings.set_metadata_paths_root(path); assert_eq!(settings.metadata_paths_root(), path); } + +#[test] +fn generate_unique_name_respects_suggestions() { + assert_eq!("oink", generate_unique_name("oink", |_| true)); +} + +#[test] +fn generate_unique_name_finds_workarounds() { + assert_eq!("oink 2", generate_unique_name("oink", |n| n != "oink")); + assert_eq!( + "oink 3", + generate_unique_name("oink", |n| n != "oink" && n != "oink 2") + ); +} + +#[test] +fn generate_unique_name_detects_existing_numbers() { + assert_eq!("oink 3", generate_unique_name("oink 2", |n| n != "oink 2")); +} diff --git a/src-tauri/src/state/document.rs b/src-tauri/src/state/document.rs index 05d5731..85d7de7 100644 --- a/src-tauri/src/state/document.rs +++ b/src-tauri/src/state/document.rs @@ -4,6 +4,7 @@ use thiserror::Error; use crate::sheet::*; +mod clipboard; mod command; mod content; mod export; @@ -13,6 +14,7 @@ mod timeline; mod transient; mod view; +pub use clipboard::*; pub use command::*; pub use content::*; pub use export::*; @@ -330,6 +332,27 @@ impl Document { .collect()) } + pub fn get_selected_hitboxes(&self) -> Result, DocumentError> { + let (animation_name, _) = self.get_workbench_animation()?; + let selection = self.view.selection.clone(); + let ((direction, index), keyframe) = self.get_workbench_keyframe()?; + Ok(keyframe + .hitboxes_iter() + .filter_map(|(hitbox_name, hitbox)| { + if selection.is_hitbox_selected( + animation_name.clone(), + direction, + index, + hitbox_name, + ) { + Some((hitbox_name, hitbox)) + } else { + None + } + }) + .collect()) + } + pub fn get_selected_hitboxes_mut( &mut self, ) -> Result, DocumentError> { diff --git a/src-tauri/src/state/document/clipboard.rs b/src-tauri/src/state/document/clipboard.rs new file mode 100644 index 0000000..a341b90 --- /dev/null +++ b/src-tauri/src/state/document/clipboard.rs @@ -0,0 +1,88 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::sheet::*; +use crate::state::*; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum Clipboard { + Hitboxes(HashMap), +} + +impl Document { + pub fn copy(&self) -> Option { + if !self.view.selection.hitboxes.is_empty() { + let hitboxes = self + .get_selected_hitboxes() + .ok()? + .into_iter() + .map(|(name, hitbox)| (name.clone(), hitbox.clone())) + .collect::>(); + Some(Clipboard::Hitboxes(hitboxes)) + } else { + None + } + } + + pub(super) fn paste(&mut self, clipboard: Clipboard) -> Result<(), DocumentError> { + match clipboard { + Clipboard::Hitboxes(hitboxes) => { + let (_, keyframe) = self.get_workbench_keyframe_mut()?; + for (name, hitbox) in hitboxes { + let (_, new_hitbox) = keyframe.create_hitbox(name); + *new_hitbox = hitbox; + } + } + } + Ok(()) + } +} + +#[test] +fn can_copy_paste_hitbox() { + let mut document = Document::new("tmp"); + document.sheet = Sheet::default(); + document.sheet.add_frame("frame.png"); + let (_, animation) = document.sheet.create_animation("animation"); + animation.apply_direction_preset(DirectionPreset::EightDirections); + + { + let mut keyframe = Keyframe::new("frame.png"); + keyframe.create_hitbox("hitbox"); + animation + .sequence_mut(Direction::East) + .unwrap() + .insert_keyframe(keyframe, 0) + .unwrap(); + } + + { + let keyframe = Keyframe::new("frame.png"); + animation + .sequence_mut(Direction::North) + .unwrap() + .insert_keyframe(keyframe, 0) + .unwrap(); + } + + document.edit_animation("animation").unwrap(); + document.select_hitbox_only( + "animation".to_owned(), + Direction::East, + 0, + "hitbox".to_owned(), + ); + let clipboard = document.copy().unwrap(); + + document.select_direction(Direction::North).unwrap(); + document.paste(clipboard).unwrap(); + assert!(document + .sheet + .animation("animation") + .unwrap() + .sequence(Direction::North) + .unwrap() + .keyframe(0) + .unwrap() + .has_hitbox("hitbox")); +} diff --git a/src-tauri/src/state/document/command.rs b/src-tauri/src/state/document/command.rs index bf0bac1..1345d26 100644 --- a/src-tauri/src/state/document/command.rs +++ b/src-tauri/src/state/document/command.rs @@ -9,6 +9,7 @@ use crate::state::*; pub enum Command { Undo, Redo, + Paste(Clipboard), SetFramesListMode(ListMode), FilterFrames(String), FilterAnimations(String), @@ -112,6 +113,7 @@ impl Document { match command { Command::Undo => self.undo()?, Command::Redo => self.redo()?, + Command::Paste(ref c) => self.paste(c.clone())?, Command::SetFramesListMode(m) => self.view.frames_list_mode = m, Command::FilterFrames(ref q) => self.view.frames_filter = q.clone(), Command::FilterAnimations(ref q) => self.view.animations_filter = q.clone(), @@ -372,6 +374,9 @@ impl Display for Command { Command::Undo => f.write_str("Undo"), Command::Redo => f.write_str("Redo"), + Command::Paste(c) => match c { + Clipboard::Hitboxes(_) => f.write_str("Paste Hitboxes"), + }, Command::ImportFrames(_) => f.write_str("Import Frames"), Command::DeleteFrame(_) => f.write_str("Delete Frame"), Command::DeleteSelectedFrames => f.write_str("Delete Frames"), diff --git a/src-tauri/src/state/document/content.rs b/src-tauri/src/state/document/content.rs index 4421f96..2e1db3b 100644 --- a/src-tauri/src/state/document/content.rs +++ b/src-tauri/src/state/document/content.rs @@ -10,7 +10,7 @@ impl Document { } pub(super) fn create_animation(&mut self) -> Result<(), DocumentError> { - let (animation_name, animation) = self.sheet.create_animation(); + let (animation_name, animation) = self.sheet.create_animation("New Animation"); animation.apply_direction_preset(DirectionPreset::FourDirections); self.select_animation_only(animation_name.clone()); self.edit_animation(animation_name) diff --git a/src-tauri/src/state/document/keyframe.rs b/src-tauri/src/state/document/keyframe.rs index c897504..aa53762 100644 --- a/src-tauri/src/state/document/keyframe.rs +++ b/src-tauri/src/state/document/keyframe.rs @@ -35,7 +35,7 @@ impl Document { let (animation_name, _) = self.get_workbench_animation()?; let animation_name = animation_name.clone(); let ((direction, index), keyframe) = self.get_workbench_keyframe_mut()?; - let (hitbox_name, hitbox) = keyframe.create_hitbox(); + let (hitbox_name, hitbox) = keyframe.create_hitbox("New Hitbox"); if let Some(position) = position { hitbox.set_position(position); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e0980db..4c47314 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -12,6 +12,10 @@ }, "tauri": { "allowlist": { + "clipboard": { + "readText": true, + "writeText": true + }, "dialog": { "open": true, "save": true diff --git a/src/api/document.ts b/src/api/document.ts index 2560a45..27dced8 100644 --- a/src/api/document.ts +++ b/src/api/document.ts @@ -34,6 +34,16 @@ export async function redo(): Promise { appStore.patch(await invoke("redo")); } +export async function copy(): Promise { + const appStore = useAppStore(); + appStore.patch(await invoke("copy")); +} + +export async function paste(): Promise { + const appStore = useAppStore(); + appStore.patch(await invoke("paste")); +} + export async function setFramesListMode(listMode: ListMode): Promise { const appStore = useAppStore(); appStore.patch(await invoke("set_frames_list_mode", { listMode: listMode })); diff --git a/src/components/AppBar.vue b/src/components/AppBar.vue index 22306fb..e0c29c6 100644 --- a/src/components/AppBar.vue +++ b/src/components/AppBar.vue @@ -21,7 +21,7 @@ import { computed } from "vue" import { useAppStore } from "@/stores/app" import { closeAllDocuments, closeCurrentDocument, saveAll } from "@/api/app" -import { beginExportAs, doExport, centerWorkbench, redo, resetTimelineZoom, resetWorkbenchZoom, save, undo, zoomInTimeline, zoomInWorkbench, zoomOutTimeline, zoomOutWorkbench } from "@/api/document" +import { beginExportAs, doExport, centerWorkbench, redo, resetTimelineZoom, resetWorkbenchZoom, save, undo, zoomInTimeline, zoomInWorkbench, zoomOutTimeline, zoomOutWorkbench, copy, paste } from "@/api/document" import { newDocument, openDocuments, saveAs } from "@/api/local" import MenuBar from "@/components/basic/MenuBar.vue" import WindowTitleBar from "@/components/basic/WindowTitleBar.vue" @@ -62,6 +62,9 @@ const editMenuEntries = computed(() => { shortcut: "Ctrl+Shift+Z", action: redo, disabled: app.currentDocument?.redoEffect == null }, + {}, + { name: "Copy", shortcut: "Ctrl+C", action: copy, }, + { name: "Paste", shortcut: "Ctrl+V", action: paste, }, ]; }); diff --git a/src/utils/keyboard.ts b/src/utils/keyboard.ts index 612e712..b4955a5 100644 --- a/src/utils/keyboard.ts +++ b/src/utils/keyboard.ts @@ -3,9 +3,11 @@ import { browseSelection, beginExportAs, centerWorkbench, + copy, deleteSelection, doExport, nudgeSelection, + paste, pause, play, redo, @@ -55,6 +57,10 @@ function onKeyDown(event: KeyboardEvent) { undo(); } else if (event.key == "Z") { redo(); + } else if (event.key == "c") { + copy(); + } else if (event.key == "v") { + paste(); } else if (event.key == " ") { centerWorkbench(); } else if (event.key == "+" || event.key == "=") { From 837cdc83cfaae083a472ac8356bc3c71f3f4e1e7 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 16:02:04 -0700 Subject: [PATCH 06/18] Minor refactor --- src-tauri/src/state/document/clipboard.rs | 34 +++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/state/document/clipboard.rs b/src-tauri/src/state/document/clipboard.rs index a341b90..77b100d 100644 --- a/src-tauri/src/state/document/clipboard.rs +++ b/src-tauri/src/state/document/clipboard.rs @@ -12,13 +12,7 @@ pub enum Clipboard { impl Document { pub fn copy(&self) -> Option { if !self.view.selection.hitboxes.is_empty() { - let hitboxes = self - .get_selected_hitboxes() - .ok()? - .into_iter() - .map(|(name, hitbox)| (name.clone(), hitbox.clone())) - .collect::>(); - Some(Clipboard::Hitboxes(hitboxes)) + self.copy_hitboxes() } else { None } @@ -26,13 +20,25 @@ impl Document { pub(super) fn paste(&mut self, clipboard: Clipboard) -> Result<(), DocumentError> { match clipboard { - Clipboard::Hitboxes(hitboxes) => { - let (_, keyframe) = self.get_workbench_keyframe_mut()?; - for (name, hitbox) in hitboxes { - let (_, new_hitbox) = keyframe.create_hitbox(name); - *new_hitbox = hitbox; - } - } + Clipboard::Hitboxes(hitboxes) => self.paste_hitboxes(hitboxes), + } + } + + fn copy_hitboxes(&self) -> Option { + let hitboxes = self + .get_selected_hitboxes() + .ok()? + .into_iter() + .map(|(name, hitbox)| (name.clone(), hitbox.clone())) + .collect::>(); + Some(Clipboard::Hitboxes(hitboxes)) + } + + fn paste_hitboxes(&mut self, hitboxes: HashMap) -> Result<(), DocumentError> { + let (_, keyframe) = self.get_workbench_keyframe_mut()?; + for (name, hitbox) in hitboxes { + let (_, new_hitbox) = keyframe.create_hitbox(name); + *new_hitbox = hitbox; } Ok(()) } From 2c9e40cc803379900df7bb9f642fb07c892b7756 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 16:41:09 -0700 Subject: [PATCH 07/18] Can copy paste animations --- src-tauri/src/dto.rs | 2 + src-tauri/src/sheet.rs | 61 +++++++++++++++++++++-- src-tauri/src/sheet/version4.rs | 8 ++- src-tauri/src/state/document.rs | 7 +++ src-tauri/src/state/document/clipboard.rs | 44 +++++++++++++++- src-tauri/src/state/document/command.rs | 1 + 6 files changed, 115 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/dto.rs b/src-tauri/src/dto.rs index d3d3e8a..20a978f 100644 --- a/src-tauri/src/dto.rs +++ b/src-tauri/src/dto.rs @@ -88,6 +88,7 @@ pub struct Animation { sequences: HashMap, direction_preset: Option, is_looping: bool, + key: Uuid, } #[derive(Clone, Copy, Deserialize, Eq, PartialEq, Hash, Serialize)] @@ -356,6 +357,7 @@ where .collect(), direction_preset: animation.1.direction_preset().map(|p| p.into()), is_looping: animation.1.looping(), + key: animation.1.key(), } } } diff --git a/src-tauri/src/sheet.rs b/src-tauri/src/sheet.rs index 492f872..355302b 100644 --- a/src-tauri/src/sheet.rs +++ b/src-tauri/src/sheet.rs @@ -182,7 +182,7 @@ impl Sheet { proposed_name: T, ) -> (String, &mut Animation) { let name = generate_unique_name(proposed_name.as_ref(), |n| !self.has_animation(&n)); - self.animations.insert(name.clone(), Animation::default()); + self.animations.insert(name.clone(), Animation::new()); (name.clone(), self.animations.get_mut(&name).unwrap()) } @@ -269,6 +269,30 @@ impl PartialOrd for Frame { } impl Animation { + fn new() -> Self { + Self { + sequences: Default::default(), + is_looping: Default::default(), + key: Uuid::new_v4(), + } + } + + pub fn duplicate(&self) -> Animation { + Animation { + sequences: self + .sequences + .iter() + .map(|(d, s)| (*d, s.duplicate())) + .collect(), + is_looping: self.is_looping, + key: Uuid::new_v4(), + } + } + + pub fn key(&self) -> Uuid { + self.key + } + pub fn looping(&self) -> bool { self.is_looping } @@ -349,6 +373,12 @@ impl DirectionPreset { } impl Sequence { + pub fn duplicate(&self) -> Sequence { + Sequence { + keyframes: self.keyframes.iter().map(Keyframe::duplicate).collect(), + } + } + pub fn num_keyframes(&self) -> usize { self.keyframes.len() } @@ -455,6 +485,20 @@ impl Keyframe { } } + pub fn duplicate(&self) -> Keyframe { + Keyframe { + frame: self.frame.clone(), + hitboxes: self + .hitboxes + .iter() + .map(|(n, h)| (n.clone(), h.duplicate())) + .collect(), + duration_millis: self.duration_millis, + offset: self.offset, + key: Uuid::new_v4(), + } + } + pub fn frame(&self) -> &Path { &self.frame } @@ -534,6 +578,13 @@ impl Hitbox { } } + pub fn duplicate(&self) -> Hitbox { + Hitbox { + geometry: self.geometry.clone(), + key: Uuid::new_v4(), + } + } + pub fn rectangle(&self) -> Rect { match &self.geometry { Shape::Rectangle(r) => { @@ -807,7 +858,7 @@ fn cannot_rename_sheet_animation_to_existing_name() { #[test] fn can_read_write_animation_looping() { - let mut animation = Animation::default(); + let mut animation = Animation::new(); animation.set_looping(true); assert!(animation.looping()); animation.set_looping(false); @@ -816,7 +867,7 @@ fn can_read_write_animation_looping() { #[test] fn can_access_animation_sequences() { - let mut animation = Animation::default(); + let mut animation = Animation::new(); animation.apply_direction_preset(DirectionPreset::FourDirections); assert!(animation.sequence(Direction::West).is_some()); assert!(animation.sequence_mut(Direction::West).is_some()); @@ -826,7 +877,7 @@ fn can_access_animation_sequences() { #[test] fn can_animation_can_apply_direction_preset() { - let mut animation = Animation::default(); + let mut animation = Animation::new(); assert_eq!(animation.direction_preset(), None); for preset in all::() { animation.apply_direction_preset(preset); @@ -836,7 +887,7 @@ fn can_animation_can_apply_direction_preset() { #[test] fn animation_can_recognize_direction_preset() { - let mut animation = Animation::default(); + let mut animation = Animation::new(); animation .sequences .insert(Direction::NorthEast, Sequence::default()); diff --git a/src-tauri/src/sheet/version4.rs b/src-tauri/src/sheet/version4.rs index a209b0e..01615d3 100644 --- a/src-tauri/src/sheet/version4.rs +++ b/src-tauri/src/sheet/version4.rs @@ -27,10 +27,15 @@ pub struct Frame { pub(in crate::sheet) source: PathBuf, } -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Derivative)] +#[derivative(PartialEq)] +#[derive(Clone, Debug, Eq, Serialize, Deserialize)] pub struct Animation { pub(in crate::sheet) sequences: BTreeMap, pub(in crate::sheet) is_looping: bool, + #[derivative(PartialEq = "ignore")] + #[serde(skip, default = "Uuid::new_v4")] + pub(in crate::sheet) key: Uuid, } #[derive( @@ -148,6 +153,7 @@ impl From for Animation { Animation { sequences: BTreeMap::from([(Direction::East, old.timeline.into())]), is_looping: old.is_looping, + key: Uuid::new_v4(), } } } diff --git a/src-tauri/src/state/document.rs b/src-tauri/src/state/document.rs index 85d7de7..e04212d 100644 --- a/src-tauri/src/state/document.rs +++ b/src-tauri/src/state/document.rs @@ -286,6 +286,13 @@ impl Document { Ok(((direction, index), keyframe)) } + pub fn get_selected_animations(&self) -> Vec<(&String, &Animation)> { + self.sheet + .animations_iter() + .filter(|(name, _)| self.view.selection.is_animation_selected(name)) + .collect() + } + pub fn get_selected_keyframes( &self, ) -> Result, DocumentError> { diff --git a/src-tauri/src/state/document/clipboard.rs b/src-tauri/src/state/document/clipboard.rs index 77b100d..c2085ee 100644 --- a/src-tauri/src/state/document/clipboard.rs +++ b/src-tauri/src/state/document/clipboard.rs @@ -6,12 +6,15 @@ use crate::state::*; #[derive(Clone, Debug, Deserialize, Serialize)] pub enum Clipboard { + Animations(HashMap), Hitboxes(HashMap), } impl Document { pub fn copy(&self) -> Option { - if !self.view.selection.hitboxes.is_empty() { + if !self.view.selection.animations.is_empty() { + self.copy_animations() + } else if !self.view.selection.hitboxes.is_empty() { self.copy_hitboxes() } else { None @@ -20,10 +23,32 @@ impl Document { pub(super) fn paste(&mut self, clipboard: Clipboard) -> Result<(), DocumentError> { match clipboard { + Clipboard::Animations(animations) => self.paste_animations(animations), Clipboard::Hitboxes(hitboxes) => self.paste_hitboxes(hitboxes), } } + fn copy_animations(&self) -> Option { + let animations = self + .get_selected_animations() + .into_iter() + .map(|(name, animation)| (name.clone(), animation.clone())) + .collect::>(); + Some(Clipboard::Animations(animations)) + } + + fn paste_animations( + &mut self, + animations: HashMap, + ) -> Result<(), DocumentError> { + for (name, animation) in animations { + let (_, new_animation) = self.sheet.create_animation(name); + *new_animation = animation.duplicate(); + } + // TODO select new animations + Ok(()) + } + fn copy_hitboxes(&self) -> Option { let hitboxes = self .get_selected_hitboxes() @@ -38,12 +63,27 @@ impl Document { let (_, keyframe) = self.get_workbench_keyframe_mut()?; for (name, hitbox) in hitboxes { let (_, new_hitbox) = keyframe.create_hitbox(name); - *new_hitbox = hitbox; + *new_hitbox = hitbox.duplicate(); } + // TODO select pasted hitboxes Ok(()) } } +#[test] +fn can_copy_paste_animation() { + let mut document = Document::new("tmp"); + document.sheet = Sheet::default(); + let (_, animation) = document.sheet.create_animation("animation"); + animation.apply_direction_preset(DirectionPreset::EightDirections); + + document.select_animation_only("animation".to_owned()); + let clipboard = document.copy().unwrap(); + document.paste(clipboard).unwrap(); + + assert_eq!(document.sheet.animations_iter().count(), 2); +} + #[test] fn can_copy_paste_hitbox() { let mut document = Document::new("tmp"); diff --git a/src-tauri/src/state/document/command.rs b/src-tauri/src/state/document/command.rs index 1345d26..a494ed7 100644 --- a/src-tauri/src/state/document/command.rs +++ b/src-tauri/src/state/document/command.rs @@ -375,6 +375,7 @@ impl Display for Command { Command::Undo => f.write_str("Undo"), Command::Redo => f.write_str("Redo"), Command::Paste(c) => match c { + Clipboard::Animations(_) => f.write_str("Paste Animations"), Clipboard::Hitboxes(_) => f.write_str("Paste Hitboxes"), }, Command::ImportFrames(_) => f.write_str("Import Frames"), From 458641ffa4640165ba658d19808d15d618dda1fd Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 16:47:22 -0700 Subject: [PATCH 08/18] Silence rust analyzer false positive --- .gitignore | 1 - .vscode/settings.json | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index a547bf3..cb0da3d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ dist-ssr *.local # Editor directories and files -.vscode/* !.vscode/extensions.json .idea .DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..643f92e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.cargo.unsetTest": [ + "core", + "derivative" // TODO.upstream remove this https://github.com/rust-lang/rust-analyzer/issues/7459 + ] +} From 4b94d4ed51bcaf016f4d3869b824921ea5287748 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 16:55:47 -0700 Subject: [PATCH 09/18] Auto-select pasted content --- Roadmap.md | 2 +- src-tauri/src/state/document/clipboard.rs | 24 ++++++++++++++++++----- src-tauri/src/state/document/selection.rs | 16 +++++++++++++++ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Roadmap.md b/Roadmap.md index e6250e9..e55fda6 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -113,7 +113,7 @@ - [x] Add toggle to hide keyframes (replaces linking/locking) - [x] Add sprite darkening readility mode - [x] Workbench tabs -- [ ] Copy/paste animations +- [x] Copy/paste animations - [ ] Copy/paste keyframes - [x] Copy/paste hitboxes - [x] Replace powershell release script with Github action diff --git a/src-tauri/src/state/document/clipboard.rs b/src-tauri/src/state/document/clipboard.rs index c2085ee..147af1c 100644 --- a/src-tauri/src/state/document/clipboard.rs +++ b/src-tauri/src/state/document/clipboard.rs @@ -41,11 +41,13 @@ impl Document { &mut self, animations: HashMap, ) -> Result<(), DocumentError> { + let mut new_animation_names = vec![]; for (name, animation) in animations { - let (_, new_animation) = self.sheet.create_animation(name); + let (new_animation_name, new_animation) = self.sheet.create_animation(name); + new_animation_names.push(new_animation_name); *new_animation = animation.duplicate(); } - // TODO select new animations + self.select_animations_only(new_animation_names); Ok(()) } @@ -60,12 +62,19 @@ impl Document { } fn paste_hitboxes(&mut self, hitboxes: HashMap) -> Result<(), DocumentError> { - let (_, keyframe) = self.get_workbench_keyframe_mut()?; + let (animation_name, _) = self.get_workbench_animation_mut()?; + let ((direction, index), keyframe) = self.get_workbench_keyframe_mut()?; + let mut new_hitbox_names = vec![]; for (name, hitbox) in hitboxes { - let (_, new_hitbox) = keyframe.create_hitbox(name); + let (new_hitbox_name, new_hitbox) = keyframe.create_hitbox(name); + new_hitbox_names.push(new_hitbox_name); *new_hitbox = hitbox.duplicate(); } - // TODO select pasted hitboxes + self.select_hitboxes_only( + new_hitbox_names + .into_iter() + .map(|n| (animation_name.clone(), direction, index, n)), + ); Ok(()) } } @@ -82,6 +91,7 @@ fn can_copy_paste_animation() { document.paste(clipboard).unwrap(); assert_eq!(document.sheet.animations_iter().count(), 2); + assert!(document.view.selection.is_animation_selected("animation")); } #[test] @@ -131,4 +141,8 @@ fn can_copy_paste_hitbox() { .keyframe(0) .unwrap() .has_hitbox("hitbox")); + assert!(document + .view + .selection + .is_hitbox_selected("animation", Direction::North, 0, "hitbox")); } diff --git a/src-tauri/src/state/document/selection.rs b/src-tauri/src/state/document/selection.rs index a4fb2a5..86c9eba 100644 --- a/src-tauri/src/state/document/selection.rs +++ b/src-tauri/src/state/document/selection.rs @@ -89,6 +89,14 @@ impl Document { self.view.selection.animations.only(vec![animation]); } + pub fn select_animations_only(&mut self, animations: T) + where + T: IntoIterator, + { + self.view.selection.clear(); + self.view.selection.animations.only(animations); + } + pub fn select_keyframe_only(&mut self, animation: String, direction: Direction, index: usize) { self.view.selection.clear(); self.view @@ -119,6 +127,14 @@ impl Document { .only(vec![(animation, direction, index, hitbox)]); } + pub fn select_hitboxes_only(&mut self, hitboxes: T) + where + T: IntoIterator, + { + self.view.selection.clear(); + self.view.selection.hitboxes.only(hitboxes); + } + pub(super) fn select_frame>(&mut self, path: T, shift: bool, ctrl: bool) { self.view.selection.animations.clear(); self.view.selection.keyframes.clear(); From 5bffb877496adef38367beee57f5668ad5da09c1 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 16:56:51 -0700 Subject: [PATCH 10/18] Fixed test --- src-tauri/src/state/document/clipboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/src/state/document/clipboard.rs b/src-tauri/src/state/document/clipboard.rs index 147af1c..3d537c4 100644 --- a/src-tauri/src/state/document/clipboard.rs +++ b/src-tauri/src/state/document/clipboard.rs @@ -91,7 +91,7 @@ fn can_copy_paste_animation() { document.paste(clipboard).unwrap(); assert_eq!(document.sheet.animations_iter().count(), 2); - assert!(document.view.selection.is_animation_selected("animation")); + assert!(document.view.selection.is_animation_selected("animation 2")); } #[test] From d45db3c8ff2d2604ed7fe2c4d04b6141baee4136 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 16:57:55 -0700 Subject: [PATCH 11/18] Capitalized file names --- Maintenance.md => MAINTENANCE.md | 0 Roadmap.md => ROADMAP.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Maintenance.md => MAINTENANCE.md (100%) rename Roadmap.md => ROADMAP.md (100%) diff --git a/Maintenance.md b/MAINTENANCE.md similarity index 100% rename from Maintenance.md rename to MAINTENANCE.md diff --git a/Roadmap.md b/ROADMAP.md similarity index 100% rename from Roadmap.md rename to ROADMAP.md From ee67088767909edece9cd5d9fb0140183fb8b570 Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 17:18:43 -0700 Subject: [PATCH 12/18] Implemented Cut operation --- src-tauri/src/api.rs | 18 ++++++++++++++++++ src-tauri/src/main.rs | 1 + src/api/document.ts | 5 +++++ src/components/AppBar.vue | 3 ++- src/utils/keyboard.ts | 3 +++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/api.rs b/src-tauri/src/api.rs index 32334e0..6801256 100644 --- a/src-tauri/src/api.rs +++ b/src-tauri/src/api.rs @@ -329,6 +329,24 @@ pub fn redo(app_state: tauri::State<'_, AppState>) -> Result { })) } +#[tauri::command] +pub fn cut( + tauri_app: tauri::AppHandle, + app_state: tauri::State<'_, AppState>, +) -> Result { + Ok(app_state.mutate(|app| { + if let Some(data) = app.current_document().and_then(|d| d.copy()) { + if let Ok(serialized) = serde_json::to_string(&data) { + let mut clipboard = tauri_app.clipboard_manager(); + clipboard.write_text(serialized).ok(); + } + } + if let Some(document) = app.current_document_mut() { + document.process_command(Command::DeleteSelection).ok(); + } + })) +} + #[tauri::command] pub fn copy( tauri_app: tauri::AppHandle, diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 56911eb..b4adb8a 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -42,6 +42,7 @@ fn main() { api::save_as, api::undo, api::redo, + api::cut, api::copy, api::paste, api::set_frames_list_mode, diff --git a/src/api/document.ts b/src/api/document.ts index 27dced8..35128d4 100644 --- a/src/api/document.ts +++ b/src/api/document.ts @@ -34,6 +34,11 @@ export async function redo(): Promise { appStore.patch(await invoke("redo")); } +export async function cut(): Promise { + const appStore = useAppStore(); + appStore.patch(await invoke("cut")); +} + export async function copy(): Promise { const appStore = useAppStore(); appStore.patch(await invoke("copy")); diff --git a/src/components/AppBar.vue b/src/components/AppBar.vue index e0c29c6..bd1a27e 100644 --- a/src/components/AppBar.vue +++ b/src/components/AppBar.vue @@ -21,7 +21,7 @@ import { computed } from "vue" import { useAppStore } from "@/stores/app" import { closeAllDocuments, closeCurrentDocument, saveAll } from "@/api/app" -import { beginExportAs, doExport, centerWorkbench, redo, resetTimelineZoom, resetWorkbenchZoom, save, undo, zoomInTimeline, zoomInWorkbench, zoomOutTimeline, zoomOutWorkbench, copy, paste } from "@/api/document" +import { beginExportAs, doExport, centerWorkbench, redo, resetTimelineZoom, resetWorkbenchZoom, save, undo, zoomInTimeline, zoomInWorkbench, zoomOutTimeline, zoomOutWorkbench, copy, paste, cut } from "@/api/document" import { newDocument, openDocuments, saveAs } from "@/api/local" import MenuBar from "@/components/basic/MenuBar.vue" import WindowTitleBar from "@/components/basic/WindowTitleBar.vue" @@ -63,6 +63,7 @@ const editMenuEntries = computed(() => { disabled: app.currentDocument?.redoEffect == null }, {}, + { name: "Cut", shortcut: "Ctrl+X", action: cut, }, { name: "Copy", shortcut: "Ctrl+C", action: copy, }, { name: "Paste", shortcut: "Ctrl+V", action: paste, }, ]; diff --git a/src/utils/keyboard.ts b/src/utils/keyboard.ts index b4955a5..01f487e 100644 --- a/src/utils/keyboard.ts +++ b/src/utils/keyboard.ts @@ -19,6 +19,7 @@ import { zoomInWorkbench, zoomOutTimeline, zoomOutWorkbench, + cut, } from "@/api/document"; import { closeAllDocuments, closeCurrentDocument, saveAll } from "@/api/app"; import { useAppStore } from "@/stores/app"; @@ -57,6 +58,8 @@ function onKeyDown(event: KeyboardEvent) { undo(); } else if (event.key == "Z") { redo(); + } else if (event.key == "x") { + cut(); } else if (event.key == "c") { copy(); } else if (event.key == "v") { From d445f5b305efb262898b8ea97ad9ec6451ec04ee Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 18:10:30 -0700 Subject: [PATCH 13/18] Can copy/paste keyframes --- ROADMAP.md | 2 +- src-tauri/src/state/document/clipboard.rs | 32 +++++++++++++++++++++++ src-tauri/src/state/document/command.rs | 1 + src-tauri/src/state/document/selection.rs | 1 + 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index e55fda6..2eb0be0 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -114,7 +114,7 @@ - [x] Add sprite darkening readility mode - [x] Workbench tabs - [x] Copy/paste animations -- [ ] Copy/paste keyframes +- [x] Copy/paste keyframes - [x] Copy/paste hitboxes - [x] Replace powershell release script with Github action - [x] Unit test sheet module diff --git a/src-tauri/src/state/document/clipboard.rs b/src-tauri/src/state/document/clipboard.rs index 3d537c4..ffa116d 100644 --- a/src-tauri/src/state/document/clipboard.rs +++ b/src-tauri/src/state/document/clipboard.rs @@ -7,6 +7,7 @@ use crate::state::*; #[derive(Clone, Debug, Deserialize, Serialize)] pub enum Clipboard { Animations(HashMap), + Keyframes(Vec), Hitboxes(HashMap), } @@ -14,6 +15,8 @@ impl Document { pub fn copy(&self) -> Option { if !self.view.selection.animations.is_empty() { self.copy_animations() + } else if !self.view.selection.keyframes.is_empty() { + self.copy_keyframes() } else if !self.view.selection.hitboxes.is_empty() { self.copy_hitboxes() } else { @@ -24,6 +27,7 @@ impl Document { pub(super) fn paste(&mut self, clipboard: Clipboard) -> Result<(), DocumentError> { match clipboard { Clipboard::Animations(animations) => self.paste_animations(animations), + Clipboard::Keyframes(keyframes) => self.paste_keyframes(keyframes), Clipboard::Hitboxes(hitboxes) => self.paste_hitboxes(hitboxes), } } @@ -51,6 +55,34 @@ impl Document { Ok(()) } + fn copy_keyframes(&self) -> Option { + let keyframes = self + .get_selected_keyframes() + .ok()? + .into_iter() + .map(|(_, _, keyframe)| keyframe.clone()) + .collect(); + Some(Clipboard::Keyframes(keyframes)) + } + + fn paste_keyframes(&mut self, keyframes: Vec) -> Result<(), DocumentError> { + let index = self + .get_workbench_keyframe() + .map(|((_, index), _)| index) + .unwrap_or_default(); + let (animation_name, _) = self.get_workbench_animation_mut()?; + let num_keyframes = keyframes.len(); + let (direction, sequence) = self.get_workbench_sequence_mut()?; + for keyframe in keyframes.into_iter().rev() { + let new_keyframe = keyframe.duplicate(); + sequence.insert_keyframe(new_keyframe, index)?; + } + self.select_keyframes_only( + (index..(index + num_keyframes)).map(|i| (animation_name.clone(), direction, i)), + ); + Ok(()) + } + fn copy_hitboxes(&self) -> Option { let hitboxes = self .get_selected_hitboxes() diff --git a/src-tauri/src/state/document/command.rs b/src-tauri/src/state/document/command.rs index a494ed7..456ea6d 100644 --- a/src-tauri/src/state/document/command.rs +++ b/src-tauri/src/state/document/command.rs @@ -376,6 +376,7 @@ impl Display for Command { Command::Redo => f.write_str("Redo"), Command::Paste(c) => match c { Clipboard::Animations(_) => f.write_str("Paste Animations"), + Clipboard::Keyframes(_) => f.write_str("Paste Keyframes"), Clipboard::Hitboxes(_) => f.write_str("Paste Hitboxes"), }, Command::ImportFrames(_) => f.write_str("Import Frames"), diff --git a/src-tauri/src/state/document/selection.rs b/src-tauri/src/state/document/selection.rs index 86c9eba..5fa6d11 100644 --- a/src-tauri/src/state/document/selection.rs +++ b/src-tauri/src/state/document/selection.rs @@ -50,6 +50,7 @@ impl Document { self.delete_selected_animations(); self.delete_selected_keyframes()?; self.delete_selected_hitboxes()?; + self.view.selection.clear(); Ok(()) } From e52e3f3f2c2256e91e99971ada96166b014c772a Mon Sep 17 00:00:00 2001 From: Antoine Gersant Date: Sun, 26 Jun 2022 18:17:48 -0700 Subject: [PATCH 14/18] Added Cut/Copy to context menus --- src/components/animations/Animation.vue | 7 +++++-- src/components/frames/Frame.vue | 2 +- src/components/keyframe/Hitbox.vue | 7 +++++-- src/components/timeline/Keyframe.vue | 7 +++++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/animations/Animation.vue b/src/components/animations/Animation.vue index 5a4ce73..b7b61d1 100644 --- a/src/components/animations/Animation.vue +++ b/src/components/animations/Animation.vue @@ -18,7 +18,7 @@