1pub mod codegen;
2pub mod css;
3pub mod events;
4pub mod html;
5pub mod js;
6pub mod layout;
7pub mod render;
8pub mod scene;
9pub mod text;
10
11#[cfg(test)]
12mod test_gpu;
13
14use std::sync::Arc;
15use winit::application::ApplicationHandler;
16use winit::event::{ElementState, MouseButton, WindowEvent};
17use winit::event_loop::{ActiveEventLoop, EventLoop};
18use winit::window::{Window, WindowAttributes, WindowId};
19
20pub fn run_embedded(html: &str, title: &str) {
22 env_logger::init();
23
24 let (scene_graph, scripts) = parse_embedded(html);
25
26 println!("foundry: {} nodes", scene_graph.node_count());
27
28 let event_loop = EventLoop::new().expect("failed to create event loop");
29 let mut app = EmbeddedApp {
30 title: title.to_string(),
31 window: None,
32 renderer: None,
33 text_engine: None,
34 scene: scene_graph,
35 scripts,
36 layout_engine: layout::LayoutEngine::new(),
37 event_system: events::EventSystem::new(),
38 js_engine: None,
39 needs_relayout: true,
40 scripts_executed: false,
41 cursor_pos: (0.0, 0.0),
42 };
43
44 event_loop.run_app(&mut app).expect("event loop failed");
45}
46
47fn parse_embedded(html_source: &str) -> (scene::SceneGraph, Vec<String>) {
48 let (mut scene_graph, inline_styles, inline_scripts, _, _) = html::parse_html_full(html_source);
49
50 for style_text in &inline_styles {
51 let (rules, kfs) = css::parse_stylesheet_with_keyframes(style_text);
52 scene_graph.keyframes.extend(kfs);
53 css::apply_styles(&mut scene_graph, &rules);
54 }
55
56 (scene_graph, inline_scripts)
57}
58
59struct EmbeddedApp {
60 title: String,
61 window: Option<Arc<Window>>,
62 renderer: Option<render::Renderer>,
63 text_engine: Option<text::TextEngine>,
64 scene: scene::SceneGraph,
65 scripts: Vec<String>,
66 layout_engine: layout::LayoutEngine,
67 event_system: events::EventSystem,
68 js_engine: Option<js::JsEngine>,
69 needs_relayout: bool,
70 scripts_executed: bool,
71 cursor_pos: (f32, f32),
72}
73
74impl ApplicationHandler for EmbeddedApp {
75 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
76 if self.window.is_some() {
77 return;
78 }
79
80 let window = Arc::new(
81 event_loop
82 .create_window(
83 WindowAttributes::default()
84 .with_title(&self.title)
85 .with_inner_size(winit::dpi::LogicalSize::new(1024.0, 768.0)),
86 )
87 .expect("failed to create window"),
88 );
89
90 let renderer = pollster::block_on(render::Renderer::new(window.clone()));
91 let te = text::TextEngine::new(&renderer.device, &renderer.queue, renderer.format);
92
93 self.window = Some(window.clone());
94 self.text_engine = Some(te);
95 self.renderer = Some(renderer);
96 self.js_engine = Some(js::JsEngine::new());
97 self.needs_relayout = true;
98 window.request_redraw();
99 }
100
101 fn window_event(
102 &mut self,
103 event_loop: &ActiveEventLoop,
104 _window_id: WindowId,
105 event: WindowEvent,
106 ) {
107 match event {
108 WindowEvent::CloseRequested => {
109 event_loop.exit();
110 }
111 WindowEvent::Resized(size) => {
112 if let Some(renderer) = &mut self.renderer {
113 renderer.resize(size.width, size.height);
114 self.needs_relayout = true;
115 if let Some(window) = &self.window {
116 window.request_redraw();
117 }
118 }
119 }
120 WindowEvent::MouseInput {
121 state: ElementState::Pressed,
122 button: MouseButton::Left,
123 ..
124 } => {
125 let (x, y) = self.cursor_pos;
126 let app_event = events::AppEvent::Click(x, y);
127 let results = self.event_system.handle(&app_event, &mut self.scene);
128 if !results.is_empty() {
129 self.execute_handlers(&results);
130 if let Some(window) = &self.window {
131 window.request_redraw();
132 }
133 }
134 }
135 WindowEvent::CursorMoved { position, .. } => {
136 self.cursor_pos = (position.x as f32, position.y as f32);
137 let old_hover = self.event_system.hover_node;
138 let app_event = events::AppEvent::MouseMove(position.x as f32, position.y as f32);
139 let results = self.event_system.handle(&app_event, &mut self.scene);
140 if !results.is_empty() {
141 self.execute_handlers(&results);
142 }
143 if self.event_system.hover_node != old_hover {
144 self.needs_relayout = true;
145 if let Some(window) = &self.window {
146 window.request_redraw();
147 }
148 }
149 }
150 WindowEvent::MouseWheel { delta, .. } => {
151 let (dx, dy) = match delta {
152 winit::event::MouseScrollDelta::LineDelta(x, y) => (x, y),
153 winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32),
154 };
155 let app_event =
156 events::AppEvent::Scroll(self.cursor_pos.0, self.cursor_pos.1, dx, -dy);
157 self.event_system.handle(&app_event, &mut self.scene);
158 if let Some(window) = &self.window {
159 window.request_redraw();
160 }
161 }
162 WindowEvent::RedrawRequested => {
163 if self.needs_relayout {
164 if let Some(renderer) = &self.renderer {
165 let (vw, vh) = renderer.viewport_size();
166 self.layout_engine.compute(&mut self.scene, vw, vh);
167 self.needs_relayout = false;
168 }
169
170 if !self.scripts_executed {
171 self.scripts_executed = true;
172 let scripts = self.scripts.clone();
173 for script in &scripts {
174 if let Some(js) = &mut self.js_engine {
175 if let Err(e) = js.execute(script) {
176 eprintln!("foundry: {}", e);
177 }
178 let mutations = js.take_mutations();
179 if !mutations.is_empty() {
180 js.apply_mutations(&mut self.scene, &mutations);
181 if let Some(renderer) = &self.renderer {
182 let (vw, vh) = renderer.viewport_size();
183 self.layout_engine.compute(&mut self.scene, vw, vh);
184 }
185 }
186 }
187 }
188 }
189 }
190
191 if let Some(renderer) = &mut self.renderer {
192 match renderer.render(&self.scene, self.text_engine.as_mut()) {
193 Ok(_) => {}
194 Err(e) => {
195 eprintln!("foundry: render error: {}", e);
196 }
197 }
198 }
199 }
200 _ => {}
201 }
202 }
203
204 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
205 if self.scene.tick_animations() {
206 self.needs_relayout = true;
207 if let Some(window) = &self.window {
208 window.request_redraw();
209 }
210 event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(
211 std::time::Instant::now() + std::time::Duration::from_millis(16),
212 ));
213 } else {
214 event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
215 }
216 }
217}
218
219impl EmbeddedApp {
220 fn execute_handlers(&mut self, results: &[events::EventResult]) {
221 for result in results {
222 if let Some(js) = &mut self.js_engine {
223 if let Err(e) = js.execute(&result.handler_code) {
224 eprintln!("foundry: event handler error: {}", e);
225 }
226 let mutations = js.take_mutations();
227 if !mutations.is_empty() {
228 js.apply_mutations(&mut self.scene, &mutations);
229 self.needs_relayout = true;
230 }
231 }
232 }
233 }
234}