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:
aiongg
2026-10-24 06:17:19 +00:00
parent 5880086522
commit f52c911959
2 changed files with 65 additions and 7 deletions
+2 -2
View File
@@ -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 {
+63 -5
View File
@@ -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() {