mirror of
https://github.com/DorianRudolph/sirula.git
synced 2024-11-25 21:47:13 +03:00
Add option to sort by most frequently used
This commit is contained in:
parent
bf216d7e45
commit
3714e5e0be
@ -41,10 +41,13 @@ exclude = [] # list of regexes for excluded app ids (name of the .desktop file)
|
||||
# use "" to disable launching commands
|
||||
command_prefix = ":"
|
||||
|
||||
frequent_first = true # sort matches of equal quality by most frequently used
|
||||
recent_first = true # sort matches of equal quality by most recently used
|
||||
# when both frequent_first and recent_first are set,
|
||||
# sorting is by frequency first, and recency is used to break ties in frequency
|
||||
|
||||
# term_command = "alacritty -e {}" # command for applications run in terminal (default uses "$TERMINAL -e")
|
||||
|
||||
# specify name overrides (id is the name of the desktop file)
|
||||
[name_overrides]
|
||||
# id = "name\rextra"
|
||||
# id = "name\rextra"
|
||||
|
@ -27,7 +27,7 @@ use gio::AppInfo;
|
||||
use glib::shell_unquote;
|
||||
use crate::locale::string_collate;
|
||||
|
||||
use super::{consts::*, Config, Field, History};
|
||||
use super::{consts::*, Config, Field, HistoryData};
|
||||
use regex::RegexSet;
|
||||
|
||||
#[derive(Eq)]
|
||||
@ -38,7 +38,7 @@ pub struct AppEntry {
|
||||
pub info: AppInfo,
|
||||
pub label: Label,
|
||||
pub score: i64,
|
||||
pub last_used: u64,
|
||||
pub history: HistoryData
|
||||
}
|
||||
|
||||
impl AppEntry {
|
||||
@ -73,7 +73,7 @@ impl AppEntry {
|
||||
|
||||
add_attrs(&attr_list, &config.markup_default, 0, self.display_string.len() as u32);
|
||||
if let Some((lo, hi)) = self.extra_range {
|
||||
add_attrs(&attr_list, &config.markup_extra, lo, hi);
|
||||
add_attrs(&attr_list, &config.markup_extra, lo, hi);
|
||||
}
|
||||
self.label.set_attributes(Some(&attr_list));
|
||||
}
|
||||
@ -81,15 +81,18 @@ impl AppEntry {
|
||||
|
||||
impl PartialEq for AppEntry {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.score.eq(&other.score) && self.last_used.eq(&other.last_used)
|
||||
self.score.eq(&other.score) && self.history.eq(&other.history)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for AppEntry {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match self.score.cmp(&other.score) {
|
||||
Ordering::Equal => match self.last_used.cmp(&other.last_used) {
|
||||
Ordering::Equal => string_collate(&self.display_string, &other.display_string),
|
||||
Ordering::Equal => match self.history.usage_count.cmp(&other.history.usage_count) {
|
||||
Ordering::Equal => match self.history.last_used.cmp(&other.history.last_used) {
|
||||
Ordering::Equal => string_collate(&self.display_string, &other.display_string),
|
||||
ord => ord.reverse()
|
||||
}
|
||||
ord => ord.reverse()
|
||||
}
|
||||
ord => ord.reverse()
|
||||
@ -129,7 +132,8 @@ fn add_attrs(list: &AttrList, attrs: &Vec<Attribute>, start: u32, end: u32) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_entries(config: &Config, history: &History) -> HashMap<ListBoxRow, AppEntry> {
|
||||
pub fn load_entries(config: &Config, history: &HashMap<String, HistoryData>)
|
||||
-> HashMap<ListBoxRow, AppEntry> {
|
||||
let mut entries = HashMap::new();
|
||||
let icon_theme = IconTheme::default().unwrap();
|
||||
let apps = gio::AppInfo::all();
|
||||
@ -151,14 +155,14 @@ pub fn load_entries(config: &Config, history: &History) -> HashMap<ListBoxRow, A
|
||||
continue
|
||||
}
|
||||
|
||||
let (display_string, extra_range) = if let Some(name)
|
||||
let (display_string, extra_range) = if let Some(name)
|
||||
= get_app_field(&app, Field::Id).and_then(|id| config.name_overrides.get(&id)) {
|
||||
let i = name.find('\r');
|
||||
(name.replace('\r', " "), i.map(|i| (i as u32 +1, name.len() as u32)))
|
||||
} else {
|
||||
let extra = config.extra_field.get(0).and_then(|f| get_app_field(&app, *f));
|
||||
match extra {
|
||||
Some(e) if (!config.hide_extra_if_contained || !name.to_lowercase().contains(&e.to_lowercase())) => (format!("{} {}", name, e),
|
||||
Some(e) if (!config.hide_extra_if_contained || !name.to_lowercase().contains(&e.to_lowercase())) => (format!("{} {}", name, e),
|
||||
Some((name.len() as u32 + 1, name.len() as u32 + 1 + e.len() as u32))),
|
||||
_ => (name, None)
|
||||
}
|
||||
@ -202,9 +206,9 @@ pub fn load_entries(config: &Config, history: &History) -> HashMap<ListBoxRow, A
|
||||
row.add(&hbox);
|
||||
row.style_context().add_class(APP_ROW_CLASS);
|
||||
|
||||
let last_used = if config.recent_first {
|
||||
history.last_used.get(&id).copied().unwrap_or_default()
|
||||
} else { 0 };
|
||||
let history_data = history.get(&id).copied().unwrap_or_default();
|
||||
let last_used = if config.recent_first { history_data.last_used } else { 0 };
|
||||
let usage_count = if config.frequent_first { history_data.usage_count } else { 0 };
|
||||
|
||||
let app_entry = AppEntry {
|
||||
display_string,
|
||||
@ -213,7 +217,7 @@ pub fn load_entries(config: &Config, history: &History) -> HashMap<ListBoxRow, A
|
||||
info: app,
|
||||
label,
|
||||
score: 100,
|
||||
last_used,
|
||||
history: HistoryData { last_used, usage_count }
|
||||
};
|
||||
app_entry.set_markup(config);
|
||||
entries.insert(row, app_entry);
|
||||
|
@ -50,6 +50,7 @@ make_config!(Config {
|
||||
markup_highlight: Vec<Attribute> = (parse_attributes("foreground=\"red\" underline=\"double\"").unwrap()) "markup_highlight" [deserialize_with = "deserialize_markup"],
|
||||
markup_extra: Vec<Attribute> = (parse_attributes("font_style=\"italic\" font_size=\"smaller\"").unwrap()) "markup_extra" [deserialize_with = "deserialize_markup"],
|
||||
exclusive: bool = (true) "exclusive",
|
||||
frequent_first: bool = (true) "frequent_first",
|
||||
recent_first: bool = (true) "recent_first",
|
||||
icon_size: i32 = (64) "icon_size",
|
||||
lines: i32 = (2) "lines",
|
||||
|
@ -5,31 +5,42 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use super::util::get_history_file;
|
||||
use std::fs::File;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct History {
|
||||
pub last_used: HashMap<String, u64>
|
||||
#[derive(Copy, Clone, Default, Eq, Deserialize, Serialize)]
|
||||
pub struct HistoryData {
|
||||
pub last_used : u64,
|
||||
pub usage_count : u32
|
||||
}
|
||||
|
||||
impl History {
|
||||
pub fn load() -> History {
|
||||
match get_history_file(false) {
|
||||
Some(file) => {
|
||||
let config_str = std::fs::read_to_string(file).expect("Cannot read history file");
|
||||
toml::from_str(&config_str).expect("Cannot parse config: {}")
|
||||
},
|
||||
_ => History { last_used: HashMap::new() }
|
||||
}
|
||||
impl PartialEq for HistoryData {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.last_used.eq(&other.last_used) && self.usage_count.eq(&other.usage_count)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_history() -> HashMap<String, HistoryData> {
|
||||
match get_history_file(false) {
|
||||
Some(file) => {
|
||||
let config_str = std::fs::read_to_string(file).expect("Cannot read history file");
|
||||
toml::from_str(&config_str).expect("Cannot parse config: {}")
|
||||
},
|
||||
_ => HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_history(history: &HashMap<String, HistoryData>) {
|
||||
let file = get_history_file(true).expect("Cannot create history file or cache directory");
|
||||
let mut file = File::create(file).expect("Cannot open history file for writing");
|
||||
let s = toml::to_string(history).unwrap();
|
||||
file.write_all(s.as_bytes()).expect("Cannot write to history file");
|
||||
}
|
||||
|
||||
pub fn update_history(history: &mut HashMap<String, HistoryData>, id: &str) {
|
||||
let epoch = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
|
||||
let usage_count;
|
||||
match history.get(&id.to_string()) {
|
||||
Some(history_match) => { usage_count = history_match.usage_count + 1 },
|
||||
None => { usage_count = 1 }
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
let file = get_history_file(true).expect("Cannot create history file or cache directory");
|
||||
let mut file = File::create(file).expect("Cannot open history file for writing");
|
||||
let s = toml::to_string(self).unwrap();
|
||||
file.write_all(s.as_bytes()).expect("Cannot write to history file");
|
||||
}
|
||||
|
||||
pub fn update(&mut self, id: &str) {
|
||||
let epoch = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
|
||||
self.last_used.insert(id.to_string(), epoch.as_secs());
|
||||
}
|
||||
}
|
||||
history.insert( id.to_string(), HistoryData{ last_used: epoch.as_secs(), usage_count } );
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ fn app_startup(application: >k::Application) {
|
||||
let listbox = gtk::ListBoxBuilder::new().name(LISTBOX_NAME).build();
|
||||
scroll.add(&listbox);
|
||||
|
||||
let history = Rc::new(RefCell::new(History::load()));
|
||||
let history = Rc::new(RefCell::new(load_history()));
|
||||
let entries = Rc::new(RefCell::new(load_entries(&config, &history.borrow())));
|
||||
|
||||
for (row, _) in &entries.borrow() as &HashMap<ListBoxRow, AppEntry> {
|
||||
@ -155,8 +155,8 @@ fn app_startup(application: >k::Application) {
|
||||
launch_app(&e.info, term_command.as_deref());
|
||||
|
||||
let mut history = history.borrow_mut();
|
||||
history.update(e.info.id().unwrap().as_str());
|
||||
history.save();
|
||||
update_history(&mut history, e.info.id().unwrap().as_str());
|
||||
save_history(&history);
|
||||
|
||||
window.close();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user