diff options
| author | Julian Hurst <ark@mansus.space> | 2025-03-19 23:53:56 +0100 |
|---|---|---|
| committer | Julian Hurst <ark@mansus.space> | 2025-03-19 23:53:58 +0100 |
| commit | 05794aae570fd3c2f6ed27937b785f3d086f57f4 (patch) | |
| tree | daadef54404131604bbda28cfa315f2d8eb5e522 | |
| parent | 0d8f330ccf366a2733bb6d5d1e51eef3579f04aa (diff) | |
| download | hare-tui-05794aae570fd3c2f6ed27937b785f3d086f57f4.tar.gz | |
Replace widget buf string with linesbuf
This allows the generic widget print code to iterate more easily on the
lines and to apply widget-specific styles without breaking them by
truncating the console codes.
| -rw-r--r-- | tui/widget/list/list.ha | 18 | ||||
| -rw-r--r-- | tui/widget/list/scrolllist.ha | 39 | ||||
| -rw-r--r-- | tui/widget/text/text.ha | 9 | ||||
| -rw-r--r-- | tui/widget/widget.ha | 114 |
4 files changed, 97 insertions, 83 deletions
diff --git a/tui/widget/list/list.ha b/tui/widget/list/list.ha index 23127ac..61ab22d 100644 --- a/tui/widget/list/list.ha +++ b/tui/widget/list/list.ha @@ -56,22 +56,10 @@ style: (*widget::style | void), items: str...) (list | tty::error) = { export fn printlist(widget: *widget::widget) void = { const list = widget: *list; - 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)!; - memio::concat(&st, list.items[i])!; - if (i != list.frame.end - 1) { - memio::concat(&st, "\n")!; - }; + list.widget.buf = widget::linesbuf { + lines = list.items[list.frame.start..list.frame.end], + styles = null, }; - list.widget.buf = memio::string(&st)!; widget::print(list); }; diff --git a/tui/widget/list/scrolllist.ha b/tui/widget/list/scrolllist.ha index 55cee9a..5057116 100644 --- a/tui/widget/list/scrolllist.ha +++ b/tui/widget/list/scrolllist.ha @@ -53,28 +53,10 @@ style: (*widget::style | void), items: str...) (scrolllist | tty::error) = { 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, widget::NEWLINE)!; - }; + list.widget.buf = widget::linesbuf { + lines = list.items[list.frame.start..list.frame.end], + styles = &stylesscrolllist, }; - list.widget.buf = memio::string(&st)!; widget::print(list); }; @@ -131,3 +113,18 @@ export fn bottom(li: *scrolllist) void = { li.frame.end = len(li.items): u16; li.frame.start = len(li.items): u16 - sz; }; + +fn stylesscrolllist(widget: *widget::widget, txt: str, idx: size) str = { + const list = widget: *scrolllist; + const idx = idx + list.frame.start; + let st = memio::dynamic(); + defer io::close(&st)!; + if (idx == list.cursor) { + memio::concat(&st, "\x1B[7m")!; + }; + memio::concat(&st, txt)!; + if (idx == list.cursor) { + memio::concat(&st, "\x1B[27m")!; + }; + return strings::dup(memio::string(&st)!); +}; diff --git a/tui/widget/text/text.ha b/tui/widget/text/text.ha index 0a30eb0..40b3f58 100644 --- a/tui/widget/text/text.ha +++ b/tui/widget/text/text.ha @@ -2,7 +2,6 @@ use io; use unix::tty; use tui; use tui::widget; -use strings; export type text = struct { widget: widget::widget, @@ -19,7 +18,7 @@ export fn newtext(state: *tui::tui, txt: str, pos: widget::coords, style: (*widg pos = pos, sz = void, style = style, - damage = widget::all, + damage = widget::damageall, ... }, txt = txt, @@ -28,8 +27,10 @@ export fn newtext(state: *tui::tui, txt: str, pos: widget::coords, style: (*widg fn printtext(widget: *widget::widget) void = { const widget = widget: *text; - widget.widget.buf = strings::dup(widget.txt); - defer free(widget.widget.buf); + widget.widget.buf = widget::linesbuf { + lines = [widget.txt], + ... + }; widget::print(widget); }; diff --git a/tui/widget/widget.ha b/tui/widget/widget.ha index 8422932..eadc773 100644 --- a/tui/widget/widget.ha +++ b/tui/widget/widget.ha @@ -25,12 +25,23 @@ export type damageitem = damagerow; export type damage = (damagenone | damageall | []damageitem); +export type stylesfn = fn(w: *widget, txt: str, idx: size) str; + +//export type line = struct { +// txt: str, +//}; + +export type linesbuf = struct { + lines: []str, + styles: nullable *stylesfn, +}; + export type widget = struct { state: *tui::tui, print: *printfn, resize: *resizefn, finish: *finishfn, - buf: str, + buf: linesbuf, pos: coords, sz: widgetsize, style: (*style | void), @@ -153,15 +164,33 @@ export fn print(w: *widget) void = { defer free(clear); let s = truncate_to_size(w); - defer free(s); + defer strings::freeall(s); let sstyle = applystyles(w.style, s); - defer free(sstyle); + defer strings::freeall(sstyle); //const clear = if (w.state.clear) "\x1B[2J" else ""; + + let st = memio::dynamic(); + defer io::close(&st)!; + + for(let i = 0z; i < len(sstyle); i += 1) { + const line = match (w.buf.styles) { + case let f: *stylesfn => + yield f(w, sstyle[i], i); + case null => + yield strings::dup(sstyle[i]); + }; + defer free(line); + memio::concat(&st, line, NEWLINE)!; + }; + + const joined = memio::string(&st)!; + let seekpos = fmt::asprintf("{}\x1B[{};{}H", clear, w.pos.0, w.pos.1); defer free(seekpos); - const sout = strings::concat(seekpos, sstyle); + + const sout = strings::concat(seekpos, joined); defer free(sout); fmt::fprint(w.state.out, sout)!; @@ -169,36 +198,40 @@ export fn print(w: *widget) void = { w.state.clear = false; }; -// Applies styling (style) to the given string -fn applystyles(st: (*style | void), s: str) str = { +// Applies styling (style) to the given string. +fn applystyles(st: (*style | void), s: []str) []str = { return match (st) { case let st: *style => let sborder = if (st.border) { yield border(s); } else { - yield strings::dup(s); + yield strings::dupall(s); }; - defer free(sborder); + //defer strings::freeall(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); + const sb = strings::concat(scolor, sborder[0]); + free(sborder[0]); + sborder[0] = sb; + const endidx = len(sborder) - 1; + const sb = strings::concat(sborder[endidx], defcolor); + free(sborder[endidx]); + sborder[endidx] = sb; + yield sborder; case void => - yield strings::dup(s); + yield strings::dupall(s); }; }; // 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 = splitstr(w.buf, NEWLINE); - defer free(spl); - const st = memio::dynamic(); - defer io::close(&st)!; - const nbrows = minrows(w.state.out, len(spl): u16, w.sz)!; +// returns the resulting lines. Must call [[strings::freeall]] on the result. +fn truncate_to_size(w: *widget) []str = { + let lines: []str = []; + const nbrows = minrows(w.state.out, len(w.buf.lines): u16, w.sz)!; for (let i = 0z; i < nbrows; i += 1) { - const line = spl[i]; + const line = w.buf.lines[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; @@ -206,39 +239,34 @@ fn truncate_to_size(w: *widget) str = { case void => yield strings::dup(line); }; + append(lines, item); //fmt::println(item)!; - defer free(item); - memio::concat(&st, item)!; - memio::concat(&st, NEWLINE)!; }; - return strings::dup(memio::string(&st)!); + return lines; }; -// Add a border around a string -fn border(s: str) str = { - let st = memio::dynamic(); - defer io::close(&st)!; - //let spl = strings::split(s, NEWLINE); - let spl = splitstr(s, NEWLINE); - defer free(spl); - 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); +// Add a border around a slice of strings. Must call [[strings::freeall]] on the +// result. +fn border(s: []str) []str = { + let lines: []str = []; + for (let i = 0z; i < len(s); i += 1) { + const si = strings::concat("|", s[i], "|"); + si = if (i == 0) { + defer free(si); + yield overline(si); + } else { + yield si; }; - if (i == len(spl) - 1) { - s = underline(s); + si = if (i == len(s) - 1) { + defer free(si); + yield underline(si); + } else { + yield si; }; - //memio::concat(&st, "|", s, "|")!; - memio::concat(&st, s)!; - if (i < len(spl) - 1) { - memio::concat(&st, NEWLINE)!; - }; + append(lines, si); }; - return strings::dup(memio::string(&st)!); + return lines; }; export fn prints(out: io::file, s: str, pos: coords) void = { |
