select_folder.rs (4070B)
1 use gettextrs::gettext; 2 use gtk::prelude::*; 3 use relm4::{ 4 adw, gtk, Component, ComponentController, ComponentParts, ComponentSender, Controller, 5 SimpleComponent, 6 }; 7 use relm4_components::open_dialog::*; 8 use std::path::PathBuf; 9 10 #[derive(Debug, PartialEq)] 11 pub(crate) enum InOut { 12 Input, 13 Output, 14 } 15 16 #[derive(Debug)] 17 pub(crate) struct SelectFolder { 18 open_dialog: Controller<OpenDialog>, 19 folder: Option<PathBuf>, 20 description: String, 21 button_label: String, 22 direction: InOut, 23 } 24 25 #[derive(Debug)] 26 pub(crate) enum SelectFolderMsg { 27 OpenRequest, 28 OpenResponse(PathBuf), 29 Ignore, 30 } 31 32 #[derive(Debug)] 33 pub(crate) enum SelectFolderOut { 34 FolderSelected(PathBuf), 35 AbortLast, 36 } 37 38 #[relm4::component(pub)] 39 impl SimpleComponent for SelectFolder { 40 type Init = InOut; 41 type Input = SelectFolderMsg; 42 type Output = SelectFolderOut; 43 44 view! { 45 adw::StatusPage { 46 set_hexpand: true, 47 set_vexpand: true, 48 set_title: &gettext("Select Folder"), 49 set_description: Some(&model.description), 50 set_icon_name: Some("mouse-click"), 51 52 gtk::Box { 53 set_halign: gtk::Align::Center, 54 set_orientation: gtk::Orientation::Horizontal, 55 set_spacing: 24, 56 57 gtk::Button { 58 set_halign: gtk::Align::Center, 59 set_label: &model.button_label, 60 add_css_class: "suggested-action", 61 add_css_class: "pill", 62 connect_clicked[sender] => move |_| { 63 sender.input(SelectFolderMsg::OpenRequest); 64 } 65 }, 66 67 // If this is the output folder selector, add an Restart button 68 gtk::Button { 69 set_visible: model.direction == InOut::Output, 70 set_halign: gtk::Align::Center, 71 set_label: &gettext("Restart"), 72 add_css_class: "destructive-action", 73 connect_clicked[sender] => move |_| { 74 let _ = sender.output(SelectFolderOut::AbortLast); 75 } 76 } 77 } 78 } 79 } 80 81 fn init( 82 init: Self::Init, 83 root: Self::Root, 84 sender: ComponentSender<Self>, 85 ) -> ComponentParts<Self> { 86 let (description, button_label) = match init { 87 InOut::Input => ( 88 gettext("Select the folder where the Heic files can be found"), 89 gettext("Select input directory"), 90 ), 91 InOut::Output => ( 92 gettext("Select the folder where the JPG files are meant to be saved"), 93 gettext("Select output directory"), 94 ), 95 }; 96 97 let dialog_settings = OpenDialogSettings { 98 folder_mode: true, 99 ..Default::default() 100 }; 101 102 let open_dialog = OpenDialog::builder() 103 .transient_for_native(&root) 104 .launch(dialog_settings) 105 .forward(sender.input_sender(), |response| match response { 106 OpenDialogResponse::Accept(path) => SelectFolderMsg::OpenResponse(path), 107 OpenDialogResponse::Cancel => SelectFolderMsg::Ignore, 108 }); 109 110 let model = Self { 111 open_dialog, 112 folder: None, 113 description: description.to_string(), 114 button_label: button_label.to_string(), 115 direction: init, 116 }; 117 118 let widgets = view_output!(); 119 ComponentParts { model, widgets } 120 } 121 122 fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) { 123 match message { 124 SelectFolderMsg::OpenRequest => self.open_dialog.emit(OpenDialogMsg::Open), 125 SelectFolderMsg::OpenResponse(path) => { 126 self.folder = Some(path.clone()); 127 let _ = sender.output(SelectFolderOut::FolderSelected(path.clone())); 128 } 129 SelectFolderMsg::Ignore => {} 130 } 131 } 132 }