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::ast::{self, ParseErrorKind, TagBody};
|
||||
use crate::block::{AllowedChildren, Block, BlockTagKind};
|
||||
use crate::layout::{BoxChild, BoxLayout, RenderedBlock};
|
||||
use crate::node::{NodeId, NodeStore};
|
||||
use crate::source::WithSource;
|
||||
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);
|
||||
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) {
|
||||
if let NodeKind::Text = self.nodes.get(node).unwrap() {
|
||||
pub fn render_from(&mut self, node: NodeId, width: usize) {
|
||||
let rendered = if let NodeKind::Text = self.nodes.get(node).unwrap() {
|
||||
let style = TextStyle::default();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
for line in layout.render(&style).lines.into_iter() {
|
||||
println!("{}", line);
|
||||
let rendered = layout.render(&style);
|
||||
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() {
|
||||
self.render_from(*child, width);
|
||||
}
|
||||
layout.render(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::ast::{ParseErrorKind, ParseResult, Value, ValueKind};
|
||||
use crate::block::Block;
|
||||
use crate::source::WithSource;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -8,6 +11,129 @@ pub struct RenderedBlock {
|
|||
pub width: 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)]
|
||||
pub enum AxisSize {
|
||||
Fixed(usize),
|
||||
|
@ -73,6 +199,17 @@ pub struct Rect<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> {
|
||||
pub fn parse_shorthand(parsed: WithSource<&[T]>) -> ParseResult<Self> {
|
||||
match parsed.len() {
|
||||
|
|
|
@ -21,7 +21,7 @@ fn main() {
|
|||
|
||||
println!("{:#?}", body);
|
||||
|
||||
let dom = match dom::Dom::parse(&body) {
|
||||
let mut dom = match dom::Dom::parse(&body) {
|
||||
Ok(dom) => dom,
|
||||
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)],
|
||||
})
|
||||
.collect(),
|
||||
width,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -300,28 +301,36 @@ impl TextBlock {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct TextLayout {
|
||||
pub lines: Vec<TaggedText>,
|
||||
pub width: usize,
|
||||
}
|
||||
|
||||
impl TextLayout {
|
||||
pub fn from_plain(plain: &str) -> Self {
|
||||
Self {
|
||||
lines: plain.lines().map(|s| TaggedText::from_plain(s)).collect(),
|
||||
let mut lines = Vec::new();
|
||||
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 {
|
||||
let mut lines = Vec::new();
|
||||
let mut width = 0;
|
||||
|
||||
for line in self.lines.iter() {
|
||||
let line = line.render(base);
|
||||
width = width.max(textwrap::core::display_width(&line));
|
||||
let mut line = line.render(base);
|
||||
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);
|
||||
}
|
||||
|
||||
RenderedBlock {
|
||||
height: lines.len(),
|
||||
width,
|
||||
width: self.width,
|
||||
lines,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue