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