// Port of rich/align.py, rich/styled.py, rich/constrain.py,
// rich/region.py, rich/screen.py and rich/pager.py.
///|
/// Align a renderable by adding spaces if necessary.
pub struct Align {
renderable : &Renderable
align : AlignMethod
style : StyleType?
vertical : VerticalAlignMethod?
pad : Bool
width : Int?
height : Int?
}
///|
pub fn Align::new(
renderable : &Renderable,
align : AlignMethod,
style? : &IntoStyle,
vertical? : VerticalAlignMethod,
pad? : Bool = true,
width? : Int,
height? : Int,
) -> Align {
{ renderable, align, style: style_opt(style), vertical, pad, width, height, }
}
///|
/// Align a renderable to the left.
pub fn Align::left(
renderable : &Renderable,
style? : &IntoStyle,
vertical? : VerticalAlignMethod,
pad? : Bool = true,
width? : Int,
height? : Int,
) -> Align {
Align::new(renderable, Left, style?, vertical?, pad~, width?, height?)
}
///|
/// Align a renderable to the center.
pub fn Align::center(
renderable : &Renderable,
style? : &IntoStyle,
vertical? : VerticalAlignMethod,
pad? : Bool = true,
width? : Int,
height? : Int,
) -> Align {
Align::new(renderable, Center, style?, vertical?, pad~, width?, height?)
}
///|
/// Align a renderable to the right.
pub fn Align::right(
renderable : &Renderable,
style? : &IntoStyle,
vertical? : VerticalAlignMethod,
pad? : Bool = true,
width? : Int,
height? : Int,
) -> Align {
Align::new(renderable, Right, style?, vertical?, pad~, width?, height?)
}
///|
pub impl Renderable for Align with fn rich_console(self, console, options) {
let align = self.align
let width = console.measure(self.renderable, options~).maximum
let rendered = console.render(
Constrain::new(
self.renderable,
width=Some(
match self.width {
None => width
Some(w) => @cmp.minimum(width, w)
},
),
),
options=options.update(height=None),
)
let lines = Segment::split_lines(rendered)
let (width, height) = Segment::get_shape(lines)
let lines = Segment::set_shape(lines, width, height~)
let new_line = Segment::line()
let excess_space = options.max_width - width
let style = match self.style {
Some(s) => Some(console.get_style(s))
None => None
}
fn generate_segments() -> Array[Segment] {
let out = []
if excess_space <= 0 {
for line in lines {
out.append(line)
out.push(new_line)
}
} else {
match align {
Left => {
let pad = Segment::new(space_string(excess_space), style?)
for line in lines {
out.append(line)
if self.pad {
out.push(pad)
}
out.push(new_line)
}
}
Center => {
let left = floor_div(excess_space, 2)
let pad = Segment::new(space_string(left), style?)
let pad_right = Segment::new(
space_string(excess_space - left),
style?,
)
for line in lines {
if left != 0 {
out.push(pad)
}
out.append(line)
if self.pad {
out.push(pad_right)
}
out.push(new_line)
}
}
Right => {
let pad = Segment::new(space_string(excess_space), style?)
for line in lines {
out.push(pad)
out.append(line)
out.push(new_line)
}
}
}
}
out
}
let blank_width = match self.width {
Some(w) if w != 0 => w
_ => options.max_width
}
let blank_line = if self.pad {
Segment::new(space_string(blank_width) + "\n", style?)
} else {
Segment::new("\n")
}
fn blank_lines(count : Int) -> Array[Segment] {
let out = []
for _ in 0.. Some(h)
_ => options.height
}
let mut segments = match (self.vertical, vertical_height) {
(Some(vertical), Some(vertical_height)) =>
match vertical {
Top => generate_segments() + blank_lines(vertical_height - height)
Middle => {
let top_space = floor_div(vertical_height - height, 2)
let bottom_space = vertical_height - top_space - height
blank_lines(top_space) +
generate_segments() +
blank_lines(bottom_space)
}
Bottom => blank_lines(vertical_height - height) + generate_segments()
}
_ => generate_segments()
}
if self.style is Some(s) && s.to_bool() {
let style = console.get_style(s)
segments = Segment::apply_style(segments, style~)
}
segments_to_items(segments)
}
///|
pub impl Renderable for Align with fn rich_measure(self, console, options) {
Some(Measurement::get(console, options, self.renderable))
}
///|
pub impl Renderable for Align with fn vertical_align(self) {
self.vertical
}
///|
/// Vertically aligns a renderable.
pub struct VerticalCenter {
renderable : &Renderable
style : StyleType?
}
///|
pub fn VerticalCenter::new(
renderable : &Renderable,
style? : &IntoStyle,
) -> VerticalCenter {
{ renderable, style: style_opt(style), }
}
///|
pub impl Renderable for VerticalCenter with fn rich_console(
self,
console,
options,
) {
let style = match self.style {
Some(s) => Some(console.get_style(s))
None => None
}
let lines = console.render_lines(
self.renderable,
options=options.update(height=None),
pad=false,
)
let (width, _) = Segment::get_shape(lines)
let new_line = Segment::line()
let height = match options.height {
Some(h) if h != 0 => h
_ => options.size.height
}
let top_space = floor_div(height - lines.length(), 2)
let bottom_space = height - top_space - lines.length()
let blank_line = Segment::new(space_string(width), style?)
let out = []
for _ in 0.. Styled {
{ renderable, style: style.into_style_type(), }
}
///|
pub impl Renderable for Styled with fn rich_console(self, console, options) {
let style = console.get_style(self.style)
let rendered = console.render(self.renderable, options~)
segments_to_items(Segment::apply_style(rendered, style~))
}
///|
pub impl Renderable for Styled with fn rich_measure(self, console, options) {
Some(Measurement::get(console, options, self.renderable))
}
///|
/// Constrain the width of a renderable to a given number of characters.
pub struct Constrain {
renderable : &Renderable
width : Int?
}
///|
pub fn Constrain::new(
renderable : &Renderable,
width? : Int? = Some(80),
) -> Constrain {
{ renderable, width, }
}
///|
pub impl Renderable for Constrain with fn rich_console(self, console, options) {
match self.width {
None => [Child(self.renderable)]
Some(w) => {
let child_options = options.update_width(
@cmp.minimum(w, options.max_width),
)
segments_to_items(console.render(self.renderable, options=child_options))
}
}
}
///|
pub impl Renderable for Constrain with fn rich_measure(self, console, options) {
let options = match self.width {
Some(w) => options.update_width(w)
None => options
}
Some(Measurement::get(console, options, self.renderable))
}
///|
/// Defines a rectangular region of the screen.
pub(all) struct Region {
x : Int
y : Int
width : Int
height : Int
} derive(Eq, Debug)
///|
/// A renderable that fills the terminal screen and crops excess.
pub struct Screen {
mut renderable : &Renderable
mut style : StyleType?
application_mode : Bool
}
///|
pub fn Screen::new(
renderables : Array[&Renderable],
style? : &IntoStyle,
application_mode? : Bool = false,
) -> Screen {
{
renderable: Group::new(renderables),
style: style_opt(style),
application_mode,
}
}
///|
pub impl Renderable for Screen with fn rich_console(self, console, options) {
let { width, height, } = options.size
let style = match self.style {
Some(s) if s.to_bool() => Some(console.get_style(s))
_ => None
}
let render_options = options.update(width~, height=Some(height))
let lines = console.render_lines(
self.renderable,
options=render_options,
style?,
pad=true,
)
let lines = Segment::set_shape(lines, width, height~, style?)
let new_line = if self.application_mode {
Segment::new("\n\r")
} else {
Segment::line()
}
let out = []
for i, line in lines {
for s in line {
out.push(Seg(s))
}
if i != lines.length() - 1 {
out.push(Seg(new_line))
}
}
out
}
///|
/// Base trait for a pager.
pub(open) trait Pager {
fn show(Self, String) -> Unit raise
}
///|
/// Uses the pager installed on the system (`$PAGER`, or `less -r`).
pub struct SystemPager {
command : String?
}
///|
pub fn SystemPager::new(command? : String) -> SystemPager {
{ command, }
}
///|
pub impl Pager for SystemPager with fn show(self, content) {
let command = match self.command {
Some(c) => c
None =>
match @env.get_env_var("PAGER") {
Some(p) if p != "" => p
_ => "less -r"
}
}
if !@term.pipe_to(command, content) {
// no pager available: print directly
if !@term.write_fd(1, content) {
raise WriteError("unable to run pager")
}
}
}