//! The data: documents, nodes, links and how they look. No UI and no files //! here, just the rules. //! //! - A [`Document`] owns a tree of [`Node`]s under one root, loose nodes //! beside it, and cross-[`Link`]s, and keeps its rules (one root, no //! cycles, links between nodes that exist). //! - Every change goes through [`Document::edit`], which returns a //! [`Change`] that [`History`] can undo and redo. //! - Text from outside ([`Title`], [`Tag`]…) is checked once, when it's //! turned into these types. //! - [`Visible`] works out which nodes folding leaves on show. mod clip; mod document; mod edit; mod fold; mod geometry; mod history; mod ids; mod link; mod look; mod node; mod op; mod portal; mod text; pub use clip::{Clip, ClipNode}; pub use document::{Document, NodeRecord, Place, StructureError}; pub use edit::{Change, Edit, EditError, Spot}; pub use fold::Visible; pub use geometry::{CanvasOffset, CanvasPos, CanvasRect, CanvasSize, Direction, Layout, Side}; pub use history::{Group, History, StateId}; pub use ids::{DocUid, NodeId}; pub use link::{LineRef, Link, LinkKey}; pub use look::{Arrows, Border, LineColor, LineLook, NodeLook, Stroke}; pub use node::{Content, Fold, Idea, Node}; pub use op::{Applied, EditOp, Inbox}; pub use portal::{Backlink, EmptyPath, LinkPath, Portal}; pub use text::{Body, ColorName, Label, StyleName, Tag, Tags, TextError, Title}; /// Small documents and helpers shared by the tests. #[cfg(test)] pub(crate) mod test_support { use super::*; pub fn title(text: &str) -> Title { Title::parse(text).expect("a valid title") } pub fn idea(text: &str) -> Node { Node::idea(title(text), CanvasPos::default()) } /// The ids in [`garden`]. pub struct Garden { pub root: NodeId, pub soil: NodeId, pub beds: NodeId, pub cedar: NodeId, pub watering: NodeId, pub compost: NodeId, } /// The document from the design mockup: /// /// ```text /// Garden plans ─┬─ Soil /// ├─ Raised beds ── Cedar /// └─ Watering /// ask about compost (loose) /// ``` pub fn garden() -> (Document, Garden) { let mut doc = Document::with_uid(title("Garden plans"), DocUid::parse("7f3c9a1e52b04d18").unwrap()); let root = doc.root(); let at = |text: &str, x: i32, y: i32| Node::idea(title(text), CanvasPos::new(x, y)); let (ids, _) = doc .edit(|e| { e.update(root, |node| node.pos = CanvasPos::new(0, 4))?; let soil = e.add(at("Soil", 22, 0), Spot::LastChildOf(root))?; let beds = e.add(at("Raised beds", 22, 4), Spot::LastChildOf(root))?; let cedar = e.add(at("Cedar", 40, 4), Spot::LastChildOf(beds))?; let watering = e.add(at("Watering", 22, 8), Spot::LastChildOf(root))?; let compost = e.add(at("ask about compost", 0, 14), Spot::Loose)?; Ok(Garden { root, soil, beds, cedar, watering, compost }) }) .expect("the garden builds"); (doc, ids) } /// Adds a portal to a "Kitchen" document under `parent`. pub fn portal(doc: &mut Document, parent: NodeId) -> NodeId { let portal = Portal { path: LinkPath::parse("kitchen.cum").expect("a path"), uid: DocUid::parse("c01d5e7aa0f3b912").expect("a uid"), title: title("Kitchen"), }; let node = Node::portal(portal, CanvasPos::new(22, 12)); doc.edit(|e| e.add(node, Spot::LastChildOf(parent))).expect("the portal goes in").0 } }