use tui; use tui::widget; use io; use unix::tty; use memio; use strings; use fmt; use strconv; export def DEFAULTSTYLE = liststyle { style = void, normal = widget::color::DEFAULTFG, marked = widget::color::BLUEBG, }; export type scrolllist = struct { widget: widget::widget, items: []str, frame: frame, cursor: int, marked: []int, style: *liststyle, }; export type liststyle = struct { style: (void | *widget::style), normal: widget::color, marked: widget::color, //normal: (void | widget::color), //marked: (void | widget::color), }; // 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: *liststyle, items: str...) (scrolllist | tty::error) = { const tsz = tty::winsize(state.out)?; let end = match (sz) { case let sz: tty::ttysize => const rows = if (tsz.rows < sz.rows) tsz.rows - 1 else sz.rows; yield rows; case void => yield tsz.rows - 1; }; let end = end: int; if (end > len(items): int) { end = len(items): int; }; return scrolllist { widget = widget::widget { state = state, print = &printscrolllist, resize = &resizescrolllist, finish = &finishscrolllist, pos = pos, sz = sz, style = style.style, damage = widget::damageall, ... }, items = items, frame = frame { start = 0, end = end, }, cursor = 0, marked = [], style = style, }; }; fn printscrolllist(widget: *widget::widget) void = { const list = widget: *scrolllist; assert(list.frame.start >= 0); assert(list.frame.end <= len(list.items): int); list.widget.buf = widget::linesbuf { lines = list.items[list.frame.start..list.frame.end], styles = &stylesscrolllist, }; widget::print(list); }; fn resizescrolllist(widget: *widget::widget, ttysize: tty::ttysize) void = { return; }; fn finishscrolllist(widget: *widget::widget) void = { const list = widget: *scrolllist; widget::finish(widget); free(list.marked); }; export fn down(li: *scrolllist) void = { if (li.cursor == li.frame.end - 1) { framedown(li); }; if (li.cursor < len(li.items): int - 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): int) { 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): int - 1; li.frame.end = len(li.items): int; li.frame.start = len(li.items): int - sz; }; export fn setcursor(li: *scrolllist, newpos: int) void = { if (newpos < 0) { li.cursor = 0; } else if (newpos > len(li.items): int) { li.cursor = len(li.items): int - 1; } else { li.cursor = newpos; }; reframe(li); }; fn reframe(li: *scrolllist) void = { if (li.cursor >= li.frame.end) { const diff = li.frame.end - li.frame.start; li.frame.end = li.cursor + 1; li.frame.start = li.frame.end - diff; } else if (li.cursor < li.frame.start) { const diff = li.frame.end - li.frame.start; li.frame.start = li.cursor; li.frame.end = li.frame.start + diff; }; assert(li.frame.start >= 0); assert(li.frame.end <= len(li.items): int); }; fn stylesscrolllist(widget: *widget::widget, txt: str, idx: size) str = { const list = widget: *scrolllist; const idx = idx: int + list.frame.start; let st = memio::dynamic(); defer io::close(&st)!; const normalst = colorordefault(list.style.normal, widget::color::DEFAULTFG); const markst = colorordefault(list.style.marked, widget::color::BLUEBG); //memio::concat(&st, widget::color_to_str(normalst))!; const normalsts = widget::color_to_str(normalst); defer free(normalsts); memio::concat(&st, normalsts)!; if (idx == list.cursor) { memio::concat(&st, "\x1B[7m")!; }; if (ismarked(*list, idx) is size) { const s = widget::color_to_str(markst); defer free(s); memio::concat(&st, s)!; }; memio::concat(&st, txt)!; memio::concat(&st, "\x1B[0m")!; //if (idx == list.cursor) { // memio::concat(&st, "\x1B[27m")!; //} else if (ismarked(*list, idx) is size) { //}; //if (idx == list.cursor) { // memio::concat(&st, "\x1B[27m")!; //}; //if (ismarked(*list, idx) is size) { // memio::concat(&st, "\x1B[49m")!; //}; return strings::dup(memio::string(&st)!); }; fn colorordefault(col: (void | widget::color), d: widget::color) widget::color = { const c = match (col) { case let c: widget::color => yield c; case void => yield d; }; return c; }; fn ismarked(li: scrolllist, j: int) (size | void) = { for (let i = 0z; i < len(li.marked); i += 1) { const idx = li.marked[i]; if (idx == j) { return i; }; }; return; }; export fn mark(li: *scrolllist) bool = { defer down(li); match (ismarked(*li, li.cursor)) { case let s: size => delete(li.marked[s]); return false; case void => append(li.marked, li.cursor); return true; }; };