// Port of rich/control.py.
///|
fn format_control_code(code : ControlCode) -> String {
match code {
Code(Bell) => "\u{07}"
Code(CarriageReturn) => "\r"
Code(Home) => "\u{1b}[H"
Code(Clear) => "\u{1b}[2J"
Code(EnableAltScreen) => "\u{1b}[?1049h"
Code(DisableAltScreen) => "\u{1b}[?1049l"
Code(ShowCursor) => "\u{1b}[?25h"
Code(HideCursor) => "\u{1b}[?25l"
CodeInt(CursorUp, n) => "\u{1b}[\{n}A"
CodeInt(CursorDown, n) => "\u{1b}[\{n}B"
CodeInt(CursorForward, n) => "\u{1b}[\{n}C"
CodeInt(CursorBackward, n) => "\u{1b}[\{n}D"
CodeInt(CursorMoveToColumn, n) => "\u{1b}[\{n + 1}G"
CodeInt(EraseInLine, n) => "\u{1b}[\{n}K"
CodeXY(CursorMoveTo, x, y) => "\u{1b}[\{y + 1};\{x + 1}H"
CodeStr(SetWindowTitle, title) => "\u{1b}]0;\{title}\u{07}"
_ => abort("invalid control code")
}
}
///|
/// A renderable that inserts a control code (non printable but may move
/// cursor).
pub struct Control {
segment : Segment
}
///|
pub fn Control::new(codes : Array[ControlCode]) -> Control {
let rendered = codes.map(format_control_code).join("")
{ segment: { text: rendered, style: None, control: Some(codes), }, }
}
///|
/// Ring the 'bell'.
pub fn Control::bell() -> Control {
Control::new([Code(Bell)])
}
///|
/// Move cursor to 'home' position.
pub fn Control::home() -> Control {
Control::new([Code(Home)])
}
///|
/// Move cursor relative to current position.
pub fn Control::move_(x? : Int = 0, y? : Int = 0) -> Control {
let codes = []
if x != 0 {
codes.push(
CodeInt(if x > 0 { CursorForward } else { CursorBackward }, x.abs()),
)
}
if y != 0 {
codes.push(CodeInt(if y > 0 { CursorDown } else { CursorUp }, y.abs()))
}
Control::new(codes)
}
///|
/// Move to the given column, optionally add offset to row.
pub fn Control::move_to_column(x : Int, y? : Int = 0) -> Control {
if y != 0 {
Control::new([
CodeInt(CursorMoveToColumn, x),
CodeInt(if y > 0 { CursorDown } else { CursorUp }, y.abs()),
])
} else {
Control::new([CodeInt(CursorMoveToColumn, x)])
}
}
///|
/// Move cursor to absolute position.
pub fn Control::move_to(x : Int, y : Int) -> Control {
Control::new([CodeXY(CursorMoveTo, x, y)])
}
///|
/// Clear the screen.
pub fn Control::clear() -> Control {
Control::new([Code(Clear)])
}
///|
/// Show or hide the cursor.
pub fn Control::show_cursor(show : Bool) -> Control {
Control::new([Code(if show { ShowCursor } else { HideCursor })])
}
///|
/// Enable or disable alt screen.
pub fn Control::alt_screen(enable : Bool) -> Control {
if enable {
Control::new([Code(EnableAltScreen), Code(Home)])
} else {
Control::new([Code(DisableAltScreen)])
}
}
///|
/// Set the terminal window title.
pub fn Control::title(title : String) -> Control {
Control::new([CodeStr(SetWindowTitle, title)])
}
///|
pub impl Show for Control with fn output(self, logger) {
logger.write_string(self.segment.text)
}
///|
pub impl Renderable for Control with fn rich_console(self, _console, _options) {
if self.segment.text != "" {
[Seg(self.segment)]
} else {
[]
}
}