use relm4::gtk::{glib, prelude::*}; use relm4::{gtk, ComponentParts, ComponentSender, SimpleComponent}; pub struct McduModel { query: String, query_changed_by_mcdu_keys: bool, } impl McduModel { fn new() -> Self { McduModel { query: String::new(), query_changed_by_mcdu_keys: false, } } } #[derive(Debug)] pub enum McduOutput { SearchStarted(String), } #[derive(Debug)] pub enum KeyboardEvent { ButtonPress(char), ClrButtonPress, SearchStarted(String), QueryChanged(String), } #[relm4::component(pub)] impl SimpleComponent for McduModel { type Init = (); type Input = KeyboardEvent; type Output = McduOutput; view! { #[root] gtk::Box { set_orientation: gtk::Orientation::Vertical, set_valign: gtk::Align::Center, gtk::SearchEntry { set_margin_bottom: 10, #[block_signal(change_handler, search_handler)] #[track = "model.query_changed_by_mcdu_keys"] set_text: &model.query, connect_changed[sender] => move |search_entry| sender.input(KeyboardEvent::QueryChanged(search_entry.text().to_string())) @change_handler, connect_search_changed[sender] => move |search_entry| sender.input(KeyboardEvent::SearchStarted(search_entry.text().to_string())) @search_handler, }, #[name = "keyboard"] gtk::Grid { set_column_homogeneous: true, set_row_homogeneous: true, set_column_spacing: 5, set_row_spacing: 5, } } } fn init( _init: Self::Init, root: Self::Root, sender: ComponentSender, ) -> ComponentParts { let model = McduModel::new(); let widgets = view_output!(); let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; let mcdu_width = 6; for (i, c) in alphabet.char_indices() { let button = gtk::Button::default(); button.set_label(&c.to_string()); button.connect_clicked(glib::clone!(@strong sender => move |_| { sender.input(KeyboardEvent::ButtonPress(c)); })); let column = i % mcdu_width; let row = (i - column) / mcdu_width; widgets .keyboard .attach(&button, column as i32, row as i32, 1, 1); } let button = gtk::Button::default(); button.set_label("SPC"); button.connect_clicked(glib::clone!(@strong sender => move |_| { sender.input(KeyboardEvent::ButtonPress(' ')); })); let i = alphabet.len(); let column = i % mcdu_width; let row = (i - column) / mcdu_width; widgets .keyboard .attach(&button, column as i32, row as i32, 1, 1); let button = gtk::Button::default(); button.set_label("CLR"); button.connect_clicked(glib::clone!(@strong sender => move |_| { sender.input(KeyboardEvent::ClrButtonPress); })); let i = alphabet.len() + 1; let column = i % mcdu_width; let row = (i - column) / mcdu_width; widgets .keyboard .attach(&button, column as i32, row as i32, 1, 1); ComponentParts { model, widgets } } fn update(&mut self, message: Self::Input, sender: ComponentSender) { self.query_changed_by_mcdu_keys = false; match message { KeyboardEvent::ButtonPress(c) => { self.query_changed_by_mcdu_keys = true; self.query.push(c); } KeyboardEvent::ClrButtonPress => { self.query_changed_by_mcdu_keys = true; self.query.clear(); // Run search since gtk::SearchEntry will not run it if text is empty sender.input(KeyboardEvent::SearchStarted(String::new())); } KeyboardEvent::SearchStarted(s) => { sender.output(McduOutput::SearchStarted(s)).unwrap(); } KeyboardEvent::QueryChanged(query) => self.query = query, } } }