about summary refs log tree commit diff
path: root/src/ui
diff options
context:
space:
mode:
authorRicky Kresslein <rk@lakoliu.com>2022-04-22 10:30:19 +0300
committerRicky Kresslein <rk@lakoliu.com>2022-04-22 10:30:19 +0300
commite3bb347a12ad929619a51b37f0ca48dcfe46b731 (patch)
tree643d2b973b003e91762af519620e34065953da64 /src/ui
parent22b05e7c934aee734005e7ba601968334467005c (diff)
downloadFurtherance-e3bb347a12ad929619a51b37f0ca48dcfe46b731.tar.zst
Add tags to tasks (Issue #8)
Diffstat (limited to 'src/ui')
-rwxr-xr-xsrc/ui/preferences_window.rs13
-rwxr-xr-xsrc/ui/task_details.rs39
-rwxr-xr-xsrc/ui/task_row.rs23
-rwxr-xr-xsrc/ui/tasks_group.rs6
-rwxr-xr-xsrc/ui/window.rs36
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."));
         }
     }
 }