Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Browser Widget

The browser widget provides a specialized interface for browsing and interacting with netidx hierarchies. It displays a table of one level’s children, one column per published column when the children are rows of a table, with selection and cursor movement support.

Interface

use super::{Constraint, Flex, line, Size, Style, style, Tui};
use sys::net;
use tui::table::{cell, row, table};

type MoveCursor = [
    `Left(i64),
    `Right(i64),
    `Up(i64),
    `Down(i64)
];

/// A netidx browser over `path`: a table of its children, with one
/// column per published column when the children are rows of a table.
/// The listing refreshes when `path` changes and about once a second,
/// so rows published later appear on the next refresh.
/// `#size` is the area the browser is drawn in, which sizes its
/// viewport (pass the enclosing block's `#size` output); `#cursor`
/// moves the selection. `#selected_row` is set to the selected row's
/// full path, and `#selected_path` to the selected cell's; each is null
/// while the listing is empty, and neither is set while a new path's
/// listing is on its way.
val browser: fn(
    ?#selected_style: Style,
    ?#header_style: Style,
    ?#style: Style,
    ?#cursor: MoveCursor,
    ?#selected_row: &mut [string, null],
    ?#selected_path: &mut [string, null],
    ?#flex: Flex,
    ?#rate: duration,
    #size: Size,
    path: string
) -> Tui;

Parameters

  • path - The netidx path whose children are listed. The listing refreshes when path changes and about once a second, so rows published later appear on the next refresh
  • cursor - Programmatic cursor movement: Left(n), Right(n), Up(n), Down(n)
  • selected_row (output) - Full path of the selected row (e.g. /t/r1), null while the listing is empty
  • selected_path (output) - Full path of the selected cell: the row in a list, the row and column in a table; null while the listing is empty
  • size (input) - The area the browser is drawn in, which sizes its viewport; pass the enclosing block’s #size output
  • rate - The most often a cell’s subscription updates the display (default duration:0.5s)
  • flex - How the columns share leftover width (default `SpaceBetween)
  • style, header_style, selected_style - Styles for the table, its header row and the selected cell

Examples

Basic Usage

use tui::{Size, block::block, browser::browser};

let selected_path: [string, null] = never();
let size: Size = never();

block(
    #border: &`None,
    #size: &mut size,
    &browser(
        #size,
        #selected_path: &mut selected_path,
        // Start browsing from root
        "/"
    )
)

Basic Browser Widget

Basic Navigation

use tui::{
  Size,
  block::block,
  browser::{MoveCursor, browser},
  input_handler::{Event, input_handler, on_press}
};

let path = "/";
let selected_path: [string, null] = never();
let selected_row: [string, null] = never();
let cursor: MoveCursor = never();
let size: Size = never();

let handle_event = |e: Event| on_press(e, |k| select k.code {
    e@ `Up => { cursor <- e ~ `Up(1); `Stop },
    e@ `Down => { cursor <- e ~ `Down(1); `Stop },
    e@ `Left => { cursor <- e ~ `Left(1); `Stop },
    e@ `Right => { cursor <- e ~ `Right(1); `Stop },
    e@ `Enter => { path <- (e ~ selected_row)$; `Stop },
    _ => `Continue
});

input_handler(
    #handle: &handle_event,
    &block(
        #border: &`None,
        #size: &mut size,
        &browser(
            #size,
            #cursor,
            #selected_path: &mut selected_path,
            #selected_row: &mut selected_row,
            path
        )
    )
)

Browser With Navigation

Commands

A : command line beside the browser: :w <value> writes the value to the selected path, and an error from the command shows in the bottom title.

use tui::{
  Size, line, span, style,
  block::block,
  browser::{MoveCursor, browser},
  input_handler::{Event, KeyCode, input_handler, on_press}
};

let path = "/";
let command_error = "";
let selected_path: [string, null] = never();
let selected_row: [string, null] = never();
let cursor: MoveCursor = never();
let command_mode = false;
let command = "";

let run_command = |c: string, path: string| {
    catch(e) command_error <- "[(e.0).error]";
    select str::sub(#start:0, #len:2, c)? {
        "w " => {
            let v = str::sub(#start:2, #len:str::char_len(c) - 2, c)?;
            let v: Primitive = str::parse(v)?;
            sys::net::write(path, v)?
        },
        _ => error("invalid command [c]")?
    }
};

let title_style = style(#fg:`Indexed(202));
let title_bottom = select command_mode {
    true => line(#style:title_style, ":[command]"),
    false => {
        let spans = [
            span(select selected_path { null as _ => path, p => "[path] ([p])" }),
            span(#style:style(#fg:`Red), "  [command_error]")
        ];
        line(#style:title_style, spans)
    }
};

let handle_normal_mode_key = |e: KeyCode| -> [`Stop, `Continue] select e {
    e@ `Left => { cursor <- e ~ `Left(1); `Stop },
    e@ `Right => { cursor <- e ~ `Right(1); `Stop },
    e@ `Down => { cursor <- e ~ `Down(1); `Stop },
    e@ `Up => { cursor <- e ~ `Up(1); `Stop },
    e@ `Enter => { path <- (e ~ selected_row)$; `Stop },
    e@ `Backspace => select e ~ str::dirname(path) {
        null as _ => `Stop,
        s => {
            path <- s;
            `Stop
        }
    },
    k@ `Char(":") => {
        command <- k ~ "";
        command_error <- k ~ "";
        command_mode <- k ~ true;
        `Stop
    },
    _ => `Continue
};

let handle_command_mode_key = |e: KeyCode| -> [`Stop, `Continue] select e {
    `Char(c) => {
        command <- c ~ "[command][c]";
        `Stop
    },
    x@ `Enter => {
        command_mode <- x ~ false;
        run_command(x ~! command, (x ~! selected_path)$);
        `Stop
    },
    e@ `Backspace if command != "" => {
        let len = str::char_len(command) - 1;
        command <- e ~ str::sub(#start:0, #len, command)$;
        `Stop
    },
    k@ `Esc => {
        command_mode <- k ~ false;
        `Stop
    },
    _ => `Continue
};

let handle_event = |e: Event| on_press(e, |k| select k ~ command_mode {
    true => handle_command_mode_key(k.code),
    false => handle_normal_mode_key(k.code)
});

let size: Size = never();

let browser = browser(
    #cursor,
    #selected_row: &mut selected_row,
    #selected_path: &mut selected_path,
    #size,
    path
);

let block = block(
    #border:&`None,
    #title_bottom:&title_bottom,
    #style:&style(#bg:`Indexed(234)),
    #size:&mut size,
    &browser
);

input_handler(#handle: &handle_event, &block)

See Also

  • list - For simpler selection interfaces
  • table - For tabular data display
  • block - For containing browsers with borders