workspace: rework workspace prev|next like i3

Changes workspace prev|next commands to visit each numbered or named
workspace first before considering workspace from the other category
This commit is contained in:
Ronan Pigott 2020-12-04 19:56:51 -07:00 committed by Tudor Brindus
parent 89ffeaae49
commit 5f1fe33d36
1 changed files with 142 additions and 34 deletions

View File

@ -382,6 +382,148 @@ struct sway_workspace *workspace_by_name(const char *name) {
}
}
static int workspace_get_number(struct sway_workspace *workspace) {
char *endptr = NULL;
errno = 0;
long long n = strtoll(workspace->name, &endptr, 10);
if (errno != 0 || n > INT32_MAX || n < 0 || endptr == workspace->name) {
n = -1;
}
return n;
}
struct sway_workspace *workspace_prev(struct sway_workspace *workspace) {
int n = workspace_get_number(workspace);
struct sway_workspace *prev = NULL, *last = NULL, *other = NULL;
bool found = false;
if (n < 0) {
// Find the prev named workspace
int othern = -1;
for (int i = root->outputs->length - 1; i >= 0; i--) {
struct sway_output *output = root->outputs->items[i];
for (int j = output->workspaces->length - 1; j >= 0; j--) {
struct sway_workspace *ws = output->workspaces->items[j];
int wsn = workspace_get_number(ws);
if (!last) {
// The first workspace in reverse order
last = ws;
}
if (!other || (wsn >= 0 && wsn > othern)) {
// The last (greatest) numbered workspace.
other = ws;
othern = workspace_get_number(other);
}
if (ws == workspace) {
found = true;
} else if (wsn < 0 && found) {
// Found a non-numbered workspace before current
return ws;
}
}
}
} else {
// Find the prev numbered workspace
int prevn = -1, lastn = -1;
for (int i = root->outputs->length - 1; i >= 0; i--) {
struct sway_output *output = root->outputs->items[i];
for (int j = output->workspaces->length - 1; j >= 0; j--) {
struct sway_workspace *ws = output->workspaces->items[j];
int wsn = workspace_get_number(ws);
if (!last || (wsn >= 0 && wsn > lastn)) {
// The greatest numbered (or last) workspace
last = ws;
lastn = workspace_get_number(last);
}
if (!other && wsn < 0) {
// The last named workspace
other = ws;
}
if (wsn < 0) {
// Haven't reached the numbered workspaces
continue;
}
if (wsn < n && (!prev || wsn > prevn)) {
// The closest workspace before the current
prev = ws;
prevn = workspace_get_number(prev);
}
}
}
}
if (!prev) {
prev = other ? other : last;
}
return prev;
}
struct sway_workspace *workspace_next(struct sway_workspace *workspace) {
int n = workspace_get_number(workspace);
struct sway_workspace *next = NULL, *first = NULL, *other = NULL;
bool found = false;
if (n < 0) {
// Find the next named workspace
int othern = -1;
for (int i = 0; i < root->outputs->length; i++) {
struct sway_output *output = root->outputs->items[i];
for (int j = 0; j < output->workspaces->length; j++) {
struct sway_workspace *ws = output->workspaces->items[j];
int wsn = workspace_get_number(ws);
if (!first) {
// The first named workspace
first = ws;
}
if (!other || (wsn >= 0 && wsn < othern)) {
// The first (least) numbered workspace
other = ws;
othern = workspace_get_number(other);
}
if (ws == workspace) {
found = true;
} else if (wsn < 0 && found) {
// The first non-numbered workspace after the current
return ws;
}
}
}
} else {
// Find the next numbered workspace
int nextn = -1, firstn = -1;
for (int i = 0; i < root->outputs->length; i++) {
struct sway_output *output = root->outputs->items[i];
for (int j = 0; j < output->workspaces->length; j++) {
struct sway_workspace *ws = output->workspaces->items[j];
int wsn = workspace_get_number(ws);
if (!first || (wsn >= 0 && wsn < firstn)) {
// The first (or least numbered) workspace
first = ws;
firstn = workspace_get_number(first);
}
if (!other && wsn < 0) {
// The first non-numbered workspace
other = ws;
}
if (wsn < 0) {
// Checked all the numbered workspaces
break;
}
if (n < wsn && (!next || wsn < nextn)) {
// The first workspace numerically after the current
next = ws;
nextn = workspace_get_number(next);
}
}
}
}
if (!next) {
// If there is no next workspace from the same category, return the
// first from this category.
next = other ? other : first;
}
return next;
}
/**
* Get the previous or next workspace on the specified output. Wraps around at
* the end and beginning. If next is false, the previous workspace is returned,
@ -409,50 +551,16 @@ static struct sway_workspace *workspace_output_prev_next_impl(
return output->workspaces->items[new_index];
}
/**
* Get the previous or next workspace. If the first/last workspace on an output
* is active, proceed to the previous/next output's previous/next workspace.
*/
static struct sway_workspace *workspace_prev_next_impl(
struct sway_workspace *workspace, int dir) {
struct sway_output *output = workspace->output;
int index = list_find(output->workspaces, workspace);
int new_index = index + dir;
if (new_index >= 0 && new_index < output->workspaces->length) {
return output->workspaces->items[new_index];
}
// Look on a different output
int output_index = list_find(root->outputs, output);
new_index = wrap(output_index + dir, root->outputs->length);
output = root->outputs->items[new_index];
if (dir == 1) {
return output->workspaces->items[0];
} else {
return output->workspaces->items[output->workspaces->length - 1];
}
}
struct sway_workspace *workspace_output_next(
struct sway_workspace *current, bool create) {
return workspace_output_prev_next_impl(current->output, 1, create);
}
struct sway_workspace *workspace_next(struct sway_workspace *current) {
return workspace_prev_next_impl(current, 1);
}
struct sway_workspace *workspace_output_prev(
struct sway_workspace *current, bool create) {
return workspace_output_prev_next_impl(current->output, -1, create);
}
struct sway_workspace *workspace_prev(struct sway_workspace *current) {
return workspace_prev_next_impl(current, -1);
}
bool workspace_switch(struct sway_workspace *workspace,
bool no_auto_back_and_forth) {
struct sway_seat *seat = input_manager_current_seat();