diff --git a/include/AAppIconLabel.hpp b/include/AAppIconLabel.hpp new file mode 100644 index 0000000..a2ae4f5 --- /dev/null +++ b/include/AAppIconLabel.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "AIconLabel.hpp" + +namespace waybar { + +class AAppIconLabel : public AIconLabel { + public: + AAppIconLabel(const Json::Value &config, const std::string &name, const std::string &id, + const std::string &format, uint16_t interval = 0, bool ellipsize = false, + bool enable_click = false, bool enable_scroll = false); + virtual ~AAppIconLabel() = default; + auto update() -> void override; + + protected: + void updateAppIconName(const std::string &app_identifier); + void updateAppIcon(); + unsigned app_icon_size_{24}; + bool update_app_icon_{true}; + std::string app_icon_name_; +}; + +} // namespace waybar diff --git a/include/modules/hyprland/window.hpp b/include/modules/hyprland/window.hpp index a916610..5316c0e 100644 --- a/include/modules/hyprland/window.hpp +++ b/include/modules/hyprland/window.hpp @@ -1,13 +1,13 @@ #include -#include "AIconLabel.hpp" +#include "AAppIconLabel.hpp" #include "bar.hpp" #include "modules/hyprland/backend.hpp" #include "util/json.hpp" namespace waybar::modules::hyprland { -class Window : public waybar::AIconLabel, public EventHandler { +class Window : public waybar::AAppIconLabel, public EventHandler { public: Window(const std::string&, const waybar::Bar&, const Json::Value&); virtual ~Window(); @@ -40,8 +40,6 @@ class Window : public waybar::AIconLabel, public EventHandler { void onEvent(const std::string&) override; void queryActiveWorkspace(); void setClass(const std::string&, bool enable); - void updateAppIconName(); - void updateAppIcon(); bool separate_outputs; std::mutex mutex_; @@ -55,9 +53,6 @@ class Window : public waybar::AIconLabel, public EventHandler { bool all_floating_; bool hidden_; bool fullscreen_; - unsigned app_icon_size_{24}; - bool update_app_icon_{true}; - std::string app_icon_name_; }; } // namespace waybar::modules::hyprland diff --git a/meson.build b/meson.build index 4137d22..f8b4a2d 100644 --- a/meson.build +++ b/meson.build @@ -159,6 +159,7 @@ src_files = files( 'src/AModule.cpp', 'src/ALabel.cpp', 'src/AIconLabel.cpp', + 'src/AAppIconLabel.cpp', 'src/modules/custom.cpp', 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', diff --git a/src/AAppIconLabel.cpp b/src/AAppIconLabel.cpp new file mode 100644 index 0000000..6e3d164 --- /dev/null +++ b/src/AAppIconLabel.cpp @@ -0,0 +1,124 @@ +#include "AAppIconLabel.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +#include "util/gtk_icon.hpp" + +namespace waybar { + +AAppIconLabel::AAppIconLabel(const Json::Value& config, const std::string& name, + const std::string& id, const std::string& format, uint16_t interval, + bool ellipsize, bool enable_click, bool enable_scroll) + : AIconLabel(config, name, id, format, interval, ellipsize, enable_click, enable_scroll) { + // Icon size + if (config["icon-size"].isUInt()) { + app_icon_size_ = config["icon-size"].asUInt(); + } + image_.set_pixel_size(app_icon_size_); +} + +std::optional getDesktopFilePath(const std::string& app_identifier) { + const auto data_dirs = Glib::get_system_data_dirs(); + for (const auto& data_dir : data_dirs) { + const auto data_app_dir = data_dir + "applications/"; + auto desktop_file_path = data_app_dir + app_identifier + ".desktop"; + if (std::filesystem::exists(desktop_file_path)) { + return desktop_file_path; + } + } + return {}; +} + +std::optional getIconName(const std::string& app_identifier) { + const auto desktop_file_path = getDesktopFilePath(app_identifier); + if (!desktop_file_path.has_value()) { + // Try some heuristics to find a matching icon + + if (DefaultGtkIconThemeWrapper::has_icon(app_identifier)) { + return app_identifier; + } + + const auto app_identifier_desktop = app_identifier + "-desktop"; + if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { + return app_identifier_desktop; + } + + const auto to_lower = [](const std::string& str) { + auto str_cpy = str; + std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(), + [](unsigned char c) { return std::tolower(c); }); + return str; + }; + + const auto first_space = app_identifier.find_first_of(' '); + if (first_space != std::string::npos) { + const auto first_word = to_lower(app_identifier.substr(0, first_space)); + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + return first_word; + } + } + + const auto first_dash = app_identifier.find_first_of('-'); + if (first_dash != std::string::npos) { + const auto first_word = to_lower(app_identifier.substr(0, first_dash)); + if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { + return first_word; + } + } + + return {}; + } + + try { + Glib::KeyFile desktop_file; + desktop_file.load_from_file(desktop_file_path.value()); + return desktop_file.get_string("Desktop Entry", "Icon"); + } catch (Glib::FileError& error) { + spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), + error.what().c_str()); + } catch (Glib::KeyFileError& error) { + spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), + error.what().c_str()); + } + return {}; +} + +void AAppIconLabel::updateAppIconName(const std::string& app_identifier) { + if (!iconEnabled()) { + return; + } + + const auto icon_name = getIconName(app_identifier); + if (icon_name.has_value()) { + app_icon_name_ = icon_name.value(); + } else { + app_icon_name_ = ""; + } + update_app_icon_ = true; +} + +void AAppIconLabel::updateAppIcon() { + if (update_app_icon_) { + update_app_icon_ = false; + if (app_icon_name_.empty()) { + image_.set_visible(false); + } else { + image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); + image_.set_visible(true); + } + } +} + +auto AAppIconLabel::update() -> void { + updateAppIcon(); + AIconLabel::update(); +} + +} // namespace waybar diff --git a/src/modules/hyprland/window.cpp b/src/modules/hyprland/window.cpp index 68c379b..cb7bf75 100644 --- a/src/modules/hyprland/window.cpp +++ b/src/modules/hyprland/window.cpp @@ -6,8 +6,6 @@ #include #include -#include -#include #include #include #include @@ -20,16 +18,10 @@ namespace waybar::modules::hyprland { Window::Window(const std::string& id, const Bar& bar, const Json::Value& config) - : AIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { + : AAppIconLabel(config, "window", id, "{title}", 0, true), bar_(bar) { modulesReady = true; separate_outputs = config["separate-outputs"].asBool(); - // Icon size - if (config["icon-size"].isUInt()) { - app_icon_size_ = config["icon-size"].asUInt(); - } - image_.set_pixel_size(app_icon_size_); - if (!gIPC.get()) { gIPC = std::make_unique(); } @@ -51,98 +43,6 @@ Window::~Window() { std::lock_guard lg(mutex_); } -std::optional getDesktopFilePath(const std::string& app_class) { - const auto data_dirs = Glib::get_system_data_dirs(); - for (const auto& data_dir : data_dirs) { - const auto data_app_dir = data_dir + "applications/"; - auto desktop_file_path = data_app_dir + app_class + ".desktop"; - if (std::filesystem::exists(desktop_file_path)) { - return desktop_file_path; - } - } - return {}; -} - -std::optional getIconName(const std::string& app_class) { - const auto desktop_file_path = getDesktopFilePath(app_class); - if (!desktop_file_path.has_value()) { - // Try some heuristics to find a matching icon - - if (DefaultGtkIconThemeWrapper::has_icon(app_class)) { - return app_class; - } - - const auto app_identifier_desktop = app_class + "-desktop"; - if (DefaultGtkIconThemeWrapper::has_icon(app_identifier_desktop)) { - return app_identifier_desktop; - } - - const auto to_lower = [](const std::string& str) { - auto str_cpy = str; - std::transform(str_cpy.begin(), str_cpy.end(), str_cpy.begin(), - [](unsigned char c) { return std::tolower(c); }); - return str; - }; - - const auto first_space = app_class.find_first_of(' '); - if (first_space != std::string::npos) { - const auto first_word = to_lower(app_class.substr(0, first_space)); - if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { - return first_word; - } - } - - const auto first_dash = app_class.find_first_of('-'); - if (first_dash != std::string::npos) { - const auto first_word = to_lower(app_class.substr(0, first_dash)); - if (DefaultGtkIconThemeWrapper::has_icon(first_word)) { - return first_word; - } - } - - return {}; - } - - try { - Glib::KeyFile desktop_file; - desktop_file.load_from_file(desktop_file_path.value()); - return desktop_file.get_string("Desktop Entry", "Icon"); - } catch (Glib::FileError& error) { - spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), - error.what().c_str()); - } catch (Glib::KeyFileError& error) { - spdlog::warn("Error while loading desktop file {}: {}", desktop_file_path.value(), - error.what().c_str()); - } - return {}; -} - -void Window::updateAppIconName() { - if (!iconEnabled()) { - return; - } - - const auto icon_name = getIconName(window_data_.class_name); - if (icon_name.has_value()) { - app_icon_name_ = icon_name.value(); - } else { - app_icon_name_ = ""; - } - update_app_icon_ = true; -} - -void Window::updateAppIcon() { - if (update_app_icon_) { - update_app_icon_ = false; - if (app_icon_name_.empty()) { - image_.set_visible(false); - } else { - image_.set_from_icon_name(app_icon_name_, Gtk::ICON_SIZE_INVALID); - image_.set_visible(true); - } - } -} - auto Window::update() -> void { // fix ampersands std::lock_guard lg(mutex_); @@ -190,9 +90,7 @@ auto Window::update() -> void { } last_solo_class_ = solo_class_; - updateAppIcon(); - - AIconLabel::update(); + AAppIconLabel::update(); } auto Window::getActiveWorkspace() -> Workspace { @@ -254,7 +152,7 @@ void Window::queryActiveWorkspace() { } window_data_ = WindowData::parse(*active_window); - updateAppIconName(); + updateAppIconName(window_data_.class_name); std::vector workspace_windows; std::copy_if(clients.begin(), clients.end(), std::back_inserter(workspace_windows), [&](Json::Value window) {