add support for location
This commit is contained in:
parent
3e34d809be
commit
2453bbaf72
7 changed files with 151 additions and 120 deletions
14
README.md
14
README.md
|
@ -17,7 +17,7 @@ Allow blur for Worf
|
||||||
layerrule = blur, worf
|
layerrule = blur, worf
|
||||||
```
|
```
|
||||||
|
|
||||||
## Additional functionality compared to Wofi (planed)
|
## Additional functionality compared to Wofi
|
||||||
* Support passing 'hidden' parameters that are not visible in the launcher but will be returned to the application
|
* Support passing 'hidden' parameters that are not visible in the launcher but will be returned to the application
|
||||||
* Window switcher for hyprland
|
* Window switcher for hyprland
|
||||||
* All arguments expect show are supported by config and args
|
* All arguments expect show are supported by config and args
|
||||||
|
@ -32,6 +32,12 @@ layerrule = blur, worf
|
||||||
* `label`: Allows styling the label
|
* `label`: Allows styling the label
|
||||||
* `row`: Allows styling to row, mainly used to disable hover effects
|
* `row`: Allows styling to row, mainly used to disable hover effects
|
||||||
|
|
||||||
|
## Library
|
||||||
|
|
||||||
|
The launcher and UI can be used to build any launcher, as the ui, config and run logic is available as a separate crate.
|
||||||
|
This library is not available publicly yet as the interface is not stable enough.
|
||||||
|
|
||||||
|
|
||||||
## Breaking changes to Wofi
|
## Breaking changes to Wofi
|
||||||
* Runtime behaviour is not guaranteed to be the same and won't ever be, this includes error messages and themes.
|
* Runtime behaviour is not guaranteed to be the same and won't ever be, this includes error messages and themes.
|
||||||
* Themes in general are mostly compatible. Worf is using the same entity ids,
|
* Themes in general are mostly compatible. Worf is using the same entity ids,
|
||||||
|
@ -39,6 +45,7 @@ layerrule = blur, worf
|
||||||
* Configuration files are not 100% compatible, Worf is using toml files instead, for most part this only means strings have to be quoted
|
* Configuration files are not 100% compatible, Worf is using toml files instead, for most part this only means strings have to be quoted
|
||||||
* Color files are not supported
|
* Color files are not supported
|
||||||
* `line_wrap` is now called `line-wrap`
|
* `line_wrap` is now called `line-wrap`
|
||||||
|
* Wofi has a C-API, that is not and won't be supported.
|
||||||
|
|
||||||
## Dropped arguments
|
## Dropped arguments
|
||||||
* `mode`, use show
|
* `mode`, use show
|
||||||
|
@ -48,8 +55,3 @@ layerrule = blur, worf
|
||||||
### Dropped configuration options
|
### Dropped configuration options
|
||||||
* stylesheet -> use style instead
|
* stylesheet -> use style instead
|
||||||
* color / colors -> GTK4 does not support color files
|
* color / colors -> GTK4 does not support color files
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Not supported
|
|
||||||
* Wofi has a C-API, that is not and won't be supported.
|
|
||||||
|
|
12
examples/dmenu.sh
Executable file
12
examples/dmenu.sh
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# A list of options, one per line
|
||||||
|
options="Option 1
|
||||||
|
Option 2
|
||||||
|
Option 3"
|
||||||
|
|
||||||
|
# Pipe options to wofi and capture the selection
|
||||||
|
selection=$(echo "$options" | cargo run -- --show dmenu)
|
||||||
|
|
||||||
|
# Do something with the selection
|
||||||
|
echo "You selected: $selection"
|
|
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{env, fmt, fs};
|
use std::{env, fmt, fs};
|
||||||
|
|
||||||
use anyhow::{Error, anyhow};
|
use anyhow::anyhow;
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
@ -23,6 +23,14 @@ impl fmt::Display for ConfigurationError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum Anchor {
|
||||||
|
Top,
|
||||||
|
Left,
|
||||||
|
Bottom,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Serialize, Deserialize)]
|
||||||
pub enum MatchMethod {
|
pub enum MatchMethod {
|
||||||
Fuzzy,
|
Fuzzy,
|
||||||
|
@ -85,6 +93,20 @@ pub enum ArgsError {
|
||||||
InvalidParameter(String),
|
InvalidParameter(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for Anchor {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.trim() {
|
||||||
|
"top" => Ok(Anchor::Top),
|
||||||
|
"left" => Ok(Anchor::Left),
|
||||||
|
"bottom" => Ok(Anchor::Bottom),
|
||||||
|
"right" => Ok(Anchor::Right),
|
||||||
|
other => Err(format!("Invalid anchor: {}", other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for Mode {
|
impl FromStr for Mode {
|
||||||
type Err = ArgsError;
|
type Err = ArgsError;
|
||||||
|
|
||||||
|
@ -201,8 +223,11 @@ pub struct Config {
|
||||||
#[clap(short = 'q', long = "parse-search")]
|
#[clap(short = 'q', long = "parse-search")]
|
||||||
pub parse_search: Option<bool>,
|
pub parse_search: Option<bool>,
|
||||||
|
|
||||||
#[clap(short = 'l', long = "location")]
|
/// set where the window is displayed.
|
||||||
pub location: Option<String>,
|
/// can be used to anchor a window to an edge by
|
||||||
|
/// setting top,left for example
|
||||||
|
#[clap(short = 'l', long = "location", value_delimiter = ',', value_parser = clap::builder::ValueParser::new(Anchor::from_str))]
|
||||||
|
pub location: Option<Vec<Anchor>>,
|
||||||
|
|
||||||
#[clap(short = 'a', long = "no-actions")]
|
#[clap(short = 'a', long = "no-actions")]
|
||||||
pub no_actions: Option<bool>,
|
pub no_actions: Option<bool>,
|
||||||
|
@ -508,7 +533,6 @@ pub fn default_line_wrap() -> Option<WrapMode> {
|
||||||
// max_lines = lines;
|
// max_lines = lines;
|
||||||
// columns = strtol(config_get(config, "columns", "1"), NULL, 10);
|
// columns = strtol(config_get(config, "columns", "1"), NULL, 10);
|
||||||
// sort_order = config_get_mnemonic(config, "sort_order", "default", 2, "default", "alphabetical");
|
// sort_order = config_get_mnemonic(config, "sort_order", "default", 2, "default", "alphabetical");
|
||||||
// line_wrap = config_get_mnemonic(config, "line_wrap", "off", 4, "off", "word", "char", "word_char") - 1;
|
|
||||||
// bool global_coords = strcmp(config_get(config, "global_coords", "false"), "true") == 0;
|
// bool global_coords = strcmp(config_get(config, "global_coords", "false"), "true") == 0;
|
||||||
// hide_search = strcmp(config_get(config, "hide_search", "false"), "true") == 0;
|
// hide_search = strcmp(config_get(config, "hide_search", "false"), "true") == 0;
|
||||||
// char* search = map_get(config, "search");
|
// char* search = map_get(config, "search");
|
||||||
|
@ -700,9 +724,9 @@ pub fn load_config(args_opt: Option<Config>) -> Result<Config, ConfigurationErro
|
||||||
Mode::Run => merge_result.prompt = Some("run".to_owned()),
|
Mode::Run => merge_result.prompt = Some("run".to_owned()),
|
||||||
Mode::Drun => merge_result.prompt = Some("drun".to_owned()),
|
Mode::Drun => merge_result.prompt = Some("drun".to_owned()),
|
||||||
Mode::Dmenu => merge_result.prompt = Some("dmenu".to_owned()),
|
Mode::Dmenu => merge_result.prompt = Some("dmenu".to_owned()),
|
||||||
Mode::Math => merge_result.prompt = Some("math".to_owned()),
|
Mode::Math => merge_result.prompt = Some("math".to_owned()),
|
||||||
Mode::File => merge_result.prompt = Some("file".to_owned()),
|
Mode::File => merge_result.prompt = Some("file".to_owned()),
|
||||||
Mode::Auto => merge_result.prompt = Some("auto".to_owned()),
|
Mode::Auto => merge_result.prompt = Some("auto".to_owned()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +1,26 @@
|
||||||
use anyhow::anyhow;
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs, string};
|
||||||
|
|
||||||
use freedesktop_file_parser::DesktopFile;
|
use freedesktop_file_parser::DesktopFile;
|
||||||
use gdk4::Display;
|
use gdk4::Display;
|
||||||
use gtk4::prelude::*;
|
use gtk4::prelude::*;
|
||||||
use gtk4::{IconLookupFlags, IconTheme, TextDirection};
|
use gtk4::{IconLookupFlags, IconTheme, TextDirection};
|
||||||
use home::home_dir;
|
use home::home_dir;
|
||||||
use log::{debug, info, warn};
|
use log;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::{env, fs, string};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DesktopError {
|
pub enum DesktopError {
|
||||||
MissingIcon,
|
MissingIcon,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// #[derive(Clone)]
|
|
||||||
// pub struct IconResolver {
|
|
||||||
// cache: HashMap<String, String>,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// impl Default for IconResolver {
|
|
||||||
// #[must_use]
|
|
||||||
// fn default() -> IconResolver {
|
|
||||||
// Self::new()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// impl IconResolver {
|
|
||||||
// #[must_use]
|
|
||||||
// pub fn new() -> IconResolver {
|
|
||||||
// IconResolver {
|
|
||||||
// cache: HashMap::new(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub fn icon_path_no_cache(&self, icon_name: &str) -> Result<String, DesktopError> {
|
|
||||||
// let icon = fetch_icon_from_theme(icon_name)
|
|
||||||
// .or_else(|_|
|
|
||||||
// fetch_icon_from_common_dirs(icon_name)
|
|
||||||
// .or_else(|_| default_icon()));
|
|
||||||
//
|
|
||||||
// icon
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub fn icon_path(&mut self, icon_name: &str) -> String {
|
|
||||||
// if let Some(icon_path) = self.cache.get(icon_name) {
|
|
||||||
// return icon_path.to_owned();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let icon = self.icon_path_no_cache(icon_name);
|
|
||||||
//
|
|
||||||
// self.cache
|
|
||||||
// .entry(icon_name.to_owned())
|
|
||||||
// .or_insert_with(|| icon.unwrap_or_default())
|
|
||||||
// .to_owned()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Will return `Err` if no icon can be found
|
/// Will return `Err` if no icon can be found
|
||||||
pub fn default_icon() -> Result<String, DesktopError> {
|
pub fn default_icon() -> Result<String, DesktopError> {
|
||||||
fetch_icon_from_theme("image-missing").map_err(|e| DesktopError::MissingIcon)
|
fetch_icon_from_theme("image-missing").map_err(|_| DesktopError::MissingIcon)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_icon_from_theme(icon_name: &str) -> Result<String, DesktopError> {
|
fn fetch_icon_from_theme(icon_name: &str) -> Result<String, DesktopError> {
|
||||||
|
@ -177,7 +133,7 @@ pub fn find_desktop_files() -> Vec<DesktopFile> {
|
||||||
.filter_map(|icon_dir| find_file_case_insensitive(&icon_dir, regex))
|
.filter_map(|icon_dir| find_file_case_insensitive(&icon_dir, regex))
|
||||||
.flat_map(|desktop_files| {
|
.flat_map(|desktop_files| {
|
||||||
desktop_files.into_iter().filter_map(|desktop_file| {
|
desktop_files.into_iter().filter_map(|desktop_file| {
|
||||||
debug!("loading desktop file {desktop_file:?}");
|
log::debug!("loading desktop file {desktop_file:?}");
|
||||||
fs::read_to_string(desktop_file)
|
fs::read_to_string(desktop_file)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|content| freedesktop_file_parser::parse(&content).ok())
|
.and_then(|content| freedesktop_file_parser::parse(&content).ok())
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -15,13 +14,17 @@ use gtk4::prelude::{
|
||||||
ApplicationExt, ApplicationExtManual, BoxExt, EditableExt, FlowBoxChildExt, GestureSingleExt,
|
ApplicationExt, ApplicationExtManual, BoxExt, EditableExt, FlowBoxChildExt, GestureSingleExt,
|
||||||
GtkWindowExt, ListBoxRowExt, NativeExt, WidgetExt,
|
GtkWindowExt, ListBoxRowExt, NativeExt, WidgetExt,
|
||||||
};
|
};
|
||||||
use gtk4::{Align, EventControllerKey, Expander, FlowBox, FlowBoxChild, GestureClick, Image, Label, ListBox, ListBoxRow, Ordering, PolicyType, ScrolledWindow, SearchEntry, Widget, gdk, NaturalWrapMode};
|
use gtk4::{
|
||||||
|
Align, EventControllerKey, Expander, FlowBox, FlowBoxChild, GestureClick, Image, Label,
|
||||||
|
ListBox, ListBoxRow, NaturalWrapMode, Ordering, PolicyType, ScrolledWindow, SearchEntry,
|
||||||
|
Widget, gdk,
|
||||||
|
};
|
||||||
use gtk4::{Application, ApplicationWindow, CssProvider, Orientation};
|
use gtk4::{Application, ApplicationWindow, CssProvider, Orientation};
|
||||||
use gtk4_layer_shell::{Edge, KeyboardMode, LayerShell};
|
use gtk4_layer_shell::{Edge, KeyboardMode, LayerShell};
|
||||||
use log;
|
use log;
|
||||||
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::config::{Animation, Config, MatchMethod, WrapMode};
|
use crate::config::{Anchor, Animation, Config, MatchMethod, WrapMode};
|
||||||
|
|
||||||
type ArcMenuMap<T> = Arc<Mutex<HashMap<FlowBoxChild, MenuItem<T>>>>;
|
type ArcMenuMap<T> = Arc<Mutex<HashMap<FlowBoxChild, MenuItem<T>>>>;
|
||||||
type ArcProvider<T> = Arc<Mutex<dyn ItemProvider<T>>>;
|
type ArcProvider<T> = Arc<Mutex<dyn ItemProvider<T>>>;
|
||||||
|
@ -32,6 +35,17 @@ pub trait ItemProvider<T: std::clone::Clone> {
|
||||||
fn get_sub_elements(&mut self, item: &MenuItem<T>) -> Option<Vec<MenuItem<T>>>;
|
fn get_sub_elements(&mut self, item: &MenuItem<T>) -> Option<Vec<MenuItem<T>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Anchor> for Edge {
|
||||||
|
fn from(value: &Anchor) -> Self {
|
||||||
|
match value {
|
||||||
|
Anchor::Top => Edge::Top,
|
||||||
|
Anchor::Left => Edge::Left,
|
||||||
|
Anchor::Bottom => Edge::Bottom,
|
||||||
|
Anchor::Right => Edge::Right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<config::Orientation> for Orientation {
|
impl From<config::Orientation> for Orientation {
|
||||||
fn from(orientation: config::Orientation) -> Self {
|
fn from(orientation: config::Orientation) -> Self {
|
||||||
match orientation {
|
match orientation {
|
||||||
|
@ -44,9 +58,9 @@ impl From<config::Orientation> for Orientation {
|
||||||
impl From<&WrapMode> for NaturalWrapMode {
|
impl From<&WrapMode> for NaturalWrapMode {
|
||||||
fn from(value: &WrapMode) -> Self {
|
fn from(value: &WrapMode) -> Self {
|
||||||
match value {
|
match value {
|
||||||
WrapMode::None => {NaturalWrapMode::None},
|
WrapMode::None => NaturalWrapMode::None,
|
||||||
WrapMode::Word => {NaturalWrapMode::Word},
|
WrapMode::Word => NaturalWrapMode::Word,
|
||||||
WrapMode::Inherit => {NaturalWrapMode::Inherit},
|
WrapMode::Inherit => NaturalWrapMode::Inherit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,8 +155,11 @@ fn build_ui<T, P>(
|
||||||
window.set_namespace(Some("worf"));
|
window.set_namespace(Some("worf"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// todo make this configurable
|
config.location.as_ref().map(|location| {
|
||||||
//window.set_anchor(Edge::Top, true);
|
for anchor in location {
|
||||||
|
window.set_anchor(anchor.into(), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let outer_box = gtk4::Box::new(config.orientation.unwrap().into(), 0);
|
let outer_box = gtk4::Box::new(config.orientation.unwrap().into(), 0);
|
||||||
outer_box.set_widget_name("outer-box");
|
outer_box.set_widget_name("outer-box");
|
||||||
|
@ -816,7 +833,7 @@ fn create_menu_row<T: Clone + 'static>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let label = Label::new(Some(menu_item.label.as_str()));
|
let label = Label::new(Some(menu_item.label.as_str()));
|
||||||
let wrap_mode : NaturalWrapMode = if let Some(config_wrap) = &config.line_wrap {
|
let wrap_mode: NaturalWrapMode = if let Some(config_wrap) = &config.line_wrap {
|
||||||
config_wrap.into()
|
config_wrap.into()
|
||||||
} else {
|
} else {
|
||||||
NaturalWrapMode::Word
|
NaturalWrapMode::Word
|
||||||
|
@ -951,7 +968,9 @@ fn percent_or_absolute(value: Option<&String>, base_value: i32) -> Option<i32> {
|
||||||
|
|
||||||
// highly unlikely that we are dealing with > i64 items
|
// highly unlikely that we are dealing with > i64 items
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
pub fn sort_menu_items_alphabetically_honor_initial_score<T: std::clone::Clone>(items: &mut [MenuItem<T>]) {
|
pub fn sort_menu_items_alphabetically_honor_initial_score<T: std::clone::Clone>(
|
||||||
|
items: &mut [MenuItem<T>],
|
||||||
|
) {
|
||||||
let mut regular_score = items.len() as i64;
|
let mut regular_score = items.len() as i64;
|
||||||
items.sort_by(|l, r| l.label.cmp(&r.label));
|
items.sort_by(|l, r| l.label.cmp(&r.label));
|
||||||
|
|
||||||
|
|
100
src/lib/mode.rs
100
src/lib/mode.rs
|
@ -1,21 +1,39 @@
|
||||||
|
use std::os::unix::prelude::CommandExt;
|
||||||
|
use std::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::config::{Config, expand_path};
|
||||||
use crate::desktop::{
|
use crate::desktop::{
|
||||||
default_icon, find_desktop_files, get_locale_variants, lookup_name_with_locale,
|
default_icon, find_desktop_files, get_locale_variants, lookup_name_with_locale,
|
||||||
};
|
};
|
||||||
|
use crate::gui;
|
||||||
use crate::gui::{ItemProvider, MenuItem};
|
use crate::gui::{ItemProvider, MenuItem};
|
||||||
use crate::{config, desktop, gui};
|
|
||||||
use anyhow::{Context, Error, anyhow};
|
#[derive(Debug)]
|
||||||
use freedesktop_file_parser::EntryType;
|
pub enum ModeError {
|
||||||
use gtk4::Image;
|
UpdateCacheError(String),
|
||||||
use libc::option;
|
MissingAction,
|
||||||
use regex::Regex;
|
RunError(String),
|
||||||
use serde::{Deserialize, Serialize};
|
MissingCache,
|
||||||
use std::collections::HashMap;
|
}
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
use std::os::unix::prelude::CommandExt;
|
impl fmt::Display for ModeError {
|
||||||
use std::path::PathBuf;
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use std::process::{Command, Stdio};
|
match self {
|
||||||
use std::{env, fs, io};
|
ModeError::UpdateCacheError(s) => write!(f, "UpdateCacheError {s}"),
|
||||||
|
ModeError::MissingAction => write!(f, "MissingAction"),
|
||||||
|
ModeError::RunError(s) => write!(f, "RunError, {s}"),
|
||||||
|
ModeError::MissingCache => write!(f, "MissingCache"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
struct DRunCache {
|
struct DRunCache {
|
||||||
|
@ -139,7 +157,7 @@ impl<T: Clone> ItemProvider<T> for DRunProvider<T> {
|
||||||
self.items.clone()
|
self.items.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sub_elements(&mut self, item: &MenuItem<T>) -> Option<Vec<MenuItem<T>>> {
|
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> Option<Vec<MenuItem<T>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,9 +301,7 @@ struct MathProvider<T: Clone> {
|
||||||
|
|
||||||
impl<T: std::clone::Clone> MathProvider<T> {
|
impl<T: std::clone::Clone> MathProvider<T> {
|
||||||
fn new(menu_item_data: T) -> Self {
|
fn new(menu_item_data: T) -> Self {
|
||||||
Self {
|
Self { menu_item_data }
|
||||||
menu_item_data,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains_math_functions_or_starts_with_number(input: &str) -> bool {
|
fn contains_math_functions_or_starts_with_number(input: &str) -> bool {
|
||||||
|
@ -327,7 +343,7 @@ impl<T: Clone> ItemProvider<T> for MathProvider<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sub_elements(&mut self, item: &MenuItem<T>) -> Option<Vec<MenuItem<T>>> {
|
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> Option<Vec<MenuItem<T>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,7 +382,9 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
|
||||||
let trimmed_search = search.trim();
|
let trimmed_search = search.trim();
|
||||||
if trimmed_search.is_empty() {
|
if trimmed_search.is_empty() {
|
||||||
self.drun_provider.get_elements(search_opt)
|
self.drun_provider.get_elements(search_opt)
|
||||||
} else if MathProvider::<AutoRunType>::contains_math_functions_or_starts_with_number(trimmed_search) {
|
} else if MathProvider::<AutoRunType>::contains_math_functions_or_starts_with_number(
|
||||||
|
trimmed_search,
|
||||||
|
) {
|
||||||
self.math_provider.get_elements(search_opt)
|
self.math_provider.get_elements(search_opt)
|
||||||
} else if trimmed_search.starts_with("$")
|
} else if trimmed_search.starts_with("$")
|
||||||
|| trimmed_search.starts_with("/")
|
|| trimmed_search.starts_with("/")
|
||||||
|
@ -392,7 +410,7 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Will return `Err` if it was not able to spawn the process
|
/// Will return `Err` if it was not able to spawn the process
|
||||||
pub fn d_run(config: &Config) -> anyhow::Result<()> {
|
pub fn d_run(config: &Config) -> Result<(), ModeError> {
|
||||||
let provider = DRunProvider::new("".to_owned());
|
let provider = DRunProvider::new("".to_owned());
|
||||||
let cache_path = provider.cache_path.clone();
|
let cache_path = provider.cache_path.clone();
|
||||||
let mut cache = provider.cache.clone();
|
let mut cache = provider.cache.clone();
|
||||||
|
@ -400,9 +418,7 @@ pub fn d_run(config: &Config) -> anyhow::Result<()> {
|
||||||
// todo ues a arc instead of cloning the config
|
// todo ues a arc instead of cloning the config
|
||||||
let selection_result = gui::show(config.clone(), provider);
|
let selection_result = gui::show(config.clone(), provider);
|
||||||
match selection_result {
|
match selection_result {
|
||||||
Ok(s) => {
|
Ok(s) => update_drun_cache_and_run(cache_path, &mut cache, s)?,
|
||||||
update_drun_cache_and_run(cache_path, &mut cache, s)?;
|
|
||||||
}
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
log::error!("No item selected");
|
log::error!("No item selected");
|
||||||
}
|
}
|
||||||
|
@ -411,7 +427,7 @@ pub fn d_run(config: &Config) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auto(config: &Config) -> anyhow::Result<()> {
|
pub fn auto(config: &Config) -> Result<(), ModeError> {
|
||||||
let provider = AutoItemProvider::new();
|
let provider = AutoItemProvider::new();
|
||||||
let cache_path = provider.drun_provider.cache_path.clone();
|
let cache_path = provider.drun_provider.cache_path.clone();
|
||||||
let mut cache = provider.drun_provider.cache.clone();
|
let mut cache = provider.drun_provider.cache.clone();
|
||||||
|
@ -429,12 +445,9 @@ pub fn auto(config: &Config) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
AutoRunType::File => {
|
AutoRunType::File => {
|
||||||
if let Some(action) = selection_result.action {
|
if let Some(action) = selection_result.action {
|
||||||
spawn_fork(&action, selection_result.working_dir.as_ref())?
|
spawn_fork(&action, selection_result.working_dir.as_ref())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
todo!("not supported yet");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,7 +459,7 @@ pub fn auto(config: &Config) -> anyhow::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file(config: &Config) -> Result<(), String> {
|
pub fn file(config: &Config) -> Result<(), ModeError> {
|
||||||
let provider = FileItemProvider::new("".to_owned());
|
let provider = FileItemProvider::new("".to_owned());
|
||||||
|
|
||||||
// todo ues a arc instead of cloning the config
|
// todo ues a arc instead of cloning the config
|
||||||
|
@ -454,7 +467,7 @@ pub fn file(config: &Config) -> Result<(), String> {
|
||||||
match selection_result {
|
match selection_result {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
if let Some(action) = s.action {
|
if let Some(action) = s.action {
|
||||||
spawn_fork(&action, s.working_dir.as_ref()).map_err(|e| e.to_string())?;
|
spawn_fork(&action, s.working_dir.as_ref())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -465,14 +478,13 @@ pub fn file(config: &Config) -> Result<(), String> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn math(config: &Config) -> Result<(), String> {
|
pub fn math(config: &Config) -> Result<(), ModeError> {
|
||||||
let provider = MathProvider::new("".to_owned());
|
let provider = MathProvider::new("".to_owned());
|
||||||
|
|
||||||
// todo ues a arc instead of cloning the config
|
// todo ues a arc instead of cloning the config
|
||||||
let selection_result = gui::show(config.clone(), provider);
|
let selection_result = gui::show(config.clone(), provider);
|
||||||
match selection_result {
|
match selection_result {
|
||||||
Ok(_) => {
|
Ok(_) => {}
|
||||||
}
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
log::error!("No item selected");
|
log::error!("No item selected");
|
||||||
}
|
}
|
||||||
|
@ -481,11 +493,15 @@ pub fn math(config: &Config) -> Result<(), String> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dmenu(_: &Config) -> Result<(), ModeError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn update_drun_cache_and_run<T: Clone>(
|
fn update_drun_cache_and_run<T: Clone>(
|
||||||
cache_path: Option<PathBuf>,
|
cache_path: Option<PathBuf>,
|
||||||
cache: &mut HashMap<String, i64>,
|
cache: &mut HashMap<String, i64>,
|
||||||
selection_result: MenuItem<T>,
|
selection_result: MenuItem<T>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), ModeError> {
|
||||||
if let Some(cache_path) = cache_path {
|
if let Some(cache_path) = cache_path {
|
||||||
*cache.entry(selection_result.label).or_insert(0) += 1;
|
*cache.entry(selection_result.label).or_insert(0) += 1;
|
||||||
if let Err(e) = save_cache_file(&cache_path, &cache) {
|
if let Err(e) = save_cache_file(&cache_path, &cache) {
|
||||||
|
@ -496,7 +512,7 @@ fn update_drun_cache_and_run<T: Clone>(
|
||||||
if let Some(action) = selection_result.action {
|
if let Some(action) = selection_result.action {
|
||||||
spawn_fork(&action, selection_result.working_dir.as_ref())
|
spawn_fork(&action, selection_result.working_dir.as_ref())
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow::anyhow!("cannot find drun action"))
|
Err(ModeError::MissingAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,12 +536,13 @@ fn save_cache_file(path: &PathBuf, data: &HashMap<String, i64>) -> anyhow::Resul
|
||||||
fs::write(path, toml_string).map_err(|e| anyhow::anyhow!(e))
|
fs::write(path, toml_string).map_err(|e| anyhow::anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_cache_file(cache_path: Option<&PathBuf>) -> anyhow::Result<HashMap<String, i64>> {
|
fn load_cache_file(cache_path: Option<&PathBuf>) -> Result<HashMap<String, i64>, ModeError> {
|
||||||
let Some(path) = cache_path else {
|
let Some(path) = cache_path else {
|
||||||
return Err(anyhow!("Cache is missing"));
|
return Err(ModeError::MissingCache);
|
||||||
};
|
};
|
||||||
|
|
||||||
let toml_content = fs::read_to_string(path)?;
|
let toml_content =
|
||||||
|
fs::read_to_string(path).map_err(|e| ModeError::UpdateCacheError(format!("{e}")))?;
|
||||||
let parsed: toml::Value = toml_content.parse().expect("Failed to parse TOML");
|
let parsed: toml::Value = toml_content.parse().expect("Failed to parse TOML");
|
||||||
|
|
||||||
let mut result: HashMap<String, i64> = HashMap::new();
|
let mut result: HashMap<String, i64> = HashMap::new();
|
||||||
|
@ -555,17 +572,18 @@ fn create_file_if_not_exists(path: &PathBuf) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> anyhow::Result<()> {
|
fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> Result<(), ModeError> {
|
||||||
// todo fix actions ??
|
// todo fix actions ??
|
||||||
// todo graphical disk map icon not working
|
// todo graphical disk map icon not working
|
||||||
|
|
||||||
let parts = cmd.split(' ').collect::<Vec<_>>();
|
let parts = cmd.split(' ').collect::<Vec<_>>();
|
||||||
if parts.is_empty() {
|
if parts.is_empty() {
|
||||||
return Err(anyhow!("empty command passed"));
|
return Err(ModeError::MissingAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(dir) = working_dir {
|
if let Some(dir) = working_dir {
|
||||||
env::set_current_dir(dir)?;
|
env::set_current_dir(dir)
|
||||||
|
.map_err(|e| ModeError::RunError(format!("cannot set workdir {e}")))?
|
||||||
}
|
}
|
||||||
|
|
||||||
let exec = parts[0].replace('"', "");
|
let exec = parts[0].replace('"', "");
|
||||||
|
|
|
@ -13,7 +13,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let args = config::parse_args();
|
let args = config::parse_args();
|
||||||
let mut config = config::load_config(Some(args)).map_err(|e| anyhow!(e))?;
|
let config = config::load_config(Some(args)).map_err(|e| anyhow!(e))?;
|
||||||
|
|
||||||
if let Some(show) = &config.show {
|
if let Some(show) = &config.show {
|
||||||
match show {
|
match show {
|
||||||
|
@ -21,10 +21,10 @@ fn main() -> anyhow::Result<()> {
|
||||||
todo!("run not implemented")
|
todo!("run not implemented")
|
||||||
}
|
}
|
||||||
Mode::Drun => {
|
Mode::Drun => {
|
||||||
mode::d_run(&config)?;
|
mode::d_run(&config).map_err(|e| anyhow!(e))?;
|
||||||
}
|
}
|
||||||
Mode::Dmenu => {
|
Mode::Dmenu => {
|
||||||
todo!("dmenu not implemented")
|
mode::dmenu(&config).map_err(|e| anyhow!(e))?;
|
||||||
}
|
}
|
||||||
Mode::File => {
|
Mode::File => {
|
||||||
mode::file(&config).map_err(|e| anyhow!(e))?;
|
mode::file(&config).map_err(|e| anyhow!(e))?;
|
||||||
|
@ -33,7 +33,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
mode::math(&config).map_err(|e| anyhow!(e))?;
|
mode::math(&config).map_err(|e| anyhow!(e))?;
|
||||||
}
|
}
|
||||||
Mode::Auto => {
|
Mode::Auto => {
|
||||||
mode::auto(&config)?;
|
mode::auto(&config).map_err(|e| anyhow!(e))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue