1use crate::{AsyncWindowContext, Bounds, Pixels, PlatformInputHandler, View, ViewContext};
2use std::ops::Range;
3
4/// Implement this trait to allow views to handle textual input when implementing an editor, field, etc.
5///
6/// Once your view `V` implements this trait, you can use it to construct an [ElementInputHandler<V>].
7/// This input handler can then be assigned during paint by calling [WindowContext::handle_input].
8pub trait InputHandler: 'static + Sized {
9 fn text_for_range(&mut self, range: Range<usize>, cx: &mut ViewContext<Self>)
10 -> Option<String>;
11 fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
12 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
13 fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
14 fn replace_text_in_range(
15 &mut self,
16 range: Option<Range<usize>>,
17 text: &str,
18 cx: &mut ViewContext<Self>,
19 );
20 fn replace_and_mark_text_in_range(
21 &mut self,
22 range: Option<Range<usize>>,
23 new_text: &str,
24 new_selected_range: Option<Range<usize>>,
25 cx: &mut ViewContext<Self>,
26 );
27 fn bounds_for_range(
28 &mut self,
29 range_utf16: Range<usize>,
30 element_bounds: Bounds<Pixels>,
31 cx: &mut ViewContext<Self>,
32 ) -> Option<Bounds<Pixels>>;
33}
34
35/// The canonical implementation of `PlatformInputHandler`. Call `WindowContext::handle_input`
36/// with an instance during your element's paint.
37pub struct ElementInputHandler<V> {
38 view: View<V>,
39 element_bounds: Bounds<Pixels>,
40 cx: AsyncWindowContext,
41}
42
43impl<V: 'static> ElementInputHandler<V> {
44 /// Used in [Element::paint] with the element's bounds and a view context for its
45 /// containing view.
46 pub fn new(element_bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) -> Self {
47 ElementInputHandler {
48 view: cx.view(),
49 element_bounds,
50 cx: cx.to_async(),
51 }
52 }
53}
54
55impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
56 fn selected_text_range(&mut self) -> Option<Range<usize>> {
57 self.view
58 .update(&mut self.cx, |view, cx| view.selected_text_range(cx))
59 .ok()
60 .flatten()
61 }
62
63 fn marked_text_range(&mut self) -> Option<Range<usize>> {
64 self.view
65 .update(&mut self.cx, |view, cx| view.marked_text_range(cx))
66 .ok()
67 .flatten()
68 }
69
70 fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
71 self.view
72 .update(&mut self.cx, |view, cx| {
73 view.text_for_range(range_utf16, cx)
74 })
75 .ok()
76 .flatten()
77 }
78
79 fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
80 self.view
81 .update(&mut self.cx, |view, cx| {
82 view.replace_text_in_range(replacement_range, text, cx)
83 })
84 .ok();
85 }
86
87 fn replace_and_mark_text_in_range(
88 &mut self,
89 range_utf16: Option<Range<usize>>,
90 new_text: &str,
91 new_selected_range: Option<Range<usize>>,
92 ) {
93 self.view
94 .update(&mut self.cx, |view, cx| {
95 view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
96 })
97 .ok();
98 }
99
100 fn unmark_text(&mut self) {
101 self.view
102 .update(&mut self.cx, |view, cx| view.unmark_text(cx))
103 .ok();
104 }
105
106 fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
107 self.view
108 .update(&mut self.cx, |view, cx| {
109 view.bounds_for_range(range_utf16, self.element_bounds, cx)
110 })
111 .ok()
112 .flatten()
113 }
114}