feat: allow sorting niri workspaces
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <gtkmm/button.h>
|
#include <gtkmm/button.h>
|
||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
@@ -18,6 +19,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
private:
|
private:
|
||||||
void onEvent(const Json::Value &ev) override;
|
void onEvent(const Json::Value &ev) override;
|
||||||
void doUpdate();
|
void doUpdate();
|
||||||
|
void sortWorkspaces(std::vector<Json::Value> &workspaces) const;
|
||||||
Gtk::Button &addButton(const Json::Value &ws);
|
Gtk::Button &addButton(const Json::Value &ws);
|
||||||
std::string getIcon(const std::string &value, const Json::Value &ws);
|
std::string getIcon(const std::string &value, const Json::Value &ws);
|
||||||
|
|
||||||
@@ -25,6 +27,10 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
// Map from niri workspace id to button.
|
// Map from niri workspace id to button.
|
||||||
std::unordered_map<uint64_t, Gtk::Button> buttons_;
|
std::unordered_map<uint64_t, Gtk::Button> buttons_;
|
||||||
|
|
||||||
|
bool sort_by_id_ = false;
|
||||||
|
bool sort_by_name_ = false;
|
||||||
|
bool sort_by_coordinates_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace waybar::modules::niri
|
} // namespace waybar::modules::niri
|
||||||
|
|||||||
@@ -17,6 +17,23 @@ Addressed by *niri/workspaces*
|
|||||||
default: false ++
|
default: false ++
|
||||||
If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output.
|
If set to false, workspaces will only be shown on the output they are on. If set to true all workspaces will be shown on every output.
|
||||||
|
|
||||||
|
*sort-by-name*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Sort workspaces by name (numeric sort when all names are numbers). Unnamed workspaces fall back to their index on the output.
|
||||||
|
|
||||||
|
*sort-by-coordinates*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Sort workspaces by output and index. If both *sort-by-name* and *sort-by-coordinates* are true, sorting by name is applied.
|
||||||
|
|
||||||
|
*sort-by-id*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Sort workspaces by id, taking precedence over other sort options.
|
||||||
|
|
||||||
|
If none of the sorting options are enabled, workspaces keep their output/index order.
|
||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {value} ++
|
default: {value} ++
|
||||||
|
|||||||
@@ -4,10 +4,34 @@
|
|||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
namespace waybar::modules::niri {
|
namespace waybar::modules::niri {
|
||||||
|
|
||||||
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config)
|
||||||
: AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) {
|
: AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) {
|
||||||
|
const auto config_sort_by_number = config_["sort-by-number"];
|
||||||
|
if (config_sort_by_number.isBool()) {
|
||||||
|
spdlog::warn("[niri/workspaces]: Prefer sort-by-id instead of sort-by-number");
|
||||||
|
sort_by_id_ = config_sort_by_number.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto config_sort_by_id = config_["sort-by-id"];
|
||||||
|
if (config_sort_by_id.isBool()) {
|
||||||
|
sort_by_id_ = config_sort_by_id.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto config_sort_by_name = config_["sort-by-name"];
|
||||||
|
if (config_sort_by_name.isBool()) {
|
||||||
|
sort_by_name_ = config_sort_by_name.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto config_sort_by_coordinates = config_["sort-by-coordinates"];
|
||||||
|
if (config_sort_by_coordinates.isBool()) {
|
||||||
|
sort_by_coordinates_ = config_sort_by_coordinates.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
box_.set_name("workspaces");
|
box_.set_name("workspaces");
|
||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
box_.get_style_context()->add_class(id);
|
box_.get_style_context()->add_class(id);
|
||||||
@@ -41,6 +65,8 @@ void Workspaces::doUpdate() {
|
|||||||
return ws["output"].asString() == bar_.output->name;
|
return ws["output"].asString() == bar_.output->name;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sortWorkspaces(my_workspaces);
|
||||||
|
|
||||||
// Remove buttons for removed workspaces.
|
// Remove buttons for removed workspaces.
|
||||||
for (auto it = buttons_.begin(); it != buttons_.end();) {
|
for (auto it = buttons_.begin(); it != buttons_.end();) {
|
||||||
auto ws = std::find_if(my_workspaces.begin(), my_workspaces.end(),
|
auto ws = std::find_if(my_workspaces.begin(), my_workspaces.end(),
|
||||||
@@ -123,8 +149,7 @@ void Workspaces::doUpdate() {
|
|||||||
for (auto it = my_workspaces.cbegin(); it != my_workspaces.cend(); ++it) {
|
for (auto it = my_workspaces.cbegin(); it != my_workspaces.cend(); ++it) {
|
||||||
const auto &ws = *it;
|
const auto &ws = *it;
|
||||||
|
|
||||||
auto pos = ws["idx"].asUInt() - 1;
|
const auto pos = static_cast<int>(std::distance(my_workspaces.cbegin(), it));
|
||||||
if (alloutputs) pos = it - my_workspaces.cbegin();
|
|
||||||
|
|
||||||
auto &button = buttons_[ws["id"].asUInt64()];
|
auto &button = buttons_[ws["id"].asUInt64()];
|
||||||
box_.reorder_child(button, pos);
|
box_.reorder_child(button, pos);
|
||||||
@@ -193,4 +218,59 @@ std::string Workspaces::getIcon(const std::string &value, const Json::Value &ws)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Workspaces::sortWorkspaces(std::vector<Json::Value> &workspaces) const {
|
||||||
|
auto get_name = [](const Json::Value &ws) -> std::string {
|
||||||
|
if (ws["name"]) return ws["name"].asString();
|
||||||
|
return std::to_string(ws["idx"].asUInt());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto is_numeric = [](const std::string &value) {
|
||||||
|
return !value.empty() &&
|
||||||
|
std::all_of(value.begin(), value.end(), [](unsigned char c) { return std::isdigit(c); });
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool names_are_numeric =
|
||||||
|
std::all_of(workspaces.begin(), workspaces.end(),
|
||||||
|
[&](const auto &ws) { return is_numeric(get_name(ws)); });
|
||||||
|
|
||||||
|
auto compare_numeric_strings = [](const std::string &a, const std::string &b) {
|
||||||
|
if (a.size() != b.size()) return a.size() < b.size();
|
||||||
|
return a < b;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::sort(workspaces.begin(), workspaces.end(), [&](const auto &a, const auto &b) {
|
||||||
|
if (sort_by_id_) {
|
||||||
|
return a["id"].asUInt64() < b["id"].asUInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sort_by_name_) {
|
||||||
|
const auto a_name = get_name(a);
|
||||||
|
const auto b_name = get_name(b);
|
||||||
|
if (a_name == b_name) return a["id"].asUInt64() < b["id"].asUInt64();
|
||||||
|
if (names_are_numeric) return compare_numeric_strings(a_name, b_name);
|
||||||
|
return a_name < b_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sort_by_coordinates_) {
|
||||||
|
const auto &a_output = a["output"].asString();
|
||||||
|
const auto &b_output = b["output"].asString();
|
||||||
|
if (a_output == b_output) {
|
||||||
|
const auto a_idx = a["idx"].asUInt();
|
||||||
|
const auto b_idx = b["idx"].asUInt();
|
||||||
|
if (a_idx == b_idx) return a["id"].asUInt64() < b["id"].asUInt64();
|
||||||
|
return a_idx < b_idx;
|
||||||
|
}
|
||||||
|
return a_output < b_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to sorting by workspace index on each output.
|
||||||
|
const auto &a_output = a["output"].asString();
|
||||||
|
const auto &b_output = b["output"].asString();
|
||||||
|
const auto a_idx = a["idx"].asUInt();
|
||||||
|
const auto b_idx = b["idx"].asUInt();
|
||||||
|
if (a_output == b_output) return a_idx < b_idx;
|
||||||
|
return a_output < b_output;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::niri
|
} // namespace waybar::modules::niri
|
||||||
|
|||||||
Reference in New Issue
Block a user