diff options
author | LakoLiu <99976966+lakoliu@users.noreply.github.com> | 2023-10-13 09:26:09 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-13 09:26:09 +0200 |
commit | 03b910060da4171209dcc834f0065a0fbc038224 (patch) | |
tree | 2ec472aeebd7d12d9c9ceb43c73122e8a61f5aca /src | |
parent | 28a73d4af630398cfa0818bdefa57e50f76dc4f4 (diff) | |
parent | 14ae87d469555af8301356f99a11c2254edee1a4 (diff) | |
download | Furtherance-03b910060da4171209dcc834f0065a0fbc038224.tar.zst |
Merge pull request #118 from jlledom/autocomplete
Implement task name autocompletion
Diffstat (limited to 'src')
-rw-r--r-- | src/database.rs | 35 | ||||
-rw-r--r-- | src/ui/window.rs | 32 |
2 files changed, 65 insertions, 2 deletions
diff --git a/src/database.rs b/src/database.rs index 7050cbb..2f97d2d 100644 --- a/src/database.rs +++ b/src/database.rs @@ -38,6 +38,12 @@ pub struct Task { pub tags: String, } +impl ToString for Task { + fn to_string(&self) -> String { + format!("{} #{}", self.task_name, self.tags) + } +} + #[derive( Debug, Clone, @@ -343,6 +349,35 @@ pub fn get_list_by_id(id_list: Vec<i32>) -> Result<Vec<Task>, rusqlite::Error> { Ok(tasks_vec) } +pub fn get_list_by_name_and_tags(task_name: String, tag_list: Vec<String>) -> Result<Vec<Task>, rusqlite::Error> { + let conn = Connection::open(get_directory())?; + + let name_param = format!("%{}%", task_name); + let tag_list_params: Vec<String> = tag_list.iter().map(|tag| format!("%{}%", tag)).collect(); + + let mut sql_query = String::from("SELECT * FROM tasks WHERE lower(task_name) LIKE lower(?)"); + tag_list_params.iter().for_each(|_| sql_query.push_str(" AND lower(tags) LIKE lower(?)")); + sql_query.push_str(" ORDER BY task_name"); + + let mut query = conn.prepare(sql_query.as_str())?; + query.raw_bind_parameter(1, name_param)?; + for (i, tag) in tag_list_params.iter().enumerate() { + query.raw_bind_parameter(i + 2, tag)?; + } + + let tasks_vec = query.raw_query().mapped(|row| { + Ok(Task { + id: row.get(0)?, + task_name: row.get(1)?, + start_time: row.get(2)?, + stop_time: row.get(3)?, + tags: row.get(4)?, + }) + }).map(|task_item| task_item.unwrap()).collect(); + + Ok(tasks_vec) +} + pub fn check_for_tasks() -> Result<String> { let conn = Connection::open(get_directory())?; diff --git a/src/ui/window.rs b/src/ui/window.rs index 2270711..8972ed9 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -115,6 +115,8 @@ glib::wrapper! { } impl FurtheranceWindow { + const MIN_PREFIX_LENGTH: i32 = 3; + pub fn new(app: &Application) -> Self { glib::Object::builder() .property("application", Some(app)) @@ -182,6 +184,13 @@ impl FurtheranceWindow { imp.start_button.set_sensitive(false); imp.start_button.add_css_class("suggested-action"); self.refresh_timer(); + + let task_autocomplete = gtk::EntryCompletion::new(); + task_autocomplete.set_text_column(0); + task_autocomplete.set_minimum_key_length(FurtheranceWindow::MIN_PREFIX_LENGTH); + task_autocomplete.set_match_func(|_ac, _s, _it| { true }); + imp.task_input.set_completion(Some(&task_autocomplete)); + imp.task_input.grab_focus(); if settings_manager::get_bool("autosave") { @@ -199,12 +208,19 @@ impl FurtheranceWindow { .connect_changed(clone!(@weak self as this => move |task_input| { let imp2 = imp::FurtheranceWindow::from_obj(&this); let task_input_text = task_input.text(); - let split_tags: Vec<&str> = task_input_text.trim().split('#').collect(); - if split_tags[0].trim().is_empty() { + let mut split_tags: Vec<String> = task_input_text.split('#').map(|tag| String::from(tag.trim())).collect(); + let task_name = split_tags.remove(0); + if task_name.is_empty() { imp2.start_button.set_sensitive(false); } else { imp2.start_button.set_sensitive(true); } + + if task_input.text().len() >= FurtheranceWindow::MIN_PREFIX_LENGTH.try_into().unwrap() { + let task_autocomplete = task_input.completion().unwrap(); + let model = Self::update_list_model(task_name.to_string(), split_tags).unwrap(); + task_autocomplete.set_model(Some(&model)); + } })); imp.start_button.connect_clicked(clone!(@weak self as this => move |button| { @@ -528,6 +544,18 @@ impl FurtheranceWindow { imp.task_input.set_activates_default(true); } + fn update_list_model(task_name: String, tag_list: Vec<String>) -> Result<gtk::ListStore, anyhow::Error> { + let col_types: [glib::Type; 1] = [glib::Type::STRING]; + let mut task_list = database::get_list_by_name_and_tags(task_name, tag_list)?; + task_list.dedup_by(|a, b| a.task_name == b.task_name && a.tags == b.tags); + let store = gtk::ListStore::new(&col_types); + + for task in task_list { + store.set(&store.append(), &[(0, &task.to_string())]); + } + Ok(store) + } + fn get_idle_time(&self) -> Result<u64, Box<dyn std::error::Error>> { let c = Connection::new_session()?; |