Struct ComponentBuilder

Source
pub struct ComponentBuilder<C: Component> {
    pub root: C::Root,
    /* private fields */
}
Expand description

A component that is ready for docking and launch.

Fields§

§root: C::Root

The root widget of the component.

Implementations§

Source§

impl<C: Component> ComponentBuilder<C>

Source

pub fn update_root<F: FnOnce(&mut C::Root)>(self, func: F) -> Self

Configure the root widget before launching.

Source

pub const fn widget(&self) -> &C::Root

Access the root widget before the component is initialized.

Source

pub fn priority(self, priority: Priority) -> Self

Change the priority at which the messages of this component are handled.

Source§

impl<C: Component> ComponentBuilder<C>
where C::Root: AsRef<Widget>,

Source

pub fn attach_to<T: RelmContainerExt<Child = Widget>>( self, container: &T, ) -> Self

Attach the component’s root widget to a given container.

Examples found in repository?
examples/settings_list.rs (line 25)
18fn main() {
19    gtk::Application::builder()
20        .application_id("org.relm4.SettingsListExample")
21        .launch(|_app, window| {
22            // Initialize a component's root widget
23            let mut component = App::builder()
24                // Attach the root widget to the given window.
25                .attach_to(&window)
26                // Start the component service with an initial parameter
27                .launch("Settings List Demo".into())
28                // Attach the returned receiver's messages to this closure.
29                .connect_receiver(move |sender, message| match message {
30                    Output::Clicked(id) => {
31                        eprintln!("ID {id} Clicked");
32
33                        match id {
34                            0 => xdg_open("https://github.com/Relm4/Relm4".into()),
35                            1 => xdg_open("https://docs.rs/relm4/".into()),
36                            2 => {
37                                sender.send(Input::Clear).unwrap();
38                            }
39                            _ => (),
40                        }
41                    }
42
43                    Output::Reload => {
44                        sender
45                            .send(Input::AddSetting {
46                                description: "Browse GitHub Repository".into(),
47                                button: "GitHub".into(),
48                                id: 0,
49                            })
50                            .unwrap();
51
52                        sender
53                            .send(Input::AddSetting {
54                                description: "Browse Documentation".into(),
55                                button: "Docs".into(),
56                                id: 1,
57                            })
58                            .unwrap();
59
60                        sender
61                            .send(Input::AddSetting {
62                                description: "Clear List".into(),
63                                button: "Clear".into(),
64                                id: 2,
65                            })
66                            .unwrap();
67                    }
68                });
69
70            // Keep runtime alive after the component is dropped
71            component.detach_runtime();
72
73            println!("parent is {:?}", component.widget().toplevel_window());
74        });
75}
Source§

impl<C: Component> ComponentBuilder<C>
where C::Root: AsRef<Window> + Clone,

Source

pub fn transient_for(self, widget: impl AsRef<Widget>) -> Self

Set the component’s root widget transient for a given window. This function doesn’t require a gtk::Window as parameter, but instead uses RelmWidgetExt::toplevel_window() to retrieve the toplevel window of any gtk::Widget. Therefore, you don’t have to pass a window to every component.

If the root widget is a native dialog, such as gtk::FileChooserNative, you should use transient_for_native instead.

Examples found in repository?
examples/transient_dialog.rs (line 111)
101    fn init(
102        _init: Self::Init,
103        root: Self::Root,
104        sender: ComponentSender<Self>,
105    ) -> ComponentParts<Self> {
106        // We don't have access to the parent window from here
107        // but we can just use the button to set the transient window for the dialog.
108        // Relm4 will get the window later by calling [`WidgetExt::root()`]
109        // on the button once all widgets are connected.
110        let dialog = Dialog::builder()
111            .transient_for(&root)
112            .launch_with_broker((), &DIALOG_BROKER)
113            .forward(sender.input_sender(), identity);
114
115        let model = Button { dialog };
116        let widgets = view_output!();
117        ComponentParts { model, widgets }
118    }
More examples
Hide additional examples
examples/components.rs (line 186)
177    fn init(
178        _: Self::Init,
179        root: Self::Root,
180        sender: ComponentSender<Self>,
181    ) -> ComponentParts<Self> {
182        let header = Header::builder()
183            .launch(())
184            .forward(sender.input_sender(), identity);
185        let dialog = Dialog::builder()
186            .transient_for(&root)
187            .launch(DialogInit {
188                text: "Do you want to close before saving?".to_string(),
189                secondary_text: Some("All unsaved changes will be lost".to_string()),
190                accept_text: "Close".to_string(),
191                cancel_text: "Cancel".to_string(),
192            })
193            .forward(sender.input_sender(), identity);
194
195        let model = App {
196            mode: AppMode::View,
197            header,
198            dialog,
199        };
200        let widgets = view_output!();
201
202        ComponentParts { model, widgets }
203    }
examples/message_stream.rs (line 147)
141    fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) {
142        match msg {
143            AppMsg::StartSearch => {
144                self.searching = true;
145
146                let stream = Dialog::builder()
147                    .transient_for(root)
148                    .launch(())
149                    .into_stream();
150                sender.oneshot_command(async move {
151                    // Use the component as stream
152                    let result = stream.recv_one().await;
153
154                    if let Some(search) = result {
155                        let response =
156                            reqwest::get(format!("https://duckduckgo.com/lite/?q={search}"))
157                                .await
158                                .unwrap();
159                        let response_text = response.text().await.unwrap();
160
161                        // Extract the url of the first search result.
162                        if let Some(url) = response_text.split("<a rel=\"nofollow\" href=\"").nth(1)
163                        {
164                            let url = url.split('\"').next().unwrap().replace("amp;", "");
165                            Some(format!("https:{url}"))
166                        } else {
167                            None
168                        }
169                    } else {
170                        None
171                    }
172                });
173            }
174        }
175    }
Source§

