From 69e2e249a61f4671edd3326c6b682a0e43a1983b Mon Sep 17 00:00:00 2001 From: Pol Rivero <65060696+pol-rivero@users.noreply.github.com> Date: Tue, 31 Dec 2024 18:33:08 +0100 Subject: [PATCH] Initial implementation of workspace taskbars Add a list of window titles and icons to each workspace (like wlr/taskbar but grouped by workspace). Only implemented on hyprland for now. --- .../hyprland/windowcreationpayload.hpp | 15 ++++- include/modules/hyprland/workspace.hpp | 6 +- include/modules/hyprland/workspaces.hpp | 5 +- .../hyprland/windowcreationpayload.cpp | 6 +- src/modules/hyprland/workspace.cpp | 61 +++++++++++++++---- src/modules/hyprland/workspaces.cpp | 2 +- 6 files changed, 73 insertions(+), 22 deletions(-) diff --git a/include/modules/hyprland/windowcreationpayload.hpp b/include/modules/hyprland/windowcreationpayload.hpp index 45229ed4..06af3074 100644 --- a/include/modules/hyprland/windowcreationpayload.hpp +++ b/include/modules/hyprland/windowcreationpayload.hpp @@ -26,10 +26,19 @@ namespace waybar::modules::hyprland { class Workspaces; +struct WindowRepr { + std::string window_class; + std::string window_title; + std::string repr_rewrite; + + public: + bool empty() const { return repr_rewrite.empty(); } +}; + class WindowCreationPayload { public: WindowCreationPayload(std::string workspace_name, WindowAddress window_address, - std::string window_repr); + WindowRepr window_repr); WindowCreationPayload(std::string workspace_name, WindowAddress window_address, std::string window_class, std::string window_title); WindowCreationPayload(Json::Value const& client_data); @@ -37,7 +46,7 @@ class WindowCreationPayload { int incrementTimeSpentUncreated(); bool isEmpty(Workspaces& workspace_manager); bool reprIsReady() const { return std::holds_alternative(m_window); } - std::string repr(Workspaces& workspace_manager); + WindowRepr repr(Workspaces& workspace_manager); std::string getWorkspaceName() const { return m_workspaceName; } WindowAddress getAddress() const { return m_windowAddress; } @@ -48,7 +57,7 @@ class WindowCreationPayload { void clearAddr(); void clearWorkspaceName(); - using Repr = std::string; + using Repr = WindowRepr; using ClassAndTitle = std::pair; std::variant m_window; diff --git a/include/modules/hyprland/workspace.hpp b/include/modules/hyprland/workspace.hpp index f1fea4e8..7a6531f9 100644 --- a/include/modules/hyprland/workspace.hpp +++ b/include/modules/hyprland/workspace.hpp @@ -56,11 +56,11 @@ class Workspace { void setOutput(std::string const& value) { m_output = value; }; bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); } void insertWindow(WindowCreationPayload create_window_paylod); - std::string removeWindow(WindowAddress const& addr); + WindowRepr removeWindow(WindowAddress const& addr); void initializeWindowMap(const Json::Value& clients_data); bool onWindowOpened(WindowCreationPayload const& create_window_paylod); - std::optional closeWindow(WindowAddress const& addr); + std::optional closeWindow(WindowAddress const& addr); void update(const std::string& format, const std::string& icon); @@ -78,7 +78,7 @@ class Workspace { bool m_isUrgent = false; bool m_isVisible = false; - std::map m_windowMap; + std::map> m_windowMap; Gtk::Button m_button; Gtk::Box m_content; diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index f5c20f69..fa9c0ac0 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -17,6 +17,7 @@ #include "modules/hyprland/windowcreationpayload.hpp" #include "modules/hyprland/workspace.hpp" #include "util/enum.hpp" +#include "util/icon_loader.hpp" #include "util/regex_collection.hpp" using WindowAddress = std::string; @@ -45,6 +46,7 @@ class Workspaces : public AModule, public EventHandler { bool isWorkspaceIgnored(std::string const& workspace_name); bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; } + const IconLoader& iconLoader() const { return m_iconLoader; } private: void onEvent(const std::string& e) override; @@ -119,7 +121,7 @@ class Workspaces : public AModule, public EventHandler { // Map for windows stored in workspaces not present in the current bar. // This happens when the user has multiple monitors (hence, multiple bars) // and doesn't share windows accross bars (a.k.a `all-outputs` = false) - std::map m_orphanWindowMap; + std::map m_orphanWindowMap; enum class SortMethod { ID, NAME, NUMBER, DEFAULT }; util::EnumParser m_enumParser; @@ -136,6 +138,7 @@ class Workspaces : public AModule, public EventHandler { bool m_anyWindowRewriteRuleUsesTitle = false; std::string m_formatWindowSeparator; + IconLoader m_iconLoader; bool m_withIcon; uint64_t m_monitorId; std::string m_activeWorkspaceName; diff --git a/src/modules/hyprland/windowcreationpayload.cpp b/src/modules/hyprland/windowcreationpayload.cpp index df7fe784..d8e3bdd5 100644 --- a/src/modules/hyprland/windowcreationpayload.cpp +++ b/src/modules/hyprland/windowcreationpayload.cpp @@ -20,7 +20,7 @@ WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data) } WindowCreationPayload::WindowCreationPayload(std::string workspace_name, - WindowAddress window_address, std::string window_repr) + WindowAddress window_address, WindowRepr window_repr) : m_window(std::move(window_repr)), m_windowAddress(std::move(window_address)), m_workspaceName(std::move(workspace_name)) { @@ -92,13 +92,13 @@ void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) { m_workspaceName = new_workspace_name; } -std::string WindowCreationPayload::repr(Workspaces &workspace_manager) { +WindowRepr WindowCreationPayload::repr(Workspaces &workspace_manager) { if (std::holds_alternative(m_window)) { return std::get(m_window); } if (std::holds_alternative(m_window)) { auto [window_class, window_title] = std::get(m_window); - return workspace_manager.getRewrite(window_class, window_title); + return {window_class, window_title, workspace_manager.getRewrite(window_class, window_title)}; } // Unreachable spdlog::error("WorkspaceWindow::repr: Unreachable"); diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index e575d1c4..229e36d2 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -6,6 +6,7 @@ #include #include "modules/hyprland/workspaces.hpp" +#include "util/icon_loader.hpp" namespace waybar::modules::hyprland { @@ -31,7 +32,13 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma false); m_button.set_relief(Gtk::RELIEF_NONE); - m_content.set_center_widget(m_label); + if (true) { + // TODO-WorkspaceTaskbar: Allow vertical? + m_content.set_orientation(Gtk::ORIENTATION_HORIZONTAL); + m_content.pack_start(m_label, false, false); + } else { + m_content.set_center_widget(m_label); + } m_button.add(m_content); initializeWindowMap(clients_data); @@ -46,7 +53,7 @@ void addOrRemoveClass(const Glib::RefPtr &context, bool condi } } -std::optional Workspace::closeWindow(WindowAddress const &addr) { +std::optional Workspace::closeWindow(WindowAddress const &addr) { if (m_windowMap.contains(addr)) { return removeWindow(addr); } @@ -108,8 +115,8 @@ bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod return false; } -std::string Workspace::removeWindow(WindowAddress const &addr) { - std::string windowRepr = m_windowMap[addr]; +WindowRepr Workspace::removeWindow(WindowAddress const &addr) { + WindowRepr windowRepr = m_windowMap[addr]; m_windowMap.erase(addr); return windowRepr; } @@ -199,21 +206,53 @@ void Workspace::update(const std::string &format, const std::string &icon) { addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); std::string windows; - auto windowSeparator = m_workspaceManager.getWindowSeparator(); + // TODO-WorkspaceTaskbar + if (false) { + auto windowSeparator = m_workspaceManager.getWindowSeparator(); - bool isNotFirst = false; + bool isNotFirst = false; - for (auto &[_pid, window_repr] : m_windowMap) { - if (isNotFirst) { - windows.append(windowSeparator); + for (auto &[_pid, window_repr] : m_windowMap) { + if (isNotFirst) { + windows.append(windowSeparator); + } + isNotFirst = true; + windows.append(window_repr.repr_rewrite); } - isNotFirst = true; - windows.append(window_repr); } m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()), fmt::arg("name", name()), fmt::arg("icon", icon), fmt::arg("windows", windows))); + + auto children = m_content.get_children(); + for (auto child : children) { + if (child != &m_label) { + m_content.remove(*child); + } + } + + for (auto &[_addr, window_repr] : m_windowMap) { + auto window_box = Gtk::make_managed(Gtk::ORIENTATION_HORIZONTAL); + auto window_icon = Gtk::make_managed(); + auto window_label = Gtk::make_managed(window_repr.window_title); + + // TODO-WorkspaceTaskbar: customizable max width and ellipsize + window_label->set_max_width_chars(20); + window_label->set_ellipsize(Pango::ELLIPSIZE_END); + + // TODO-WorkspaceTaskbar: support themes + auto app_info_ = IconLoader::get_app_info_from_app_id_list(window_repr.window_class); + // TODO-WorkspaceTaskbar: icon size + m_workspaceManager.iconLoader().image_load_icon(*window_icon, app_info_, 24); + + window_box->pack_start(*window_icon, false, false); + window_box->pack_start(*window_label, true, true); + window_box->set_tooltip_text(window_repr.window_title); + + m_content.pack_start(*window_box, true, false); + window_box->show_all(); + } } } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 047703cc..543e3e61 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -481,7 +481,7 @@ void Workspaces::onWindowMoved(std::string const &payload) { std::string workspaceName = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx); - std::string windowRepr; + WindowRepr windowRepr; // If the window was still queued to be created, just change its destination // and exit