tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 1118HTML 1044Markdown 12TOML 94787 Definiciones

⎇ bce89b1006f36ce35abfaf1f1c2b3dd95f3dffd3 bindings/js/src/lib.rs Rust · 644 líneas · 19.0 KB Historia Quién tocó qué crudo

Implement new rust API for bindings and release v0.3 bce89b100 · Emil Sjölander ·

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}