pub struct Connector<C: Component> { /* private fields */ }
Expand description
Contains the post-launch input sender and output receivers with the root widget.
The receiver can be separated from the Connector
by choosing a method for handling it.
Implementations§
source§impl<C: Component> Connector<C>
impl<C: Component> Connector<C>
sourcepub fn forward<X: 'static, F: Fn(C::Output) -> X + 'static>(
self,
sender_: &Sender<X>,
transform: F
) -> Controller<C>
pub fn forward<X: 'static, F: Fn(C::Output) -> X + 'static>( self, sender_: &Sender<X>, transform: F ) -> Controller<C>
Forwards output events to the designated sender.
Examples found in repository?
relm4/examples/message_broker.rs (line 189)
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
relm4-components/examples/open_button.rs (line 57)
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/transient_dialog.rs (line 108)
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 128 129 130 131 132 133 134 135 136 137 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> {
// 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 }
}
fn update(&mut self, _msg: Self::Input, _sender: ComponentSender<Self>) {}
}
#[derive(Debug)]
enum AppMsg {}
struct App {
button: Controller<Button>,
}
#[relm4::component]
impl SimpleComponent for App {
type Init = ();
type Input = AppMsg;
type Output = ();
view! {
main_window = gtk::ApplicationWindow {
set_default_size: (500, 250),
set_child: Some(model.button.widget()),
}
}
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 }
}
relm4/examples/components.rs (line 180)
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 (lines 99-102)
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 }
}
relm4-components/examples/alert.rs (line 116)
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 }
}
sourcepub fn connect_receiver<F: FnMut(&mut Sender<C::Input>, C::Output) + 'static>(
self,
func: F
) -> Controller<C>
pub fn connect_receiver<F: FnMut(&mut Sender<C::Input>, C::Output) + 'static>( self, func: F ) -> Controller<C>
Given a mutable closure, captures the receiver for handling.
Examples found in repository?
relm4/examples/settings_list.rs (lines 29-68)
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());
});
}
sourcepub fn detach(self) -> Controller<C>
pub fn detach(self) -> Controller<C>
Ignore outputs from the component and take the handle.
Examples found in repository?
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 }
}
sourcepub fn into_stream(self) -> ComponentStream<C>
pub fn into_stream(self) -> ComponentStream<C>
Convert his type into a Stream
that yields output events
as futures.
Examples found in repository?
relm4/examples/message_stream.rs (line 145)
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
}
});
}
}
}
Trait Implementations§
source§impl<C: Component> ComponentController<C> for Connector<C>
impl<C: Component> ComponentController<C> for Connector<C>
source§fn state(&self) -> &StateWatcher<C>
fn state(&self) -> &StateWatcher<C>
Provides access to the state of a component.
source§fn detach_runtime(&mut self)
fn detach_runtime(&mut self)
Dropping this type will usually stop the runtime of the component.
With this method you can give the runtime a static lifetime.
In other words, dropping the controller or connector will not stop
the runtime anymore, instead it will run until the app is closed.