Skip to main content

craft_core/elements/
container.rs

1use crate::components::component::ComponentSpecification;
2use crate::components::Props;
3use crate::components::Event;
4use crate::elements::element::Element;
5use crate::elements::element_data::ElementData;
6use crate::elements::element_styles::ElementStyles;
7use crate::layout::layout_context::LayoutContext;
8use crate::elements::scroll_state::ScrollState;
9use crate::events::CraftMessage;
10use crate::geometry::{Point, Size};
11use crate::reactive::element_state_store::{ElementStateStore, ElementStateStoreItem};
12use crate::style::Style;
13use crate::{generate_component_methods};
14use std::any::Any;
15use std::sync::Arc;
16use taffy::{NodeId, TaffyTree};
17use winit::window::Window;
18use crate::renderer::renderer::RenderList;
19use crate::text::text_context::TextContext;
20
21/// An element for storing related elements.
22#[derive(Clone, Default, Debug)]
23pub struct Container {
24    pub element_data: ElementData,
25}
26
27#[derive(Clone, Copy, Default)]
28pub struct ContainerState {
29    pub(crate) scroll_state: ScrollState,
30}
31
32impl Element for Container {
33    fn element_data(&self) -> &ElementData {
34        &self.element_data
35    }
36
37    fn element_data_mut(&mut self) -> &mut ElementData {
38        &mut self.element_data
39    }
40
41    fn name(&self) -> &'static str {
42        "Container"
43    }
44
45    fn draw(
46        &mut self,
47        renderer: &mut RenderList,
48        text_context: &mut TextContext,
49        taffy_tree: &mut TaffyTree<LayoutContext>,
50        _root_node: NodeId,
51        element_state: &mut ElementStateStore,
52        pointer: Option<Point>,
53        window: Option<Arc<dyn Window>>,
54    ) {
55        let base_state = self.get_base_state_mut(element_state);
56        let current_style = base_state.base.current_style(self.element_data());
57        
58        if !current_style.visible() {
59            return;
60        }
61        
62        // We draw the borders before we start any layers, so that we don't clip the borders.
63        self.draw_borders(renderer, element_state);
64        self.maybe_start_layer(renderer);
65        {
66            self.draw_children(renderer, text_context, taffy_tree, element_state, pointer, window);
67        }
68        self.maybe_end_layer(renderer);
69
70        self.draw_scrollbar(renderer);
71    }
72
73    fn compute_layout(
74        &mut self,
75        taffy_tree: &mut TaffyTree<LayoutContext>,
76        element_state: &mut ElementStateStore,
77        scale_factor: f64,
78    ) -> Option<NodeId> {
79        self.merge_default_style();
80        let mut child_nodes: Vec<NodeId> = Vec::with_capacity(self.children().len());
81
82        for child in self.element_data.children.iter_mut() {
83            let child_node = child.internal.compute_layout(taffy_tree, element_state, scale_factor);
84            if let Some(child_node) = child_node {
85                child_nodes.push(child_node);
86            }
87        }
88
89        let base_state = self.get_base_state_mut(element_state);
90        let current_style = base_state.base.current_style(self.element_data());
91        let style: taffy::Style = current_style.to_taffy_style_with_scale_factor(scale_factor);
92
93        self.element_data_mut().taffy_node_id = Some(taffy_tree.new_with_children(style, &child_nodes).unwrap());
94        self.element_data().taffy_node_id
95    }
96
97    fn finalize_layout(
98        &mut self,
99        taffy_tree: &mut TaffyTree<LayoutContext>,
100        root_node: NodeId,
101        position: Point,
102        z_index: &mut u32,
103        transform: glam::Mat4,
104        element_state: &mut ElementStateStore,
105        pointer: Option<Point>,
106        text_context: &mut TextContext,
107    ) {
108        let result = taffy_tree.layout(root_node).unwrap();
109        self.resolve_box(position, transform, result, z_index);
110
111        self.finalize_borders(element_state);
112
113        self.element_data.scrollbar_size = Size::new(result.scrollbar_size.width, result.scrollbar_size.height);
114        self.element_data.computed_scrollbar_size = Size::new(result.scroll_width(), result.scroll_height());
115
116        let scroll_y = if let Some(container_state) =
117            element_state.storage.get(&self.element_data.component_id).unwrap().data.downcast_ref::<ContainerState>()
118        {
119            container_state.scroll_state.scroll_y
120        } else {
121            0.0
122        };
123
124        self.finalize_scrollbar(scroll_y);
125        let child_transform = glam::Mat4::from_translation(glam::Vec3::new(0.0, -scroll_y, 0.0));
126
127        for child in self.element_data.children.iter_mut() {
128            let taffy_child_node_id = child.internal.element_data().taffy_node_id;
129            if taffy_child_node_id.is_none() {
130                continue;
131            }
132
133            child.internal.finalize_layout(
134                taffy_tree,
135                taffy_child_node_id.unwrap(),
136                self.element_data.computed_box.position,
137                z_index,
138                transform * child_transform,
139                element_state,
140                pointer,
141                text_context,
142            );
143        }
144    }
145
146    fn as_any(&self) -> &dyn Any {
147        self
148    }
149
150    fn on_event(
151        &self,
152        message: &CraftMessage,
153        element_state: &mut ElementStateStore,
154        _text_context: &mut TextContext,
155        should_style: bool,
156    ) -> Event {
157        self.on_style_event(message, element_state, should_style);
158        let base_state = self.get_base_state_mut(element_state);
159        let container_state = base_state.data.as_mut().downcast_mut::<ContainerState>().unwrap();
160
161        container_state.scroll_state.on_event(message, &self.element_data, &mut base_state.base)
162    }
163
164    fn initialize_state(&mut self, _scaling_factor: f64) -> ElementStateStoreItem {
165        ElementStateStoreItem {
166            base: Default::default(),
167            data: Box::new(ContainerState::default()),
168        }
169    }
170}
171
172impl Container {
173    #[allow(dead_code)]
174    fn get_state<'a>(&self, element_state: &'a ElementStateStore) -> &'a ContainerState {
175        element_state.storage.get(&self.element_data.component_id).unwrap().data.as_ref().downcast_ref().unwrap()
176    }
177
178    pub fn new() -> Container {
179        Container {
180            element_data: Default::default(),
181        }
182    }
183
184    generate_component_methods!();
185}
186
187impl ElementStyles for Container {
188    fn styles_mut(&mut self) -> &mut Style {
189        self.element_data.current_style_mut()
190    }
191}