Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: hyprland workspaces add sort-by #2486

Merged
merged 10 commits into from
Sep 11, 2023
5 changes: 5 additions & 0 deletions include/modules/hyprland/workspaces.hpp
Expand Up @@ -11,6 +11,7 @@
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"
#include "util/enum.hpp"

namespace waybar::modules::hyprland {

Expand Down Expand Up @@ -80,10 +81,14 @@ class Workspaces : public AModule, public EventHandler {
void create_workspace(Json::Value& value);
void remove_workspace(std::string name);
void set_urgent_workspace(std::string windowaddress);
void parse_config(const Json::Value& config);
void register_ipc();

bool all_outputs_ = false;
bool show_special_ = false;
bool active_only_ = false;
util::EnumParser enum_parser_;
util::EnumParser::SORT_METHOD sort_by_ = util::EnumParser::SORT_METHOD::DEFAULT;

void fill_persistent_workspaces();
void create_persistent_workspaces();
Expand Down
34 changes: 34 additions & 0 deletions include/util/enum.hpp
@@ -0,0 +1,34 @@
#pragma once

#include <iostream>
#include <map>
#include <string>

namespace waybar::util {

struct EnumParser {
khaneliman marked this conversation as resolved.
Show resolved Hide resolved
EnumParser() {}

enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT };

SORT_METHOD sortStringToEnum(const std::string& str) {
// Convert the input string to uppercase (make it lenient on config input)
std::string uppercaseStr;
for (char c : str) {
uppercaseStr += std::toupper(c);
}

static const std::map<std::string, SORT_METHOD> enumMap = {
{"ID", ID}, {"NAME", NAME}, {"NUMBER", NUMBER}, {"DEFAULT", DEFAULT}};
khaneliman marked this conversation as resolved.
Show resolved Hide resolved

auto it = enumMap.find(uppercaseStr);
if (it != enumMap.end()) {
return it->second;
khaneliman marked this conversation as resolved.
Show resolved Hide resolved
} else {
throw std::invalid_argument("Invalid string representation for enum");
}
}

~EnumParser() = default;
};
} // namespace waybar::util
8 changes: 8 additions & 0 deletions man/waybar-hyprland-workspaces.5.scd
Expand Up @@ -36,6 +36,14 @@ Addressed by *hyprland/workspaces*
default: false ++
If set to true, only the active workspace will be shown.

*sort-by*: ++
typeof: string ++
default: "default" ++
If set to number, workspaces will sort by number.
If set to name, workspaces will sort by name.
If set to id, workspaces will sort by id.
If none of those, workspaces will sort with default behavior.

# FORMAT REPLACEMENTS

*{id}*: id of workspace assigned by compositor
Expand Down
17 changes: 14 additions & 3 deletions man/waybar.5.scd.in
Expand Up @@ -273,28 +273,39 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert
- *waybar-cpu(5)*
- *waybar-custom(5)*
- *waybar-disk(5)*
- *waybar-dwl-tags(5)*
- *waybar-gamemode(5)*
- *waybar-hyprland-language(5)*
- *waybar-hyprland-submap(5)*
- *waybar-hyprland-window(5)*
- *waybar-hyprland-workspaces(5)*
- *waybar-idle-inhibitor(5)*
- *waybar-image(5)*
- *waybar-inhibitor(5)*
- *waybar-jack(5)*
- *waybar-keyboard-state(5)*
- *waybar-memory(5)*
- *waybar-mpd(5)*
- *waybar-mpris(5)*
- *waybar-network(5)*
- *waybar-pulseaudio(5)*
- *waybar-river-layout(5)*
- *waybar-river-mode(5)*
- *waybar-river-tags(5)*
- *waybar-river-window(5)*
- *waybar-river-layout(5)*
- *waybar-sndio(5)*
- *waybar-states(5)*
- *waybar-sway-language(5)*
- *waybar-sway-mode(5)*
- *waybar-sway-scratchpad(5)*
- *waybar-sway-window(5)*
- *waybar-sway-workspaces(5)*
- *waybar-temperature(5)*
- *waybar-tray(5)*
- *waybar-upower(5)*
- *waybar-wireplumber(5)*
- *waybar-wlr-taskbar(5)*
- *waybar-wlr-workspaces(5)*
- *waybar-temperature(5)*
- *waybar-tray(5)*

# SEE ALSO

Expand Down
116 changes: 82 additions & 34 deletions src/modules/hyprland/workspaces.cpp
Expand Up @@ -14,6 +14,20 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
: AModule(config, "workspaces", id, false, false),
bar_(bar),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
parse_config(config);
khaneliman marked this conversation as resolved.
Show resolved Hide resolved

box_.set_name("workspaces");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
}
event_box_.add(box_);

