View

We need to add a button with a trash to the right of our task description. When user presses the button we will remove the task from the store.

So in the view/task_list.rs we need to add imports

use gtk::Button;
use gtk::prelude::ButtonExt;

Then we need to update the TaskWidgets so they will hold reference to the delete button


#[derive(Debug)]
#[allow(dead_code)]
pub struct TaskWidgets {
    checkbox: CheckButton,
    label: Label,
    delete_button: Button,
    root: Box,

We need to add Delete event to the TaskMsg so we can track which record should be deleted.

pub enum TaskMsg {
    Toggle{
        complete: bool,
        id: Id<Task>,
    },
    Delete(Id<Task>),
    New,
}

Now we need to update the init_view method in the implementation of the StoreViewPrototype for TaskListViewModel

impl<Config> StoreViewPrototype for TasksListViewModel<Config> 
where Config: TasksListConfiguration + 'static,
{

    ...

    fn init_view(
        record: &Task,
        _position: Position,
        sender: Sender<TaskMsg>,
    ) -> Self::RecordWidgets {
        let root = Box::builder()
            .orientation(Orientation::Horizontal)
            .build();

        let checkbox = CheckButton::builder()
            .margin_top(12)
            .margin_start(12)
            .margin_end(12)
            .margin_bottom(12)
            .active(record.completed)
            .build();

        {
            let sender = sender.clone();
            let id = record.get_id();

            checkbox.connect_toggled(move |btn| {
                send!(sender, TaskMsg::Toggle{
                    id,
                    complete: btn.is_active()
                });
            });
        }

        let label = Label::builder()
            .margin_top(12)
            .margin_start(12)
            .margin_end(12)
            .margin_bottom(12)
            .label(&record.description)
            .hexpand(true)
            .xalign(0.0)
            .build();

        let delete_button = Button::builder()
            .margin_top(12)
            .margin_start(12)
            .margin_end(12)
            .margin_bottom(12)
            .icon_name("user-trash-symbolic")
            .build();
            
        delete_button.add_css_class("flat");

        {
            let sender = sender.clone();
            let id = record.get_id();

            delete_button.connect_clicked(move |_| {
                send!(sender, TaskMsg::Delete(id));
            });
        }

        root.append(&checkbox);
        root.append(&label);
        root.append(&delete_button);

        TaskWidgets {
            checkbox,
            label,
            delete_button,
            root,
        }
    }

    ...

}

The most important part is that we've built delete_button and added it to the root widget. When delete_button is clicked it will send TaskMsg::Delete. To make ui nice we in the label we've added to extra settings

            .hexpand(true)
            .xalign(0.0)

This will expand horizontally and text will be left aligned.

Last thing left to do is to handle the changes in the view method in the implementation of StoreViewPrototype for TaskListViewModel. Compiler will tell you exactly where.

impl<Config> StoreViewPrototype for TasksListViewModel<Config> 
where Config: TasksListConfiguration + 'static,
{

    ...

    fn update(
        view_model: &mut Self::ViewModel, 
        msg: <Self as ViewModel>::Msg, 
        _sender: Sender<<Self as ViewModel>::Msg>
    ) {
        match msg {
            TaskMsg::New => {
                let description = view_model.new_task_description.text();
                let task = Task::new(description, false);
                view_model.new_task_description.set_text("");
                view_model.tasks.send(StoreMsg::Commit(task));
            },
            TaskMsg::Toggle{ complete, id } => {
                let tasks = &view_model.tasks;
                if let Some(record) = tasks.get(&id) {
                    let mut updated = record.clone();
                    updated.completed = complete;
                    tasks.send(StoreMsg::Commit(updated));
                }
            },
            TaskMsg::Delete(id) => {
                let tasks = &view_model.tasks;
                tasks.send(StoreMsg::Delete(id));
            }
        }
    }

    ...

}

Sending StorMsg::Delete to the task with record id will remove it from the data store.

After this chapter you know how to create, update and delete the records from the store. Full source code can be found in the examples todo_4.