From 1c07ca0099951ae307ffc3418c15722e6e4f59ed Mon Sep 17 00:00:00 2001 From: Pol Rivero <65060696+pol-rivero@users.noreply.github.com> Date: Tue, 31 Dec 2024 19:51:53 +0100 Subject: [PATCH] workspace taskbars: Add config parsing Use format from config instead of hardcoding --- include/modules/hyprland/workspace.hpp | 6 +- include/modules/hyprland/workspaces.hpp | 19 +++++- src/modules/hyprland/workspace.cpp | 80 ++++++++++++++++--------- src/modules/hyprland/workspaces.cpp | 40 ++++++++++++- 4 files changed, 110 insertions(+), 35 deletions(-) diff --git a/include/modules/hyprland/workspace.hpp b/include/modules/hyprland/workspace.hpp index 7a6531f9..48a027c3 100644 --- a/include/modules/hyprland/workspace.hpp +++ b/include/modules/hyprland/workspace.hpp @@ -62,7 +62,8 @@ class Workspace { bool onWindowOpened(WindowCreationPayload const& create_window_paylod); std::optional closeWindow(WindowAddress const& addr); - void update(const std::string& format, const std::string& icon); + void update(const std::string& workspace_icon); + void updateTaskbar(const std::string& workspace_icon); private: Workspaces& m_workspaceManager; @@ -82,7 +83,8 @@ class Workspace { Gtk::Button m_button; Gtk::Box m_content; - Gtk::Label m_label; + Gtk::Label m_labelBefore; + Gtk::Label m_labelAfter; }; } // namespace waybar::modules::hyprland diff --git a/include/modules/hyprland/workspaces.hpp b/include/modules/hyprland/workspaces.hpp index fa9c0ac0..d2842b73 100644 --- a/include/modules/hyprland/workspaces.hpp +++ b/include/modules/hyprland/workspaces.hpp @@ -38,8 +38,14 @@ class Workspaces : public AModule, public EventHandler { auto activeOnly() const -> bool { return m_activeOnly; } auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; } auto moveToMonitor() const -> bool { return m_moveToMonitor; } + auto enableWorkspaceTaskbar() const -> bool { return m_enableWorkspaceTaskbar; } + auto taskbarWithIcon() const -> bool { return m_taskbarWithIcon; } auto getBarOutput() const -> std::string { return m_bar.output->name; } + auto formatBefore() const -> std::string { return m_formatBefore; } + auto formatAfter() const -> std::string { return m_formatAfter; } + auto taskbarFormatBefore() const -> std::string { return m_taskbarFormatBefore; } + auto taskbarFormatAfter() const -> std::string { return m_taskbarFormatAfter; } std::string getRewrite(std::string window_class, std::string window_title); std::string& getWindowSeparator() { return m_formatWindowSeparator; } @@ -69,6 +75,7 @@ class Workspaces : public AModule, public EventHandler { auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; auto populateWindowRewriteConfig(const Json::Value& config) -> void; + auto populateWorkspaceTaskbarConfig(const Json::Value& config) -> void; void registerIpc(); @@ -121,7 +128,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; @@ -131,14 +138,14 @@ class Workspaces : public AModule, public EventHandler { {"NUMBER", SortMethod::NUMBER}, {"DEFAULT", SortMethod::DEFAULT}}; - std::string m_format; + std::string m_formatBefore; + std::string m_formatAfter; std::map m_iconsMap; util::RegexCollection m_windowRewriteRules; bool m_anyWindowRewriteRuleUsesTitle = false; std::string m_formatWindowSeparator; - IconLoader m_iconLoader; bool m_withIcon; uint64_t m_monitorId; std::string m_activeWorkspaceName; @@ -148,6 +155,12 @@ class Workspaces : public AModule, public EventHandler { std::vector m_workspacesToRemove; std::vector m_windowsToCreate; + IconLoader m_iconLoader; + bool m_enableWorkspaceTaskbar = false; + bool m_taskbarWithIcon = false; + std::string m_taskbarFormatBefore; + std::string m_taskbarFormatAfter; + std::vector m_ignoreWorkspaces; std::mutex m_mutex; diff --git a/src/modules/hyprland/workspace.cpp b/src/modules/hyprland/workspace.cpp index 229e36d2..140f4f77 100644 --- a/src/modules/hyprland/workspace.cpp +++ b/src/modules/hyprland/workspace.cpp @@ -35,9 +35,9 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma if (true) { // TODO-WorkspaceTaskbar: Allow vertical? m_content.set_orientation(Gtk::ORIENTATION_HORIZONTAL); - m_content.pack_start(m_label, false, false); + m_content.pack_start(m_labelBefore, false, false); } else { - m_content.set_center_widget(m_label); + m_content.set_center_widget(m_labelBefore); } m_button.add(m_content); @@ -178,7 +178,7 @@ std::string &Workspace::selectIcon(std::map &icons_map return m_name; } -void Workspace::update(const std::string &format, const std::string &icon) { +void Workspace::update(const std::string &workspace_icon) { // clang-format off if (this->m_workspaceManager.activeOnly() && \ !this->isActive() && \ @@ -206,13 +206,14 @@ void Workspace::update(const std::string &format, const std::string &icon) { addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor"); std::string windows; - // TODO-WorkspaceTaskbar - if (false) { + // Optimization: The {windows} substitution string is only possible if the taskbar is disabled, no + // need to compute this if enableWorkspaceTaskbar() is true + if (!m_workspaceManager.enableWorkspaceTaskbar()) { auto windowSeparator = m_workspaceManager.getWindowSeparator(); bool isNotFirst = false; - for (auto &[_pid, window_repr] : m_windowMap) { + for (const auto &[_pid, window_repr] : m_windowMap) { if (isNotFirst) { windows.append(windowSeparator); } @@ -221,38 +222,63 @@ void Workspace::update(const std::string &format, const std::string &icon) { } } - 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 formatBefore = m_workspaceManager.formatBefore(); + m_labelBefore.set_markup(fmt::format(fmt::runtime(formatBefore), fmt::arg("id", id()), + fmt::arg("name", name()), fmt::arg("icon", workspace_icon), + fmt::arg("windows", windows))); - auto children = m_content.get_children(); - for (auto child : children) { - if (child != &m_label) { + if (m_workspaceManager.enableWorkspaceTaskbar()) { + updateTaskbar(workspace_icon); + } +} + +void Workspace::updateTaskbar(const std::string &workspace_icon) { + for (auto child : m_content.get_children()) { + if (child != &m_labelBefore) { m_content.remove(*child); } } - for (auto &[_addr, window_repr] : m_windowMap) { + for (const 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); + auto text_before = fmt::format(fmt::runtime(m_workspaceManager.taskbarFormatBefore()), + 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()) { + // TODO-WorkspaceTaskbar: support themes + auto app_info_ = IconLoader::get_app_info_from_app_id_list(window_repr.window_class); + + // TODO-WorkspaceTaskbar: icon size + auto window_icon = Gtk::make_managed(); + m_workspaceManager.iconLoader().image_load_icon(*window_icon, app_info_, 24); + window_box->pack_start(*window_icon, false, false); + } + + auto text_after = fmt::format(fmt::runtime(m_workspaceManager.taskbarFormatAfter()), + 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); + } + m_content.pack_start(*window_box, true, false); window_box->show_all(); } + + auto formatAfter = m_workspaceManager.formatAfter(); + if (!formatAfter.empty()) { + m_labelAfter.set_markup(fmt::format(fmt::runtime(formatAfter), fmt::arg("id", id()), + fmt::arg("name", name()), + fmt::arg("icon", workspace_icon))); + m_content.pack_end(m_labelAfter, false, false); + m_labelAfter.show(); + } } } // namespace waybar::modules::hyprland diff --git a/src/modules/hyprland/workspaces.cpp b/src/modules/hyprland/workspaces.cpp index 543e3e61..917999b7 100644 --- a/src/modules/hyprland/workspaces.cpp +++ b/src/modules/hyprland/workspaces.cpp @@ -9,6 +9,7 @@ #include #include "util/regex_collection.hpp" +#include "util/string.hpp" namespace waybar::modules::hyprland { @@ -563,8 +564,9 @@ void Workspaces::onConfigReloaded() { auto Workspaces::parseConfig(const Json::Value &config) -> void { const auto &configFormat = config["format"]; - m_format = configFormat.isString() ? configFormat.asString() : "{name}"; - m_withIcon = m_format.find("{icon}") != std::string::npos; + m_formatBefore = configFormat.isString() ? configFormat.asString() : "{name}"; + m_withIcon = m_formatBefore.find("{icon}") != std::string::npos; + auto withWindows = m_formatBefore.find("{windows}") != std::string::npos; if (m_withIcon && m_iconsMap.empty()) { populateIconsMap(config["format-icons"]); @@ -581,6 +583,15 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void { populateIgnoreWorkspacesConfig(config); populateFormatWindowSeparatorConfig(config); populateWindowRewriteConfig(config); + + if (withWindows) { + populateWorkspaceTaskbarConfig(config); + } + if (m_enableWorkspaceTaskbar) { + auto parts = split(m_formatBefore, "{windows}", 1); + m_formatBefore = parts[0]; + m_formatAfter = parts.size() > 1 ? parts[1] : ""; + } } auto Workspaces::populateIconsMap(const Json::Value &formatIcons) -> void { @@ -653,6 +664,29 @@ auto Workspaces::populateWindowRewriteConfig(const Json::Value &config) -> void [this](std::string &window_rule) { return windowRewritePriorityFunction(window_rule); }); } +auto Workspaces::populateWorkspaceTaskbarConfig(const Json::Value &config) -> void { + const auto &workspaceTaskbar = config["workspace-taskbar"]; + if (!workspaceTaskbar.isObject()) { + spdlog::debug("workspace-taskbar is not defined or is not an object, using default rules."); + return; + } + + populateBoolConfig(workspaceTaskbar, "enable", m_enableWorkspaceTaskbar); + + if (workspaceTaskbar["format"].isString()) { + /* The user defined a format string, use it */ + auto parts = split(workspaceTaskbar["format"].asString(), "{icon}", 1); + m_taskbarFormatBefore = parts[0]; + if (parts.size() > 1) { + m_taskbarWithIcon = true; + m_taskbarFormatAfter = parts[1]; + } + } else { + /* The default is to only show the icon */ + m_taskbarWithIcon = true; + } +} + void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) { if (!create_window_payload.isEmpty(*this)) { m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this); @@ -881,7 +915,7 @@ void Workspaces::updateWorkspaceStates() { if (updatedWorkspace != updatedWorkspaces.end()) { workspace->setOutput((*updatedWorkspace)["monitor"].asString()); } - workspace->update(m_format, workspaceIcon); + workspace->update(workspaceIcon); } }