diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index d91a43f3..fb8003f8 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -53,6 +53,7 @@ class Workspaces : public AModule, public EventHandler { auto taskbarFormatBefore() const -> std::string { return m_taskbarFormatBefore; } auto taskbarFormatAfter() const -> std::string { return m_taskbarFormatAfter; } auto taskbarIconSize() const -> int { return m_taskbarIconSize; } + auto taskbarMaxIcons() const -> int { return m_taskbarMaxIcons; } auto taskbarOrientation() const -> Gtk::Orientation { return m_taskbarOrientation; } auto taskbarReverseDirection() const -> bool { return m_taskbarReverseDirection; } auto onClickWindow() const -> std::string { return m_onClickWindow; } @@ -199,6 +200,7 @@ class Workspaces : public AModule, public EventHandler { std::string m_taskbarFormatBefore; std::string m_taskbarFormatAfter; int m_taskbarIconSize = 16; + int m_taskbarMaxIcons = 0; // 0 means unlimited Gtk::Orientation m_taskbarOrientation = Gtk::ORIENTATION_HORIZONTAL; bool m_taskbarReverseDirection = false; util::EnumParser m_activeWindowEnumParser; diff --git a/man/waybar-hyprland-workspaces.5.scd b/man/waybar-hyprland-workspaces.5.scd index 759e76cc..fda536a7 100644 --- a/man/waybar-hyprland-workspaces.5.scd +++ b/man/waybar-hyprland-workspaces.5.scd @@ -88,6 +88,11 @@ This setting is ignored if *workspace-taskbar.enable* is set to true. default: 16 ++ Size of the icons in the workspace taskbar. + *max-icons*: ++ + typeof: int ++ + default: 0 (unlimited) ++ + Maximum number of icons to show per workspace. When set, duplicate icons (windows with the same class) are removed first, then the list is trimmed to this limit. Set to 0 for unlimited icons. + *icon-theme*: ++ typeof: string | array ++ default: [] ++ diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index 5046688f..72e4aaf4 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -516,11 +517,47 @@ void Workspace::updateTaskbar(const std::string& workspace_icon) { } } - bool isFirst = true; - auto processWindow = [&](const WindowRepr& window_repr) { + // Build a list of windows to display, removing duplicates by window_class + // and respecting max-icons limit + std::vector windowsToShow; + std::set seenClasses; + + auto addWindowIfUnique = [&](const WindowRepr& window_repr) { if (shouldSkipWindow(window_repr)) { - return; // skip + return; } + // Deduplicate by window_class + if (seenClasses.find(window_repr.window_class) != seenClasses.end()) { + return; + } + seenClasses.insert(window_repr.window_class); + windowsToShow.push_back(&window_repr); + }; + + if (m_workspaceManager.taskbarReverseDirection()) { + for (auto it = m_windowMap.rbegin(); it != m_windowMap.rend(); ++it) { + addWindowIfUnique(*it); + } + } else { + for (const auto& window_repr : m_windowMap) { + addWindowIfUnique(window_repr); + } + } + + // Apply max-icons limit if configured + int maxIcons = m_workspaceManager.taskbarMaxIcons(); + if (maxIcons > 0 && static_cast(windowsToShow.size()) > maxIcons) { + windowsToShow.resize(maxIcons); + } + + // Apply max-windows limit if configured + int maxWindows = m_workspaceManager.maxWindows(); + if (maxWindows > 0 && static_cast(windowsToShow.size()) > maxWindows) { + windowsToShow.resize(maxWindows); + } + + bool isFirst = true; + for (const auto* window_repr : windowsToShow) { if (isFirst) { isFirst = false; } else if (m_workspaceManager.getWindowSeparator() != "") { @@ -530,29 +567,29 @@ void Workspace::updateTaskbar(const std::string& workspace_icon) { } auto window_box = Gtk::make_managed(Gtk::ORIENTATION_HORIZONTAL); - window_box->set_tooltip_markup(window_repr.window_title); + window_box->set_tooltip_markup(window_repr->window_title); auto button = Gtk::manage(new Gtk::Button()); button->set_relief(Gtk::RELIEF_NONE); button->add(*window_box); button->get_style_context()->add_class("taskbar-window"); - if (window_repr.isActive) { + if (window_repr->isActive) { button->get_style_context()->add_class("active"); } if (m_workspaceManager.onClickWindow() != "") { button->signal_button_press_event().connect( - sigc::bind(sigc::mem_fun(*this, &Workspace::handleClick), window_repr.address), false); + sigc::bind(sigc::mem_fun(*this, &Workspace::handleClick), window_repr->address), false); } auto text_before = fmt::format(fmt::runtime(m_workspaceManager.taskbarFormatBefore()), - fmt::arg("title", window_repr.window_title)); + fmt::arg("title", window_repr->window_title)); if (!text_before.empty()) { auto window_label_before = Gtk::make_managed(text_before); window_box->pack_start(*window_label_before, true, true); } if (m_workspaceManager.taskbarWithIcon()) { - auto app_info_ = IconLoader::get_app_info_from_app_id_list(window_repr.window_class); + auto app_info_ = IconLoader::get_app_info_from_app_id_list(window_repr->window_class); int icon_size = m_workspaceManager.taskbarIconSize(); auto window_icon = Gtk::make_managed(); m_workspaceManager.iconLoader().image_load_icon(*window_icon, app_info_, icon_size); @@ -560,7 +597,7 @@ void Workspace::updateTaskbar(const std::string& workspace_icon) { } auto text_after = fmt::format(fmt::runtime(m_workspaceManager.taskbarFormatAfter()), - fmt::arg("title", window_repr.window_title)); + fmt::arg("title", window_repr->window_title)); if (!text_after.empty()) { auto window_label_after = Gtk::make_managed(text_after); window_box->pack_start(*window_label_after, true, true); @@ -568,24 +605,6 @@ void Workspace::updateTaskbar(const std::string& workspace_icon) { m_content.pack_start(*button, true, false); button->show_all(); - }; - - if (m_workspaceManager.taskbarReverseDirection()) { - auto rend_it = m_workspaceManager.maxWindows() == 0 - ? m_windowMap.rend() - : m_windowMap.rbegin() + m_workspaceManager.maxWindows(); - - for (auto it = m_windowMap.rbegin(); it != rend_it; ++it) { - processWindow(*it); - } - } else { - auto end_it = m_workspaceManager.maxWindows() == 0 - ? m_windowMap.end() - : m_windowMap.begin() + m_workspaceManager.maxWindows(); - - for (auto it = m_windowMap.begin(); it != end_it; ++it) { - processWindow(*it); - } } auto formatAfter = m_workspaceManager.formatAfter(); diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 2887372f..79d618c8 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -826,6 +826,9 @@ auto Workspaces::populateWorkspaceTaskbarConfig(const Json::Value& config) -> vo if (workspaceTaskbar["icon-size"].isInt()) { m_taskbarIconSize = workspaceTaskbar["icon-size"].asInt(); } + if (workspaceTaskbar["max-icons"].isInt()) { + m_taskbarMaxIcons = workspaceTaskbar["max-icons"].asInt(); + } if (workspaceTaskbar["orientation"].isString() && toLower(workspaceTaskbar["orientation"].asString()) == "vertical") { m_taskbarOrientation = Gtk::ORIENTATION_VERTICAL; diff --git a/subprojects/.wraplock b/subprojects/.wraplock new file mode 100644 index 00000000..e69de29b