diff --git a/Cargo.toml b/Cargo.toml index e0879ec..426c635 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -iced = "0.13.1" \ No newline at end of file +iced = { version = "0.13.1", features = ["advanced"] } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f8a3fba..8c5ed9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,14 @@ +use iced::keyboard::key; use iced::widget::{button, center, checkbox, column, row, scrollable, text_input, Text}; use iced::window::Settings; -use iced::{Element, Length, Size, Theme}; -use std::collections::HashMap; +use iced::{event, keyboard, widget, Element, Event, Length, Size, Subscription, Task, Theme}; struct TaskData { checked: bool, value: String, edit: bool, can_update: bool, + can_delete: bool, } fn main() -> iced::Result { @@ -18,6 +19,7 @@ fn main() -> iced::Result { }; iced::application("To Do App", Todo::update, Todo::view) + .subscription(Todo::subscription) .theme(Todo::theme) .window(settings) .run() @@ -27,8 +29,7 @@ fn main() -> iced::Result { struct Todo { new_task: String, updated_task: String, - tasks: HashMap, - task_id: usize, + tasks: Vec, completed_tasks: usize, } @@ -40,10 +41,12 @@ enum Message { DeleteTask(usize), DeleteAll, ContentUpdated(bool, String), + Event(Event), + TaskPush(Option), } impl Todo { - fn update(&mut self, message: Message) { + fn update(&mut self, message: Message) -> Task { match message { Message::ContentUpdated(new, value) => { if new { @@ -51,63 +54,97 @@ impl Todo { } else { self.updated_task = value } + + Task::none() } Message::AddTask => { - if self.new_task.is_empty() { return; } + if self.new_task.is_empty() { return Task::none(); } let data = TaskData { checked: false, value: self.new_task.to_string(), edit: false, can_update: true, + can_delete: true, }; - self.tasks.insert(self.task_id, data); - self.task_id += 1; + self.tasks.push(data); + self.new_task = String::new(); - // temp disable for testing fixme - //self.new_task = String::new(); + Task::none() }, Message::DeleteTask(id) => { - // todo maybe some better checks - if let Some(t) = self.tasks.get_mut(&id) { - if t.checked { - self.completed_tasks -= 1; - } + if self.tasks[id].checked { + self.completed_tasks -= 1; } + self.tasks.remove(id); - if let Some(t) = self.tasks.remove(&id) { - println!("Removed {}", t.value); - } + Task::none() }, Message::CheckTask(choice, id) => { - if let Some(t) = self.tasks.get_mut(&id) { - self.completed_tasks = if choice { self.completed_tasks + 1 } else { self.completed_tasks - 1 }; - t.checked = choice; - } + self.completed_tasks = if choice { self.completed_tasks + 1 } else { self.completed_tasks - 1 }; + self.tasks[id].checked = choice; + + Task::none() }, Message::EditTask(id) => { - let mut set_update = None; - - if let Some(t) = self.tasks.get_mut(&id) { - if t.edit == true { - t.value = self.updated_task.clone(); - set_update = Some(true); - self.updated_task = String::new(); - } else { - set_update = Some(false); + let set_update; + + if self.tasks[id].edit { + if self.updated_task.is_empty() { + return Task::none(); } - - t.edit = !t.edit; - } - - if let Some(t) = set_update { - self.set_other_update(id, t); + + self.tasks[id].value = self.updated_task.clone(); + set_update = true; + self.updated_task = String::new(); + } else { + set_update = false; } + + self.tasks[id].edit = !self.tasks[id].edit; + self.set_other_update(id, set_update); + + widget::focus_previous() }, Message::DeleteAll => { + self.new_task = String::from(""); + self.updated_task = String::from(""); self.tasks.clear(); self.completed_tasks = 0; + + Task::none() + }, + Message::Event(event) => match event { + Event::Keyboard(keyboard::Event::KeyPressed { + key: keyboard::Key::Named(key::Named::Tab), + modifiers, + .. + }) => { + if modifiers.shift() { + widget::focus_previous() + } else { + widget::focus_next() + } + } + Event::Keyboard(keyboard::Event::KeyPressed { + key: keyboard::Key::Named(key::Named::Delete), + modifiers, + .. + }) => { + if modifiers.control() { + Task::perform(async move { Message::DeleteAll }, |result| result) + } else { + Task::none() + } + } + _ => Task::none() + }, + Message::TaskPush(option_id) => { + match option_id { + Some(i) => Task::perform(async move { Message::EditTask(i) }, |result| result), + None => Task::perform(async { Message::AddTask }, |result| result), + } }, } } @@ -116,7 +153,8 @@ impl Todo { let input = text_input("Enter new task", &self.new_task) .width(300) .size(25) - .on_input(|str| Message::ContentUpdated(true, str)); + .on_input(|str| Message::ContentUpdated(true, str)) + .on_submit(Message::TaskPush(None)); let add_btn = button( Text::new("Add") @@ -140,18 +178,23 @@ impl Todo { let new_task = row![input, add_btn, clear_btn].spacing(10); let mut saved_tasks = column![]; - for task in &self.tasks { - let chk = checkbox("", task.1.checked).size(25).on_toggle(|b| Message::CheckTask(b, *task.0)); - let label = Text::new(&task.1.value).width(200); - let input = text_input("Enter new name", &self.updated_task).width(200).on_input(|str| Message::ContentUpdated(false, str)); - let mut edit = button(if task.1.edit { "save" } else { "edit" }).width(Length::Shrink); - if task.1.can_update { - edit = edit.on_press(Message::EditTask(*task.0)); + for (i, task) in self.tasks.iter().enumerate() { + let chk = checkbox("", task.checked).size(25).on_toggle(move |b| Message::CheckTask(b, i)); + let label = Text::new(&task.value).width(200); + let input = text_input("Enter new name", &self.updated_task) + .width(200) + .on_input(|str| Message::ContentUpdated(false, str)) + .on_submit(Message::TaskPush(Some(i))); + let mut edit = button(if task.edit { "save" } else { "edit" }).width(Length::Shrink); + if task.can_update { + edit = edit.on_press(Message::EditTask(i)); + } + let mut delete = button("delete").width(Length::Shrink); + if task.can_delete { + delete = delete.on_press(Message::DeleteTask(i)); } - let delete = button("delete").width(Length::Shrink).on_press(Message::DeleteTask(*task.0)); - let mut task_line = row![chk]; - task_line = if task.1.edit { task_line.push(input) } else { task_line.push(label) }; + task_line = if task.edit { task_line.push(input) } else { task_line.push(label) }; task_line = task_line.push(edit).push(delete).spacing(10).padding([5, 10]); saved_tasks = saved_tasks.push(task_line); @@ -169,12 +212,17 @@ impl Todo { fn theme(&self) -> Theme { Theme::Dark } + + fn subscription(&self) -> Subscription { + event::listen().map(Message::Event) + } fn set_other_update(&mut self, id: usize, enable: bool) { - for task in self.tasks.iter_mut() { - if id != self.task_id { - task.1.can_update = enable; + for (i, task) in self.tasks.iter_mut().enumerate() { + if id != i { + task.can_update = enable; } + task.can_delete = enable; } } } \ No newline at end of file