Merge branch 'master' into darkmode

This commit is contained in:
Alexis Rouillard 2023-09-11 09:25:45 +02:00 committed by GitHub
commit fc67558717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 429 additions and 77 deletions

View File

@ -91,6 +91,9 @@ class Bar {
bool vertical = false;
Gtk::Window window;
int x_global;
int y_global;
#ifdef HAVE_SWAY
std::string bar_id;
#endif
@ -102,11 +105,16 @@ class Bar {
void setupAltFormatKeyForModule(const std::string &module_name);
void setupAltFormatKeyForModuleList(const char *module_list_name);
void setMode(const bar_mode &);
void onConfigure(GdkEventConfigure *ev);
void configureGlobalOffset(int width, int height);
void onOutputGeometryChanged();
/* Copy initial set of modes to allow customization */
bar_mode_map configured_modes = PRESET_MODES;
std::string last_mode_{MODE_DEFAULT};
struct bar_margins margins_;
std::unique_ptr<BarSurface> surface_impl_;
Gtk::Box left_;
Gtk::Box center_;

View File

@ -1,10 +1,12 @@
#pragma once
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>
#include "util/json.hpp"

View File

@ -1,5 +1,9 @@
#pragma once
#include <fmt/format.h>
#include <string>
#include "ALabel.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"

View File

@ -1,5 +1,9 @@
#pragma once
#include <fmt/format.h>
#include <string>
#include "ALabel.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"

View File

@ -1,5 +1,9 @@
#pragma once
#include <fmt/format.h>
#include <string>
#include "AAppIconLabel.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"

View File

@ -1,17 +1,25 @@
#pragma once
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "AModule.hpp"
#include "bar.hpp"
#include "modules/hyprland/backend.hpp"
#include "util/enum.hpp"
namespace waybar::modules::hyprland {
class Workspaces;
class Workspace {
public:
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_; };
@ -21,6 +29,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_; };
@ -28,11 +37,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_;
@ -41,6 +54,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_;
@ -56,6 +70,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; }
@ -66,9 +81,20 @@ class Workspaces : public AModule, public EventHandler {
void create_workspace(Json::Value& value);
void remove_workspace(std::string name);
void set_urgent_workspace(std::string windowaddress);
void parse_config(const Json::Value& config);
void register_ipc();
bool all_outputs_ = false;
bool show_special_ = false;
bool active_only_ = false;
enum class SORT_METHOD { ID, NAME, NUMBER, DEFAULT };
util::EnumParser<SORT_METHOD> enum_parser_;
SORT_METHOD sort_by_ = SORT_METHOD::DEFAULT;
std::map<std::string, SORT_METHOD> sort_map_ = {{"ID", SORT_METHOD::ID},
{"NAME", SORT_METHOD::NAME},
{"NUMBER", SORT_METHOD::NUMBER},
{"DEFAULT", SORT_METHOD::DEFAULT}};
void fill_persistent_workspaces();
void create_persistent_workspaces();

View File

@ -84,6 +84,8 @@ class Item : public sigc::trackable {
// visibility of items with Status == Passive
bool show_passive_ = false;
const Bar& bar_;
Glib::RefPtr<Gio::DBus::Proxy> proxy_;
Glib::RefPtr<Gio::Cancellable> cancellable_;
std::set<std::string_view> update_pending_;

19
include/util/enum.hpp Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <map>
#include <stdexcept>
#include <string>
namespace waybar::util {
template <typename EnumType>
struct EnumParser {
public:
EnumParser();
~EnumParser();
EnumType parseStringToEnum(const std::string& str,
const std::map<std::string, EnumType>& enumMap);
};
} // namespace waybar::util

View File

@ -1,5 +1,6 @@
#pragma once
#include <iostream>
#include <string>
const std::string WHITESPACE = " \n\r\t\f\v";
@ -15,3 +16,10 @@ inline std::string rtrim(const std::string& s) {
}
inline std::string trim(const std::string& s) { return rtrim(ltrim(s)); }
inline std::string capitalize(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c) { return std::toupper(c); });
return result;
}

View File

@ -24,13 +24,26 @@ 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.
*sort-by*: ++
typeof: string ++
default: "default" ++
If set to number, workspaces will sort by number.
If set to name, workspaces will sort by name.
If set to id, workspaces will sort by id.
If none of those, workspaces will sort with default behavior.
# FORMAT REPLACEMENTS
*{id}*: id of workspace assigned by compositor
@ -43,10 +56,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
@ -63,7 +77,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
"active": "",
"default": ""
},
"persistent_workspaces": {
"persistent-workspaces": {
"*": 5, // 5 workspaces by default on every monitor
"HDMI-A-1": 3 // but only three on HDMI-A-1
}
@ -82,7 +96,7 @@ Additional to workspace name matching, the following *format-icons* can be set.
"active": "",
"default": ""
},
"persistent_workspaces": {
"persistent-workspaces": {
"*": [ 2,3,4,5 ], // 2-5 on every monitor
"HDMI-A-1": [ 1 ] // but only workspace 1 on HDMI-A-1
}
@ -95,6 +109,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

@ -60,7 +60,7 @@ Addressed by *sway/workspaces*
default: false ++
If set to true. Only focused workspaces will be shown.
*persistent_workspaces*: ++
*persistent-workspaces*: ++
typeof: json (see below) ++
default: empty ++
Lists workspaces that should always be shown, even when non existent
@ -112,7 +112,7 @@ an empty list denoting all outputs.
```
"sway/workspaces": {
"persistent_workspaces": {
"persistent-workspaces": {
"3": [], // Always show a workspace with name '3', on all outputs if it does not exists
"4": ["eDP-1"], // Always show a workspace with name '4', on output 'eDP-1' if it does not exists
"5": ["eDP-1", "DP-2"] // Always show a workspace with name '5', on outputs 'eDP-1' and 'DP-2' if it does not exists

View File

@ -273,28 +273,39 @@ Valid options for the (optional) "orientation" property are: "horizontal", "vert
- *waybar-cpu(5)*
- *waybar-custom(5)*
- *waybar-disk(5)*
- *waybar-dwl-tags(5)*
- *waybar-gamemode(5)*
- *waybar-hyprland-language(5)*
- *waybar-hyprland-submap(5)*
- *waybar-hyprland-window(5)*
- *waybar-hyprland-workspaces(5)*
- *waybar-idle-inhibitor(5)*
- *waybar-image(5)*
- *waybar-inhibitor(5)*
- *waybar-jack(5)*
- *waybar-keyboard-state(5)*
- *waybar-memory(5)*
- *waybar-mpd(5)*
- *waybar-mpris(5)*
- *waybar-network(5)*
- *waybar-pulseaudio(5)*
- *waybar-river-layout(5)*
- *waybar-river-mode(5)*
- *waybar-river-tags(5)*
- *waybar-river-window(5)*
- *waybar-river-layout(5)*
- *waybar-sndio(5)*
- *waybar-states(5)*
- *waybar-sway-language(5)*
- *waybar-sway-mode(5)*
- *waybar-sway-scratchpad(5)*
- *waybar-sway-window(5)*
- *waybar-sway-workspaces(5)*
- *waybar-temperature(5)*
- *waybar-tray(5)*
- *waybar-upower(5)*
- *waybar-wireplumber(5)*
- *waybar-wlr-taskbar(5)*
- *waybar-wlr-workspaces(5)*
- *waybar-temperature(5)*
- *waybar-tray(5)*
# SEE ALSO

View File

@ -172,6 +172,7 @@ src_files = files(
'src/config.cpp',
'src/group.cpp',
'src/util/portal.cpp',
'src/util/enum.cpp',
'src/util/prepare_for_sleep.cpp',
'src/util/ustring_clen.cpp',
'src/util/sanitize_str.cpp',

View File

@ -481,6 +481,9 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
: output(w_output),
config(w_config),
window{Gtk::WindowType::WINDOW_TOPLEVEL},
x_global(0),
y_global(0),
margins_{.top = 0, .right = 0, .bottom = 0, .left = 0},
left_(Gtk::ORIENTATION_HORIZONTAL, 0),
center_(Gtk::ORIENTATION_HORIZONTAL, 0),
right_(Gtk::ORIENTATION_HORIZONTAL, 0),
@ -516,8 +519,6 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
uint32_t height = config["height"].isUInt() ? config["height"].asUInt() : 0;
uint32_t width = config["width"].isUInt() ? config["width"].asUInt() : 0;
struct bar_margins margins_;
if (config["margin-top"].isInt() || config["margin-right"].isInt() ||
config["margin-bottom"].isInt() || config["margin-left"].isInt()) {
margins_ = {
@ -563,6 +564,10 @@ waybar::Bar::Bar(struct waybar_output* w_output, const Json::Value& w_config)
margins_ = {.top = gaps, .right = gaps, .bottom = gaps, .left = gaps};
}
window.signal_configure_event().connect_notify(sigc::mem_fun(*this, &Bar::onConfigure));
output->monitor->property_geometry().signal_changed().connect(
sigc::mem_fun(*this, &Bar::onOutputGeometryChanged));
#ifdef HAVE_GTK_LAYER_SHELL
bool use_gls = config["gtk-layer-shell"].isBool() ? config["gtk-layer-shell"].asBool() : true;
if (use_gls) {
@ -674,6 +679,7 @@ void waybar::Bar::onMap(GdkEventAny*) {
*/
auto gdk_window = window.get_window()->gobj();
surface = gdk_wayland_window_get_wl_surface(gdk_window);
configureGlobalOffset(gdk_window_get_width(gdk_window), gdk_window_get_height(gdk_window));
}
void waybar::Bar::setVisible(bool value) {
@ -815,3 +821,47 @@ auto waybar::Bar::setupWidgets() -> void {
right_.pack_end(*module, false, false);
}
}
void waybar::Bar::onConfigure(GdkEventConfigure* ev) {
configureGlobalOffset(ev->width, ev->height);
}
void waybar::Bar::configureGlobalOffset(int width, int height) {
auto monitor_geometry = *output->monitor->property_geometry().get_value().gobj();
auto position = config["position"].asString();
int x;
int y;
if (position == "bottom") {
if (width + margins_.left + margins_.right >= monitor_geometry.width)
x = margins_.left;
else
x = (monitor_geometry.width - width) / 2;
y = monitor_geometry.height - height - margins_.bottom;
} else if (position == "left") {
x = margins_.left;
if (height + margins_.top + margins_.bottom >= monitor_geometry.height)
y = margins_.top;
else
y = (monitor_geometry.height - height) / 2;
} else if (position == "right") {
x = monitor_geometry.width - width - margins_.right;
if (height + margins_.top + margins_.bottom >= monitor_geometry.height)
y = margins_.top;
else
y = (monitor_geometry.height - height) / 2;
} else {
// position is top
if (width + margins_.left + margins_.right >= monitor_geometry.width)
x = margins_.left;
else
x = (monitor_geometry.width - width) / 2;
y = margins_.top;
}
x_global = x + monitor_geometry.x;
y_global = y + monitor_geometry.y;
}
void waybar::Bar::onOutputGeometryChanged() {
configureGlobalOffset(window.get_width(), window.get_height());
}

View File

@ -534,6 +534,13 @@ const std::tuple<uint8_t, float, std::string, float> waybar::modules::Battery::g
}
}
// Handle weighted-average
if ((config_["weighted-average"].isBool() ? config_["weighted-average"].asBool() : false) &&
total_energy_exists && total_energy_full_exists) {
if (total_energy_full > 0.0f)
calculated_capacity = ((float)total_energy * 100.0f / (float)total_energy_full);
}
// Handle design-capacity
if ((config_["design-capacity"].isBool() ? config_["design-capacity"].asBool() : false) &&
total_energy_exists && total_energy_full_design_exists) {

View File

@ -166,9 +166,15 @@ std::string IPC::getSocket1Reply(const std::string& rq) {
std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock";
strcpy(serverAddress.sun_path, socketPath.c_str());
// Use snprintf to copy the socketPath string into serverAddress.sun_path
if (snprintf(serverAddress.sun_path, sizeof(serverAddress.sun_path), "%s", socketPath.c_str()) <
0) {
spdlog::error("Hyprland IPC: Couldn't copy socket path (6)");
return "";
}
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
if (connect(SERVERSOCKET, reinterpret_cast<sockaddr*>(&serverAddress), sizeof(serverAddress)) <
0) {
spdlog::error("Hyprland IPC: Couldn't connect to " + socketPath + ". (3)");
return "";
}

View File

@ -4,8 +4,7 @@
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbregistry.h>
#include <util/sanitize_str.hpp>
#include "util/sanitize_str.hpp"
#include "util/string.hpp"
namespace waybar::modules::hyprland {
@ -97,7 +96,6 @@ void Language::initLanguage() {
spdlog::debug("hyprland language initLanguage found {}", layout_.full_name);
dp.emit();
} catch (std::exception& e) {
spdlog::error("hyprland language initLanguage failed with {}", e.what());
}

View File

@ -2,7 +2,7 @@
#include <spdlog/spdlog.h>
#include <util/sanitize_str.hpp>
#include "util/sanitize_str.hpp"
namespace waybar::modules::hyprland {

View File

@ -6,12 +6,11 @@
#include <spdlog/spdlog.h>
#include <algorithm>
#include <regex>
#include <util/sanitize_str.hpp>
#include <vector>
#include "modules/hyprland/backend.hpp"
#include "util/rewrite_string.hpp"
#include "util/sanitize_str.hpp"
namespace waybar::modules::hyprland {

View File

@ -14,6 +14,20 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
: AModule(config, "workspaces", id, false, false),
bar_(bar),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0) {
parse_config(config);
box_.set_name("workspaces");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
}
event_box_.add(box_);
register_ipc();
init();
}
auto Workspaces::parse_config(const Json::Value &config) -> void {
Json::Value config_format = config["format"];
format_ = config_format.isString() ? config_format.asString() : "{name}";
@ -38,23 +52,37 @@ Workspaces::Workspaces(const std::string &id, const Bar &bar, const Json::Value
show_special_ = config_show_special.asBool();
}
box_.set_name("workspaces");
if (!id.empty()) {
box_.get_style_context()->add_class(id);
auto config_active_only = config_["active-only"];
if (config_active_only.isBool()) {
active_only_ = config_active_only.asBool();
}
event_box_.add(box_);
auto config_sort_by = config_["sort-by"];
if (config_sort_by.isString()) {
auto sort_by_str = config_sort_by.asString();
try {
sort_by_ = enum_parser_.parseStringToEnum(sort_by_str, sort_map_);
} catch (const std::invalid_argument &e) {
// Handle the case where the string is not a valid enum representation.
sort_by_ = SORT_METHOD::DEFAULT;
g_warning("Invalid string representation for sort-by. Falling back to default sort method.");
}
}
}
auto Workspaces::register_ipc() -> void {
modulesReady = true;
if (!gIPC) {
gIPC = std::make_unique<IPC>();
}
init();
gIPC->registerForIPC("workspace", this);
gIPC->registerForIPC("createworkspace", this);
gIPC->registerForIPC("destroyworkspace", this);
gIPC->registerForIPC("focusedmon", this);
gIPC->registerForIPC("moveworkspace", this);
gIPC->registerForIPC("renameworkspace", this);
gIPC->registerForIPC("openwindow", this);
gIPC->registerForIPC("closewindow", this);
gIPC->registerForIPC("movewindow", this);
@ -74,11 +102,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_);
@ -102,9 +148,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;
}
@ -119,8 +166,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;
}
@ -132,6 +179,19 @@ void Workspaces::onEvent(const std::string &ev) {
update_window_count();
} else if (eventName == "urgent") {
set_urgent_workspace(payload);
} else if (eventName == "renameworkspace") {
std::string workspace_id_str = payload.substr(0, payload.find(','));
int workspace_id = workspace_id_str == "special" ? -99 : std::stoi(workspace_id_str);
std::string new_name = payload.substr(payload.find(',') + 1);
for (auto &workspace : workspaces_) {
if (workspace->id() == workspace_id) {
if (workspace->name() == active_workspace_name_) {
active_workspace_name_ = new_name;
}
workspace->set_name(new_name);
break;
}
}
}
dp.emit();
@ -143,15 +203,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);
}
}
@ -170,7 +230,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();
@ -196,8 +256,15 @@ void Workspaces::remove_workspace(std::string name) {
}
void Workspaces::fill_persistent_workspaces() {
if (config_["persistent_workspaces"].isObject() && !all_outputs()) {
const Json::Value persistent_workspaces = config_["persistent_workspaces"];
if (config_["persistent_workspaces"].isObject()) {
spdlog::warn(
"persistent_workspaces is deprecated. Please change config to use persistent-workspaces.");
}
if (config_["persistent-workspaces"].isObject() || config_["persistent_workspaces"].isObject()) {
const Json::Value persistent_workspaces = config_["persistent-workspaces"].isObject()
? config_["persistent-workspaces"]
: config_["persistent_workspaces"];
const std::vector<std::string> keys = persistent_workspaces.getMemberNames();
for (const std::string &key : keys) {
@ -286,8 +353,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();
@ -303,8 +371,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()),
@ -327,7 +396,7 @@ Workspace::Workspace(const Json::Value &workspace_data)
button_.set_relief(Gtk::RELIEF_NONE);
content_.set_center_widget(label_);
button_.add(content_);
};
}
void add_or_remove_class(const Glib::RefPtr<Gtk::StyleContext> &context, bool condition,
const std::string &class_name) {
@ -339,12 +408,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)));
@ -352,36 +435,62 @@ void Workspace::update(const std::string &format, const std::string &icon) {
void Workspaces::sort_workspaces() {
std::sort(workspaces_.begin(), workspaces_.end(),
[](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
// normal -> named persistent -> named -> special -> named special
[&](std::unique_ptr<Workspace> &a, std::unique_ptr<Workspace> &b) {
// Helper comparisons
auto is_id_less = a->id() < b->id();
auto is_name_less = a->name() < b->name();
auto is_number_less = std::stoi(a->name()) < std::stoi(b->name());
// both normal (includes numbered persistent) => sort by ID
if (a->id() > 0 && b->id() > 0) {
return a->id() < b->id();
switch (sort_by_) {
case SORT_METHOD::ID:
return is_id_less;
case SORT_METHOD::NAME:
return is_name_less;
case SORT_METHOD::NUMBER:
try {
return is_number_less;
} catch (const std::invalid_argument &) {
// Handle the exception if necessary.
break;
}
case SORT_METHOD::DEFAULT:
default:
// Handle the default case here.
// normal -> named persistent -> named -> special -> named special
// both normal (includes numbered persistent) => sort by ID
if (a->id() > 0 && b->id() > 0) {
return is_id_less;
}
// one normal, one special => normal first
if ((a->is_special()) ^ (b->is_special())) {
return b->is_special();
}
// only one normal, one named
if ((a->id() > 0) ^ (b->id() > 0)) {
return a->id() > 0;
}
// both special
if (a->is_special() && b->is_special()) {
// if one is -99 => put it last
if (a->id() == -99 || b->id() == -99) {
return b->id() == -99;
}
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID
// <=-1)
return is_name_less;
}
// sort non-special named workspaces by name (ID <= -1377)
return is_name_less;
break;
}
// one normal, one special => normal first
if ((a->is_special()) ^ (b->is_special())) {
return b->is_special();
}
// only one normal, one named
if ((a->id() > 0) ^ (b->id() > 0)) {
return a->id() > 0;
}
// both special
if (a->is_special() && b->is_special()) {
// if one is -99 => put it last
if (a->id() == -99 || b->id() == -99) {
return b->id() == -99;
}
// both are 0 (not yet named persistents) / both are named specials (-98 <= ID <=-1)
return a->name() < b->name();
}
// sort non-special named workspaces by name (ID <= -1377)
return a->name() < b->name();
// Return a default value if none of the cases match.
return is_name_less; // You can adjust this to your specific needs.
});
for (size_t i = 0; i < workspaces_.size(); ++i) {
@ -390,6 +499,13 @@ void Workspaces::sort_workspaces() {
}
std::string &Workspace::select_icon(std::map<std::string, std::string> &icons_map) {
if (is_urgent()) {
auto urgent_icon_it = icons_map.find("urgent");
if (urgent_icon_it != icons_map.end()) {
return urgent_icon_it->second;
}
}
if (active()) {
auto active_icon_it = icons_map.find("active");
if (active_icon_it != icons_map.end()) {
@ -409,6 +525,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()) {
@ -450,7 +573,7 @@ auto Workspace::handle_clicked(GdkEventButton *bt) -> bool {
void Workspaces::set_urgent_workspace(std::string windowaddress) {
const Json::Value clients_json = gIPC->getSocket1JsonReply("clients");
int workspace_id;
int workspace_id = -1;
for (Json::Value client_json : clients_json) {
if (client_json["address"].asString().ends_with(windowaddress)) {
@ -462,7 +585,7 @@ void Workspaces::set_urgent_workspace(std::string windowaddress) {
auto workspace =
std::find_if(workspaces_.begin(), workspaces_.end(),
[&](std::unique_ptr<Workspace> &x) { return x->id() == workspace_id; });
if (workspace->get() != nullptr) {
if (workspace != workspaces_.end()) {
workspace->get()->set_urgent();
}
}

View File

@ -39,7 +39,8 @@ Item::Item(const std::string& bn, const std::string& op, const Json::Value& conf
object_path(op),
icon_size(16),
effective_icon_size(0),
icon_theme(Gtk::IconTheme::create()) {
icon_theme(Gtk::IconTheme::create()),
bar_(bar) {
if (config["icon-size"].isUInt()) {
icon_size = config["icon-size"].asUInt();
}
@ -410,7 +411,8 @@ void Item::makeMenu() {
bool Item::handleClick(GdkEventButton* const& ev) {
auto parameters = Glib::VariantContainerBase::create_tuple(
{Glib::Variant<int>::create(ev->x), Glib::Variant<int>::create(ev->y)});
{Glib::Variant<int>::create(ev->x_root + bar_.x_global),
Glib::Variant<int>::create(ev->y_root + bar_.y_global)});
if ((ev->button == 1 && item_is_menu) || ev->button == 3) {
makeMenu();
if (gtk_menu != nullptr) {

View File

@ -79,9 +79,18 @@ void Workspaces::onCmd(const struct Ipc::ipc_response &res) {
: true;
});
// adding persistent workspaces (as per the config file)
if (config_["persistent_workspaces"].isObject()) {
const Json::Value &p_workspaces = config_["persistent_workspaces"];
spdlog::warn(
"persistent_workspaces is deprecated. Please change config to use "
"persistent-workspaces.");
}
// adding persistent workspaces (as per the config file)
if (config_["persistent-workspaces"].isObject() ||
config_["persistent_workspaces"].isObject()) {
const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject()
? config_["persistent-workspaces"]
: config_["persistent_workspaces"];
const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames();
for (const std::string &p_w_name : p_workspaces_names) {

View File

@ -209,8 +209,17 @@ WorkspaceGroup::WorkspaceGroup(const Bar &bar, Gtk::Box &box, const Json::Value
}
auto WorkspaceGroup::fill_persistent_workspaces() -> void {
if (config_["persistent_workspaces"].isObject() && !workspace_manager_.all_outputs()) {
const Json::Value &p_workspaces = config_["persistent_workspaces"];
if (config_["persistent_workspaces"].isObject()) {
spdlog::warn(
"persistent_workspaces is deprecated. Please change config to use persistent-workspaces.");
}
if ((config_["persistent-workspaces"].isObject() ||
config_["persistent_workspaces"].isObject()) &&
!workspace_manager_.all_outputs()) {
const Json::Value &p_workspaces = config_["persistent-workspaces"].isObject()
? config_["persistent-workspaces"]
: config_["persistent_workspaces"];
const std::vector<std::string> p_workspaces_names = p_workspaces.getMemberNames();
for (const std::string &p_w_name : p_workspaces_names) {

45
src/util/enum.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "util/enum.hpp"
#include <algorithm> // for std::transform
#include <cctype> // for std::toupper
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include "modules/hyprland/workspaces.hpp"
#include "util/string.hpp"
namespace waybar::util {
template <typename EnumType>
EnumParser<EnumType>::EnumParser() = default;
template <typename EnumType>
EnumParser<EnumType>::~EnumParser() = default;
template <typename EnumType>
EnumType EnumParser<EnumType>::parseStringToEnum(const std::string& str,
const std::map<std::string, EnumType>& enumMap) {
// Convert the input string to uppercase
std::string uppercaseStr = capitalize(str);
// Capitalize the map keys before searching
std::map<std::string, EnumType> capitalizedEnumMap;
std::transform(
enumMap.begin(), enumMap.end(), std::inserter(capitalizedEnumMap, capitalizedEnumMap.end()),
[this](const auto& pair) { return std::make_pair(capitalize(pair.first), pair.second); });
// Return enum match of string
auto it = capitalizedEnumMap.find(uppercaseStr);
if (it != capitalizedEnumMap.end()) return it->second;
// Throw error if it doesn't return
throw std::invalid_argument("Invalid string representation for enum");
}
// Explicit instantiations for specific EnumType types you intend to use
// Add explicit instantiations for all relevant EnumType types
template struct EnumParser<modules::hyprland::Workspaces::SORT_METHOD>;
} // namespace waybar::util