diff --git a/.gitignore b/.gitignore index ea8c4bf7..185ca35e 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 efd6dcac..7c08755a 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 fb9f62ec..efb867cb 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 145be442..03995b5b 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 30cebb85..78b0de1d 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 2e2b153c..d5fe6fef 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 d7f3189a..1621d8b9 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 73f330c0..70c1a93e 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 7df0d0ce..198d6b10 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 1d2f4733..01caa367 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 6ebe6eb2..1255aeb7 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 2d6a0299..53562756 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 8a5e9bec..b2f4f844 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 d4af44f4..fdb095fb 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 68dcd53a..3d72057a 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()