Skip to content

Commit 974b0a3

Browse files
committed
[View] Apply a requested selection after the reload that carries it
rofi_view_set_selected_line() resolves the line against line_map, which only describes content the view has already loaded. A mode that reloads and selects in one go cannot satisfy that: rofi_view_reload() arms the backend's reload timer, and line_map is rebuilt later, in rofi_view_refilter_real(). The lookup therefore ran against the previous list and fell back to row 0 whenever the requested line was out of its range, leaving rofi_view_get_selected_line() and listview_get_selected() disagreeing. Record such a request in state->pending_selected_line instead of falling back to row 0, and apply it in refilter, in the same pass that installs the new content and before it repaints, so no frame shows the wrong row. Requests are one-shot and honoured only on the reload path: state->selected_line is stale between activations, so re-applying it on an ordinary refilter would move the selection away from where the user left it. Script mode's keep-selection is affected: it bounds-checks new-selection against the new list length, then resolves it against the old line_map.
1 parent 108f494 commit 974b0a3

2 files changed

Lines changed: 49 additions & 11 deletions

File tree

include/view-internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ struct RofiViewState {
101101
int skip_absorb;
102102
/** The selected line (in the unfiltered list) */
103103
unsigned int selected_line;
104+
/** Selection requested before the content it refers to was loaded, applied
105+
* on the next reload. UINT32_MAX when there is none. */
106+
unsigned int pending_selected_line;
104107
/** The previously selected line (in the unfiltered list) */
105108
unsigned int previous_line;
106109
/** The return state of the view */

source/view.c

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -344,20 +344,38 @@ void rofi_view_set_active(RofiViewState *state) {
344344
rofi_view_queue_redraw();
345345
}
346346

347+
/**
348+
* @param state The current RofiViewState
349+
* @param line The line (in the unfiltered list) to move the selection to
350+
*
351+
* Move the selection to `line`, if it is part of the loaded content.
352+
*
353+
* @returns TRUE when the line was found.
354+
*/
355+
static gboolean rofi_view_select_line(RofiViewState *state, unsigned int line) {
356+
if (line == UINT32_MAX) {
357+
return FALSE;
358+
}
359+
for (unsigned int i = 0; i < state->filtered_lines; i++) {
360+
if (state->line_map[i] == line) {
361+
listview_set_selected(state->list_view, i);
362+
return TRUE;
363+
}
364+
}
365+
return FALSE;
366+
}
367+
347368
void rofi_view_set_selected_line(RofiViewState *state,
348369
unsigned int selected_line) {
349370
state->selected_line = selected_line;
350-
// Find the line.
351-
unsigned int selected = 0;
352-
for (unsigned int i = 0; ((state->selected_line)) < UINT32_MAX && !selected &&
353-
i < state->filtered_lines;
354-
i++) {
355-
if (state->line_map[i] == (state->selected_line)) {
356-
selected = i;
357-
break;
358-
}
371+
/* line_map only describes content the view has already loaded; a reload
372+
* rebuilds it later, in rofi_view_refilter_real(). Record what cannot be
373+
* resolved yet instead of falling back to row 0, which would show the wrong
374+
* selection until that reload lands. */
375+
state->pending_selected_line = selected_line;
376+
if (rofi_view_select_line(state, selected_line)) {
377+
state->pending_selected_line = UINT32_MAX;
359378
}
360-
listview_set_selected(state->list_view, selected);
361379
#ifdef ENABLE_XCB
362380
if (config.backend == DISPLAY_XCB) {
363381
// Clear the window and force an expose event resulting in a redraw.
@@ -419,7 +437,10 @@ const char *rofi_view_get_user_input(const RofiViewState *state) {
419437
* @returns a new 0 initialized RofiViewState
420438
*/
421439
static RofiViewState *__rofi_view_state_create(void) {
422-
return g_malloc0(sizeof(RofiViewState));
440+
RofiViewState *state = g_malloc0(sizeof(RofiViewState));
441+
// 0 is a valid line, so the sentinel has to be set explicitly.
442+
state->pending_selected_line = UINT32_MAX;
443+
return state;
423444
}
424445

425446
/**
@@ -784,9 +805,11 @@ static gboolean rofi_view_refilter_real(RofiViewState *state) {
784805
}
785806
GTimer *timer = g_timer_new();
786807
TICK_N("Filter start");
808+
gboolean content_reloaded = FALSE;
787809
if (state->reload) {
788810
_rofi_view_reload_row(state);
789811
state->reload = FALSE;
812+
content_reloaded = TRUE;
790813
}
791814
TICK_N("Filter reload rows");
792815
if (state->tokens) {
@@ -882,6 +905,18 @@ static gboolean rofi_view_refilter_real(RofiViewState *state) {
882905
TICK_N("Filter matching done");
883906
listview_set_num_elements(state->list_view, state->filtered_lines);
884907

908+
/* line_map now describes the content this pass installed, so a selection
909+
* requested before it existed can be resolved. Doing so before the
910+
* rofi_view_update() below keeps the wrong row off the first frame. Only on
911+
* the reload path: a plain refilter would consume the request and drop it. */
912+
if (content_reloaded && state->pending_selected_line != UINT32_MAX) {
913+
unsigned int pending = state->pending_selected_line;
914+
state->pending_selected_line = UINT32_MAX;
915+
if (rofi_view_select_line(state, pending)) {
916+
state->selected_line = pending;
917+
}
918+
}
919+
885920
if (state->tb_filtered_rows) {
886921
char *r = g_strdup_printf("%u", state->filtered_lines);
887922
textbox_text(state->tb_filtered_rows, r);

0 commit comments

Comments
 (0)