Merge rect and box 🦚

This commit is contained in:
Martin Haug 2021-02-06 12:30:44 +01:00
parent 8469bad748
commit 80e076814d
11 changed files with 100 additions and 168 deletions

View File

@ -156,19 +156,19 @@ impl<'a> PdfExporter<'a> {
c.b as f32 / 255., c.b as f32 / 255.,
); );
} }
Fill::Image(_) => unimplemented!(), Fill::Image(_) => todo!(),
} }
let x = pos.x.to_pt() as f32;
match &geometry.shape { match &geometry.shape {
Shape::Rect(r) => { Shape::Rect(r) => {
content.rect( let w = r.width.to_pt() as f32;
pos.x.to_pt() as f32, let h = r.height.to_pt() as f32;
(page.size.height - pos.y - r.size.height).to_pt() as f32, let y = (page.size.height - pos.y - r.height).to_pt() as f32;
r.size.width.to_pt() as f32, if w > 0.0 && h > 0.0 {
r.size.height.to_pt() as f32, content.rect(x, y, w, h, false, true);
false, }
true,
);
} }
} }

37
src/layout/background.rs Normal file
View File

@ -0,0 +1,37 @@
use super::*;
/// A node that represents a rectangular box.
#[derive(Debug, Clone, PartialEq)]
pub struct NodeBackground {
/// The background fill.
pub fill: Fill,
/// The child node whose sides to pad.
pub child: NodeFixed,
}
impl Layout for NodeBackground {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
let mut layouted = self.child.layout(ctx, areas);
if let Some(first) = layouted.frames_mut().first_mut() {
first.elements.insert(
0,
(
Point::ZERO,
Element::Geometry(Geometry {
shape: Shape::Rect(first.size),
fill: self.fill.clone(),
}),
),
)
}
layouted
}
}
impl From<NodeBackground> for NodeAny {
fn from(background: NodeBackground) -> Self {
Self::new(background)
}
}

View File

@ -4,7 +4,7 @@ mod fixed;
mod node; mod node;
mod pad; mod pad;
mod par; mod par;
mod rect; mod background;
mod spacing; mod spacing;
mod stack; mod stack;
mod text; mod text;
@ -18,7 +18,7 @@ pub use fixed::*;
pub use node::*; pub use node::*;
pub use pad::*; pub use pad::*;
pub use par::*; pub use par::*;
pub use rect::*; pub use background::*;
pub use spacing::*; pub use spacing::*;
pub use stack::*; pub use stack::*;
pub use text::*; pub use text::*;
@ -236,7 +236,6 @@ pub enum Fill {
Color(Color), Color(Color),
/// The fill is an image. /// The fill is an image.
Image(Image), Image(Image),
// Gradient(Gradient),
} }
/// A shape with some kind of fill. /// A shape with some kind of fill.
@ -245,10 +244,10 @@ pub struct Geometry {
/// The shape to draw. /// The shape to draw.
pub shape: Shape, pub shape: Shape,
/// How the shape looks on the inside. /// How the shape looks on the inside.
/// //
/// **TODO:** This could be made into a Vec<Fill> or something such that // TODO: This could be made into a Vec<Fill> or something such that
/// the user can compose multiple fills with alpha values less // the user can compose multiple fills with alpha values less
/// than one to achieve cool effects. // than one to achieve cool effects.
pub fill: Fill, pub fill: Fill,
} }
@ -256,15 +255,7 @@ pub struct Geometry {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Shape { pub enum Shape {
/// A rectangle. /// A rectangle.
Rect(Rect), Rect(Size),
// Ellipse(Ellipse),
}
/// An rectangle.
#[derive(Debug, Clone, PartialEq)]
pub struct Rect {
/// The dimensions of the rectangle.
pub size: Size,
} }
/// An image element. /// An image element.

View File

@ -1,71 +0,0 @@
use std::cmp::Ordering;
use super::*;
use crate::{color::RgbaColor, geom::Linear};
/// A node that represents a rectangular box.
#[derive(Debug, Clone, PartialEq)]
pub struct NodeRect {
/// The fixed width, if any.
pub width: Option<Linear>,
/// The fixed height, if any.
pub height: Option<Linear>,
/// The background color.
pub color: Option<Color>,
/// The child node whose sides to pad.
pub child: Node,
}
impl Layout for NodeRect {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
let Areas { current, full, .. } = areas;
let height_opt = self.height.map(|h| h.resolve(full.height));
let mut size = Size::new(
self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width),
height_opt.unwrap_or(current.height),
);
let areas = Areas::once(size);
let mut layouted = self.child.layout(ctx, &areas);
// If the children have some height, apply that,
// otherwise fall back to zero or the height property.
if let Some(max) = layouted
.frames()
.iter()
.map(|f| f.size.height)
.max_by(|x, y| x.partial_cmp(y).unwrap_or(Ordering::Equal))
{
size.height = max;
} else {
size.height = height_opt.unwrap_or(Length::ZERO)
}
if let Some(first) = layouted.frames_mut().first_mut() {
first.elements.insert(
0,
(
Point::ZERO,
Element::Geometry(Geometry {
shape: Shape::Rect(Rect { size }),
fill: Fill::Color(self.color.unwrap_or(Color::Rgba(RgbaColor {
r: 255,
g: 255,
b: 255,
a: 0,
}))),
}),
),
)
}
layouted
}
}
impl From<NodeRect> for NodeAny {
fn from(pad: NodeRect) -> Self {
Self::new(pad)
}
}

