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)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
fn main() {
    gtk::Application::builder()
        .application_id("org.relm4.SettingsListExample")
        .launch(|_app, window| {
            // Initialize a component's root widget
            let mut component = App::builder()
                // Attach the root widget to the given window.
                .attach_to(&window)
                // Start the component service with an initial parameter
                .launch("Settings List Demo".into())
                // Attach the returned receiver's messages to this closure.
                .connect_receiver(move |sender, message| match message {
                    Output::Clicked(id) => {
                        eprintln!("ID {id} Clicked");

                        match id {
                            0 => xdg_open("https://github.com/Relm4/Relm4".into()),
                            1 => xdg_open("https://docs.rs/relm4/".into()),
                            2 => {
                                sender.send(Input::Clear).unwrap();
                            }
                            _ => (),
                        }
                    }

                    Output::Reload => {
                        sender
                            .send(Input::AddSetting {
                                description: "Browse GitHub Repository".into(),
                                button: "GitHub".into(),
                                id: 0,
                            })
                            .unwrap();

                        sender
                            .send(Input::AddSetting {
                                description: "Browse Documentation".into(),
                                button: "Docs".into(),
                                id: 1,
                            })
                            .unwrap();

                        sender
                            .send(Input::AddSetting {
                                description: "Clear List".into(),
                                button: "Clear".into(),
                                id: 2,
                            })
                            .unwrap();
                    }
                });

            // Keep runtime alive after the component is dropped
            component.detach_runtime();

            println!("parent is {:?}", component.widget().toplevel_window());
        });
}
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    fn init(
        _init: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        // We don't have access to the parent window from here
        // but we can just use the button to set the transient window for the dialog.
        // Relm4 will get the window later by calling [`WidgetExt::root()`]
        // on the button once all widgets are connected.
        let dialog = Dialog::builder()
            .transient_for(&root)
            .launch_with_broker((), &DIALOG_BROKER)
            .forward(sender.input_sender(), identity);

        let model = Button { dialog };
        let widgets = view_output!();
        ComponentParts { model, widgets }
    }
More examples
Hide additional examples
examples/components.rs (line 186)
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
    fn init(
        _: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let header = Header::builder()
            .launch(())
            .forward(sender.input_sender(), identity);
        let dialog = Dialog::builder()
            .transient_for(&root)
            .launch(DialogInit {
                text: "Do you want to close before saving?".to_string(),
                secondary_text: Some("All unsaved changes will be lost".to_string()),
                accept_text: "Close".to_string(),
                cancel_text: "Cancel".to_string(),
            })
            .forward(sender.input_sender(), identity);

        let model = App {
            mode: AppMode::View,
            header,
            dialog,
        };
        let widgets = view_output!();

        ComponentParts { model, widgets }
    }
examples/message_stream.rs (line 147)
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
    fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) {
        match msg {
            AppMsg::StartSearch => {
                self.searching = true;

                let stream = Dialog::builder()
                    .transient_for(root)
                    .launch(())
                    .into_stream();
                sender.oneshot_command(async move {
                    // Use the component as stream
                    let result = stream.recv_one().await;

                    if let Some(search) = result {
                        let response =
                            reqwest::get(format!("https://duckduckgo.com/lite/?q={search}"))
                                .await
                                .unwrap();
                        let response_text = response.text().await.unwrap();

                        // Extract the url of the first search result.
                        if let Some(url) = response_text.split("<a rel=\"nofollow\" href=\"").nth(1)
                        {
                            let url = url.split('\"').next().unwrap().replace("amp;", "");
                            Some(format!("https:{url}"))
                        } else {
                            None
                        }
                    } else {
                        None
                    }
                });
            }
        }
    }
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
144
145
146
147
148
149
150
151
152
153
154
    fn init(
        _init: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let button = Button::builder()
            .launch(())
            .forward(sender.input_sender(), identity);
        let model = App { button };
        let widgets = view_output!();
        ComponentParts { model, widgets }
    }
More examples
Hide additional examples
examples/message_broker.rs (line 196)
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    fn init(
        _init: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let header = Header::builder()
            .launch_with_broker((), &HEADER_BROKER)
            .forward(sender.input_sender(), identity);

        let dialog = Dialog::builder()
            .launch(root.clone().upcast())
            .forward(sender.input_sender(), identity);

        let model = App {
            mode: AppMode::View,
            header,
            dialog,
        };

        let widgets = view_output!();

        ComponentParts { model, widgets }
    }
