hyprland/workspaces: implement 'active_only' option and visible class

pull/2408/head
zjeffer 2023-08-10 19:40:14 +02:00
parent 116aa5cdbd
commit 04b39ea64e
3 changed files with 80 additions and 17 deletions

View File

@ -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_; };
@ -26,6 +28,7 @@ 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_; };
@ -33,12 +36,15 @@ class Workspace {
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_;
@ -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_;
@ -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; }
@ -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();

View File

@ -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
@ -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
@ -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*

View File

@ -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);
@ -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_);
@ -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;
}
@ -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;
}
@ -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);
}
}
@ -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();
@ -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();
@ -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();
@ -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()),
@ -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)));
@ -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()) {