add support for system clipboard copy and paste

This commit is contained in:
Crispy 2025-03-29 19:44:44 +01:00
parent fc1670f97d
commit 57512a4c6b
5 changed files with 280 additions and 26 deletions

View file

@ -3,18 +3,23 @@ Game store page: https://crispypin.itch.io/marble-machinations
## [Unreleased]
### added
- OS clipboard copy/paste, with fallback to old behavior when copying
- in-grid text comments (not yet editable in-game)
- changelog file
- (dev) sub-tick visualisation in debug mode
- (dev) tests and benchmarks
### fixed
- equal comparator did not output one of two incoming signals in some cases, depending on wire length and update order
### changed
- changed: comparators can now power other tiles without a wire between, including other comparators
- changed: directly moving marbles (to adjactent tile without anything between) now have priority over new marbles being created, instead of the two events cancelling each other
- made early levels (1-5) easier and more tutorial-like
- comparators can now power other tiles without a wire between, including other comparators
- directly moving marbles (to adjactent tile without anything between) now have priority over new marbles being created, instead of the two events cancelling each other
## v0.2.1 - 2025-03-14
- added: "Simple comparison" level
- fixed: phantom marble (empty tile causing other marbles to bounce away) appearing after multiple machines tried to output to the same location at once
### added
- "Simple comparison" level
### fixed
- phantom marble (empty tile causing other marbles to bounce away) appearing after multiple machines tried to output to the same location at once
## v0.2.0 - 2024-12-24
*everything else*

243
Cargo.lock generated
View file

