ComponentBuilder

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?
relm4/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 = AppModel::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?
relm4/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
relm4/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    }
relm4/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    }
relm4-components/examples/alert.rs (line 107)
98    fn init(
99        _: Self::Init,
100        root: Self::Root,
101        sender: ComponentSender<Self>,
102    ) -> ComponentParts<Self> {
103        let model = App {
104            counter: 0,
105            alert_toggle: false,
106            dialog: Alert::builder()
107                .transient_for(&root)
108                .launch(AlertSettings {
109                    text: Some(String::from(
110                        "Do you want to quit without saving? (First alert)",
111                    )),
112                    secondary_text: Some(String::from("Your counter hasn't reached 42 yet")),
113                    confirm_label: Some(String::from("Close without saving")),
114                    cancel_label: Some(String::from("Cancel")),
115                    option_label: Some(String::from("Save")),
116                    is_modal: true,
117                    destructive_accept: true,
118                    extra_child: Some(gtk::Button::with_label("Button in Alert").into()),
119                })
120                .forward(sender.input_sender(), convert_alert_response),
121            second_dialog: Alert::builder()
122                .transient_for(&root)
123                .launch(AlertSettings {
124                    text: Some(String::from(
125                        "Do you want to quit without saving? (Second alert)",
126                    )),
127                    secondary_text: Some(String::from("Your counter hasn't reached 42 yet")),
128                    confirm_label: Some(String::from("Close without saving")),
129                    cancel_label: Some(String::from("Cancel")),
130                    option_label: Some(String::from("Save")),
131                    is_modal: true,
132                    destructive_accept: true,
133                    extra_child: None,
134                })
135                .forward(sender.input_sender(), convert_alert_response),
136        };
137
138        let widgets = view_output!();
139
140        ComponentParts { model, widgets }
141    }
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.

