entry/
entry.rs

1use gtk::prelude::*;
2use relm4::factory::FactoryVecDeque;
3use relm4::prelude::*;
4
5#[derive(Debug)]
6enum AppMsg {
7    AddCounters,
8    Clicked(DynamicIndex),
9}
10
11struct Counter {
12    value: u8,
13}
14
15struct App {
16    counters: FactoryVecDeque<Counter>,
17    created_counters: u8,
18    // stores entered values
19    entry: gtk::EntryBuffer,
20}
21
22#[relm4::factory]
23impl FactoryComponent for Counter {
24    type Init = u8;
25    type Input = ();
26    type Output = AppMsg;
27    type CommandOutput = ();
28    type ParentWidget = gtk::Box;
29
30    view! {
31        gtk::Button {
32            #[watch]
33            set_label: &self.value.to_string(),
34            connect_clicked[index] => move |_| {
35                sender.output(AppMsg::Clicked(index.clone())).unwrap();
36            },
37        }
38    }
39
40    fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
41        Self { value }
42    }
43}
44
45#[relm4::component]
46impl SimpleComponent for App {
47    type Init = ();
48    type Input = AppMsg;
49    type Output = ();
50
51    view! {
52        gtk::ApplicationWindow {
53            set_title: Some("Entry example"),
54            set_default_size: (300, 200),
55
56            gtk::Box {
57                set_orientation: gtk::Orientation::Vertical,
58                set_margin_all: 5,
59                set_spacing: 5,
60
61                gtk::Entry {
62                    set_buffer: &model.entry,
63                    set_tooltip_text: Some("How many counters shall be added/removed?"),
64                    connect_activate => AppMsg::AddCounters,
65                },
66
67                #[local]
68                factory_box -> gtk::Box {
69                    set_orientation: gtk::Orientation::Vertical,
70                    set_margin_all: 5,
71                    set_spacing: 5,
72                },
73            }
74        }
75    }
76
77    fn init(
78        _init: Self::Init,
79        root: Self::Root,
80        sender: ComponentSender<Self>,
81    ) -> ComponentParts<Self> {
82        let factory_box = gtk::Box::default();
83
84        let counters = FactoryVecDeque::builder()
85            .launch(factory_box.clone())
86            .forward(sender.input_sender(), |output| output);
87
88        let model = App {
89            counters,
90            created_counters: 0,
91            entry: gtk::EntryBuffer::default(),
92        };
93
94        let widgets = view_output!();
95
96        ComponentParts { model, widgets }
97    }
98
99    fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
100        match message {
101            AppMsg::AddCounters => {
102                let text = self.entry.text();
103                if let Ok(v) = text.parse::<i32>() {
104                    let mut guard = self.counters.guard();
105                    if v.is_positive() {
106                        // add as many counters as user entered
107                        for _ in 0..v {
108                            guard.push_back(self.created_counters);
109                            self.created_counters += 1;
110                        }
111                    } else if v.is_negative() {
112                        // remove counters
113                        for _ in v..0 {
114                            guard.pop_front();
115                        }
116                    }
117
118                    // clearing the entry value clears the entry widget
119                    self.entry.set_text("");
120                }
121            }
122            AppMsg::Clicked(index) => {
123                if let Some(counter) = self.counters.guard().get_mut(index.current_index()) {
124                    counter.value = counter.value.wrapping_sub(1);
125                }
126            }
127        }
128    }
129}
130
131fn main() {
132    let app = RelmApp::new("relm4.example.entry");
133    app.run::<App>(());
134}