Compare commits
3 Commits
8fb89ad70b
...
bfe3db415c
Author | SHA1 | Date |
---|---|---|
mars | bfe3db415c | |
mars | 3d214a8230 | |
mars | 8f92067869 |
42
src/dom.rs
42
src/dom.rs
|
@ -4,6 +4,7 @@ use std::str::FromStr;
|
||||||
use crate::args::ParseArgs;
|
use crate::args::ParseArgs;
|
||||||
use crate::ast::{self, ParseErrorKind, TagBody};
|
use crate::ast::{self, ParseErrorKind, TagBody};
|
||||||
use crate::block::{AllowedChildren, Block, BlockTagKind};
|
use crate::block::{AllowedChildren, Block, BlockTagKind};
|
||||||
|
use crate::layout::{BoxChild, BoxLayout, RenderedBlock};
|
||||||
use crate::node::{NodeId, NodeStore};
|
use crate::node::{NodeId, NodeStore};
|
||||||
use crate::source::WithSource;
|
use crate::source::WithSource;
|
||||||
use crate::text::{TaggedText, TextBlock, TextLayout, TextParseErrorKind, TextStyle, TextTagKind};
|
use crate::text::{TaggedText, TextBlock, TextLayout, TextParseErrorKind, TextStyle, TextTagKind};
|
||||||
|
@ -53,12 +54,17 @@ impl Dom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, width: usize) {
|
pub fn render(&mut self, width: usize) {
|
||||||
self.render_from(self.root, width);
|
self.render_from(self.root, width);
|
||||||
|
let rendered: &RenderedBlock = self.nodes.get(self.root).unwrap();
|
||||||
|
|
||||||
|
for line in rendered.lines.iter() {
|
||||||
|
println!("{}", line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_from(&self, node: NodeId, width: usize) {
|
pub fn render_from(&mut self, node: NodeId, width: usize) {
|
||||||
if let NodeKind::Text = self.nodes.get(node).unwrap() {
|
let rendered = if let NodeKind::Text = self.nodes.get(node).unwrap() {
|
||||||
let style = TextStyle::default();
|
let style = TextStyle::default();
|
||||||
|
|
||||||
let layout: TextLayout = if let Some(layout) = self.nodes.get::<TextLayout>(node) {
|
let layout: TextLayout = if let Some(layout) = self.nodes.get::<TextLayout>(node) {
|
||||||
|
@ -69,16 +75,32 @@ impl Dom {
|
||||||
panic!("Couldn't find text info on node {}", node);
|
panic!("Couldn't find text info on node {}", node);
|
||||||
};
|
};
|
||||||
|
|
||||||
for line in layout.render(&style).lines.into_iter() {
|
let rendered = layout.render(&style);
|
||||||
println!("{}", line);
|
let block: &Block = self.nodes.get(node).unwrap();
|
||||||
|
rendered.add_padding(&block.padding)
|
||||||
|
} else {
|
||||||
|
let children = self.nodes.get::<NodeChildren>(node).unwrap().0.clone();
|
||||||
|
for child in children.iter() {
|
||||||
|
self.render_from(*child, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("");
|
let layout = BoxLayout {
|
||||||
}
|
children: children
|
||||||
|
.iter()
|
||||||
|
.map(|child| self.get_box_child(*child))
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
for child in self.nodes.get::<NodeChildren>(node).unwrap().0.iter() {
|
layout.render(width)
|
||||||
self.render_from(*child, width);
|
};
|
||||||
}
|
|
||||||
|
self.nodes.insert(node, rendered);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_box_child(&self, node: NodeId) -> BoxChild {
|
||||||
|
let block: &Block = self.nodes.get(node).unwrap();
|
||||||
|
let rendered: &RenderedBlock = self.nodes.get(node).unwrap();
|
||||||
|
BoxChild { rendered, block }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
137
src/layout.rs
137
src/layout.rs
|
@ -1,5 +1,8 @@
|
||||||
|
use strum::EnumString;
|
||||||
|
|
||||||
use crate::args::*;
|
use crate::args::*;
|
||||||
use crate::ast::{ParseErrorKind, ParseResult, Value, ValueKind};
|
use crate::ast::{ParseErrorKind, ParseResult, Value, ValueKind};
|
||||||
|
use crate::block::Block;
|
||||||
use crate::source::WithSource;
|
use crate::source::WithSource;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -8,6 +11,129 @@ pub struct RenderedBlock {
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RenderedBlock {
|
||||||
|
pub fn add_padding(&self, padding: &Rect<usize>) -> Self {
|
||||||
|
let make_spaces = |num: usize| -> String { std::iter::repeat(" ").take(num).collect() };
|
||||||
|
let new_width = self.width + padding.l + padding.r;
|
||||||
|
let left_pad = make_spaces(padding.l);
|
||||||
|
let right_pad = make_spaces(padding.r);
|
||||||
|
let vertical_pad = make_spaces(new_width);
|
||||||
|
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
|
||||||
|
lines.extend(std::iter::repeat(vertical_pad.clone()).take(padding.t));
|
||||||
|
|
||||||
|
lines.extend(
|
||||||
|
self.lines
|
||||||
|
.iter()
|
||||||
|
.map(|line| format!("{}{}{}", left_pad, line, right_pad)),
|
||||||
|
);
|
||||||
|
|
||||||
|
lines.extend(std::iter::repeat(vertical_pad.clone()).take(padding.b));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
lines,
|
||||||
|
width: new_width,
|
||||||
|
height: self.height + padding.t + padding.b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, EnumString)]
|
||||||
|
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
|
||||||
|
pub enum Direction {
|
||||||
|
Left,
|
||||||
|
Up,
|
||||||
|
Right,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BoxChild<'a> {
|
||||||
|
pub block: &'a Block,
|
||||||
|
pub rendered: &'a RenderedBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BoxLayout<'a> {
|
||||||
|
pub children: Vec<BoxChild<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> BoxLayout<'a> {
|
||||||
|
pub fn render(&self, width: usize) -> RenderedBlock {
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
let mut last_margin = 0;
|
||||||
|
|
||||||
|
let make_padding = |num: usize| -> String { std::iter::repeat(" ").take(num).collect() };
|
||||||
|
|
||||||
|
let blank_line = make_padding(width);
|
||||||
|
|
||||||
|
let make_blank_lines = |num: usize| -> Vec<String> {
|
||||||
|
std::iter::repeat(blank_line.clone()).take(num).collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
for child in self.children.iter() {
|
||||||
|
let unwrap_margin = |margin: &Margin| -> usize {
|
||||||
|
match margin {
|
||||||
|
Margin::Auto => unimplemented!("length-wise auto margins"),
|
||||||
|
Margin::Fixed(m) => *m,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let start = unwrap_margin(&child.block.margin.t);
|
||||||
|
let start = start.max(last_margin);
|
||||||
|
|
||||||
|
lines.extend(make_blank_lines(start).into_iter());
|
||||||
|
|
||||||
|
let left = &child.block.margin.l;
|
||||||
|
let right = &child.block.margin.r;
|
||||||
|
let available = width - child.rendered.width.min(width);
|
||||||
|
let (left, right) = Self::calc_margins(left, right, available);
|
||||||
|
let left = make_padding(left);
|
||||||
|
let right = make_padding(right);
|
||||||
|
|
||||||
|
for line in child.rendered.lines.iter() {
|
||||||
|
let line = format!("{}{}{}", left, line, right);
|
||||||
|
lines.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_margin = unwrap_margin(&child.block.margin.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.extend(make_blank_lines(last_margin).into_iter());
|
||||||
|
|
||||||
|
RenderedBlock {
|
||||||
|
height: lines.len(),
|
||||||
|
width,
|
||||||
|
lines,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calc_margins(start: &Margin, end: &Margin, available: usize) -> (usize, usize) {
|
||||||
|
let split_available = |available: usize| -> (usize, usize) {
|
||||||
|
if available & 2 == 0 {
|
||||||
|
(available >> 1, available >> 1)
|
||||||
|
} else {
|
||||||
|
(available >> 1, (available >> 1) | 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
use Margin::*;
|
||||||
|
match (start, end) {
|
||||||
|
(Auto, Auto) => split_available(available),
|
||||||
|
(Fixed(start), Auto) => (*start, available - start),
|
||||||
|
(Auto, Fixed(end)) => (available - end, *end),
|
||||||
|
(Fixed(fixed_start), Fixed(fixed_end)) => {
|
||||||
|
let taken = fixed_start + fixed_end;
|
||||||
|
let available = available - taken.min(available);
|
||||||
|
let (start, end) = split_available(available);
|
||||||
|
(fixed_start + start, fixed_end + end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum AxisSize {
|
pub enum AxisSize {
|
||||||
Fixed(usize),
|
Fixed(usize),
|
||||||
|
@ -73,6 +199,17 @@ pub struct Rect<T> {
|
||||||
pub b: T,
|
pub b: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Rect<T> {
|
||||||
|
pub fn map<O>(&self, f: impl Fn(&T) -> O) -> Rect<O> {
|
||||||
|
Rect::<O> {
|
||||||
|
l: f(&self.l),
|
||||||
|
t: f(&self.t),
|
||||||
|
r: f(&self.r),
|
||||||
|
b: f(&self.b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Clone + ParseValue> Rect<T> {
|
impl<T: Clone + ParseValue> Rect<T> {
|
||||||
pub fn parse_shorthand(parsed: WithSource<&[T]>) -> ParseResult<Self> {
|
pub fn parse_shorthand(parsed: WithSource<&[T]>) -> ParseResult<Self> {
|
||||||
match parsed.len() {
|
match parsed.len() {
|
||||||
|
|
|
@ -21,7 +21,7 @@ fn main() {
|
||||||
|
|
||||||
println!("{:#?}", body);
|
println!("{:#?}", body);
|
||||||
|
|
||||||
let dom = match dom::Dom::parse(&body) {
|
let mut dom = match dom::Dom::parse(&body) {
|
||||||
Ok(dom) => dom,
|
Ok(dom) => dom,
|
||||||
Err(e) => panic!("DOM parse error:\n{}", e),
|
Err(e) => panic!("DOM parse error:\n{}", e),
|
||||||
};
|
};
|
||||||
|
|
21
src/text.rs
21
src/text.rs
|
@ -293,6 +293,7 @@ impl TextBlock {
|
||||||
strings: vec![StyledString::from_plain(&line)],
|
strings: vec![StyledString::from_plain(&line)],
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
width,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,28 +301,36 @@ impl TextBlock {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TextLayout {
|
pub struct TextLayout {
|
||||||
pub lines: Vec<TaggedText>,
|
pub lines: Vec<TaggedText>,
|
||||||
|
pub width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextLayout {
|
impl TextLayout {
|
||||||
pub fn from_plain(plain: &str) -> Self {
|
pub fn from_plain(plain: &str) -> Self {
|
||||||
Self {
|
let mut lines = Vec::new();
|
||||||
lines: plain.lines().map(|s| TaggedText::from_plain(s)).collect(),
|
let mut width = 0;
|
||||||
|
|
||||||
|
for line in plain.lines() {
|
||||||
|
width = width.max(textwrap::core::display_width(&line));
|
||||||
|
lines.push(TaggedText::from_plain(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self { lines, width }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, base: &TextStyle) -> RenderedBlock {
|
pub fn render(&self, base: &TextStyle) -> RenderedBlock {
|
||||||
let mut lines = Vec::new();
|
let mut lines = Vec::new();
|
||||||
let mut width = 0;
|
|
||||||
|
|
||||||
for line in self.lines.iter() {
|
for line in self.lines.iter() {
|
||||||
let line = line.render(base);
|
let mut line = line.render(base);
|
||||||
width = width.max(textwrap::core::display_width(&line));
|
let used_width = textwrap::core::display_width(&line);
|
||||||
|
let needed_width = self.width - used_width.min(self.width);
|
||||||
|
line.extend(std::iter::repeat(' ').take(needed_width));
|
||||||
lines.push(line);
|
lines.push(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderedBlock {
|
RenderedBlock {
|
||||||
height: lines.len(),
|
height: lines.len(),
|
||||||
width,
|
width: self.width,
|
||||||
lines,
|
lines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue