add dmenu support

This commit is contained in:
Alexander Mohr 2025-04-21 22:00:09 +02:00
parent 3e7f341acc
commit e3ee26f8b3
4 changed files with 71 additions and 12 deletions

View file

@ -7,6 +7,7 @@ Option 3"
# Pipe options to wofi and capture the selection
selection=$(echo "$options" | cargo run -- --show dmenu)
#selection=$(echo "$options" | wofi --show dmenu)
# Do something with the selection
echo "You selected: $selection"

View file

@ -622,7 +622,7 @@ pub fn default_fuzzy_min_length() -> Option<i32> {
#[allow(clippy::unnecessary_wraps)]
#[must_use]
pub fn default_fuzzy_min_score() -> Option<f64> {
Some(0.1)
Some(0.0)
}
// allowed because option is needed for serde macro

View file

@ -902,7 +902,7 @@ fn filter_widgets<T: Clone>(
score
> config
.fuzzy_min_score
.unwrap_or(config::default_fuzzy_min_score().unwrap_or(0.0)),
.unwrap_or(config::default_fuzzy_min_score().unwrap_or(0.0)) && score > 0.0,
)
}
MatchMethod::Contains => {
@ -922,6 +922,9 @@ fn filter_widgets<T: Clone>(
}
};
log::debug!("menu item {}, search score {}", menu_item_search, search_sort_score);
menu_item.search_sort_score = search_sort_score;
flowbox_child.set_visible(visible);
}

View file

@ -3,18 +3,19 @@ use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::{env, fmt, fs, io};
use anyhow::Context;
use freedesktop_file_parser::EntryType;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::config::{Config, expand_path};
use crate::desktop::{
default_icon, find_desktop_files, get_locale_variants, lookup_name_with_locale,
DesktopError, default_icon, find_desktop_files, get_locale_variants, lookup_name_with_locale,
};
use crate::gui;
use crate::gui::{ItemProvider, MenuItem};
use anyhow::Context;
use freedesktop_file_parser::EntryType;
use gtk4::AccessibleRole::Menu;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io::Read;
#[derive(Debug)]
pub enum ModeError {
@ -22,6 +23,8 @@ pub enum ModeError {
MissingAction,
RunError(String),
MissingCache,
StdInReadFail,
InvalidSelection,
}
impl fmt::Display for ModeError {
@ -31,6 +34,8 @@ impl fmt::Display for ModeError {
ModeError::MissingAction => write!(f, "MissingAction"),
ModeError::RunError(s) => write!(f, "RunError, {s}"),
ModeError::MissingCache => write!(f, "MissingCache"),
ModeError::StdInReadFail => write!(f, "StdInReadFail"),
&ModeError::InvalidSelection => write!(f, "InvalidSelection"),
}
}
}
@ -306,7 +311,7 @@ struct MathProvider<T: Clone> {
menu_item_data: T,
}
impl<T: std::clone::Clone> MathProvider<T> {
impl<T: Clone> MathProvider<T> {
fn new(menu_item_data: T) -> Self {
Self { menu_item_data }
}
@ -355,6 +360,47 @@ impl<T: Clone> ItemProvider<T> for MathProvider<T> {
}
}
#[derive(Clone)]
struct DMenuProvider {
items: Vec<MenuItem<String>>,
}
impl DMenuProvider {
fn new() -> Result<DMenuProvider, ModeError> {
let mut input = String::new();
io::stdin()
.read_to_string(&mut input)
.map_err(|e| ModeError::StdInReadFail)?;
let items: Vec<MenuItem<String>> = input
.lines()
.map(String::from)
.map(|s| MenuItem {
label: s,
icon_path: None,
action: None,
sub_elements: vec![],
working_dir: None,
initial_sort_score: 0,
search_sort_score: 0.0,
data: None,
})
.collect();
Ok(Self { items })
}
}
impl ItemProvider<String> for DMenuProvider {
fn get_elements(&mut self, _: Option<&str>) -> Vec<MenuItem<String>> {
self.items.clone()
}
fn get_sub_elements(&mut self, _: &MenuItem<String>) -> Option<Vec<MenuItem<String>>> {
None
}
}
#[derive(Debug, Clone, PartialEq)]
enum AutoRunType {
Math,
@ -509,9 +555,18 @@ pub fn math(config: &Config) {
/// # Errors
///
/// todo
pub fn dmenu(_: &Config) -> Result<(), ModeError> {
pub fn dmenu(config: &Config) -> Result<(), ModeError> {
let provider = DMenuProvider::new()?;
let selection_result = gui::show(config.clone(), provider);
match selection_result {
Ok(s) => {
println!("{}", s.label);
Ok(())
}
Err(_) => Err(ModeError::InvalidSelection),
}
}
fn update_drun_cache_and_run<T: Clone>(
cache_path: Option<PathBuf>,