URI: 
       Added more commands. - 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 e05887ee8c37c8af479f3dd7fad5ace5d4704b47
   DIR parent 78d802b51493b05e62a3e351ffa8d13a99a0a2f5
  HTML Author: Mike Krüger <mkrueger@posteo.de>
       Date:   Wed,  6 Sep 2023 15:27:16 +0200
       
       Added more commands.
       
       Diffstat:
         M Cargo.toml                          |       9 +++++----
         M i18n/en/icy_draw.ftl                |      17 +++++++++++++++++
         M src/model/tools/brush_imp.rs        |       5 +----
         M src/model/tools/click_imp.rs        |       8 ++------
         M src/model/tools/draw_ellipse_fille… |      17 ++++++++++-------
         M src/model/tools/draw_ellipse_imp.rs |       7 ++-----
         M src/model/tools/draw_rectangle_fil… |      12 ++++++------
         M src/model/tools/draw_rectangle_imp… |       7 ++-----
         M src/model/tools/erase_imp.rs        |       5 +----
         M src/model/tools/line_imp.rs         |       7 ++-----
         M src/model/tools/mod.rs              |      11 ++++-------
         M src/model/tools/move_layer_imp.rs   |      10 ++++------
         M src/model/tools/paste_tool.rs       |      11 ++++-------
         M src/model/tools/pencil_imp.rs       |       7 ++-----
         M src/ui/commands.rs                  |     253 +++++++++++++++++++++----------
         M src/ui/document.rs                  |       4 ++--
         M src/ui/editor/ansi/mod.rs           |      40 +++++++++++++++++++-------------
         M src/ui/main_window.rs               |      15 +++++++--------
         M src/ui/messages.rs                  |     145 +++++++++++++++++++++++++++++--
         M src/ui/top_bar.rs                   |      61 ++++++++++++++++++++++++++-----
       
       20 files changed, 456 insertions(+), 195 deletions(-)
       ---
   DIR diff --git a/Cargo.toml b/Cargo.toml
       @@ -17,6 +17,7 @@ egui-modal = "0.2.4"
        glow = "0.12.1"
        egui_file = "0.10.0"
        egui_tiles = { git ="https://github.com/mkrueger/egui_tiles"}
       +egui-bind = "0.7.0"
        #egui_tiles = { path = "../egui_tiles"  }
        egui-notify = "0.8.0"
        log = "0.4.20"
       @@ -24,10 +25,10 @@ open = "5.0.0"
        dark-light = "1.0.0"
        rfd = "0.11.4"
        
       -icy_engine = { git ="https://github.com/mkrueger/icy_engine" }
       -icy_engine_egui = { git ="https://github.com/mkrueger/icy_engine_egui" }
       -#icy_engine = { path = "../icy_engine" }
       -#icy_engine_egui = { path = "../icy_engine_egui" }
       +#icy_engine = { git ="https://github.com/mkrueger/icy_engine" }
       +#icy_engine_egui = { git ="https://github.com/mkrueger/icy_engine_egui" }
       +icy_engine = { path = "../icy_engine" }
       +icy_engine_egui = { path = "../icy_engine_egui" }
        walkdir = "2"
        serde = { version = "1", features = ["derive"], optional = true }
        lazy_static = "1.4.0"
   DIR diff --git a/i18n/en/icy_draw.ftl b/i18n/en/icy_draw.ftl
       @@ -30,6 +30,23 @@ menu-justifyleft=Justify Left
        menu-justifyright=Justify Right
        menu-justifycenter=Center
        menu-crop=Crop
       +menu-justify_line_center=Center Line
       +menu-justify_line_left=Left Justify Line
       +menu-justify_line_right=Right Justify Line
       +menu-insert_row=Insert Row
       +menu-delete_row=Delete Row
       +menu-insert_colum=Insert Column
       +menu-delete_colum=Delete Column
       +menu-erase_row=Erase Row
       +menu-erase_row_to_start=Erase Row to Start
       +menu-erase_row_to_end=Erase Row to End
       +menu-erase_column=Erase Column
       +menu-erase_column_to_start=Erase Column to Start
       +menu-erase_column_to_end=Erase Column to End
       +menu-scroll_area_up=Scroll Area Up
       +menu-scroll_area_down=Scroll Area Down
       +menu-scroll_area_left=Scroll Area Left
       +menu-scroll_area_right=Scroll Area Right
        
        menu-selection=Selection
        menu-select-all=Select All
   DIR diff --git a/src/model/tools/brush_imp.rs b/src/model/tools/brush_imp.rs
       @@ -256,10 +256,7 @@ impl Tool for BrushTool {
                Event::None
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        _editor: &mut AnsiEditor
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, _editor: &mut AnsiEditor) -> Event {
                self.undo_op = None;
                Event::None
            }
   DIR diff --git a/src/model/tools/click_imp.rs b/src/model/tools/click_imp.rs
       @@ -37,9 +37,8 @@ impl Tool for ClickTool {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
       -
                if editor.drag_pos.start == editor.drag_pos.cur {
                    editor.buffer_view.lock().clear_selection();
                } else {
       @@ -64,10 +63,7 @@ impl Tool for ClickTool {
                response
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        editor: &mut AnsiEditor,
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Event {
                let mut cur = editor.drag_pos.cur;
                if editor.drag_pos.start < cur {
                    cur += Position::new(1, 1);
   DIR diff --git a/src/model/tools/draw_ellipse_filled_imp.rs b/src/model/tools/draw_ellipse_filled_imp.rs
       @@ -101,16 +101,22 @@ impl Tool for DrawEllipseFilledTool {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
                editor.clear_overlay_layer();
        
                let mut lines = ScanLines::new(1);
        
                if editor.drag_pos.start < editor.drag_pos.cur {
       -            lines.add_ellipse(Rectangle::from_pt(editor.drag_pos.start, editor.drag_pos.cur));
       +            lines.add_ellipse(Rectangle::from_pt(
       +                editor.drag_pos.start,
       +                editor.drag_pos.cur,
       +            ));
                } else {
       -            lines.add_ellipse(Rectangle::from_pt(editor.drag_pos.cur, editor.drag_pos.start));
       +            lines.add_ellipse(Rectangle::from_pt(
       +                editor.drag_pos.cur,
       +                editor.drag_pos.start,
       +            ));
                }
        
                let draw = move |rect: Rectangle| {
       @@ -128,10 +134,7 @@ impl Tool for DrawEllipseFilledTool {
                response
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        editor: &mut AnsiEditor,
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Event {
                if editor.drag_pos.start == editor.drag_pos.cur {
                    editor.buffer_view.lock().get_buffer_mut().remove_overlay();
                } else {
   DIR diff --git a/src/model/tools/draw_ellipse_imp.rs b/src/model/tools/draw_ellipse_imp.rs
       @@ -102,7 +102,7 @@ impl Tool for DrawEllipseTool {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
                editor.clear_overlay_layer();
        
       @@ -141,10 +141,7 @@ impl Tool for DrawEllipseTool {
                response
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        editor: &mut AnsiEditor
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Event {
                if editor.drag_pos.start == editor.drag_pos.cur {
                    editor.buffer_view.lock().get_buffer_mut().remove_overlay();
                } else {
   DIR diff --git a/src/model/tools/draw_rectangle_filled_imp.rs b/src/model/tools/draw_rectangle_filled_imp.rs
       @@ -100,13 +100,16 @@ impl Tool for DrawRectangleFilledTool {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
                editor.buffer_view.lock().get_buffer_mut().remove_overlay();
                editor.clear_overlay_layer();
        
                let mut lines = ScanLines::new(1);
       -        lines.add_rectangle(Rectangle::from_pt(editor.drag_pos.start, editor.drag_pos.cur));
       +        lines.add_rectangle(Rectangle::from_pt(
       +            editor.drag_pos.start,
       +            editor.drag_pos.cur,
       +        ));
        
                let draw = move |rect: Rectangle| {
                    for y in 0..rect.size.height {
       @@ -124,10 +127,7 @@ impl Tool for DrawRectangleFilledTool {
                response
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        editor: &mut AnsiEditor
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Event {
                if editor.drag_pos.start == editor.drag_pos.cur {
                    editor.buffer_view.lock().get_buffer_mut().remove_overlay();
                } else {
   DIR diff --git a/src/model/tools/draw_rectangle_imp.rs b/src/model/tools/draw_rectangle_imp.rs
       @@ -102,7 +102,7 @@ impl Tool for DrawRectangleTool {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
                editor.clear_overlay_layer();
                let mut start = editor.drag_pos.start;
       @@ -136,10 +136,7 @@ impl Tool for DrawRectangleTool {
                response
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        editor: &mut AnsiEditor,
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Event {
                if editor.drag_pos.start == editor.drag_pos.cur {
                    editor.buffer_view.lock().get_buffer_mut().remove_overlay();
                } else {
   DIR diff --git a/src/model/tools/erase_imp.rs b/src/model/tools/erase_imp.rs
       @@ -135,10 +135,7 @@ impl Tool for EraseTool {
                Event::None
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        _editor: &mut AnsiEditor,
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, _editor: &mut AnsiEditor) -> Event {
                self.undo_op = None;
                Event::None
            }
   DIR diff --git a/src/model/tools/line_imp.rs b/src/model/tools/line_imp.rs
       @@ -250,7 +250,7 @@ impl Tool for LineTool {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
                editor.clear_overlay_layer();
                let mut lines = ScanLines::new(1);
       @@ -296,10 +296,7 @@ impl Tool for LineTool {
                response
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        editor: &mut AnsiEditor
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Event {
                if editor.drag_pos.start == editor.drag_pos.cur {
                    editor.buffer_view.lock().get_buffer_mut().remove_overlay();
                } else {
   DIR diff --git a/src/model/tools/mod.rs b/src/model/tools/mod.rs
       @@ -83,8 +83,8 @@ pub struct DragPos {
            pub start_abs: Position,
            pub cur_abs: Position,
            pub start: Position,
       -    pub cur: Position
       -} 
       +    pub cur: Position,
       +}
        
        pub trait Tool {
            fn get_icon_name(&self) -> &'static RetainedImage;
       @@ -353,7 +353,7 @@ pub trait Tool {
                _ui: &egui::Ui,
                response: Response,
                _editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> Response {
                response
            }
       @@ -368,10 +368,7 @@ pub trait Tool {
                response
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        _editor: &mut AnsiEditor,
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, _editor: &mut AnsiEditor) -> Event {
                Event::None
            }
        }
   DIR diff --git a/src/model/tools/move_layer_imp.rs b/src/model/tools/move_layer_imp.rs
       @@ -51,7 +51,7 @@ impl Tool for MoveLayer {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
                if !self.drag_started {
                    return response;
       @@ -62,7 +62,8 @@ impl Tool for MoveLayer {
                    .get_edit_state_mut()
                    .get_cur_layer_mut()
                {
       -            self.drag_offset = self.start_offset + editor.drag_pos.cur_abs - editor.drag_pos.start_abs;
       +            self.drag_offset =
       +                self.start_offset + editor.drag_pos.cur_abs - editor.drag_pos.start_abs;
                    layer.set_preview_offset(Some(self.drag_offset));
                }
                response.on_hover_cursor(egui::CursorIcon::Grabbing)
       @@ -78,10 +79,7 @@ impl Tool for MoveLayer {
                response.on_hover_cursor(egui::CursorIcon::Move)
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        editor: &mut AnsiEditor
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Event {
                if !self.drag_started {
                    return Event::None;
                }
   DIR diff --git a/src/model/tools/paste_tool.rs b/src/model/tools/paste_tool.rs
       @@ -74,7 +74,6 @@ impl Tool for PasteTool {
                }
                ui.label("Show fancy paste ui");
        
       -
                if ctx.input(|i| i.key_pressed(Key::Escape)) {
                    return Some(Message::RemoveFloatingLayer);
                }
       @@ -114,7 +113,7 @@ impl Tool for PasteTool {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
                if !self.drag_started {
                    return response;
       @@ -125,7 +124,8 @@ impl Tool for PasteTool {
                    .get_edit_state_mut()
                    .get_cur_layer_mut()
                {
       -            self.drag_offset = self.start_offset + editor.drag_pos.cur_abs - editor.drag_pos.start_abs;
       +            self.drag_offset =
       +                self.start_offset + editor.drag_pos.cur_abs - editor.drag_pos.start_abs;
                    layer.set_preview_offset(Some(self.drag_offset));
                }
                response.on_hover_cursor(egui::CursorIcon::Grabbing)
       @@ -147,10 +147,7 @@ impl Tool for PasteTool {
                response.on_hover_cursor(egui::CursorIcon::Move)
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        editor: &mut AnsiEditor
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, editor: &mut AnsiEditor) -> Event {
                if !self.drag_started {
                    return Event::None;
                }
   DIR diff --git a/src/model/tools/pencil_imp.rs b/src/model/tools/pencil_imp.rs
       @@ -192,7 +192,7 @@ impl Tool for PencilTool {
                _ui: &egui::Ui,
                response: egui::Response,
                editor: &mut AnsiEditor,
       -        _calc: &TerminalCalc
       +        _calc: &TerminalCalc,
            ) -> egui::Response {
                self.paint_brush(editor, editor.drag_pos.cur);
                self.last_pos = editor.drag_pos.cur;
       @@ -205,10 +205,7 @@ impl Tool for PencilTool {
                Event::None
            }
        
       -    fn handle_drag_end(
       -        &mut self,
       -        _editor: &mut AnsiEditor
       -    ) -> Event {
       +    fn handle_drag_end(&mut self, _editor: &mut AnsiEditor) -> Event {
                self.undo_op = None;
                Event::None
            }
   DIR diff --git a/src/ui/commands.rs b/src/ui/commands.rs
       @@ -1,9 +1,8 @@
       -use eframe::egui::{Modifiers, self};
       -use egui_bind::{KeyOrPointer, BindTarget};
       +use eframe::egui::{self, Modifiers};
       +use egui_bind::{BindTarget, KeyOrPointer};
        use i18n_embed_fl::fl;
        
       -
       -use crate::{Message, button_with_shortcut};
       +use crate::{button_with_shortcut, Message};
        
        pub struct Command {
            key: Option<(KeyOrPointer, Modifiers)>,
       @@ -30,6 +29,22 @@ mod modifier_keys {
                command: false,
            };
        
       +    pub const ALT: Modifiers = Modifiers {
       +        alt: true,
       +        ctrl: false,
       +        shift: false,
       +        mac_cmd: false,
       +        command: false,
       +    };
       +
       +    pub const ALT_CTRL: Modifiers = Modifiers {
       +        alt: true,
       +        ctrl: true,
       +        shift: false,
       +        mac_cmd: false,
       +        command: false,
       +    };
       +
            pub const CTRL_SHIFT: Modifiers = Modifiers {
                alt: false,
                ctrl: true,
       @@ -37,15 +52,15 @@ mod modifier_keys {
                mac_cmd: false,
                command: false,
            };
       -    
        }
        
       -
        macro_rules! key {
       -    () => { None };
       +    () => {
       +        None
       +    };
            ($key:ident, $modifier: ident) => {
                Some((KeyOrPointer::Key(egui::Key::$key), modifier_keys::$modifier))
       -    }
       +    };
        }
        
        macro_rules! keys {
       @@ -74,41 +89,17 @@ macro_rules! keys {
                                return;
                            }
                        )*
       -            } 
       +            }
                }
            };
        }
        
       -keys![
       -    (new_file, "menu-new", NewFile, N, CTRL),
       -    (save, "menu-save", SaveFile, S, CTRL),
       -    (save_as, "menu-save-as", SaveFileAs, S, CTRL_SHIFT),
       -    (open_file, "menu-open", OpenFile, O, CTRL),
       -    (export, "menu-export", ExportFile),
       -    (edit_font_outline, "menu-edit-font-outline", ShowOutlineDialog),
       -    (close_window, "menu-close", CloseWindow, Q, CTRL),
       -    (undo, "menu-undo", Undo, Z, CTRL),
       -    (redo, "menu-redo", Redo, Z, CTRL_SHIFT),
       -    (cut, "menu-cut", Cut, X, CTRL),
       -    (copy, "menu-copy", Copy, C, CTRL),
       -    (paste, "menu-paste", Paste, V, CTRL),
       -    (select_all, "menu-select-all", SelectAll, A, CTRL),
       -    (deselect, "menu-deselect", Deselect, Escape, NONE),
       -    (erase_selection, "menu-erase", DeleteSelection, Delete, NONE),
       -
       -    (flip_x, "menu-flipx", FlipX),
       -    (flip_y, "menu-flipy", FlipY),
       -    (justifycenter, "menu-justifycenter", Center),
       -    (justifyleft, "menu-justifyleft", JustifyLeft),
       -    (justifyright, "menu-justifyright", JustifyRight),
       -    (crop, "menu-crop", Crop),
       -    (about, "menu-about", ShowAboutDialog),
       -];
       -
       -
       -
        impl Command {
       -    pub fn new(key: Option<(KeyOrPointer, Modifiers)>, message: Message, description: String) -> Self {
       +    pub fn new(
       +        key: Option<(KeyOrPointer, Modifiers)>,
       +        message: Message,
       +        description: String,
       +    ) -> Self {
                Self {
                    key,
                    message,
       @@ -120,59 +111,162 @@ impl Command {
                self.key.pressed(ctx)
            }
        
       -    pub fn ui_enabled(&self, ui: &mut egui::Ui, enabled: bool, message: &mut Option<Message>)  {
       +    pub fn ui_enabled(&self, ui: &mut egui::Ui, enabled: bool, message: &mut Option<Message>) {
                let response = ui.with_layout(ui.layout().with_cross_justify(true), |ui| {
       -                ui.set_enabled(enabled);
       -
       -                if let Some((KeyOrPointer::Key(k), modifier)) = self.key {
       -                    let mut shortcut = k.name().to_string();
       +            ui.set_enabled(enabled);
        
       -                    if modifier.ctrl {
       -                        shortcut.insert_str(0, "Ctrl+");
       -                    }
       +            if let Some((KeyOrPointer::Key(k), modifier)) = self.key {
       +                let mut shortcut = k.name().to_string();
        
       -                    if modifier.alt {
       -                        shortcut.insert_str(0, "Alt+");
       -                    }
       +                if modifier.ctrl {
       +                    shortcut.insert_str(0, "Ctrl+");
       +                }
        
       -                    if modifier.shift {
       -                        shortcut.insert_str(0, "Shift+");
       -                    }
       +                if modifier.alt {
       +                    shortcut.insert_str(0, "Alt+");
       +                }
        
       -                    button_with_shortcut(
       -                        ui,
       -                        true,
       -                        &self.description,
       -                        shortcut,
       -                    )
       -                } else {
       -                    ui.add(egui::Button::new(&self.description).wrap(false))
       +                if modifier.shift {
       +                    shortcut.insert_str(0, "Shift+");
                        }
       -            });
        
       +                button_with_shortcut(ui, true, &self.description, shortcut)
       +            } else {
       +                ui.add(egui::Button::new(&self.description).wrap(false))
       +            }
       +        });
        
       -        if response.inner.clicked()
       -        {
       +        if response.inner.clicked() {
                    *message = Some(self.message.clone());
                    ui.close_menu();
                }
            }
        
       -    pub fn ui(&self, ui: &mut egui::Ui, message: &mut Option<Message>)  {
       +    pub fn ui(&self, ui: &mut egui::Ui, message: &mut Option<Message>) {
                self.ui_enabled(ui, true, message)
            }
       -/*
       -
       -                let button = button_with_shortcut(
       -                    ui,
       -                    true,
       -                    fl!(crate::LANGUAGE_LOADER, "menu-close"),
       -                    "Ctrl+Q",
       -                );
       -                if button.clicked() {
       -                    frame.close();
       -                    ui.close_menu();
       -                }
       -*/
       -    
       -}
       -\ No newline at end of file
       +}
       +
       +keys![
       +    (new_file, "menu-new", NewFile, N, CTRL),
       +    (save, "menu-save", SaveFile, S, CTRL),
       +    (save_as, "menu-save-as", SaveFileAs, S, CTRL_SHIFT),
       +    (open_file, "menu-open", OpenFile, O, CTRL),
       +    (export, "menu-export", ExportFile),
       +    (
       +        edit_font_outline,
       +        "menu-edit-font-outline",
       +        ShowOutlineDialog
       +    ),
       +    (close_window, "menu-close", CloseWindow, Q, CTRL),
       +    (undo, "menu-undo", Undo, Z, CTRL),
       +    (redo, "menu-redo", Redo, Z, CTRL_SHIFT),
       +    (cut, "menu-cut", Cut, X, CTRL),
       +    (copy, "menu-copy", Copy, C, CTRL),
       +    (paste, "menu-paste", Paste, V, CTRL),
       +    (select_all, "menu-select-all", SelectAll, A, CTRL),
       +    (deselect, "menu-deselect", Deselect, Escape, NONE),
       +    (erase_selection, "menu-erase", DeleteSelection, Delete, NONE),
       +    (flip_x, "menu-flipx", FlipX),
       +    (flip_y, "menu-flipy", FlipY),
       +    (justifycenter, "menu-justifycenter", Center),
       +    (justifyleft, "menu-justifyleft", JustifyLeft),
       +    (justifyright, "menu-justifyright", JustifyRight),
       +    (crop, "menu-crop", Crop),
       +    (about, "menu-about", ShowAboutDialog),
       +    (
       +        justify_line_center,
       +        "menu-justify_line_center",
       +        CenterLine,
       +        C,
       +        ALT
       +    ),
       +    (
       +        justify_line_left,
       +        "menu-justify_line_left",
       +        JustifyLineLeft,
       +        L,
       +        ALT
       +    ),
       +    (
       +        justify_line_right,
       +        "menu-justify_line_right",
       +        JustifyLineRight,
       +        R,
       +        ALT
       +    ),
       +    (insert_row, "menu-insert_row", InsertRow, ArrowUp, ALT),
       +    (delete_row, "menu-delete_row", DeleteRow, ArrowDown, ALT),
       +    (
       +        insert_column,
       +        "menu-insert_colum",
       +        InsertColumn,
       +        ArrowRight,
       +        ALT
       +    ),
       +    (
       +        delete_column,
       +        "menu-delete_colum",
       +        DeleteColumn,
       +        ArrowLeft,
       +        ALT
       +    ),
       +    (erase_row, "menu-erase_row", EraseRow, E, ALT),
       +    (
       +        erase_row_to_start,
       +        "menu-erase_row_to_start",
       +        EraseRowToStart,
       +        Home,
       +        ALT
       +    ),
       +    (
       +        erase_row_to_end,
       +        "menu-erase_row_to_end",
       +        EraseRowToEnd,
       +        End,
       +        ALT
       +    ),
       +    (erase_column, "menu-erase_column", EraseColumn, E, ALT),
       +    (
       +        erase_column_to_start,
       +        "menu-erase_column_to_start",
       +        EraseColumnToStart,
       +        Home,
       +        ALT
       +    ),
       +    (
       +        erase_column_to_end,
       +        "menu-erase_column_to_end",
       +        EraseColumnToEnd,
       +        End,
       +        ALT
       +    ),
       +    (
       +        scroll_area_up,
       +        "menu-scroll_area_up",
       +        ScrollAreaUp,
       +        ArrowUp,
       +        ALT_CTRL
       +    ),
       +    (
       +        scroll_area_down,
       +        "menu-scroll_area_down",
       +        ScrollAreaDown,
       +        ArrowDown,
       +        ALT_CTRL
       +    ),
       +    (
       +        scroll_area_left,
       +        "menu-scroll_area_left",
       +        ScrollAreaLeft,
       +        ArrowLeft,
       +        ALT_CTRL
       +    ),
       +    (
       +        scroll_area_right,
       +        "menu-scroll_area_right",
       +        ScrollAreaRight,
       +        ArrowRight,
       +        ALT_CTRL
       +    ),
       +];
   DIR diff --git a/src/ui/document.rs b/src/ui/document.rs
       @@ -1,7 +1,7 @@
        use eframe::{egui, epaint::Vec2};
        use icy_engine::{editor::UndoState, EngineResult};
        
       -use crate::{model::Tool, AnsiEditor, Message, TerminalResult, Commands};
       +use crate::{model::Tool, AnsiEditor, Commands, Message, TerminalResult};
        
        pub trait ClipboardHandler {
            fn can_cut(&self) -> bool {
       @@ -48,5 +48,5 @@ pub trait Document: UndoState + ClipboardHandler {
        
        pub struct DocumentOptions {
            pub scale: Vec2,
       -    pub commands: Commands
       +    pub commands: Commands,
        }
   DIR diff --git a/src/ui/editor/ansi/mod.rs b/src/ui/editor/ansi/mod.rs
       @@ -24,8 +24,8 @@ use icy_engine_egui::{
        };
        
        use crate::{
       -    model::{MKey, MModifiers, Tool, DragPos},
       -    ClipboardHandler, Document, DocumentOptions, Message, TerminalResult, FIRST_TOOL, Commands,
       +    model::{DragPos, MKey, MModifiers, Tool},
       +    ClipboardHandler, Commands, Document, DocumentOptions, Message, TerminalResult, FIRST_TOOL,
        };
        
        pub enum Event {
       @@ -111,13 +111,15 @@ impl ClipboardHandler for AnsiEditor {
            }
        
            fn paste(&mut self) -> EngineResult<()> {
       -        if self.buffer_view
       -        .lock()
       -        .get_edit_state_mut().has_floating_layer() {
       +        if self
       +            .buffer_view
       +            .lock()
       +            .get_edit_state_mut()
       +            .has_floating_layer()
       +        {
                    return Ok(());
                }
        
       -
                if let Some(data) = pop_data(BUFFER_DATA) {
                    self.buffer_view
                        .lock()
       @@ -639,7 +641,7 @@ impl AnsiEditor {
                                            break;
                                        }
                                    }
       -                        } 
       +                        }
                            }
                            _ => {}
                        }
       @@ -647,8 +649,9 @@ impl AnsiEditor {
                }
        
                if response.clicked_by(egui::PointerButton::Primary) {
       -            if let Some(mouse_pos) = response.interact_pointer_pos()  {
       -                if calc.buffer_rect.contains(mouse_pos) && !calc.scrollbar_rect.contains(mouse_pos) {
       +            if let Some(mouse_pos) = response.interact_pointer_pos() {
       +                if calc.buffer_rect.contains(mouse_pos) && !calc.scrollbar_rect.contains(mouse_pos)
       +                {
                            let click_pos = calc.calc_click_pos(mouse_pos);
                            let cp: Position = Position::new(click_pos.x as i32, click_pos.y as i32)
                                - self.get_cur_click_offset();
       @@ -668,19 +671,19 @@ impl AnsiEditor {
        
                if response.drag_started_by(egui::PointerButton::Primary) {
                    if let Some(mouse_pos) = response.interact_pointer_pos() {
       -                if calc.buffer_rect.contains(mouse_pos) && !calc.scrollbar_rect.contains(mouse_pos) {
       +                if calc.buffer_rect.contains(mouse_pos) && !calc.scrollbar_rect.contains(mouse_pos)
       +                {
                            let click_pos = calc.calc_click_pos(mouse_pos);
                            let click_pos = Position::new(click_pos.x as i32, click_pos.y as i32);
       -                    
       -                    let cp: Position = click_pos 
       -                        - self.get_cur_click_offset();
       +
       +                    let cp: Position = click_pos - self.get_cur_click_offset();
                            self.drag_pos.start_abs = click_pos;
                            self.drag_pos.start = cp;
        
                            self.drag_pos.cur_abs = click_pos;
                            self.drag_pos.cur = cp;
                            self.drag_started = true;
       -    
       +
                            cur_tool.handle_drag_begin(self);
                        }
                    }
       @@ -709,7 +712,8 @@ impl AnsiEditor {
        
                if response.hovered() {
                    if let Some(mouse_pos) = response.hover_pos() {
       -                if calc.buffer_rect.contains(mouse_pos) && !calc.scrollbar_rect.contains(mouse_pos) {
       +                if calc.buffer_rect.contains(mouse_pos) && !calc.scrollbar_rect.contains(mouse_pos)
       +                {
                            let click_pos = calc.calc_click_pos(mouse_pos);
                            let cp = Position::new(click_pos.x as i32, click_pos.y as i32)
                                - self.get_cur_click_offset();
       @@ -772,7 +776,11 @@ pub const DEFAULT_OUTLINE_TABLE: [[u8; 10]; 15] = [
            [147, 148, 149, 162, 167, 150, 129, 151, 163, 154],
        ];
        
       -pub fn terminal_context_menu(editor: &AnsiEditor, commands: &Commands, ui: &mut egui::Ui) -> Option<Message> {
       +pub fn terminal_context_menu(
       +    editor: &AnsiEditor,
       +    commands: &Commands,
       +    ui: &mut egui::Ui,
       +) -> Option<Message> {
            let mut result = None;
            ui.input_mut(|i| i.events.clear());
        
   DIR diff --git a/src/ui/main_window.rs b/src/ui/main_window.rs
       @@ -9,11 +9,11 @@ use std::{
        
        use crate::{
            add_child, model::Tool, AnsiEditor, BitFontEditor, BitFontSelector, CharFontEditor,
       -    CharTableToolWindow, Document, DocumentBehavior, DocumentOptions, DocumentTab, LayerToolWindow,
       -    Message, ModalDialog, ToolBehavior, ToolTab, TopBar, Commands,
       +    CharTableToolWindow, Commands, Document, DocumentBehavior, DocumentOptions, DocumentTab,
       +    LayerToolWindow, Message, ModalDialog, ToolBehavior, ToolTab, TopBar,
        };
        use eframe::{
       -    egui::{self, Response, SidePanel, TextStyle, Ui, Key},
       +    egui::{self, Key, Response, SidePanel, TextStyle, Ui},
            epaint::{pos2, FontId},
        };
        use glow::Context;
       @@ -39,7 +39,7 @@ pub struct MainWindow {
            pub right_panel: bool,
            pub bottom_panel: bool,
        
       -    pub commands: Commands
       +    pub commands: Commands,
        }
        
        pub const PASTE_TOOL: usize = 0;
       @@ -185,7 +185,7 @@ impl MainWindow {
                        selected_tool: FIRST_TOOL,
                        document_options: DocumentOptions {
                            scale: eframe::egui::Vec2::new(1.0, 1.0),
       -                    commands: Commands::default()
       +                    commands: Commands::default(),
                        },
                        request_close: None,
                        message: None,
       @@ -204,7 +204,7 @@ impl MainWindow {
                    palette_mode: 0,
                    top_bar: TopBar::new(&cc.egui_ctx),
                    commands: Commands::default(),
       -            is_closed: false
       +            is_closed: false,
                }
            }
        
       @@ -391,7 +391,7 @@ pub fn button_with_shortcut(
            label: impl Into<String>,
            shortcut: impl Into<String>,
        ) -> Response {
       -    ui.set_width(250.0);
       +    ui.set_width(280.0);
            let btn_re = ui.add_enabled(enabled, egui::Button::new(label.into()));
            let font_id = TextStyle::Body.resolve(ui.style());
            let color = ui.style().visuals.noninteractive().fg_stroke.color;
       @@ -573,7 +573,6 @@ impl eframe::App for MainWindow {
                self.commands.check(ctx, &mut msg);
                self.handle_message(msg);
        
       -
                ctx.request_repaint_after(Duration::from_millis(150));
            }
        
   DIR diff --git a/src/ui/messages.rs b/src/ui/messages.rs
       @@ -64,6 +64,28 @@ pub enum Message {
            Copy,
            Cut,
            RemoveFloatingLayer,
       +
       +    CenterLine,
       +    JustifyLineLeft,
       +    JustifyLineRight,
       +
       +    InsertRow,
       +    DeleteRow,
       +    InsertColumn,
       +    DeleteColumn,
       +
       +    EraseRow,
       +    EraseRowToStart,
       +    EraseRowToEnd,
       +
       +    EraseColumn,
       +    EraseColumnToStart,
       +    EraseColumnToEnd,
       +
       +    ScrollAreaUp,
       +    ScrollAreaDown,
       +    ScrollAreaLeft,
       +    ScrollAreaRight,
        }
        
        pub const CTRL_SHIFT: egui::Modifiers = egui::Modifiers {
       @@ -169,7 +191,13 @@ impl MainWindow {
                    }
                    Message::DeleteSelection => {
                        self.run_editor_command(0, |_, editor: &mut AnsiEditor, _| {
       -                    to_message(editor.buffer_view.lock().get_edit_state_mut().delete_selection())
       +                    to_message(
       +                        editor
       +                            .buffer_view
       +                            .lock()
       +                            .get_edit_state_mut()
       +                            .delete_selection(),
       +                    )
                        });
                    }
                    Message::ShowCharacterSelectionDialog(ch) => {
       @@ -260,14 +288,11 @@ impl MainWindow {
                        );
                    }
                    Message::RemoveFloatingLayer => {
       -                self.run_editor_command(
       -                    0,
       -                    |_, editor: &mut crate::AnsiEditor, _| {
       -                        let mut lock = editor.buffer_view.lock();
       -                        let layer = lock.get_edit_state().get_current_layer();
       -                        to_message(lock.get_edit_state_mut().remove_layer(layer))
       -                    },
       -                );
       +                self.run_editor_command(0, |_, editor: &mut crate::AnsiEditor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    let layer = lock.get_edit_state().get_current_layer();
       +                    to_message(lock.get_edit_state_mut().remove_layer(layer))
       +                });
                    }
                    Message::DuplicateLayer(cur_layer) => {
                        self.run_editor_command(
       @@ -486,6 +511,108 @@ impl MainWindow {
                    Message::CloseWindow => {
                        self.is_closed = true;
                    }
       +            Message::CenterLine => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().center_line())
       +                });
       +            }
       +            Message::JustifyLineLeft => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().justify_line_left())
       +                });
       +            }
       +            Message::JustifyLineRight => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().justify_line_right())
       +                });
       +            }
       +            Message::InsertRow => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().insert_row())
       +                });
       +            }
       +            Message::DeleteRow => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().delete_row())
       +                });
       +            }
       +            Message::InsertColumn => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().insert_column())
       +                });
       +            }
       +            Message::DeleteColumn => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().delete_column())
       +                });
       +            }
       +            Message::EraseRow => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().erase_row())
       +                });
       +            }
       +            Message::EraseRowToStart => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().erase_row_to_start())
       +                });
       +            }
       +            Message::EraseRowToEnd => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().erase_row_to_end())
       +                });
       +            }
       +            Message::EraseColumn => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().erase_column())
       +                });
       +            }
       +            Message::EraseColumnToStart => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().erase_column_to_start())
       +                });
       +            }
       +            Message::EraseColumnToEnd => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().erase_column_to_end())
       +                });
       +            }
       +            Message::ScrollAreaUp => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().scroll_area_up())
       +                });
       +            }
       +            Message::ScrollAreaDown => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().scroll_area_down())
       +                });
       +            }
       +            Message::ScrollAreaLeft => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().scroll_area_left())
       +                });
       +            }
       +            Message::ScrollAreaRight => {
       +                self.run_editor_command(0, |_, editor, _| {
       +                    let mut lock = editor.buffer_view.lock();
       +                    to_message(lock.get_edit_state_mut().scroll_area_right())
       +                });
       +            }
                }
            }
        }
   DIR diff --git a/src/ui/top_bar.rs b/src/ui/top_bar.rs
       @@ -111,9 +111,17 @@ impl MainWindow {
                        }
                        ui.separator();
                        if let Some(doc) = self.get_active_document() {
       -                    self.commands.cut.ui_enabled(ui, doc.lock().unwrap().can_cut(), &mut result);
       -                    self.commands.copy.ui_enabled(ui, doc.lock().unwrap().can_copy(), &mut result);
       -                    self.commands.paste.ui_enabled(ui, doc.lock().unwrap().can_paste(), &mut result);
       +                    self.commands
       +                        .cut
       +                        .ui_enabled(ui, doc.lock().unwrap().can_cut(), &mut result);
       +                    self.commands
       +                        .copy
       +                        .ui_enabled(ui, doc.lock().unwrap().can_copy(), &mut result);
       +                    self.commands.paste.ui_enabled(
       +                        ui,
       +                        doc.lock().unwrap().can_paste(),
       +                        &mut result,
       +                    );
                        }
        
                        ui.menu_button(fl!(crate::LANGUAGE_LOADER, "menu-paste-as"), |ui| {
       @@ -139,7 +147,30 @@ impl MainWindow {
                                ui.close_menu();
                            }
                        });
       +                ui.separator();
        
       +                self.commands.justify_line_left.ui_enabled(ui, false, &mut result);
       +                self.commands.justify_line_right.ui_enabled(ui, false, &mut result);
       +                self.commands.justify_line_center.ui_enabled(ui, false, &mut result);
       +                ui.separator();
       +                self.commands.insert_row.ui_enabled(ui, false, &mut result);
       +                self.commands.delete_row.ui_enabled(ui, false, &mut result);
       +                ui.separator();
       +                self.commands.insert_column.ui_enabled(ui, false, &mut result);
       +                self.commands.delete_column.ui_enabled(ui, false, &mut result);
       +                ui.separator();
       +                self.commands.erase_row.ui_enabled(ui, false, &mut result);
       +                self.commands.erase_row_to_start.ui_enabled(ui, false, &mut result);
       +                self.commands.erase_row_to_end.ui_enabled(ui, false, &mut result);
       +                ui.separator();
       +                self.commands.erase_column.ui_enabled(ui, false, &mut result);
       +                self.commands.erase_column_to_end.ui_enabled(ui, false, &mut result);
       +                self.commands.erase_column_to_start.ui_enabled(ui, false, &mut result);
       +                ui.separator();
       +                self.commands.scroll_area_up.ui_enabled(ui, false, &mut result);
       +                self.commands.scroll_area_down.ui_enabled(ui, false, &mut result);
       +                self.commands.scroll_area_left.ui_enabled(ui, false, &mut result);
       +                self.commands.scroll_area_right.ui_enabled(ui, false, &mut result);
                        ui.separator();
                        if ui
                            .add_enabled(
       @@ -167,15 +198,27 @@ impl MainWindow {
                    });
        
                    ui.menu_button(fl!(crate::LANGUAGE_LOADER, "menu-selection"), |ui| {
       -                self.commands.select_all.ui_enabled(ui, has_buffer, &mut result);
       -                self.commands.deselect.ui_enabled(ui, has_buffer, &mut result);
       +                self.commands
       +                    .select_all
       +                    .ui_enabled(ui, has_buffer, &mut result);
       +                self.commands
       +                    .deselect
       +                    .ui_enabled(ui, has_buffer, &mut result);
                        ui.separator();
       -                self.commands.erase_selection.ui_enabled(ui, has_buffer, &mut result);
       +                self.commands
       +                    .erase_selection
       +                    .ui_enabled(ui, has_buffer, &mut result);
                        self.commands.flip_x.ui_enabled(ui, has_buffer, &mut result);
                        self.commands.flip_y.ui_enabled(ui, has_buffer, &mut result);
       -                self.commands.justifycenter.ui_enabled(ui, has_buffer, &mut result);
       -                self.commands.justifyleft.ui_enabled(ui, has_buffer, &mut result);
       -                self.commands.justifyright.ui_enabled(ui, has_buffer, &mut result);
       +                self.commands
       +                    .justifycenter
       +                    .ui_enabled(ui, has_buffer, &mut result);
       +                self.commands
       +                    .justifyleft
       +                    .ui_enabled(ui, has_buffer, &mut result);
       +                self.commands
       +                    .justifyright
       +                    .ui_enabled(ui, has_buffer, &mut result);
                        self.commands.crop.ui_enabled(ui, has_buffer, &mut result);
                    });