added border and more attributes
This commit is contained in:
1 parent
7a1fdeb673
commit
f6ef9981dd
7 files changed
+745
-115
No files matched your search
+1
-1
@@ -6,7 +6,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tui = { version = "0.16.0", features = ["crossterm"], default-features = false }
|
||||
tui = "0.17.0"
|
||||
crossterm = "0.22.1"
|
||||
anyhow = "1.0.42"
|
||||
thiserror = "1.0.24"
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
rink::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let (radius, set_radius) = use_state(&cx, || 0);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
justify_content: "center",
|
||||
align_items: "center",
|
||||
background_color: "hsl(248, 53%, 58%)",
|
||||
onwheel: move |w| set_radius((radius + w.delta_y as i8).abs()),
|
||||
|
||||
// the border can either be solid, double, thick, OR rounded
|
||||
// if multable are set only the last style is appiled
|
||||
// to skip a side set the style to none
|
||||
border_style: "solid none solid double",
|
||||
border_width: "thick",
|
||||
border_radius: "{radius}px",
|
||||
border_color: "#0000FF #FF00FF #FF0000 #00FF00",
|
||||
|
||||
"{radius}"
|
||||
}
|
||||
})
|
||||
}
|
||||
+26
-7
@@ -53,13 +53,6 @@ fn app(cx: Scope) -> Element {
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
"zib"
|
||||
}
|
||||
p {
|
||||
background_color: "yellow",
|
||||
@@ -77,6 +70,32 @@ fn app(cx: Scope) -> Element {
|
||||
background_color: "cyan",
|
||||
"asd"
|
||||
}
|
||||
div {
|
||||
font_weight: "bold",
|
||||
color: "#666666",
|
||||
p{
|
||||
"bold"
|
||||
}
|
||||
p {
|
||||
font_weight: "normal",
|
||||
" normal"
|
||||
}
|
||||
}
|
||||
p {
|
||||
font_style: "italic",
|
||||
color: "red",
|
||||
"italic"
|
||||
}
|
||||
p {
|
||||
text_decoration: "underline",
|
||||
color: "rgb(50, 100, 255)",
|
||||
"underline"
|
||||
}
|
||||
p {
|
||||
text_decoration: "line-through",
|
||||
color: "hsl(10, 100%, 70%)",
|
||||
"line-through"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
+349
-95
@@ -30,15 +30,155 @@
|
||||
*/
|
||||
|
||||
use stretch2::{prelude::*, style::PositionType, style::Style};
|
||||
use tui::style::Style as TuiStyle;
|
||||
use tui::style::{Color, Style as TuiStyle};
|
||||
|
||||
pub struct StyleModifer {
|
||||
pub style: Style,
|
||||
pub tui_style: TuiStyle,
|
||||
pub tui_modifier: TuiModifier,
|
||||
}
|
||||
|
||||
enum TuiModifier {
|
||||
Text,
|
||||
pub struct TuiModifier {
|
||||
// border arrays start at the top and proceed clockwise
|
||||
pub border_colors: [Option<Color>; 4],
|
||||
pub border_types: [BorderType; 4],
|
||||
pub border_widths: [UnitSystem; 4],
|
||||
pub border_radi: [UnitSystem; 4],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum BorderType {
|
||||
DOTTED,
|
||||
DASHED,
|
||||
SOLID,
|
||||
DOUBLE,
|
||||
GROOVE,
|
||||
RIDGE,
|
||||
INSET,
|
||||
OUTSET,
|
||||
HIDDEN,
|
||||
NONE,
|
||||
}
|
||||
|
||||
impl Default for TuiModifier {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
border_colors: [None; 4],
|
||||
border_types: [BorderType::NONE; 4],
|
||||
border_widths: [UnitSystem::Point(0.0); 4],
|
||||
border_radi: [UnitSystem::Point(0.0); 4],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_color(color: &str) -> Option<tui::style::Color> {
|
||||
match color {
|
||||
"red" => Some(Color::Red),
|
||||
"green" => Some(Color::Green),
|
||||
"blue" => Some(Color::Blue),
|
||||
"yellow" => Some(Color::Yellow),
|
||||
"cyan" => Some(Color::Cyan),
|
||||
"magenta" => Some(Color::Magenta),
|
||||
"white" => Some(Color::White),
|
||||
"black" => Some(Color::Black),
|
||||
_ => {
|
||||
if color.len() == 7 && color.starts_with('#') {
|
||||
let mut values = [0, 0, 0];
|
||||
let mut color_ok = true;
|
||||
for i in 0..values.len() {
|
||||
if let Ok(v) = u8::from_str_radix(&color[(1 + 2 * i)..(1 + 2 * (i + 1))], 16) {
|
||||
values[i] = v;
|
||||
} else {
|
||||
color_ok = false;
|
||||
}
|
||||
}
|
||||
if color_ok {
|
||||
Some(Color::Rgb(values[0], values[1], values[2]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if color.starts_with("rgb(") {
|
||||
let mut values = [0, 0, 0];
|
||||
let mut color_ok = true;
|
||||
for (v, i) in color[4..]
|
||||
.trim_end_matches(')')
|
||||
.split(',')
|
||||
.zip(0..values.len())
|
||||
{
|
||||
if let Ok(v) = v.trim().parse() {
|
||||
values[i] = v;
|
||||
} else {
|
||||
color_ok = false;
|
||||
}
|
||||
}
|
||||
if color_ok {
|
||||
Some(Color::Rgb(values[0], values[1], values[2]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if color.starts_with("hsl(") {
|
||||
let mut values = [0, 0, 0];
|
||||
let mut color_ok = true;
|
||||
for (v, i) in color[4..]
|
||||
.trim_end_matches(')')
|
||||
.split(',')
|
||||
.zip(0..values.len())
|
||||
{
|
||||
if let Ok(v) = v.trim_end_matches('%').trim().parse() {
|
||||
values[i] = v;
|
||||
} else {
|
||||
color_ok = false;
|
||||
}
|
||||
}
|
||||
if color_ok {
|
||||
let [h, s, l] = [
|
||||
values[0] as f32 / 360.0,
|
||||
values[1] as f32 / 100.0,
|
||||
values[2] as f32 / 100.0,
|
||||
];
|
||||
let rgb = if s == 0.0 {
|
||||
[l as u8; 3]
|
||||
} else {
|
||||
fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
|
||||
if t < 0.0 {
|
||||
t += 1.0;
|
||||
}
|
||||
if t > 1.0 {
|
||||
t -= 1.0;
|
||||
}
|
||||
if t < 1.0 / 6.0 {
|
||||
p + (q - p) * 6.0 * t
|
||||
} else if t < 1.0 / 2.0 {
|
||||
q
|
||||
} else if t < 2.0 / 3.0 {
|
||||
p + (q - p) * (2.0 / 3.0 - t) * 6.0
|
||||
} else {
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
let q = if l < 0.5 {
|
||||
l * (1.0 + s)
|
||||
} else {
|
||||
l + s - l * s
|
||||
};
|
||||
let p = 2.0 * l - q;
|
||||
[
|
||||
(hue_to_rgb(p, q, h + 1.0 / 3.0) * 255.0) as u8,
|
||||
(hue_to_rgb(p, q, h) * 255.0) as u8,
|
||||
(hue_to_rgb(p, q, h - 1.0 / 3.0) * 255.0) as u8,
|
||||
]
|
||||
};
|
||||
|
||||
Some(Color::Rgb(rgb[0], rgb[1], rgb[2]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// applies the entire html namespace defined in dioxus-html
|
||||
@@ -117,7 +257,9 @@ pub fn apply_attributes(
|
||||
"clip" => {}
|
||||
|
||||
"color" => {
|
||||
// text color
|
||||
if let Some(c) = parse_color(value) {
|
||||
style.tui_style.fg.replace(c);
|
||||
}
|
||||
}
|
||||
|
||||
"column-count"
|
||||
@@ -170,14 +312,11 @@ pub fn apply_attributes(
|
||||
| "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);
|
||||
}
|
||||
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" => {
|
||||
@@ -286,14 +425,11 @@ pub fn apply_attributes(
|
||||
"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);
|
||||
}
|
||||
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" => {}
|
||||
@@ -304,7 +440,8 @@ pub fn apply_attributes(
|
||||
}
|
||||
}
|
||||
|
||||
enum UnitSystem {
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum UnitSystem {
|
||||
Percent(f32),
|
||||
Point(f32),
|
||||
}
|
||||
@@ -344,7 +481,6 @@ fn apply_overflow(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -376,40 +512,9 @@ fn apply_display(_name: &str, value: &str, style: &mut StyleModifer) {
|
||||
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),
|
||||
_ => {
|
||||
if value.len() == 7 {
|
||||
let mut values = [0, 0, 0];
|
||||
let mut color_ok = true;
|
||||
for i in 0..values.len() {
|
||||
if let Ok(v) =
|
||||
u8::from_str_radix(&value[(1 + 2 * i)..(1 + 2 * (i + 1))], 16)
|
||||
{
|
||||
values[i] = v;
|
||||
} else {
|
||||
color_ok = false;
|
||||
}
|
||||
}
|
||||
if color_ok {
|
||||
let color = Color::Rgb(values[0], values[1], values[2]);
|
||||
style.tui_style.bg.replace(color)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(c) = parse_color(value) {
|
||||
style.tui_style.bg.replace(c);
|
||||
}
|
||||
}
|
||||
"background" => {}
|
||||
"background-attachment" => {}
|
||||
@@ -423,17 +528,63 @@ fn apply_background(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_border(name: &str, value: &str, _style: &mut StyleModifer) {
|
||||
fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
fn parse_border_type(v: &str) -> BorderType {
|
||||
match v {
|
||||
"dotted" => BorderType::DOTTED,
|
||||
"dashed" => BorderType::DASHED,
|
||||
"solid" => BorderType::SOLID,
|
||||
"double" => BorderType::DOUBLE,
|
||||
"groove" => BorderType::GROOVE,
|
||||
"ridge" => BorderType::RIDGE,
|
||||
"inset" => BorderType::INSET,
|
||||
"outset" => BorderType::OUTSET,
|
||||
"none" => BorderType::NONE,
|
||||
"hidden" => BorderType::HIDDEN,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
match name {
|
||||
"border" => {}
|
||||
"border-bottom" => {}
|
||||
"border-bottom-color" => {}
|
||||
"border-bottom-left-radius" => {}
|
||||
"border-bottom-right-radius" => {}
|
||||
"border-bottom-style" => {}
|
||||
"border-bottom-width" => {}
|
||||
"border-bottom-color" => {
|
||||
if let Some(c) = parse_color(value) {
|
||||
style.tui_modifier.border_colors[2] = Some(c);
|
||||
}
|
||||
}
|
||||
"border-bottom-left-radius" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.tui_modifier.border_radi[2] = v;
|
||||
}
|
||||
}
|
||||
"border-bottom-right-radius" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.tui_modifier.border_radi[1] = v;
|
||||
}
|
||||
}
|
||||
"border-bottom-style" => style.tui_modifier.border_types[2] = parse_border_type(value),
|
||||
"border-bottom-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.tui_modifier.border_widths[2] = v;
|
||||
}
|
||||
}
|
||||
"border-collapse" => {}
|
||||
"border-color" => {}
|
||||
"border-color" => {
|
||||
let values: Vec<_> = value.split(' ').collect();
|
||||
if values.len() == 1 {
|
||||
if let Some(c) = parse_color(values[0]) {
|
||||
for i in 0..4 {
|
||||
style.tui_modifier.border_colors[i] = Some(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i, v) in values.into_iter().enumerate() {
|
||||
if let Some(c) = parse_color(v) {
|
||||
style.tui_modifier.border_colors[i] = Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"border-image" => {}
|
||||
"border-image-outset" => {}
|
||||
"border-image-repeat" => {}
|
||||
@@ -441,28 +592,89 @@ fn apply_border(name: &str, value: &str, _style: &mut StyleModifer) {
|
||||
"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;
|
||||
"border-left-color" => {
|
||||
if let Some(c) = parse_color(value) {
|
||||
style.tui_modifier.border_colors[3] = Some(c);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
"border-left-style" => style.tui_modifier.border_types[3] = parse_border_type(value),
|
||||
"border-left-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.tui_modifier.border_widths[3] = v;
|
||||
}
|
||||
}
|
||||
"border-radius" => {
|
||||
let values: Vec<_> = value.split(' ').collect();
|
||||
if values.len() == 1 {
|
||||
if let Some(r) = parse_value(values[0]) {
|
||||
for i in 0..4 {
|
||||
style.tui_modifier.border_radi[i] = r;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i, v) in values.into_iter().enumerate() {
|
||||
if let Some(r) = parse_value(v) {
|
||||
style.tui_modifier.border_radi[i] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"border-right" => {}
|
||||
"border-right-color" => {
|
||||
if let Some(c) = parse_color(value) {
|
||||
style.tui_modifier.border_colors[1] = Some(c);
|
||||
}
|
||||
}
|
||||
"border-right-style" => style.tui_modifier.border_types[1] = parse_border_type(value),
|
||||
"border-right-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.tui_modifier.border_widths[1] = v;
|
||||
}
|
||||
}
|
||||
"border-spacing" => {}
|
||||
"border-style" => {
|
||||
let values: Vec<_> = value.split(' ').collect();
|
||||
if values.len() == 1 {
|
||||
let border = parse_border_type(values[0]);
|
||||
for i in 0..4 {
|
||||
style.tui_modifier.border_types[i] = border;
|
||||
}
|
||||
} else {
|
||||
for (i, v) in values.into_iter().enumerate() {
|
||||
style.tui_modifier.border_types[i] = parse_border_type(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
"border-top" => {}
|
||||
"border-top-color" => {
|
||||
if let Some(c) = parse_color(value) {
|
||||
style.tui_modifier.border_colors[0] = Some(c);
|
||||
}
|
||||
}
|
||||
"border-top-left-radius" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.tui_modifier.border_radi[3] = v;
|
||||
}
|
||||
}
|
||||
"border-top-right-radius" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.tui_modifier.border_radi[0] = v;
|
||||
}
|
||||
}
|
||||
"border-top-style" => style.tui_modifier.border_types[0] = parse_border_type(value),
|
||||
"border-top-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
style.tui_modifier.border_widths[0] = v;
|
||||
}
|
||||
}
|
||||
"border-width" => {
|
||||
if let Some(v) = parse_value(value) {
|
||||
for i in 0..4 {
|
||||
style.tui_modifier.border_widths[i] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,14 +728,11 @@ fn apply_flex(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
};
|
||||
}
|
||||
"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);
|
||||
}
|
||||
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" => {}
|
||||
@@ -550,8 +759,27 @@ fn apply_flex(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_font(_name: &str, _value: &str, _style: &mut StyleModifer) {
|
||||
todo!()
|
||||
fn apply_font(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
use tui::style::Modifier;
|
||||
match name {
|
||||
"font" => todo!(),
|
||||
"font-family" => todo!(),
|
||||
"font-size" => todo!(),
|
||||
"font-size-adjust" => todo!(),
|
||||
"font-stretch" => todo!(),
|
||||
"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) {
|
||||
@@ -587,8 +815,34 @@ fn apply_padding(name: &str, value: &str, style: &mut StyleModifer) {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_text(_name: &str, _value: &str, _style: &mut StyleModifer) {
|
||||
todo!()
|
||||
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) {
|
||||
|
||||
+4
-1
@@ -4,7 +4,7 @@ use tui::style::Style as TuiStyle;
|
||||
|
||||
use crate::{
|
||||
attributes::{apply_attributes, StyleModifer},
|
||||
TuiNode,
|
||||
TuiModifier, TuiNode,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -42,6 +42,7 @@ pub fn collect_layout<'a>(
|
||||
TuiNode {
|
||||
node,
|
||||
block_style: tui::style::Style::default(),
|
||||
tui_modifier: TuiModifier::default(),
|
||||
layout: layout.new_node(style, &[]).unwrap(),
|
||||
},
|
||||
);
|
||||
@@ -51,6 +52,7 @@ pub fn collect_layout<'a>(
|
||||
let mut modifier = StyleModifer {
|
||||
style: Style::default(),
|
||||
tui_style: TuiStyle::default(),
|
||||
tui_modifier: TuiModifier::default(),
|
||||
};
|
||||
|
||||
for &Attribute { name, value, .. } in el.attributes {
|
||||
@@ -86,6 +88,7 @@ pub fn collect_layout<'a>(
|
||||
TuiNode {
|
||||
node,
|
||||
block_style: modifier.tui_style,
|
||||
tui_modifier: modifier.tui_modifier,
|
||||
layout: layout.new_node(modifier.style, &child_layout).unwrap(),
|
||||
},
|
||||
);
|
||||
|
||||
+9
-1
@@ -43,6 +43,7 @@ pub fn launch(app: Component<()>) {
|
||||
pub struct TuiNode<'a> {
|
||||
pub layout: stretch2::node::Node,
|
||||
pub block_style: TuiStyle,
|
||||
pub tui_modifier: TuiModifier,
|
||||
pub node: &'a VNode<'a>,
|
||||
}
|
||||
|
||||
@@ -132,7 +133,14 @@ pub fn render_vdom(
|
||||
|
||||
// resolve events before rendering
|
||||
events = handler.get_events(vdom, &layout, &mut nodes, root_node);
|
||||
render::render_vnode(frame, &layout, &mut nodes, vdom, root_node);
|
||||
render::render_vnode(
|
||||
frame,
|
||||
&layout,
|
||||
&mut nodes,
|
||||
vdom,
|
||||
root_node,
|
||||
&TuiStyle::default(),
|
||||
);
|
||||
assert!(nodes.is_empty());
|
||||
})?;
|
||||
|
||||
|
||||
+326
-10
@@ -9,11 +9,320 @@ use tui::{
|
||||
backend::CrosstermBackend,
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::Style as TuiStyle,
|
||||
widgets::{Block, Widget},
|
||||
style::{Color, Style as TuiStyle},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
use crate::TuiNode;
|
||||
use crate::{BorderType, TuiNode, UnitSystem};
|
||||
|
||||
const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5];
|
||||
|
||||
impl<'a> Widget for TuiNode<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
use tui::symbols::line::*;
|
||||
|
||||
enum Direction {
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
|
||||
fn draw(
|
||||
buf: &mut Buffer,
|
||||
points_history: [[i32; 2]; 3],
|
||||
symbols: &Set,
|
||||
pos: [u16; 2],
|
||||
color: &Option<Color>,
|
||||
) {
|
||||
let [before, current, after] = points_history;
|
||||
let start_dir = match [before[0] - current[0], before[1] - current[1]] {
|
||||
[1, 0] => Direction::Right,
|
||||
[-1, 0] => Direction::Left,
|
||||
[0, 1] => Direction::Down,
|
||||
[0, -1] => Direction::Up,
|
||||
[a, b] => {
|
||||
panic!(
|
||||
"draw({:?} {:?} {:?}) {}, {} no cell adjacent",
|
||||
before, current, after, a, b
|
||||
)
|
||||
}
|
||||
};
|
||||
let end_dir = match [after[0] - current[0], after[1] - current[1]] {
|
||||
[1, 0] => Direction::Right,
|
||||
[-1, 0] => Direction::Left,
|
||||
[0, 1] => Direction::Down,
|
||||
[0, -1] => Direction::Up,
|
||||
[a, b] => {
|
||||
panic!(
|
||||
"draw({:?} {:?} {:?}) {}, {} no cell adjacent",
|
||||
before, current, after, a, b
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let cell = buf.get_mut(
|
||||
(current[0] + pos[0] as i32) as u16,
|
||||
(current[1] + pos[1] as i32) as u16,
|
||||
);
|
||||
if let Some(c) = color {
|
||||
cell.fg = *c;
|
||||
}
|
||||
cell.symbol = match [start_dir, end_dir] {
|
||||
[Direction::Down, Direction::Up] => symbols.vertical,
|
||||
[Direction::Down, Direction::Right] => symbols.top_left,
|
||||
[Direction::Down, Direction::Left] => symbols.top_right,
|
||||
[Direction::Up, Direction::Down] => symbols.vertical,
|
||||
[Direction::Up, Direction::Right] => symbols.bottom_left,
|
||||
[Direction::Up, Direction::Left] => symbols.bottom_right,
|
||||
[Direction::Right, Direction::Left] => symbols.horizontal,
|
||||
[Direction::Right, Direction::Up] => symbols.bottom_left,
|
||||
[Direction::Right, Direction::Down] => symbols.top_left,
|
||||
[Direction::Left, Direction::Up] => symbols.bottom_right,
|
||||
[Direction::Left, Direction::Right] => symbols.horizontal,
|
||||
[Direction::Left, Direction::Down] => symbols.top_right,
|
||||
_ => panic!(
|
||||
"{:?} {:?} {:?} cannont connect cell to itself",
|
||||
before, current, after
|
||||
),
|
||||
}
|
||||
.to_string();
|
||||
}
|
||||
|
||||
fn draw_arc(
|
||||
pos: [u16; 2],
|
||||
starting_angle: f32,
|
||||
arc_angle: f32,
|
||||
radius: f32,
|
||||
symbols: &Set,
|
||||
buf: &mut Buffer,
|
||||
color: &Option<Color>,
|
||||
) {
|
||||
if radius < 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let num_points = (radius * arc_angle) as i32;
|
||||
let starting_point = [
|
||||
(starting_angle.cos() * (radius * RADIUS_MULTIPLIER[0])) as i32,
|
||||
(starting_angle.sin() * (radius * RADIUS_MULTIPLIER[1])) as i32,
|
||||
];
|
||||
let mut points_history = [
|
||||
[0, 0],
|
||||
{
|
||||
// change the x or y value based on which one is changing quicker
|
||||
let ddx = -starting_angle.sin();
|
||||
let ddy = starting_angle.cos();
|
||||
if ddx.abs() > ddy.abs() {
|
||||
[starting_point[0] - ddx.signum() as i32, starting_point[1]]
|
||||
} else {
|
||||
[starting_point[0], starting_point[1] - ddy.signum() as i32]
|
||||
}
|
||||
},
|
||||
starting_point,
|
||||
];
|
||||
|
||||
for i in 1..=num_points {
|
||||
let angle = (i as f32 / num_points as f32) * arc_angle + starting_angle;
|
||||
let x = angle.cos() * radius * RADIUS_MULTIPLIER[0];
|
||||
let y = angle.sin() * radius * RADIUS_MULTIPLIER[1];
|
||||
let new = [x as i32, y as i32];
|
||||
|
||||
if new != points_history[2] {
|
||||
points_history = [points_history[1], points_history[2], new];
|
||||
|
||||
let dx = points_history[2][0] - points_history[1][0];
|
||||
let dy = points_history[2][1] - points_history[1][1];
|
||||
// fill diagonals
|
||||
if dx != 0 && dy != 0 {
|
||||
let connecting_point = match [dx, dy] {
|
||||
[1, 1] => [points_history[1][0] + 1, points_history[1][1]],
|
||||
[1, -1] => [points_history[1][0], points_history[1][1] - 1],
|
||||
[-1, 1] => [points_history[1][0], points_history[1][1] + 1],
|
||||
[-1, -1] => [points_history[1][0] - 1, points_history[1][1]],
|
||||
_ => todo!(),
|
||||
};
|
||||
draw(
|
||||
buf,
|
||||
[points_history[0], points_history[1], connecting_point],
|
||||
&symbols,
|
||||
pos,
|
||||
color,
|
||||
);
|
||||
points_history = [points_history[1], connecting_point, points_history[2]];
|
||||
}
|
||||
|
||||
draw(buf, points_history, &symbols, pos, color);
|
||||
}
|
||||
}
|
||||
|
||||
points_history = [points_history[1], points_history[2], {
|
||||
// change the x or y value based on which one is changing quicker
|
||||
let ddx = -(starting_angle + arc_angle).sin();
|
||||
let ddy = (starting_angle + arc_angle).cos();
|
||||
if ddx.abs() > ddy.abs() {
|
||||
[
|
||||
points_history[2][0] + ddx.signum() as i32,
|
||||
points_history[2][1],
|
||||
]
|
||||
} else {
|
||||
[
|
||||
points_history[2][0],
|
||||
points_history[2][1] + ddy.signum() as i32,
|
||||
]
|
||||
}
|
||||
}];
|
||||
|
||||
draw(buf, points_history, &symbols, pos, color);
|
||||
}
|
||||
|
||||
if area.area() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
for i in 0..4 {
|
||||
// the radius for the curve between this line and the next
|
||||
let r = match self.tui_modifier.border_types[(i + 1) % 4] {
|
||||
BorderType::HIDDEN => 0.0,
|
||||
BorderType::NONE => 0.0,
|
||||
_ => match self.tui_modifier.border_radi[i] {
|
||||
UnitSystem::Percent(p) => p * area.width as f32 / 100.0,
|
||||
UnitSystem::Point(p) => p,
|
||||
}
|
||||
.abs()
|
||||
.min((area.width as f32 / RADIUS_MULTIPLIER[0]) / 2.0)
|
||||
.min((area.height as f32 / RADIUS_MULTIPLIER[1]) / 2.0),
|
||||
};
|
||||
let radius = [
|
||||
(r * RADIUS_MULTIPLIER[0]) as u16,
|
||||
(r * RADIUS_MULTIPLIER[1]) as u16,
|
||||
];
|
||||
|
||||
// the radius for the curve between this line and the last
|
||||
let last_idx = if i == 0 { 3 } else { i - 1 };
|
||||
let last_r = match self.tui_modifier.border_types[last_idx] {
|
||||
BorderType::HIDDEN => 0.0,
|
||||
BorderType::NONE => 0.0,
|
||||
_ => match self.tui_modifier.border_radi[last_idx] {
|
||||
UnitSystem::Percent(p) => p * area.width as f32 / 100.0,
|
||||
UnitSystem::Point(p) => p,
|
||||
}
|
||||
.abs()
|
||||
.min((area.width as f32 / RADIUS_MULTIPLIER[0]) / 2.0)
|
||||
.min((area.height as f32 / RADIUS_MULTIPLIER[1]) / 2.0),
|
||||
};
|
||||
let last_radius = [
|
||||
(last_r * RADIUS_MULTIPLIER[0]) as u16,
|
||||
(last_r * RADIUS_MULTIPLIER[1]) as u16,
|
||||
];
|
||||
|
||||
let symbols = match self.tui_modifier.border_types[i] {
|
||||
BorderType::DOTTED => NORMAL,
|
||||
BorderType::DASHED => NORMAL,
|
||||
BorderType::SOLID => NORMAL,
|
||||
BorderType::DOUBLE => DOUBLE,
|
||||
BorderType::GROOVE => NORMAL,
|
||||
BorderType::RIDGE => NORMAL,
|
||||
BorderType::INSET => NORMAL,
|
||||
BorderType::OUTSET => NORMAL,
|
||||
BorderType::HIDDEN => continue,
|
||||
BorderType::NONE => continue,
|
||||
};
|
||||
|
||||
let color = self.tui_modifier.border_colors[i].or(self.block_style.fg);
|
||||
|
||||
match i {
|
||||
0 => {
|
||||
for x in (area.left() + last_radius[0] + 1)..(area.right() - radius[0]) {
|
||||
let cell = buf.get_mut(x, area.top());
|
||||
if let Some(c) = color {
|
||||
cell.fg = c;
|
||||
}
|
||||
cell.symbol = symbols.horizontal.to_string();
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
for y in (area.top() + last_radius[1] + 1)..(area.bottom() - radius[1]) {
|
||||
let cell = buf.get_mut(area.right() - 1, y);
|
||||
if let Some(c) = color {
|
||||
cell.fg = c;
|
||||
}
|
||||
cell.symbol = symbols.vertical.to_string();
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
for x in (area.left() + radius[0])..(area.right() - last_radius[0] - 1) {
|
||||
let cell = buf.get_mut(x, area.bottom() - 1);
|
||||
if let Some(c) = color {
|
||||
cell.fg = c;
|
||||
}
|
||||
cell.symbol = symbols.horizontal.to_string();
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
for y in (area.top() + radius[1])..(area.bottom() - last_radius[1] - 1) {
|
||||
let cell = buf.get_mut(area.left(), y);
|
||||
if let Some(c) = color {
|
||||
cell.fg = c;
|
||||
}
|
||||
cell.symbol = symbols.vertical.to_string();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match i {
|
||||
0 => draw_arc(
|
||||
[area.right() - radius[0] - 1, area.top() + radius[1]],
|
||||
std::f32::consts::FRAC_PI_2 * 3.0,
|
||||
std::f32::consts::FRAC_PI_2,
|
||||
r,
|
||||
&symbols,
|
||||
buf,
|
||||
&color,
|
||||
),
|
||||
1 => draw_arc(
|
||||
[area.right() - radius[0] - 1, area.bottom() - radius[1] - 1],
|
||||
0.0,
|
||||
std::f32::consts::FRAC_PI_2,
|
||||
r,
|
||||
&symbols,
|
||||
buf,
|
||||
&color,
|
||||
),
|
||||
2 => draw_arc(
|
||||
[area.left() + radius[0], area.bottom() - radius[1] - 1],
|
||||
std::f32::consts::FRAC_PI_2,
|
||||
std::f32::consts::FRAC_PI_2,
|
||||
r,
|
||||
&symbols,
|
||||
buf,
|
||||
&color,
|
||||
),
|
||||
3 => draw_arc(
|
||||
[area.left() + radius[0], area.top() + radius[1]],
|
||||
std::f32::consts::PI,
|
||||
std::f32::consts::FRAC_PI_2,
|
||||
r,
|
||||
&symbols,
|
||||
buf,
|
||||
&color,
|
||||
),
|
||||
_ => panic!("more than 4 sides?"),
|
||||
}
|
||||
}
|
||||
|
||||
// todo: only render inside borders
|
||||
for x in area.left()..area.right() {
|
||||
for y in area.top()..area.bottom() {
|
||||
let cell = buf.get_mut(x, y);
|
||||
if let Some(c) = self.block_style.bg {
|
||||
cell.bg = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_vnode<'a>(
|
||||
frame: &mut tui::Frame<CrosstermBackend<Stdout>>,
|
||||
@@ -21,11 +330,13 @@ pub fn render_vnode<'a>(
|
||||
layouts: &mut HashMap<ElementId, TuiNode<'a>>,
|
||||
vdom: &'a VirtualDom,
|
||||
node: &'a VNode<'a>,
|
||||
// this holds the parents syle state for styled text rendering and potentially transparentcy
|
||||
style: &TuiStyle,
|
||||
) {
|
||||
match node {
|
||||
VNode::Fragment(f) => {
|
||||
for child in f.children {
|
||||
render_vnode(frame, layout, layouts, vdom, child);
|
||||
render_vnode(frame, layout, layouts, vdom, child, style);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -33,7 +344,7 @@ pub fn render_vnode<'a>(
|
||||
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);
|
||||
render_vnode(frame, layout, layouts, vdom, new_node, style);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,11 +366,12 @@ pub fn render_vnode<'a>(
|
||||
#[derive(Default)]
|
||||
struct Label<'a> {
|
||||
text: &'a str,
|
||||
style: TuiStyle,
|
||||
}
|
||||
|
||||
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());
|
||||
buf.set_string(area.left(), area.top(), self.text, self.style);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +379,10 @@ pub fn render_vnode<'a>(
|
||||
|
||||
// Block::default().
|
||||
|
||||
let label = Label { text: t.text };
|
||||
let label = Label {
|
||||
text: t.text,
|
||||
style: *style,
|
||||
};
|
||||
let area = Rect::new(*x as u16, *y as u16, *width as u16, *height as u16);
|
||||
|
||||
// the renderer will panic if a node is rendered out of range even if the size is zero
|
||||
@@ -76,16 +391,17 @@ pub fn render_vnode<'a>(
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
let new_style = style.patch(node.block_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(block, area);
|
||||
frame.render_widget(node, area);
|
||||
}
|
||||
|
||||
for el in el.children {
|
||||
render_vnode(frame, layout, layouts, vdom, el);
|
||||
render_vnode(frame, layout, layouts, vdom, el, &new_style);
|
||||
}
|
||||
}
|
||||
VNode::Fragment(_) => todo!(),
|
||||
|
||||
Reference in new issue
Block a user