Merge pull request #2316 from MightyPlaza/workspaces
hyprland/workspaces improvements
This commit is contained in:
commit
c087d8c318
|
@ -9,27 +9,30 @@
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
struct WorkspaceDto {
|
|
||||||
int id;
|
|
||||||
|
|
||||||
static WorkspaceDto parse(const Json::Value& value);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Workspace {
|
class Workspace {
|
||||||
public:
|
public:
|
||||||
Workspace(int id);
|
Workspace(const Json::Value& workspace_data);
|
||||||
Workspace(WorkspaceDto dto);
|
|
||||||
int id() const { return id_; };
|
|
||||||
int active() const { return active_; };
|
|
||||||
std::string& select_icon(std::map<std::string, std::string>& icons_map);
|
std::string& select_icon(std::map<std::string, std::string>& icons_map);
|
||||||
void set_active(bool value = true) { active_ = value; };
|
|
||||||
Gtk::Button& button() { return button_; };
|
Gtk::Button& button() { return button_; };
|
||||||
|
|
||||||
|
int id() const { return id_; };
|
||||||
|
std::string name() const { return name_; };
|
||||||
|
std::string output() const { return output_; };
|
||||||
|
int active() const { return active_; };
|
||||||
|
bool is_special() const { return is_special_; };
|
||||||
|
|
||||||
|
auto handle_clicked(GdkEventButton* bt) -> bool;
|
||||||
|
void set_active(bool value = true) { active_ = value; };
|
||||||
|
|
||||||
void update(const std::string& format, const std::string& icon);
|
void update(const std::string& format, const std::string& icon);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int id_;
|
int id_;
|
||||||
|
std::string name_;
|
||||||
|
std::string output_;
|
||||||
|
int windows_;
|
||||||
bool active_;
|
bool active_;
|
||||||
|
bool is_special_;
|
||||||
|
|
||||||
Gtk::Button button_;
|
Gtk::Button button_;
|
||||||
Gtk::Box content_;
|
Gtk::Box content_;
|
||||||
|
@ -39,23 +42,31 @@ class Workspace {
|
||||||
class Workspaces : public AModule, public EventHandler {
|
class Workspaces : public AModule, public EventHandler {
|
||||||
public:
|
public:
|
||||||
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
|
Workspaces(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
virtual ~Workspaces();
|
~Workspaces() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
auto all_outputs() const -> bool { return all_outputs_; }
|
||||||
|
auto show_special() const -> bool { return show_special_; }
|
||||||
|
|
||||||
|
auto get_bar_output() const -> std::string { return bar_.output->name; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onEvent(const std::string&) override;
|
void onEvent(const std::string&) override;
|
||||||
void sort_workspaces();
|
void sort_workspaces();
|
||||||
void create_workspace(int id);
|
void create_workspace(Json::Value& value);
|
||||||
void remove_workspace(int id);
|
void remove_workspace(std::string name);
|
||||||
|
|
||||||
|
bool all_outputs_ = false;
|
||||||
|
bool show_special_ = false;
|
||||||
|
|
||||||
std::string format_;
|
std::string format_;
|
||||||
std::map<std::string, std::string> icons_map_;
|
std::map<std::string, std::string> icons_map_;
|
||||||
bool with_icon_;
|
bool with_icon_;
|
||||||
int active_workspace_id;
|
std::string active_workspace_name;
|
||||||
std::vector<std::unique_ptr<Workspace>> workspaces_;
|
std::vector<std::unique_ptr<Workspace>> workspaces_;
|
||||||
std::vector<int> workspaces_to_create_;
|
std::vector<Json::Value> workspaces_to_create_;
|
||||||
std::vector<int> workspaces_to_remove_;
|
std::vector<std::string> workspaces_to_remove_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
const Bar& bar_;
|
const Bar& bar_;
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
|
|
|
@ -21,10 +21,22 @@ Addressed by *hyprland/workspaces*
|
||||||
typeof: array ++
|
typeof: array ++
|
||||||
Based on the workspace id and state, the corresponding icon gets selected. See *icons*.
|
Based on the workspace id and state, the corresponding icon gets selected. See *icons*.
|
||||||
|
|
||||||
|
*show-special*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
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.
|
||||||
|
|
||||||
# FORMAT REPLACEMENTS
|
# FORMAT REPLACEMENTS
|
||||||
|
|
||||||
*{id}*: id of workspace assigned by compositor
|
*{id}*: id of workspace assigned by compositor
|
||||||
|
|
||||||
|
*{name}*: workspace name assigned by compositor
|
||||||
|
|
||||||
*{icon}*: Icon, as defined in *format-icons*.
|
*{icon}*: Icon, as defined in *format-icons*.
|
||||||
|
|
||||||
# ICONS
|
# ICONS
|
||||||
|
@ -48,7 +60,8 @@ Additional to workspace name matching, the following *format-icons* can be set.
|
||||||
"active": "",
|
"active": "",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"sort-by-number": true
|
"all-outputs": false,
|
||||||
|
"show-special": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -28,13 +28,23 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||||
icons_map_.emplace("", "");
|
icons_map_.emplace("", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto config_all_outputs = config_["all-outputs"];
|
||||||
|
if (config_all_outputs.isBool()) {
|
||||||
|
all_outputs_ = config_all_outputs.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto config_show_special = config_["show-special"];
|
||||||
|
if (config_show_special.isBool()) {
|
||||||
|
show_special_ = config_show_special.asBool();
|
||||||
|
}
|
||||||
|
|
||||||
box_.set_name("workspaces");
|
box_.set_name("workspaces");
|
||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
box_.get_style_context()->add_class(id);
|
box_.get_style_context()->add_class(id);
|
||||||
}
|
}
|
||||||
event_box_.add(box_);
|
event_box_.add(box_);
|
||||||
modulesReady = true;
|
modulesReady = true;
|
||||||
if (!gIPC.get()) {
|
if (!gIPC) {
|
||||||
gIPC = std::make_unique<IPC>();
|
gIPC = std::make_unique<IPC>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,32 +53,31 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
|
||||||
gIPC->registerForIPC("workspace", this);
|
gIPC->registerForIPC("workspace", this);
|
||||||
gIPC->registerForIPC("createworkspace", this);
|
gIPC->registerForIPC("createworkspace", this);
|
||||||
gIPC->registerForIPC("destroyworkspace", this);
|
gIPC->registerForIPC("destroyworkspace", this);
|
||||||
|
gIPC->registerForIPC("focusedmon", this);
|
||||||
|
gIPC->registerForIPC("moveworkspace", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Workspaces::update() -> void {
|
auto Workspaces::update() -> void {
|
||||||
for (int &workspace_to_remove : workspaces_to_remove_) {
|
for (std::string workspace_to_remove : workspaces_to_remove_) {
|
||||||
remove_workspace(workspace_to_remove);
|
remove_workspace(workspace_to_remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaces_to_remove_.clear();
|
workspaces_to_remove_.clear();
|
||||||
|
|
||||||
for (int &workspace_to_create : workspaces_to_create_) {
|
for (Json::Value &workspace_to_create : workspaces_to_create_) {
|
||||||
create_workspace(workspace_to_create);
|
create_workspace(workspace_to_create);
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaces_to_create_.clear();
|
workspaces_to_create_.clear();
|
||||||
|
|
||||||
for (std::unique_ptr<Workspace> &workspace : workspaces_) {
|
for (auto &workspace : workspaces_) {
|
||||||
workspace->set_active(workspace->id() == active_workspace_id);
|
workspace->set_active(workspace->name() == active_workspace_name);
|
||||||
|
|
||||||
std::string &workspace_icon = icons_map_[""];
|
std::string &workspace_icon = icons_map_[""];
|
||||||
if (with_icon_) {
|
if (with_icon_) {
|
||||||
workspace_icon = workspace->select_icon(icons_map_);
|
workspace_icon = workspace->select_icon(icons_map_);
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace->update(format_, workspace_icon);
|
workspace->update(format_, workspace_icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
AModule::update();
|
AModule::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,35 +85,60 @@ void Workspaces::onEvent(const std::string &ev) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
std::string eventName(begin(ev), begin(ev) + ev.find_first_of('>'));
|
||||||
std::string payload = ev.substr(eventName.size() + 2);
|
std::string payload = ev.substr(eventName.size() + 2);
|
||||||
|
|
||||||
if (eventName == "workspace") {
|
if (eventName == "workspace") {
|
||||||
std::from_chars(payload.data(), payload.data() + payload.size(), active_workspace_id);
|
active_workspace_name = payload;
|
||||||
|
|
||||||
} else if (eventName == "destroyworkspace") {
|
} else if (eventName == "destroyworkspace") {
|
||||||
int deleted_workspace_id;
|
workspaces_to_remove_.push_back(payload);
|
||||||
std::from_chars(payload.data(), payload.data() + payload.size(), deleted_workspace_id);
|
|
||||||
workspaces_to_remove_.push_back(deleted_workspace_id);
|
|
||||||
} else if (eventName == "createworkspace") {
|
} else if (eventName == "createworkspace") {
|
||||||
int new_workspace_id;
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
std::from_chars(payload.data(), payload.data() + payload.size(), new_workspace_id);
|
for (Json::Value workspace_json : workspaces_json) {
|
||||||
workspaces_to_create_.push_back(new_workspace_id);
|
if (workspace_json["name"].asString() == payload &&
|
||||||
|
(all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
||||||
|
(show_special() || workspace_json["name"].asString().find("special:") != 0)) {
|
||||||
|
workspaces_to_create_.push_back(workspace_json);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (eventName == "focusedmon") {
|
||||||
|
active_workspace_name = payload.substr(payload.find(',') + 1);
|
||||||
|
|
||||||
|
} else if (eventName == "moveworkspace" && !all_outputs()) {
|
||||||
|
std::string workspace = payload.substr(0, payload.find(','));
|
||||||
|
std::string new_output = payload.substr(payload.find(',') + 1);
|
||||||
|
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()) {
|
||||||
|
workspaces_to_create_.push_back(workspace_json);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
workspaces_to_remove_.push_back(workspace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dp.emit();
|
dp.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::create_workspace(int id) {
|
void Workspaces::create_workspace(Json::Value &value) {
|
||||||
workspaces_.push_back(std::make_unique<Workspace>(id));
|
workspaces_.push_back(std::make_unique<Workspace>(value));
|
||||||
Gtk::Button &new_workspace_button = workspaces_.back()->button();
|
Gtk::Button &new_workspace_button = workspaces_.back()->button();
|
||||||
box_.pack_start(new_workspace_button, false, false);
|
box_.pack_start(new_workspace_button, false, false);
|
||||||
sort_workspaces();
|
sort_workspaces();
|
||||||
new_workspace_button.show_all();
|
new_workspace_button.show_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::remove_workspace(int id) {
|
void Workspaces::remove_workspace(std::string name) {
|
||||||
auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(),
|
auto workspace = std::find_if(workspaces_.begin(), workspaces_.end(),
|
||||||
[&](std::unique_ptr<Workspace> &x) { return x->id() == id; });
|
[&](std::unique_ptr<Workspace> &x) { return x->name() == name; });
|
||||||
|
|
||||||
if (workspace == workspaces_.end()) {
|
if (workspace == workspaces_.end()) {
|
||||||
spdlog::warn("Can't find workspace with id {}", id);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,16 +147,13 @@ void Workspaces::remove_workspace(int id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::init() {
|
void Workspaces::init() {
|
||||||
const auto activeWorkspace = WorkspaceDto::parse(gIPC->getSocket1JsonReply("activeworkspace"));
|
active_workspace_name = (gIPC->getSocket1JsonReply("activeworkspace"))["name"].asString();
|
||||||
active_workspace_id = activeWorkspace.id;
|
|
||||||
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
|
||||||
for (const Json::Value &workspace_json : workspaces_json) {
|
|
||||||
workspaces_.push_back(
|
|
||||||
std::make_unique<Workspace>(Workspace(WorkspaceDto::parse(workspace_json))));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &workspace : workspaces_) {
|
const Json::Value workspaces_json = gIPC->getSocket1JsonReply("workspaces");
|
||||||
box_.pack_start(workspace->button(), false, false);
|
for (Json::Value workspace_json : workspaces_json) {
|
||||||
|
if ((all_outputs() || bar_.output->name == workspace_json["monitor"].asString()) &&
|
||||||
|
(workspace_json["name"].asString().find("special") != 0 || show_special()))
|
||||||
|
create_workspace(workspace_json);
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_workspaces();
|
sort_workspaces();
|
||||||
|
@ -136,19 +167,30 @@ Workspaces::~Workspaces() {
|
||||||
std::lock_guard<std::mutex> lg(mutex_);
|
std::lock_guard<std::mutex> lg(mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkspaceDto WorkspaceDto::parse(const Json::Value &value) {
|
Workspace::Workspace(const Json::Value &workspace_data)
|
||||||
return WorkspaceDto{value["id"].asInt()};
|
: id_(workspace_data["id"].asInt()),
|
||||||
}
|
name_(workspace_data["name"].asString()),
|
||||||
|
output_(workspace_data["monitor"].asString()), // TODO:allow using monitor desc
|
||||||
|
windows_(workspace_data["id"].asInt()),
|
||||||
|
active_(true),
|
||||||
|
is_special_(false) {
|
||||||
|
if (name_.find("name:") == 0) {
|
||||||
|
name_ = name_.substr(5);
|
||||||
|
} else if (name_.find("special") == 0) {
|
||||||
|
name_ = id_ == -99 ? name_ : name_.substr(13);
|
||||||
|
is_special_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
Workspace::Workspace(WorkspaceDto dto) : Workspace(dto.id){};
|
button_.add_events(Gdk::BUTTON_PRESS_MASK);
|
||||||
|
button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Workspace::handle_clicked),
|
||||||
|
false);
|
||||||
|
|
||||||
Workspace::Workspace(int id) : id_(id) {
|
|
||||||
button_.set_relief(Gtk::RELIEF_NONE);
|
button_.set_relief(Gtk::RELIEF_NONE);
|
||||||
content_.set_center_widget(label_);
|
content_.set_center_widget(label_);
|
||||||
button_.add(content_);
|
button_.add(content_);
|
||||||
};
|
};
|
||||||
|
|
||||||
void add_or_remove_class(Glib::RefPtr<Gtk::StyleContext> context, bool condition,
|
void add_or_remove_class(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
|
||||||
const std::string &class_name) {
|
const std::string &class_name) {
|
||||||
if (condition) {
|
if (condition) {
|
||||||
context->add_class(class_name);
|
context->add_class(class_name);
|
||||||
|
@ -158,17 +200,30 @@ void add_or_remove_class(Glib::RefPtr<Gtk::StyleContext> context, bool condition
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspace::update(const std::string &format, const std::string &icon) {
|
void Workspace::update(const std::string &format, const std::string &icon) {
|
||||||
Glib::RefPtr<Gtk::StyleContext> style_context = button_.get_style_context();
|
auto style_context = button_.get_style_context();
|
||||||
add_or_remove_class(style_context, active(), "active");
|
add_or_remove_class(style_context, active(), "active");
|
||||||
|
|
||||||
label_.set_markup(
|
label_.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
||||||
fmt::format(fmt::runtime(format), fmt::arg("id", id()), fmt::arg("icon", icon)));
|
fmt::arg("name", name()), fmt::arg("icon", icon)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workspaces::sort_workspaces() {
|
void Workspaces::sort_workspaces() {
|
||||||
std::sort(workspaces_.begin(), workspaces_.end(),
|
std::sort(workspaces_.begin(), workspaces_.end(),
|
||||||
[](std::unique_ptr<Workspace> &lhs, std::unique_ptr<Workspace> &rhs) {
|
[](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
|
||||||
return lhs->id() < rhs->id();
|
// normal -> named -> special -> named special
|
||||||
|
if (a->id() > 0 && b->id() > 0) {
|
||||||
|
return a->id() < b->id();
|
||||||
|
}
|
||||||
|
if (a->id() < 0 && b->id() < 0) {
|
||||||
|
if ((a->is_special()) ^ (a->is_special())) {
|
||||||
|
return a->id() > b->id();
|
||||||
|
}
|
||||||
|
return a->id() < b->id();
|
||||||
|
}
|
||||||
|
if ((a->id() > 0) ^ (b->id() > 0)) {
|
||||||
|
return a->id() > b->id();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (size_t i = 0; i < workspaces_.size(); ++i) {
|
for (size_t i = 0; i < workspaces_.size(); ++i) {
|
||||||
|
@ -193,7 +248,25 @@ std::string &Workspace::select_icon(std::map<std::string, std::string> &icons_ma
|
||||||
if (default_icon_it != icons_map.end()) {
|
if (default_icon_it != icons_map.end()) {
|
||||||
return default_icon_it->second;
|
return default_icon_it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return icons_map[""];
|
return icons_map[""];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
|
||||||
|
try {
|
||||||
|
if (id() > 0) { // normal
|
||||||
|
gIPC->getSocket1Reply("dispatch workspace " + std::to_string(id()));
|
||||||
|
} else if (!is_special()) { // named normal
|
||||||
|
gIPC->getSocket1Reply("dispatch workspace name" + name());
|
||||||
|
} else if (id() != -99) { // named special
|
||||||
|
gIPC->getSocket1Reply("dispatch togglespecialworkspace name" + name());
|
||||||
|
} else { // special
|
||||||
|
gIPC->getSocket1Reply("dispatch togglespecialworkspace");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
spdlog::error("Failed to dispatch workspace: {}", e.what());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
|
Loading…
Reference in New Issue