Examples found in repository?
relm4-components/examples/file_dialogs.rs (line 99)
93    fn init(
94        _: Self::Init,
95        root: Self::Root,
96        sender: ComponentSender<Self>,
97    ) -> ComponentParts<Self> {
98        let open_dialog = OpenDialog::builder()
99            .transient_for_native(&root)
100            .launch(OpenDialogSettings::default())
101            .forward(sender.input_sender(), |response| match response {
102                OpenDialogResponse::Accept(path) => Input::OpenResponse(path),
103                OpenDialogResponse::Cancel => Input::Ignore,
104            });
105
106        let save_dialog = SaveDialog::builder()
107            .transient_for_native(&root)
108            .launch(SaveDialogSettings::default())
109            .forward(sender.input_sender(), |response| match response {
110                SaveDialogResponse::Accept(path) => Input::SaveResponse(path),
111                SaveDialogResponse::Cancel => Input::Ignore,
112            });
113
114        let model = App {
115            open_dialog,
116            save_dialog,
117            buffer: gtk::TextBuffer::new(None),
118            file_name: None,
119            message: None,
120        };
121
122        let widgets = view_output!();
123
124        sender.input(Input::ShowMessage(String::from(
125            "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.",
126        )));
127
128        ComponentParts { model, widgets }
129    }
More examples
Hide additional examples
relm4/examples/state_management.rs (line 577)
543    fn init(
544        _: Self::Init,
545        root: Self::Root,
546        sender: ComponentSender<Self>,
547    ) -> ComponentParts<Self> {
548        let view =
549            FactoryVecDeque::builder()
550                .launch_default()
551                .forward(sender.input_sender(), |msg| match msg {
552                    TaskOutput::Delete(index) => AppInput::DeleteTask(index),
553                    TaskOutput::Name(index, name) => AppInput::ChangeTaskName(index, name),
554                    TaskOutput::AddTag(index, name) => AppInput::AddTag(index, name),
555                    TaskOutput::DeleteTag(task_index, tag_index) => {
556                        AppInput::DeleteTag(task_index, tag_index)
557                    }
558                });
559
560        let document =
561            Document::builder()
562                .launch(())
563                .forward(sender.input_sender(), |msg| match msg {
564                    DocumentOutput::Cleared => AppInput::Cleared,
565                    DocumentOutput::DeletedTask(index) => AppInput::DeletedTask(index),
566                    DocumentOutput::DeletedTag(task_index, tag_index) => {
567                        AppInput::DeletedTag(task_index, tag_index)
568                    }
569                    DocumentOutput::AddedTask => AppInput::AddedTask,
570                    DocumentOutput::AddedTag(index, name) => AppInput::AddedTag(index, name),
571                    DocumentOutput::ChangedTaskName(index, name) => {
572                        AppInput::ChangedTaskName(index, name)
573                    }
574                });
575
576        let save_dialog = SaveDialog::builder()
577            .transient_for_native(&root)
578            .launch(SaveDialogSettings {
579                create_folders: true,
580                accept_label: "Save".into(),
581                cancel_label: "Cancel".into(),
582                is_modal: true,
583                filters: tasks_filename_filters(),
584            })
585            .forward(sender.input_sender(), |response| match response {
586                SaveDialogResponse::Accept(path) => AppInput::SaveResponse(path),
587                SaveDialogResponse::Cancel => AppInput::None,
588            });
589
590        let open_dialog = OpenDialog::builder()
591            .transient_for_native(&root)
592            .launch(OpenDialogSettings {
593                create_folders: false,
594                folder_mode: false,
595                cancel_label: "Cancel".into(),
596                accept_label: "Open".into(),
597                is_modal: true,
598                filters: tasks_filename_filters(),
599            })
600            .forward(sender.input_sender(), |response| match response {
601                OpenDialogResponse::Accept(path) => AppInput::OpenResponse(path),
602                OpenDialogResponse::Cancel => AppInput::None,
603            });
604
605        let app = App {
606            view,
607            document,
608            open_dialog,
609            save_dialog,
610        };
611
612        let task_list_box = app.view.widget();
613        let widgets = view_output!();
614
615        ComponentParts {
616            model: app,
617            widgets,
618        }
619    }
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 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
relm4-components/examples/web_image.rs (line 71)
66    fn init(
67        _: Self::Init,
68        root: Self::Root,
69        sender: ComponentSender<Self>,
70    ) -> ComponentParts<Self> {
71        let image = WebImage::builder().launch(IMAGES[0].to_owned()).detach();
72        let model = App { image, idx: 0 };
73
74        let image = model.image.widget();
75        let widgets = view_output!();
76
77        ComponentParts { model, widgets }
78    }
relm4-components/examples/adw_combo_row.rs (lines 69-72)
62    fn init(
63        _: Self::Init,
64        root: Self::Root,
65        sender: ComponentSender<Self>,
66    ) -> ComponentParts<Self> {
67        let model = App {
68            combo_row: SimpleComboRow::builder()
69                .launch(SimpleComboRow {
70                    variants: vec!["Variant 1", "Variant 2"],
71                    active_index: None,
72                })
73                .forward(sender.input_sender(), AppMsg::Selected),
74            selected_variant: 0,
75        };
76
77        let combo_row = model.combo_row.widget();
78        let widgets = view_output!();
79
80        ComponentParts { model, widgets }
81    }
relm4/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    }
relm4-components/examples/open_button.rs (lines 51-57)
45    fn init(
46        _: Self::Init,
47        root: Self::Root,
48        sender: ComponentSender<Self>,
49    ) -> ComponentParts<Self> {
50        let open_button = OpenButton::builder()
51            .launch(OpenButtonSettings {
52                dialog_settings: OpenDialogSettings::default(),
53                icon: None,
54                text: "Open file",
55                recently_opened_files: Some(".recent_files"),
56                max_recent_files: 10,
57            })
58            .forward(sender.input_sender(), AppMsg::Open);
59        let model = App { open_button };
60
61        let widgets = view_output!();
62
63        ComponentParts { model, widgets }
64    }
relm4-components/examples/combo_box.rs (lines 84-87)
72    fn init(
73        _: Self::Init,
74        root: Self::Root,
75        sender: ComponentSender<Self>,
76    ) -> ComponentParts<Self> {
77        let default_idx = 0;
78
79        let langs = vec![
80            "English", "German", "French", "Polish", "Russian", "Chinese",
81        ];
82
83        let combo = SimpleComboBox::builder()
84            .launch(SimpleComboBox {
85                variants: langs,
86                active_index: Some(default_idx),
87            })
88            .forward(sender.input_sender(), AppMsg::ComboChanged);
89
90        let model = App {
91            combo,
92            idx: default_idx,
93        };
94
95        let combo = model.combo.widget();
96        let widgets = view_output!();
97
98        ComponentParts { model, widgets }
99    }
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?
relm4/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
relm4/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?
relm4/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