use std::collections::{BTreeMap, HashMap};
use std::path::{PathBuf, absolute};
use std::vec::Vec;
use eframe::egui;
use serde::{Deserialize, Serialize};
use strum_macros::{Display, EnumString, VariantNames};
pub use crate::app_impl::canvas_settings::CanvasOptions;
pub use crate::app_impl::pose_edit::PoseEditOptions;
pub use crate::app_impl::tint_settings::TintOptions;
pub use crate::error::{Error, Result};
pub use crate::grid_options::GridOptions;
pub use crate::lens::LensOptions;
pub use maps_io_ros::ColorMap;
use crate::app_impl::CUSTOM_TITLEBAR_SUPPORTED;
use crate::draw_order::DrawOrder;
use crate::map_state::MapState;
use crate::persistence::{PersistenceOptions, save_app_options};
use crate::tiles::Tiles;
use crate::tracing::Tracing;
use maps_io_ros::Meta;
use maps_rendering::render_options::default_crop_threshold;
#[cfg(target_arch = "wasm32")]
use crate::wasm::async_data::AsyncData;
#[cfg(target_arch = "wasm32")]
use std::sync::{Arc, Mutex};
#[derive(
Clone, Debug, Default, PartialEq, Display, EnumString, VariantNames, Serialize, Deserialize,
)]
pub enum ViewMode {
Tiles,
Stacked,
#[default]
Aligned,
LoadScreen,
}
#[derive(Clone, Debug, Display, Default, PartialEq, EnumString, VariantNames)]
pub enum ActiveMovable {
None,
#[strum(to_string = "Map Pose")]
MapPose,
#[default]
Grid,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub enum ActiveTool {
#[default]
None,
HoverLens,
PlaceLens,
Measure,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub enum TitleBar {
#[default]
Default,
Custom,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CollapsedState {
pub app_settings: bool,
pub canvas_settings: bool,
pub tint_settings: bool,
pub lens_settings: bool,
pub grid_settings: bool,
pub tool_settings: bool,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct DisplayOptions {
pub show_full_paths: bool,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AppOptions {
pub version: String,
pub persistence: PersistenceOptions,
#[serde(default)]
pub advanced: AdvancedOptions,
#[serde(skip)]
pub titlebar: TitleBar,
pub canvas_settings: CanvasOptions,
pub menu_visible: bool,
pub settings_visible: bool,
pub help_visible: bool,
pub view_mode: ViewMode,
pub lens: LensOptions,
pub grid: GridOptions,
#[serde(skip)]
pub tint_settings: TintOptions,
#[serde(skip)]
pub pose_edit: PoseEditOptions,
#[serde(skip)]
pub active_movable: ActiveMovable,
#[serde(skip)]
pub active_tool: ActiveTool,
#[serde(default)]
pub collapsed: CollapsedState,
#[serde(default)]
pub display: DisplayOptions,
}
impl AppOptions {
pub fn with_custom_titlebar(mut self) -> Self {
if CUSTOM_TITLEBAR_SUPPORTED {
self.titlebar = TitleBar::Custom;
}
self
}
pub fn custom_titlebar(&self) -> bool {
self.titlebar == TitleBar::Custom
}
#[cfg(target_arch = "wasm32")]
pub fn with_dark_theme(mut self) -> Self {
self.canvas_settings.theme_preference = egui::ThemePreference::Dark;
self
}
}
#[derive(Default)]
pub struct StatusInfo {
pub error: String,
pub hover_position: Option<egui::Pos2>,
pub quit_modal_active: bool,
pub debug_window_active: bool,
pub draw_order_edit_active: bool,
pub unsaved_changes: bool,
pub quit_after_save: bool,
pub move_action: Option<String>,
pub active_tool: Option<String>,
}
#[derive(Default, Serialize, Deserialize)]
pub struct SessionData {
pub version: Option<String>,
pub maps: BTreeMap<String, MapState>,
#[serde(default)]
pub draw_order: DrawOrder,
pub grid_lenses: HashMap<String, egui::Pos2>,
#[cfg(target_arch = "wasm32")]
#[serde(skip)]
pub(crate) wasm_io: Arc<Mutex<AsyncData>>,
#[cfg(target_arch = "wasm32")]
#[serde(skip)]
pub(crate) demo_button_image_handle: Option<egui::TextureHandle>,
#[cfg(target_arch = "wasm32")]
#[serde(skip)]
pub(crate) nav2_demo_button_image_handle: Option<egui::TextureHandle>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AdvancedOptions {
#[serde(default = "default_crop_threshold")]
pub grid_crop_threshold: u32,
#[serde(skip)]
pub dry_run: bool,
}
impl Default for AdvancedOptions {
fn default() -> Self {
Self {
grid_crop_threshold: default_crop_threshold(),
dry_run: false,
}
}
}
#[derive(Default)]
pub struct AppState {
pub options: AppOptions,
pub build_info: String,
pub data: SessionData,
pub status: StatusInfo,
pub tracing: Tracing,
pub last_file_dir: Option<PathBuf>,
pub tile_manager: Tiles,
}
impl AppState {
pub fn init(metas: Vec<Meta>, options: AppOptions) -> Result<AppState> {
let mut state = AppState {
options,
..Default::default()
};
state.data.version = Some(state.options.version.clone());
let mut _default_dir = None;
for meta in metas {
_default_dir = absolute(meta.yaml_path.parent().expect("No parent dir?")).ok();
state.load_map(meta)?;
}
for map in state.data.maps.values_mut() {
map.tint = Some(state.options.tint_settings.tint_for_all);
if let Some(color_to_alpha) = state.options.tint_settings.color_to_alpha_for_all {
map.color_to_alpha = Some(color_to_alpha);
}
}
state.last_file_dir = _default_dir;
const TRACING_BUFFER_SIZE: usize = 600;
state.tracing = Tracing::new("frame update", TRACING_BUFFER_SIZE);
Ok(state)
}
pub fn with_build_info(mut self, build_info: String) -> Self {
self.build_info = build_info;
self
}
}
impl eframe::App for AppState {
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
self.tracing.start();
ui.ctx()
.set_theme(self.options.canvas_settings.theme_preference);
let mut central_rect = egui::Rect::ZERO;
egui::CentralPanel::no_frame().show_inside(ui, |ui| {
self.error_modal(ui);
self.quit_modal(ui);
self.handle_key_shortcuts(ui);
self.header_panel(ui);
self.menu_panel(ui);
self.footer_panel(ui);
self.settings_panel(ui);
central_rect = self.central_panel(ui);
self.info_window(ui);
self.debug_window(ui);
});
self.handle_new_screenshot(ui.ctx(), ¢ral_rect);
#[cfg(target_arch = "wasm32")]
self.consume_wasm_io();
if ui.ctx().input(|i| i.viewport().close_requested())
&& self.status.unsaved_changes
&& !self.data.maps.is_empty()
{
ui.ctx()
.send_viewport_cmd(egui::ViewportCommand::CancelClose);
self.status.quit_modal_active = true;
}
self.tracing.measure();
}
fn on_exit(&mut self) {
if !self.options.persistence.autosave {
return;
}
save_app_options(&self.options);
}
}