From bad6cfae6f638b166d55ad543050c69286038840 Mon Sep 17 00:00:00 2001 From: Bruce Blore Date: Sat, 22 Apr 2023 23:43:04 -0700 Subject: [PATCH 01/25] Added option to calculate battery percentage as total_energy * 100 / total_energy_full --- src/modules/battery.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index abd1240c..83e7398c 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -533,6 +533,13 @@ const std::tuple waybar::modules::Battery::g } } + // Handle weighted-average + if ((config_["weighted-average"].isBool() ? config_["weighted-average"].asBool() : false) && + total_energy_exists && total_energy_full_exists) { + if (total_energy_full > 0.0f) + calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full); + } + // Handle design-capacity if ((config_["design-capacity"].isBool() ? config_["design-capacity"].asBool() : false) && total_energy_exists && total_energy_full_design_exists) { From 936937ec7803eaa4f11e0b94822401b34ad53a4c Mon Sep 17 00:00:00 2001 From: Cherser-s <29800876+Cherser-s@users.noreply.github.com> Date: Mon, 21 Aug 2023 22:03:20 +0300 Subject: [PATCH 02/25] store margins and global window offset in the bar object --- include/bar.hpp | 8 ++++++++ src/bar.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/include/bar.hpp b/include/bar.hpp index 7c5525f6..ee4a1d5e 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -91,6 +91,9 @@ class Bar { bool vertical = false; Gtk::Window window; + int x_global; + int y_global; + #ifdef HAVE_SWAY std::string bar_id; #endif @@ -102,11 +105,16 @@ class Bar { void setupAltFormatKeyForModule(const std::string &module_name); void setupAltFormatKeyForModuleList(const char *module_list_name); void setMode(const bar_mode &); + void onConfigure(GdkEventConfigure *ev); + void configureGlobalOffset(int width, int height); + void onOutputGeometryChanged(); /* Copy initial set of modes to allow customization */ bar_mode_map configured_modes = PRESET_MODES; std::string last_mode_{MODE_DEFAULT}; + struct bar_margins margins_; + std::unique_ptr surface_impl_; Gtk::Box left_; Gtk::Box center_; diff --git a/src/bar.cpp b/src/bar.cpp index 60104f0d..30cf7fad 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -481,6 +481,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) : output(w_output), config(w_config), window{Gtk::WindowType::WINDOW_TOPLEVEL}, + x_global(0), + y_global(0), + margins_{.top = 0, .right = 0, .bottom = 0, .left = 0}, left_(Gtk::ORIENTATION_HORIZONTAL, 0), center_(Gtk::ORIENTATION_HORIZONTAL, 0), right_(Gtk::ORIENTATION_HORIZONTAL, 0), @@ -516,8 +519,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0; uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0; - struct bar_margins margins_; - if (config["margin-top"].isInt() || config["margin-right"].isInt() || config["margin-bottom"].isInt() || config["margin-left"].isInt()) { margins_ = { @@ -563,6 +564,10 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config) margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps}; } + window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure)); + output->monitor->property_geometry().signal_changed().connect( + sigc::mem_fun(*this, &Bar::onOutputGeometryChanged)); + #ifdef HAVE_GTK_LAYER_SHELL bool use_gls = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true; if (use_gls) { @@ -674,6 +679,7 @@ void waybar::Bar::onMap(GdkEventAny*) { */ auto gdk_window = window.get_window()->gobj(); surface = gdk_wayland_window_get_wl_surface(gdk_window); + configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window)); } void waybar::Bar::setVisible(bool value) { @@ -815,3 +821,47 @@ auto waybar::Bar::setupWidgets() -> void { right_.pack_end(*module, false, false); } } + +void waybar::Bar::onConfigure(GdkEventConfigure* ev) { + configureGlobalOffset(ev->width, ev->height); +} + +void waybar::Bar::configureGlobalOffset(int width, int height) { + auto monitor_geometry = *output->monitor->property_geometry().get_value().gobj(); + auto position = config["position"].asString(); + int x; + int y; + if (position == "bottom") { + if (width + margins_.left + margins_.right >= monitor_geometry.width) + x = margins_.left; + else + x = (monitor_geometry.width - width) / 2; + y = monitor_geometry.height - height - margins_.bottom; + } else if (position == "left") { + x = margins_.left; + if (height + margins_.top + margins_.bottom >= monitor_geometry.height) + y = margins_.top; + else + y = (monitor_geometry.height - height) / 2; + } else if (position == "right") { + x = monitor_geometry.width - width - margins_.right; + if (height + margins_.top + margins_.bottom >= monitor_geometry.height) + y = margins_.top; + else + y = (monitor_geometry.height - height) / 2; + } else { + // position is top + if (width + margins_.left + margins_.right >= monitor_geometry.width) + x = margins_.left; + else + x = (monitor_geometry.width - width) / 2; + y = margins_.top; + } + + x_global = x + monitor_geometry.x; + y_global = y + monitor_geometry.y; +} + +void waybar::Bar::onOutputGeometryChanged() { + configureGlobalOffset(window.get_width(), window.get_height()); +} From 65dfabc430fdc531b70da505b1481cbc706f5305 Mon Sep 17 00:00:00 2001 From: Cherser-s <29800876+Cherser-s@users.noreply.github.com> Date: Mon, 21 Aug 2023 22:03:43 +0300 Subject: [PATCH 03/25] sni: fix passing relative coordinates to dbus methods Doesn't correctly handle the case with both margin and width/height being set at the same time. --- include/modules/sni/item.hpp | 2 ++ src/modules/sni/item.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/modules/sni/item.hpp b/include/modules/sni/item.hpp index 423ec7c5..1043157c 100644 --- a/include/modules/sni/item.hpp +++ b/include/modules/sni/item.hpp @@ -84,6 +84,8 @@ class Item : public sigc::trackable { // visibility of items with Status == Passive bool show_passive_ = false; + const Bar& bar_; + Glib::RefPtr proxy_; Glib::RefPtr cancellable_; std::set update_pending_; diff --git a/src/modules/sni/item.cpp b/src/modules/sni/item.cpp index 871621fb..dfaca665 100644 --- a/src/modules/sni/item.cpp +++ b/src/modules/sni/item.cpp @@ -39,7 +39,8 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf object_path(op), icon_size(16), effective_icon_size(0), - icon_theme(Gtk::IconTheme::create()) { + icon_theme(Gtk::IconTheme::create()), + bar_(bar) { if (config["icon-size"].isUInt()) { icon_size = config["icon-size"].asUInt(); } @@ -410,7 +411,8 @@ void Item::makeMenu() { bool Item::handleClick(GdkEventButton* const& ev) { auto parameters = Glib::VariantContainerBase::create_tuple( - {Glib::Variant::create(ev->x), Glib::Variant::create(ev->y)}); + {Glib::Variant::create(ev->x_root + bar_.x_global), + Glib::Variant::create(ev->y_root + bar_.y_global)}); if ((ev->button == 1 && item_is_menu) || ev->button == 3) { makeMenu(); if (gtk_menu != nullptr) { From ce076927f3f5d131031cc1d76de9de4958dbafb8 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 2 Sep 2023 15:22:26 -0500 Subject: [PATCH 04/25] chore: cpplint fixes hyprland headers --- include/modules/hyprland/backend.hpp | 2 ++ include/modules/hyprland/language.hpp | 4 ++++ include/modules/hyprland/submap.hpp | 4 ++++ include/modules/hyprland/window.hpp | 4 ++++ include/modules/hyprland/workspaces.hpp | 7 ++++++- 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/modules/hyprland/backend.hpp b/include/modules/hyprland/backend.hpp index e23b1582..7d97b553 100644 --- a/include/modules/hyprland/backend.hpp +++ b/include/modules/hyprland/backend.hpp @@ -1,10 +1,12 @@ #pragma once + #include #include #include #include #include #include +#include #include "util/json.hpp" diff --git a/include/modules/hyprland/language.hpp b/include/modules/hyprland/language.hpp index 30789d06..eb220609 100644 --- a/include/modules/hyprland/language.hpp +++ b/include/modules/hyprland/language.hpp @@ -1,5 +1,9 @@ +#pragma once + #include +#include + #include "ALabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" diff --git a/include/modules/hyprland/submap.hpp b/include/modules/hyprland/submap.hpp index e2a84981..4ff232ff 100644 --- a/include/modules/hyprland/submap.hpp +++ b/include/modules/hyprland/submap.hpp @@ -1,5 +1,9 @@ +#pragma once + #include +#include + #include "ALabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index fd68b049..c9f0be03 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,5 +1,9 @@ +#pragma once + #include +#include + #include "AAppIconLabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 96443629..b994c9e4 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -1,7 +1,12 @@ +#pragma once + #include #include +#include #include +#include +#include #include "AModule.hpp" #include "bar.hpp" @@ -11,7 +16,7 @@ namespace waybar::modules::hyprland { class Workspace { public: - Workspace(const Json::Value& workspace_data); + explicit Workspace(const Json::Value& workspace_data); std::string& select_icon(std::map& icons_map); Gtk::Button& button() { return button_; }; From 8fc41877135b6480785f9a4a3ed9f8ff934902a6 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 2 Sep 2023 23:21:44 -0500 Subject: [PATCH 05/25] refactor: replace strcpy with snprintf --- src/modules/hyprland/backend.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index 6e586966..def38f81 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -166,9 +166,13 @@ std::string IPC::getSocket1Reply(const std::string& rq) { std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock"; - strcpy(serverAddress.sun_path, socketPath.c_str()); + // Use snprintf to copy the socketPath string into serverAddress.sun_path + if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < 0) { + spdlog::error("Hyprland IPC: Couldn't copy socket path (6)"); + return ""; + } - if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { + if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); return ""; } From 4cb8efbecc3596a1d32ae9186a5414903368b34f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 2 Sep 2023 23:34:11 -0500 Subject: [PATCH 06/25] chore: cpplint fixes hyprland classes --- src/modules/hyprland/backend.cpp | 6 ++++-- src/modules/hyprland/language.cpp | 4 +--- src/modules/hyprland/submap.cpp | 2 +- src/modules/hyprland/window.cpp | 3 +-- src/modules/hyprland/workspaces.cpp | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/modules/hyprland/backend.cpp b/src/modules/hyprland/backend.cpp index def38f81..5a48d369 100644 --- a/src/modules/hyprland/backend.cpp +++ b/src/modules/hyprland/backend.cpp @@ -167,12 +167,14 @@ std::string IPC::getSocket1Reply(const std::string& rq) { std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock"; // Use snprintf to copy the socketPath string into serverAddress.sun_path - if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < 0) { + if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) < + 0) { spdlog::error("Hyprland IPC: Couldn't copy socket path (6)"); return ""; } - if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) { + if (connect(SERVERSOCKET, reinterpret_cast(&serverAddress), sizeof(serverAddress)) < + 0) { spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)"); return ""; } diff --git a/src/modules/hyprland/language.cpp b/src/modules/hyprland/language.cpp index 423e2b5c..5339ee9e 100644 --- a/src/modules/hyprland/language.cpp +++ b/src/modules/hyprland/language.cpp @@ -4,8 +4,7 @@ #include #include -#include - +#include "util/sanitize_str.hpp" #include "util/string.hpp" namespace waybar::modules::hyprland { @@ -97,7 +96,6 @@ void Language::initLanguage() { spdlog::debug("hyprland language initLanguage found {}", layout_.full_name); dp.emit(); - } catch (std::exception& e) { spdlog::error("hyprland language initLanguage failed with {}", e.what()); } diff --git a/src/modules/hyprland/submap.cpp b/src/modules/hyprland/submap.cpp index 22acbf31..d1d9a116 100644 --- a/src/modules/hyprland/submap.cpp +++ b/src/modules/hyprland/submap.cpp @@ -2,7 +2,7 @@ #include -#include +#include "util/sanitize_str.hpp" namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 60de074c..77723bc0 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -6,12 +6,11 @@ #include #include -#include -#include #include #include "modules/hyprland/backend.hpp" #include "util/rewrite_string.hpp" +#include "util/sanitize_str.hpp" namespace waybar::modules::hyprland { diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 75fb19e3..3f27df8a 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -327,7 +327,7 @@ Workspace::Workspace(const Json::Value &workspace_data) button_.set_relief(Gtk::RELIEF_NONE); content_.set_center_widget(label_); button_.add(content_); -}; +} void add_or_remove_class(const Glib::RefPtr &context, bool condition, const std::string &class_name) { From d40ccd0da8cc19654297cbefb3ffad6c7a7a2bd6 Mon Sep 17 00:00:00 2001 From: xercesblue Date: Mon, 4 Sep 2023 16:50:57 -0700 Subject: [PATCH 07/25] modules/hyprland: Fix segfault when attempting to set_urgent on a missing workspace --- src/modules/hyprland/workspaces.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 3f27df8a..346e7754 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -450,7 +450,7 @@ auto Workspace::handle_clicked(GdkEventButton *bt) -> bool { void Workspaces::set_urgent_workspace(std::string windowaddress) { const Json::Value clients_json = gIPC->getSocket1JsonReply("clients"); - int workspace_id; + int workspace_id = -1; for (Json::Value client_json : clients_json) { if (client_json["address"].asString().ends_with(windowaddress)) { @@ -462,7 +462,7 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) { auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(), [&](std::unique_ptr &x) { return x->id() == workspace_id; }); - if (workspace->get() != nullptr) { + if (workspace != workspaces_.end()) { workspace->get()->set_urgent(); } } From 9c49f46b0142267d493a15b38144cb287429a794 Mon Sep 17 00:00:00 2001 From: Maxim Baz Date: Tue, 5 Sep 2023 17:13:25 +0200 Subject: [PATCH 08/25] hyprland/workspaces: react on renameworkspace event --- include/modules/hyprland/workspaces.hpp | 1 + src/modules/hyprland/workspaces.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index b994c9e4..5fd0e457 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -34,6 +34,7 @@ class Workspace { void set_persistent(bool value = true) { is_persistent_ = value; }; void set_urgent(bool value = true) { is_urgent_ = value; }; void set_windows(uint value) { windows_ = value; }; + void set_name(std::string value) { name_ = value; }; void update(const std::string& format, const std::string& icon); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 346e7754..ebb47419 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -55,6 +55,7 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value gIPC->registerForIPC("destroyworkspace", this); gIPC->registerForIPC("focusedmon", this); gIPC->registerForIPC("moveworkspace", this); + gIPC->registerForIPC("renameworkspace", this); gIPC->registerForIPC("openwindow", this); gIPC->registerForIPC("closewindow", this); gIPC->registerForIPC("movewindow", this); @@ -132,6 +133,16 @@ void Workspaces::onEvent(const std::string &ev) { update_window_count(); } else if (eventName == "urgent") { set_urgent_workspace(payload); + } else if (eventName == "renameworkspace") { + std::string workspace_id_str = payload.substr(0, payload.find(',')); + int workspace_id = workspace_id_str == "special" ? -99 : std::stoi(workspace_id_str); + std::string new_name = payload.substr(payload.find(',') + 1); + for (auto &workspace : workspaces_) { + if (workspace->id() == workspace_id) { + workspace->set_name(new_name); + break; + } + } } dp.emit(); From 04b39ea64e04c2a096bcf92a591a8ea9af0975cf Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 10 Aug 2023 19:40:14 +0200 Subject: [PATCH 09/25] hyprland/workspaces: implement 'active_only' option and visible class --- include/modules/hyprland/workspaces.hpp | 11 +++- man/waybar-hyprland-workspaces.5.scd | 13 ++++- src/modules/hyprland/workspaces.cpp | 73 ++++++++++++++++++++----- 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 5fd0e457..270c1e36 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -14,9 +14,11 @@ namespace waybar::modules::hyprland { +class Workspaces; + class Workspace { public: - explicit Workspace(const Json::Value& workspace_data); + explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager); std::string& select_icon(std::map& icons_map); Gtk::Button& button() { return button_; }; @@ -26,6 +28,7 @@ class Workspace { bool active() const { return active_; }; bool is_special() const { return is_special_; }; bool is_persistent() const { return is_persistent_; }; + bool is_visible() const { return is_visible_; }; bool is_empty() const { return windows_ == 0; }; bool is_urgent() const { return is_urgent_; }; @@ -33,12 +36,15 @@ class Workspace { void set_active(bool value = true) { active_ = value; }; void set_persistent(bool value = true) { is_persistent_ = value; }; void set_urgent(bool value = true) { is_urgent_ = value; }; + void set_visible(bool value = true) { is_visible_ = value; }; void set_windows(uint value) { windows_ = value; }; void set_name(std::string value) { name_ = value; }; void update(const std::string& format, const std::string& icon); private: + Workspaces& workspace_manager_; + int id_; std::string name_; std::string output_; @@ -47,6 +53,7 @@ class Workspace { bool is_special_ = false; bool is_persistent_ = false; bool is_urgent_ = false; + bool is_visible_ = false; Gtk::Button button_; Gtk::Box content_; @@ -62,6 +69,7 @@ class Workspaces : public AModule, public EventHandler { auto all_outputs() const -> bool { return all_outputs_; } auto show_special() const -> bool { return show_special_; } + auto active_only() const -> bool { return active_only_; } auto get_bar_output() const -> std::string { return bar_.output->name; } @@ -75,6 +83,7 @@ class Workspaces : public AModule, public EventHandler { bool all_outputs_ = false; bool show_special_ = false; + bool active_only_ = false; void fill_persistent_workspaces(); void create_persistent_workspaces(); diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 7e462358..13764752 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -24,13 +24,18 @@ Addressed by *hyprland/workspaces* *show-special*: ++ typeof: bool ++ default: false ++ - If set to true special workspaces will be shown. + If set to true, special workspaces will be shown. *all-outputs*: ++ typeof: bool ++ default: false ++ If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown. +*active-only*: ++ + typeof: bool ++ + default: false ++ + If set to true, only the active workspace will be shown. + # FORMAT REPLACEMENTS *{id}*: id of workspace assigned by compositor @@ -43,10 +48,11 @@ Addressed by *hyprland/workspaces* Additional to workspace name matching, the following *format-icons* can be set. -- *default*: Will be shown, when no string match is found. +- *default*: Will be shown, when no string match is found and none of the below conditions have defined icons. - *active*: Will be shown, when workspace is active - *special*: Will be shown on non-active special workspaces -- *empty*: Will be shown on empty persistent workspaces +- *empty*: Will be shown on non-active, non-special empty persistent workspaces +- *visible*: Will be shown on workspaces that are visible but not active. For example: this is useful if you want your visible workspaces on other monitors to have the same look as active. - *persistent*: Will be shown on non-empty persistent workspaces # EXAMPLES @@ -95,6 +101,7 @@ Additional to workspace name matching, the following *format-icons* can be set. - *#workspaces button* - *#workspaces button.active* - *#workspaces button.empty* +- *#workspaces button.visible* - *#workspaces button.persistent* - *#workspaces button.special* - *#workspaces button.urgent* diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index ebb47419..64c7a9ed 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -38,6 +38,11 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value show_special_ = config_show_special.asBool(); } + auto config_active_only = config_["active-only"]; + if (config_active_only.isBool()) { + active_only_ = config_active_only.asBool(); + } + box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); @@ -75,11 +80,29 @@ auto Workspaces::update() -> void { workspaces_to_create_.clear(); + // get all active workspaces + auto monitors = gIPC->getSocket1JsonReply("monitors"); + std::vector visible_workspaces; + for (Json::Value &monitor : monitors) { + auto ws = monitor["activeWorkspace"]; + if (ws.isObject() && (ws["name"].isString())) { + visible_workspaces.push_back(ws["name"].asString()); + } + } + for (auto &workspace : workspaces_) { + // active workspace->set_active(workspace->name() == active_workspace_name_); - if (workspace->name() == active_workspace_name_ && workspace.get()->is_urgent()) { + // disable urgency if workspace is active + if (workspace->name() == active_workspace_name_ && workspace->is_urgent()) { workspace->set_urgent(false); } + + // visible + workspace->set_visible(std::find(visible_workspaces.begin(), visible_workspaces.end(), + workspace->name()) != visible_workspaces.end()); + + // set workspace icon std::string &workspace_icon = icons_map_[""]; if (with_icon_) { workspace_icon = workspace->select_icon(icons_map_); @@ -103,9 +126,10 @@ void Workspaces::onEvent(const std::string &ev) { } else if (eventName == "createworkspace") { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { - if (workspace_json["name"].asString() == payload && + std::string name = workspace_json["name"].asString(); + if (name == payload && (all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (show_special() || !workspace_json["name"].asString().starts_with("special"))) { + (show_special() || !name.starts_with("special"))) { workspaces_to_create_.push_back(workspace_json); break; } @@ -120,8 +144,8 @@ void Workspaces::onEvent(const std::string &ev) { if (bar_.output->name == new_output) { // TODO: implement this better const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { - if (workspace_json["name"].asString() == workspace && - bar_.output->name == workspace_json["monitor"].asString()) { + std::string name = workspace_json["name"].asString(); + if (name == workspace && bar_.output->name == workspace_json["monitor"].asString()) { workspaces_to_create_.push_back(workspace_json); break; } @@ -154,15 +178,15 @@ void Workspaces::update_window_count() { auto workspace_json = std::find_if( workspaces_json.begin(), workspaces_json.end(), [&](Json::Value const &x) { return x["name"].asString() == workspace->name(); }); + uint32_t count = 0; if (workspace_json != workspaces_json.end()) { try { - workspace->set_windows((*workspace_json)["windows"].asUInt()); + count = (*workspace_json)["windows"].asUInt(); } catch (const std::exception &e) { spdlog::error("Failed to update window count: {}", e.what()); } - } else { - workspace->set_windows(0); } + workspace->set_windows(count); } } @@ -181,7 +205,7 @@ void Workspaces::create_workspace(Json::Value &value) { } // create new workspace - workspaces_.emplace_back(std::make_unique(value)); + workspaces_.emplace_back(std::make_unique(value, *this)); Gtk::Button &new_workspace_button = workspaces_.back()->button(); box_.pack_start(new_workspace_button, false, false); sort_workspaces(); @@ -207,7 +231,7 @@ void Workspaces::remove_workspace(std::string name) { } void Workspaces::fill_persistent_workspaces() { - if (config_["persistent_workspaces"].isObject() && !all_outputs()) { + if (config_["persistent_workspaces"].isObject()) { const Json::Value persistent_workspaces = config_["persistent_workspaces"]; const std::vector keys = persistent_workspaces.getMemberNames(); @@ -297,8 +321,9 @@ void Workspaces::init() { const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces"); for (Json::Value workspace_json : workspaces_json) { if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) && - (!workspace_json["name"].asString().starts_with("special") || show_special())) + (!workspace_json["name"].asString().starts_with("special") || show_special())) { create_workspace(workspace_json); + } } update_window_count(); @@ -314,8 +339,9 @@ Workspaces::~Workspaces() { std::lock_guard lg(mutex_); } -Workspace::Workspace(const Json::Value &workspace_data) - : id_(workspace_data["id"].asInt()), +Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager) + : workspace_manager_(workspace_manager), + id_(workspace_data["id"].asInt()), name_(workspace_data["name"].asString()), output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc windows_(workspace_data["windows"].asInt()), @@ -350,12 +376,26 @@ void add_or_remove_class(const Glib::RefPtr &context, bool co } void Workspace::update(const std::string &format, const std::string &icon) { + // clang-format off + if (this->workspace_manager_.active_only() && \ + !this->active() && \ + !this->is_persistent() && \ + !this->is_visible() && \ + !this->is_special()) { + // clang-format on + // if active_only is true, hide if not active, persistent, visible or special + button_.hide(); + return; + } + button_.show(); + auto style_context = button_.get_style_context(); add_or_remove_class(style_context, active(), "active"); add_or_remove_class(style_context, is_special(), "special"); add_or_remove_class(style_context, is_empty(), "empty"); add_or_remove_class(style_context, is_persistent(), "persistent"); add_or_remove_class(style_context, is_urgent(), "urgent"); + add_or_remove_class(style_context, is_visible(), "visible"); label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), fmt::arg("name", name()), fmt::arg("icon", icon))); @@ -420,6 +460,13 @@ std::string &Workspace::select_icon(std::map &icons_ma return named_icon_it->second; } + if (is_visible()) { + auto visible_icon_it = icons_map.find("visible"); + if (visible_icon_it != icons_map.end()) { + return visible_icon_it->second; + } + } + if (is_empty()) { auto empty_icon_it = icons_map.find("empty"); if (empty_icon_it != icons_map.end()) { From 44ac6b804488e1a4356a1426c23b8d2808fef512 Mon Sep 17 00:00:00 2001 From: khaneliman Date: Tue, 5 Sep 2023 13:10:34 -0500 Subject: [PATCH 10/25] refactor!: hyprland persistent workspaces config option name standardization --- man/waybar-hyprland-workspaces.5.scd | 4 ++-- src/modules/hyprland/workspaces.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 13764752..e975179f 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -69,7 +69,7 @@ Additional to workspace name matching, the following *format-icons* can be set. "active": "", "default": "" }, - "persistent_workspaces": { + "persistent-workspaces": { "*": 5, // 5 workspaces by default on every monitor "HDMI-A-1": 3 // but only three on HDMI-A-1 } @@ -88,7 +88,7 @@ Additional to workspace name matching, the following *format-icons* can be set. "active": "", "default": "" }, - "persistent_workspaces": { + "persistent-workspaces": { "*": [ 2,3,4,5 ], // 2-5 on every monitor "HDMI-A-1": [ 1 ] // but only workspace 1 on HDMI-A-1 } diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 64c7a9ed..d4b9cf2d 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -231,8 +231,8 @@ void Workspaces::remove_workspace(std::string name) { } void Workspaces::fill_persistent_workspaces() { - if (config_["persistent_workspaces"].isObject()) { - const Json::Value persistent_workspaces = config_["persistent_workspaces"]; + if (config_["persistent-workspaces"].isObject()) { + const Json::Value persistent_workspaces = config_["persistent-workspaces"]; const std::vector keys = persistent_workspaces.getMemberNames(); for (const std::string &key : keys) { From 4a6c1269fb33078ce7273ffce005c1faa1ed592f Mon Sep 17 00:00:00 2001 From: khaneliman Date: Tue, 5 Sep 2023 13:12:39 -0500 Subject: [PATCH 11/25] refactor!: sway persistent workspaces config name rename --- man/waybar-sway-workspaces.5.scd | 4 ++-- src/modules/sway/workspaces.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/man/waybar-sway-workspaces.5.scd b/man/waybar-sway-workspaces.5.scd index 82d858de..2441a936 100644 --- a/man/waybar-sway-workspaces.5.scd +++ b/man/waybar-sway-workspaces.5.scd @@ -60,7 +60,7 @@ Addressed by *sway/workspaces* default: false ++ If set to true. Only focused workspaces will be shown. -*persistent_workspaces*: ++ +*persistent-workspaces*: ++ typeof: json (see below) ++ default: empty ++ Lists workspaces that should always be shown, even when non existent @@ -112,7 +112,7 @@ an empty list denoting all outputs. ``` "sway/workspaces": { - "persistent_workspaces": { + "persistent-workspaces": { "3": [], // Always show a workspace with name '3', on all outputs if it does not exists "4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists "5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index a5e5fa75..e2ddbbd3 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -80,8 +80,8 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { }); // adding persistent workspaces (as per the config file) - if (config_["persistent_workspaces"].isObject()) { - const Json::Value &p_workspaces = config_["persistent_workspaces"]; + if (config_["persistent-workspaces"].isObject()) { + const Json::Value &p_workspaces = config_["persistent-workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { From b405dc436c49d075d41da938cec7f7e190e0348a Mon Sep 17 00:00:00 2001 From: khaneliman Date: Tue, 5 Sep 2023 13:13:29 -0500 Subject: [PATCH 12/25] refactor!: wlr persistent workspaces config rename --- src/modules/wlr/workspace_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 8933d691..7f97d294 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -209,8 +209,8 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value } auto WorkspaceGroup::fill_persistent_workspaces() -> void { - if (config_["persistent_workspaces"].isObject() && !workspace_manager_.all_outputs()) { - const Json::Value &p_workspaces = config_["persistent_workspaces"]; + if (config_["persistent-workspaces"].isObject() && !workspace_manager_.all_outputs()) { + const Json::Value &p_workspaces = config_["persistent-workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { From c9e1899594f9c36dc7fc7bc201238438d59ca6ff Mon Sep 17 00:00:00 2001 From: khaneliman Date: Tue, 5 Sep 2023 13:45:09 -0500 Subject: [PATCH 13/25] refactor: deprecate instead of remove persistent_workspaces --- src/modules/hyprland/workspaces.cpp | 11 +++++++++-- src/modules/sway/workspaces.cpp | 13 +++++++++++-- src/modules/wlr/workspace_manager.cpp | 13 +++++++++++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index d4b9cf2d..fc94b39b 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -231,8 +231,15 @@ void Workspaces::remove_workspace(std::string name) { } void Workspaces::fill_persistent_workspaces() { - if (config_["persistent-workspaces"].isObject()) { - const Json::Value persistent_workspaces = config_["persistent-workspaces"]; + if (config_["persistent_workspaces"].isObject()) { + spdlog::warn( + "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); + } + + if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) { + const Json::Value persistent_workspaces = config_["persistent-workspaces"].isObject() + ? config_["persistent-workspaces"] + : config_["persistent_workspaces"]; const std::vector keys = persistent_workspaces.getMemberNames(); for (const std::string &key : keys) { diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index e2ddbbd3..6674731f 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -79,9 +79,18 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) { : true; }); + if (config_["persistent_workspaces"].isObject()) { + spdlog::warn( + "persistent_workspaces is deprecated. Please change config to use " + "persistent-workspaces."); + } + // adding persistent workspaces (as per the config file) - if (config_["persistent-workspaces"].isObject()) { - const Json::Value &p_workspaces = config_["persistent-workspaces"]; + if (config_["persistent-workspaces"].isObject() || + config_["persistent_workspaces"].isObject()) { + const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject() + ? config_["persistent-workspaces"] + : config_["persistent_workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { diff --git a/src/modules/wlr/workspace_manager.cpp b/src/modules/wlr/workspace_manager.cpp index 7f97d294..ce14b3b5 100644 --- a/src/modules/wlr/workspace_manager.cpp +++ b/src/modules/wlr/workspace_manager.cpp @@ -209,8 +209,17 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value } auto WorkspaceGroup::fill_persistent_workspaces() -> void { - if (config_["persistent-workspaces"].isObject() && !workspace_manager_.all_outputs()) { - const Json::Value &p_workspaces = config_["persistent-workspaces"]; + if (config_["persistent_workspaces"].isObject()) { + spdlog::warn( + "persistent_workspaces is deprecated. Please change config to use persistent-workspaces."); + } + + if ((config_["persistent-workspaces"].isObject() || + config_["persistent_workspaces"].isObject()) && + !workspace_manager_.all_outputs()) { + const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject() + ? config_["persistent-workspaces"] + : config_["persistent_workspaces"]; const std::vector p_workspaces_names = p_workspaces.getMemberNames(); for (const std::string &p_w_name : p_workspaces_names) { From 6c3565c52061f86f0cdbbca116aa4222fb670dc7 Mon Sep 17 00:00:00 2001 From: zjeffer <4633209+zjeffer@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:33:35 +0200 Subject: [PATCH 14/25] Add urgent icon Fixes #2476 --- src/modules/hyprland/workspaces.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 64c7a9ed..ccf75310 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -441,6 +441,13 @@ void Workspaces::sort_workspaces() { } std::string &Workspace::select_icon(std::map &icons_map) { + if (is_urgent()) { + auto urgent_icon_it = icons_map.find("urgent"); + if (urgent_icon_it != icons_map.end()) { + return urgent_icon_it->second; + } + } + if (active()) { auto active_icon_it = icons_map.find("active"); if (active_icon_it != icons_map.end()) { From 2837b720643da5663a63e9d20cd06b5796c3762e Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 8 Sep 2023 14:11:02 -0500 Subject: [PATCH 15/25] fix: rename workspace active fix --- src/modules/hyprland/workspaces.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index ccf75310..82442d53 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -163,6 +163,9 @@ void Workspaces::onEvent(const std::string &ev) { std::string new_name = payload.substr(payload.find(',') + 1); for (auto &workspace : workspaces_) { if (workspace->id() == workspace_id) { + if (workspace->name() == active_workspace_name_) { + active_workspace_name_ = new_name; + } workspace->set_name(new_name); break; } From 587bd0cd628a3cc5c7a333acdba31a5503d1e2d4 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 8 Sep 2023 22:24:05 -0500 Subject: [PATCH 16/25] refactor: cleanup hyprland workspaces constructor --- include/modules/hyprland/workspaces.hpp | 2 ++ src/modules/hyprland/workspaces.cpp | 27 +++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 270c1e36..93faf494 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -80,6 +80,8 @@ 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; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 82442d53..504a6caa 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -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); + + 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}"; @@ -43,18 +57,19 @@ 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()) { + sort_by = config_sort_by.asString(); } - event_box_.add(box_); +} + +auto Workspaces::register_ipc() -> void { modulesReady = true; + if (!gIPC) { gIPC = std::make_unique(); } - init(); - gIPC->registerForIPC("workspace", this); gIPC->registerForIPC("createworkspace", this); gIPC->registerForIPC("destroyworkspace", this); From cbc12e544355e0df311c5e44216639970b7b72ab Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 8 Sep 2023 22:24:28 -0500 Subject: [PATCH 17/25] feat: hyprland workspaces add sort-by --- include/modules/hyprland/workspaces.hpp | 1 + man/waybar-hyprland-workspaces.5.scd | 8 +++ src/modules/hyprland/workspaces.cpp | 71 +++++++++++++++---------- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 93faf494..3b8de4ae 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -86,6 +86,7 @@ class Workspaces : public AModule, public EventHandler { bool all_outputs_ = false; bool show_special_ = false; bool active_only_ = false; + std::string sort_by = "default"; void fill_persistent_workspaces(); void create_persistent_workspaces(); diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 13764752..e0caf80f 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -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 diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 504a6caa..517de1be 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -421,36 +421,53 @@ void Workspace::update(const std::string &format, const std::string &icon) { void Workspaces::sort_workspaces() { std::sort(workspaces_.begin(), workspaces_.end(), - [](std::unique_ptr &a, std::unique_ptr &b) { - // normal -> named persistent -> named -> special -> named special + [&](std::unique_ptr &a, std::unique_ptr &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()); - // 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; + if (sort_by == "number") { + try { + return is_number_less; + } catch (const std::invalid_argument &) { } - // both are 0 (not yet named persistents) / both are named specials (-98 <= ID <=-1) - return a->name() < b->name(); - } + } else if (sort_by == "name") { + return is_name_less; + } else if (sort_by == "id") { + return is_id_less; + } else { + // normal -> named persistent -> named -> special -> named special - // sort non-special named workspaces by name (ID <= -1377) - return a->name() < b->name(); + // 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; + } }); for (size_t i = 0; i < workspaces_.size(); ++i) { From 65ba449460bea5cd3a7008fe3777a76a0952d453 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 8 Sep 2023 23:17:21 -0500 Subject: [PATCH 18/25] chore: update man page index --- man/waybar.5.scd.in | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index a8376697..92b365d9 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -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 From 8ea2626de8058348a36f752da4a2c1fafa05a538 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 09:32:55 -0500 Subject: [PATCH 19/25] refactor: sort-by enum hyprland --- include/modules/hyprland/workspaces.hpp | 4 +- include/util/enum.hpp | 28 ++++++++ src/modules/hyprland/workspaces.cpp | 92 +++++++++++++++---------- 3 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 include/util/enum.hpp diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 3b8de4ae..9cd17f88 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -11,6 +11,7 @@ #include "AModule.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" +#include "util/enum.hpp" namespace waybar::modules::hyprland { @@ -86,7 +87,8 @@ class Workspaces : public AModule, public EventHandler { bool all_outputs_ = false; bool show_special_ = false; bool active_only_ = false; - std::string sort_by = "default"; + util::EnumParser enum_parser_; + util::EnumParser::SORT_METHOD sort_by_ = util::EnumParser::SORT_METHOD::DEFAULT; void fill_persistent_workspaces(); void create_persistent_workspaces(); diff --git a/include/util/enum.hpp b/include/util/enum.hpp new file mode 100644 index 00000000..a1cb6f6c --- /dev/null +++ b/include/util/enum.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +namespace waybar::util { + +struct EnumParser { + EnumParser() {} + + enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; + + SORT_METHOD sortStringToEnum(const std::string& str) { + static const std::map enumMap = { + {"ID", ID}, {"NAME", NAME}, {"NUMBER", NUMBER}, {"DEFAULT", DEFAULT}}; + + auto it = enumMap.find(str); + if (it != enumMap.end()) { + return it->second; + } else { + throw std::invalid_argument("Invalid string representation for enum"); + } + } + + ~EnumParser() = default; +}; +} // namespace waybar::util diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 517de1be..9eda2c39 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -59,7 +59,14 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { auto config_sort_by = config_["sort-by"]; if (config_sort_by.isString()) { - sort_by = config_sort_by.asString(); + 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."); + } } } @@ -427,47 +434,56 @@ void Workspaces::sort_workspaces() { auto is_name_less = a->name() < b->name(); auto is_number_less = std::stoi(a->name()) < std::stoi(b->name()); - if (sort_by == "number") { - try { - return is_number_less; - } catch (const std::invalid_argument &) { - } - } else if (sort_by == "name") { - return is_name_less; - } else if (sort_by == "id") { - return is_id_less; - } else { - // normal -> named persistent -> named -> special -> named special - - // both normal (includes numbered persistent) => sort by ID - if (a->id() > 0 && b->id() > 0) { + switch (sort_by_) { + case util::EnumParser::SORT_METHOD::ID: 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) + 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 - // sort non-special named workspaces by name (ID <= -1377) - return is_name_less; + // 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; } + + // 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) { From 8ce64ea784ca3345735cef7de9464583abd965dc Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 09:38:03 -0500 Subject: [PATCH 20/25] refactor: make parsing sort-by more lenient --- include/util/enum.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/util/enum.hpp b/include/util/enum.hpp index a1cb6f6c..dcf0e45c 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -12,10 +12,16 @@ struct 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 enumMap = { {"ID", ID}, {"NAME", NAME}, {"NUMBER", NUMBER}, {"DEFAULT", DEFAULT}}; - auto it = enumMap.find(str); + auto it = enumMap.find(uppercaseStr); if (it != enumMap.end()) { return it->second; } else { From 2b8c92e8fdbe98282a625741abf8d9b21f8b969f Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 11:18:12 -0500 Subject: [PATCH 21/25] refactor: enum utility allow overriding --- include/modules/hyprland/workspaces.hpp | 10 ++++++++-- include/util/enum.hpp | 15 +++++++-------- src/modules/hyprland/workspaces.cpp | 12 ++++++------ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 9cd17f88..7c4d919e 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -87,8 +87,14 @@ class Workspaces : public AModule, public EventHandler { 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; + + enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; + util::EnumParser enum_parser_; + SORT_METHOD sort_by_ = SORT_METHOD::DEFAULT; + std::map sort_map_ = {{"ID", SORT_METHOD::ID}, + {"NAME", SORT_METHOD::NAME}, + {"NUMBER", SORT_METHOD::NUMBER}, + {"DEFAULT", SORT_METHOD::DEFAULT}}; void fill_persistent_workspaces(); void create_persistent_workspaces(); diff --git a/include/util/enum.hpp b/include/util/enum.hpp index dcf0e45c..7ee80694 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -1,26 +1,25 @@ #pragma once +#include #include #include +#include #include namespace waybar::util { +template struct EnumParser { 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) + EnumType sortStringToEnum(const std::string& str, + const std::map& enumMap) { + // Convert the input string to uppercase std::string uppercaseStr; for (char c : str) { - uppercaseStr += std::toupper(c); + uppercaseStr += std::toupper(c); } - static const std::map enumMap = { - {"ID", ID}, {"NAME", NAME}, {"NUMBER", NUMBER}, {"DEFAULT", DEFAULT}}; - auto it = enumMap.find(uppercaseStr); if (it != enumMap.end()) { return it->second; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 9eda2c39..21a2a8f7 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -61,10 +61,10 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { if (config_sort_by.isString()) { auto sort_by_str = config_sort_by.asString(); try { - sort_by_ = enum_parser_.sortStringToEnum(sort_by_str); + sort_by_ = enum_parser_.sortStringToEnum(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_ = util::EnumParser::SORT_METHOD::DEFAULT; + sort_by_ = SORT_METHOD::DEFAULT; g_warning("Invalid string representation for sort-by. Falling back to default sort method."); } } @@ -435,18 +435,18 @@ void Workspaces::sort_workspaces() { auto is_number_less = std::stoi(a->name()) < std::stoi(b->name()); switch (sort_by_) { - case util::EnumParser::SORT_METHOD::ID: + case SORT_METHOD::ID: return is_id_less; - case util::EnumParser::SORT_METHOD::NAME: + case SORT_METHOD::NAME: return is_name_less; - case util::EnumParser::SORT_METHOD::NUMBER: + case 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: + case SORT_METHOD::DEFAULT: default: // Handle the default case here. // normal -> named persistent -> named -> special -> named special From 3ae2fe3272782af0459e40f2605d88927fc86678 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 12:02:56 -0500 Subject: [PATCH 22/25] refactor: PR review cleanup --- include/modules/hyprland/workspaces.hpp | 2 +- include/util/enum.hpp | 21 ++++++++++----------- src/modules/hyprland/workspaces.cpp | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index 7c4d919e..14b9ba0e 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -88,7 +88,7 @@ class Workspaces : public AModule, public EventHandler { bool show_special_ = false; bool active_only_ = false; - enum SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; + enum class SORT_METHOD { ID, NAME, NUMBER, DEFAULT }; util::EnumParser enum_parser_; SORT_METHOD sort_by_ = SORT_METHOD::DEFAULT; std::map sort_map_ = {{"ID", SORT_METHOD::ID}, diff --git a/include/util/enum.hpp b/include/util/enum.hpp index 7ee80694..a4239bd2 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -12,20 +12,19 @@ template struct EnumParser { EnumParser() {} - EnumType sortStringToEnum(const std::string& str, - const std::map& enumMap) { + EnumType parseStringToEnum(const std::string& str, + const std::map& enumMap) { // Convert the input string to uppercase - std::string uppercaseStr; - for (char c : str) { - uppercaseStr += std::toupper(c); - } + std::string uppercaseStr = str; + std::transform(uppercaseStr.begin(), uppercaseStr.end(), uppercaseStr.begin(), + [](unsigned char c) { return std::toupper(c); }); + // Return enum match of string auto it = enumMap.find(uppercaseStr); - if (it != enumMap.end()) { - return it->second; - } else { - throw std::invalid_argument("Invalid string representation for enum"); - } + if (it != enumMap.end()) return it->second; + + // Throw error if it doesnt return + throw std::invalid_argument("Invalid string representation for enum"); } ~EnumParser() = default; diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 21a2a8f7..91da69de 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -61,7 +61,7 @@ auto Workspaces::parse_config(const Json::Value &config) -> void { if (config_sort_by.isString()) { auto sort_by_str = config_sort_by.asString(); try { - sort_by_ = enum_parser_.sortStringToEnum(sort_by_str, sort_map_); + 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; From 2fee12d930eda56310276e0ef80f10a70a093868 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 12:14:52 -0500 Subject: [PATCH 23/25] fix: enumparser capitalize everything to avoid issues --- include/util/enum.hpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/include/util/enum.hpp b/include/util/enum.hpp index a4239bd2..6f402614 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -12,12 +12,26 @@ template struct EnumParser { EnumParser() {} + // Helper function to capitalize a string + std::string capitalizeString(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; + } + EnumType parseStringToEnum(const std::string& str, const std::map& enumMap) { // Convert the input string to uppercase - std::string uppercaseStr = str; - std::transform(uppercaseStr.begin(), uppercaseStr.end(), uppercaseStr.begin(), - [](unsigned char c) { return std::toupper(c); }); + std::string uppercaseStr = capitalizeString(str); + + // Capitalize the map keys before searching + std::map capitalizedEnumMap; + std::transform(enumMap.begin(), enumMap.end(), + std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), + [this](const auto& pair) { + return std::make_pair(capitalizeString(pair.first), pair.second); + }); // Return enum match of string auto it = enumMap.find(uppercaseStr); From b8630968b262726b01c00a34a65907ca86eed784 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 13:23:17 -0500 Subject: [PATCH 24/25] refactor: move capitalize string helper --- include/util/enum.hpp | 20 ++++++-------------- include/util/string.hpp | 8 ++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/util/enum.hpp b/include/util/enum.hpp index 6f402614..951cace3 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -6,32 +6,24 @@ #include #include +#include "util/string.hpp" + namespace waybar::util { template struct EnumParser { EnumParser() {} - // Helper function to capitalize a string - std::string capitalizeString(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; - } - EnumType parseStringToEnum(const std::string& str, const std::map& enumMap) { // Convert the input string to uppercase - std::string uppercaseStr = capitalizeString(str); + std::string uppercaseStr = capitalize(str); // Capitalize the map keys before searching std::map capitalizedEnumMap; - std::transform(enumMap.begin(), enumMap.end(), - std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()), - [this](const auto& pair) { - return std::make_pair(capitalizeString(pair.first), pair.second); - }); + 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 = enumMap.find(uppercaseStr); diff --git a/include/util/string.hpp b/include/util/string.hpp index 24a9b2b9..d06557c1 100644 --- a/include/util/string.hpp +++ b/include/util/string.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include 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 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; +} From 79cf33b9f1643d8dca5809ae8ca208d234a8e071 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Sat, 9 Sep 2023 17:48:36 -0500 Subject: [PATCH 25/25] refactor: enumparser create implementation file --- include/util/enum.hpp | 29 +++++----------------------- meson.build | 1 + src/util/enum.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 src/util/enum.cpp diff --git a/include/util/enum.hpp b/include/util/enum.hpp index 951cace3..681385fd 100644 --- a/include/util/enum.hpp +++ b/include/util/enum.hpp @@ -1,38 +1,19 @@ #pragma once -#include -#include #include #include #include -#include "util/string.hpp" - namespace waybar::util { template struct EnumParser { - EnumParser() {} + public: + EnumParser(); + ~EnumParser(); EnumType parseStringToEnum(const std::string& str, - const std::map& enumMap) { - // Convert the input string to uppercase - std::string uppercaseStr = capitalize(str); - - // Capitalize the map keys before searching - std::map 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 = enumMap.find(uppercaseStr); - if (it != enumMap.end()) return it->second; - - // Throw error if it doesnt return - throw std::invalid_argument("Invalid string representation for enum"); - } - - ~EnumParser() = default; + const std::map& enumMap); }; + } // namespace waybar::util diff --git a/meson.build b/meson.build index e71807ec..b27bc05b 100644 --- a/meson.build +++ b/meson.build @@ -171,6 +171,7 @@ src_files = files( 'src/client.cpp', 'src/config.cpp', 'src/group.cpp', + 'src/util/enum.cpp', 'src/util/prepare_for_sleep.cpp', 'src/util/ustring_clen.cpp', 'src/util/sanitize_str.cpp', diff --git a/src/util/enum.cpp b/src/util/enum.cpp new file mode 100644 index 00000000..a29304c5 --- /dev/null +++ b/src/util/enum.cpp @@ -0,0 +1,45 @@ +#include "util/enum.hpp" + +#include // for std::transform +#include // for std::toupper +#include +#include +#include +#include + +#include "modules/hyprland/workspaces.hpp" +#include "util/string.hpp" + +namespace waybar::util { + +template +EnumParser::EnumParser() = default; + +template +EnumParser::~EnumParser() = default; + +template +EnumType EnumParser::parseStringToEnum(const std::string& str, + const std::map& enumMap) { + // Convert the input string to uppercase + std::string uppercaseStr = capitalize(str); + + // Capitalize the map keys before searching + std::map 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; + +} // namespace waybar::util