Tidy up
This commit is contained in:
parent
29cfef0a6d
commit
bce553a991
@ -18,7 +18,6 @@ use std::rc::Rc;
|
||||
use crate::cache::Cache;
|
||||
use crate::color::Color;
|
||||
use crate::diag::{Diag, DiagSet, Pass};
|
||||
use crate::geom::TrackSizing;
|
||||
use crate::geom::{Angle, Fractional, Length, Relative};
|
||||
use crate::loading::{FileHash, Loader};
|
||||
use crate::parse::parse;
|
||||
@ -245,6 +244,7 @@ impl Eval for Expr {
|
||||
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
|
||||
match *self {
|
||||
Self::None(_) => Value::None,
|
||||
Self::Auto(_) => Value::Auto,
|
||||
Self::Bool(_, v) => Value::Bool(v),
|
||||
Self::Int(_, v) => Value::Int(v),
|
||||
Self::Float(_, v) => Value::Float(v),
|
||||
@ -252,7 +252,6 @@ impl Eval for Expr {
|
||||
Self::Angle(_, v, unit) => Value::Angle(Angle::with_unit(v, unit)),
|
||||
Self::Percent(_, v) => Value::Relative(Relative::new(v / 100.0)),
|
||||
Self::Fractional(_, v) => Value::Fractional(Fractional::new(v)),
|
||||
Self::Auto(_) => Value::TrackSizing(TrackSizing::Auto),
|
||||
Self::Color(_, v) => Value::Color(Color::Rgba(v)),
|
||||
Self::Str(_, ref v) => Value::Str(v.clone()),
|
||||
Self::Ident(ref v) => match ctx.scopes.get(&v) {
|
||||
|
@ -11,8 +11,8 @@ pub fn pos(value: Value) -> Value {
|
||||
Length(v) => Length(v),
|
||||
Angle(v) => Angle(v),
|
||||
Relative(v) => Relative(v),
|
||||
Fractional(v) => Fractional(v),
|
||||
Linear(v) => Linear(v),
|
||||
Fractional(v) => Fractional(v),
|
||||
_ => Error,
|
||||
}
|
||||
}
|
||||
@ -25,8 +25,8 @@ pub fn neg(value: Value) -> Value {
|
||||
Length(v) => Length(-v),
|
||||
Angle(v) => Angle(-v),
|
||||
Relative(v) => Relative(-v),
|
||||
Fractional(v) => Fractional(-v),
|
||||
Linear(v) => Linear(-v),
|
||||
Fractional(v) => Fractional(-v),
|
||||
_ => Error,
|
||||
}
|
||||
}
|
||||
@ -34,29 +34,31 @@ pub fn neg(value: Value) -> Value {
|
||||
/// Compute the sum of two values.
|
||||
pub fn add(lhs: Value, rhs: Value) -> Value {
|
||||
match (lhs, rhs) {
|
||||
// Math.
|
||||
(Int(a), Int(b)) => Int(a + b),
|
||||
(Int(a), Float(b)) => Float(a as f64 + b),
|
||||
(Float(a), Int(b)) => Float(a + b as f64),
|
||||
(Float(a), Float(b)) => Float(a + b),
|
||||
|
||||
(Angle(a), Angle(b)) => Angle(a + b),
|
||||
|
||||
(Length(a), Length(b)) => Length(a + b),
|
||||
(Length(a), Relative(b)) => Linear(a + b),
|
||||
(Length(a), Linear(b)) => Linear(a + b),
|
||||
|
||||
(Relative(a), Length(b)) => Linear(a + b),
|
||||
(Relative(a), Relative(b)) => Relative(a + b),
|
||||
(Relative(a), Linear(b)) => Linear(a + b),
|
||||
(Fractional(a), Fractional(b)) => Fractional(a + b),
|
||||
|
||||
(Linear(a), Length(b)) => Linear(a + b),
|
||||
(Linear(a), Relative(b)) => Linear(a + b),
|
||||
(Linear(a), Linear(b)) => Linear(a + b),
|
||||
|
||||
// Collections.
|
||||
(Fractional(a), Fractional(b)) => Fractional(a + b),
|
||||
|
||||
(Str(a), Str(b)) => Str(a + &b),
|
||||
(Array(a), Array(b)) => Array(concat(a, b)),
|
||||
(Dict(a), Dict(b)) => Dict(concat(a, b)),
|
||||
|
||||
// Templates.
|
||||
(Template(a), Template(b)) => Template(concat(a, b)),
|
||||
(Template(a), None) => Template(a),
|
||||
(None, Template(b)) => Template(b),
|
||||
@ -80,17 +82,23 @@ pub fn sub(lhs: Value, rhs: Value) -> Value {
|
||||
(Int(a), Float(b)) => Float(a as f64 - b),
|
||||
(Float(a), Int(b)) => Float(a - b as f64),
|
||||
(Float(a), Float(b)) => Float(a - b),
|
||||
|
||||
(Angle(a), Angle(b)) => Angle(a - b),
|
||||
|
||||
(Length(a), Length(b)) => Length(a - b),
|
||||
(Length(a), Relative(b)) => Linear(a - b),
|
||||
(Length(a), Linear(b)) => Linear(a - b),
|
||||
|
||||
(Relative(a), Length(b)) => Linear(a - b),
|
||||
(Relative(a), Relative(b)) => Relative(a - b),
|
||||
(Relative(a), Linear(b)) => Linear(a - b),
|
||||
(Fractional(a), Fractional(b)) => Fractional(a - b),
|
||||
|
||||
(Linear(a), Length(b)) => Linear(a - b),
|
||||
(Linear(a), Relative(b)) => Linear(a - b),
|
||||
(Linear(a), Linear(b)) => Linear(a - b),
|
||||
|
||||
(Fractional(a), Fractional(b)) => Fractional(a - b),
|
||||
|
||||
_ => Error,
|
||||
}
|
||||
}
|
||||
@ -102,27 +110,37 @@ pub fn mul(lhs: Value, rhs: Value) -> Value {
|
||||
(Int(a), Float(b)) => Float(a as f64 * b),
|
||||
(Float(a), Int(b)) => Float(a * b as f64),
|
||||
(Float(a), Float(b)) => Float(a * b),
|
||||
|
||||
(Length(a), Int(b)) => Length(a * b as f64),
|
||||
(Length(a), Float(b)) => Length(a * b),
|
||||
(Int(a), Length(b)) => Length(a as f64 * b),
|
||||
(Float(a), Length(b)) => Length(a * b),
|
||||
|
||||
(Angle(a), Int(b)) => Angle(a * b as f64),
|
||||
(Angle(a), Float(b)) => Angle(a * b),
|
||||
(Int(a), Angle(b)) => Angle(a as f64 * b),
|
||||
(Float(a), Angle(b)) => Angle(a * b),
|
||||
|
||||
(Relative(a), Int(b)) => Relative(a * b as f64),
|
||||
(Relative(a), Float(b)) => Relative(a * b),
|
||||
(Fractional(a), Fractional(b)) => Fractional(a * b.get()),
|
||||
(Fractional(a), Int(b)) => Fractional(a * b as f64),
|
||||
(Fractional(a), Float(b)) => Fractional(a * b),
|
||||
(Int(a), Relative(b)) => Relative(a as f64 * b),
|
||||
(Int(a), Fractional(b)) => Fractional(a as f64 * b),
|
||||
(Float(a), Relative(b)) => Relative(a * b),
|
||||
(Float(a), Fractional(b)) => Fractional(a * b),
|
||||
(Int(a), Relative(b)) => Relative(a as f64 * b),
|
||||
|
||||
(Linear(a), Int(b)) => Linear(a * b as f64),
|
||||
(Linear(a), Float(b)) => Linear(a * b),
|
||||
(Int(a), Linear(b)) => Linear(a as f64 * b),
|
||||
(Float(a), Linear(b)) => Linear(a * b),
|
||||
|
||||
(Float(a), Fractional(b)) => Fractional(a * b),
|
||||
(Fractional(a), Int(b)) => Fractional(a * b as f64),
|
||||
(Fractional(a), Float(b)) => Fractional(a * b),
|
||||
(Int(a), Fractional(b)) => Fractional(a as f64 * b),
|
||||
|
||||
(Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)),
|
||||
(Int(a), Str(b)) => Str(b.repeat(a.max(0) as usize)),
|
||||
(Array(a), Int(b)) => Array(repeat(a, b.max(0) as usize)),
|
||||
(Int(a), Array(b)) => Array(repeat(b, a.max(0) as usize)),
|
||||
|
||||
_ => Error,
|
||||
}
|
||||
}
|
||||
@ -134,20 +152,26 @@ pub fn div(lhs: Value, rhs: Value) -> Value {
|
||||
(Int(a), Float(b)) => Float(a as f64 / b),
|
||||
(Float(a), Int(b)) => Float(a / b as f64),
|
||||
(Float(a), Float(b)) => Float(a / b),
|
||||
|
||||
(Length(a), Int(b)) => Length(a / b as f64),
|
||||
(Length(a), Float(b)) => Length(a / b),
|
||||
(Length(a), Length(b)) => Float(a / b),
|
||||
|
||||
(Angle(a), Int(b)) => Angle(a / b as f64),
|
||||
(Angle(a), Float(b)) => Angle(a / b),
|
||||
(Angle(a), Angle(b)) => Float(a / b),
|
||||
|
||||
(Relative(a), Int(b)) => Relative(a / b as f64),
|
||||
(Relative(a), Float(b)) => Relative(a / b),
|
||||
(Relative(a), Relative(b)) => Float(a / b),
|
||||
(Fractional(a), Fractional(b)) => Float(a.get() / b.get()),
|
||||
|
||||
(Fractional(a), Int(b)) => Fractional(a / b as f64),
|
||||
(Fractional(a), Float(b)) => Fractional(a / b),
|
||||
(Fractional(a), Fractional(b)) => Float(a / b),
|
||||
|
||||
(Linear(a), Int(b)) => Linear(a / b as f64),
|
||||
(Linear(a), Float(b)) => Linear(a / b),
|
||||
|
||||
_ => Error,
|
||||
}
|
||||
}
|
||||
@ -209,3 +233,9 @@ where
|
||||
a.extend(b);
|
||||
a
|
||||
}
|
||||
|
||||
/// Repeat a vector `n` times.
|
||||
fn repeat<T: Clone>(vec: Vec<T>, n: usize) -> Vec<T> {
|
||||
let len = n * vec.len();
|
||||
vec.into_iter().cycle().take(len).collect()
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use std::rc::Rc;
|
||||
use super::EvalContext;
|
||||
use crate::color::{Color, RgbaColor};
|
||||
use crate::exec::ExecContext;
|
||||
use crate::geom::{Angle, Fractional, Length, Linear, Relative, TrackSizing};
|
||||
use crate::geom::{Angle, Fractional, Length, Linear, Relative};
|
||||
use crate::syntax::{Expr, Span, Spanned, Tree};
|
||||
|
||||
/// A computational value.
|
||||
@ -16,6 +16,8 @@ use crate::syntax::{Expr, Span, Spanned, Tree};
|
||||
pub enum Value {
|
||||
/// The value that indicates the absence of a meaningful value.
|
||||
None,
|
||||
/// A value that indicates some smart default behaviour.
|
||||
Auto,
|
||||
/// A boolean: `true, false`.
|
||||
Bool(bool),
|
||||
/// An integer: `120`.
|
||||
@ -28,12 +30,10 @@ pub enum Value {
|
||||
Angle(Angle),
|
||||
/// A relative value: `50%`.
|
||||
Relative(Relative),
|
||||
/// A fractional value: `1fr`.
|
||||
Fractional(Fractional),
|
||||
/// A combination of an absolute length and a relative value: `20% + 5cm`.
|
||||
Linear(Linear),
|
||||
/// One of the units that can appear in a grid definition.
|
||||
TrackSizing(TrackSizing),
|
||||
/// A fractional value: `1fr`.
|
||||
Fractional(Fractional),
|
||||
/// A color value: `#f79143ff`.
|
||||
Color(Color),
|
||||
/// A string: `"string"`.
|
||||
@ -73,15 +73,15 @@ impl Value {
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "none",
|
||||
Self::Auto => "auto",
|
||||
Self::Bool(_) => bool::TYPE_NAME,
|
||||
Self::Int(_) => i64::TYPE_NAME,
|
||||
Self::Float(_) => f64::TYPE_NAME,
|
||||
Self::Length(_) => Length::TYPE_NAME,
|
||||
Self::Angle(_) => Angle::TYPE_NAME,
|
||||
Self::Relative(_) => Relative::TYPE_NAME,
|
||||
Self::Fractional(_) => Fractional::TYPE_NAME,
|
||||
Self::Linear(_) => Linear::TYPE_NAME,
|
||||
Self::TrackSizing(_) => TrackSizing::TYPE_NAME,
|
||||
Self::Fractional(_) => Fractional::TYPE_NAME,
|
||||
Self::Color(_) => Color::TYPE_NAME,
|
||||
Self::Str(_) => String::TYPE_NAME,
|
||||
Self::Array(_) => ArrayValue::TYPE_NAME,
|
||||
@ -100,14 +100,6 @@ impl Value {
|
||||
(&Self::Float(a), &Self::Int(b)) => a == b as f64,
|
||||
(&Self::Length(a), &Self::Linear(b)) => a == b.abs && b.rel.is_zero(),
|
||||
(&Self::Relative(a), &Self::Linear(b)) => a == b.rel && b.abs.is_zero(),
|
||||
(&Self::Length(a), &Self::TrackSizing(b)) => TrackSizing::from(a) == b,
|
||||
(&Self::Relative(a), &Self::TrackSizing(b)) => TrackSizing::from(a) == b,
|
||||
(&Self::Linear(a), &Self::TrackSizing(b)) => TrackSizing::from(a) == b,
|
||||
(&Self::Fractional(a), &Self::TrackSizing(b)) => TrackSizing::from(a) == b,
|
||||
(&Self::TrackSizing(a), &Self::Length(b)) => TrackSizing::from(b) == a,
|
||||
(&Self::TrackSizing(a), &Self::Relative(b)) => TrackSizing::from(b) == a,
|
||||
(&Self::TrackSizing(a), &Self::Linear(b)) => TrackSizing::from(b) == a,
|
||||
(&Self::TrackSizing(a), &Self::Fractional(b)) => TrackSizing::from(b) == a,
|
||||
(&Self::Linear(a), &Self::Length(b)) => a.abs == b && a.rel.is_zero(),
|
||||
(&Self::Linear(a), &Self::Relative(b)) => a.rel == b && a.abs.is_zero(),
|
||||
(Self::Array(a), Self::Array(b)) => {
|
||||
@ -615,21 +607,13 @@ primitive! {
|
||||
primitive! { Length: "length", Value::Length }
|
||||
primitive! { Angle: "angle", Value::Angle }
|
||||
primitive! { Relative: "relative", Value::Relative }
|
||||
primitive! { Fractional: "fractional", Value::Fractional }
|
||||
primitive! {
|
||||
Linear: "linear",
|
||||
Value::Linear,
|
||||
Value::Length(v) => v.into(),
|
||||
Value::Relative(v) => v.into(),
|
||||
}
|
||||
primitive! {
|
||||
TrackSizing: "GridUnit",
|
||||
Value::TrackSizing,
|
||||
Value::Length(v) => v.into(),
|
||||
Value::Relative(v) => v.into(),
|
||||
Value::Linear(v) => v.into(),
|
||||
Value::Fractional(v) => v.into(),
|
||||
}
|
||||
primitive! { Fractional: "fractional", Value::Fractional }
|
||||
primitive! { Color: "color", Value::Color }
|
||||
primitive! { String: "string", Value::Str }
|
||||
primitive! { ArrayValue: "array", Value::Array }
|
||||
|
@ -22,6 +22,14 @@ impl<T> Gen<T> {
|
||||
{
|
||||
Self { cross: value.clone(), main: value }
|
||||
}
|
||||
|
||||
/// Convert to the specific representation.
|
||||
pub fn to_spec(self, main: SpecAxis) -> Spec<T> {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Spec::new(self.main, self.cross),
|
||||
SpecAxis::Vertical => Spec::new(self.cross, self.main),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Gen<Length> {
|
||||
@ -32,6 +40,16 @@ impl Gen<Length> {
|
||||
cross: Length::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to a point.
|
||||
pub fn to_point(self, main: SpecAxis) -> Point {
|
||||
self.to_spec(main).to_point()
|
||||
}
|
||||
|
||||
/// Convert to a size.
|
||||
pub fn to_size(self, main: SpecAxis) -> Size {
|
||||
self.to_spec(main).to_size()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Get<GenAxis> for Gen<T> {
|
||||
@ -52,17 +70,6 @@ impl<T> Get<GenAxis> for Gen<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Switch for Gen<T> {
|
||||
type Other = Spec<T>;
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Spec::new(self.main, self.cross),
|
||||
SpecAxis::Vertical => Spec::new(self.cross, self.main),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Gen<T> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "Gen({:?}, {:?})", self.main, self.cross)
|
||||
@ -88,17 +95,6 @@ impl GenAxis {
|
||||
}
|
||||
}
|
||||
|
||||
impl Switch for GenAxis {
|
||||
type Other = SpecAxis;
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
match self {
|
||||
Self::Main => main,
|
||||
Self::Cross => main.other(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for GenAxis {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.pad(match self {
|
||||
|
@ -1,73 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
/// An enum with the length that a grid cell may have.
|
||||
#[derive(Copy, Clone, PartialEq, Hash)]
|
||||
pub enum TrackSizing {
|
||||
/// A length stated in absolute values and fractions of the parent's size.
|
||||
Linear(Linear),
|
||||
/// A length that is the fraction of the remaining free space in the parent.
|
||||
Fractional(Fractional),
|
||||
/// The cell will fit its contents.
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl TrackSizing {
|
||||
pub fn is_zero(&self) -> bool {
|
||||
match self {
|
||||
Self::Linear(l) => l.is_zero(),
|
||||
Self::Fractional(f) => f.is_zero(),
|
||||
Self::Auto => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn preliminary_length(&self, resolve: Length) -> Length {
|
||||
match self {
|
||||
Self::Linear(l) => l.resolve(resolve),
|
||||
_ => resolve,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TrackSizing {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Linear(x) => <Linear as Display>::fmt(x, f),
|
||||
Self::Fractional(x) => <Fractional as Display>::fmt(x, f),
|
||||
Self::Auto => write!(f, "auto"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for TrackSizing {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Linear(x) => <Linear as Debug>::fmt(x, f),
|
||||
Self::Fractional(x) => <Fractional as Debug>::fmt(x, f),
|
||||
Self::Auto => write!(f, "auto"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Length> for TrackSizing {
|
||||
fn from(abs: Length) -> Self {
|
||||
Self::Linear(abs.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Relative> for TrackSizing {
|
||||
fn from(rel: Relative) -> Self {
|
||||
Self::Linear(rel.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Linear> for TrackSizing {
|
||||
fn from(lin: Linear) -> Self {
|
||||
Self::Linear(lin)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fractional> for TrackSizing {
|
||||
fn from(fr: Fractional) -> Self {
|
||||
Self::Fractional(fr)
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ mod angle;
|
||||
mod dir;
|
||||
mod fr;
|
||||
mod gen;
|
||||
mod gridu;
|
||||
mod length;
|
||||
mod linear;
|
||||
mod path;
|
||||
@ -22,7 +21,6 @@ pub use angle::*;
|
||||
pub use dir::*;
|
||||
pub use fr::*;
|
||||
pub use gen::*;
|
||||
pub use gridu::*;
|
||||
pub use length::*;
|
||||
pub use linear::*;
|
||||
pub use path::*;
|
||||
@ -48,15 +46,3 @@ pub trait Get<Index> {
|
||||
/// Borrow the component for the specified index mutably.
|
||||
fn get_mut(&mut self, index: Index) -> &mut Self::Component;
|
||||
}
|
||||
|
||||
/// Switch between the specific and generic representations of a type.
|
||||
///
|
||||
/// The generic representation deals with main and cross axes while the specific
|
||||
/// representation deals with horizontal and vertical axes.
|
||||
pub trait Switch {
|
||||
/// The type of the other version.
|
||||
type Other;
|
||||
|
||||
/// The other version of this type based on the current main axis.
|
||||
fn switch(self, main: SpecAxis) -> Self::Other;
|
||||
}
|
||||
|
@ -26,6 +26,14 @@ impl Point {
|
||||
pub fn splat(value: Length) -> Self {
|
||||
Self { x: value, y: value }
|
||||
}
|
||||
|
||||
/// Convert to the generic representation.
|
||||
pub fn to_gen(self, main: SpecAxis) -> Gen<Length> {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.y, self.x),
|
||||
SpecAxis::Vertical => Gen::new(self.x, self.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Get<SpecAxis> for Point {
|
||||
@ -46,17 +54,6 @@ impl Get<SpecAxis> for Point {
|
||||
}
|
||||
}
|
||||
|
||||
impl Switch for Point {
|
||||
type Other = Gen<Length>;
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.y, self.x),
|
||||
SpecAxis::Vertical => Gen::new(self.x, self.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Point {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "Point({:?}, {:?})", self.x, self.y)
|
||||
|
@ -49,6 +49,14 @@ impl Size {
|
||||
pub fn to_point(self) -> Point {
|
||||
Point::new(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Convert to the generic representation.
|
||||
pub fn to_gen(self, main: SpecAxis) -> Gen<Length> {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.height, self.width),
|
||||
SpecAxis::Vertical => Gen::new(self.width, self.height),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Get<SpecAxis> for Size {
|
||||
@ -69,17 +77,6 @@ impl Get<SpecAxis> for Size {
|
||||
}
|
||||
}
|
||||
|
||||
impl Switch for Size {
|
||||
type Other = Gen<Length>;
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.height, self.width),
|
||||
SpecAxis::Vertical => Gen::new(self.width, self.height),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Size {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "Size({:?}, {:?})", self.width, self.height)
|
||||
|
@ -25,6 +25,14 @@ impl<T> Spec<T> {
|
||||
vertical: value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to the generic representation.
|
||||
pub fn to_gen(self, main: SpecAxis) -> Gen<T> {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.vertical, self.horizontal),
|
||||
SpecAxis::Vertical => Gen::new(self.horizontal, self.vertical),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Spec<Length> {
|
||||
@ -65,17 +73,6 @@ impl<T> Get<SpecAxis> for Spec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Switch for Spec<T> {
|
||||
type Other = Gen<T>;
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.vertical, self.horizontal),
|
||||
SpecAxis::Vertical => Gen::new(self.horizontal, self.vertical),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Spec<T> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "Spec({:?}, {:?})", self.horizontal, self.vertical)
|
||||
@ -101,14 +98,6 @@ impl SpecAxis {
|
||||
}
|
||||
}
|
||||
|
||||
impl Switch for SpecAxis {
|
||||
type Other = GenAxis;
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
if self == main { GenAxis::Main } else { GenAxis::Cross }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SpecAxis {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.pad(match self {
|
||||
|
@ -35,14 +35,6 @@ impl Frame {
|
||||
self.push(pos + subpos, element);
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate the positions of all elements in the frame by adding the
|
||||
/// argument to their position.
|
||||
pub fn translate(&mut self, amount: Point) {
|
||||
for (pos, _) in &mut self.elements {
|
||||
*pos += amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The building block frames are composed of.
|
||||
|
@ -1,216 +1,128 @@
|
||||
use std::usize;
|
||||
|
||||
use super::*;
|
||||
use crate::library::GridUnits;
|
||||
|
||||
/// A node that stacks its children.
|
||||
/// A node that arranges its children in a grid.
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
pub struct GridNode {
|
||||
/// The `main` and `cross` directions of this stack.
|
||||
///
|
||||
/// The children are stacked along the `main` direction. The `cross`
|
||||
/// direction is required for aligning the children.
|
||||
pub dir: Dir,
|
||||
/// The nodes to be stacked.
|
||||
/// The column (cross) direction of this stack.
|
||||
pub column_dir: Dir,
|
||||
/// The nodes to be arranged in a grid.
|
||||
pub children: Vec<AnyNode>,
|
||||
pub tracks: Gen<GridUnits>,
|
||||
pub gutter: Gen<GridUnits>,
|
||||
/// Defines sizing for rows and columns.
|
||||
pub tracks: Gen<Tracks>,
|
||||
/// Defines sizing of the gutter between rows and columns.
|
||||
pub gutter: Gen<Tracks>,
|
||||
}
|
||||
|
||||
impl Layout for GridNode {
|
||||
fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec<Frame> {
|
||||
let layout = GridLayouter::new(self, regions).layout(ctx);
|
||||
layout
|
||||
GridLayouter::new(self, regions.clone()).layout(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum GridItem<'a> {
|
||||
impl From<GridNode> for AnyNode {
|
||||
fn from(grid: GridNode) -> Self {
|
||||
Self::new(grid)
|
||||
}
|
||||
}
|
||||
|
||||
struct GridLayouter<'a> {
|
||||
cross: SpecAxis,
|
||||
main: SpecAxis,
|
||||
cols: Vec<TrackSizing>,
|
||||
rows: Vec<TrackSizing>,
|
||||
cells: Vec<Cell<'a>>,
|
||||
regions: Regions,
|
||||
rrows: Vec<(usize, Option<Length>)>,
|
||||
rcols: Vec<Length>,
|
||||
finished: Vec<Frame>,
|
||||
}
|
||||
|
||||
enum Cell<'a> {
|
||||
Node(&'a AnyNode),
|
||||
Gutter,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GridLayouter<'a> {
|
||||
items: Vec<GridItem<'a>>,
|
||||
cols: Vec<TrackSizing>,
|
||||
rows: Vec<TrackSizing>,
|
||||
region: Regions,
|
||||
dir: Dir,
|
||||
rrows: Vec<(usize, Option<Length>)>,
|
||||
rcols: Vec<Length>,
|
||||
frames: Vec<Frame>,
|
||||
}
|
||||
|
||||
impl<'a> GridLayouter<'a> {
|
||||
fn new(
|
||||
grid: &'a GridNode,
|
||||
regions: &Regions,
|
||||
) -> Self {
|
||||
let mut items = vec![];
|
||||
fn new(grid: &'a GridNode, regions: Regions) -> Self {
|
||||
let mut col_sizes = vec![];
|
||||
let mut row_sizes = vec![];
|
||||
let cols = grid.tracks.cross.0.len();
|
||||
// Create at least as many rows as specified and a row to fit every item.
|
||||
let rows = if cols > 0 {
|
||||
let res = grid
|
||||
.tracks
|
||||
.main
|
||||
.0
|
||||
.len()
|
||||
.max(grid.children.len() / cols + (grid.children.len() % cols).clamp(0, 1));
|
||||
res
|
||||
} else {
|
||||
0
|
||||
let mut cells = vec![];
|
||||
|
||||
// A grid always needs to have at least one column.
|
||||
let cols = grid.tracks.cross.0.len().max(1);
|
||||
|
||||
// Create at least as many rows as specified and also at least as many
|
||||
// as necessary to place each item.
|
||||
let rows = {
|
||||
let len = grid.children.len();
|
||||
let specified = grid.tracks.main.0.len();
|
||||
let necessary = len / cols + (len % cols).clamp(0, 1);
|
||||
specified.max(necessary)
|
||||
};
|
||||
|
||||
for (i, col_size) in grid.tracks.cross.0.iter().enumerate() {
|
||||
let last = i == cols - 1;
|
||||
col_sizes.push(*col_size);
|
||||
|
||||
if !last {
|
||||
let gutter = grid.gutter.cross.get(i);
|
||||
col_sizes.push(gutter);
|
||||
// Collect the track sizing for all columns, including gutter columns.
|
||||
for i in 0 .. cols {
|
||||
col_sizes.push(grid.tracks.cross.get(i));
|
||||
if i < cols - 1 {
|
||||
col_sizes.push(grid.gutter.cross.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (i, row_size) in (0 .. rows).map(|i| (i, grid.tracks.main.get(i))) {
|
||||
let last = i == rows - 1;
|
||||
row_sizes.push(row_size);
|
||||
|
||||
if !last {
|
||||
let gutter = grid.gutter.main.get(i);
|
||||
row_sizes.push(gutter);
|
||||
// Collect the track sizing for all rows, including gutter rows.
|
||||
for i in 0 .. rows {
|
||||
row_sizes.push(grid.tracks.main.get(i));
|
||||
if i < rows - 1 {
|
||||
row_sizes.push(grid.gutter.main.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Build up the matrix of cells, including gutter cells.
|
||||
for (i, item) in grid.children.iter().enumerate() {
|
||||
if cols == 0 {
|
||||
break;
|
||||
}
|
||||
cells.push(Cell::Node(item));
|
||||
|
||||
let row = i / cols;
|
||||
let col = i % cols;
|
||||
|
||||
items.push(GridItem::Node(item));
|
||||
|
||||
if col != cols - 1 {
|
||||
// Push gutter
|
||||
items.push(GridItem::Gutter);
|
||||
} else if row != rows - 1 {
|
||||
// Push gutter row.
|
||||
if col < cols - 1 {
|
||||
// Push gutter after each child.
|
||||
cells.push(Cell::Gutter);
|
||||
} else if row < rows - 1 {
|
||||
// Except for the last child of each row.
|
||||
// There we push a gutter row.
|
||||
for _ in 0 .. col_sizes.len() {
|
||||
items.push(GridItem::Gutter);
|
||||
cells.push(Cell::Gutter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the thing up
|
||||
while items.len() < col_sizes.len() * row_sizes.len() {
|
||||
items.push(GridItem::Gutter)
|
||||
// Fill the thing up.
|
||||
while cells.len() < col_sizes.len() * row_sizes.len() {
|
||||
cells.push(Cell::Gutter)
|
||||
}
|
||||
|
||||
GridLayouter {
|
||||
Self {
|
||||
cross: grid.column_dir.axis(),
|
||||
main: grid.column_dir.axis().other(),
|
||||
cols: col_sizes,
|
||||
rows: row_sizes,
|
||||
region: regions.clone(),
|
||||
dir: grid.dir,
|
||||
items,
|
||||
cells,
|
||||
regions,
|
||||
rrows: vec![],
|
||||
rcols: vec![],
|
||||
frames: vec![],
|
||||
finished: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, x: usize, y: usize) -> &GridItem<'_> {
|
||||
assert!(x < self.cols.len());
|
||||
assert!(y < self.rows.len());
|
||||
let row_cmp = y * self.cols.len();
|
||||
|
||||
self.items.get(row_cmp + x).unwrap()
|
||||
}
|
||||
|
||||
fn main(&self) -> SpecAxis {
|
||||
self.dir.axis().other()
|
||||
}
|
||||
|
||||
fn cross(&self) -> SpecAxis {
|
||||
self.dir.axis()
|
||||
}
|
||||
|
||||
fn finish_region(&mut self, ctx: &mut LayoutContext, total_frs: f64) {
|
||||
let mut pos = Gen::splat(Length::zero());
|
||||
let pos2point = |mut pos: Gen<Length>| {
|
||||
if !self.dir.is_positive() {
|
||||
pos.cross = -pos.cross;
|
||||
}
|
||||
pos.switch(self.main()).to_point()
|
||||
};
|
||||
let mut frame = Frame::new(Size::zero(), Length::zero());
|
||||
let mut total_cross = Length::zero();
|
||||
let mut total_main = Length::zero();
|
||||
|
||||
for (x, &w) in self.rcols.iter().enumerate() {
|
||||
let total: Length = self.rrows.iter().filter_map(|(_, x)| *x).sum();
|
||||
let available = self.region.current.get(self.main()) - total;
|
||||
total_cross += w;
|
||||
|
||||
for (y, h) in self.rrows.iter() {
|
||||
let element = self.get(x, *y);
|
||||
let h = if let Some(len) = h {
|
||||
*len
|
||||
} else {
|
||||
if let TrackSizing::Fractional(f) = self.rows[*y] {
|
||||
if total_frs > 0.0 {
|
||||
let res = available * (f.get() / total_frs);
|
||||
if res.is_finite() {
|
||||
res
|
||||
} else {
|
||||
Length::zero()
|
||||
}
|
||||
} else {
|
||||
Length::zero()
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
if x == 0 {
|
||||
total_main += h;
|
||||
}
|
||||
|
||||
if let GridItem::Node(n) = element {
|
||||
let item = n.layout(ctx, &Regions::one(Gen::new(w, h).switch(self.main()).to_size(), Spec::splat(false))).remove(0);
|
||||
frame.push_frame(pos2point(pos), item);
|
||||
}
|
||||
|
||||
pos.main += h;
|
||||
}
|
||||
pos.main = Length::zero();
|
||||
pos.cross += self.dir.factor() as f64 * w;
|
||||
}
|
||||
|
||||
if !self.dir.is_positive() {
|
||||
frame.translate(Gen::new(total_cross, Length::zero()).switch(self.main()).to_point());
|
||||
}
|
||||
|
||||
frame.size = Gen::new(total_cross, total_main).switch(self.main()).to_size();
|
||||
frame.baseline = frame.size.height;
|
||||
|
||||
self.frames.push(frame);
|
||||
|
||||
self.rrows.clear();
|
||||
self.region.next();
|
||||
}
|
||||
|
||||
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Frame> {
|
||||
// Shrink area by linear sizing.
|
||||
let mut available = self.region.current.get(self.cross());
|
||||
let mut available = self.regions.current.get(self.cross);
|
||||
available -= self
|
||||
.cols
|
||||
.iter()
|
||||
.filter_map(|x| match x {
|
||||
TrackSizing::Linear(l) => Some(l.resolve(self.region.base.get(self.cross()))),
|
||||
TrackSizing::Linear(l) => {
|
||||
Some(l.resolve(self.regions.base.get(self.cross)))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.sum();
|
||||
@ -232,34 +144,31 @@ impl<'a> GridLayouter<'a> {
|
||||
|
||||
let mut col_width = vec![];
|
||||
|
||||
// For each of the auto columns, lay out all elements with `preliminary_length`
|
||||
// rows and build max.
|
||||
// For each of the auto columns, lay out all elements with
|
||||
// `preliminary_length` rows and build max.
|
||||
for x in auto_columns {
|
||||
let mut max = Length::zero();
|
||||
for (y, row_height) in
|
||||
self.rows.iter().enumerate().map(|(y, s)| {
|
||||
(y, s.preliminary_length(self.region.base.get(self.main())))
|
||||
})
|
||||
{
|
||||
let item = self.get(x, y);
|
||||
let size =
|
||||
Gen::new(self.region.current.get(self.cross()), row_height).switch(self.main()).to_size();
|
||||
|
||||
for (y, row) in self.rows.iter().enumerate() {
|
||||
let mut size = self.regions.current;
|
||||
if let TrackSizing::Linear(l) = row {
|
||||
*size.get_mut(self.main) =
|
||||
l.resolve(self.regions.base.get(self.main));
|
||||
}
|
||||
|
||||
let region = Regions::one(size, Spec::splat(false));
|
||||
match item {
|
||||
GridItem::Node(n) => {
|
||||
max = max.max(
|
||||
n.layout(ctx, ®ion).first().unwrap().size.get(self.cross()),
|
||||
)
|
||||
}
|
||||
GridItem::Gutter => {}
|
||||
if let Cell::Node(node) = self.get(x, y) {
|
||||
let frame = node.layout(ctx, ®ion).remove(0);
|
||||
max = max.max(frame.size.get(self.cross))
|
||||
}
|
||||
}
|
||||
|
||||
col_width.push((x, max));
|
||||
}
|
||||
|
||||
// If accumulated auto column size exceeds available size, redistribute space
|
||||
// proportionally amongst elements that exceed their size allocation.
|
||||
// If accumulated auto column size exceeds available size, redistribute
|
||||
// space proportionally amongst elements that exceed their size
|
||||
// allocation.
|
||||
let mut total: Length = col_width.iter().map(|(_, x)| *x).sum();
|
||||
if total > available {
|
||||
let alloc = available / col_width.len() as f64;
|
||||
@ -288,23 +197,25 @@ impl<'a> GridLayouter<'a> {
|
||||
}
|
||||
|
||||
// Build rcols
|
||||
for (x, len) in col_width.into_iter().map(|(x, s)| (x, Some(s))).chain(std::iter::once((self.cols.len(), None))) {
|
||||
for (x, len) in col_width
|
||||
.into_iter()
|
||||
.map(|(x, s)| (x, Some(s)))
|
||||
.chain(std::iter::once((self.cols.len(), None)))
|
||||
{
|
||||
for i in self.rcols.len() .. x {
|
||||
let len = match self.cols[i] {
|
||||
TrackSizing::Linear(l) => l.resolve(self.region.base.get(self.cross())),
|
||||
TrackSizing::Linear(l) => {
|
||||
l.resolve(self.regions.base.get(self.cross))
|
||||
}
|
||||
TrackSizing::Fractional(f) => {
|
||||
if col_frac == 0.0 {
|
||||
Length::zero()
|
||||
} else {
|
||||
let res: Length = (available - total) * (f.get() / col_frac);
|
||||
if res.is_finite() {
|
||||
res
|
||||
} else {
|
||||
Length::zero()
|
||||
}
|
||||
if res.is_finite() { res } else { Length::zero() }
|
||||
}
|
||||
}
|
||||
TrackSizing::Auto => unreachable!(),
|
||||
TrackSizing::Auto => unreachable!("x is an auto track"),
|
||||
};
|
||||
|
||||
self.rcols.push(len);
|
||||
@ -317,27 +228,23 @@ impl<'a> GridLayouter<'a> {
|
||||
|
||||
// Determine non-`fr` row heights
|
||||
let mut total_frs = 0.0;
|
||||
let mut current = self.region.current.get(self.main());
|
||||
let mut current = self.regions.current.get(self.main);
|
||||
|
||||
for y in 0..self.rows.len() {
|
||||
let height = &self.rows[y];
|
||||
let resolved = match height {
|
||||
TrackSizing::Linear(l) => Some(l.resolve(self.region.base.get(self.main()))),
|
||||
for y in 0 .. self.rows.len() {
|
||||
let resolved = match self.rows[y] {
|
||||
TrackSizing::Linear(l) => {
|
||||
Some(l.resolve(self.regions.base.get(self.main)))
|
||||
}
|
||||
TrackSizing::Auto => {
|
||||
let mut max = Length::zero();
|
||||
for (x, len) in self.rcols.iter().enumerate() {
|
||||
let node = self.get(x, y);
|
||||
if let GridItem::Node(node) = node {
|
||||
let frames = node.layout(
|
||||
ctx,
|
||||
&Regions::one(
|
||||
Gen::new(*len, current)
|
||||
.switch(self.main())
|
||||
.to_size(),
|
||||
Spec::splat(false),
|
||||
),
|
||||
if let Cell::Node(node) = self.get(x, y) {
|
||||
let regions = Regions::one(
|
||||
Gen::new(*len, current).to_size(self.main),
|
||||
Spec::splat(false),
|
||||
);
|
||||
max = max.max(frames.first().unwrap().size.get(self.main()));
|
||||
let frame = node.layout(ctx, ®ions).remove(0);
|
||||
max = max.max(frame.size.get(self.main));
|
||||
}
|
||||
}
|
||||
Some(max)
|
||||
@ -345,13 +252,13 @@ impl<'a> GridLayouter<'a> {
|
||||
TrackSizing::Fractional(f) => {
|
||||
total_frs += f.get();
|
||||
None
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(resolved) = resolved {
|
||||
while !current.fits(resolved) && !self.region.in_full_last() {
|
||||
while !current.fits(resolved) && !self.regions.in_full_last() {
|
||||
self.finish_region(ctx, total_frs);
|
||||
current = self.region.current.get(self.main());
|
||||
current = self.regions.current.get(self.main);
|
||||
total_frs = 0.0;
|
||||
}
|
||||
current -= resolved;
|
||||
@ -361,12 +268,91 @@ impl<'a> GridLayouter<'a> {
|
||||
}
|
||||
|
||||
self.finish_region(ctx, total_frs);
|
||||
self.frames
|
||||
self.finished
|
||||
}
|
||||
|
||||
fn finish_region(&mut self, ctx: &mut LayoutContext, total_frs: f64) {
|
||||
let mut pos = Gen::splat(Length::zero());
|
||||
let mut frame = Frame::new(Size::zero(), Length::zero());
|
||||
let mut total_cross = Length::zero();
|
||||
let mut total_main = Length::zero();
|
||||
|
||||
for (x, &w) in self.rcols.iter().enumerate() {
|
||||
let total: Length = self.rrows.iter().filter_map(|(_, x)| *x).sum();
|
||||
let available = self.regions.current.get(self.main) - total;
|
||||
total_cross += w;
|
||||
|
||||
for (y, h) in self.rrows.iter() {
|
||||
let element = self.get(x, *y);
|
||||
let h = if let Some(len) = h {
|
||||
*len
|
||||
} else if let TrackSizing::Fractional(f) = self.rows[*y] {
|
||||
if total_frs > 0.0 {
|
||||
let res = available * (f.get() / total_frs);
|
||||
if res.is_finite() { res } else { Length::zero() }
|
||||
} else {
|
||||
Length::zero()
|
||||
}
|
||||
} else {
|
||||
unreachable!("non-fractional tracks are already resolved");
|
||||
};
|
||||
|
||||
if x == 0 {
|
||||
total_main += h;
|
||||
}
|
||||
|
||||
if let Cell::Node(n) = element {
|
||||
let regions = Regions::one(
|
||||
Gen::new(w, h).to_size(self.main),
|
||||
Spec::splat(false),
|
||||
);
|
||||
let item = n.layout(ctx, ®ions).remove(0);
|
||||
frame.push_frame(pos.to_point(self.main), item);
|
||||
}
|
||||
|
||||
pos.main += h;
|
||||
}
|
||||
pos.main = Length::zero();
|
||||
pos.cross += w;
|
||||
}
|
||||
|
||||
frame.size = Gen::new(total_cross, total_main).to_size(self.main);
|
||||
frame.baseline = frame.size.height;
|
||||
|
||||
self.rrows.clear();
|
||||
self.regions.next();
|
||||
self.finished.push(frame);
|
||||
}
|
||||
|
||||
fn get(&self, x: usize, y: usize) -> &Cell<'a> {
|
||||
assert!(x < self.cols.len());
|
||||
assert!(y < self.rows.len());
|
||||
self.cells.get(y * self.cols.len() + x).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GridNode> for AnyNode {
|
||||
fn from(grid: GridNode) -> Self {
|
||||
Self::new(grid)
|
||||
/// A list of track sizing definitions.
|
||||
#[derive(Default, Debug, Clone, PartialEq, Hash)]
|
||||
pub struct Tracks(pub Vec<TrackSizing>);
|
||||
|
||||
impl Tracks {
|
||||
/// Get the sizing for the track at the given `idx`.
|
||||
fn get(&self, idx: usize) -> TrackSizing {
|
||||
self.0
|
||||
.get(idx)
|
||||
.or(self.0.last())
|
||||
.copied()
|
||||
.unwrap_or(TrackSizing::Auto)
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines how to size a grid cell along an axis.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||
pub enum TrackSizing {
|
||||
/// Fit the cell to its contents.
|
||||
Auto,
|
||||
/// A length stated in absolute values and fractions of the parent's size.
|
||||
Linear(Linear),
|
||||
/// A length that is the fraction of the remaining free space in the parent.
|
||||
Fractional(Fractional),
|
||||
}
|
||||
|
@ -29,24 +29,7 @@ pub enum StackChild {
|
||||
|
||||
impl Layout for StackNode {
|
||||
fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec<Frame> {
|
||||
let mut layouter = StackLayouter::new(self.dirs, self.aspect, regions.clone());
|
||||
for child in &self.children {
|
||||
match *child {
|
||||
StackChild::Spacing(amount) => layouter.push_spacing(amount),
|
||||
StackChild::Any(ref node, aligns) => {
|
||||
let mut frames = node.layout(ctx, &layouter.regions).into_iter();
|
||||
if let Some(frame) = frames.next() {
|
||||
layouter.push_frame(frame, aligns);
|
||||
}
|
||||
|
||||
for frame in frames {
|
||||
layouter.finish_region();
|
||||
layouter.push_frame(frame, aligns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
layouter.finish()
|
||||
StackLayouter::new(self, regions.clone()).layout(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,107 +39,154 @@ impl From<StackNode> for AnyNode {
|
||||
}
|
||||
}
|
||||
|
||||
struct StackLayouter {
|
||||
dirs: Gen<Dir>,
|
||||
aspect: Option<N64>,
|
||||
struct StackLayouter<'a> {
|
||||
/// The directions of the stack.
|
||||
stack: &'a StackNode,
|
||||
/// The axis of the main direction.
|
||||
main: SpecAxis,
|
||||
/// The region to layout into.
|
||||
regions: Regions,
|
||||
finished: Vec<Frame>,
|
||||
frames: Vec<(Length, Frame, Gen<Align>)>,
|
||||
/// Offset, alignment and frame for all children that fit into the current
|
||||
/// region. The exact positions are not known yet.
|
||||
frames: Vec<(Length, Gen<Align>, Frame)>,
|
||||
/// The full size of `regions.current` that was available before we started
|
||||
/// subtracting.
|
||||
full: Size,
|
||||
size: Gen<Length>,
|
||||
/// The generic size used by the frames for the current region.
|
||||
used: Gen<Length>,
|
||||
/// The alignment ruler for the current region.
|
||||
ruler: Align,
|
||||
/// Finished frames for previous regions.
|
||||
finished: Vec<Frame>,
|
||||
}
|
||||
|
||||
impl StackLayouter {
|
||||
fn new(dirs: Gen<Dir>, aspect: Option<N64>, mut regions: Regions) -> Self {
|
||||
if let Some(aspect) = aspect {
|
||||
impl<'a> StackLayouter<'a> {
|
||||
fn new(stack: &'a StackNode, mut regions: Regions) -> Self {
|
||||
if let Some(aspect) = stack.aspect {
|
||||
regions.apply_aspect_ratio(aspect);
|
||||
}
|
||||
|
||||
Self {
|
||||
dirs,
|
||||
aspect,
|
||||
main: dirs.main.axis(),
|
||||
stack,
|
||||
main: stack.dirs.main.axis(),
|
||||
finished: vec![],
|
||||
frames: vec![],
|
||||
full: regions.current,
|
||||
size: Gen::zero(),
|
||||
used: Gen::zero(),
|
||||
ruler: Align::Start,
|
||||
regions,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Frame> {
|
||||
for child in &self.stack.children {
|
||||
match *child {
|
||||
StackChild::Spacing(amount) => self.push_spacing(amount),
|
||||
StackChild::Any(ref node, aligns) => {
|
||||
let mut frames = node.layout(ctx, &self.regions).into_iter();
|
||||
if let Some(frame) = frames.next() {
|
||||
self.push_frame(frame, aligns);
|
||||
}
|
||||
|
||||
for frame in frames {
|
||||
self.finish_region();
|
||||
self.push_frame(frame, aligns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.finish_region();
|
||||
self.finished
|
||||
}
|
||||
|
||||
fn push_spacing(&mut self, amount: Length) {
|
||||
// Cap the spacing to the remaining available space.
|
||||
let remaining = self.regions.current.get_mut(self.main);
|
||||
let capped = amount.min(*remaining);
|
||||
|
||||
// Grow our size and shrink the available space in the region.
|
||||
self.used.main += capped;
|
||||
*remaining -= capped;
|
||||
self.size.main += capped;
|
||||
}
|
||||
|
||||
fn push_frame(&mut self, frame: Frame, aligns: Gen<Align>) {
|
||||
let size = frame.size;
|
||||
|
||||
// Don't allow `Start` after `End` in the same region.
|
||||
if self.ruler > aligns.main {
|
||||
self.finish_region();
|
||||
}
|
||||
|
||||
while !self.regions.current.fits(frame.size) && !self.regions.in_full_last() {
|
||||
// Adjust the ruler.
|
||||
self.ruler = aligns.main;
|
||||
|
||||
// Find a fitting region.
|
||||
while !self.regions.current.fits(size) && !self.regions.in_full_last() {
|
||||
self.finish_region();
|
||||
}
|
||||
|
||||
let offset = self.size.main;
|
||||
let size = frame.size.switch(self.main);
|
||||
self.size.main += size.main;
|
||||
self.size.cross.set_max(size.cross);
|
||||
self.ruler = aligns.main;
|
||||
*self.regions.current.get_mut(self.main) -= size.main;
|
||||
self.frames.push((offset, frame, aligns));
|
||||
// Remember the frame with offset and alignment.
|
||||
self.frames.push((self.used.main, aligns, frame));
|
||||
|
||||
// Grow our size and shrink available space in the region.
|
||||
let gen = size.to_gen(self.main);
|
||||
self.used.main += gen.main;
|
||||
self.used.cross.set_max(gen.cross);
|
||||
*self.regions.current.get_mut(self.main) -= gen.main;
|
||||
}
|
||||
|
||||
fn finish_region(&mut self) {
|
||||
let used = self.used.to_size(self.main);
|
||||
let fixed = self.regions.fixed;
|
||||
|
||||
let used = self.size.switch(self.main).to_size();
|
||||
let mut size = Size::new(
|
||||
// Determine the stack's size dependening on whether the region is
|
||||
// fixed.
|
||||
let mut stack_size = Size::new(
|
||||
if fixed.horizontal { self.full.width } else { used.width },
|
||||
if fixed.vertical { self.full.height } else { used.height },
|
||||
);
|
||||
|
||||
if let Some(aspect) = self.aspect {
|
||||
let width = size
|
||||
// Make sure the stack's size satisfies the aspect ratio.
|
||||
if let Some(aspect) = self.stack.aspect {
|
||||
let width = stack_size
|
||||
.width
|
||||
.max(aspect.into_inner() * size.height)
|
||||
.max(aspect.into_inner() * stack_size.height)
|
||||
.min(self.full.width)
|
||||
.min(aspect.into_inner() * self.full.height);
|
||||
|
||||
size = Size::new(width, width / aspect.into_inner());
|
||||
stack_size = Size::new(width, width / aspect.into_inner());
|
||||
}
|
||||
|
||||
let mut output = Frame::new(size, size.height);
|
||||
let mut output = Frame::new(stack_size, stack_size.height);
|
||||
let mut first = true;
|
||||
|
||||
let used = self.size;
|
||||
let size = size.switch(self.main);
|
||||
|
||||
for (offset, frame, aligns) in std::mem::take(&mut self.frames) {
|
||||
let child = frame.size.switch(self.main);
|
||||
// Place all frames.
|
||||
for (offset, aligns, frame) in std::mem::take(&mut self.frames) {
|
||||
let stack_size = stack_size.to_gen(self.main);
|
||||
let child_size = frame.size.to_gen(self.main);
|
||||
|
||||
// Align along the cross axis.
|
||||
let cross = aligns
|
||||
.cross
|
||||
.resolve(self.dirs.cross, Length::zero() .. size.cross - child.cross);
|
||||
let cross = aligns.cross.resolve(
|
||||
self.stack.dirs.cross,
|
||||
Length::zero() .. stack_size.cross - child_size.cross,
|
||||
);
|
||||
|
||||
// Align along the main axis.
|
||||
let main = aligns.main.resolve(
|
||||
self.dirs.main,
|
||||
if self.dirs.main.is_positive() {
|
||||
offset .. size.main - used.main + offset
|
||||
self.stack.dirs.main,
|
||||
if self.stack.dirs.main.is_positive() {
|
||||
offset .. stack_size.main - self.used.main + offset
|
||||
} else {
|
||||
let offset_with_self = offset + child.main;
|
||||
used.main - offset_with_self .. size.main - offset_with_self
|
||||
let offset_with_self = offset + child_size.main;
|
||||
self.used.main - offset_with_self
|
||||
.. stack_size.main - offset_with_self
|
||||
},
|
||||
);
|
||||
|
||||
let pos = Gen::new(cross, main).switch(self.main).to_point();
|
||||
let pos = Gen::new(cross, main).to_point(self.main);
|
||||
|
||||
// The baseline of the stack is that of the first frame.
|
||||
if first {
|
||||
output.baseline = pos.y + frame.baseline;
|
||||
first = false;
|
||||
@ -165,18 +195,15 @@ impl StackLayouter {
|
||||
output.push_frame(pos, frame);
|
||||
}
|
||||
|
||||
self.size = Gen::zero();
|
||||
self.ruler = Align::Start;
|
||||
// Move on to the next region.
|
||||
self.regions.next();
|
||||
if let Some(aspect) = self.aspect {
|
||||
if let Some(aspect) = self.stack.aspect {
|
||||
self.regions.apply_aspect_ratio(aspect);
|
||||
}
|
||||
|
||||
self.full = self.regions.current;
|
||||
self.used = Gen::zero();
|
||||
self.ruler = Align::Start;
|
||||
self.finished.push(output);
|
||||
}
|
||||
|
||||
fn finish(mut self) -> Vec<Frame> {
|
||||
self.finish_region();
|
||||
self.finished
|
||||
}
|
||||
}
|
||||
|
@ -1,76 +1,79 @@
|
||||
use crate::layout::GridNode;
|
||||
use crate::layout::{GridNode, TrackSizing, Tracks};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// `stack`: Stack children along an axis.
|
||||
/// `grid`: Arrange children into a grid.
|
||||
///
|
||||
/// # Positional parameters
|
||||
/// - Children: variadic, of type `template`.
|
||||
///
|
||||
/// # Named parameters
|
||||
/// - Column widths: `columns`, of type `Array<GridUnit>`.
|
||||
/// - Row widths: `rows`, of type `Array<GridUnit>`.
|
||||
/// - Gutter: `gutter-vertical` and `gutter-horizontal` for individual track axis or `gutter` for both, of type `Array<GridUnit>` respectively.
|
||||
/// - Stacking direction: `dir`, of type `direction`.
|
||||
/// - Column sizing: `columns`, of type `tracks`.
|
||||
/// - Row sizing: `rows`, of type `tracks`.
|
||||
/// - Column direction: `column-dir`, of type `direction`.
|
||||
/// - Gutter: `gutter`, shorthand for equal column and row gutter, of type `tracks`.
|
||||
/// - Gutter for rows: `gutter-rows`, of type `tracks`.
|
||||
/// - Gutter for columns: `gutter-columns`, of type `tracks`.
|
||||
///
|
||||
/// # Return value
|
||||
/// A template that arranges its children along the specified grid cells.
|
||||
///
|
||||
/// # Relevant types and constants
|
||||
/// - Type `tracks`
|
||||
/// - coerces from `array` of `track-sizing`
|
||||
/// - Type `track-sizing`
|
||||
/// - `auto`
|
||||
// - coerces from `length`
|
||||
// - coerces from `relative`
|
||||
// - coerces from `linear`
|
||||
// - coerces from `fractional`
|
||||
/// - Type `direction`
|
||||
/// - `ltr`
|
||||
/// - `rtl`
|
||||
/// - `ttb`
|
||||
/// - `btt`
|
||||
pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
let cols = args.eat_named::<GridUnits>(ctx, "columns").unwrap_or_default();
|
||||
let rows = args.eat_named::<GridUnits>(ctx, "rows").unwrap_or_default();
|
||||
|
||||
let gutter = args.eat_named(ctx, "gutter");
|
||||
let gutter_vertical = args
|
||||
.eat_named::<GridUnits>(ctx, "gutter-col")
|
||||
.or_else(|| gutter.clone())
|
||||
.unwrap_or_default();
|
||||
let gutter_horizontal = args
|
||||
.eat_named::<GridUnits>(ctx, "gutter-row")
|
||||
.or(gutter)
|
||||
.unwrap_or_default();
|
||||
|
||||
let dir = args.eat_named(ctx, "dir");
|
||||
let columns = args.eat_named::<Tracks>(ctx, "columns").unwrap_or_default();
|
||||
let rows = args.eat_named::<Tracks>(ctx, "rows").unwrap_or_default();
|
||||
let column_dir = args.eat_named(ctx, "column-dir");
|
||||
let gutter = args.eat_named::<Tracks>(ctx, "gutter").unwrap_or_default();
|
||||
let gutter_columns = args.eat_named::<Tracks>(ctx, "gutter-columns");
|
||||
let gutter_rows = args.eat_named::<Tracks>(ctx, "gutter-rows");
|
||||
let children = args.eat_all::<TemplateValue>(ctx);
|
||||
|
||||
Value::template("grid", move |ctx| {
|
||||
let children =
|
||||
children.iter().map(|child| ctx.exec_template_stack(child).into()).collect();
|
||||
let children = children
|
||||
.iter()
|
||||
.map(|child| ctx.exec_template_stack(child).into())
|
||||
.collect();
|
||||
|
||||
ctx.push(GridNode {
|
||||
dir: dir.unwrap_or_else(|| ctx.state.lang.dir),
|
||||
column_dir: column_dir.unwrap_or(ctx.state.lang.dir),
|
||||
children,
|
||||
gutter: Gen::new(gutter_vertical.clone(), gutter_horizontal.clone()),
|
||||
tracks: Gen::new(cols.clone(), rows.clone()),
|
||||
tracks: Gen::new(columns.clone(), rows.clone()),
|
||||
gutter: Gen::new(
|
||||
gutter_columns.as_ref().unwrap_or(&gutter).clone(),
|
||||
gutter_rows.as_ref().unwrap_or(&gutter).clone(),
|
||||
),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// A list of [`GridUnit`]s.
|
||||
#[derive(Default, Debug, Clone, PartialEq, Hash)]
|
||||
pub struct GridUnits(pub Vec<TrackSizing>);
|
||||
|
||||
impl GridUnits {
|
||||
pub fn get(&self, index: usize) -> TrackSizing {
|
||||
if self.0.is_empty() {
|
||||
TrackSizing::Auto
|
||||
} else {
|
||||
*self.0.get(index).unwrap_or(self.0.last().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value! {
|
||||
GridUnits: "array of fractional values, lengths, and the `auto` keyword",
|
||||
Value::TrackSizing(value) => Self(vec![value]),
|
||||
Tracks: "array of `auto`s, linears, and fractionals",
|
||||
Value::Int(count) => Self(vec![TrackSizing::Auto; count.max(0) as usize]),
|
||||
Value::Array(values) => Self(values
|
||||
.into_iter()
|
||||
.filter_map(|v| v.cast().ok())
|
||||
.collect()
|
||||
),
|
||||
}
|
||||
|
||||
value! {
|
||||
TrackSizing: "`auto`, linear, or fractional",
|
||||
Value::Auto => TrackSizing::Auto,
|
||||
Value::Length(v) => TrackSizing::Linear(v.into()),
|
||||
Value::Relative(v) => TrackSizing::Linear(v.into()),
|
||||
Value::Linear(v) => TrackSizing::Linear(v),
|
||||
Value::Fractional(v) => TrackSizing::Fractional(v),
|
||||
}
|
||||
|
@ -314,11 +314,6 @@ fn primary(p: &mut Parser, atomic: bool) -> Option<Expr> {
|
||||
Some(Token::For) => expr_for(p),
|
||||
Some(Token::Import) => expr_import(p),
|
||||
Some(Token::Include) => expr_include(p),
|
||||
Some(Token::Auto) => {
|
||||
let start = p.next_start();
|
||||
p.assert(Token::Auto);
|
||||
Some(Expr::Auto(p.span(start)))
|
||||
}
|
||||
|
||||
// Nothing.
|
||||
_ => {
|
||||
@ -334,6 +329,7 @@ fn literal(p: &mut Parser) -> Option<Expr> {
|
||||
let expr = match p.peek()? {
|
||||
// Basic values.
|
||||
Token::None => Expr::None(span),
|
||||
Token::Auto => Expr::Auto(span),
|
||||
Token::Bool(b) => Expr::Bool(span, b),
|
||||
Token::Int(i) => Expr::Int(span, i),
|
||||
Token::Float(f) => Expr::Float(span, f),
|
||||
|
@ -350,6 +350,7 @@ impl<'s> Tokens<'s> {
|
||||
"and" => Token::And,
|
||||
"or" => Token::Or,
|
||||
"none" => Token::None,
|
||||
"auto" => Token::Auto,
|
||||
"true" => Token::Bool(true),
|
||||
"false" => Token::Bool(false),
|
||||
id => keyword(id).unwrap_or(Token::Ident(id)),
|
||||
@ -474,7 +475,6 @@ fn keyword(id: &str) -> Option<Token<'static>> {
|
||||
"for" => Token::For,
|
||||
"in" => Token::In,
|
||||
"while" => Token::While,
|
||||
"auto" => Token::Auto,
|
||||
"break" => Token::Break,
|
||||
"continue" => Token::Continue,
|
||||
"return" => Token::Return,
|
||||
@ -759,12 +759,6 @@ mod tests {
|
||||
("for", For),
|
||||
("in", In),
|
||||
("import", Import),
|
||||
("while", While),
|
||||
("break", Break),
|
||||
("continue", Continue),
|
||||
("using", Using),
|
||||
("auto", Auto),
|
||||
("return", Return),
|
||||
];
|
||||
|
||||
for &(s, t) in &list {
|
||||
|
@ -4,7 +4,7 @@ use std::fmt::{self, Arguments, Write};
|
||||
|
||||
use crate::color::{Color, RgbaColor};
|
||||
use crate::eval::*;
|
||||
use crate::geom::{Angle, Fractional, Length, Linear, Relative, TrackSizing};
|
||||
use crate::geom::{Angle, Fractional, Length, Linear, Relative};
|
||||
use crate::syntax::*;
|
||||
|
||||
/// Pretty print an item and return the resulting string.
|
||||
@ -451,24 +451,24 @@ impl Pretty for Ident {
|
||||
impl Pretty for Value {
|
||||
fn pretty(&self, p: &mut Printer) {
|
||||
match self {
|
||||
Value::None => p.push_str("none"),
|
||||
Value::Bool(v) => v.pretty(p),
|
||||
Value::Int(v) => v.pretty(p),
|
||||
Value::Float(v) => v.pretty(p),
|
||||
Value::Length(v) => v.pretty(p),
|
||||
Value::Angle(v) => v.pretty(p),
|
||||
Value::Relative(v) => v.pretty(p),
|
||||
Value::Fractional(v) => v.pretty(p),
|
||||
Value::Linear(v) => v.pretty(p),
|
||||
Value::TrackSizing(v) => v.pretty(p),
|
||||
Value::Color(v) => v.pretty(p),
|
||||
Value::Str(v) => v.pretty(p),
|
||||
Value::Array(v) => v.pretty(p),
|
||||
Value::Dict(v) => v.pretty(p),
|
||||
Value::Template(v) => v.pretty(p),
|
||||
Value::Func(v) => v.pretty(p),
|
||||
Value::Any(v) => v.pretty(p),
|
||||
Value::Error => p.push_str("<error>"),
|
||||
Self::None => p.push_str("none"),
|
||||
Self::Auto => p.push_str("auto"),
|
||||
Self::Bool(v) => v.pretty(p),
|
||||
Self::Int(v) => v.pretty(p),
|
||||
Self::Float(v) => v.pretty(p),
|
||||
Self::Length(v) => v.pretty(p),
|
||||
Self::Angle(v) => v.pretty(p),
|
||||
Self::Relative(v) => v.pretty(p),
|
||||
Self::Linear(v) => v.pretty(p),
|
||||
Self::Fractional(v) => v.pretty(p),
|
||||
Self::Color(v) => v.pretty(p),
|
||||
Self::Str(v) => v.pretty(p),
|
||||
Self::Array(v) => v.pretty(p),
|
||||
Self::Dict(v) => v.pretty(p),
|
||||
Self::Template(v) => v.pretty(p),
|
||||
Self::Func(v) => v.pretty(p),
|
||||
Self::Any(v) => v.pretty(p),
|
||||
Self::Error => p.push_str("<error>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -579,9 +579,8 @@ pretty_display! {
|
||||
Length,
|
||||
Angle,
|
||||
Relative,
|
||||
Fractional,
|
||||
Linear,
|
||||
TrackSizing,
|
||||
Fractional,
|
||||
RgbaColor,
|
||||
Color,
|
||||
AnyValue,
|
||||
|
@ -9,7 +9,7 @@ use crate::geom::{AngularUnit, LengthUnit};
|
||||
pub enum Expr {
|
||||
/// The none literal: `none`.
|
||||
None(Span),
|
||||
/// The `auto` constant.
|
||||
/// The auto literal: `auto`.
|
||||
Auto(Span),
|
||||
/// A boolean literal: `true`, `false`.
|
||||
Bool(Span, bool),
|
||||
|
@ -76,6 +76,8 @@ pub enum Token<'s> {
|
||||
Or,
|
||||
/// The none literal: `none`.
|
||||
None,
|
||||
/// The auto literal: `auto`.
|
||||
Auto,
|
||||
/// The `let` keyword.
|
||||
Let,
|
||||
/// The `if` keyword.
|
||||
@ -100,8 +102,6 @@ pub enum Token<'s> {
|
||||
Include,
|
||||
/// The `using` keyword.
|
||||
Using,
|
||||
/// The `auto` keyword.
|
||||
Auto,
|
||||
/// One or more whitespace characters.
|
||||
///
|
||||
/// The contained `usize` denotes the number of newlines that were contained
|
||||
@ -238,6 +238,7 @@ impl<'s> Token<'s> {
|
||||
Self::And => "operator `and`",
|
||||
Self::Or => "operator `or`",
|
||||
Self::None => "`none`",
|
||||
Self::Auto => "`auto`",
|
||||
Self::Let => "keyword `let`",
|
||||
Self::If => "keyword `if`",
|
||||
Self::Else => "keyword `else`",
|
||||
@ -250,7 +251,6 @@ impl<'s> Token<'s> {
|
||||
Self::Import => "keyword `import`",
|
||||
Self::Include => "keyword `include`",
|
||||
Self::Using => "keyword `using`",
|
||||
Self::Auto => "keyword `auto`",
|
||||
Self::Space(_) => "space",
|
||||
Self::Text(_) => "text",
|
||||
Self::UnicodeEscape(_) => "unicode escape sequence",
|
||||
|
BIN
tests/ref/library/grid-table.png
Normal file
BIN
tests/ref/library/grid-table.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 4.6 KiB |
29
tests/typ/library/grid-table.typ
Normal file
29
tests/typ/library/grid-table.typ
Normal file
@ -0,0 +1,29 @@
|
||||
// Test using the `grid` function to create a finance table.
|
||||
|
||||
---
|
||||
#page(width: 12cm, height: 2.5cm)
|
||||
#grid(
|
||||
columns: 5,
|
||||
gutter-columns: (2fr, 1fr, 1fr),
|
||||
gutter-rows: 4 * (6pt,),
|
||||
[*Quarter*],
|
||||
[Expenditure],
|
||||
[External Revenue],
|
||||
[Financial ROI],
|
||||
[_total_],
|
||||
[*Q1*],
|
||||
[173,472.57 \$],
|
||||
[472,860.91 \$],
|
||||
[51,286.84 \$],
|
||||
[_350,675.18 \$_],
|
||||
[*Q2*],
|
||||
[93,382.12 \$],
|
||||
[439,382.85 \$],
|
||||
[-1,134.30 \$],
|
||||
[_344,866.43 \$_],
|
||||
[*Q3*],
|
||||
[96,421.49 \$],
|
||||
[238,583.54 \$],
|
||||
[3,497.12 \$],
|
||||
[_145,659.17 \$_],
|
||||
)
|
@ -1,8 +1,9 @@
|
||||
// Test the `grid` function.
|
||||
|
||||
---
|
||||
#page(width: 100pt, height: 140pt)
|
||||
#let rect(width, color) = rect(width: width, height: 2cm, fill: color)
|
||||
|
||||
#page(width: 100pt, height: 140pt)
|
||||
#grid(
|
||||
columns: (auto, 1fr, 3fr, 0.25cm, 3%, 2mm + 10%),
|
||||
rect(0.5cm, #2a631a),
|
||||
@ -22,7 +23,6 @@
|
||||
#grid()
|
||||
|
||||
---
|
||||
|
||||
#grid(
|
||||
columns: (auto, auto, 40%),
|
||||
gutter: (1fr,),
|
||||
@ -31,49 +31,11 @@
|
||||
rect(width: 100%, fill: #dddddd)[aaa],
|
||||
)
|
||||
|
||||
#grid(
|
||||
columns: (auto, auto, 40%),
|
||||
gutter: (1fr,),
|
||||
rect(fill: eastern)[dddaa aaa aaa],
|
||||
rect(fill: conifer)[ccc],
|
||||
rect(width: 100%, fill: #dddddd)[aaa],
|
||||
)
|
||||
|
||||
|
||||
---
|
||||
|
||||
#page(width: 12cm, height: 2.5cm)
|
||||
#grid(
|
||||
columns: (auto, auto, auto, auto, auto),
|
||||
gutter-col: (2fr, 1fr, 1fr),
|
||||
gutter-row: (6pt, 6pt, 6pt, 6pt),
|
||||
[*Quarter*],
|
||||
[Expenditure],
|
||||
[External Revenue],
|
||||
[Financial ROI],
|
||||
[_total_],
|
||||
[*Q1*],
|
||||
[173,472.57 \$],
|
||||
[472,860.91 \$],
|
||||
[51,286.84 \$],
|
||||
[_350,675.18 \$_],
|
||||
[*Q2*],
|
||||
[93,382.12 \$],
|
||||
[439,382.85 \$],
|
||||
[-1,134.30 \$],
|
||||
[_344,866.43 \$_],
|
||||
[*Q3*],
|
||||
[96,421.49 \$],
|
||||
[238,583.54 \$],
|
||||
[3,497.12 \$],
|
||||
[_145,659.17 \$_],
|
||||
)
|
||||
|
||||
---
|
||||
#page(height: 3cm, width: 2cm)
|
||||
#grid(
|
||||
dir: ttb,
|
||||
columns: (1fr, 1cm, 1fr, 1fr),
|
||||
column-dir: ttb,
|
||||
rows: (auto, 1fr),
|
||||
rect(height: 100%, fill: #222222)[foo],
|
||||
rect(height: 100%, fill: #547d0a)[bar],
|
||||
@ -81,3 +43,12 @@
|
||||
rect(height: 100%, fill: conifer)[baz],
|
||||
rect(height: 100%, width: 100%, fill: #547d0a)[bar],
|
||||
)
|
||||
|
||||
---
|
||||
#page(height: 3cm, margins: 0pt)
|
||||
#align(center)
|
||||
#grid(
|
||||
columns: (1fr,),
|
||||
rows: (1fr, auto, 2fr),
|
||||
[], rect(width: 100%)[A bit more to the top], [],
|
||||
)
|
||||
|
@ -259,6 +259,10 @@
|
||||
"name": "constant.language.none.typst",
|
||||
"match": "\\bnone\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.auto.typst",
|
||||
"match": "\\bauto\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.boolean.typst",
|
||||
"match": "\\b(true|false)\\b"
|
||||
|
Loading…
x
Reference in New Issue
Block a user