Skip to main content

ferroui_base/input/text_input/
text_input_method_client.rs

1use crate::reactive::{Disposable, IDisposable};
2use crate::utilities::HandlerList;
3use crate::{Rect, Ref, Visual};
4use std::rc::Rc;
5
6/// The start and the end of a selection, in characters.
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
8pub struct TextSelection {
9    pub start: i32,
10    pub end: i32,
11}
12
13impl TextSelection {
14    pub const fn new(start: i32, end: i32) -> Self {
15        Self { start, end }
16    }
17}
18
19/// An action of the context menu of a text input.
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
21#[repr(i32)]
22pub enum ContextMenuAction {
23    Copy = 0,
24    Cut = 1,
25    Paste = 2,
26    SelectAll = 3,
27}
28
29type Event = Rc<HandlerList<dyn Fn()>>;
30
31fn subscribe(event: &Event, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
32    let token = event.add(handler);
33    let event = event.clone();
34    Disposable::create(move || {
35        event.remove(token);
36    })
37}
38
39fn raise(event: &Event) {
40    if event.is_empty() {
41        return;
42    }
43    for (_, handler) in event.snapshot().iter() {
44        handler();
45    }
46}
47
48/// The events of a [`TextInputMethodClient`]: the state every client
49/// embeds and returns from [`TextInputMethodClient::events`].
50#[derive(Default)]
51pub struct TextInputMethodClientEvents {
52    text_view_visual_changed: Event,
53    cursor_rectangle_changed: Event,
54    surrounding_text_changed: Event,
55    selection_changed: Event,
56    reset_requested: Event,
57    input_pane_activation_requested: Event,
58}
59
60impl TextInputMethodClientEvents {
61    /// Creates the events of a client, without any subscription.
62    pub fn new() -> Self {
63        Self::default()
64    }
65}
66
67/// The text editing side of a text input method: what a text editing
68/// control gives the input method of the platform to work with.
69///
70/// Implementations embed a [`TextInputMethodClientEvents`] and provide the
71/// members describing the text; the events and the methods raising them are
72/// provided. Clients are compared by reference.
73pub trait TextInputMethodClient {
74    /// The events of the client.
75    fn events(&self) -> &TextInputMethodClientEvents;
76
77    /// The visual that's showing the text, or `None` when the client has
78    /// none (a text box without a template).
79    fn text_view_visual(&self) -> Option<Ref<Visual>>;
80
81    /// Indicates if the text input method client is capable of displaying
82    /// non-committed input on the cursor position.
83    fn supports_preedit(&self) -> bool;
84
85    /// Indicates if the text input method client supports sending
86    /// surrounding text.
87    fn supports_surrounding_text(&self) -> bool;
88
89    /// Returns the text around the cursor, usually the current paragraph.
90    fn surrounding_text(&self) -> String;
91
92    /// Gets the cursor rectangle relative to the text view visual.
93    fn cursor_rectangle(&self) -> Rect;
94
95    /// Gets the selection that is currently active.
96    fn selection(&self) -> TextSelection;
97
98    /// Sets the selection that is currently active.
99    fn set_selection(&self, value: TextSelection);
100
101    /// Sets the non-committed input string.
102    fn set_preedit_text(&self, _preedit_text: Option<&str>) {}
103
104    /// Executes a context menu action.
105    fn execute_context_menu_action(&self, _action: ContextMenuAction) {}
106
107    /// Sets the non-committed input string and the cursor offset in that
108    /// string.
109    fn set_preedit_text_with_cursor(&self, preedit_text: Option<&str>, _cursor_pos: Option<i32>) {
110        self.set_preedit_text(preedit_text)
111    }
112
113    /// Fires when the text view visual has changed. Disposing the returned
114    /// handle unsubscribes.
115    fn text_view_visual_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
116        subscribe(&self.events().text_view_visual_changed, handler)
117    }
118
119    /// Fires when the cursor rectangle has changed. Disposing the returned
120    /// handle unsubscribes.
121    fn cursor_rectangle_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
122        subscribe(&self.events().cursor_rectangle_changed, handler)
123    }
124
125    /// Fires when the surrounding text has changed. Disposing the returned
126    /// handle unsubscribes.
127    fn surrounding_text_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
128        subscribe(&self.events().surrounding_text_changed, handler)
129    }
130
131    /// Fires when the selection has changed. Disposing the returned handle
132    /// unsubscribes.
133    fn selection_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
134        subscribe(&self.events().selection_changed, handler)
135    }
136
137    /// Fires when client wants to reset the input state. Disposing the
138    /// returned handle unsubscribes.
139    fn reset_requested(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
140        subscribe(&self.events().reset_requested, handler)
141    }
142
143    /// Fires when the client requires the input pane (on-screen keyboard)
144    /// to be shown. Disposing the returned handle unsubscribes.
145    fn input_pane_activation_requested(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
146        subscribe(&self.events().input_pane_activation_requested, handler)
147    }
148
149    /// Raises the event telling that the text view visual has changed.
150    fn raise_text_view_visual_changed(&self) {
151        raise(&self.events().text_view_visual_changed)
152    }
153
154    /// Raises the event telling that the cursor rectangle has changed.
155    fn raise_cursor_rectangle_changed(&self) {
156        raise(&self.events().cursor_rectangle_changed)
157    }
158
159    /// Raises the event telling that the surrounding text has changed.
160    fn raise_surrounding_text_changed(&self) {
161        raise(&self.events().surrounding_text_changed)
162    }
163
164    /// Raises the event telling that the selection has changed.
165    fn raise_selection_changed(&self) {
166        raise(&self.events().selection_changed)
167    }
168
169    /// Raises the event asking for the input pane to be shown.
170    fn raise_input_pane_activation_requested(&self) {
171        raise(&self.events().input_pane_activation_requested)
172    }
173
174    /// Asks for the input state to be reset.
175    fn request_reset(&self) {
176        raise(&self.events().reset_requested)
177    }
178}