hyprland/workspaces: expose workspace name as CSS class
Enable per-workspace styling by adding the workspace name as a
CSS class to each workspace button. The name is sanitized to a
valid CSS identifier: lowercased, non-alphanumeric characters
collapsed to hyphens, digit-leading names prefixed with "ws-".
Examples: "IDE" → .ide, "special:telegram" → .telegram, "1" → .ws-1
Allows users to style individual workspaces:
#workspaces button.telegram { color: #54a0e0; }
This commit is contained in:
@@ -71,6 +71,7 @@ class Workspace {
|
||||
|
||||
int m_id;
|
||||
std::string m_name;
|
||||
std::string m_prevNameClass;
|
||||
std::string m_output;
|
||||
uint m_windows;
|
||||
bool m_isActive = false;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <json/value.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -9,6 +10,33 @@
|
||||
#include "util/command.hpp"
|
||||
#include "util/icon_loader.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr std::string_view kCssClassPrefix = "ws-";
|
||||
|
||||
// Convert a workspace name to a valid CSS class name.
|
||||
// Lowercases, replaces non-alphanumeric runs with single hyphens,
|
||||
// and prefixes digit-leading names (CSS classes can't start with a digit).
|
||||
std::string sanitizeCssClass(const std::string& name) {
|
||||
std::string result;
|
||||
result.reserve(name.size() + kCssClassPrefix.size());
|
||||
for (auto c : name) {
|
||||
auto uc = static_cast<unsigned char>(c);
|
||||
if (std::isalnum(uc)) {
|
||||
result += static_cast<char>(std::tolower(uc));
|
||||
} else if (!result.empty() && result.back() != '-') {
|
||||
result += '-';
|
||||
}
|
||||
}
|
||||
if (!result.empty() && result.back() == '-') {
|
||||
result.pop_back();
|
||||
}
|
||||
if (!result.empty() && std::isdigit(static_cast<unsigned char>(result.front()))) {
|
||||
result.insert(0, kCssClassPrefix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace waybar::modules::hyprland {
|
||||
|
||||
Workspace::Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager,
|
||||
@@ -209,6 +237,7 @@ std::string& Workspace::selectIcon(std::map<std::string, std::string>& icons_map
|
||||
return m_name;
|
||||
}
|
||||
|
||||
|
||||
void Workspace::update(const std::string& workspace_icon) {
|
||||
if (this->m_workspaceManager.persistentOnly() && !this->isPersistent()) {
|
||||
m_button.hide();
|
||||
@@ -240,6 +269,17 @@ void Workspace::update(const std::string& workspace_icon) {
|
||||
addOrRemoveClass(styleContext, isVisible(), "visible");
|
||||
addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor");
|
||||
|
||||
// Add workspace name as CSS class for per-workspace styling
|
||||
if (!m_prevNameClass.empty()) {
|
||||
styleContext->remove_class(m_prevNameClass);
|
||||
m_prevNameClass.clear();
|
||||
}
|
||||
auto nameClass = sanitizeCssClass(name());
|
||||
if (!nameClass.empty()) {
|
||||
styleContext->add_class(nameClass);
|
||||
m_prevNameClass = nameClass;
|
||||
}
|
||||
|
||||
std::string windows;
|
||||
// Optimization: The {windows} substitution string is only possible if the taskbar is disabled, no
|
||||
// need to compute this if enableTaskbar() is true
|
||||
|
||||
Reference in New Issue
Block a user