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 }