Merge pull request #2486 from khaneliman/workspace-sorting

feat: hyprland workspaces add sort-by
pull/2470/head^2
Alexis Rouillard 2023-09-11 09:21:35 +02:00 committed by GitHub
commit 05a2af2d7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 187 additions and 36 deletions

View File

@ -11,6 +11,7 @@
#include "AModule.hpp" #include "AModule.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "modules/hyprland/backend.hpp" #include "modules/hyprland/backend.hpp"
#include "util/enum.hpp"
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
@ -80,11 +81,21 @@ class Workspaces : public AModule, public EventHandler {
void create_workspace(Json::Value& value); void create_workspace(Json::Value& value);
void remove_workspace(std::string name); void remove_workspace(std::string name);
void set_urgent_workspace(std::string windowaddress); void set_urgent_workspace(std::string windowaddress);
void parse_config(const Json::Value& config);
void register_ipc();
bool all_outputs_ = false; bool all_outputs_ = false;
bool show_special_ = false; bool show_special_ = false;
bool active_only_ = false; bool active_only_ = false;
enum class SORT_METHOD { ID, NAME, NUMBER, DEFAULT };
util::EnumParser<SORT_METHOD> enum_parser_;
SORT_METHOD sort_by_ = SORT_METHOD::DEFAULT;
std::map<std::string, SORT_METHOD> sort_map_ = {{"ID", SORT_METHOD::ID},
{"NAME", SORT_METHOD::NAME},
{"NUMBER", SORT_METHOD::NUMBER},
{"DEFAULT", SORT_METHOD::DEFAULT}};
void fill_persistent_workspaces(); void fill_persistent_workspaces();
void create_persistent_workspaces(); void create_persistent_workspaces();
std::vector<std::string> persistent_workspaces_to_create_; std::vector<std::string> persistent_workspaces_to_create_;

View File

@ -0,0 +1,19 @@
#pragma once
#include <map>
#include <stdexcept>
#include <string>
namespace waybar::util {
template <typename EnumType>
struct EnumParser {
public:
EnumParser();
~EnumParser();
EnumType parseStringToEnum(const std::string& str,
const std::map<std::string, EnumType>& enumMap);
};
} // namespace waybar::util

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <iostream>
#include <string> #include <string>
const std::string WHITESPACE = " \n\r\t\f\v"; const std::string WHITESPACE = " \n\r\t\f\v";
@ -15,3 +16,10 @@ inline std::string rtrim(const std::string& s) {
} }
inline std::string trim(const std::string& s) { return rtrim(ltrim(s)); } inline std::string trim(const std::string& s) { return rtrim(ltrim(s)); }
inline std::string capitalize(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c) { return std::toupper(c); });
return result;
}

View File

@ -36,6 +36,14 @@ Addressed by *hyprland/workspaces*
default: false ++ default: false ++
If set to true, only the active workspace will be shown. 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 # FORMAT REPLACEMENTS
*{id}*: id of workspace assigned by compositor *{id}*: id of workspace assigned by compositor

View File

