1use crate::lowering::{LoweringContext, NodeBuilder};
2use crate::ui::traits::Lower;
3use crate::ui::Node;
4use fission_ir::{
5 op::{BoxShadow, Color, Fill, LayoutOp, Op, PaintOp, Stroke},
6 NodeId,
7};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct Container {
29 pub id: Option<NodeId>,
31 pub child: Option<Box<Node>>,
33
34 pub width: Option<f32>,
37 pub height: Option<f32>,
39 pub min_width: Option<f32>,
41 pub max_width: Option<f32>,
43 pub min_height: Option<f32>,
45 pub max_height: Option<f32>,
47 pub padding: [f32; 4],
49 pub flex_grow: f32,
51 pub flex_shrink: f32,
53
54 pub background_fill: Option<Fill>,
57 pub background_color: Option<Color>,
59 pub border_color: Option<Color>,
61 pub border_width: f32,
63 pub border_radius: f32,
65 pub shadow: Option<BoxShadow>,
67 pub shadows: Vec<BoxShadow>,
69}
70
71impl Default for Container {
72 fn default() -> Self {
73 Self {
74 id: None,
75 child: None,
76 width: None,
77 height: None,
78 min_width: None,
79 max_width: None,
80 min_height: None,
81 max_height: None,
82 padding: [0.0; 4],
83 flex_grow: 0.0,
84 flex_shrink: 1.0,
85 background_fill: None,
86 background_color: None,
87 border_color: None,
88 border_width: 0.0,
89 border_radius: 0.0,
90 shadow: None,
91 shadows: Vec::new(),
92 }
93 }
94}
95impl Container {
96 pub fn new(child: Node) -> Self {
97 Self {
98 child: Some(Box::new(child)),
99 ..Default::default()
100 }
101 }
102
103 pub fn id(mut self, id: NodeId) -> Self {
104 self.id = Some(id);
105 self
106 }
107
108 pub fn size(mut self, w: f32, h: f32) -> Self {
109 self.width = Some(w);
110 self.height = Some(h);
111 self
112 }
113
114 pub fn width(mut self, w: f32) -> Self {
115 self.width = Some(w);
116 self
117 }
118
119 pub fn height(mut self, h: f32) -> Self {
120 self.height = Some(h);
121 self
122 }
123
124 pub fn min_width(mut self, w: f32) -> Self {
125 self.min_width = Some(w);
126 self
127 }
128
129 pub fn max_width(mut self, w: f32) -> Self {
130 self.max_width = Some(w);
131 self
132 }
133
134 pub fn min_height(mut self, h: f32) -> Self {
135 self.min_height = Some(h);
136 self
137 }
138
139 pub fn max_height(mut self, h: f32) -> Self {
140 self.max_height = Some(h);
141 self
142 }
143
144 pub fn padding_all(mut self, p: f32) -> Self {
145 self.padding = [p; 4];
146 self
147 }
148
149 pub fn padding(mut self, padding: [f32; 4]) -> Self {
150 self.padding = padding;
151 self
152 }
153
154 pub fn flex_grow(mut self, grow: f32) -> Self {
155 self.flex_grow = grow;
156 self
157 }
158
159 pub fn flex_shrink(mut self, shrink: f32) -> Self {
160 self.flex_shrink = shrink;
161 self
162 }
163
164 pub fn bg(mut self, color: Color) -> Self {
165 self.background_fill = Some(Fill::Solid(color));
166 self.background_color = Some(color);
167 self
168 }
169
170 pub fn bg_fill(mut self, fill: Fill) -> Self {
171 self.background_fill = Some(fill);
172 self.background_color = None;
173 self
174 }
175
176 pub fn border(mut self, color: Color, width: f32) -> Self {
177 self.border_color = Some(color);
178 self.border_width = width;
179 self
180 }
181
182 pub fn border_radius(mut self, radius: f32) -> Self {
183 self.border_radius = radius;
184 self
185 }
186
187 pub fn shadow(mut self, shadow: BoxShadow) -> Self {
188 self.shadow = Some(shadow);
189 self
190 }
191
192 pub fn shadows(mut self, shadows: Vec<BoxShadow>) -> Self {
193 self.shadows = shadows;
194 self
195 }
196
197 pub fn into_node(self) -> Node {
198 Node::Container(self)
199 }
200}
201
202impl Lower for Container {
203 fn lower(&self, cx: &mut LoweringContext) -> NodeId {
204 let id = self.id.unwrap_or_else(|| cx.next_node_id());
205 cx.push_scope(id);
206
207 let mut children_ids = Vec::new();
208
209 if self.background_fill.is_some()
211 || self.background_color.is_some()
212 || self.border_color.is_some()
213 || self.shadow.is_some()
214 || !self.shadows.is_empty()
215 {
216 for shadow in &self.shadows {
217 let paint = NodeBuilder::new(
218 cx.next_node_id(),
219 Op::Paint(PaintOp::DrawRect {
220 fill: None,
221 stroke: None,
222 corner_radius: self.border_radius,
223 shadow: Some(*shadow),
224 }),
225 )
226 .build(cx);
227 children_ids.push(paint);
228 }
229 let paint = NodeBuilder::new(
230 cx.next_node_id(),
231 Op::Paint(PaintOp::DrawRect {
232 fill: self
233 .background_fill
234 .clone()
235 .or_else(|| self.background_color.map(Fill::Solid)),
236 stroke: self.border_color.map(|c| Stroke {
237 fill: Fill::Solid(c),
238 width: self.border_width,
239 dash_array: None,
240 line_cap: fission_ir::op::LineCap::Butt,
241 line_join: fission_ir::op::LineJoin::Miter,
242 }),
243 corner_radius: self.border_radius,
244 shadow: self.shadow,
245 }),
246 )
247 .build(cx);
248 children_ids.push(paint);
249 }
250
251 if let Some(child) = &self.child {
253 children_ids.push(child.lower(cx));
254 }
255
256 cx.pop_scope();
257
258 let mut layout = NodeBuilder::new(
259 id,
260 Op::Layout(LayoutOp::Box {
261 width: self.width,
262 height: self.height,
263 min_width: self.min_width,
264 max_width: self.max_width,
265 min_height: self.min_height,
266 max_height: self.max_height,
267 padding: self.padding,
268 flex_grow: self.flex_grow,
269 flex_shrink: self.flex_shrink,
270 aspect_ratio: None,
271 }),
272 );
273
274 for cid in children_ids {
275 layout.add_child(cid);
276 }
277
278 layout.build(cx)
279 }
280}