Merge pull request #2486 from khaneliman/workspace-sorting
feat: hyprland workspaces add sort-bypull/2470/head^2
commit
05a2af2d7c
|
@ -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_;
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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,12 +428,32 @@ 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) {
|
||||||
|
// 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 SORT_METHOD::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
|
// normal -> named persistent -> named -> special -> named special
|
||||||
|
|
||||||
// both normal (includes numbered persistent) => sort by ID
|
// both normal (includes numbered persistent) => sort by ID
|
||||||
if (a->id() > 0 && b->id() > 0) {
|
if (a->id() > 0 && b->id() > 0) {
|
||||||
return a->id() < b->id();
|
return is_id_less;
|
||||||
}
|
}
|
||||||
|
|
||||||
// one normal, one special => normal first
|
// one normal, one special => normal first
|
||||||
|
@ -430,12 +472,18 @@ void Workspaces::sort_workspaces() {
|
||||||
if (a->id() == -99 || b->id() == -99) {
|
if (a->id() == -99 || b->id() == -99) {
|
||||||
return b->id() == -99;
|
return b->id() == -99;
|
||||||
}
|
}
|
||||||
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID <=-1)
|
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID
|
||||||
return a->name() < b->name();
|
// <=-1)
|
||||||
|
return is_name_less;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort non-special named workspaces by name (ID <= -1377)
|
// sort non-special named workspaces by name (ID <= -1377)
|
||||||
return a->name() < b->name();
|
return is_name_less;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
for (size_t i = 0; i < workspaces_.size(); ++i) {
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue