Merge pull request #3740 from NoakPalander/feature/squash-list

Added a taskbar squash option
This commit is contained in:
Alexis Rouillard
2026-07-04 01:07:25 +02:00
committed by GitHub
2 changed files with 79 additions and 1 deletions
+11
View File
@@ -11,6 +11,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <ranges>
#include <string> #include <string>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@@ -69,6 +70,7 @@ class Task {
Glib::RefPtr<Gio::DesktopAppInfo> app_info_; Glib::RefPtr<Gio::DesktopAppInfo> app_info_;
bool button_visible_ = false; bool button_visible_ = false;
bool ignored_ = false; bool ignored_ = false;
bool squashed_ = false;
bool with_icon_ = false; bool with_icon_ = false;
bool with_name_ = false; bool with_name_ = false;
@@ -93,6 +95,7 @@ class Task {
void set_minimize_hint(); void set_minimize_hint();
void on_button_size_allocated(Gtk::Allocation& alloc); void on_button_size_allocated(Gtk::Allocation& alloc);
void hide_if_ignored(); void hide_if_ignored();
void hide_if_duplicate();
public: public:
/* Getter functions */ /* Getter functions */
@@ -164,6 +167,7 @@ class Taskbar : public waybar::AModule {
IconLoader icon_loader_; IconLoader icon_loader_;
std::unordered_set<std::string> ignore_list_; std::unordered_set<std::string> ignore_list_;
std::unordered_set<std::string> squash_list_;
std::map<std::string, std::string> app_ids_replace_map_; std::map<std::string, std::string> app_ids_replace_map_;
struct zwlr_foreign_toplevel_manager_v1* manager_; struct zwlr_foreign_toplevel_manager_v1* manager_;
@@ -202,7 +206,14 @@ class Taskbar : public waybar::AModule {
const IconLoader& icon_loader() const; const IconLoader& icon_loader() const;
const std::unordered_set<std::string>& ignore_list() const; const std::unordered_set<std::string>& ignore_list() const;
const std::unordered_set<std::string>& squash_list() const;
const std::map<std::string, std::string>& app_ids_replace_map() const; const std::map<std::string, std::string>& 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; });
}
private: private:
void set_bar_css_class(const std::string&, bool); void set_bar_css_class(const std::string&, bool);
+68 -1
View File
@@ -234,6 +234,7 @@ void Task::handle_title(const char* title) {
} }
title_ = title; title_ = title;
hide_if_ignored(); hide_if_ignored();
hide_if_duplicate();
if ((!with_icon_ && !with_name_) || app_info_) { if ((!with_icon_ && !with_name_) || app_info_) {
return; return;
@@ -258,6 +259,26 @@ void Task::set_minimize_hint() {
minimize_hint.y, minimize_hint.w, minimize_hint.h); 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() { void Task::hide_if_ignored() {
if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) { if (tbar_->ignore_list().count(app_id_) || tbar_->ignore_list().count(title_)) {
ignored_ = true; ignored_ = true;
@@ -265,7 +286,9 @@ void Task::hide_if_ignored() {
auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj()); auto output = gdk_wayland_monitor_get_wl_output(bar_.output->monitor->gobj());
handle_output_leave(output); handle_output_leave(output);
} }
} else { }
if (!ignored_ && !squashed_) {
bool is_was_ignored = ignored_; bool is_was_ignored = ignored_;
ignored_ = false; ignored_ = false;
if (is_was_ignored) { if (is_was_ignored) {
@@ -283,6 +306,7 @@ void Task::handle_app_id(const char* app_id) {
} }
app_id_ = app_id; app_id_ = app_id;
hide_if_ignored(); hide_if_ignored();
hide_if_duplicate();
auto ids_replace_map = tbar_->app_ids_replace_map(); auto ids_replace_map = tbar_->app_ids_replace_map();
if (ids_replace_map.count(app_id_)) { if (ids_replace_map.count(app_id_)) {
@@ -322,6 +346,10 @@ void Task::handle_output_enter(struct wl_output* output) {
spdlog::debug("{} is ignored", repr()); spdlog::debug("{} is ignored", repr());
return; return;
} }
if (squashed_) {
spdlog::debug("{} was squashed", repr());
return;
}
spdlog::debug("{} entered output {}", repr(), (void*)output); spdlog::debug("{} entered output {}", repr(), (void*)output);
@@ -419,6 +447,28 @@ void Task::handle_closed() {
tbar_->remove_button(button); tbar_->remove_button(button);
button_visible_ = false; 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_); tbar_->remove_task(id_);
} }
@@ -672,6 +722,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 // Load app_id remappings
if (config_["app_ids-mapping"].isObject()) { if (config_["app_ids-mapping"].isObject()) {
const Json::Value& mapping = config_["app_ids-mapping"]; const Json::Value& mapping = config_["app_ids-mapping"];
@@ -1056,8 +1113,18 @@ const IconLoader& Taskbar::icon_loader() const { return icon_loader_; }
const std::unordered_set<std::string>& Taskbar::ignore_list() const { return ignore_list_; } const std::unordered_set<std::string>& Taskbar::ignore_list() const { return ignore_list_; }
const std::unordered_set<std::string>& Taskbar::squash_list() const { return squash_list_; }
const std::map<std::string, std::string>& Taskbar::app_ids_replace_map() const { const std::map<std::string, std::string>& Taskbar::app_ids_replace_map() const {
return app_ids_replace_map_; 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 */ } /* namespace waybar::modules::wlr */