impl<C: Component> ComponentBuilder<C>

Source

pub fn transient_for_native(self, widget: impl AsRef<Widget>) -> Self

Set the component’s root widget transient for a given window. This function doesn’t require a gtk::Window as parameter, but instead uses RelmWidgetExt::toplevel_window() to retrieve the toplevel window of any gtk::Widget. Therefore, you don’t have to pass a window to every component.

Applicable to native dialogs only, such as gtk::FileChooserNative. If the root widget is a non-native dialog, you should use transient_for instead.

Source§

impl<C: Component> ComponentBuilder<C>

Source

pub fn launch(self, payload: C::Init) -> Connector<C>

Starts the component, passing ownership to a future attached to a gtk::glib::MainContext.

Examples found in repository?
examples/transient_dialog.rs (line 149)
143    fn init(
144        _init: Self::Init,
145        root: Self::Root,
146        sender: ComponentSender<Self>,
147    ) -> ComponentParts<Self> {
148        let button = Button::builder()
149            .launch(())
150            .forward(sender.input_sender(), identity);
151        let model = App { button };
152        let widgets = view_output!();
153        ComponentParts { model, widgets }
154    }
More examples
Hide additional examples
examples/message_broker.rs (line 196)
186    fn init(
187        _init: Self::Init,
188        root: Self::Root,
189        sender: ComponentSender<Self>,
190    ) -> ComponentParts<Self> {
191        let header = Header::builder()
192            .launch_with_broker((), &HEADER_BROKER)
193            .forward(sender.input_sender(), identity);
194
195        let dialog = Dialog::builder()
196            .launch(root.clone().upcast())
197            .forward(sender.input_sender(), identity);
198
199        let model = App {
200            mode: AppMode::View,
201            header,
202            dialog,
203        };
204
205        let widgets = view_output!();
206
207        ComponentParts { model, widgets }
208    }
examples/multi_window.rs (line 88)
71    fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
72        match msg {
73            Msg::Increment => {
74                self.counter = self.counter.wrapping_add(1);
75            }
76            Msg::Decrement => {
77                self.counter = self.counter.wrapping_sub(1);
78            }
79            Msg::NewWindow => {
80                let app = relm4::main_application();
81                let builder = Self::builder();
82
83                // Add window to the GTK application.
84                // This ensures that the app will live as long
85                // as at least one window exists.
86                app.add_window(&builder.root);
87
88                builder.launch(self.counter).detach_runtime();
89            }
90        }
91    }
examples/components.rs (line 183)
177    fn init(
178        _: Self::Init,
179        root: Self::Root,
180        sender: ComponentSender<Self>,
181    ) -> ComponentParts<Self> {
182        let header = Header::builder()
183            .launch(())
184            .forward(sender.input_sender(), identity);
185        let dialog = Dialog::builder()
186            .transient_for(&root)
187            .launch(DialogInit {
188                text: "Do you want to close before saving?".to_string(),
189                secondary_text: Some("All unsaved changes will be lost".to_string()),
190                accept_text: "Close".to_string(),
191                cancel_text: "Cancel".to_string(),
192            })
193            .forward(sender.input_sender(), identity);
194
195        let model = App {
196            mode: AppMode::View,
197            header,
198            dialog,
199        };
200        let widgets = view_output!();
201
202        ComponentParts { model, widgets }
203    }
examples/message_stream.rs (line 148)
141    fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) {
142        match msg {
143            AppMsg::StartSearch => {
144                self.searching = true;
145
146                let stream = Dialog::builder()
147                    .transient_for(root)
148                    .launch(())
149                    .into_stream();
150                sender.oneshot_command(async move {
151                    // Use the component as stream
152                    let result = stream.recv_one().await;
153
154                    if let Some(search) = result {
155                        let response =
156                            reqwest::get(format!("https://duckduckgo.com/lite/?q={search}"))
157                                .await
158                                .unwrap();
159                        let response_text = response.text().await.unwrap();
160
161                        // Extract the url of the first search result.
162                        if let Some(url) = response_text.split("<a rel=\"nofollow\" href=\"").nth(1)
163                        {
164                            let url = url.split('\"').next().unwrap().replace("amp;", "");
165                            Some(format!("https:{url}"))
166                        } else {
167                            None
168                        }
169                    } else {
170                        None
171                    }
172                });
173            }
174        }
175    }
examples/drop_sub_components.rs (line 74)
62    fn update_with_view(
63        &mut self,
64        widgets: &mut Self::Widgets,
65        message: Self::Input,
66        sender: ComponentSender<Self>,
67        root: &Self::Root,
68    ) {
69        widgets.container.remove_all();
70        match message {
71            Msg::ShowInitialScreen => {
72                let controller =
73                    InitialScreen::builder()
74                        .launch(())
75                        .forward(sender.input_sender(), |msg| match msg {
76                            InitialScreenOutput::ShowSubScreen1 => Msg::ShowSubScreen1,
77                            InitialScreenOutput::ShowSubScreen2 => Msg::ShowSubScreen2,
78                        });
79                widgets.container.append(controller.widget());
80                self.mode = Some(AppMode::Initial(controller));
81            }
82            Msg::ShowSubScreen1 => {
83                let controller = SubScreen1::builder()
84                    .launch(())
85                    .forward(sender.input_sender(), |_| Msg::ShowInitialScreen);
86                widgets.container.append(controller.widget());
87                self.mode = Some(AppMode::SubScreen1(controller));
88            }
89            Msg::ShowSubScreen2 => {
90                let controller = SubScreen2::builder()
91                    .launch(())
92                    .forward(sender.input_sender(), |_| Msg::ShowInitialScreen);
93                widgets.container.append(controller.widget());
94                self.mode = Some(AppMode::SubScreen2(controller));
95            }
96        }
97        root.set_default_size(400, 300);
98    }
Source

