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(self, container: &impl RelmContainerExt) -> Self

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

Examples found in repository?
relm4/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
fn main() {
    gtk::Application::builder()
        .application_id("org.relm4.SettingsListExample")
        .launch(|_app, window| {
            // Initialize a component's root widget
            let 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();
                    }
                });

            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?
relm4/examples/transient_dialog.rs (line 106)
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    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
relm4/examples/components.rs (line 182)
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    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 }
    }
relm4/examples/message_stream.rs (line 143)
137
138
139
140
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
    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
                    }
                });
            }
        }
    }
relm4-components/examples/alert.rs (line 106)
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
    fn init(
        _: Self::Init,
        root: &Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let model = App {
            counter: 0,
            alert_toggle: false,
            dialog: Alert::builder()
                .transient_for(root)
                .launch(AlertSettings {
                    text: String::from("Do you want to quit without saving? (First alert)"),
                    secondary_text: Some(String::from("Your counter hasn't reached 42 yet")),
                    confirm_label: String::from("Close without saving"),
                    cancel_label: String::from("Cancel"),
                    option_label: Some(String::from("Save")),
                    is_modal: true,
                    destructive_accept: true,
                })
                .forward(sender.input_sender(), convert_alert_response),
            second_dialog: Alert::builder()
                .transient_for(root)
                .launch(AlertSettings {
                    text: String::from("Do you want to quit without saving? (Second alert)"),
                    secondary_text: Some(String::from("Your counter hasn't reached 42 yet")),
                    confirm_label: String::from("Close without saving"),
                    cancel_label: String::from("Cancel"),
                    option_label: Some(String::from("Save")),
                    is_modal: true,
                    destructive_accept: true,
                })
                .forward(sender.input_sender(), convert_alert_response),
        };

        let widgets = view_output!();

        ComponentParts { model, widgets }
    }
source§

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

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.

Examples found in repository?
relm4-components/examples/file_dialogs.rs (line 97)
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
    fn init(
        _: Self::Init,
        root: &Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let open_dialog = OpenDialog::builder()
            .transient_for_native(root)
            .launch(OpenDialogSettings::default())
            .forward(sender.input_sender(), |response| match response {
                OpenDialogResponse::Accept(path) => Input::OpenResponse(path),
                OpenDialogResponse::Cancel => Input::Ignore,
            });

        let save_dialog = SaveDialog::builder()
            .transient_for_native(root)
            .launch(SaveDialogSettings::default())
            .forward(sender.input_sender(), |response| match response {
                SaveDialogResponse::Accept(path) => Input::SaveResponse(path),
                SaveDialogResponse::Cancel => Input::Ignore,
            });

        let model = App {
            open_dialog,
            save_dialog,
            buffer: gtk::TextBuffer::new(None),
            file_name: None,
            message: None,
        };

        let widgets = view_output!();

        sender.input(Input::ShowMessage(String::from(
            "A simple text editor showing the usage of\n<b>OpenFileDialog</b> and <b>SaveFileDialog</b> components.\n\nStart by clicking <b>Open</b> on the header bar.",
        )));

        ComponentParts { model, widgets }
    }
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?
relm4/examples/transient_dialog.rs (line 144)
138
139
140
141
142
143
144
145
146
147
148
149
    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
relm4-components/examples/web_image.rs (line 71)
66
67
68
69
70
71
72
73
74
75
76
77
78
    fn init(
        _: Self::Init,
        root: &Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let image = WebImage::builder().launch(IMAGES[0].to_owned()).detach();
        let model = App { image, idx: 0 };

        let image = model.image.widget();
        let widgets = view_output!();

        ComponentParts { model, widgets }
    }
relm4/examples/message_broker.rs (line 192)
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
    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 }
    }
relm4-components/examples/open_button.rs (lines 51-56)
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    fn init(
        _: Self::Init,
        root: &Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let open_button = OpenButton::builder()
            .launch(OpenButtonSettings {
                dialog_settings: OpenDialogSettings::default(),
                text: "Open file",
                recently_opened_files: Some(".recent_files"),
                max_recent_files: 10,
            })
            .forward(sender.input_sender(), AppMsg::Open);
        let model = App { open_button };

        let widgets = view_output!();

        ComponentParts { model, widgets }
    }
relm4/examples/components.rs (line 179)
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    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 }
    }
relm4-components/examples/file_dialogs.rs (line 98)
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
    fn init(
        _: Self::Init,
        root: &Self::Root,
        sender: ComponentSender<Self>,
    ) -> ComponentParts<Self> {
        let open_dialog = OpenDialog::builder()
            .transient_for_native(root)
            .launch(OpenDialogSettings::default())
            .forward(sender.input_sender(), |response| match response {
                OpenDialogResponse::Accept(path) => Input::OpenResponse(path),
                OpenDialogResponse::Cancel => Input::Ignore,
            });

        let save_dialog = SaveDialog::builder()
            .transient_for_native(root)
            .launch(SaveDialogSettings::default())
            .forward(sender.input_sender(), |response| match response {
                SaveDialogResponse::Accept(path) => Input::SaveResponse(path),
                SaveDialogResponse::Cancel => Input::Ignore,
            });

        let model = App {
            open_dialog,
            save_dialog,
            buffer: gtk::TextBuffer::new(None),
            file_name: None,
            message: None,
        };

        let widgets = view_output!();

        sender.input(Input::ShowMessage(String::from(
            "A simple text editor showing the usage of\n<b>OpenFileDialog</b> and <b>SaveFileDialog</b> components.\n\nStart by clicking <b>Open</b> on the header bar.",
        )));

        ComponentParts { model, widgets }
    }
source

pub fn launch_with_broker( self, payload: C::Init, broker: &MessageBroker<C> ) -> 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?
relm4/examples/message_broker.rs (line 188)
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
    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
relm4/examples/transient_dialog.rs (line 107)
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    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?
relm4/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> RefUnwindSafe for ComponentBuilder<C>where C: RefUnwindSafe, <C as Component>::Root: RefUnwindSafe,

§

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 Twhere 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 Twhere T: ?Sized,

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

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

const: unstable · source§

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

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · 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 Twhere U: From<T>,

const: unstable · 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> Position<()> for C

source§

fn position(&self, _index: usize)

Returns the position. Read more
source§

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

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

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

Performs the conversion.
source§

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

§

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

The type returned in the event of a conversion error.
const: unstable · 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