Files
Waybar/src/modules/hyprland/workspace.cpp
Pol Rivero 1c07ca0099 workspace taskbars: Add config parsing
Use format from config instead of hardcoding
2025-01-27 11:46:35 +01:00

285 lines
9.2 KiB
C++

#include <json/value.h>
#include <spdlog/spdlog.h>
#include <memory>
#include <string>
#include <utility>
#include "modules/hyprland/workspaces.hpp"
#include "util/icon_loader.hpp"
namespace waybar::modules::hyprland {
Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager,
const Json::Value &clients_data)
: m_workspaceManager(workspace_manager),
m_id(workspace_data["id"].asInt()),
m_name(workspace_data["name"].asString()),
m_output(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
m_windows(workspace_data["windows"].asInt()),
m_isActive(true),
m_isPersistentRule(workspace_data["persistent-rule"].asBool()),
m_isPersistentConfig(workspace_data["persistent-config"].asBool()) {
if (m_name.starts_with("name:")) {
m_name = m_name.substr(5);
} else if (m_name.starts_with("special")) {
m_name = m_id == -99 ? m_name : m_name.substr(8);
m_isSpecial = true;
}
m_button.add_events(Gdk::BUTTON_PRESS_MASK);
m_button.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handleClicked),
false);
m_button.set_relief(Gtk::RELIEF_NONE);
if (true) {
// TODO-WorkspaceTaskbar: Allow vertical?
m_content.set_orientation(Gtk::ORIENTATION_HORIZONTAL);
m_content.pack_start(m_labelBefore, false, false);
} else {
m_content.set_center_widget(m_labelBefore);
}
m_button.add(m_content);
initializeWindowMap(clients_data);
}
void addOrRemoveClass(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
const std::string &class_name) {
if (condition) {
context->add_class(class_name);
} else {
context->remove_class(class_name);
}
}
std::optional<WindowRepr> Workspace::closeWindow(WindowAddress const &addr) {
if (m_windowMap.contains(addr)) {
return removeWindow(addr);
}
return std::nullopt;
}
bool Workspace::handleClicked(GdkEventButton *bt) const {
if (bt->type == GDK_BUTTON_PRESS) {
try {
if (id() > 0) { // normal
if (m_workspaceManager.moveToMonitor()) {
gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor " + std::to_string(id()));
} else {
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id()));
}
} else if (!isSpecial()) { // named (this includes persistent)
if (m_workspaceManager.moveToMonitor()) {
gIPC->getSocket1Reply("dispatch focusworkspaceoncurrentmonitor name:" + name());
} else {
gIPC->getSocket1Reply("dispatch workspace name:" + name());
}
} else if (id() != -99) { // named special
gIPC->getSocket1Reply("dispatch togglespecialworkspace " + name());
} else { // special
gIPC->getSocket1Reply("dispatch togglespecialworkspace");
}
return true;
} catch (const std::exception &e) {
spdlog::error("Failed to dispatch workspace: {}", e.what());
}
}
return false;
}
void Workspace::initializeWindowMap(const Json::Value &clients_data) {
m_windowMap.clear();
for (auto client : clients_data) {
if (client["workspace"]["id"].asInt() == id()) {
insertWindow({client});
}
}
}
void Workspace::insertWindow(WindowCreationPayload create_window_paylod) {
if (!create_window_paylod.isEmpty(m_workspaceManager)) {
auto repr = create_window_paylod.repr(m_workspaceManager);
if (!repr.empty()) {
m_windowMap[create_window_paylod.getAddress()] = repr;
}
}
};
bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod) {
if (create_window_paylod.getWorkspaceName() == name()) {
insertWindow(create_window_paylod);
return true;
}
return false;
}
WindowRepr Workspace::removeWindow(WindowAddress const &addr) {
WindowRepr windowRepr = m_windowMap[addr];
m_windowMap.erase(addr);
return windowRepr;
}
std::string &Workspace::selectIcon(std::map<std::string, std::string> &icons_map) {
spdlog::trace("Selecting icon for workspace {}", name());
if (isUrgent()) {
auto urgentIconIt = icons_map.find("urgent");
if (urgentIconIt != icons_map.end()) {
return urgentIconIt->second;
}
}
if (isActive()) {
auto activeIconIt = icons_map.find("active");
if (activeIconIt != icons_map.end()) {
return activeIconIt->second;
}
}
if (isSpecial()) {
auto specialIconIt = icons_map.find("special");
if (specialIconIt != icons_map.end()) {
return specialIconIt->second;
}
}
auto namedIconIt = icons_map.find(name());
if (namedIconIt != icons_map.end()) {
return namedIconIt->second;
}
if (isVisible()) {
auto visibleIconIt = icons_map.find("visible");
if (visibleIconIt != icons_map.end()) {
return visibleIconIt->second;
}
}
if (isEmpty()) {
auto emptyIconIt = icons_map.find("empty");
if (emptyIconIt != icons_map.end()) {
return emptyIconIt->second;
}
}
if (isPersistent()) {
auto persistentIconIt = icons_map.find("persistent");
if (persistentIconIt != icons_map.end()) {
return persistentIconIt->second;
}
}
auto defaultIconIt = icons_map.find("default");
if (defaultIconIt != icons_map.end()) {
return defaultIconIt->second;
}
return m_name;
}
void Workspace::update(const std::string &workspace_icon) {
// clang-format off
if (this->m_workspaceManager.activeOnly() && \
!this->isActive() && \
!this->isPersistent() && \
!this->isVisible() && \
!this->isSpecial()) {
// clang-format on
// if activeOnly is true, hide if not active, persistent, visible or special
m_button.hide();
return;
}
if (this->m_workspaceManager.specialVisibleOnly() && this->isSpecial() && !this->isVisible()) {
m_button.hide();
return;
}
m_button.show();
auto styleContext = m_button.get_style_context();
addOrRemoveClass(styleContext, isActive(), "active");
addOrRemoveClass(styleContext, isSpecial(), "special");
addOrRemoveClass(styleContext, isEmpty(), "empty");
addOrRemoveClass(styleContext, isPersistent(), "persistent");
addOrRemoveClass(styleContext, isUrgent(), "urgent");
addOrRemoveClass(styleContext, isVisible(), "visible");
addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor");
std::string windows;
// 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 (const auto &[_pid, window_repr] : m_windowMap) {
if (isNotFirst) {
windows.append(windowSeparator);
}
isNotFirst = true;
windows.append(window_repr.repr_rewrite);
}
}
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)));
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 (const auto &[_addr, window_repr] : m_windowMap) {
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
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);
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