From b7ccf78b90f7fded5f6627babcd7b0e92ac807fd Mon Sep 17 00:00:00 2001 From: Yorhel <git@yorhel.nl> Date: Wed, 28 Apr 2010 13:32:30 +0200 Subject: [PATCH] More intuitive multi-page browsing Here is the new multi-page listing functionality I promised in 5db9c2aea11052451c7e11bf8eef73393e4a072e. It may look very easy, but getting this to work right wasn't, unfortunately. --- ChangeLog | 1 + src/browser.c | 20 +++++++++++----- src/calc.c | 1 + src/delete.c | 1 + src/dirlist.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/dirlist.h | 3 +++ 6 files changed, 84 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index d98653a..d2428e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ git - ? - Fixed crash on browsing dirs with a small window size (#2991787) - Fixed buffer overflow when some directories can't be scanned (#2981704) - Improved browsing performance + - More intuitive multi-page browsing - Various minor fixes 1.6 - 2009-10-23 diff --git a/src/browser.c b/src/browser.c index 3d43159..325d993 100644 --- a/src/browser.c +++ b/src/browser.c @@ -205,12 +205,10 @@ void browse_draw() { return; /* get start position */ - t = dirlist_get(-1*((winrows)/2)); - if(t == dirlist_next(NULL)) - t = NULL; + t = dirlist_top(0); /* 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); /* save the selected row number for later */ if(t->flags & FF_BSEL) @@ -284,28 +282,34 @@ int browse_key(int ch) { case KEY_UP: case 'k': dirlist_select(dirlist_get(-1)); + dirlist_top(-1); info_start = 0; break; case KEY_DOWN: case 'j': dirlist_select(dirlist_get(1)); + dirlist_top(1); info_start = 0; break; case KEY_HOME: dirlist_select(dirlist_next(NULL)); + dirlist_top(2); info_start = 0; break; case KEY_LL: case KEY_END: dirlist_select(dirlist_get(1<<30)); + dirlist_top(1); info_start = 0; break; case KEY_PPAGE: dirlist_select(dirlist_get(-1*(winrows-3))); + dirlist_top(-1); info_start = 0; break; case KEY_NPAGE: dirlist_select(dirlist_get(winrows-3)); + dirlist_top(1); info_start = 0; break; @@ -338,15 +342,19 @@ int browse_key(int ch) { case 10: case KEY_RIGHT: case 'l': - if(sel != NULL && sel->sub != NULL) + if(sel != NULL && sel->sub != NULL) { dirlist_open(sel->sub); + dirlist_top(-3); + } info_show = 0; break; case KEY_LEFT: case 'h': case '<': - if(sel != NULL && sel->parent->parent != NULL) + if(sel != NULL && sel->parent->parent != NULL) { dirlist_open(sel->parent); + dirlist_top(-3); + } info_show = 0; break; diff --git a/src/calc.c b/src/calc.c index 78afde0..7a73878 100644 --- a/src/calc.c +++ b/src/calc.c @@ -509,6 +509,7 @@ int calc_process() { freedir(orig); } browse_init(root->sub); + dirlist_top(-3); return 0; } diff --git a/src/delete.c b/src/delete.c index b26ef54..fdc4467 100644 --- a/src/delete.c +++ b/src/delete.c @@ -236,6 +236,7 @@ void delete_process() { else { nextsel->flags |= FF_BSEL; browse_init(nextsel); + dirlist_top(-4); } } diff --git a/src/dirlist.c b/src/dirlist.c index 9e43eec..2239914 100644 --- a/src/dirlist.c +++ b/src/dirlist.c @@ -40,7 +40,7 @@ int dirlist_sort_desc = 1, /* private state vars */ 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) { /* update config */ if(col != DL_NOCHANGE) @@ -297,11 +358,13 @@ void dirlist_set_sort(int col, int desc, int df) { dirlist_parent->next = head_real; else head = head_real; + dirlist_top(-3); } void dirlist_set_hidden(int hidden) { dirlist_hidden = hidden; dirlist_fixup(); + dirlist_top(-5); } diff --git a/src/dirlist.h b/src/dirlist.h index f3e7c58..e8174d5 100644 --- a/src/dirlist.h +++ b/src/dirlist.h @@ -49,6 +49,9 @@ struct dir *dirlist_next(struct dir *); * hidden items aren't considered */ 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) */ void dirlist_select(struct dir *); -- GitLab