diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | cmd/list.ha | 6 | ||||
| -rw-r--r-- | cmd/list_nostyle.ha | 16 | ||||
| -rw-r--r-- | cmd/list_strictsz.ha | 10 | ||||
| -rw-r--r-- | cmd/text.ha | 36 | ||||
| -rw-r--r-- | tui/widget/list/list.ha | 27 | ||||
| -rw-r--r-- | tui/widget/text/text.ha | 11 | ||||
| -rw-r--r-- | tui/widget/widget.ha | 151 |
8 files changed, 234 insertions, 24 deletions
@@ -1,3 +1,4 @@ /text /list /list_strictsz +/list_nostyle diff --git a/cmd/list.ha b/cmd/list.ha index 8729dd9..0148adb 100644 --- a/cmd/list.ha +++ b/cmd/list.ha @@ -10,7 +10,11 @@ use time; export fn main() void = { const out = tui::init()!; defer io::close(out)!; - let li = list::newlist(out, (1, 1), widget::nosize, "hello", "world")!; + let li = list::newlist(out, (1, 1), void, &widget::style { + border = true, + colorfg = widget::color::REDFG, + colorbg = widget::color::REDBG, + },"hello", "world", "bye", "world")!; let l = layout::newvlayout(&li); l.layout.print(&l); }; diff --git a/cmd/list_nostyle.ha b/cmd/list_nostyle.ha new file mode 100644 index 0000000..4e77955 --- /dev/null +++ b/cmd/list_nostyle.ha @@ -0,0 +1,16 @@ +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 out = tui::init()!; + defer io::close(out)!; + let li = list::newlist(out, (1, 1), void, void,"hello", "world", "bye", "world")!; + let l = layout::newvlayout(&li); + l.layout.print(&l); +}; diff --git a/cmd/list_strictsz.ha b/cmd/list_strictsz.ha index 1a7ea1c..13c2808 100644 --- a/cmd/list_strictsz.ha +++ b/cmd/list_strictsz.ha @@ -11,9 +11,13 @@ export fn main() void = { const out = tui::init()!; defer io::close(out)!; let li = list::newlist(out, (1, 1), tty::ttysize { - rows = 2, - columns = 2, - }, "hello", "world")!; + rows = 3, + columns = 4, + }, &widget::style { + border = true, + colorfg = widget::color::BLUEFG, + colorbg = widget::color::BLUEBG, + }, "hello", "world", "bye", "world")!; let l = layout::newvlayout(&li); l.layout.print(&l); }; diff --git a/cmd/text.ha b/cmd/text.ha index 6e6e102..3ec0f2a 100644 --- a/cmd/text.ha +++ b/cmd/text.ha @@ -1,5 +1,6 @@ use tui; use tui::layout; +use tui::widget; use tui::widget::text; use io; use fmt; @@ -8,10 +9,41 @@ use time; export fn main() void = { const out = tui::init()!; defer io::close(out)!; - let txt = text::newtext(out, "hello world", (50, 20)); + let txt = text::newtext(out, "hello world", (50, 20), &widget::style { + border = true, + colorfg = widget::color::REDFG, + colorbg = widget::color::REDBG, + }); let l = layout::newvlayout(&txt); l.layout.print(&l); - time::sleep(5 * time::SECOND); + + time::sleep(1 * time::SECOND); + let st = txt.widget.style as *widget::style; + st.colorfg = widget::color::GREENFG; text::settext(&txt, "bye world"); l.layout.print(&l); + + time::sleep(1 * time::SECOND); + st.colorfg = widget::color::BROWNFG; + l.layout.print(&l); + + time::sleep(1 * time::SECOND); + st.colorfg = widget::color::BLUEFG; + l.layout.print(&l); + + time::sleep(1 * time::SECOND); + st.colorfg = widget::color::MAGENTAFG; + l.layout.print(&l); + + time::sleep(1 * time::SECOND); + st.colorfg = widget::color::CYANFG; + l.layout.print(&l); + + time::sleep(1 * time::SECOND); + st.colorfg = widget::color::WHITEFG; + l.layout.print(&l); + + time::sleep(1 * time::SECOND); + st.colorfg = widget::color::DEFAULTFG; + l.layout.print(&l); }; diff --git a/tui/widget/list/list.ha b/tui/widget/list/list.ha index 7e60299..50fb8df 100644 --- a/tui/widget/list/list.ha +++ b/tui/widget/list/list.ha @@ -16,15 +16,16 @@ export type list = struct { }; // Return an instance of list. out is the tty file, pos the starting position, -// sz is the size of the widget (if [[widget::nosize]] is used, the maximum possible +// 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 newlist(out: io::file, pos: widget::coords, sz: widget::widgetsize, items: str...) (list | tty::error) = { +export fn newlist(out: io::file, pos: widget::coords, sz: widget::widgetsize, +style: (*widget::style | void), items: str...) (list | tty::error) = { const tsz = tty::winsize(out)?; let end = match (sz) { case let sz: tty::ttysize => yield if (tsz.rows < sz.rows) tsz.rows else sz.rows; - case widget::nosize => + case void => yield tsz.rows; }; @@ -39,6 +40,8 @@ export fn newlist(out: io::file, pos: widget::coords, sz: widget::widgetsize, it resize = &resizelist, pos = pos, sz = sz, + style = style, + ... }, items = items, frame = frame { @@ -53,18 +56,20 @@ export fn printlist(widget: *widget::widget) void = { 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)!; + //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)!; + memio::concat(&st, list.items[i])!; if (i != list.frame.end - 1) { memio::concat(&st, "\n")!; }; }; - widget::print(list.widget.out, memio::string(&st)!, (1, 1)); + list.widget.buf = memio::string(&st)!; + widget::print(list); }; export fn resizelist(widget: *widget::widget, ttysize: tty::ttysize) void = { diff --git a/tui/widget/text/text.ha b/tui/widget/text/text.ha index 5cd8da7..ecb5c2c 100644 --- a/tui/widget/text/text.ha +++ b/tui/widget/text/text.ha @@ -1,20 +1,23 @@ use io; use unix::tty; use tui::widget; +use strings; export type text = struct { widget: widget::widget, txt: str, }; -export fn newtext(out: io::file, txt: str, pos: widget::coords) text = { +export fn newtext(out: io::file, txt: str, pos: widget::coords, style: (*widget::style | void)) text = { return text { widget = widget::widget { out = out, print = &printtext, resize = &resizetext, pos = pos, - sz = widget::nosize, + sz = void, + style = style, + ... }, txt = txt, }; @@ -22,7 +25,9 @@ export fn newtext(out: io::file, txt: str, pos: widget::coords) text = { fn printtext(widget: *widget::widget) void = { const widget = widget: *text; - widget::print(widget.widget.out, widget.txt, widget.widget.pos); + widget.widget.buf = strings::dup(widget.txt); + defer free(widget.widget.buf); + widget::print(widget); }; fn resizetext(widget: *widget::widget, ttysize: tty::ttysize) void = { diff --git a/tui/widget/widget.ha b/tui/widget/widget.ha index 77b6f88..dbbfa11 100644 --- a/tui/widget/widget.ha +++ b/tui/widget/widget.ha @@ -1,35 +1,178 @@ use fmt; use unix::tty; use io; +use strings; +use memio; +use os; +use strconv; export type coords = (u16, u16); export type printfn = fn(w: *widget) void; export type resizefn = fn(w: *widget, ttysize: tty::ttysize) void; -export type nosize = void; -export type widgetsize = (tty::ttysize | nosize); +export type widgetsize = (tty::ttysize | void); + +export type color = enum uint { + BLACKFG = 30, + REDFG = 31, + GREENFG = 32, + BROWNFG = 33, + BLUEFG = 34, + MAGENTAFG = 35, + CYANFG = 36, + WHITEFG = 37, + DEFAULTFG = 39, + + BLACKBG = 40, + REDBG = 41, + GREENBG = 42, + BROWNBG = 43, + BLUEBG = 44, + MAGENTABG = 45, + CYANBG = 46, + WHITEBG = 47, + DEFAULTBG = 49, +}; export type style = struct { border: bool, + colorfg: color, + colorbg: color, }; export def DEFAULT_STYLE: style = style { border = false, + colorfg = color::DEFAULTFG, + colorbg = color::DEFAULTBG, }; export type widget = struct { out: io::file, print: *printfn, resize: *resizefn, + buf: str, pos: coords, sz: widgetsize, - style: style, + style: (*style | void), }; def gotoroot: str = "\x1B[1;1H"; -export fn print(out: io::file, s: str, pos: coords) void = { +def UNDERLINE: str = "\x1B[4m"; +def NOUNDERLINE: str = "\x1B[0m"; + +def OVERLINE: rune = '\u0305'; +def NOOVERLINE: str = "\x1B[0m"; + +// Must free return value +fn underline(s: str) str = { + return strings::concat(UNDERLINE, s, NOUNDERLINE); +}; + +// Must free return value +fn overline(s: str) str = { + const st = memio::dynamic(); + defer io::close(&st)!; + let iter = strings::iter(s); + for (let r: rune => strings::next(&iter)) { + const char = strings::fromrunes([r, OVERLINE]); + defer free(char); + memio::concat(&st, char)!; + }; + return strings::dup(memio::string(&st)!); +}; + +fn minrows(out: io::file, x: u16, y: widgetsize) (u16 | tty::error) = { + const y = match (y) { + case let y: tty::ttysize => + yield y; + case void => + yield tty::winsize(out)?; + }; + return if (x < y.rows) x else y.rows; +}; + +// Must free return string +fn color_to_str(color: color) str = fmt::asprintf("\x1B[{}m", strconv::utos(color)); + +export fn print(w: *widget) void = { + fmt::fprintf(w.out, "\x1B[{};{}H", w.pos.0, w.pos.1)!; + + let s = truncate_to_size(w); + + let sstyle = match (w.style) { + case let st: *style => + defer free(s); + let sborder = if (st.border) { + yield border(s); + } else { + yield strings::dup(s); + }; + defer free(sborder); + const scolor = color_to_str(st.colorfg); + defer free(scolor); + const defcolor = color_to_str(color::DEFAULTFG); + defer free(defcolor); + yield strings::concat(scolor, sborder, defcolor); + case void => + yield s; + }; + + defer free(sstyle); + + fmt::fprint(w.out, sstyle)!; +}; + +// Truncates the text of the widget to the widget's size (rows and columns) and +// returns the resulting string. +fn truncate_to_size(w: *widget) str = { + let spl = strings::split(w.buf, "\n"); + const st = memio::dynamic(); + defer io::close(&st)!; + for (let i = 0z; i < minrows(w.out, len(spl): u16, w.sz)!; i += 1) { + const line = spl[i]; + let item = match (w.sz) { + case let sz: tty::ttysize => + const s = if (len(line) > sz.columns) strings::sub(line, 0z, sz.columns) else line; + yield strings::rpad(s, ' ', sz.columns); + case void => + yield strings::dup(line); + }; + defer free(item); + memio::concat(&st, item)!; + if (i < len(spl) - 1) { + memio::concat(&st, "\n")!; + }; + }; + return strings::dup(memio::string(&st)!); +}; + +fn border(s: str) str = { + let st = memio::dynamic(); + defer io::close(&st)!; + let spl = strings::split(s, "\n"); + for (let i = 0z; i < len(spl); i += 1) { + const s = strings::concat("|", spl[i], "|"); + //const s = strings::dup(spl[i]); + defer free(s); + if (i == 0) { + s = overline(s); + }; + if (i == len(spl) - 1) { + s = underline(s); + }; + //memio::concat(&st, "|", s, "|")!; + memio::concat(&st, s)!; + + if (i < len(spl) - 1) { + memio::concat(&st, "\n")!; + }; + }; + return strings::dup(memio::string(&st)!); +}; + +export fn prints(out: io::file, s: str, pos: coords) void = { fmt::fprintf(out, "\x1B[{};{}H", pos.0, pos.1)!; fmt::fprint(out, s)!; }; |
