From 4ae7e13a532c1fdf05c4b810376bb5a72901f3b8 Mon Sep 17 00:00:00 2001 From: Julian Antonielli Date: Sun, 12 Feb 2023 23:57:50 +0000 Subject: [PATCH 1/2] Start using the system clipboard for copy/paste --- src/runty8-core/src/sprite_sheet.rs | 13 +++++++++++++ src/runty8-editor/Cargo.toml | 1 + src/runty8-editor/src/editor.rs | 11 +++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/runty8-core/src/sprite_sheet.rs b/src/runty8-core/src/sprite_sheet.rs index 958bcd3f..624c2876 100644 --- a/src/runty8-core/src/sprite_sheet.rs +++ b/src/runty8-core/src/sprite_sheet.rs @@ -195,6 +195,14 @@ impl Sprite { } } } + + pub fn to_clipboard_string(&self) -> String { + let width = Self::WIDTH; + let height = Self::HEIGHT; + let pixel_data = vec![]; + + format!("[gfx]{width:x}{height}{pixel_data}[/gfx]") + } } #[cfg(test)] @@ -209,4 +217,9 @@ mod tests { assert_eq!(SpriteSheet::to_linear_index(8, 1), 64 + 8); assert_eq!(SpriteSheet::to_linear_index(1, 9), 1033); } + + #[test] + fn clipboard_works() { + let sprite = Sprite::new(&[15, 15, 15, 15]); + } } diff --git a/src/runty8-editor/Cargo.toml b/src/runty8-editor/Cargo.toml index ae97d454..8793525f 100644 --- a/src/runty8-editor/Cargo.toml +++ b/src/runty8-editor/Cargo.toml @@ -11,6 +11,7 @@ itertools = "0.10" instant = "0.1" once_cell = "1.16" log = "0.4" +arboard = "3.2.0" [target.'cfg(target_arch = "wasm32")'.dependencies] web-sys = { version = "0.3", features = ["Window", "Storage"] } diff --git a/src/runty8-editor/src/editor.rs b/src/runty8-editor/src/editor.rs index ae8a673b..58172baa 100644 --- a/src/runty8-editor/src/editor.rs +++ b/src/runty8-editor/src/editor.rs @@ -16,6 +16,7 @@ use crate::ui::{ }; use crate::ui::{DrawFn, Element, Tree}; use brush_size::BrushSize; +use once_cell::sync::Lazy; use runty8_core::InputEvent; use runty8_core::{ serialize::Serialize, Color, Event, Flags, Key, KeyState, KeyboardEvent, Map, Resources, @@ -25,6 +26,10 @@ use runty8_core::{ use self::key_combo::KeyCombos; use self::top_bar::TopBar; use self::undo_redo::{Command, Commands}; +use std::sync::Mutex; + +static CLIPBOARD: Lazy> = + Lazy::new(|| Mutex::new(arboard::Clipboard::new().unwrap())); #[derive(Debug)] pub(crate) struct Editor { @@ -134,6 +139,12 @@ impl Clipboard { } fn copy_sprite(&mut self, sprite: &Sprite) { + CLIPBOARD + .lock() + .unwrap() + .set_text(format!("[gfx]0202ffff[/gfx]")) + .unwrap(); + self.data = sprite.to_owned(); } From c9a4bd0719775b9237a1a39c464d1d42becbf9be Mon Sep 17 00:00:00 2001 From: Julian Antonielli Date: Fri, 24 Feb 2023 22:23:34 +0000 Subject: [PATCH 2/2] Add `Sprite::from_clipboard` --- src/runty8-core/src/sprite_sheet.rs | 86 ++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/src/runty8-core/src/sprite_sheet.rs b/src/runty8-core/src/sprite_sheet.rs index 624c2876..5f132f85 100644 --- a/src/runty8-core/src/sprite_sheet.rs +++ b/src/runty8-core/src/sprite_sheet.rs @@ -199,9 +199,67 @@ impl Sprite { pub fn to_clipboard_string(&self) -> String { let width = Self::WIDTH; let height = Self::HEIGHT; - let pixel_data = vec![]; + let pixel_data = self.to_owned(); - format!("[gfx]{width:x}{height}{pixel_data}[/gfx]") + let mut str = String::new(); + for pixel in pixel_data { + str.push(char::from_digit(pixel as u32, 16).unwrap()); + } + + // TODO: Google how to pad width/height to 2 characters with zeros. + format!("[gfx]0{width:x}0{height:x}{str}[/gfx]") + } + + pub fn from_clipboard(str: &str) -> Result, String> { + let str = chomp(str, "[gfx]")?; + let (width, str) = chomp_int(str, 2)?; + let (height, str) = chomp_int(str, 2)?; + let mut data = Vec::with_capacity(width as usize * height as usize); + + let data_size = { + let expected_data_size = (width * height) as usize; + let actual_data_size = str.len() as i64 - "[/gfx]".len() as i64; + + if expected_data_size as i64 == actual_data_size { + Ok(actual_data_size as usize) + } else { + let actual_len = str.len() - "[/gfx]".len(); + + Err(format!( + "Expected data section to be {expected_data_size} long, but it is {actual_len}", + )) + } + }?; + + let (data_str, str) = str.split_at(data_size); + + for char in data_str.chars() { + let digit = char + .to_digit(16) + .ok_or_else(|| format!("{char} is not a hex digit"))? as u8; + data.push(digit); + } + let _ = chomp(str, "[/gfx]")?; + + Ok(data) + } +} + +fn chomp<'a>(source: &'a str, chomped: &str) -> Result<&'a str, String> { + if source.starts_with(chomped) { + Ok(&source[chomped.len()..]) + } else { + Err(format!("\"{source}\" doesn't start with \"{chomped}\"")) + } +} + +fn chomp_int(source: &str, len: usize) -> Result<(i32, &str), String> { + let (int_str, next) = source.split_at(len); + + if let Ok(int) = int_str.parse() { + Ok((int, next)) + } else { + Err(format!("Couldn't parse int: {int_str}")) } } @@ -220,6 +278,28 @@ mod tests { #[test] fn clipboard_works() { - let sprite = Sprite::new(&[15, 15, 15, 15]); + let str_sprite = + "[gfx]0808fff00000fff00000fff000000000000000000000000000000000000000000000[/gfx]"; + + let mut arr = [0; Sprite::WIDTH * Sprite::HEIGHT]; + let sprite = { Sprite::new_mut(&mut arr) }; + + for (x, y) in (0..3).cartesian_product(0..3) { + sprite.set(x + y * Sprite::WIDTH, 15); + } + + assert_eq!(&sprite.to_clipboard_string(), str_sprite); + assert_eq!( + Sprite::from_clipboard(str_sprite).unwrap(), + sprite.to_owned() + ); + } + + #[test] + fn from_clipboard_with_wrong_size() { + let str_sprite = "[gfx]0808ff00[/gfx]"; + + let actual = Sprite::from_clipboard(str_sprite); + assert!(actual.is_err()); } }