Skip to main content

Crate ctl_layout

Crate ctl_layout 

Source
Expand description

Terminal widgets built on ctl_text: tables, records, rules, stacks, indents, and a way to swap one widget for another by width.

Every widget implements Renderable. It first measures: the narrowest width it can be drawn at without breaking a word it keeps whole, and its natural width. Then it renders lines at the width it is given.

A container measures its children, decides their widths, and renders each child at its own. The result is plain lines of styled spans with no escape sequence in them. Whatever writes the lines to a terminal adds the styling.

The crate re-exports ctl_text and the four types every widget uses: Context, Overflow, Renderable, and Text.

§Quick start

A Table gives each column the widest cell it holds, and shrinks the widest columns first when the width runs short. A column sets what happens to a cell that does not fit, and which edge its cells sit against:

use ctl_layout::{Column, Context, Frame, Overflow, Renderable, Table};

let table = Table::new([
    Column::new("id"),
    Column::new("title").overflow(Overflow::Ellipsis),
    Column::new("age").numeric(),
])
.frame(Frame::HeaderRule)
.row(["job-0014", "Fix the layout of narrow tables", "3"])
.row(["job-0015", "Document each public item", "120"]);

let lines: Vec<String> = table
    .render(&Context::new(40))
    .iter()
    .map(ToString::to_string)
    .collect();
assert_eq!(
    lines,
    [
        "id        title                      age",
        "────────────────────────────────────────",
        "job-0014  Fix the layout of narrow…    3",
        "job-0015  Document each public item  120",
    ]
);

A Record draws labelled values, a Rule draws a line with an optional title, a Stack puts widgets one above the other, and an Indent moves one right. A stack and an indent take a widget by value or by reference:

use ctl_layout::{Context, Indent, Record, Renderable, Rule, Stack, Text};

let screen = Stack::new()
    .spacing(1)
    .push(Rule::titled(Text::plain("Release")))
    .push(Indent::new(
        2,
        Record::new()
            .row("version", "1.4.0")
            .row("channel", "stable"),
    ));

let lines: Vec<String> = screen
    .render(&Context::new(24))
    .iter()
    .map(ToString::to_string)
    .collect();
assert_eq!(
    lines,
    [
        "── Release ─────────────",
        "",
        "  version  1.4.0",
        "  channel  stable",
    ]
);

A Breakpoint draws Definitions where a table no longer fits, so a narrow terminal gets one line of flags over an indented description:

use ctl_layout::{Breakpoint, Column, Context, Definitions, Frame, Renderable, Table};

let flags = Breakpoint::new(
    40,
    Definitions::new()
        .entry(["-v", "--verbose"], ["Print more detail"])
        .entry(["-q", "--quiet"], ["Print nothing on success"]),
    Table::new([Column::untitled(), Column::untitled(), Column::untitled()])
        .frame(Frame::Plain)
        .row(["-v", "--verbose", "Print more detail"])
        .row(["-q", "--quiet", "Print nothing on success"]),
);

let at = |width| -> Vec<String> {
    flags
        .render(&Context::new(width))
        .iter()
        .map(ToString::to_string)
        .collect()
};
assert_eq!(
    at(50),
    [
        "-v  --verbose  Print more detail",
        "-q  --quiet    Print nothing on success",
    ]
);
assert_eq!(
    at(30),
    [
        "  -v --verbose",
        "    Print more detail",
        "  -q --quiet",
        "    Print nothing on success",
    ]
);

A widget of your own sits in a Stack beside these once it implements Renderable. Its Measurement and Line types come through ctl_layout::ctl_text.

§Widgets

  • Table: rows under column headers. A Column sets the header, the overflow policy, and how its cells are aligned (Align). A Frame and a RowSeparation set the rules between them.
  • Record: labelled values, drawn in a RecordStyle.
  • Definitions: entries as a line of labels over indented description lines, the narrow form of a table.
  • Breakpoint: one widget below a width and another from it up.
  • Rule: a horizontal line with an optional title.
  • Stack: widgets one above the other.
  • Indent: a widget moved right.

§Cargo features

This crate has no optional features.

Re-exports§

pub use ctl_text;

Structs§

Breakpoint
One widget below a width and another from it up.
Column
A column of a table: its header, what it does with a cell wider than it gets, and which edge its cells sit against.
Context
What a widget is told when it measures and renders: the width to fill, the tab stop spacing, and the box characters.
Definitions
Entries, each a line of labels over indented lines of description.
Indent
A widget moved right by a number of cells.
Record
Labelled values, one pair to a row.
Rule
A rule across the whole width, drawn with ─, or - for BoxChars::Ascii.
Stack
Widgets one above the other, each rendered at the full width.
Table
A table of rows under column headers.
Text
Styled text of any length: a paragraph, a table cell, a title.

Enums§

Align
Which edge of its column a cell’s text sits against.
Frame
What a table draws around and between its columns.
Overflow
What happens to a line wider than the width it is given.
RecordStyle
How a Record of key and value rows is drawn.
RowSeparation
What separates consecutive rows of a table.

Traits§

Renderable
Content that knows its own size and can draw itself at a width.