diff options
Diffstat (limited to 'libui/widget/list/list.ha')
| -rw-r--r-- | libui/widget/list/list.ha | 351 |
1 files changed, 0 insertions, 351 deletions
diff --git a/libui/widget/list/list.ha b/libui/widget/list/list.ha deleted file mode 100644 index d8906d3..0000000 --- a/libui/widget/list/list.ha +++ /dev/null @@ -1,351 +0,0 @@ -use libui; -use libui::widget; -use fmt; -use os; -use strings; -use io; -use strio; -use unix::tty; -use regex; -use fnmatch; -use wcwidth; -use set; - -export type listwidget = struct { - widget: widget::widget, - items: []str, - marked: set::set, - 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, -}; - -// An input listener on a list widget. The returning value is intended to be -// used as a signal that will be returned by [[notify]] in order to trigger -// certain more global ui events (terminate the program, change widget focus, -// etc.). To register a listener with a listwidget, use [[addlistener]]. -export type listener = *fn(l: *listwidget, r: rune) bool; - -// Create a new list with the given items. -export fn newlist(ui: libui::ttyui, items: str...) listwidget = { - let sz = libui::getwinsize(ui)!; - let rows = sz.rows - 2; - //let rows: (u16 | size) = if (sz.rows - 2 < len(items)) { - //yield sz.rows - 2; - //} else { - //yield len(items); - //}; - let w = listwidget { - widget = widget::widget { - print = &print, - finish = &finish, - ui = ui, - ... - }, - items = items, - marked = set::set {...}, - cursor = 0z, - //listeners = [], - frame = frame { - start = 0u16, - end = rows: u16, - }, - sz = ttysize { - rows = rows: u16, - cols = sz.columns, - }, - }; - return w; -}; - -// Free the list's items, marked items and call the common widget finish -// function [[widget::finishcommon]]. -export fn finish(list: *widget::widget) void = { - const list = list: *listwidget; - free(list.items); - set::finish(&list.marked); - widget::finishcommon(list); -}; - -// Set the list's items. If the length of the given items is smaller than the -// list's current items, the cursor will be set to 0z and [[reframe]] will be -// called to reset the frame. -export fn setitems(list: *listwidget, items: str...) void = { - const doreset = len(items) < len(list.items); - list.items = strings::dupall(items); - if (doreset) { - reset(list); - }; -}; - -export fn reset(list: *listwidget) void = { - list.cursor = 0z; - list.frame.start = 0u16; - list.frame.end = list.sz.rows; -}; - -// Add a listener to the given list. -//export fn addlistener(list: *listwidget, l: listener) void = { - //append(list.listeners, l); -//}; - -const SELECTED: str = "\x1B[104;1m\x1B[30m"; -const MARKED: str = "\x1B[46;1m\x1B[30m"; -const RESET: str = "\x1B[0m"; - -// Print the list's items while truncating the items to not be wider than the -// list.sz.cols. -export fn print(list: *widget::widget) (void | widget::error) = { - const list = list: *listwidget; - //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 + list.sz.rows; - - let st = strio::dynamic(); - strio::concat(&st, "\r")?; - let end = if (list.frame.end < len(list.items)) { - yield list.frame.end; - } else { - yield len(list.items); - }; - for (let i = list.frame.start; i < end: u16; i += 1) { - const item = list.items[i]; - const truncitem = wcwidth::truncate(item, list.sz.cols); - if (list.cursor == i) { - strio::concat(&st, SELECTED, truncitem, RESET)?; - //libui::print(list.ui, strings::concat("\x1B[31;1m> ", list.items[i], "\x1B[0m")); - } else if (set::contains(list.marked, i) is size){ - strio::concat(&st, MARKED, truncitem, RESET)?; - //libui::print(list.ui, list.items[i]); - } else { - strio::concat(&st, truncitem)?; - }; - strio::concat(&st, "\r\n")?; - }; - // unsupported? - //io::copy(list.ui.f, &st)?; - let s = strio::string(&st); - libui::print(list.widget.ui, s); - io::close(&st)?; -}; - -// Notify (call) the listwidget's listeners with the listwidget and r as a -// parameter. Returns true if a listener returned true, false otherwise. -export fn notify(l: *listwidget, r: rune) bool = { - for (let i = 0z; i < len(l.widget.listeners); i += 1) { - if (l.widget.listeners[i](l, r)) { - return true; - }; - }; - return false; -}; - -// Reset the list's frame based on the cursor. Returns whether the frame was -// updated. -export fn reframe(l: *listwidget) bool = { - let reframed: bool = false; - if (l.cursor < l.frame.start) { - l.frame.start = l.cursor: u16; - l.frame.end = l.frame.start + l.sz.rows; - //l.frame.end -= 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 + 1; - reframed = true; - }; - return reframed; -}; - -export fn resize(l: *listwidget, oldsz: ttysize) bool = { - if (l.frame.end - l.frame.start != l.sz.rows) { - if (l.cursor < (l.frame.end / 2)) { - fmt::fprintln(os::stderr, "closer to start")!; - l.frame.end = if (l.frame.start + l.sz.rows > len(l.items)) { - l.frame.start = len(l.items): u16 - l.sz.rows; - yield len(l.items): u16; - } else { - yield l.frame.start + l.sz.rows; - }; - for (l.cursor > l.frame.end) { - fmt::fprintln(os::stderr, "looping")!; - l.frame.start += 1; - l.frame.end += 1; - }; - } else { - fmt::fprintln(os::stderr, "closer to end")!; - l.frame.start = if (l.frame.end: int - l.sz.rows: int < 0) { - l.frame.end = 0 + l.sz.rows; - yield 0; - } else { - yield l.frame.end - l.sz.rows; - }; - for (l.cursor < l.frame.start) { - fmt::fprintln(os::stderr, "looping")!; - l.frame.start -= 1; - l.frame.end -= 1; - }; - }; - return true; - }; - return false; -}; - -fn cursorinframe(l: *listwidget) bool = { - return l.cursor >= l.frame.start && l.cursor < l.frame.end; -}; - -// Move the list's cursor down one item. Returns the new cursor. -export fn down(l: *listwidget) size = { - if (l.cursor < len(l.items) - 1) { - l.cursor += 1; - reframe(l); - }; - return l.cursor; -}; - -// Move the list's cursor up one item. Returns the new cursor. -export fn up(l: *listwidget) size = { - if (l.cursor > 0) { - l.cursor -= 1; - reframe(l); - }; - return l.cursor; -}; - -// Move the list's cursor up one page. Returns the new cursor. -export fn pageup(l: *listwidget) size = { - if (l.cursor: int - l.sz.rows: int >= 0) { - l.cursor -= l.sz.rows; - } else { - l.cursor = 0z; - }; - reframe(l); - return l.cursor; -}; - -// Move the list's cursor down one page. Returns the new cursor. -export fn pagedown(l: *listwidget) size = { - if (l.cursor + l.sz.rows < len(l.items)) { - l.cursor += l.sz.rows; - } else { - l.cursor = len(l.items) - 1; - }; - reframe(l); - return l.cursor; -}; - -// Move the list's cursor to the top (first item). Returns the new cursor. -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; -}; - -// Move the list's cursor to the bottom (last item). Returns the new 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; -}; - -// Forward search through the list's items for an item containing s. Returns the -// new cursor. -export fn search(l: *listwidget, s: str) size = { - for (let i = l.cursor + 1; i < len(l.items); i += 1) { - if (strings::contains(l.items[i], s)) { - l.cursor = i; - reframe(l); - return l.cursor; - }; - }; - return l.cursor; -}; - -// Backwards search through the list's items for an item containing s. Returns -// the new cursor. -export fn rsearch(l: *listwidget, s: str) size = { - // size wraps to max value for size when < 0 - for (let i = l.cursor: int - 1; i >= 0; i -= 1) { - if (strings::contains(l.items[i], s)) { - l.cursor = i: size; - reframe(l); - return l.cursor; - }; - }; - return l.cursor; -}; - -// Toggles marking the currently selected item. -export fn tmark(l: *listwidget) void = { - if (!set::add(&l.marked, l.cursor)) { - set::del(&l.marked, l.cursor); - }; -}; - -// Clears all marked items. -export fn clearmarked(l: *listwidget) void = { - set::clear(&l.marked); -}; - -// Marks items that contain s (case sensitive). -export fn containsmark(l: *listwidget, s: str) void = { - for (let i = 0z; i < len(l.items); i += 1) { - if (strings::contains(l.items[i], s)) { - set::add(&l.marked, i); - }; - }; -}; - -// Marks items based on fnmatch (globbing syntax). -export fn fnmatchmark(l: *listwidget, s: str) void = { - for (let i = 0z; i < len(l.items); i += 1) { - if (fnmatch::fnmatch(s, l.items[i])) { - set::add(&l.marked, i); - }; - }; -}; - -// Marks items according to a regular expression (POSIX ERE). -export fn regexmark(l: *listwidget, re: *regex::regex) void = { - for (let i = 0z; i < len(l.items); i += 1) { - if (regex::test(re, l.items[i])) { - set::add(&l.marked, i); - }; - }; -}; - -// Returns the selected item or marked items if there are any. -export fn selected(l: listwidget) (str | []str) = { - if (len(l.marked.items) > 0) { - let result: []str = []; - for (let i = 0z; i < len(l.marked.items); i += 1) { - append(result, l.items[l.marked.items[i]]); - }; - return result; - } else { - return l.items[l.cursor]; - }; -}; |
