use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt}; use relm4::factory::{DynamicIndex, FactoryComponent, FactorySender, FactoryVecDeque}; use relm4::{ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent}; #[derive(Debug)] struct Counter { value: u8, } #[derive(Debug)] enum CounterMsg { Increment, Decrement, } #[derive(Debug)] enum CounterOutput { SendFront(DynamicIndex), MoveUp(DynamicIndex), MoveDown(DynamicIndex), Remove(DynamicIndex), } #[relm4::factory] impl FactoryComponent for Counter { type Init = u8; type Input = CounterMsg; type Output = CounterOutput; type CommandOutput = (); type ParentWidget = gtk::Box; view! { root = gtk::Box { set_orientation: gtk::Orientation::Horizontal, set_spacing: 10, #[name(label)] gtk::Label { #[watch] set_label: &self.value.to_string(), set_width_chars: 3, }, #[name(add_button)] gtk::Button { set_label: "+", connect_clicked => CounterMsg::Increment, }, #[name(remove_button)] gtk::Button { set_label: "-", connect_clicked => CounterMsg::Decrement, }, #[name(move_up_button)] gtk::Button { set_label: "Up", connect_clicked[sender, index] => move |_| { sender.output(CounterOutput::MoveUp(index.clone())).unwrap(); } }, #[name(move_down_button)] gtk::Button { set_label: "Down", connect_clicked[sender, index] => move |_| { sender.output(CounterOutput::MoveDown(index.clone())).unwrap(); } }, #[name(to_front_button)] gtk::Button { set_label: "To Start", connect_clicked[sender, index] => move |_| { sender.output(CounterOutput::SendFront(index.clone())).unwrap(); } }, gtk::Button { set_label: "Remove", connect_clicked[sender, index] => move |_| { sender.output(CounterOutput::Remove(index.clone())).unwrap(); } } } } fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender) -> Self { Self { value } } fn update(&mut self, msg: Self::Input, _sender: FactorySender) { match msg { CounterMsg::Increment => { self.value = self.value.wrapping_add(1); } CounterMsg::Decrement => { self.value = self.value.wrapping_sub(1); } } } fn shutdown(&mut self, _widgets: &mut Self::Widgets, _output: relm4::Sender) { println!("Counter with value {} was destroyed", self.value); } } struct App { created_widgets: u8, counters: FactoryVecDeque, } #[derive(Debug)] enum AppMsg { AddCounter, RemoveCounter, SendFront(DynamicIndex), MoveUp(DynamicIndex), MoveDown(DynamicIndex), Remove(DynamicIndex), } #[relm4::component] impl SimpleComponent for App { type Init = u8; type Input = AppMsg; type Output = (); view! { gtk::Window { set_title: Some("Factory example"), set_default_size: (300, 100), gtk::Box { set_orientation: gtk::Orientation::Vertical, set_spacing: 5, set_margin_all: 5, gtk::Button { set_label: "Add counter", connect_clicked => AppMsg::AddCounter, }, gtk::Button { set_label: "Remove counter", connect_clicked => AppMsg::RemoveCounter, }, #[local_ref] counter_box -> gtk::Box { set_orientation: gtk::Orientation::Vertical, set_spacing: 5, } } } } fn init( counter: Self::Init, root: Self::Root, sender: ComponentSender, ) -> ComponentParts { let counters = FactoryVecDeque::builder() .launch_default() .forward(sender.input_sender(), |msg| match msg { CounterOutput::SendFront(index) => AppMsg::SendFront(index), CounterOutput::MoveUp(index) => AppMsg::MoveUp(index), CounterOutput::MoveDown(index) => AppMsg::MoveDown(index), CounterOutput::Remove(index) => AppMsg::Remove(index), }); let model = App { created_widgets: counter, counters, }; let counter_box = model.counters.widget(); let widgets = view_output!(); ComponentParts { model, widgets } } fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { let mut counters_guard = self.counters.guard(); match msg { AppMsg::AddCounter => { counters_guard.push_back(self.created_widgets); self.created_widgets = self.created_widgets.wrapping_add(1); } AppMsg::RemoveCounter => { counters_guard.pop_back(); } AppMsg::SendFront(index) => { counters_guard.move_front(index.current_index()); } AppMsg::MoveDown(index) => { let index = index.current_index(); let new_index = index + 1; // Already at the end? if new_index < counters_guard.len() { counters_guard.move_to(index, new_index); } } AppMsg::MoveUp(index) => { let index = index.current_index(); // Already at the start? if index != 0 { counters_guard.move_to(index, index - 1); } } AppMsg::Remove(index) => { counters_guard.remove(index.current_index()); } } } } fn main() { let app = RelmApp::new("relm4.example.factory"); app.run::(0); }