mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 14:44:12 +00:00
feat: wire up stretch into place
This commit is contained in:
parent
e3171e8303
commit
dc34805ee6
14 changed files with 1430 additions and 257 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"rust-analyzer.inlayHints.enable": true
|
||||
}
|
15
Cargo.toml
15
Cargo.toml
|
@ -6,12 +6,13 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
stretch = "0.3.2"
|
||||
tui = { version = "0.15.0", features = ["crossterm"] }
|
||||
crossterm = "0.20.0"
|
||||
tui = { version = "0.16.0", features = ["crossterm"] }
|
||||
crossterm = "0.22.1"
|
||||
anyhow = "1.0.42"
|
||||
thiserror = "1.0.24"
|
||||
dioxus = { path = "../../dioxus", features = ["core"] }
|
||||
hecs = "0.6.0"
|
||||
tui-template = { path = "../../Tinkering/tui-builder" }
|
||||
# tui-template = { git = "https://github.com/jkelleyrtp/tui-builder.git" }
|
||||
dioxus = { path = "../../dioxus" }
|
||||
hecs = "0.7.3"
|
||||
ctrlc = "3.2.1"
|
||||
bumpalo = { version = "3.8.0", features = ["boxed"] }
|
||||
stretch2 = { path = "../../Tinkering/stretch2" }
|
||||
# stretch2 = { git = "https://github.com/elbaro/stretch2.git" }
|
||||
|
|
85
examples/frame.rs
Normal file
85
examples/frame.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
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",
|
||||
// justify_content: "center",
|
||||
// align_items: "center",
|
||||
// flex_direction: "row",
|
||||
// background_color: "red",
|
||||
|
||||
p {
|
||||
background_color: "black",
|
||||
flex_direction: "column",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
// height: "10%",
|
||||
"hi"
|
||||
"hi"
|
||||
"hi"
|
||||
}
|
||||
|
||||
li {
|
||||
background_color: "red",
|
||||
flex_direction: "column",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
// height: "10%",
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
}
|
||||
li {
|
||||
background_color: "blue",
|
||||
flex_direction: "column",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
// height: "10%",
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
}
|
||||
p {
|
||||
background_color: "yellow",
|
||||
"asd"
|
||||
}
|
||||
p {
|
||||
background_color: "green",
|
||||
"asd"
|
||||
}
|
||||
p {
|
||||
background_color: "white",
|
||||
"asd"
|
||||
}
|
||||
p {
|
||||
background_color: "cyan",
|
||||
"asd"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
53
examples/layouts.rs
Normal file
53
examples/layouts.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use dioxus::{core::ElementId, prelude::*};
|
||||
use rink::TuiNode;
|
||||
|
||||
fn main() {
|
||||
let mut dom = VirtualDom::new(app);
|
||||
dom.rebuild();
|
||||
|
||||
let mut layout = stretch2::Stretch::new();
|
||||
let mut nodes = HashMap::new();
|
||||
rink::collect_layout(&mut layout, &mut nodes, &dom, dom.base_scope().root_node());
|
||||
|
||||
let node = nodes
|
||||
.remove(&dom.base_scope().root_node().mounted_id())
|
||||
.unwrap();
|
||||
|
||||
layout
|
||||
.compute_layout(node.layout, stretch2::geometry::Size::undefined())
|
||||
.unwrap();
|
||||
|
||||
for (id, node) in nodes.drain() {
|
||||
println!("{:?}", layout.layout(node.layout));
|
||||
}
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
flex_direction: "column",
|
||||
|
||||
div {
|
||||
"hi"
|
||||
}
|
||||
div {
|
||||
"bi"
|
||||
"bi"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// fn print_layout(mut nodes: HashMap<ElementId, TuiNode>, node: &VNode) {
|
||||
// match node {
|
||||
// VNode::Text(_) => todo!(),
|
||||
// VNode::Element(_) => todo!(),
|
||||
// VNode::Fragment(_) => todo!(),
|
||||
// VNode::Component(_) => todo!(),
|
||||
// VNode::Placeholder(_) => todo!(),
|
||||
// }
|
||||
// }
|
31
examples/list.rs
Normal file
31
examples/list.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
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",
|
||||
border_width: "1px",
|
||||
|
||||
h1 { height: "2px", color: "green",
|
||||
"that's awesome!"
|
||||
}
|
||||
|
||||
ul {
|
||||
flex_direction: "column",
|
||||
padding_left: "3px",
|
||||
(0..10).map(|i| rsx!(
|
||||
"> hello {i}"
|
||||
))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
63
examples/quadrants.rs
Normal file
63
examples/quadrants.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
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",
|
||||
|
||||
div {
|
||||
width: "100%",
|
||||
height: "50%",
|
||||
flex_direction: "row",
|
||||
div {
|
||||
border_width: "1px",
|
||||
width: "50%",
|
||||
height: "100%",
|
||||
background_color: "red",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
"[A]"
|
||||
}
|
||||
div {
|
||||
width: "50%",
|
||||
height: "100%",
|
||||
background_color: "black",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
"[B]"
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
width: "100%",
|
||||
height: "50%",
|
||||
flex_direction: "row",
|
||||
div {
|
||||
width: "50%",
|
||||
height: "100%",
|
||||
background_color: "green",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
"[C]"
|
||||
}
|
||||
div {
|
||||
width: "50%",
|
||||
height: "100%",
|
||||
background_color: "blue",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
"[D]"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
34
examples/strecher.rs
Normal file
34
examples/strecher.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use stretch2::prelude::*;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let mut stretch = Stretch::new();
|
||||
|
||||
let child = stretch.new_node(
|
||||
Style {
|
||||
size: Size {
|
||||
width: Dimension::Percent(0.5),
|
||||
height: Dimension::Auto,
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&[],
|
||||
)?;
|
||||
|
||||
let node = stretch.new_node(
|
||||
Style {
|
||||
size: Size {
|
||||
width: Dimension::Points(100.0),
|
||||
height: Dimension::Points(100.0),
|
||||
},
|
||||
justify_content: JustifyContent::Center,
|
||||
..Default::default()
|
||||
},
|
||||
&[child],
|
||||
)?;
|
||||
|
||||
stretch.compute_layout(node, Size::undefined())?;
|
||||
println!("node: {:#?}", stretch.layout(node)?);
|
||||
println!("child: {:#?}", stretch.layout(child)?);
|
||||
|
||||
Ok(())
|
||||
}
|
85
examples/text.rs
Normal file
85
examples/text.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
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",
|
||||
// justify_content: "center",
|
||||
// align_items: "center",
|
||||
// flex_direction: "row",
|
||||
// background_color: "red",
|
||||
|
||||
p {
|
||||
background_color: "black",
|
||||
flex_direction: "column",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
// height: "10%",
|
||||
"hi"
|
||||
"hi"
|
||||
"hi"
|
||||
}
|
||||
|
||||
li {
|
||||
background_color: "red",
|
||||
flex_direction: "column",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
// height: "10%",
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
"bib"
|
||||
}
|
||||
li {
|
||||
background_color: "blue",
|
||||
flex_direction: "column",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
// height: "10%",
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
}
|
||||
p {
|
||||
background_color: "yellow",
|
||||
"asd"
|
||||
}
|
||||
p {
|
||||
background_color: "green",
|
||||
"asd"
|
||||
}
|
||||
p {
|
||||
background_color: "white",
|
||||
"asd"
|
||||
}
|
||||
p {
|
||||
background_color: "cyan",
|
||||
"asd"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
610
src/attributes.rs
Normal file
610
src/attributes.rs
Normal file
|
@ -0,0 +1,610 @@
|
|||
/*
|
||||
- [ ] pub display: Display,
|
||||
- [ ] pub position_type: PositionType,
|
||||
- [ ] pub direction: Direction,
|
||||
|
||||
- [x] pub flex_direction: FlexDirection,
|
||||
- [x] pub flex_wrap: FlexWrap,
|
||||
- [x] pub flex_grow: f32,
|
||||
- [x] pub flex_shrink: f32,
|
||||
- [x] pub flex_basis: Dimension,
|
||||
|
||||
- [ ] pub overflow: 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 justify_content: JustifyContent,
|
||||
- [ ] pub position: Rect<Dimension>,
|
||||
- [ ] pub border: Rect<Dimension>,
|
||||
- [ ] pub size: Size<Dimension>,
|
||||
|
||||
- [ ] pub min_size: Size<Dimension>,
|
||||
- [ ] pub max_size: Size<Dimension>,
|
||||
- [ ] pub aspect_ratio: Number,
|
||||
*/
|
||||
|
||||
use stretch2::{prelude::*, style::Style};
|
||||
use tui::style::Style as TuiStyle;
|
||||
|
||||
pub struct StyleModifer {
|
||||
pub style: Style,
|
||||
pub tui_style: TuiStyle,
|
||||
}
|
||||
|
||||
enum TuiModifier {
|
||||
Text,
|
||||
}
|
||||
|
||||
/// applies the entire html namespace defined in dioxus-html
|
||||
pub fn apply_attributes(
|
||||
//
|
||||
name: &str,
|
||||
value: &str,
|
||||
style: &mut StyleModifer,
|
||||
) {
|
||||
match name {
|
||||
"align-content"
|
||||
| "align-items"
|
||||
| "align-self" => apply_align(name, value, style),
|
||||
|
||||
"animation"
|
||||
| "animation-delay"
|
||||
| "animation-direction"
|
||||
| "animation-duration"
|
||||
| "animation-fill-mode"
|
||||
| "animation-iteration-count"
|
||||
| "animation-name"
|
||||
| "animation-play-state"
|
||||
| "animation-timing-function" => apply_animation(name, value, style),
|
||||
|
||||
"backface-visibility" => {}
|
||||
|
||||
"background"
|
||||
| "background-attachment"
|
||||
| "background-clip"
|
||||
| "background-color"
|
||||
| "background-image"
|
||||
| "background-origin"
|
||||
| "background-position"
|
||||
| "background-repeat"
|
||||
| "background-size" => apply_background(name, value, style),
|
||||
|
||||
"border"
|
||||
| "border-bottom"
|
||||
| "border-bottom-color"
|
||||
| "border-bottom-left-radius"
|
||||
| "border-bottom-right-radius"
|
||||
| "border-bottom-style"
|
||||
| "border-bottom-width"
|
||||
| "border-collapse"
|
||||
| "border-color"
|
||||
| "border-image"
|
||||
| "border-image-outset"
|
||||
| "border-image-repeat"
|
||||
| "border-image-slice"
|
||||
| "border-image-source"
|
||||
| "border-image-width"
|
||||
| "border-left"
|
||||
| "border-left-color"
|
||||
| "border-left-style"
|
||||
| "border-left-width"
|
||||
| "border-radius"
|
||||
| "border-right"
|
||||
| "border-right-color"
|
||||
| "border-right-style"
|
||||
| "border-right-width"
|
||||
| "border-spacing"
|
||||
| "border-style"
|
||||
| "border-top"
|
||||
| "border-top-color"
|
||||
| "border-top-left-radius"
|
||||
| "border-top-right-radius"
|
||||
| "border-top-style"
|
||||
| "border-top-width"
|
||||
| "border-width" => apply_border(name, value, style),
|
||||
|
||||
"bottom" => {}
|
||||
"box-shadow" => {}
|
||||
"box-sizing" => {}
|
||||
"caption-side" => {}
|
||||
"clear" => {}
|
||||
"clip" => {}
|
||||
|
||||
"color" => {
|
||||
// text color
|
||||
}
|
||||
|
||||
"column-count"
|
||||
| "column-fill"
|
||||
| "column-gap"
|
||||
| "column-rule"
|
||||
| "column-rule-color"
|
||||
| "column-rule-style"
|
||||
| "column-rule-width"
|
||||
| "column-span"
|
||||
// add column-width
|
||||
| "column-width" => apply_column(name, value, style),
|
||||
|
||||
"columns" => {}
|
||||
|
||||
"content" => {}
|
||||
"counter-increment" => {}
|
||||
"counter-reset" => {}
|
||||
|
||||
"cursor" => {}
|
||||
"direction" => {}
|
||||
|
||||
"display" => apply_display(name, value, style),
|
||||
|
||||
"empty-cells" => {}
|
||||
|
||||
"flex"
|
||||
| "flex-basis"
|
||||
| "flex-direction"
|
||||
| "flex-flow"
|
||||
| "flex-grow"
|
||||
| "flex-shrink"
|
||||
| "flex-wrap" => apply_flex(name, value, style),
|
||||
|
||||
"float" => {}
|
||||
|
||||
"font"
|
||||
| "font-family"
|
||||
| "font-size"
|
||||
| "font-size-adjust"
|
||||
| "font-stretch"
|
||||
| "font-style"
|
||||
| "font-variant"
|
||||
| "font-weight" => apply_font(name, value, style),
|
||||
|
||||
"height" => {
|
||||
if value.ends_with("%") {
|
||||
if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
|
||||
style.style.size.height = Dimension::Percent(pct / 100.0);
|
||||
}
|
||||
} else if value.ends_with("px") {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.size.height = Dimension::Points(px);
|
||||
}
|
||||
}
|
||||
}
|
||||
"justify-content" => {
|
||||
use JustifyContent::*;
|
||||
style.style.justify_content = match value {
|
||||
"flex-start" => FlexStart,
|
||||
"flex-end" => FlexEnd,
|
||||
"center" => Center,
|
||||
"space-between" => SpaceBetween,
|
||||
"space-around" => SpaceAround,
|
||||
"space-evenly" => SpaceEvenly,
|
||||
_ => FlexStart,
|
||||
};
|
||||
}
|
||||
"left" => {}
|
||||
"letter-spacing" => {}
|
||||
"line-height" => {}
|
||||
|
||||
"list-style"
|
||||
| "list-style-image"
|
||||
| "list-style-position"
|
||||
| "list-style-type" => {}
|
||||
|
||||
"margin"
|
||||
| "margin-bottom"
|
||||
| "margin-left"
|
||||
| "margin-right"
|
||||
| "margin-top" => apply_margin(name, value, style),
|
||||
|
||||
"max-height" => {}
|
||||
"max-width" => {}
|
||||
"min-height" => {}
|
||||
"min-width" => {}
|
||||
|
||||
"opacity" => {}
|
||||
"order" => {}
|
||||
"outline" => {}
|
||||
|
||||
"outline-color"
|
||||
| "outline-offset"
|
||||
| "outline-style"
|
||||
| "outline-width" => {}
|
||||
|
||||
"overflow"
|
||||
| "overflow-x"
|
||||
| "overflow-y" => apply_overflow(name, value, style),
|
||||
|
||||
"padding"
|
||||
| "padding-bottom"
|
||||
| "padding-left"
|
||||
| "padding-right"
|
||||
| "padding-top" => apply_padding(name, value, style),
|
||||
|
||||
"page-break-after"
|
||||
| "page-break-before"
|
||||
| "page-break-inside" => {}
|
||||
|
||||
"perspective"
|
||||
| "perspective-origin" => {}
|
||||
|
||||
"position" => {}
|
||||
"pointer-events" => {}
|
||||
"quotes" => {}
|
||||
"resize" => {}
|
||||
"right" => {}
|
||||
"tab-size" => {}
|
||||
"table-layout" => {}
|
||||
|
||||
"text-align"
|
||||
| "text-align-last"
|
||||
| "text-decoration"
|
||||
| "text-decoration-color"
|
||||
| "text-decoration-line"
|
||||
| "text-decoration-style"
|
||||
| "text-indent"
|
||||
| "text-justify"
|
||||
| "text-overflow"
|
||||
| "text-shadow"
|
||||
| "text-transform" => apply_text(name, value, style),
|
||||
|
||||
"top" => {}
|
||||
|
||||
"transform"
|
||||
| "transform-origin"
|
||||
| "transform-style" => apply_transform(name, value, style),
|
||||
|
||||
"transition"
|
||||
| "transition-delay"
|
||||
| "transition-duration"
|
||||
| "transition-property"
|
||||
| "transition-timing-function" => apply_transition(name, value, style),
|
||||
|
||||
"vertical-align" => {}
|
||||
"visibility" => {}
|
||||
"white-space" => {}
|
||||
"width" => {
|
||||
if value.ends_with("%") {
|
||||
if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
|
||||
style.style.size.width = Dimension::Percent(pct / 100.0);
|
||||
}
|
||||
} else if value.ends_with("px") {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.size.width = Dimension::Points(px);
|
||||
}
|
||||
}
|
||||
}
|
||||
"word-break" => {}
|
||||
"word-spacing" => {}
|
||||
"word-wrap" => {}
|
||||
"z-index" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_overflow(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
match name {
|
||||
// todo: add more overflow support to stretch2
|
||||
"overflow" | "overflow-x" | "overflow-y" => {
|
||||
style.style.overflow = match value {
|
||||
"auto" => Overflow::Visible,
|
||||
"hidden" => Overflow::Hidden,
|
||||
"scroll" => Overflow::Scroll,
|
||||
"visible" => Overflow::Visible,
|
||||
_ => Overflow::Visible,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_display(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
use stretch2::style::Display;
|
||||
style.style.display = match value {
|
||||
"flex" => Display::Flex,
|
||||
"block" => Display::None,
|
||||
_ => Display::Flex,
|
||||
}
|
||||
|
||||
// TODO: there are way more variants
|
||||
// stretch needs to be updated to handle them
|
||||
//
|
||||
// "block" => Display::Block,
|
||||
// "inline" => Display::Inline,
|
||||
// "inline-block" => Display::InlineBlock,
|
||||
// "inline-table" => Display::InlineTable,
|
||||
// "list-item" => Display::ListItem,
|
||||
// "run-in" => Display::RunIn,
|
||||
// "table" => Display::Table,
|
||||
// "table-caption" => Display::TableCaption,
|
||||
// "table-cell" => Display::TableCell,
|
||||
// "table-column" => Display::TableColumn,
|
||||
// "table-column-group" => Display::TableColumnGroup,
|
||||
// "table-footer-group" => Display::TableFooterGroup,
|
||||
// "table-header-group" => Display::TableHeaderGroup,
|
||||
// "table-row" => Display::TableRow,
|
||||
// "table-row-group" => Display::TableRowGroup,
|
||||
// "none" => Display::None,
|
||||
// _ => Display::Inline,
|
||||
}
|
||||
|
||||
fn apply_background(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
match name {
|
||||
"background-color" => {
|
||||
use tui::style::Color;
|
||||
match value {
|
||||
"red" => style.tui_style.bg.replace(Color::Red),
|
||||
"green" => style.tui_style.bg.replace(Color::Green),
|
||||
"blue" => style.tui_style.bg.replace(Color::Blue),
|
||||
"yellow" => style.tui_style.bg.replace(Color::Yellow),
|
||||
"cyan" => style.tui_style.bg.replace(Color::Cyan),
|
||||
"magenta" => style.tui_style.bg.replace(Color::Magenta),
|
||||
"white" => style.tui_style.bg.replace(Color::White),
|
||||
"black" => style.tui_style.bg.replace(Color::Black),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
"background" => {}
|
||||
"background-attachment" => {}
|
||||
"background-clip" => {}
|
||||
"background-image" => {}
|
||||
"background-origin" => {}
|
||||
"background-position" => {}
|
||||
"background-repeat" => {}
|
||||
"background-size" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
match name {
|
||||
"border" => {}
|
||||
"border-bottom" => {}
|
||||
"border-bottom-color" => {}
|
||||
"border-bottom-left-radius" => {}
|
||||
"border-bottom-right-radius" => {}
|
||||
"border-bottom-style" => {}
|
||||
"border-bottom-width" => {}
|
||||
"border-collapse" => {}
|
||||
"border-color" => {}
|
||||
"border-image" => {}
|
||||
"border-image-outset" => {}
|
||||
"border-image-repeat" => {}
|
||||
"border-image-slice" => {}
|
||||
"border-image-source" => {}
|
||||
"border-image-width" => {}
|
||||
"border-left" => {}
|
||||
"border-left-color" => {}
|
||||
"border-left-style" => {}
|
||||
"border-left-width" => {}
|
||||
"border-radius" => {}
|
||||
"border-right" => {}
|
||||
"border-right-color" => {}
|
||||
"border-right-style" => {}
|
||||
"border-right-width" => {}
|
||||
"border-spacing" => {}
|
||||
"border-style" => {}
|
||||
"border-top" => {}
|
||||
"border-top-color" => {}
|
||||
"border-top-left-radius" => {}
|
||||
"border-top-right-radius" => {}
|
||||
"border-top-style" => {}
|
||||
"border-top-width" => {}
|
||||
"border-width" => {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
// tuistyle = px;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_animation(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
match name {
|
||||
"animation" => {}
|
||||
"animation-delay" => {}
|
||||
"animation-direction =>{}" => {}
|
||||
"animation-duration" => {}
|
||||
"animation-fill-mode" => {}
|
||||
"animation-itera =>{}tion-count" => {}
|
||||
"animation-name" => {}
|
||||
"animation-play-state" => {}
|
||||
"animation-timing-function" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_column(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
match name {
|
||||
"column-count" => {}
|
||||
"column-fill" => {}
|
||||
"column-gap" => {}
|
||||
"column-rule" => {}
|
||||
"column-rule-color" => {}
|
||||
"column-rule-style" => {}
|
||||
"column-rule-width" => {}
|
||||
"column-span" => {}
|
||||
"column-width" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_flex(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
// - [x] pub flex_direction: FlexDirection,
|
||||
// - [x] pub flex_wrap: FlexWrap,
|
||||
// - [x] pub flex_grow: f32,
|
||||
// - [x] pub flex_shrink: f32,
|
||||
// - [x] pub flex_basis: Dimension,
|
||||
|
||||
match name {
|
||||
"flex" => {}
|
||||
"flex-direction" => {
|
||||
use FlexDirection::*;
|
||||
style.style.flex_direction = match value {
|
||||
"row" => Row,
|
||||
"row-reverse" => RowReverse,
|
||||
"column" => Column,
|
||||
"column-reverse" => ColumnReverse,
|
||||
_ => Row,
|
||||
};
|
||||
}
|
||||
"flex-basis" => {
|
||||
if value.ends_with("%") {
|
||||
if let Ok(pct) = value.trim_end_matches("%").parse::<f32>() {
|
||||
style.style.flex_basis = Dimension::Percent(pct / 100.0);
|
||||
}
|
||||
} else if value.ends_with("px") {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.flex_basis = Dimension::Points(px);
|
||||
}
|
||||
}
|
||||
}
|
||||
"flex-flow" => {}
|
||||
"flex-grow" => {
|
||||
if let Ok(val) = value.parse::<f32>() {
|
||||
style.style.flex_grow = val;
|
||||
}
|
||||
}
|
||||
"flex-shrink" => {
|
||||
if let Ok(px) = value.parse::<f32>() {
|
||||
style.style.flex_shrink = px;
|
||||
}
|
||||
}
|
||||
"flex-wrap" => {
|
||||
use FlexWrap::*;
|
||||
style.style.flex_wrap = match value {
|
||||
"nowrap" => NoWrap,
|
||||
"wrap" => Wrap,
|
||||
"wrap-reverse" => WrapReverse,
|
||||
_ => NoWrap,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_font(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
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>() {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
"padding-bottom" => {
|
||||
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
|
||||
style.style.padding.bottom = Dimension::Points(px);
|
||||
}
|
||||
}
|
||||
"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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_text(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_transform(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_transition(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_align(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
match name {
|
||||
"align-items" => {
|
||||
use AlignItems::*;
|
||||
style.style.align_items = match value {
|
||||
"flex-start" => FlexStart,
|
||||
"flex-end" => FlexEnd,
|
||||
"center" => Center,
|
||||
"baseline" => Baseline,
|
||||
"stretch" => Stretch,
|
||||
_ => FlexStart,
|
||||
};
|
||||
}
|
||||
"align-content" => {
|
||||
use AlignContent::*;
|
||||
style.style.align_content = match value {
|
||||
"flex-start" => FlexStart,
|
||||
"flex-end" => FlexEnd,
|
||||
"center" => Center,
|
||||
"space-between" => SpaceBetween,
|
||||
"space-around" => SpaceAround,
|
||||
_ => FlexStart,
|
||||
};
|
||||
}
|
||||
"align-self" => {
|
||||
use AlignSelf::*;
|
||||
style.style.align_self = match value {
|
||||
"auto" => Auto,
|
||||
"flex-start" => FlexStart,
|
||||
"flex-end" => FlexEnd,
|
||||
"center" => Center,
|
||||
"baseline" => Baseline,
|
||||
"stretch" => Stretch,
|
||||
_ => Auto,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
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" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
88
src/layout.rs
Normal file
88
src/layout.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
use dioxus::core::*;
|
||||
use std::collections::HashMap;
|
||||
use tui::style::Style as TuiStyle;
|
||||
|
||||
use crate::{
|
||||
attributes::{apply_attributes, StyleModifer},
|
||||
TuiNode,
|
||||
};
|
||||
|
||||
pub fn collect_layout<'a>(
|
||||
layout: &mut stretch2::Stretch,
|
||||
nodes: &mut HashMap<ElementId, TuiNode<'a>>,
|
||||
vdom: &'a VirtualDom,
|
||||
node: &'a VNode<'a>,
|
||||
) {
|
||||
use stretch2::prelude::*;
|
||||
|
||||
match node {
|
||||
VNode::Text(t) => {
|
||||
//
|
||||
let id = t.id.get().unwrap();
|
||||
let char_len = t.text.chars().count();
|
||||
|
||||
let style = Style {
|
||||
size: Size {
|
||||
// characters are 1 point tall
|
||||
height: Dimension::Points(1.0),
|
||||
|
||||
// text is as long as it is declared
|
||||
width: Dimension::Points(char_len as f32),
|
||||
},
|
||||
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
nodes.insert(
|
||||
id,
|
||||
TuiNode {
|
||||
node,
|
||||
block_style: tui::style::Style::default(),
|
||||
layout: layout.new_node(style, &[]).unwrap(),
|
||||
},
|
||||
);
|
||||
}
|
||||
VNode::Element(el) => {
|
||||
// gather up all the styles from the attribute list
|
||||
let mut modifier = StyleModifer {
|
||||
style: Style::default(),
|
||||
tui_style: TuiStyle::default(),
|
||||
};
|
||||
|
||||
for &Attribute { name, value, .. } in el.attributes {
|
||||
apply_attributes(name, value, &mut modifier);
|
||||
}
|
||||
|
||||
// Layout the children
|
||||
for child in el.children {
|
||||
collect_layout(layout, nodes, vdom, child);
|
||||
}
|
||||
|
||||
// Set all direct nodes as our children
|
||||
let mut child_layout = vec![];
|
||||
for el in el.children {
|
||||
let ite = ElementIdIterator::new(vdom, el);
|
||||
for node in ite {
|
||||
child_layout.push(nodes[&node.mounted_id()].layout)
|
||||
}
|
||||
}
|
||||
|
||||
nodes.insert(
|
||||
node.mounted_id(),
|
||||
TuiNode {
|
||||
node,
|
||||
block_style: modifier.tui_style,
|
||||
layout: layout.new_node(modifier.style, &child_layout).unwrap(),
|
||||
},
|
||||
);
|
||||
}
|
||||
VNode::Fragment(el) => {
|
||||
//
|
||||
for child in el.children {
|
||||
collect_layout(layout, nodes, vdom, child);
|
||||
}
|
||||
}
|
||||
VNode::Component(_) => todo!(),
|
||||
VNode::Placeholder(_) => todo!(),
|
||||
};
|
||||
}
|
376
src/lib.rs
376
src/lib.rs
|
@ -1,270 +1,152 @@
|
|||
use anyhow::Result;
|
||||
use crossterm::{
|
||||
event::{DisableMouseCapture, EnableMouseCapture},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use dioxus::core::*;
|
||||
use tui::backend::CrosstermBackend;
|
||||
use tui::layout::{Layout, Rect};
|
||||
use tui::style::{Modifier, Style as TuiStyle};
|
||||
use tui::text::{Span, Spans};
|
||||
use tui::widgets::{Block, Paragraph};
|
||||
use tui_template::tuiapp::CrosstermFrame;
|
||||
struct RinkDom {
|
||||
vdom: VirtualDom,
|
||||
use std::{collections::HashMap, io};
|
||||
use stretch2::{prelude::Size, Stretch};
|
||||
use tui::{backend::CrosstermBackend, style::Style as TuiStyle, Terminal};
|
||||
|
||||
mod attributes;
|
||||
mod layout;
|
||||
mod render;
|
||||
|
||||
pub use attributes::*;
|
||||
pub use layout::*;
|
||||
pub use render::*;
|
||||
|
||||
pub struct TuiNode<'a> {
|
||||
pub layout: stretch2::node::Node,
|
||||
pub block_style: TuiStyle,
|
||||
pub node: &'a VNode<'a>,
|
||||
}
|
||||
|
||||
impl RinkDom {
|
||||
pub fn new(app: FC<()>) -> Self {
|
||||
Self {
|
||||
vdom: VirtualDom::new(app),
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
fn render_vnode<'a>(
|
||||
&self,
|
||||
f: &mut CrosstermFrame,
|
||||
node: &'a VNode<'a>,
|
||||
state: &mut RenderState<'a>,
|
||||
) -> Rect {
|
||||
match &node.kind {
|
||||
VNodeKind::Fragment(_) => todo!(),
|
||||
VNodeKind::Component(_) => todo!(),
|
||||
VNodeKind::Suspended { node } => todo!(),
|
||||
use simd to compare lines for diffing?
|
||||
|
||||
VNodeKind::Text(te) => {
|
||||
let span = Span::styled(te.text, TuiStyle::default());
|
||||
*/
|
||||
let mut layout = Stretch::new();
|
||||
let mut nodes = HashMap::new();
|
||||
|
||||
let mut m = Modifier::empty();
|
||||
let root_node = vdom.base_scope().root_node();
|
||||
layout::collect_layout(&mut layout, &mut nodes, vdom, root_node);
|
||||
|
||||
for style in &state.current_styles {
|
||||
match style {
|
||||
Styles::Bold => m = m | Modifier::BOLD,
|
||||
Styles::Italic => m = m | Modifier::ITALIC,
|
||||
Styles::Strikethrough => m = m | Modifier::CROSSED_OUT,
|
||||
Styles::Emphasis => m = m | Modifier::ITALIC,
|
||||
Styles::Underline => m = m | Modifier::UNDERLINED,
|
||||
}
|
||||
}
|
||||
/*
|
||||
Get the terminal to calcualte the layout from
|
||||
*/
|
||||
enable_raw_mode().unwrap();
|
||||
ctrlc::set_handler(move || {
|
||||
disable_raw_mode().unwrap();
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
let mut stdout = std::io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();
|
||||
let backend = CrosstermBackend::new(io::stdout());
|
||||
let mut terminal = Terminal::new(backend).unwrap();
|
||||
|
||||
let style = TuiStyle::default().add_modifier(m);
|
||||
let span = span.styled_graphemes(style);
|
||||
let cur_block = state.block_stack.last_mut().unwrap();
|
||||
let dims = terminal.size().unwrap();
|
||||
let width = dims.width;
|
||||
let height = dims.height;
|
||||
|
||||
// Paragraph
|
||||
/*
|
||||
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),
|
||||
},
|
||||
)?;
|
||||
|
||||
// f.render_widget(span);
|
||||
}
|
||||
VNodeKind::Element(el) => {
|
||||
//
|
||||
let mut new_layout = false;
|
||||
terminal.draw(|frame| {
|
||||
//
|
||||
render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
|
||||
assert!(nodes.is_empty());
|
||||
})?;
|
||||
|
||||
// all of our supported styles
|
||||
std::thread::sleep(std::time::Duration::from_millis(5000));
|
||||
|
||||
match el.tag_name {
|
||||
// obviously semantically not really correct
|
||||
"div" => {
|
||||
state.layouts.push(Layout::default());
|
||||
new_layout = true;
|
||||
}
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
DisableMouseCapture
|
||||
)?;
|
||||
terminal.show_cursor()?;
|
||||
|
||||
"title" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => {
|
||||
let mut block = state.block_stack.pop().unwrap();
|
||||
let children = el.children;
|
||||
|
||||
if let (1, Some(VNodeKind::Text(te))) =
|
||||
(children.len(), children.get(0).map(|f| &f.kind))
|
||||
{
|
||||
block = block.title(vec![Span::from(te.text)]);
|
||||
}
|
||||
|
||||
state.block_stack.push(block);
|
||||
}
|
||||
|
||||
"span" | "header" => {}
|
||||
|
||||
"footer" => {}
|
||||
|
||||
"p" => {
|
||||
state.layouts.push(Layout::default());
|
||||
new_layout = true;
|
||||
}
|
||||
|
||||
// elements that style for whatever reason
|
||||
"em" => state.current_styles.push(Styles::Emphasis),
|
||||
"i" => state.current_styles.push(Styles::Italic),
|
||||
"b" => state.current_styles.push(Styles::Bold),
|
||||
"u" => state.current_styles.push(Styles::Underline),
|
||||
"strike" => state.current_styles.push(Styles::Strikethrough),
|
||||
|
||||
"li" => {}
|
||||
"ul" => {}
|
||||
"ol" => {}
|
||||
"code" => {}
|
||||
"hr" => {}
|
||||
|
||||
// Tables
|
||||
"table" => {}
|
||||
"tr" => {}
|
||||
"th" => {}
|
||||
"td" => {}
|
||||
|
||||
// Inputs
|
||||
"input" => {}
|
||||
"label" => {}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let cur_layout = state.layouts.last_mut().unwrap();
|
||||
let cur_block = state.block_stack.last_mut().unwrap();
|
||||
let mut cur_style = TuiStyle::default();
|
||||
|
||||
for attr in el.attributes {
|
||||
if attr.namespace == Some("style") {
|
||||
match attr.name {
|
||||
"width" => {}
|
||||
"height" => {}
|
||||
|
||||
"background" => {
|
||||
//
|
||||
// cur_style.bg
|
||||
// cur_block.style()
|
||||
}
|
||||
"background-color" => {}
|
||||
|
||||
"border" => {}
|
||||
"border-bottom" => {}
|
||||
"border-bottom-color" => {}
|
||||
"border-bottom-style" => {}
|
||||
"border-bottom-width" => {}
|
||||
"border-color" => {}
|
||||
"border-left" => {}
|
||||
"border-left-color" => {}
|
||||
"border-left-style" => {}
|
||||
"border-left-width" => {}
|
||||
"border-right" => {}
|
||||
"border-right-color" => {}
|
||||
"border-right-style" => {}
|
||||
"border-right-width" => {}
|
||||
"border-style" => {}
|
||||
"border-top" => {}
|
||||
"border-top-color" => {}
|
||||
"border-top-style" => {}
|
||||
"border-top-width" => {}
|
||||
"border-width" => {}
|
||||
|
||||
"clear" => {}
|
||||
"clip" => {}
|
||||
"color" => {}
|
||||
"cursor" => {}
|
||||
"display" => {}
|
||||
"filter" => {}
|
||||
"float" => {}
|
||||
"font" => {}
|
||||
"font-family" => {}
|
||||
"font-size" => {}
|
||||
"font-variant" => {}
|
||||
"font-weight" => {}
|
||||
|
||||
"left" => {}
|
||||
"letter-spacing" => {}
|
||||
"line-height" => {}
|
||||
"list-style" => {}
|
||||
"list-style-image" => {}
|
||||
"list-style-position" => {}
|
||||
"list-style-type" => {}
|
||||
"margin" => {}
|
||||
"margin-bottom" => {}
|
||||
"margin-left" => {}
|
||||
"margin-right" => {}
|
||||
"margin-top" => {}
|
||||
"overflow" => {}
|
||||
"padding" => {}
|
||||
"padding-bottom" => {}
|
||||
"padding-left" => {}
|
||||
"padding-right" => {}
|
||||
"padding-top" => {}
|
||||
"position" => {}
|
||||
"stroke-dasharray" => {}
|
||||
"stroke-dashoffset" => {}
|
||||
"text-align" => {}
|
||||
"text-decoration" => {}
|
||||
"text-indent" => {}
|
||||
"text-transform" => {}
|
||||
"top" => {}
|
||||
"vertical-align" => {}
|
||||
"visibility" => {}
|
||||
"z-index" => {}
|
||||
|
||||
"page-break-after"
|
||||
| "page-break-before"
|
||||
| "background-position"
|
||||
| "background-attachment"
|
||||
| "background-image"
|
||||
| "background-repeat"
|
||||
| _ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for child in el.children {}
|
||||
}
|
||||
}
|
||||
Rect::new(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
fn render_text(&self, f: &mut CrosstermFrame, node: &VNode) {}
|
||||
|
||||
fn render_fragment(&self, f: &mut CrosstermFrame) {}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'a> tui_template::tuiapp::TuiApp for RinkDom {
|
||||
fn render(&mut self, frame: &mut CrosstermFrame) {
|
||||
let base_scope = self.vdom.base_scope();
|
||||
let root = base_scope.root();
|
||||
// enum InputEvent {
|
||||
// UserInput(KeyEvent),
|
||||
// Close,
|
||||
// Tick,
|
||||
// }
|
||||
|
||||
let mut render_state = RenderState::new();
|
||||
self.render_vnode(frame, root, &mut render_state);
|
||||
}
|
||||
// // 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));
|
||||
|
||||
fn event_handler(&self, action: tui_template::crossterm::event::Event) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
// if event::poll(timeout).unwrap() {
|
||||
// if let TermEvent::Key(key) = event::read().unwrap() {
|
||||
// tx.send(InputEvent::UserInput(key)).unwrap();
|
||||
// }
|
||||
// }
|
||||
|
||||
fn handle_key(&mut self, key: tui_template::crossterm::event::KeyEvent) {
|
||||
todo!()
|
||||
}
|
||||
// // if last_tick.elapsed() >= tick_rate {
|
||||
// // tx.send(InputEvent::Tick).unwrap();
|
||||
// // last_tick = Instant::now();
|
||||
// // }
|
||||
// }
|
||||
// });
|
||||
|
||||
fn tick(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
// terminal.clear()?;
|
||||
|
||||
fn should_quit(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
struct RenderState<'a> {
|
||||
block_stack: Vec<Block<'a>>,
|
||||
// // 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);
|
||||
|
||||
layouts: Vec<Layout>,
|
||||
// frame.render_widget(block, size);
|
||||
// })?;
|
||||
|
||||
/// All the current styles applied through the "style" tag
|
||||
current_styles: Vec<Styles>,
|
||||
}
|
||||
|
||||
// I don't think we can do any of these?
|
||||
enum Styles {
|
||||
Bold,
|
||||
Italic,
|
||||
Strikethrough,
|
||||
Emphasis,
|
||||
Underline,
|
||||
}
|
||||
|
||||
impl<'a> RenderState<'a> {
|
||||
fn new() -> Self {
|
||||
let block_stack = Vec::new();
|
||||
Self {
|
||||
block_stack,
|
||||
current_styles: Vec::new(),
|
||||
layouts: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
// // // 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;
|
||||
// }
|
||||
// };
|
||||
|
|
97
src/render.rs
Normal file
97
src/render.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use anyhow::Result;
|
||||
use crossterm::{
|
||||
event,
|
||||
event::{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::{self, Stdout},
|
||||
sync::mpsc,
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use stretch2::{
|
||||
geometry::Point,
|
||||
prelude::{Layout, Size},
|
||||
style::Style as StretchStyle,
|
||||
Stretch,
|
||||
};
|
||||
use tui::{
|
||||
backend::CrosstermBackend,
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::Style as TuiStyle,
|
||||
text::Span,
|
||||
widgets::{canvas::Label, Block, BorderType, Borders, Widget},
|
||||
Terminal,
|
||||
};
|
||||
|
||||
use crate::TuiNode;
|
||||
|
||||
pub fn render_vnode<'a>(
|
||||
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
||||
layout: &Stretch,
|
||||
layouts: &mut HashMap<ElementId, TuiNode<'a>>,
|
||||
vdom: &'a VirtualDom,
|
||||
node: &'a VNode<'a>,
|
||||
) {
|
||||
match node {
|
||||
VNode::Fragment(f) => {
|
||||
for child in f.children {
|
||||
render_vnode(frame, layout, layouts, vdom, child);
|
||||
}
|
||||
return;
|
||||
}
|
||||
VNode::Component(_) => todo!(),
|
||||
|
||||
VNode::Placeholder(_) | VNode::Element(_) | VNode::Text(_) => {}
|
||||
}
|
||||
|
||||
let id = node.try_mounted_id().unwrap();
|
||||
let node = layouts.remove(&id).unwrap();
|
||||
|
||||
let Layout { location, size, .. } = layout.layout(node.layout).unwrap();
|
||||
|
||||
let Point { x, y } = location;
|
||||
let Size { width, height } = size;
|
||||
|
||||
match node.node {
|
||||
VNode::Text(t) => {
|
||||
#[derive(Default)]
|
||||
struct Label<'a> {
|
||||
text: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Widget for Label<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_string(area.left(), area.top(), self.text, TuiStyle::default());
|
||||
}
|
||||
}
|
||||
|
||||
// let s = Span::raw(t.text);
|
||||
|
||||
// Block::default().
|
||||
|
||||
let label = Label { text: t.text };
|
||||
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
|
||||
|
||||
frame.render_widget(label, area);
|
||||
}
|
||||
VNode::Element(el) => {
|
||||
let block = Block::default().style(node.block_style);
|
||||
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
|
||||
|
||||
frame.render_widget(block, area);
|
||||
|
||||
for el in el.children {
|
||||
render_vnode(frame, layout, layouts, vdom, el);
|
||||
}
|
||||
}
|
||||
VNode::Fragment(_) => todo!(),
|
||||
VNode::Component(_) => todo!(),
|
||||
VNode::Placeholder(_) => todo!(),
|
||||
}
|
||||
}
|
45
test.html
Normal file
45
test.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Test</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* margin: auto; */
|
||||
}
|
||||
|
||||
.smaller {
|
||||
height: 50%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: violet;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="smaller">
|
||||
hello world
|
||||
<div style="color: red;">
|
||||
goodbye
|
||||
<div>
|
||||
asdasdasd
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
102
tests/margin.rs
Normal file
102
tests/margin.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use stretch2 as stretch;
|
||||
|
||||
#[test]
|
||||
fn margin_and_flex_row() {
|
||||
let mut stretch = stretch::Stretch::new();
|
||||
let node0 = stretch
|
||||
.new_node(
|
||||
stretch::style::Style {
|
||||
flex_grow: 1f32,
|
||||
margin: stretch::geometry::Rect {
|
||||
start: stretch::style::Dimension::Points(10f32),
|
||||
end: stretch::style::Dimension::Points(10f32),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
let node = stretch
|
||||
.new_node(
|
||||
stretch::style::Style {
|
||||
size: stretch::geometry::Size {
|
||||
width: stretch::style::Dimension::Points(100f32),
|
||||
height: stretch::style::Dimension::Points(100f32),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&[node0],
|
||||
)
|
||||
.unwrap();
|
||||
stretch
|
||||
.compute_layout(node, stretch::geometry::Size::undefined())
|
||||
.unwrap();
|
||||
assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
|
||||
assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
|
||||
assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
|
||||
assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
|
||||
assert_eq!(stretch.layout(node0).unwrap().size.width, 80f32);
|
||||
assert_eq!(stretch.layout(node0).unwrap().size.height, 100f32);
|
||||
assert_eq!(stretch.layout(node0).unwrap().location.x, 10f32);
|
||||
assert_eq!(stretch.layout(node0).unwrap().location.y, 0f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn margin_and_flex_row2() {
|
||||
let mut stretch = stretch::Stretch::new();
|
||||
let node0 = stretch
|
||||
.new_node(
|
||||
stretch::style::Style {
|
||||
flex_grow: 1f32,
|
||||
margin: stretch::geometry::Rect {
|
||||
// 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),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let node = stretch
|
||||
.new_node(
|
||||
stretch::style::Style {
|
||||
size: stretch::geometry::Size {
|
||||
width: stretch::style::Dimension::Points(100f32),
|
||||
height: stretch::style::Dimension::Points(100f32),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&[node0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
stretch
|
||||
.compute_layout(node, stretch::geometry::Size::undefined())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(stretch.layout(node).unwrap().size.width, 100f32);
|
||||
assert_eq!(stretch.layout(node).unwrap().size.height, 100f32);
|
||||
assert_eq!(stretch.layout(node).unwrap().location.x, 0f32);
|
||||
assert_eq!(stretch.layout(node).unwrap().location.y, 0f32);
|
||||
|
||||
dbg!(stretch.layout(node0));
|
||||
|
||||
// assert_eq!(stretch.layout(node0).unwrap().size.width, 80f32);
|
||||
// assert_eq!(stretch.layout(node0).unwrap().size.height, 100f32);
|
||||
// assert_eq!(stretch.layout(node0).unwrap().location.x, 10f32);
|
||||
// assert_eq!(stretch.layout(node0).unwrap().location.y, 0f32);
|
||||
}
|
Loading…
Reference in a new issue