Skip to main content

omp_tui/components/
col.rs

1use super::layout::{stack_height, stack_measure, stack_place};
2use crate::{
3	component::{Cached, Component, IntoChildren, PaintCtx, ResizeTail, Slot, next_slot},
4	context::UiContext,
5	frame::Rect,
6	props::{Prop, PropValue, Props},
7};
8
9/// A vertical child stack backing the `<col>` markup tag.
10pub struct Col {
11	props:    Props,
12	slot:     Slot,
13	children: Vec<Cached>,
14}
15
16impl Col {
17	/// Creates an empty column.
18	pub fn new() -> Self {
19		Self { props: Props::new(), slot: next_slot(), children: Vec::new() }
20	}
21
22	/// Sets one column property.
23	pub fn with(mut self, prop: Prop, value: impl Into<PropValue>) -> Self {
24		self.props.set(prop, value);
25		self
26	}
27
28	/// Sets one column property from a string.
29	pub fn with_str(mut self, prop: Prop, value: &str) -> Self {
30		self.props.set(prop, value);
31		self
32	}
33
34	/// Appends child components to the column.
35	pub fn child(mut self, children: impl IntoChildren) -> Self {
36		children.extend_children(&mut self.children);
37		self
38	}
39}
40
41impl Default for Col {
42	fn default() -> Self {
43		Self::new()
44	}
45}
46
47impl Component for Col {
48	fn props(&self) -> &Props {
49		&self.props
50	}
51
52	fn props_mut(&mut self) -> &mut Props {
53		&mut self.props
54	}
55
56	fn slot(&self) -> Slot {
57		self.slot
58	}
59
60	fn children(&self) -> &[Cached] {
61		&self.children
62	}
63
64	fn children_mut(&mut self) -> &mut [Cached] {
65		&mut self.children
66	}
67
68	fn resize_tail(&mut self) -> Option<ResizeTail<'_>> {
69		// Only a chrome-neutral flow decomposes into drag-frame tails;
70		// styled columns render whole so borders, backgrounds, and
71		// alignment keep full fidelity during a resize.
72		if self.props.border().is_some()
73			|| self.props.valign().is_some()
74			|| self.props.get(Prop::Bg).is_some()
75			|| self.props.get(Prop::On).is_some()
76		{
77			return None;
78		}
79		Some(ResizeTail { children: &mut self.children, gap: self.props.gap() })
80	}
81
82	fn measure(&mut self, ctx: &UiContext) -> (u16, u16) {
83		stack_measure(ctx, &mut self.children)
84	}
85
86	fn height(&mut self, ctx: &UiContext, width: u16) -> u16 {
87		stack_height(ctx, &mut self.children, width, self.props.gap())
88	}
89
90	fn place(&mut self, ctx: &UiContext, content: Rect) {
91		stack_place(
92			ctx,
93			&mut self.children,
94			content,
95			self.props.gap(),
96			self.props.valign(),
97			self.props.align(),
98		);
99	}
100
101	fn paint(&mut self, pc: &mut PaintCtx<'_>, _rect: Rect) {
102		for child in self.children.iter_mut().filter(|child| child.visible) {
103			child.paint(pc);
104		}
105	}
106}
107
108#[cfg(test)]
109mod tests {
110	use super::Col;
111	use crate::{
112		component::{Cached, PaintCtx},
113		components::TextLeaf,
114		context::UiContext,
115		frame::{Frame, Rect, Size},
116		props::Prop,
117		test_support::frame_row_text,
118	};
119
120	#[test]
121	fn stacks_children_with_gap() {
122		let ctx = UiContext::default();
123		let mut root = Cached::new(Box::new(
124			Col::new()
125				.with(Prop::Gap, 1_u16)
126				.child(TextLeaf::new().text("first"))
127				.child(TextLeaf::new().text("second")),
128		));
129		let height = root.height(&ctx, 12);
130		assert_eq!(height, 3);
131		root.place(&ctx, Rect::new(0, 0, 12, height));
132		let mut frame = Frame::new(Size::new(12, height));
133		let mut hits = Vec::new();
134		root.paint(&mut PaintCtx::new(&mut frame, &ctx, &mut hits, &mut Vec::new()));
135		assert_eq!(frame_row_text(&frame, 0), "first");
136		assert_eq!(frame_row_text(&frame, 1), "");
137		assert_eq!(frame_row_text(&frame, 2), "second");
138	}
139}