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>
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::HIGH
for high priority event sources. - Use
glib::Priority::LOW
for very low priority background tasks. - Use
glib::Priority::DEFAULT_IDLE
for default priority idle functions. - Use
glib::Priority::HIGH_IDLE
for 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 = 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>
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 }
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.
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
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 }
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 }
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 }
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 }
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 }