build ui in glib idle task
this improves startup times, especially with large item sets imensly
This commit is contained in:
parent
bc6d1de673
commit
bdbe3c49cd
4 changed files with 94 additions and 104 deletions
|
@ -12,9 +12,9 @@ It supports various modes:
|
||||||
* DRun
|
* DRun
|
||||||
* File
|
* File
|
||||||
* Ssh
|
* Ssh
|
||||||
|
* Run
|
||||||
* // WebSearch
|
* // WebSearch
|
||||||
* // Emoji
|
* // Emoji
|
||||||
* // Run
|
|
||||||
* Auto
|
* Auto
|
||||||
|
|
||||||
Auto mode tries to detect the desired mode automatically, i.e. `ssh`, `?` (for web search), `emoji`, `/` or `~` (for file).
|
Auto mode tries to detect the desired mode automatically, i.e. `ssh`, `?` (for web search), `emoji`, `/` or `~` (for file).
|
||||||
|
@ -22,7 +22,6 @@ Auto mode tries to detect the desired mode automatically, i.e. `ssh`, `?` (for w
|
||||||
<img src="images/demo.gif" style="width:600px;">
|
<img src="images/demo.gif" style="width:600px;">
|
||||||
|
|
||||||
## Not finished
|
## Not finished
|
||||||
* [ ] run
|
|
||||||
* [ ] key support
|
* [ ] key support
|
||||||
* [ ] full config support
|
* [ ] full config support
|
||||||
* [ ] web search mode
|
* [ ] web search mode
|
||||||
|
|
|
@ -4,13 +4,13 @@ use freedesktop_file_parser::DesktopFile;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::os::unix::prelude::CommandExt;
|
use std::os::unix::prelude::CommandExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{env, fs, io};
|
use std::{env, fs, io};
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
|
|
||||||
/// Returns a regex with supported image extensions
|
/// Returns a regex with supported image extensions
|
||||||
/// # Panics
|
/// # Panics
|
||||||
|
@ -281,9 +281,9 @@ pub fn create_file_if_not_exists(path: &PathBuf) -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Check if the given dir entry is an executable
|
/// Check if the given dir entry is an executable
|
||||||
pub fn is_executable(entry: &PathBuf) -> bool {
|
#[must_use]
|
||||||
|
pub fn is_executable(entry: &Path) -> bool {
|
||||||
if let Ok(metadata) = entry.metadata() {
|
if let Ok(metadata) = entry.metadata() {
|
||||||
let permissions = metadata.permissions();
|
let permissions = metadata.permissions();
|
||||||
metadata.is_file() && (permissions.mode() & 0o111 != 0)
|
metadata.is_file() && (permissions.mode() & 0o111 != 0)
|
||||||
|
|
126
src/lib/gui.rs
126
src/lib/gui.rs
|
@ -1,5 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -23,7 +22,7 @@ use gtk4::prelude::{
|
||||||
use gtk4::{
|
use gtk4::{
|
||||||
Align, EventControllerKey, Expander, FlowBox, FlowBoxChild, GestureClick, Image, Label,
|
Align, EventControllerKey, Expander, FlowBox, FlowBoxChild, GestureClick, Image, Label,
|
||||||
ListBox, ListBoxRow, NaturalWrapMode, Ordering, PolicyType, ScrolledWindow, SearchEntry,
|
ListBox, ListBoxRow, NaturalWrapMode, Ordering, PolicyType, ScrolledWindow, SearchEntry,
|
||||||
Widget, gdk,
|
Widget, gdk, glib,
|
||||||
};
|
};
|
||||||
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};
|
||||||
|
@ -217,7 +216,6 @@ fn build_ui<T, P>(
|
||||||
});
|
});
|
||||||
|
|
||||||
let provider_clone = Arc::clone(&meta.item_provider);
|
let provider_clone = Arc::clone(&meta.item_provider);
|
||||||
|
|
||||||
let get_provider_elements = thread::spawn(move || {
|
let get_provider_elements = thread::spawn(move || {
|
||||||
log::debug!("getting items");
|
log::debug!("getting items");
|
||||||
provider_clone.lock().unwrap().get_elements(None)
|
provider_clone.lock().unwrap().get_elements(None)
|
||||||
|
@ -239,8 +237,6 @@ fn build_ui<T, P>(
|
||||||
menu_rows: Arc::new(Mutex::new(HashMap::new())),
|
menu_rows: Arc::new(Mutex::new(HashMap::new())),
|
||||||
});
|
});
|
||||||
|
|
||||||
let window_show = Instant::now();
|
|
||||||
|
|
||||||
// handle keys as soon as possible
|
// handle keys as soon as possible
|
||||||
setup_key_event_handler(&ui_elements, &meta);
|
setup_key_event_handler(&ui_elements, &meta);
|
||||||
|
|
||||||
|
@ -260,8 +256,6 @@ fn build_ui<T, P>(
|
||||||
ui_elements.window.set_namespace(Some("worf"));
|
ui_elements.window.set_namespace(Some("worf"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let window_done = Instant::now();
|
|
||||||
|
|
||||||
if let Some(location) = config.location() {
|
if let Some(location) = config.location() {
|
||||||
for anchor in location {
|
for anchor in location {
|
||||||
ui_elements.window.set_anchor(anchor.into(), true);
|
ui_elements.window.set_anchor(anchor.into(), true);
|
||||||
|
@ -293,26 +287,14 @@ fn build_ui<T, P>(
|
||||||
let wait_for_items = Instant::now();
|
let wait_for_items = Instant::now();
|
||||||
let (_changed, provider_elements) = get_provider_elements.join().unwrap();
|
let (_changed, provider_elements) = get_provider_elements.join().unwrap();
|
||||||
log::debug!("got items after {:?}", wait_for_items.elapsed());
|
log::debug!("got items after {:?}", wait_for_items.elapsed());
|
||||||
{
|
build_ui_from_menu_items(&ui_elements, &meta, provider_elements);
|
||||||
let mut lock = ui_elements.menu_rows.lock().unwrap();
|
|
||||||
build_ui_from_menu_items(&ui_elements, &meta, &provider_elements, lock.deref_mut());
|
|
||||||
}
|
|
||||||
|
|
||||||
let items_sort = ArcMenuMap::clone(&ui_elements.menu_rows);
|
|
||||||
ui_elements
|
|
||||||
.main_box
|
|
||||||
.set_sort_func(move |child1, child2| sort_menu_items_by_score(child1, child2, &items_sort));
|
|
||||||
|
|
||||||
|
let window_start = Instant::now();
|
||||||
ui_elements.window.show();
|
ui_elements.window.show();
|
||||||
animate_window_show(config, ui_elements.window.clone());
|
log::debug!("window show took {:?}", window_start.elapsed());
|
||||||
|
|
||||||
let animation_done = Instant::now();
|
animate_window_show(config, ui_elements.window.clone());
|
||||||
log::debug!(
|
log::debug!("Building UI took {:?}", start.elapsed(),);
|
||||||
"Building UI took {:?}, window show {:?}, animation {:?}",
|
|
||||||
start.elapsed(),
|
|
||||||
window_done - window_show,
|
|
||||||
animation_done - window_done
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_main_box<T: Clone + 'static>(config: &Config, ui_elements: &Rc<UiElements<T>>) {
|
fn build_main_box<T: Clone + 'static>(config: &Config, ui_elements: &Rc<UiElements<T>>) {
|
||||||
|
@ -343,7 +325,7 @@ fn build_main_box<T: Clone + 'static>(config: &Config, ui_elements: &Rc<UiElemen
|
||||||
fb.invalidate_sort();
|
fb.invalidate_sort();
|
||||||
|
|
||||||
let lock = ui_clone.menu_rows.lock().unwrap();
|
let lock = ui_clone.menu_rows.lock().unwrap();
|
||||||
select_first_visible_child(lock.deref(), &ui_clone.main_box);
|
select_first_visible_child(&*lock, &ui_clone.main_box);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,37 +344,51 @@ fn build_search_entry<T: Clone>(config: &Config, ui_elements: &UiElements<T>) {
|
||||||
fn build_ui_from_menu_items<T: Clone + 'static>(
|
fn build_ui_from_menu_items<T: Clone + 'static>(
|
||||||
ui: &Rc<UiElements<T>>,
|
ui: &Rc<UiElements<T>>,
|
||||||
meta: &Rc<MetaData<T>>,
|
meta: &Rc<MetaData<T>>,
|
||||||
items: &Vec<MenuItem<T>>,
|
mut items: Vec<MenuItem<T>>,
|
||||||
map: &mut HashMap<FlowBoxChild, MenuItem<T>>,
|
|
||||||
) {
|
) {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
{
|
{
|
||||||
let got_lock = Instant::now();
|
|
||||||
|
|
||||||
ui.main_box.unset_sort_func();
|
|
||||||
|
|
||||||
while let Some(b) = ui.main_box.child_at_index(0) {
|
while let Some(b) = ui.main_box.child_at_index(0) {
|
||||||
ui.main_box.remove(&b);
|
ui.main_box.remove(&b);
|
||||||
drop(b);
|
drop(b);
|
||||||
}
|
}
|
||||||
map.clear();
|
ui.menu_rows.lock().unwrap().clear();
|
||||||
|
|
||||||
let cleared_box = Instant::now();
|
let meta_clone = Rc::<MetaData<T>>::clone(meta);
|
||||||
|
let ui_clone = Rc::<UiElements<T>>::clone(ui);
|
||||||
|
|
||||||
for entry in items {
|
glib::idle_add_local(move || {
|
||||||
if entry.visible {
|
ui_clone.main_box.unset_sort_func();
|
||||||
map.insert(add_menu_item(ui, meta, entry), (*entry).clone());
|
let mut done = false;
|
||||||
|
{
|
||||||
|
let mut lock = ui_clone.menu_rows.lock().unwrap();
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
if let Some(item) = items.pop() {
|
||||||
|
lock.insert(add_menu_item(&ui_clone, &meta_clone, &item), item);
|
||||||
|
} else {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = ui_clone.search.text();
|
||||||
|
let menus = &mut *lock;
|
||||||
|
set_menu_visibility_for_search(&query, menus, &meta_clone.config);
|
||||||
|
select_first_visible_child(&*lock, &ui_clone.main_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let created_ui = Instant::now();
|
let items_sort = ArcMenuMap::clone(&ui_clone.menu_rows);
|
||||||
log::debug!(
|
ui_clone.main_box.set_sort_func(move |child1, child2| {
|
||||||
"Creating UI took {:?}, got lock after {:?}, cleared box after {:?}, created UI after {:?}",
|
sort_menu_items_by_score(child1, child2, &items_sort)
|
||||||
start.elapsed(),
|
});
|
||||||
got_lock - start,
|
|
||||||
cleared_box - start,
|
if done {
|
||||||
created_ui - start
|
log::debug!("Created menu items in {:?}", start.elapsed());
|
||||||
);
|
ControlFlow::Break
|
||||||
|
} else {
|
||||||
|
ControlFlow::Continue
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,8 +415,8 @@ fn handle_key_press<T: Clone + 'static>(
|
||||||
) -> Propagation {
|
) -> Propagation {
|
||||||
let update_view = |query: &String| {
|
let update_view = |query: &String| {
|
||||||
let mut lock = ui.menu_rows.lock().unwrap();
|
let mut lock = ui.menu_rows.lock().unwrap();
|
||||||
let mut menus = lock.iter_mut().map(|(s, v)| v).collect::<Vec<_>>();
|
let menus = &mut *lock;
|
||||||
set_menu_visibility_for_search(query, menus.as_mut_slice(), &meta.config);
|
set_menu_visibility_for_search(query, menus, &meta.config);
|
||||||
for (fb, item) in lock.iter() {
|
for (fb, item) in lock.iter() {
|
||||||
fb.set_visible(item.visible);
|
fb.set_visible(item.visible);
|
||||||
}
|
}
|
||||||
|
@ -431,18 +427,13 @@ fn handle_key_press<T: Clone + 'static>(
|
||||||
let update_view_from_provider = |query: &String| {
|
let update_view_from_provider = |query: &String| {
|
||||||
let (changed, filtered_list) = meta.item_provider.lock().unwrap().get_elements(Some(query));
|
let (changed, filtered_list) = meta.item_provider.lock().unwrap().get_elements(Some(query));
|
||||||
if changed {
|
if changed {
|
||||||
|
build_ui_from_menu_items(ui, meta, filtered_list);
|
||||||
let mut lock = ui.menu_rows.lock().unwrap();
|
|
||||||
build_ui_from_menu_items(&ui, &meta, &filtered_list, lock.deref_mut());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update_view(query);
|
update_view(query);
|
||||||
};
|
};
|
||||||
|
|
||||||
match keyboard_key {
|
match keyboard_key {
|
||||||
Key::Down => {
|
|
||||||
return Propagation::Stop;
|
|
||||||
}
|
|
||||||
Key::Escape => {
|
Key::Escape => {
|
||||||
if let Err(e) = meta.selected_sender.send(Err(anyhow!("No item selected"))) {
|
if let Err(e) = meta.selected_sender.send(Err(anyhow!("No item selected"))) {
|
||||||
log::error!("failed to send message {e}");
|
log::error!("failed to send message {e}");
|
||||||
|
@ -481,8 +472,7 @@ fn handle_key_press<T: Clone + 'static>(
|
||||||
|
|
||||||
let items = items.unwrap_or_default();
|
let items = items.unwrap_or_default();
|
||||||
if changed {
|
if changed {
|
||||||
let mut lock = ui.menu_rows.lock().unwrap();
|
build_ui_from_menu_items(ui, meta, items);
|
||||||
build_ui_from_menu_items(ui, meta, &items, &mut *lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = menu_item.label.clone();
|
let query = menu_item.label.clone();
|
||||||
|
@ -526,16 +516,20 @@ fn sort_menu_items_by_score<T: Clone>(
|
||||||
|
|
||||||
match (m1, m2) {
|
match (m1, m2) {
|
||||||
(Some(menu1), Some(menu2)) => {
|
(Some(menu1), Some(menu2)) => {
|
||||||
if menu1.search_sort_score > 0.0 || menu2.search_sort_score > 0.0 {
|
fn compare(a: f64, b: f64) -> Ordering {
|
||||||
if menu1.search_sort_score < menu2.search_sort_score {
|
if a > b {
|
||||||
Ordering::Smaller
|
Ordering::Smaller
|
||||||
} else {
|
} else if a < b {
|
||||||
Ordering::Larger
|
Ordering::Larger
|
||||||
|
} else {
|
||||||
|
Ordering::Equal
|
||||||
}
|
}
|
||||||
} else if menu1.initial_sort_score > menu2.initial_sort_score {
|
}
|
||||||
Ordering::Smaller
|
|
||||||
|
if menu1.search_sort_score > 0.0 || menu2.search_sort_score > 0.0 {
|
||||||
|
compare(menu1.search_sort_score, menu2.search_sort_score)
|
||||||
} else {
|
} else {
|
||||||
Ordering::Larger
|
compare(menu1.initial_sort_score, menu2.initial_sort_score)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Some(_), None) => Ordering::Larger,
|
(Some(_), None) => Ordering::Larger,
|
||||||
|
@ -863,14 +857,15 @@ fn lookup_icon<T: Clone>(menu_item: &MenuItem<T>, config: &Config) -> Option<Ima
|
||||||
|
|
||||||
fn set_menu_visibility_for_search<T: Clone>(
|
fn set_menu_visibility_for_search<T: Clone>(
|
||||||
query: &str,
|
query: &str,
|
||||||
items: &mut [&mut MenuItem<T>],
|
items: &mut HashMap<FlowBoxChild, MenuItem<T>>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) {
|
) {
|
||||||
{
|
{
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
for menu_item in items.iter_mut() {
|
for (fb, menu_item) in items.iter_mut() {
|
||||||
menu_item.search_sort_score = menu_item.initial_sort_score;
|
menu_item.search_sort_score = 0.0;
|
||||||
menu_item.visible = true;
|
menu_item.visible = true;
|
||||||
|
fb.set_visible(menu_item.visible);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let query = if config.insensitive() {
|
let query = if config.insensitive() {
|
||||||
|
@ -878,7 +873,7 @@ fn set_menu_visibility_for_search<T: Clone>(
|
||||||
} else {
|
} else {
|
||||||
query.to_owned()
|
query.to_owned()
|
||||||
};
|
};
|
||||||
for menu_item in items.iter_mut() {
|
for (fb, menu_item) in items.iter_mut() {
|
||||||
let menu_item_search = format!(
|
let menu_item_search = format!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
menu_item
|
menu_item
|
||||||
|
@ -917,6 +912,7 @@ fn set_menu_visibility_for_search<T: Clone>(
|
||||||
|
|
||||||
menu_item.search_sort_score = search_sort_score + menu_item.initial_sort_score;
|
menu_item.search_sort_score = search_sort_score + menu_item.initial_sort_score;
|
||||||
menu_item.visible = visible;
|
menu_item.visible = visible;
|
||||||
|
fb.set_visible(menu_item.visible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use crate::config::{Config, expand_path};
|
use crate::config::{Config, expand_path};
|
||||||
use crate::desktop::{create_file_if_not_exists, find_desktop_files, get_locale_variants, is_executable, load_cache_file, lookup_name_with_locale, save_cache_file, spawn_fork};
|
use crate::desktop::{
|
||||||
|
create_file_if_not_exists, find_desktop_files, get_locale_variants, is_executable,
|
||||||
|
load_cache_file, lookup_name_with_locale, save_cache_file, spawn_fork,
|
||||||
|
};
|
||||||
use crate::gui::{ItemProvider, MenuItem};
|
use crate::gui::{ItemProvider, MenuItem};
|
||||||
use crate::{Error, gui};
|
use crate::{Error, gui};
|
||||||
use freedesktop_file_parser::EntryType;
|
use freedesktop_file_parser::EntryType;
|
||||||
|
@ -7,11 +10,11 @@ use rayon::prelude::*;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::ffi::CString;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{env, fs, io};
|
use std::{env, fs, io};
|
||||||
use std::ffi::CString;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
struct DRunCache {
|
struct DRunCache {
|
||||||
|
@ -143,45 +146,43 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + Send + Sync> ItemProvider<T> for RunProvider<T> {
|
impl ItemProvider<i32> for RunProvider {
|
||||||
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<gui::MenuItem<T>>) {
|
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<MenuItem<i32>>) {
|
||||||
if self.items.is_none() {
|
if self.items.is_none() {
|
||||||
self.items = Some(self.load().clone());
|
self.items = Some(self.load().clone());
|
||||||
}
|
}
|
||||||
(false, self.items.clone().unwrap())
|
(false, self.items.clone().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, std::option::Option<Vec<gui::MenuItem<T>>>) {
|
fn get_sub_elements(&mut self, _: &MenuItem<i32>) -> (bool, Option<Vec<MenuItem<i32>>>) {
|
||||||
(false, None)
|
(false, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct RunProvider<T: Clone> {
|
struct RunProvider {
|
||||||
items: Option<Vec<MenuItem<T>>>,
|
items: Option<Vec<MenuItem<i32>>>,
|
||||||
cache_path: Option<PathBuf>,
|
cache_path: Option<PathBuf>,
|
||||||
cache: HashMap<String, i64>,
|
cache: HashMap<String, i64>,
|
||||||
data: T,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + Send + Sync> RunProvider<T> {
|
impl RunProvider {
|
||||||
fn new(menu_item_data: T) -> Self {
|
fn new() -> Self {
|
||||||
let (cache_path, d_run_cache) = load_run_cache();
|
let (cache_path, d_run_cache) = load_run_cache();
|
||||||
RunProvider {
|
RunProvider {
|
||||||
items: None,
|
items: None,
|
||||||
cache_path,
|
cache_path,
|
||||||
cache: d_run_cache,
|
cache: d_run_cache,
|
||||||
data: menu_item_data,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
fn load(&self) -> Vec<MenuItem<T>> {
|
fn load(&self) -> Vec<MenuItem<i32>> {
|
||||||
let path_var = env::var("PATH").unwrap_or_default();
|
let path_var = env::var("PATH").unwrap_or_default();
|
||||||
let paths = env::split_paths(&path_var);
|
let paths = env::split_paths(&path_var);
|
||||||
|
|
||||||
let entries : Vec<_>= paths
|
let entries: Vec<_> = paths
|
||||||
.filter(|dir| dir.is_dir())
|
.filter(|dir| dir.is_dir())
|
||||||
.flat_map(|dir| {
|
.flat_map(|dir| {
|
||||||
fs::read_dir(dir)
|
fs::read_dir(dir)
|
||||||
|
@ -212,9 +213,8 @@ impl<T: Clone + Send + Sync> RunProvider<T> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
||||||
let mut seen_actions = HashSet::new();
|
let mut seen_actions = HashSet::new();
|
||||||
let mut entries: Vec<MenuItem<T>> = entries
|
let mut entries: Vec<MenuItem<i32>> = entries
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|entry| {
|
.filter(|entry| {
|
||||||
if let Some(action) = &entry.action {
|
if let Some(action) = &entry.action {
|
||||||
|
@ -229,26 +229,24 @@ impl<T: Clone + Send + Sync> RunProvider<T> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
||||||
gui::sort_menu_items_alphabetically_honor_initial_score(&mut entries);
|
gui::sort_menu_items_alphabetically_honor_initial_score(&mut entries);
|
||||||
entries
|
entries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + Send + Sync> ItemProvider<T> for DRunProvider<T> {
|
impl<T: Clone + Send + Sync> ItemProvider<T> for DRunProvider<T> {
|
||||||
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<gui::MenuItem<T>>) {
|
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<MenuItem<T>>) {
|
||||||
if self.items.is_none() {
|
if self.items.is_none() {
|
||||||
self.items = Some(self.load().clone());
|
self.items = Some(self.load().clone());
|
||||||
}
|
}
|
||||||
(false, self.items.clone().unwrap())
|
(false, self.items.clone().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, std::option::Option<Vec<gui::MenuItem<T>>>) {
|
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, Option<Vec<MenuItem<T>>>) {
|
||||||
(false, None)
|
(false, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct FileItemProvider<T: Clone> {
|
struct FileItemProvider<T: Clone> {
|
||||||
last_result: Option<Vec<MenuItem<T>>>,
|
last_result: Option<Vec<MenuItem<T>>>,
|
||||||
|
@ -304,7 +302,7 @@ impl<T: Clone> FileItemProvider<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> ItemProvider<T> for FileItemProvider<T> {
|
impl<T: Clone> ItemProvider<T> for FileItemProvider<T> {
|
||||||
fn get_elements(&mut self, search: Option<&str>) -> (bool, Vec<gui::MenuItem<T>>) {
|
fn get_elements(&mut self, search: Option<&str>) -> (bool, Vec<MenuItem<T>>) {
|
||||||
let default_path = if let Some(home) = dirs::home_dir() {
|
let default_path = if let Some(home) = dirs::home_dir() {
|
||||||
home.display().to_string()
|
home.display().to_string()
|
||||||
} else {
|
} else {
|
||||||
|
@ -389,7 +387,7 @@ impl<T: Clone> ItemProvider<T> for FileItemProvider<T> {
|
||||||
(true, items)
|
(true, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, std::option::Option<Vec<gui::MenuItem<T>>>) {
|
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, Option<Vec<MenuItem<T>>>) {
|
||||||
(false, self.last_result.clone())
|
(false, self.last_result.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -435,11 +433,11 @@ impl<T: Clone> SshProvider<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> ItemProvider<T> for SshProvider<T> {
|
impl<T: Clone> ItemProvider<T> for SshProvider<T> {
|
||||||
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<gui::MenuItem<T>>) {
|
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<MenuItem<T>>) {
|
||||||
(false, self.elements.clone())
|
(false, self.elements.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, std::option::Option<Vec<gui::MenuItem<T>>>) {
|
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, Option<Vec<MenuItem<T>>>) {
|
||||||
(false, None)
|
(false, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,7 +475,7 @@ impl<T: Clone> MathProvider<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> ItemProvider<T> for MathProvider<T> {
|
impl<T: Clone> ItemProvider<T> for MathProvider<T> {
|
||||||
fn get_elements(&mut self, search: Option<&str>) -> (bool, Vec<gui::MenuItem<T>>) {
|
fn get_elements(&mut self, search: Option<&str>) -> (bool, Vec<MenuItem<T>>) {
|
||||||
if let Some(search_text) = search {
|
if let Some(search_text) = search {
|
||||||
let result = match meval::eval_str(search_text) {
|
let result = match meval::eval_str(search_text) {
|
||||||
Ok(result) => result.to_string(),
|
Ok(result) => result.to_string(),
|
||||||
|
@ -501,7 +499,7 @@ impl<T: Clone> ItemProvider<T> for MathProvider<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, std::option::Option<Vec<gui::MenuItem<T>>>) {
|
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, Option<Vec<MenuItem<T>>>) {
|
||||||
(false, None)
|
(false, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -529,11 +527,11 @@ impl DMenuProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ItemProvider<String> for DMenuProvider {
|
impl ItemProvider<String> for DMenuProvider {
|
||||||
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<gui::MenuItem<std::string::String>>) {
|
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<MenuItem<String>>) {
|
||||||
(false, self.items.clone())
|
(false, self.items.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sub_elements(&mut self, _: &MenuItem<String>) -> (bool, Option<Vec<gui::MenuItem<std::string::String>>>) {
|
fn get_sub_elements(&mut self, _: &MenuItem<String>) -> (bool, Option<Vec<MenuItem<String>>>) {
|
||||||
(false, None)
|
(false, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -569,7 +567,7 @@ impl AutoItemProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ItemProvider<AutoRunType> for AutoItemProvider {
|
impl ItemProvider<AutoRunType> for AutoItemProvider {
|
||||||
fn get_elements(&mut self, search_opt: Option<&str>) -> (bool, Vec<gui::MenuItem<AutoRunType>>) {
|
fn get_elements(&mut self, search_opt: Option<&str>) -> (bool, Vec<MenuItem<AutoRunType>>) {
|
||||||
if let Some(search) = search_opt {
|
if let Some(search) = search_opt {
|
||||||
let trimmed_search = search.trim();
|
let trimmed_search = search.trim();
|
||||||
if trimmed_search.is_empty() {
|
if trimmed_search.is_empty() {
|
||||||
|
@ -599,7 +597,7 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
|
||||||
fn get_sub_elements(
|
fn get_sub_elements(
|
||||||
&mut self,
|
&mut self,
|
||||||
item: &MenuItem<AutoRunType>,
|
item: &MenuItem<AutoRunType>,
|
||||||
) -> (bool, std::option::Option<Vec<gui::MenuItem<AutoRunType>>>) {
|
) -> (bool, Option<Vec<MenuItem<AutoRunType>>>) {
|
||||||
let (changed, items) = self.get_elements(Some(item.label.as_ref()));
|
let (changed, items) = self.get_elements(Some(item.label.as_ref()));
|
||||||
(changed, Some(items))
|
(changed, Some(items))
|
||||||
}
|
}
|
||||||
|
@ -631,7 +629,7 @@ pub fn d_run(config: &Config) -> Result<(), Error> {
|
||||||
///
|
///
|
||||||
/// 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 run(config: &Config) -> Result<(), Error> {
|
pub fn run(config: &Config) -> Result<(), Error> {
|
||||||
let provider = RunProvider::new(String::new());
|
let provider = RunProvider::new();
|
||||||
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();
|
||||||
|
|
||||||
|
@ -646,7 +644,6 @@ pub fn run(config: &Config) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Shows the auto mode
|
/// Shows the auto mode
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
|
@ -809,7 +806,6 @@ fn update_drun_cache_and_run<T: Clone>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn update_run_cache_and_run<T: Clone>(
|
fn update_run_cache_and_run<T: Clone>(
|
||||||
cache_path: Option<PathBuf>,
|
cache_path: Option<PathBuf>,
|
||||||
cache: &mut HashMap<String, i64>,
|
cache: &mut HashMap<String, i64>,
|
||||||
|
@ -836,7 +832,7 @@ fn update_run_cache_and_run<T: Clone>(
|
||||||
|
|
||||||
fn load_d_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
|
fn load_d_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
|
||||||
let cache_path = dirs::cache_dir().map(|x| x.join("worf-drun"));
|
let cache_path = dirs::cache_dir().map(|x| x.join("worf-drun"));
|
||||||
load_cache(cache_path)
|
load_cache(cache_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
|
fn load_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
|
||||||
|
@ -844,7 +840,6 @@ fn load_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
|
||||||
load_cache(cache_path)
|
load_cache(cache_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn load_cache(cache_path: Option<PathBuf>) -> (Option<PathBuf>, HashMap<String, i64>) {
|
fn load_cache(cache_path: Option<PathBuf>) -> (Option<PathBuf>, HashMap<String, i64>) {
|
||||||
let cache = {
|
let cache = {
|
||||||
if let Some(ref cache_path) = cache_path {
|
if let Some(ref cache_path) = cache_path {
|
||||||
|
|
Loading…
Add table
Reference in a new issue