diff --git a/include/data.h b/include/data.h index 95acd66da5..d04593bf2c 100644 --- a/include/data.h +++ b/include/data.h @@ -642,6 +642,12 @@ struct Con { * workspace is not a named workspace (for named workspaces, num == -1) */ int num; + /** + * Used to track the last selected workspace for a num (in situation where we + * have multiple workspaces with the same num (e.g. 1a, 1b, 1c). + */ + bool num_last_selected; + struct Con *parent; /* The position and size for this con. These coordinates are absolute. Note diff --git a/include/workspace.h b/include/workspace.h index 2193ed0ba9..c3ee2552a2 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -45,6 +45,12 @@ Con *get_existing_workspace_by_name(const char *name); */ Con *get_existing_workspace_by_num(int num); +/** + * Clears the num_last_selected property of all workspaces by workspace number. + * (there can be multiple: 1a, 1b, etc.). + */ +void clear_num_last_selected_by_num(int num); + /** * Returns the first output that is assigned to a workspace specified by the * given name or number. Returns NULL if no such output exists. diff --git a/src/workspace.c b/src/workspace.c index 739358a8a7..03e5b9e19b 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -42,12 +42,58 @@ Con *get_existing_workspace_by_name(const char *name) { * */ Con *get_existing_workspace_by_num(int num) { - Con *output, *workspace = NULL; + Con *output, *first = NULL, *last_select = NULL; TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { - GREP_FIRST(workspace, output_get_content(output), child->num == num); + /* + * iterate through workspaces and get first (as fallback) - otherwise return + * the workspace right after the current workspace (of the same num). + */ + NODES_FOREACH (output_get_content(output)) { + if (child->num != num) { + continue; + } + // select first (will be fallback) + if (!first) { + first = child; + } + + if (child->num_last_selected) { + last_select = child; + } + } } - return workspace; + clear_num_last_selected_by_num(num); + + // selection logic + // if last selected is current -> then check for after_current + if (last_select) { + last_select->num_last_selected = true; + return last_select; + } + + // fallback is return first + if (first) { + first->num_last_selected = true; + } + return first; +} + +/** + * Clears the num_last_selected property of all workspaces by workspace number. + * (there can be multiple: 1a, 1b, etc.). + */ +void clear_num_last_selected_by_num(int num) { + Con *output; + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + // reset num_last_selected + NODES_FOREACH (output_get_content(output)) { + if (child->num != num) { + continue; + } + child->num_last_selected = false; + } + } } /* @@ -438,6 +484,8 @@ void workspace_show(Con *workspace) { current = con_get_workspace(focused); if (workspace == current) { DLOG("Not switching, already there.\n"); + clear_num_last_selected_by_num(workspace->num); + workspace->num_last_selected = true; return; } @@ -543,6 +591,10 @@ void workspace_show(Con *workspace) { /* Push any sticky windows to the now visible workspace. */ output_push_sticky_windows(old_focus); + + /* update num_last_selected for this workspaces with same num (but diff name) */ + clear_num_last_selected_by_num(workspace->num); + workspace->num_last_selected = true; } /* @@ -588,6 +640,7 @@ Con *workspace_next(void) { } } else { /* If currently a numbered workspace, find next numbered workspace. */ + bool found_current = false; TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) @@ -606,12 +659,21 @@ Con *workspace_next(void) { * relative order between the list of workspaces. */ if (current->num < child->num && (!next || child->num < next->num)) next = child; + + /* If two workspaces have the same number, but different names + * (eg '5:a', '5:b') then just take the next one. */ + if (child == current) { + found_current = true; + } else if (found_current && current->num == child->num) { + return child; + } } } } - if (!next) + if (!next) { next = first_opposite ? first_opposite : first; + } return next; } @@ -654,6 +716,7 @@ Con *workspace_prev(void) { } } else { /* If numbered workspace, find previous numbered workspace. */ + bool found_current = false; TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) @@ -672,12 +735,21 @@ Con *workspace_prev(void) { * the relative order between the list of workspaces. */ if (current->num > child->num && (!prev || child->num > prev->num)) prev = child; + + /* If two workspaces have the same number, but different names + * (eg '5:a', '5:b') then just take the previous one. */ + if (child == current) { + found_current = true; + } else if (found_current && current->num == child->num) { + return child; + } } } } - if (!prev) + if (!prev) { prev = first_opposite ? first_opposite : last; + } return prev; } @@ -696,6 +768,7 @@ Con *workspace_next_on_output(void) { next = TAILQ_NEXT(current, nodes); } else { /* If currently a numbered workspace, find next numbered workspace. */ + bool found_current = false; NODES_FOREACH (output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; @@ -706,6 +779,14 @@ Con *workspace_next_on_output(void) { * relative order between the list of workspaces. */ if (current->num < child->num && (!next || child->num < next->num)) next = child; + + /* If two workspaces have the same number, but different names + * (eg '5:a', '5:b') then just take the next one. */ + if (child == current) { + found_current = true; + } else if (found_current && current->num == child->num) { + return child; + } } } @@ -754,6 +835,7 @@ Con *workspace_prev_on_output(void) { prev = NULL; } else { /* If numbered workspace, find previous numbered workspace. */ + bool found_current = false; NODES_FOREACH_REVERSE (output_get_content(output)) { if (child->type != CT_WORKSPACE || child->num == -1) continue; @@ -762,6 +844,14 @@ Con *workspace_prev_on_output(void) { * the relative order between the list of workspaces. */ if (current->num > child->num && (!prev || child->num > prev->num)) prev = child; + + /* If two workspaces have the same number, but different names + * (eg '5:a', '5:b') then just take the previous one. */ + if (child == current) { + found_current = true; + } else if (found_current && current->num == child->num) { + return child; + } } } diff --git a/testcases/t/503-workspace.t b/testcases/t/503-workspace.t index cb0ad92f6d..4605c18010 100644 --- a/testcases/t/503-workspace.t +++ b/testcases/t/503-workspace.t @@ -41,6 +41,10 @@ is(focused_ws, '2', 'workspace 2 on second output'); # ensure workspace 2 stays open open_window; +cmd 'workspace 6:c'; +# ensure workspace 6:c stays open +open_window; + cmd 'focus output right'; is(focused_ws, '1', 'back on workspace 1'); @@ -50,13 +54,25 @@ cmd 'workspace 5'; # ensure workspace 5 stays open open_window; +# numbered w/ name workspaces must be created in reverse order compared to +# other workspace types (because a new numbered w/ name workspace is prepended +# to the list of similarly numbered workspaces). + +cmd 'workspace 6:b'; +# ensure workspace 5 stays open +open_window; + +cmd 'workspace 6:a'; +# ensure workspace 5 stays open +open_window; + ################################################################################ # Use workspace next and verify the correct order. ################################################################################ # The current order should be: -# output 1: 1, 5 -# output 2: 2 +# output 1: 1, 5, 6:a, 6:b +# output 2: 2, 6:c cmd 'workspace 1'; cmd 'workspace next'; # We need to sync after changing focus to a different output to wait for the @@ -72,6 +88,27 @@ cmd 'workspace next'; sync_with_i3; is(focused_ws, '5', 'workspace 5 focused'); +cmd 'workspace next'; +# We need to sync after changing focus to a different output to wait for the +# EnterNotify to be processed, otherwise it will be processed at some point +# later in time and mess up our subsequent tests. +sync_with_i3; + +is(focused_ws, '6:a', 'workspace 6:a focused'); +cmd 'workspace next'; +# We need to sync after changing focus to a different output to wait for the +# EnterNotify to be processed, otherwise it will be processed at some point +# later in time and mess up our subsequent tests. +sync_with_i3; + +is(focused_ws, '6:b', 'workspace 6:b focused'); +cmd 'workspace next'; +# We need to sync after changing focus to a different output to wait for the +# EnterNotify to be processed, otherwise it will be processed at some point +# later in time and mess up our subsequent tests. +sync_with_i3; + +is(focused_ws, '6:c', 'workspace 6:b focused'); ################################################################################ # Now try the same with workspace next_on_output. @@ -81,8 +118,16 @@ cmd 'workspace 1'; cmd 'workspace next_on_output'; is(focused_ws, '5', 'workspace 5 focused'); cmd 'workspace next_on_output'; +is(focused_ws, '6:a', 'workspace 6:a focused'); +cmd 'workspace next_on_output'; +is(focused_ws, '6:b', 'workspace 6:b focused'); +cmd 'workspace next_on_output'; is(focused_ws, '1', 'workspace 1 focused'); +cmd 'workspace prev_on_output'; +is(focused_ws, '6:b', 'workspace 6:b focused'); +cmd 'workspace prev_on_output'; +is(focused_ws, '6:a', 'workspace 6:a focused'); cmd 'workspace prev_on_output'; is(focused_ws, '5', 'workspace 5 focused'); cmd 'workspace prev_on_output'; @@ -94,6 +139,9 @@ cmd 'workspace 2'; # later in time and mess up our subsequent tests. sync_with_i3; +cmd 'workspace prev_on_output'; +is(focused_ws, '6:c', 'workspace 6:c focused'); + cmd 'workspace prev_on_output'; is(focused_ws, '2', 'workspace 2 focused'); diff --git a/testcases/t/528-workspace-next-prev-reversed.t b/testcases/t/528-workspace-next-prev-reversed.t index 4f63e7fe2b..b5c42fb17d 100644 --- a/testcases/t/528-workspace-next-prev-reversed.t +++ b/testcases/t/528-workspace-next-prev-reversed.t @@ -56,9 +56,9 @@ sync_with_i3; # Setup workspaces so that they stay open (with an empty container). # open_window ensures, this # -# numbered named -# output 1 (left) : 1, 2, 3, 6, 7, B, F, C -# output 2 (right): 4, 5, A, D, E +# numbered numbered w/ names named +# output 1 (left): 4, 5, 8:d, 8:e, A, D, E +# output 2 (right): 1, 2, 3, 6, 7, 8:a, 8:b, 8:c B, F, C # ################################################################################ @@ -68,6 +68,11 @@ cmd 'workspace D'; open_window; cmd 'workspace 4'; open_window; cmd 'workspace 5'; open_window; cmd 'workspace E'; open_window; +# numbered w/ name workspaces must be created in reverse order compared to +# other workspace types (because a new numbered w/ name workspace is prepended +# to the list of similarly numbered workspaces). +cmd 'workspace 8:e'; open_window; +cmd 'workspace 8:d'; open_window; cmd 'focus output right'; cmd 'workspace 1'; open_window; @@ -78,10 +83,19 @@ cmd 'workspace F'; open_window; cmd 'workspace 6'; open_window; cmd 'workspace C'; open_window; cmd 'workspace 7'; open_window; +# numbered w/ name workspaces must be created in reverse order compared to +# other workspace types (because a new numbered w/ name workspace is prepended +# to the list of similarly numbered workspaces). +cmd 'workspace 8:c'; open_window; +cmd 'workspace 8:b'; open_window; +cmd 'workspace 8:a'; open_window; ################################################################################ # Use workspace next and verify the correct order. # numbered -> numerical sort +# numbered w/ names -> numerical sort. Workspaces with the same number but +# different names sort by output, followed by reverse creation time on each +# output. # named -> sort by creation time ################################################################################ cmd 'workspace 1'; @@ -94,6 +108,12 @@ assert_next('5'); assert_next('6'); assert_next('7'); +assert_next('8:a'); +assert_next('8:b'); +assert_next('8:c'); +assert_next('8:d'); +assert_next('8:e'); + assert_next('B'); assert_next('F'); assert_next('C'); @@ -112,6 +132,12 @@ assert_prev('C'); assert_prev('F'); assert_prev('B'); +assert_prev('8:e'); +assert_prev('8:d'); +assert_prev('8:c'); +assert_prev('8:b'); +assert_prev('8:a'); + assert_prev('7'); assert_prev('6'); assert_prev('5'); diff --git a/testcases/t/535-workspace-next-prev.t b/testcases/t/535-workspace-next-prev.t index dc881b354a..90d45d32b0 100644 --- a/testcases/t/535-workspace-next-prev.t +++ b/testcases/t/535-workspace-next-prev.t @@ -56,9 +56,9 @@ sync_with_i3; # Setup workspaces so that they stay open (with an empty container). # open_window ensures, this # -# numbered named -# output 1 (left) : 1, 2, 3, 6, 7, B, F, C -# output 2 (right): 4, 5, A, D, E +# numbered numbered w/ names named +# output 1 (left) : 1, 2, 3, 6, 7, 8:a, 8:b, 8:c B, F, C +# output 2 (right): 4, 5, 8:d, 8:e, A, D, E # ################################################################################ @@ -68,6 +68,11 @@ cmd 'workspace D'; open_window; cmd 'workspace 4'; open_window; cmd 'workspace 5'; open_window; cmd 'workspace E'; open_window; +# numbered w/ name workspaces must be created in reverse order compared to +# other workspace types (because a new numbered w/ name workspace is prepended +# to the list of similarly numbered workspaces). +cmd 'workspace 8:e'; open_window; +cmd 'workspace 8:d'; open_window; cmd 'focus output left'; cmd 'workspace 1'; open_window; @@ -78,10 +83,19 @@ cmd 'workspace F'; open_window; cmd 'workspace 6'; open_window; cmd 'workspace C'; open_window; cmd 'workspace 7'; open_window; +# numbered w/ name workspaces must be created in reverse order compared to +# other workspace types (because a new numbered w/ name workspace is prepended +# to the list of similarly numbered workspaces). +cmd 'workspace 8:c'; open_window; +cmd 'workspace 8:b'; open_window; +cmd 'workspace 8:a'; open_window; ################################################################################ # Use workspace next and verify the correct order. # numbered -> numerical sort +# numbered w/ names -> numerical sort. Workspaces with the same number but +# different names sort by output, followed by reverse creation time on each +# output. # named -> sort by creation time ################################################################################ cmd 'workspace 1'; @@ -94,6 +108,12 @@ assert_next('5'); assert_next('6'); assert_next('7'); +assert_next('8:a'); +assert_next('8:b'); +assert_next('8:c'); +assert_next('8:d'); +assert_next('8:e'); + assert_next('B'); assert_next('F'); assert_next('C'); @@ -112,6 +132,12 @@ assert_prev('C'); assert_prev('F'); assert_prev('B'); +assert_prev('8:e'); +assert_prev('8:d'); +assert_prev('8:c'); +assert_prev('8:b'); +assert_prev('8:a'); + assert_prev('7'); assert_prev('6'); assert_prev('5');