diff --git a/src/agenda.rs b/src/agenda.rs index 9f4d56b..9f579ed 100644 --- a/src/agenda.rs +++ b/src/agenda.rs @@ -40,42 +40,43 @@ macro_rules! create { } macro_rules! update { - ($self:ident, $exp:ident, $task:ident, $get:ident, $list:ident, $date:ident) => {{ + ($self:ident, $exp:expr, $task:ident, $get:ident, $list:ident, $date:ident) => {{ use relm4::ComponentController as _; let tasks = $self.$get(&$list, $date); - $self.$exp.set_expanded(!tasks.is_empty()); - $self.$exp.set_sensitive(!tasks.is_empty()); + $exp.set_expanded(!tasks.is_empty()); + $exp.set_sensitive(!tasks.is_empty()); $self.$task.emit(crate::widgets::tasks::Msg::Update(tasks)); }}; } pub struct Model { - calendar: gtk::Calendar, date: chrono::NaiveDate, - month_exp: gtk::Expander, month: relm4::Controller, - past_exp: gtk::Expander, past: relm4::Controller, - today_exp: gtk::Expander, today: relm4::Controller, - tomorrow_exp: gtk::Expander, tomorrow: relm4::Controller, - week_exp: gtk::Expander, week: relm4::Controller, } impl Model { - fn update_tasks(&self) { + fn update_tasks(&self, widgets: &ModelWidgets) { let list = crate::application::tasks(); - let date = crate::date::from_glib(self.calendar.date()); - - update!(self, past_exp, past, past_tasks, list, date); - update!(self, today_exp, today, today_tasks, list, date); - update!(self, tomorrow_exp, tomorrow, tomorrow_tasks, list, date); - update!(self, week_exp, week, week_tasks, list, date); - update!(self, month_exp, month, month_tasks, list, date); + let date = crate::date::from_glib(widgets.calendar.date()); + + update!(self, widgets.past_exp, past, past_tasks, list, date); + update!(self, widgets.today_exp, today, today_tasks, list, date); + update!( + self, + widgets.tomorrow_exp, + tomorrow, + tomorrow_tasks, + list, + date + ); + update!(self, widgets.week_exp, week, week_tasks, list, date); + update!(self, widgets.month_exp, month, month_tasks, list, date); } fn past_tasks( @@ -160,13 +161,13 @@ impl Model { tasks } - fn update_marks(&self) { + fn update_marks(&self, widgets: &ModelWidgets) { use chrono::Datelike as _; - self.calendar.clear_marks(); + widgets.calendar.clear_marks(); let list = crate::application::tasks(); - let date = self.calendar.date(); + let date = widgets.calendar.date(); let month = date.month() as u32; let year = date.year(); @@ -176,14 +177,15 @@ impl Model { }; if due_date.year() == year && due_date.month() == month { - self.calendar.mark_day(due_date.day()); + widgets.calendar.mark_day(due_date.day()); } } } } #[relm4::component(pub)] -impl relm4::SimpleComponent for Model { +impl relm4::Component for Model { + type CommandOutput = (); type Init = chrono::NaiveDate; type Input = MsgInput; type Output = MsgOutput; @@ -193,36 +195,29 @@ impl relm4::SimpleComponent for Model { root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { - use relm4::Component as _; use relm4::ComponentController as _; let model = Self { - calendar: gtk::Calendar::new(), date: init, month: create!(sender), - month_exp: gtk::Expander::new(None), past: create!(sender), - past_exp: gtk::Expander::new(None), today: create!(sender), - today_exp: gtk::Expander::new(None), tomorrow: create!(sender), - tomorrow_exp: gtk::Expander::new(None), week: create!(sender), - week_exp: gtk::Expander::new(None), }; - let calendar = &model.calendar; - let month_exp = &model.month_exp; - let past_exp = &model.past_exp; - let today_exp = &model.today_exp; - let tomorrow_exp = &model.tomorrow_exp; - let week_exp = &model.week_exp; let widgets = view_output!(); relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _: relm4::ComponentSender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + _: relm4::ComponentSender, + _: &Self::Root, + ) { use MsgInput::*; match msg { @@ -235,13 +230,13 @@ impl relm4::SimpleComponent for Model { } .unwrap(); - self.update_marks(); + self.update_marks(widgets); } DateSelect(date) => self.date = date, Update => (), } - self.update_tasks(); + self.update_tasks(widgets); } view! { @@ -253,8 +248,8 @@ impl relm4::SimpleComponent for Model { set_orientation: gtk::Orientation::Vertical, set_spacing: 5, - #[local_ref] - calendar -> gtk::Calendar { + #[name = "calendar"] + gtk::Calendar { #[watch] set_day: model.date.day() as i32, #[watch] @@ -281,28 +276,28 @@ impl relm4::SimpleComponent for Model { set_orientation: gtk::Orientation::Vertical, set_vexpand: true, - #[local_ref] - past_exp -> gtk::Expander { + #[name = "past_exp"] + gtk::Expander { set_child: Some(model.past.widget()), set_label: Some("Past due"), }, - #[local_ref] - today_exp -> gtk::Expander { + #[name = "today_exp"] + gtk::Expander { set_child: Some(model.today.widget()), set_label: Some("Today"), }, - #[local_ref] - tomorrow_exp -> gtk::Expander { + #[name = "tomorrow_exp"] + gtk::Expander { set_child: Some(model.tomorrow.widget()), set_label: Some("Tomorrow"), }, - #[local_ref] - week_exp -> gtk::Expander { + #[name = "week_exp"] + gtk::Expander { set_child: Some(model.week.widget()), set_label: Some("This week"), }, - #[local_ref] - month_exp -> gtk::Expander { + #[name = "month_exp"] + gtk::Expander { set_child: Some(model.month.widget()), set_label: Some("This month"), }, diff --git a/src/application/mod.rs b/src/application/mod.rs index fc7e820..35b0e8f 100644 --- a/src/application/mod.rs +++ b/src/application/mod.rs @@ -7,7 +7,6 @@ pub use globals::tasks::get as tasks; use globals::tasks::add as add_task; use preferences::Preferences; -use gtk::glib::clone; use gtk::prelude::*; use relm4::ComponentController as _; @@ -58,18 +57,14 @@ pub enum Msg { } pub struct Model { - add_popover: gtk::Popover, agenda: relm4::Controller, config: todo_txt::Config, contexts: relm4::Controller, - defered_button: gtk::CheckButton, - done_button: gtk::CheckButton, done: relm4::Controller, edit: relm4::Controller, flag: relm4::Controller, inbox: relm4::Controller, logger: relm4::Controller, - notebook: gtk::Notebook, projects: relm4::Controller, search: relm4::Controller, #[allow(dead_code)] @@ -163,16 +158,16 @@ impl Model { Some(path) } - fn add(&mut self, text: &str) { + fn add(&mut self, widgets: &ModelWidgets, text: &str) { match add_task(text) { - Ok(_) => self.update_tasks(), + Ok(_) => self.update_tasks(widgets), Err(err) => log::error!("Unable to create task: '{err}'"), } - self.add_popover.popdown(); + widgets.add_popover.popdown(); } - fn complete(&mut self, task: &crate::tasks::Task) { + fn complete(&mut self, widgets: &ModelWidgets, task: &crate::tasks::Task) { let id = task.id; let mut list = tasks(); @@ -220,7 +215,7 @@ impl Model { Err(err) => log::error!("Unable to save tasks: {err}"), }; - self.update_tasks(); + self.update_tasks(widgets); } fn edit(&mut self, task: &crate::tasks::Task) { @@ -229,7 +224,7 @@ impl Model { self.edit.widget().set_visible(true); } - fn save(&mut self, task: &crate::tasks::Task) { + fn save(&mut self, widgets: &ModelWidgets, task: &crate::tasks::Task) { let id = task.id; let mut list = tasks(); @@ -244,30 +239,30 @@ impl Model { log::info!("Task updated"); - self.update_tasks(); + self.update_tasks(widgets); self.edit.widget().set_visible(false); } - fn search(&self, query: &str) { + fn search(&self, widgets: &ModelWidgets, query: &str) { if query.is_empty() { - self.notebook.set_current_page(Some(Page::Inbox.into())); + widgets.notebook.set_current_page(Some(Page::Inbox.into())); self.search.widget().set_visible(false); } else { self.search.widget().set_visible(true); - self.notebook.set_current_page(Some(Page::Search.into())); + widgets.notebook.set_current_page(Some(Page::Search.into())); } self.search .emit(crate::search::MsgInput::UpdateFilter(query.to_string())); } - fn update_tasks(&self) { + fn update_tasks(&self, widgets: &ModelWidgets) { let list = crate::tasks::List::from_files(&self.config.todo_file, &self.config.done_file); globals::tasks::replace(list); globals::preferences::replace(crate::application::Preferences { - defered: self.defered_button.is_active(), - done: self.done_button.is_active(), + defered: widgets.defered_button.is_active(), + done: widgets.done_button.is_active(), }); self.agenda.sender().emit(crate::agenda::MsgInput::Update); @@ -307,7 +302,8 @@ impl Model { } #[relm4::component(pub)] -impl relm4::SimpleComponent for Model { +impl relm4::Component for Model { + type CommandOutput = (); type Init = todo_txt::Config; type Input = Msg; type Output = (); @@ -317,8 +313,6 @@ impl relm4::SimpleComponent for Model { root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { - use relm4::Component as _; - let logger = crate::logger::Model::builder().launch(()).detach(); let agenda = crate::agenda::Model::builder() @@ -380,60 +374,46 @@ impl relm4::SimpleComponent for Model { crate::widgets::task::MsgOutput::Edit(task) => Msg::Edit(task), }); - let defered_button = gtk::CheckButton::with_label("Display defered tasks"); - defered_button.connect_toggled(clone!( - #[strong] - sender, - move |_| sender.input(Msg::Refresh) - )); - - let done_button = gtk::CheckButton::with_label("Display done tasks"); - done_button.connect_toggled(clone!( - #[strong] - sender, - move |_| sender.input(Msg::Refresh) - )); - let model = Self { - add_popover: gtk::Popover::new(), agenda, config: init, contexts, - defered_button, - done_button, done, edit, flag, inbox, logger, - notebook: gtk::Notebook::new(), projects, search, xdg: xdg::BaseDirectories::with_prefix(NAME.to_lowercase()).unwrap(), }; - let add_popover = &model.add_popover; - let notebook = &model.notebook; let widgets = view_output!(); model.load_style(); - model.add_tab_widgets(notebook); - model.update_tasks(); + model.add_tab_widgets(&widgets.notebook); + model.update_tasks(&widgets); model.search.widget().set_visible(false); model.watch(sender); relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: relm4::ComponentSender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + _: relm4::ComponentSender, + _: &Self::Root, + ) { match msg { - Msg::Add(task) => self.add(&task), - Msg::Complete(task) => self.complete(&task), + Msg::Add(task) => self.add(widgets, &task), + Msg::Complete(task) => self.complete(widgets, &task), Msg::EditCancel => self.edit.widget().set_visible(false), - Msg::EditDone(task) => self.save(&task), + Msg::EditDone(task) => self.save(widgets, &task), Msg::Edit(task) => self.edit(&task), - Msg::Refresh => self.update_tasks(), - Msg::Search(query) => self.search(&query), + Msg::Refresh => self.update_tasks(widgets), + Msg::Search(query) => self.search(widgets, &query), } } @@ -455,8 +435,8 @@ impl relm4::SimpleComponent for Model { set_icon_name: "list-add", set_tooltip_text: "Add".into(), #[wrap(Some)] - #[local_ref] - set_popover = add_popover -> gtk::Popover { + #[name = "add_popover"] + set_popover = >k::Popover { gtk::Box { set_orientation: gtk::Orientation::Vertical, @@ -479,8 +459,17 @@ impl relm4::SimpleComponent for Model { set_popover = >k::Popover { gtk::Box { set_orientation: gtk::Orientation::Vertical, - append: &model.defered_button, - append: &model.done_button, + #[name = "defered_button"] + gtk::CheckButton { + set_label: Some("Display defered tasks"), + + connect_toggled => Msg::Refresh, + }, + #[name = "done_button"] + gtk::CheckButton { + set_label: Some("Display done tasks"), + connect_toggled => Msg::Refresh, + }, }, }, }, @@ -499,8 +488,8 @@ impl relm4::SimpleComponent for Model { set_wide_handle: true, #[wrap(Some)] - #[local_ref] - set_start_child = notebook -> gtk::Notebook { + #[name = "notebook"] + set_start_child = >k::Notebook { set_tab_pos: gtk::PositionType::Left, append_page: (model.inbox.widget(), None::<>k::Label>), diff --git a/src/done.rs b/src/done.rs index 1464b81..2782486 100644 --- a/src/done.rs +++ b/src/done.rs @@ -29,7 +29,7 @@ impl relm4::SimpleComponent for Model { type Output = crate::widgets::task::MsgOutput; fn init( - _init: Self::Init, + _: Self::Init, root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { @@ -47,7 +47,7 @@ impl relm4::SimpleComponent for Model { relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: relm4::ComponentSender) { + fn update(&mut self, msg: Self::Input, _: relm4::ComponentSender) { match msg { Msg::Update => self.update_tasks(), } diff --git a/src/edit.rs b/src/edit.rs index 9020752..b4b2529 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -19,7 +19,6 @@ pub enum MsgOutput { } pub struct Model { - buffer: gtk::TextBuffer, created: relm4::Controller, due: relm4::Controller, finish: relm4::Controller, @@ -53,7 +52,8 @@ pub enum DateType { } #[relm4::component(pub)] -impl relm4::SimpleComponent for Model { +impl relm4::Component for Model { + type CommandOutput = (); type Init = crate::tasks::Task; type Input = MsgInput; type Output = MsgOutput; @@ -63,8 +63,6 @@ impl relm4::SimpleComponent for Model { root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { - use relm4::Component as _; - let created = crate::widgets::calendar::Model::builder() .launch("Created") .detach(); @@ -119,7 +117,6 @@ impl relm4::SimpleComponent for Model { }); let model = Self { - buffer: gtk::TextBuffer::new(None), created, due, finish, @@ -132,22 +129,24 @@ impl relm4::SimpleComponent for Model { let widgets = view_output!(); - let note = model.task.note.content().unwrap_or_default(); - widgets.note.set_buffer(Some(&model.buffer)); - model.buffer.set_text(¬e); - relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, sender: relm4::ComponentSender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + sender: relm4::ComponentSender, + _: &Self::Root, + ) { use MsgInput::*; match msg { Flag(flagged) => self.task.flagged = flagged, Ok => { - let start = self.buffer.start_iter(); - let end = self.buffer.start_iter(); - self.task.note = self.buffer.text(&start, &end, true).to_string().into(); + let start = widgets.buffer.start_iter(); + let end = widgets.buffer.start_iter(); + self.task.note = widgets.buffer.text(&start, &end, true).to_string().into(); sender .output(MsgOutput::Done(Box::new(self.task.clone()))) @@ -243,6 +242,12 @@ impl relm4::SimpleComponent for Model { gtk::TextView { set_hexpand: true, set_vexpand: true, + #[wrap(Some)] + #[name = "buffer"] + set_buffer = >k::TextBuffer { + #[watch] + set_text?: &model.task.note.content(), + }, }, }, gtk::ActionBar { diff --git a/src/flag.rs b/src/flag.rs index fc60778..2438105 100644 --- a/src/flag.rs +++ b/src/flag.rs @@ -43,7 +43,7 @@ impl relm4::SimpleComponent for Model { type Output = crate::widgets::task::MsgOutput; fn init( - _init: Self::Init, + _: Self::Init, root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { @@ -61,7 +61,7 @@ impl relm4::SimpleComponent for Model { relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: relm4::ComponentSender) { + fn update(&mut self, msg: Self::Input, _: relm4::ComponentSender) { match msg { Msg::Update => self.update_tasks(), } diff --git a/src/inbox.rs b/src/inbox.rs index 479f708..5a262b6 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -43,7 +43,7 @@ impl relm4::SimpleComponent for Model { type Output = crate::widgets::task::MsgOutput; fn init( - _init: Self::Init, + _: Self::Init, root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { @@ -61,7 +61,7 @@ impl relm4::SimpleComponent for Model { relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: relm4::ComponentSender) { + fn update(&mut self, msg: Self::Input, _: relm4::ComponentSender) { match msg { Msg::Update => self.update_tasks(), } diff --git a/src/logger.rs b/src/logger.rs index b9bcdc2..1c1e13e 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,4 +1,3 @@ -use gtk::glib::clone; use gtk::prelude::*; type ChannelData = (log::Level, String); @@ -45,7 +44,6 @@ pub enum Msg { pub struct Model { count: usize, - list_box: gtk::ListBox, } impl Model { @@ -61,24 +59,25 @@ impl Model { gtk::glib::ControlFlow::Continue } - fn add_message(&self, level: log::Level, text: &str) { + fn add_message(&self, widgets: &ModelWidgets, level: log::Level, text: &str) { let class = level.to_string(); let label = gtk::Label::new(Some(text)); label.add_css_class(&class.to_lowercase()); - self.list_box.append(&label); + widgets.list_box.append(&label); } } #[relm4::component(pub)] -impl relm4::SimpleComponent for Model { +impl relm4::Component for Model { + type CommandOutput = (); type Init = (); type Input = Msg; type Output = (); fn init( - _init: Self::Init, + _: Self::Init, root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { @@ -88,14 +87,7 @@ impl relm4::SimpleComponent for Model { log::set_max_level(log::LevelFilter::Info); log::set_boxed_logger(Box::new(log)).unwrap_or_default(); - let list_box = gtk::ListBox::new(); - list_box.connect_row_activated(clone!( - #[strong] - sender, - move |_, row| sender.input(Msg::Read(row.clone())) - )); - - let model = Self { count: 0, list_box }; + let model = Self { count: 0 }; let widgets = view_output!(); @@ -105,18 +97,24 @@ impl relm4::SimpleComponent for Model { relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: relm4::ComponentSender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + _: relm4::ComponentSender, + _: &Self::Root, + ) { match msg { Msg::Add((level, text)) => { - self.add_message(level, &text); + self.add_message(widgets, level, &text); self.count += 1; } Msg::Clear => { - self.list_box.remove_all(); + widgets.list_box.remove_all(); self.count = 0; } Msg::Read(row) => { - self.list_box.remove(&row); + widgets.list_box.remove(&row); self.count = self.count.saturating_sub(1); } } @@ -141,7 +139,11 @@ impl relm4::SimpleComponent for Model { gtk::ScrolledWindow { set_vexpand: true, set_policy: (gtk::PolicyType::Never, gtk::PolicyType::Automatic), - set_child: Some(&model.list_box), + + #[name = "list_box"] + gtk::ListBox { + connect_row_activated[sender] => move |_, row| sender.input(Msg::Read(row.clone())), + } }, gtk::Button { set_label: "Clear all", diff --git a/src/main.rs b/src/main.rs index ab2b397..daf40de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,8 +25,7 @@ fn main() { let config = todo_txt::Config::from_env(); - let app = relm4::RelmApp::new("txt.todo.effitask") - .with_args(Vec::new()); + let app = relm4::RelmApp::new("txt.todo.effitask").with_args(Vec::new()); app.run::(config); } diff --git a/src/widgets/calendar.rs b/src/widgets/calendar.rs index ea37026..23be3d2 100644 --- a/src/widgets/calendar.rs +++ b/src/widgets/calendar.rs @@ -3,9 +3,7 @@ use gtk::prelude::*; pub struct Model { date: Option, - entry: gtk::Entry, label: &'static str, - popover: gtk::Popover, } #[derive(Debug)] @@ -27,16 +25,22 @@ impl Model { sender.output(MsgOutput::Updated(self.date)).ok(); } - fn date_selected(&mut self, sender: relm4::ComponentSender, date: gtk::glib::DateTime) { + fn date_selected( + &mut self, + widgets: &ModelWidgets, + sender: relm4::ComponentSender, + date: gtk::glib::DateTime, + ) { self.date = Some(crate::date::from_glib(date)); sender.output(MsgOutput::Updated(self.date)).ok(); - self.popover.popdown(); + widgets.popover.popdown(); } } #[relm4::component(pub)] -impl relm4::SimpleComponent for Model { +impl relm4::Component for Model { + type CommandOutput = (); type Init = &'static str; type Input = MsgInput; type Output = MsgOutput; @@ -47,25 +51,27 @@ impl relm4::SimpleComponent for Model { sender: relm4::ComponentSender, ) -> relm4::ComponentParts { let model = Self { - entry: gtk::Entry::new(), date: None, label: init, - popover: gtk::Popover::new(), }; - let entry = &model.entry; - let popover = &model.popover; let widgets = view_output!(); relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, sender: relm4::ComponentSender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + sender: relm4::ComponentSender, + _: &Self::Root, + ) { use MsgInput::*; match msg { Add(period) => self.add(sender, period), - DateSelected(date) => self.date_selected(sender, date), + DateSelected(date) => self.date_selected(widgets, sender, date), Set(date) => self.date = date, DateUpdated => { sender.output(MsgOutput::Updated(self.date)).ok(); @@ -93,8 +99,8 @@ impl relm4::SimpleComponent for Model { gtk::MenuButton { set_icon_name: "x-office-calendar", #[wrap(Some)] - #[local_ref] - set_popover = popover -> gtk::Popover { + #[name = "popover"] + set_popover = >k::Popover { gtk::Calendar { #[watch] set_day?: model.date.map(|x| x.day() as i32), @@ -109,8 +115,7 @@ impl relm4::SimpleComponent for Model { }, }, }, - #[local_ref] - entry -> gtk::Entry { + gtk::Entry { set_hexpand: true, #[watch] set_text?: &model.date.map(|x| x.format("%Y-%m-%d").to_string()), diff --git a/src/widgets/circle.rs b/src/widgets/circle.rs index fb669de..7828773 100644 --- a/src/widgets/circle.rs +++ b/src/widgets/circle.rs @@ -93,7 +93,7 @@ impl relm4::SimpleComponent for Model { fn init( init: Self::Init, root: Self::Root, - _sender: relm4::ComponentSender, + _: relm4::ComponentSender, ) -> relm4::ComponentParts { root.set_height_request(60); root.set_width_request(60); diff --git a/src/widgets/filter.rs b/src/widgets/filter.rs index ce2eb4b..49a5f6e 100644 --- a/src/widgets/filter.rs +++ b/src/widgets/filter.rs @@ -38,34 +38,33 @@ pub enum MsgOutput { } pub struct Model { - store: gtk::TreeStore, filters: std::collections::BTreeMap, tasks: relm4::Controller, - tree_view: gtk::TreeView, } impl Model { - fn update_filters(&mut self, filters: Vec<(String, (u32, u32))>) { - let selection = self.tree_view.selection(); + fn update_filters(&mut self, widgets: &ModelWidgets, filters: Vec<(String, (u32, u32))>) { + let selection = widgets.tree_view.selection(); let (paths, _) = selection.selected_rows(); self.filters.clear(); - self.store.clear(); + widgets.store.clear(); let mut root = std::collections::HashMap::new(); for filter in filters { - self.append(&mut root, filter); + self.append(widgets, &mut root, filter); } - self.tree_view.expand_all(); + widgets.tree_view.expand_all(); for path in paths { - gtk::prelude::TreeViewExt::set_cursor(&self.tree_view, &path, None, false); + gtk::prelude::TreeViewExt::set_cursor(&widgets.tree_view, &path, None, false); } } fn append( &mut self, + widgets: &ModelWidgets, root: &mut std::collections::HashMap, filter: (String, (u32, u32)), ) { @@ -80,25 +79,29 @@ impl Model { let parent = levels.join(&separator.to_string()); if !parent.is_empty() && root.get(&parent).is_none() { - self.append(root, (parent.clone(), (0, 0))); + self.append(widgets, root, (parent.clone(), (0, 0))); } - let row = self.store.append(root.get(&parent)); + let row = widgets.store.append(root.get(&parent)); - self.store + widgets + .store .set_value(&row, Column::Title.into(), &title.to_value()); - self.store + widgets + .store .set_value(&row, Column::Raw.into(), &filter.to_value()); - self.store + widgets + .store .set_value(&row, Column::Progress.into(), &progress.to_value()); let tooltip = format!("{done}/{total}"); - self.store + widgets + .store .set_value(&row, Column::Tooltip.into(), &tooltip.to_value()); root.insert(filter.clone(), row); - let path = self.store.path(&row); + let path = widgets.store.path(&row); self.filters.insert(path, filter); } @@ -126,18 +129,17 @@ impl Model { } #[relm4::component(pub)] -impl relm4::SimpleComponent for Model { +impl relm4::Component for Model { + type CommandOutput = (); type Init = (); type Input = MsgInput; type Output = MsgOutput; fn init( - _init: Self::Init, + _: Self::Init, root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { - use relm4::Component as _; - let tasks = crate::widgets::tasks::Model::builder().launch(()).forward( sender.output_sender(), |output| match output { @@ -156,23 +158,18 @@ impl relm4::SimpleComponent for Model { let model = Self { tasks, filters: std::collections::BTreeMap::new(), - store: gtk::TreeStore::new(&columns), - tree_view: gtk::TreeView::new(), }; - let filters = &model.tree_view; let widgets = view_output!(); - filters.set_model(Some(&model.store)); - - let selection = filters.selection(); + let selection = widgets.tree_view.selection(); selection.set_mode(gtk::SelectionMode::Multiple); selection.connect_changed(move |_| { sender.input(MsgInput::SelectionChange); }); let column = gtk::TreeViewColumn::new(); - filters.append_column(&column); + widgets.tree_view.append_column(&column); let cell = gtk::CellRendererProgress::new(); cell.set_text_xalign(0.); @@ -180,19 +177,25 @@ impl relm4::SimpleComponent for Model { column.add_attribute(&cell, "text", Column::Title.into()); column.add_attribute(&cell, "value", Column::Progress.into()); - filters.set_tooltip_column(Column::Tooltip.into()); + widgets.tree_view.set_tooltip_column(Column::Tooltip.into()); relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, sender: relm4::ComponentSender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + sender: relm4::ComponentSender, + _: &Self::Root, + ) { use MsgInput::*; match msg { SelectionChange => { let mut filters = Vec::new(); - let (paths, _) = self.tree_view.selection().selected_rows(); + let (paths, _) = widgets.tree_view.selection().selected_rows(); for path in paths { match self.filters.get(&path) { @@ -203,7 +206,7 @@ impl relm4::SimpleComponent for Model { sender.output(MsgOutput::Filters(filters)).ok(); } - UpdateFilters(filters) => self.update_filters(filters), + UpdateFilters(filters) => self.update_filters(widgets, filters), UpdateTasks(tasks) => self.update_tasks(tasks), } } @@ -218,10 +221,13 @@ impl relm4::SimpleComponent for Model { set_start_child = >k::ScrolledWindow { set_policy: (gtk::PolicyType::Never, gtk::PolicyType::Automatic), - #[local_ref] - filters -> gtk::TreeView { + #[name = "tree_view"] + gtk::TreeView { set_enable_tree_lines: true, set_headers_visible: false, + #[wrap(Some)] + #[name = "store"] + set_model = >k::TreeStore::new(&columns), connect_row_activated => |treeview, path, _| Self::select_range(treeview, path), }, diff --git a/src/widgets/keywords.rs b/src/widgets/keywords.rs index 70dbb4c..60b55b4 100644 --- a/src/widgets/keywords.rs +++ b/src/widgets/keywords.rs @@ -17,8 +17,6 @@ pub enum MsgOutput { pub struct Model { keywords: std::collections::BTreeMap, - store: gtk::ListStore, - tree_view: gtk::TreeView, } #[repr(u32)] @@ -41,28 +39,28 @@ impl From for i32 { } impl Model { - fn add(&mut self) { - let iter = self.store.append(); - let path = self.store.path(&iter); - let column = self.tree_view.column(Column::Name.into()); + fn add(&mut self, widgets: &ModelWidgets) { + let iter = widgets.store.append(); + let path = widgets.store.path(&iter); + let column = widgets.tree_view.column(Column::Name.into()); self.keywords .insert(path.clone(), (String::new(), String::new())); - gtk::prelude::TreeViewExt::set_cursor(&self.tree_view, &path, column.as_ref(), true); + gtk::prelude::TreeViewExt::set_cursor(&widgets.tree_view, &path, column.as_ref(), true); } - fn delete(&mut self, sender: relm4::ComponentSender) { - let selection = self.tree_view.selection(); + fn delete(&mut self, widgets: &ModelWidgets, sender: relm4::ComponentSender) { + let selection = widgets.tree_view.selection(); let (rows, _) = selection.selected_rows(); let references = rows .iter() - .map(|x| gtk::TreeRowReference::new(&self.store, x)); + .map(|x| gtk::TreeRowReference::new(&widgets.store, x)); for reference in references.flatten() { if let Some(path) = reference.path() { self.keywords.remove(&path); - if let Some(iter) = self.store.iter(&path) { - self.store.remove(&iter); + if let Some(iter) = widgets.store.iter(&path) { + widgets.store.remove(&iter); } } } @@ -72,6 +70,7 @@ impl Model { fn edit( &mut self, + widgets: &ModelWidgets, sender: relm4::ComponentSender, column: Column, path: >k::TreePath, @@ -84,8 +83,9 @@ impl Model { } } - let iter = self.store.iter(path).unwrap(); - self.store + let iter = widgets.store.iter(path).unwrap(); + widgets + .store .set_value(&iter, column.into(), &new_text.to_value()); sender.output(MsgOutput::Updated(self.keywords())).ok(); @@ -95,19 +95,21 @@ impl Model { self.keywords.values().cloned().collect() } - fn set(&mut self, tags: std::collections::BTreeMap) { + fn set(&mut self, widgets: &ModelWidgets, tags: std::collections::BTreeMap) { self.keywords.clear(); - self.store.clear(); + widgets.store.clear(); for (name, value) in tags { - let iter = self.store.append(); + let iter = widgets.store.append(); - self.store + widgets + .store .set_value(&iter, Column::Name.into(), &name.to_value()); - self.store + widgets + .store .set_value(&iter, Column::Value.into(), &value.to_value()); - let path = self.store.path(&iter); + let path = widgets.store.path(&iter); self.keywords.insert(path, (name, value)); } @@ -115,13 +117,14 @@ impl Model { } #[relm4::component(pub)] -impl relm4::SimpleComponent for Model { +impl relm4::Component for Model { + type CommandOutput = (); type Init = std::collections::BTreeMap; type Input = MsgInput; type Output = MsgOutput; fn init( - _init: Self::Init, + _: Self::Init, root: Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { @@ -134,19 +137,18 @@ impl relm4::SimpleComponent for Model { let model = Self { keywords: std::collections::BTreeMap::new(), - store: gtk::ListStore::new(&columns), - tree_view: gtk::TreeView::new(), }; - let tree_view = &model.tree_view; let widgets = view_output!(); - tree_view.set_model(Some(&model.store)); - tree_view.selection().set_mode(gtk::SelectionMode::Multiple); + widgets + .tree_view + .selection() + .set_mode(gtk::SelectionMode::Multiple); let column = gtk::TreeViewColumn::new(); column.set_title("name"); - tree_view.append_column(&column); + widgets.tree_view.append_column(&column); let cell = gtk::CellRendererText::new(); cell.set_editable(true); @@ -165,7 +167,7 @@ impl relm4::SimpleComponent for Model { let column = gtk::TreeViewColumn::new(); column.set_title("value"); - tree_view.append_column(&column); + widgets.tree_view.append_column(&column); let cell = gtk::CellRendererText::new(); cell.set_editable(true); @@ -185,16 +187,22 @@ impl relm4::SimpleComponent for Model { relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, sender: relm4::ComponentSender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + sender: relm4::ComponentSender, + _: &Self::Root, + ) { use MsgInput::*; match msg { - Add => self.add(), - Delete => self.delete(sender), + Add => self.add(widgets), + Delete => self.delete(widgets, sender), Edit(ref column, ref path, ref new_text) => { - self.edit(sender, column.clone(), path, new_text) + self.edit(widgets, sender, column.clone(), path, new_text) } - Set(keywords) => self.set(keywords), + Set(keywords) => self.set(widgets, keywords), } } @@ -205,11 +213,14 @@ impl relm4::SimpleComponent for Model { set_height_request: 150, set_policy: (gtk::PolicyType::Never, gtk::PolicyType::Automatic), - #[local_ref] - tree_view -> gtk::TreeView { + #[name = "tree_view"] + gtk::TreeView { set_headers_visible: true, set_hexpand: true, set_vexpand: true, + #[wrap(Some)] + #[name = "store"] + set_model = >k::ListStore::new(&columns), }, }, gtk::ActionBar { diff --git a/src/widgets/priority.rs b/src/widgets/priority.rs index ce58c7a..9ee3418 100644 --- a/src/widgets/priority.rs +++ b/src/widgets/priority.rs @@ -36,7 +36,7 @@ impl relm4::SimpleComponent for Model { relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: relm4::ComponentSender) { + fn update(&mut self, msg: Self::Input, _: relm4::ComponentSender) { match msg { MsgInput::More => self.show_more = true, } diff --git a/src/widgets/tasks.rs b/src/widgets/tasks.rs index 1673c37..7e74ebb 100644 --- a/src/widgets/tasks.rs +++ b/src/widgets/tasks.rs @@ -7,21 +7,29 @@ pub enum Msg { pub struct Model { children: Vec>, - list_box: gtk::ListBox, } impl Model { - fn update_tasks(&mut self, sender: relm4::ComponentSender, tasks: &[crate::tasks::Task]) { + fn update_tasks( + &mut self, + widgets: &ModelWidgets, + sender: relm4::ComponentSender, + tasks: &[crate::tasks::Task], + ) { use relm4::Component as _; use relm4::ComponentController as _; - self.clear(); + self.clear(widgets); if tasks.is_empty() { - self.list_box.set_visible(false); + widgets.label.set_visible(true); + widgets.list_box.set_visible(false); return; } + widgets.label.set_visible(false); + widgets.list_box.set_visible(true); + let mut sorted_tasks = tasks.to_owned(); sorted_tasks.sort(); sorted_tasks.reverse(); @@ -31,36 +39,32 @@ impl Model { .launch(task.clone()) .forward(sender.output_sender(), std::convert::identity); - self.list_box.append(child.widget()); + widgets.list_box.append(child.widget()); self.children.push(child); } } - fn clear(&mut self) { - self.list_box.remove_all(); + fn clear(&mut self, widgets: &ModelWidgets) { + widgets.list_box.remove_all(); self.children = Vec::new(); } } #[relm4::component(pub)] -impl relm4::SimpleComponent for Model { +impl relm4::Component for Model { + type CommandOutput = (); type Init = (); type Input = Msg; type Output = crate::widgets::task::MsgOutput; fn init( - _init: Self::Init, + _: Self::Init, root: Self::Root, _sender: relm4::ComponentSender, ) -> relm4::ComponentParts { - let list_box = gtk::ListBox::new(); - list_box.set_hexpand(true); - list_box.set_vexpand(true); - let model = Self { children: Vec::new(), - list_box, }; let widgets = view_output!(); @@ -68,25 +72,33 @@ impl relm4::SimpleComponent for Model { relm4::ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, sender: relm4::ComponentSender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + sender: relm4::ComponentSender, + _: &Self::Root, + ) { use Msg::*; match msg { - Update(tasks) => self.update_tasks(sender, &tasks), + Update(tasks) => self.update_tasks(widgets, sender, &tasks), } } view! { gtk::ScrolledWindow { gtk::Box { - append: &model.list_box, - - gtk::Label { - #[watch] - set_visible: model.children.is_empty(), + #[name = "list_box"] + gtk::ListBox { set_hexpand: true, set_vexpand: true, + }, + #[name = "label"] + gtk::Label { + set_hexpand: true, set_text: "Nothing to do :)", + set_vexpand: true, }, }, },