mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-30 21:36:46 -06:00
Export frames to PDF: use the asynchronous save dialog
Main's file dialogs answer asynchronously now (they no longer re-enter the event loop on macOS); the menu item takes the path in a callback, and the test draws a frame to let the dialog answer.
This commit is contained in:
@@ -160,10 +160,11 @@ pub fn invoke(app: &mut FigApp, id: &str) {
|
||||
/// Every visible top-level frame of the page as one PDF page each, through a save dialog.
|
||||
fn export_frames_pdf(app: &mut FigApp) {
|
||||
let name = app.session.doc().map(|d| d.name.clone()).unwrap_or_else(|_| "Untitled".into());
|
||||
let Some(p) = app.services.pick_save(tr("Export"), &format!("{name}.pdf"), &["pdf"]) else { return };
|
||||
if app.run("file.exportFramesPdf", json!({"path": p.to_string_lossy()})).is_some() {
|
||||
app.toast(trf("Exported {name}", &[("name", &p.file_name().map(|f| f.to_string_lossy().to_string()).unwrap_or_default())]));
|
||||
}
|
||||
app.pick_save(tr("Export"), &format!("{name}.pdf"), &["pdf"], |app, p| {
|
||||
if app.run("file.exportFramesPdf", json!({"path": p.to_string_lossy()})).is_some() {
|
||||
app.toast(trf("Exported {name}", &[("name", &p.file_name().map(|f| f.to_string_lossy().to_string()).unwrap_or_default())]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn figcraft_svg_export(doc: &figcraft_doc::Document, ids: &[figcraft_doc::NodeId]) -> Option<String> {
|
||||
|
||||
@@ -5,14 +5,25 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use figcraft_engine::Session;
|
||||
use figcraft_ui_egui::{FigApp, Services, menus};
|
||||
use figcraft_ui_egui::{DialogFuture, FigApp, Services, menus};
|
||||
use serde_json::json;
|
||||
|
||||
/// Services whose save dialog always returns the given path.
|
||||
struct SaveTo(PathBuf);
|
||||
impl Services for SaveTo {
|
||||
fn pick_save(&mut self, _title: &str, _name: &str, _exts: &[&str]) -> Option<PathBuf> {
|
||||
Some(self.0.clone())
|
||||
fn pick_save(&mut self, _title: &str, _name: &str, _exts: &[&str]) -> DialogFuture {
|
||||
Box::pin(std::future::ready(Some(self.0.clone())))
|
||||
}
|
||||
}
|
||||
|
||||
/// The menu item, then a frame: the save dialog answers asynchronously and the frame takes its answer.
|
||||
fn invoke(app: &mut FigApp, id: &str) {
|
||||
menus::invoke(app, id);
|
||||
let ctx = egui::Context::default();
|
||||
for _ in 0..2 {
|
||||
let input =
|
||||
egui::RawInput { screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1440.0, 900.0))), ..Default::default() };
|
||||
ctx.run_ui(input, |ui| app.ui(ui)).textures_delta.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +48,7 @@ fn frames_pdf_menu_saves_the_pdf() {
|
||||
let path = out_path("one");
|
||||
let mut app = FigApp::new(Session::new(), Box::new(SaveTo(path.clone())));
|
||||
frame(&mut app, 0, 0);
|
||||
menus::invoke(&mut app, "file.exportFramesPdf");
|
||||
invoke(&mut app, "file.exportFramesPdf");
|
||||
assert!(app.ui.last_error.is_none(), "{:?}", app.ui.last_error);
|
||||
let bytes = std::fs::read(&path).expect("the menu writes the PDF where the user chose");
|
||||
let _ = std::fs::remove_file(&path);
|
||||
@@ -60,7 +71,7 @@ fn frames_pdf_menu_one_page_per_visible_frame() {
|
||||
let hidden = frame(&mut app, 600, 0);
|
||||
let id = hidden["id"].clone();
|
||||
app.session.execute("node.set", &json!({"id": id, "visible": false})).unwrap();
|
||||
menus::invoke(&mut app, "file.exportFramesPdf");
|
||||
invoke(&mut app, "file.exportFramesPdf");
|
||||
assert!(app.ui.last_error.is_none(), "{:?}", app.ui.last_error);
|
||||
let bytes = std::fs::read(&path).expect("the menu writes the PDF where the user chose");
|
||||
let _ = std::fs::remove_file(&path);
|
||||
|
||||
Reference in New Issue
Block a user