tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91822 Definiciones

⎇ a33184b2d8cdcb69578f3f5789e1cbfd6f85ec7b bindings/js/src/lib.rs Rust · 613 líneas · 17.7 KB Historia Quién tocó qué crudo

Implement js bindings a33184b2d · Emil Sjölander ·

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