2021-02-08 18:37:25 +11:00
|
|
|
//! This implements our ListView, which displays and helps interact with the Todo list. This is a
|
|
|
|
//! mostly single-threaded example, so we can get away with cutting a few corners and keeping our
|
|
|
|
//! data store in here - but for a larger app, you'd likely do something else.
|
|
|
|
|
|
|
|
use cacao::listview::{
|
|
|
|
ListView, ListViewDelegate, ListViewRow,
|
|
|
|
RowAnimation, RowEdge, RowAction, RowActionStyle
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::storage::{dispatch_ui, Message, Todos, TodoStatus};
|
|
|
|
|
|
|
|
mod row;
|
|
|
|
use row::TodoViewRow;
|
|
|
|
|
|
|
|
/// An identifier for the cell(s) we dequeue.
|
|
|
|
const TODO_ROW: &'static str = "TodoViewRowCell";
|
|
|
|
|
2022-07-11 01:15:29 +10:00
|
|
|
/// The list view for todos.
|
2021-02-08 18:37:25 +11:00
|
|
|
#[derive(Debug, Default)]
|
|
|
|
pub struct TodosListView {
|
|
|
|
view: Option<ListView>,
|
|
|
|
todos: Todos
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TodosListView {
|
|
|
|
/// This manages updates to the underlying database. You might opt to do this elsewhere, etc.
|
|
|
|
pub fn on_message(&self, message: Message) {
|
|
|
|
match message {
|
|
|
|
Message::MarkTodoComplete(row) => {
|
|
|
|
self.todos.with_mut(row, |todo| todo.status = TodoStatus::Complete);
|
|
|
|
if let Some(view) = &self.view {
|
|
|
|
view.reload_rows(&[row]);
|
|
|
|
view.set_row_actions_visible(false);
|
|
|
|
}
|
|
|
|
},
|
2022-07-11 01:15:29 +10:00
|
|
|
|
2021-02-08 18:37:25 +11:00
|
|
|
Message::MarkTodoIncomplete(row) => {
|
|
|
|
self.todos.with_mut(row, |todo| todo.status = TodoStatus::Incomplete);
|
2022-07-11 01:15:29 +10:00
|
|
|
|
2021-02-08 18:37:25 +11:00
|
|
|
if let Some(view) = &self.view {
|
|
|
|
view.reload_rows(&[row]);
|
|
|
|
view.set_row_actions_visible(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
Message::StoreNewTodo(todo) => {
|
|
|
|
self.todos.insert(todo);
|
|
|
|
self.view.as_ref().unwrap().perform_batch_updates(|listview| {
|
|
|
|
// We know we always insert at the 0 index, so this is a simple calculation.
|
|
|
|
// You'd need to diff yourself for anything more complicated.
|
2021-03-05 12:24:39 +11:00
|
|
|
listview.insert_rows(&[0], RowAnimation::SlideDown);
|
2021-02-08 18:37:25 +11:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ListViewDelegate for TodosListView {
|
|
|
|
const NAME: &'static str = "TodosListView";
|
|
|
|
|
|
|
|
/// Essential configuration and retaining of a `ListView` handle to do updates later on.
|
|
|
|
fn did_load(&mut self, view: ListView) {
|
|
|
|
view.register(TODO_ROW, TodoViewRow::default);
|
|
|
|
view.set_uses_alternating_backgrounds(true);
|
2022-01-07 18:25:36 +11:00
|
|
|
view.set_row_height(64.);
|
2021-02-08 18:37:25 +11:00
|
|
|
self.view = Some(view);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The number of todos we currently have.
|
|
|
|
fn number_of_items(&self) -> usize {
|
|
|
|
self.todos.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// For a given row, dequeues a view from the system and passes the appropriate `Transfer` for
|
|
|
|
/// configuration.
|
|
|
|
fn item_for(&self, row: usize) -> ListViewRow {
|
|
|
|
let mut view = self.view.as_ref().unwrap().dequeue::<TodoViewRow>(TODO_ROW);
|
2022-07-11 01:15:29 +10:00
|
|
|
|
2021-02-08 18:37:25 +11:00
|
|
|
if let Some(view) = &mut view.delegate {
|
|
|
|
self.todos.with(row, |todo| view.configure_with(todo));
|
|
|
|
}
|
|
|
|
|
|
|
|
view.into_row()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Provides support for _swipe-to-reveal_ actions. After a user has completed one of these
|
|
|
|
/// actions, we make sure to mark the tableview as done (see the message handlers in this
|
|
|
|
/// file).
|
|
|
|
fn actions_for(&self, row: usize, edge: RowEdge) -> Vec<RowAction> {
|
|
|
|
if let RowEdge::Leading = edge {
|
|
|
|
return vec![];
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut actions = vec![];
|
|
|
|
|
|
|
|
self.todos.with(row, |todo| match todo.status {
|
|
|
|
TodoStatus::Complete => {
|
|
|
|
actions.push(RowAction::new("Mark Incomplete", RowActionStyle::Destructive, move |_action, row| {
|
|
|
|
dispatch_ui(Message::MarkTodoIncomplete(row));
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
|
|
|
|
TodoStatus::Incomplete => {
|
|
|
|
actions.push(RowAction::new("Mark Complete", RowActionStyle::Regular, move |_action, row| {
|
|
|
|
dispatch_ui(Message::MarkTodoComplete(row));
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
actions
|
|
|
|
}
|
|
|
|
}
|