Modules

Modules are used to encapsulate related private data into a central type; along with its own message type and functions. These could be individual pages of your application, sections of a page, or even a reusable widgets composed of smaller widgets.

Below is a hypothetical application which contains two modules: todo and config. Each containing their own respective Page and Message types.

struct App { active_page: PageId, todo_page: todo::Page, config_page: config::Page, }

Starting with todo page module, which manages todo tasks.

mod todo { use cosmic::prelude::*; use cosmic::widget; pub async fn load() -> Message { // .. } #[derive(Debug, Clone)] pub enum Message { /// Add a new task Add, /// Edit an existing task EditInput(usize, String), /// Move the given task down MoveDown(usize), /// Move the given task up MoveUp(usize), /// Update the new task input editor NewInput(String), /// Remove an existing task Remove(usize) /// Save to disk Save } pub struct Page { new_task_input: String, tasks: Vec<String>, } impl Page { pub fn view(&self) -> cosmic::Element<Message> { // Where new tasks will be input before being added to the task list. let new_task_input = widget::text_input("Write down a new task here", &self.new_task_input) .on_input(Message::NewInput) .on_submit(Message::Add); // Fold each enumerated task into a widget that is pushed to a scrollable column. let saved_tasks = self.tasks.iter() .enumerate() .fold(widget::column(), |column, (id, task)| { column.push( // A hypothetical widget created for this app crate::widget::task(task.as_str()) .on_remove(Message::Remove(id)) .on_input(|text| Message::EditInput(id, text)) .on_move_down(Message::MoveDown(id)) .on_move_up(Message::MoveUp(id)) .into() ) }) .apply(widget::scrollable); // Compose the above widgets into the column view. widget::column::with_capacity(2) .spacing(cosmic::theme::active().cosmic().spacing.space_l) .push(new_task_input) .push(saved_tasks) .into() } pub fn update(&mut self, message: Message) -> cosmic::Task<cosmic::Action<Message>> { match message { Message::Add => { self.tasks.insert(std::mem::take(&mut self.new_task_input)); } Message::EditInput(id, task) => { self.tasks[id] = task; } Message::MoveDown(id) => { if id + 1 < self.tasks.len() { self.tasks.swap(id, id + 1); } } Message::MoveUp(id) => { if id > 0 { self.tasks.swap(id, id - 1); } } Message::NewInput(input) => { self.new_task_input = input; } Message::Remove(id) => { self.tasks.remove(id); } Message::Save => { // Hypothetical method to save the tasks to disk. let save_future = self.save_to_disk(); return cosmic::task::future(save_future); } } cosmic::Task::none() } } }

And now the config module:

mod config { #[derive(Debug, Clone)] pub enum Message { OpenUrl(url::Url) } pub struct Page { author_name: String, donate_url: url::Url, homepage_url: url::Url, repository_urlL: url::Url, } impl Page { pub fn view(&self) -> cosmic::Element<Message> { // Hypothetical config page } pub fn update(&mut self, message: Message) -> cosmic::Task<cosmic::Action<Message>> { match message { OpenUrl(url) => { tokio::spawn(open_url(url)); } } cosmic::Task::none() } } pub async fn open_url(url: url::Url) { // ... } }

We can then use them in your application's own native view and update functions like so:

#[derive(Debug, Clone)] enum Message { SetPage(PageId), ConfigPage(config::Message), TodoPage(todo::Message), } #[derive(Debug, Clone)] enum PageId { Config Todo, } // ... fn view(&self) -> cosmic::Element<Message> { match self.active_page { PageId::Todo => self.todo_page.view(), PageId::Config => self.config_page.view(), } } fn update(&mut self, message: Message) -> cosmic::Task<cosmic::Action<Message>> { match message { Message::SetPage(id) => { self.active_page = id; match self.active_page { PageId::Config => (), PageId::Todo => return cosmic::task::future(async move { Message::TodoPage(todo::load().await) }), } } Message::ConfigPage(message) => return self.config_page.update(message), Message::TodoPage(message) => return self.todo_page.update(message), } }

We may even implement the Application::on_close_requested() method in our app to handle that Save message for our todo page.

fn on_close_requested(&mut self) -> Option<Message> { Some(Message::TodoPage(todo::Message::Save)) }