add support for system clipboard copy and paste
This commit is contained in:
parent
fc1670f97d
commit
57512a4c6b
5 changed files with 280 additions and 26 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -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
243
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.);
|
||||
|
||||
|
|
|
@ -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 => {
|
||||
|
|
Loading…
Add table
Reference in a new issue