new_file_dialog.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
       ---
       new_file_dialog.rs (20419B)
       ---
            1 use std::path::Path;
            2 
            3 use eframe::{
            4     egui::{self, Layout, Sense, SidePanel, Ui, WidgetText},
            5     emath::Align,
            6     epaint::{FontId, Pos2, Rect, Rounding},
            7 };
            8 use egui::{Image, Vec2};
            9 use egui_modal::Modal;
           10 use i18n_embed_fl::fl;
           11 use icy_engine::{BitFont, Buffer, FontType, Palette, TheDrawFont, ATARI, ATARI_DEFAULT_PALETTE};
           12 
           13 use crate::{add_child, AnsiEditor, MainWindow, Message};
           14 
           15 trait Template {
           16     fn image(&self) -> &Image<'static>;
           17     fn title(&self) -> String;
           18     fn description(&self) -> String;
           19     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>>;
           20 
           21     fn show_ui(&mut self, ui: &mut Ui);
           22 }
           23 
           24 pub struct NewFileDialog {
           25     pub create: bool,
           26 
           27     selected: usize,
           28 
           29     templates: Vec<Box<dyn Template>>,
           30 }
           31 
           32 struct Dos16Template {
           33     pub width: i32,
           34     pub height: i32,
           35 }
           36 
           37 impl Template for Dos16Template {
           38     fn image(&self) -> &Image<'static> {
           39         &crate::ANSI_TEMPLATE_IMG
           40     }
           41 
           42     fn title(&self) -> String {
           43         fl!(crate::LANGUAGE_LOADER, "new-file-template-cp437-title")
           44     }
           45 
           46     fn description(&self) -> String {
           47         fl!(crate::LANGUAGE_LOADER, "new-file-template-cp437-description")
           48     }
           49 
           50     fn show_ui(&mut self, ui: &mut Ui) {
           51         show_file_ui(ui, &mut self.width, &mut self.height);
           52     }
           53 
           54     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
           55         let mut buf = Buffer::create((self.width, self.height));
           56         buf.ice_mode = icy_engine::IceMode::Blink;
           57         buf.palette_mode = icy_engine::PaletteMode::Fixed16;
           58         buf.font_mode = icy_engine::FontMode::Sauce;
           59 
           60         let id = window.create_id();
           61         let editor = AnsiEditor::new(&window.gl, id, buf);
           62         add_child(&mut window.document_tree, None, Box::new(editor));
           63         Ok(None)
           64     }
           65 }
           66 
           67 struct Ice16Template {
           68     pub width: i32,
           69     pub height: i32,
           70 }
           71 
           72 impl Template for Ice16Template {
           73     fn image(&self) -> &Image<'static> {
           74         &crate::ANSI_TEMPLATE_IMG
           75     }
           76 
           77     fn title(&self) -> String {
           78         fl!(crate::LANGUAGE_LOADER, "new-file-template-ice-title")
           79     }
           80 
           81     fn description(&self) -> String {
           82         fl!(crate::LANGUAGE_LOADER, "new-file-template-ice-description")
           83     }
           84 
           85     fn show_ui(&mut self, ui: &mut Ui) {
           86         show_file_ui(ui, &mut self.width, &mut self.height);
           87     }
           88 
           89     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
           90         let mut buf = Buffer::create((self.width, self.height));
           91         buf.ice_mode = icy_engine::IceMode::Ice;
           92         buf.palette_mode = icy_engine::PaletteMode::Fixed16;
           93         buf.font_mode = icy_engine::FontMode::Sauce;
           94 
           95         let id = window.create_id();
           96         let editor = AnsiEditor::new(&window.gl, id, buf);
           97         add_child(&mut window.document_tree, None, Box::new(editor));
           98         Ok(None)
           99     }
          100 }
          101 
          102 struct XB16Template {
          103     pub width: i32,
          104     pub height: i32,
          105 }
          106 
          107 impl Template for XB16Template {
          108     fn image(&self) -> &Image<'static> {
          109         &crate::ANSI_TEMPLATE_IMG
          110     }
          111 
          112     fn title(&self) -> String {
          113         fl!(crate::LANGUAGE_LOADER, "new-file-template-xb-title")
          114     }
          115 
          116     fn description(&self) -> String {
          117         fl!(crate::LANGUAGE_LOADER, "new-file-template-xb-description")
          118     }
          119 
          120     fn show_ui(&mut self, ui: &mut Ui) {
          121         show_file_ui(ui, &mut self.width, &mut self.height);
          122     }
          123 
          124     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          125         let mut buf = Buffer::create((self.width, self.height));
          126         buf.ice_mode = icy_engine::IceMode::Ice;
          127         buf.palette_mode = icy_engine::PaletteMode::Free16;
          128         buf.font_mode = icy_engine::FontMode::Single;
          129 
          130         let id = window.create_id();
          131         let editor = AnsiEditor::new(&window.gl, id, buf);
          132         add_child(&mut window.document_tree, None, Box::new(editor));
          133         Ok(None)
          134     }
          135 }
          136 
          137 struct XBExtTemplate {
          138     pub width: i32,
          139     pub height: i32,
          140 }
          141 
          142 impl Template for XBExtTemplate {
          143     fn image(&self) -> &Image<'static> {
          144         &crate::ANSI_TEMPLATE_IMG
          145     }
          146 
          147     fn title(&self) -> String {
          148         fl!(crate::LANGUAGE_LOADER, "new-file-template-xb-ext-title")
          149     }
          150 
          151     fn description(&self) -> String {
          152         fl!(crate::LANGUAGE_LOADER, "new-file-template-xb-ext-description")
          153     }
          154 
          155     fn show_ui(&mut self, ui: &mut Ui) {
          156         show_file_ui(ui, &mut self.width, &mut self.height);
          157     }
          158 
          159     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          160         let mut buf = Buffer::create((self.width, self.height));
          161         buf.ice_mode = icy_engine::IceMode::Ice;
          162         buf.palette_mode = icy_engine::PaletteMode::Free16;
          163         buf.font_mode = icy_engine::FontMode::FixedSize;
          164         buf.set_font(1, BitFont::default());
          165         let id = window.create_id();
          166         let editor = AnsiEditor::new(&window.gl, id, buf);
          167         add_child(&mut window.document_tree, None, Box::new(editor));
          168         Ok(None)
          169     }
          170 }
          171 
          172 struct AnsiTemplate {
          173     pub width: i32,
          174     pub height: i32,
          175 }
          176 
          177 impl Template for AnsiTemplate {
          178     fn image(&self) -> &Image<'static> {
          179         &crate::ANSI_TEMPLATE_IMG
          180     }
          181 
          182     fn title(&self) -> String {
          183         fl!(crate::LANGUAGE_LOADER, "new-file-template-ansi-title")
          184     }
          185 
          186     fn description(&self) -> String {
          187         fl!(crate::LANGUAGE_LOADER, "new-file-template-ansi-description")
          188     }
          189 
          190     fn show_ui(&mut self, ui: &mut Ui) {
          191         show_file_ui(ui, &mut self.width, &mut self.height);
          192     }
          193 
          194     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          195         let mut buf = Buffer::create((self.width, self.height));
          196         buf.ice_mode = icy_engine::IceMode::Unlimited;
          197         buf.palette_mode = icy_engine::PaletteMode::RGB;
          198         buf.font_mode = icy_engine::FontMode::Unlimited;
          199 
          200         let id = window.create_id();
          201         let editor = AnsiEditor::new(&window.gl, id, buf);
          202         add_child(&mut window.document_tree, None, Box::new(editor));
          203         Ok(None)
          204     }
          205 }
          206 
          207 struct FileIdTemplate {
          208     pub width: i32,
          209     pub height: i32,
          210 }
          211 
          212 impl Template for FileIdTemplate {
          213     fn image(&self) -> &Image<'static> {
          214         &crate::ANSI_TEMPLATE_IMG
          215     }
          216 
          217     fn title(&self) -> String {
          218         fl!(crate::LANGUAGE_LOADER, "new-file-template-file_id-title")
          219     }
          220 
          221     fn description(&self) -> String {
          222         fl!(crate::LANGUAGE_LOADER, "new-file-template-file_id-description")
          223     }
          224 
          225     fn show_ui(&mut self, ui: &mut Ui) {
          226         show_file_ui(ui, &mut self.width, &mut self.height);
          227     }
          228 
          229     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          230         let mut buf = Buffer::create((self.width, self.height));
          231         buf.ice_mode = icy_engine::IceMode::Blink;
          232         buf.palette_mode = icy_engine::PaletteMode::Fixed16;
          233         buf.font_mode = icy_engine::FontMode::Sauce;
          234         let id = window.create_id();
          235         let editor = AnsiEditor::new(&window.gl, id, buf);
          236         add_child(&mut window.document_tree, None, Box::new(editor));
          237         Ok(None)
          238     }
          239 }
          240 
          241 struct AtasciiTemplate {
          242     pub width: i32,
          243     pub height: i32,
          244 }
          245 
          246 impl Template for AtasciiTemplate {
          247     fn image(&self) -> &Image<'static> {
          248         &crate::ANSI_TEMPLATE_IMG
          249     }
          250 
          251     fn title(&self) -> String {
          252         fl!(crate::LANGUAGE_LOADER, "new-file-template-atascii-title")
          253     }
          254 
          255     fn description(&self) -> String {
          256         fl!(crate::LANGUAGE_LOADER, "new-file-template-atascii-description")
          257     }
          258 
          259     fn show_ui(&mut self, ui: &mut Ui) {
          260         show_file_ui(ui, &mut self.width, &mut self.height);
          261     }
          262 
          263     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          264         let mut buf = Buffer::create((self.width, self.height));
          265         buf.buffer_type = icy_engine::BufferType::Atascii;
          266         buf.ice_mode = icy_engine::IceMode::Ice;
          267         buf.palette_mode = icy_engine::PaletteMode::Fixed16;
          268         buf.font_mode = icy_engine::FontMode::Single;
          269         buf.clear_font_table();
          270         let mut font = BitFont::from_bytes("", ATARI).unwrap();
          271         font.length = 128;
          272         for k in 0..128u8 {
          273             font.glyphs.remove(&((128 + k) as char));
          274         }
          275 
          276         buf.set_font(0, font);
          277         buf.palette = Palette::from_slice(&ATARI_DEFAULT_PALETTE);
          278 
          279         let id = window.create_id();
          280         let editor = AnsiEditor::new(&window.gl, id, buf);
          281         add_child(&mut window.document_tree, None, Box::new(editor));
          282         Ok(None)
          283     }
          284 }
          285 
          286 fn show_file_ui(ui: &mut Ui, width: &mut i32, height: &mut i32) {
          287     egui::Grid::new("some_unique_id").num_columns(2).spacing([4.0, 8.0]).show(ui, |ui| {
          288         ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
          289             ui.label(fl!(crate::LANGUAGE_LOADER, "new-file-width"));
          290         });
          291         let mut tmp_str = width.to_string();
          292         ui.add(egui::TextEdit::singleline(&mut tmp_str).char_limit(35));
          293         if let Ok(new_width) = tmp_str.parse::<i32>() {
          294             *width = new_width;
          295         }
          296         ui.end_row();
          297 
          298         ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
          299             ui.label(fl!(crate::LANGUAGE_LOADER, "new-file-height"));
          300         });
          301         let mut tmp_str = height.to_string();
          302         ui.add(egui::TextEdit::singleline(&mut tmp_str).char_limit(35));
          303         if let Ok(new_height) = tmp_str.parse::<i32>() {
          304             *height = new_height;
          305         }
          306         ui.end_row();
          307     });
          308 }
          309 
          310 struct AnsiMationTemplate {}
          311 
          312 impl Template for AnsiMationTemplate {
          313     fn image(&self) -> &Image<'static> {
          314         &crate::ANSIMATION_TEMPLATE_IMG
          315     }
          316 
          317     fn title(&self) -> String {
          318         fl!(crate::LANGUAGE_LOADER, "new-file-template-ansimation-title")
          319     }
          320 
          321     fn description(&self) -> String {
          322         fl!(crate::LANGUAGE_LOADER, "new-file-template-ansimation-description")
          323     }
          324 
          325     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          326         let id = window.create_id();
          327         let txt = r#"local buf = new_buffer(80, 25)
          328 
          329 for i=0,9,1 do
          330     buf:clear()
          331     buf.x = 10 + i * 5
          332     buf.y = 10
          333     buf:print("Hello World " .. cur_frame)
          334     next_frame(buf)
          335 end"#;
          336         let editor = crate::AnimationEditor::new(window.gl.clone(), id, Path::new("."), txt.into());
          337         add_child(&mut window.document_tree, None, Box::new(editor));
          338         Ok(None)
          339     }
          340 
          341     fn show_ui(&mut self, ui: &mut Ui) {
          342         ui.label(fl!(crate::LANGUAGE_LOADER, "new-file-template-ansimation-ui-label"));
          343         ui.hyperlink("https://github.com/mkrueger/icy_draw/blob/main/doc/lua_api.md");
          344     }
          345 }
          346 
          347 struct BitFontTemplate {
          348     width: i32,
          349     height: i32,
          350 }
          351 
          352 impl Template for BitFontTemplate {
          353     fn image(&self) -> &Image<'static> {
          354         &crate::BITFONT_TEMPLATE_IMG
          355     }
          356 
          357     fn title(&self) -> String {
          358         fl!(crate::LANGUAGE_LOADER, "new-file-template-bit_font-title")
          359     }
          360 
          361     fn description(&self) -> String {
          362         fl!(crate::LANGUAGE_LOADER, "new-file-template-bit_font-description")
          363     }
          364 
          365     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          366         let id = window.create_id();
          367         let font = if self.width == 8 && self.height == 16 {
          368             BitFont::default()
          369         } else if self.width == 8 && self.height == 8 {
          370             BitFont::from_sauce_name("IBM VGA50")?
          371         } else if self.width == 8 && self.height == 14 {
          372             BitFont::from_sauce_name("IBM EGA")?
          373         } else {
          374             BitFont::create_8(
          375                 format!("Empty {}x{}", self.width, self.height),
          376                 self.width as u8,
          377                 self.height as u8,
          378                 &vec![0; 256 * self.height as usize],
          379             )
          380         };
          381         let editor = crate::BitFontEditor::new(&window.gl, id, font);
          382         add_child(&mut window.document_tree, None, Box::new(editor));
          383         Ok(None)
          384     }
          385     fn show_ui(&mut self, ui: &mut Ui) {
          386         ui.label(fl!(crate::LANGUAGE_LOADER, "new-file-template-bitfont-ui-label"));
          387         ui.add_space(8.0);
          388         egui::Grid::new("some_unique_id").num_columns(2).spacing([4.0, 8.0]).show(ui, |ui| {
          389             ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
          390                 ui.label(fl!(crate::LANGUAGE_LOADER, "new-file-width"));
          391             });
          392             ui.add(egui::Slider::new(&mut self.width, 2..=8));
          393             ui.end_row();
          394 
          395             ui.with_layout(Layout::right_to_left(egui::Align::Center), |ui| {
          396                 ui.label(fl!(crate::LANGUAGE_LOADER, "new-file-height"));
          397             });
          398             ui.add(egui::Slider::new(&mut self.height, 2..=19));
          399             ui.end_row();
          400         });
          401     }
          402 }
          403 
          404 struct TdfFontTemplate {
          405     pub font_type: FontType,
          406 }
          407 
          408 impl Template for TdfFontTemplate {
          409     fn image(&self) -> &Image<'static> {
          410         match self.font_type {
          411             FontType::Outline => &crate::OUTLINEFONT_TEMPLATE_IMG,
          412             FontType::Block => &crate::BLOCKFONT_TEMPLATE_IMG,
          413             FontType::Color => &crate::COLORFONT_TEMPLATE_IMG,
          414         }
          415     }
          416 
          417     fn title(&self) -> String {
          418         match self.font_type {
          419             FontType::Outline => fl!(crate::LANGUAGE_LOADER, "new-file-template-outline_font-title"),
          420             FontType::Block => fl!(crate::LANGUAGE_LOADER, "new-file-template-block_font-title"),
          421             FontType::Color => fl!(crate::LANGUAGE_LOADER, "new-file-template-color_font-title"),
          422         }
          423     }
          424 
          425     fn description(&self) -> String {
          426         match self.font_type {
          427             FontType::Outline => fl!(crate::LANGUAGE_LOADER, "new-file-template-outline_font-description"),
          428             FontType::Block => fl!(crate::LANGUAGE_LOADER, "new-file-template-block_font-description"),
          429             FontType::Color => fl!(crate::LANGUAGE_LOADER, "new-file-template-color_font-description"),
          430         }
          431     }
          432 
          433     fn create_file(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          434         let id = window.create_id();
          435         let fonts = vec![TheDrawFont::new(self.title(), self.font_type, 1)];
          436         let editor = crate::CharFontEditor::new(&window.gl, id, fonts);
          437         add_child(&mut window.document_tree, None, Box::new(editor));
          438         Ok(None)
          439     }
          440 
          441     fn show_ui(&mut self, ui: &mut Ui) {
          442         ui.label(fl!(crate::LANGUAGE_LOADER, "new-file-template-thedraw-ui-label"));
          443         ui.hyperlink("http://www.roysac.com/thedrawfonts-tdf.html");
          444     }
          445 }
          446 
          447 impl Default for NewFileDialog {
          448     fn default() -> Self {
          449         let templates: Vec<Box<dyn Template>> = vec![
          450             Box::new(AnsiTemplate { width: 80, height: 25 }),
          451             Box::new(XB16Template { width: 80, height: 25 }),
          452             Box::new(Dos16Template { width: 80, height: 25 }),
          453             Box::new(Ice16Template { width: 80, height: 25 }),
          454             Box::new(XBExtTemplate { width: 80, height: 25 }),
          455             Box::new(FileIdTemplate { width: 44, height: 25 }),
          456             Box::new(AnsiMationTemplate {}),
          457             Box::new(AtasciiTemplate { width: 40, height: 24 }),
          458             Box::new(BitFontTemplate { width: 8, height: 16 }),
          459             Box::new(TdfFontTemplate { font_type: FontType::Color }),
          460             Box::new(TdfFontTemplate { font_type: FontType::Block }),
          461             Box::new(TdfFontTemplate { font_type: FontType::Outline }),
          462         ];
          463 
          464         Self {
          465             create: false,
          466             templates,
          467             selected: 0,
          468         }
          469     }
          470 }
          471 
          472 impl crate::ModalDialog for NewFileDialog {
          473     fn show(&mut self, ctx: &egui::Context) -> bool {
          474         let mut result = false;
          475         let modal = Modal::new(ctx, "new_file_dialog");
          476 
          477         modal.show(|ui| {
          478             ui.set_height(420.);
          479             ui.set_width(800.);
          480 
          481             modal.title(ui, fl!(crate::LANGUAGE_LOADER, "new-file-title"));
          482 
          483             modal.frame(ui, |ui| {
          484                 SidePanel::left("new_file_side_panel")
          485                     .exact_width(280.0)
          486                     .resizable(false)
          487                     .show_inside(ui, |ui| {
          488                         let row_height = 58.0;
          489                         egui::ScrollArea::vertical().id_source("bitfont_scroll_area").show(ui, |ui| {
          490                             for (i, template) in self.templates.iter().enumerate() {
          491                                 let is_selected = i == self.selected;
          492 
          493                                 let (id, rect) = ui.allocate_space([ui.available_width(), row_height].into());
          494                                 let response = ui.interact(rect, id, Sense::click());
          495                                 if response.hovered() {
          496                                     ui.painter()
          497                                         .rect_filled(rect.expand(1.0), Rounding::same(4.0), ui.style().visuals.widgets.active.bg_fill);
          498                                 } else if is_selected {
          499                                     ui.painter()
          500                                         .rect_filled(rect.expand(1.0), Rounding::same(4.0), ui.style().visuals.extreme_bg_color);
          501                                 }
          502                                 let image = template.image();
          503                                 let r = Rect::from_min_size(Pos2::new(rect.left() + 4.0, rect.top() + 4.0), Vec2::new(32.0, 32.0));
          504                                 image.paint_at(ui, r);
          505 
          506                                 let font_id = FontId::new(20.0, eframe::epaint::FontFamily::Proportional);
          507                                 let text: WidgetText = template.title().into();
          508                                 let galley = text.into_galley(ui, Some(false), f32::INFINITY, font_id);
          509                                 let mut title_rect = rect;
          510                                 title_rect.set_left(title_rect.left() + 40.0);
          511                                 title_rect.set_top(title_rect.top() + 4.0);
          512                                 ui.painter().galley_with_override_text_color(
          513                                     egui::Align2::LEFT_TOP.align_size_within_rect(galley.size(), title_rect.shrink(4.0)).min,
          514                                     galley,
          515                                     ui.style().visuals.strong_text_color(),
          516                                 );
          517 
          518                                 let font_id = FontId::new(14.0, eframe::epaint::FontFamily::Proportional);
          519                                 let text: WidgetText = template.description().lines().next().unwrap_or_default().into();
          520                                 let galley = text.into_galley(ui, Some(false), f32::INFINITY, font_id);
          521                                 let mut descr_rect = rect;
          522                                 descr_rect.set_top(descr_rect.top() + 34.0);
          523                                 ui.painter().galley_with_override_text_color(
          524                                     egui::Align2::LEFT_TOP.align_size_within_rect(galley.size(), descr_rect.shrink(4.0)).min,
          525                                     galley,
          526                                     ui.style().visuals.text_color(),
          527                                 );
          528 
          529                                 if response.clicked() {
          530                                     self.selected = i;
          531                                 }
          532                                 if response.double_clicked() {
          533                                     self.selected = i;
          534                                     self.create = true;
          535                                     result = true;
          536                                 }
          537                             }
          538                         });
          539                     });
          540 
          541                 egui::CentralPanel::default().show_inside(ui, |ui| {
          542                     ui.label(self.templates[self.selected].description());
          543                     ui.separator();
          544                     self.templates[self.selected].show_ui(ui);
          545                 });
          546             });
          547             ui.separator();
          548             ui.add_space(4.0);
          549             ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
          550                 if ui.button(fl!(crate::LANGUAGE_LOADER, "new-file-create")).clicked() {
          551                     self.create = true;
          552                     result = true;
          553                 }
          554                 if ui.button(fl!(crate::LANGUAGE_LOADER, "new-file-cancel")).clicked() {
          555                     result = true;
          556                 }
          557             });
          558         });
          559         modal.open();
          560         result
          561     }
          562 
          563     fn should_commit(&self) -> bool {
          564         self.create
          565     }
          566 
          567     fn commit_self(&self, window: &mut MainWindow<'_>) -> crate::TerminalResult<Option<Message>> {
          568         self.templates[self.selected].create_file(window)
          569     }
          570 }