View File

@ -4,43 +4,6 @@ use crate::env::{ImageResource, ResourceId};
use crate::layout::*; use crate::layout::*;
use crate::prelude::*; use crate::prelude::*;
/// `rect`: Layout content into a rectangle that also might have a fill.
///
/// # Named arguments
/// - Width of the box: `width`, of type `linear` relative to parent width.
/// - Height of the box: `height`, of type `linear` relative to parent height.
pub fn rect(ctx: &mut EvalContext, args: &mut Args) -> Value {
let snapshot = ctx.state.clone();
let width = args.get(ctx, "width");
let height = args.get(ctx, "height");
let color = args.get(ctx, "color");
let dirs = ctx.state.dirs;
let align = ctx.state.align;
ctx.start_content_group();
if let Some(body) = args.find::<ValueTemplate>(ctx) {
body.eval(ctx);
}
let children = ctx.end_content_group();
let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
ctx.push(NodeRect {
width,
height,
color,
child: NodeStack { dirs, align, expand, children }.into(),
});
ctx.state = snapshot;
Value::None
}
/// `image`: Insert an image. /// `image`: Insert an image.
/// ///
/// Supports PNG and JPEG files. /// Supports PNG and JPEG files.

View File

@ -1,7 +1,7 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use crate::eval::Softness; use crate::{eval::Softness, layout::NodeBackground};
use crate::layout::{Expansion, NodeFixed, NodeSpacing, NodeStack}; use crate::layout::{Expansion, Fill, NodeFixed, NodeSpacing, NodeStack};
use crate::paper::{Paper, PaperClass}; use crate::paper::{Paper, PaperClass};
use crate::prelude::*; use crate::prelude::*;
@ -175,6 +175,7 @@ impl Display for Alignment {
/// # Named arguments /// # Named arguments
/// - Width of the box: `width`, of type `linear` relative to parent width. /// - Width of the box: `width`, of type `linear` relative to parent width.
/// - Height of the box: `height`, of type `linear` relative to parent height. /// - Height of the box: `height`, of type `linear` relative to parent height.
/// - Background color of the box: `color`, of type `color`.
pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value { pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();
@ -182,6 +183,7 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
let height = args.get(ctx, "height"); let height = args.get(ctx, "height");
let main = args.get(ctx, "main-dir"); let main = args.get(ctx, "main-dir");
let cross = args.get(ctx, "cross-dir"); let cross = args.get(ctx, "cross-dir");
let color = args.get(ctx, "color");
ctx.set_dirs(Gen::new(main, cross)); ctx.set_dirs(Gen::new(main, cross));
@ -199,11 +201,21 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value {
let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit }; let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some())); let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
ctx.push(NodeFixed { let fixed_node = NodeFixed {
width, width,
height, height,
child: NodeStack { dirs, align, expand, children }.into(), child: NodeStack { dirs, align, expand, children }.into(),
}); };
if let Some(color) = color {
ctx.push(NodeBackground {
fill: Fill::Color(color),
child: fixed_node,
})
} else {
ctx.push(fixed_node);
}
ctx.state = snapshot; ctx.state = snapshot;
Value::None Value::None

