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] 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()) {