mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-08-17 14:51:04 +02:00
feat(tray): image support in menu items
This commit is contained in:
parent
fb1799531b
commit
662ddb6946
3 changed files with 78 additions and 11 deletions
|
@ -68,7 +68,7 @@ notifications = ["zbus"]
|
||||||
|
|
||||||
sys_info = ["sysinfo", "regex"]
|
sys_info = ["sysinfo", "regex"]
|
||||||
|
|
||||||
tray = ["system-tray"]
|
tray = ["system-tray", "png"]
|
||||||
|
|
||||||
upower = ["upower_dbus", "zbus", "futures-lite"]
|
upower = ["upower_dbus", "zbus", "futures-lite"]
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use gtk::ffi::gtk_icon_theme_get_search_path;
|
||||||
use gtk::gdk_pixbuf::{Colorspace, InterpType, Pixbuf};
|
use gtk::gdk_pixbuf::{Colorspace, InterpType, Pixbuf};
|
||||||
use gtk::prelude::IconThemeExt;
|
use gtk::prelude::IconThemeExt;
|
||||||
use gtk::{IconLookupFlags, IconTheme, Image};
|
use gtk::{IconLookupFlags, IconTheme, Image};
|
||||||
|
use png::ColorType;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
@ -125,3 +126,36 @@ fn get_image_from_pixmap(item: &TrayMenu, size: u32) -> Result<Image> {
|
||||||
ImageProvider::create_and_load_surface(&pixbuf, &image)?;
|
ImageProvider::create_and_load_surface(&pixbuf, &image)?;
|
||||||
Ok(image)
|
Ok(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PngData<'a>(pub &'a [u8]);
|
||||||
|
impl TryFrom<PngData<'_>> for Image {
|
||||||
|
type Error = Report;
|
||||||
|
|
||||||
|
fn try_from(value: PngData) -> std::result::Result<Self, Self::Error> {
|
||||||
|
let data = value.0;
|
||||||
|
|
||||||
|
let decoder = png::Decoder::new(data);
|
||||||
|
let mut reader = decoder.read_info()?;
|
||||||
|
let mut buf = vec![0; reader.output_buffer_size()];
|
||||||
|
|
||||||
|
let info = reader.next_frame(&mut buf)?;
|
||||||
|
let bytes = glib::Bytes::from(&buf[..info.buffer_size()]);
|
||||||
|
|
||||||
|
let has_alpha = matches!(info.color_type, ColorType::Rgba | ColorType::GrayscaleAlpha);
|
||||||
|
let row_stride_multiplier = if has_alpha { 4 } else { 3 };
|
||||||
|
|
||||||
|
let pixbuf = Pixbuf::from_bytes(
|
||||||
|
&bytes,
|
||||||
|
Colorspace::Rgb,
|
||||||
|
has_alpha,
|
||||||
|
info.bit_depth as i32,
|
||||||
|
info.width as i32,
|
||||||
|
info.height as i32,
|
||||||
|
(info.width * row_stride_multiplier) as i32,
|
||||||
|
);
|
||||||
|
|
||||||
|
let image = Image::new();
|
||||||
|
ImageProvider::create_and_load_surface(&pixbuf, &image)?;
|
||||||
|
Ok(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
use super::diff::{Diff, MenuItemDiff};
|
use super::diff::{Diff, MenuItemDiff};
|
||||||
|
use crate::image::ImageProvider;
|
||||||
|
use crate::modules::tray::icon::PngData;
|
||||||
use crate::{spawn, try_send};
|
use crate::{spawn, try_send};
|
||||||
use glib::Propagation;
|
use glib::Propagation;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{CheckMenuItem, Image, Label, Menu, MenuItem, SeparatorMenuItem};
|
use gtk::{
|
||||||
|
CheckMenuItem, Container, IconTheme, Image, Label, Menu, MenuItem, Orientation,
|
||||||
|
SeparatorMenuItem,
|
||||||
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use system_tray::client::ActivateRequest;
|
use system_tray::client::ActivateRequest;
|
||||||
use system_tray::item::{IconPixmap, StatusNotifierItem};
|
use system_tray::item::{IconPixmap, StatusNotifierItem};
|
||||||
use system_tray::menu::{MenuItem as MenuItemInfo, MenuType, ToggleState, ToggleType};
|
use system_tray::menu::{MenuItem as MenuItemInfo, MenuType, ToggleState, ToggleType};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
use tracing::{error, warn};
|
||||||
|
|
||||||
/// Calls a method on the underlying widget,
|
/// Calls a method on the underlying widget,
|
||||||
/// passing in a single argument.
|
/// passing in a single argument.
|
||||||
|
@ -214,6 +220,37 @@ enum TrayMenuWidget {
|
||||||
Checkbox(CheckMenuItem),
|
Checkbox(CheckMenuItem),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setup_item<W>(widget: &W, info: &MenuItemInfo)
|
||||||
|
where
|
||||||
|
W: IsA<MenuItem> + IsA<Container>,
|
||||||
|
{
|
||||||
|
let container = gtk::Box::new(Orientation::Horizontal, 10);
|
||||||
|
widget.add(&container);
|
||||||
|
|
||||||
|
if let Some(icon) = &info.icon_name {
|
||||||
|
// TODO: Get theme here
|
||||||
|
let image = Image::new();
|
||||||
|
match ImageProvider::parse(icon, &IconTheme::new(), true, 24)
|
||||||
|
.map(|provider| provider.load_into_image(image.clone()))
|
||||||
|
{
|
||||||
|
Some(Ok(())) => container.add(&image),
|
||||||
|
_ => warn!("Failed to load icon: {icon}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(icon_data) = &info.icon_data {
|
||||||
|
match Image::try_from(PngData(icon_data.as_slice())) {
|
||||||
|
Ok(image) => container.add(&image),
|
||||||
|
Err(err) => error!("{err:?}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let label = Label::new(info.label.as_deref());
|
||||||
|
container.add(&label);
|
||||||
|
|
||||||
|
container.show_all();
|
||||||
|
}
|
||||||
|
|
||||||
impl TrayMenuItem {
|
impl TrayMenuItem {
|
||||||
fn new(info: &MenuItemInfo, tx: mpsc::Sender<i32>) -> Self {
|
fn new(info: &MenuItemInfo, tx: mpsc::Sender<i32>) -> Self {
|
||||||
let mut submenu = HashMap::new();
|
let mut submenu = HashMap::new();
|
||||||
|
@ -234,7 +271,9 @@ impl TrayMenuItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
let widget = match (info.menu_type, info.toggle_type) {
|
let widget = match (info.menu_type, info.toggle_type) {
|
||||||
(MenuType::Separator, _) => TrayMenuWidget::Separator(SeparatorMenuItem::new()),
|
(MenuType::Separator, _) => {
|
||||||
|
TrayMenuWidget::Separator(SeparatorMenuItem::builder().visible(true).build())
|
||||||
|
}
|
||||||
(MenuType::Standard, ToggleType::Checkmark) => {
|
(MenuType::Standard, ToggleType::Checkmark) => {
|
||||||
let widget = CheckMenuItem::builder()
|
let widget = CheckMenuItem::builder()
|
||||||
.visible(info.visible)
|
.visible(info.visible)
|
||||||
|
@ -242,10 +281,7 @@ impl TrayMenuItem {
|
||||||
.active(info.toggle_state == ToggleState::On)
|
.active(info.toggle_state == ToggleState::On)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if let Some(label) = &info.label {
|
setup_item(&widget, info);
|
||||||
widget.set_label(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_submenu!(menu, widget);
|
add_submenu!(menu, widget);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -266,10 +302,7 @@ impl TrayMenuItem {
|
||||||
.sensitive(info.enabled)
|
.sensitive(info.enabled)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if let Some(label) = &info.label {
|
setup_item(&widget, info);
|
||||||
widget.set_label(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_submenu!(menu, widget);
|
add_submenu!(menu, widget);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue