1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-01 18:51:04 +02:00

feat(custom): image widget

This commit is contained in:
Jake Stanger 2023-01-29 17:47:01 +00:00
parent 5772711192
commit 393800aaa2
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61
4 changed files with 64 additions and 14 deletions

View file

@ -19,12 +19,14 @@ It is well worth looking at the examples.
### `Widget` ### `Widget`
| Name | Type | Default | Description | | Name | Type | Default | Description |
|---------------|------------------------------|--------------|---------------------------------------------------------------------------| |---------------|-----------------------------------------|--------------|---------------------------------------------------------------------------|
| `widget_type` | `box` or `label` or `button` | `null` | Type of GTK widget to create. | | `widget_type` | `box` or `label` or `button` or `image` | `null` | Type of GTK widget to create. |
| `name` | `string` | `null` | Widget name. | | `name` | `string` | `null` | Widget name. |
| `class` | `string` | `null` | Widget class name. | | `class` | `string` | `null` | Widget class name. |
| `label` | `string` | `null` | [`label` and `button`] Widget text label. Pango markup supported. | | `label` | `string` | `null` | [`label` and `button`] Widget text label. Pango markup supported. |
| `on_click` | `string` | `null` | [`button`] Command to execute. More on this [below](#commands). | | `on_click` | `string` | `null` | [`button`] Command to execute. More on this [below](#commands). |
| `src` | `string` | `null` | [`image`] Image source. More on this [below](#images). |
| `size` | `integer` | `null` | [`image`] Width/height of the image. Aspect ratio is preserved. |
| `orientation` | `horizontal` or `vertical` | `horizontal` | [`box`] Whether child widgets should be horizontally or vertically added. | | `orientation` | `horizontal` or `vertical` | `horizontal` | [`box`] Whether child widgets should be horizontally or vertically added. |
| `widgets` | `Widget[]` | `[]` | [`box`] List of widgets to add to this box. | | `widgets` | `Widget[]` | `[]` | [`box`] List of widgets to add to this box. |
@ -56,6 +58,19 @@ The following bar commands are supported:
- `popup:open` - `popup:open`
- `popup:close` - `popup:close`
### Images
Ironbar is capable of loading images from multiple sources:
- GTK icons: `icon:firefox`
- Local files: `file:///path/to/file.jpg`
- Remote files (over HTTP/HTTPS): `https://example.com/image.jpg`
Remote images are loaded asynchronously to avoid blocking the UI thread.
Be aware this can cause elements to change size upon load if the image is large enough.
---
XML is arguably better-suited and easier to read for this sort of markup, XML is arguably better-suited and easier to read for this sort of markup,
but currently is not supported. but currently is not supported.
Nonetheless, it may be worth comparing the examples to the below equivalent Nonetheless, it may be worth comparing the examples to the below equivalent

View file

@ -68,7 +68,7 @@ impl<'a> ImageProvider<'a> {
None if PathBuf::from(input_name).exists() => { None if PathBuf::from(input_name).exists() => {
Ok(ImageLocation::Local(PathBuf::from(input_name))) Ok(ImageLocation::Local(PathBuf::from(input_name)))
} }
None => match get_desktop_icon_name(&input_name) { None => match get_desktop_icon_name(input_name) {
Some(input) => Self::get_location(input, theme, size), Some(input) => Self::get_location(input, theme, size),
None => Err(Report::msg("Unknown image type")), None => Err(Report::msg("Unknown image type")),
}, },

View file

@ -1,12 +1,13 @@
use crate::config::CommonConfig; use crate::config::CommonConfig;
use crate::dynamic_string::DynamicString; use crate::dynamic_string::DynamicString;
use crate::image::ImageProvider;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::popup::{ButtonGeometry, Popup}; use crate::popup::{ButtonGeometry, Popup};
use crate::script::Script; use crate::script::Script;
use crate::{send_async, try_send}; use crate::{send_async, try_send};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, Label, Orientation}; use gtk::{Button, IconTheme, Label, Orientation};
use serde::Deserialize; use serde::Deserialize;
use tokio::spawn; use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::mpsc::{Receiver, Sender};
@ -46,6 +47,8 @@ pub struct Widget {
class: Option<String>, class: Option<String>,
on_click: Option<String>, on_click: Option<String>,
orientation: Option<String>, orientation: Option<String>,
src: Option<String>,
size: Option<i32>,
} }
/// Supported GTK widget types /// Supported GTK widget types
@ -55,6 +58,7 @@ pub enum WidgetType {
Box, Box,
Label, Label,
Button, Button,
Image,
} }
impl Widget { impl Widget {
@ -64,6 +68,7 @@ impl Widget {
WidgetType::Box => parent.add(&self.into_box(&tx, bar_orientation)), WidgetType::Box => parent.add(&self.into_box(&tx, bar_orientation)),
WidgetType::Label => parent.add(&self.into_label()), WidgetType::Label => parent.add(&self.into_label()),
WidgetType::Button => parent.add(&self.into_button(tx, bar_orientation)), WidgetType::Button => parent.add(&self.into_button(tx, bar_orientation)),
WidgetType::Image => parent.add(&self.into_image()),
} }
} }
@ -157,6 +162,32 @@ impl Widget {
button button
} }
fn into_image(self) -> gtk::Image {
let mut builder = gtk::Image::builder();
if let Some(name) = self.name {
builder = builder.name(&name);
}
let gtk_image = builder.build();
if let Some(src) = self.src {
let theme = IconTheme::new();
let size = self.size.unwrap_or(32);
if let Err(err) = ImageProvider::parse(src, &theme, size)
.and_then(|image| image.load_into_image(gtk_image.clone()))
{
error!("{err:?}");
}
}
if let Some(class) = self.class {
gtk_image.style_context().add_class(&class);
}
gtk_image
}
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,6 +1,5 @@
mod config; mod config;
use std::path::PathBuf;
use crate::clients::music::{self, MusicClient, PlayerState, PlayerUpdate, Status, Track}; use crate::clients::music::{self, MusicClient, PlayerState, PlayerUpdate, Status, Track};
use crate::image::ImageProvider; use crate::image::ImageProvider;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
@ -11,6 +10,7 @@ use glib::Continue;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, IconTheme, Label, Orientation, Scale}; use gtk::{Button, IconTheme, Label, Orientation, Scale};
use regex::Regex; use regex::Regex;
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::spawn; use tokio::spawn;
@ -306,14 +306,18 @@ impl Module<Button> for MusicModule {
let new_cover = update.song.cover_path; let new_cover = update.song.cover_path;
if prev_cover != new_cover { if prev_cover != new_cover {
prev_cover = new_cover.clone(); prev_cover = new_cover.clone();
let res = match new_cover.map(|cover_path| ImageProvider::parse(cover_path, &icon_theme, 128)) let res = match new_cover
.map(|cover_path| ImageProvider::parse(cover_path, &icon_theme, 128))
{ {
Some(Ok(image)) => image.load_into_image(album_image.clone()), Some(Ok(image)) => image.load_into_image(album_image.clone()),
Some(Err(err)) => { Some(Err(err)) => {
album_image.set_from_pixbuf(None); album_image.set_from_pixbuf(None);
Err(err) Err(err)
} }
None => Ok(album_image.set_from_pixbuf(None)), None => {
album_image.set_from_pixbuf(None);
Ok(())
}
}; };
if let Err(err) = res { if let Err(err) = res {
error!("{err:?}"); error!("{err:?}");