feat(ime): minimal IME support for the text_box widget (#539)
* Support CJK input method * Fix the input method window occasionally jumps around Use the previous caret(text cursor) position to place the input method window when the position is not available * refactor: Omit calling request_input_method() if doesn't focus - iced UserInterface.update() starts with InputMethod::Disabled state, and merges any input_method_request() which is InputMethod::Enabled in widgets tree traversal. - We don't enable the input method feature if the widget doesn't have focus, we can safely omit request_input_method() call itself.
This commit is contained in:
parent
5f96f6191b
commit
4c53d607f7
1 changed files with 68 additions and 0 deletions
|
|
@ -7,6 +7,7 @@ use cosmic::{
|
|||
Border, Radians, Shell, Transformation,
|
||||
clipboard::Clipboard,
|
||||
image,
|
||||
input_method::{Event as InputMethodEvent, InputMethod, Preedit, Purpose},
|
||||
keyboard::{Key, key::Named},
|
||||
layout::{self, Layout},
|
||||
renderer::{self, Quad, Renderer as _},
|
||||
|
|
@ -16,6 +17,7 @@ use cosmic::{
|
|||
operation::{self, Operation},
|
||||
tree,
|
||||
},
|
||||
window::Event as WindowEvent,
|
||||
},
|
||||
iced::{
|
||||
Color, Element, Length, Padding, Point, Rectangle, Size, Vector,
|
||||
|
|
@ -127,6 +129,35 @@ where
|
|||
self.on_focus = Some(on_focus);
|
||||
self
|
||||
}
|
||||
|
||||
fn input_method<'b>(
|
||||
&self,
|
||||
state: &'b State,
|
||||
scale_factor: f32,
|
||||
layout: Layout<'_>,
|
||||
) -> InputMethod<&'b str> {
|
||||
if !state.is_focused {
|
||||
return InputMethod::Disabled;
|
||||
};
|
||||
|
||||
let editor_pos = layout.position() + [self.padding.left, self.padding.top].into();
|
||||
let (caret_x, caret_y) = state.caret_position;
|
||||
InputMethod::Enabled {
|
||||
cursor: Rectangle::new(
|
||||
Point::new(
|
||||
editor_pos.x
|
||||
+ (caret_x as f32 + state.editor_offset_x.get() as f32) / scale_factor,
|
||||
editor_pos.y + (caret_y as f32) / scale_factor,
|
||||
),
|
||||
Size::new(
|
||||
1.0,
|
||||
(self.metrics.scale(scale_factor).line_height) / scale_factor,
|
||||
),
|
||||
),
|
||||
purpose: Purpose::Normal,
|
||||
preedit: state.preedit.as_ref().map(Preedit::as_ref),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_box<'a, Message>(
|
||||
|
|
@ -1101,6 +1132,33 @@ where
|
|||
}
|
||||
state.modifiers = *modifiers;
|
||||
}
|
||||
Event::InputMethod(event) => match event {
|
||||
InputMethodEvent::Opened | InputMethodEvent::Closed => {
|
||||
let metrics = self.metrics.scale(scale_factor);
|
||||
state.preedit = matches!(event, InputMethodEvent::Opened).then(|| {
|
||||
let mut preedit = Preedit::new();
|
||||
preedit.text_size = Some(metrics.font_size.into());
|
||||
preedit
|
||||
});
|
||||
}
|
||||
InputMethodEvent::Preedit(content, selection) => {
|
||||
if state.is_focused {
|
||||
state.preedit = Some(Preedit {
|
||||
content: content.to_owned(),
|
||||
selection: selection.clone(),
|
||||
text_size: Some(self.metrics.font_size.into()),
|
||||
});
|
||||
}
|
||||
}
|
||||
InputMethodEvent::Commit(text) => {
|
||||
if state.is_focused {
|
||||
editor.start_change();
|
||||
editor.insert_string(&text, None);
|
||||
editor.finish_change();
|
||||
shell.capture_event();
|
||||
}
|
||||
}
|
||||
},
|
||||
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||
state.is_focused = true;
|
||||
|
|
@ -1336,6 +1394,12 @@ where
|
|||
shell.capture_event();
|
||||
}
|
||||
}
|
||||
Event::Window(WindowEvent::RedrawRequested(_now)) => {
|
||||
if state.is_focused {
|
||||
state.caret_position = editor.cursor_position().unwrap_or(state.caret_position);
|
||||
shell.request_input_method(&self.input_method(state, scale_factor, layout));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
|
@ -1385,6 +1449,8 @@ pub struct State {
|
|||
scrollbar_h_rect: Cell<Option<Rectangle<f32>>>,
|
||||
handle_opt: Mutex<Option<image::Handle>>,
|
||||
shift_anchor: Mutex<Option<Cursor>>,
|
||||
caret_position: (i32, i32),
|
||||
preedit: Option<Preedit>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
|
@ -1402,6 +1468,8 @@ impl State {
|
|||
scrollbar_h_rect: Cell::new(None),
|
||||
handle_opt: Mutex::new(None),
|
||||
shift_anchor: Mutex::new(None),
|
||||
caret_position: (0, 0),
|
||||
preedit: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue