1use crate::internal::InternalLower;
2use crate::lowering::{InternalIrBuilder, InternalLoweringCx};
3use crate::ui::Widget;
4use fission_ir::{
5 op::{BoxShadow, Color, Fill, LayoutOp, Op, PaintOp, Stroke},
6 WidgetId,
7};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct Container {
29 pub id: Option<WidgetId>,
31 pub child: Option<Widget>,
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: impl Into<Widget>) -> Self {
97 Self {
98 child: Some(child.into()),
99 ..Default::default()
100 }
101 }
102
103 pub fn size(mut self, w: f32, h: f32) -> Self {
104 self.width = Some(w);
105 self.height = Some(h);
106 self
107 }
108
109 pub fn width(mut self, w: f32) -> Self {
110 self.width = Some(w);
111 self
112 }
113
114 pub fn height(mut self, h: f32) -> Self {
115 self.height = Some(h);
116 self
117 }
118
119 pub fn min_width(mut self, w: f32) -> Self {
120 self.min_width = Some(w);
121 self
122 }
123
124 pub fn max_width(mut self, w: f32) -> Self {
125 self.max_width = Some(w);
126 self
127 }
128
129 pub fn min_height(mut self, h: f32) -> Self {
130 self.min_height = Some(h);
131 self
132 }
133
134 pub fn max_height(mut self, h: f32) -> Self {
135 self.max_height = Some(h);
136 self
137 }
138
139 pub fn padding_all(mut self, p: f32) -> Self {
140 self.padding = [p; 4];
141 self
142 }
143
144 pub fn padding(mut self, padding: [f32; 4]) -> Self {
145 self.padding = padding;
146 self
147 }
148
149 pub fn flex_grow(mut self, grow: f32) -> Self {
150 self.flex_grow = grow;
151 self
152 }
153
154 pub fn flex_shrink(mut self, shrink: f32) -> Self {
155 self.flex_shrink = shrink;
156 self
157 }
158
159 pub fn bg(mut self, color: Color) -> Self {
160 self.background_fill = Some(Fill::Solid(color));
161 self.background_color = Some(color);
162 self
163 }
164
165 pub fn bg_fill(mut self, fill: Fill) -> Self {
166 self.background_fill = Some(fill);
167 self.background_color = None;
168 self
169 }
170
171 pub fn border(mut self, color: Color, width: f32) -> Self {
172 self.border_color = Some(color);
173 self.border_width = width;
174 self
175 }
176
177 pub fn border_radius(mut self, radius: f32) -> Self {
178 self.border_radius = radius;
179 self
180 }
181
182 pub fn shadow(mut self, shadow: BoxShadow) -> Self {
183 self.shadow = Some(shadow);
184 self
185 }
186
187 pub fn shadows(mut self, shadows: Vec<BoxShadow>) -> Self {
188 self.shadows = shadows;
189 self
190 }
191}
192
193impl InternalLower for Container {
194 fn lower(&self, cx: &mut InternalLoweringCx) -> WidgetId {
195 let id = self.id.map(Into::into).unwrap_or_else(|| cx.next_node_id());
196 cx.push_scope(id);
197
198 let mut children_ids = Vec::new();
199
200 if self.background_fill.is_some()
202 || self.background_color.is_some()
203 || self.border_color.is_some()
204 || self.shadow.is_some()
205 || !self.shadows.is_empty()
206 {
207 for shadow in &self.shadows {
208 let paint = InternalIrBuilder::new(
209 cx.next_node_id(),
210 Op::Paint(PaintOp::DrawRect {
211 fill: None,
212 stroke: None,
213 corner_radius: self.border_radius,
214 shadow: Some(*shadow),
215 }),
216 )
217 .build(cx);
218 children_ids.push(paint);
219 }
220 let paint = InternalIrBuilder::new(
221 cx.next_node_id(),
222 Op::Paint(PaintOp::DrawRect {
223 fill: self
224 .background_fill
225 .clone()
226 .or_else(|| self.background_color.map(Fill::Solid)),
227 stroke: self.border_color.map(|c| Stroke {
228 fill: Fill::Solid(c),
229 width: self.border_width,
230 dash_array: None,
231 line_cap: fission_ir::op::LineCap::Butt,
232 line_join: fission_ir::op::LineJoin::Miter,
233 }),
234 corner_radius: self.border_radius,
235 shadow: self.shadow,
236 }),
237 )
238 .build(cx);
239 children_ids.push(paint);
240 }
241
242 if let Some(child) = &self.child {
244 children_ids.push(child.lower(cx));
245 }
246
247 cx.pop_scope();
248
249 let mut layout = InternalIrBuilder::new(
250 id,
251 Op::Layout(LayoutOp::Box {
252 width: self.width,
253 height: self.height,
254 min_width: self.min_width,
255 max_width: self.max_width,
256 min_height: self.min_height,
257 max_height: self.max_height,
258 padding: self.padding,
259 flex_grow: self.flex_grow,
260 flex_shrink: self.flex_shrink,
261 aspect_ratio: None,
262 }),
263 );
264
265 for cid in children_ids {
266 layout.add_child(cid);
267 }
268
269 layout.build(cx)
270 }
271}