diff options
author | Ricky Kresslein <rk@lakoliu.com> | 2022-04-22 10:30:19 +0300 |
---|---|---|
committer | Ricky Kresslein <rk@lakoliu.com> | 2022-04-22 10:30:19 +0300 |
commit | e3bb347a12ad929619a51b37f0ca48dcfe46b731 (patch) | |
tree | 643d2b973b003e91762af519620e34065953da64 /src/ui | |
parent | 22b05e7c934aee734005e7ba601968334467005c (diff) | |
download | Furtherance-e3bb347a12ad929619a51b37f0ca48dcfe46b731.tar.zst |
Add tags to tasks (Issue #8)
Diffstat (limited to 'src/ui')
-rwxr-xr-x | src/ui/preferences_window.rs | 13 | ||||
-rwxr-xr-x | src/ui/task_details.rs | 39 | ||||
-rwxr-xr-x | src/ui/task_row.rs | 23 | ||||
-rwxr-xr-x | src/ui/tasks_group.rs | 6 | ||||
-rwxr-xr-x | src/ui/window.rs | 36 |
5 files changed, 99 insertions, 18 deletions
diff --git a/src/ui/preferences_window.rs b/src/ui/preferences_window.rs index 1955d09..47a7adc 100755 --- a/src/ui/preferences_window.rs +++ b/src/ui/preferences_window.rs @@ -55,6 +55,8 @@ mod imp { pub show_seconds_switch: TemplateChild<gtk::Switch>, #[template_child] pub show_daily_sums_switch: TemplateChild<gtk::Switch>, + #[template_child] + pub show_tags_switch: TemplateChild<gtk::Switch>, } #[glib::object_subclass] @@ -167,6 +169,12 @@ impl FurPreferencesWindow { "active" ); + settings_manager::bind_property( + "show-tags", + &*imp.show_tags_switch, + "active" + ); + imp.dark_theme_switch.connect_active_notify(move |_|{ let app = FurtheranceApplication::default(); app.update_light_dark(); @@ -191,6 +199,11 @@ impl FurPreferencesWindow { let window = FurtheranceWindow::default(); window.reset_history_box(); }); + + imp.show_tags_switch.connect_active_notify(move |_|{ + let window = FurtheranceWindow::default(); + window.reset_history_box(); + }); } } diff --git a/src/ui/task_details.rs b/src/ui/task_details.rs index d96a993..e462eb4 100755 --- a/src/ui/task_details.rs +++ b/src/ui/task_details.rs @@ -20,6 +20,7 @@ use glib::clone; use gtk::subclass::prelude::*; use gtk::{glib, prelude::*, CompositeTemplate}; use chrono::{DateTime, NaiveDateTime, Local, ParseError, offset::TimeZone}; +use itertools::Itertools; use crate::FurtheranceApplication; use crate::ui::FurtheranceWindow; @@ -172,6 +173,14 @@ impl FurTaskDetails { let vert_box = gtk::Box::new(gtk::Orientation::Vertical, 5); let task_name_edit = gtk::Entry::new(); task_name_edit.set_text(&task.task_name); + let task_tags_edit = gtk::Entry::new(); + let tags_placeholder = format!("#{}", &gettext("Tags")); + task_tags_edit.set_placeholder_text(Some(&tags_placeholder)); + let mut task_tags: String = task.tags.clone(); + if !task.tags.trim().is_empty() { + task_tags = format!("#{}", task.tags); + task_tags_edit.set_text(&task_tags); + } let labels_box = gtk::Box::new(gtk::Orientation::Horizontal, 5); labels_box.set_homogeneous(true); let start_label = gtk::Label::new(Some(&gettext("Start"))); @@ -220,6 +229,7 @@ impl FurTaskDetails { delete_task_btn.set_halign(gtk::Align::End); vert_box.append(&task_name_edit); + vert_box.append(&task_tags_edit); labels_box.append(&start_label); labels_box.append(&stop_label); times_box.append(&start_time_edit); @@ -276,9 +286,10 @@ impl FurTaskDetails { dialog.connect_response( clone!(@strong dialog, - @strong task.task_name as name - @strong task.start_time as start_time - @strong task.stop_time as stop_time => move |_ , resp| { + @strong task.task_name as name, + @strong task.start_time as start_time, + @strong task.stop_time as stop_time, + @strong task.tags as tags => move |_ , resp| { if resp == gtk::ResponseType::Ok { instructions.set_visible(false); time_error.set_visible(false); @@ -333,7 +344,7 @@ impl FurTaskDetails { database::update_stop_time(task.id, new_stop_time_edited.clone()) .expect("Failed to update stop time."); database::update_start_time(task.id, new_start_time_edited.clone()) - .expect("Failed to update stop time."); + .expect("Failed to update start time."); } } else { let old_start_time = DateTime::parse_from_rfc3339(&start_time); @@ -354,7 +365,22 @@ impl FurTaskDetails { } if task_name_edit.text() != name { database::update_task_name(task.id, task_name_edit.text().to_string()) - .expect("Failed to update start time."); + .expect("Failed to update task name."); + } + + if task_tags_edit.text() != task_tags { + let new_tags = task_tags_edit.text(); + let mut split_tags: Vec<&str> = new_tags.trim().split("#").collect(); + split_tags = split_tags.iter().map(|x| x.trim()).collect(); + // Don't allow empty tags + split_tags.retain(|&x| !x.trim().is_empty()); + // Handle duplicate tags before they are saved + split_tags = split_tags.into_iter().unique().collect(); + // Lowercase tags + let lower_tags: Vec<String> = split_tags.iter().map(|x| x.to_lowercase()).collect(); + let new_tag_list = lower_tags.join(" #"); + database::update_tags(task.id, new_tag_list) + .expect("Failed to update tags."); } if start_successful && !stop_successful { @@ -367,7 +393,6 @@ impl FurTaskDetails { time_error.set_visible(true); do_not_close = true; } - } if !do_not_close { @@ -375,7 +400,6 @@ impl FurTaskDetails { dialog.close(); } - } else { // If Cancel, close dialog and do nothing. dialog.close(); @@ -383,7 +407,6 @@ impl FurTaskDetails { }), ); - dialog.show(); })); diff --git a/src/ui/task_row.rs b/src/ui/task_row.rs index fe8db68..0e46d81 100755 --- a/src/ui/task_row.rs +++ b/src/ui/task_row.rs @@ -35,8 +35,12 @@ mod imp { #[template(resource = "/com/lakoliu/Furtherance/gtk/task_row.ui")] pub struct FurTaskRow { #[template_child] + pub row_box: TemplateChild<gtk::Box>, + #[template_child] pub task_name_label: TemplateChild<gtk::Label>, #[template_child] + pub task_tags_label: TemplateChild<gtk::Label>, + #[template_child] pub total_time_label: TemplateChild<gtk::Label>, pub tasks: Lazy<Mutex<Vec<Task>>>, @@ -102,16 +106,27 @@ impl FurTaskRow { for task in task_list.clone() { imp.tasks.lock().unwrap().push(task); } - let task_name_text = &task_list[0].task_name; + + // Display task's name imp.task_name_label.set_text(&imp.tasks.lock().unwrap()[0].task_name); + // Display task's tags + if task_list[0].tags.trim().is_empty() || !settings_manager::get_bool("show-tags") { + imp.task_tags_label.hide(); + } else { + let task_tags = format!("#{}", task_list[0].tags); + imp.task_tags_label.set_text(&task_tags); + imp.row_box.set_margin_top(5); + imp.row_box.set_margin_bottom(5); + } + // Create right-click gesture let gesture = gtk::GestureClick::new(); gesture.set_button(gtk::gdk::ffi::GDK_BUTTON_SECONDARY as u32); - gesture.connect_pressed(clone!(@strong task_name_text => move |gesture, _, _, _| { + gesture.connect_pressed(clone!(@strong task_list => move |gesture, _, _, _| { gesture.set_state(gtk::EventSequenceState::Claimed); let window = FurtheranceWindow::default(); - window.duplicate_task(task_name_text.to_string()); + window.duplicate_task(task_list[0].clone()); })); self.add_controller(&gesture); @@ -135,7 +150,7 @@ impl FurTaskRow { if !settings_manager::get_bool("show-seconds") { total_time_str = format!("{:02}:{:02}", h, m); } - + // Display task's total time imp.total_time_label.set_text(&total_time_str); } diff --git a/src/ui/tasks_group.rs b/src/ui/tasks_group.rs index d6da39c..2453991 100755 --- a/src/ui/tasks_group.rs +++ b/src/ui/tasks_group.rs @@ -20,6 +20,7 @@ use gtk::{glib, prelude::*}; use crate::ui::FurTaskRow; use crate::database; +use crate::settings_manager; mod imp { use super::*; @@ -85,7 +86,10 @@ impl FurTasksGroup { for task in &tasks { unique = true; for i in 0..tasks_by_name.len() { - if tasks_by_name[i][0].task_name == task.task_name { + if tasks_by_name[i][0].task_name == task.task_name + && ( ( settings_manager::get_bool("show-tags") + && tasks_by_name[i][0].tags == task.tags ) || + !settings_manager::get_bool("show-tags") ) { tasks_by_name[i].push(task.clone()); unique = false; } diff --git a/src/ui/window.rs b/src/ui/window.rs index 9052e34..964cf64 100755 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -26,6 +26,7 @@ use std::rc::Rc; use std::cell::RefCell; use chrono::{DateTime, Local, Duration as ChronDur}; use dbus::blocking::Connection; +use itertools::Itertools; use crate::ui::FurHistoryBox; use crate::FurtheranceApplication; @@ -119,6 +120,7 @@ impl FurtheranceWindow { } } + // TODO remove function, just use set_sensitive fn activate_task_input(&self, sensitive: bool) { // Deactivate task_input while timer is running let imp = imp::FurtheranceWindow::from_instance(self); @@ -135,7 +137,23 @@ impl FurtheranceWindow { *imp.subtract_idle.lock().unwrap() = false; } - let _ = database::db_write(&imp.task_input.text().trim(), start_time, stop_time); + // let task_input_text = imp.task_input.text().trim(); + let task_input_text = imp.task_input.text(); + let mut split_tags: Vec<&str> = task_input_text.trim().split("#").collect(); + // Remove task name from tags list + let task_name = *split_tags.first().unwrap(); + split_tags.remove(0); + // Trim whitespace around each tag + split_tags = split_tags.iter().map(|x| x.trim()).collect(); + // Don't allow empty tags + split_tags.retain(|&x| !x.trim().is_empty()); + // Handle duplicate tags before they are ever saved + split_tags = split_tags.into_iter().unique().collect(); + // Lowercase tags + let lower_tags: Vec<String> = split_tags.iter().map(|x| x.to_lowercase()).collect(); + let tag_list = lower_tags.join(" #"); + + let _ = database::db_write(task_name.trim(), start_time, stop_time, tag_list); imp.task_input.set_text(""); imp.history_box.create_tasks_page(); } @@ -178,7 +196,9 @@ impl FurtheranceWindow { imp.task_input.connect_changed(clone!(@weak self as this => move |task_input| { let imp2 = imp::FurtheranceWindow::from_instance(&this); - if task_input.text().trim().is_empty() { + 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() { imp2.start_button.set_sensitive(false); } else { imp2.start_button.set_sensitive(true); @@ -337,13 +357,19 @@ impl FurtheranceWindow { *imp.subtract_idle.lock().unwrap() = val; } - pub fn duplicate_task(&self, task_name_text: String) { + pub fn duplicate_task(&self, task: database::Task) { let imp = imp::FurtheranceWindow::from_instance(self); if !*imp.running.lock().unwrap() { - imp.task_input.set_text(&task_name_text); + let task_text: String; + if task.tags.trim().is_empty() { + task_text = task.task_name; + } else { + task_text = format!("{} #{}", task.task_name, task.tags); + } + imp.task_input.set_text(&task_text); imp.start_button.emit_clicked(); } else { - self.display_toast("Stop the timer to duplicate a task."); + self.display_toast(&gettext("Stop the timer to duplicate a task.")); } } } |