Compare commits

...

3 Commits

Author SHA1 Message Date
mars bfe3db415c Fix text block line width inconsistencies 2022-10-23 00:26:12 -06:00
mars 3d214a8230 Rudimentary layout 2022-10-23 00:10:55 -06:00
mars 8f92067869 Render text block padding 2022-10-22 22:57:25 -06:00
4 changed files with 185 additions and 17 deletions

View File

@ -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 }
}
}

View File

@ -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() {

View File

@ -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),
};

View File

@ -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,
}
}