mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 00:17:42 +00:00
Merge pull request #5417 from storytold/fix/sec-ui-automation-handoffs
UI: automation can't open the browser or viewer; Preview in Browser stays in its folder; Graph Data is bounded
This commit is contained in:
@@ -468,7 +468,10 @@ fn typed_number(x: f64) -> Value {
|
||||
/// the rows then scroll ([`super::modal::scrolled_rows`]).
|
||||
pub(super) fn schema_fields(app: &mut crate::VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, m: &Manifest) -> bool {
|
||||
let (changed, help) = schema_rows(ui, d, m);
|
||||
if let Some(url) = help {
|
||||
// The browser is another app: a click automation sent opens nothing.
|
||||
if let Some(url) = help
|
||||
&& app.refuse_automation("the plug-in help button").is_ok()
|
||||
{
|
||||
app.open_url(&url);
|
||||
}
|
||||
changed
|
||||
|
||||
@@ -106,8 +106,11 @@ pub(crate) fn grid_from_data(v: &Value) -> Grid {
|
||||
/// columns past the data.
|
||||
pub(crate) fn data_from_grid(grid: &Grid) -> Value {
|
||||
let filled = |s: &String| !s.trim().is_empty();
|
||||
let rows = grid.iter().rposition(|r| r.iter().any(filled)).map_or(1, |i| i + 1).max(1);
|
||||
let cols = grid.iter().take(rows).filter_map(|r| r.iter().rposition(filled)).max().map_or(1, |i| i + 1).max(1);
|
||||
// At most a graph's rows and columns, as a file gives (`grid_from_text`): a grid sent by
|
||||
// automation is any size, and the values are their product.
|
||||
let grid = &grid[..grid.len().min(MAX_ROWS)];
|
||||
let rows = grid.iter().rposition(|r| r.iter().take(MAX_COLS).any(filled)).map_or(1, |i| i + 1).max(1);
|
||||
let cols = grid.iter().take(rows).filter_map(|r| r.iter().take(MAX_COLS).rposition(filled)).max().map_or(1, |i| i + 1).max(1);
|
||||
let label = |s: &str| {
|
||||
let s = s.trim();
|
||||
// A label typed in straight quotes (as the CSV form takes a number as a label) is the text inside.
|
||||
@@ -163,7 +166,7 @@ fn clean(s: &str) -> String {
|
||||
s.chars().map(|c| if matches!(c, '\t' | '\r' | '\n') { ' ' } else { c }).take(MAX_CELL).collect()
|
||||
}
|
||||
|
||||
fn cell(grid: &Grid, r: usize, c: usize) -> String {
|
||||
fn cell(grid: &[Vec<String>], r: usize, c: usize) -> String {
|
||||
grid.get(r).and_then(|row| row.get(c)).cloned().unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -514,6 +517,16 @@ mod tests {
|
||||
assert_eq!(grid_from_text(&wide)[0].len(), MAX_COLS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_sheet_sent_wider_and_taller_than_a_graph_is_cut_to_one() {
|
||||
// One long row and one long column: their product would be ten billion cells.
|
||||
let mut grid: Grid = vec![vec!["1".to_string(); 100_000]];
|
||||
grid.extend((0..100_000).map(|_| vec!["2".to_string()]));
|
||||
let p = data_from_grid(&grid);
|
||||
assert_eq!(p["rows"].as_array().unwrap().len(), MAX_ROWS - 1);
|
||||
assert_eq!(p["series"].as_array().unwrap().len(), MAX_COLS - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_switch_and_moves() {
|
||||
assert_eq!(transpose(&g(&[&["", "a", "b"], &["Q1", "1", "2"]])), g(&[&["", "Q1"], &["a", "1"], &["b", "2"]]));
|
||||
|
||||
@@ -175,6 +175,13 @@ fn has_warnings(r: &Value) -> bool {
|
||||
r["warnings"].as_array().is_some_and(|w| !w.is_empty())
|
||||
}
|
||||
|
||||
/// The preview page's name from the document's title `stem`: one file name in the preview folder.
|
||||
/// `file_stem` gives back a title it can't take a stem of ("../.."), so one with a separator or of
|
||||
/// dots only is "Untitled".
|
||||
pub(super) fn preview_name(stem: &str) -> &str {
|
||||
if stem.trim().is_empty() || stem.contains(['/', '\\']) || stem.chars().all(|c| c == '.') { "Untitled" } else { stem }
|
||||
}
|
||||
|
||||
/// `file.saveForWeb.browser`: the HTML page and its images in a new temporary folder, opened in the
|
||||
/// default browser (desktop). When the export has warnings, the status bar shows the first in
|
||||
/// place of "Opened …".
|
||||
@@ -182,9 +189,10 @@ pub(crate) fn browser_preview(app: &mut VectorcraftApp, p: &Value) -> Result<Val
|
||||
if io::is_web(app) {
|
||||
return Err("Preview in Browser needs the desktop app (the web downloads files instead)".into());
|
||||
}
|
||||
app.refuse_automation("file.saveForWeb.browser")?;
|
||||
let stem = app.session.active().map(|st| vectorcraft_engine::cmd::fileio::file_stem(&st.doc.title)).ok_or("no document")?;
|
||||
let dir = temp_folder()?;
|
||||
let path = format!("{dir}/{}.html", if stem.trim().is_empty() { "Untitled" } else { stem.as_str() });
|
||||
let path = format!("{dir}/{}.html", preview_name(&stem));
|
||||
let mut q = if p.is_object() { p.clone() } else { json!({}) };
|
||||
if let Some(o) = q.as_object_mut() {
|
||||
o.insert("path".into(), json!(path));
|
||||
|
||||
@@ -317,6 +317,21 @@ fn the_web_downloads_and_has_no_browser_preview() {
|
||||
assert!(app.run("file.saveForWeb.browser", json!({})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_in_browser_stays_in_its_folder_and_is_the_persons_to_open() {
|
||||
for (title, name) in
|
||||
[("Poster", "Poster"), ("../..", "Untitled"), ("..", "Untitled"), ("a/..", "Untitled"), ("..\\x", "Untitled"), (" ", "Untitled")]
|
||||
{
|
||||
assert_eq!(save_for_web::preview_name(title), name, "{title}");
|
||||
}
|
||||
let dir = vectorcraft_testkit::temp_dir("sfw-browser-automation");
|
||||
let (mut app, log) = app(&dir, false);
|
||||
app.ui.answering_control = true;
|
||||
assert!(app.run("file.saveForWeb.browser", json!({"format": "jpg"})).unwrap_err().contains("opens another app"));
|
||||
app.ui.answering_control = false;
|
||||
assert!(!log.borrow().iter().any(|l| l.starts_with("open ")), "{:?}", log.borrow());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_in_browser_opens_the_page() {
|
||||
let dir = vectorcraft_testkit::temp_dir("sfw-browser");
|
||||
|
||||
@@ -370,7 +370,8 @@ pub fn save_pdf(app: &mut VectorcraftApp, mode: SaveMode, mut params: Value) ->
|
||||
|
||||
/// Open a written file in the system viewer (not on the web, which downloads it).
|
||||
pub(super) fn view_file(app: &mut VectorcraftApp, path: &str) {
|
||||
if !is_web(app) {
|
||||
// The viewer is another app: an export automation asked for is written but not opened.
|
||||
if !is_web(app) && app.refuse_automation("viewAfterSaving").is_ok() {
|
||||
app.open_url(&file_url(path));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user