@ -273,28 +273,39 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert
- *waybar-cpu(5)* - *waybar-cpu(5)*
- *waybar-custom(5)* - *waybar-custom(5)*
- *waybar-disk(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-idle-inhibitor(5)*
- *waybar-image(5)* - *waybar-image(5)*
- *waybar-inhibitor(5)*
- *waybar-jack(5)*
- *waybar-keyboard-state(5)* - *waybar-keyboard-state(5)*
- *waybar-memory(5)* - *waybar-memory(5)*
- *waybar-mpd(5)* - *waybar-mpd(5)*
- *waybar-mpris(5)* - *waybar-mpris(5)*
- *waybar-network(5)* - *waybar-network(5)*
- *waybar-pulseaudio(5)* - *waybar-pulseaudio(5)*
- *waybar-river-layout(5)*
- *waybar-river-mode(5)* - *waybar-river-mode(5)*
- *waybar-river-tags(5)* - *waybar-river-tags(5)*
- *waybar-river-window(5)* - *waybar-river-window(5)*
- *waybar-river-layout(5)* - *waybar-sndio(5)*
- *waybar-states(5)* - *waybar-states(5)*
- *waybar-sway-language(5)*
- *waybar-sway-mode(5)* - *waybar-sway-mode(5)*
- *waybar-sway-scratchpad(5)* - *waybar-sway-scratchpad(5)*
- *waybar-sway-window(5)* - *waybar-sway-window(5)*
- *waybar-sway-workspaces(5)* - *waybar-sway-workspaces(5)*
- *waybar-temperature(5)*
- *waybar-tray(5)*
- *waybar-upower(5)*
- *waybar-wireplumber(5)* - *waybar-wireplumber(5)*
- *waybar-wlr-taskbar(5)* - *waybar-wlr-taskbar(5)*
- *waybar-wlr-workspaces(5)* - *waybar-wlr-workspaces(5)*
- *waybar-temperature(5)*
- *waybar-tray(5)*
# SEE ALSO # SEE ALSO

View File

@ -171,6 +171,7 @@ src_files = files(
'src/client.cpp', 'src/client.cpp',
'src/config.cpp', 'src/config.cpp',
'src/group.cpp', 'src/group.cpp',
'src/util/enum.cpp',
'src/util/prepare_for_sleep.cpp', 'src/util/prepare_for_sleep.cpp',
'src/util/ustring_clen.cpp', 'src/util/ustring_clen.cpp',
'src/util/sanitize_str.cpp', 'src/util/sanitize_str.cpp',

View File

@ -14,6 +14,20 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
: AModule(config, "workspaces", id, false, false), : AModule(config, "workspaces", id, false, false),
bar_(bar), bar_(bar),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) { box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
parse_config(config);
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"]; Json::Value config_format = config["format"];
format_ = config_format.isString() ? config_format.asString() : "{name}"; format_ = config_format.isString() ? config_format.asString() : "{name}";
@ -43,18 +57,26 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
active_only_ = config_active_only.asBool(); active_only_ = config_active_only.asBool();
} }
box_.set_name("workspaces"); auto config_sort_by = config_["sort-by"];
if (!id.empty()) { if (config_sort_by.isString()) {
box_.get_style_context()->add_class(id); auto sort_by_str = config_sort_by.asString();
try {
sort_by_ = enum_parser_.parseStringToEnum(sort_by_str, sort_map_);
} catch (const std::invalid_argument &e) {
// Handle the case where the string is not a valid enum representation.
sort_by_ = 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; modulesReady = true;
if (!gIPC) { if (!gIPC) {
gIPC = std::make_unique<IPC>(); gIPC = std::make_unique<IPC>();
} }
init();
gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("workspace", this);
gIPC->registerForIPC("createworkspace", this); gIPC->registerForIPC("createworkspace", this);
gIPC->registerForIPC("destroyworkspace", this); gIPC->registerForIPC("destroyworkspace", this);
@ -406,36 +428,62 @@ void Workspace::update(const std::string &format, const std::string &icon) {
void Workspaces::sort_workspaces() { void Workspaces::sort_workspaces() {
std::sort(workspaces_.begin(), workspaces_.end(), std::sort(workspaces_.begin(), workspaces_.end(),
[](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) { [&](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
// normal -> named persistent -> named -> special -> named special // 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());
// both normal (includes numbered persistent) => sort by ID switch (sort_by_) {
if (a->id() > 0 && b->id() > 0) { case SORT_METHOD::ID:
return a->id() < b->id(); return is_id_less;
case SORT_METHOD::NAME:
return is_name_less;
case SORT_METHOD::NUMBER:
try {
return is_number_less;
} catch (const std::invalid_argument &) {
// Handle the exception if necessary.
break;
}
case 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;
} }
// one normal, one special => normal first // Return a default value if none of the cases match.
if ((a->is_special()) ^ (b->is_special())) { return is_name_less; // You can adjust this to your specific needs.
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();
}
// sort non-special named workspaces by name (ID <= -1377)
return a->name() < b->name();
}); });
for (size_t i = 0; i < workspaces_.size(); ++i) { for (size_t i = 0; i < workspaces_.size(); ++i) {

45
src/util/enum.cpp 100644
View File

@ -0,0 +1,45 @@
#include "util/enum.hpp"
#include <algorithm> // for std::transform
#include <cctype> // for std::toupper
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include "modules/hyprland/workspaces.hpp"
#include "util/string.hpp"
namespace waybar::util {
template <typename EnumType>
EnumParser<EnumType>::EnumParser() = default;
template <typename EnumType>
EnumParser<EnumType>::~EnumParser() = default;
template <typename EnumType>
EnumType EnumParser<EnumType>::parseStringToEnum(const std::string& str,
const std::map<std::string, EnumType>& enumMap) {
// Convert the input string to uppercase
std::string uppercaseStr = capitalize(str);
// Capitalize the map keys before searching
std::map<std::string, EnumType> capitalizedEnumMap;
std::transform(
enumMap.begin(), enumMap.end(), std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()),
[this](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); });
// Return enum match of string
auto it = capitalizedEnumMap.find(uppercaseStr);
if (it != capitalizedEnumMap.end()) return it->second;
// Throw error if it doesn't return
throw std::invalid_argument("Invalid string representation for enum");
}
// Explicit instantiations for specific EnumType types you intend to use
// Add explicit instantiations for all relevant EnumType types
template struct EnumParser<modules::hyprland::Workspaces::SORT_METHOD>;
} // namespace waybar::util