Skip to main content

fui/controls/
form.rs

1use super::Button;
2use crate::event;
3use crate::ffi::{FlexDirection, KeyEventType, SemanticRole};
4use crate::node::{flex_box, FlexBox, Node, NodeRef, ThemeBindable};
5use std::any::Any;
6use std::cell::{Cell, RefCell};
7use std::rc::Rc;
8
9#[derive(Clone)]
10pub struct Form {
11    shared: Rc<FormShared>,
12}
13
14struct FormShared {
15    root: FlexBox,
16    default_button: RefCell<Option<Button>>,
17    cancel_button: RefCell<Option<Button>>,
18    key_filter_token: Cell<u32>,
19    armed_key: RefCell<Option<String>>,
20    armed_button: RefCell<Option<Button>>,
21}
22
23impl Default for Form {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl Form {
30    pub fn new() -> Self {
31        let root = flex_box();
32        root.flex_direction(FlexDirection::Column)
33            .semantic_role(SemanticRole::Form);
34        let form = Self {
35            shared: Rc::new(FormShared {
36                root,
37                default_button: RefCell::new(None),
38                cancel_button: RefCell::new(None),
39                key_filter_token: Cell::new(0),
40                armed_key: RefCell::new(None),
41                armed_button: RefCell::new(None),
42            }),
43        };
44        form.bind_events();
45        form
46    }
47
48    pub fn default_btn(&self, button: &Button) -> &Self {
49        self.shared
50            .default_button
51            .borrow_mut()
52            .replace(button.clone());
53        self
54    }
55
56    pub fn cancel_btn(&self, button: &Button) -> &Self {
57        self.shared
58            .cancel_button
59            .borrow_mut()
60            .replace(button.clone());
61        self
62    }
63
64    pub fn activate(&self) {
65        if self.shared.key_filter_token.get() != 0
66            || (self.shared.default_button.borrow().is_none()
67                && self.shared.cancel_button.borrow().is_none())
68        {
69            return;
70        }
71        let weak = Rc::downgrade(&self.shared);
72        let token = event::push_key_filter(move |event_type, key, modifiers| {
73            let Some(shared) = weak.upgrade() else {
74                return false;
75            };
76            shared.handle_global_key_event(event_type, key, modifiers)
77        });
78        self.shared.key_filter_token.set(token);
79    }
80
81    pub fn deactivate(&self) {
82        self.shared.deactivate();
83    }
84
85    fn bind_events(&self) {
86        let weak = Rc::downgrade(&self.shared);
87        self.shared.root.on_key_down(move |event| {
88            let Some(shared) = weak.upgrade() else {
89                return;
90            };
91            if shared.handle_global_key_event(
92                KeyEventType::Down,
93                event.key.as_str(),
94                event.modifiers,
95            ) {
96                event.handled = true;
97            }
98        });
99        let weak = Rc::downgrade(&self.shared);
100        self.shared.root.on_key_up(move |event| {
101            let Some(shared) = weak.upgrade() else {
102                return;
103            };
104            if shared.handle_global_key_event(KeyEventType::Up, event.key.as_str(), event.modifiers)
105            {
106                event.handled = true;
107            }
108        });
109    }
110}
111
112impl Drop for Form {
113    fn drop(&mut self) {
114        if Rc::strong_count(&self.shared) == 1 {
115            self.shared.deactivate();
116        }
117    }
118}
119
120impl FormShared {
121    fn deactivate(&self) {
122        self.cancel_armed_button();
123        let token = self.key_filter_token.replace(0);
124        if token != 0 {
125            event::remove_key_filter(token);
126        }
127    }
128
129    fn handle_global_key_event(&self, event_type: KeyEventType, key: &str, modifiers: u32) -> bool {
130        if key == "Enter" && self.should_defer_enter_to_focused_button(modifiers) {
131            return false;
132        }
133        match event_type {
134            KeyEventType::Down => self.handle_key_down(key),
135            KeyEventType::Up => self.handle_key_up(key),
136        }
137    }
138
139    fn handle_key_down(&self, key: &str) -> bool {
140        let Some(button) = self.resolve_button_for_key(key) else {
141            return false;
142        };
143        if let Some(armed_key) = self.armed_key.borrow().as_ref() {
144            return armed_key == key;
145        }
146        self.armed_key.borrow_mut().replace(key.to_string());
147        self.armed_button.borrow_mut().replace(button.clone());
148        button.begin_press();
149        true
150    }
151
152    fn handle_key_up(&self, key: &str) -> bool {
153        let armed_matches = self
154            .armed_key
155            .borrow()
156            .as_ref()
157            .is_some_and(|armed_key| armed_key == key);
158        if !armed_matches {
159            return false;
160        }
161        let button = self.armed_button.borrow_mut().take();
162        self.armed_key.borrow_mut().take();
163        if let Some(button) = button {
164            button.end_press(true);
165        }
166        true
167    }
168
169    fn cancel_armed_button(&self) {
170        let button = self.armed_button.borrow_mut().take();
171        self.armed_key.borrow_mut().take();
172        if let Some(button) = button {
173            button.cancel_press();
174        }
175    }
176
177    fn resolve_button_for_key(&self, key: &str) -> Option<Button> {
178        match key {
179            "Enter" => self.default_button.borrow().clone(),
180            "Escape" => self.cancel_button.borrow().clone(),
181            _ => None,
182        }
183    }
184
185    fn should_defer_enter_to_focused_button(&self, modifiers: u32) -> bool {
186        if modifiers != 0 {
187            return false;
188        }
189        event::focused_node_is_enabled_button()
190    }
191}
192
193impl Node for Form {
194    fn retained_node_ref(&self) -> NodeRef {
195        self.shared.root.retained_node_ref()
196    }
197
198    fn retained_owner_attachment(&self) -> Option<Rc<dyn Any>> {
199        Some(self.shared.clone())
200    }
201
202    fn build_self(&self) {
203        self.shared.root.build_self();
204    }
205
206    fn dispose(&self) {
207        self.deactivate();
208        self.shared.root.dispose();
209    }
210}
211
212impl crate::node::HasFlexBoxRoot for Form {
213    fn flex_box_root(&self) -> &FlexBox {
214        &self.shared.root
215    }
216}
217
218impl ThemeBindable for Form {
219    fn theme_binding_node(&self) -> NodeRef {
220        self.shared.root.retained_node_ref()
221    }
222
223    fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
224        let shared = Rc::downgrade(&self.shared);
225        Box::new(move || {
226            Some(Self {
227                shared: shared.upgrade()?,
228            })
229        })
230    }
231}