1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
use super::*;
/// `align`: Align content along the layouting axes.
///
/// # Positional arguments
/// - At most two of `left`, `right`, `top`, `bottom`, `center`.
///
/// When `center` is used as a positional argument, it is automatically inferred
/// which axis it should apply to depending on further arguments, defaulting
/// to the axis, text is set along.
///
/// # Keyword arguments
/// - `horizontal`: Any of `left`, `right` or `center`.
/// - `vertical`: Any of `top`, `bottom` or `center`.
///
/// There may not be two alignment specifications for the same axis.
pub async fn align(_: Span, mut args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
let mut f = Feedback::new();
let content = args.take::<SyntaxTree>();
let h = args.take_key::<Spanned<SpecAlign>>("horizontal", &mut f);
let v = args.take_key::<Spanned<SpecAlign>>("vertical", &mut f);
let all = args
.take_all_num_vals::<Spanned<SpecAlign>>()
.map(|align| (align.v.axis(), align))
.chain(h.into_iter().map(|align| (Some(Horizontal), align)))
.chain(v.into_iter().map(|align| (Some(Vertical), align)));
let mut aligns = ctx.align;
let mut had = [false; 2];
let mut deferred_center = false;
for (axis, align) in all {
// Check whether we know which axis this alignment belongs to. We don't
// if the alignment is `center` for a positional argument. Then we set
// `deferred_center` to true and handle the situation once we know more.
if let Some(axis) = axis {
if align.v.axis().map(|a| a != axis).unwrap_or(false) {
error!(
@f, align.span,
"invalid alignment {} for {} axis", align.v, axis,
);
} else if had[axis as usize] {
error!(@f, align.span, "duplicate alignment for {} axis", axis);
} else {
let gen_align = align.v.to_generic(ctx.axes);
*aligns.get_mut(axis.to_generic(ctx.axes)) = gen_align;
had[axis as usize] = true;
}
} else {
if had == [true, true] {
error!(@f, align.span, "duplicate alignment");
} else if deferred_center {
// We have two unflushed centers, meaning we know that both axes
// are to be centered.
had = [true, true];
aligns = LayoutAlign::new(Center, Center);
} else {
deferred_center = true;
}
}
// Flush a deferred center alignment if we know have had at least one
// known alignment.
if deferred_center && had != [false, false] {
let axis = if !had[Horizontal as usize] {
Horizontal
} else {
Vertical
};
*aligns.get_mut(axis.to_generic(ctx.axes)) = Center;
had[axis as usize] = true;
deferred_center = false;
}
}
// If center has not been flushed by known, it is the only argument and then
// we default to applying it to the primary axis.
if deferred_center {
aligns.primary = Center;
}
let commands = match content {
Some(tree) => vec![
SetAlignment(aligns),
LayoutSyntaxTree(tree),
SetAlignment(ctx.align),
],
None => vec![SetAlignment(aligns)],
};
args.unexpected(&mut f);
Pass::commands(commands, f)
}
|