ctl_layout/lib.rs
1//! Terminal widgets built on [`ctl_text`]: tables, records, rules, stacks,
2//! indents, and a way to swap one widget for another by width.
3//!
4//! Every widget implements [`Renderable`]. It first *measures*: the narrowest
5//! width it can be drawn at without breaking a word it keeps whole, and its
6//! natural width. Then it *renders* lines at the width it is given.
7//!
8//! A container measures its children, decides their widths, and renders each
9//! child at its own. The result is plain lines of styled spans with no escape
10//! sequence in them. Whatever writes the lines to a terminal adds the styling.
11//!
12//! The crate re-exports [`ctl_text`] and the four types every widget uses:
13//! [`Context`], [`Overflow`], [`Renderable`], and [`Text`].
14//!
15//! ## Quick start
16//!
17//! A [`Table`] gives each column the widest cell it holds, and shrinks the
18//! widest columns first when the width runs short. A column sets what
19//! happens to a cell that does not fit, and which edge its cells sit against:
20//!
21//! ```
22//! use ctl_layout::{Column, Context, Frame, Overflow, Renderable, Table};
23//!
24//! let table = Table::new([
25//! Column::new("id"),
26//! Column::new("title").overflow(Overflow::Ellipsis),
27//! Column::new("age").numeric(),
28//! ])
29//! .frame(Frame::HeaderRule)
30//! .row(["job-0014", "Fix the layout of narrow tables", "3"])
31//! .row(["job-0015", "Document each public item", "120"]);
32//!
33//! let lines: Vec<String> = table
34//! .render(&Context::new(40))
35//! .iter()
36//! .map(ToString::to_string)
37//! .collect();
38//! assert_eq!(
39//! lines,
40//! [
41//! "id title age",
42//! "────────────────────────────────────────",
43//! "job-0014 Fix the layout of narrow… 3",
44//! "job-0015 Document each public item 120",
45//! ]
46//! );
47//! ```
48//!
49//! A [`Record`] draws labelled values, a [`Rule`] draws a line with an optional
50//! title, a [`Stack`] puts widgets one above the other, and an [`Indent`] moves
51//! one right. A stack and an indent take a widget by value or by reference:
52//!
53//! ```
54//! use ctl_layout::{Context, Indent, Record, Renderable, Rule, Stack, Text};
55//!
56//! let screen = Stack::new()
57//! .spacing(1)
58//! .push(Rule::titled(Text::plain("Release")))
59//! .push(Indent::new(
60//! 2,
61//! Record::new()
62//! .row("version", "1.4.0")
63//! .row("channel", "stable"),
64//! ));
65//!
66//! let lines: Vec<String> = screen
67//! .render(&Context::new(24))
68//! .iter()
69//! .map(ToString::to_string)
70//! .collect();
71//! assert_eq!(
72//! lines,
73//! [
74//! "── Release ─────────────",
75//! "",
76//! " version 1.4.0",
77//! " channel stable",
78//! ]
79//! );
80//! ```
81//!
82//! A [`Breakpoint`] draws [`Definitions`] where a table no longer fits, so a
83//! narrow terminal gets one line of flags over an indented description:
84//!
85//! ```
86//! use ctl_layout::{Breakpoint, Column, Context, Definitions, Frame, Renderable, Table};
87//!
88//! let flags = Breakpoint::new(
89//! 40,
90//! Definitions::new()
91//! .entry(["-v", "--verbose"], ["Print more detail"])
92//! .entry(["-q", "--quiet"], ["Print nothing on success"]),
93//! Table::new([Column::untitled(), Column::untitled(), Column::untitled()])
94//! .frame(Frame::Plain)
95//! .row(["-v", "--verbose", "Print more detail"])
96//! .row(["-q", "--quiet", "Print nothing on success"]),
97//! );
98//!
99//! let at = |width| -> Vec<String> {
100//! flags
101//! .render(&Context::new(width))
102//! .iter()
103//! .map(ToString::to_string)
104//! .collect()
105//! };
106//! assert_eq!(
107//! at(50),
108//! [
109//! "-v --verbose Print more detail",
110//! "-q --quiet Print nothing on success",
111//! ]
112//! );
113//! assert_eq!(
114//! at(30),
115//! [
116//! " -v --verbose",
117//! " Print more detail",
118//! " -q --quiet",
119//! " Print nothing on success",
120//! ]
121//! );
122//! ```
123//!
124//! A widget of your own sits in a [`Stack`] beside these once it implements
125//! [`Renderable`]. Its [`Measurement`](ctl_text::Measurement) and
126//! [`Line`](ctl_text::Line) types come through
127//! [`ctl_layout::ctl_text`](ctl_text).
128//!
129//! ## Widgets
130//!
131//! - [`Table`]: rows under column headers. A [`Column`] sets the header, the
132//! overflow policy, and how its cells are aligned ([`Align`]). A [`Frame`]
133//! and a [`RowSeparation`] set the rules between them.
134//! - [`Record`]: labelled values, drawn in a [`RecordStyle`].
135//! - [`Definitions`]: entries as a line of labels over indented description
136//! lines, the narrow form of a table.
137//! - [`Breakpoint`]: one widget below a width and another from it up.
138//! - [`Rule`]: a horizontal line with an optional title.
139//! - [`Stack`]: widgets one above the other.
140//! - [`Indent`]: a widget moved right.
141//!
142//! ## Cargo features
143//!
144//! This crate has no optional features.
145
146mod align;
147mod breakpoint;
148mod column;
149mod definitions;
150mod frame;
151mod glyphs;
152mod indent;
153mod lines;
154mod record;
155mod rule;
156mod stack;
157mod table;
158mod widths;
159
160pub use align::Align;
161pub use breakpoint::Breakpoint;
162pub use column::Column;
163#[doc(no_inline)]
164pub use ctl_text;
165pub use ctl_text::{Context, Overflow, Renderable, Text};
166pub use definitions::Definitions;
167pub use frame::{Frame, RecordStyle, RowSeparation};
168pub use indent::Indent;
169pub use record::Record;
170pub use rule::Rule;
171pub use stack::Stack;
172pub use table::Table;
173
174/// The README's examples, compiled and run as doctests.
175#[doc = include_str!("../README.md")]
176#[cfg(doctest)]
177pub struct ReadmeDoctests;