Simplify InputMethod API with only two states
Co-authored-by: rhysd <lin90162@yahoo.co.jp> Co-authored-by: KENZ <KENZ.gelsoft@gmail.com>
This commit is contained in:
parent
97f1db3783
commit
7979125ed7
8 changed files with 113 additions and 100 deletions
|
|
@ -6,14 +6,10 @@ use std::ops::Range;
|
|||
/// The input method strategy of a widget.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InputMethod<T = String> {
|
||||
/// No input method strategy has been specified.
|
||||
None,
|
||||
/// No input method is allowed.
|
||||
/// Input method is disabled.
|
||||
Disabled,
|
||||
/// Input methods are allowed, but not open yet.
|
||||
Allowed,
|
||||
/// Input method is open.
|
||||
Open {
|
||||
/// Input method is enabled.
|
||||
Enabled {
|
||||
/// The position at which the input method dialog should be placed.
|
||||
position: Point,
|
||||
/// The [`Purpose`] of the input method.
|
||||
|
|
@ -91,13 +87,13 @@ impl InputMethod {
|
|||
/// # use iced_core::input_method::{InputMethod, Purpose, Preedit};
|
||||
/// # use iced_core::Point;
|
||||
///
|
||||
/// let open = InputMethod::Open {
|
||||
/// let open = InputMethod::Enabled {
|
||||
/// position: Point::ORIGIN,
|
||||
/// purpose: Purpose::Normal,
|
||||
/// preedit: Some(Preedit { content: "1".to_owned(), selection: None, text_size: None }),
|
||||
/// };
|
||||
///
|
||||
/// let open_2 = InputMethod::Open {
|
||||
/// let open_2 = InputMethod::Enabled {
|
||||
/// position: Point::ORIGIN,
|
||||
/// purpose: Purpose::Secure,
|
||||
/// preedit: Some(Preedit { content: "2".to_owned(), selection: None, text_size: None }),
|
||||
|
|
@ -105,12 +101,6 @@ impl InputMethod {
|
|||
///
|
||||
/// let mut ime = InputMethod::Disabled;
|
||||
///
|
||||
/// ime.merge(&InputMethod::<String>::Allowed);
|
||||
/// assert_eq!(ime, InputMethod::Allowed);
|
||||
///
|
||||
/// ime.merge(&InputMethod::<String>::Disabled);
|
||||
/// assert_eq!(ime, InputMethod::Allowed);
|
||||
///
|
||||
/// ime.merge(&open);
|
||||
/// assert_eq!(ime, open);
|
||||
///
|
||||
|
|
@ -118,22 +108,16 @@ impl InputMethod {
|
|||
/// assert_eq!(ime, open);
|
||||
/// ```
|
||||
pub fn merge<T: AsRef<str>>(&mut self, other: &InputMethod<T>) {
|
||||
match (&self, other) {
|
||||
(InputMethod::Open { .. }, _)
|
||||
| (
|
||||
InputMethod::Allowed,
|
||||
InputMethod::None | InputMethod::Disabled,
|
||||
)
|
||||
| (InputMethod::Disabled, InputMethod::None) => {}
|
||||
_ => {
|
||||
*self = other.to_owned();
|
||||
}
|
||||
if let InputMethod::Enabled { .. } = self {
|
||||
return;
|
||||
}
|
||||
|
||||
*self = other.to_owned();
|
||||
}
|
||||
|
||||
/// Returns true if the [`InputMethod`] is open.
|
||||
pub fn is_open(&self) -> bool {
|
||||
matches!(self, Self::Open { .. })
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
matches!(self, Self::Enabled { .. })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,14 +128,12 @@ impl<T> InputMethod<T> {
|
|||
T: AsRef<str>,
|
||||
{
|
||||
match self {
|
||||
Self::None => InputMethod::None,
|
||||
Self::Disabled => InputMethod::Disabled,
|
||||
Self::Allowed => InputMethod::Allowed,
|
||||
Self::Open {
|
||||
Self::Enabled {
|
||||
position,
|
||||
purpose,
|
||||
preedit,
|
||||
} => InputMethod::Open {
|
||||
} => InputMethod::Enabled {
|
||||
position: *position,
|
||||
purpose: *purpose,
|
||||
preedit: preedit.as_ref().map(Preedit::to_owned),
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl<'a, Message> Shell<'a, Message> {
|
|||
redraw_request: window::RedrawRequest::Wait,
|
||||
is_layout_invalid: false,
|
||||
are_widgets_invalid: false,
|
||||
input_method: InputMethod::None,
|
||||
input_method: InputMethod::Disabled,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use cosmic_text::Edit as _;
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::sync::{self, Arc};
|
||||
use std::sync::{self, Arc, RwLock};
|
||||
|
||||
/// A multi-line text editor.
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
|
@ -19,6 +19,7 @@ pub struct Editor(Option<Arc<Internal>>);
|
|||
|
||||
struct Internal {
|
||||
editor: cosmic_text::Editor<'static>,
|
||||
cursor: RwLock<Option<Cursor>>,
|
||||
font: Font,
|
||||
bounds: Size,
|
||||
topmost_line_changed: Option<usize>,
|
||||
|
|
@ -114,10 +115,14 @@ impl editor::Editor for Editor {
|
|||
fn cursor(&self) -> editor::Cursor {
|
||||
let internal = self.internal();
|
||||
|
||||
if let Ok(Some(cursor)) = internal.cursor.read().as_deref() {
|
||||
return cursor.clone();
|
||||
}
|
||||
|
||||
let cursor = internal.editor.cursor();
|
||||
let buffer = buffer_from_editor(&internal.editor);
|
||||
|
||||
match internal.editor.selection_bounds() {
|
||||
let cursor = match internal.editor.selection_bounds() {
|
||||
Some((start, end)) => {
|
||||
let line_height = buffer.metrics().line_height;
|
||||
let selected_lines = end.line - start.line + 1;
|
||||
|
|
@ -237,7 +242,12 @@ impl editor::Editor for Editor {
|
|||
- buffer.scroll().vertical,
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
*internal.cursor.write().expect("Write to cursor cache") =
|
||||
Some(cursor.clone());
|
||||
|
||||
cursor
|
||||
}
|
||||
|
||||
fn cursor_position(&self) -> (usize, usize) {
|
||||
|
|
@ -259,6 +269,13 @@ impl editor::Editor for Editor {
|
|||
|
||||
let editor = &mut internal.editor;
|
||||
|
||||
// Clear cursor cache
|
||||
let _ = internal
|
||||
.cursor
|
||||
.write()
|
||||
.expect("Write to cursor cache")
|
||||
.take();
|
||||
|
||||
match action {
|
||||
// Motion events
|
||||
Action::Move(motion) => {
|
||||
|
|
@ -527,6 +544,13 @@ impl editor::Editor for Editor {
|
|||
|
||||
internal.editor.shape_as_needed(font_system.raw(), false);
|
||||
|
||||
// Clear cursor cache
|
||||
let _ = internal
|
||||
.cursor
|
||||
.write()
|
||||
.expect("Write to cursor cache")
|
||||
.take();
|
||||
|
||||
self.0 = Some(Arc::new(internal));
|
||||
}
|
||||
|
||||
|
|
@ -635,6 +659,7 @@ impl Default for Internal {
|
|||
line_height: 1.0,
|
||||
},
|
||||
)),
|
||||
cursor: RwLock::new(None),
|
||||
font: Font::default(),
|
||||
bounds: Size::ZERO,
|
||||
topmost_line_changed: None,
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ where
|
|||
|
||||
let mut outdated = false;
|
||||
let mut redraw_request = window::RedrawRequest::Wait;
|
||||
let mut input_method = InputMethod::None;
|
||||
let mut input_method = InputMethod::Disabled;
|
||||
|
||||
let mut manual_overlay = ManuallyDrop::new(
|
||||
self.root
|
||||
|
|
|
|||
|
|
@ -729,7 +729,7 @@ where
|
|||
_ => mouse::Cursor::Unavailable,
|
||||
};
|
||||
|
||||
let had_input_method = shell.input_method().is_open();
|
||||
let had_input_method = shell.input_method().is_enabled();
|
||||
|
||||
let translation =
|
||||
state.translation(self.direction, bounds, content_bounds);
|
||||
|
|
@ -750,7 +750,7 @@ where
|
|||
);
|
||||
|
||||
if !had_input_method {
|
||||
if let InputMethod::Open { position, .. } =
|
||||
if let InputMethod::Enabled { position, .. } =
|
||||
shell.input_method_mut()
|
||||
{
|
||||
*position = *position - translation;
|
||||
|
|
|
|||
|
|
@ -339,10 +339,6 @@ where
|
|||
return InputMethod::Disabled;
|
||||
};
|
||||
|
||||
let Some(preedit) = &state.preedit else {
|
||||
return InputMethod::Allowed;
|
||||
};
|
||||
|
||||
let bounds = layout.bounds();
|
||||
let internal = self.content.0.borrow_mut();
|
||||
|
||||
|
|
@ -363,10 +359,10 @@ where
|
|||
let position =
|
||||
cursor + translation + Vector::new(0.0, f32::from(line_height));
|
||||
|
||||
InputMethod::Open {
|
||||
InputMethod::Enabled {
|
||||
position,
|
||||
purpose: input_method::Purpose::Normal,
|
||||
preedit: Some(preedit.as_ref()),
|
||||
preedit: state.preedit.as_ref().map(input_method::Preedit::as_ref),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,10 +406,6 @@ where
|
|||
return InputMethod::Disabled;
|
||||
};
|
||||
|
||||
let Some(preedit) = &state.is_ime_open else {
|
||||
return InputMethod::Allowed;
|
||||
};
|
||||
|
||||
let secure_value = self.is_secure.then(|| value.secure());
|
||||
let value = secure_value.as_ref().unwrap_or(value);
|
||||
|
||||
|
|
@ -433,14 +429,14 @@ where
|
|||
let x = (text_bounds.x + cursor_x).floor() - scroll_offset
|
||||
+ alignment_offset;
|
||||
|
||||
InputMethod::Open {
|
||||
InputMethod::Enabled {
|
||||
position: Point::new(x, text_bounds.y + text_bounds.height),
|
||||
purpose: if self.is_secure {
|
||||
input_method::Purpose::Secure
|
||||
} else {
|
||||
input_method::Purpose::Normal
|
||||
},
|
||||
preedit: Some(preedit.as_ref()),
|
||||
preedit: state.preedit.as_ref().map(input_method::Preedit::as_ref),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -584,7 +580,7 @@ where
|
|||
let draw = |renderer: &mut Renderer, viewport| {
|
||||
let paragraph = if text.is_empty()
|
||||
&& state
|
||||
.is_ime_open
|
||||
.preedit
|
||||
.as_ref()
|
||||
.map(|preedit| preedit.content.is_empty())
|
||||
.unwrap_or(true)
|
||||
|
|
@ -1260,7 +1256,7 @@ where
|
|||
input_method::Event::Opened | input_method::Event::Closed => {
|
||||
let state = state::<Renderer>(tree);
|
||||
|
||||
state.is_ime_open =
|
||||
state.preedit =
|
||||
matches!(event, input_method::Event::Opened)
|
||||
.then(input_method::Preedit::new);
|
||||
|
||||
|
|
@ -1270,7 +1266,7 @@ where
|
|||
let state = state::<Renderer>(tree);
|
||||
|
||||
if state.is_focused.is_some() {
|
||||
state.is_ime_open = Some(input_method::Preedit {
|
||||
state.preedit = Some(input_method::Preedit {
|
||||
content: content.to_owned(),
|
||||
selection: selection.clone(),
|
||||
text_size: self.size,
|
||||
|
|
@ -1340,6 +1336,12 @@ where
|
|||
millis_until_redraw as u64,
|
||||
),
|
||||
);
|
||||
|
||||
shell.request_input_method(&self.input_method(
|
||||
state,
|
||||
layout,
|
||||
&self.value,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1363,12 +1365,6 @@ where
|
|||
|
||||
if let Event::Window(window::Event::RedrawRequested(_now)) = event {
|
||||
self.last_status = Some(status);
|
||||
|
||||
shell.request_input_method(&self.input_method(
|
||||
state,
|
||||
layout,
|
||||
&self.value,
|
||||
));
|
||||
} else if self
|
||||
.last_status
|
||||
.is_some_and(|last_status| status != last_status)
|
||||
|
|
@ -1528,9 +1524,9 @@ pub struct State<P: text::Paragraph> {
|
|||
placeholder: paragraph::Plain<P>,
|
||||
icon: paragraph::Plain<P>,
|
||||
is_focused: Option<Focus>,
|
||||
is_ime_open: Option<input_method::Preedit>,
|
||||
is_dragging: bool,
|
||||
is_pasting: Option<Value>,
|
||||
preedit: Option<input_method::Preedit>,
|
||||
last_click: Option<mouse::Click>,
|
||||
cursor: Cursor,
|
||||
keyboard_modifiers: keyboard::Modifiers,
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ where
|
|||
mouse_interaction: mouse::Interaction::None,
|
||||
redraw_at: None,
|
||||
preedit: None,
|
||||
ime_state: None,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -166,6 +167,7 @@ where
|
|||
pub renderer: P::Renderer,
|
||||
pub redraw_at: Option<Instant>,
|
||||
preedit: Option<Preedit<P::Renderer>>,
|
||||
ime_state: Option<(Point, input_method::Purpose)>,
|
||||
}
|
||||
|
||||
impl<P, C> Window<P, C>
|
||||
|
|
@ -206,52 +208,39 @@ where
|
|||
|
||||
pub fn request_input_method(&mut self, input_method: InputMethod) {
|
||||
match input_method {
|
||||
InputMethod::None => {}
|
||||
InputMethod::Disabled => {
|
||||
self.raw.set_ime_allowed(false);
|
||||
self.disable_ime();
|
||||
}
|
||||
InputMethod::Allowed | InputMethod::Open { .. } => {
|
||||
self.raw.set_ime_allowed(true);
|
||||
}
|
||||
}
|
||||
InputMethod::Enabled {
|
||||
position,
|
||||
purpose,
|
||||
preedit,
|
||||
} => {
|
||||
self.enable_ime(position, purpose);
|
||||
|
||||
if let InputMethod::Open {
|
||||
position,
|
||||
purpose,
|
||||
preedit,
|
||||
} = input_method
|
||||
{
|
||||
self.raw.set_ime_cursor_area(
|
||||
LogicalPosition::new(position.x, position.y),
|
||||
LogicalSize::new(10, 10), // TODO?
|
||||
);
|
||||
if let Some(preedit) = preedit {
|
||||
if preedit.content.is_empty() {
|
||||
self.preedit = None;
|
||||
} else if let Some(overlay) = &mut self.preedit {
|
||||
overlay.update(
|
||||
position,
|
||||
&preedit,
|
||||
self.state.background_color(),
|
||||
&self.renderer,
|
||||
);
|
||||
} else {
|
||||
let mut overlay = Preedit::new();
|
||||
overlay.update(
|
||||
position,
|
||||
&preedit,
|
||||
self.state.background_color(),
|
||||
&self.renderer,
|
||||
);
|
||||
|
||||
self.raw.set_ime_purpose(conversion::ime_purpose(purpose));
|
||||
|
||||
if let Some(preedit) = preedit {
|
||||
if preedit.content.is_empty() {
|
||||
self.preedit = None;
|
||||
} else if let Some(overlay) = &mut self.preedit {
|
||||
overlay.update(
|
||||
position,
|
||||
&preedit,
|
||||
self.state.background_color(),
|
||||
&self.renderer,
|
||||
);
|
||||
} else {
|
||||
let mut overlay = Preedit::new();
|
||||
overlay.update(
|
||||
position,
|
||||
&preedit,
|
||||
self.state.background_color(),
|
||||
&self.renderer,
|
||||
);
|
||||
|
||||
self.preedit = Some(overlay);
|
||||
self.preedit = Some(overlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.preedit = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268,6 +257,31 @@ where
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_ime(&mut self, position: Point, purpose: input_method::Purpose) {
|
||||
if self.ime_state.is_none() {
|
||||
self.raw.set_ime_allowed(true);
|
||||
}
|
||||
|
||||
if self.ime_state != Some((position, purpose)) {
|
||||
self.raw.set_ime_cursor_area(
|
||||
LogicalPosition::new(position.x, position.y),
|
||||
LogicalSize::new(10, 10), // TODO?
|
||||
);
|
||||
self.raw.set_ime_purpose(conversion::ime_purpose(purpose));
|
||||
|
||||
self.ime_state = Some((position, purpose));
|
||||
}
|
||||
}
|
||||
|
||||
fn disable_ime(&mut self) {
|
||||
if self.ime_state.is_some() {
|
||||
self.raw.set_ime_allowed(false);
|
||||
self.ime_state = None;
|
||||
}
|
||||
|
||||
self.preedit = None;
|
||||
}
|
||||
}
|
||||
|
||||
struct Preedit<Renderer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue