mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-25 19:17:06 -06:00
Integration: #927 Header & Footer dialog sends dateFormat only when picked; set_date doc comment back above set_date
Without it, Apply rewrote every slide's existing date field to datetime1.
This commit is contained in:
@@ -961,8 +961,6 @@ fn header_footer(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
ok()
|
||||
}
|
||||
|
||||
/// Point a slide's date placeholder at a fixed date (its own plain text) or, for an empty
|
||||
/// `fixed`, at an automatic date field (one it already has keeps its format).
|
||||
/// The date field type `format` names, `datetime1` when it is empty or unknown.
|
||||
fn date_kind(format: &str) -> &str {
|
||||
if deckcraft_text::datetime::DATE_KINDS.contains(&format) { format } else { "datetime1" }
|
||||
@@ -974,6 +972,8 @@ fn date_now(format: &str) -> String {
|
||||
deckcraft_text::datetime::DateTime::now_local().and_then(|now| deckcraft_text::datetime::field_text(kind, None, now)).unwrap_or_else(today)
|
||||
}
|
||||
|
||||
/// Point a slide's date placeholder at a fixed date (its own plain text) or, for an empty
|
||||
/// `fixed`, at an automatic date field (one it already has keeps its format).
|
||||
fn set_date(sh: &mut Shape, fixed: &str, format: &str) {
|
||||
let runs = || sh.text.iter().flat_map(|t| &t.paragraphs).flat_map(|p| &p.runs);
|
||||
let field_now = runs().find_map(|r| match &r.kind {
|
||||
|
||||
@@ -1126,11 +1126,20 @@ fn body(app: &mut SlideApp, ui: &mut Ui, d: &mut Dialog) -> bool {
|
||||
if apply || apply_all {
|
||||
let dt = d.get("dateText", "");
|
||||
let ft = d.get("footerText", "");
|
||||
run(
|
||||
app,
|
||||
"design.headerFooter",
|
||||
json!({"date": date, "dateText": dt, "dateFormat": d.get("dateFormat", &fmt_now), "slideNumber": num_on, "footer": foot, "footerText": ft, "hideOnTitle": hide, "all": apply_all}),
|
||||
);
|
||||
let mut params = serde_json::Map::new();
|
||||
params.insert("date".into(), json!(date));
|
||||
params.insert("dateText".into(), json!(dt));
|
||||
// Only a format the user picked: sending the default would rewrite every slide's
|
||||
// existing date field (an imported datetime4, say) to datetime1.
|
||||
if d.fields.contains_key("dateFormat") {
|
||||
params.insert("dateFormat".into(), json!(d.get("dateFormat", &fmt_now)));
|
||||
}
|
||||
params.insert("slideNumber".into(), json!(num_on));
|
||||
params.insert("footer".into(), json!(foot));
|
||||
params.insert("footerText".into(), json!(ft));
|
||||
params.insert("hideOnTitle".into(), json!(hide));
|
||||
params.insert("all".into(), json!(apply_all));
|
||||
run(app, "design.headerFooter", serde_json::Value::Object(params));
|
||||
}
|
||||
apply || apply_all || d.fields.contains_key("cancel")
|
||||
}
|
||||
@@ -3410,6 +3419,55 @@ mod tests {
|
||||
h
|
||||
}
|
||||
|
||||
/// Header & Footer ▸ Apply without picking a format leaves a slide's existing date field in
|
||||
/// its own format instead of rewriting it to the default one.
|
||||
#[test]
|
||||
fn header_footer_apply_keeps_an_existing_date_format() {
|
||||
use std::sync::Arc;
|
||||
|
||||
use egui::vec2;
|
||||
use egui_kittest::kittest::Queryable as _;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::SlideApp;
|
||||
let mut session = deckcraft_engine::Session::with_new();
|
||||
session.execute("design.headerFooter", &json!({"date": true, "dateText": "", "dateFormat": "datetime4", "all": true})).unwrap();
|
||||
// A file whose document-level format is unset, as an imported deck's is.
|
||||
Arc::make_mut(&mut session.doc_mut().unwrap().doc).header_footer.date_format = String::new();
|
||||
let date_fields = |app: &SlideApp| -> Vec<String> {
|
||||
app.session
|
||||
.doc()
|
||||
.unwrap()
|
||||
.doc
|
||||
.slides
|
||||
.iter()
|
||||
.flat_map(|sl| sl.shapes.iter())
|
||||
.filter(|sh| sh.ph_type() == Some(deckcraft_model::PhType::Date))
|
||||
.flat_map(|sh| sh.text.iter())
|
||||
.flat_map(|t| t.paragraphs.iter())
|
||||
.flat_map(|p| p.runs.iter())
|
||||
.filter_map(|r| match &r.kind {
|
||||
deckcraft_model::text::RunKind::Field { field } => Some(field.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let mut harness = egui_kittest::Harness::builder().with_size(vec2(1200.0, 900.0)).build_ui_state(
|
||||
|ui, app: &mut SlideApp| {
|
||||
app.logic(ui.ctx());
|
||||
app.ui(ui);
|
||||
},
|
||||
SlideApp::new(session, crate::Services::default()),
|
||||
);
|
||||
harness.run_steps(2);
|
||||
assert_eq!(date_fields(harness.state()), vec!["datetime4".to_string()]);
|
||||
harness.state_mut().run("app.dialog", json!({"id": "headerFooter"})).unwrap();
|
||||
harness.run_steps(2);
|
||||
harness.get_by_label("Apply").click_accesskit();
|
||||
harness.run_steps(3);
|
||||
assert_eq!(date_fields(harness.state()), vec!["datetime4".to_string()], "the dialog left the field's format alone");
|
||||
}
|
||||
|
||||
/// The Export… dialog (Animated GIF) shows the GIF options and exports through `file.export` (#171).
|
||||
#[test]
|
||||
fn export_dialog_writes_an_animated_gif() {
|
||||
|
||||
Reference in New Issue
Block a user