Files
ARCADIA-Studio/crates/oa-desktop/src/connection_edit.rs
T

145 lines
5.2 KiB
Rust

//! A palette operation is transient; only a completed connection enters history.
use crate::{connection_tool::Tool, route_edit::Context};
use oa_core::{Id, Project, projection::ProjectedView};
use std::collections::BTreeSet;
#[derive(Debug, Clone)]
pub enum Event {
Choose(Option<Tool>),
Begin(Tool, Id),
Endpoint(Context, Id),
Restart(Context),
}
#[derive(Debug, Clone)]
pub struct Session {
pub context: Context,
pub tool: Tool,
pub source: Option<Id>,
pub candidates: BTreeSet<Id>,
}
impl Session {
pub fn new(project: &Project, view: &ProjectedView, context: Context, tool: Tool) -> Self {
let candidates = visible(view)
.filter(|id| tool.can_start(project, *id))
.collect();
Self {
context,
tool,
source: None,
candidates,
}
}
pub fn start(&mut self, project: &Project, view: &ProjectedView, source: Id) {
self.source = Some(source);
let compatible = self.tool.targets(project, source);
self.candidates = visible(view).filter(|id| compatible.contains(id)).collect();
}
}
pub fn visible(view: &ProjectedView) -> impl Iterator<Item = Id> + '_ {
view.nodes
.iter()
.map(|n| n.element)
.chain(view.ports.iter().map(|p| p.element))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::app::{App, Message, Page};
use oa_core::*;
include!("connection_allocation_tests.rs");
fn fixture() -> (App, Id, Id, Id) {
let mut p = Project::empty();
let a = p.add(Kind::Function, "Send command", 0., 0.);
let b = p.add(Kind::Function, "Receive command", 400., 0.);
let hidden = p
.add_element(Kind::Function, Layer::Sa, "Hidden work", None)
.unwrap();
(App::with_project(p), a, b, hidden)
}
fn send(app: &mut App, event: Event) {
let _ = app.update(Message::Connection(event));
}
#[test]
fn visible_endpoint_drawing_commits_ports_and_exchange_once_with_exact_history() {
let (mut app, a, b, hidden) = fixture();
let before = app.document.bundle().clone();
let revision = app.document.revision();
send(&mut app, Event::Choose(Some(Tool::FunctionalExchange)));
assert!(
!app.connection
.as_ref()
.unwrap()
.candidates
.contains(&hidden)
);
let context = app.route_context().unwrap();
send(&mut app, Event::Endpoint(context, a));
send(&mut app, Event::Endpoint(context, hidden));
assert_eq!(app.document.revision(), revision);
assert_eq!(app.connection.as_ref().unwrap().source, Some(a));
send(&mut app, Event::Endpoint(context, b));
assert!(app.connection.is_none());
assert_eq!(app.document.revision(), revision + 1);
let relation = &app.project().relations[&app.selected];
assert_eq!(relation.kind(), RelationKind::FunctionalExchange);
assert_eq!((relation.source(), relation.target()), (a, b));
assert_eq!(
app.project()
.elements
.values()
.filter(|e| e.kind() == Kind::Port)
.count(),
2
);
let after = app.document.bundle().clone();
let _ = app.update(Message::Undo);
assert_eq!(*app.document.bundle(), before);
let _ = app.update(Message::Redo);
assert_eq!(*app.document.bundle(), after);
}
#[test]
fn cancel_navigation_revision_and_dirty_drafts_cannot_commit_a_stale_connection() {
let (mut app, a, b, _) = fixture();
send(&mut app, Event::Choose(Some(Tool::FunctionalExchange)));
let context = app.route_context().unwrap();
send(&mut app, Event::Endpoint(context, a));
let _ = app.update(Message::Escape);
send(&mut app, Event::Endpoint(context, b));
assert!(app.project().relations.is_empty());
send(&mut app, Event::Choose(Some(Tool::FunctionalExchange)));
let current = app.route_context().unwrap();
send(&mut app, Event::Endpoint(current, a));
send(&mut app, Event::Endpoint(context, b));
assert_eq!(
app.connection.as_ref().unwrap().source,
Some(a),
"A stale click must leave the newer drawing session intact"
);
let revision = app.document.revision();
let _ = app.update(Message::Route(crate::route_edit::Event::AddBend));
assert_eq!(app.document.revision(), revision);
assert!(app.connection.is_some());
let _ = app.update(Message::Page(Page::Catalog));
assert!(app.connection.is_none());
let _ = app.update(Message::Page(Page::Diagram));
send(&mut app, Event::Choose(Some(Tool::FunctionalExchange)));
send(&mut app, Event::Endpoint(context, b));
assert!(app.project().relations.is_empty());
let _ = app.update(Message::Select(a));
let _ = app.update(Message::Field("/name".into(), "Pending rename".into()));
assert!(app.draft_dirty());
send(&mut app, Event::Choose(Some(Tool::FunctionalExchange)));
assert!(app.connection.is_none());
assert_eq!(app.project().named(a), "Send command");
}
}