use crate::fixture_editor::state::EditorState;
use gled_model::patching::Patch;
use gled_model::patching::fixture::{Fixture, FixtureId, LayoutPosition};
use gled_model::patching::layout::LayoutRef;
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
pub(crate) struct ClipboardFixtures {
pub(crate) gled_fixtures: Vec<(Fixture, LayoutPosition)>,
#[serde(default)]
pub(crate) edges: Vec<(usize, usize)>,
}
impl ClipboardFixtures {
pub(crate) fn is_empty(&self) -> bool {
self.gled_fixtures.is_empty()
}
}
pub(crate) fn clipboard_text(patch: &Patch, state: &EditorState) -> Option<String> {
let clipboard = selected_fixtures(patch, state);
if clipboard.is_empty() {
return None;
}
serde_json::to_string(&clipboard).ok()
}
pub(crate) fn fixtures_from_clipboard(text: &str) -> Option<ClipboardFixtures> {
serde_json::from_str::<ClipboardFixtures>(text)
.ok()
.filter(|clipboard| !clipboard.is_empty())
}
fn selected_fixtures(patch: &Patch, state: &EditorState) -> ClipboardFixtures {
if state.selection != LayoutRef::Topology {
return ClipboardFixtures::default();
}
let ids: Vec<FixtureId> = patch
.topology
.fixtures
.keys()
.copied()
.filter(|id| state.selected_fixtures.contains(id))
.collect();
let gled_fixtures: Vec<(Fixture, LayoutPosition)> = ids
.iter()
.filter_map(|id| {
let fixture = patch.fixture(*id)?.clone();
let position = patch.topology.default_layout.members.get(id).copied()?;
Some((fixture, position))
})
.collect();
if gled_fixtures.len() != ids.len() {
return ClipboardFixtures::default();
}
let edges = ids
.iter()
.enumerate()
.filter_map(|(parent, id)| {
let child = patch.topology.successor(*id)?;
let child = ids.iter().position(|other| *other == child)?;
Some((parent, child))
})
.collect();
ClipboardFixtures {
gled_fixtures,
edges,
}
}
pub(super) fn copy_selected(patch: &Patch, state: &mut EditorState) {
if state.selection != LayoutRef::Topology {
return;
}
state.clipboard = selected_fixtures(patch, state);
}
#[cfg(test)]
mod tests {
use super::*;
use egui::pos2;
#[test]
fn fixtures_survive_the_clipboard_as_text() {
let mut patch = Patch::default();
let mut state = EditorState::default();
let id = patch.add_fixture(Fixture::new("bar"), LayoutPosition::at(pos2(10.0, 20.0)));
state.select_single(id);
let text = clipboard_text(&patch, &state).expect("something to copy");
let restored = fixtures_from_clipboard(&text).expect("our own copy");
assert_eq!(restored.gled_fixtures.len(), 1);
assert_eq!(restored.gled_fixtures[0].0.name, "bar");
assert_eq!(restored.gled_fixtures[0].1.pos, pos2(10.0, 20.0));
}
#[test]
fn foreign_clipboard_text_is_not_ours() {
assert!(fixtures_from_clipboard("hello").is_none());
assert!(fixtures_from_clipboard("{\"gled_fixtures\":[]}").is_none());
}
}