fret_ui_kit/primitives/
field_state.rs1use crate::primitives::control_registry::ControlId;
14use fret_ui::{ElementContext, UiHost};
15
16#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
17pub struct FieldState {
18 pub invalid: bool,
19 pub disabled: bool,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct FieldControlAssociation {
24 pub control_id: ControlId,
25}
26
27pub fn inherited_field_state<H: UiHost>(cx: &ElementContext<'_, H>) -> Option<FieldState> {
28 cx.provided::<FieldState>().copied()
29}
30
31pub fn use_field_state_in_scope<H: UiHost>(
32 cx: &ElementContext<'_, H>,
33 local: Option<FieldState>,
34) -> FieldState {
35 local.or(inherited_field_state(cx)).unwrap_or_default()
36}
37
38pub fn inherited_field_control_id<H: UiHost>(cx: &ElementContext<'_, H>) -> Option<ControlId> {
39 cx.provided::<FieldControlAssociation>()
40 .map(|assoc| assoc.control_id.clone())
41}
42
43pub fn use_field_control_id_in_scope<H: UiHost>(
44 cx: &ElementContext<'_, H>,
45 local: Option<ControlId>,
46) -> Option<ControlId> {
47 local.or_else(|| inherited_field_control_id(cx))
48}
49
50#[track_caller]
51pub fn with_field_state_provider<H: UiHost, R>(
52 cx: &mut ElementContext<'_, H>,
53 state: FieldState,
54 f: impl FnOnce(&mut ElementContext<'_, H>) -> R,
55) -> R {
56 cx.provide(state, f)
57}
58
59#[track_caller]
60pub fn with_field_control_association_provider<H: UiHost, R>(
61 cx: &mut ElementContext<'_, H>,
62 control_id: Option<ControlId>,
63 f: impl FnOnce(&mut ElementContext<'_, H>) -> R,
64) -> R {
65 if let Some(control_id) = control_id {
66 cx.provide(FieldControlAssociation { control_id }, f)
67 } else {
68 f(cx)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 use fret_app::App;
77 use fret_core::{AppWindowId, Point, Px, Rect, Size};
78
79 fn bounds() -> Rect {
80 Rect::new(Point::new(Px(0.0), Px(0.0)), Size::new(Px(10.0), Px(10.0)))
81 }
82
83 #[test]
84 fn field_state_provider_inherits_and_restores() {
85 let window = AppWindowId::default();
86 let mut app = App::new();
87
88 fret_ui::elements::with_element_cx(&mut app, window, bounds(), "test", |cx| {
89 assert_eq!(inherited_field_state(cx), None);
90 assert_eq!(use_field_state_in_scope(cx, None), FieldState::default());
91
92 with_field_state_provider(
93 cx,
94 FieldState {
95 invalid: true,
96 disabled: false,
97 },
98 |cx| {
99 assert_eq!(
100 use_field_state_in_scope(cx, None),
101 FieldState {
102 invalid: true,
103 disabled: false,
104 }
105 );
106 cx.scope(|cx| {
107 assert_eq!(
108 use_field_state_in_scope(cx, None),
109 FieldState {
110 invalid: true,
111 disabled: false,
112 }
113 );
114 });
115 },
116 );
117
118 assert_eq!(use_field_state_in_scope(cx, None), FieldState::default());
119 });
120 }
121
122 #[test]
123 fn field_control_association_provider_inherits_and_restores() {
124 let window = AppWindowId::default();
125 let mut app = App::new();
126 let control_id = ControlId::from("field.control");
127
128 fret_ui::elements::with_element_cx(&mut app, window, bounds(), "test", |cx| {
129 assert_eq!(inherited_field_control_id(cx), None);
130 assert_eq!(use_field_control_id_in_scope(cx, None), None);
131
132 with_field_control_association_provider(cx, Some(control_id.clone()), |cx| {
133 assert_eq!(inherited_field_control_id(cx), Some(control_id.clone()));
134 assert_eq!(
135 use_field_control_id_in_scope(cx, None),
136 Some(control_id.clone())
137 );
138 cx.scope(|cx| {
139 assert_eq!(
140 use_field_control_id_in_scope(cx, None),
141 Some(control_id.clone())
142 );
143 });
144 });
145
146 assert_eq!(use_field_control_id_in_scope(cx, None), None);
147 });
148 }
149}