View File

@ -38,7 +38,6 @@ pub fn new() -> Scope {
set!(func: "image", image); set!(func: "image", image);
set!(func: "page", page); set!(func: "page", page);
set!(func: "pagebreak", pagebreak); set!(func: "pagebreak", pagebreak);
set!(func: "rect", rect);
set!(func: "rgb", rgb); set!(func: "rgb", rgb);
set!(func: "type", type_); set!(func: "type", type_);
set!(func: "v", v); set!(func: "v", v);

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

23
tests/library/typ/box.typ Normal file
View File

@ -0,0 +1,23 @@
#[page "a5", flip: true]
// Rectangle with width, should have paragraph height
#[box width: 2cm, color: #9650D6][aa]
Sometimes there is no box
// Rectangle with height, should span line
#[box height: 2cm, width: 100%, color: #734CED][bb]
// Empty rectangle with width and height
#[box width: 6cm, height: 12pt, color: #CB4CED]
// This empty rectangle should not be displayed
#[box width: 2in, color: #ff0000]
// This one should be
#[box height: 15mm, width: 100%, color: #494DE3]
// These are in a row!
#[box width: 2in, height: 10pt, color: #D6CD67]
#[box width: 2in, height: 10pt, color: #EDD466]
#[box width: 2in, height: 10pt, color: #E3BE62]

View File

@ -1,23 +0,0 @@
#[page "a5", flip: true]
// Rectangle with width, should have paragraph height
#[rect width: 2cm, color: #9650D6][aa]
Sometimes there is no box
// Rectangle with height, should span line
#[rect height: 2cm, color: #734CED][bb]
// Empty rectangle with width and height
#[rect width: 6cm, height: 12pt, color: #CB4CED]
// This empty rectangle should not be displayed
#[rect width: 2in, color: #ff0000]
// This one should be
#[rect height: 15mm, color: #494DE3]
// These are in a row!
#[rect width: 2in, height: 10pt, color: #D6CD67]
#[rect width: 2in, height: 10pt, color: #EDD466]
#[rect width: 2in, height: 10pt, color: #E3BE62]

View File

@ -451,10 +451,6 @@ fn draw_geometry(canvas: &mut Canvas, pos: Point, _: &Env, element: &Geometry) {
let x = pos.x.to_pt() as f32; let x = pos.x.to_pt() as f32;
let y = pos.y.to_pt() as f32; let y = pos.y.to_pt() as f32;
let (w, h) = match &element.shape {
Shape::Rect(s) => (s.size.width.to_pt() as f32, s.size.height.to_pt() as f32),
};
let mut paint = Paint::default(); let mut paint = Paint::default();
match &element.fill { match &element.fill {
@ -464,9 +460,14 @@ fn draw_geometry(canvas: &mut Canvas, pos: Point, _: &Env, element: &Geometry) {
Fill::Image(_) => todo!(), Fill::Image(_) => todo!(),
}; };
if let Some(rect) = Rect::from_xywh(x, y, w, h) { match &element.shape {
canvas.fill_rect(rect, &paint); Shape::Rect(s) => {
} let (w, h) = (s.width.to_pt() as f32, s.height.to_pt() as f32);
canvas.fill_rect(Rect::from_xywh(x, y, w, h).unwrap(), &paint);
},
};
} }
fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &Image) { fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &Image) {