#include "modules/niri/workspaces.hpp" #include #include #include namespace waybar::modules::niri { Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value &config) : AModule(config, "workspaces", id, false, false), bar_(bar), box_(bar.orientation, 0) { box_.set_name("workspaces"); if (!id.empty()) { box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); if (!gIPC) gIPC = std::make_unique(); gIPC->registerForIPC("WorkspacesChanged", this); gIPC->registerForIPC("WorkspaceActivated", this); gIPC->registerForIPC("WorkspaceActiveWindowChanged", this); dp.emit(); } Workspaces::~Workspaces() { gIPC->unregisterForIPC(this); } void Workspaces::onEvent(const Json::Value &ev) { dp.emit(); } void Workspaces::doUpdate() { auto ipcLock = gIPC->lockData(); const auto alloutputs = config_["all-outputs"].asBool(); std::vector my_workspaces; const auto &workspaces = gIPC->workspaces(); std::copy_if(workspaces.cbegin(), workspaces.cend(), std::back_inserter(my_workspaces), [&](const auto &ws) { if (alloutputs) return true; return ws["output"].asString() == bar_.output->name; }); // Remove buttons for removed workspaces. for (auto it = buttons_.begin(); it != buttons_.end(); ) { auto ws = std::find_if(my_workspaces.begin(), my_workspaces.end(), [it](const auto &ws) { return ws["id"].asUInt64() == it->first; }); if (ws == my_workspaces.end()) { it = buttons_.erase(it); } else { ++it; } } // Add buttons for new workspaces, update existing ones. for (const auto &ws : my_workspaces) { auto bit = buttons_.find(ws["id"].asUInt64()); auto &button = bit == buttons_.end() ? addButton(ws) : bit->second; auto style_context = button.get_style_context(); if (ws["is_focused"].asBool()) style_context->add_class("focused"); else style_context->remove_class("focused"); if (ws["is_active"].asBool()) style_context->add_class("active"); else style_context->remove_class("active"); if (ws["output"]) { if (ws["output"].asString() == bar_.output->name) style_context->add_class("current_output"); else style_context->remove_class("current_output"); } else { style_context->remove_class("current_output"); } if (ws["active_window_id"].isNull()) style_context->add_class("empty"); else style_context->remove_class("empty"); std::string name; if (ws["name"]) { name = ws["name"].asString(); } else { name = std::to_string(ws["idx"].asUInt()); } button.set_name("niri-workspace-" + name); if (config_["format"].isString()) { auto format = config_["format"].asString(); name = fmt::format( fmt::runtime(format), fmt::arg("icon", getIcon(name, ws)), fmt::arg("value", name), fmt::arg("name", ws["name"].asString()), fmt::arg("index", ws["idx"].asUInt()), fmt::arg("output", ws["output"].asString())); } if (!config_["disable-markup"].asBool()) { static_cast(button.get_children()[0])->set_markup(name); } else { button.set_label(name); } if (config_["current-only"].asBool()) { const auto *property = alloutputs ? "is_focused" : "is_active"; if (ws[property].asBool()) button.show(); else button.hide(); } else { button.show(); } } // Refresh the button order. for (auto it = my_workspaces.cbegin(); it != my_workspaces.cend(); ++it) { const auto &ws = *it; auto pos = ws["idx"].asUInt() - 1; if (alloutputs) pos = it - my_workspaces.cbegin(); auto &button = buttons_[ws["id"].asUInt64()]; box_.reorder_child(button, pos); } } void Workspaces::update() { doUpdate(); AModule::update(); } Gtk::Button &Workspaces::addButton(const Json::Value &ws) { std::string name; if (ws["name"]) { name = ws["name"].asString(); } else { name = std::to_string(ws["idx"].asUInt()); } auto pair = buttons_.emplace(ws["id"].asUInt64(), name); auto &&button = pair.first->second; box_.pack_start(button, false, false, 0); button.set_relief(Gtk::RELIEF_NONE); if (!config_["disable-click"].asBool()) { const auto id = ws["id"].asUInt64(); button.signal_pressed().connect([=] { try { // {"Action":{"FocusWorkspace":{"reference":{"Id":1}}}} Json::Value request(Json::objectValue); auto &action = (request["Action"] = Json::Value(Json::objectValue)); auto &focusWorkspace = (action["FocusWorkspace"] = Json::Value(Json::objectValue)); auto &reference = (focusWorkspace["reference"] = Json::Value(Json::objectValue)); reference["Id"] = id; IPC::send(request); } catch (const std::exception &e) { spdlog::error("Error switching workspace: {}", e.what()); } }); } return button; } std::string Workspaces::getIcon(const std::string &value, const Json::Value &ws) { const auto &icons = config_["format-icons"]; if (!icons) return value; if (ws["is_focused"].asBool() && icons["focused"]) return icons["focused"].asString(); if (ws["is_active"].asBool() && icons["active"]) return icons["active"].asString(); if (ws["name"]) { const auto &name = ws["name"].asString(); if (icons[name]) return icons[name].asString(); } const auto idx = ws["idx"].asString(); if (icons[idx]) return icons[idx].asString(); if (icons["default"]) return icons["default"].asString(); return value; } } // namespace waybar::modules::niri