fix: match sway workspace order with sway one

Ensure that sway workspaces are always displayed in the same order as
used internally by sway. The previous sorting code always sorted
unnumbered workspaces lexicographically. This isn't the order used by
sway internally. Therefore, commands such as "workspace next" might have
jumped arbitrarily in waybar.

This commit reworks the sorting code such that the internal order is
always obeyed. Additionally, numbered persistent workspaces are inserted
at their natural position at the front of the workspace list while
unnumbered ones are appended. This should match the expectations of
workspace ordering known from sway's behavior.

The changes make the configuration property "numeric-first" unnecessary
as this will always be the case now. There's also no reasonable way
around this behavior now. Otherwise, persistent workspaces would jump
around in the visual representation as soon as they become known to
sway.

Fixes #802
pull/1376/head
Johannes Wienke 2022-01-06 17:49:06 +01:00
parent 621d686310
commit 4a85ec0f59
2 changed files with 39 additions and 40 deletions

View File

@ -69,10 +69,6 @@ Addressed by *sway/workspaces*
typeof: string ++ typeof: string ++
Command to execute when the module is updated. Command to execute when the module is updated.
*numeric-first*: ++
typeof: bool ++
Whether to put workspaces starting with numbers before workspaces that do not start with a number.
*disable-auto-back-and-forth*: ++ *disable-auto-back-and-forth*: ++
typeof: bool ++ typeof: bool ++
Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration. Whether to disable *workspace_auto_back_and_forth* when clicking on workspaces. If this is set to *true*, clicking on a workspace you are already on won't do anything, even if *workspace_auto_back_and_forth* is enabled in the Sway configuration.
@ -120,7 +116,6 @@ n.b.: the list of outputs can be obtained from command line using *swaymsg -t ge
"sway/workspaces": { "sway/workspaces": {
"disable-scroll": true, "disable-scroll": true,
"all-outputs": true, "all-outputs": true,
"numeric-first": false,
"format": "{name}: {icon}", "format": "{name}: {icon}",
"format-icons": { "format-icons": {
"1": "", "1": "",

View File

@ -2,6 +2,7 @@
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <algorithm>
#include <cctype> #include <cctype>
#include <string> #include <string>
@ -98,6 +99,7 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
Json::Value v; Json::Value v;
v["name"] = p_w_name; v["name"] = p_w_name;
v["target_output"] = bar_.output->name; v["target_output"] = bar_.output->name;
v["num"] = convertWorkspaceNameToNum(p_w_name);
workspaces_.emplace_back(std::move(v)); workspaces_.emplace_back(std::move(v));
break; break;
} }
@ -107,57 +109,59 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
Json::Value v; Json::Value v;
v["name"] = p_w_name; v["name"] = p_w_name;
v["target_output"] = ""; v["target_output"] = "";
v["num"] = convertWorkspaceNameToNum(p_w_name);
workspaces_.emplace_back(std::move(v)); workspaces_.emplace_back(std::move(v));
} }
} }
} }
// config option to sort numeric workspace names before others // sway has a defined ordering of workspaces that should be preserved in
bool config_numeric_first = config_["numeric-first"].asBool(); // the representation displayed by waybar to ensure that commands such
// as "workspace prev" or "workspace next" make sense when looking at
// the workspace representation in the bar.
// Due to waybar's own feature of persistent workspaces unknown to sway,
// custom sorting logic is necessary to make these workspaces appear
// naturally in the list of workspaces without messing up sway's
// sorting. For this purpose, a custom numbering property is created
// that preserves the order provided by sway while inserting numbered
// persistent workspaces at their natural positions.
//
// All of this code assumes that sway provides numbered workspaces first
// and other workspaces are sorted by their creation time.
//
// In a first pass, the maximum "num" value is computed to enqueue
// unnumbered workspaces behind numbered ones when computing the sort
// attribute.
int max_num = -1;
for (auto & workspace : workspaces_) {
max_num = std::max(workspace["num"].asInt(), max_num);
}
for (auto & workspace : workspaces_) {
auto workspace_num = workspace["num"].asInt();
if (workspace_num > -1) {
workspace["sort"] = workspace_num;
} else {
workspace["sort"] = ++max_num;
}
}
std::sort(workspaces_.begin(), std::sort(workspaces_.begin(),
workspaces_.end(), workspaces_.end(),
[config_numeric_first](const Json::Value &lhs, const Json::Value &rhs) { [](const Json::Value &lhs, const Json::Value &rhs) {
// the "num" property (integer type):
// The workspace number or -1 for workspaces that do
// not start with a number.
// We could rely on sway providing this property:
//
// auto l = lhs["num"].asInt();
// auto r = rhs["num"].asInt();
//
// We cannot rely on the "num" property as provided by sway
// via IPC, because persistent workspace might not exist in
// sway's view. However, we need this property also for
// not-yet created persistent workspace. As such, we simply
// duplicate sway's logic of assigning the "num" property
// into waybar (see convertWorkspaceNameToNum). This way the
// sorting should work out even when we include workspaces
// that do not currently exist.
auto lname = lhs["name"].asString(); auto lname = lhs["name"].asString();
auto rname = rhs["name"].asString(); auto rname = rhs["name"].asString();
int l = convertWorkspaceNameToNum(lname); int l = lhs["sort"].asInt();
int r = convertWorkspaceNameToNum(rname); int r = rhs["sort"].asInt();
if (l == r) { if (l == r) {
// in case both integers are the same, lexicographical // In case both integers are the same, lexicographical
// sort. This also covers the case when both don't have a // sort. The code above already ensure that this will only
// number (i.e., l == r == -1). // happend in case of explicitly numbered workspaces.
return lname < rname; return lname < rname;
} }
// one of the workspaces doesn't begin with a number, so
// num is -1.
if (l < 0 || r < 0) {
if (config_numeric_first) {
return r < 0;
}
return l < 0;
}
// both workspaces have a "num" so let's just compare those
return l < r; return l < r;
}); });
} }
dp.emit(); dp.emit();
} catch (const std::exception &e) { } catch (const std::exception &e) {