From a45c59b8ba6bee42e088f97d0ea59f9f7ab38ced Mon Sep 17 00:00:00 2001 From: Noak Palander Date: Sat, 2 Nov 2024 03:01:54 +0100 Subject: [PATCH 1/2] added a taskbar squash option --- include/modules/wlr/taskbar.hpp | 6 +++++ src/modules/wlr/taskbar.cpp | 47 ++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 07110ddee..ddce3445b 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -67,6 +67,7 @@ class Task { Glib::RefPtr app_info_; bool button_visible_ = false; bool ignored_ = false; + bool squashed_ = false; bool with_icon_ = false; bool with_name_ = false; @@ -93,6 +94,7 @@ class Task { bool image_load_icon(Gtk::Image &image, const Glib::RefPtr &icon_theme, Glib::RefPtr app_info, int size); void hide_if_ignored(); + void hide_if_duplicate(); public: /* Getter functions */ @@ -155,6 +157,7 @@ class Taskbar : public waybar::AModule { std::vector> icon_themes_; std::unordered_set ignore_list_; + std::unordered_set squash_list_; std::map app_ids_replace_map_; struct zwlr_foreign_toplevel_manager_v1 *manager_; @@ -180,7 +183,10 @@ class Taskbar : public waybar::AModule { const std::vector> &icon_themes() const; const std::unordered_set &ignore_list() const; + const std::unordered_set &squash_list() const; const std::map &app_ids_replace_map() const; + std::size_t task_id_count(std::string_view id) const; + std::size_t task_title_count(std::string_view title) const; }; } /* namespace waybar::modules::wlr */ diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 30e4ee488..c1bd1b453 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -385,6 +385,7 @@ std::string Task::state_string(bool shortened) const { void Task::handle_title(const char *title) { title_ = title; hide_if_ignored(); + hide_if_duplicate(); } void Task::set_minimize_hint() { @@ -392,6 +393,26 @@ void Task::set_minimize_hint() { minimize_hint.y, minimize_hint.w, minimize_hint.h); } +void Task::hide_if_duplicate() { + const auto &squash_list = tbar_->squash_list(); + bool contains_app = + squash_list.contains("*") || squash_list.contains(title_) || squash_list.contains(app_id_); + + // Squashes if the app is in the squash list and more than 1 instance is open + if (contains_app && (tbar_->task_id_count(app_id_) > 1 || tbar_->task_title_count(title_) > 1)) { + squashed_ = true; + if (button_visible_) { + auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + handle_output_leave(output); + } + } + + if (!squashed_ && !ignored_) { + auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); + handle_output_enter(output); + } +} + void Task::hide_if_ignored() { if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) { ignored_ = true; @@ -399,7 +420,9 @@ void Task::hide_if_ignored() { auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); handle_output_leave(output); } - } else { + } + + if (!ignored_ && !squashed_) { bool is_was_ignored = ignored_; ignored_ = false; if (is_was_ignored) { @@ -417,6 +440,7 @@ void Task::handle_app_id(const char *app_id) { } app_id_ = app_id; hide_if_ignored(); + hide_if_duplicate(); auto ids_replace_map = tbar_->app_ids_replace_map(); if (ids_replace_map.count(app_id_)) { @@ -464,6 +488,10 @@ void Task::handle_output_enter(struct wl_output *output) { spdlog::debug("{} is ignored", repr()); return; } + if (squashed_) { + spdlog::debug("{} was squashed", repr()); + return; + } spdlog::debug("{} entered output {}", repr(), (void *)output); @@ -794,6 +822,13 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu } } + // Load squash-list + if (config_["squash-list"].isArray()) { + for (auto &app_name : config_["squash-list"]) { + squash_list_.emplace(app_name.asString()); + } + } + // Load app_id remappings if (config_["app_ids-mapping"].isObject()) { const Json::Value &mapping = config_["app_ids-mapping"]; @@ -945,8 +980,18 @@ const std::vector> &Taskbar::icon_themes() const { const std::unordered_set &Taskbar::ignore_list() const { return ignore_list_; } +const std::unordered_set &Taskbar::squash_list() const { return squash_list_; } + const std::map &Taskbar::app_ids_replace_map() const { return app_ids_replace_map_; } +std::size_t Taskbar::task_id_count(std::string_view id) const { + return std::ranges::count_if(tasks_, [=](auto &&task) { return id == task->app_id(); }); +} + +std::size_t Taskbar::task_title_count(std::string_view title) const { + return std::ranges::count_if(tasks_, [=](auto &&task) { return title == task->title(); }); +} + } /* namespace waybar::modules::wlr */ From a58771577a7de7cf2591cd421ab0f773cdbfd881 Mon Sep 17 00:00:00 2001 From: Noak Palander Date: Sun, 3 Nov 2024 01:01:36 +0100 Subject: [PATCH 2/2] fixed bug; when the shown task is removed it promotes another one --- include/modules/wlr/taskbar.hpp | 5 +++++ src/modules/wlr/taskbar.cpp | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index ddce3445b..6c4658fdc 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -187,6 +188,10 @@ class Taskbar : public waybar::AModule { const std::map &app_ids_replace_map() const; std::size_t task_id_count(std::string_view id) const; std::size_t task_title_count(std::string_view title) const; + + auto tasks() { + return tasks_ | std::views::transform([](auto &task) -> Task & { return *task; }); + } }; } /* namespace waybar::modules::wlr */ diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index c1bd1b453..3a7b1bcb7 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -571,6 +571,28 @@ void Task::handle_closed() { tbar_->remove_button(button); button_visible_ = false; } + + const auto &squash_list = tbar_->squash_list(); + const bool in_squash_list = + squash_list.contains("*") || squash_list.contains(title_) || squash_list.contains(app_id_); + if (in_squash_list && !squashed_ && + (tbar_->task_id_count(app_id_) > 1 || tbar_->task_title_count(title_) > 1)) { + // Find next squashed task with same title or id (excluding ourselves) + auto tasks = tbar_->tasks(); + const auto it = std::ranges::find_if(tasks, [this](auto &&task) { + return &task != this && task.squashed_ && + (task.app_id() == app_id_ || task.title() == title_); + }); + + if (it != tasks.end() && !(*it).ignored_) { + Task &task = *it; + task.squashed_ = false; + tbar_->add_button(task.button); + task.button.show(); + task.button_visible_ = true; + } + } + tbar_->remove_task(id_); }