1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::component::Component;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum Display {
7 Block,
8 Inline,
9 InlineBlock,
10 Flex,
11 None,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum Position {
17 #[default]
18 Static,
19 Relative,
20 Absolute,
21 Fixed,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
26pub enum FlexDirection {
27 #[default]
28 Row,
29 Column,
30 RowReverse,
31 ColumnReverse,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
36pub enum JustifyContent {
37 #[default]
38 FlexStart,
39 FlexEnd,
40 Center,
41 SpaceBetween,
42 SpaceAround,
43 SpaceEvenly,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
48pub enum AlignItems {
49 #[default]
50 Stretch,
51 FlexStart,
52 FlexEnd,
53 Center,
54 Baseline,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
59pub enum FlexWrap {
60 #[default]
61 NoWrap,
62 Wrap,
63 WrapReverse,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum WordWrapMode {
74 Normal,
78 BreakWord,
83 BreakAll,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
96pub enum TextAlign {
97 #[default]
98 Auto,
99 Left,
100 Center,
101 Right,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
109pub enum VerticalAlign {
110 #[default]
111 Auto,
112 Top,
113 Middle,
114 Bottom,
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
127pub enum BoxSizing {
128 ContentBox,
129 #[default]
130 BorderBox,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
135pub enum Overflow {
136 #[default]
137 Visible,
138 Hidden,
139 Scroll,
140 Auto,
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Default)]
149pub enum SizeDimension {
150 #[default]
151 Auto,
152 GlyphUnits(f32),
154 WorldUnits(f32),
157 Percent(f32),
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Default)]
166pub struct EdgeInsets {
167 pub top: SizeDimension,
168 pub right: SizeDimension,
169 pub bottom: SizeDimension,
170 pub left: SizeDimension,
171}
172
173impl EdgeInsets {
174 pub const ZERO: Self = Self {
175 top: SizeDimension::GlyphUnits(0.0),
176 right: SizeDimension::GlyphUnits(0.0),
177 bottom: SizeDimension::GlyphUnits(0.0),
178 left: SizeDimension::GlyphUnits(0.0),
179 };
180
181 pub fn all(v: f32) -> Self {
182 let sd = SizeDimension::GlyphUnits(v);
183 Self {
184 top: sd,
185 right: sd,
186 bottom: sd,
187 left: sd,
188 }
189 }
190
191 pub fn all_dim(sd: SizeDimension) -> Self {
192 Self {
193 top: sd,
194 right: sd,
195 bottom: sd,
196 left: sd,
197 }
198 }
199
200 pub fn axes(vertical: f32, horizontal: f32) -> Self {
201 let v = SizeDimension::GlyphUnits(vertical);
202 let h = SizeDimension::GlyphUnits(horizontal);
203 Self {
204 top: v,
205 right: h,
206 bottom: v,
207 left: h,
208 }
209 }
210
211 pub fn axes_dim(vertical: SizeDimension, horizontal: SizeDimension) -> Self {
212 Self {
213 top: vertical,
214 right: horizontal,
215 bottom: vertical,
216 left: horizontal,
217 }
218 }
219
220 pub fn resolve(&self, container_w_gu: f32, unit_scale: f32) -> ResolvedInsets {
225 ResolvedInsets {
226 top: resolve_size_inline(self.top, container_w_gu, unit_scale),
227 right: resolve_size_inline(self.right, container_w_gu, unit_scale),
228 bottom: resolve_size_inline(self.bottom, container_w_gu, unit_scale),
229 left: resolve_size_inline(self.left, container_w_gu, unit_scale),
230 }
231 }
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Default)]
236pub struct ResolvedInsets {
237 pub top: f32,
238 pub right: f32,
239 pub bottom: f32,
240 pub left: f32,
241}
242
243impl ResolvedInsets {
244 pub const ZERO: Self = Self {
245 top: 0.0,
246 right: 0.0,
247 bottom: 0.0,
248 left: 0.0,
249 };
250 pub fn horizontal(&self) -> f32 {
251 self.left + self.right
252 }
253 pub fn vertical(&self) -> f32 {
254 self.top + self.bottom
255 }
256}
257
258pub fn resolve_size_inline(sd: SizeDimension, container_w_gu: f32, unit_scale: f32) -> f32 {
267 match sd {
268 SizeDimension::GlyphUnits(v) => v,
269 SizeDimension::WorldUnits(v) => {
270 if unit_scale.abs() > f32::EPSILON {
271 v / unit_scale
272 } else {
273 v
274 }
275 }
276 SizeDimension::Percent(p) => container_w_gu * p / 100.0,
277 SizeDimension::Auto => 0.0,
278 }
279}
280
281#[derive(Debug, Clone, Default)]
286pub struct StylePatch {
287 pub display: Option<Option<Display>>,
288 pub width: Option<SizeDimension>,
289 pub height: Option<SizeDimension>,
290 pub min_width: Option<Option<f32>>,
291 pub max_width: Option<Option<f32>>,
292 pub min_height: Option<Option<f32>>,
293 pub max_height: Option<Option<f32>>,
294 pub margin: Option<EdgeInsets>,
295 pub padding: Option<EdgeInsets>,
296 pub box_sizing: Option<BoxSizing>,
297 pub flex_direction: Option<FlexDirection>,
298 pub justify_content: Option<JustifyContent>,
299 pub align_items: Option<AlignItems>,
300 pub flex_wrap: Option<FlexWrap>,
301 pub row_gap: Option<f32>,
302 pub column_gap: Option<f32>,
303 pub flex_grow: Option<f32>,
304 pub flex_shrink: Option<f32>,
305 pub flex_basis: Option<SizeDimension>,
306 pub position: Option<Position>,
307 pub top: Option<Option<SizeDimension>>,
308 pub right: Option<Option<SizeDimension>>,
309 pub bottom: Option<Option<SizeDimension>>,
310 pub left: Option<Option<SizeDimension>>,
311 pub line_height: Option<f32>,
312 pub font_size: Option<SizeDimension>,
313 pub vertical_align: Option<VerticalAlign>,
314 pub overflow: Option<Overflow>,
315 pub z_index: Option<Option<i32>>,
316 pub background_color: Option<Option<[f32; 4]>>,
317 pub background_z: Option<Option<f32>>,
318 pub color: Option<Option<[f32; 4]>>,
319 pub word_wrap: Option<Option<WordWrapMode>>,
320 pub word_wrap_tokens: Option<Option<Vec<String>>>,
321}
322
323#[derive(Debug, Clone)]
337pub struct StyleComponent {
338 pub display: Option<Display>,
341
342 pub width: SizeDimension,
344 pub height: SizeDimension,
345 pub min_width: Option<f32>,
346 pub max_width: Option<f32>,
347 pub min_height: Option<f32>,
348 pub max_height: Option<f32>,
349
350 pub margin: EdgeInsets,
352 pub padding: EdgeInsets,
353 pub box_sizing: BoxSizing,
355
356 pub flex_direction: FlexDirection,
358 pub justify_content: JustifyContent,
359 pub align_items: AlignItems,
360 pub flex_wrap: FlexWrap,
361 pub row_gap: f32,
362 pub column_gap: f32,
363
364 pub flex_grow: f32,
366 pub flex_shrink: f32,
367 pub flex_basis: SizeDimension,
368
369 pub position: Position,
371 pub top: Option<SizeDimension>,
372 pub right: Option<SizeDimension>,
373 pub bottom: Option<SizeDimension>,
374 pub left: Option<SizeDimension>,
375
376 pub line_height: f32,
379 pub font_size: SizeDimension,
388 pub text_align: TextAlign,
390 pub vertical_align: VerticalAlign,
395
396 pub overflow: Overflow,
398
399 pub z_index: Option<i32>,
401
402 pub background_color: Option<[f32; 4]>,
407 pub background_z: Option<f32>,
414
415 pub color: Option<[f32; 4]>,
423
424 pub word_wrap: Option<WordWrapMode>,
429 pub word_wrap_tokens: Option<Vec<String>>,
432
433 component: Option<ComponentId>,
434}
435
436impl Default for StyleComponent {
437 fn default() -> Self {
438 Self {
439 display: None,
440 width: SizeDimension::Auto,
441 height: SizeDimension::Auto,
442 min_width: None,
443 max_width: None,
444 min_height: None,
445 max_height: None,
446 margin: EdgeInsets::ZERO,
447 padding: EdgeInsets::ZERO,
448 box_sizing: BoxSizing::BorderBox,
449 flex_direction: FlexDirection::Row,
450 justify_content: JustifyContent::FlexStart,
451 align_items: AlignItems::Stretch,
452 flex_wrap: FlexWrap::NoWrap,
453 row_gap: 0.0,
454 column_gap: 0.0,
455 flex_grow: 0.0,
456 flex_shrink: 1.0,
457 flex_basis: SizeDimension::Auto,
458 position: Position::Static,
459 top: None,
460 right: None,
461 bottom: None,
462 left: None,
463 line_height: 1.0,
464 font_size: SizeDimension::GlyphUnits(1.0),
465 text_align: TextAlign::Auto,
466 vertical_align: VerticalAlign::Auto,
467 overflow: Overflow::Visible,
468 z_index: None,
469 background_color: None,
470 background_z: None,
471 color: None,
472 word_wrap: None,
473 word_wrap_tokens: None,
474 component: None,
475 }
476 }
477}
478
479impl StyleComponent {
480 pub fn new() -> Self {
481 Self::default()
482 }
483
484 pub fn apply_patch(&mut self, patch: StylePatch) {
486 if let Some(v) = patch.display {
487 self.display = v;
488 }
489 if let Some(v) = patch.width {
490 self.width = v;
491 }
492 if let Some(v) = patch.height {
493 self.height = v;
494 }
495 if let Some(v) = patch.min_width {
496 self.min_width = v;
497 }
498 if let Some(v) = patch.max_width {
499 self.max_width = v;
500 }
501 if let Some(v) = patch.min_height {
502 self.min_height = v;
503 }
504 if let Some(v) = patch.max_height {
505 self.max_height = v;
506 }
507 if let Some(v) = patch.margin {
508 self.margin = v;
509 }
510 if let Some(v) = patch.padding {
511 self.padding = v;
512 }
513 if let Some(v) = patch.box_sizing {
514 self.box_sizing = v;
515 }
516 if let Some(v) = patch.flex_direction {
517 self.flex_direction = v;
518 }
519 if let Some(v) = patch.justify_content {
520 self.justify_content = v;
521 }
522 if let Some(v) = patch.align_items {
523 self.align_items = v;
524 }
525 if let Some(v) = patch.flex_wrap {
526 self.flex_wrap = v;
527 }
528 if let Some(v) = patch.row_gap {
529 self.row_gap = v;
530 }
531 if let Some(v) = patch.column_gap {
532 self.column_gap = v;
533 }
534 if let Some(v) = patch.flex_grow {
535 self.flex_grow = v;
536 }
537 if let Some(v) = patch.flex_shrink {
538 self.flex_shrink = v;
539 }
540 if let Some(v) = patch.flex_basis {
541 self.flex_basis = v;
542 }
543 if let Some(v) = patch.position {
544 self.position = v;
545 }
546 if let Some(v) = patch.top {
547 self.top = v;
548 }
549 if let Some(v) = patch.right {
550 self.right = v;
551 }
552 if let Some(v) = patch.bottom {
553 self.bottom = v;
554 }
555 if let Some(v) = patch.left {
556 self.left = v;
557 }
558 if let Some(v) = patch.line_height {
559 self.line_height = v;
560 }
561 if let Some(v) = patch.font_size {
562 self.font_size = v;
563 }
564 if let Some(v) = patch.vertical_align {
565 self.vertical_align = v;
566 }
567 if let Some(v) = patch.overflow {
568 self.overflow = v;
569 }
570 if let Some(v) = patch.z_index {
571 self.z_index = v;
572 }
573 if let Some(v) = patch.background_color {
574 self.background_color = v;
575 }
576 if let Some(v) = patch.background_z {
577 self.background_z = v;
578 }
579 if let Some(v) = patch.color {
580 self.color = v;
581 }
582 if let Some(v) = patch.word_wrap {
583 self.word_wrap = v;
584 }
585 if let Some(v) = patch.word_wrap_tokens {
586 self.word_wrap_tokens = v;
587 }
588 }
589}
590
591impl Component for StyleComponent {
592 fn name(&self) -> &'static str {
593 "style"
594 }
595
596 fn set_id(&mut self, id: ComponentId) {
597 self.component = Some(id);
598 }
599
600 fn as_any(&self) -> &dyn std::any::Any {
601 self
602 }
603 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
604 self
605 }
606
607 fn to_mms_ast(
608 &self,
609 _world: &crate::engine::ecs::World,
610 ) -> crate::scripting::ast::ComponentExpression {
611 use crate::engine::ecs::component::ce_helpers::*;
612 ce("Style")
616 }
617}