maps 1.10.1

Inspect, compare and align multiple grid maps in an intuitive & fast GUI
Documentation
use eframe::egui;
use log::{debug, error, info};
use strum::Display;

use crate::app::AppState;
use maps_rendering::image::from_egui_image;

#[cfg(target_arch = "wasm32")]
use crate::wasm::async_image_io;
#[cfg(not(target_arch = "wasm32"))]
use maps_io_ros::save_image;

#[derive(Clone, Debug, Display)]
pub enum Viewport {
    Full,
    Clipped,
}

impl AppState {
    pub(crate) fn request_screenshot(&self, ui: &egui::Ui, viewport: Viewport) {
        debug!("{viewport} screenshot requested for the next frame.");
        ui.ctx()
            .send_viewport_cmd(egui::ViewportCommand::Screenshot(egui::UserData::new(
                viewport,
            )));
    }

    pub(crate) fn handle_new_screenshot(&mut self, ctx: &egui::Context, clip_rect: &egui::Rect) {
        let event_data = ctx.input(|i| {
            i.events
                .iter()
                .filter_map(|e| {
                    if let egui::Event::Screenshot {
                        image, user_data, ..
                    } = e
                    {
                        Some((image.clone(), user_data.clone()))
                    } else {
                        None
                    }
                })
                .next_back()
        });

        if let Some(event) = event_data {
            let viewport: Viewport = match event.1.data {
                Some(data) => data
                    .downcast_ref::<Viewport>()
                    .expect("Failed to downcast viewport data.")
                    .clone(),
                _ => {
                    error!("Invalid viewport data for screenshot.");
                    return;
                }
            };
            info!(
                "Captured {} screenshot.",
                viewport.to_string().to_lowercase()
            );

            let image = match viewport {
                Viewport::Full => from_egui_image(&event.0),
                Viewport::Clipped => from_egui_image(&event.0).crop(
                    (clip_rect.min.x * ctx.pixels_per_point()) as u32,
                    (clip_rect.min.y * ctx.pixels_per_point()) as u32,
                    (clip_rect.width() * ctx.pixels_per_point()) as u32,
                    (clip_rect.height() * ctx.pixels_per_point()) as u32,
                ),
            };

            #[cfg(not(target_arch = "wasm32"))]
            {
                let mut dialog = rfd::FileDialog::new()
                    .add_filter("PNG", &["png"])
                    .set_file_name("maps_screenshot.png");
                if let Some(dir) = &self.last_file_dir {
                    dialog = dialog.set_directory(dir);
                }
                if let Some(file_path) = dialog.save_file() {
                    match save_image(&file_path, &image) {
                        Ok(_) => {
                            info!("Saved screenshot to {file_path:?}");
                            self.last_file_dir =
                                file_path.parent().map(std::path::Path::to_path_buf);
                        }
                        Err(e) => {
                            self.status.error = format!("Failed to save screenshot: {e}");
                            error!("{e}");
                        }
                    }
                }
            }
            #[cfg(target_arch = "wasm32")]
            {
                async_image_io::pick_save_png(
                    self.data.wasm_io.clone(),
                    "maps_screenshot.png".to_string(),
                    image,
                );
            }
        }
    }
}