M6: deleting a radio button turns the group off when no button left shows its choice

CodeRabbit on #4631: a twin with the same on-state name that is currently
off does not keep the choice visible, so compare the remaining buttons'
appearance state (/AS), not their on-state name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LymhVaGZCWwB9HNEbLzeMM
This commit is contained in:
Claude
2026-10-25 10:26:51 +00:00
parent e3662af3a5
commit f75af23d4e
2 changed files with 25 additions and 1 deletions
+3 -1
View File
@@ -1593,8 +1593,10 @@ pub fn delete_widget(doc: &mut Document, name: &str, widget: usize) -> Result<()
return delete_found_field(doc, f);
}
let per_widget_opt = matches!(f.kind, FieldKind::CheckBox | FieldKind::Radio) && f.options.len() == f.widgets.len();
// Turn the group off when no button left *shows* the deleted one's state: a twin with the same
// on-state name that is currently off does not keep the choice visible.
let on_only_here = w.on_state.as_deref().is_some_and(|on| {
f.value.iter().any(|v| v == on) && !f.widgets.iter().enumerate().any(|(i, o)| i != widget && o.on_state.as_deref() == Some(on))
f.value.iter().any(|v| v == on) && !f.widgets.iter().enumerate().any(|(i, o)| i != widget && o.state.as_deref() == Some(on))
});
drop_from_pages(doc, &std::iter::once(w.obj).collect())?;
detach(doc, w.obj)?;
+22
View File
@@ -4414,6 +4414,28 @@ fn deleting_the_chosen_radio_button_turns_the_group_off() {
assert_eq!((size.export_of(0), size.export_of(1)), (Some("medium"), Some("large")));
}
#[test]
fn deleting_the_chosen_radio_button_turns_the_group_off_when_a_twin_is_not_shown_on() {
// Medium shares Small's on-state name but shows Off: once Small is gone, no button shows
// the group's /V, so the group must not keep reporting it.
let mut doc = radio_group_form();
let twin = ObjRef::new(7, 0);
let blank = Object::Ref(ObjRef::new(12, 0));
let mut n = Dict::new();
n.set(b"S".to_vec(), blank.clone());
n.set(b"Off".to_vec(), blank);
let mut ap = Dict::new();
ap.set(b"N".to_vec(), Object::Dict(n));
let mut w = doc.get(twin).as_dict().cloned().unwrap();
w.set(b"AP".to_vec(), Object::Dict(ap));
doc.set(twin, Object::Dict(w));
delete_widget(&mut doc, "size", 0).unwrap();
let all = fields(&reopen(&doc));
let size = field(&all, "size");
assert_eq!(size.widgets.iter().map(|w| w.obj.num).collect::<Vec<_>>(), [7, 8]);
assert!(size.value.is_empty(), "no button shows S any more: {:?}", size.value);
}
#[test]
fn deleting_one_copy_of_a_field_keeps_the_field_and_its_value() {
let mut doc = radio_group_form();