maps 1.10.1

Inspect, compare and align multiple grid maps in an intuitive & fast GUI
Documentation
use eframe::egui;

use crate::app::{ActiveMovable, ActiveTool, AppState};
use crate::app_impl::screenshot;
use crate::movable::{DragDirection, Draggable, MovableAmounts, Rotatable};

impl AppState {
    fn dialogs_open(&self) -> bool {
        self.status.quit_modal_active || !self.status.error.is_empty()
    }

    fn text_editing(&self) -> bool {
        self.options.pose_edit.edit_map_frame || self.options.pose_edit.edit_root_frame
    }

    pub(crate) fn handle_key_shortcuts(&mut self, ui: &egui::Ui) {
        if self.dialogs_open() || self.text_editing() {
            self.status.move_action = None;
            return;
        }

        let mut screenshot_request: Option<screenshot::Viewport> = None;
        let mut selected_move_preset: Option<MovableAmounts> = None;
        let mut moving_via_keyboard = false;
        ui.input(|i| {
            if i.key_released(egui::Key::Escape) {
                self.options.menu_visible = false;
                self.options.settings_visible = false;
                self.options.help_visible = false;
                self.options.active_tool = ActiveTool::None;
            } else if i.key_released(egui::Key::L) || i.pointer.secondary_released() {
                if self.options.active_tool == ActiveTool::HoverLens {
                    self.options.active_tool = ActiveTool::None;
                } else {
                    self.options.active_tool = ActiveTool::HoverLens;
                }
            }
            if i.key_released(egui::Key::M) {
                self.options.menu_visible = !self.options.menu_visible;
            }
            if i.key_released(egui::Key::O) {
                self.options.settings_visible = !self.options.settings_visible;
            }
            if i.key_released(egui::Key::G) {
                self.options.grid.lines_visible = !self.options.grid.lines_visible;
            }

            // Screenshot shortcuts.
            if i.modifiers.shift && i.key_released(egui::Key::P) {
                screenshot_request = Some(screenshot::Viewport::Clipped);
            } else if i.key_released(egui::Key::P) {
                screenshot_request = Some(screenshot::Viewport::Full);
            }

            // Get the obects that can be currently dragged.
            let drag_amount = match self.options.active_movable {
                ActiveMovable::MapPose => self.options.pose_edit.movable_amounts.drag,
                ActiveMovable::Grid => self.options.grid.movable_amounts.drag,
                _ => Default::default(),
            };
            let draggable: Option<&mut dyn Draggable> = match self.options.active_movable {
                ActiveMovable::MapPose => {
                    match self
                        .data
                        .maps
                        .get_mut(self.options.pose_edit.selected_map.as_str())
                    {
                        Some(map) => Some(&mut map.pose),
                        None => None,
                    }
                }
                ActiveMovable::Grid => Some(&mut self.options.grid),
                _ => None,
            };

            if let Some(draggable) = draggable {
                let previous_offset = draggable.offset_rhs();
                if i.key_down(egui::Key::W) {
                    draggable.drag_directed(drag_amount, DragDirection::Up);
                }
                if i.key_down(egui::Key::A) {
                    draggable.drag_directed(drag_amount, DragDirection::Left);
                }
                if i.key_down(egui::Key::S) {
                    draggable.drag_directed(drag_amount, DragDirection::Down);
                }
                if i.key_down(egui::Key::D) {
                    draggable.drag_directed(drag_amount, DragDirection::Right);
                }
                let delta = draggable.offset_rhs() - previous_offset;
                moving_via_keyboard = delta != egui::Vec2::ZERO;
                if delta.x != 0. && delta.y == 0. {
                    self.status.move_action = Some("".to_string());
                } else if delta.x == 0. && delta.y != 0. {
                    self.status.move_action = Some("".to_string());
                } else if delta.x > 0. && delta.y > 0. {
                    self.status.move_action = Some("".to_string());
                } else if delta.x < 0. && delta.y > 0. {
                    self.status.move_action = Some("".to_string());
                } else if delta.x < 0. && delta.y < 0. {
                    self.status.move_action = Some("".to_string());
                } else if delta.x > 0. && delta.y < 0. {
                    self.status.move_action = Some("".to_string());
                } else {
                    self.status.move_action = None;
                }
            }

            let rotatable: Option<&mut dyn Rotatable> = match self.options.active_movable {
                ActiveMovable::MapPose => {
                    match self
                        .data
                        .maps
                        .get_mut(self.options.pose_edit.selected_map.as_str())
                    {
                        Some(map) => Some(&mut map.pose),
                        None => None,
                    }
                }
                _ => None,
            };

            let rotation_amount = self.options.pose_edit.movable_amounts.rotate;
            if let Some(rotatable) = rotatable {
                if i.key_down(egui::Key::Q) {
                    rotatable.rotate_directed(rotation_amount, DragDirection::Left);
                    self.status.move_action = Some("".to_string());
                    moving_via_keyboard = true;
                } else if i.key_down(egui::Key::E) {
                    rotatable.rotate_directed(rotation_amount, DragDirection::Right);
                    self.status.move_action = Some("".to_string());
                    moving_via_keyboard = true;
                }
            }

            if i.key_down(egui::Key::Minus) {
                self.options
                    .grid
                    .zoom(-self.options.grid.scroll_delta_percent, None);
                self.status.move_action = Some("-".to_string());
                moving_via_keyboard = true;
            }
            if i.key_down(egui::Key::Plus) {
                self.options
                    .grid
                    .zoom(self.options.grid.scroll_delta_percent, None);
                self.status.move_action = Some("+".to_string());
                moving_via_keyboard = true;
            }

            if i.key_released(egui::Key::Num1) {
                selected_move_preset = Some(MovableAmounts::PRESET_FINE);
            } else if i.key_released(egui::Key::Num2) {
                selected_move_preset = Some(MovableAmounts::PRESET_MEDIUM);
            } else if i.key_released(egui::Key::Num3) {
                selected_move_preset = Some(MovableAmounts::PRESET_COARSE);
            }
        });

        if let Some(viewport) = screenshot_request {
            // Has to be called here outside of the input closure to not block.
            self.request_screenshot(ui, viewport);
        }

        if moving_via_keyboard {
            // Trigger directly a repaint when movement keys are pressed,
            // this makes the movement smooth and responsive.
            // (otherwise there is a very short but noticeable delay / stutter)
            ui.ctx().request_repaint();
        }

        if moving_via_keyboard && self.options.active_movable == ActiveMovable::MapPose {
            self.status.unsaved_changes = true;
        }

        if let Some(preset) = selected_move_preset {
            match self.options.active_movable {
                ActiveMovable::MapPose => self.options.pose_edit.movable_amounts = preset,
                ActiveMovable::Grid => self.options.grid.movable_amounts = preset,
                _ => (),
            }
        }
    }
}