aboutsummaryrefslogtreecommitdiff
path: root/libui/list/list.ha
diff options
context:
space:
mode:
Diffstat (limited to 'libui/list/list.ha')
-rw-r--r--libui/list/list.ha155
1 files changed, 155 insertions, 0 deletions
diff --git a/libui/list/list.ha b/libui/list/list.ha
new file mode 100644
index 0000000..6b11b75
--- /dev/null
+++ b/libui/list/list.ha
@@ -0,0 +1,155 @@
+use libui;
+use fmt;
+use os;
+use strings;
+use io;
+use strio;
+use unix::tty;
+use wcwidth;
+
+export type listwidget = struct {
+ ui: libui::ttyui,
+ items: []str,
+ cursor: size,
+ listeners: []listener,
+ frame: frame,
+ sz: ttysize,
+};
+
+export type frame = struct {
+ start: u16,
+ // largest value is nb of items
+ end: u16,
+};
+
+export type ttysize = struct {
+ rows: u16,
+ cols: u16,
+};
+
+export type listener = *fn(l: *listwidget, r: rune) bool;
+
+export fn newlist(ui: libui::ttyui, items: str...) listwidget = {
+ let sz = libui::getwinsize(ui)!;
+ let rows: (u16 | size) = if (sz.rows - 2 < len(items)) {
+ yield sz.rows - 2;
+ } else {
+ yield len(items);
+ };
+ let w = listwidget {
+ ui = ui,
+ items = items,
+ cursor = 0z,
+ listeners = [],
+ frame = frame {
+ start = 0u16,
+ end = rows: u16,
+ },
+ sz = ttysize {
+ rows = rows: u16,
+ cols = sz.columns,
+ },
+ };
+ return w;
+};
+
+export fn addlistener(list: *listwidget, l: listener) void = {
+ append(list.listeners, l);
+};
+
+export fn print(list: *listwidget) (void | io::error | tty::error) = {
+ let sz = libui::getwinsize(list.ui)?;
+ let rows: (u16 | size) = if (sz.rows - 2 < len(list.items)) {
+ yield sz.rows - 2;
+ } else {
+ yield len(list.items);
+ };
+
+ //fmt::fprintln(os::stderr, rows)!;
+
+ list.frame.end = list.frame.start + rows: u16;
+
+ let st = strio::dynamic();
+ strio::concat(&st, "\r")?;
+ for (let i = list.frame.start; i < list.frame.end: u16; i += 1) {
+ let item = list.items[i];
+ let truncitem = wcwidth::truncate(item, sz.columns);
+ if (list.cursor == i) {
+ strio::concat(&st, "\x1B[104;1m\x1B[30m")?;
+ strio::concat(&st, truncitem)?;
+ strio::concat(&st, "\x1B[0m")?;
+ //libui::print(list.ui, strings::concat("\x1B[31;1m> ", list.items[i], "\x1B[0m"));
+ } else {
+ strio::concat(&st, truncitem)?;
+ //libui::print(list.ui, list.items[i]);
+ };
+ strio::concat(&st, "\r\n")?;
+ };
+ let s = strio::string(&st);
+ defer free(s);
+ libui::print(list.ui, s);
+};
+
+export fn notify(l: *listwidget, r: rune) bool = {
+ for (let i = 0z; i < len(l.listeners); i += 1) {
+ if (l.listeners[i](l, r)) {
+ return true;
+ };
+ };
+ return false;
+};
+
+export fn down(l: *listwidget) size = {
+ if (l.cursor < len(l.items) - 1) {
+ l.cursor += 1;
+ };
+ reframe(l);
+ return l.cursor;
+};
+
+export fn up(l: *listwidget) size = {
+ if (l.cursor > 0) {
+ l.cursor -= 1;
+ };
+ reframe(l);
+ return l.cursor;
+};
+
+fn reframe(l: *listwidget) bool = {
+ let reframed: bool = false;
+ if (l.cursor < l.frame.start) {
+ l.frame.end -= l.frame.start - l.cursor: u16;
+ l.frame.start = l.cursor: u16;
+ reframed = true;
+ };
+ if (l.cursor >= l.frame.end) {
+ l.frame.start += l.cursor: u16 - l.frame.end + 1;
+ l.frame.end = l.cursor: u16;
+ reframed = true;
+ };
+ return reframed;
+};
+
+export fn top(l: *listwidget) size = {
+ l.cursor = 0;
+ l.frame.start = 0;
+ l.frame.end = l.frame.start + l.sz.rows;
+ return l.cursor;
+};
+
+export fn bottom(l: *listwidget) size = {
+ l.cursor = len(l.items) - 1;
+ l.frame.end = len(l.items): u16;
+ l.frame.start = l.frame.end - l.sz.rows;
+ return l.cursor;
+};
+
+export fn search(l: *listwidget, s: str) size = {
+ for (let i = 0z; i < len(l.items); i += 1) {
+ if (strings::contains(l.items[i], s)) {
+ l.cursor = i;
+ reframe(l);
+ };
+ };
+ return l.cursor;
+};