URI: 
       Implemented infrastructure for tools to show modal dialogs. - icy_draw - icy_draw is the successor to mystic draw. fork / mirror
  HTML git clone https://git.drkhsh.at/icy_draw.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 12c2e402fb179e793a7ef318d70eb90a03a9a2a5
   DIR parent f23f4c1556c2ac7c2095a6b74debf94be5e14d71
  HTML Author: Mike Krüger <mkrueger@posteo.de>
       Date:   Thu, 27 Jul 2023 07:40:50 +0200
       
       Implemented infrastructure for tools to show modal dialogs.
       
       Diffstat:
         M i18n/en/icy_draw.ftl                |       6 ++++--
         M src/model/tool/brush_imp.rs         |     124 ++++++++++++++++---------------
         M src/model/tool/click_imp.rs         |       5 +++--
         M src/model/tool/draw_ellipse_filled… |      12 +++++++-----
         M src/model/tool/draw_ellipse_imp.rs  |      12 +++++++-----
         M src/model/tool/draw_rectangle_fill… |      12 +++++++-----
         M src/model/tool/draw_rectangle_imp.… |      12 +++++++-----
         M src/model/tool/erase_imp.rs         |       5 +++--
         M src/model/tool/fill_imp.rs          |      12 +++++++-----
         M src/model/tool/flip_imp.rs          |       5 +++--
         M src/model/tool/font_imp.rs          |      13 +++++++------
         M src/model/tool/line_imp.rs          |      12 +++++++-----
         M src/model/tool/mod.rs               |      13 ++++++++++++-
         M src/model/tool/move_layer_imp.rs    |       5 +++--
         M src/model/tool/pencil_imp.rs        |      14 ++++++++------
         M src/model/tool/pipette_imp.rs       |       5 +++--
         M src/ui/ansi_editor/mod.rs           |       2 +-
         M src/ui/char_table.rs                |       2 +-
         M src/ui/main_window.rs               |      23 +++++++++++++----------
         M src/ui/mod.rs                       |       3 +++
         A src/ui/select_character_dialog.rs   |      64 +++++++++++++++++++++++++++++++
       
       21 files changed, 234 insertions(+), 127 deletions(-)
       ---
   DIR diff --git a/i18n/en/icy_draw.ftl b/i18n/en/icy_draw.ftl
       @@ -80,4 +80,6 @@ export-save-sauce-label=Save sauce info
        export-compression-level-label=Compression level
        export-compression-level-off=Off
        export-compression-level-medium=Medium
       -export-compression-level-high=High
       -\ No newline at end of file
       +export-compression-level-high=High
       +
       +select-character-title=Select Character
       +\ No newline at end of file
   DIR diff --git a/src/model/tool/brush_imp.rs b/src/model/tool/brush_imp.rs
       @@ -5,11 +5,11 @@ use eframe::{
        use egui_extras::RetainedImage;
        use i18n_embed_fl::fl;
        use icy_engine::AttributedChar;
       -use std::sync::{Arc, Mutex};
       +use std::{sync::{Arc, Mutex}, rc::Rc, cell::RefCell};
        
       -use crate::ansi_editor::BufferView;
       +use crate::{ansi_editor::BufferView, SelectCharacterDialog};
        
       -use super::{Editor, Position, Tool};
       +use super::{Editor, Position, Tool, ToolUiResult};
        
        #[derive(PartialEq, Eq)]
        pub enum BrushType {
       @@ -22,7 +22,7 @@ pub struct BrushTool {
            pub use_fore: bool,
            pub use_back: bool,
            pub size: i32,
       -    pub char_code: char,
       +    pub char_code: Rc<RefCell<char>>,
            pub font_page: usize,
        
            pub brush_type: BrushType,
       @@ -66,7 +66,7 @@ impl BrushTool {
                                let attribute = editor.caret.get_attribute();
                                editor.set_char(
                                    center + Position::new(x, y),
       -                            Some(AttributedChar::new(self.char_code, attribute)),
       +                            Some(AttributedChar::new(*self.char_code.borrow(), attribute)),
                                );
                            }
                            BrushType::Color => {
       @@ -106,7 +106,8 @@ impl Tool for BrushTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        let mut result = ToolUiResult::new();
                ui.vertical_centered(|ui| {
                    ui.horizontal(|ui| {
                        if ui
       @@ -144,7 +145,7 @@ impl Tool for BrushTool {
                    );
        
                    if let Some(b) = &buffer_opt {
       -                ui.add(draw_glyph(b.clone(), self.char_code, self.font_page));
       +                draw_glyph(ui, b.clone(), &mut result,self.char_code.clone(), self.font_page);
                    }
                });
                ui.radio_value(
       @@ -152,6 +153,7 @@ impl Tool for BrushTool {
                    BrushType::Color,
                    fl!(crate::LANGUAGE_LOADER, "tool-colorize"),
                );
       +        result
            }
        
            fn handle_click(
       @@ -179,62 +181,64 @@ impl Tool for BrushTool {
            }
        }
        
       -pub fn draw_glyph(buf: Arc<Mutex<BufferView>>, ch: char, font_page: usize) -> impl egui::Widget {
       -    move |ui: &mut egui::Ui| {
       -        let font = &buf.lock().unwrap().editor.buf.font_table[font_page];
       -        let scale = 1.5;
       -        let (id, stroke_rect) = ui.allocate_space(Vec2::new(
       -            scale * font.size.width as f32,
       -            scale * font.size.height as f32,
       -        ));
       -        let mut response = ui.interact(stroke_rect, id, Sense::click());
       -
       -        let col = if response.hovered() {
       -            Color32::WHITE
       -        } else {
       -            Color32::GRAY
       -        };
       -
       -        let painter = ui.painter_at(stroke_rect);
       -        painter.rect_filled(stroke_rect, Rounding::none(), Color32::BLACK);
       -        let s = font.size;
       -        if let Some(glyph) = font.get_glyph(ch) {
       -            for y in 0..s.height {
       -                for x in 0..s.width {
       -                    if glyph.data[y as usize] & (128 >> x) != 0 {
       -                        painter.rect_filled(
       -                            Rect::from_min_size(
       -                                Pos2::new(
       -                                    stroke_rect.left() + x as f32 * scale,
       -                                    stroke_rect.top() + y as f32 * scale,
       -                                ),
       -                                Vec2::new(scale, scale),
       +pub fn draw_glyph(ui: &mut egui::Ui, buf: Arc<Mutex<BufferView>>, ui_result: &mut ToolUiResult, ch: Rc<RefCell<char>>, font_page: usize) {
       +    let font = &buf.lock().unwrap().editor.buf.font_table[font_page];
       +    let scale = 1.5;
       +    let (id, stroke_rect) = ui.allocate_space(Vec2::new(
       +        scale * font.size.width as f32,
       +        scale * font.size.height as f32,
       +    ));
       +    let mut response = ui.interact(stroke_rect, id, Sense::click());
       +
       +    let col = if response.hovered() {
       +        Color32::WHITE
       +    } else {
       +        Color32::GRAY
       +    };
       +
       +    if response.clicked() {
       +        ui_result.modal_dialog = Some(Box::new(SelectCharacterDialog::new(ch.clone())));
       +    }
       +
       +    let painter = ui.painter_at(stroke_rect);
       +    painter.rect_filled(stroke_rect, Rounding::none(), Color32::BLACK);
       +    let s = font.size;
       +    let ch  = *ch.borrow();
       +    if let Some(glyph) = font.get_glyph(ch) {
       +        for y in 0..s.height {
       +            for x in 0..s.width {
       +                if glyph.data[y as usize] & (128 >> x) != 0 {
       +                    painter.rect_filled(
       +                        Rect::from_min_size(
       +                            Pos2::new(
       +                                stroke_rect.left() + x as f32 * scale,
       +                                stroke_rect.top() + y as f32 * scale,
                                    ),
       -                            Rounding::none(),
       -                            col,
       -                        );
       -                    }
       +                            Vec2::new(scale, scale),
       +                        ),
       +                        Rounding::none(),
       +                        col,
       +                    );
                        }
                    }
       -            response = response.on_hover_ui(|ui| {
       -                ui.horizontal(|ui| {
       -                    ui.label(RichText::new("Char").small());
       -                    ui.label(
       -                        RichText::new(format!("{0}/0x{0:02X}", ch as u32))
       -                            .small()
       -                            .color(Color32::WHITE),
       -                    );
       -                });
       -                ui.horizontal(|ui| {
       -                    ui.label(RichText::new("Font").small());
       -                    ui.label(
       -                        RichText::new(font.name.to_string())
       -                            .small()
       -                            .color(Color32::WHITE),
       -                    );
       -                });
       -            });
                }
       -        response
       +        response = response.on_hover_ui(|ui| {
       +            ui.horizontal(|ui| {
       +                ui.label(RichText::new("Char").small());
       +                ui.label(
       +                    RichText::new(format!("{0}/0x{0:02X}", ch as u32))
       +                        .small()
       +                        .color(Color32::WHITE),
       +                );
       +            });
       +            ui.horizontal(|ui| {
       +                ui.label(RichText::new("Font").small());
       +                ui.label(
       +                    RichText::new(font.name.to_string())
       +                        .small()
       +                        .color(Color32::WHITE),
       +                );
       +            });
       +        });
            }
        }
   DIR diff --git a/src/model/tool/click_imp.rs b/src/model/tool/click_imp.rs
       @@ -9,7 +9,7 @@ use crate::{
            model::{Selection, Shape},
        };
        
       -use super::{Event, Position, Tool};
       +use super::{Event, Position, Tool, ToolUiResult};
        
        pub struct ClickTool {}
        
       @@ -23,7 +23,8 @@ impl Tool for ClickTool {
                _ctx: &egui::Context,
                _ui: &mut egui::Ui,
                _buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        ToolUiResult::new()
            }
        
            fn handle_click(
   DIR diff --git a/src/model/tool/draw_ellipse_filled_imp.rs b/src/model/tool/draw_ellipse_filled_imp.rs
       @@ -7,7 +7,7 @@ use icy_engine::{Rectangle, TextAttribute};
        use crate::ansi_editor::BufferView;
        
        use super::{
       -    brush_imp::draw_glyph, plot_point, DrawMode, Event, Plottable, Position, ScanLines, Tool,
       +    brush_imp::draw_glyph, plot_point, DrawMode, Event, Plottable, Position, ScanLines, Tool, ToolUiResult,
        };
        
        pub struct DrawEllipseFilledTool {
       @@ -16,7 +16,7 @@ pub struct DrawEllipseFilledTool {
            pub use_fore: bool,
            pub use_back: bool,
            pub attr: TextAttribute,
       -    pub char_code: char,
       +    pub char_code: std::rc::Rc<std::cell::RefCell<char>>,
            pub font_page: usize,
        }
        
       @@ -31,7 +31,7 @@ impl Plottable for DrawEllipseFilledTool {
                self.use_back
            }
            fn get_char_code(&self) -> char {
       -        self.char_code
       +        *self.char_code.borrow()
            }
        }
        
       @@ -52,7 +52,8 @@ impl Tool for DrawEllipseFilledTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        let mut result = ToolUiResult::new();
                ui.vertical_centered(|ui| {
                    ui.horizontal(|ui| {
                        if ui
       @@ -83,7 +84,7 @@ impl Tool for DrawEllipseFilledTool {
                    );
        
                    if let Some(b) = &buffer_opt {
       -                ui.add(draw_glyph(b.clone(), self.char_code, self.font_page));
       +                draw_glyph(ui, b.clone(), &mut result,self.char_code.clone(), self.font_page);
                    }
                });
                ui.radio_value(
       @@ -96,6 +97,7 @@ impl Tool for DrawEllipseFilledTool {
                    DrawMode::Colorize,
                    fl!(crate::LANGUAGE_LOADER, "tool-colorize"),
                );
       +        result
            }
        
            fn handle_drag(
   DIR diff --git a/src/model/tool/draw_ellipse_imp.rs b/src/model/tool/draw_ellipse_imp.rs
       @@ -8,7 +8,7 @@ use crate::ansi_editor::BufferView;
        
        use super::{
            brush_imp::draw_glyph, line_imp::set_half_block, DrawMode, Event, Plottable, Position,
       -    ScanLines, Tool,
       +    ScanLines, Tool, ToolUiResult,
        };
        
        pub struct DrawEllipseTool {
       @@ -17,7 +17,7 @@ pub struct DrawEllipseTool {
            pub use_fore: bool,
            pub use_back: bool,
            pub attr: TextAttribute,
       -    pub char_code: char,
       +    pub char_code: std::rc::Rc<std::cell::RefCell<char>>,
            pub font_page: usize,
        }
        
       @@ -32,7 +32,7 @@ impl Plottable for DrawEllipseTool {
                self.use_back
            }
            fn get_char_code(&self) -> char {
       -        self.char_code
       +        *self.char_code.borrow()
            }
        }
        
       @@ -53,7 +53,8 @@ impl Tool for DrawEllipseTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        let mut result = ToolUiResult::new();
                ui.vertical_centered(|ui| {
                    ui.horizontal(|ui| {
                        if ui
       @@ -84,7 +85,7 @@ impl Tool for DrawEllipseTool {
                    );
        
                    if let Some(b) = &buffer_opt {
       -                ui.add(draw_glyph(b.clone(), self.char_code, self.font_page));
       +                draw_glyph(ui, b.clone(), &mut result,self.char_code.clone(), self.font_page);
                    }
                });
                ui.radio_value(
       @@ -97,6 +98,7 @@ impl Tool for DrawEllipseTool {
                    DrawMode::Colorize,
                    fl!(crate::LANGUAGE_LOADER, "tool-colorize"),
                );
       +        result
            }
        
            fn handle_drag(
   DIR diff --git a/src/model/tool/draw_rectangle_filled_imp.rs b/src/model/tool/draw_rectangle_filled_imp.rs
       @@ -4,7 +4,7 @@ use icy_engine::{Position, Rectangle, TextAttribute};
        
        use crate::ansi_editor::BufferView;
        
       -use super::{brush_imp::draw_glyph, plot_point, DrawMode, Event, Plottable, ScanLines, Tool};
       +use super::{brush_imp::draw_glyph, plot_point, DrawMode, Event, Plottable, ScanLines, Tool, ToolUiResult};
        use std::sync::{Arc, Mutex};
        
        pub struct DrawRectangleFilledTool {
       @@ -13,7 +13,7 @@ pub struct DrawRectangleFilledTool {
            pub use_fore: bool,
            pub use_back: bool,
            pub attr: TextAttribute,
       -    pub char_code: char,
       +    pub char_code: std::rc::Rc<std::cell::RefCell<char>>,
            pub font_page: usize,
        }
        
       @@ -29,7 +29,7 @@ impl Plottable for DrawRectangleFilledTool {
                self.use_back
            }
            fn get_char_code(&self) -> char {
       -        self.char_code
       +        *self.char_code.borrow()
            }
        }
        
       @@ -50,7 +50,8 @@ impl Tool for DrawRectangleFilledTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        let mut result = ToolUiResult::new();
                ui.vertical_centered(|ui| {
                    ui.horizontal(|ui| {
                        if ui
       @@ -81,7 +82,7 @@ impl Tool for DrawRectangleFilledTool {
                    );
        
                    if let Some(b) = &buffer_opt {
       -                ui.add(draw_glyph(b.clone(), self.char_code, self.font_page));
       +                draw_glyph(ui, b.clone(), &mut result,self.char_code.clone(), self.font_page);
                    }
                });
                ui.radio_value(
       @@ -94,6 +95,7 @@ impl Tool for DrawRectangleFilledTool {
                    DrawMode::Colorize,
                    fl!(crate::LANGUAGE_LOADER, "tool-colorize"),
                );
       +        result
            }
        
            fn handle_drag(
   DIR diff --git a/src/model/tool/draw_rectangle_imp.rs b/src/model/tool/draw_rectangle_imp.rs
       @@ -6,7 +6,7 @@ use crate::ansi_editor::BufferView;
        
        use super::{
            brush_imp::draw_glyph, line_imp::set_half_block, DrawMode, Event, Plottable, Position,
       -    ScanLines, Tool,
       +    ScanLines, Tool, ToolUiResult,
        };
        use std::sync::{Arc, Mutex};
        
       @@ -16,7 +16,7 @@ pub struct DrawRectangleTool {
            pub use_fore: bool,
            pub use_back: bool,
            pub attr: TextAttribute,
       -    pub char_code: char,
       +    pub char_code: std::rc::Rc<std::cell::RefCell<char>>,
            pub font_page: usize,
        }
        
       @@ -32,7 +32,7 @@ impl Plottable for DrawRectangleTool {
                self.use_back
            }
            fn get_char_code(&self) -> char {
       -        self.char_code
       +        *self.char_code.borrow()
            }
        }
        
       @@ -52,7 +52,8 @@ impl Tool for DrawRectangleTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        let mut result = ToolUiResult::new();
                ui.vertical_centered(|ui| {
                    ui.horizontal(|ui| {
                        if ui
       @@ -83,7 +84,7 @@ impl Tool for DrawRectangleTool {
                    );
        
                    if let Some(b) = &buffer_opt {
       -                ui.add(draw_glyph(b.clone(), self.char_code, self.font_page));
       +                draw_glyph(ui, b.clone(), &mut result,self.char_code.clone(), self.font_page);
                    }
                });
                ui.radio_value(
       @@ -96,6 +97,7 @@ impl Tool for DrawRectangleTool {
                    DrawMode::Colorize,
                    fl!(crate::LANGUAGE_LOADER, "tool-colorize"),
                );
       +        result
            }
        
            fn handle_drag(
   DIR diff --git a/src/model/tool/erase_imp.rs b/src/model/tool/erase_imp.rs
       @@ -6,7 +6,7 @@ use icy_engine::{AttributedChar, TextAttribute};
        
        use crate::ansi_editor::BufferView;
        
       -use super::{Editor, Position, Tool};
       +use super::{Editor, Position, Tool, ToolUiResult};
        
        #[derive(PartialEq, Eq)]
        pub enum EraseType {
       @@ -87,7 +87,7 @@ impl Tool for EraseTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                _buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
                ui.horizontal(|ui| {
                    ui.label(fl!(crate::LANGUAGE_LOADER, "tool-size-label"));
                    ui.add(
       @@ -106,6 +106,7 @@ impl Tool for EraseTool {
                    EraseType::Shade,
                    fl!(crate::LANGUAGE_LOADER, "tool-shade"),
                );
       +        ToolUiResult::new()
            }
        
            fn handle_click(
   DIR diff --git a/src/model/tool/fill_imp.rs b/src/model/tool/fill_imp.rs
       @@ -9,7 +9,7 @@ use icy_engine::{AttributedChar, TextAttribute};
        
        use crate::ansi_editor::BufferView;
        
       -use super::{brush_imp::draw_glyph, Editor, Event, Position, Tool};
       +use super::{brush_imp::draw_glyph, Editor, Event, Position, Tool, ToolUiResult};
        
        #[derive(PartialEq, Eq)]
        pub enum FillType {
       @@ -22,7 +22,7 @@ pub struct FillTool {
            pub use_back: bool,
        
            pub attr: TextAttribute,
       -    pub char_code: char,
       +    pub char_code: std::rc::Rc<std::cell::RefCell<char>>,
            pub font_page: usize,
            pub fill_type: FillType,
        }
       @@ -157,7 +157,8 @@ impl Tool for FillTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        let mut result = ToolUiResult::new();
                ui.vertical_centered(|ui| {
                    ui.horizontal(|ui| {
                        if ui
       @@ -183,7 +184,7 @@ impl Tool for FillTool {
                    );
        
                    if let Some(b) = &buffer_opt {
       -                ui.add(draw_glyph(b.clone(), self.char_code, self.font_page));
       +                draw_glyph(ui, b.clone(), &mut result,self.char_code.clone(), self.font_page);
                    }
                });
                ui.radio_value(
       @@ -191,6 +192,7 @@ impl Tool for FillTool {
                    FillType::Colorize,
                    fl!(crate::LANGUAGE_LOADER, "tool-colorize"),
                );
       +        result
            }
        
            fn handle_click(
       @@ -215,7 +217,7 @@ impl Tool for FillTool {
                            attr,
                            pos,
                            ch,
       -                    AttributedChar::new(self.char_code, attr),
       +                    AttributedChar::new(*self.char_code.borrow(), attr),
                        );
                        editor.end_atomic_undo();
                    }
   DIR diff --git a/src/model/tool/flip_imp.rs b/src/model/tool/flip_imp.rs
       @@ -4,7 +4,7 @@ use eframe::egui;
        
        use crate::ansi_editor::BufferView;
        
       -use super::{Event, Position, Tool};
       +use super::{Event, Position, Tool, ToolUiResult};
        pub struct FlipTool {}
        
        impl Tool for FlipTool {
       @@ -22,7 +22,8 @@ impl Tool for FlipTool {
                _ctx: &egui::Context,
                _ui: &mut egui::Ui,
                _buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        ToolUiResult::new()
            }
        
            fn handle_click(
   DIR diff --git a/src/model/tool/font_imp.rs b/src/model/tool/font_imp.rs
       @@ -5,7 +5,7 @@ use std::{
        
        use crate::ansi_editor::BufferView;
        
       -use super::{Event, MKey,  MModifiers, Position, Tool};
       +use super::{Event, MKey,  MModifiers, Position, Tool, ToolUiResult};
        use directories::ProjectDirs;
        use eframe::{
            egui::{self, ComboBox},
       @@ -23,9 +23,9 @@ pub struct FontTool {
        }
        
        impl FontTool {
       -    pub fn get_selected_font(&self) -> Option<&TheDrawFont> {
       +    /*pub fn get_selected_font(&self) -> Option<&TheDrawFont> {
                self.fonts.get(self.selected_font as usize)
       -    }
       +    }*/
        
            fn is_hidden(entry: &DirEntry) -> bool {
                entry
       @@ -86,10 +86,10 @@ impl Tool for FontTool {
        
            fn show_ui(
                &mut self,
       -        ctx: &egui::Context,
       +        _ctx: &egui::Context,
                ui: &mut egui::Ui,
       -        buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +        _buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       +    ) -> ToolUiResult {
                ui.vertical_centered(|ui| {
                    let mut selected_text = "<none>".to_string();
        
       @@ -113,6 +113,7 @@ impl Tool for FontTool {
                            }
                        });
                });
       +        ToolUiResult::new()
            }
        
            fn handle_click(
   DIR diff --git a/src/model/tool/line_imp.rs b/src/model/tool/line_imp.rs
       @@ -7,7 +7,7 @@ use icy_engine::{AttributedChar, Rectangle, TextAttribute};
        use crate::{ansi_editor::BufferView, model::ScanLines};
        
        use super::{
       -    brush_imp::draw_glyph, plot_point, DrawMode, Editor, Event, Plottable, Position, Tool,
       +    brush_imp::draw_glyph, plot_point, DrawMode, Editor, Event, Plottable, Position, Tool, ToolUiResult,
        };
        
        pub struct LineTool {
       @@ -16,7 +16,7 @@ pub struct LineTool {
            pub use_fore: bool,
            pub use_back: bool,
            pub attr: TextAttribute,
       -    pub char_code: char,
       +    pub char_code: std::rc::Rc<std::cell::RefCell<char>>,
            pub font_page: usize,
        
            pub old_pos: Position,
       @@ -34,7 +34,7 @@ impl Plottable for LineTool {
                self.use_back
            }
            fn get_char_code(&self) -> char {
       -        self.char_code
       +        *self.char_code.borrow()
            }
        }
        
       @@ -196,7 +196,8 @@ impl Tool for LineTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        let mut result = ToolUiResult::new();
                ui.vertical_centered(|ui| {
                    ui.horizontal(|ui| {
                        if ui
       @@ -227,7 +228,7 @@ impl Tool for LineTool {
                    );
        
                    if let Some(b) = &buffer_opt {
       -                ui.add(draw_glyph(b.clone(), self.char_code, self.font_page));
       +                draw_glyph(ui, b.clone(), &mut result,self.char_code.clone(), self.font_page);
                    }
                });
                ui.radio_value(
       @@ -240,6 +241,7 @@ impl Tool for LineTool {
                    DrawMode::Colorize,
                    fl!(crate::LANGUAGE_LOADER, "tool-colorize"),
                );
       +        result
            }
            /*
            fn handle_key(
   DIR diff --git a/src/model/tool/mod.rs b/src/model/tool/mod.rs
       @@ -78,6 +78,17 @@ impl MModifiers {
                matches!(self, MModifiers::Control)
            }
        }
       +pub struct ToolUiResult {
       +    pub modal_dialog: Option<Box<dyn crate::ModalDialog>>
       +}
       +
       +impl ToolUiResult {
       +    pub fn new() -> Self {
       +        Self {
       +            modal_dialog: None
       +        }
       +    }
       +}
        
        pub trait Tool {
            fn get_icon_name(&self) -> &'static RetainedImage;
       @@ -95,7 +106,7 @@ pub trait Tool {
                ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    );
       +    ) -> ToolUiResult;
        
            fn handle_key(
                &mut self,
   DIR diff --git a/src/model/tool/move_layer_imp.rs b/src/model/tool/move_layer_imp.rs
       @@ -1,4 +1,4 @@
       -use super::{Event, Position, Tool};
       +use super::{Event, Position, Tool, ToolUiResult};
        use crate::ansi_editor::BufferView;
        use eframe::egui;
        use std::sync::{Arc, Mutex};
       @@ -21,7 +21,8 @@ impl Tool for MoveLayer {
                _ctx: &egui::Context,
                _ui: &mut egui::Ui,
                _buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        ToolUiResult::new()
            }
        
            fn handle_drag_begin(
   DIR diff --git a/src/model/tool/pencil_imp.rs b/src/model/tool/pencil_imp.rs
       @@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex};
        
        use crate::{ansi_editor::BufferView, model::ScanLines};
        
       -use super::{line_imp::set_half_block, Position, Tool};
       +use super::{line_imp::set_half_block, Position, Tool, ToolUiResult, brush_imp::draw_glyph};
        
        #[derive(PartialEq, Eq)]
        pub enum PencilType {
       @@ -22,7 +22,7 @@ pub enum PencilType {
        pub struct PencilTool {
            pub use_fore: bool,
            pub use_back: bool,
       -    pub char_code: char,
       +    pub char_code: std::rc::Rc<std::cell::RefCell<char>>,
            pub font_page: usize,
        
            pub last_pos: Position,
       @@ -77,7 +77,7 @@ impl PencilTool {
                    PencilType::Solid => {
                        let editor = &mut buffer_view.lock().unwrap().editor;
                        let attribute = editor.caret.get_attribute();
       -                editor.set_char(center, Some(AttributedChar::new(self.char_code, attribute)));
       +                editor.set_char(center, Some(AttributedChar::new(*self.char_code.borrow(), attribute)));
                    }
                    PencilType::Color => {
                        let editor = &mut buffer_view.lock().unwrap().editor;
       @@ -109,7 +109,8 @@ impl Tool for PencilTool {
                _ctx: &egui::Context,
                ui: &mut egui::Ui,
                buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        let mut result = ToolUiResult::new();
                ui.vertical_centered(|ui| {
                    ui.horizontal(|ui| {
                        if ui
       @@ -144,7 +145,7 @@ impl Tool for PencilTool {
                    );
        
                    if let Some(b) = &buffer_opt {
       -                ui.add(draw_glyph(b.clone(), self.char_code, self.font_page));
       +                draw_glyph(ui, b.clone(), &mut result,self.char_code.clone(), self.font_page);
                    }
                });
                ui.radio_value(
       @@ -152,6 +153,7 @@ impl Tool for PencilTool {
                    PencilType::Color,
                    fl!(crate::LANGUAGE_LOADER, "tool-colorize"),
                );
       +        result
            }
        
            fn handle_click(
       @@ -180,7 +182,7 @@ impl Tool for PencilTool {
            }
        }
        
       -pub fn draw_glyph(buf: Arc<Mutex<BufferView>>, ch: char, font_page: usize) -> impl egui::Widget {
       +pub fn draw_glyph_plain(buf: Arc<Mutex<BufferView>>, ch: char, font_page: usize) -> impl egui::Widget {
            move |ui: &mut egui::Ui| {
                let font = &buf.lock().unwrap().editor.buf.font_table[font_page];
                let scale = 1.5;
   DIR diff --git a/src/model/tool/pipette_imp.rs b/src/model/tool/pipette_imp.rs
       @@ -4,7 +4,7 @@ use eframe::egui;
        
        use crate::ansi_editor::BufferView;
        
       -use super::{Event, Position, Tool};
       +use super::{Event, Position, Tool, ToolUiResult};
        pub struct PipetteTool {}
        
        impl Tool for PipetteTool {
       @@ -22,7 +22,8 @@ impl Tool for PipetteTool {
                _ctx: &egui::Context,
                _ui: &mut egui::Ui,
                _buffer_opt: Option<std::sync::Arc<std::sync::Mutex<crate::ui::ansi_editor::BufferView>>>,
       -    ) {
       +    ) -> ToolUiResult {
       +        ToolUiResult::new()
            }
        
            fn handle_click(
   DIR diff --git a/src/ui/ansi_editor/mod.rs b/src/ui/ansi_editor/mod.rs
       @@ -430,7 +430,7 @@ impl Document for AnsiEditor {
                                        .get_outline_char_code(i as i32)
                                        .unwrap();
                                    let cur_page = self.buffer_view.lock().unwrap().editor.cur_font_page;
       -                            ui.add(draw_glyph(
       +                            ui.add(crate::model::pencil_imp::draw_glyph_plain(
                                        self.buffer_view.clone(),
                                        unsafe { char::from_u32_unchecked(ch as u32) },
                                        cur_page,
   DIR diff --git a/src/ui/char_table.rs b/src/ui/char_table.rs
       @@ -18,7 +18,7 @@ pub fn show_char_table(buffer_opt: Option<Arc<Mutex<BufferView>>>) -> impl egui:
                        ui.horizontal_wrapped(|ui| {
                            for i in 0..font_length {
                                let ch = unsafe { char::from_u32_unchecked(i as u32) };
       -                        if ui.add(draw_glyph(buffer.clone(), ch, font_page)).clicked() {
       +                        if ui.add(crate::model::pencil_imp::draw_glyph_plain(buffer.clone(), ch, font_page)).clicked() {
                                    if let Ok(b) = &mut buffer.lock() {
                                        let mut p = AsciiParser::new();
                                        let editor = &mut b.editor;
   DIR diff --git a/src/ui/main_window.rs b/src/ui/main_window.rs
       @@ -2,7 +2,7 @@ use std::{
            fs,
            path::{Path, PathBuf},
            sync::Arc,
       -    time::Duration,
       +    time::Duration, cell::RefCell, rc::Rc,
        };
        
        use crate::{model::Tool, Document, EditSauceDialog, FontEditor, NewFileDialog, ModalDialog};
       @@ -44,7 +44,7 @@ impl MainWindow {
                    use_back: true,
                    use_fore: true,
                    brush_type: crate::model::pencil_imp::PencilType::Shade,
       -            char_code: '\u{00B0}',
       +            char_code: Rc::new(RefCell::new('\u{00B0}')),
                    font_page: 0,
                    last_pos: Position::default(),
                }));
       @@ -53,7 +53,7 @@ impl MainWindow {
                    use_back: true,
                    use_fore: true,
                    brush_type: crate::model::brush_imp::BrushType::Shade,
       -            char_code: '\u{00B0}',
       +            char_code: Rc::new(RefCell::new('\u{00B0}')),
                    font_page: 0,
                }));
                tools.push(Box::new(crate::model::erase_imp::EraseTool {
       @@ -66,7 +66,7 @@ impl MainWindow {
                    use_fore: true,
                    use_back: true,
                    attr: icy_engine::TextAttribute::default(),
       -            char_code: '\u{00B0}',
       +            char_code: Rc::new(RefCell::new('\u{00B0}')),
                    font_page: 0,
                    old_pos: icy_engine::Position { x: 0, y: 0 },
                }));
       @@ -76,7 +76,7 @@ impl MainWindow {
                        use_fore: true,
                        use_back: true,
                        attr: icy_engine::TextAttribute::default(),
       -                char_code: '\u{00B0}',
       +                char_code: Rc::new(RefCell::new('\u{00B0}')),
                        font_page: 0,
                    },
                ));
       @@ -87,7 +87,7 @@ impl MainWindow {
                        use_fore: true,
                        use_back: true,
                        attr: icy_engine::TextAttribute::default(),
       -                char_code: '\u{00B0}',
       +                char_code: Rc::new(RefCell::new('\u{00B0}')),
                        font_page: 0,
                    },
                ));
       @@ -96,7 +96,7 @@ impl MainWindow {
                    use_fore: true,
                    use_back: true,
                    attr: icy_engine::TextAttribute::default(),
       -            char_code: '\u{00B0}',
       +            char_code: Rc::new(RefCell::new('\u{00B0}')),
                    font_page: 0,
                }));
        
       @@ -106,7 +106,7 @@ impl MainWindow {
                        use_fore: true,
                        use_back: true,
                        attr: icy_engine::TextAttribute::default(),
       -                char_code: '\u{00B0}',
       +                char_code: Rc::new(RefCell::new('\u{00B0}')),
                        font_page: 0,
                    },
                ));
       @@ -114,7 +114,7 @@ impl MainWindow {
                tools.push(Box::new(crate::model::fill_imp::FillTool {
                    use_fore: true,
                    use_back: true,
       -            char_code: '\u{00B0}',
       +            char_code: Rc::new(RefCell::new('\u{00B0}')),
                    font_page: 0,
                    fill_type: crate::model::fill_imp::FillType::Character,
                    attr: icy_engine::TextAttribute::default(),
       @@ -588,7 +588,10 @@ impl eframe::App for MainWindow {
                    crate::add_tool_switcher(ctx, ui, self);
        
                    if let Some(tool) = self.tab_viewer.tools.get_mut(self.tab_viewer.selected_tool) {
       -                tool.show_ui(ctx, ui, buffer_opt.clone());
       +                let tool_result = tool.show_ui(ctx, ui, buffer_opt.clone());
       +                if tool_result.modal_dialog.is_some() {
       +                    self.modal_dialog = tool_result.modal_dialog;
       +                }
                    }
                });
                SidePanel::right("right_panel").show(ctx, |ui| {
   DIR diff --git a/src/ui/mod.rs b/src/ui/mod.rs
       @@ -36,6 +36,9 @@ mod export_file_dialog;
        mod layer_view;
        pub use layer_view::*;
        
       +mod select_character_dialog;
       +pub use select_character_dialog::*;
       +
        pub type TerminalResult<T> = Result<T, Box<dyn Error>>;
        
        pub trait ModalDialog {
   DIR diff --git a/src/ui/select_character_dialog.rs b/src/ui/select_character_dialog.rs
       @@ -0,0 +1,63 @@
       +use std::{rc::Rc, cell::RefCell};
       +
       +use eframe::egui::{self};
       +use egui_modal::Modal;
       +use i18n_embed_fl::fl;
       +
       +use crate::{TerminalResult, ModalDialog};
       +
       +pub struct SelectCharacterDialog {
       +    pub should_commit: bool,
       +    ch: Rc<RefCell<char>>,
       +    selected_ch: char
       +}
       +
       +impl SelectCharacterDialog {
       +    pub fn new(ch: Rc<RefCell<char>>) -> Self {
       +        let selected_ch = *ch.borrow();
       +        SelectCharacterDialog {
       +            should_commit: false,
       +            ch,
       +            selected_ch
       +        }
       +    }
       +}
       +
       +impl ModalDialog for SelectCharacterDialog {
       +    fn show(&mut self, ctx: &egui::Context) -> bool {
       +        let mut result = false;
       +        let modal = Modal::new(ctx, "my_modal");
       +
       +        modal.show(|ui| {
       +            modal.title(ui, fl!(crate::LANGUAGE_LOADER, "select-character-title"));
       +
       +            modal.frame(ui, |ui| {
       +            });
       +
       +            modal.buttons(ui, |ui| {
       +                if ui
       +                    .button(fl!(crate::LANGUAGE_LOADER, "new-file-ok"))
       +                    .clicked()
       +                {
       +                    self.should_commit = true;
       +                    result = true;
       +                }
       +                if ui
       +                    .button(fl!(crate::LANGUAGE_LOADER, "new-file-cancel"))
       +                    .clicked()
       +                {
       +                    result = true;
       +                }
       +            });
       +        });
       +        modal.open();
       +        result
       +    }
       +
       +    fn should_commit(&self) -> bool { self.should_commit }
       +
       +    fn commit(&self, editor: &mut crate::model::Editor) -> TerminalResult<bool>  {
       +        self.ch.swap(&RefCell::new(self.selected_ch));
       +        Ok(true)
       +    }
       +}
       +\ No newline at end of file