tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
⎇ 92dd0e5bfb2ca4456006da99437f4100e156d856
widgets/nodegraph/examples/nodegraph_demo.rs
Historia
Quién tocó qué
crudo
feat: llimphi standalone — framework UI soberano extraído del monorepo
1//! Showcase de `llimphi-widget-nodegraph`. Cuatro nodos pre-conectados
2//! representando una cadena de audio (`Source → Filter → Mixer →
3//! Output`) y un `LFO` huérfano para que el usuario lo conecte
4//! arrastrando desde su pin de salida hasta el `mod` del filtro.
5//!
6//! - Arrastrá la title bar de cualquier nodo para moverlo.
7//! - Arrastrá desde un pin de salida (lado derecho) y soltá sobre un
8//! pin de entrada (lado izquierdo) de otro nodo para conectar.
9//!
10//! Corré con: `cargo run -p llimphi-widget-nodegraph --example
11//! nodegraph_demo --release`.
12
13use llimphi_theme::Theme;
14use llimphi_ui::{App, DragPhase, Handle, View};
15use llimphi_widget_nodegraph::{
16 nodegraph_view, NodeId, NodeSpec, NodegraphMetrics, NodegraphPalette, PinIdx, Wire,
17};
18
19#[derive(Clone)]
20enum Msg {
21 DragNode {
22 id: NodeId,
23 // El demo no diferencia Move/End; lo dejamos en el Msg por si
24 // un caller real quiere persistir layout solo en End.
25 #[allow(dead_code)]
26 phase: DragPhase,
27 dx: f32,
28 dy: f32,
29 },
30 Connect {
31 from_node: NodeId,
32 from_pin: PinIdx,
33 to_node: NodeId,
34 to_pin: PinIdx,
35 },
36}
37
38struct Model {
39 nodes: Vec<NodeSpec>,
40 wires: Vec<Wire>,
41}
42
43const ID_SOURCE: NodeId = 1;
44const ID_FILTER: NodeId = 2;
45const ID_MIXER: NodeId = 3;
46const ID_OUTPUT: NodeId = 4;
47const ID_LFO: NodeId = 5;
48
49struct Showcase;
50
51impl App for Showcase {
52 type Model = Model;
53 type Msg = Msg;
54
55 fn title() -> &'static str {
56 "llimphi · nodegraph showcase (drag títulos, arrastrá pin → pin)"
57 }
58
59 fn initial_size() -> (u32, u32) {
60 (1100, 720)
61 }
62
63 fn init(_: &Handle<Msg>) -> Model {
64 Model {
65 nodes: vec![
66 NodeSpec {
67 id: ID_SOURCE,
68 label: "Source".into(),
69 x: 60.0,
70 y: 80.0,
71 inputs: vec![],
72 outputs: vec!["out".into()],
73 },
74 NodeSpec {
75 id: ID_FILTER,
76 label: "Filter".into(),
77 x: 290.0,
78 y: 80.0,
79 inputs: vec!["in".into(), "mod".into()],
80 outputs: vec!["out".into()],
81 },
82 NodeSpec {
83 id: ID_MIXER,
84 label: "Mixer".into(),
85 x: 520.0,
86 y: 80.0,
87 inputs: vec!["a".into(), "b".into()],
88 outputs: vec!["out".into()],
89 },
90 NodeSpec {
91 id: ID_OUTPUT,
92 label: "Output".into(),
93 x: 750.0,
94 y: 80.0,
95 inputs: vec!["in".into()],
96 outputs: vec![],
97 },
98 NodeSpec {
99 id: ID_LFO,
100 label: "LFO".into(),
101 x: 290.0,
102 y: 260.0,
103 inputs: vec![],
104 outputs: vec!["out".into()],
105 },
106 ],
107 wires: vec![
108 Wire {
109 from_node: ID_SOURCE,
110 from_output: 0,
111 to_node: ID_FILTER,
112 to_input: 0,
113 },
114 Wire {
115 from_node: ID_FILTER,
116 from_output: 0,
117 to_node: ID_MIXER,
118 to_input: 0,
119 },
120 Wire {
121 from_node: ID_MIXER,
122 from_output: 0,
123 to_node: ID_OUTPUT,
124 to_input: 0,
125 },
126 ],
127 }
128 }
129
130 fn update(model: Model, msg: Msg, _: &Handle<Msg>) -> Model {
131 let mut m = model;
132 match msg {
133 Msg::DragNode { id, phase: _, dx, dy } => {
134 if let Some(n) = m.nodes.iter_mut().find(|n| n.id == id) {
135 n.x += dx;
136 n.y += dy;
137 if n.x < 0.0 {
138 n.x = 0.0;
139 }
140 if n.y < 0.0 {
141 n.y = 0.0;
142 }
143 }
144 }
145 Msg::Connect {
146 from_node,
147 from_pin,
148 to_node,
149 to_pin,
150 } => {
151 if from_node == to_node {
152 return m;
153 }
154 let exists = m.wires.iter().any(|w| {
155 w.from_node == from_node
156 && w.from_output == from_pin
157 && w.to_node == to_node
158 && w.to_input == to_pin
159 });
160 if !exists {
161 m.wires.push(Wire {
162 from_node,
163 from_output: from_pin,
164 to_node,
165 to_input: to_pin,
166 });
167 }
168 }
169 }
170 m
171 }
172
173 fn view(model: &Model) -> View<Msg> {
174 let theme = Theme::dark();
175 let palette = NodegraphPalette::from_theme(&theme);
176 let metrics = NodegraphMetrics::default();
177 nodegraph_view(
178 &model.nodes,
179 &model.wires,
180 &palette,
181 &metrics,
182 |id, phase, dx, dy| Some(Msg::DragNode { id, phase, dx, dy }),
183 |from_node, from_pin, to_node, to_pin| {
184 Some(Msg::Connect {
185 from_node,
186 from_pin,
187 to_node,
188 to_pin,
189 })
190 },
191 )
192 }
193}
194
195fn main() {
196 llimphi_ui::run::<Showcase>();
197}