presenter.rs

  1use crate::{
  2    app::{AppContext, MutableAppContext, WindowInvalidation},
  3    elements::Element,
  4    font_cache::FontCache,
  5    geometry::rect::RectF,
  6    json::{self, ToJson},
  7    platform::Event,
  8    text_layout::TextLayoutCache,
  9    Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox,
 10    ElementStateContext, Entity, FontSystem, ModelHandle, ReadModel, ReadView, Scene,
 11    UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
 12};
 13use pathfinder_geometry::vector::{vec2f, Vector2F};
 14use serde_json::json;
 15use std::{
 16    collections::{HashMap, HashSet},
 17    ops::{Deref, DerefMut},
 18    sync::Arc,
 19};
 20
 21pub struct Presenter {
 22    window_id: usize,
 23    pub(crate) rendered_views: HashMap<usize, ElementBox>,
 24    parents: HashMap<usize, usize>,
 25    font_cache: Arc<FontCache>,
 26    text_layout_cache: TextLayoutCache,
 27    asset_cache: Arc<AssetCache>,
 28    last_mouse_moved_event: Option<Event>,
 29    titlebar_height: f32,
 30}
 31
 32impl Presenter {
 33    pub fn new(
 34        window_id: usize,
 35        titlebar_height: f32,
 36        font_cache: Arc<FontCache>,
 37        text_layout_cache: TextLayoutCache,
 38        asset_cache: Arc<AssetCache>,
 39        cx: &mut MutableAppContext,
 40    ) -> Self {
 41        Self {
 42            window_id,
 43            rendered_views: cx.render_views(window_id, titlebar_height),
 44            parents: HashMap::new(),
 45            font_cache,
 46            text_layout_cache,
 47            asset_cache,
 48            last_mouse_moved_event: None,
 49            titlebar_height,
 50        }
 51    }
 52
 53    pub fn dispatch_path(&self, app: &AppContext) -> Vec<usize> {
 54        if let Some(view_id) = app.focused_view_id(self.window_id) {
 55            self.dispatch_path_from(view_id)
 56        } else {
 57            Vec::new()
 58        }
 59    }
 60
 61    pub(crate) fn dispatch_path_from(&self, mut view_id: usize) -> Vec<usize> {
 62        let mut path = Vec::new();
 63        path.push(view_id);
 64        while let Some(parent_id) = self.parents.get(&view_id).copied() {
 65            path.push(parent_id);
 66            view_id = parent_id;
 67        }
 68        path.reverse();
 69        path
 70    }
 71
 72    pub fn invalidate(
 73        &mut self,
 74        invalidation: &mut WindowInvalidation,
 75        cx: &mut MutableAppContext,
 76    ) {
 77        cx.start_frame();
 78        for view_id in &invalidation.removed {
 79            invalidation.updated.remove(&view_id);
 80            self.rendered_views.remove(&view_id);
 81            self.parents.remove(&view_id);
 82        }
 83        for view_id in &invalidation.updated {
 84            self.rendered_views.insert(
 85                *view_id,
 86                cx.render_view(self.window_id, *view_id, self.titlebar_height, false)
 87                    .unwrap(),
 88            );
 89        }
 90    }
 91
 92    pub fn refresh(&mut self, invalidation: &mut WindowInvalidation, cx: &mut MutableAppContext) {
 93        self.invalidate(invalidation, cx);
 94        for (view_id, view) in &mut self.rendered_views {
 95            if !invalidation.updated.contains(view_id) {
 96                *view = cx
 97                    .render_view(self.window_id, *view_id, self.titlebar_height, true)
 98                    .unwrap();
 99            }
100        }
101    }
102
103    pub fn build_scene(
104        &mut self,
105        window_size: Vector2F,
106        scale_factor: f32,
107        refreshing: bool,
108        cx: &mut MutableAppContext,
109    ) -> Scene {
110        let mut scene = Scene::new(scale_factor);
111
112        if let Some(root_view_id) = cx.root_view_id(self.window_id) {
113            self.layout(window_size, refreshing, cx);
114            let mut paint_cx = self.build_paint_context(&mut scene, cx);
115            paint_cx.paint(
116                root_view_id,
117                Vector2F::zero(),
118                RectF::new(Vector2F::zero(), window_size),
119            );
120            self.text_layout_cache.finish_frame();
121
122            if let Some(event) = self.last_mouse_moved_event.clone() {
123                self.dispatch_event(event, cx)
124            }
125        } else {
126            log::error!("could not find root_view_id for window {}", self.window_id);
127        }
128
129        scene
130    }
131
132    fn layout(&mut self, size: Vector2F, refreshing: bool, cx: &mut MutableAppContext) {
133        if let Some(root_view_id) = cx.root_view_id(self.window_id) {
134            self.build_layout_context(refreshing, cx)
135                .layout(root_view_id, SizeConstraint::strict(size));
136        }
137    }
138
139    pub fn build_layout_context<'a>(
140        &'a mut self,
141        refreshing: bool,
142        cx: &'a mut MutableAppContext,
143    ) -> LayoutContext<'a> {
144        LayoutContext {
145            rendered_views: &mut self.rendered_views,
146            parents: &mut self.parents,
147            refreshing,
148            font_cache: &self.font_cache,
149            font_system: cx.platform().fonts(),
150            text_layout_cache: &self.text_layout_cache,
151            asset_cache: &self.asset_cache,
152            view_stack: Vec::new(),
153            app: cx,
154        }
155    }
156
157    pub fn build_paint_context<'a>(
158        &'a mut self,
159        scene: &'a mut Scene,
160        cx: &'a mut MutableAppContext,
161    ) -> PaintContext {
162        PaintContext {
163            scene,
164            font_cache: &self.font_cache,
165            text_layout_cache: &self.text_layout_cache,
166            rendered_views: &mut self.rendered_views,
167            app: cx,
168        }
169    }
170
171    pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) {
172        if let Some(root_view_id) = cx.root_view_id(self.window_id) {
173            match event {
174                Event::MouseMoved { .. } => {
175                    self.last_mouse_moved_event = Some(event.clone());
176                }
177                Event::LeftMouseDragged { position } => {
178                    self.last_mouse_moved_event = Some(Event::MouseMoved {
179                        position,
180                        left_mouse_down: true,
181                    });
182                }
183                _ => {}
184            }
185
186            let mut event_cx = self.build_event_context(cx);
187            event_cx.dispatch_event(root_view_id, &event);
188
189            let invalidated_views = event_cx.invalidated_views;
190            let dispatch_directives = event_cx.dispatched_actions;
191
192            for view_id in invalidated_views {
193                cx.notify_view(self.window_id, view_id);
194            }
195            for directive in dispatch_directives {
196                cx.dispatch_action_any(self.window_id, &directive.path, directive.action.as_ref());
197            }
198        }
199    }
200
201    pub fn build_event_context<'a>(
202        &'a mut self,
203        cx: &'a mut MutableAppContext,
204    ) -> EventContext<'a> {
205        EventContext {
206            rendered_views: &mut self.rendered_views,
207            dispatched_actions: Default::default(),
208            font_cache: &self.font_cache,
209            text_layout_cache: &self.text_layout_cache,
210            view_stack: Default::default(),
211            invalidated_views: Default::default(),
212            notify_count: 0,
213            app: cx,
214        }
215    }
216
217    pub fn debug_elements(&self, cx: &AppContext) -> Option<json::Value> {
218        let view = cx.root_view(self.window_id)?;
219        Some(json!({
220            "root_view": view.debug_json(cx),
221            "root_element": self.rendered_views.get(&view.id())
222                .map(|root_element| {
223                    root_element.debug(&DebugContext {
224                        rendered_views: &self.rendered_views,
225                        font_cache: &self.font_cache,
226                        app: cx,
227                    })
228                })
229        }))
230    }
231}
232
233pub struct DispatchDirective {
234    pub path: Vec<usize>,
235    pub action: Box<dyn Action>,
236}
237
238pub struct LayoutContext<'a> {
239    rendered_views: &'a mut HashMap<usize, ElementBox>,
240    parents: &'a mut HashMap<usize, usize>,
241    view_stack: Vec<usize>,
242    pub refreshing: bool,
243    pub font_cache: &'a Arc<FontCache>,
244    pub font_system: Arc<dyn FontSystem>,
245    pub text_layout_cache: &'a TextLayoutCache,
246    pub asset_cache: &'a AssetCache,
247    pub app: &'a mut MutableAppContext,
248}
249
250impl<'a> LayoutContext<'a> {
251    fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
252        if let Some(parent_id) = self.view_stack.last() {
253            self.parents.insert(view_id, *parent_id);
254        }
255        self.view_stack.push(view_id);
256        let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
257        let size = rendered_view.layout(constraint, self);
258        self.rendered_views.insert(view_id, rendered_view);
259        self.view_stack.pop();
260        size
261    }
262}
263
264impl<'a> Deref for LayoutContext<'a> {
265    type Target = MutableAppContext;
266
267    fn deref(&self) -> &Self::Target {
268        self.app
269    }
270}
271
272impl<'a> DerefMut for LayoutContext<'a> {
273    fn deref_mut(&mut self) -> &mut Self::Target {
274        self.app
275    }
276}
277
278impl<'a> ReadView for LayoutContext<'a> {
279    fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
280        self.app.read_view(handle)
281    }
282}
283
284impl<'a> ReadModel for LayoutContext<'a> {
285    fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
286        self.app.read_model(handle)
287    }
288}
289
290impl<'a> UpgradeModelHandle for LayoutContext<'a> {
291    fn upgrade_model_handle<T: Entity>(
292        &self,
293        handle: &WeakModelHandle<T>,
294    ) -> Option<ModelHandle<T>> {
295        self.app.upgrade_model_handle(handle)
296    }
297
298    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
299        self.app.model_handle_is_upgradable(handle)
300    }
301
302    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
303        self.app.upgrade_any_model_handle(handle)
304    }
305}
306
307impl<'a> UpgradeViewHandle for LayoutContext<'a> {
308    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
309        self.app.upgrade_view_handle(handle)
310    }
311
312    fn upgrade_any_view_handle(&self, handle: &crate::AnyWeakViewHandle) -> Option<AnyViewHandle> {
313        self.app.upgrade_any_view_handle(handle)
314    }
315}
316
317impl<'a> ElementStateContext for LayoutContext<'a> {
318    fn current_view_id(&self) -> usize {
319        *self.view_stack.last().unwrap()
320    }
321}
322
323pub struct PaintContext<'a> {
324    rendered_views: &'a mut HashMap<usize, ElementBox>,
325    pub scene: &'a mut Scene,
326    pub font_cache: &'a FontCache,
327    pub text_layout_cache: &'a TextLayoutCache,
328    pub app: &'a AppContext,
329}
330
331impl<'a> PaintContext<'a> {
332    fn paint(&mut self, view_id: usize, origin: Vector2F, visible_bounds: RectF) {
333        if let Some(mut tree) = self.rendered_views.remove(&view_id) {
334            tree.paint(origin, visible_bounds, self);
335            self.rendered_views.insert(view_id, tree);
336        }
337    }
338}
339
340impl<'a> Deref for PaintContext<'a> {
341    type Target = AppContext;
342
343    fn deref(&self) -> &Self::Target {
344        self.app
345    }
346}
347
348pub struct EventContext<'a> {
349    rendered_views: &'a mut HashMap<usize, ElementBox>,
350    dispatched_actions: Vec<DispatchDirective>,
351    pub font_cache: &'a FontCache,
352    pub text_layout_cache: &'a TextLayoutCache,
353    pub app: &'a mut MutableAppContext,
354    pub notify_count: usize,
355    view_stack: Vec<usize>,
356    invalidated_views: HashSet<usize>,
357}
358
359impl<'a> EventContext<'a> {
360    fn dispatch_event(&mut self, view_id: usize, event: &Event) -> bool {
361        if let Some(mut element) = self.rendered_views.remove(&view_id) {
362            self.view_stack.push(view_id);
363            let result = element.dispatch_event(event, self);
364            self.view_stack.pop();
365            self.rendered_views.insert(view_id, element);
366            result
367        } else {
368            false
369        }
370    }
371
372    pub fn dispatch_action<A: Action>(&mut self, action: A) {
373        self.dispatched_actions.push(DispatchDirective {
374            path: self.view_stack.clone(),
375            action: Box::new(action),
376        });
377    }
378
379    pub fn notify(&mut self) {
380        self.notify_count += 1;
381        if let Some(view_id) = self.view_stack.last() {
382            self.invalidated_views.insert(*view_id);
383        }
384    }
385
386    pub fn notify_count(&self) -> usize {
387        self.notify_count
388    }
389}
390
391impl<'a> Deref for EventContext<'a> {
392    type Target = MutableAppContext;
393
394    fn deref(&self) -> &Self::Target {
395        self.app
396    }
397}
398
399impl<'a> DerefMut for EventContext<'a> {
400    fn deref_mut(&mut self) -> &mut Self::Target {
401        self.app
402    }
403}
404
405pub struct DebugContext<'a> {
406    rendered_views: &'a HashMap<usize, ElementBox>,
407    pub font_cache: &'a FontCache,
408    pub app: &'a AppContext,
409}
410
411#[derive(Clone, Copy, Debug, Eq, PartialEq)]
412pub enum Axis {
413    Horizontal,
414    Vertical,
415}
416
417impl Axis {
418    pub fn invert(self) -> Self {
419        match self {
420            Self::Horizontal => Self::Vertical,
421            Self::Vertical => Self::Horizontal,
422        }
423    }
424}
425
426impl ToJson for Axis {
427    fn to_json(&self) -> serde_json::Value {
428        match self {
429            Axis::Horizontal => json!("horizontal"),
430            Axis::Vertical => json!("vertical"),
431        }
432    }
433}
434
435pub trait Vector2FExt {
436    fn along(self, axis: Axis) -> f32;
437}
438
439impl Vector2FExt for Vector2F {
440    fn along(self, axis: Axis) -> f32 {
441        match axis {
442            Axis::Horizontal => self.x(),
443            Axis::Vertical => self.y(),
444        }
445    }
446}
447
448#[derive(Copy, Clone, Debug)]
449pub struct SizeConstraint {
450    pub min: Vector2F,
451    pub max: Vector2F,
452}
453
454impl SizeConstraint {
455    pub fn new(min: Vector2F, max: Vector2F) -> Self {
456        Self { min, max }
457    }
458
459    pub fn strict(size: Vector2F) -> Self {
460        Self {
461            min: size,
462            max: size,
463        }
464    }
465
466    pub fn strict_along(axis: Axis, max: f32) -> Self {
467        match axis {
468            Axis::Horizontal => Self {
469                min: vec2f(max, 0.0),
470                max: vec2f(max, f32::INFINITY),
471            },
472            Axis::Vertical => Self {
473                min: vec2f(0.0, max),
474                max: vec2f(f32::INFINITY, max),
475            },
476        }
477    }
478
479    pub fn max_along(&self, axis: Axis) -> f32 {
480        match axis {
481            Axis::Horizontal => self.max.x(),
482            Axis::Vertical => self.max.y(),
483        }
484    }
485
486    pub fn min_along(&self, axis: Axis) -> f32 {
487        match axis {
488            Axis::Horizontal => self.min.x(),
489            Axis::Vertical => self.min.y(),
490        }
491    }
492
493    pub fn constrain(&self, size: Vector2F) -> Vector2F {
494        vec2f(
495            size.x().min(self.max.x()).max(self.min.x()),
496            size.y().min(self.max.y()).max(self.min.y()),
497        )
498    }
499}
500
501impl ToJson for SizeConstraint {
502    fn to_json(&self) -> serde_json::Value {
503        json!({
504            "min": self.min.to_json(),
505            "max": self.max.to_json(),
506        })
507    }
508}
509
510pub struct ChildView {
511    view: AnyViewHandle,
512}
513
514impl ChildView {
515    pub fn new(view: impl Into<AnyViewHandle>) -> Self {
516        Self { view: view.into() }
517    }
518}
519
520impl Element for ChildView {
521    type LayoutState = ();
522    type PaintState = ();
523
524    fn layout(
525        &mut self,
526        constraint: SizeConstraint,
527        cx: &mut LayoutContext,
528    ) -> (Vector2F, Self::LayoutState) {
529        let size = cx.layout(self.view.id(), constraint);
530        (size, ())
531    }
532
533    fn paint(
534        &mut self,
535        bounds: RectF,
536        visible_bounds: RectF,
537        _: &mut Self::LayoutState,
538        cx: &mut PaintContext,
539    ) -> Self::PaintState {
540        cx.paint(self.view.id(), bounds.origin(), visible_bounds);
541    }
542
543    fn dispatch_event(
544        &mut self,
545        event: &Event,
546        _: RectF,
547        _: RectF,
548        _: &mut Self::LayoutState,
549        _: &mut Self::PaintState,
550        cx: &mut EventContext,
551    ) -> bool {
552        cx.dispatch_event(self.view.id(), event)
553    }
554
555    fn debug(
556        &self,
557        bounds: RectF,
558        _: &Self::LayoutState,
559        _: &Self::PaintState,
560        cx: &DebugContext,
561    ) -> serde_json::Value {
562        json!({
563            "type": "ChildView",
564            "view_id": self.view.id(),
565            "bounds": bounds.to_json(),
566            "view": self.view.debug_json(cx.app),
567            "child": if let Some(view) = cx.rendered_views.get(&self.view.id()) {
568                view.debug(cx)
569            } else {
570                json!(null)
571            }
572        })
573    }
574}