zng_ext_window/
ime.rs

1use std::sync::Arc;
2
3use atomic::Atomic;
4use zng_app::{
5    event::{event, event_args},
6    widget::info::{WidgetInfo, WidgetInfoBuilder, WidgetPath},
7};
8use zng_layout::unit::PxRect;
9use zng_state_map::{StateId, static_id};
10use zng_txt::Txt;
11
12event_args! {
13    /// Arguments for [`IME_EVENT`].
14    pub struct ImeArgs {
15        /// The enabled text input widget.
16        pub target: WidgetPath,
17
18        /// The text, preview or actual insert.
19        pub txt: Txt,
20
21        /// Caret/selection within the `txt` when it is preview.
22        ///
23        /// The indexes are in char byte offsets and indicate where the caret or selection must be placed on
24        /// the inserted or preview `txt`, if not set the position is at the end of the insert.
25        ///
26        /// If this is `None` the text must [`commit`].
27        ///
28        /// [`commit`]: Self::commit
29        pub preview_caret: Option<(usize, usize)>,
30
31        ..
32
33        /// Target.
34        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
35            list.insert_wgt(&self.target);
36        }
37    }
38}
39impl ImeArgs {
40    /// If the text must be actually inserted.
41    ///
42    /// If `true` the [`txt`] must be actually inserted at the last non-preview caret/selection, the caret then must be moved to
43    /// after the inserted text.
44    ///
45    /// If `false` the widget must visually adjust the text and caret to look as if the input has committed, but the
46    /// actual text must not be altered, and if the [`txt`] is empty the previous caret/selection must be restored.
47    /// Usually the preview text is rendered with an underline effect, otherwise it has the same appearance as the
48    /// committed text.
49    ///
50    /// [`txt`]: Self::txt
51    pub fn commit(&self) -> bool {
52        self.preview_caret.is_none()
53    }
54}
55
56event! {
57    /// Input Method Editor event targeting a text input widget.
58    pub static IME_EVENT: ImeArgs;
59}
60
61/// IME extension methods for [`WidgetInfo`].
62///
63/// [`WidgetInfo`]: zng_app::widget::info::WidgetInfo
64pub trait WidgetInfoImeArea {
65    /// IME exclusion area in the window space.
66    ///
67    /// Widgets are IME targets when they are focused and subscribe to [`IME_EVENT`]. This
68    /// value is an area the IME window should avoid covering, by default it is the widget inner-bounds,
69    /// but the widget can override it using [`set_ime_area`].
70    ///
71    /// This value can change after every render update.
72    ///
73    /// [`set_ime_area`]: WidgetInfoBuilderImeArea::set_ime_area
74    fn ime_area(&self) -> PxRect;
75}
76
77/// IME extension methods for [`WidgetInfoBuilder`].
78///
79/// [`WidgetInfoBuilder`]: zng_app::widget::info::WidgetInfoBuilder
80pub trait WidgetInfoBuilderImeArea {
81    /// Set a custom [`ime_area`].
82    ///
83    /// The value can be updated every frame using interior mutability, without needing to rebuild the info.
84    ///
85    /// [`ime_area`]: WidgetInfoImeArea::ime_area
86    fn set_ime_area(&mut self, area: Arc<Atomic<PxRect>>);
87}
88
89static_id! {
90    static ref IME_AREA: StateId<Arc<Atomic<PxRect>>>;
91}
92
93impl WidgetInfoImeArea for WidgetInfo {
94    fn ime_area(&self) -> PxRect {
95        self.meta()
96            .get(*IME_AREA)
97            .map(|r| r.load(atomic::Ordering::Relaxed))
98            .unwrap_or_else(|| self.inner_bounds())
99    }
100}
101
102impl WidgetInfoBuilderImeArea for WidgetInfoBuilder {
103    fn set_ime_area(&mut self, area: Arc<Atomic<PxRect>>) {
104        self.set_meta(*IME_AREA, area);
105    }
106}