wip lazy layout

This commit is contained in:
Evan Almloff committed 2022-03-26 20:24:44 -05:00
1 parent 14099e9889
commit 499971e9b3
14 files changed
+2117 -1223

No files matched your search

-968
View File
@@ -1,968 +0,0 @@
/*
- [ ] pub display: Display,
- [x] pub position_type: PositionType, --> kinda, stretch doesnt support everything
- [ ] 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,
- [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,
- [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>, ----> ??? 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::PositionType, style::Style};
use crate::style::{RinkColor, RinkStyle};
pub struct StyleModifer {
pub style: Style,
pub tui_style: RinkStyle,
pub tui_modifier: TuiModifier,
}
#[derive(Default)]
pub struct TuiModifier {
pub borders: Borders,
}
#[derive(Default)]
pub struct Borders {
pub top: BorderEdge,
pub right: BorderEdge,
pub bottom: BorderEdge,
pub left: BorderEdge,
}
impl Borders {
fn slice(&mut self) -> [&mut BorderEdge; 4] {
[
&mut self.top,
&mut self.right,
&mut self.bottom,
&mut self.left,
]
}
}
pub struct BorderEdge {
pub color: Option<RinkColor>,
pub style: BorderStyle,
pub width: UnitSystem,
pub radius: UnitSystem,
}
impl Default for BorderEdge {
fn default() -> Self {
Self {
color: None,
style: BorderStyle::NONE,
width: UnitSystem::Point(0.0),
radius: UnitSystem::Point(0.0),
}
}
}
#[derive(Clone, Copy)]
pub enum BorderStyle {
DOTTED,
DASHED,
SOLID,
DOUBLE,
GROOVE,
RIDGE,
INSET,
OUTSET,
HIDDEN,
NONE,
}
impl BorderStyle {
pub fn symbol_set(&self) -> Option<tui::symbols::line::Set> {
use tui::symbols::line::*;
const DASHED: Set = Set {
horizontal: "╌",
vertical: "╎",
..NORMAL
};
const DOTTED: Set = Set {
horizontal: "┈",
vertical: "┊",
..NORMAL
};
match self {
BorderStyle::DOTTED => Some(DOTTED),
BorderStyle::DASHED => Some(DASHED),
BorderStyle::SOLID => Some(NORMAL),
BorderStyle::DOUBLE => Some(DOUBLE),
BorderStyle::GROOVE => Some(NORMAL),
BorderStyle::RIDGE => Some(NORMAL),
BorderStyle::INSET => Some(NORMAL),
BorderStyle::OUTSET => Some(NORMAL),
BorderStyle::HIDDEN => None,
BorderStyle::NONE => None,
}
}
}
/// 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" => {
if let Ok(c) = value.parse() {
style.tui_style.fg.replace(c);
}
}
"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" => {
match value {
"ltr" => style.style.direction = Direction::LTR,
"rtl" => style.style.direction = Direction::RTL,
_ => {}
}
}
"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 let Some(v) = parse_value(value){
style.style.size.height = match v {
UnitSystem::Percent(v)=> Dimension::Percent(v/100.0),
UnitSystem::Point(v)=> Dimension::Points(v),
};
}
}
"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" => {
match value {
"static" => {}
"relative" => style.style.position_type = PositionType::Relative,
"fixed" => {}
"absolute" => style.style.position_type = PositionType::Absolute,
"sticky" => {}
_ => {}
}
}
"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 let Some(v) = parse_value(value){
style.style.size.width = match v {
UnitSystem::Percent(v)=> Dimension::Percent(v/100.0),
UnitSystem::Point(v)=> Dimension::Points(v),
};
}
}
"word-break" => {}
"word-spacing" => {}
"word-wrap" => {}
"z-index" => {}
_ => {}
}
}
#[derive(Clone, Copy)]
pub enum UnitSystem {
Percent(f32),
Point(f32),
}
impl Into<Dimension> for UnitSystem {
fn into(self) -> Dimension {
match self {
Self::Percent(v) => Dimension::Percent(v),
Self::Point(v) => Dimension::Points(v),
}
}
}
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
"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) {
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" => {
if let Ok(c) = value.parse() {
style.tui_style.bg.replace(c);
}
}
"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) {
fn parse_border_style(v: &str) -> BorderStyle {
match v {
"dotted" => BorderStyle::DOTTED,
"dashed" => BorderStyle::DASHED,
"solid" => BorderStyle::SOLID,
"double" => BorderStyle::DOUBLE,
"groove" => BorderStyle::GROOVE,
"ridge" => BorderStyle::RIDGE,
"inset" => BorderStyle::INSET,
"outset" => BorderStyle::OUTSET,
"none" => BorderStyle::NONE,
"hidden" => BorderStyle::HIDDEN,
_ => todo!(),
}
}
match name {
"border" => {}
"border-bottom" => {}
"border-bottom-color" => {
if let Ok(c) = value.parse() {
style.tui_modifier.borders.bottom.color = Some(c);
}
}
"border-bottom-left-radius" => {
if let Some(v) = parse_value(value) {
style.tui_modifier.borders.left.radius = v;
}
}
"border-bottom-right-radius" => {
if let Some(v) = parse_value(value) {
style.tui_modifier.borders.right.radius = v;
}
}
"border-bottom-style" => {
if style.style.border.bottom == Dimension::default() {
let v = Dimension::Points(1.0);
style.style.border.bottom = v;
}
style.tui_modifier.borders.bottom.style = parse_border_style(value)
}
"border-bottom-width" => {
if let Some(v) = parse_value(value) {
style.tui_modifier.borders.bottom.width = v;
style.style.border.bottom = v.into();
}
}
"border-collapse" => {}
"border-color" => {
let values: Vec<_> = value.split(' ').collect();
if values.len() == 1 {
if let Ok(c) = values[0].parse() {
style
.tui_modifier
.borders
.slice()
.iter_mut()
.for_each(|b| b.color = Some(c));
}
} else {
for (v, b) in values
.into_iter()
.zip(style.tui_modifier.borders.slice().iter_mut())
{
if let Ok(c) = v.parse() {
b.color = Some(c);
}
}
}
}
"border-image" => {}
"border-image-outset" => {}
"border-image-repeat" => {}
"border-image-slice" => {}
"border-image-source" => {}
"border-image-width" => {}
"border-left" => {}
"border-left-color" => {
if let Ok(c) = value.parse() {
style.tui_modifier.borders.left.color = Some(c);
}
}
"border-left-style" => {
if style.style.border.start == Dimension::default() {
let v = Dimension::Points(1.0);
style.style.border.start = v;
}
style.tui_modifier.borders.left.style = parse_border_style(value)
}
"border-left-width" => {
if let Some(v) = parse_value(value) {
style.tui_modifier.borders.left.width = v;
style.style.border.start = v.into();
}
}
"border-radius" => {
let values: Vec<_> = value.split(' ').collect();
if values.len() == 1 {
if let Some(r) = parse_value(values[0]) {
style
.tui_modifier
.borders
.slice()
.iter_mut()
.for_each(|b| b.radius = r);
}
} else {
for (v, b) in values
.into_iter()
.zip(style.tui_modifier.borders.slice().iter_mut())
{
if let Some(r) = parse_value(v) {
b.radius = r;
}
}
}
}
"border-right" => {}
"border-right-color" => {
if let Ok(c) = value.parse() {
style.tui_modifier.borders.right.color = Some(c);
}
}
"border-right-style" => {
let v = Dimension::Points(1.0);
style.style.border.end = v;
style.tui_modifier.borders.right.style = parse_border_style(value)
}
"border-right-width" => {
if let Some(v) = parse_value(value) {
style.tui_modifier.borders.right.width = v;
}
}
"border-spacing" => {}
"border-style" => {
let values: Vec<_> = value.split(' ').collect();
if style.style.border.top == Dimension::default() {
let v = Dimension::Points(1.0);
style.style.border.top = v;
}
if style.style.border.bottom == Dimension::default() {
let v = Dimension::Points(1.0);
style.style.border.bottom = v;
}
if style.style.border.start == Dimension::default() {
let v = Dimension::Points(1.0);
style.style.border.start = v;
}
if style.style.border.end == Dimension::default() {
let v = Dimension::Points(1.0);
style.style.border.end = v;
}
if values.len() == 1 {
let border_style = parse_border_style(values[0]);
style
.tui_modifier
.borders
.slice()
.iter_mut()
.for_each(|b| b.style = border_style);
} else {
for (v, b) in values
.into_iter()
.zip(style.tui_modifier.borders.slice().iter_mut())
{
b.style = parse_border_style(v);
}
}
}
"border-top" => {}
"border-top-color" => {
if let Ok(c) = value.parse() {
style.tui_modifier.borders.top.color = Some(c);
}
}
"border-top-left-radius" => {
if let Some(v) = parse_value(value) {
style.tui_modifier.borders.left.radius = v;
}
}
"border-top-right-radius" => {
if let Some(v) = parse_value(value) {
style.tui_modifier.borders.right.radius = v;
}
}
"border-top-style" => {
if style.style.border.top == Dimension::default() {
let v = Dimension::Points(1.0);
style.style.border.top = v;
}
style.tui_modifier.borders.top.style = parse_border_style(value)
}
"border-top-width" => {
if let Some(v) = parse_value(value) {
style.style.border.top = v.into();
style.tui_modifier.borders.top.width = v;
}
}
"border-width" => {
let values: Vec<_> = value.split(' ').collect();
if values.len() == 1 {
if let Some(w) = parse_value(values[0]) {
style.style.border.top = w.into();
style.style.border.bottom = w.into();
style.style.border.start = w.into();
style.style.border.end = w.into();
style
.tui_modifier
.borders
.slice()
.iter_mut()
.for_each(|b| b.width = w);
}
} else {
let border_widths = [
&mut style.style.border.top,
&mut style.style.border.bottom,
&mut style.style.border.start,
&mut style.style.border.end,
];
for ((v, b), width) in values
.into_iter()
.zip(style.tui_modifier.borders.slice().iter_mut())
.zip(border_widths)
{
if let Some(w) = parse_value(v) {
*width = w.into();
b.width = w;
}
}
}
}
_ => (),
}
}
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 let Some(v) = parse_value(value) {
style.style.flex_basis = match v {
UnitSystem::Percent(v) => Dimension::Percent(v / 100.0),
UnitSystem::Point(v) => Dimension::Points(v),
};
}
}
"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) {
use tui::style::Modifier;
match name {
"font" => (),
"font-family" => (),
"font-size" => (),
"font-size-adjust" => (),
"font-stretch" => (),
"font-style" => match value {
"italic" => style.tui_style = style.tui_style.add_modifier(Modifier::ITALIC),
"oblique" => style.tui_style = style.tui_style.add_modifier(Modifier::ITALIC),
_ => (),
},
"font-variant" => todo!(),
"font-weight" => match value {
"bold" => style.tui_style = style.tui_style.add_modifier(Modifier::BOLD),
"normal" => style.tui_style = style.tui_style.remove_modifier(Modifier::BOLD),
_ => (),
},
_ => (),
}
}
fn apply_padding(name: &str, value: &str, style: &mut StyleModifer) {
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" => 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-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 => {}
}
}
fn apply_text(name: &str, value: &str, style: &mut StyleModifer) {
use tui::style::Modifier;
match name {
"text-align" => todo!(),
"text-align-last" => todo!(),
"text-decoration" | "text-decoration-line" => {
for v in value.split(' ') {
match v {
"line-through" => {
style.tui_style = style.tui_style.add_modifier(Modifier::CROSSED_OUT)
}
"underline" => {
style.tui_style = style.tui_style.add_modifier(Modifier::UNDERLINED)
}
_ => (),
}
}
}
"text-decoration-color" => todo!(),
"text-decoration-style" => todo!(),
"text-indent" => todo!(),
"text-justify" => todo!(),
"text-overflow" => todo!(),
"text-shadow" => todo!(),
"text-transform" => todo!(),
_ => 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 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 => {}
}
}
+15 -14
View File
@@ -4,18 +4,19 @@ use crossterm::event::{
use dioxus_core::*;
use dioxus_html::{on::*, KeyCode};
use dioxus_native_core::{Tree, TreeNodeType};
use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
use std::{
any::Any,
cell::RefCell,
collections::{HashMap, HashSet},
collections::HashSet,
rc::Rc,
sync::Arc,
time::{Duration, Instant},
};
use stretch2::{prelude::Layout, Stretch};
use crate::TuiNode;
use crate::{style_attributes::StyleModifier, RinkLayout};
// a wrapper around the input state for easier access
// todo: fix loop
@@ -166,7 +167,7 @@ impl InnerInputState {
evts: &mut Vec<EventCore>,
resolved_events: &mut Vec<UserEvent>,
layout: &Stretch,
layouts: &mut HashMap<ElementId, TuiNode<'a>>,
layouts: &mut Tree<RinkLayout, StyleModifier>,
node: &'a VNode<'a>,
) {
struct Data<'b> {
@@ -190,7 +191,7 @@ impl InnerInputState {
dom: &'c VirtualDom,
resolved_events: &mut Vec<UserEvent>,
layout: &Stretch,
layouts: &HashMap<ElementId, TuiNode<'c>>,
layouts: &Tree<RinkLayout, StyleModifier>,
node: &'c VNode<'c>,
data: &'d Data<'d>,
) -> HashSet<&'static str> {
@@ -225,9 +226,9 @@ impl InnerInputState {
}
let id = node.try_mounted_id().unwrap();
let node = layouts.get(&id).unwrap();
let node = layouts.get(id.0);
let node_layout = layout.layout(node.layout).unwrap();
let node_layout = layout.layout(node.up_state.node.unwrap()).unwrap();
let previously_contained = data
.old_pos
@@ -235,18 +236,18 @@ impl InnerInputState {
.is_some();
let currently_contains = layout_contains_point(node_layout, data.new_pos);
match node.node {
VNode::Element(el) => {
match &node.node_type {
TreeNodeType::Element { children, .. } => {
let mut events = HashSet::new();
if previously_contained || currently_contains {
for c in el.children {
for c in children {
events = events
.union(&get_mouse_events(
dom,
resolved_events,
layout,
layouts,
c,
dom.get_element(*c).unwrap(),
data,
))
.copied()
@@ -260,7 +261,7 @@ impl InnerInputState {
scope_id: None,
priority: EventPriority::Medium,
name,
element: Some(el.id.get().unwrap()),
element: Some(node.id),
data: Arc::new(clone_mouse_data(data.mouse_data)),
})
}
@@ -287,7 +288,7 @@ impl InnerInputState {
scope_id: None,
priority: EventPriority::Medium,
name: "wheel",
element: Some(el.id.get().unwrap()),
element: Some(node.id),
data: Arc::new(clone_wheel_data(w)),
})
}
@@ -298,7 +299,7 @@ impl InnerInputState {
}
events
}
VNode::Text(_) => HashSet::new(),
TreeNodeType::Text { .. } => HashSet::new(),
_ => todo!(),
}
}
@@ -392,7 +393,7 @@ impl RinkInputHandler {
&self,
dom: &'a VirtualDom,
layout: &Stretch,
layouts: &mut HashMap<ElementId, TuiNode<'a>>,
layouts: &mut Tree<RinkLayout, StyleModifier>,
node: &'a VNode<'a>,
) -> Vec<UserEvent> {
// todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
+68 -106
View File
@@ -1,11 +1,8 @@
use dioxus_core::*;
use std::collections::HashMap;
use dioxus_native_core::BubbledUpState;
use stretch2::prelude::*;
use crate::{
attributes::{apply_attributes, StyleModifer},
style::RinkStyle,
TuiModifier, TuiNode,
};
use crate::layout_attributes::apply_layout_attributes;
/*
The layout system uses the lineheight as one point.
@@ -13,119 +10,84 @@ 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>>,
vdom: &'a VirtualDom,
node: &'a VNode<'a>,
) {
use stretch2::prelude::*;
#[derive(Clone, PartialEq, Default, Debug)]
pub struct RinkLayout {
pub style: Style,
pub node: Option<Node>,
}
match node {
VNode::Text(t) => {
let id = t.id.get().unwrap();
let char_len = t.text.chars().count();
impl BubbledUpState for RinkLayout {
type Ctx = Stretch;
let style = Style {
size: Size {
// characters are 1 point tall
height: Dimension::Points(1.0),
// Although we pass in the parent, the state of RinkLayout must only depend on the parent for optimiztion purposes
fn reduce<'a, I>(&mut self, children: I, vnode: &VNode, stretch: &mut Self::Ctx)
where
I: Iterator<Item = &'a Self>,
Self: 'a,
{
match vnode {
VNode::Text(t) => {
let char_len = t.text.chars().count();
// text is as long as it is declared
width: Dimension::Points(char_len as f32),
},
..Default::default()
};
let style = Style {
size: Size {
// characters are 1 point tall
height: Dimension::Points(1.0),
nodes.insert(
id,
TuiNode {
node,
block_style: RinkStyle::default(),
tui_modifier: TuiModifier::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: RinkStyle::default(),
tui_modifier: TuiModifier::default(),
};
// text is as long as it is declared
width: Dimension::Points(char_len as f32),
},
..Default::default()
};
// handle text modifier elements
if el.namespace.is_none() {
match el.tag {
"b" => apply_attributes("font-weight", "bold", &mut modifier),
"strong" => apply_attributes("font-weight", "bold", &mut modifier),
"u" => apply_attributes("text-decoration", "underline", &mut modifier),
"ins" => apply_attributes("text-decoration", "underline", &mut modifier),
"del" => apply_attributes("text-decoration", "line-through", &mut modifier),
"i" => apply_attributes("font-style", "italic", &mut modifier),
"em" => apply_attributes("font-style", "italic", &mut modifier),
"mark" => apply_attributes(
"background-color",
"rgba(241, 231, 64, 50%)",
&mut modifier,
),
_ => (),
if let Some(n) = self.node {
if self.style != style {
panic!("new style: {style:?}");
stretch.set_style(n, style).unwrap();
}
} else {
self.node = Some(stretch.new_node(style, &[]).unwrap());
}
self.style = style;
}
VNode::Element(el) => {
// gather up all the styles from the attribute list
let mut style = Style::default();
for &Attribute { name, value, .. } in el.attributes {
apply_layout_attributes(name, value, &mut style);
}
for &Attribute { name, value, .. } in el.attributes {
apply_attributes(name, value, &mut modifier);
}
// todo: move
if el.id.get() == Some(ElementId(0)) {
apply_layout_attributes("width", "100%", &mut style);
apply_layout_attributes("height", "100%", &mut style);
}
// 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 l in children {
child_layout.push(l.node.unwrap());
}
child_layout.reverse();
// 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 {
match node {
VNode::Element(_) | VNode::Text(_) => {
//
child_layout.push(nodes[&node.mounted_id()].layout)
}
VNode::Placeholder(_) => {}
VNode::Fragment(_) => todo!(),
VNode::Component(_) => todo!(),
if let Some(n) = self.node {
if &stretch.children(n).unwrap() != &child_layout {
panic!("new children: {child_layout:?}");
stretch.set_children(n, &child_layout).unwrap();
}
if self.style != style {
panic!("new style: {style:?}");
stretch.set_style(n, style).unwrap();
}
// child_layout.push(nodes[&node.mounted_id()].layout)
} else {
self.node = Some(stretch.new_node(style, &child_layout).unwrap());
}
}
nodes.insert(
node.mounted_id(),
TuiNode {
node,
block_style: modifier.tui_style,
tui_modifier: modifier.tui_modifier,
layout: layout.new_node(modifier.style, &child_layout).unwrap(),
},
);
}
VNode::Fragment(el) => {
//
for child in el.children {
collect_layout(layout, nodes, vdom, child);
self.style = style;
}
}
VNode::Component(sc) => {
//
let scope = vdom.get_scope(sc.scope.get().unwrap()).unwrap();
let root = scope.root_node();
collect_layout(layout, nodes, vdom, root);
}
VNode::Placeholder(_) => {
//
_ => (),
}
};
}
}
+643
View File
@@ -0,0 +1,643 @@
/*
- [ ] pub display: Display,
- [x] pub position_type: PositionType, --> kinda, stretch doesnt support everything
- [ ] 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,
- [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,
- [x] pub margin: Rect<Dimension>,
- [x] pub padding: Rect<Dimension>,
- [x] pub justify_content: JustifyContent,
- [ ] pub position: Rect<Dimension>,
- [x] pub border: Rect<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::PositionType, style::Style};
/// applies the entire html namespace defined in dioxus-html
pub fn apply_layout_attributes(
//
name: &str,
value: &str,
style: &mut Style,
) {
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" => {}
"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" => {}
"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" => {
match value {
"ltr" => style.direction = Direction::LTR,
"rtl" => style.direction = Direction::RTL,
_ => {}
}
}
"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" => {}
"height" => {
if let Some(v) = parse_value(value){
style.size.height = match v {
UnitSystem::Percent(v)=> Dimension::Percent(v/100.0),
UnitSystem::Point(v)=> Dimension::Points(v),
};
}
}
"justify-content" => {
use JustifyContent::*;
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" => {
match value {
"static" => {}
"relative" => style.position_type = PositionType::Relative,
"fixed" => {}
"absolute" => style.position_type = PositionType::Absolute,
"sticky" => {}
_ => {}
}
}
"pointer-events" => {}
"quotes" => {}
"resize" => {}
"right" => {}
"tab-size" => {}
"table-layout" => {}
"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 let Some(v) = parse_value(value){
style.size.width = match v {
UnitSystem::Percent(v)=> Dimension::Percent(v/100.0),
UnitSystem::Point(v)=> Dimension::Points(v),
};
}
}
"word-break" => {}
"word-spacing" => {}
"word-wrap" => {}
"z-index" => {}
_ => {}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum UnitSystem {
Percent(f32),
Point(f32),
}
impl Into<Dimension> for UnitSystem {
fn into(self) -> Dimension {
match self {
Self::Percent(v) => Dimension::Percent(v),
Self::Point(v) => Dimension::Points(v),
}
}
}
pub 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 Style) {
match name {
// todo: add more overflow support to stretch2
"overflow" | "overflow-x" | "overflow-y" => {
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 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_border(name: &str, value: &str, style: &mut Style) {
match name {
"border" => {}
"border-bottom" => {}
"border-bottom-color" => {}
"border-bottom-left-radius" => {}
"border-bottom-right-radius" => {}
"border-bottom-style" => {
if style.border.bottom == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.bottom = v;
}
}
"border-bottom-width" => {
if let Some(v) = parse_value(value) {
style.border.bottom = v.into();
}
}
"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" => {
if style.border.start == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.start = v;
}
}
"border-left-width" => {
if let Some(v) = parse_value(value) {
style.border.start = v.into();
}
}
"border-radius" => {}
"border-right" => {}
"border-right-color" => {}
"border-right-style" => {
let v = Dimension::Points(1.0);
style.border.end = v;
}
"border-right-width" => {
if let Some(v) = parse_value(value) {
style.border.end = v.into();
}
}
"border-spacing" => {}
"border-style" => {
if style.border.top == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.top = v;
}
if style.border.bottom == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.bottom = v;
}
if style.border.start == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.start = v;
}
if style.border.end == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.end = v;
}
}
"border-top" => {}
"border-top-color" => {}
"border-top-left-radius" => {}
"border-top-right-radius" => {}
"border-top-style" => {
if style.border.top == Dimension::default() {
let v = Dimension::Points(1.0);
style.border.top = v;
}
}
"border-top-width" => {
if let Some(v) = parse_value(value) {
style.border.top = v.into();
}
}
"border-width" => {
let values: Vec<_> = value.split(' ').collect();
if values.len() == 1 {
if let Some(w) = parse_value(values[0]) {
style.border.top = w.into();
style.border.bottom = w.into();
style.border.start = w.into();
style.border.end = w.into();
}
} else {
let border_widths = [
&mut style.border.top,
&mut style.border.bottom,
&mut style.border.start,
&mut style.border.end,
];
for (v, width) in values.into_iter().zip(border_widths) {
if let Some(w) = parse_value(v) {
*width = w.into();
}
}
}
}
_ => (),
}
}
fn apply_animation(name: &str, _value: &str, _style: &mut Style) {
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 Style) {
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 Style) {
// - [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.flex_direction = match value {
"row" => Row,
"row-reverse" => RowReverse,
"column" => Column,
"column-reverse" => ColumnReverse,
_ => Row,
};
}
"flex-basis" => {
if let Some(v) = parse_value(value) {
style.flex_basis = match v {
UnitSystem::Percent(v) => Dimension::Percent(v / 100.0),
UnitSystem::Point(v) => Dimension::Points(v),
};
}
}
"flex-flow" => {}
"flex-grow" => {
if let Ok(val) = value.parse::<f32>() {
style.flex_grow = val;
}
}
"flex-shrink" => {
if let Ok(px) = value.parse::<f32>() {
style.flex_shrink = px;
}
}
"flex-wrap" => {
use FlexWrap::*;
style.flex_wrap = match value {
"nowrap" => NoWrap,
"wrap" => Wrap,
"wrap-reverse" => WrapReverse,
_ => NoWrap,
};
}
_ => {}
}
}
fn apply_padding(name: &str, value: &str, style: &mut Style) {
match parse_value(value) {
Some(UnitSystem::Percent(v)) => match name {
"padding" => {
let v = Dimension::Percent(v / 100.0);
style.padding.top = v;
style.padding.bottom = v;
style.padding.start = v;
style.padding.end = v;
}
"padding-bottom" => style.padding.bottom = Dimension::Percent(v / 100.0),
"padding-left" => style.padding.start = Dimension::Percent(v / 100.0),
"padding-right" => style.padding.end = Dimension::Percent(v / 100.0),
"padding-top" => style.padding.top = Dimension::Percent(v / 100.0),
_ => {}
},
Some(UnitSystem::Point(v)) => match name {
"padding" => {
style.padding.top = Dimension::Points(v);
style.padding.bottom = Dimension::Points(v);
style.padding.start = Dimension::Points(v);
style.padding.end = Dimension::Points(v);
}
"padding-bottom" => style.padding.bottom = Dimension::Points(v),
"padding-left" => style.padding.start = Dimension::Points(v),
"padding-right" => style.padding.end = Dimension::Points(v),
"padding-top" => style.padding.top = Dimension::Points(v),
_ => {}
},
None => {}
}
}
fn apply_transform(_name: &str, _value: &str, _style: &mut Style) {
todo!()
}
fn apply_transition(_name: &str, _value: &str, _style: &mut Style) {
todo!()
}
fn apply_align(name: &str, value: &str, style: &mut Style) {
match name {
"align-items" => {
use AlignItems::*;
style.align_items = match value {
"flex-start" => FlexStart,
"flex-end" => FlexEnd,
"center" => Center,
"baseline" => Baseline,
"stretch" => Stretch,
_ => FlexStart,
};
}
"align-content" => {
use AlignContent::*;
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.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 Style) {
//
}
pub fn apply_margin(name: &str, value: &str, style: &mut Style) {
match parse_value(value) {
Some(UnitSystem::Percent(v)) => match name {
"margin" => {
let v = Dimension::Percent(v / 100.0);
style.margin.top = v;
style.margin.bottom = v;
style.margin.start = v;
style.margin.end = v;
}
"margin-top" => style.margin.top = Dimension::Percent(v / 100.0),
"margin-bottom" => style.margin.bottom = Dimension::Percent(v / 100.0),
"margin-left" => style.margin.start = Dimension::Percent(v / 100.0),
"margin-right" => style.margin.end = Dimension::Percent(v / 100.0),
_ => {}
},
Some(UnitSystem::Point(v)) => match name {
"margin" => {
style.margin.top = Dimension::Points(v);
style.margin.bottom = Dimension::Points(v);
style.margin.start = Dimension::Points(v);
style.margin.end = Dimension::Points(v);
}
"margin-top" => style.margin.top = Dimension::Points(v),
"margin-bottom" => style.margin.bottom = Dimension::Points(v),
"margin-left" => style.margin.start = Dimension::Points(v),
"margin-right" => style.margin.end = Dimension::Points(v),
_ => {}
},
None => {}
}
}
+49 -38
View File
@@ -6,28 +6,32 @@ use crossterm::{
};
use dioxus_core::exports::futures_channel::mpsc::unbounded;
use dioxus_core::*;
use dioxus_native_core::Tree;
use futures::{channel::mpsc::UnboundedSender, pin_mut, StreamExt};
use std::{
collections::HashMap,
io,
time::{Duration, Instant},
};
use stretch2::{prelude::Size, Stretch};
use style::RinkStyle;
use stretch2::{
prelude::{Layout, Size},
Stretch,
};
use style_attributes::StyleModifier;
use tui::{backend::CrosstermBackend, Terminal};
mod attributes;
mod config;
mod hooks;
mod layout;
mod layout_attributes;
mod render;
mod style;
mod style_attributes;
mod widget;
pub use attributes::*;
pub use config::*;
pub use hooks::*;
pub use layout::*;
pub use layout_attributes::*;
pub use render::*;
pub fn launch(app: Component<()>) {
@@ -44,16 +48,15 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) {
cx.provide_root_context(state);
dom.rebuild();
let mut tree: Tree<RinkLayout, StyleModifier> = Tree::new();
let mutations = dom.rebuild();
let to_update = tree.apply_mutations(vec![mutations]);
let mut stretch = Stretch::new();
let _to_rerender = tree
.update_state(&dom, to_update, &mut stretch, &mut ())
.unwrap();
render_vdom(&mut dom, tx, handler, cfg).unwrap();
}
pub struct TuiNode<'a> {
pub layout: stretch2::node::Node,
pub block_style: RinkStyle,
pub tui_modifier: TuiModifier,
pub node: &'a VNode<'a>,
render_vdom(&mut dom, tx, handler, cfg, tree, stretch).unwrap();
}
pub fn render_vdom(
@@ -61,6 +64,8 @@ pub fn render_vdom(
ctx: UnboundedSender<TermEvent>,
handler: RinkInputHandler,
cfg: Config,
mut tree: Tree<RinkLayout, StyleModifier>,
mut stretch: Stretch,
) -> Result<()> {
// Setup input handling
let (tx, mut rx) = unbounded();
@@ -102,11 +107,11 @@ pub fn render_vdom(
loop {
/*
-> collect all the nodes with their layout
-> solve their layout
-> collect all the nodes
-> resolve events
-> render the nodes in the right place with tui/crosstream
-> while rendering, apply styling
-> render the nodes in the right place with tui/crossterm
-> rendering
-> lazily update the layout and style based on nodes changed
use simd to compare lines for diffing?
@@ -114,45 +119,47 @@ pub fn render_vdom(
todo: reuse the layout and node objects.
our work_with_deadline method can tell us which nodes are dirty.
*/
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 the terminal size
*/
let node_id = root_node.try_mounted_id().unwrap();
let root_layout = nodes[&node_id].layout;
let mut events = Vec::new();
terminal.draw(|frame| {
// size is guaranteed to not change when rendering
let dims = frame.size();
println!("{dims:?}");
let width = dims.width;
let height = dims.height;
layout
let root_id = tree.root;
let root_node = tree.get(root_id).up_state.node.unwrap();
stretch
.compute_layout(
root_layout,
root_node,
Size {
width: stretch2::prelude::Number::Defined(width as f32),
height: stretch2::prelude::Number::Defined(height as f32),
width: stretch2::prelude::Number::Defined((width - 1) as f32),
height: stretch2::prelude::Number::Defined((height - 1) as f32),
},
)
.unwrap();
// resolve events before rendering
events = handler.get_events(vdom, &layout, &mut nodes, root_node);
render::render_vnode(
frame,
&layout,
&mut nodes,
events = handler.get_events(
vdom,
root_node,
&RinkStyle::default(),
cfg,
&stretch,
&mut tree,
vdom.base_scope().root_node(),
);
assert!(nodes.is_empty());
for n in &tree.nodes {
if let Some(node) = n {
let Layout { location, size, .. } =
stretch.layout(node.up_state.node.unwrap()).unwrap();
println!("{node:#?}");
println!("\t{location:?}: {size:?}");
}
}
let root = tree.get(tree.root);
// render::render_vnode(frame, &stretch, &tree, &root, cfg);
})?;
for e in events {
@@ -191,7 +198,11 @@ pub fn render_vdom(
}
}
vdom.work_with_deadline(|| false);
let mutations = vdom.work_with_deadline(|| false);
let to_update = tree.apply_mutations(mutations);
let _to_rerender = tree
.update_state(&vdom, to_update, &mut stretch, &mut ())
.unwrap();
}
disable_raw_mode()?;
+26 -52
View File
@@ -1,5 +1,5 @@
use dioxus_core::*;
use std::{collections::HashMap, io::Stdout};
use dioxus_native_core::{Tree, TreeNode};
use std::io::Stdout;
use stretch2::{
geometry::Point,
prelude::{Layout, Size},
@@ -9,8 +9,9 @@ use tui::{backend::CrosstermBackend, layout::Rect};
use crate::{
style::{RinkColor, RinkStyle},
style_attributes::{BorderEdge, BorderStyle},
widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext},
BorderEdge, BorderStyle, Config, TuiNode, UnitSystem,
Config, RinkLayout, StyleModifier, UnitSystem,
};
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
@@ -18,43 +19,22 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
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>,
// this holds the accumulated syle state for styled text rendering
style: &RinkStyle,
tree: &Tree<RinkLayout, StyleModifier>,
node: &TreeNode<RinkLayout, StyleModifier>,
cfg: Config,
) {
match node {
VNode::Fragment(f) => {
for child in f.children {
render_vnode(frame, layout, layouts, vdom, child, style, cfg);
}
return;
}
VNode::Component(vcomp) => {
let idx = vcomp.scope.get().unwrap();
let new_node = vdom.get_scope(idx).unwrap().root_node();
render_vnode(frame, layout, layouts, vdom, new_node, style, cfg);
return;
}
VNode::Placeholder(_) => return,
VNode::Element(_) | VNode::Text(_) => {}
match &node.node_type {
dioxus_native_core::TreeNodeType::Placeholder => return,
_ => (),
}
let id = node.try_mounted_id().unwrap();
let mut node = layouts.remove(&id).unwrap();
let Layout { location, size, .. } = layout.layout(node.layout).unwrap();
let Layout { location, size, .. } = layout.layout(node.up_state.node.unwrap()).unwrap();
let Point { x, y } = location;
let Size { width, height } = size;
match node.node {
VNode::Text(t) => {
match &node.node_type {
dioxus_native_core::TreeNodeType::Text { text } => {
#[derive(Default)]
struct Label<'a> {
text: &'a str,
@@ -65,6 +45,7 @@ pub fn render_vnode<'a>(
fn render(self, area: Rect, mut buf: RinkBuffer) {
for (i, c) in self.text.char_indices() {
let mut new_cell = RinkCell::default();
// println!("{:?}", self.style);
new_cell.set_style(self.style);
new_cell.symbol = c.to_string();
buf.set(area.left() + i as u16, area.top(), &new_cell);
@@ -73,8 +54,8 @@ pub fn render_vnode<'a>(
}
let label = Label {
text: t.text,
style: *style,
text: &text,
style: node.down_state.style,
};
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
@@ -83,30 +64,23 @@ pub fn render_vnode<'a>(
frame.render_widget(WidgetWithContext::new(label, cfg), area);
}
}
VNode::Element(el) => {
dioxus_native_core::TreeNodeType::Element { children, .. } => {
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
let mut new_style = node.block_style.merge(*style);
node.block_style = new_style;
// the renderer will panic if a node is rendered out of range even if the size is zero
if area.width > 0 && area.height > 0 {
frame.render_widget(WidgetWithContext::new(node, cfg), area);
}
// do not pass background color to children
new_style.bg = None;
for el in el.children {
render_vnode(frame, layout, layouts, vdom, el, &new_style, cfg);
for c in children {
render_vnode(frame, layout, tree, tree.get(c.0), cfg);
}
}
VNode::Fragment(_) => todo!(),
VNode::Component(_) => todo!(),
VNode::Placeholder(_) => todo!(),
dioxus_native_core::TreeNodeType::Placeholder => unreachable!(),
}
}
impl<'a> RinkWidget for TuiNode<'a> {
impl RinkWidget for &TreeNode<RinkLayout, StyleModifier> {
fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
use tui::symbols::line::*;
@@ -290,14 +264,14 @@ impl<'a> RinkWidget for TuiNode<'a> {
for x in area.left()..area.right() {
for y in area.top()..area.bottom() {
let mut new_cell = RinkCell::default();
if let Some(c) = self.block_style.bg {
if let Some(c) = self.down_state.style.bg {
new_cell.bg = c;
}
buf.set(x, y, &new_cell);
}
}
let borders = self.tui_modifier.borders;
let borders = &self.down_state.modifier.borders;
let last_edge = &borders.left;
let current_edge = &borders.top;
@@ -314,7 +288,7 @@ impl<'a> RinkWidget for TuiNode<'a> {
(last_r * RADIUS_MULTIPLIER[0]) as u16,
(last_r * RADIUS_MULTIPLIER[1]) as u16,
];
let color = current_edge.color.or(self.block_style.fg);
let color = current_edge.color.or(self.down_state.style.fg);
let mut new_cell = RinkCell::default();
if let Some(c) = color {
new_cell.fg = c;
@@ -349,7 +323,7 @@ impl<'a> RinkWidget for TuiNode<'a> {
(last_r * RADIUS_MULTIPLIER[0]) as u16,
(last_r * RADIUS_MULTIPLIER[1]) as u16,
];
let color = current_edge.color.or(self.block_style.fg);
let color = current_edge.color.or(self.down_state.style.fg);
let mut new_cell = RinkCell::default();
if let Some(c) = color {
new_cell.fg = c;
@@ -384,7 +358,7 @@ impl<'a> RinkWidget for TuiNode<'a> {
(last_r * RADIUS_MULTIPLIER[0]) as u16,
(last_r * RADIUS_MULTIPLIER[1]) as u16,
];
let color = current_edge.color.or(self.block_style.fg);
let color = current_edge.color.or(self.down_state.style.fg);
let mut new_cell = RinkCell::default();
if let Some(c) = color {
new_cell.fg = c;
@@ -419,7 +393,7 @@ impl<'a> RinkWidget for TuiNode<'a> {
(last_r * RADIUS_MULTIPLIER[0]) as u16,
(last_r * RADIUS_MULTIPLIER[1]) as u16,
];
let color = current_edge.color.or(self.block_style.fg);
let color = current_edge.color.or(self.down_state.style.fg);
let mut new_cell = RinkCell::default();
if let Some(c) = color {
new_cell.fg = c;
+46 -42
View File
@@ -4,17 +4,17 @@ use tui::style::{Color, Modifier, Style};
use crate::RenderingMode;
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RinkColor {
pub color: Color,
pub alpha: f32,
pub alpha: u8,
}
impl Default for RinkColor {
fn default() -> Self {
Self {
color: Color::Black,
alpha: 0.0,
alpha: 0,
}
}
}
@@ -23,22 +23,17 @@ impl RinkColor {
pub fn blend(self, other: Color) -> Color {
if self.color == Color::Reset {
Color::Reset
} else if self.alpha == 0.0 {
} else if self.alpha == 0 {
other
} else {
let [sr, sg, sb] = to_rgb(self.color);
let [or, og, ob] = to_rgb(other);
let (sr, sg, sb, sa) = (
sr as f32 / 255.0,
sg as f32 / 255.0,
sb as f32 / 255.0,
self.alpha,
);
let (or, og, ob) = (or as f32 / 255.0, og as f32 / 255.0, ob as f32 / 255.0);
let [sr, sg, sb] = to_rgb(self.color).map(|e| e as u16);
let [or, og, ob] = to_rgb(other).map(|e| e as u16);
let sa: u16 = self.alpha as u16;
let rsa = 255 - sa;
Color::Rgb(
(255.0 * (sr * sa + or * (1.0 - sa))) as u8,
(255.0 * (sg * sa + og * (1.0 - sa))) as u8,
(255.0 * (sb * sa + ob * (1.0 - sa))) as u8,
((sr * sa + or * rsa) / 255) as u8,
((sg * sa + og * rsa) / 255) as u8,
((sb * sa + ob * rsa) / 255) as u8,
)
}
}
@@ -151,75 +146,75 @@ impl FromStr for RinkColor {
match color {
"red" => Ok(RinkColor {
color: Color::Red,
alpha: 1.0,
alpha: 255,
}),
"black" => Ok(RinkColor {
color: Color::Black,
alpha: 1.0,
alpha: 255,
}),
"green" => Ok(RinkColor {
color: Color::Green,
alpha: 1.0,
alpha: 255,
}),
"yellow" => Ok(RinkColor {
color: Color::Yellow,
alpha: 1.0,
alpha: 255,
}),
"blue" => Ok(RinkColor {
color: Color::Blue,
alpha: 1.0,
alpha: 255,
}),
"magenta" => Ok(RinkColor {
color: Color::Magenta,
alpha: 1.0,
alpha: 255,
}),
"cyan" => Ok(RinkColor {
color: Color::Cyan,
alpha: 1.0,
alpha: 255,
}),
"gray" => Ok(RinkColor {
color: Color::Gray,
alpha: 1.0,
alpha: 255,
}),
"darkgray" => Ok(RinkColor {
color: Color::DarkGray,
alpha: 1.0,
alpha: 255,
}),
// light red does not exist
"orangered" => Ok(RinkColor {
color: Color::LightRed,
alpha: 1.0,
alpha: 255,
}),
"lightgreen" => Ok(RinkColor {
color: Color::LightGreen,
alpha: 1.0,
alpha: 255,
}),
"lightyellow" => Ok(RinkColor {
color: Color::LightYellow,
alpha: 1.0,
alpha: 255,
}),
"lightblue" => Ok(RinkColor {
color: Color::LightBlue,
alpha: 1.0,
alpha: 255,
}),
// light magenta does not exist
"orchid" => Ok(RinkColor {
color: Color::LightMagenta,
alpha: 1.0,
alpha: 255,
}),
"lightcyan" => Ok(RinkColor {
color: Color::LightCyan,
alpha: 1.0,
alpha: 255,
}),
"white" => Ok(RinkColor {
color: Color::White,
alpha: 1.0,
alpha: 255,
}),
_ => {
if color.len() == 7 && color.starts_with('#') {
parse_hex(color).map(|c| RinkColor {
color: c,
alpha: 1.0,
alpha: 255,
})
} else if let Some(stripped) = color.strip_prefix("rgb(") {
let color_values = stripped.trim_end_matches(')');
@@ -234,7 +229,7 @@ impl FromStr for RinkColor {
} else {
parse_rgb(color_values).map(|c| RinkColor {
color: c,
alpha: 1.0,
alpha: 255,
})
}
} else if let Some(stripped) = color.strip_prefix("rgba(") {
@@ -243,14 +238,17 @@ impl FromStr for RinkColor {
let (rgb_values, alpha) =
color_values.rsplit_once(',').ok_or(ParseColorError)?;
if let Ok(a) = parse_value(alpha, 1.0, 1.0) {
parse_rgb(rgb_values).map(|c| RinkColor { color: c, alpha: a })
parse_rgb(rgb_values).map(|c| RinkColor {
color: c,
alpha: (a * 255.0) as u8,
})
} else {
Err(ParseColorError)
}
} else {
parse_rgb(color_values).map(|c| RinkColor {
color: c,
alpha: 1.0,
alpha: 255,
})
}
} else if let Some(stripped) = color.strip_prefix("hsl(") {
@@ -259,14 +257,17 @@ impl FromStr for RinkColor {
let (rgb_values, alpha) =
color_values.rsplit_once(',').ok_or(ParseColorError)?;
if let Ok(a) = parse_value(alpha, 1.0, 1.0) {
parse_hsl(rgb_values).map(|c| RinkColor { color: c, alpha: a })
parse_hsl(rgb_values).map(|c| RinkColor {
color: c,
alpha: (a * 255.0) as u8,
})
} else {
Err(ParseColorError)
}
} else {
parse_hsl(color_values).map(|c| RinkColor {
color: c,
alpha: 1.0,
alpha: 255,
})
}
} else if let Some(stripped) = color.strip_prefix("hsla(") {
@@ -275,14 +276,17 @@ impl FromStr for RinkColor {
let (rgb_values, alpha) =
color_values.rsplit_once(',').ok_or(ParseColorError)?;
if let Ok(a) = parse_value(alpha, 1.0, 1.0) {
parse_hsl(rgb_values).map(|c| RinkColor { color: c, alpha: a })
parse_hsl(rgb_values).map(|c| RinkColor {
color: c,
alpha: (a * 255.0) as u8,
})
} else {
Err(ParseColorError)
}
} else {
parse_hsl(color_values).map(|c| RinkColor {
color: c,
alpha: 1.0,
alpha: 255,
})
}
} else {
@@ -393,7 +397,7 @@ fn rgb_to_ansi() {
}
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct RinkStyle {
pub fg: Option<RinkColor>,
pub bg: Option<RinkColor>,
@@ -406,7 +410,7 @@ impl Default for RinkStyle {
Self {
fg: Some(RinkColor {
color: Color::White,
alpha: 1.0,
alpha: 255,
}),
bg: None,
add_modifier: Modifier::empty(),
+582
View File
@@ -0,0 +1,582 @@
/*
- [ ] pub display: Display,
- [x] pub position_type: PositionType, --> kinda, stretch doesnt support everything
- [ ] 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,
- [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,
- [x] pub margin: Rect<Dimension>,
- [x] pub padding: Rect<Dimension>,
- [x] pub justify_content: JustifyContent,
- [ ] pub position: Rect<Dimension>,
- [x] pub border: Rect<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 dioxus_core::{Attribute, VNode};
use dioxus_native_core::PushedDownState;
use crate::{
parse_value,
style::{RinkColor, RinkStyle},
UnitSystem,
};
#[derive(Default, Clone, PartialEq, Debug)]
pub struct StyleModifier {
pub style: RinkStyle,
pub modifier: TuiModifier,
}
impl PushedDownState for StyleModifier {
type Ctx = ();
fn reduce(&mut self, parent: Option<&Self>, vnode: &VNode, _ctx: &mut Self::Ctx) {
*self = StyleModifier::default();
self.style.fg = None;
match vnode {
VNode::Element(el) => {
// handle text modifier elements
if el.namespace.is_none() {
match el.tag {
"b" => apply_style_attributes("font-weight", "bold", self),
"strong" => apply_style_attributes("font-weight", "bold", self),
"u" => apply_style_attributes("text-decoration", "underline", self),
"ins" => apply_style_attributes("text-decoration", "underline", self),
"del" => apply_style_attributes("text-decoration", "line-through", self),
"i" => apply_style_attributes("font-style", "italic", self),
"em" => apply_style_attributes("font-style", "italic", self),
"mark" => apply_style_attributes(
"background-color",
"rgba(241, 231, 64, 50%)",
self,
),
_ => (),
}
}
// gather up all the styles from the attribute list
for &Attribute { name, value, .. } in el.attributes {
apply_style_attributes(name, value, self);
}
}
_ => (),
}
// keep the text styling from the parent element
if let Some(parent) = parent {
let mut new_style = self.style.merge(parent.style);
new_style.bg = self.style.bg;
self.style = new_style;
}
}
}
#[derive(Default, Clone, PartialEq, Debug)]
pub struct TuiModifier {
pub borders: Borders,
}
#[derive(Default, Clone, PartialEq, Debug)]
pub struct Borders {
pub top: BorderEdge,
pub right: BorderEdge,
pub bottom: BorderEdge,
pub left: BorderEdge,
}
impl Borders {
fn slice(&mut self) -> [&mut BorderEdge; 4] {
[
&mut self.top,
&mut self.right,
&mut self.bottom,
&mut self.left,
]
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct BorderEdge {
pub color: Option<RinkColor>,
pub style: BorderStyle,
pub width: UnitSystem,
pub radius: UnitSystem,
}
impl Default for BorderEdge {
fn default() -> Self {
Self {
color: None,
style: BorderStyle::NONE,
width: UnitSystem::Point(0.0),
radius: UnitSystem::Point(0.0),
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum BorderStyle {
DOTTED,
DASHED,
SOLID,
DOUBLE,
GROOVE,
RIDGE,
INSET,
OUTSET,
HIDDEN,
NONE,
}
impl BorderStyle {
pub fn symbol_set(&self) -> Option<tui::symbols::line::Set> {
use tui::symbols::line::*;
const DASHED: Set = Set {
horizontal: "╌",
vertical: "╎",
..NORMAL
};
const DOTTED: Set = Set {
horizontal: "┈",
vertical: "┊",
..NORMAL
};
match self {
BorderStyle::DOTTED => Some(DOTTED),
BorderStyle::DASHED => Some(DASHED),
BorderStyle::SOLID => Some(NORMAL),
BorderStyle::DOUBLE => Some(DOUBLE),
BorderStyle::GROOVE => Some(NORMAL),
BorderStyle::RIDGE => Some(NORMAL),
BorderStyle::INSET => Some(NORMAL),
BorderStyle::OUTSET => Some(NORMAL),
BorderStyle::HIDDEN => None,
BorderStyle::NONE => None,
}
}
}
/// applies the entire html namespace defined in dioxus-html
pub fn apply_style_attributes(
//
name: &str,
value: &str,
style: &mut StyleModifier,
) {
match name {
"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" => {
if let Ok(c) = value.parse() {
style.style.fg.replace(c);
}
}
"columns" => {}
"content" => {}
"counter-increment" => {}
"counter-reset" => {}
"cursor" => {}
"empty-cells" => {}
"float" => {}
"font" | "font-family" | "font-size" | "font-size-adjust" | "font-stretch"
| "font-style" | "font-variant" | "font-weight" => apply_font(name, value, style),
"letter-spacing" => {}
"line-height" => {}
"list-style" | "list-style-image" | "list-style-position" | "list-style-type" => {}
"opacity" => {}
"order" => {}
"outline" => {}
"outline-color" | "outline-offset" | "outline-style" | "outline-width" => {}
"page-break-after" | "page-break-before" | "page-break-inside" => {}
"perspective" | "perspective-origin" => {}
"pointer-events" => {}
"quotes" => {}
"resize" => {}
"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),
"transition"
| "transition-delay"
| "transition-duration"
| "transition-property"
| "transition-timing-function" => apply_transition(name, value, style),
"visibility" => {}
"white-space" => {}
_ => {}
}
}
fn apply_background(name: &str, value: &str, style: &mut StyleModifier) {
match name {
"background-color" => {
if let Ok(c) = value.parse() {
style.style.bg.replace(c);
}
}
"background" => {}
"background-attachment" => {}
"background-clip" => {}
"background-image" => {}
"background-origin" => {}
"background-position" => {}
"background-repeat" => {}
"background-size" => {}
_ => {}
}
}
fn apply_border(name: &str, value: &str, style: &mut StyleModifier) {
fn parse_border_style(v: &str) -> BorderStyle {
match v {
"dotted" => BorderStyle::DOTTED,
"dashed" => BorderStyle::DASHED,
"solid" => BorderStyle::SOLID,
"double" => BorderStyle::DOUBLE,
"groove" => BorderStyle::GROOVE,
"ridge" => BorderStyle::RIDGE,
"inset" => BorderStyle::INSET,
"outset" => BorderStyle::OUTSET,
"none" => BorderStyle::NONE,
"hidden" => BorderStyle::HIDDEN,
_ => todo!(),
}
}
match name {
"border" => {}
"border-bottom" => {}
"border-bottom-color" => {
if let Ok(c) = value.parse() {
style.modifier.borders.bottom.color = Some(c);
}
}
"border-bottom-left-radius" => {
if let Some(v) = parse_value(value) {
style.modifier.borders.left.radius = v;
}
}
"border-bottom-right-radius" => {
if let Some(v) = parse_value(value) {
style.modifier.borders.right.radius = v;
}
}
"border-bottom-style" => style.modifier.borders.bottom.style = parse_border_style(value),
"border-bottom-width" => {
if let Some(v) = parse_value(value) {
style.modifier.borders.bottom.width = v;
}
}
"border-collapse" => {}
"border-color" => {
let values: Vec<_> = value.split(' ').collect();
if values.len() == 1 {
if let Ok(c) = values[0].parse() {
style
.modifier
.borders
.slice()
.iter_mut()
.for_each(|b| b.color = Some(c));
}
} else {
for (v, b) in values
.into_iter()
.zip(style.modifier.borders.slice().iter_mut())
{
if let Ok(c) = v.parse() {
b.color = Some(c);
}
}
}
}
"border-image" => {}
"border-image-outset" => {}
"border-image-repeat" => {}
"border-image-slice" => {}
"border-image-source" => {}
"border-image-width" => {}
"border-left" => {}
"border-left-color" => {
if let Ok(c) = value.parse() {
style.modifier.borders.left.color = Some(c);
}
}
"border-left-style" => style.modifier.borders.left.style = parse_border_style(value),
"border-left-width" => {
if let Some(v) = parse_value(value) {
style.modifier.borders.left.width = v;
}
}
"border-radius" => {
let values: Vec<_> = value.split(' ').collect();
if values.len() == 1 {
if let Some(r) = parse_value(values[0]) {
style
.modifier
.borders
.slice()
.iter_mut()
.for_each(|b| b.radius = r);
}
} else {
for (v, b) in values
.into_iter()
.zip(style.modifier.borders.slice().iter_mut())
{
if let Some(r) = parse_value(v) {
b.radius = r;
}
}
}
}
"border-right" => {}
"border-right-color" => {
if let Ok(c) = value.parse() {
style.modifier.borders.right.color = Some(c);
}
}
"border-right-style" => style.modifier.borders.right.style = parse_border_style(value),
"border-right-width" => {
if let Some(v) = parse_value(value) {
style.modifier.borders.right.width = v;
}
}
"border-spacing" => {}
"border-style" => {
let values: Vec<_> = value.split(' ').collect();
if values.len() == 1 {
let border_style = parse_border_style(values[0]);
style
.modifier
.borders
.slice()
.iter_mut()
.for_each(|b| b.style = border_style);
} else {
for (v, b) in values
.into_iter()
.zip(style.modifier.borders.slice().iter_mut())
{
b.style = parse_border_style(v);
}
}
}
"border-top" => {}
"border-top-color" => {
if let Ok(c) = value.parse() {
style.modifier.borders.top.color = Some(c);
}
}
"border-top-left-radius" => {
if let Some(v) = parse_value(value) {
style.modifier.borders.left.radius = v;
}
}
"border-top-right-radius" => {
if let Some(v) = parse_value(value) {
style.modifier.borders.right.radius = v;
}
}
"border-top-style" => style.modifier.borders.top.style = parse_border_style(value),
"border-top-width" => {
if let Some(v) = parse_value(value) {
style.modifier.borders.top.width = v;
}
}
"border-width" => {
let values: Vec<_> = value.split(' ').collect();
if values.len() == 1 {
if let Some(w) = parse_value(values[0]) {
style
.modifier
.borders
.slice()
.iter_mut()
.for_each(|b| b.width = w);
}
} else {
for (v, width) in values
.into_iter()
.zip(style.modifier.borders.slice().iter_mut())
{
if let Some(w) = parse_value(v) {
width.width = w.into();
}
}
}
}
_ => (),
}
}
fn apply_animation(name: &str, _value: &str, _style: &mut StyleModifier) {
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_font(name: &str, value: &str, style: &mut StyleModifier) {
use tui::style::Modifier;
match name {
"font" => (),
"font-family" => (),
"font-size" => (),
"font-size-adjust" => (),
"font-stretch" => (),
"font-style" => match value {
"italic" => style.style = style.style.add_modifier(Modifier::ITALIC),
"oblique" => style.style = style.style.add_modifier(Modifier::ITALIC),
_ => (),
},
"font-variant" => todo!(),
"font-weight" => match value {
"bold" => style.style = style.style.add_modifier(Modifier::BOLD),
"normal" => style.style = style.style.remove_modifier(Modifier::BOLD),
_ => (),
},
_ => (),
}
}
fn apply_text(name: &str, value: &str, style: &mut StyleModifier) {
use tui::style::Modifier;
match name {
"text-align" => todo!(),
"text-align-last" => todo!(),
"text-decoration" | "text-decoration-line" => {
for v in value.split(' ') {
match v {
"line-through" => style.style = style.style.add_modifier(Modifier::CROSSED_OUT),
"underline" => style.style = style.style.add_modifier(Modifier::UNDERLINED),
_ => (),
}
}
}
"text-decoration-color" => todo!(),
"text-decoration-style" => todo!(),
"text-indent" => todo!(),
"text-justify" => todo!(),
"text-overflow" => todo!(),
"text-shadow" => todo!(),
"text-transform" => todo!(),
_ => todo!(),
}
}
fn apply_transition(_name: &str, _value: &str, _style: &mut StyleModifier) {
todo!()
}
+83
View File
@@ -0,0 +1,83 @@
// use dioxus_core::{Element, ElementId, Mutations, VNode, VirtualDom, DomEdit};
// /// The focus system needs a iterator that can persist through changes in the [VirtualDom]. Iterate through it with [ElementIter::next], and update it with [ElementIter::update] (with data from [`VirtualDom::work_with_deadline`]).
// pub(crate) struct ElementIter {
// // stack of elements and fragments
// stack: smallvec::SmallVec<[(ElementId, usize); 5]>,
// }
// impl ElementIter {
// pub(crate) fn new(initial: ElementId) -> Self {
// ElementIter {
// stack: smallvec::smallvec![(initial, 0)],
// }
// }
// /// remove stale element refreneces
// pub(crate) fn update(&mut self, mutations: &Mutations, vdom: &VirtualDom) {
// let ids_removed: Vec<_> = mutations.edits.iter().filter_map(|e| if let DomEdit::Remove{root: }).collect();
// for node in self.stack {
// match node.0 {
// VNode::Fragment(f) => {
// }
// VNode::Element(_) => {}
// _ => unreachable!(),
// }
// }
// }
// pub(crate) fn next<'a>(&mut self, vdom: &'a VirtualDom) -> Option<&'a VNode<'a>> {
// let last = self.stack.last()?.0;
// let node = vdom.get_element(last)?;
// match node {
// VNode::Fragment(f) => {
// let mut last_mut = self.stack.last_mut()?;
// if last_mut.1 + 1 >= f.children.len() {
// self.stack.pop();
// self.next(vdom)
// } else {
// last_mut.1 += 1;
// let new_node = &f.children[last_mut.1];
// if matches!(new_node, VNode::Fragment(_) | VNode::Element(_)) {
// self.stack.push((new_node.mounted_id(), 0));
// }
// self.next(vdom)
// }
// }
// VNode::Component(vcomp) => {
// let idx = vcomp.scope.get().unwrap();
// let new_node = vdom.get_scope(idx).unwrap().root_node();
// *self.stack.last_mut()? = (new_node.mounted_id(), 0);
// self.next(vdom)
// }
// VNode::Placeholder(_) | VNode::Text(_) => {
// self.stack.pop();
// self.next(vdom)
// }
// VNode::Element(e) => {
// let mut last_mut = self.stack.last_mut()?;
// if last_mut.1 + 1 >= e.children.len() {
// self.stack.pop();
// self.next(vdom);
// } else {
// last_mut.1 += 1;
// let new_node = &e.children[last_mut.1];
// if matches!(new_node, VNode::Fragment(_) | VNode::Element(_)) {
// self.stack.push((new_node.mounted_id(), 0));
// }
// self.next(vdom);
// }
// Some(node)
// }
// }
// }
// pub(crate) fn peak(&self) -> Option<&ElementId> {
// self.stack.last().map(|(e, c)| e)
// }
// }
+6 -2
View File
@@ -21,6 +21,10 @@ impl<'a> RinkBuffer<'a> {
}
pub fn set(&mut self, x: u16, y: u16, new: &RinkCell) {
let area = self.buf.area();
if x < area.x || x > area.width || y < area.y || y > area.height {
panic!("({x}, {y}) is not in {area:?}");
}
let mut cell = self.buf.get_mut(x, y);
cell.bg = convert(self.cfg.rendering_mode, new.bg.blend(cell.bg));
if new.symbol.is_empty() {
@@ -71,11 +75,11 @@ impl Default for RinkCell {
symbol: "".to_string(),
fg: RinkColor {
color: Color::Rgb(0, 0, 0),
alpha: 0.0,
alpha: 0,
},
bg: RinkColor {
color: Color::Rgb(0, 0, 0),
alpha: 0.0,
alpha: 0,
},
modifier: Modifier::empty(),
}