@ -17,6 +17,21 @@ dependencies = [
"memchr",
]
[[package]]
name = "arboard"
version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4"
dependencies = [
"clipboard-win",
"log",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"parking_lot",
"x11rb",
]
[[package]]
name = "arrayvec"
version = "0.5.2"
@ -58,6 +73,15 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "block2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
dependencies = [
"objc2",
]
[[package]]
name = "cc"
version = "1.1.24"
@ -99,6 +123,15 @@ dependencies = [
"libloading",
]
[[package]]
name = "clipboard-win"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
dependencies = [
"error-code",
]
[[package]]
name = "cmake"
version = "0.1.51"
@ -146,12 +179,28 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "error-code"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
[[package]]
name = "fs_extra"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]]
name = "glob"
version = "0.3.1"
@ -223,7 +272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if 1.0.0",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@ -252,6 +301,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
name = "marble-machinations"
version = "0.2.1"
dependencies = [
"arboard",
"raylib",
"serde",
"serde_json",
@ -291,6 +341,105 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "objc-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
[[package]]
name = "objc2"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
dependencies = [
"objc-sys",
"objc2-encode",
]
[[package]]
name = "objc2-app-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
"bitflags",
"block2",
"libc",
"objc2",
"objc2-core-data",
"objc2-core-image",
"objc2-foundation",
"objc2-quartz-core",
]
[[package]]
name = "objc2-core-data"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
"bitflags",
"block2",
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-core-image"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
dependencies = [
"block2",
"objc2",
"objc2-foundation",
"objc2-metal",
]
[[package]]
name = "objc2-encode"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc2-foundation"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags",
"block2",
"libc",
"objc2",
]
[[package]]
name = "objc2-metal"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [
"bitflags",
"block2",
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-quartz-core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [
"bitflags",
"block2",
"objc2",
"objc2-foundation",
"objc2-metal",
]
[[package]]
name = "once_cell"
version = "1.20.1"
@ -320,7 +469,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
@ -603,7 +752,22 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
@ -612,28 +776,46 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -646,26 +828,67 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "x11rb"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12"
dependencies = [
"gethostname",
"rustix",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"

View file

@ -5,6 +5,7 @@ edition = "2021"
default-run = "marble-machinations"
[dependencies]
arboard = { version = "3.4.1", default-features = false }
raylib = "5.0.2"
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"

View file

@ -5,6 +5,7 @@ use std::{
time::Instant,
};
use arboard::Clipboard;
use raylib::prelude::*;
use crate::{
@ -449,7 +450,7 @@ impl Editor {
self.push_action(Action::SetTile(resize, pos, old_tile, tile));
}
pub fn update(&mut self, rl: &RaylibHandle) {
pub fn update(&mut self, rl: &RaylibHandle, clipboard: Option<&mut Clipboard>) {
self.tooltip.init_frame(rl);
self.mouse = MouseInput::get(rl);
if self.popup != Popup::None {
@ -550,12 +551,15 @@ impl Editor {
if self.sim_state == SimState::Editing {
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::from_user_str(&text);
self.pasting_board = Some(b);
if let Some(clipboard) = clipboard {
if rl.is_key_pressed(KeyboardKey::KEY_V) {
if let Ok(text) = clipboard.get_text() {
let b = Board::from_user_str(&text);
self.pasting_board = Some(b);
}
}
} else if rl.is_key_pressed(KeyboardKey::KEY_Z) {
}
if rl.is_key_pressed(KeyboardKey::KEY_Z) {
self.undo();
} else if rl.is_key_pressed(KeyboardKey::KEY_Y) {
self.redo();
@ -593,7 +597,12 @@ impl Editor {
}
}
pub fn draw(&mut self, d: &mut RaylibDrawHandle, textures: &Textures) {
pub fn draw(
&mut self,
d: &mut RaylibDrawHandle,
textures: &Textures,
clipboard: Option<&mut Clipboard>,
) {
d.clear_background(BG_WORLD);
if self.draw_overlay && self.zoom >= 0.5 {
@ -612,7 +621,7 @@ impl Editor {
self.draw_board(d, textures);
self.board_overlay(d, textures);
self.draw_bottom_bar(d, textures);
self.draw_bottom_bar(d, textures, clipboard);
self.draw_top_bar(d, textures);
if self.active_tool == Tool::Blueprint {
@ -986,7 +995,12 @@ impl Editor {
}
}
fn draw_bottom_bar(&mut self, d: &mut RaylibDrawHandle, textures: &Textures) {
fn draw_bottom_bar(
&mut self,
d: &mut RaylibDrawHandle,
textures: &Textures,
clipboard: Option<&mut Clipboard>,
) {
let height = d.get_screen_height();
let footer_top = (height - FOOTER_HEIGHT) as f32;
// background
@ -1036,7 +1050,13 @@ impl Editor {
&& d.is_key_down(KeyboardKey::KEY_LEFT_CONTROL))
{
let board = self.get_selected_as_board(selection);
self.pasting_board = Some(board);
if let Some(clipboard) = clipboard {
clipboard
.set_text(serde_json::to_string(&board).unwrap())
.unwrap();
} else {
self.pasting_board = Some(board);
}
}
draw_scaled_texture(d, textures.get("copy"), 192, y + 4, 2.);

View file

@ -3,6 +3,7 @@ use std::{
fs::{read_dir, read_to_string},
};
use arboard::Clipboard;
use raylib::prelude::*;
use marble_machinations::*;
@ -27,6 +28,7 @@ struct Game {
delete_solution: Option<usize>,
editing_solution_name: bool,
level_desc_text: ShapedText,
clipboard: Option<Clipboard>,
}
#[derive(Debug)]
@ -68,6 +70,9 @@ impl Game {
delete_solution: None,
editing_solution_name: false,
level_desc_text: ShapedText::new(20),
clipboard: Clipboard::new()
.map_err(|e| eprintln!("System clipboard error: {e}"))
.ok(),
}
}
@ -75,8 +80,8 @@ impl Game {
while !rl.window_should_close() {
let mut d = rl.begin_drawing(thread);
if let Some(editor) = &mut self.open_editor {
editor.update(&d);
editor.draw(&mut d, &self.textures);
editor.update(&d, self.clipboard.as_mut());
editor.draw(&mut d, &self.textures, self.clipboard.as_mut());
match editor.get_exit_state() {
ExitState::Dont => (),
ExitState::ExitAndSave => {