input.rs

  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().clone(),
 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}