mod.rs - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
 (HTM) git clone https://git.drkhsh.at/icy_draw.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       mod.rs (9036B)
       ---
            1 #![allow(clippy::needless_range_loop)]
            2 
            3 use std::path::PathBuf;
            4 
            5 use eframe::egui::{self, TextEdit, Ui};
            6 use egui_file::FileDialog;
            7 use egui_modal::Modal;
            8 use i18n_embed_fl::fl;
            9 use icy_engine::{BufferType, Rectangle, SaveOptions, TextPane};
           10 
           11 use crate::{AnsiEditor, Message, ModalDialog, TerminalResult, SETTINGS};
           12 
           13 mod ansi;
           14 mod artworx;
           15 mod ascii;
           16 mod atascii;
           17 mod avatar;
           18 mod bin;
           19 mod ice_draw;
           20 mod pcboard;
           21 mod png;
           22 mod tundra_draw;
           23 mod xbin;
           24 
           25 pub struct ExportFileDialog {
           26     pub should_commit: bool,
           27     pub file_name: PathBuf,
           28     folder_dialog: Option<FileDialog>,
           29     format_type: i32,
           30     buffer_type: BufferType,
           31 }
           32 
           33 impl ExportFileDialog {
           34     pub fn new(buf: &icy_engine::Buffer) -> Self {
           35         let file_name = match &buf.file_name {
           36             Some(path) => {
           37                 let mut p = path.clone();
           38                 let desc: &[(&str, CreateSettingsFunction, &str)] = if matches!(buf.buffer_type, BufferType::Atascii) {
           39                     &ATASCII_TYPE_DESCRIPTIONS
           40                 } else {
           41                     &TYPE_DESCRIPTIONS
           42                 };
           43                 let format_type = get_format_type(buf.buffer_type, path) as usize;
           44                 let ext = desc[format_type].2;
           45                 p.set_extension(ext);
           46                 p
           47             }
           48             _ => PathBuf::from("Untitled.ans"),
           49         };
           50         let format_type = get_format_type(buf.buffer_type, &file_name);
           51 
           52         ExportFileDialog {
           53             should_commit: false,
           54             file_name,
           55             format_type,
           56             folder_dialog: None,
           57             buffer_type: buf.buffer_type,
           58         } // self.file_name.set_extension(TYPE_DESCRIPTIONS[format_type].2);
           59     }
           60 }
           61 
           62 fn get_format_type(buf: BufferType, path: &std::path::Path) -> i32 {
           63     if let Some(ext) = path.extension() {
           64         if let Some(ext) = ext.to_str() {
           65             let ext = ext.to_lowercase();
           66             let desc: &[(&str, CreateSettingsFunction, &str)] = if matches!(buf, BufferType::Atascii) {
           67                 &ATASCII_TYPE_DESCRIPTIONS
           68             } else {
           69                 &TYPE_DESCRIPTIONS
           70             };
           71             for i in 0..desc.len() {
           72                 let td = desc[i];
           73                 if ext == td.2 {
           74                     return i as i32;
           75                 }
           76             }
           77         }
           78     }
           79     0
           80 }
           81 
           82 impl ModalDialog for ExportFileDialog {
           83     fn show(&mut self, ctx: &egui::Context) -> bool {
           84         if let Some(ed) = &mut self.folder_dialog {
           85             if ed.show(ctx).selected() {
           86                 if let Some(res) = ed.path() {
           87                     self.file_name = res.to_path_buf();
           88                 }
           89                 self.folder_dialog = None
           90             } else {
           91                 return false;
           92             }
           93         }
           94 
           95         let mut result = false;
           96 
           97         let modal = Modal::new(ctx, "export_file-dialog");
           98         modal.show(|ui| {
           99             ui.set_width(550.);
          100             ui.set_height(320.);
          101 
          102             modal.title(ui, fl!(crate::LANGUAGE_LOADER, "export-title"));
          103 
          104             modal.frame(ui, |ui| {
          105                 let desc: &[(&str, CreateSettingsFunction, &str)] = if matches!(self.buffer_type, BufferType::Atascii) {
          106                     &ATASCII_TYPE_DESCRIPTIONS
          107                 } else {
          108                     &TYPE_DESCRIPTIONS
          109                 };
          110 
          111                 egui::Grid::new("paste_mode_grid")
          112                     .num_columns(2)
          113                     .spacing([4.0, 8.0])
          114                     .min_row_height(24.)
          115                     .show(ui, |ui| {
          116                         ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
          117                             ui.label(fl!(crate::LANGUAGE_LOADER, "export-file-label"));
          118                         });
          119                         ui.horizontal(|ui| {
          120                             let mut path_edit = self.file_name.to_string_lossy().to_string();
          121                             let response = ui.add(TextEdit::singleline(&mut path_edit).desired_width(450.));
          122                             if response.changed() {
          123                                 self.file_name = path_edit.into();
          124                                 let format_type = get_format_type(self.buffer_type, &self.file_name);
          125                                 if format_type >= 0 {
          126                                     self.format_type = format_type;
          127                                 }
          128                             }
          129                             if ui.add(egui::Button::new("…").wrap(false)).clicked() {
          130                                 let mut initial_path = None;
          131                                 crate::set_default_initial_directory_opt(&mut initial_path);
          132                                 let mut dialog = FileDialog::save_file(initial_path);
          133                                 dialog.open();
          134                                 self.folder_dialog = Some(dialog);
          135 
          136                                 ui.close_menu();
          137                             }
          138                         });
          139                         ui.end_row();
          140 
          141                         ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
          142                             ui.label(fl!(crate::LANGUAGE_LOADER, "export-format-label"));
          143                         });
          144                         egui::ComboBox::from_id_source("format_combo")
          145                             .selected_text(desc[self.format_type as usize].0)
          146                             .width(190.)
          147                             .show_ui(ui, |ui| {
          148                                 (0..desc.len()).for_each(|i| {
          149                                     let td = desc[i];
          150                                     if ui.selectable_value(&mut self.format_type, i as i32, td.0).clicked() {
          151                                         self.file_name.set_extension(td.2);
          152                                     }
          153                                 });
          154                             });
          155                         ui.end_row();
          156                     });
          157 
          158                 ui.separator();
          159 
          160                 unsafe {
          161                     desc[self.format_type as usize].1(ui, &mut SETTINGS.save_options);
          162                 }
          163             });
          164 
          165             ui.add_space(ui.available_height() - 23.0);
          166 
          167             modal.buttons(ui, |ui| {
          168                 if ui.button(fl!(crate::LANGUAGE_LOADER, "export-button-title")).clicked() {
          169                     self.should_commit = true;
          170                     result = true;
          171                 }
          172                 if ui.button(fl!(crate::LANGUAGE_LOADER, "new-file-cancel")).clicked() {
          173                     result = true;
          174                 }
          175             });
          176         });
          177         modal.open();
          178         result
          179     }
          180 
          181     fn should_commit(&self) -> bool {
          182         self.should_commit
          183     }
          184 
          185     fn commit(&self, editor: &mut AnsiEditor) -> TerminalResult<Option<crate::Message>> {
          186         if let Some(ext) = self.file_name.extension() {
          187             if let Some(ext) = ext.to_str() {
          188                 let ext = ext.to_lowercase();
          189                 if ext == "png" {
          190                     let lock = &editor.buffer_view.lock();
          191                     let buf = lock.get_buffer();
          192                     let (size, pixels) = buf.render_to_rgba(Rectangle::from(0, 0, buf.get_width(), buf.get_height()));
          193                     let image_buffer = image::RgbaImage::from_raw(size.width as u32, size.height as u32, pixels);
          194                     match image_buffer {
          195                         Some(img) => {
          196                             if let Err(err) = img.save(&self.file_name) {
          197                                 return Ok(Some(Message::ShowError(format!("Failed to save image: {}", err))));
          198                             }
          199                         }
          200                         None => {
          201                             return Ok(Some(Message::ShowError("Failed to save image".to_string())));
          202                         }
          203                     }
          204 
          205                     return Ok(None);
          206                 }
          207             }
          208         }
          209         unsafe {
          210             editor.save_content(self.file_name.as_path(), &SETTINGS.save_options)?;
          211         }
          212         Ok(None)
          213     }
          214 }
          215 
          216 type CreateSettingsFunction = fn(&mut Ui, &mut SaveOptions);
          217 
          218 const TYPE_DESCRIPTIONS: [(&str, CreateSettingsFunction, &str); 12] = [
          219     ("Ansi (.ans)", ansi::create_settings_page, "ans"),
          220     ("Avatar (.avt)", avatar::create_settings_page, "avt"),
          221     ("PCBoard (.pcb)", pcboard::create_settings_page, "pcb"),
          222     ("Ascii (.asc)", ascii::create_settings_page, "asc"),
          223     ("Artworx (.adf)", artworx::create_settings_page, "adf"),
          224     ("Ice Draw (.idf)", ice_draw::create_settings_page, "idf"),
          225     ("Tundra Draw (.tnd)", tundra_draw::create_settings_page, "tnd"),
          226     ("Bin (.bin)", bin::create_settings_page, "bin"),
          227     ("XBin (.xb)", xbin::create_settings_page, "xb"),
          228     ("CtrlA (.msg)", pcboard::create_settings_page, "msg"),
          229     ("Renegade (.an1)", pcboard::create_settings_page, "an1"),
          230     ("PNG (.png)", png::create_settings_page, "png"),
          231 ];
          232 
          233 const ATASCII_TYPE_DESCRIPTIONS: [(&str, CreateSettingsFunction, &str); 3] = [
          234     ("Atascii (.ata)", atascii::create_settings_page, "ata"),
          235     ("XBin (.xb)", xbin::create_settings_page, "xb"),
          236     ("PNG (.png)", png::create_settings_page, "png"),
          237 ];