pub fn launch_with_broker( self, payload: C::Init, broker: &MessageBroker<C::Input>, ) -> Connector<C>

Similar to launch() but also initializes a MessageBroker.

§Panics

This method panics if the message broker was already initialized in another launch.

Examples found in repository?
examples/message_broker.rs (line 192)
186    fn init(
187        _init: Self::Init,
188        root: Self::Root,
189        sender: ComponentSender<Self>,
190    ) -> ComponentParts<Self> {
191        let header = Header::builder()
192            .launch_with_broker((), &HEADER_BROKER)
193            .forward(sender.input_sender(), identity);
194
195        let dialog = Dialog::builder()
196            .launch(root.clone().upcast())
197            .forward(sender.input_sender(), identity);
198
199        let model = App {
200            mode: AppMode::View,
201            header,
202            dialog,
203        };
204
205        let widgets = view_output!();
206
207        ComponentParts { model, widgets }
208    }
More examples
Hide additional examples
examples/transient_dialog.rs (line 112)
101    fn init(
102        _init: Self::Init,
103        root: Self::Root,
104        sender: ComponentSender<Self>,
105    ) -> ComponentParts<Self> {
106        // We don't have access to the parent window from here
107        // but we can just use the button to set the transient window for the dialog.
108        // Relm4 will get the window later by calling [`WidgetExt::root()`]
109        // on the button once all widgets are connected.
110        let dialog = Dialog::builder()
111            .transient_for(&root)
112            .launch_with_broker((), &DIALOG_BROKER)
113            .forward(sender.input_sender(), identity);
114
115        let model = Button { dialog };
116        let widgets = view_output!();
117        ComponentParts { model, widgets }
118    }
Source§

impl<C> ComponentBuilder<C>
where C: Component<Root = (), Widgets = ()> + Send, C::Input: Send, C::Output: Send, C::CommandOutput: Send,

Source

pub fn detach_worker(self, payload: C::Init) -> WorkerHandle<C>

Starts a worker on a separate thread, passing ownership to a future attached to a gtk::glib::MainContext.

Examples found in repository?
examples/worker.rs (line 96)
88    fn init(
89        _: Self::Init,
90        root: Self::Root,
91        sender: ComponentSender<Self>,
92    ) -> ComponentParts<Self> {
93        let model = App {
94            counter: 0,
95            worker: AsyncHandler::builder()
96                .detach_worker(())
97                .forward(sender.input_sender(), identity),
98        };
99
100        let widgets = view_output!();
101
102        ComponentParts { model, widgets }
103    }

Trait Implementations§

Source§

impl<C: Debug + Component> Debug for ComponentBuilder<C>
where C::Root: Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<C: Component> Default for ComponentBuilder<C>

Source§

fn default() -> Self

Prepares a component for initialization.

Auto Trait Implementations§

§

impl<C> Freeze for ComponentBuilder<C>
where <C as Component>::Root: Freeze,

§

impl<C> RefUnwindSafe for ComponentBuilder<C>

§

impl<C> Send for ComponentBuilder<C>
where <C as Component>::Root: Send, C: Send,

§

impl<C> Sync for ComponentBuilder<C>
where <C as Component>::Root: Sync, C: Sync,

§

impl<C> Unpin for ComponentBuilder<C>
where <C as Component>::Root: Unpin, C: Unpin,

§

impl<C> UnwindSafe for ComponentBuilder<C>
where <C as Component>::Root: UnwindSafe, C: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<C> AsyncPosition<()> for C

Source§

fn position(_index: usize)

Returns the position. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<C, I> Position<(), I> for C

Source§

fn position(&self, _index: &I)

Returns the position. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more