use petgraph::visit::{IntoNodeReferences, NodeRef};
use std::collections::HashMap;
use steel::{
SteelErr, SteelVal,
rvals::{FromSteelVal, IntoSteelVal},
steel_vm::engine::Engine,
};
pub mod export;
mod impls;
pub mod node;
pub mod reg;
pub mod widget;
pub use node::{FnNodeNames, NameRegistry};
pub use reg::RegistryRef;
pub use widget::gantz::NodeTypeRegistry;
pub use widget::graph_select::GraphRegistry;
pub trait Registry: NameRegistry + FnNodeNames + NodeTypeRegistry + GraphRegistry {
fn node(&self, ca: &gantz_ca::ContentAddr) -> Option<&dyn gantz_core::Node>;
}
pub trait HeadAccess {
type Node;
fn heads(&self) -> &[gantz_ca::Head];
fn with_head_mut<R>(
&mut self,
head: &gantz_ca::Head,
f: impl FnOnce(HeadDataMut<'_, Self::Node>) -> R,
) -> Option<R>;
fn compiled_module(&self, head: &gantz_ca::Head) -> Option<&str>;
}
pub struct HeadDataMut<'a, N> {
pub graph: &'a mut gantz_core::node::graph::Graph<N>,
pub views: &'a mut GraphViews,
pub vm: &'a mut Engine,
}
pub type GraphViews = HashMap<Vec<node::Id>, egui_graph::View>;
pub trait NodeUi {
fn name(&self, _registry: &dyn Registry) -> &str;
fn ui(
&mut self,
ctx: NodeCtx,
uictx: egui_graph::NodeCtx,
) -> egui::InnerResponse<egui::Response>;
fn inspector_rows(&mut self, _ctx: &mut NodeCtx, _body: &mut egui_extras::TableBody) {}
fn inspector_ui(&mut self, _ctx: NodeCtx, _ui: &mut egui::Ui) -> Option<egui::Response> {
None
}
fn flow(&self, _registry: &dyn Registry) -> egui::Direction {
egui::Direction::TopDown
}
}
pub struct NodeCtx<'a> {
registry: &'a dyn Registry,
path: &'a [node::Id],
inlets: &'a [node::Id],
outlets: &'a [node::Id],
vm: &'a mut Engine,
cmds: &'a mut Vec<Cmd>,
}
#[derive(Debug)]
pub enum Cmd {
PushEval(Vec<node::Id>),
PullEval(Vec<node::Id>),
OpenGraph(Vec<node::Id>),
OpenNamedNode(String, gantz_ca::ContentAddr),
ForkNamedNode {
new_name: String,
ca: gantz_ca::ContentAddr,
},
InspectEdge(InspectEdge),
CreateNode(CreateNode),
CopySelection,
PasteClipboard {
text: Option<String>,
offset: egui::Vec2,
},
Undo,
Redo,
ExportHead,
ExportAllNamed,
}
#[derive(Clone, Debug)]
pub struct CreateNode {
pub path: Vec<node::Id>,
pub node_type: String,
}
#[derive(Clone, Debug)]
pub struct InspectEdge {
pub path: Vec<node::Id>,
pub edge: petgraph::graph::EdgeIndex<usize>,
pub pos: egui::Pos2,
}
impl<'a, N> NodeUi for &'a mut N
where
N: ?Sized + NodeUi,
{
fn name(&self, registry: &dyn Registry) -> &str {
(**self).name(registry)
}
fn ui(
&mut self,
ctx: NodeCtx,
uictx: egui_graph::NodeCtx,
) -> egui::InnerResponse<egui::Response> {
(**self).ui(ctx, uictx)
}
fn inspector_rows(&mut self, ctx: &mut NodeCtx, body: &mut egui_extras::TableBody) {
(**self).inspector_rows(ctx, body)
}
fn inspector_ui(&mut self, ctx: NodeCtx, ui: &mut egui::Ui) -> Option<egui::Response> {
(**self).inspector_ui(ctx, ui)
}
fn flow(&self, registry: &dyn Registry) -> egui::Direction {
(**self).flow(registry)
}
}
macro_rules! impl_node_ui_for_ptr {
($($Ty:ident)::*) => {
impl<T> NodeUi for $($Ty)::*<T>
where
T: ?Sized + NodeUi,
{
fn name(&self, registry: &dyn Registry) -> &str {
(**self).name(registry)
}
fn ui(&mut self, ctx: NodeCtx, uictx: egui_graph::NodeCtx) -> egui::InnerResponse<egui::Response> {
(**self).ui(ctx, uictx)
}
fn inspector_rows(&mut self, ctx: &mut NodeCtx, body: &mut egui_extras::TableBody) {
(**self).inspector_rows(ctx, body)
}
fn inspector_ui(&mut self, ctx: NodeCtx, ui: &mut egui::Ui) -> Option<egui::Response> {
(**self).inspector_ui(ctx, ui)
}
fn flow(&self, registry: &dyn Registry) -> egui::Direction {
(**self).flow(registry)
}
}
};
}
impl_node_ui_for_ptr!(Box);
impl<'a> NodeCtx<'a> {
pub fn new(
registry: &'a dyn Registry,
path: &'a [node::Id],
inlets: &'a [node::Id],
outlets: &'a [node::Id],
vm: &'a mut Engine,
cmds: &'a mut Vec<Cmd>,
) -> Self {
Self {
registry,
path,
inlets,
outlets,
vm,
cmds,
}
}
pub fn registry(&self) -> &dyn Registry {
self.registry
}
pub fn path(&self) -> &[node::Id] {
&self.path
}
pub fn vm(&self) -> &Engine {
&*self.vm
}
pub fn extract_value(&self) -> Result<Option<SteelVal>, SteelErr> {
node::state::extract_value(self.vm, self.path)
}
pub fn extract<T: FromSteelVal>(&self) -> Result<Option<T>, SteelErr> {
node::state::extract(self.vm, self.path)
}
pub fn update_value(&mut self, val: SteelVal) -> Result<(), SteelErr> {
node::state::update_value(self.vm, self.path, val)
}
pub fn update<T: IntoSteelVal>(&mut self, val: T) -> Result<(), SteelErr> {
node::state::update(self.vm, self.path, val)
}
pub fn push_eval(&mut self) {
self.cmds.push(Cmd::PushEval(self.path.to_vec()));
}
pub fn pull_eval(&mut self) {
self.cmds.push(Cmd::PullEval(self.path.to_vec()));
}
pub fn inlets(&self) -> &[node::Id] {
self.inlets
}
pub fn outlets(&self) -> &[node::Id] {
self.outlets
}
}
pub(crate) fn inlet_outlet_ids<N>(
registry: &dyn Registry,
g: &gantz_core::node::graph::Graph<N>,
) -> (Vec<node::Id>, Vec<node::Id>)
where
N: gantz_core::Node,
{
let get_node = |ca: &gantz_ca::ContentAddr| registry.node(ca);
let ctx = gantz_core::node::MetaCtx::new(&get_node);
let mut inlets = vec![];
let mut outlets = vec![];
for n_ref in g.node_references() {
if n_ref.weight().inlet(ctx) {
inlets.push(n_ref.id().index());
}
if n_ref.weight().outlet(ctx) {
outlets.push(n_ref.id().index());
}
}
(inlets, outlets)
}
fn system_time_from_web(t: web_time::SystemTime) -> Option<std::time::SystemTime> {
let duration = t.duration_since(web_time::UNIX_EPOCH).ok()?;
std::time::UNIX_EPOCH.checked_add(duration)
}
pub fn head_is_focused<'a>(
heads: impl IntoIterator<Item = &'a gantz_ca::Head>,
focused_head: usize,
head: &gantz_ca::Head,
) -> bool {
heads
.into_iter()
.position(|h| h == head)
.map(|ix| ix == focused_head)
.unwrap_or(false)
}