Skip to main content

pdfluent_forms/
choice.rs

1//! Dropdown and listbox implementation (B.4).
2
3use crate::flags::FieldFlags;
4use crate::tree::*;
5
6/// Choice field sub-kind.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ChoiceKind {
9    ComboBox,
10    EditableCombo,
11    ListBox,
12    MultiSelectListBox,
13}
14
15/// Determine choice sub-kind from flags.
16pub fn choice_kind(flags: FieldFlags) -> ChoiceKind {
17    if flags.combo() {
18        if flags.edit() {
19            ChoiceKind::EditableCombo
20        } else {
21            ChoiceKind::ComboBox
22        }
23    } else if flags.multi_select() {
24        ChoiceKind::MultiSelectListBox
25    } else {
26        ChoiceKind::ListBox
27    }
28}
29
30/// Get the currently selected value(s).
31pub fn get_selection(tree: &FieldTree, id: FieldId) -> Vec<String> {
32    match tree.effective_value(id) {
33        Some(FieldValue::Text(s)) => vec![s.clone()],
34        Some(FieldValue::StringArray(arr)) => arr.clone(),
35        None => vec![],
36    }
37}
38
39/// Get the list of available options.
40pub fn get_options(tree: &FieldTree, id: FieldId) -> &[ChoiceOption] {
41    &tree.get(id).options
42}
43
44/// Set the selection for a single-select choice field.
45/// For non-editable combos, value must match an option. Returns `false` if read-only or invalid.
46pub fn set_selection(tree: &mut FieldTree, id: FieldId, value: &str) -> bool {
47    let flags = tree.effective_flags(id);
48    if flags.read_only() {
49        return false;
50    }
51    if choice_kind(flags) == ChoiceKind::ComboBox
52        && !tree
53            .get(id)
54            .options
55            .iter()
56            .any(|o| o.export == value || o.display == value)
57    {
58        return false;
59    }
60    tree.get_mut(id).value = Some(FieldValue::Text(value.to_string()));
61    true
62}
63
64/// Set multiple selections for a multi-select list box. Returns `false` if read-only or not multi-select.
65pub fn set_multi_selection(tree: &mut FieldTree, id: FieldId, values: Vec<String>) -> bool {
66    let flags = tree.effective_flags(id);
67    if flags.read_only() || !flags.multi_select() {
68        return false;
69    }
70    tree.get_mut(id).value = Some(FieldValue::StringArray(values));
71    true
72}
73
74/// Get the index of the first selected option, if any.
75pub fn selected_index(tree: &FieldTree, id: FieldId) -> Option<usize> {
76    let first = get_selection(tree, id).into_iter().next()?;
77    tree.get(id)
78        .options
79        .iter()
80        .position(|o| o.export == first || o.display == first)
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    fn make_choice_tree() -> (FieldTree, FieldId) {
87        let mut tree = FieldTree::new();
88        let id = tree.alloc(FieldNode {
89            partial_name: "dd".into(),
90            alternate_name: None,
91            mapping_name: None,
92            field_type: Some(FieldType::Choice),
93            flags: FieldFlags::from_bits(1 << 17),
94            value: None,
95            default_value: None,
96            default_appearance: None,
97            quadding: None,
98            max_len: None,
99            options: vec![
100                ChoiceOption {
101                    export: "a".into(),
102                    display: "Alpha".into(),
103                },
104                ChoiceOption {
105                    export: "b".into(),
106                    display: "Beta".into(),
107                },
108                ChoiceOption {
109                    export: "c".into(),
110                    display: "Gamma".into(),
111                },
112            ],
113            top_index: None,
114            rect: Some([0.0, 0.0, 150.0, 20.0]),
115            appearance_state: None,
116            page_index: None,
117            parent: None,
118            children: vec![],
119            object_id: None,
120            has_actions: false,
121            mk: None,
122            border_style: None,
123        });
124        (tree, id)
125    }
126    #[test]
127    fn kind_combo() {
128        assert_eq!(
129            choice_kind(FieldFlags::from_bits(1 << 17)),
130            ChoiceKind::ComboBox
131        );
132    }
133    #[test]
134    fn kind_editable() {
135        assert_eq!(
136            choice_kind(FieldFlags::from_bits((1 << 17) | (1 << 18))),
137            ChoiceKind::EditableCombo
138        );
139    }
140    #[test]
141    fn kind_listbox() {
142        assert_eq!(choice_kind(FieldFlags::empty()), ChoiceKind::ListBox);
143    }
144    #[test]
145    fn kind_multi() {
146        assert_eq!(
147            choice_kind(FieldFlags::from_bits(1 << 21)),
148            ChoiceKind::MultiSelectListBox
149        );
150    }
151    #[test]
152    fn set_valid() {
153        let (mut tree, id) = make_choice_tree();
154        assert!(set_selection(&mut tree, id, "a"));
155        assert_eq!(get_selection(&tree, id), vec!["a"]);
156    }
157    #[test]
158    fn set_invalid() {
159        let (mut tree, id) = make_choice_tree();
160        assert!(!set_selection(&mut tree, id, "nope"));
161    }
162    #[test]
163    fn sel_index() {
164        let (mut tree, id) = make_choice_tree();
165        set_selection(&mut tree, id, "b");
166        assert_eq!(selected_index(&tree, id), Some(1));
167    }
168}