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::RootThe root widget of the component.
Implementations§
Source§impl<C: Component> ComponentBuilder<C>
impl<C: Component> ComponentBuilder<C>
Sourcepub fn update_root<F: FnOnce(&mut C::Root)>(self, func: F) -> Self
pub fn update_root<F: FnOnce(&mut C::Root)>(self, func: F) -> Self
Configure the root widget before launching.
Sourcepub const fn widget(&self) -> &C::Root
pub const fn widget(&self) -> &C::Root
Access the root widget before the component is initialized.
Sourcepub fn priority(self, priority: Priority) -> Self
pub fn priority(self, priority: Priority) -> Self
Change the priority at which the messages of this component are handled.
- Use
glib::Priority::HIGHfor high priority event sources. - Use
glib::Priority::LOWfor very low priority background tasks. - Use
glib::Priority::DEFAULT_IDLEfor default priority idle functions. - Use
glib::Priority::HIGH_IDLEfor high priority idle functions.
Source§impl<C: Component> ComponentBuilder<C>
impl<C: Component> ComponentBuilder<C>
Sourcepub fn attach_to<T: RelmContainerExt<Child = Widget>>(
self,
container: &T,
) -> Self
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?
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>
impl<C: Component> ComponentBuilder<C>
Sourcepub fn transient_for(self, widget: impl AsRef<Widget>) -> Self
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?
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
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 }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 }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>
impl<C: Component> ComponentBuilder<C>
Sourcepub fn transient_for_native(self, widget: impl AsRef<Widget>) -> Self
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?
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
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>
impl<C: Component> ComponentBuilder<C>
Sourcepub fn launch(self, payload: C::Init) -> Connector<C>
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?
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
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 }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 }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 }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 }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 }- relm4/examples/multi_window.rs
- relm4/examples/components.rs
- relm4/examples/navigation_splitview_with_stack.rs
- relm4-components/examples/file_dialogs.rs
- relm4/examples/message_stream.rs
- relm4/examples/drop_sub_components.rs
- relm4-components/examples/alert.rs
- relm4/examples/settings_list.rs
- relm4/examples/state_management.rs
Sourcepub fn launch_with_broker(
self,
payload: C::Init,
broker: &MessageBroker<C::Input>,
) -> Connector<C>
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?
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
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>
impl<C> ComponentBuilder<C>
Sourcepub fn detach_worker(self, payload: C::Init) -> WorkerHandle<C>
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?
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 }