nightshade 0.8.0

A cross-platform data-oriented game engine.
Documentation
use crate::mosaic::project::{LayoutSaveFile, ProjectSaveFile, WindowLayout};
use crate::mosaic::widget::{Pane, Widget};

use super::Mosaic;

pub fn format_window_title(
    app_name: &str,
    path: Option<&std::path::Path>,
    modified: bool,
) -> String {
    match path {
        Some(path) => {
            if modified {
                format!("{} - {} (*)", app_name, path.display())
            } else {
                format!("{} - {}", app_name, path.display())
            }
        }
        None => app_name.to_string(),
    }
}

impl<W: Widget<C, M>, C, M> Mosaic<W, C, M> {
    pub fn save_layout(&self, name: &str, version: &str) -> Option<LayoutSaveFile<W>> {
        self.tile_tree.as_ref().map(|tree| LayoutSaveFile {
            version: version.to_string(),
            tree: tree.clone(),
            layout_name: name.to_string(),
        })
    }

    pub fn load_layout(&mut self, layout: LayoutSaveFile<W>) {
        self.tile_tree = Some(layout.tree);
        self.layout_name = layout.layout_name;
        self.layout_modified = true;
    }

    pub fn save_tree(&self) -> Result<serde_json::Value, serde_json::Error> {
        serde_json::to_value(&self.tile_tree)
    }

    pub fn load_tree(&mut self, value: serde_json::Value) -> Result<(), serde_json::Error> {
        let tree: Option<egui_tiles::Tree<Pane<W>>> = serde_json::from_value(value)?;
        self.tile_tree = tree;
        self.layout_modified = true;
        Ok(())
    }

    #[cfg(any(target_arch = "wasm32", feature = "file_dialog"))]
    pub fn save_project_to_file(&self, filename: &str) -> Result<(), String> {
        let tree_json = self.save_tree().map_err(|error| error.to_string())?;
        let bytes = serde_json::to_vec_pretty(&tree_json).map_err(|error| error.to_string())?;
        let filters = [crate::filesystem::FileFilter {
            name: "JSON".to_string(),
            extensions: vec!["json".to_string()],
        }];
        crate::filesystem::save_file(filename, &bytes, &filters).map_err(|error| error.to_string())
    }

    #[cfg(any(target_arch = "wasm32", feature = "file_dialog"))]
    pub fn request_project_load(&self) -> crate::filesystem::PendingFileLoad {
        let filters = [crate::filesystem::FileFilter {
            name: "JSON".to_string(),
            extensions: vec!["json".to_string()],
        }];
        crate::filesystem::request_file_load(&filters)
    }

    pub fn load_project_from_bytes(&mut self, bytes: &[u8]) -> Result<(), String> {
        let value: serde_json::Value =
            serde_json::from_slice(bytes).map_err(|error| error.to_string())?;
        self.load_tree(value).map_err(|error| error.to_string())
    }
}

pub fn save_project<W, C, M, D>(
    name: &str,
    version: &str,
    mosaics: &[&Mosaic<W, C, M>],
    data: Option<D>,
) -> ProjectSaveFile<W, D>
where
    W: Widget<C, M>,
{
    let windows = mosaics
        .iter()
        .filter_map(|mosaic| {
            mosaic.tile_tree.as_ref().map(|tree| WindowLayout {
                tree: tree.clone(),
                layout_name: mosaic.layout_name.clone(),
            })
        })
        .collect();

    ProjectSaveFile {
        version: version.to_string(),
        name: name.to_string(),
        windows,
        data,
    }
}

pub fn load_project<W, D>(project: ProjectSaveFile<W, D>) -> (Vec<WindowLayout<W>>, Option<D>) {
    (project.windows, project.data)
}