diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | cmd/scrolllist.ha | 52 | ||||
| -rw-r--r-- | tui/tui.ha | 26 | ||||
| -rw-r--r-- | tui/widget/list/scrolllist.ha | 133 |
4 files changed, 211 insertions, 1 deletions
@@ -2,3 +2,4 @@ /list /list_strictsz /list_nostyle +/scrolllist diff --git a/cmd/scrolllist.ha b/cmd/scrolllist.ha new file mode 100644 index 0000000..6b631fc --- /dev/null +++ b/cmd/scrolllist.ha @@ -0,0 +1,52 @@ +use tui; +use tui::layout; +use tui::widget; +use tui::widget::list; +use unix::tty; +use io; +use fmt; +use time; + +export fn main() void = { + const state = tui::init()!; + defer tui::finish(&state); + let li = list::newscrolllist(&state, (1, 1), tty::ttysize { + rows = 2u16, + columns = 50u16, + }, + &widget::style { + border = true, + colorfg = widget::color::REDFG, + colorbg = widget::color::REDBG, + },"hello", "world", "bye", "world")!; + + let l = layout::newvlayout(&li); + defer l.layout.finish(&l); + l.layout.print(&l); + + for (true) { + const r = tui::read(&state)!; + if (r == 'j') { + list::down(&li); + }; + if (r == 'k') { + list::up(&li); + }; + if (r == 'K') { + list::frameup(&li); + }; + if (r == 'J') { + list::framedown(&li); + }; + if (r == 'g') { + list::top(&li); + }; + if (r == 'G') { + list::bottom(&li); + }; + if (r == 'q') { + break; + }; + l.layout.print(&l); + }; +}; @@ -1,18 +1,41 @@ use fmt; use io; use unix::tty; +use errors; +use encoding::utf8; +use bufio; export type tui = struct { out: io::file, clear: bool, + tq: tty::termios, }; -export fn init() (tui | tty::error) = { +export type skey = enum { + BS, +}; + +export type key = (skey | rune); + +export fn init() (tui | tty::error | errors::error) = { const f = tty::open()?; + const tq = tty::termios_query(f)?; + tty::noncanonical(&tq)?; doclear(f); return tui { out = f, clear = false, + tq = tq, + }; + +}; + +export fn read(state: *tui) (key | utf8::invalid | io::error | io::EOF) = { + match (bufio::read_rune(state.out)?) { + case let r: rune => + return r; + case io::EOF => + return io::EOF; }; }; @@ -27,5 +50,6 @@ export fn clear(state: *tui) void = { }; export fn finish(state: *tui) void = { + tty::termios_restore(&state.tq); io::close(state.out)!; }; diff --git a/tui/widget/list/scrolllist.ha b/tui/widget/list/scrolllist.ha new file mode 100644 index 0000000..5ffa70f --- /dev/null +++ b/tui/widget/list/scrolllist.ha @@ -0,0 +1,133 @@ +use tui; +use tui::widget; +use io; +use unix::tty; +use memio; +use strings; + +export type scrolllist = struct { + widget: widget::widget, + items: []str, + frame: frame, + cursor: u16, +}; + +// Return an instance of list. out is the tty file, pos the starting position, +// sz is the size of the widget (if void is used, the maximum possible +// size is used), items is the slice of items of the list. +export fn newscrolllist(state: *tui::tui, pos: widget::coords, sz: widget::widgetsize, +style: (*widget::style | void), items: str...) (scrolllist | tty::error) = { + const tsz = tty::winsize(state.out)?; + + let end = match (sz) { + case let sz: tty::ttysize => + yield if (tsz.rows < sz.rows) tsz.rows else sz.rows; + case void => + yield tsz.rows; + }; + + if (end > len(items)) { + end = len(items): u16; + }; + + return scrolllist { + widget = widget::widget { + state = state, + print = &printscrolllist, + resize = &resizescrolllist, + finish = &finishscrolllist, + pos = pos, + sz = sz, + style = style, + damage = widget::all, + ... + }, + items = items, + frame = frame { + start = 0, + end = end, + }, + cursor = 0u16, + }; +}; + +fn printscrolllist(widget: *widget::widget) void = { + const list = widget: *scrolllist; + let st = memio::dynamic(); + defer io::close(&st)!; + for (let i = list.frame.start; i < list.frame.end; i += 1) { + //let item = match (list.widget.sz) { + //case let sz: tty::ttysize => + // yield strings::sub(list.items[i], 0z, sz.columns); + //case widget::nosize => + // yield list.items[i]; + //}; + //memio::concat(&st, item)!; + if (i == list.cursor) { + memio::concat(&st, "\x1B[7m")!; + }; + memio::concat(&st, list.items[i])!; + if (i == list.cursor) { + memio::concat(&st, "\x1B[27m")!; + }; + if (i != list.frame.end - 1) { + memio::concat(&st, "\n")!; + }; + }; + list.widget.buf = memio::string(&st)!; + widget::print(list); +}; + +fn resizescrolllist(widget: *widget::widget, ttysize: tty::ttysize) void = { + return; +}; + +fn finishscrolllist(widget: *widget::widget) void = { + widget::finish(widget); +}; + +export fn down(li: *scrolllist) void = { + if (li.cursor == li.frame.end - 1) { + framedown(li); + }; + if (li.cursor < len(li.items) - 1) { + li.cursor += 1; + }; +}; + +export fn up(li: *scrolllist) void = { + if (li.cursor == li.frame.start) { + frameup(li); + }; + if (li.cursor > 0) { + li.cursor -= 1; + }; +}; + +export fn frameup(li: *scrolllist) void = { + if (li.frame.start > 0) { + li.frame.start -= 1; + li.frame.end -= 1; + }; +}; + +export fn framedown(li: *scrolllist) void = { + if (li.frame.end < len(li.items)) { + li.frame.start += 1; + li.frame.end += 1; + }; +}; + +export fn top(li: *scrolllist) void = { + const sz = li.frame.end - li.frame.start; + li.cursor = 0; + li.frame.end = sz; + li.frame.start = 0; +}; + +export fn bottom(li: *scrolllist) void = { + const sz = li.frame.end - li.frame.start; + li.cursor = len(li.items): u16 - 1; + li.frame.end = len(li.items): u16; + li.frame.start = len(li.items): u16 - sz; +}; |