register_ipc();

init();
}

auto Workspaces::parse_config(const Json::Value &config) -> void {
Json::Value config_format = config["format"];

format_ = config_format.isString() ? config_format.asString() : "{name}";
Expand Down Expand Up @@ -43,18 +57,26 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
active_only_ = config_active_only.asBool();
}

box_.set_name("workspaces");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
auto config_sort_by = config_["sort-by"];
if (config_sort_by.isString()) {
auto sort_by_str = config_sort_by.asString();
try {
sort_by_ = enum_parser_.sortStringToEnum(sort_by_str);
} catch (const std::invalid_argument &e) {
// Handle the case where the string is not a valid enum representation.
sort_by_ = util::EnumParser::SORT_METHOD::DEFAULT;
g_warning("Invalid string representation for sort-by. Falling back to default sort method.");
}
}
event_box_.add(box_);
}

auto Workspaces::register_ipc() -> void {
modulesReady = true;

if (!gIPC) {
gIPC = std::make_unique<IPC>();
}

init();

gIPC->registerForIPC("workspace", this);
gIPC->registerForIPC("createworkspace", this);
gIPC->registerForIPC("destroyworkspace", this);
Expand Down Expand Up @@ -406,36 +428,62 @@ void Workspace::update(const std::string &format, const std::string &icon) {

void Workspaces::sort_workspaces() {
std::sort(workspaces_.begin(), workspaces_.end(),
[](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
// normal -> named persistent -> named -> special -> named special

// both normal (includes numbered persistent) => sort by ID
if (a->id() > 0 && b->id() > 0) {
return a->id() < b->id();
}

// one normal, one special => normal first
if ((a->is_special()) ^ (b->is_special())) {
return b->is_special();
}

// only one normal, one named
if ((a->id() > 0) ^ (b->id() > 0)) {
return a->id() > 0;
}

// both special
if (a->is_special() && b->is_special()) {
// if one is -99 => put it last
if (a->id() == -99 || b->id() == -99) {
return b->id() == -99;
}
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID <=-1)
return a->name() < b->name();
[&](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
// Helper comparisons
auto is_id_less = a->id() < b->id();
auto is_name_less = a->name() < b->name();
auto is_number_less = std::stoi(a->name()) < std::stoi(b->name());

switch (sort_by_) {
case util::EnumParser::SORT_METHOD::ID:
return is_id_less;
case util::EnumParser::SORT_METHOD::NAME:
return is_name_less;
case util::EnumParser::SORT_METHOD::NUMBER:
try {
return is_number_less;
} catch (const std::invalid_argument &) {
// Handle the exception if necessary.
break;
}
case util::EnumParser::SORT_METHOD::DEFAULT:
default:
// Handle the default case here.
// normal -> named persistent -> named -> special -> named special

// both normal (includes numbered persistent) => sort by ID
if (a->id() > 0 && b->id() > 0) {
return is_id_less;
}

// one normal, one special => normal first
if ((a->is_special()) ^ (b->is_special())) {
return b->is_special();
}

// only one normal, one named
if ((a->id() > 0) ^ (b->id() > 0)) {
return a->id() > 0;
}

// both special
if (a->is_special() && b->is_special()) {
// if one is -99 => put it last
if (a->id() == -99 || b->id() == -99) {
return b->id() == -99;
}
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID
// <=-1)
return is_name_less;
}

// sort non-special named workspaces by name (ID <= -1377)
return is_name_less;
break;
}

// sort non-special named workspaces by name (ID <= -1377)
return a->name() < b->name();
// Return a default value if none of the cases match.
return is_name_less; // You can adjust this to your specific needs.
});

for (size_t i = 0; i < workspaces_.size(); ++i) {
Expand Down