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