combobox initial implementation
This commit is contained in:
parent
33207c2620
commit
68411dc657
269
src/todo.rs
269
src/todo.rs
@ -1,35 +1,41 @@
|
|||||||
use iced::{event, keyboard, widget, Center, Element, Event, Length, Subscription, Task, Theme};
|
|
||||||
use iced::keyboard::key;
|
|
||||||
use iced::widget::{button, center, checkbox, column, row, scrollable, text_input, Space, Text};
|
|
||||||
use iced::widget::text::danger;
|
|
||||||
use rusqlite::Connection;
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
use iced::keyboard::key;
|
||||||
|
use iced::widget::text::danger;
|
||||||
|
use iced::widget::{
|
||||||
|
button, center, checkbox, column, combo_box, row, scrollable, text_input, Text,
|
||||||
|
};
|
||||||
|
use iced::{event, keyboard, widget, Center, Element, Event, Length, Subscription, Task, Theme};
|
||||||
|
use rusqlite::Connection;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TaskColumn {
|
pub struct TaskColumn {
|
||||||
pub(crate) id: usize,
|
pub(crate) id: usize,
|
||||||
pub(crate) checked: bool,
|
pub(crate) checked: bool,
|
||||||
pub(crate) value: String,
|
pub(crate) value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TaskData {
|
pub struct TaskData {
|
||||||
db_id: usize,
|
db_id: usize,
|
||||||
checked: bool,
|
checked: bool,
|
||||||
value: String,
|
value: String,
|
||||||
edit: bool,
|
edit: bool,
|
||||||
can_update: bool,
|
can_update: bool,
|
||||||
can_delete: bool,
|
can_delete: bool,
|
||||||
already_exists: bool,
|
already_exists: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Todo {
|
pub(crate) struct Todo {
|
||||||
new_task: String,
|
new_task: String,
|
||||||
updated_task: String,
|
updated_task: String,
|
||||||
tasks: Vec<TaskData>,
|
tasks: Vec<TaskData>,
|
||||||
completed_tasks: usize,
|
completed_tasks: usize,
|
||||||
local_storage: bool,
|
local_storage: bool,
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
select_all: bool,
|
select_all: bool,
|
||||||
|
task_list_input: String,
|
||||||
|
task_list_state: combo_box::State<String>,
|
||||||
|
task_list: Vec<String>,
|
||||||
|
current_task_group: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -39,11 +45,13 @@ pub enum Message {
|
|||||||
EditTask(usize),
|
EditTask(usize),
|
||||||
DeleteTask(usize),
|
DeleteTask(usize),
|
||||||
DeleteAll,
|
DeleteAll,
|
||||||
ContentUpdated(bool, String),
|
ContentUpdated(ContentType, String),
|
||||||
Event(Event),
|
Event(Event),
|
||||||
TaskPush(Option<usize>),
|
TaskPush(Option<usize>),
|
||||||
StorageToggle(bool),
|
StorageToggle(bool),
|
||||||
ToggleUnselect(bool),
|
ToggleUnselect(bool),
|
||||||
|
SelectedTaskList(String),
|
||||||
|
AddTaskList,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ChangeType {
|
pub enum ChangeType {
|
||||||
@ -51,6 +59,13 @@ pub enum ChangeType {
|
|||||||
Value,
|
Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum ContentType {
|
||||||
|
NewTask,
|
||||||
|
ExistingTask,
|
||||||
|
NewList,
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Todo {
|
impl Default for Todo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Todo::new()
|
Todo::new()
|
||||||
@ -65,21 +80,37 @@ impl Todo {
|
|||||||
tasks: Vec::new(),
|
tasks: Vec::new(),
|
||||||
completed_tasks: 0,
|
completed_tasks: 0,
|
||||||
local_storage: true, // todo eventually get this info from db - if no db set it to true
|
local_storage: true, // todo eventually get this info from db - if no db set it to true
|
||||||
conn: db::startup("./todolist-db.db3".as_ref()).expect("[ERROR] Failed to access the local storage"),
|
conn: db::startup("./todolist-db.db3".as_ref())
|
||||||
|
.expect("[ERROR] Failed to access the local storage"),
|
||||||
select_all: false,
|
select_all: false,
|
||||||
|
task_list_input: String::from(""),
|
||||||
|
task_list_state: Default::default(),
|
||||||
|
task_list: vec![String::from("general")],
|
||||||
|
current_task_group: Some(String::from("")),
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::load_data_from_db(&mut init);
|
Self::load_data_from_db(&mut init);
|
||||||
|
init.task_list_state = combo_box::State::new(init.task_list.clone());
|
||||||
|
init.current_task_group = Some(init.task_list[0].clone());
|
||||||
|
|
||||||
init
|
init
|
||||||
}
|
}
|
||||||
pub(crate) fn update(&mut self, message: Message) -> Task<Message> {
|
pub(crate) fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::ContentUpdated(new, value) => {
|
Message::ContentUpdated(new, value) => {
|
||||||
if new {
|
/*if new {
|
||||||
self.new_task = String::from(&value);
|
self.new_task = String::from(&value);
|
||||||
} else {
|
} else {
|
||||||
self.updated_task = String::from(&value)
|
self.updated_task = String::from(&value)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
match new {
|
||||||
|
ContentType::NewTask => self.new_task = String::from(&value),
|
||||||
|
ContentType::ExistingTask => self.updated_task = String::from(&value),
|
||||||
|
ContentType::NewList => {
|
||||||
|
self.task_list_input = String::from(&value);
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for task in &mut self.tasks {
|
for task in &mut self.tasks {
|
||||||
@ -89,14 +120,21 @@ impl Todo {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::AddTask => {
|
Message::AddTask => {
|
||||||
if self.new_task.is_empty() { return Task::none(); }
|
if self.new_task.is_empty() {
|
||||||
if self.duplicate_exists(None) { return Task::none(); }
|
return Task::none();
|
||||||
|
}
|
||||||
|
if self.duplicate_exists(None) {
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
|
||||||
let result = db::insert_task(&self.conn, TaskColumn {
|
let result = db::insert_task(
|
||||||
id: 0,
|
&self.conn,
|
||||||
checked: false,
|
TaskColumn {
|
||||||
value: self.new_task.to_string(),
|
id: 0,
|
||||||
});
|
checked: false,
|
||||||
|
value: self.new_task.to_string(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
@ -110,60 +148,81 @@ impl Todo {
|
|||||||
already_exists: false,
|
already_exists: false,
|
||||||
};
|
};
|
||||||
self.tasks.push(data);
|
self.tasks.push(data);
|
||||||
},
|
}
|
||||||
Err(e) => eprintln!("[ERROR] Failed to insert new task into DB:\n{e}"),
|
Err(e) => eprintln!("[ERROR] Failed to insert new task into DB:\n{e}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.new_task = String::new();
|
self.new_task = String::new();
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
},
|
}
|
||||||
Message::DeleteTask(index) => {
|
Message::DeleteTask(index) => {
|
||||||
if self.tasks[index].checked {
|
if self.tasks[index].checked {
|
||||||
self.completed_tasks -= 1;
|
self.completed_tasks -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = db::delete_tasks(&self.conn, Some(self.tasks[index].db_id)) {
|
if let Err(e) = db::delete_tasks(&self.conn, Some(self.tasks[index].db_id)) {
|
||||||
eprintln!("[ERROR] Failed to delete task '{}':\n{e}", self.tasks[index].value);
|
eprintln!(
|
||||||
|
"[ERROR] Failed to delete task '{}':\n{e}",
|
||||||
|
self.tasks[index].value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tasks.remove(index);
|
self.tasks.remove(index);
|
||||||
|
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::CheckTask(choice, id) => {
|
Message::CheckTask(choice, id) => {
|
||||||
self.completed_tasks = if choice { self.completed_tasks + 1 } else { self.completed_tasks - 1 };
|
self.completed_tasks = if choice {
|
||||||
|
self.completed_tasks + 1
|
||||||
|
} else {
|
||||||
|
self.completed_tasks - 1
|
||||||
|
};
|
||||||
self.tasks[id].checked = choice;
|
self.tasks[id].checked = choice;
|
||||||
|
|
||||||
let result = db::update_task(&self.conn, TaskColumn {
|
let result = db::update_task(
|
||||||
id: self.tasks[id].db_id,
|
&self.conn,
|
||||||
checked: self.tasks[id].checked,
|
TaskColumn {
|
||||||
value: self.tasks[id].value.to_string(),
|
id: self.tasks[id].db_id,
|
||||||
}, ChangeType::Checked);
|
checked: self.tasks[id].checked,
|
||||||
|
value: self.tasks[id].value.to_string(),
|
||||||
|
},
|
||||||
|
ChangeType::Checked,
|
||||||
|
);
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
eprintln!("[ERROR] Failed to update checkbox in local storage:\n{e}");
|
eprintln!("[ERROR] Failed to update checkbox in local storage:\n{e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
},
|
}
|
||||||
Message::EditTask(index) => {
|
Message::EditTask(index) => {
|
||||||
let set_update;
|
let set_update;
|
||||||
|
|
||||||
if self.tasks[index].edit {
|
if self.tasks[index].edit {
|
||||||
if self.updated_task.is_empty() { return Task::none(); }
|
if self.updated_task.is_empty() {
|
||||||
if self.duplicate_exists(Some(&self.tasks[index].value)) { return Task::none(); }
|
return Task::none();
|
||||||
|
}
|
||||||
|
if self.duplicate_exists(Some(&self.tasks[index].value)) {
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
|
||||||
self.tasks[index].value = self.updated_task.clone();
|
self.tasks[index].value = self.updated_task.clone();
|
||||||
let result = db::update_task(&self.conn, TaskColumn {
|
let result = db::update_task(
|
||||||
id: self.tasks[index].db_id,
|
&self.conn,
|
||||||
checked: self.tasks[index].checked,
|
TaskColumn {
|
||||||
value: self.tasks[index].value.to_string(),
|
id: self.tasks[index].db_id,
|
||||||
}, ChangeType::Value);
|
checked: self.tasks[index].checked,
|
||||||
|
value: self.tasks[index].value.to_string(),
|
||||||
|
},
|
||||||
|
ChangeType::Value,
|
||||||
|
);
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
eprintln!("[ERROR] Failed to update task '{}' in local storage:\n{e}", self.tasks[index].value);
|
eprintln!(
|
||||||
|
"[ERROR] Failed to update task '{}' in local storage:\n{e}",
|
||||||
|
self.tasks[index].value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_update = true;
|
set_update = true;
|
||||||
@ -194,7 +253,7 @@ impl Todo {
|
|||||||
self.completed_tasks = 0;
|
self.completed_tasks = 0;
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
},
|
}
|
||||||
Message::Event(event) => match event {
|
Message::Event(event) => match event {
|
||||||
Event::Keyboard(keyboard::Event::KeyPressed {
|
Event::Keyboard(keyboard::Event::KeyPressed {
|
||||||
key: keyboard::Key::Named(key::Named::Tab),
|
key: keyboard::Key::Named(key::Named::Tab),
|
||||||
@ -211,22 +270,20 @@ impl Todo {
|
|||||||
key: keyboard::Key::Named(key::Named::Delete),
|
key: keyboard::Key::Named(key::Named::Delete),
|
||||||
modifiers,
|
modifiers,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
if modifiers.control() {
|
if modifiers.control() {
|
||||||
Task::perform(async move { Message::DeleteAll }, |result| result)
|
Task::perform(async move { Message::DeleteAll }, |result| result)
|
||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Task::none()
|
_ => Task::none(),
|
||||||
},
|
},
|
||||||
Message::TaskPush(option_id) => {
|
Message::TaskPush(option_id) => match option_id {
|
||||||
match option_id {
|
Some(i) => Task::perform(async move { Message::EditTask(i) }, |result| result),
|
||||||
Some(i) => Task::perform(async move { Message::EditTask(i) }, |result| result),
|
None => Task::perform(async { Message::AddTask }, |result| result),
|
||||||
None => Task::perform(async { Message::AddTask }, |result| result),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Message::StorageToggle(toggle) => {
|
Message::StorageToggle(_toggle) => {
|
||||||
// todo must be enabled eventually
|
// todo must be enabled eventually
|
||||||
//self.local_storage = toggle;
|
//self.local_storage = toggle;
|
||||||
|
|
||||||
@ -239,7 +296,7 @@ impl Todo {
|
|||||||
// also how do we even implement that
|
// also how do we even implement that
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
},
|
}
|
||||||
Message::ToggleUnselect(toggle) => {
|
Message::ToggleUnselect(toggle) => {
|
||||||
self.select_all = toggle;
|
self.select_all = toggle;
|
||||||
for task in &mut self.tasks {
|
for task in &mut self.tasks {
|
||||||
@ -257,31 +314,34 @@ impl Todo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
},
|
}
|
||||||
|
Message::SelectedTaskList(task) => {
|
||||||
|
self.current_task_group = Some(task);
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::AddTaskList => {
|
||||||
|
if self.task_list_input.is_empty() { return Task::none(); }
|
||||||
|
self.task_list.push(self.task_list_input.clone());
|
||||||
|
self.task_list_state = combo_box::State::new(self.task_list.clone());
|
||||||
|
self.task_list_input = String::from("");
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn view(&self) -> Element<Message> {
|
pub(crate) fn view(&self) -> Element<Message> {
|
||||||
let input = text_input("Enter new task", &self.new_task)
|
let input = text_input("Enter new task", &self.new_task)
|
||||||
.width(300)
|
.width(300)
|
||||||
.size(25)
|
.size(25)
|
||||||
.on_input(|str| Message::ContentUpdated(true, str))
|
.on_input(|str| Message::ContentUpdated(ContentType::NewTask, str))
|
||||||
.on_submit(Message::TaskPush(None));
|
.on_submit(Message::TaskPush(None));
|
||||||
|
|
||||||
let add_btn = button(
|
let add_btn = button(Text::new("Add").size(19).center())
|
||||||
Text::new("Add")
|
|
||||||
.size(19)
|
|
||||||
.center()
|
|
||||||
)
|
|
||||||
.width(Length::Shrink)
|
.width(Length::Shrink)
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.on_press(Message::AddTask);
|
.on_press(Message::AddTask);
|
||||||
|
|
||||||
let clear_btn = button(
|
let clear_btn = button(Text::new("Clear").size(19).center())
|
||||||
Text::new("Clear")
|
|
||||||
.size(19)
|
|
||||||
.center()
|
|
||||||
)
|
|
||||||
.width(Length::Shrink)
|
.width(Length::Shrink)
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.style(button::danger)
|
.style(button::danger)
|
||||||
@ -291,14 +351,16 @@ impl Todo {
|
|||||||
let mut saved_tasks = column![];
|
let mut saved_tasks = column![];
|
||||||
|
|
||||||
for (i, task) in self.tasks.iter().enumerate() {
|
for (i, task) in self.tasks.iter().enumerate() {
|
||||||
let chk = checkbox("", task.checked).size(25).on_toggle(move |b| Message::CheckTask(b, i));
|
let chk = checkbox("", task.checked)
|
||||||
|
.size(25)
|
||||||
|
.on_toggle(move |b| Message::CheckTask(b, i));
|
||||||
let mut label = Text::new(&task.value).width(200);
|
let mut label = Text::new(&task.value).width(200);
|
||||||
if task.already_exists {
|
if task.already_exists {
|
||||||
label = label.style(danger);
|
label = label.style(danger);
|
||||||
}
|
}
|
||||||
let input = text_input("Enter new name", &self.updated_task)
|
let input = text_input("Enter new name", &self.updated_task)
|
||||||
.width(200)
|
.width(200)
|
||||||
.on_input(|str| Message::ContentUpdated(false, str))
|
.on_input(|str| Message::ContentUpdated(ContentType::ExistingTask, str))
|
||||||
.on_submit(Message::TaskPush(Some(i)));
|
.on_submit(Message::TaskPush(Some(i)));
|
||||||
let mut edit = button(if task.edit { "save" } else { "edit" }).width(Length::Shrink);
|
let mut edit = button(if task.edit { "save" } else { "edit" }).width(Length::Shrink);
|
||||||
if task.can_update {
|
if task.can_update {
|
||||||
@ -309,29 +371,59 @@ impl Todo {
|
|||||||
delete = delete.on_press(Message::DeleteTask(i));
|
delete = delete.on_press(Message::DeleteTask(i));
|
||||||
}
|
}
|
||||||
let mut task_line = row![chk];
|
let mut task_line = row![chk];
|
||||||
task_line = if task.edit { task_line.push(input) } else { task_line.push(label) };
|
task_line = if task.edit {
|
||||||
task_line = task_line.push(edit).push(delete).spacing(10).padding([5, 10]);
|
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);
|
saved_tasks = saved_tasks.push(task_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = Text::new(format!("{} / {}", self.completed_tasks, self.tasks.len()));
|
let status = Text::new(format!("{} / {}", self.completed_tasks, self.tasks.len()));
|
||||||
let unselect = checkbox("Un/select all", self.select_all).on_toggle(Message::ToggleUnselect);
|
let tasklist = combo_box(
|
||||||
let storage = checkbox("Local storage", self.local_storage).on_toggle(Message::StorageToggle);
|
&self.task_list_state,
|
||||||
let footer = row![status, Space::with_width(Length::Fill), unselect, storage].padding(10).spacing(10);
|
"Test...",
|
||||||
|
self.current_task_group.as_ref(),
|
||||||
|
Message::SelectedTaskList,
|
||||||
|
)
|
||||||
|
.on_input(|str| Message::ContentUpdated(ContentType::NewList, str));
|
||||||
|
let add_tasklist = button("+").on_press(Message::AddTaskList);
|
||||||
|
let tasklist_group = row![tasklist.width(Length::Fill), add_tasklist].spacing(5);
|
||||||
|
let unselect =
|
||||||
|
checkbox("Select all", self.select_all).on_toggle(Message::ToggleUnselect);
|
||||||
|
let storage =
|
||||||
|
checkbox("Local storage", self.local_storage).on_toggle(Message::StorageToggle);
|
||||||
|
let footer = row![
|
||||||
|
status,
|
||||||
|
tasklist_group,
|
||||||
|
unselect,
|
||||||
|
storage
|
||||||
|
]
|
||||||
|
.padding(10)
|
||||||
|
.spacing(10);
|
||||||
|
|
||||||
let mut output = column![new_task.padding(10)];
|
let mut output = column![new_task.padding(10)];
|
||||||
output = if self.tasks.is_empty() { output.push(saved_tasks.height(Length::Fill)) } else { output.push(scrollable(saved_tasks).height(Length::Fill).spacing(10)) };
|
output = if self.tasks.is_empty() {
|
||||||
|
output.push(saved_tasks.height(Length::Fill))
|
||||||
|
} else {
|
||||||
|
output.push(scrollable(saved_tasks).height(Length::Fill).spacing(10))
|
||||||
|
};
|
||||||
output = output.align_x(Center).push(footer);
|
output = output.align_x(Center).push(footer);
|
||||||
|
|
||||||
center(output).into()
|
center(output).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn theme(&self) -> Theme {
|
pub(crate) fn theme(&self) -> Theme {
|
||||||
Theme::Dark
|
Theme::Dark
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn subscription(&self) -> Subscription<Message> {
|
pub(crate) fn subscription(&self) -> Subscription<Message> {
|
||||||
event::listen().map(Message::Event)
|
event::listen().map(Message::Event)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,11 +463,12 @@ impl Todo {
|
|||||||
if task.already_exists {
|
if task.already_exists {
|
||||||
match task_name {
|
match task_name {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
if task.value != *t { return true; }
|
if task.value != *t {
|
||||||
},
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
None => return true,
|
None => return true,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user