diff --git a/.gitignore b/.gitignore index ea8c4bf..185ca35 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target + +.idea diff --git a/anyrun-interface/src/lib.rs b/anyrun-interface/src/lib.rs index efd6dca..7c08755 100644 --- a/anyrun-interface/src/lib.rs +++ b/anyrun-interface/src/lib.rs @@ -41,6 +41,8 @@ pub struct Match { pub use_pango: bool, /// The icon name from the icon theme in use pub icon: ROption, + /// The path to a custom image to use instead of an icon + pub image: ROption, /// For runners to differentiate between the matches. Not required. pub id: ROption, } diff --git a/anyrun/src/main.rs b/anyrun/src/main.rs index fb9f62e..efb867c 100644 --- a/anyrun/src/main.rs +++ b/anyrun/src/main.rs @@ -37,6 +37,10 @@ struct Config { #[serde(default)] hide_icons: bool, + #[serde(default = "Config::default_max_image_width")] + max_image_width: i32, + #[serde(default = "Config::default_max_image_height")] + max_image_height: i32, #[serde(default)] hide_plugin_info: bool, #[serde(default)] @@ -77,6 +81,14 @@ impl Config { ] } + fn default_max_image_width() -> i32 { + 150 + } + + fn default_max_image_height() -> i32 { + 100 + } + fn default_layer() -> Layer { Layer::Overlay } @@ -91,6 +103,8 @@ impl Default for Config { height: Self::default_height(), plugins: Self::default_plugins(), hide_icons: false, + max_image_width: 150, + max_image_height: 100, hide_plugin_info: false, ignore_exclusive_zones: false, close_on_click: false, @@ -714,7 +728,21 @@ fn handle_matches(plugin_view: PluginView, runtime_data: &RuntimeData, matches: .hexpand(true) .build(); if !runtime_data.config.hide_icons { - if let ROption::RSome(icon) = &_match.icon { + if let ROption::RSome(image) = &_match.image { + let mut builder = gtk::Image::builder().name(style_names::MATCH); + + match gdk_pixbuf::Pixbuf::from_file_at_size( + image.as_str(), + runtime_data.config.max_image_width, + runtime_data.config.max_image_height, + ) { + Ok(pixbuf) => { + builder = builder.pixbuf(&pixbuf); + hbox.add(&builder.build()); + } + Err(why) => println!("Failed to load image file: {}", why), + } + } else if let ROption::RSome(icon) = &_match.icon { let mut builder = gtk::Image::builder() .name(style_names::MATCH) .pixel_size(32); diff --git a/examples/config.ron b/examples/config.ron index 145be44..03995b5 100644 --- a/examples/config.ron +++ b/examples/config.ron @@ -16,7 +16,13 @@ Config( height: Absolute(0), // Hide match and plugin info icons - hide_icons: false, + hide_icons: false, + + // The maximum width of custom images + max_image_width: 150, + + // The maximum height of custom images + max_image_height: 100, // ignore exclusive zones, f.e. Waybar ignore_exclusive_zones: false, diff --git a/plugins/applications/src/lib.rs b/plugins/applications/src/lib.rs index 30cebb8..78b0de1 100644 --- a/plugins/applications/src/lib.rs +++ b/plugins/applications/src/lib.rs @@ -122,7 +122,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { // prioritize actions if entry.desc.is_some() { - score = score * 2; + score *= 2; } if score > 0 { @@ -143,6 +143,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { description: entry.desc.clone().map(|desc| desc.into()).into(), use_pango: false, icon: ROption::RSome(entry.icon.clone().into()), + image: ROption::RNone, id: ROption::RSome(id), }) .collect() diff --git a/plugins/dictionary/src/lib.rs b/plugins/dictionary/src/lib.rs index 2e2b153..d5fe6fe 100644 --- a/plugins/dictionary/src/lib.rs +++ b/plugins/dictionary/src/lib.rs @@ -89,6 +89,7 @@ pub fn get_matches(input: RString, config: &Config) -> RVec { description: ROption::RSome(meaning.part_of_speech.clone().into()), use_pango: false, icon: ROption::RSome("accessories-dictionary".into()), + image: ROption::RNone, id: ROption::RNone, }) .collect::>() diff --git a/plugins/kidex/src/lib.rs b/plugins/kidex/src/lib.rs index d7f3189..1621d8b 100644 --- a/plugins/kidex/src/lib.rs +++ b/plugins/kidex/src/lib.rs @@ -102,6 +102,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { use_pango: false, id: ROption::RSome(IndexAction::Open as u64), icon: ROption::RSome("document-open".into()), + image: ROption::RNone, }, Match { title: "Copy Path".into(), @@ -109,6 +110,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { use_pango: false, id: ROption::RSome(IndexAction::CopyPath as u64), icon: ROption::RSome("edit-copy".into()), + image: ROption::RNone, }, Match { title: "Back".into(), @@ -116,6 +118,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { use_pango: false, id: ROption::RSome(IndexAction::Back as u64), icon: ROption::RSome("edit-undo".into()), + image: ROption::RNone, }, ] .into() @@ -155,6 +158,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { } else { "text-x-generic".into() }), + image: ROption::RNone, id: ROption::RSome(id as u64), }) .collect() diff --git a/plugins/randr/src/lib.rs b/plugins/randr/src/lib.rs index 73f330c..70c1a93 100644 --- a/plugins/randr/src/lib.rs +++ b/plugins/randr/src/lib.rs @@ -124,6 +124,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { ), use_pango: false, icon: ROption::RSome("object-flip-horizontal".into()), + image: ROption::RNone, id: ROption::RSome(mon.id), }) .collect::>(), @@ -150,6 +151,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { description: ROption::RNone, use_pango: false, icon: ROption::RSome(configure.icon().into()), + image: ROption::RNone, // Store 2 32 bit IDs in the single 64 bit integer, a bit of a hack id: ROption::RSome(_mon.id << 32 | Into::::into(configure)), }) @@ -165,6 +167,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { description: ROption::RNone, use_pango: false, icon: ROption::RSome(Configure::Zero.icon().into()), + image: ROption::RNone, id: ROption::RSome((&Configure::Zero).into()), }); @@ -173,6 +176,7 @@ pub fn get_matches(input: RString, state: &State) -> RVec { description: ROption::RSome("Return to the previous menu".into()), use_pango: false, icon: ROption::RSome("edit-undo".into()), + image: ROption::RNone, id: ROption::RSome(u64::MAX), }); diff --git a/plugins/rink/src/lib.rs b/plugins/rink/src/lib.rs index 7df0d0c..198d6b1 100644 --- a/plugins/rink/src/lib.rs +++ b/plugins/rink/src/lib.rs @@ -50,6 +50,7 @@ fn get_matches(input: RString, ctx: &mut rink_core::Context) -> RVec { description: desc.map(RString::from).into(), use_pango: false, icon: ROption::RNone, + image: ROption::RNone, id: ROption::RNone, }] .into() diff --git a/plugins/shell/src/lib.rs b/plugins/shell/src/lib.rs index 1d2f473..01caa36 100644 --- a/plugins/shell/src/lib.rs +++ b/plugins/shell/src/lib.rs @@ -55,6 +55,7 @@ fn get_matches(input: RString, config: &Config) -> RVec { ), use_pango: false, icon: ROption::RNone, + image: ROption::RNone, id: ROption::RNone, }] .into() diff --git a/plugins/stdin/README.md b/plugins/stdin/README.md index 6ebe6eb..1255aeb 100644 --- a/plugins/stdin/README.md +++ b/plugins/stdin/README.md @@ -7,3 +7,18 @@ Allows for easy integration into scripts that have been made with something like This plugin should generally be used alone, if a dmenu replacement is needed. This can be done with `anyrun --plugins libstdin.so`. The content to fuzzy match on needs to be piped into Anyrun. + +## Icons and images + +This plugin uses tabs to separate the text from the custom icon or image file. This means that you need to make sure that you don't pipe any tabs, unless you want to set a custom icon or image. + +This feature works by adding a tab after the title text, and then either: +- specifying an icon name or path +- or specifying an image path after with the `image:` prefix + +For example: +``` +Option 1 help-about +Option 2 /path/to/icon.png +Option 3 image:/path/to/image.png +``` diff --git a/plugins/stdin/src/lib.rs b/plugins/stdin/src/lib.rs index 2d6a029..5356275 100644 --- a/plugins/stdin/src/lib.rs +++ b/plugins/stdin/src/lib.rs @@ -35,7 +35,7 @@ fn init(config_dir: RString) -> State { State { config, - lines: stdin().lines().filter_map(|line| line.ok()).collect(), + lines: stdin().lines().map_while(Result::ok).collect(), } } @@ -68,12 +68,36 @@ fn get_matches(input: RString, state: &State) -> RVec { lines .into_iter() - .map(|(line, _)| Match { - title: line.into(), - description: ROption::RNone, - use_pango: false, - icon: ROption::RNone, - id: ROption::RNone, + .map(|(line, _)| { + let mut line = line.split('\t'); + let title = line.next().unwrap_or("").into(); + let (icon, image) = line + .next() + .map_or((ROption::RNone, ROption::RNone), |second| { + if second.starts_with("image:") { + ( + ROption::RNone, + ROption::RSome( + second + .chars() + .skip("image:".len()) + .collect::() + .into(), + ), + ) + } else { + (ROption::RSome(second.into()), ROption::RNone) + } + }); + + Match { + title, + description: ROption::RNone, + use_pango: false, + icon, + image, + id: ROption::RNone, + } }) .collect::>() .into() diff --git a/plugins/symbols/src/lib.rs b/plugins/symbols/src/lib.rs index 8a5e9be..b2f4f84 100644 --- a/plugins/symbols/src/lib.rs +++ b/plugins/symbols/src/lib.rs @@ -92,6 +92,7 @@ fn get_matches(input: RString, state: &State) -> RVec { description: ROption::RSome(symbol.name.clone().into()), use_pango: false, icon: ROption::RNone, + image: ROption::RNone, id: ROption::RNone, }) .collect() diff --git a/plugins/translate/src/lib.rs b/plugins/translate/src/lib.rs index d4af44f..fdb095f 100644 --- a/plugins/translate/src/lib.rs +++ b/plugins/translate/src/lib.rs @@ -211,7 +211,12 @@ fn get_matches(input: RString, state: &State) -> RVec { let mut matches = src_matches .into_iter() - .flat_map(|src| dest_matches.clone().into_iter().map(move |dest| (Some(src), dest))) + .flat_map(|src| { + dest_matches + .clone() + .into_iter() + .map(move |dest| (Some(src), dest)) + }) .collect::>(); matches.sort_by(|a, b| (b.1 .2 + b.0.unwrap().2).cmp(&(a.1 .2 + a.0.unwrap().2))); @@ -237,12 +242,12 @@ fn get_matches(input: RString, state: &State) -> RVec { .into_iter() .map(|(src, dest)| async move { match src { - Some(src) => + Some(src) => (dest.1, state.client.get(format!("https://translate.googleapis.com/translate_a/single?client=gtx&sl={}&tl={}&dt=t&q={}", src.0, dest.0, text)).send().await), None => (dest.1, state.client.get(format!("https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl={}&dt=t&q={}", dest.0, text)).send().await) } }); - + let res = futures::future::join_all(futures) // Wait for all futures to complete .await; @@ -276,6 +281,7 @@ fn get_matches(input: RString, state: &State) -> RVec { .into()), use_pango: false, icon: ROption::RNone, + image: ROption::RNone, id: ROption::RNone } ) diff --git a/plugins/websearch/src/lib.rs b/plugins/websearch/src/lib.rs index 68dcd53..3d72057 100644 --- a/plugins/websearch/src/lib.rs +++ b/plugins/websearch/src/lib.rs @@ -82,6 +82,7 @@ fn get_matches(input: RString, config: &Config) -> RVec { description: ROption::RSome(format!("Search with {}", engine).into()), use_pango: false, icon: ROption::RNone, + image: ROption::RNone, id: ROption::RSome(i as u64), }) .collect()