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