examples/components.rs (line 183)
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
    fn init(
        _: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let header = Header::builder()
            .launch(())
            .forward(sender.input_sender(), identity);
        let dialog = Dialog::builder()
            .transient_for(&root)
            .launch(DialogInit {
                text: "Do you want to close before saving?".to_string(),
                secondary_text: Some("All unsaved changes will be lost".to_string()),
                accept_text: "Close".to_string(),
                cancel_text: "Cancel".to_string(),
            })
            .forward(sender.input_sender(), identity);

        let model = App {
            mode: AppMode::View,
            header,
            dialog,
        };
        let widgets = view_output!();

        ComponentParts { model, widgets }
    }
examples/message_stream.rs (line 148)
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
    fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>, root: &Self::Root) {
        match msg {
            AppMsg::StartSearch => {
                self.searching = true;

                let stream = Dialog::builder()
                    .transient_for(root)
                    .launch(())
                    .into_stream();
                sender.oneshot_command(async move {
                    // Use the component as stream
                    let result = stream.recv_one().await;

                    if let Some(search) = result {
                        let response =
                            reqwest::get(format!("https://duckduckgo.com/lite/?q={search}"))
                                .await
                                .unwrap();
                        let response_text = response.text().await.unwrap();

                        // Extract the url of the first search result.
                        if let Some(url) = response_text.split("<a rel=\"nofollow\" href=\"").nth(1)
                        {
                            let url = url.split('\"').next().unwrap().replace("amp;", "");
                            Some(format!("https:{url}"))
                        } else {
                            None
                        }
                    } else {
                        None
                    }
                });
            }
        }
    }
examples/settings_list.rs (line 27)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
fn main() {
    gtk::Application::builder()
        .application_id("org.relm4.SettingsListExample")
        .launch(|_app, window| {
            // Initialize a component's root widget
            let mut component = App::builder()
                // Attach the root widget to the given window.
                .attach_to(&window)
                // Start the component service with an initial parameter
                .launch("Settings List Demo".into())
                // Attach the returned receiver's messages to this closure.
                .connect_receiver(move |sender, message| match message {
                    Output::Clicked(id) => {
                        eprintln!("ID {id} Clicked");

                        match id {
                            0 => xdg_open("https://github.com/Relm4/Relm4".into()),
                            1 => xdg_open("https://docs.rs/relm4/".into()),
                            2 => {
                                sender.send(Input::Clear).unwrap();
                            }
                            _ => (),
                        }
                    }

                    Output::Reload => {
                        sender
                            .send(Input::AddSetting {
                                description: "Browse GitHub Repository".into(),
                                button: "GitHub".into(),
                                id: 0,
                            })
                            .unwrap();

                        sender
                            .send(Input::AddSetting {
                                description: "Browse Documentation".into(),
                                button: "Docs".into(),
                                id: 1,
                            })
                            .unwrap();

                        sender
                            .send(Input::AddSetting {
                                description: "Clear List".into(),
                                button: "Clear".into(),
                                id: 2,
                            })
                            .unwrap();
                    }
                });

            // Keep runtime alive after the component is dropped
            component.detach_runtime();

            println!("parent is {:?}", component.widget().toplevel_window());
        });
}
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    fn init(
        _init: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let header = Header::builder()
            .launch_with_broker((), &HEADER_BROKER)
            .forward(sender.input_sender(), identity);

        let dialog = Dialog::builder()
            .launch(root.clone().upcast())
            .forward(sender.input_sender(), identity);

        let model = App {
            mode: AppMode::View,
            header,
            dialog,
        };

        let widgets = view_output!();

        ComponentParts { model, widgets }
    }
More examples
Hide additional examples
examples/transient_dialog.rs (line 112)
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    fn init(
        _init: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        // We don't have access to the parent window from here
        // but we can just use the button to set the transient window for the dialog.
        // Relm4 will get the window later by calling [`WidgetExt::root()`]
        // on the button once all widgets are connected.
        let dialog = Dialog::builder()
            .transient_for(&root)
            .launch_with_broker((), &DIALOG_BROKER)
            .forward(sender.input_sender(), identity);

        let model = Button { dialog };
        let widgets = view_output!();
        ComponentParts { model, widgets }
    }
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    fn init(
        _: Self::Init,
        root: Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let model = App {
            counter: 0,
            worker: AsyncHandler::builder()
                .detach_worker(())
                .forward(sender.input_sender(), identity),
        };

        let widgets = view_output!();

        ComponentParts { model, widgets }
    }

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: Send, <C as Component>::Root: Send,

§

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

§

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

§

impl<C> UnwindSafe for ComponentBuilder<C>
where C: UnwindSafe, <C as Component>::Root: 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>,

§

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>,

§

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