+ char *str;
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+ str = strdup_nf(text);
+ darr_push(wl->entries, &str);
+ wl->dirty = 1;
+}
+
+int tui_num_list_items(struct tui_widget *w)
+{
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+ return darr_size(wl->entries);
+}
+
+int tui_list_select(struct tui_widget *w, int idx)
+{
+ int offs, nelem;
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+
+ if(idx == wl->sel) {
+ return 0; /* no change */
+ }
+
+ if(idx < 0) {
+ wl->sel = -1;
+ return 0;
+ }
+ if(idx >= (nelem = darr_size(wl->entries))) {
+ return -1;
+ }
+ wl->sel = idx;
+
+ if(idx < wl->view_offs || idx >= wl->view_offs + wl->height) {
+ offs = idx - wl->height / 2;
+ if(offs + wl->height >= nelem) {
+ offs = nelem - wl->height;
+ }
+ if(offs < 0) {
+ offs = 0;
+ }
+ wl->view_offs = offs;
+ }
+
+ wl->dirty = 1;
+ return 0;
+}
+
+int tui_get_list_sel(struct tui_widget *w)
+{
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+ return wl->sel;
+}
+
+int tui_list_sel_next(struct tui_widget *w)
+{
+ int nelem;
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+
+ nelem = darr_size(wl->entries);
+
+ if(wl->sel + 1 >= nelem) {
+ return -1;
+ }
+
+ if(++wl->sel - wl->view_offs >= wl->height) {
+ wl->view_offs = wl->sel - wl->height;
+ }
+ wl->dirty = 1;
+ return 0;
+}
+
+int tui_list_sel_prev(struct tui_widget *w)
+{
+ struct tui_list *wl = (struct tui_list*)w;
+ assert(wl->type == TUI_LIST);
+
+ if(wl->sel <= 0) {
+ return -1;
+ }
+ if(--wl->sel < wl->view_offs) {
+ wl->view_offs = wl->sel;
+ }
+ wl->dirty = 1;
+ return 0;
+}
+
+int tui_list_sel_start(struct tui_widget *w)
+{