From 9fb07e4ac7ccc4065867f354b8d6d05975c90160 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 17:39:30 +0000 Subject: [PATCH] M6: deleting the default radio button clears the group's default too CodeRabbit on #4631: with /V already Off, a /DV naming the deleted button's state was kept, so Reset would put back a state no button has. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LymhVaGZCWwB9HNEbLzeMM --- crates/forms/src/author.rs | 9 +++++++++ crates/forms/src/tests.rs | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/crates/forms/src/author.rs b/crates/forms/src/author.rs index eccd9a34e2..8165f56afc 100644 --- a/crates/forms/src/author.rs +++ b/crates/forms/src/author.rs @@ -1616,6 +1616,15 @@ pub fn delete_widget(doc: &mut Document, name: &str, widget: usize) -> Result<() d.set(b"V".to_vec(), Object::name("Off")); changed = true; } + // The default (`/DV`, what Reset puts back) can't name a state no button has any more. + let dv_only_here = w.on_state.as_deref().is_some_and(|on| { + d.get(b"DV").map(|v| doc.resolve(v)).and_then(|v| v.as_name().map(<[u8]>::to_vec)).is_some_and(|dv| dv == on.as_bytes()) + && !f.widgets.iter().enumerate().any(|(i, o)| i != widget && o.on_state.as_deref() == Some(on)) + }); + if dv_only_here { + d.set(b"DV".to_vec(), Object::name("Off")); + changed = true; + } if changed { doc.set(f.obj, Object::Dict(d)); } diff --git a/crates/forms/src/tests.rs b/crates/forms/src/tests.rs index f52be5306d..b85aeb726c 100644 --- a/crates/forms/src/tests.rs +++ b/crates/forms/src/tests.rs @@ -4654,6 +4654,28 @@ fn deleting_the_chosen_radio_button_turns_the_group_off_when_a_twin_is_not_shown assert!(size.value.is_empty(), "no button shows S any more: {:?}", size.value); } +#[test] +fn deleting_the_default_radio_button_clears_the_default_too() { + // The group is off, but Reset would put back Small, which no button has once it is gone. + let mut doc = radio_group_form(); + let size = ObjRef::new(5, 0); + let mut d = doc.get(size).as_dict().cloned().unwrap(); + d.set(b"V".to_vec(), Object::name("Off")); + d.set(b"DV".to_vec(), Object::name("S")); + doc.set(size, Object::Dict(d)); + delete_widget(&mut doc, "size", 0).unwrap(); + let doc = reopen(&doc); + let dv = doc.get(size).as_dict().and_then(|d| d.get(b"DV").cloned()); + assert_eq!(dv, Some(Object::name("Off"))); + // A default another button still has stays. + let mut doc = radio_group_form(); + let mut d = doc.get(size).as_dict().cloned().unwrap(); + d.set(b"DV".to_vec(), Object::name("S")); + doc.set(size, Object::Dict(d)); + delete_widget(&mut doc, "size", 1).unwrap(); + assert_eq!(doc.get(size).as_dict().and_then(|d| d.get(b"DV").cloned()), Some(Object::name("S"))); +} + #[test] fn deleting_one_copy_of_a_field_keeps_the_field_and_its_value() { let mut doc = radio_group_form();