window.rs

  1use crate::{
  2    px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, Effect, Element, EntityId,
  3    FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas,
  4    PlatformWindow, Point, RasterizeGlyphParams, Reference, Scene, Size, StackContext,
  5    StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
  6};
  7use anyhow::Result;
  8use futures::Future;
  9use smallvec::SmallVec;
 10use std::{any::TypeId, marker::PhantomData, mem, sync::Arc};
 11use util::ResultExt;
 12
 13pub struct AnyWindow {}
 14
 15pub struct Window {
 16    handle: AnyWindowHandle,
 17    platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
 18    glyph_atlas: Arc<dyn PlatformAtlas<RasterizeGlyphParams>>,
 19    rem_size: Pixels,
 20    content_size: Size<Pixels>,
 21    layout_engine: TaffyLayoutEngine,
 22    pub(crate) root_view: Option<AnyView<()>>,
 23    mouse_position: Point<Pixels>,
 24    current_layer_id: StackingOrder,
 25    pub(crate) scene: Scene,
 26    pub(crate) dirty: bool,
 27}
 28
 29impl Window {
 30    pub fn new(
 31        handle: AnyWindowHandle,
 32        options: WindowOptions,
 33        cx: &mut MainThread<AppContext>,
 34    ) -> Self {
 35        let platform_window = cx.platform().open_window(handle, options);
 36        let glyph_atlas = platform_window.glyph_atlas();
 37        let mouse_position = platform_window.mouse_position();
 38        let content_size = platform_window.content_size();
 39        let scale_factor = platform_window.scale_factor();
 40        platform_window.on_resize(Box::new({
 41            let handle = handle;
 42            let cx = cx.to_async();
 43            move |content_size, scale_factor| {
 44                cx.update_window(handle, |cx| {
 45                    cx.window.scene = Scene::new(scale_factor);
 46                    cx.window.content_size = content_size;
 47                    cx.window.dirty = true;
 48                })
 49                .log_err();
 50            }
 51        }));
 52
 53        let platform_window =
 54            MainThreadOnly::new(Arc::new(platform_window), cx.platform().dispatcher());
 55
 56        Window {
 57            handle,
 58            platform_window,
 59            glyph_atlas,
 60            rem_size: px(16.),
 61            content_size,
 62            layout_engine: TaffyLayoutEngine::new(),
 63            root_view: None,
 64            mouse_position,
 65            current_layer_id: SmallVec::new(),
 66            scene: Scene::new(scale_factor),
 67            dirty: true,
 68        }
 69    }
 70}
 71
 72pub struct WindowContext<'a, 'w> {
 73    app: Reference<'a, AppContext>,
 74    window: Reference<'w, Window>,
 75}
 76
 77impl<'a, 'w> WindowContext<'a, 'w> {
 78    pub(crate) fn mutable(app: &'a mut AppContext, window: &'w mut Window) -> Self {
 79        Self {
 80            app: Reference::Mutable(app),
 81            window: Reference::Mutable(window),
 82        }
 83    }
 84
 85    pub fn notify(&mut self) {
 86        self.window.dirty = true;
 87    }
 88
 89    pub fn request_layout(
 90        &mut self,
 91        style: Style,
 92        children: impl IntoIterator<Item = LayoutId>,
 93    ) -> Result<LayoutId> {
 94        self.app.layout_id_buffer.clear();
 95        self.app.layout_id_buffer.extend(children.into_iter());
 96        let rem_size = self.rem_size();
 97
 98        self.window
 99            .layout_engine
100            .request_layout(style, rem_size, &self.app.layout_id_buffer)
101    }
102
103    pub fn request_measured_layout<
104        F: Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels> + Send + Sync + 'static,
105    >(
106        &mut self,
107        style: Style,
108        rem_size: Pixels,
109        measure: F,
110    ) -> Result<LayoutId> {
111        self.window
112            .layout_engine
113            .request_measured_layout(style, rem_size, measure)
114    }
115
116    pub fn layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
117        Ok(self
118            .window
119            .layout_engine
120            .layout(layout_id)
121            .map(Into::into)?)
122    }
123
124    pub fn scale_factor(&self) -> f32 {
125        self.window.scene.scale_factor
126    }
127
128    pub fn rem_size(&self) -> Pixels {
129        self.window.rem_size
130    }
131
132    pub fn mouse_position(&self) -> Point<Pixels> {
133        self.window.mouse_position
134    }
135
136    pub fn scene(&mut self) -> &mut Scene {
137        &mut self.window.scene
138    }
139
140    pub fn stack<R>(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R {
141        self.window.current_layer_id.push(order);
142        let result = f(self);
143        self.window.current_layer_id.pop();
144        result
145    }
146
147    pub fn current_layer_id(&self) -> StackingOrder {
148        self.window.current_layer_id.clone()
149    }
150
151    pub fn run_on_main<R>(
152        &self,
153        f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,
154    ) -> impl Future<Output = Result<R>>
155    where
156        R: Send + 'static,
157    {
158        let id = self.window.handle.id;
159        self.app.run_on_main(move |cx| {
160            cx.update_window(id, |cx| {
161                f(unsafe {
162                    mem::transmute::<&mut WindowContext, &mut MainThread<WindowContext>>(cx)
163                })
164            })
165        })
166    }
167
168    pub fn rasterize_glyph(
169        &self,
170        font_id: FontId,
171        glyph_id: GlyphId,
172        font_size: Pixels,
173        target_position: Point<Pixels>,
174        scale_factor: f32,
175    ) -> Result<AtlasTile> {
176        let target_position = target_position * scale_factor;
177        let subpixel_variant = Point {
178            x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
179            y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
180        };
181        let rasterized_glyph_id = RasterizeGlyphParams {
182            font_id,
183            glyph_id,
184            font_size,
185            subpixel_variant,
186            scale_factor,
187        };
188        self.window
189            .glyph_atlas
190            .get_or_insert_with(&rasterized_glyph_id, &mut || {
191                self.text_system().rasterize_glyph(&rasterized_glyph_id)
192            })
193    }
194
195    pub(crate) fn draw(&mut self) -> Result<()> {
196        let unit_entity = self.unit_entity.clone();
197        self.update_entity(&unit_entity, |_, cx| {
198            let mut root_view = cx.window.root_view.take().unwrap();
199            let (root_layout_id, mut frame_state) = root_view.layout(&mut (), cx)?;
200            let available_space = cx.window.content_size.map(Into::into);
201
202            dbg!("computing layout");
203            cx.window
204                .layout_engine
205                .compute_layout(root_layout_id, available_space)?;
206            dbg!("asking for layout");
207            let layout = cx.window.layout_engine.layout(root_layout_id)?;
208
209            dbg!("painting root view");
210
211            root_view.paint(layout, &mut (), &mut frame_state, cx)?;
212            cx.window.root_view = Some(root_view);
213            let scene = cx.window.scene.take();
214
215            let _ = cx.run_on_main(|cx| {
216                cx.window
217                    .platform_window
218                    .borrow_on_main_thread()
219                    .draw(scene);
220            });
221
222            cx.window.dirty = false;
223            Ok(())
224        })
225    }
226}
227
228impl MainThread<WindowContext<'_, '_>> {
229    // todo!("implement other methods that use platform window")
230    fn platform_window(&self) -> &dyn PlatformWindow {
231        self.window.platform_window.borrow_on_main_thread().as_ref()
232    }
233}
234
235impl Context for WindowContext<'_, '_> {
236    type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>;
237    type Result<T> = T;
238
239    fn entity<T: Send + Sync + 'static>(
240        &mut self,
241        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
242    ) -> Handle<T> {
243        let slot = self.app.entities.reserve();
244        let entity = build_entity(&mut ViewContext::mutable(
245            &mut *self.app,
246            &mut self.window,
247            slot.id,
248        ));
249        self.entities.redeem(slot, entity)
250    }
251
252    fn update_entity<T: Send + Sync + 'static, R>(
253        &mut self,
254        handle: &Handle<T>,
255        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
256    ) -> R {
257        let mut entity = self.entities.lease(handle);
258        let result = update(
259            &mut *entity,
260            &mut ViewContext::mutable(&mut *self.app, &mut *self.window, handle.id),
261        );
262        self.entities.end_lease(entity);
263        result
264    }
265}
266
267impl<'a, 'w> std::ops::Deref for WindowContext<'a, 'w> {
268    type Target = AppContext;
269
270    fn deref(&self) -> &Self::Target {
271        &self.app
272    }
273}
274
275impl<'a, 'w> std::ops::DerefMut for WindowContext<'a, 'w> {
276    fn deref_mut(&mut self) -> &mut Self::Target {
277        &mut self.app
278    }
279}
280
281impl<S> StackContext for ViewContext<'_, '_, S> {
282    fn app(&mut self) -> &mut AppContext {
283        &mut *self.window_cx.app
284    }
285
286    fn with_text_style<F, R>(&mut self, style: crate::TextStyleRefinement, f: F) -> R
287    where
288        F: FnOnce(&mut Self) -> R,
289    {
290        self.window_cx.app.push_text_style(style);
291        let result = f(self);
292        self.window_cx.app.pop_text_style();
293        result
294    }
295
296    fn with_state<T: Send + Sync + 'static, F, R>(&mut self, state: T, f: F) -> R
297    where
298        F: FnOnce(&mut Self) -> R,
299    {
300        self.window_cx.app.push_state(state);
301        let result = f(self);
302        self.window_cx.app.pop_state::<T>();
303        result
304    }
305}
306
307pub struct ViewContext<'a, 'w, S> {
308    window_cx: WindowContext<'a, 'w>,
309    entity_type: PhantomData<S>,
310    entity_id: EntityId,
311}
312
313impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
314    fn mutable(app: &'a mut AppContext, window: &'w mut Window, entity_id: EntityId) -> Self {
315        Self {
316            window_cx: WindowContext::mutable(app, window),
317            entity_id,
318            entity_type: PhantomData,
319        }
320    }
321
322    pub fn handle(&self) -> WeakHandle<S> {
323        self.entities.weak_handle(self.entity_id)
324    }
325
326    pub fn observe<E: Send + Sync + 'static>(
327        &mut self,
328        handle: &Handle<E>,
329        on_notify: impl Fn(&mut S, Handle<E>, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static,
330    ) {
331        let this = self.handle();
332        let handle = handle.downgrade();
333        let window_handle = self.window.handle;
334        self.app
335            .observers
336            .entry(handle.id)
337            .or_default()
338            .push(Arc::new(move |cx| {
339                cx.update_window(window_handle.id, |cx| {
340                    if let Some(handle) = handle.upgrade(cx) {
341                        this.update(cx, |this, cx| on_notify(this, handle, cx))
342                            .is_ok()
343                    } else {
344                        false
345                    }
346                })
347                .unwrap_or(false)
348            }));
349    }
350
351    pub fn notify(&mut self) {
352        self.window_cx.notify();
353        self.window_cx
354            .app
355            .pending_effects
356            .push_back(Effect::Notify(self.entity_id));
357    }
358
359    pub(crate) fn erase_state<R>(&mut self, f: impl FnOnce(&mut ViewContext<()>) -> R) -> R {
360        let entity_id = self.unit_entity.id;
361        let mut cx = ViewContext::mutable(
362            &mut *self.window_cx.app,
363            &mut *self.window_cx.window,
364            entity_id,
365        );
366        f(&mut cx)
367    }
368}
369
370impl<'a, 'w, S> Context for ViewContext<'a, 'w, S> {
371    type EntityContext<'b, 'c, U: 'static + Send + Sync> = ViewContext<'b, 'c, U>;
372    type Result<U> = U;
373
374    fn entity<T2: Send + Sync + 'static>(
375        &mut self,
376        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T2>) -> T2,
377    ) -> Handle<T2> {
378        self.window_cx.entity(build_entity)
379    }
380
381    fn update_entity<U: Send + Sync + 'static, R>(
382        &mut self,
383        handle: &Handle<U>,
384        update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
385    ) -> R {
386        self.window_cx.update_entity(handle, update)
387    }
388}
389
390impl<'a, 'w, S: 'static> std::ops::Deref for ViewContext<'a, 'w, S> {
391    type Target = WindowContext<'a, 'w>;
392
393    fn deref(&self) -> &Self::Target {
394        &self.window_cx
395    }
396}
397
398impl<'a, 'w, S: 'static> std::ops::DerefMut for ViewContext<'a, 'w, S> {
399    fn deref_mut(&mut self) -> &mut Self::Target {
400        &mut self.window_cx
401    }
402}
403
404// #[derive(Clone, Copy, Eq, PartialEq, Hash)]
405slotmap::new_key_type! { pub struct WindowId; }
406
407#[derive(PartialEq, Eq)]
408pub struct WindowHandle<S> {
409    id: WindowId,
410    state_type: PhantomData<S>,
411}
412
413impl<S> Copy for WindowHandle<S> {}
414
415impl<S> Clone for WindowHandle<S> {
416    fn clone(&self) -> Self {
417        WindowHandle {
418            id: self.id,
419            state_type: PhantomData,
420        }
421    }
422}
423
424impl<S> WindowHandle<S> {
425    pub fn new(id: WindowId) -> Self {
426        WindowHandle {
427            id,
428            state_type: PhantomData,
429        }
430    }
431}
432
433impl<S: 'static> Into<AnyWindowHandle> for WindowHandle<S> {
434    fn into(self) -> AnyWindowHandle {
435        AnyWindowHandle {
436            id: self.id,
437            state_type: TypeId::of::<S>(),
438        }
439    }
440}
441
442#[derive(Copy, Clone, PartialEq, Eq)]
443pub struct AnyWindowHandle {
444    pub(crate) id: WindowId,
445    state_type: TypeId,
446}
447
448#[derive(Clone)]
449pub struct Layout {
450    pub order: u32,
451    pub bounds: Bounds<Pixels>,
452}