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 is_in_target(&self, id: WidgetId) -> bool {
35            self.target.contains(id)
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 when this widget is focused.
66    ///
67    /// The are is usually inside the widget inner bounds, representing the text caret or selection.
68    fn ime_area(&self) -> Option<PxRect>;
69}
70
71/// IME extension methods for [`WidgetInfoBuilder`].
72///
73/// [`WidgetInfoBuilder`]: zng_app::widget::info::WidgetInfoBuilder
74pub trait WidgetInfoBuilderImeArea {
75    /// Set an IME exclusion area in the window space when this widget is focused.
76    fn set_ime_area(&mut self, area: Arc<Atomic<PxRect>>);
77}
78
79static_id! {
80    static ref IME_AREA_ID: StateId<Arc<Atomic<PxRect>>>;
81}
82
83impl WidgetInfoImeArea for WidgetInfo {
84    fn ime_area(&self) -> Option<PxRect> {
85        Some(self.meta().get(*IME_AREA_ID)?.load(std::sync::atomic::Ordering::Relaxed))
86    }
87}
88
89impl WidgetInfoBuilderImeArea for WidgetInfoBuilder {
90    fn set_ime_area(&mut self, area: Arc<Atomic<PxRect>>) {
91        self.set_meta(*IME_AREA_ID, area);
92    }
93}