Refactor and simplify input_method API
This commit is contained in:
parent
d5ee9c2795
commit
ae10adda74
19 changed files with 540 additions and 472 deletions
|
|
@ -6,7 +6,7 @@ use crate::core::window;
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Action<Message> {
|
||||
message_to_publish: Option<Message>,
|
||||
redraw_request: Option<window::RedrawRequest>,
|
||||
redraw_request: window::RedrawRequest,
|
||||
event_status: event::Status,
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ impl<Message> Action<Message> {
|
|||
fn new() -> Self {
|
||||
Self {
|
||||
message_to_publish: None,
|
||||
redraw_request: None,
|
||||
redraw_request: window::RedrawRequest::Wait,
|
||||
event_status: event::Status::Ignored,
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ impl<Message> Action<Message> {
|
|||
/// soon as possible; without publishing any `Message`.
|
||||
pub fn request_redraw() -> Self {
|
||||
Self {
|
||||
redraw_request: Some(window::RedrawRequest::NextFrame),
|
||||
redraw_request: window::RedrawRequest::NextFrame,
|
||||
..Self::new()
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ impl<Message> Action<Message> {
|
|||
/// blinking caret on a text input.
|
||||
pub fn request_redraw_at(at: Instant) -> Self {
|
||||
Self {
|
||||
redraw_request: Some(window::RedrawRequest::At(at)),
|
||||
redraw_request: window::RedrawRequest::At(at),
|
||||
..Self::new()
|
||||
}
|
||||
}
|
||||
|
|
@ -75,11 +75,7 @@ impl<Message> Action<Message> {
|
|||
/// widget implementations.
|
||||
pub fn into_inner(
|
||||
self,
|
||||
) -> (
|
||||
Option<Message>,
|
||||
Option<window::RedrawRequest>,
|
||||
event::Status,
|
||||
) {
|
||||
) -> (Option<Message>, window::RedrawRequest, event::Status) {
|
||||
(
|
||||
self.message_to_publish,
|
||||
self.redraw_request,
|
||||
|
|
|
|||
|
|
@ -238,27 +238,18 @@ where
|
|||
{
|
||||
let (message, redraw_request, event_status) = action.into_inner();
|
||||
|
||||
shell.request_redraw_at(redraw_request);
|
||||
|
||||
if let Some(message) = message {
|
||||
shell.publish(message);
|
||||
}
|
||||
|
||||
if let Some(redraw_request) = redraw_request {
|
||||
match redraw_request {
|
||||
window::RedrawRequest::NextFrame => {
|
||||
shell.request_redraw();
|
||||
}
|
||||
window::RedrawRequest::At(at) => {
|
||||
shell.request_redraw_at(at);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if event_status == event::Status::Captured {
|
||||
shell.capture_event();
|
||||
}
|
||||
}
|
||||
|
||||
if shell.redraw_request() != Some(window::RedrawRequest::NextFrame) {
|
||||
if shell.redraw_request() != window::RedrawRequest::NextFrame {
|
||||
let mouse_interaction = self
|
||||
.mouse_interaction(tree, layout, cursor, viewport, renderer);
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ use crate::core::renderer;
|
|||
use crate::core::text;
|
||||
use crate::core::time::Instant;
|
||||
use crate::core::widget::{self, Widget};
|
||||
use crate::core::window;
|
||||
use crate::core::{
|
||||
Clipboard, Element, Event, Length, Padding, Rectangle, Shell, Size, Theme,
|
||||
Vector,
|
||||
|
|
@ -554,17 +553,8 @@ where
|
|||
shell.capture_event();
|
||||
}
|
||||
|
||||
if let Some(redraw_request) = local_shell.redraw_request() {
|
||||
match redraw_request {
|
||||
window::RedrawRequest::NextFrame => {
|
||||
shell.request_redraw();
|
||||
}
|
||||
window::RedrawRequest::At(at) => {
|
||||
shell.request_redraw_at(at);
|
||||
}
|
||||
}
|
||||
}
|
||||
shell.update_caret_info(local_shell.caret_info());
|
||||
shell.request_redraw_at(local_shell.redraw_request());
|
||||
shell.request_input_method(local_shell.input_method());
|
||||
|
||||
// Then finally react to them here
|
||||
for message in local_messages {
|
||||
|
|
@ -757,7 +747,7 @@ where
|
|||
&mut local_shell,
|
||||
viewport,
|
||||
);
|
||||
shell.update_caret_info(local_shell.caret_info());
|
||||
shell.request_input_method(local_shell.input_method());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use crate::core::overlay;
|
|||
use crate::core::renderer;
|
||||
use crate::core::widget;
|
||||
use crate::core::widget::tree::{self, Tree};
|
||||
use crate::core::window;
|
||||
use crate::core::{
|
||||
self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector,
|
||||
Widget,
|
||||
|
|
@ -344,18 +343,8 @@ where
|
|||
}
|
||||
|
||||
local_shell.revalidate_layout(|| shell.invalidate_layout());
|
||||
|
||||
if let Some(redraw_request) = local_shell.redraw_request() {
|
||||
match redraw_request {
|
||||
window::RedrawRequest::NextFrame => {
|
||||
shell.request_redraw();
|
||||
}
|
||||
window::RedrawRequest::At(at) => {
|
||||
shell.request_redraw_at(at);
|
||||
}
|
||||
}
|
||||
}
|
||||
shell.update_caret_info(local_shell.caret_info());
|
||||
shell.request_redraw_at(local_shell.redraw_request());
|
||||
shell.request_input_method(local_shell.input_method());
|
||||
|
||||
if !local_messages.is_empty() {
|
||||
let mut heads = self.state.take().unwrap().into_heads();
|
||||
|
|
@ -630,18 +619,8 @@ where
|
|||
}
|
||||
|
||||
local_shell.revalidate_layout(|| shell.invalidate_layout());
|
||||
|
||||
if let Some(redraw_request) = local_shell.redraw_request() {
|
||||
match redraw_request {
|
||||
window::RedrawRequest::NextFrame => {
|
||||
shell.request_redraw();
|
||||
}
|
||||
window::RedrawRequest::At(at) => {
|
||||
shell.request_redraw_at(at);
|
||||
}
|
||||
}
|
||||
}
|
||||
shell.update_caret_info(local_shell.caret_info());
|
||||
shell.request_redraw_at(local_shell.redraw_request());
|
||||
shell.request_input_method(local_shell.input_method());
|
||||
|
||||
if !local_messages.is_empty() {
|
||||
let mut inner =
|
||||
|
|
|
|||
|
|
@ -687,7 +687,7 @@ where
|
|||
_ => {}
|
||||
}
|
||||
|
||||
if shell.redraw_request() != Some(window::RedrawRequest::NextFrame) {
|
||||
if shell.redraw_request() != window::RedrawRequest::NextFrame {
|
||||
let interaction = self
|
||||
.grid_interaction(action, layout, cursor)
|
||||
.or_else(|| {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ use crate::core::widget::operation::{self, Operation};
|
|||
use crate::core::widget::tree::{self, Tree};
|
||||
use crate::core::window;
|
||||
use crate::core::{
|
||||
self, Background, CaretInfo, Clipboard, Color, Element, Event, Layout,
|
||||
self, Background, Clipboard, Color, Element, Event, InputMethod, Layout,
|
||||
Length, Padding, Pixels, Point, Rectangle, Shell, Size, Theme, Vector,
|
||||
Widget,
|
||||
};
|
||||
|
|
@ -730,7 +730,6 @@ where
|
|||
let translation =
|
||||
state.translation(self.direction, bounds, content_bounds);
|
||||
|
||||
let children_may_have_caret = shell.caret_info().is_none();
|
||||
self.content.as_widget_mut().update(
|
||||
&mut tree.children[0],
|
||||
event.clone(),
|
||||
|
|
@ -746,17 +745,10 @@ where
|
|||
},
|
||||
);
|
||||
|
||||
if children_may_have_caret {
|
||||
if let Some(caret_info) = shell.caret_info() {
|
||||
shell.update_caret_info(Some(CaretInfo {
|
||||
position: Point::new(
|
||||
caret_info.position.x - translation.x,
|
||||
caret_info.position.y - translation.y,
|
||||
),
|
||||
input_method_allowed: caret_info
|
||||
.input_method_allowed,
|
||||
}));
|
||||
}
|
||||
if let InputMethod::Open { position, .. } =
|
||||
shell.input_method_mut()
|
||||
{
|
||||
*position = *position + translation;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use crate::core::mouse;
|
|||
use crate::core::renderer;
|
||||
use crate::core::widget::tree::{self, Tree};
|
||||
use crate::core::widget::{self, Widget};
|
||||
use crate::core::window;
|
||||
use crate::core::{Clipboard, Element, Event, Length, Rectangle, Shell, Size};
|
||||
use crate::renderer::wgpu::primitive;
|
||||
|
||||
|
|
@ -105,21 +104,12 @@ where
|
|||
{
|
||||
let (message, redraw_request, event_status) = action.into_inner();
|
||||
|
||||
shell.request_redraw_at(redraw_request);
|
||||
|
||||
if let Some(message) = message {
|
||||
shell.publish(message);
|
||||
}
|
||||
|
||||
if let Some(redraw_request) = redraw_request {
|
||||
match redraw_request {
|
||||
window::RedrawRequest::NextFrame => {
|
||||
shell.request_redraw();
|
||||
}
|
||||
window::RedrawRequest::At(at) => {
|
||||
shell.request_redraw_at(at);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if event_status == event::Status::Captured {
|
||||
shell.capture_event();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ use crate::core::widget::operation;
|
|||
use crate::core::widget::{self, Widget};
|
||||
use crate::core::window;
|
||||
use crate::core::{
|
||||
Background, Border, CaretInfo, Color, Element, Event, Length, Padding,
|
||||
Background, Border, Color, Element, Event, InputMethod, Length, Padding,
|
||||
Pixels, Point, Rectangle, Shell, Size, SmolStr, Theme, Vector,
|
||||
};
|
||||
|
||||
|
|
@ -324,43 +324,49 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
fn caret_rect(
|
||||
fn input_method<'b>(
|
||||
&self,
|
||||
tree: &widget::Tree,
|
||||
state: &'b State<Highlighter>,
|
||||
renderer: &Renderer,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<Rectangle> {
|
||||
let bounds = layout.bounds();
|
||||
) -> InputMethod<&'b str> {
|
||||
let Some(Focus {
|
||||
is_window_focused: true,
|
||||
is_ime_open,
|
||||
..
|
||||
}) = &state.focus
|
||||
else {
|
||||
return InputMethod::Disabled;
|
||||
};
|
||||
|
||||
let Some(preedit) = &is_ime_open else {
|
||||
return InputMethod::Allowed;
|
||||
};
|
||||
|
||||
let bounds = layout.bounds();
|
||||
let internal = self.content.0.borrow_mut();
|
||||
let state = tree.state.downcast_ref::<State<Highlighter>>();
|
||||
|
||||
let text_bounds = bounds.shrink(self.padding);
|
||||
let translation = text_bounds.position() - Point::ORIGIN;
|
||||
|
||||
if state.focus.is_some() {
|
||||
let position = match internal.editor.cursor() {
|
||||
Cursor::Caret(position) => position,
|
||||
Cursor::Selection(ranges) => ranges
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap_or(Rectangle::default())
|
||||
.position(),
|
||||
};
|
||||
Some(Rectangle::new(
|
||||
position + translation,
|
||||
Size::new(
|
||||
1.0,
|
||||
self.line_height
|
||||
.to_absolute(
|
||||
self.text_size
|
||||
.unwrap_or_else(|| renderer.default_size()),
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
let cursor = match internal.editor.cursor() {
|
||||
Cursor::Caret(position) => position,
|
||||
Cursor::Selection(ranges) => {
|
||||
ranges.first().cloned().unwrap_or_default().position()
|
||||
}
|
||||
};
|
||||
|
||||
let line_height = self.line_height.to_absolute(
|
||||
self.text_size.unwrap_or_else(|| renderer.default_size()),
|
||||
);
|
||||
|
||||
let position =
|
||||
cursor + translation + Vector::new(0.0, f32::from(line_height));
|
||||
|
||||
InputMethod::Open {
|
||||
position,
|
||||
purpose: input_method::Purpose::Normal,
|
||||
preedit: Some(preedit),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -499,11 +505,12 @@ pub struct State<Highlighter: text::Highlighter> {
|
|||
highlighter_format_address: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct Focus {
|
||||
updated_at: Instant,
|
||||
now: Instant,
|
||||
is_window_focused: bool,
|
||||
is_ime_open: Option<String>,
|
||||
}
|
||||
|
||||
impl Focus {
|
||||
|
|
@ -516,6 +523,7 @@ impl Focus {
|
|||
updated_at: now,
|
||||
now,
|
||||
is_window_focused: true,
|
||||
is_ime_open: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -742,11 +750,23 @@ where
|
|||
}));
|
||||
shell.capture_event();
|
||||
}
|
||||
Update::Commit(text) => {
|
||||
shell.publish(on_edit(Action::Edit(Edit::Paste(
|
||||
Arc::new(text),
|
||||
))));
|
||||
}
|
||||
Update::InputMethod(update) => match update {
|
||||
Ime::Toggle(is_open) => {
|
||||
if let Some(focus) = &mut state.focus {
|
||||
focus.is_ime_open = is_open.then(String::new);
|
||||
}
|
||||
}
|
||||
Ime::Preedit(text) => {
|
||||
if let Some(focus) = &mut state.focus {
|
||||
focus.is_ime_open = Some(text);
|
||||
}
|
||||
}
|
||||
Ime::Commit(text) => {
|
||||
shell.publish(on_edit(Action::Edit(Edit::Paste(
|
||||
Arc::new(text),
|
||||
))));
|
||||
}
|
||||
},
|
||||
Update::Binding(binding) => {
|
||||
fn apply_binding<
|
||||
H: text::Highlighter,
|
||||
|
|
@ -871,22 +891,12 @@ where
|
|||
}
|
||||
};
|
||||
|
||||
shell.update_caret_info(if state.is_focused() {
|
||||
let rect =
|
||||
self.caret_rect(tree, renderer, layout).unwrap_or_default();
|
||||
|
||||
let bottom_left = Point::new(rect.x, rect.y + rect.height);
|
||||
|
||||
Some(CaretInfo {
|
||||
position: bottom_left,
|
||||
input_method_allowed: true,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
});
|
||||
|
||||
if is_redraw {
|
||||
self.last_status = Some(status);
|
||||
|
||||
shell.request_input_method(
|
||||
&self.input_method(state, renderer, layout),
|
||||
);
|
||||
} else if self
|
||||
.last_status
|
||||
.is_some_and(|last_status| status != last_status)
|
||||
|
|
@ -1189,10 +1199,16 @@ enum Update<Message> {
|
|||
Drag(Point),
|
||||
Release,
|
||||
Scroll(f32),
|
||||
Commit(String),
|
||||
InputMethod(Ime),
|
||||
Binding(Binding<Message>),
|
||||
}
|
||||
|
||||
enum Ime {
|
||||
Toggle(bool),
|
||||
Preedit(String),
|
||||
Commit(String),
|
||||
}
|
||||
|
||||
impl<Message> Update<Message> {
|
||||
fn from_event<H: Highlighter>(
|
||||
event: Event,
|
||||
|
|
@ -1252,9 +1268,20 @@ impl<Message> Update<Message> {
|
|||
}
|
||||
_ => None,
|
||||
},
|
||||
Event::InputMethod(input_method::Event::Commit(text)) => {
|
||||
Some(Update::Commit(text))
|
||||
}
|
||||
Event::InputMethod(event) => match event {
|
||||
input_method::Event::Opened | input_method::Event::Closed => {
|
||||
Some(Update::InputMethod(Ime::Toggle(matches!(
|
||||
event,
|
||||
input_method::Event::Opened
|
||||
))))
|
||||
}
|
||||
input_method::Event::Preedit(content, _range) => {
|
||||
Some(Update::InputMethod(Ime::Preedit(content)))
|
||||
}
|
||||
input_method::Event::Commit(content) => {
|
||||
Some(Update::InputMethod(Ime::Commit(content)))
|
||||
}
|
||||
},
|
||||
Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key,
|
||||
modifiers,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ use crate::core::widget::operation::{self, Operation};
|
|||
use crate::core::widget::tree::{self, Tree};
|
||||
use crate::core::window;
|
||||
use crate::core::{
|
||||
Background, Border, CaretInfo, Color, Element, Event, Layout, Length,
|
||||
Background, Border, Color, Element, Event, InputMethod, Layout, Length,
|
||||
Padding, Pixels, Point, Rectangle, Shell, Size, Theme, Vector, Widget,
|
||||
};
|
||||
use crate::runtime::task::{self, Task};
|
||||
|
|
@ -392,14 +392,24 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn caret_rect(
|
||||
fn input_method<'b>(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
state: &'b State<Renderer::Paragraph>,
|
||||
layout: Layout<'_>,
|
||||
value: Option<&Value>,
|
||||
) -> Option<Rectangle> {
|
||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
||||
let value = value.unwrap_or(&self.value);
|
||||
value: &Value,
|
||||
) -> InputMethod<&'b str> {
|
||||
let Some(Focus {
|
||||
is_window_focused: true,
|
||||
is_ime_open,
|
||||
..
|
||||
}) = &state.is_focused
|
||||
else {
|
||||
return InputMethod::Disabled;
|
||||
};
|
||||
|
||||
let Some(preedit) = 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);
|
||||
|
|
@ -407,38 +417,32 @@ where
|
|||
let mut children_layout = layout.children();
|
||||
let text_bounds = children_layout.next().unwrap().bounds();
|
||||
|
||||
if state
|
||||
.is_focused
|
||||
.is_some_and(|focus| focus.is_window_focused)
|
||||
{
|
||||
let caret_index = match state.cursor.state(value) {
|
||||
cursor::State::Index(position) => position,
|
||||
cursor::State::Selection { start, end } => start.min(end),
|
||||
};
|
||||
let caret_index = match state.cursor.state(value) {
|
||||
cursor::State::Index(position) => position,
|
||||
cursor::State::Selection { start, end } => start.min(end),
|
||||
};
|
||||
|
||||
let text = state.value.raw();
|
||||
let (caret_x, offset) = measure_cursor_and_scroll_offset(
|
||||
text,
|
||||
text_bounds,
|
||||
caret_index,
|
||||
);
|
||||
let text = state.value.raw();
|
||||
let (cursor_x, scroll_offset) =
|
||||
measure_cursor_and_scroll_offset(text, text_bounds, caret_index);
|
||||
|
||||
let alignment_offset = alignment_offset(
|
||||
text_bounds.width,
|
||||
text.min_width(),
|
||||
self.alignment,
|
||||
);
|
||||
let alignment_offset = alignment_offset(
|
||||
text_bounds.width,
|
||||
text.min_width(),
|
||||
self.alignment,
|
||||
);
|
||||
|
||||
let x = (text_bounds.x + caret_x).floor();
|
||||
let x = (text_bounds.x + cursor_x).floor() - scroll_offset
|
||||
+ alignment_offset;
|
||||
|
||||
Some(Rectangle {
|
||||
x: (alignment_offset - offset) + x,
|
||||
y: text_bounds.y,
|
||||
width: 1.0,
|
||||
height: text_bounds.height,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
InputMethod::Open {
|
||||
position: Point::new(x, text_bounds.y),
|
||||
purpose: if self.is_secure {
|
||||
input_method::Purpose::Secure
|
||||
} else {
|
||||
input_method::Purpose::Normal
|
||||
},
|
||||
preedit: Some(preedit),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -725,6 +729,7 @@ where
|
|||
updated_at: now,
|
||||
now,
|
||||
is_window_focused: true,
|
||||
is_ime_open: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
@ -1248,28 +1253,46 @@ where
|
|||
|
||||
state.keyboard_modifiers = *modifiers;
|
||||
}
|
||||
Event::InputMethod(input_method::Event::Commit(text)) => {
|
||||
let state = state::<Renderer>(tree);
|
||||
Event::InputMethod(event) => match event {
|
||||
input_method::Event::Opened | input_method::Event::Closed => {
|
||||
let state = state::<Renderer>(tree);
|
||||
|
||||
if let Some(focus) = &mut state.is_focused {
|
||||
let Some(on_input) = &self.on_input else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut editor =
|
||||
Editor::new(&mut self.value, &mut state.cursor);
|
||||
editor.paste(Value::new(text));
|
||||
|
||||
focus.updated_at = Instant::now();
|
||||
state.is_pasting = None;
|
||||
|
||||
let message = (on_input)(editor.contents());
|
||||
shell.publish(message);
|
||||
shell.capture_event();
|
||||
|
||||
update_cache(state, &self.value);
|
||||
if let Some(focus) = &mut state.is_focused {
|
||||
focus.is_ime_open =
|
||||
matches!(event, input_method::Event::Opened)
|
||||
.then(String::new);
|
||||
}
|
||||
}
|
||||
}
|
||||
input_method::Event::Preedit(content, _range) => {
|
||||
let state = state::<Renderer>(tree);
|
||||
|
||||
if let Some(focus) = &mut state.is_focused {
|
||||
focus.is_ime_open = Some(content.to_owned());
|
||||
}
|
||||
}
|
||||
input_method::Event::Commit(text) => {
|
||||
let state = state::<Renderer>(tree);
|
||||
|
||||
if let Some(focus) = &mut state.is_focused {
|
||||
let Some(on_input) = &self.on_input else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut editor =
|
||||
Editor::new(&mut self.value, &mut state.cursor);
|
||||
editor.paste(Value::new(text));
|
||||
|
||||
focus.updated_at = Instant::now();
|
||||
state.is_pasting = None;
|
||||
|
||||
let message = (on_input)(editor.contents());
|
||||
shell.publish(message);
|
||||
shell.capture_event();
|
||||
|
||||
update_cache(state, &self.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
Event::Window(window::Event::Unfocused) => {
|
||||
let state = state::<Renderer>(tree);
|
||||
|
||||
|
|
@ -1329,21 +1352,14 @@ where
|
|||
Status::Active
|
||||
};
|
||||
|
||||
shell.update_caret_info(if state.is_focused() {
|
||||
let rect = self
|
||||
.caret_rect(tree, layout, Some(&self.value))
|
||||
.unwrap_or(Rectangle::with_size(Size::<f32>::default()));
|
||||
let bottom_left = Point::new(rect.x, rect.y + rect.height);
|
||||
Some(CaretInfo {
|
||||
position: bottom_left,
|
||||
input_method_allowed: true,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
});
|
||||
|
||||
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)
|
||||
|
|
@ -1517,11 +1533,12 @@ fn state<Renderer: text::Renderer>(
|
|||
tree.state.downcast_mut::<State<Renderer::Paragraph>>()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct Focus {
|
||||
updated_at: Instant,
|
||||
now: Instant,
|
||||
is_window_focused: bool,
|
||||
is_ime_open: Option<String>,
|
||||
}
|
||||
|
||||
impl<P: text::Paragraph> State<P> {
|
||||
|
|
@ -1548,6 +1565,7 @@ impl<P: text::Paragraph> State<P> {
|
|||
updated_at: now,
|
||||
now,
|
||||
is_window_focused: true,
|
||||
is_ime_open: None,
|
||||
});
|
||||
|
||||
self.move_cursor_to_end();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue