More intuitive multi-page browsing

Here is the new multi-page listing functionality I promised in
5db9c2aea1.

It may look very easy, but getting this to work right wasn't,
unfortunately.
This commit is contained in:
Yorhel 2010-04-28 13:32:30 +02:00
parent c68a229e09
commit b7ccf78b90
6 changed files with 84 additions and 7 deletions

View file

@ -4,6 +4,7 @@ git - ?
- Fixed crash on browsing dirs with a small window size (#2991787) - Fixed crash on browsing dirs with a small window size (#2991787)
- Fixed buffer overflow when some directories can't be scanned (#2981704) - Fixed buffer overflow when some directories can't be scanned (#2981704)
- Improved browsing performance - Improved browsing performance
- More intuitive multi-page browsing
- Various minor fixes - Various minor fixes
1.6 - 2009-10-23 1.6 - 2009-10-23

View file

@ -205,12 +205,10 @@ void browse_draw() {
return; return;
/* get start position */ /* get start position */
t = dirlist_get(-1*((winrows)/2)); t = dirlist_top(0);
if(t == dirlist_next(NULL))
t = NULL;
/* print the list to the screen */ /* print the list to the screen */
for(i=0; (t=dirlist_next(t)) && i<winrows-3; i++) { for(i=0; t && i<winrows-3; t=dirlist_next(t),i++) {
browse_draw_item(t, 2+i); browse_draw_item(t, 2+i);
/* save the selected row number for later */ /* save the selected row number for later */
if(t->flags & FF_BSEL) if(t->flags & FF_BSEL)
@ -284,28 +282,34 @@ int browse_key(int ch) {
case KEY_UP: case KEY_UP:
case 'k': case 'k':
dirlist_select(dirlist_get(-1)); dirlist_select(dirlist_get(-1));
dirlist_top(-1);
info_start = 0; info_start = 0;
break; break;
case KEY_DOWN: case KEY_DOWN:
case 'j': case 'j':
dirlist_select(dirlist_get(1)); dirlist_select(dirlist_get(1));
dirlist_top(1);
info_start = 0; info_start = 0;
break; break;
case KEY_HOME: case KEY_HOME:
dirlist_select(dirlist_next(NULL)); dirlist_select(dirlist_next(NULL));
dirlist_top(2);
info_start = 0; info_start = 0;
break; break;
case KEY_LL: case KEY_LL:
case KEY_END: case KEY_END:
dirlist_select(dirlist_get(1<<30)); dirlist_select(dirlist_get(1<<30));
dirlist_top(1);
info_start = 0; info_start = 0;
break; break;
case KEY_PPAGE: case KEY_PPAGE:
dirlist_select(dirlist_get(-1*(winrows-3))); dirlist_select(dirlist_get(-1*(winrows-3)));
dirlist_top(-1);
info_start = 0; info_start = 0;
break; break;
case KEY_NPAGE: case KEY_NPAGE:
dirlist_select(dirlist_get(winrows-3)); dirlist_select(dirlist_get(winrows-3));
dirlist_top(1);
info_start = 0; info_start = 0;
break; break;
@ -338,15 +342,19 @@ int browse_key(int ch) {
case 10: case 10:
case KEY_RIGHT: case KEY_RIGHT:
case 'l': case 'l':
if(sel != NULL && sel->sub != NULL) if(sel != NULL && sel->sub != NULL) {
dirlist_open(sel->sub); dirlist_open(sel->sub);
dirlist_top(-3);
}
info_show = 0; info_show = 0;
break; break;
case KEY_LEFT: case KEY_LEFT:
case 'h': case 'h':
case '<': case '<':
if(sel != NULL && sel->parent->parent != NULL) if(sel != NULL && sel->parent->parent != NULL) {
dirlist_open(sel->parent); dirlist_open(sel->parent);
dirlist_top(-3);
}
info_show = 0; info_show = 0;
break; break;

View file

@ -509,6 +509,7 @@ int calc_process() {
freedir(orig); freedir(orig);
} }
browse_init(root->sub); browse_init(root->sub);
dirlist_top(-3);
return 0; return 0;
} }

View file

@ -236,6 +236,7 @@ void delete_process() {
else { else {
nextsel->flags |= FF_BSEL; nextsel->flags |= FF_BSEL;
browse_init(nextsel); browse_init(nextsel);
dirlist_top(-4);
} }
} }

View file

@ -40,7 +40,7 @@ int dirlist_sort_desc = 1,
/* private state vars */ /* private state vars */
struct dir dirlist_parent_alloc; struct dir dirlist_parent_alloc;
struct dir *head, *head_real, *selected; struct dir *head, *head_real, *selected, *top = NULL;
@ -282,6 +282,67 @@ void dirlist_select(struct dir *d) {
} }
/* We need a hint in order to figure out which item should be on top:
* 0 = only get the current top, don't set anything
* 1 = selected has moved down
* -1 = selected has moved up
* -2 = selected = first item in the list (faster version of '1')
* -3 = top should be considered as invalid (after sorting or opening an other dir)
* -4 = an item has been deleted
* -5 = hidden flag has been changed
*
* Actions:
* hint = -1 or -4 -> top = selected_is_visible ? top : selected
* hint = -2 or -3 -> top = selected-(winrows-3)/2
* hint = 1 -> top = selected_is_visible ? top : selected-(winrows-4)
* hint = 0 or -5 -> top = selected_is_visible ? top : selected-(winrows-3)/2
*
* Regardless of the hint, the returned top will always be chosen such that the
* selected item is visible.
*/
struct dir *dirlist_top(int hint) {
struct dir *t;
int i = winrows-3, visible = 0;
if(hint == -2 || hint == -3)
top = NULL;
/* check whether the current selected item is within the visible window */
if(top) {
i = winrows-3;
t = dirlist_get(0);
while(t && i--) {
if(t == top) {
visible++;
break;
}
t = dirlist_prev(t);
}
}
/* otherwise, get a new top */
if(!visible)
top = hint == -1 || hint == -4 ? dirlist_get(0) :
hint == 1 ? dirlist_get(-1*(winrows-4)) :
dirlist_get(-1*(winrows-3)/2);
/* also make sure that if the list is longer than the window and the last
* item is visible, that this last item is also the last on the window */
t = top;
i = winrows-3;
while(t && i--)
t = dirlist_next(t);
t = top;
do {
top = t;
t = dirlist_prev(t);
} while(t && i-- > 0);
return top;
}
void dirlist_set_sort(int col, int desc, int df) { void dirlist_set_sort(int col, int desc, int df) {
/* update config */ /* update config */
if(col != DL_NOCHANGE) if(col != DL_NOCHANGE)
@ -297,11 +358,13 @@ void dirlist_set_sort(int col, int desc, int df) {
dirlist_parent->next = head_real; dirlist_parent->next = head_real;
else else
head = head_real; head = head_real;
dirlist_top(-3);
} }
void dirlist_set_hidden(int hidden) { void dirlist_set_hidden(int hidden) {
dirlist_hidden = hidden; dirlist_hidden = hidden;
dirlist_fixup(); dirlist_fixup();
dirlist_top(-5);
} }

View file

@ -49,6 +49,9 @@ struct dir *dirlist_next(struct dir *);
* hidden items aren't considered */ * hidden items aren't considered */
struct dir *dirlist_get(int i); struct dir *dirlist_get(int i);
/* Get/set the first visible item in the list on the screen */
struct dir *dirlist_top(int hint);
/* Set selected dir (must be in the currently opened directory, obviously) */ /* Set selected dir (must be in the currently opened directory, obviously) */
void dirlist_select(struct dir *); void dirlist_select(struct dir *);