Rework the list so navigating with arrows fully works.

It's still necessary to fix display after a query, but the current
navigation should just work when that's added.
This commit is contained in:
Érico Rolim 2021-01-07 20:59:58 -03:00
parent 1e42b5527e
commit 7385d08bf1
1 changed files with 89 additions and 34 deletions

123
browser.c
View File

@ -71,13 +71,39 @@ int main()
init_pair(1, COLOR_GREEN, COLOR_BLACK); init_pair(1, COLOR_GREEN, COLOR_BLACK);
// red,green,yellow,blue,cyan,magenta,white // red,green,yellow,blue,cyan,magenta,white
} }
/* noop function - should use the windows from below */ int cattr = COLOR_PAIR(1) | A_BOLD;
attrset(COLOR_PAIR(1)); int nattr = A_NORMAL;
/* list should work as follows:
* - can fit up to entries.n
* - element 0 is at the bottom (entries.n - 1)
* - last element is at the top (0)
*/
/* store number of rows that can be displayed in list */ /* store number of rows that can be displayed in list */
const int nrows = LINES - 1; const int nrows = LINES - 1;
/* list position in x and y;
* listx currently isn't changed anywhere */
int listy;
const int listx = 0;
/* current pick */
size_t pick = 0;
/* list size */
int listsize;
WINDOW *list = newpad(entries.n, COLS); if (entries.n <= (size_t)nrows) {
/* list should be at least as big as nrows */
listsize = nrows;
listy = 0;
} else {
listsize = entries.n;
/* move to position where we show the end of the list */
listy = entries.n - nrows;
}
WINDOW *list = newpad(listsize, COLS);
WINDOW *prompt = newwin(1, 0, LINES - 1, 0); WINDOW *prompt = newwin(1, 0, LINES - 1, 0);
if (!list || !prompt) { if (!list || !prompt) {
perror("newwin"); perror("newwin");
@ -85,20 +111,24 @@ int main()
} }
keypad(prompt, TRUE); keypad(prompt, TRUE);
/* inital dump of list */ /* initial prompt */
for (size_t i = 0; i < entries.n; i++) {
mvwaddstr(list, i, 0, get_entry(&entries, i));
}
prefresh(list, 0, 0, 0, 0, nrows - 1, COLS);
mvwaddstr(prompt, 0, 0, "> "); mvwaddstr(prompt, 0, 0, "> ");
wrefresh(prompt); wrefresh(prompt);
struct str_array toks = { 0 }; #define WRITELIST(idx) mvwaddstr(list, listsize - idx - 1, 0, get_entry(&entries, idx))
/* inital dump of list */
for (size_t i = 0; i < entries.n; i++) {
if (i == pick) wattrset(list, cattr);
WRITELIST(i);
if (i == pick) wattrset(list, nattr);
}
prefresh(list, listy, listx, 0, 0, nrows-1, COLS);
/* variables to control current search token */
size_t n, cap; size_t n, cap;
char *name = NULL; char *name = NULL;
/* listx isn't changed anywhere */ /* search tokens */
int listx = 0, listy = 0; struct str_array toks = { 0 };
for (;;) { for (;;) {
if (!name) { if (!name) {
cap = 1024; cap = 1024;
@ -109,62 +139,53 @@ int main()
add_entry(&toks, name); add_entry(&toks, name);
} }
bool name_changed = false; int pos_change = 0;
int c = wgetch(prompt); int c = wgetch(prompt);
switch (c) { switch (c) {
/* Ctrl+[ or ESC */ case 27: /* Ctrl+[ or ESC */
case 27:
exit(1); exit(1);
break; break;
case KEY_ENTER: case KEY_ENTER:
case '\r': case '\r':
/* since we are using nonl above, only capture '\r' itself /* since we are using nonl above, only capture '\r' itself */
* TODO: actually store the entry name */ final_name = get_entry(&entries, pick);
final_name = name;
exit(0); exit(0);
case KEY_DOWN: case KEY_DOWN:
/* Ctrl-N */ case 14: /* Ctrl-N */
case 14: pos_change = -1;
if ((size_t)listy < entries.n && (size_t)nrows < entries.ms ) listy++;
break; break;
case KEY_UP: case KEY_UP:
/* Ctrl-P */ case 16: /* Ctrl-P */
case 16: pos_change = +1;
if (listy) listy--;
break; break;
/* Ctrl+W */ /* TODO: treat n==0 */
case 23: case 23: /* Ctrl+W */
n = 0; n = 0;
name[n] = 0; name[n] = 0;
name_changed = true;
break; break;
case KEY_BACKSPACE: case KEY_BACKSPACE:
/* DEL */ case 127: /* DEL */
case 127:
/* go back to previous word */ /* go back to previous word */
if (toks.n > 1 && n == 0) { if (toks.n > 1 && n == 0) {
free(pop_entry(&toks)); free(pop_entry(&toks));
name = get_entry(&toks, toks.n - 1); name = get_entry(&toks, toks.n - 1);
cap = malloc_usable_size(name); cap = malloc_usable_size(name);
n = strlen(name); n = strlen(name);
name_changed = true;
break; break;
} }
if (n) n--; if (n) n--;
name[n] = 0; name[n] = 0;
name_changed = true;
break; break;
case ' ': case ' ':
if (*name) { if (*name) {
name = NULL; name = NULL;
} }
name_changed = true;
break; break;
default: default:
@ -176,11 +197,45 @@ int main()
n++; n++;
/* clear chars from previous entries and/or dirty memory */ /* clear chars from previous entries and/or dirty memory */
name[n] = 0; name[n] = 0;
name_changed = true;
break; break;
} }
if (!name_changed) {
if (pos_change) {
const size_t old_pick = pick;
/* XXX: trusting integer overflow to work things out here */
for (size_t i = pick + pos_change; i < entries.n; i += pos_change) {
if (get_entry_match(&entries, i)) {
pick = i;
break;
}
}
if (pick == old_pick) continue;
/* clean up appearance */
wattrset(list, nattr);
WRITELIST(old_pick);
wattrset(list, cattr);
WRITELIST(pick);
wattrset(list, nattr);
/* find index inside set of matches */
size_t index_in_matched = 0;
for (size_t i = 0; i < pick; i++) {
if (get_entry_match(&entries, i)) index_in_matched++;
}
/* check if all matched entries fit in visible list */
if (entries.ms <= (size_t)nrows) {
/* pass */
} else {
/* check if pick is above or below */
const size_t bottom = (entries.ms) - (listy+nrows);
const size_t top = bottom + nrows - 1;
if (index_in_matched < bottom) listy++;
else if (index_in_matched > top) listy--;
}
prefresh(list, listy, listx, 0, 0, nrows-1, COLS); prefresh(list, listy, listx, 0, 0, nrows-1, COLS);
continue; continue;
} }