瀏覽代碼

wip: add an image

Jonathan Kelley 3 年之前
父節點
當前提交
7b29fbbad0
共有 8 個文件被更改,包括 334 次插入177 次删除
  1. 10 4
      README.md
  2. 二進制
      examples/example.png
  3. 59 0
      examples/margin.rs
  4. 22 0
      examples/readme.rs
  5. 106 61
      src/attributes.rs
  6. 6 2
      src/layout.rs
  7. 96 103
      src/lib.rs
  8. 35 7
      test.html

+ 10 - 4
README.md

@@ -12,15 +12,21 @@ You can use html-esque semantics with stylesheets, inline styles, tree hierarchy
 
 static App: FC<()> = |cx| {
     cx.render(rsx!{
-        div { width: "100%", height: "3px", border_style: "solid",
-            h1 { "Hello world!" }
-            p  { "This is a paragraph." }
+        div { 
+            width: "100%", 
+            height: "10px",
+            background_color: "red",
+            justify_content: "center",
+            align_items: "center",
+
+
+            "Hello world!"
         }
     })
 }
 ```
 
-an image should go here
+![demo app](examples/example.png)
 
 
 Rink is basically a port of [Ink]() but for Rust and Dioxus. Rink doesn't depend on Node.js or any other JavaScript runtime, so your binaries are portable and beautiful.

二進制
examples/example.png


+ 59 - 0
examples/margin.rs

@@ -0,0 +1,59 @@
+use dioxus::prelude::*;
+
+fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    rink::render_vdom(&dom).unwrap();
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+            background_color: "black",
+            // margin_right: "10px",
+
+
+
+            div {
+                width: "70%",
+                height: "70%",
+                // margin_left: "4px",
+                background_color: "green",
+
+                div {
+                    width: "100%",
+                    height: "100%",
+
+
+                    margin_top: "2px",
+                    margin_bottom: "2px",
+                    margin_left: "2px",
+                    margin_right: "2px",
+                    // flex_shrink: "0",
+
+                    background_color: "red",
+                    justify_content: "center",
+                    align_items: "center",
+                    flex_direction: "column",
+
+
+                    // padding_top: "2px",
+                    // padding_bottom: "2px",
+                    // padding_left: "4px",
+                    // padding_right: "4px",
+
+
+                    "[A]"
+                    "[A]"
+                    "[A]"
+                    "[A]"
+                }
+            }
+
+        }
+    })
+}

+ 22 - 0
examples/readme.rs

@@ -0,0 +1,22 @@
+use dioxus::prelude::*;
+
+fn main() {
+    let mut dom = VirtualDom::new(app);
+    dom.rebuild();
+
+    rink::render_vdom(&dom).unwrap();
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "10px",
+            background_color: "red",
+            justify_content: "center",
+            align_items: "center",
+
+            "Hello world!"
+        }
+    })
+}

+ 106 - 61
src/attributes.rs

@@ -1,6 +1,6 @@
 /*
 - [ ] pub display: Display,
-- [ ] pub position_type: PositionType,
+- [x] pub position_type: PositionType,  --> kinda, stretch doesnt support everything
 - [ ] pub direction: Direction,
 
 - [x] pub flex_direction: FlexDirection,
@@ -9,26 +9,27 @@
 - [x] pub flex_shrink: f32,
 - [x] pub flex_basis: Dimension,
 
-- [ ] pub overflow: Overflow,
+- [x] pub overflow: Overflow, ---> kinda implemented... stretch doesnt have support for directional overflow
 
 - [x] pub align_items: AlignItems,
 - [x] pub align_self: AlignSelf,
 - [x] pub align_content: AlignContent,
 
-- [ ] pub margin: Rect<Dimension>,
-- [ ] pub padding: Rect<Dimension>,
+- [x] pub margin: Rect<Dimension>,
+- [x] pub padding: Rect<Dimension>,
 
 - [x] pub justify_content: JustifyContent,
 - [ ] pub position: Rect<Dimension>,
 - [ ] pub border: Rect<Dimension>,
-- [ ] pub size: Size<Dimension>,
 
+- [ ] pub size: Size<Dimension>, ----> ??? seems to only be relevant for input?
 - [ ] pub min_size: Size<Dimension>,
 - [ ] pub max_size: Size<Dimension>,
+
 - [ ] pub aspect_ratio: Number,
 */
 
-use stretch2::{prelude::*, style::Style};
+use stretch2::{prelude::*, style::PositionType, style::Style};
 use tui::style::Style as TuiStyle;
 
 pub struct StyleModifer {
@@ -137,7 +138,13 @@ pub fn apply_attributes(
         "counter-reset" => {}
 
         "cursor" => {}
-        "direction" => {}
+        "direction" => {
+            match value {
+                "ltr" => style.style.direction = Direction::LTR,
+                "rtl" => style.style.direction = Direction::RTL,
+                _ => {}
+            }
+        }
 
         "display" => apply_display(name, value, style),
 
@@ -231,8 +238,20 @@ pub fn apply_attributes(
         "perspective"
         | "perspective-origin" => {}
 
-        "position" => {}
+        "position" => {
+            match value {
+                "static" => {}
+                "relative" => style.style.position_type = PositionType::Relative,
+                "fixed" => {}
+                "absolute" => style.style.position_type = PositionType::Absolute,
+                "sticky" => {}
+                _ => {}
+            }
+
+        }
+
         "pointer-events" => {}
+
         "quotes" => {}
         "resize" => {}
         "right" => {}
@@ -285,6 +304,29 @@ pub fn apply_attributes(
     }
 }
 
+enum UnitSystem {
+    Percent(f32),
+    Point(f32),
+}
+
+fn parse_value(value: &str) -> Option<UnitSystem> {
+    if value.ends_with("px") {
+        if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
+            Some(UnitSystem::Point(px))
+        } else {
+            None
+        }
+    } else if value.ends_with("%") {
+        if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
+            Some(UnitSystem::Percent(pct))
+        } else {
+            None
+        }
+    } else {
+        None
+    }
+}
+
 fn apply_overflow(name: &str, value: &str, style: &mut StyleModifer) {
     match name {
         // todo: add more overflow support to stretch2
@@ -491,54 +533,35 @@ fn apply_font(name: &str, value: &str, style: &mut StyleModifer) {
 }
 
 fn apply_padding(name: &str, value: &str, style: &mut StyleModifer) {
-    // // left
-    // start: stretch::style::Dimension::Points(10f32),
-
-    // // right?
-    // end: stretch::style::Dimension::Points(10f32),
-
-    // // top?
-    // // top: stretch::style::Dimension::Points(10f32),
-
-    // // bottom?
-    // // bottom: stretch::style::Dimension::Points(10f32),
-
-    match name {
-        "padding" => {
-            if name.ends_with("px") {
-                if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
-                    style.style.padding.bottom = Dimension::Points(px);
-                    style.style.padding.top = Dimension::Points(px);
-                    style.style.padding.start = Dimension::Points(px);
-                    style.style.padding.end = Dimension::Points(px);
-                }
-            } else if name.ends_with("%") {
-                if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
-                    //
-                }
+    match parse_value(value) {
+        Some(UnitSystem::Percent(v)) => match name {
+            "padding" => {
+                let v = Dimension::Percent(v / 100.0);
+                style.style.padding.top = v;
+                style.style.padding.bottom = v;
+                style.style.padding.start = v;
+                style.style.padding.end = v;
             }
-        }
-        "padding-bottom" => {
-            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
-                style.style.padding.bottom = Dimension::Points(px);
+            "padding-bottom" => style.style.padding.bottom = Dimension::Percent(v / 100.0),
+            "padding-left" => style.style.padding.start = Dimension::Percent(v / 100.0),
+            "padding-right" => style.style.padding.end = Dimension::Percent(v / 100.0),
+            "padding-top" => style.style.padding.top = Dimension::Percent(v / 100.0),
+            _ => {}
+        },
+        Some(UnitSystem::Point(v)) => match name {
+            "padding" => {
+                style.style.padding.top = Dimension::Points(v);
+                style.style.padding.bottom = Dimension::Points(v);
+                style.style.padding.start = Dimension::Points(v);
+                style.style.padding.end = Dimension::Points(v);
             }
-        }
-        "padding-left" => {
-            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
-                style.style.padding.start = Dimension::Points(px);
-            }
-        }
-        "padding-right" => {
-            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
-                style.style.padding.end = Dimension::Points(px);
-            }
-        }
-        "padding-top" => {
-            if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
-                style.style.padding.top = Dimension::Points(px);
-            }
-        }
-        _ => {}
+            "padding-bottom" => style.style.padding.bottom = Dimension::Points(v),
+            "padding-left" => style.style.padding.start = Dimension::Points(v),
+            "padding-right" => style.style.padding.end = Dimension::Points(v),
+            "padding-top" => style.style.padding.top = Dimension::Points(v),
+            _ => {}
+        },
+        None => {}
     }
 }
 
@@ -599,12 +622,34 @@ pub fn apply_size(name: &str, value: &str, style: &mut StyleModifer) {
 }
 
 pub fn apply_margin(name: &str, value: &str, style: &mut StyleModifer) {
-    match name {
-        "margin" => {}
-        "margin-bottom" => {}
-        "margin-left" => {}
-        "margin-right" => {}
-        "margin-top" => {}
-        _ => {}
+    match parse_value(value) {
+        Some(UnitSystem::Percent(v)) => match name {
+            "margin" => {
+                let v = Dimension::Percent(v / 100.0);
+                style.style.margin.top = v;
+                style.style.margin.bottom = v;
+                style.style.margin.start = v;
+                style.style.margin.end = v;
+            }
+            "margin-top" => style.style.margin.top = Dimension::Percent(v / 100.0),
+            "margin-bottom" => style.style.margin.bottom = Dimension::Percent(v / 100.0),
+            "margin-left" => style.style.margin.start = Dimension::Percent(v / 100.0),
+            "margin-right" => style.style.margin.end = Dimension::Percent(v / 100.0),
+            _ => {}
+        },
+        Some(UnitSystem::Point(v)) => match name {
+            "margin" => {
+                style.style.margin.top = Dimension::Points(v);
+                style.style.margin.bottom = Dimension::Points(v);
+                style.style.margin.start = Dimension::Points(v);
+                style.style.margin.end = Dimension::Points(v);
+            }
+            "margin-top" => style.style.margin.top = Dimension::Points(v),
+            "margin-bottom" => style.style.margin.bottom = Dimension::Points(v),
+            "margin-left" => style.style.margin.start = Dimension::Points(v),
+            "margin-right" => style.style.margin.end = Dimension::Points(v),
+            _ => {}
+        },
+        None => {}
     }
 }

+ 6 - 2
src/layout.rs

@@ -7,6 +7,12 @@ use crate::{
     TuiNode,
 };
 
+/*
+The layout system uses the lineheight as one point.
+
+stretch uses fractional points, so we can rasterize if we need too, but not with characters
+this means anything thats "1px" is 1 lineheight. Unfortunately, text cannot be smaller or bigger
+*/
 pub fn collect_layout<'a>(
     layout: &mut stretch2::Stretch,
     nodes: &mut HashMap<ElementId, TuiNode<'a>>,
@@ -17,7 +23,6 @@ pub fn collect_layout<'a>(
 
     match node {
         VNode::Text(t) => {
-            //
             let id = t.id.get().unwrap();
             let char_len = t.text.chars().count();
 
@@ -29,7 +34,6 @@ pub fn collect_layout<'a>(
                     // text is as long as it is declared
                     width: Dimension::Points(char_len as f32),
                 },
-
                 ..Default::default()
             };
 

+ 96 - 103
src/lib.rs

@@ -1,11 +1,16 @@
 use anyhow::Result;
 use crossterm::{
-    event::{DisableMouseCapture, EnableMouseCapture},
+    event::{self, DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyEvent},
     execute,
     terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
 };
 use dioxus::core::*;
-use std::{collections::HashMap, io};
+use std::{
+    collections::HashMap,
+    io,
+    sync::mpsc,
+    time::{Duration, Instant},
+};
 use stretch2::{prelude::Size, Stretch};
 use tui::{backend::CrosstermBackend, style::Style as TuiStyle, Terminal};
 
@@ -24,21 +29,6 @@ pub struct TuiNode<'a> {
 }
 
 pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
-    /*
-     -> collect all the nodes with their layout
-     -> solve their layout
-     -> render the nodes in the right place with tui/crosstream
-     -> while rendering, apply styling
-
-     use simd to compare lines for diffing?
-
-    */
-    let mut layout = Stretch::new();
-    let mut nodes = HashMap::new();
-
-    let root_node = vdom.base_scope().root_node();
-    layout::collect_layout(&mut layout, &mut nodes, vdom, root_node);
-
     /*
     Get the terminal to calcualte the layout from
     */
@@ -52,30 +42,90 @@ pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
     let backend = CrosstermBackend::new(io::stdout());
     let mut terminal = Terminal::new(backend).unwrap();
 
-    let dims = terminal.size().unwrap();
-    let width = dims.width;
-    let height = dims.height;
-
-    /*
-    Compute the layout given th terminal size
-    */
-    let node_id = root_node.try_mounted_id().unwrap();
-    let root_layout = nodes[&node_id].layout;
-    layout.compute_layout(
-        root_layout,
-        Size {
-            width: stretch2::prelude::Number::Defined(width as f32),
-            height: stretch2::prelude::Number::Defined(height as f32),
-        },
-    )?;
-
-    terminal.draw(|frame| {
-        //
-        render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
-        assert!(nodes.is_empty());
-    })?;
-
-    std::thread::sleep(std::time::Duration::from_millis(5000));
+    // Setup input handling
+    let (tx, rx) = mpsc::channel();
+    std::thread::spawn(move || {
+        let tick_rate = Duration::from_millis(100);
+        let mut last_tick = Instant::now();
+        loop {
+            // poll for tick rate duration, if no events, sent tick event.
+            let timeout = tick_rate
+                .checked_sub(last_tick.elapsed())
+                .unwrap_or_else(|| Duration::from_secs(0));
+
+            if event::poll(timeout).unwrap() {
+                if let TermEvent::Key(key) = event::read().unwrap() {
+                    tx.send(InputEvent::UserInput(key)).unwrap();
+                }
+            }
+
+            if last_tick.elapsed() >= tick_rate {
+                tx.send(InputEvent::Tick).unwrap();
+                last_tick = Instant::now();
+            }
+        }
+    });
+
+    terminal.clear().unwrap();
+
+    loop {
+        let dims = terminal.size().unwrap();
+        let width = dims.width;
+        let height = dims.height;
+
+        /*
+         -> collect all the nodes with their layout
+         -> solve their layout
+         -> render the nodes in the right place with tui/crosstream
+         -> while rendering, apply styling
+
+         use simd to compare lines for diffing?
+
+        */
+        let mut layout = Stretch::new();
+        let mut nodes = HashMap::new();
+
+        let root_node = vdom.base_scope().root_node();
+        layout::collect_layout(&mut layout, &mut nodes, vdom, root_node);
+        /*
+        Compute the layout given th terminal size
+        */
+        let node_id = root_node.try_mounted_id().unwrap();
+        let root_layout = nodes[&node_id].layout;
+        layout.compute_layout(
+            root_layout,
+            Size {
+                width: stretch2::prelude::Number::Defined(width as f32),
+                height: stretch2::prelude::Number::Defined(height as f32),
+            },
+        )?;
+
+        terminal.draw(|frame| {
+            //
+            render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
+            assert!(nodes.is_empty());
+        })?;
+
+        match rx.recv()? {
+            InputEvent::UserInput(event) => match event.code {
+                KeyCode::Char('q') => {
+                    disable_raw_mode()?;
+                    execute!(
+                        terminal.backend_mut(),
+                        LeaveAlternateScreen,
+                        DisableMouseCapture
+                    )?;
+                    terminal.show_cursor()?;
+                    break;
+                }
+                _ => {} // handle event
+            },
+            InputEvent::Tick => {} // tick
+            InputEvent::Close => {
+                break;
+            }
+        };
+    }
 
     disable_raw_mode()?;
     execute!(
@@ -88,65 +138,8 @@ pub fn render_vdom(vdom: &VirtualDom) -> Result<()> {
     Ok(())
 }
 
-// enum InputEvent {
-//     UserInput(KeyEvent),
-//     Close,
-//     Tick,
-// }
-
-// // Setup input handling
-// let (tx, rx) = mpsc::channel();
-// let tick_rate = Duration::from_millis(100);
-// thread::spawn(move || {
-//     let mut last_tick = Instant::now();
-//     loop {
-//         // poll for tick rate duration, if no events, sent tick event.
-//         let timeout = tick_rate
-//             .checked_sub(last_tick.elapsed())
-//             .unwrap_or_else(|| Duration::from_secs(0));
-
-//         if event::poll(timeout).unwrap() {
-//             if let TermEvent::Key(key) = event::read().unwrap() {
-//                 tx.send(InputEvent::UserInput(key)).unwrap();
-//             }
-//         }
-
-//         // if last_tick.elapsed() >= tick_rate {
-//         //     tx.send(InputEvent::Tick).unwrap();
-//         //     last_tick = Instant::now();
-//         // }
-//     }
-// });
-
-// terminal.clear()?;
-
-// // loop {
-// terminal.draw(|frame| {
-//     // draw the frame
-//     let size = frame.size();
-//     let root_node = vdom.base_scope().root_node();
-//     let block = render_vnode(frame, vdom, root_node);
-
-//     frame.render_widget(block, size);
-// })?;
-
-// // // terminal.draw(|f| ui::draw(f, &mut app))?;
-// match rx.recv()? {
-//     InputEvent::UserInput(event) => match event.code {
-//         KeyCode::Char('q') => {
-//             disable_raw_mode()?;
-//             execute!(
-//                 terminal.backend_mut(),
-//                 LeaveAlternateScreen,
-//                 DisableMouseCapture
-//             )?;
-//             terminal.show_cursor()?;
-//             // break;
-//         }
-//         _ => {} // handle event
-//     },
-//     InputEvent::Tick => {} // tick
-//     InputEvent::Close => {
-//         // break;
-//     }
-// };
+enum InputEvent {
+    UserInput(KeyEvent),
+    Close,
+    Tick,
+}

+ 35 - 7
test.html

@@ -14,32 +14,60 @@
             height: 100%;
             display: flex;
             flex-direction: column;
-            justify-content: center;
-            align-items: center;
+            background-color: black;
+            /* justify-content: center;
+            align-items: center; */
             /* margin: auto; */
         }
         
         .smaller {
-            height: 50%;
+            height: 70%;
+            width: 70%;
+            background-color: green;
+            /* justify-content: center; */
+            /* align-items: center; */
+        }
+        
+        .superinner {
+            height: 100%;
+            width: 100%;
+            /* display: flex; */
+            /*  */
+            margin-top: 20px;
+            margin-bottom: 20px;
+            margin-left: 20px;
+            margin-right: 20px;
+            /*  */
+            background-color: red;
             justify-content: center;
             align-items: center;
-            color: violet;
+            flex-direction: column;
+            /* margin: 20px; */
+            /* margin: 20px; */
         }
     </style>
 </head>
 
 <body>
     <div class="container">
+        <div class="smaller">
+            <div class="superinner">
+                <h1>Hello World</h1>
+                <p>This is a test</p>
+            </div>
+        </div>
+    </div>
+    <!-- <div class="container">
         <div class="smaller">
             hello world
-            <div style="color: red;">
+            <div style="color: green; margin: 40px;">
                 goodbye
-                <div>
+                <div style="color:red;">
                     asdasdasd
                 </div>
             </div>
         </div>
-    </div>
+    </div> -->
 </body>
 
 </html>