Skip to main content

ratatui_kit/render/
updater.rs

1use std::{
2    any::Any,
3    cell::{Ref, RefMut},
4};
5
6use crate::{
7    ElementKey,
8    component::{Components, InstantiatedComponent},
9    context::{Context, ContextLookup, ContextStack},
10    element::ElementRepr,
11    layout_style::LayoutStyle,
12    multimap::AppendOnlyMultimap,
13    terminal::UpdaterTerminal,
14};
15
16pub struct ComponentUpdater<'a, 'c: 'a> {
17    key: ElementKey,
18    component_context_stack: &'a mut ContextStack<'c>,
19    // 对象安全的终端投影:update 路径只需 insert_before/events 两项能力,
20    // 不绑定具体 `Terminal<CrossTerminal>`,从而可在无头测试里用 no-op 终端驱动。
21    terminal: &'a mut dyn UpdaterTerminal,
22    components: &'a mut Components,
23    transparent_layout: bool,
24    layout_style: &'a mut LayoutStyle,
25}
26
27impl<'a, 'c: 'a> ComponentUpdater<'a, 'c> {
28    pub(crate) fn new(
29        key: ElementKey,
30        component_context_stack: &'a mut ContextStack<'c>,
31        terminal: &'a mut dyn UpdaterTerminal,
32        components: &'a mut Components,
33        layout_style: &'a mut LayoutStyle,
34    ) -> ComponentUpdater<'a, 'c> {
35        ComponentUpdater {
36            key,
37            component_context_stack,
38            terminal,
39            components,
40            transparent_layout: false,
41            layout_style,
42        }
43    }
44
45    pub fn component_context_stack(&self) -> &ContextStack<'c> {
46        self.component_context_stack
47    }
48
49    pub fn key(&self) -> &ElementKey {
50        &self.key
51    }
52
53    pub fn get_context<T: Any>(&'_ self) -> Option<Ref<'_, T>> {
54        match self.component_context_stack.get_context::<T>() {
55            ContextLookup::Found(res) => Some(res),
56            _ => None,
57        }
58    }
59
60    pub fn get_context_mut<T: Any>(&'_ self) -> Option<RefMut<'_, T>> {
61        match self.component_context_stack.get_context_mut::<T>() {
62            ContextLookup::Found(res) => Some(res),
63            _ => None,
64        }
65    }
66
67    pub fn terminal(&mut self) -> &mut dyn UpdaterTerminal {
68        self.terminal
69    }
70
71    pub fn set_transparent_layout(&mut self, transparent: bool) {
72        self.transparent_layout = transparent;
73    }
74
75    pub(crate) fn has_transparent_layout(&self) -> bool {
76        self.transparent_layout
77    }
78
79    pub fn set_layout_style(&mut self, layout_style: LayoutStyle) {
80        *self.layout_style = layout_style;
81    }
82
83    pub fn update_children<I, T>(&mut self, elements: I, context: Option<Context>)
84    where
85        I: IntoIterator<Item = T>,
86        T: ElementRepr,
87    {
88        self.component_context_stack
89            .with_context(context, |context_stack| {
90                let mut used_components = AppendOnlyMultimap::default();
91
92                for mut child in elements {
93                    let mut component = match self.components.pop_front(child.key()) {
94                        Some(component)
95                            if component.component().type_id()
96                                == child.helper().component_type_id() =>
97                        {
98                            component
99                        }
100                        _ => {
101                            let h = child.helper();
102                            InstantiatedComponent::new(child.key().clone(), child.props_mut(), h)
103                        }
104                    };
105
106                    component.update(self.terminal, context_stack, child.props_mut());
107                    used_components.push_back(child.key().clone(), component);
108                }
109
110                self.components.components = used_components.into();
111            });
112    }
113}