1use std::{any::TypeId, sync::Arc};
2
3use crate::{
4 DispatchPhase, FocusEvent, FocusHandle, Interactive, KeyDownEvent, KeyUpEvent, StyleRefinement,
5 ViewContext,
6};
7
8pub trait Focus: Interactive {
9 fn set_focus_style(&mut self, style: StyleRefinement);
10 fn set_focus_in_style(&mut self, style: StyleRefinement);
11 fn set_in_focus_style(&mut self, style: StyleRefinement);
12 fn handle(&self) -> &FocusHandle;
13
14 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
15 where
16 Self: Sized,
17 {
18 self.set_focus_style(f(StyleRefinement::default()));
19 self
20 }
21
22 fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
23 where
24 Self: Sized,
25 {
26 self.set_focus_in_style(f(StyleRefinement::default()));
27 self
28 }
29
30 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
31 where
32 Self: Sized,
33 {
34 self.set_in_focus_style(f(StyleRefinement::default()));
35 self
36 }
37
38 fn on_focus(
39 mut self,
40 listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
41 + Send
42 + Sync
43 + 'static,
44 ) -> Self
45 where
46 Self: Sized,
47 {
48 let handle = self.handle().clone();
49 self.listeners()
50 .focus
51 .push(Arc::new(move |view, event, cx| {
52 if event.focused.as_ref() == Some(&handle) {
53 listener(view, event, cx)
54 }
55 }));
56 self
57 }
58
59 fn on_blur(
60 mut self,
61 listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
62 + Send
63 + Sync
64 + 'static,
65 ) -> Self
66 where
67 Self: Sized,
68 {
69 let handle = self.handle().clone();
70 self.listeners()
71 .focus
72 .push(Arc::new(move |view, event, cx| {
73 if event.blurred.as_ref() == Some(&handle) {
74 listener(view, event, cx)
75 }
76 }));
77 self
78 }
79
80 fn on_focus_in(
81 mut self,
82 listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
83 + Send
84 + Sync
85 + 'static,
86 ) -> Self
87 where
88 Self: Sized,
89 {
90 let handle = self.handle().clone();
91 self.listeners()
92 .focus
93 .push(Arc::new(move |view, event, cx| {
94 let descendant_blurred = event
95 .blurred
96 .as_ref()
97 .map_or(false, |blurred| handle.contains(blurred, cx));
98 let descendant_focused = event
99 .focused
100 .as_ref()
101 .map_or(false, |focused| handle.contains(focused, cx));
102
103 if !descendant_blurred && descendant_focused {
104 listener(view, event, cx)
105 }
106 }));
107 self
108 }
109
110 fn on_focus_out(
111 mut self,
112 listener: impl Fn(&mut Self::ViewState, &FocusEvent, &mut ViewContext<Self::ViewState>)
113 + Send
114 + Sync
115 + 'static,
116 ) -> Self
117 where
118 Self: Sized,
119 {
120 let handle = self.handle().clone();
121 self.listeners()
122 .focus
123 .push(Arc::new(move |view, event, cx| {
124 let descendant_blurred = event
125 .blurred
126 .as_ref()
127 .map_or(false, |blurred| handle.contains(blurred, cx));
128 let descendant_focused = event
129 .focused
130 .as_ref()
131 .map_or(false, |focused| handle.contains(focused, cx));
132 if descendant_blurred && !descendant_focused {
133 listener(view, event, cx)
134 }
135 }));
136 self
137 }
138
139 fn on_key_down(
140 mut self,
141 listener: impl Fn(
142 &mut Self::ViewState,
143 &KeyDownEvent,
144 DispatchPhase,
145 &mut ViewContext<Self::ViewState>,
146 ) + Send
147 + Sync
148 + 'static,
149 ) -> Self
150 where
151 Self: Sized,
152 {
153 self.listeners().key.push((
154 TypeId::of::<KeyDownEvent>(),
155 Arc::new(move |view, event, phase, cx| {
156 let event = event.downcast_ref().unwrap();
157 listener(view, event, phase, cx)
158 }),
159 ));
160 self
161 }
162
163 fn on_key_up(
164 mut self,
165 listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
166 + Send
167 + Sync
168 + 'static,
169 ) -> Self
170 where
171 Self: Sized,
172 {
173 self.listeners().key.push((
174 TypeId::of::<KeyUpEvent>(),
175 Arc::new(move |view, event, phase, cx| {
176 let event = event.downcast_ref().unwrap();
177 listener(view, event, phase, cx)
178 }),
179 ));
180 self
181 }
182}