workspace taskbars: Add config parsing

Use format from config instead of hardcoding
This commit is contained in:
Pol Rivero
2024-12-31 19:51:53 +01:00
parent 69e2e249a6
commit 1c07ca0099
4 changed files with 110 additions and 35 deletions

View File

@ -62,7 +62,8 @@ class Workspace {
bool onWindowOpened(WindowCreationPayload const& create_window_paylod); bool onWindowOpened(WindowCreationPayload const& create_window_paylod);
std::optional<WindowRepr> closeWindow(WindowAddress const& addr); std::optional<WindowRepr> 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: private:
Workspaces& m_workspaceManager; Workspaces& m_workspaceManager;
@ -82,7 +83,8 @@ class Workspace {
Gtk::Button m_button; Gtk::Button m_button;
Gtk::Box m_content; Gtk::Box m_content;
Gtk::Label m_label; Gtk::Label m_labelBefore;
Gtk::Label m_labelAfter;
}; };
} // namespace waybar::modules::hyprland } // namespace waybar::modules::hyprland

View File

@ -38,8 +38,14 @@ class Workspaces : public AModule, public EventHandler {
auto activeOnly() const -> bool { return m_activeOnly; } auto activeOnly() const -> bool { return m_activeOnly; }
auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; } auto specialVisibleOnly() const -> bool { return m_specialVisibleOnly; }
auto moveToMonitor() const -> bool { return m_moveToMonitor; } 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 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 getRewrite(std::string window_class, std::string window_title);
std::string& getWindowSeparator() { return m_formatWindowSeparator; } std::string& getWindowSeparator() { return m_formatWindowSeparator; }
@ -69,6 +75,7 @@ class Workspaces : public AModule, public EventHandler {
auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void; auto populateIgnoreWorkspacesConfig(const Json::Value& config) -> void;
auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void; auto populateFormatWindowSeparatorConfig(const Json::Value& config) -> void;
auto populateWindowRewriteConfig(const Json::Value& config) -> void; auto populateWindowRewriteConfig(const Json::Value& config) -> void;
auto populateWorkspaceTaskbarConfig(const Json::Value& config) -> void;
void registerIpc(); void registerIpc();
@ -121,7 +128,7 @@ class Workspaces : public AModule, public EventHandler {
// Map for windows stored in workspaces not present in the current bar. // Map for windows stored in workspaces not present in the current bar.
// This happens when the user has multiple monitors (hence, multiple bars) // This happens when the user has multiple monitors (hence, multiple bars)
// and doesn't share windows accross bars (a.k.a `all-outputs` = false) // and doesn't share windows accross bars (a.k.a `all-outputs` = false)
std::map<WindowAddress, WindowRepr> m_orphanWindowMap; std::map<WindowAddress, WindowRepr, std::less<>> m_orphanWindowMap;
enum class SortMethod { ID, NAME, NUMBER, DEFAULT }; enum class SortMethod { ID, NAME, NUMBER, DEFAULT };
util::EnumParser<SortMethod> m_enumParser; util::EnumParser<SortMethod> m_enumParser;
@ -131,14 +138,14 @@ class Workspaces : public AModule, public EventHandler {
{"NUMBER", SortMethod::NUMBER}, {"NUMBER", SortMethod::NUMBER},
{"DEFAULT", SortMethod::DEFAULT}}; {"DEFAULT", SortMethod::DEFAULT}};
std::string m_format; std::string m_formatBefore;
std::string m_formatAfter;
std::map<std::string, std::string> m_iconsMap; std::map<std::string, std::string> m_iconsMap;
util::RegexCollection m_windowRewriteRules; util::RegexCollection m_windowRewriteRules;
bool m_anyWindowRewriteRuleUsesTitle = false; bool m_anyWindowRewriteRuleUsesTitle = false;
std::string m_formatWindowSeparator; std::string m_formatWindowSeparator;
IconLoader m_iconLoader;
bool m_withIcon; bool m_withIcon;
uint64_t m_monitorId; uint64_t m_monitorId;
std::string m_activeWorkspaceName; std::string m_activeWorkspaceName;
@ -148,6 +155,12 @@ class Workspaces : public AModule, public EventHandler {
std::vector<std::string> m_workspacesToRemove; std::vector<std::string> m_workspacesToRemove;
std::vector<WindowCreationPayload> m_windowsToCreate; std::vector<WindowCreationPayload> m_windowsToCreate;
IconLoader m_iconLoader;
bool m_enableWorkspaceTaskbar = false;
bool m_taskbarWithIcon = false;
std::string m_taskbarFormatBefore;
std::string m_taskbarFormatAfter;
std::vector<std::regex> m_ignoreWorkspaces; std::vector<std::regex> m_ignoreWorkspaces;
std::mutex m_mutex; std::mutex m_mutex;

View File

@ -35,9 +35,9 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma
if (true) { if (true) {
// TODO-WorkspaceTaskbar: Allow vertical? // TODO-WorkspaceTaskbar: Allow vertical?
m_content.set_orientation(Gtk::ORIENTATION_HORIZONTAL); m_content.set_orientation(Gtk::ORIENTATION_HORIZONTAL);
m_content.pack_start(m_label, false, false); m_content.pack_start(m_labelBefore, false, false);
} else { } else {
m_content.set_center_widget(m_label); m_content.set_center_widget(m_labelBefore);
} }
m_button.add(m_content); m_button.add(m_content);
@ -178,7 +178,7 @@ std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map
return m_name; 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 // clang-format off
if (this->m_workspaceManager.activeOnly() && \ if (this->m_workspaceManager.activeOnly() && \
!this->isActive() && \ !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"); addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor");
std::string windows; std::string windows;
// TODO-WorkspaceTaskbar // Optimization: The {windows} substitution string is only possible if the taskbar is disabled, no
if (false) { // need to compute this if enableWorkspaceTaskbar() is true
if (!m_workspaceManager.enableWorkspaceTaskbar()) {
auto windowSeparator = m_workspaceManager.getWindowSeparator(); auto windowSeparator = m_workspaceManager.getWindowSeparator();
bool isNotFirst = false; bool isNotFirst = false;
for (auto &[_pid, window_repr] : m_windowMap) { for (const auto &[_pid, window_repr] : m_windowMap) {
if (isNotFirst) { if (isNotFirst) {
windows.append(windowSeparator); 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()), auto formatBefore = m_workspaceManager.formatBefore();
fmt::arg("name", name()), fmt::arg("icon", icon), m_labelBefore.set_markup(fmt::format(fmt::runtime(formatBefore), fmt::arg("id", id()),
fmt::arg("windows", windows))); fmt::arg("name", name()), fmt::arg("icon", workspace_icon),
fmt::arg("windows", windows)));
auto children = m_content.get_children(); if (m_workspaceManager.enableWorkspaceTaskbar()) {
for (auto child : children) { updateTaskbar(workspace_icon);
if (child != &m_label) { }
}
void Workspace::updateTaskbar(const std::string &workspace_icon) {
for (auto child : m_content.get_children()) {
if (child != &m_labelBefore) {
m_content.remove(*child); 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::Box>(Gtk::ORIENTATION_HORIZONTAL); auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
auto window_icon = Gtk::make_managed<Gtk::Image>();
auto window_label = Gtk::make_managed<Gtk::Label>(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); 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<Gtk::Label>(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<Gtk::Image>();
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<Gtk::Label>(text_after);
window_box->pack_start(*window_label_after, true, true);
}
m_content.pack_start(*window_box, true, false); m_content.pack_start(*window_box, true, false);
window_box->show_all(); 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 } // namespace waybar::modules::hyprland

View File

@ -9,6 +9,7 @@
#include <utility> #include <utility>
#include "util/regex_collection.hpp" #include "util/regex_collection.hpp"
#include "util/string.hpp"
namespace waybar::modules::hyprland { namespace waybar::modules::hyprland {
@ -563,8 +564,9 @@ void Workspaces::onConfigReloaded() {
auto Workspaces::parseConfig(const Json::Value &config) -> void { auto Workspaces::parseConfig(const Json::Value &config) -> void {
const auto &configFormat = config["format"]; const auto &configFormat = config["format"];
m_format = configFormat.isString() ? configFormat.asString() : "{name}"; m_formatBefore = configFormat.isString() ? configFormat.asString() : "{name}";
m_withIcon = m_format.find("{icon}") != std::string::npos; m_withIcon = m_formatBefore.find("{icon}") != std::string::npos;
auto withWindows = m_formatBefore.find("{windows}") != std::string::npos;
if (m_withIcon && m_iconsMap.empty()) { if (m_withIcon && m_iconsMap.empty()) {
populateIconsMap(config["format-icons"]); populateIconsMap(config["format-icons"]);
@ -581,6 +583,15 @@ auto Workspaces::parseConfig(const Json::Value &config) -> void {
populateIgnoreWorkspacesConfig(config); populateIgnoreWorkspacesConfig(config);
populateFormatWindowSeparatorConfig(config); populateFormatWindowSeparatorConfig(config);
populateWindowRewriteConfig(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 { 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); }); [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) { void Workspaces::registerOrphanWindow(WindowCreationPayload create_window_payload) {
if (!create_window_payload.isEmpty(*this)) { if (!create_window_payload.isEmpty(*this)) {
m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this); m_orphanWindowMap[create_window_payload.getAddress()] = create_window_payload.repr(*this);
@ -881,7 +915,7 @@ void Workspaces::updateWorkspaceStates() {
if (updatedWorkspace != updatedWorkspaces.end()) { if (updatedWorkspace != updatedWorkspaces.end()) {
workspace->setOutput((*updatedWorkspace)["monitor"].asString()); workspace->setOutput((*updatedWorkspace)["monitor"].asString());
} }
workspace->update(m_format, workspaceIcon); workspace->update(workspaceIcon);
} }
} }