177 lines
5.6 KiB
Rust
177 lines
5.6 KiB
Rust
use gtk::prelude::*;
|
|
use relm4::{
|
|
gtk, Component, ComponentController, ComponentParts, ComponentSender, Controller, RelmApp,
|
|
RelmWidgetExt, SimpleComponent,
|
|
};
|
|
use relm4_components::{open_dialog::*, save_dialog::*};
|
|
|
|
use std::path::PathBuf;
|
|
|
|
struct App {
|
|
open_dialog: Controller<OpenDialog>,
|
|
save_dialog: Controller<SaveDialog>,
|
|
buffer: gtk::TextBuffer,
|
|
file_name: Option<String>,
|
|
message: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum Input {
|
|
OpenRequest,
|
|
OpenResponse(PathBuf),
|
|
SaveRequest,
|
|
SaveResponse(PathBuf),
|
|
ShowMessage(String),
|
|
ResetMessage,
|
|
Ignore,
|
|
}
|
|
|
|
#[relm4::component]
|
|
impl SimpleComponent for App {
|
|
type Init = ();
|
|
type Input = Input;
|
|
type Output = ();
|
|
|
|
view! {
|
|
root = gtk::ApplicationWindow {
|
|
set_default_size: (600, 400),
|
|
|
|
#[watch]
|
|
set_title: Some(model.file_name.as_deref().unwrap_or_default()),
|
|
|
|
#[wrap(Some)]
|
|
set_titlebar = >k::HeaderBar {
|
|
pack_start = >k::Button {
|
|
set_label: "Open",
|
|
connect_clicked => Input::OpenRequest,
|
|
},
|
|
pack_end = >k::Button {
|
|
set_label: "Save As",
|
|
connect_clicked => Input::SaveRequest,
|
|
|
|
#[watch]
|
|
set_sensitive: model.file_name.is_some(),
|
|
}
|
|
},
|
|
|
|
gtk::Box {
|
|
set_orientation: gtk::Orientation::Vertical,
|
|
set_margin_all: 5,
|
|
|
|
gtk::ScrolledWindow {
|
|
set_min_content_height: 380,
|
|
|
|
#[wrap(Some)]
|
|
set_child = >k::TextView {
|
|
set_buffer: Some(&model.buffer),
|
|
|
|
#[watch]
|
|
set_visible: model.file_name.is_some(),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn post_view() {
|
|
if let Some(text) = &model.message {
|
|
let dialog = gtk::MessageDialog::builder()
|
|
.text(text)
|
|
.use_markup(true)
|
|
.transient_for(&widgets.root)
|
|
.modal(true)
|
|
.buttons(gtk::ButtonsType::Ok)
|
|
.build();
|
|
dialog.connect_response(|dialog, _| dialog.destroy());
|
|
dialog.set_visible(true);
|
|
sender.input(Input::ResetMessage);
|
|
}
|
|
}
|
|
|
|
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 }
|
|
}
|
|
|
|
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
|
match message {
|
|
Input::OpenRequest => self.open_dialog.emit(OpenDialogMsg::Open),
|
|
Input::OpenResponse(path) => match std::fs::read_to_string(&path) {
|
|
Ok(contents) => {
|
|
self.buffer.set_text(&contents);
|
|
self.file_name = Some(
|
|
path.file_name()
|
|
.expect("The path has no file name")
|
|
.to_str()
|
|
.expect("Cannot convert file name to string")
|
|
.to_string(),
|
|
);
|
|
}
|
|
Err(e) => sender.input(Input::ShowMessage(e.to_string())),
|
|
},
|
|
Input::SaveRequest => self
|
|
.save_dialog
|
|
.emit(SaveDialogMsg::SaveAs(self.file_name.clone().unwrap())),
|
|
Input::SaveResponse(path) => match std::fs::write(
|
|
&path,
|
|
self.buffer
|
|
.text(&self.buffer.start_iter(), &self.buffer.end_iter(), false),
|
|
) {
|
|
Ok(_) => {
|
|
sender.input(Input::ShowMessage(format!(
|
|
"File saved successfully at {path:?}"
|
|
)));
|
|
self.buffer.set_text("");
|
|
self.file_name = None;
|
|
}
|
|
Err(e) => sender.input(Input::ShowMessage(e.to_string())),
|
|
},
|
|
Input::ShowMessage(message) => {
|
|
self.message = Some(message);
|
|
}
|
|
Input::ResetMessage => {
|
|
self.message = None;
|
|
}
|
|
Input::Ignore => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let app = RelmApp::new("relm4.example.file_dialogs");
|
|
app.run::<App>(());
|
|
}
|