From e324ed3998a076ead7c04b80f818ea2874b8029e Mon Sep 17 00:00:00 2001 From: trigg Date: Sat, 2 May 2026 13:29:16 +0100 Subject: [PATCH] util: centralise icon-loading code path --- src/dock/dock-app.cpp | 3 +- src/dock/toplevel-icon.cpp | 188 +-------------------- src/dock/toplevel-icon.hpp | 7 - src/panel/widgets/command-output.cpp | 2 +- src/panel/widgets/launchers.cpp | 2 +- src/panel/widgets/menu.cpp | 2 +- src/panel/widgets/window-list/toplevel.cpp | 113 +------------ src/util/gtk-utils.cpp | 114 ++++++++++++- src/util/gtk-utils.hpp | 8 +- 9 files changed, 125 insertions(+), 314 deletions(-) diff --git a/src/dock/dock-app.cpp b/src/dock/dock-app.cpp index c10f0164..e40b59e9 100644 --- a/src/dock/dock-app.cpp +++ b/src/dock/dock-app.cpp @@ -1,5 +1,6 @@ #include "dock.hpp" #include "giomm/application.h" +#include "gtk-utils.hpp" #include "toplevel.hpp" #include "toplevel-icon.hpp" #include @@ -49,7 +50,7 @@ void WfDockApp::on_activate() { WayfireShellApp::on_activate(); new CssFromConfigInt("dock/icon_height", ".toplevel-icon {-gtk-icon-size:", "px;}"); - IconProvider::load_custom_icons(); + IconProvider::load_custom_icons("dock"); /* At this point, wayland connection has been initialized, * and hopefully outputs have been created */ diff --git a/src/dock/toplevel-icon.cpp b/src/dock/toplevel-icon.cpp index 9a4b3353..b4d40236 100644 --- a/src/dock/toplevel-icon.cpp +++ b/src/dock/toplevel-icon.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -12,16 +11,9 @@ #include "toplevel.hpp" #include "toplevel-icon.hpp" #include "gtk-utils.hpp" -#include -#include #include #include "wf-option-wrap.hpp" -namespace IconProvider -{ -void set_image_from_icon(Gtk::Image& image, - std::string app_id_list, int size, int scale); -} class WfToplevelIcon::impl { @@ -88,10 +80,8 @@ class WfToplevelIcon::impl } this->app_id = app_id; - IconProvider::set_image_from_icon(image, - app_id, - icon_height, - button.get_scale_factor()); + IconProvider::image_set_icon(image, + app_id); } void send_rectangle_hint() @@ -210,177 +200,3 @@ void WfToplevelIcon::close() { return pimpl->close(); } - -/* Icon loading functions */ -namespace IconProvider -{ -using Icon = Glib::RefPtr; - -namespace -{ -std::string tolower(std::string str) -{ - for (auto& c : str) - { - c = std::tolower(c); - } - - return str; -} - -std::map custom_icons; -} - -void load_custom_icons() -{ - static const std::string prefix = "icon_mapping_"; - auto section = WayfireShellApp::get().config.get_section("dock"); - - for (auto option : section->get_registered_options()) - { - if (option->get_name().compare(0, prefix.length(), prefix) != 0) - { - continue; - } - - auto app_id = option->get_name().substr(prefix.length()); - custom_icons[app_id] = option->get_value_str(); - } -} - -bool set_custom_icon(Gtk::Image& image, std::string app_id, int size, int scale) -{ - if (!custom_icons.count(app_id)) - { - return false; - } - - image_set_icon(&image, custom_icons[app_id]); - return true; -} - -/* Gio::DesktopAppInfo - * - * Usually knowing the app_id, we can get a desktop app info from Gio - * The filename is either the app_id + ".desktop" or lower_app_id + ".desktop" */ -Icon get_from_desktop_app_info(std::string app_id) -{ - Glib::RefPtr app_info; - - std::vector prefixes = { - "", - "/usr/share/applications/", - "/usr/share/applications/kde/", - "/usr/share/applications/org.kde.", - "/usr/share/applications/org.gnome.", - "/usr/local/share/applications/", - "/usr/local/share/applications/org.kde.", - "/usr/local/share/applications/org.gnome.", - }; - - std::vector app_id_variations = { - app_id, - tolower(app_id), - tolower(app_id), - }; - // e.g. org.gnome.Evince.desktop - app_id_variations[2][0] = std::toupper(app_id_variations[2][0]); - - std::vector suffixes = { - "", - ".desktop" - }; - - for (auto& prefix : prefixes) - { - for (auto& id : app_id_variations) - { - for (auto& suffix : suffixes) - { - if (!app_info) - { - app_info = Gio::DesktopAppInfo - ::create_from_filename(prefix + id + suffix); - } - } - } - } - - if (!app_info) - { - // special treatment for snap apps - std::string prefix = "/var/lib/snapd/desktop/applications/"; - auto& id = app_id_variations[1]; // seems to be lower case - for (auto& suffix : suffixes) - { - app_info = Gio::DesktopAppInfo::create_from_filename( - prefix + id + "_" + id + suffix); - if (app_info) - { - break; - } - } - } - - if (app_info) // success - { - return app_info->get_icon(); - } - - return Icon{}; -} - -void set_image_from_icon(Gtk::Image& image, - std::string app_id_list, int size, int scale) -{ - std::string app_id; - std::istringstream stream(app_id_list); - - bool found_icon = false; - - /* Wayfire sends a list of app-id's in space separated format, other compositors - * send a single app-id, but in any case this works fine */ - auto display = image.get_display(); - while (stream >> app_id) - { - /* Try first method: custom icon file provided by the user */ - if (set_custom_icon(image, app_id, size, scale)) - { - found_icon = true; - break; - } - - /* Then try to load the DesktopAppInfo */ - auto icon = get_from_desktop_app_info(app_id); - std::string icon_name = "unknown"; - - if (!icon) - { - /* Finally try directly looking up the icon, if it exists */ - if (Gtk::IconTheme::get_for_display(display)->lookup_icon(app_id, 24)) - { - icon_name = app_id; - } - } else - { - icon_name = icon->to_string(); - } - - WfIconLoadOptions options; - options.user_scale = scale; - image_set_icon(&image, icon_name); - - /* finally found some icon */ - if (icon_name != "unknown") - { - found_icon = true; - break; - } - } - - if (!found_icon) - { - std::cout << "Failed to load icon for any of " << app_id_list << std::endl; - } -} -} diff --git a/src/dock/toplevel-icon.hpp b/src/dock/toplevel-icon.hpp index e6c33fb0..c8d0be29 100644 --- a/src/dock/toplevel-icon.hpp +++ b/src/dock/toplevel-icon.hpp @@ -18,10 +18,3 @@ class WfToplevelIcon private: std::unique_ptr pimpl; }; - -namespace IconProvider -{ -/* Loads custom app_id -> icon file mappings from the section -* They have the format icon_mapping_ = */ -void load_custom_icons(); -} diff --git a/src/panel/widgets/command-output.cpp b/src/panel/widgets/command-output.cpp index f0b3bd7b..87515698 100644 --- a/src/panel/widgets/command-output.cpp +++ b/src/panel/widgets/command-output.cpp @@ -53,7 +53,7 @@ WfCommandOutputButtons::CommandOutput::CommandOutput(const std::string & name, { this->tooltip_command = tooltip_command; - image_set_icon(&icon, icon_name); + IconProvider::image_set_icon(icon, icon_name); if (icon_size > 0) { diff --git a/src/panel/widgets/launchers.cpp b/src/panel/widgets/launchers.cpp index 567bbfc7..16438594 100644 --- a/src/panel/widgets/launchers.cpp +++ b/src/panel/widgets/launchers.cpp @@ -63,7 +63,7 @@ bool WfLauncherButton::initialize(std::string name, std::string icon, std::strin void WfLauncherButton::update_icon() { - image_set_icon(&m_icon, app_info->get_icon()->to_string()); + IconProvider::image_set_icon(m_icon, app_info->get_icon()->to_string()); } void WfLauncherButton::launch() diff --git a/src/panel/widgets/menu.cpp b/src/panel/widgets/menu.cpp index 7a73d568..b7f385de 100644 --- a/src/panel/widgets/menu.cpp +++ b/src/panel/widgets/menu.cpp @@ -534,7 +534,7 @@ bool WayfireMenu::update_icon() icon = menu_icon; } - image_set_icon(&main_image, icon); + IconProvider::image_set_icon(main_image, icon); return true; } diff --git a/src/panel/widgets/window-list/toplevel.cpp b/src/panel/widgets/window-list/toplevel.cpp index cc9c8ab9..d12cc691 100644 --- a/src/panel/widgets/window-list/toplevel.cpp +++ b/src/panel/widgets/window-list/toplevel.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -19,12 +18,6 @@ namespace extern zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_v1_impl; } -namespace IconProvider -{ -void set_image_from_icon(Gtk::Image& image, - std::string app_id_list, int size, int scale); -} - static int create_anon_file(off_t size) { int fd = memfd_create("wf-live-preview", MFD_CLOEXEC); @@ -900,8 +893,7 @@ class WayfireToplevel::impl { WfOption minimal_panel_height{"panel/minimal_height"}; this->app_id = app_id; - IconProvider::set_image_from_icon(image, app_id, - std::min(int(minimal_panel_height), 24), button.get_scale_factor()); + IconProvider::image_set_icon(image, app_id); this->view_id = get_view_id_from_full_app_id(app_id); if (this->view_id == 0) { @@ -1212,106 +1204,3 @@ struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_v1_impl = { .parent = handle_toplevel_parent }; } - -/* Icon loading functions */ -namespace IconProvider -{ -using Icon = Glib::RefPtr; - -namespace -{ -std::string tolower(std::string str) -{ - for (auto& c : str) - { - c = std::tolower(c); - } - - return str; -} -} - -/* Gio::DesktopAppInfo - * - * Usually knowing the app_id, we can get a desktop app info from Gio - * The filename is either the app_id + ".desktop" or lower_app_id + ".desktop" */ -Icon get_from_desktop_app_info(std::string app_id) -{ - Glib::RefPtr app_info; - - std::vector prefixes = { - "", - "org.kde.", - }; - - std::vector app_id_variations = { - app_id, - tolower(app_id), - }; - - std::vector suffixes = { - "", - ".desktop" - }; - - for (auto& prefix : prefixes) - { - for (auto& id : app_id_variations) - { - for (auto& suffix : suffixes) - { - if (!app_info) - { - app_info = Gio::DesktopAppInfo::create(prefix + id + suffix); - } - } - } - } - - if (app_info) // success - { - return app_info->get_icon(); - } - - return Icon{}; -} - -/* Second method: Just look up the built-in icon theme, - * perhaps some icon can be found there */ - -void set_image_from_icon(Gtk::Image& image, - std::string app_id_list, int size, int scale) -{ - std::string app_id; - std::istringstream stream(app_id_list); - - /* Wayfire sends a list of app-id's in space separated format, other compositors - * send a single app-id, but in any case this works fine */ - auto display = image.get_display(); - while (stream >> app_id) - { - auto icon = get_from_desktop_app_info(app_id); - std::string icon_name = "unknown"; - if (!icon) - { - /* Perhaps no desktop app info, but we might still be able to - * get an icon directly from the icon theme */ - if (Gtk::IconTheme::get_for_display(display)->lookup_icon(app_id, size)) - { - icon_name = app_id; - } - } else - { - icon_name = icon->to_string(); - } - - image_set_icon(&image, icon_name); - - /* finally found some icon */ - if (icon_name != "unknown") - { - break; - } - } -} -} diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 80cec7b5..e25f1ad9 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -1,8 +1,11 @@ +#include "glibmm/ustring.h" #include #include #include #include #include +#include +#include "wf-shell-app.hpp" Glib::RefPtr load_icon_pixbuf_safe(std::string icon_path, int size) { @@ -62,13 +65,118 @@ void invert_pixbuf(Glib::RefPtr& pbuff) } } -void image_set_icon(Gtk::Image *image, std::string path) +namespace IconProvider { +std::map custom_icons; + +std::string tolower(std::string str) +{ + for (auto& c : str) + { + c = std::tolower(c); + } + + return str; +} + +/* Gio::DesktopAppInfo + * + * Usually knowing the app_id, we can get a desktop app info from Gio + * The filename is either the app_id + ".desktop" or lower_app_id + ".desktop" */ +Glib::RefPtr get_from_desktop_app_info(std::string app_id) +{ + std::vector prefixes = { + "", + "org.kde.", + }; + + std::vector app_id_variations = { + app_id, + tolower(app_id), + }; + + std::vector suffixes = { + "", + ".desktop" + }; + + for (auto& prefix : prefixes) + { + for (auto& id : app_id_variations) + { + for (auto& suffix : suffixes) + { + auto app_info = Gio::DesktopAppInfo::create(prefix + id + suffix); + if (app_info) + { + return app_info->get_icon(); + } + } + } + } + + return {}; +} + +bool image_set_icon(Gtk::Image & image, Glib::ustring path) +{ + if (path.empty()) + { + return false; + } + if ((path.rfind("/", 0) == 0) || (path.rfind("~", 0) == 0)) { - image->set(path); + image.set(path); + return true; } else { - image->set_from_icon_name(path); + /* It might be a space delimited list */ + std::istringstream stream(path); + std::string app_id; + auto theme = Gtk::IconTheme::get_for_display(Gdk::Display::get_default()); + while (stream >> app_id) + { + if (custom_icons.count(app_id) > 0) + { + image.set_from_icon_name(custom_icons[app_id]); + return true; + } + + /* If the icon theme has this, use it */ + if (theme && theme->has_icon(app_id)) + { + image.set_from_icon_name(app_id); + return true; + } + + /* Might be appid, get the .desktop file */ + auto icon = get_from_desktop_app_info(app_id); + if (icon) + { + image.set_from_icon_name(icon->to_string()); + return true; + } + } + } + + return false; +} + +void load_custom_icons(std::string section_name) +{ + static const std::string prefix = "icon_mapping_"; + auto section = WayfireShellApp::get().config.get_section(section_name); + + for (auto option : section->get_registered_options()) + { + if (option->get_name().compare(0, prefix.length(), prefix) != 0) + { + continue; + } + + auto app_id = option->get_name().substr(prefix.length()); + custom_icons[app_id] = option->get_value_str(); } } +} diff --git a/src/util/gtk-utils.hpp b/src/util/gtk-utils.hpp index 8cac6c29..3b73e972 100644 --- a/src/util/gtk-utils.hpp +++ b/src/util/gtk-utils.hpp @@ -1,5 +1,6 @@ #pragma once +#include "glibmm/ustring.h" #include #include #include @@ -17,6 +18,9 @@ struct WfIconLoadOptions bool invert = false; }; +namespace IconProvider +{ void invert_pixbuf(Glib::RefPtr& pbuff); - -void image_set_icon(Gtk::Image *image, std::string path); +bool image_set_icon(Gtk::Image & image, Glib::ustring path); +void load_custom_icons(std::string section_name); +}