URI: 
       Added more tools/worked on ui. - 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 853d441dcbd42ee09494d55064db568bf7e41117
   DIR parent 0d66d7dbf52a5f449c3677cc205896b632263f24
  HTML Author: Mike Krüger <mkrueger@posteo.de>
       Date:   Sat,  2 Sep 2023 18:22:25 +0200
       
       Added more tools/worked on ui.
       
       Diffstat:
         M i18n/en/icy_draw.ftl                |      14 ++++++++++++--
         D src/ui/bitfont_selector.rs          |      56 -------------------------------
         D src/ui/char_table.rs                |      61 -------------------------------
         M src/ui/dialogs/save_file_dialog.rs  |       2 +-
         M src/ui/editor/ansi/mod.rs           |       2 +-
         D src/ui/layer_view.rs                |     206 -------------------------------
         M src/ui/main_window.rs               |      56 ++++++++++++++++++++-----------
         M src/ui/messages.rs                  |     151 +++++++++++++++++++------------
         M src/ui/mod.rs                       |       8 ++------
         A src/ui/tools/bitfont_selector.rs    |      83 +++++++++++++++++++++++++++++++
         A src/ui/tools/char_table.rs          |      79 +++++++++++++++++++++++++++++++
         A src/ui/tools/layer_view.rs          |     247 +++++++++++++++++++++++++++++++
         A src/ui/tools/mod.rs                 |       9 +++++++++
         M src/ui/top_bar.rs                   |      72 ++++++++++++++++----------------
       
       14 files changed, 601 insertions(+), 445 deletions(-)
       ---
   DIR diff --git a/i18n/en/icy_draw.ftl b/i18n/en/icy_draw.ftl
       @@ -126,4 +126,14 @@ select-font-dialog-filter-text=Filter fonts
        select-font-dialog-no-fonts=No fonts matches the filter
        select-font-dialog-no-fonts-installed=No fonts installed
        
       -layer_tool_title=Layer
       -\ No newline at end of file
       +layer_tool_title=Layer
       +layer_tool_menu_layer_properties=Layer properties
       +layer_tool_menu_new_layer=New layer
       +layer_tool_menu_duplicate_layer=Duplicate layer
       +layer_tool_menu_merge_layer=Merge layer
       +layer_tool_menu_delete_layer=Delete layer
       +
       +char_table_tool_title=Char table
       +bitfont_tool_title=Fonts
       +
       +no_document_selected=No document selected
       +\ No newline at end of file
   DIR diff --git a/src/ui/bitfont_selector.rs b/src/ui/bitfont_selector.rs
       @@ -1,56 +0,0 @@
       -use eframe::egui;
       -
       -use crate::{AnsiEditor, Message};
       -
       -#[derive(Default)]
       -pub struct BitFontSelector {}
       -
       -impl BitFontSelector {
       -    pub fn show_ui(
       -        &self,
       -        _ctx: &egui::Context,
       -        ui: &mut egui::Ui,
       -        editor: &AnsiEditor,
       -    ) -> Option<Message> {
       -        let mut result = None;
       -
       -        /*
       -        for (id, font) in editor.buffer_view.lock().buf.font_iter() {
       -
       -            if id >= 100 {
       -                // TODO
       -            }
       -        }*/
       -        let row_height = 23.0;
       -
       -        let cur_font_page = editor.buffer_view.lock().caret.get_font_page();
       -
       -        egui::ScrollArea::vertical()
       -            .id_source("bitfont_scroll_area")
       -            .max_height(300.)
       -            .show_rows(
       -                ui,
       -                row_height,
       -                icy_engine::parsers::ansi::constants::ANSI_FONT_NAMES.len(),
       -                |ui, range| {
       -                    for r in range {
       -                        ui.horizontal(|ui| {
       -                            if ui
       -                                .selectable_label(
       -                                    cur_font_page == r,
       -                                    format!(
       -                                        "{r}. {}",
       -                                        icy_engine::parsers::ansi::constants::ANSI_FONT_NAMES[r]
       -                                    ),
       -                                )
       -                                .clicked()
       -                            {
       -                                result = Some(Message::SetFontPage(r));
       -                            }
       -                        });
       -                    }
       -                },
       -            );
       -        result
       -    }
       -}
   DIR diff --git a/src/ui/char_table.rs b/src/ui/char_table.rs
       @@ -1,61 +0,0 @@
       -/* TODO
       -
       -use icy_engine::{ascii, BufferParser};
       -use eframe::egui::{self};
       -
       -use crate::AnsiEditor;
       -
       -
       -pub fn show_char_table(editor: Option<&AnsiEditor>) -> impl egui::Widget {
       -    let Some(editor) = &editor else {
       -        return move |ui: &mut egui::Ui| { ui.label("no selected editor") }.inner.response;
       -    };
       -
       -    let buffer_view = editor.buffer_view.clone();
       -
       -    move |ui: &mut egui::Ui| {
       -        let font_page = editor.buffer_view.lock().caret.get_font_page();
       -        let font_length = editor
       -            .buffer_view
       -            .lock()
       -            .buf
       -            .get_font(font_page)
       -            .unwrap()
       -            .length;
       -
       -        egui::ScrollArea::vertical()
       -            .id_source("char_table_scroll_area")
       -            .show(ui, |ui| {
       -                ui.horizontal_wrapped(|ui| {
       -                    for i in 0..font_length {
       -                        let ch = unsafe { char::from_u32_unchecked(i as u32) };
       -                        if ui
       -                            .add(crate::model::pencil_imp::draw_glyph_plain(
       -                                editor.clone(),
       -                                ch,
       -                                font_page,
       -                            ))
       -                            .clicked()
       -                        {
       -                            let mut p = ascii::Parser::default();
       -                            let editor = &mut b.editor;
       -                            let res = BufferParser::print_char(
       -                                &mut p,
       -                                &mut editor.buf,
       -                                &mut editor.caret,
       -                                ch,
       -                            );
       -                            if let Err(err) = res {
       -                                eprintln!("{}", err);
       -                            }
       -                            b.redraw_view();
       -                        }
       -                    }
       -                })
       -            })
       -            .inner
       -            .response
       -    }
       -
       -}
       -    */
   DIR diff --git a/src/ui/dialogs/save_file_dialog.rs b/src/ui/dialogs/save_file_dialog.rs
       @@ -46,7 +46,7 @@ impl crate::ModalDialog for SaveFileDialog {
            fn commit_self(&self, window: &mut MainWindow) -> crate::TerminalResult<bool> {
                if let Some(file) = &self.opened_file.clone() {
                    let file = file.with_extension("icd");
       -            if let Some(editor) = window.get_ansi_editor() {
       +            if let Some(editor) = window.get_active_document().unwrap().lock().unwrap().get_ansi_editor() {
                        let options = SaveOptions::new();
                        editor
                            .save_content(file.to_path_buf().as_path(), &options)
   DIR diff --git a/src/ui/editor/ansi/mod.rs b/src/ui/editor/ansi/mod.rs
       @@ -43,7 +43,7 @@ pub struct AnsiEditor {
            last_pos: Position,
        
            pub buffer_view: Arc<eframe::epaint::mutex::Mutex<BufferView>>,
       -    buffer_parser: Box<dyn BufferParser>,
       +    pub buffer_parser: Box<dyn BufferParser>,
        
            pub cur_outline: usize,
            pub is_inactive: bool,
   DIR diff --git a/src/ui/layer_view.rs b/src/ui/layer_view.rs
       @@ -1,206 +0,0 @@
       -use std::sync::{Arc, Mutex};
       -
       -use eframe::{
       -    egui::{self, RichText, Sense, TextStyle},
       -    epaint::{Color32, Vec2, Rounding, Rect, pos2}, emath::Align2,
       -};
       -use i18n_embed_fl::fl;
       -
       -use crate::{AnsiEditor, Message, ToolWindow, Document};
       -
       -#[derive(Default)]
       -pub struct LayerToolWindow {}
       -
       -
       -impl ToolWindow for LayerToolWindow {
       -    fn get_title(&self) -> String {
       -        fl!(crate::LANGUAGE_LOADER, "layer_tool_title")
       -    }
       - 
       -    fn show_ui(
       -        &mut self,
       -        ui: &mut egui::Ui,
       -        active_document: Option<Arc<Mutex<Box<dyn Document>>>> 
       -    ) -> Option<Message> {
       -
       -        if let Some(doc) = active_document {
       -            if let Some(editor) = doc.lock().unwrap().get_ansi_editor() {
       -                return show_layer_view(ui, editor);
       -            }
       -        }
       -
       -        ui.label("No document selected");
       -        None
       -    }
       -}
       -
       -
       -fn show_layer_view(
       -    ui: &mut egui::Ui,
       -    editor: &AnsiEditor,
       -) -> Option<Message> {
       -    let row_height = 24.0;
       -    let mut result = None;
       -
       -    let max = editor.buffer_view.lock().buf.layers.len();
       -    let cur_layer = editor.cur_layer;
       -    let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
       -
       -    egui::ScrollArea::vertical()
       -    .id_source("layer_view_scroll_area")
       -    .max_height(200.)
       -    .show_rows(ui, row_height, max, |ui, range| {
       -        for i in range {           
       -            ui.horizontal(|ui| {
       -                ui.add_space(4.0);
       -                let (is_visible, title, color) = {
       -                    let layer = &editor.buffer_view.lock().buf.layers[i];
       -                    (layer.is_visible, layer.title.clone(), layer.color)
       -                };
       -                let width =  ui.available_width();
       -
       -                let (id, back_rect) = ui.allocate_space(Vec2::new(width, row_height));
       -                let response = ui.interact(back_rect, id, Sense::click());
       -
       -                let back_painter = ui.painter_at(back_rect);
       -
       -                if response.hovered() {
       -                    back_painter.rect_filled(
       -                        back_rect,
       -                        Rounding::none(),
       -                        ui.style().visuals.widgets.active.bg_fill,
       -                    );
       -                } else if i == cur_layer {
       -                    back_painter.rect_filled(
       -                        back_rect,
       -                        Rounding::none(),
       -                        ui.style().visuals.extreme_bg_color,
       -                    );
       -                }
       -
       -                let stroke_rect = Rect::from_min_size(
       -                    back_rect.min + Vec2::new(0.0, 1.0),
       -                    Vec2::new(22.0, 22.0),
       -                );
       -                let visible_icon_response = ui.interact(stroke_rect, id.with("visible"), Sense::click());
       -
       -                let painter = ui.painter_at(stroke_rect);
       -
       -    
       -                if let Some(color) = color {
       -                    let (r, g, b) = color.into();
       -                    painter.rect_filled(
       -                        stroke_rect,
       -                        Rounding::none(),
       -                        Color32::from_rgb(r, g, b)
       -                    );
       -                } 
       -
       -                let image = if is_visible {
       -                    super::VISIBLE_SVG.texture_id(ui.ctx())
       -                } else {
       -                    super::INVISIBLE_SVG.texture_id(ui.ctx())
       -                };
       -
       -                let tint = if i == cur_layer {
       -                    ui.visuals().widgets.active.fg_stroke.color
       -                } else {
       -                    ui.visuals().widgets.inactive.fg_stroke.color
       -                };
       -                
       -                painter.image(image, stroke_rect, uv, tint);
       -                
       -
       -                let color = if  i == cur_layer {
       -                    ui.style().visuals.strong_text_color()
       -                } else {
       -                    ui.style().visuals.text_color()
       -                };
       -                let font_id = TextStyle::Button.resolve(ui.style());
       -
       -                back_painter.text(
       -                    stroke_rect.right_center() + Vec2::new(4., 0.),
       -                    Align2::LEFT_CENTER,
       -                    title,
       -                    font_id,
       -                    color,
       -                );
       -
       -                if visible_icon_response.clicked() {
       -                    result = Some(Message::ToggleVisibility(i));
       -                }
       -
       -                if response.clicked() {
       -                    result = Some(Message::SelectLayer(i));
       -                }
       -
       -                if response.double_clicked() {
       -                    result = Some(Message::EditLayer(i));
       -                }
       -            });
       -        }
       -    });
       -            
       -
       -    let img_size = Vec2::new(24., 24.);
       -    ui.horizontal(|ui| {
       -        let r = ui
       -            .add(egui::ImageButton::new(
       -                super::ADD_LAYER_SVG.texture_id(ui.ctx()),
       -                img_size,
       -            ))
       -            .on_hover_ui(|ui| {
       -                ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "add_layer_tooltip")).small());
       -            });
       -
       -        if r.clicked() {
       -            result = Some(Message::NewLayer);
       -        }
       -
       -        let r = ui
       -            .add(egui::ImageButton::new(
       -                super::MOVE_UP_SVG.texture_id(ui.ctx()),
       -                img_size,
       -            ))
       -            .on_hover_ui(|ui| {
       -                ui.label(
       -                    RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_up_tooltip")).small(),
       -                );
       -            });
       -
       -        if r.clicked() && cur_layer > 0 {
       -            result = Some(Message::MoveLayerUp(cur_layer));
       -        }
       -
       -        let r = ui
       -            .add(egui::ImageButton::new(
       -                super::MOVE_DOWN_SVG.texture_id(ui.ctx()),
       -                img_size,
       -            ))
       -            .on_hover_ui(|ui| {
       -                ui.label(
       -                    RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_down_tooltip")).small(),
       -                );
       -            });
       -
       -        if r.clicked() && (1 + cur_layer) < max {
       -            result = Some(Message::MoveLayerDown(cur_layer));
       -        }
       -
       -        let r = ui
       -            .add(egui::ImageButton::new(
       -                super::DELETE_SVG.texture_id(ui.ctx()),
       -                img_size,
       -            ))
       -            .on_hover_ui(|ui| {
       -                ui.label(
       -                    RichText::new(fl!(crate::LANGUAGE_LOADER, "delete_layer_tooltip")).small(),
       -                );
       -            });
       -
       -        if r.clicked() && cur_layer < max {
       -            result = Some(Message::DeleteLayer(cur_layer));
       -        }
       -    });
       -    result
       -}
   DIR diff --git a/src/ui/main_window.rs b/src/ui/main_window.rs
       @@ -8,8 +8,8 @@ use std::{
        };
        
        use crate::{
       -    add_child, model::Tool, AnsiEditor, BitFontEditor, BitFontSelector, CharFontEditor,
       -    Document, DocumentOptions, ModalDialog, DocumentTab, DocumentBehavior, TopBar, ToolTab, ToolBehavior, ui::layer_view::LayerToolWindow,
       +    add_child, model::Tool, AnsiEditor, BitFontEditor, CharFontEditor,
       +    Document, DocumentOptions, ModalDialog, DocumentTab, DocumentBehavior, TopBar, ToolTab, ToolBehavior, LayerToolWindow, CharTableToolWindow, BitFontSelector,
        };
        use eframe::{
            egui::{self, Response, SidePanel, TextStyle, Ui},
       @@ -30,7 +30,6 @@ pub struct MainWindow {
        
            dialog_open: bool,
            modal_dialog: Option<Box<dyn ModalDialog>>,
       -    bitfont_selector: Option<BitFontSelector>,
            id: usize,
            palette_mode: usize,
            pub top_bar: TopBar,
       @@ -151,9 +150,15 @@ impl MainWindow {
                .into();
                ctx.set_style(style);
        
       -        let mut tool_tree= egui_tiles::Tree::<ToolTab>::default();
       +        let mut tool_tree= egui_tiles::Tree::<ToolTab>::empty("tool_tree");
                let layers = tool_tree.tiles.insert_pane(ToolTab::new(LayerToolWindow::default()));
       -        tool_tree.root = Some(layers);
       +        let charTable = tool_tree.tiles.insert_pane(ToolTab::new(CharTableToolWindow::default()));
       +        let bitfont_selector = tool_tree.tiles.insert_pane(ToolTab::new(BitFontSelector::default()));
       +
       +        let tab = tool_tree.tiles.insert_tab_tile(vec![charTable, bitfont_selector]);
       +        let v = tool_tree.tiles.insert_vertical_tile(vec![tab, layers]);
       +
       +        tool_tree.root = Some(v);
                
                MainWindow {
                    document_behavior: DocumentBehavior {
       @@ -166,7 +171,7 @@ impl MainWindow {
                    },
                    tool_behavior: ToolBehavior::default(),
                    toasts: egui_notify::Toasts::default(),
       -            document_tree:  egui_tiles::Tree::<DocumentTab>::default(),
       +            document_tree:  egui_tiles::Tree::<DocumentTab>::empty("document_tree"),
                    tool_tree,
                    gl: cc.gl.clone().unwrap(),
                    dialog_open: false,
       @@ -176,7 +181,6 @@ impl MainWindow {
                    right_panel: true,
                    bottom_panel: false,
                    palette_mode: 0,
       -            bitfont_selector: Some(BitFontSelector::default()),
                    top_bar: TopBar::new(&cc.egui_ctx),
                }
            }
       @@ -323,15 +327,19 @@ impl MainWindow {
                None
            }
        
       -    pub fn get_ansi_editor(&mut self) -> Option<&AnsiEditor> {
       -        if let Some(pane) = self.get_active_document() {
       -            return pane.lock().unwrap().get_ansi_editor();
       -        }
       -        None
       -    }
            pub(crate) fn open_dialog<T: ModalDialog + 'static>(&mut self, dialog: T) {
                self.modal_dialog = Some(Box::new(dialog));
            }
       +
       +    pub(crate) fn run_editor_command<T>(&mut self, param: T, func: fn(&mut MainWindow, &mut AnsiEditor, T))  {
       +        if let Some(doc) = self.get_active_document() {
       +            if let Ok(mut doc) = doc.lock() {
       +                if let Some(editor) = doc.get_ansi_editor_mut() {
       +                    func(self, editor, param);
       +                }
       +            }
       +        }
       +    }
        }
        
        pub fn button_with_shortcut(
       @@ -377,7 +385,8 @@ impl eframe::App for MainWindow {
                        ui.add_space(8.0);
        
                        let mut palette: usize = self.palette_mode;
       -                if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(doc) = self.get_active_document() {
       +                    if let Some(editor) = doc.lock().unwrap().get_ansi_editor() {
                            ui.vertical_centered(|ui| {
                                ui.add(crate::palette_switcher(ctx, editor));
                            });
       @@ -409,6 +418,7 @@ impl eframe::App for MainWindow {
                            }
                            ui.separator();
                        }
       +            }
                        self.palette_mode = palette;
        
                        crate::add_tool_switcher(ctx, ui, self);
       @@ -423,10 +433,13 @@ impl eframe::App for MainWindow {
                            ui.horizontal(|ui| {
                                ui.add_space(4.0);
                                ui.vertical(|ui| {
       -                            if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(doc) = self.get_active_document() {
       +
       +                            if let Some(editor) = doc.lock().unwrap().get_ansi_editor() {
                                        let tool_result = tool.show_ui(ctx, ui, editor);
                                        self.handle_message(tool_result);
                                    }
       +                        }
                                });
                            });
                        }
       @@ -442,10 +455,11 @@ impl eframe::App for MainWindow {
                    .exact_width(200.0)
                    .resizable(false)
                    .show_animated(ctx, self.right_panel, |ui| {
       -               // self.tool_behavior.active_document = Arc::new(Mutex::new(self.get_active_document()));
       -
       +                self.tool_behavior.active_document = self.get_active_document();
                        self.tool_tree.ui(&mut self.tool_behavior, ui);
       -               // self.tool_behavior.active_document = None;
       +                self.tool_behavior.active_document = None;
       +                let msg = self.tool_behavior.message.take();
       +                self.handle_message(msg);
        
                        /* 
                        let message = if let Some(doc) = self.get_active_document_mut() {
       @@ -470,7 +484,6 @@ impl eframe::App for MainWindow {
                        } else {
                            None
                        };
       -                self.handle_message(message);
        
                        self.bitfont_selector = Some(sel);
        
       @@ -494,9 +507,12 @@ impl eframe::App for MainWindow {
                    if self.modal_dialog.as_mut().unwrap().show(ctx) {
                        let modal_dialog = self.modal_dialog.take().unwrap();
                        if modal_dialog.should_commit() {
       -                    if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(doc) = self.get_active_document() {
       +
       +                    if let Some(editor) = doc.lock().unwrap().get_ansi_editor_mut() {
                                modal_dialog.commit(editor).unwrap();
                            }
       +                }
                             modal_dialog.commit_self(self).unwrap();
                        }
                    }
   DIR diff --git a/src/ui/messages.rs b/src/ui/messages.rs
       @@ -28,6 +28,9 @@ pub enum Message {
            MoveLayerDown(usize),
            ToggleVisibility(usize),
            SelectLayer(usize),
       +    DuplicateLayer(usize),
       +    MergeLayer(usize),
       +
            Undo,
            Redo,
            EditSauce,
       @@ -41,6 +44,7 @@ pub enum Message {
            SelectFontDialog(Arc<Mutex<Vec<TheDrawFont>>>, Arc<Mutex<i32>>),
            ShowError(String),
            SetFontPage(usize),
       +    CharTable(char),
        }
        
        pub const CTRL_SHIFT: egui::Modifiers = egui::Modifiers {
       @@ -88,21 +92,26 @@ impl MainWindow {
                        }
                    }
                    Message::ExportFile => {
       -                let mut buffer_opt = self.get_ansi_editor();
       -                let view = buffer_opt.unwrap().buffer_view.clone();
       -                self.open_dialog(crate::ExportFileDialog::new(&view.lock().buf));
       +                self.run_editor_command(0, |window, editor, _| {
       +                    let view = editor.buffer_view.clone();
       +                    window.open_dialog(crate::ExportFileDialog::new(&view.lock().buf));
       +                });
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor() {
       +                    let view = editor.buffer_view.clone();
       +                    self.open_dialog(crate::ExportFileDialog::new(&view.lock().buf));
       +                }
                    }
                    Message::ShowOutlineDialog => {
                        self.open_dialog(SelectOutlineDialog::default());
                    }
                    Message::Undo => {
       -                if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                            editor.undo();
                            editor.buffer_view.lock().redraw_view();
                        }
                    }
                    Message::Redo => {
       -                if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                        editor.redo();
                            editor.buffer_view.lock().redraw_view();
                        }
       @@ -110,7 +119,7 @@ impl MainWindow {
                    }
        
                    Message::SelectAll => {
       -                if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor() {
                            let buf = &mut editor.buffer_view.lock();
                                let w = buf.buf.get_width();
                                let h = buf.buf.get_line_count();
       @@ -119,14 +128,14 @@ impl MainWindow {
                            }
                    }
                    Message::Deselect => {
       -                if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor() {
                            editor.buffer_view.lock().clear_selection();
                                editor.redraw_view();
                            }
                    }
        
                    Message::DeleteSelection => {
       -                if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                            if editor.buffer_view.lock().get_selection().is_some() {
                                    editor.delete_selection();
                                    editor.redraw_view();
       @@ -135,7 +144,7 @@ impl MainWindow {
                    }
        
                    Message::ShowCharacterSelectionDialog(ch) => {
       -                if let Some(editor) = self.get_ansi_editor() {
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor() {
                            let buf = editor.buffer_view.clone();
                                self.open_dialog(SelectCharacterDialog::new(buf, ch));
                            }
       @@ -145,27 +154,27 @@ impl MainWindow {
                    }
        
                    Message::EditSauce => {
       -                let mut buffer_opt = self.get_ansi_editor() ;
       -       
       -                let view = buffer_opt.unwrap().buffer_view.clone();
       -                self.open_dialog(crate::EditSauceDialog::new(&view.lock().buf));
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor()  {
       +                    let view = editor.buffer_view.clone();
       +                    self.open_dialog(crate::EditSauceDialog::new(&view.lock().buf));
       +                }
                    }
                    Message::SetCanvasSize => {
       -                let mut buffer_opt = self.get_ansi_editor();
       -                let view = buffer_opt.unwrap().buffer_view.clone();
       -                self.open_dialog(crate::SetCanvasSizeDialog::new(&view.lock().buf));
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor()  {
       +                    let view = editor.buffer_view.clone();
       +                    self.open_dialog(crate::SetCanvasSizeDialog::new(&view.lock().buf));
       +                }
                    }
        
                    Message::EditLayer(i) => {
       -                let editor = self.get_ansi_editor()
       -                    .unwrap();
       -                let buffer_view = editor.buffer_view.clone();
       -                self.open_dialog(crate::EditLayerDialog::new(&buffer_view.lock().buf, i));
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor()  {
       +                    let view = editor.buffer_view.clone();
       +                    self.open_dialog(crate::EditLayerDialog::new(&view.lock().buf, i));
       +                }
                    }
                    Message::NewLayer => {
       -                let editor = self.get_ansi_editor()
       -                    .unwrap();
       -                let buf = &mut editor.buffer_view.lock().buf;
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut()  {
       +                    let buf = &mut editor.buffer_view.lock().buf;
                        let size = buf.get_buffer_size();
                        let mut new_layer = icy_engine::Layer::new("New Layer", size);
                        new_layer.has_alpha_channel = true;
       @@ -175,9 +184,9 @@ impl MainWindow {
        
                        buf.layers.insert(0, new_layer);
                    }
       +            }
                    Message::MoveLayerUp(cur_layer) => {
       -                let editor = self.get_ansi_editor()
       -                    .unwrap();
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut()  {
        
                        editor
                            .buffer_view
       @@ -186,61 +195,91 @@ impl MainWindow {
                            .layers
                            .swap(cur_layer, cur_layer - 1);
                        editor.cur_layer -= 1;
       +                }
                    }
                    Message::MoveLayerDown(cur_layer) => {
       -                let editor = self.get_ansi_editor()
       -                    .unwrap();
       -
       -                editor
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut()  {
       +                    editor
                            .buffer_view
                            .lock()
                            .buf
                            .layers
                            .swap(cur_layer, cur_layer + 1);
                        editor.cur_layer += 1;
       +                }
                    }
                    Message::DeleteLayer(cur_layer) => {
       -                let editor = self.get_ansi_editor()
       -                    .unwrap();
       -                editor.buffer_view.lock().buf.layers.remove(cur_layer);
       -                editor.cur_layer = editor.cur_layer.clamp(
       +                self.run_editor_command(cur_layer, |_, editor, cur_layer| {
       +                    editor.buffer_view.lock().buf.layers.remove(cur_layer);
       +                    editor.cur_layer = editor.cur_layer.clamp(
                            0,
                            editor.buffer_view.lock().buf.layers.len().saturating_sub(1),
       -                );
       +                    );
       +                });
       +            }
       +            Message::DuplicateLayer(cur_layer) => {
       +                self.run_editor_command(cur_layer, |_, editor, cur_layer| {
       +                    let layer = editor.buffer_view.lock().buf.layers[cur_layer].clone();
       +                    editor.buffer_view.lock().buf.layers.insert(cur_layer + 1, layer);
       +                    editor.cur_layer += 1;
       +                });
                    }
       +            Message::MergeLayer(cur_layer) => {
       +                self.run_editor_command(cur_layer, |_, editor, cur_layer| {
       +                    let layer = editor.buffer_view.lock().buf.layers.remove(cur_layer);
       +                    if let Some(layer_below) = editor.buffer_view.lock().buf.layers.get_mut(cur_layer) {
       +                        for y in 0..layer.get_height() {
       +                            for x in 0..layer.get_width() {
       +                                println!("{} {}", x, y);
       +                                let ch = layer.get_char((x, y));
       +                                if ch.is_visible() {
       +                                    layer_below.set_char((x, y), ch);
       +                                }
       +                            }
       +                        }
       +                    }
       +                });
       +            }
       +
                    Message::ToggleVisibility(cur_layer) => {
       -                let editor = self.get_ansi_editor()
       -                    .unwrap();
       -                let is_visible = editor.buffer_view.lock().buf.layers[cur_layer].is_visible;
       -                editor.buffer_view.lock().buf.layers[cur_layer].is_visible = !is_visible;
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut()  {
       +                    let is_visible = editor.buffer_view.lock().buf.layers[cur_layer].is_visible;
       +                    editor.buffer_view.lock().buf.layers[cur_layer].is_visible = !is_visible;
       +                }
                    }
                    Message::SelectLayer(cur_layer) => {
       -                let editor = self .get_ansi_editor()
       -                    .unwrap();
       -                editor.cur_layer = cur_layer;
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut()  {
       +                    editor.cur_layer = cur_layer;
       +                }
                    }
        
                    Message::SetFontPage(page) => {
       -                let editor = self.get_ansi_editor()
       -                    .unwrap();
       -                editor.buffer_view.lock().caret.set_font_page(page);
       -
       -                let buf = &mut editor.buffer_view.lock().buf;
       -                if buf.get_font(page).is_none() {
       -                    if let Some(font_name) =
       -                        icy_engine::parsers::ansi::constants::ANSI_FONT_NAMES.get(page)
       -                    {
       -                        match BitFont::from_name(font_name) {
       -                            Ok(font) => {
       -                                buf.set_font(page, font);
       -                            }
       -                            Err(err) => {
       -                                log::error!("Failed to load font: {err}");
       +                if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut()  {
       +                    editor.buffer_view.lock().caret.set_font_page(page);
       +
       +                    let buf = &mut editor.buffer_view.lock().buf;
       +                    if buf.get_font(page).is_none() {
       +                        if let Some(font_name) =
       +                            icy_engine::parsers::ansi::constants::ANSI_FONT_NAMES.get(page)
       +                        {
       +                            match BitFont::from_name(font_name) {
       +                                Ok(font) => {
       +                                    buf.set_font(page, font);
       +                                }
       +                                Err(err) => {
       +                                    log::error!("Failed to load font: {err}");
       +                                }
                                    }
                                }
                            }
                        }
                    }
       +            Message::CharTable(ch) => {
       +                let ch  =ch as u8;
       +                self.run_editor_command(ch,|_, editor, ch| {
       +                    editor.print_char(ch);
       +                });
       +            }
        
                    Message::ShowAboutDialog => {
                        self.open_dialog(crate::AboutDialog::default());
   DIR diff --git a/src/ui/mod.rs b/src/ui/mod.rs
       @@ -13,14 +13,10 @@ pub use palette_editor::*;
        mod tool_switcher;
        pub use tool_switcher::*;
        
       -mod char_table;
       -pub use char_table::*;
        
        mod icons;
        pub use icons::*;
        
       -mod layer_view;
       -
        mod settings;
        pub use settings::*;
        
       @@ -41,8 +37,8 @@ pub use top_bar::*;
        mod messages;
        pub use messages::*;
        
       -mod bitfont_selector;
       -pub use bitfont_selector::*;
       +mod tools;
       +pub use tools::*;
        
        pub type TerminalResult<T> = Result<T, Box<dyn Error>>;
        
   DIR diff --git a/src/ui/tools/bitfont_selector.rs b/src/ui/tools/bitfont_selector.rs
       @@ -0,0 +1,83 @@
       +use std::sync::{Arc, Mutex};
       +
       +use eframe::{
       +    egui::{self, RichText, Sense, TextStyle},
       +    epaint::{Color32, Vec2, Rounding, Rect, pos2}, emath::Align2,
       +};
       +use egui_extras::RetainedImage;
       +use i18n_embed_fl::fl;
       +use icy_engine::{ascii, BufferParser};
       +
       +use crate::{AnsiEditor, Message, ToolWindow, Document, VISIBLE_SVG, INVISIBLE_SVG};
       +
       +#[derive(Default)]
       +pub struct BitFontSelector {}
       +
       +impl ToolWindow for BitFontSelector {
       +    fn get_title(&self) -> String {
       +        fl!(crate::LANGUAGE_LOADER, "bitfont_tool_title")
       +    }
       + 
       +    fn show_ui(
       +        &mut self,
       +        ui: &mut egui::Ui,
       +        active_document: Option<Arc<Mutex<Box<dyn Document>>>> 
       +    ) -> Option<Message> {
       +        if let Some(doc) = active_document {
       +            if let Some(editor) = doc.lock().unwrap().get_ansi_editor() {
       +                return show_font_list(ui, editor);
       +            }
       +        }
       +        ui.vertical_centered(|ui| {
       +            ui.add_space(8.0);
       +            ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "no_document_selected")).small());
       +        });
       +        None
       +    }
       +}
       +
       +fn show_font_list(
       +    ui: &mut egui::Ui,
       +    editor: &AnsiEditor,
       +) -> Option<Message> {
       +    let mut result = None;
       +
       +    /*
       +    for (id, font) in editor.buffer_view.lock().buf.font_iter() {
       +
       +        if id >= 100 {
       +            // TODO
       +        }
       +    }*/
       +    let row_height = 23.0;
       +
       +    let cur_font_page = editor.buffer_view.lock().caret.get_font_page();
       +
       +    egui::ScrollArea::vertical()
       +        .id_source("bitfont_scroll_area")
       +        .max_height(300.)
       +        .show_rows(
       +            ui,
       +            row_height,
       +            icy_engine::parsers::ansi::constants::ANSI_FONT_NAMES.len(),
       +            |ui, range| {
       +                for r in range {
       +                    ui.horizontal(|ui| {
       +                        if ui
       +                            .selectable_label(
       +                                cur_font_page == r,
       +                                format!(
       +                                    "{r}. {}",
       +                                    icy_engine::parsers::ansi::constants::ANSI_FONT_NAMES[r]
       +                                ),
       +                            )
       +                            .clicked()
       +                        {
       +                            result = Some(Message::SetFontPage(r));
       +                        }
       +                    });
       +                }
       +            },
       +        );
       +    result
       +}
   DIR diff --git a/src/ui/tools/char_table.rs b/src/ui/tools/char_table.rs
       @@ -0,0 +1,79 @@
       +use std::sync::{Arc, Mutex};
       +
       +use eframe::{
       +    egui::{self, RichText, Sense, TextStyle},
       +    epaint::{Color32, Vec2, Rounding, Rect, pos2}, emath::Align2,
       +};
       +use egui_extras::RetainedImage;
       +use i18n_embed_fl::fl;
       +use icy_engine::{ascii, BufferParser};
       +
       +use crate::{AnsiEditor, Message, ToolWindow, Document, VISIBLE_SVG, INVISIBLE_SVG};
       +
       +#[derive(Default)]
       +pub struct CharTableToolWindow {}
       +
       +
       +impl ToolWindow for CharTableToolWindow {
       +    fn get_title(&self) -> String {
       +        fl!(crate::LANGUAGE_LOADER, "char_table_tool_title")
       +    }
       + 
       +    fn show_ui(
       +        &mut self,
       +        ui: &mut egui::Ui,
       +        active_document: Option<Arc<Mutex<Box<dyn Document>>>> 
       +    ) -> Option<Message> {
       +        if let Some(doc) = active_document {
       +            if let Some(editor) = doc.lock().unwrap().get_ansi_editor() {
       +                return show_char_table(ui, editor);
       +            }
       +        }
       +        ui.vertical_centered(|ui| {
       +            ui.add_space(8.0);
       +            ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "no_document_selected")).small());
       +        });
       +        None
       +    }
       +}
       +
       +
       +pub fn show_char_table(ui: &mut egui::Ui, editor: &AnsiEditor) -> Option<Message> {
       +    let mut result = None;
       +  
       +        let font_page = editor.buffer_view.lock().caret.get_font_page();
       +        let font_length = editor
       +            .buffer_view
       +            .lock()
       +            .buf
       +            .get_font(font_page)
       +            .unwrap()
       +            .length;
       +
       +        egui::ScrollArea::vertical()
       +            .id_source("char_table_scroll_area")
       +            .show(ui, |ui| {
       +                ui.horizontal_wrapped(|ui| {
       +                    for i in 0..font_length {
       +                        let ch = unsafe { char::from_u32_unchecked(i as u32) };
       +                        if ui
       +                            .add(crate::model::pencil_imp::draw_glyph_plain(
       +                                editor,
       +                                ch,
       +                                font_page,
       +                            ))
       +                            .clicked()
       +                        { 
       +
       +                            result = Some(Message::CharTable(ch));
       +
       +                            /* 
       +                            let editor = &mut b.editor;
       +                          
       +                            b.redraw_view();*/
       +                        }
       +                    }
       +                })
       +            });
       +    result
       +}
   DIR diff --git a/src/ui/tools/layer_view.rs b/src/ui/tools/layer_view.rs
       @@ -0,0 +1,246 @@
       +use std::sync::{Arc, Mutex};
       +
       +use eframe::{
       +    egui::{self, RichText, Sense, TextStyle},
       +    epaint::{Color32, Vec2, Rounding, Rect, pos2}, emath::Align2,
       +};
       +use egui_extras::RetainedImage;
       +use i18n_embed_fl::fl;
       +
       +use crate::{AnsiEditor, Message, ToolWindow, Document, VISIBLE_SVG, INVISIBLE_SVG};
       +
       +#[derive(Default)]
       +pub struct LayerToolWindow {}
       +
       +impl ToolWindow for LayerToolWindow {
       +    fn get_title(&self) -> String {
       +        fl!(crate::LANGUAGE_LOADER, "layer_tool_title")
       +    }
       + 
       +    fn show_ui(
       +        &mut self,
       +        ui: &mut egui::Ui,
       +        active_document: Option<Arc<Mutex<Box<dyn Document>>>> 
       +    ) -> Option<Message> {
       +        if let Some(doc) = active_document {
       +            if let Some(editor) = doc.lock().unwrap().get_ansi_editor() {
       +                return show_layer_view(ui, editor);
       +            }
       +        }
       +        ui.vertical_centered(|ui| {
       +            ui.add_space(8.0);
       +            ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "no_document_selected")).small());
       +        });
       +        None
       +    }
       +}
       +
       +fn show_layer_view(
       +    ui: &mut egui::Ui,
       +    editor: &AnsiEditor,
       +) -> Option<Message> {
       +    let row_height = 24.0;
       +    let mut result = None;
       +
       +    let max = editor.buffer_view.lock().buf.layers.len();
       +    let cur_layer = editor.cur_layer;
       +    let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
       +    ui.set_height(row_height * 6.0);
       +    egui::ScrollArea::vertical()
       +    .id_source("layer_view_scroll_area")
       +    
       +    .max_height(180.)
       +    .show_rows(ui, row_height, max, |ui, range| {
       +        for i in range {           
       +            ui.horizontal(|ui| {
       +                ui.add_space(4.0);
       +                let (is_visible, title, color) = {
       +                    let layer = &editor.buffer_view.lock().buf.layers[i];
       +                    (layer.is_visible, layer.title.clone(), layer.color)
       +                };
       +                let width =  ui.available_width();
       +
       +                let (id, back_rect) = ui.allocate_space(Vec2::new(width, row_height));
       +                let mut response = ui.interact(back_rect, id, Sense::click());
       +
       +                let back_painter = ui.painter_at(back_rect);
       +
       +                if response.hovered() {
       +                    back_painter.rect_filled(
       +                        back_rect,
       +                        Rounding::none(),
       +                        ui.style().visuals.widgets.active.bg_fill,
       +                    );
       +                } else if i == cur_layer {
       +                    back_painter.rect_filled(
       +                        back_rect,
       +                        Rounding::none(),
       +                        ui.style().visuals.extreme_bg_color,
       +                    );
       +                }
       +
       +                let stroke_rect = Rect::from_min_size(
       +                    back_rect.min + Vec2::new(0.0, 1.0),
       +                    Vec2::new(22.0, 22.0),
       +                );
       +                let visible_icon_response = ui.interact(stroke_rect, id.with("visible"), Sense::click());
       +
       +                let painter = ui.painter_at(stroke_rect);
       +
       +    
       +                if let Some(color) = color {
       +                    let (r, g, b) = color.into();
       +                    painter.rect_filled(
       +                        stroke_rect,
       +                        Rounding::none(),
       +                        Color32::from_rgb(r, g, b)
       +                    );
       +                } 
       +
       +                let image = if is_visible {
       +                    VISIBLE_SVG.texture_id(ui.ctx())
       +                } else {
       +                    INVISIBLE_SVG.texture_id(ui.ctx())
       +                };
       +
       +                let tint = if i == cur_layer {
       +                    ui.visuals().widgets.active.fg_stroke.color
       +                } else {
       +                    ui.visuals().widgets.inactive.fg_stroke.color
       +                };
       +                
       +                painter.image(image, stroke_rect, uv, tint);
       +                
       +
       +                let color = if  i == cur_layer {
       +                    ui.style().visuals.strong_text_color()
       +                } else {
       +                    ui.style().visuals.text_color()
       +                };
       +                let font_id = TextStyle::Button.resolve(ui.style());
       +
       +                back_painter.text(
       +                    stroke_rect.right_center() + Vec2::new(4., 0.),
       +                    Align2::LEFT_CENTER,
       +                    title,
       +                    font_id,
       +                    color,
       +                );
       +
       +                if visible_icon_response.clicked() {
       +                    result = Some(Message::ToggleVisibility(i));
       +                }
       +
       +                response = response.context_menu(|ui| {
       +                    if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_layer_properties")).clicked() {
       +                        result = Some(Message::EditLayer(i));
       +                        ui.close_menu();
       +                    }
       +                    ui.separator();
       +                    if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_new_layer")).clicked() {
       +                        result = Some(Message::NewLayer);
       +                        ui.close_menu();
       +                    }
       +                    if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_duplicate_layer")).clicked() {
       +                        result = Some(Message::DuplicateLayer(i));
       +                        ui.close_menu();
       +                    }
       +                    if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_merge_layer")).clicked() {
       +                        result = Some(Message::MergeLayer(i));
       +                        ui.close_menu();
       +                    }
       +                    if ui.button(fl!(crate::LANGUAGE_LOADER, "layer_tool_menu_delete_layer")).clicked() {
       +                        result = Some(Message::DeleteLayer(i));
       +                        ui.close_menu();
       +                    }
       +
       +                });
       +
       +                if response.clicked() {
       +                    result = Some(Message::SelectLayer(i));
       +                }
       +
       +                if response.double_clicked() {
       +                    result = Some(Message::EditLayer(i));
       +                }
       +            });
       +        }
       +    });
       +    ui.add_space(ui.available_height());
       +    ui.separator();
       +    ui.horizontal(|ui| {
       +        ui.add_space(4.0);
       +        ui.spacing_mut().item_spacing = eframe::epaint::Vec2::new(0.0, 0.0);
       +        let r = medium_hover_button(ui, &crate::ADD_LAYER_SVG)
       +            .on_hover_ui(|ui| {
       +                ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "add_layer_tooltip")).small());
       +            });
       +
       +        if r.clicked() {
       +            result = Some(Message::NewLayer);
       +        }
       +
       +        let r = medium_hover_button(ui, &crate::MOVE_UP_SVG)
       +            .on_hover_ui(|ui| {
       +                ui.label(
       +                    RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_up_tooltip")).small(),
       +                );
       +            });
       +
       +        if r.clicked() && cur_layer > 0 {
       +            result = Some(Message::MoveLayerUp(cur_layer));
       +        }
       +
       +        let r = medium_hover_button(ui, &crate::MOVE_DOWN_SVG)
       +            .on_hover_ui(|ui| {
       +                ui.label(
       +                    RichText::new(fl!(crate::LANGUAGE_LOADER, "move_layer_down_tooltip")).small(),
       +                );
       +            });
       +
       +        if r.clicked() && (1 + cur_layer) < max {
       +            result = Some(Message::MoveLayerDown(cur_layer));
       +        }
       +
       +        let r = medium_hover_button(ui, &crate::DELETE_SVG)
       +            .on_hover_ui(|ui| {
       +                ui.label(
       +                    RichText::new(fl!(crate::LANGUAGE_LOADER, "delete_layer_tooltip")).small(),
       +                );
       +            });
       +
       +        if r.clicked() && cur_layer < max {
       +            result = Some(Message::DeleteLayer(cur_layer));
       +        }
       +    });
       +    result
       +}
       +
       +pub fn medium_hover_button(
       +    ui: &mut egui::Ui,
       +    image: &RetainedImage
       +) -> egui::Response {
       +    let size_points = egui::Vec2::splat(28.0);
       +
       +
       +    let (id, rect) = ui.allocate_space(size_points);
       +    let response = ui.interact(rect, id, Sense::click());
       +    let painter = ui.painter_at(rect);
       +
       +    let tint = if response.hovered() {
       +        ui.painter().rect_filled(
       +            rect,
       +            Rounding::same(4.0),
       +            ui.style().visuals.extreme_bg_color,
       +        );
       +
       +        ui.visuals().widgets.active.fg_stroke.color
       +    } else {
       +        ui.visuals().widgets.inactive.fg_stroke.color
       +    };
       +
       +    let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
       +    painter.image(image.texture_id(ui.ctx()), rect.shrink(4.0), uv, tint);
       +
       +    response
       +}
       +\ No newline at end of file
   DIR diff --git a/src/ui/tools/mod.rs b/src/ui/tools/mod.rs
       @@ -0,0 +1,8 @@
       +mod layer_view;
       +pub use layer_view::*;
       +
       +mod char_table;
       +pub use char_table::*;
       +
       +mod bitfont_selector;
       +pub use bitfont_selector::*;
       +\ No newline at end of file
   DIR diff --git a/src/ui/top_bar.rs b/src/ui/top_bar.rs
       @@ -13,7 +13,7 @@ pub struct TopBar {
        }
        
        impl TopBar {
       -    pub fn new(ctx: &egui::Context) -> Self {
       +    pub fn new(_ctx: &egui::Context) -> Self {
                let left_bytes = include_bytes!("../../data/icons/dock_left.svg");
                let right_bytes = include_bytes!("../../data/icons/dock_right.svg");
        
       @@ -40,9 +40,11 @@ impl MainWindow {
            fn main_menu(&mut self, ui: &mut Ui, frame: &mut eframe::Frame) -> Option<Message> {
                let mut result = None;
                menu::bar(ui, |ui| {
       -            let mut buffer_opt = self.get_ansi_editor();
        
       -            let has_buffer = buffer_opt.is_some();
       +            let mut has_buffer = false;
       +            if let Some(doc) = self.get_active_document() {
       +                has_buffer = doc.lock().unwrap().get_ansi_editor().is_some();
       +            }
        
                    ui.menu_button(fl!(crate::LANGUAGE_LOADER, "menu-file"), |ui| {
                        if ui
       @@ -208,7 +210,7 @@ impl MainWindow {
                            "X",
                        );
                        if button.clicked() {
       -                    if let Some(editor) = self.get_ansi_editor() {
       +                    if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                                editor.flip_x();
                                    editor.redraw_view();
                                }
       @@ -222,7 +224,7 @@ impl MainWindow {
                            "Y",
                        );
                        if button.clicked() {
       -                    if let Some(editor) = self.get_ansi_editor() {
       +                    if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                                editor.flip_y();
                                    editor.redraw_view();
                                }
       @@ -236,7 +238,7 @@ impl MainWindow {
                            "Y",
                        );
                        if button.clicked() {
       -                    if let Some(editor) = self.get_ansi_editor() {
       +                    if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                                editor.justify_center();
                                    editor.redraw_view();
                                }
       @@ -250,7 +252,7 @@ impl MainWindow {
                            "L",
                        );
                        if button.clicked() {
       -                    if let Some(editor) = self.get_ansi_editor() {
       +                    if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                                editor.justify_left();
                                    editor.redraw_view();
                            }
       @@ -264,7 +266,7 @@ impl MainWindow {
                            "R",
                        );
                        if button.clicked() {
       -                    if let Some(editor) = self.get_ansi_editor() {
       +                    if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                                editor.justify_right();
                                    editor.redraw_view();
                            }
       @@ -279,7 +281,7 @@ impl MainWindow {
                            "",
                        );
                        if button.clicked() {
       -                    if let Some(editor) = self.get_ansi_editor() {
       +                    if let Some(editor) = self.get_active_document().unwrap().lock().unwrap().get_ansi_editor_mut() {
                                editor.crop();
                                    editor.redraw_view();
                            }
       @@ -352,40 +354,37 @@ impl MainWindow {
        
            fn top_bar_ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
                ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
       -            let tint = if self.right_panel {
       -                ui.visuals().widgets.active.fg_stroke.color
       -            } else {
       -                ui.visuals().widgets.inactive.fg_stroke.color
       -            };
       -            let icon_size = 20.0;
       -
       -            let right = ui.add(
       -                ImageButton::new(
       -                    self.top_bar.dock_right.texture_id(ui.ctx()),
       -                    Vec2::new(icon_size, icon_size),
       -                )
       -                .tint(tint),
       -            );
       +            let right = medium_toggle_button(ui, &self.top_bar.dock_right, self.right_panel);
                    if right.clicked() {
                        self.right_panel = !self.right_panel;
                    }
        
       -            let tint = if self.left_panel {
       -                ui.visuals().widgets.active.fg_stroke.color
       -            } else {
       -                ui.visuals().widgets.inactive.fg_stroke.color
       -            };
       -
       -            let left = ui.add(
       -                ImageButton::new(
       -                    self.top_bar.dock_left.texture_id(ui.ctx()),
       -                    Vec2::new(icon_size, icon_size),
       -                )
       -                .tint(tint),
       -            );
       +            let left = medium_toggle_button(ui, &self.top_bar.dock_left, self.left_panel);
                    if left.clicked() {
                        self.left_panel = !self.left_panel;
                    }
                });
            }
        }
       +
       +pub fn medium_toggle_button(
       +    ui: &mut egui::Ui,
       +    icon: &RetainedImage,
       +    selected: bool,
       +) -> egui::Response {
       +    let size_points = egui::Vec2::splat(20.0);
       +
       +    let tint = if selected {
       +        ui.visuals().widgets.active.fg_stroke.color
       +    } else {
       +        ui.visuals().widgets.inactive.fg_stroke.color
       +    };
       +
       +    ui.add(
       +        ImageButton::new(
       +            icon.texture_id(ui.ctx()),
       +            size_points,
       +        )
       +        .tint(tint),
       +    )
       +}
       +\ No newline at end of file