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