use crate::{
ByteIndex, ComponentMask, Context, DEFAULT_BASIC, DEFAULT_FLEX, DebugStore, EffectCategory,
Element, ElementState, EntityId, ImeState, InputContents, InputOp, LayoutPoint, LayoutSize,
LayoutStore, MichiuSoA, MichiuString, Modifiers, MouseButton, Prop, ResolvedGeometry,
SelectedRectsSparse, SelectionStartIndexSparse, SystemStore, TextEngine, TextLayoutSize,
TextSelectionsSparse, TextSpan, UnderlineStyle, VirtualKey, with_context,
};
use cosmic_text::Buffer;
use std::{ops::Range, time::Instant};
impl Element {
#[must_use]
pub fn input(self, contents: impl Into<Prop<InputContents>>) -> Self {
match contents.into() {
Prop::None => {}
Prop::Static(c) => {
with_context(|cx| self.input_internal(cx, c));
}
Prop::Dynamic(f) => {
let id = self.id;
with_context(|cx| {
cx.create_element_effect(id, EffectCategory::Input, move |cx| {
let c = f();
let el = Element { id };
el.input_internal(cx, c);
});
});
}
}
self
}
#[must_use]
#[inline]
pub fn input_d<P, F>(self, f: F) -> Self
where
P: Clone + 'static,
F: Fn(&P) -> InputContents + Send + Sync + 'static,
{
let dynamic_prop = Prop::Dynamic(Box::new(move || {
let signal = with_context(|cx| cx.use_provided::<P>());
let val = signal.get();
f(&val)
}));
self.input(dynamic_prop)
}
#[must_use]
pub fn input_area(self, contents: impl Into<Prop<InputContents>>) -> Self {
match contents.into() {
Prop::None => {}
Prop::Static(mut c) => {
c.is_multiline = true; with_context(|cx| self.input_internal(cx, c));
}
Prop::Dynamic(f) => {
let id = self.id;
with_context(|cx| {
cx.create_element_effect(id, EffectCategory::Input, move |cx| {
let mut c = f();
c.is_multiline = true;
let el = Element { id };
el.input_internal(cx, c);
});
});
}
}
self
}
#[must_use]
#[inline]
pub fn input_area_d<P, F>(self, f: F) -> Self
where
P: Clone + 'static,
F: Fn(&P) -> InputContents + Send + Sync + 'static,
{
let dynamic_prop = Prop::Dynamic(Box::new(move || {
let signal = with_context(|cx| cx.use_provided::<P>());
let val = signal.get();
f(&val)
}));
self.input_area(dynamic_prop)
}
fn sync_existing_input_properties(existing: &mut InputContents, c: InputContents) {
existing.placeholder = c.placeholder;
existing.placeholder_color = c.placeholder_color;
existing.caret_color = c.caret_color;
existing.caret_width = c.caret_width;
existing.caret_height = c.caret_height;
existing.caret_offset = c.caret_offset;
existing.is_blink = c.is_blink;
existing.blink_frequency = c.blink_frequency;
existing.has_caret = c.has_caret;
existing.placeholder_select = c.placeholder_select;
existing.is_multiline = c.is_multiline;
existing.is_password = c.is_password;
existing.mask_text = c.mask_text;
let current_text = existing.to_michiu();
let text_len = current_text.byte_len();
existing.selected_range.start = existing.selected_range.start.min(text_len);
existing.selected_range.end = existing.selected_range.end.min(text_len);
}
#[inline]
fn set_caret_position(
id: EntityId,
contents: &mut InputContents,
caret: ByteIndex,
edit_selections: &mut TextSelectionsSparse,
edit_selection_start_index: &mut SelectionStartIndexSparse,
edit_selected_rects: Option<&mut SelectedRectsSparse>,
) {
contents.selected_range = caret..caret;
contents.selection_reversed = false;
edit_selections.insert(id, caret..caret);
edit_selection_start_index.insert(id, caret);
if let Some(rects) = edit_selected_rects {
rects.remove(id);
}
}
#[inline]
fn set_selection_range(
id: EntityId,
contents: &mut InputContents,
range: Range<ByteIndex>,
selection_reversed: bool,
edit_selections: &mut TextSelectionsSparse,
) {
contents.selected_range = range.clone();
contents.selection_reversed = selection_reversed;
edit_selections.insert(id, range);
}
#[track_caller]
fn handle_input_mouse_pressed(
cx: &mut Context,
id: EntityId,
btn: MouseButton,
mods: Modifiers,
state: ElementState,
) {
if btn != MouseButton::Left || state != ElementState::Pressed {
return;
}
let Some(pointer_pos) = cx.events.evt_current_pointer_position else {
return;
};
let flex = cx
.layouts
.lay_resolved_flex
.find_or(id, &DEFAULT_FLEX, &mut cx.debug);
let resolved_geom = ResolvedGeometry::resolved(
id,
&cx.states.scroll.sc_offsets,
&cx.layouts.lay_resolved_basic,
&cx.outputs.out_rects,
&mut cx.debug,
);
let auto_wrap = cx.renders.rnd_visual.auto_wrap(id);
let content_width = resolved_geom.content_width();
let max_width_opt = (auto_wrap && content_width > 0.0).then_some(content_width);
let buffer = SystemStore::get_or_create_text_buffer(
id,
max_width_opt,
&cx.system.sys_text_buffers,
|| {
let text = cx.contents.cont_text_contents.at(id);
let font = cx.renders.rnd_visual.font(id);
let spans = cx.contents.cont_text_spans.span(id);
cx.system.sys_text_engine.create_buffer(
text,
&font,
flex.text_align,
max_width_opt,
auto_wrap,
spans,
)
},
);
let text_size = if let Some(c) = &cx.contents.cont_input_contents.find(id) {
let size = c
.last_layout
.map_or(LayoutSize::ZERO, |r| LayoutSize::new(r.width, r.height));
TextLayoutSize::new(size.width, size.height, Some(c.is_multiline))
} else {
TextEngine::get_layout_size(&buffer).set_multiline(Some(false))
};
let local = resolved_geom.pressed_local_point(
pointer_pos,
flex.text_align,
flex.align_items,
text_size,
);
let Some(contents) = cx.contents.cont_input_contents.find_mut(id) else {
return;
};
let text_val = contents.to_michiu();
let is_placeholder = text_val.is_empty()
&& contents
.ime_state
.as_ref()
.is_none_or(|s| s.composition_text.is_empty());
if is_placeholder && !contents.placeholder_select {
Element::set_caret_position(
id,
contents,
ByteIndex(0),
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
Some(&mut cx.states.edit.edit_selected_rects),
);
} else {
let display_text = cx
.contents
.cont_text_contents
.find(id)
.cloned()
.unwrap_or_default();
let (display_caret, is_trailing) = TextEngine::hit_test_point(&buffer, local);
let final_display_caret = if is_trailing {
display_text.next_char_boundary(display_caret)
} else {
display_caret
};
let final_caret = contents.display_byte_to_raw_byte(final_display_caret, &text_val);
let editable_len = if is_placeholder {
contents
.placeholder
.as_ref()
.map_or(ByteIndex(0), |p| ByteIndex(p.len()))
} else {
text_val.byte_len()
};
let final_caret_clamped = final_caret.min(editable_len);
if mods.shift {
let anchor = cx
.states
.edit
.edit_selection_start_index
.entry(id)
.map_or(contents.selected_range.start, |e| {
*e.or_insert(contents.selected_range.start)
});
let (range, reversed) = if anchor <= final_caret_clamped {
(anchor..final_caret_clamped, false)
} else {
(final_caret_clamped..anchor, true)
};
Element::set_selection_range(
id,
contents,
range,
reversed,
&mut cx.states.edit.edit_selections,
);
} else {
Element::set_caret_position(
id,
contents,
final_caret_clamped,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
Some(&mut cx.states.edit.edit_selected_rects),
);
}
}
contents.last_interacted_time = Some(Instant::now());
cx.apply_input_update(id, InputOp::MousePress);
}
fn handle_input_focus_gained(cx: &mut Context, id: EntityId) {
if let Some(contents) = cx.contents.cont_input_contents.find_mut(id) {
contents.is_selecting = false;
contents.last_interacted_time = Some(Instant::now());
}
cx.mark_render_dirty(id);
}
fn handle_input_char_typed(cx: &mut Context, id: EntityId, ch: &mut char) {
let Some(contents) = cx.contents.cont_input_contents.find_mut(id) else {
return;
};
if contents
.ime_state
.as_ref()
.is_some_and(|s| !s.composition_text.is_empty())
{
return;
}
let mut is_allowed = !ch.is_control();
if contents.is_multiline && (*ch == '\r' || *ch == '\n') {
*ch = '\n';
is_allowed = true;
}
if !is_allowed {
return;
}
if contents.numeric_only && !ch.is_numeric() && *ch != '.' && *ch != '-' {
return;
}
let text_val = contents.to_michiu();
let range = contents.selected_range.clone();
if let Some(max) = contents.max_length {
let selected_chars = text_val.slice(range.clone()).chars().count();
let chars_after_delete = text_val.char_count() - selected_chars;
if chars_after_delete + 1 > max {
return; }
}
contents.record_undo(text_val, range.clone());
contents.last_interacted_time = Some(Instant::now());
let mut buf = [0u8; 4];
let ch_str = ch.encode_utf8(&mut buf);
let new_caret = contents.update_michiu(|m| m.replace_range(range, ch_str));
Element::set_caret_position(
id,
contents,
new_caret,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
Some(&mut cx.states.edit.edit_selected_rects),
);
cx.apply_input_update(id, InputOp::CharTyped);
}
fn pressed_back(
id: EntityId,
contents: &mut InputContents,
caret: ByteIndex,
text_val: &MichiuString,
edit_selections: &mut TextSelectionsSparse,
edit_selection_start_index: &mut SelectionStartIndexSparse,
) {
let range = contents.selected_range.clone();
contents.record_undo(text_val.clone(), range.clone());
let new_caret = if range.start < range.end {
contents.update_michiu(|m| m.remove_range(range))
} else {
contents.update_michiu(|m| m.backspace(caret))
};
Element::set_caret_position(
id,
contents,
new_caret,
edit_selections,
edit_selection_start_index,
None,
);
contents.last_interacted_time = Some(Instant::now());
}
fn pressed_delete(
id: EntityId,
contents: &mut InputContents,
caret: ByteIndex,
text_val: &MichiuString,
edit_selections: &mut TextSelectionsSparse,
edit_selection_start_index: &mut SelectionStartIndexSparse,
) {
let range = contents.selected_range.clone();
contents.record_undo(text_val.clone(), range.clone());
let new_caret = if range.start < range.end {
contents.update_michiu(|m| m.remove_range(range))
} else {
contents.update_michiu(|m| m.delete(caret))
};
Element::set_caret_position(
id,
contents,
new_caret,
edit_selections,
edit_selection_start_index,
None,
);
contents.last_interacted_time = Some(Instant::now());
}
fn pressed_left(
id: EntityId,
contents: &mut InputContents,
text_val: &MichiuString,
caret: ByteIndex,
mods: Modifiers,
edit_selections: &mut TextSelectionsSparse,
edit_selection_start_index: &mut SelectionStartIndexSparse,
edit_selected_rects: &mut SelectedRectsSparse,
) -> bool {
let range = contents.selected_range.clone();
if range.start < range.end && !mods.shift {
let new_caret = range.start;
Element::set_caret_position(
id,
contents,
new_caret,
edit_selections,
edit_selection_start_index,
Some(edit_selected_rects),
);
contents.last_interacted_time = Some(Instant::now());
return true;
} else if caret.0 > 0 {
let new_caret = text_val.prev_char_boundary(caret);
if mods.shift {
let anchor = edit_selection_start_index
.find(id)
.copied()
.unwrap_or(new_caret);
if !edit_selection_start_index.contains_key(id) {
edit_selection_start_index.insert(id, new_caret);
}
let (range, reversed) = if anchor <= new_caret {
(anchor..new_caret, false)
} else {
(new_caret..anchor, true)
};
Element::set_selection_range(id, contents, range, reversed, edit_selections);
} else {
Element::set_caret_position(
id,
contents,
new_caret,
edit_selections,
edit_selection_start_index,
Some(edit_selected_rects),
);
}
contents.last_interacted_time = Some(Instant::now());
return true;
}
false
}
fn pressed_right(
id: EntityId,
contents: &mut InputContents,
text_val: &MichiuString,
caret: ByteIndex,
mods: Modifiers,
edit_selections: &mut TextSelectionsSparse,
edit_selection_start_index: &mut SelectionStartIndexSparse,
edit_selected_rects: &mut SelectedRectsSparse,
debug: &mut DebugStore,
) -> bool {
let range = contents.selected_range.clone();
if range.start < range.end && !mods.shift {
Element::set_caret_position(
id,
contents,
range.end,
edit_selections,
edit_selection_start_index,
Some(edit_selected_rects),
);
contents.last_interacted_time = Some(Instant::now());
return true;
} else if caret < text_val.byte_len() {
let new_caret = text_val.next_char_boundary(caret);
if mods.shift {
let anchor = *edit_selection_start_index.find_or(id, &caret, debug);
if !edit_selection_start_index.contains_key(id) {
edit_selection_start_index.insert(id, caret);
}
let (range, reversed) = if anchor <= new_caret {
(anchor..new_caret, false)
} else {
(new_caret..anchor, true)
};
Element::set_selection_range(id, contents, range, reversed, edit_selections);
} else {
Element::set_caret_position(
id,
contents,
new_caret,
edit_selections,
edit_selection_start_index,
Some(edit_selected_rects),
);
}
contents.last_interacted_time = Some(Instant::now());
return true;
}
false
}
fn pressed_up(
id: EntityId,
contents: &mut InputContents,
buffer: &Buffer,
caret: ByteIndex,
text_val: &MichiuString,
mods: Modifiers,
edit_selections: &mut TextSelectionsSparse,
edit_selection_start_index: &mut SelectionStartIndexSparse,
debug: &mut DebugStore,
) -> bool {
if !contents.is_multiline {
return false;
}
let (cx_offset, cy_offset, ch_height) = TextEngine::get_caret_position(buffer, caret);
let target_y = (cy_offset - ch_height * 0.5).max(0.0);
let (new_caret, is_trailing) =
TextEngine::hit_test_point(buffer, LayoutPoint::new(cx_offset, target_y));
let final_caret = if is_trailing {
text_val.next_char_boundary(new_caret)
} else {
new_caret
};
if mods.shift {
let anchor = *edit_selection_start_index.find_or(id, &caret, debug);
if !edit_selection_start_index.contains_key(id) {
edit_selection_start_index.insert(id, caret);
}
let (range, reversed) = if anchor <= final_caret {
(anchor..final_caret, false)
} else {
(final_caret..anchor, true)
};
Element::set_selection_range(id, contents, range, reversed, edit_selections);
} else {
Element::set_caret_position(
id,
contents,
final_caret,
edit_selections,
edit_selection_start_index,
None,
);
}
contents.last_interacted_time = Some(Instant::now());
true
}
fn pressed_down(
id: EntityId,
contents: &mut InputContents,
buffer: &Buffer,
caret: ByteIndex,
text_val: &MichiuString,
mods: Modifiers,
edit_selections: &mut TextSelectionsSparse,
edit_selection_start_index: &mut SelectionStartIndexSparse,
debug: &mut DebugStore,
) -> bool {
if !contents.is_multiline {
return false;
}
let (cx_offset, cy_offset, ch_height) = TextEngine::get_caret_position(buffer, caret);
let target_y = cy_offset + ch_height * 1.5;
let (new_caret, is_trailing) =
TextEngine::hit_test_point(buffer, LayoutPoint::new(cx_offset, target_y));
let final_caret = if is_trailing {
text_val.next_char_boundary(new_caret)
} else {
new_caret
};
if mods.shift {
let anchor = *edit_selection_start_index.find_or(id, &caret, debug);
if !edit_selection_start_index.contains_key(id) {
edit_selection_start_index.insert(id, caret);
}
let (range, reversed) = if anchor <= final_caret {
(anchor..final_caret, false)
} else {
(final_caret..anchor, true)
};
Element::set_selection_range(id, contents, range, reversed, edit_selections);
} else {
Element::set_caret_position(
id,
contents,
final_caret,
edit_selections,
edit_selection_start_index,
None,
);
}
contents.last_interacted_time = Some(Instant::now());
true
}
#[track_caller]
fn handle_input_key_pressed(
cx: &mut Context,
id: EntityId,
key: VirtualKey,
mods: Modifiers,
state: ElementState,
) {
if state != ElementState::Pressed {
return;
}
let basic = cx
.layouts
.lay_resolved_basic
.find_or(id, &DEFAULT_BASIC, &mut cx.debug);
let flex = cx
.layouts
.lay_resolved_flex
.find_or(id, &DEFAULT_FLEX, &mut cx.debug);
let rect = cx.outputs.out_rects.find_or_default(id, &mut cx.debug);
let (border, padding) =
LayoutStore::get_physical_border_padding(rect, basic.border, basic.padding);
let auto_wrap = cx.renders.rnd_visual.auto_wrap(id);
let content_width = rect.width - border.right - border.left - padding.right - padding.left;
let max_width_opt = (auto_wrap && content_width > 0.0).then_some(content_width);
let buffer = SystemStore::get_or_create_text_buffer(
id,
max_width_opt,
&cx.system.sys_text_buffers,
|| {
let text = cx.contents.cont_text_contents.at(id);
let font = cx.renders.rnd_visual.font(id);
let spans = cx.contents.cont_text_spans.span(id);
cx.system.sys_text_engine.create_buffer(
text,
&font,
flex.text_align,
max_width_opt,
auto_wrap,
spans,
)
},
);
let Some(contents) = cx.contents.cont_input_contents.find_mut(id) else {
return;
};
let text_val = contents.to_michiu();
let text_len = text_val.byte_len();
contents.selected_range =
contents.selected_range.start.min(text_len)..contents.selected_range.end.min(text_len);
let raw_caret = if contents.selection_reversed {
contents.selected_range.start
} else {
contents.selected_range.end
};
let caret = raw_caret.min(text_len);
let mut changed = false;
match key {
VirtualKey::BACK => {
Element::pressed_back(
id,
contents,
caret,
&text_val,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
);
cx.apply_input_update(id, InputOp::Backspace);
}
VirtualKey::DELETE => {
Element::pressed_delete(
id,
contents,
caret,
&text_val,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
);
cx.apply_input_update(id, InputOp::Delete);
}
VirtualKey::LEFT => {
changed = Element::pressed_left(
id,
contents,
&text_val,
caret,
mods,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
&mut cx.states.edit.edit_selected_rects,
);
}
VirtualKey::RIGHT => {
changed = Element::pressed_right(
id,
contents,
&text_val,
caret,
mods,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
&mut cx.states.edit.edit_selected_rects,
&mut cx.debug,
);
}
VirtualKey::UP => {
changed = Element::pressed_up(
id,
contents,
&buffer,
caret,
&text_val,
mods,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
&mut cx.debug,
);
}
VirtualKey::DOWN => {
changed = Element::pressed_down(
id,
contents,
&buffer,
caret,
&text_val,
mods,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
&mut cx.debug,
);
}
_ => {}
}
if changed {
cx.apply_input_update(id, InputOp::ArrowMove);
}
}
fn handle_input_ime_updated(cx: &mut Context, id: EntityId, ime: &ImeState) {
let Some(contents) = cx.contents.cont_input_contents.find_mut(id) else {
return;
};
contents.last_interacted_time = Some(Instant::now());
contents.ime_state = Some(ime.clone());
let mut text_val = contents.to_michiu();
let range = contents.selected_range.clone();
if range.start < range.end
&& (!ime.composition_text.is_empty() || !ime.result_text.is_empty())
{
contents.record_undo(text_val.clone(), range.clone());
let caret = contents.update_michiu(|m| m.remove_range(range));
Element::set_caret_position(
id,
contents,
caret,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
Some(&mut cx.states.edit.edit_selected_rects),
);
text_val = contents.to_michiu();
}
if !ime.result_text.is_empty() {
let allowed_len = if let Some(max) = contents.max_length {
let current_chars = text_val.char_count().0;
if current_chars >= max.0 {
contents.marked_range = None;
return;
}
max.0 - current_chars
} else {
usize::MAX
};
let mut filtered = String::new();
let mut chars_added = 0;
for c in ime.result_text.chars() {
if contents.numeric_only && !c.is_numeric() && c != '.' && c != '-' {
continue;
}
if chars_added >= allowed_len {
break;
}
filtered.push(c);
chars_added += 1;
}
if !filtered.is_empty() {
let caret = contents.selected_range.start;
let new_caret = contents.update_michiu(|m| m.insert_str(caret, &filtered));
Element::set_caret_position(
id,
contents,
new_caret,
&mut cx.states.edit.edit_selections,
&mut cx.states.edit.edit_selection_start_index,
Some(&mut cx.states.edit.edit_selected_rects),
);
}
contents.marked_range = None;
} else if !ime.composition_text.is_empty() {
let caret = contents.selected_range.start;
let comp_len = ime.composition_text.byte_len();
contents.marked_range = Some(caret..(caret + comp_len));
} else {
contents.marked_range = None;
}
if ime.composition_text.is_empty() {
cx.contents.cont_text_spans.remove(id);
cx.topology
.topo_active_masks
.at_mut(id)
.unset(ComponentMask::STYLE_TEXT_SPANS);
} else {
let mut spans = Vec::new();
let caret = contents.selected_range.start;
let char_byte_offsets: Vec<ByteIndex> = {
let mut offsets = Vec::with_capacity(ime.composition_text.char_count().0 + 1);
let mut curr_byte = ByteIndex(0);
for c in ime.composition_text.chars() {
offsets.push(curr_byte);
curr_byte += c.len_utf8();
}
offsets.push(curr_byte);
offsets
};
if ime.composition_attrs.is_empty() {
let comp_len = ime.composition_text.byte_len();
spans.push(TextSpan {
range: caret..(caret + comp_len),
underline: Some(UnderlineStyle::Wave),
..Default::default()
});
} else {
let attrs = &ime.composition_attrs;
let mut start_idx = 0;
while start_idx < attrs.len() {
let attr = attrs[start_idx];
let mut end_idx = start_idx + 1;
while end_idx < attrs.len() && attrs[end_idx] == attr {
end_idx += 1;
}
let underline_style = match attr {
1 => Some(UnderlineStyle::Thick),
2 => Some(UnderlineStyle::Solid),
_ => Some(UnderlineStyle::Wave),
};
let span_start = caret + char_byte_offsets[start_idx];
let span_end = caret + char_byte_offsets[end_idx];
spans.push(TextSpan {
range: span_start..span_end,
color: None,
bg_color: None,
font_size: None,
font_family: None,
font_weight: None,
font_style: None,
underline: underline_style,
underline_color: None,
strikethrough: None,
strikethrough_color: None,
link_id: None,
});
start_idx = end_idx;
}
}
cx.contents.cont_text_spans.insert(id, spans);
cx.topology
.topo_active_masks
.at_mut(id)
.set(ComponentMask::STYLE_TEXT_SPANS);
}
cx.apply_input_update(id, InputOp::ImeUpdated);
}
fn input_internal(self, cx: &mut Context, mut c: InputContents) {
let id = self.id;
if let Some(existing) = cx.contents.cont_input_contents.find_mut(id) {
Element::sync_existing_input_properties(existing, c);
cx.apply_input_update(id, InputOp::Init);
return;
}
let current_text = c.to_michiu();
let current_len = current_text.byte_len();
c.selected_range = current_len..current_len;
cx.contents.cont_input_contents.insert(id, c);
cx.topology
.topo_active_masks
.at_mut(id)
.set(ComponentMask::COMP_INPUT_CONTENT | ComponentMask::COMP_TEXT_CONTENT);
self.get_or_create_listeners(|l| {
let mut existing_mouse = l.on_mouse_input.take();
l.on_mouse_input = Some(Box::new(move |cx, btn, mods, state| {
Element::handle_input_mouse_pressed(cx, id, btn, mods, state);
if let Some(ref mut ext) = existing_mouse {
ext(cx, btn, mods, state);
}
}));
let mut existing_focus = l.on_focus.take();
l.on_focus = Some(Box::new(move |cx| {
Element::handle_input_focus_gained(cx, id);
if let Some(ref mut ext) = existing_focus {
ext(cx);
}
}));
let mut existing_char = l.on_char_input.take();
l.on_char_input = Some(Box::new(move |cx, mut ch| {
Element::handle_input_char_typed(cx, id, &mut ch);
if let Some(ref mut ext) = existing_char {
ext(cx, ch);
}
}));
let mut existing_keyboard = l.on_keyboard_input.take();
l.on_keyboard_input = Some(Box::new(move |cx, key, mods, state| {
Element::handle_input_key_pressed(cx, id, key, mods, state);
if let Some(ref mut ext) = existing_keyboard {
ext(cx, key, mods, state);
}
}));
let mut existing_ime = l.on_ime.take();
l.on_ime = Some(Box::new(move |cx, ime| {
Element::handle_input_ime_updated(cx, id, &ime);
if let Some(ref mut ext) = existing_ime {
ext(cx, ime);
}
}));
});
cx.create_element_effect(id, EffectCategory::Text, move |cx| {
if let Some(contents) = cx.contents.cont_input_contents.find(id) {
let _base_text_val = contents.text.0.get();
}
cx.apply_input_update(id, InputOp::TextEffect);
});
}
}