From a87d8d5c3071a1d8ab149deae17d261ae97368ea Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Sun, 16 Apr 2023 21:37:47 +0100 Subject: [PATCH 1/3] fix(tray): icons sometimes not showing Previously icons were only loaded from the theme based on the provided icon name. Sometimes no icon name was provided, and sometimes the name is just missing from the theme. This falls back to using the provided pixbuf, and then falls back to just displaying the name as text if that is not available. --- src/modules/tray.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/modules/tray.rs b/src/modules/tray.rs index 064507a..297ba88 100644 --- a/src/modules/tray.rs +++ b/src/modules/tray.rs @@ -3,8 +3,9 @@ use crate::config::CommonConfig; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; use crate::{await_sync, try_send}; use color_eyre::Result; +use gtk::gio::{Cancellable, MemoryInputStream}; use gtk::prelude::*; -use gtk::{IconLookupFlags, IconTheme, Image, Menu, MenuBar, MenuItem, SeparatorMenuItem}; +use gtk::{gdk_pixbuf, IconLookupFlags, IconTheme, Image, Label, Menu, MenuBar, MenuItem, SeparatorMenuItem}; use serde::Deserialize; use std::collections::HashMap; use stray::message::menu::{MenuItem as MenuItemInfo, MenuType}; @@ -147,12 +148,35 @@ impl Module for TrayModule { address, menu, } => { + let addr = &address; let menu_item = widgets.remove(address.as_str()).unwrap_or_else(|| { let menu_item = MenuItem::new(); menu_item.style_context().add_class("item"); + if let Some(image) = get_icon(&item) { image.set_widget_name(address.as_str()); menu_item.add(&image); + } else if let Some(pixmap) = + item.icon_pixmap.as_ref().and_then(|vec| vec.first()) + { + let bytes = glib::Bytes::from(&pixmap.pixels); + let stream = MemoryInputStream::from_bytes(&bytes); + let pixbuf = gdk_pixbuf::Pixbuf::from_stream_at_scale( + &stream, + pixmap.width, + pixmap.height, + true, + Some(&Cancellable::new()), + ); + + if let Ok(pixbuf) = pixbuf { + let image = Image::new(); + image.set_pixbuf(Some(&pixbuf)); + menu_item.add(&image); + } + } else { + let label = Label::new(Some(&item.title.as_ref().unwrap_or(addr))); + menu_item.add(&label); } container.add(&menu_item); menu_item.show_all(); From 68bc8230ddf3352cc0de9f8cc770632744c22747 Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Sun, 16 Apr 2023 21:37:47 +0100 Subject: [PATCH 2/3] fix(tray): icons sometimes not showing Previously icons were only loaded from the theme based on the provided icon name. Sometimes no icon name was provided, and sometimes the name is just missing from the theme. This falls back to using the provided pixbuf, and then falls back to just displaying the name as text if that is not available. --- src/modules/tray.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/modules/tray.rs b/src/modules/tray.rs index 064507a..297ba88 100644 --- a/src/modules/tray.rs +++ b/src/modules/tray.rs @@ -3,8 +3,9 @@ use crate::config::CommonConfig; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; use crate::{await_sync, try_send}; use color_eyre::Result; +use gtk::gio::{Cancellable, MemoryInputStream}; use gtk::prelude::*; -use gtk::{IconLookupFlags, IconTheme, Image, Menu, MenuBar, MenuItem, SeparatorMenuItem}; +use gtk::{gdk_pixbuf, IconLookupFlags, IconTheme, Image, Label, Menu, MenuBar, MenuItem, SeparatorMenuItem}; use serde::Deserialize; use std::collections::HashMap; use stray::message::menu::{MenuItem as MenuItemInfo, MenuType}; @@ -147,12 +148,35 @@ impl Module for TrayModule { address, menu, } => { + let addr = &address; let menu_item = widgets.remove(address.as_str()).unwrap_or_else(|| { let menu_item = MenuItem::new(); menu_item.style_context().add_class("item"); + if let Some(image) = get_icon(&item) { image.set_widget_name(address.as_str()); menu_item.add(&image); + } else if let Some(pixmap) = + item.icon_pixmap.as_ref().and_then(|vec| vec.first()) + { + let bytes = glib::Bytes::from(&pixmap.pixels); + let stream = MemoryInputStream::from_bytes(&bytes); + let pixbuf = gdk_pixbuf::Pixbuf::from_stream_at_scale( + &stream, + pixmap.width, + pixmap.height, + true, + Some(&Cancellable::new()), + ); + + if let Ok(pixbuf) = pixbuf { + let image = Image::new(); + image.set_pixbuf(Some(&pixbuf)); + menu_item.add(&image); + } + } else { + let label = Label::new(Some(&item.title.as_ref().unwrap_or(addr))); + menu_item.add(&label); } container.add(&menu_item); menu_item.show_all(); From b038e7671af4bfa41060adf724deb8c6151fac1f Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Fri, 21 Apr 2023 23:02:02 +0100 Subject: [PATCH 3/3] fix(tray): icons sometimes not showing Previously icons were only loaded from the theme based on the provided icon name. Sometimes no icon name was provided, and sometimes the name is just missing from the theme. This falls back to using the provided pixbuf, and then falls back to just displaying the name as text if that is not available. --- src/modules/tray.rs | 77 +++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/src/modules/tray.rs b/src/modules/tray.rs index 297ba88..99b6fbe 100644 --- a/src/modules/tray.rs +++ b/src/modules/tray.rs @@ -3,9 +3,12 @@ use crate::config::CommonConfig; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; use crate::{await_sync, try_send}; use color_eyre::Result; -use gtk::gio::{Cancellable, MemoryInputStream}; +use gtk::gdk_pixbuf::{Colorspace, InterpType}; use gtk::prelude::*; -use gtk::{gdk_pixbuf, IconLookupFlags, IconTheme, Image, Label, Menu, MenuBar, MenuItem, SeparatorMenuItem}; +use gtk::{ + gdk_pixbuf, IconLookupFlags, IconTheme, Image, Label, Menu, MenuBar, MenuItem, + SeparatorMenuItem, +}; use serde::Deserialize; use std::collections::HashMap; use stray::message::menu::{MenuItem as MenuItemInfo, MenuType}; @@ -21,9 +24,9 @@ pub struct TrayModule { pub common: Option, } -/// Gets a GTK `Image` component +/// Attempts to get a GTK `Image` component /// for the status notifier item's icon. -fn get_icon(item: &StatusNotifierItem) -> Option { +fn get_image_from_icon_name(item: &StatusNotifierItem) -> Option { item.icon_theme_path.as_ref().and_then(|path| { let theme = IconTheme::new(); theme.append_search_path(path); @@ -35,6 +38,37 @@ fn get_icon(item: &StatusNotifierItem) -> Option { }) } +/// Attempts to get an image from the item pixmap. +/// +/// The pixmap is supplied in ARGB32 format, +/// which has 8 bits per sample and a bit stride of `4*width`. +fn get_image_from_pixmap(item: &StatusNotifierItem) -> Option { + const BITS_PER_SAMPLE: i32 = 8; // + + let pixmap = item + .icon_pixmap + .as_ref() + .and_then(|pixmap| pixmap.first())?; + + let bytes = glib::Bytes::from(&pixmap.pixels); + let row_stride = pixmap.width * 4; // + + let pixbuf = gdk_pixbuf::Pixbuf::from_bytes( + &bytes, + Colorspace::Rgb, + true, + BITS_PER_SAMPLE, + pixmap.width, + pixmap.height, + row_stride, + ); + + let pixbuf = pixbuf + .scale_simple(16, 16, InterpType::Bilinear) + .unwrap_or(pixbuf); + Some(Image::from_pixbuf(Some(&pixbuf))) +} + /// Recursively gets GTK `MenuItem` components /// for the provided submenu array. fn get_menu_items( @@ -153,31 +187,20 @@ impl Module for TrayModule { let menu_item = MenuItem::new(); menu_item.style_context().add_class("item"); - if let Some(image) = get_icon(&item) { - image.set_widget_name(address.as_str()); - menu_item.add(&image); - } else if let Some(pixmap) = - item.icon_pixmap.as_ref().and_then(|vec| vec.first()) - { - let bytes = glib::Bytes::from(&pixmap.pixels); - let stream = MemoryInputStream::from_bytes(&bytes); - let pixbuf = gdk_pixbuf::Pixbuf::from_stream_at_scale( - &stream, - pixmap.width, - pixmap.height, - true, - Some(&Cancellable::new()), + get_image_from_icon_name(&item) + .or_else(|| get_image_from_pixmap(&item)) + .map_or_else( + || { + let label = + Label::new(Some(item.title.as_ref().unwrap_or(addr))); + menu_item.add(&label); + }, + |image| { + image.set_widget_name(address.as_str()); + menu_item.add(&image); + }, ); - if let Ok(pixbuf) = pixbuf { - let image = Image::new(); - image.set_pixbuf(Some(&pixbuf)); - menu_item.add(&image); - } - } else { - let label = Label::new(Some(&item.title.as_ref().unwrap_or(addr))); - menu_item.add(&label); - } container.add(&menu_item); menu_item.show_all(); menu_item