Merge branch 'master' into darkmode
This commit is contained in:
		
						commit
						fc67558717
					
				|  | @ -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_; | ||||
|  |  | |||
|  | @ -1,10 +1,12 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <list> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| #include <string> | ||||
| #include <thread> | ||||
| #include <utility> | ||||
| 
 | ||||
| #include "util/json.hpp" | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include "ALabel.hpp" | ||||
| #include "bar.hpp" | ||||
| #include "modules/hyprland/backend.hpp" | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include "ALabel.hpp" | ||||
| #include "bar.hpp" | ||||
| #include "modules/hyprland/backend.hpp" | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include "AAppIconLabel.hpp" | ||||
| #include "bar.hpp" | ||||
| #include "modules/hyprland/backend.hpp" | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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_; | ||||
|  |  | |||
|  | @ -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
 | ||||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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* | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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', | ||||
|  |  | |||
							
								
								
									
										54
									
								
								src/bar.cpp
								
								
								
								
							
							
						
						
									
										54
									
								
								src/bar.cpp
								
								
								
								
							|  | @ -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()); | ||||
| } | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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 ""; | ||||
|   } | ||||
|  |  | |||
|  | @ -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()); | ||||
|   } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| #include <spdlog/spdlog.h> | ||||
| 
 | ||||
| #include <util/sanitize_str.hpp> | ||||
| #include "util/sanitize_str.hpp" | ||||
| 
 | ||||
| namespace waybar::modules::hyprland { | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 { | ||||
| 
 | ||||
|  |  | |||
|  | @ -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(); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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
 | ||||
		Loading…
	
		Reference in New Issue