summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor <hector@hecrj.dev>2025-02-13 05:19:32 +0100
committerLibravatar GitHub <noreply@github.com>2025-02-13 05:19:32 +0100
commitf889008e21971b461ec7c54d9a7667a23f6ab35b (patch)
tree91d9d34c87796da424104c46705e9f1aceb69f92
parent97f1db3783dca5a4f60a9f89668613de4dfe9edd (diff)
parent89a4dc2ac2a751fdcae921997bb93a76f9b667f9 (diff)
downloadiced-f889008e21971b461ec7c54d9a7667a23f6ab35b.tar.gz
iced-f889008e21971b461ec7c54d9a7667a23f6ab35b.tar.bz2
iced-f889008e21971b461ec7c54d9a7667a23f6ab35b.zip
Merge pull request #2793 from rhysd/issue-2792
Fix the initial candidate window position
-rw-r--r--core/src/input_method.rs44
-rw-r--r--core/src/shell.rs2
-rw-r--r--graphics/src/text/editor.rs31
-rw-r--r--runtime/src/user_interface.rs2
-rw-r--r--widget/src/scrollable.rs4
-rw-r--r--widget/src/text_editor.rs8
-rw-r--r--widget/src/text_input.rs59
-rw-r--r--winit/src/program/window_manager.rs93
8 files changed, 127 insertions, 116 deletions
diff --git a/core/src/input_method.rs b/core/src/input_method.rs
index 9c83b083..cd8d459d 100644
--- a/core/src/input_method.rs
+++ b/core/src/input_method.rs
@@ -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),
diff --git a/core/src/shell.rs b/core/src/shell.rs
index 509e3822..56250e2e 100644
--- a/core/src/shell.rs
+++ b/core/src/shell.rs
@@ -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,
}
}
diff --git a/graphics/src/text/editor.rs b/graphics/src/text/editor.rs
index c73d189c..765de07e 100644
--- a/graphics/src/text/editor.rs
+++ b/graphics/src/text/editor.rs
@@ -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,
diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs
index cb441678..9b396c69 100644
--- a/runtime/src/user_interface.rs
+++ b/runtime/src/user_interface.rs
@@ -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
diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs
index fe71fd6b..0cf75c04 100644
--- a/widget/src/scrollable.rs
+++ b/widget/src/scrollable.rs
@@ -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;
diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs
index ce5da9ef..7e40a56a 100644
--- a/widget/src/text_editor.rs
+++ b/widget/src/text_editor.rs
@@ -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),
}
}
}
diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs
index bb2685bd..ae3dfe4c 100644
--- a/widget/src/text_input.rs
+++ b/widget/src/text_input.rs
@@ -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,
@@ -1323,23 +1319,30 @@ where
let state = state::<Renderer>(tree);
if let Some(focus) = &mut state.is_focused {
- if focus.is_window_focused
- && matches!(
+ if focus.is_window_focused {
+ if matches!(
state.cursor.state(&self.value),
cursor::State::Index(_)
- )
- {
- focus.now = *now;
-
- let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS
- - (*now - focus.updated_at).as_millis()
- % CURSOR_BLINK_INTERVAL_MILLIS;
-
- shell.request_redraw_at(
- *now + Duration::from_millis(
- millis_until_redraw as u64,
- ),
- );
+ ) {
+ focus.now = *now;
+
+ let millis_until_redraw =
+ CURSOR_BLINK_INTERVAL_MILLIS
+ - (*now - focus.updated_at).as_millis()
+ % CURSOR_BLINK_INTERVAL_MILLIS;
+
+ shell.request_redraw_at(
+ *now + Duration::from_millis(
+ millis_until_redraw as u64,
+ ),
+ );
+ }
+
+ shell.request_input_method(&self.input_method(
+ state,
+ layout,
+ &self.value,
+ ));
}
}
}
@@ -1363,12 +1366,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 +1525,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,
diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs
index d5b334df..27306439 100644
--- a/winit/src/program/window_manager.rs
+++ b/winit/src/program/window_manager.rs
@@ -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,36 @@ 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);
- }
- }
-
- 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?
- );
-
- 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,
- );
+ InputMethod::Enabled {
+ position,
+ purpose,
+ preedit,
+ } => {
+ self.enable_ime(position, purpose);
+
+ if let Some(preedit) = preedit {
+ if preedit.content.is_empty() {
+ self.preedit = None;
+ } else {
+ let mut overlay =
+ self.preedit.take().unwrap_or_else(Preedit::new);
+
+ overlay.update(
+ position,
+ &preedit,
+ self.state.background_color(),
+ &self.renderer,
+ );
+
+ self.preedit = Some(overlay);
+ }
} else {
- let mut overlay = Preedit::new();
- overlay.update(
- position,
- &preedit,
- self.state.background_color(),
- &self.renderer,
- );
-
- self.preedit = Some(overlay);
+ self.preedit = None;
}
}
- } else {
- self.preedit = None;
}
}
@@ -268,6 +254,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>