feat(text_input): minimal IME support for COSMIC specific text widgets
This commit is contained in:
parent
0ba668eb52
commit
f6eb314606
1 changed files with 103 additions and 1 deletions
|
|
@ -22,10 +22,11 @@ use iced::Limits;
|
|||
use iced::clipboard::dnd::{DndAction, DndEvent, OfferEvent, SourceEvent};
|
||||
use iced::clipboard::mime::AsMimeTypes;
|
||||
use iced_core::event::{self, Event};
|
||||
use iced_core::input_method::{self, InputMethod, Preedit};
|
||||
use iced_core::mouse::{self, click};
|
||||
use iced_core::overlay::Group;
|
||||
use iced_core::renderer::{self, Renderer as CoreRenderer};
|
||||
use iced_core::text::{self, Paragraph, Renderer, Text};
|
||||
use iced_core::text::{self, Affinity, Paragraph, Renderer, Text};
|
||||
use iced_core::time::{Duration, Instant};
|
||||
use iced_core::touch;
|
||||
use iced_core::widget::Id;
|
||||
|
|
@ -2083,6 +2084,66 @@ pub fn update<'a, Message: Clone + 'static>(
|
|||
|
||||
state.keyboard_modifiers = *modifiers;
|
||||
}
|
||||
Event::InputMethod(event) => {
|
||||
let state = state();
|
||||
|
||||
match event {
|
||||
input_method::Event::Opened | input_method::Event::Closed => {
|
||||
state.preedit = matches!(event, input_method::Event::Opened)
|
||||
.then(input_method::Preedit::new);
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
input_method::Event::Preedit(content, selection) => {
|
||||
if state.is_focused.is_some() {
|
||||
state.preedit = Some(input_method::Preedit {
|
||||
content: content.to_owned(),
|
||||
selection: selection.clone(),
|
||||
text_size: Some(size.into()),
|
||||
});
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
input_method::Event::Commit(text) => {
|
||||
let Some(focus) = &mut state.is_focused else {
|
||||
return;
|
||||
};
|
||||
let Some(on_input) = on_input else {
|
||||
return;
|
||||
};
|
||||
if state.is_read_only {
|
||||
return;
|
||||
}
|
||||
|
||||
focus.updated_at = Instant::now();
|
||||
LAST_FOCUS_UPDATE.with(|x| x.set(focus.updated_at));
|
||||
|
||||
let mut editor = Editor::new(unsecured_value, &mut state.cursor);
|
||||
editor.paste(Value::new(&text));
|
||||
|
||||
let contents = editor.contents();
|
||||
let unsecured_value = Value::new(&contents);
|
||||
let message = if let Some(paste) = &on_paste {
|
||||
(paste)(contents)
|
||||
} else {
|
||||
(on_input)(contents)
|
||||
};
|
||||
shell.publish(message);
|
||||
|
||||
state.is_pasting = None;
|
||||
let value = if is_secure {
|
||||
unsecured_value.secure()
|
||||
} else {
|
||||
unsecured_value
|
||||
};
|
||||
|
||||
update_cache(state, &value);
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Window(window::Event::RedrawRequested(now)) => {
|
||||
let state = state();
|
||||
|
||||
|
|
@ -2095,6 +2156,8 @@ pub fn update<'a, Message: Clone + 'static>(
|
|||
now.checked_add(Duration::from_millis(millis_until_redraw as u64))
|
||||
.unwrap_or(*now),
|
||||
));
|
||||
|
||||
shell.request_input_method(&input_method(state, text_layout, unsecured_value));
|
||||
} else if always_active {
|
||||
shell.request_redraw();
|
||||
}
|
||||
|
|
@ -2269,6 +2332,43 @@ pub fn update<'a, Message: Clone + 'static>(
|
|||
}
|
||||
}
|
||||
|
||||
fn input_method<'b>(
|
||||
state: &'b State,
|
||||
text_layout: Layout<'_>,
|
||||
value: &Value,
|
||||
) -> InputMethod<&'b str> {
|
||||
if state.is_focused() {
|
||||
} else {
|
||||
return InputMethod::Disabled;
|
||||
};
|
||||
|
||||
let text_bounds = text_layout.bounds();
|
||||
let cursor_index = match state.cursor.state(value) {
|
||||
cursor::State::Index(position) => position,
|
||||
cursor::State::Selection { start, end } => start.min(end),
|
||||
};
|
||||
let (cursor, offset) = measure_cursor_and_scroll_offset(
|
||||
state.value.raw(),
|
||||
text_bounds,
|
||||
cursor_index,
|
||||
value,
|
||||
state.cursor.affinity(),
|
||||
state.scroll_offset,
|
||||
);
|
||||
InputMethod::Enabled {
|
||||
cursor: Rectangle::new(
|
||||
Point::new(text_bounds.x + cursor - offset, text_bounds.y),
|
||||
Size::new(1.0, text_bounds.height),
|
||||
),
|
||||
purpose: if state.is_secure {
|
||||
input_method::Purpose::Secure
|
||||
} else {
|
||||
input_method::Purpose::Normal
|
||||
},
|
||||
preedit: state.preedit.as_ref().map(input_method::Preedit::as_ref),
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the [`TextInput`] with the given [`Renderer`], overriding its
|
||||
/// [`Value`] if provided.
|
||||
///
|
||||
|
|
@ -2789,6 +2889,7 @@ pub struct State {
|
|||
is_pasting: Option<Value>,
|
||||
last_click: Option<mouse::Click>,
|
||||
cursor: Cursor,
|
||||
preedit: Option<Preedit>,
|
||||
keyboard_modifiers: keyboard::Modifiers,
|
||||
scroll_offset: f32,
|
||||
}
|
||||
|
|
@ -2868,6 +2969,7 @@ impl State {
|
|||
is_pasting: None,
|
||||
last_click: None,
|
||||
cursor: Cursor::default(),
|
||||
preedit: None,
|
||||
keyboard_modifiers: keyboard::Modifiers::default(),
|
||||
scroll_offset: 0.0,
|
||||
dirty: false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue