// Catch potential problems with clippy and the macro #![warn( missing_debug_implementations, rust_2018_idioms, unreachable_pub, unused_qualifications, clippy::cargo, clippy::must_use_candidate, clippy::used_underscore_binding )] use gtk::prelude::{ BoxExt, ButtonExt, GridExt, GtkWindowExt, ObjectExt, OrientableExt, ToggleButtonExt, WidgetExt, }; use relm4::{ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent}; #[tracker::track] struct App { value: u8, } #[derive(Debug)] enum Msg { Increment, Decrement, } struct AppInit { counter: u8, } #[relm4::component(pub)] impl SimpleComponent for App { type Init = AppInit; type Input = Msg; type Output = (); // AppWidgets is generated by the macro, // Can be omitted in most cases. type Widgets = AppWidgets; view! { #[root] #[name(main_window)] gtk::Window { set_title: Some("Macro reference example"), set_default_size: (300, 100), gtk::Box { set_orientation: gtk::Orientation::Vertical, set_spacing: 5, set_margin_all: 5, append: inc_button = >k::Button { set_label: "Increment", // Only set this if `icon_name` is Some set_icon_name?: icon_name, connect_clicked[sender] => move |_| { sender.input(Msg::Increment); } }, gtk::Button { set_label: "Decrement", connect_clicked => Msg::Decrement, }, gtk::Grid { attach[1, 1, 1, 1] = >k::Label { set_label: "Count to 10 to see if the tracker works!", // Alternative: #[track = "counter.value % 10 == 0"] #[track(skip_init, counter.value % 10 == 0)] set_label: &format!("Grid works! ({})", counter.value), } }, // A conditional widget // Alternative: #[transition = "SlideLeft"] #[transition(SlideLeft)] append = if counter.value % 2 == 0 { gtk::Label { set_label: "Value is even", } } else if counter.value % 3 == 0 { gtk::Label { set_label: "Value is dividable by 3", } } else { gtk::Label { set_label: "Value is odd", } }, #[transition = "SlideRight"] append: match_stack = match counter.value { (0..=2) => { gtk::Label { set_label: "Value is smaller than 3", } }, _ => { gtk::Label { set_label: "Value is higher than 2", } }, }, append = >k::Label, gtk::Label::builder() .label("Builder pattern works!") .selectable(true) .build(), gtk::Label::new(Some("Constructors work!")), /// Counter label #[name = "counter_label"] gtk::Label { // Mirror property "label" for widget "bind_label" #[chain(build())] bind_property: ("label", &bind_label, "label"), set_label: "Click the counter so see the value!", #[watch(skip_init)] set_label: &format!("Counter: {}", counter.value), #[track] set_margin_all: counter.value.into(), }, #[name = "bind_label"] gtk::Label {}, // You can also use returned widgets gtk::Stack { add_child = >k::Label { set_label: "placeholder", } -> { // Set the title of the stack page set_title: "page title", } }, gtk::ToggleButton { set_label: "Counter is even", #[watch] #[block_signal(toggle_handler)] set_active: counter.value % 2 == 0, connect_toggled[sender] => move |_| { sender.input(Msg::Increment); } @toggle_handler, }, #[local] local_label -> gtk::Label { set_opacity: 0.7, }, #[local_ref] local_ref_label -> gtk::Label { set_opacity: 0.7, set_size_request: (40, 40), }, } }, gtk::Window { set_title: Some("Another window"), set_default_size: (300, 100), set_transient_for: Some(&main_window), set_visible: false, // Empty args grab_focus: (), #[watch] set_visible: counter.value == 42, #[name = "my_label_name"] gtk::Label { set_label: "You made it to 42!", } } } // Initialize the component. fn init( init: Self::Init, renamed_root: Self::Root, sender: ComponentSender, ) -> ComponentParts { let counter = App { value: init.counter, tracker: 0, }; // Set icon name randomly to Some("go-up-symbolic") or None let icon_name = rand::random::().then_some("go-up-symbolic"); let local_label = gtk::Label::new(Some("local_label")); let local_ref_label_value = gtk::Label::new(Some("local_ref_label")); let local_ref_label = &local_ref_label_value; // Insert the macro code generation here let widgets = view_output!(); ComponentParts { model: counter, widgets, } } fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { self.reset(); match msg { Msg::Increment => { self.set_value(self.value.wrapping_add(1)); } Msg::Decrement => { self.set_value(self.value.wrapping_sub(1)); } } } } fn main() { let app = RelmApp::new("relm4.example.macro_reference"); app.run::(AppInit { counter: 0 }); }