Skip to content

Commit f83eac4

Browse files
committed
test: add simple plugin example
1 parent 6168259 commit f83eac4

1 file changed

Lines changed: 142 additions & 0 deletions

File tree

crates/core/examples/plugin.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
use ferrum_flow::*;
2+
use gpui::{
3+
AnyElement, AppContext as _, Application, Element as _, ParentElement as _, Styled,
4+
WindowOptions, div, px, rgb, white,
5+
};
6+
use serde_json::json;
7+
8+
/// A beginner-friendly custom plugin example.
9+
///
10+
/// Run with:
11+
/// `cargo run -p ferrum-flow --example plugin`
12+
fn main() {
13+
Application::new().run(|cx| {
14+
let mut graph = Graph::new();
15+
16+
graph
17+
.create_node("default")
18+
.position(120.0, 120.0)
19+
.input()
20+
.output()
21+
.data(json!({ "label": "Base Node" }))
22+
.build();
23+
24+
cx.open_window(WindowOptions::default(), |window, cx| {
25+
cx.new(|ctx| {
26+
FlowCanvas::builder(graph, ctx, window)
27+
.default_plugins()
28+
.plugin(StarterPlugin::new())
29+
.plugin(ToastPlugin::new())
30+
.build()
31+
})
32+
})
33+
.unwrap();
34+
});
35+
}
36+
37+
/// A tiny plugin that demonstrates:
38+
/// 1) plugin state
39+
/// 2) input handling
40+
/// 3) custom overlay rendering
41+
/// 4) mutating graph data through `PluginContext`
42+
struct StarterPlugin {
43+
next_index: usize,
44+
clicks: usize,
45+
show_hud: bool,
46+
}
47+
48+
impl StarterPlugin {
49+
fn new() -> Self {
50+
Self {
51+
next_index: 1,
52+
clicks: 0,
53+
show_hud: true,
54+
}
55+
}
56+
57+
fn add_demo_node(&mut self, ctx: &mut PluginContext) {
58+
let i = self.next_index;
59+
self.next_index += 1;
60+
61+
let x = 120.0 + ((i % 6) as f32) * 180.0;
62+
let y = 280.0 + ((i / 6) as f32) * 140.0;
63+
64+
ctx.create_node("default")
65+
.position(x, y)
66+
.input()
67+
.output()
68+
.data(json!({ "label": format!("Plugin Node {i}") }))
69+
.build();
70+
71+
ctx.emit(FlowEvent::custom(ToastMessage::success(format!(
72+
"Created node #{i} from StarterPlugin"
73+
))));
74+
}
75+
}
76+
77+
impl Plugin for StarterPlugin {
78+
fn name(&self) -> &'static str {
79+
"starter_plugin"
80+
}
81+
82+
fn setup(&mut self, _ctx: &mut InitPluginContext) {
83+
// Put one initial node index behind the first generated node label.
84+
self.next_index = 1;
85+
}
86+
87+
fn on_event(&mut self, event: &FlowEvent, ctx: &mut PluginContext) -> EventResult {
88+
if let FlowEvent::Input(InputEvent::KeyDown(ev)) = event {
89+
if ev.keystroke.key == "n" {
90+
self.add_demo_node(ctx);
91+
return EventResult::Stop;
92+
}
93+
if ev.keystroke.key == "h" {
94+
self.show_hud = !self.show_hud;
95+
ctx.notify();
96+
return EventResult::Stop;
97+
}
98+
}
99+
100+
if let FlowEvent::Input(InputEvent::MouseDown(_)) = event {
101+
self.clicks += 1;
102+
ctx.notify();
103+
}
104+
105+
EventResult::Continue
106+
}
107+
108+
fn render(&mut self, _ctx: &mut RenderContext) -> Option<AnyElement> {
109+
if !self.show_hud {
110+
return None;
111+
}
112+
113+
Some(
114+
div()
115+
.absolute()
116+
.left(px(12.0))
117+
.top(px(12.0))
118+
.px_3()
119+
.py_2()
120+
.rounded(px(8.0))
121+
.bg(rgb(0x001F2937))
122+
.text_color(white())
123+
.child(div().text_sm().child("StarterPlugin (custom example)"))
124+
.child(
125+
div()
126+
.text_sm()
127+
.child(format!("Mouse clicks: {}", self.clicks)),
128+
)
129+
.child(div().text_sm().child("Press N: create node"))
130+
.child(div().text_sm().child("Press H: hide/show this panel"))
131+
.into_any(),
132+
)
133+
}
134+
135+
fn priority(&self) -> i32 {
136+
120
137+
}
138+
139+
fn render_layer(&self) -> RenderLayer {
140+
RenderLayer::Overlay
141+
}
142+
}

0 commit comments

Comments
 (0)