1use crate::{
2 app::{AppContext, MutableAppContext, WindowInvalidation},
3 elements::Element,
4 platform::Event,
5 Scene,
6};
7use pathfinder_geometry::vector::{vec2f, Vector2F};
8use std::{any::Any, collections::HashMap, rc::Rc};
9
10pub struct Presenter {
11 window_id: usize,
12 rendered_views: HashMap<usize, Box<dyn Element>>,
13 parents: HashMap<usize, usize>,
14 font_cache: Rc<FontCache>,
15 text_layout_cache: LayoutCache,
16 asset_cache: Rc<AssetCache>,
17}
18
19impl Presenter {
20 pub fn new(
21 window_id: usize,
22 font_cache: Rc<FontCache>,
23 asset_cache: Rc<AssetCache>,
24 app: &MutableAppContext,
25 ) -> Self {
26 Self {
27 window_id,
28 rendered_views: app.render_views(window_id).unwrap(),
29 parents: HashMap::new(),
30 font_cache,
31 text_layout_cache: LayoutCache::new(),
32 asset_cache,
33 }
34 }
35
36 fn invalidate(&mut self, invalidation: WindowInvalidation, app: &AppContext) {
37 for view_id in invalidation.updated {
38 self.rendered_views
39 .insert(view_id, app.render_view(self.window_id, view_id).unwrap());
40 }
41 for view_id in invalidation.removed {
42 self.rendered_views.remove(&view_id);
43 self.parents.remove(&view_id);
44 }
45 }
46
47 pub fn build_scene(
48 &mut self,
49 window_size: Vector2F,
50 scale_factor: f32,
51 app: &mut MutableAppContext,
52 ) -> Scene {
53 self.layout(window_size, app.ctx());
54 self.after_layout(app);
55 let scene = self.paint(window_size, scale_factor, app.ctx());
56 self.text_layout_cache.finish_frame();
57 scene
58 }
59
60 fn layout(&mut self, size: Vector2F, app: &AppContext) {
61 if let Some(root_view_id) = app.root_view_id(self.window_id) {
62 let mut layout_ctx = LayoutContext {
63 rendered_views: &mut self.rendered_views,
64 parents: &mut self.parents,
65 font_cache: &self.font_cache,
66 text_layout_cache: &self.text_layout_cache,
67 asset_cache: &self.asset_cache,
68 view_stack: Vec::new(),
69 };
70 layout_ctx.layout(root_view_id, SizeConstraint::strict(size), app);
71 }
72 }
73
74 fn after_layout(&mut self, app: &mut MutableAppContext) {
75 if let Some(root_view_id) = app.root_view_id(self.window_id) {
76 let mut ctx = AfterLayoutContext {
77 rendered_views: &mut self.rendered_views,
78 font_cache: &self.font_cache,
79 text_layout_cache: &self.text_layout_cache,
80 };
81 ctx.after_layout(root_view_id, app);
82 }
83 }
84
85 fn paint(&mut self, size: Vector2F, scale_factor: f32, app: &AppContext) -> Scene {
86 // let mut canvas = Canvas::new(size * scale_factor).get_context_2d(self.font_context.clone());
87 // canvas.scale(scale_factor);
88
89 // if let Some(root_view_id) = app.root_view_id(self.window_id) {
90 // let mut paint_ctx = PaintContext {
91 // canvas: &mut canvas,
92 // font_cache: &self.font_cache,
93 // text_layout_cache: &self.text_layout_cache,
94 // rendered_views: &mut self.rendered_views,
95 // };
96 // paint_ctx.paint(root_view_id, Vector2F::zero(), app);
97 // }
98
99 // canvas.into_canvas().into_scene()
100 todo!()
101 }
102
103 pub fn responder_chain(&self, app: &AppContext) -> Option<Vec<usize>> {
104 app.focused_view_id(self.window_id).map(|mut view_id| {
105 let mut chain = vec![view_id];
106 while let Some(parent_id) = self.parents.get(&view_id) {
107 view_id = *parent_id;
108 chain.push(view_id);
109 }
110 chain.reverse();
111 chain
112 })
113 }
114
115 pub fn dispatch_event(
116 &self,
117 event: Event,
118 app: &AppContext,
119 ) -> Vec<(usize, &'static str, Box<dyn Any>)> {
120 let mut event_ctx = EventContext {
121 rendered_views: &self.rendered_views,
122 actions: Vec::new(),
123 font_cache: &self.font_cache,
124 text_layout_cache: &self.text_layout_cache,
125 view_stack: Vec::new(),
126 };
127 if let Some(root_view_id) = app.root_view_id(self.window_id) {
128 event_ctx.dispatch_event_on_view(root_view_id, &event, app);
129 }
130 event_ctx.actions
131 }
132}
133
134pub struct LayoutContext<'a> {
135 rendered_views: &'a mut HashMap<usize, Box<dyn Element>>,
136 parents: &'a mut HashMap<usize, usize>,
137 pub font_cache: &'a FontCache,
138 pub text_layout_cache: &'a LayoutCache,
139 pub asset_cache: &'a AssetCache,
140 view_stack: Vec<usize>,
141}
142
143impl<'a> LayoutContext<'a> {
144 fn layout(&mut self, view_id: usize, constraint: SizeConstraint, app: &AppContext) -> Vector2F {
145 if let Some(parent_id) = self.view_stack.last() {
146 self.parents.insert(view_id, *parent_id);
147 }
148 self.view_stack.push(view_id);
149 let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
150 let size = rendered_view.layout(constraint, self, app);
151 self.rendered_views.insert(view_id, rendered_view);
152 self.view_stack.pop();
153 size
154 }
155}
156
157pub struct AfterLayoutContext<'a> {
158 rendered_views: &'a mut HashMap<usize, Box<dyn Element>>,
159 pub font_cache: &'a FontCache,
160 pub text_layout_cache: &'a LayoutCache,
161}
162
163impl<'a> AfterLayoutContext<'a> {
164 fn after_layout(&mut self, view_id: usize, app: &mut MutableAppContext) {
165 if let Some(mut view) = self.rendered_views.remove(&view_id) {
166 view.after_layout(self, app);
167 self.rendered_views.insert(view_id, view);
168 }
169 }
170}
171
172pub struct PaintContext<'a> {
173 rendered_views: &'a mut HashMap<usize, Box<dyn Element>>,
174 // pub canvas: &'a mut CanvasRenderingContext2D,
175 pub font_cache: &'a FontCache,
176 pub text_layout_cache: &'a LayoutCache,
177}
178
179impl<'a> PaintContext<'a> {
180 fn paint(&mut self, view_id: usize, origin: Vector2F, app: &AppContext) {
181 if let Some(mut tree) = self.rendered_views.remove(&view_id) {
182 tree.paint(origin, self, app);
183 self.rendered_views.insert(view_id, tree);
184 }
185 }
186}
187
188pub struct EventContext<'a> {
189 rendered_views: &'a HashMap<usize, Box<dyn Element>>,
190 actions: Vec<(usize, &'static str, Box<dyn Any>)>,
191 pub font_cache: &'a FontCache,
192 pub text_layout_cache: &'a LayoutCache,
193 view_stack: Vec<usize>,
194}
195
196impl<'a> EventContext<'a> {
197 pub fn dispatch_event_on_view(
198 &mut self,
199 view_id: usize,
200 event: &Event,
201 app: &AppContext,
202 ) -> bool {
203 if let Some(element) = self.rendered_views.get(&view_id) {
204 self.view_stack.push(view_id);
205 let result = element.dispatch_event(event, self, app);
206 self.view_stack.pop();
207 result
208 } else {
209 false
210 }
211 }
212
213 pub fn dispatch_action<A: 'static + Any>(&mut self, name: &'static str, arg: A) {
214 self.actions
215 .push((*self.view_stack.last().unwrap(), name, Box::new(arg)));
216 }
217}
218
219#[derive(Clone, Copy, Debug, Eq, PartialEq)]
220pub enum Axis {
221 Horizontal,
222 Vertical,
223}
224
225impl Axis {
226 pub fn invert(self) -> Self {
227 match self {
228 Self::Horizontal => Self::Vertical,
229 Self::Vertical => Self::Horizontal,
230 }
231 }
232}
233
234pub trait Vector2FExt {
235 fn along(self, axis: Axis) -> f32;
236}
237
238impl Vector2FExt for Vector2F {
239 fn along(self, axis: Axis) -> f32 {
240 match axis {
241 Axis::Horizontal => self.x(),
242 Axis::Vertical => self.y(),
243 }
244 }
245}
246
247#[derive(Copy, Clone, Debug)]
248pub struct SizeConstraint {
249 pub min: Vector2F,
250 pub max: Vector2F,
251}
252
253impl SizeConstraint {
254 pub fn new(min: Vector2F, max: Vector2F) -> Self {
255 Self { min, max }
256 }
257
258 pub fn strict(size: Vector2F) -> Self {
259 Self {
260 min: size,
261 max: size,
262 }
263 }
264
265 pub fn strict_along(axis: Axis, max: f32) -> Self {
266 match axis {
267 Axis::Horizontal => Self {
268 min: vec2f(max, 0.0),
269 max: vec2f(max, f32::INFINITY),
270 },
271 Axis::Vertical => Self {
272 min: vec2f(0.0, max),
273 max: vec2f(f32::INFINITY, max),
274 },
275 }
276 }
277
278 pub fn max_along(&self, axis: Axis) -> f32 {
279 match axis {
280 Axis::Horizontal => self.max.x(),
281 Axis::Vertical => self.max.y(),
282 }
283 }
284}
285
286pub struct ChildView {
287 view_id: usize,
288 size: Option<Vector2F>,
289 origin: Option<Vector2F>,
290}
291
292impl ChildView {
293 pub fn new(view_id: usize) -> Self {
294 Self {
295 view_id,
296 size: None,
297 origin: None,
298 }
299 }
300}
301
302impl Element for ChildView {
303 fn layout(
304 &mut self,
305 constraint: SizeConstraint,
306 ctx: &mut LayoutContext,
307 app: &AppContext,
308 ) -> Vector2F {
309 let size = ctx.layout(self.view_id, constraint, app);
310 self.size = Some(size);
311 size
312 }
313
314 fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &mut MutableAppContext) {
315 ctx.after_layout(self.view_id, app);
316 }
317
318 fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
319 self.origin = Some(origin);
320 ctx.paint(self.view_id, origin, app);
321 }
322
323 fn dispatch_event(&self, event: &Event, ctx: &mut EventContext, app: &AppContext) -> bool {
324 ctx.dispatch_event_on_view(self.view_id, event, app)
325 }
326
327 fn size(&self) -> Option<Vector2F> {
328 self.size
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 // #[test]
335 // fn test_responder_chain() {
336 // let settings = settings_rx(None);
337 // let mut app = App::new().unwrap();
338 // let workspace = app.add_model(|ctx| Workspace::new(Vec::new(), ctx));
339 // let (window_id, workspace_view) =
340 // app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx));
341
342 // let invalidations = Rc::new(RefCell::new(Vec::new()));
343 // let invalidations_ = invalidations.clone();
344 // app.on_window_invalidated(window_id, move |invalidation, _| {
345 // invalidations_.borrow_mut().push(invalidation)
346 // });
347
348 // let active_pane_id = workspace_view.update(&mut app, |view, ctx| {
349 // ctx.focus(view.active_pane());
350 // view.active_pane().id()
351 // });
352
353 // app.update(|app| {
354 // let mut presenter = Presenter::new(
355 // window_id,
356 // Rc::new(FontCache::new()),
357 // Rc::new(AssetCache::new()),
358 // app,
359 // );
360 // for invalidation in invalidations.borrow().iter().cloned() {
361 // presenter.update(vec2f(1024.0, 768.0), 2.0, Some(invalidation), app);
362 // }
363
364 // assert_eq!(
365 // presenter.responder_chain(app.ctx()).unwrap(),
366 // vec![workspace_view.id(), active_pane_id]
367 // );
368 // });
369 // }
370}