ribir_core/context/
build_ctx.rs1use std::{
2 cell::{OnceCell, Ref, RefCell},
3 rc::Rc,
4};
5
6use ribir_algo::Sc;
7
8use crate::{
9 prelude::*,
10 widget::widget_id::new_node,
11 window::{DelayEvent, WindowId},
12};
13
14pub struct BuildCtx<'a> {
16 pub(crate) themes: OnceCell<Vec<Sc<Theme>>>,
17 ctx_from: Option<WidgetId>,
20 pub(crate) tree: &'a RefCell<WidgetTree>,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct BuildCtxHandle {
27 ctx_from: Option<WidgetId>,
28 wnd_id: WindowId,
29}
30
31impl<'a> BuildCtx<'a> {
32 pub fn window(&self) -> Rc<Window> { self.tree.borrow().window() }
34
35 pub fn ctx_from(&self) -> WidgetId {
37 self
38 .ctx_from
39 .unwrap_or_else(|| self.tree.borrow().root())
40 }
41
42 pub fn handle(&self) -> BuildCtxHandle {
45 BuildCtxHandle { wnd_id: self.window().id(), ctx_from: self.ctx_from }
46 }
47
48 #[inline]
49 pub(crate) fn new(from: Option<WidgetId>, tree: &'a RefCell<WidgetTree>) -> Self {
50 Self { themes: OnceCell::new(), ctx_from: from, tree }
51 }
52
53 pub(crate) fn new_with_data(
54 from: Option<WidgetId>, tree: &'a RefCell<WidgetTree>, data: Vec<Sc<Theme>>,
55 ) -> Self {
56 let themes: OnceCell<Vec<Sc<Theme>>> = OnceCell::new();
57 unsafe { themes.set(data).unwrap_unchecked() };
59
60 Self { themes, ctx_from: from, tree }
61 }
62
63 pub(crate) fn find_cfg<T>(&self, f: impl Fn(&Theme) -> Option<&T>) -> Option<&T> {
64 for t in self.themes().iter() {
65 let v = f(t);
66 if v.is_some() {
67 return v;
68 } else if matches!(t.deref(), Theme::Full(_)) {
69 return None;
70 }
71 }
72 f(AppCtx::app_theme())
73 }
74
75 pub(crate) fn assert_get(&self, id: WidgetId) -> Ref<dyn Render> {
77 Ref::map(self.tree.borrow(), |tree| id.assert_get(&tree.arena))
78 }
79
80 pub(crate) fn alloc_widget(&self, widget: Box<dyn Render>) -> WidgetId {
81 new_node(&mut self.tree.borrow_mut().arena, widget)
82 }
83
84 pub(crate) fn append_child(&self, parent: WidgetId, child: Widget) {
85 parent.append(child.consume(), &mut self.tree.borrow_mut().arena);
86 }
87
88 pub(crate) fn insert_after(&self, prev: WidgetId, next: WidgetId) {
90 prev.insert_after(next, &mut self.tree.borrow_mut().arena);
91 }
92
93 pub(crate) fn on_subtree_mounted(&self, id: WidgetId) {
96 id.descendants(&self.tree.borrow().arena)
97 .for_each(|w| self.on_widget_mounted(w));
98 self.tree.borrow_mut().mark_dirty(id);
99 }
100
101 pub(crate) fn on_widget_mounted(&self, id: WidgetId) {
104 self
105 .window()
106 .add_delay_event(DelayEvent::Mounted(id));
107 }
108
109 pub(crate) fn dispose_subtree(&self, id: WidgetId) {
111 id.dispose_subtree(&mut self.tree.borrow_mut());
112 }
113
114 pub(crate) fn mark_dirty(&self, id: WidgetId) { self.tree.borrow_mut().mark_dirty(id); }
115
116 pub(crate) fn themes(&self) -> &Vec<Sc<Theme>> {
117 self.themes.get_or_init(|| {
118 let mut themes = vec![];
119 let Some(p) = self.ctx_from else {
120 return themes;
121 };
122
123 let arena = &self.tree.borrow().arena;
124 p.ancestors(arena).any(|p| {
125 p.assert_get(arena)
126 .query_type_inside_first(|t: &Sc<Theme>| {
127 themes.push(t.clone());
128 matches!(t.deref(), Theme::Inherit(_))
129 });
130 matches!(themes.last().map(Sc::deref), Some(Theme::Full(_)))
131 });
132 themes
133 })
134 }
135}
136
137impl BuildCtxHandle {
138 pub fn with_ctx<R>(self, f: impl FnOnce(&BuildCtx) -> R) -> Option<R> {
141 AppCtx::get_window(self.wnd_id).map(|wnd| {
142 let mut ctx = BuildCtx::new(self.ctx_from, &wnd.widget_tree);
143 f(&mut ctx)
144 })
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::{reset_test_env, test_helper::*};
152
153 #[test]
154 fn themes() {
155 reset_test_env!();
156
157 let themes: Stateful<Vec<Sc<Theme>>> = Stateful::new(vec![]);
158 let c_themes = themes.clone_writer();
159
160 let light_dark = fn_widget! {
161 let light_palette = Palette {
162 brightness: Brightness::Light,
163 ..Default::default()
164 };
165 @ThemeWidget {
166 theme: Sc::new(Theme::Inherit(InheritTheme {
167 palette: Some(Rc::new(light_palette)),
168 ..<_>::default()
169 })),
170 @ {
171 Box::new(fn_widget!{
172 let c_themes = c_themes.clone_writer();
173 let dark_palette = Palette {
174 brightness: Brightness::Dark,
175 ..Default::default()
176 };
177 @MockBox {
178 size: INFINITY_SIZE,
179 @ThemeWidget {
180 theme: Sc::new(Theme::Inherit(InheritTheme {
181 palette: Some(Rc::new(dark_palette)),
182 ..<_>::default()
183 })),
184 @ {
185 Box::new(fn_widget!{
186 @MockBox {
187 size: ZERO_SIZE,
188 @ {
189 Clone::clone_from(&mut *$c_themes.write(), ctx!().themes());
190 Void.build(ctx!())
191 }
192 }
193 })
194 }
195 }
196 }
197 })
198 }
199 }
200 };
201
202 let wnd = TestWindow::new(light_dark);
203 wnd.layout();
204 let themes = themes.read();
205 assert_eq!(themes.len(), 2);
206 let mut iter = themes.iter().filter_map(|t| match t.deref() {
207 Theme::Full(t) => Some(t.palette.brightness),
208 Theme::Inherit(i) => i
209 .palette
210 .as_ref()
211 .map(|palette| palette.brightness),
212 });
213
214 assert_eq!(iter.next(), Some(Brightness::Light));
215 assert_eq!(iter.next(), Some(Brightness::Dark));
216 }
217}