Skip to content

Commit b61e8ce

Browse files
committed
Add fallback to internal clipboard when Arboard is disabled or fails to initiate
1 parent 76ca142 commit b61e8ce

File tree

3 files changed

+57
-16
lines changed

3 files changed

+57
-16
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
3737
selected entity from the level.
3838
- Copy/paste support for entities: Use `Ctrl+C` to copy and `Ctrl+V` to paste
3939
entities with all their components and values.
40-
- Cross-level entity copying: Entities can be copied between different level
40+
- Cross-editor entity copying: Entities can be copied between different level
4141
editors through system clipboard (opt in via the `arboard` feature)
42+
- If `arboard` is not enabled the copy/paste will not be cross-editor
4243
- Entity deletion via keyboard in addition to the existing UI button.
4344
- Auto-selection of pasted entities: Newly pasted entities are automatically
4445
selected for immediate editing.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ bevy_reflect = []
4646
vpeol = []
4747
vpeol_2d = ["vpeol", "bevy/bevy_text", "bevy/bevy_sprite", "bevy/png"]
4848
vpeol_3d = ["vpeol", "bevy/bevy_pbr"]
49-
# Support clipboard with the Arboard crate.
49+
# Support clipboard with the Arboard crate. Otherwise the clipboard will be internal.
5050
arboard = ["dep:arboard"]
5151
# Enable Wayland support in Arboard.
5252
arboard_wayland = ["arboard", "arboard/wayland-data-control"]

src/vpeol.rs

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ impl Plugin for VpeolBasePlugin {
7373
)
7474
.chain(), // .run_if(in_state(YoleckEditorState::EditorActive)),
7575
);
76+
app.init_resource::<VpeolClipboard>();
7677
app.add_systems(
7778
Update,
7879
(prepare_camera_state, update_camera_world_position)
@@ -757,11 +758,35 @@ fn handle_delete_entity_key(
757758
Ok(())
758759
}
759760

761+
#[derive(Resource)]
762+
enum VpeolClipboard {
763+
#[cfg(feature = "arboard")]
764+
Arboard(arboard::Clipboard),
765+
Internal(String),
766+
}
767+
768+
impl FromWorld for VpeolClipboard {
769+
fn from_world(_: &mut World) -> Self {
770+
#[cfg(feature = "arboard")]
771+
match arboard::Clipboard::new() {
772+
Ok(clipboard) => {
773+
debug!("Arboard clipbaord successfully initiated");
774+
return VpeolClipboard::Arboard(clipboard);
775+
}
776+
Err(err) => {
777+
warn!("Cannot initiate Arboard clipboard: {err}");
778+
}
779+
}
780+
VpeolClipboard::Internal(String::new())
781+
}
782+
}
783+
760784
fn handle_copy_entity_key(
761785
mut egui_context: EguiContexts,
762786
keyboard_input: Res<ButtonInput<KeyCode>>,
763787
query: Query<&YoleckManaged, With<YoleckEditMarker>>,
764788
construction_specs: Res<YoleckEntityConstructionSpecs>,
789+
mut clipboard: ResMut<VpeolClipboard>,
765790
) -> Result {
766791
if egui_context.ctx_mut()?.wants_keyboard_input() {
767792
return Ok(());
@@ -800,14 +825,17 @@ fn handle_copy_entity_key(
800825
})
801826
.collect();
802827

803-
if !entities.is_empty() {
804-
#[allow(unused_variables)] // TODO: try to remove when the arboard alternative gets in
805-
if let Ok(json) = serde_json::to_string(&entities) {
828+
if !entities.is_empty()
829+
&& let Ok(json) = serde_json::to_string(&entities)
830+
{
831+
match clipboard.as_mut() {
806832
#[cfg(feature = "arboard")]
807-
{
808-
let mut clipboard = arboard::Clipboard::new()?;
833+
VpeolClipboard::Arboard(clipboard) => {
809834
clipboard.set_text(json)?;
810835
}
836+
VpeolClipboard::Internal(clipboard) => {
837+
*clipboard = json;
838+
}
811839
}
812840
}
813841
}
@@ -822,6 +850,7 @@ fn handle_paste_entity_key(
822850
mut commands: Commands,
823851
mut writer: MessageWriter<YoleckEditorEvent>,
824852
query: Query<Entity, With<YoleckEditMarker>>,
853+
mut clipboard: ResMut<VpeolClipboard>,
825854
) -> Result {
826855
if egui_context.ctx_mut()?.wants_keyboard_input() {
827856
return Ok(());
@@ -831,19 +860,30 @@ fn handle_paste_entity_key(
831860
|| keyboard_input.pressed(KeyCode::ControlRight);
832861

833862
if ctrl_pressed && keyboard_input.just_pressed(KeyCode::KeyV) {
834-
#[allow(unused_variables)] // TODO: try to remove when the arboard alternative gets in
835-
let text_to_paste: Option<String> = None;
836-
837863
#[cfg(feature = "arboard")]
838-
let text_to_paste = {
839-
let mut clipboard = arboard::Clipboard::new()?;
840-
clipboard.get_text().ok()
864+
let arboard_text_storage: String;
865+
let text_to_paste: Option<&str> = match clipboard.as_mut() {
866+
#[cfg(feature = "arboard")]
867+
VpeolClipboard::Arboard(clipboard) => match clipboard.get_text() {
868+
Ok(text) => {
869+
arboard_text_storage = text;
870+
Some(&arboard_text_storage)
871+
}
872+
Err(err) => {
873+
error!("Cannot load text from arboard: {err}");
874+
None
875+
}
876+
},
877+
VpeolClipboard::Internal(clipboard) => {
878+
Some(clipboard.as_str()).filter(|txt| !txt.is_empty())
879+
}
841880
};
842881

843-
// TODO: add fallback when arboard is not enabled or doesn't work
844-
845882
if let Some(text) = text_to_paste
846-
&& let Ok(entities) = serde_json::from_str::<Vec<YoleckRawEntry>>(&text)
883+
&& let Ok(entities) =
884+
serde_json::from_str::<Vec<YoleckRawEntry>>(text).inspect_err(|err| {
885+
warn!("Cannot paste - failure to parse copied text: {err}");
886+
})
847887
&& !entities.is_empty()
848888
{
849889
for prev_selected in query.iter() {

0 commit comments

Comments
 (0)