Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement 'active_only' option and 'visible' class in hyprland/workspaces #2408

Merged
merged 1 commit into from Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 10 additions & 1 deletion include/modules/hyprland/workspaces.hpp
Expand Up @@ -14,9 +14,11 @@

namespace waybar::modules::hyprland {

class Workspaces;

class Workspace {
public:
explicit Workspace(const Json::Value& workspace_data);
explicit Workspace(const Json::Value& workspace_data, Workspaces& workspace_manager);
std::string& select_icon(std::map<std::string, std::string>& icons_map);
Gtk::Button& button() { return button_; };

Expand All @@ -26,19 +28,23 @@ class Workspace {
bool active() const { return active_; };
bool is_special() const { return is_special_; };
bool is_persistent() const { return is_persistent_; };
bool is_visible() const { return is_visible_; };
bool is_empty() const { return windows_ == 0; };
bool is_urgent() const { return is_urgent_; };

auto handle_clicked(GdkEventButton* bt) -> bool;
void set_active(bool value = true) { active_ = value; };
void set_persistent(bool value = true) { is_persistent_ = value; };
void set_urgent(bool value = true) { is_urgent_ = value; };
void set_visible(bool value = true) { is_visible_ = value; };
void set_windows(uint value) { windows_ = value; };
void set_name(std::string value) { name_ = value; };

void update(const std::string& format, const std::string& icon);

private:
Workspaces& workspace_manager_;

int id_;
std::string name_;
std::string output_;
Expand All @@ -47,6 +53,7 @@ class Workspace {
bool is_special_ = false;
bool is_persistent_ = false;
bool is_urgent_ = false;
bool is_visible_ = false;

Gtk::Button button_;
Gtk::Box content_;
Expand All @@ -62,6 +69,7 @@ class Workspaces : public AModule, public EventHandler {

auto all_outputs() const -> bool { return all_outputs_; }
auto show_special() const -> bool { return show_special_; }
auto active_only() const -> bool { return active_only_; }

auto get_bar_output() const -> std::string { return bar_.output->name; }

Expand All @@ -75,6 +83,7 @@ class Workspaces : public AModule, public EventHandler {

bool all_outputs_ = false;
bool show_special_ = false;
bool active_only_ = false;

void fill_persistent_workspaces();
void create_persistent_workspaces();
Expand Down
13 changes: 10 additions & 3 deletions man/waybar-hyprland-workspaces.5.scd
Expand Up @@ -24,13 +24,18 @@ Addressed by *hyprland/workspaces*
*show-special*: ++
typeof: bool ++
default: false ++
If set to true special workspaces will be shown.
If set to true, special workspaces will be shown.

*all-outputs*: ++
typeof: bool ++
default: false ++
If set to false workspaces group will be shown only in assigned output. Otherwise all workspace groups are shown.

*active-only*: ++
typeof: bool ++
default: false ++
If set to true, only the active workspace will be shown.

# FORMAT REPLACEMENTS

*{id}*: id of workspace assigned by compositor
Expand All @@ -43,10 +48,11 @@ Addressed by *hyprland/workspaces*

Additional to workspace name matching, the following *format-icons* can be set.

- *default*: Will be shown, when no string match is found.
- *default*: Will be shown, when no string match is found and none of the below conditions have defined icons.
- *active*: Will be shown, when workspace is active
- *special*: Will be shown on non-active special workspaces
- *empty*: Will be shown on empty persistent workspaces
- *empty*: Will be shown on non-active, non-special empty persistent workspaces
- *visible*: Will be shown on workspaces that are visible but not active. For example: this is useful if you want your visible workspaces on other monitors to have the same look as active.
- *persistent*: Will be shown on non-empty persistent workspaces

# EXAMPLES
Expand Down Expand Up @@ -95,6 +101,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
- *#workspaces button*
- *#workspaces button.active*
- *#workspaces button.empty*
- *#workspaces button.visible*
- *#workspaces button.persistent*
- *#workspaces button.special*
- *#workspaces button.urgent*
73 changes: 60 additions & 13 deletions src/modules/hyprland/workspaces.cpp
Expand Up @@ -38,6 +38,11 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
show_special_ = config_show_special.asBool();
}

auto config_active_only = config_["active-only"];
if (config_active_only.isBool()) {
active_only_ = config_active_only.asBool();
}

box_.set_name("workspaces");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
Expand Down Expand Up @@ -75,11 +80,29 @@ auto Workspaces::update() -> void {

workspaces_to_create_.clear();

// get all active workspaces
auto monitors = gIPC->getSocket1JsonReply("monitors");
std::vector<std::string> visible_workspaces;
for (Json::Value &monitor : monitors) {
auto ws = monitor["activeWorkspace"];
if (ws.isObject() && (ws["name"].isString())) {
visible_workspaces.push_back(ws["name"].asString());
}
}

for (auto &workspace : workspaces_) {
// active
workspace->set_active(workspace->name() == active_workspace_name_);
if (workspace->name() == active_workspace_name_ && workspace.get()->is_urgent()) {
// disable urgency if workspace is active
if (workspace->name() == active_workspace_name_ && workspace->is_urgent()) {
workspace->set_urgent(false);
}

// visible
workspace->set_visible(std::find(visible_workspaces.begin(), visible_workspaces.end(),
workspace->name()) != visible_workspaces.end());

// set workspace icon
std::string &workspace_icon = icons_map_[""];
if (with_icon_) {
workspace_icon = workspace->select_icon(icons_map_);
Expand All @@ -103,9 +126,10 @@ void Workspaces::onEvent(const std::string &ev) {
} else if (eventName == "createworkspace") {
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
for (Json::Value workspace_json : workspaces_json) {
if (workspace_json["name"].asString() == payload &&
std::string name = workspace_json["name"].asString();
if (name == payload &&
(all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
(show_special() || !workspace_json["name"].asString().starts_with("special"))) {
(show_special() || !name.starts_with("special"))) {
workspaces_to_create_.push_back(workspace_json);
break;
}
Expand All @@ -120,8 +144,8 @@ void Workspaces::onEvent(const std::string &ev) {
if (bar_.output->name == new_output) { // TODO: implement this better
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
for (Json::Value workspace_json : workspaces_json) {
if (workspace_json["name"].asString() == workspace &&
bar_.output->name == workspace_json["monitor"].asString()) {
std::string name = workspace_json["name"].asString();
if (name == workspace && bar_.output->name == workspace_json["monitor"].asString()) {
workspaces_to_create_.push_back(workspace_json);
break;
}
Expand Down Expand Up @@ -154,15 +178,15 @@ void Workspaces::update_window_count() {
auto workspace_json = std::find_if(
workspaces_json.begin(), workspaces_json.end(),
[&](Json::Value const &x) { return x["name"].asString() == workspace->name(); });
uint32_t count = 0;
if (workspace_json != workspaces_json.end()) {
try {
workspace->set_windows((*workspace_json)["windows"].asUInt());
count = (*workspace_json)["windows"].asUInt();
} catch (const std::exception &e) {
spdlog::error("Failed to update window count: {}", e.what());
}
} else {
workspace->set_windows(0);
}
workspace->set_windows(count);
}
}

Expand All @@ -181,7 +205,7 @@ void Workspaces::create_workspace(Json::Value &value) {
}

// create new workspace
workspaces_.emplace_back(std::make_unique<Workspace>(value));
workspaces_.emplace_back(std::make_unique<Workspace>(value, *this));
Gtk::Button &new_workspace_button = workspaces_.back()->button();
box_.pack_start(new_workspace_button, false, false);
sort_workspaces();
Expand All @@ -207,7 +231,7 @@ void Workspaces::remove_workspace(std::string name) {
}

void Workspaces::fill_persistent_workspaces() {
if (config_["persistent_workspaces"].isObject() && !all_outputs()) {
if (config_["persistent_workspaces"].isObject()) {
const Json::Value persistent_workspaces = config_["persistent_workspaces"];
const std::vector<std::string> keys = persistent_workspaces.getMemberNames();

Expand Down Expand Up @@ -297,8 +321,9 @@ void Workspaces::init() {
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
for (Json::Value workspace_json : workspaces_json) {
if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
(!workspace_json["name"].asString().starts_with("special") || show_special()))
(!workspace_json["name"].asString().starts_with("special") || show_special())) {
create_workspace(workspace_json);
}
}

update_window_count();
Expand All @@ -314,8 +339,9 @@ Workspaces::~Workspaces() {
std::lock_guard<std::mutex> lg(mutex_);
}

Workspace::Workspace(const Json::Value &workspace_data)
: id_(workspace_data["id"].asInt()),
Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_manager)
: workspace_manager_(workspace_manager),
id_(workspace_data["id"].asInt()),
name_(workspace_data["name"].asString()),
output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
windows_(workspace_data["windows"].asInt()),
Expand Down Expand Up @@ -350,12 +376,26 @@ void add_or_remove_class(const Glib::RefPtr<Gtk::StyleContext> &context, bool co
}

void Workspace::update(const std::string &format, const std::string &icon) {
// clang-format off
if (this->workspace_manager_.active_only() && \
!this->active() && \
!this->is_persistent() && \
!this->is_visible() && \
!this->is_special()) {
// clang-format on
// if active_only is true, hide if not active, persistent, visible or special
button_.hide();
return;
}
button_.show();

auto style_context = button_.get_style_context();
add_or_remove_class(style_context, active(), "active");
add_or_remove_class(style_context, is_special(), "special");
add_or_remove_class(style_context, is_empty(), "empty");
add_or_remove_class(style_context, is_persistent(), "persistent");
add_or_remove_class(style_context, is_urgent(), "urgent");
add_or_remove_class(style_context, is_visible(), "visible");

label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
fmt::arg("name", name()), fmt::arg("icon", icon)));
Expand Down Expand Up @@ -420,6 +460,13 @@ std::string &Workspace::select_icon(std::map<std::string, std::string> &icons_ma
return named_icon_it->second;
}

if (is_visible()) {
auto visible_icon_it = icons_map.find("visible");
if (visible_icon_it != icons_map.end()) {
return visible_icon_it->second;
}
}

if (is_empty()) {
auto empty_icon_it = icons_map.find("empty");
if (empty_icon_it != icons_map.end()) {
Expand Down