1#![allow(non_snake_case)]
2
3mod utils;
4
5use wasm_bindgen::prelude::*;
6use js_sys::Reflect;
7use js_sys::Function;
8
9#[cfg(feature = "wee_alloc")]
10#[global_allocator]
11static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
12
13#[wasm_bindgen]
14#[repr(u8)]
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum AlignItems {
17 FlexStart,
18 FlexEnd,
19 Center,
20 Baseline,
21 Stretch,
22}
23
24impl Into<stretch::style::AlignItems> for AlignItems {
25 fn into(self) -> stretch::style::AlignItems {
26 match self {
27 AlignItems::FlexStart => stretch::style::AlignItems::FlexStart,
28 AlignItems::FlexEnd => stretch::style::AlignItems::FlexEnd,
29 AlignItems::Center => stretch::style::AlignItems::Center,
30 AlignItems::Baseline => stretch::style::AlignItems::Baseline,
31 AlignItems::Stretch => stretch::style::AlignItems::Stretch,
32 }
33 }
34}
35
36impl From<i32> for AlignItems {
37 fn from(n: i32) -> Self {
38 match n {
39 0 => AlignItems::FlexStart,
40 1 => AlignItems::FlexEnd,
41 2 => AlignItems::Center,
42 3 => AlignItems::Baseline,
43 4 => AlignItems::Stretch,
44 _ => AlignItems::Stretch,
45 }
46 }
47}
48
49#[wasm_bindgen]
50#[repr(u8)]
51#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub enum AlignSelf {
53 Auto,
54 FlexStart,
55 FlexEnd,
56 Center,
57 Baseline,
58 Stretch,
59}
60
61impl Into<stretch::style::AlignSelf> for AlignSelf {
62 fn into(self) -> stretch::style::AlignSelf {
63 match self {
64 AlignSelf::Auto => stretch::style::AlignSelf::Auto,
65 AlignSelf::FlexStart => stretch::style::AlignSelf::FlexStart,
66 AlignSelf::FlexEnd => stretch::style::AlignSelf::FlexEnd,
67 AlignSelf::Center => stretch::style::AlignSelf::Center,
68 AlignSelf::Baseline => stretch::style::AlignSelf::Baseline,
69 AlignSelf::Stretch => stretch::style::AlignSelf::Stretch,
70 }
71 }
72}
73
74impl From<i32> for AlignSelf {
75 fn from(n: i32) -> Self {
76 match n {
77 0 => AlignSelf::Auto,
78 1 => AlignSelf::FlexStart,
79 2 => AlignSelf::FlexEnd,
80 3 => AlignSelf::Center,
81 4 => AlignSelf::Baseline,
82 5 => AlignSelf::Stretch,
83 _ => AlignSelf::Auto,
84 }
85 }
86}
87
88#[wasm_bindgen]
89#[repr(u8)]
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub enum AlignContent {
92 FlexStart,
93 FlexEnd,
94 Center,
95 Stretch,
96 SpaceBetween,
97 SpaceAround,
98}
99
100impl Into<stretch::style::AlignContent> for AlignContent {
101 fn into(self) -> stretch::style::AlignContent {
102 match self {
103 AlignContent::FlexStart => stretch::style::AlignContent::FlexStart,
104 AlignContent::FlexEnd => stretch::style::AlignContent::FlexEnd,
105 AlignContent::Center => stretch::style::AlignContent::Center,
106 AlignContent::Stretch => stretch::style::AlignContent::Stretch,
107 AlignContent::SpaceBetween => stretch::style::AlignContent::SpaceBetween,
108 AlignContent::SpaceAround => stretch::style::AlignContent::SpaceAround,
109 }
110 }
111}
112
113impl From<i32> for AlignContent {
114 fn from(n: i32) -> Self {
115 match n {
116 0 => AlignContent::FlexStart,
117 1 => AlignContent::FlexEnd,
118 2 => AlignContent::Center,
119 3 => AlignContent::Stretch,
120 4 => AlignContent::SpaceBetween,
121 5 => AlignContent::SpaceAround,
122 _ => AlignContent::Stretch,
123 }
124 }
125}
126
127#[wasm_bindgen]
128#[repr(u8)]
129#[derive(Clone, Copy, Debug, PartialEq, Eq)]
130pub enum Direction {
131 Inherit,
132 LTR,
133 RTL,
134}
135
136impl Into<stretch::style::Direction> for Direction {
137 fn into(self) -> stretch::style::Direction {
138 match self {
139 Direction::Inherit => stretch::style::Direction::Inherit,
140 Direction::LTR => stretch::style::Direction::LTR,
141 Direction::RTL => stretch::style::Direction::RTL,
142 }
143 }
144}
145
146impl From<i32> for Direction {
147 fn from(n: i32) -> Self {
148 match n {
149 0 => Direction::Inherit,
150 1 => Direction::LTR,
151 2 => Direction::RTL,
152 _ => Direction::Inherit,
153 }
154 }
155}
156
157#[wasm_bindgen]
158#[repr(u8)]
159#[derive(Clone, Copy, Debug, PartialEq, Eq)]
160pub enum Display {
161 Flex,
162 None,
163}
164
165impl Into<stretch::style::Display> for Display {
166 fn into(self) -> stretch::style::Display {
167 match self {
168 Display::Flex => stretch::style::Display::Flex,
169 Display::None => stretch::style::Display::None,
170 }
171 }
172}
173
174impl From<i32> for Display {
175 fn from(n: i32) -> Self {
176 match n {
177 0 => Display::Flex,
178 1 => Display::None,
179 _ => Display::Flex,
180 }
181 }
182}
183
184#[wasm_bindgen]
185#[repr(u8)]
186#[derive(Clone, Copy, Debug, PartialEq, Eq)]
187pub enum FlexDirection {
188 Row,
189 Column,
190 RowReverse,
191 ColumnReverse,
192}
193
194impl Into<stretch::style::FlexDirection> for FlexDirection {
195 fn into(self) -> stretch::style::FlexDirection {
196 match self {
197 FlexDirection::Row => stretch::style::FlexDirection::Row,
198 FlexDirection::Column => stretch::style::FlexDirection::Column,
199 FlexDirection::RowReverse => stretch::style::FlexDirection::RowReverse,
200 FlexDirection::ColumnReverse => stretch::style::FlexDirection::ColumnReverse,
201 }
202 }
203}
204
205impl From<i32> for FlexDirection {
206 fn from(n: i32) -> Self {
207 match n {
208 0 => FlexDirection::Row,
209 1 => FlexDirection::Column,
210 2 => FlexDirection::RowReverse,
211 3 => FlexDirection::ColumnReverse,
212 _ => FlexDirection::Row,
213 }
214 }
215}
216
217#[wasm_bindgen]
218#[repr(u8)]
219#[derive(Clone, Copy, Debug, PartialEq, Eq)]
220pub enum JustifyContent {
221 FlexStart,
222 FlexEnd,
223 Center,
224 SpaceBetween,
225 SpaceAround,
226 SpaceEvenly,
227}
228
229impl Into<stretch::style::JustifyContent> for JustifyContent {
230 fn into(self) -> stretch::style::JustifyContent {
231 match self {
232 JustifyContent::FlexStart => stretch::style::JustifyContent::FlexStart,
233 JustifyContent::FlexEnd => stretch::style::JustifyContent::FlexEnd,
234 JustifyContent::Center => stretch::style::JustifyContent::Center,
235 JustifyContent::SpaceBetween => stretch::style::JustifyContent::SpaceBetween,
236 JustifyContent::SpaceAround => stretch::style::JustifyContent::SpaceAround,
237 JustifyContent::SpaceEvenly => stretch::style::JustifyContent::SpaceEvenly,
238 }
239 }
240}
241
242impl From<i32> for JustifyContent {
243 fn from(n: i32) -> Self {
244 match n {
245 0 => JustifyContent::FlexStart,
246 1 => JustifyContent::FlexEnd,
247 2 => JustifyContent::Center,
248 3 => JustifyContent::SpaceBetween,
249 4 => JustifyContent::SpaceAround,
250 5 => JustifyContent::SpaceEvenly,
251 _ => JustifyContent::FlexStart,
252 }
253 }
254}
255
256#[wasm_bindgen]
257#[repr(u8)]
258#[derive(Clone, Copy, Debug, PartialEq, Eq)]
259pub enum Overflow {
260 Visible,
261 Hidden,
262 Scroll,
263}
264
265impl Into<stretch::style::Overflow> for Overflow {
266 fn into(self) -> stretch::style::Overflow {
267 match self {
268 Overflow::Visible => stretch::style::Overflow::Visible,
269 Overflow::Hidden => stretch::style::Overflow::Hidden,
270 Overflow::Scroll => stretch::style::Overflow::Scroll,
271 }
272 }
273}
274
275impl From<i32> for Overflow {
276 fn from(n: i32) -> Self {
277 match n {
278 0 => Overflow::Visible,
279 1 => Overflow::Hidden,
280 2 => Overflow::Scroll,
281 _ => Overflow::Visible,
282 }
283 }
284}
285
286#[wasm_bindgen]
287#[repr(u8)]
288#[derive(Clone, Copy, Debug, PartialEq, Eq)]
289pub enum PositionType {
290 Relative,
291 Absolute,
292}
293
294impl Into<stretch::style::PositionType> for PositionType {
295 fn into(self) -> stretch::style::PositionType {
296 match self {
297 PositionType::Relative => stretch::style::PositionType::Relative,
298 PositionType::Absolute => stretch::style::PositionType::Absolute,
299 }
300 }
301}
302
303impl From<i32> for PositionType {
304 fn from(n: i32) -> Self {
305 match n {
306 0 => PositionType::Relative,
307 1 => PositionType::Absolute,
308 _ => PositionType::Relative,
309 }
310 }
311}
312
313#[wasm_bindgen]
314#[repr(u8)]
315#[derive(Clone, Copy, Debug, PartialEq, Eq)]
316pub enum FlexWrap {
317 NoWrap,
318 Wrap,
319 WrapReverse,
320}
321
322impl Into<stretch::style::FlexWrap> for FlexWrap {
323 fn into(self) -> stretch::style::FlexWrap {
324 match self {
325 FlexWrap::NoWrap => stretch::style::FlexWrap::NoWrap,
326 FlexWrap::Wrap => stretch::style::FlexWrap::Wrap,
327 FlexWrap::WrapReverse => stretch::style::FlexWrap::WrapReverse,
328 }
329 }
330}
331
332impl From<i32> for FlexWrap {
333 fn from(n: i32) -> Self {
334 match n {
335 0 => FlexWrap::NoWrap,
336 1 => FlexWrap::Wrap,
337 2 => FlexWrap::WrapReverse,
338 _ => FlexWrap::NoWrap,
339 }
340 }
341}
342
343#[wasm_bindgen]
344#[derive(Clone, Debug)]
345pub struct Layout {
346 #[wasm_bindgen(readonly)]
347 pub width: f32,
348
349 #[wasm_bindgen(readonly)]
350 pub height: f32,
351
352 #[wasm_bindgen(readonly)]
353 pub x: f32,
354
355 #[wasm_bindgen(readonly)]
356 pub y: f32,
357
358 #[wasm_bindgen(readonly)]
359 pub childCount: usize,
360
361 children: Vec<Layout>,
362}
363
364#[wasm_bindgen]
365impl Layout {
366 fn new(layout: stretch::result::Layout) -> Layout {
367 Layout {
368 width: layout.size.width,
369 height: layout.size.height,
370 x: layout.location.x,
371 y: layout.location.y,
372 childCount: layout.children.len(),
373 children: layout.children.into_iter().map(|l| Layout::new(l)).collect(),
374 }
375 }
376
377 #[wasm_bindgen]
378 pub fn child(&self, at: usize) -> Layout {
379 self.children[at].clone()
380 }
381}
382
383#[wasm_bindgen]
384pub struct Node {
385 node: stretch::node::Node,
386 style: JsValue,
387
388 #[wasm_bindgen(readonly)]
389 pub childCount: usize,
390}
391
392#[wasm_bindgen]
393impl Node {
394
395 #[wasm_bindgen(constructor)]
396 pub fn new(style: &JsValue) -> Node {
397 Node {
398 node: stretch::node::Node::new(parse_style(&style), vec![]),
399 style: style.clone(),
400 childCount: 0,
401 }
402 }
403
404 #[wasm_bindgen(js_name = setMeasure)]
405 pub fn set_measure(&mut self, measure: &JsValue) {
406 let measure = Function::from(measure.clone());
407
408 self.node.set_measure(Some(Box::new(move |constraints| {
409 let widthConstraint = if let stretch::number::Number::Defined(val) = constraints.width {
410 val.into()
411 } else {
412 JsValue::UNDEFINED
413 };
414
415 let heightConstaint = if let stretch::number::Number::Defined(val) = constraints.height {
416 val.into()
417 } else {
418 JsValue::UNDEFINED
419 };
420
421 if let Ok(result) = measure.call2(&JsValue::UNDEFINED, &widthConstraint, &heightConstaint) {
422 let width = get_f32(&result, "width");
423 let height = get_f32(&result, "height");
424
425 if width.is_some() && height.is_some() {
426 return Ok(stretch::geometry::Size {width: width.unwrap(), height: height.unwrap()});
427 }
428 }
429
430 Err(Box::new("Failed in javascript"))
431 })));
432 }
433
434 #[wasm_bindgen(js_name = addChild)]
435 pub fn add_child(&mut self, child: &Node) {
436 self.node.add_child(&child.node);
437 self.childCount += 1;
438 }
439
440 #[wasm_bindgen(js_name = removeChild)]
441 pub fn remove_child(&mut self, child: &Node) {
442 self.node.remove_child(&child.node);
443 self.childCount -= 1;
444 }
445
446 #[wasm_bindgen(js_name = replaceChildAtIndex)]
447 pub fn replace_child_at_index(&mut self, index: usize, child: &Node) {
448 self.node.replace_child_at_index(index, &child.node);
449 }
450
451 #[wasm_bindgen(js_name = removeChildAtIndex)]
452 pub fn remove_child_at_index(&mut self, index: usize) {
453 self.node.remove_child_at_index(index);
454 self.childCount -= 1;
455 }
456
457 #[wasm_bindgen(js_name = getStyle)]
458 pub fn get_style(&self) -> JsValue {
459 self.style.clone()
460 }
461
462 #[wasm_bindgen(js_name = setStyle)]
463 pub fn set_style(&mut self, style: &JsValue) {
464 self.node.set_style(parse_style(style));
465 self.style = style.clone();
466 }
467
468 #[wasm_bindgen(js_name = markDirty)]
469 pub fn mark_dirty(&mut self) {
470 self.node.mark_dirty()
471 }
472
473 #[wasm_bindgen(js_name = isDirty)]
474 pub fn is_dirty(&self) -> bool {
475 self.node.dirty()
476 }
477
478 #[wasm_bindgen(js_name = computeLayout)]
479 pub fn compute_layout(&self, size: &JsValue) -> Layout {
480 Layout::new(
481 self.node.compute_layout(stretch::geometry::Size {
482 width: if let Some(val) = get_f32(size, "width") {
483 stretch::number::Number::Defined(val)
484 } else {
485 stretch::number::Number::Undefined
486 },
487 height: if let Some(val) = get_f32(size, "height") {
488 stretch::number::Number::Defined(val)
489 } else {
490 stretch::number::Number::Undefined
491 }
492 }).unwrap()
493 )
494 }
495}
496
497fn parse_style(style: &JsValue) -> stretch::style::Style {
498 stretch::style::Style {
499 display: get_i32(style, "display").map(|i| Display::from(i).into()).unwrap_or(Default::default()),
500 position_type: get_i32(style, "positionType").map(|i| PositionType::from(i).into()).unwrap_or(Default::default()),
501 direction: get_i32(style, "direction").map(|i| Direction::from(i).into()).unwrap_or(Default::default()),
502 flex_direction: get_i32(style, "flexDirection").map(|i| FlexDirection::from(i).into()).unwrap_or(Default::default()),
503 flex_wrap: get_i32(style, "flexWrap").map(|i| FlexWrap::from(i).into()).unwrap_or(Default::default()),
504 overflow: get_i32(style, "overflow").map(|i| Overflow::from(i).into()).unwrap_or(Default::default()),
505 align_items: get_i32(style, "alignItems").map(|i| AlignItems::from(i).into()).unwrap_or(Default::default()),
506 align_self: get_i32(style, "alignSelf").map(|i| AlignSelf::from(i).into()).unwrap_or(Default::default()),
507 align_content: get_i32(style, "alignContent").map(|i| AlignContent::from(i).into()).unwrap_or(Default::default()),
508 justify_content: get_i32(style, "justifyContent").map(|i| JustifyContent::from(i).into()).unwrap_or(Default::default()),
509
510 position: stretch::geometry::Rect {
511 start: get_dimension(style, "start"),
512 end: get_dimension(style, "end"),
513 top: get_dimension(style, "top"),
514 bottom: get_dimension(style, "bottom"),
515 },
516
517 margin: stretch::geometry::Rect {
518 start: get_dimension(style, "marginStart"),
519 end: get_dimension(style, "marginEnd"),
520 top: get_dimension(style, "marginTop"),
521 bottom: get_dimension(style, "marginBottom"),
522 },
523
524 padding: stretch::geometry::Rect {
525 start: get_dimension(style, "paddingStart"),
526 end: get_dimension(style, "paddingEnd"),
527 top: get_dimension(style, "paddingTop"),
528 bottom: get_dimension(style, "paddingBottom"),
529 },
530
531 border: stretch::geometry::Rect {
532 start: get_dimension(style, "borderStart"),
533 end: get_dimension(style, "borderEnd"),
534 top: get_dimension(style, "borderTop"),
535 bottom: get_dimension(style, "borderBottom"),
536 },
537
538 flex_grow: get_f32(style, "flexGrow").unwrap_or(0.0),
539 flex_shrink: get_f32(style, "flexShrink").unwrap_or(1.0),
540 flex_basis: get_dimension(style, "flexBasis"),
541
542 size: stretch::geometry::Size {
543 width: get_dimension(style, "width"),
544 height: get_dimension(style, "height"),
545 },
546
547 min_size: stretch::geometry::Size {
548 width: get_dimension(style, "minWidth"),
549 height: get_dimension(style, "minHeight"),
550 },
551
552 max_size: stretch::geometry::Size {
553 width: get_dimension(style, "maxWidth"),
554 height: get_dimension(style, "maxHeight"),
555 },
556
557 aspect_ratio: get_f32(style, "aspectRatio").map(|f| stretch::number::Number::Defined(f)).unwrap_or(stretch::number::Number::Undefined),
558
559 ..Default::default()
560 }
561}
562
563fn get_dimension(obj: &JsValue, key: &str) -> stretch::style::Dimension {
564 if has_key(obj, key) {
565 if let Ok(val) = Reflect::get(obj, &key.into()) {
566 if let Some(number) = val.as_f64() {
567 return stretch::style::Dimension::Points(number as f32);
568 }
569 if let Some(string) = val.as_string() {
570 if string == "auto" {
571 return stretch::style::Dimension::Auto;
572 }
573 if let Ok(number) = string.parse::<f32>() {
574 return stretch::style::Dimension::Points(number);
575 }
576 if string.chars().last().unwrap() == '%' {
577 let len = string.len();
578 if let Ok(number) = string[..len-1].parse::<f32>() {
579 return stretch::style::Dimension::Points(number);
580 }
581 }
582 }
583 }
584 }
585 stretch::style::Dimension::Undefined
586}
587
588fn get_i32(obj: &JsValue, key: &str) -> Option<i32> {
589 if has_key(obj, key) {
590 if let Ok(val) = Reflect::get(obj, &key.into()) {
591 return val.as_f64().map(|v| v as i32)
592 }
593 }
594 None
595}
596
597fn get_f32(obj: &JsValue, key: &str) -> Option<f32> {
598 if has_key(obj, key) {
599 if let Ok(val) = Reflect::get(obj, &key.into()) {
600 return val.as_f64().map(|v| v as f32)
601 }
602 }
603 None
604}
605
606fn has_key(obj: &JsValue, key: &str) -> bool {
607 if let Ok(exists) = Reflect::has(obj, &key.into()) {
608 exists
609 } else {
610 false
611 }
612}
613