summaryrefslogtreecommitdiff
path: root/tui
diff options
context:
space:
mode:
authorJulian Hurst <ark@mansus.space>2025-03-17 23:13:06 +0100
committerJulian Hurst <ark@mansus.space>2025-03-17 23:13:06 +0100
commita574b7a367956efca0e0a13123bc60c98b9908c9 (patch)
tree56399fb7c4fada35b5990ce94950827aef484980 /tui
parent0d0186d63712472a037cf510812e68774c73c12f (diff)
downloadhare-tui-a574b7a367956efca0e0a13123bc60c98b9908c9.tar.gz
Initial scrolllist and input support
Diffstat (limited to 'tui')
-rw-r--r--tui/tui.ha26
-rw-r--r--tui/widget/list/scrolllist.ha133
2 files changed, 158 insertions, 1 deletions
diff --git a/tui/tui.ha b/tui/tui.ha
index 4f6c1d5..f6014a6 100644
--- a/tui/tui.ha
+++ b/tui/tui.ha
@@ -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;
+};