gled-ui 2.30.1

gled's egui interface, shared by the desktop app and the browser
Documentation
#[cfg(not(target_arch = "wasm32"))]
use crate::app::svg::save_svg;
use crate::ui::{
    asset_tree::{AssetTree, TreeSelection},
    window_common::{gled_window_frame, tool_window_builder},
};
use egui::{Button, Id, Vec2, ViewportId};
use gled_model::{
    asset::{Asset, project::Project},
    collection::Collections,
};
use gled_protocol::action::UiAction;
#[cfg(not(target_arch = "wasm32"))]
use tracing::debug;

#[derive(Debug, Default)]
pub(crate) struct ProjectsWindow {
    open: bool,
    dirty: bool,
    tree: AssetTree<Project>,
}

impl ProjectsWindow {
    #[cfg_attr(feature = "profiling", profiling::function)]
    pub(crate) fn update(&mut self, ctx: &egui::Context, collections: &mut Collections) {
        if !self.open {
            return;
        }

        ctx.show_viewport_immediate(
            ViewportId(Id::new("projects window")),
            tool_window_builder("Projects")
                .with_inner_size(Vec2::new(550.0, 500.0))
                .with_min_inner_size(Vec2::new(550.0, 500.0)),
            |ctx, _viewport_class| {
                ctx.input(|input| {
                    if input.viewport().close_requested()
                        || crate::ui::window_common::embedded_close_clicked("Projects")
                    {
                        self.open = false;
                    }
                });

                gled_window_frame(ctx, "Projects", |ui| {
                    egui::Panel::left("projects tree")
                        .exact_size(200.0)
                        .resizable(false)
                        .show(ui, |ui| {
                            if self.tree.show(
                                ui,
                                ui.make_persistent_id("projects_tree"),
                                collections,
                            ) {
                                self.dirty = false;
                            }
                        });

                    egui::CentralPanel::default().show(ui, |ui| {
                        self.tree.common_settings(ui, &mut self.dirty, collections);
                        if let TreeSelection::Asset(project) = self.tree.selected() {
                            ui.label(format!(
                                "Scenes in Grid: {}",
                                project.data.scenes_instances_grid_len()
                            ));
                            ui.vertical_centered_justified(|ui| {
                                if ui.button("Load Project").clicked() {
                                    UiAction::SetProject(project.id).enqueue();
                                    self.open = false;
                                }
                            });
                            ui.vertical_centered_justified(|ui| {
                                if ui
                                    .add_enabled(
                                        project.data.svg.is_some(),
                                        Button::new("🖻 Export SVG file"),
                                    )
                                    .clicked()
                                {
                                    export_svg(project);
                                }
                            });
                        }
                    });
                });
            },
        );
    }

    pub(crate) fn open(&mut self) {
        self.open = true;
    }
}

/// Saves the project's SVG to a file picked in a dialog, which runs on its own
/// thread.
#[cfg(not(target_arch = "wasm32"))]
fn export_svg(project: &Asset<Project>) {
    let svg = project.data.svg.clone();
    std::thread::Builder::new()
        .name("gled:ui:save_svg".to_string())
        .spawn(move || {
            #[cfg(feature = "profiling")]
            profiling::register_thread!("save_svg_file");

            if let (Some(svg), Some(path)) = (
                svg,
                rfd::FileDialog::new()
                    .set_title("Save SVG file")
                    .add_filter("svg", &["svg"])
                    .save_file(),
            ) {
                match save_svg(&svg, &path) {
                    Ok(_) => {
                        debug!("Saved svg file \"{}\"", path.display());
                    }
                    Err(err) => {
                        UiAction::Error(format!(
                            "Could not save svg file \"{}\": {err:?}",
                            path.display()
                        ))
                        .enqueue();
                    }
                };
            }
        })
        .ok();
}

/// Lets the browser download the project's SVG.
#[cfg(target_arch = "wasm32")]
fn export_svg(project: &Asset<Project>) {
    if let Some(svg) = &project.data.svg
        && let Err(err) =
            crate::platform::download(&format!("{}.svg", project.name()), svg.contents())
    {
        UiAction::Error(format!("Could not save svg file: {err}")).enqueue();
    }
}