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}