gled-ui 2.30.1

gled's egui interface, shared by the desktop app and the browser
Documentation
//! What the interface can do only on some platforms.

/// Shows the SVG templates of the asset repository in the file manager. Only
/// on the desktop, where the repository is a local folder.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn open_svg_templates_folder(storage_dir: &str) {
    if let Err(err) = open::that(std::path::Path::new(storage_dir).join("svg")) {
        tracing::warn!("Could not open the SVG templates folder: {err}");
    }
}

/// Hands `contents` to the browser as a download named `file_name`.
#[cfg(target_arch = "wasm32")]
pub(crate) fn download(file_name: &str, contents: &str) -> Result<(), String> {
    use wasm_bindgen::JsCast;

    let error = |err: wasm_bindgen::JsValue| format!("{err:?}");
    let window = web_sys::window().ok_or("no window")?;
    let document = window.document().ok_or("no document")?;
    let parts = js_sys::Array::of1(&wasm_bindgen::JsValue::from_str(contents));
    let options = web_sys::BlobPropertyBag::new();
    options.set_type("image/svg+xml");
    let blob = web_sys::Blob::new_with_str_sequence_and_options(&parts, &options).map_err(error)?;
    let url = web_sys::Url::create_object_url_with_blob(&blob).map_err(error)?;
    let anchor = document
        .create_element("a")
        .map_err(error)?
        .dyn_into::<web_sys::HtmlAnchorElement>()
        .map_err(|_| "no anchor element")?;
    anchor.set_href(&url);
    anchor.set_download(file_name);
    anchor.click();
    web_sys::Url::revoke_object_url(&url).map_err(error)?;
    Ok(())
}

/// Opens the interface again in a new browser tab, which is a new window
/// connected to the same engine.
#[cfg(target_arch = "wasm32")]
pub(crate) fn open_in_new_tab() {
    let Some(window) = web_sys::window() else {
        return;
    };
    let opened = window
        .location()
        .href()
        .and_then(|href| window.open_with_url_and_target(&href, "_blank"));
    match opened {
        Ok(Some(_)) => {}
        Ok(None) => gled_protocol::action::UiAction::Error(
            "The browser blocked the new window, allow pop-ups for this page".to_owned(),
        )
        .enqueue(),
        Err(err) => tracing::warn!("Could not open a new tab: {err:?}"),
    }
}

/// Lets the user pick an SVG file with the browser's file picker and makes it
/// the project's SVG.
#[cfg(target_arch = "wasm32")]
pub(crate) fn upload_svg(ctx: egui::Context) {
    use gled_model::asset::svg::Svg;
    use gled_protocol::action::UiAction;

    wasm_bindgen_futures::spawn_local(async move {
        let Some(file) = rfd::AsyncFileDialog::new()
            .set_title("Open SVG file")
            .add_filter("svg", &["svg"])
            .pick_file()
            .await
        else {
            return;
        };
        match String::from_utf8(file.read().await) {
            Ok(contents) => UiAction::SetSvg(Some(Svg::new(contents))).enqueue(),
            Err(err) => UiAction::Error(format!(
                "Could not load svg file \"{}\": {err}",
                file.file_name()
            ))
            .enqueue(),
        }
        ctx.request_repaint();
    });
}

/// Sets the title of the browser tab.
#[cfg(target_arch = "wasm32")]
pub(crate) fn set_page_title(title: &str) {
    if let Some(document) = web_sys::window().and_then(|window| window.document()) {
        document.set_title(title);
    }
}

/// Whether the page fills the screen.
#[cfg(target_arch = "wasm32")]
pub(crate) fn is_fullscreen() -> bool {
    web_sys::window()
        .and_then(|window| window.document())
        .and_then(|document| document.fullscreen_element())
        .is_some()
}

/// Shows the page on the whole screen, or leaves that. Browsers only allow
/// it in answer to a click or key, which is when egui handles them on the web.
#[cfg(target_arch = "wasm32")]
pub(crate) fn set_fullscreen(fullscreen: bool) {
    let Some(document) = web_sys::window().and_then(|window| window.document()) else {
        return;
    };
    if !fullscreen {
        document.exit_fullscreen();
    } else if let Some(element) = document.document_element()
        && let Err(err) = element.request_fullscreen()
    {
        tracing::warn!("The browser refused fullscreen: {err:?}");
    }
}