Fix cursor positioning in TextInput on overflow

This commit is contained in:
Héctor Ramón Jiménez 2019-12-21 05:30:48 +01:00
parent 453d2d5bb0
commit 36e7ac2048
3 changed files with 95 additions and 27 deletions

View File

@ -93,6 +93,16 @@ impl text_input::Renderer for Null {
0.0
}
fn offset(
&self,
_text_bounds: Rectangle,
_size: u16,
_value: &text_input::Value,
_state: &text_input::State,
) -> f32 {
0.0
}
fn draw(
&mut self,
_bounds: Rectangle,

View File

@ -179,35 +179,40 @@ where
button: mouse::Button::Left,
state: ButtonState::Pressed,
}) => {
self.state.is_focused =
layout.bounds().contains(cursor_position);
let is_clicked = layout.bounds().contains(cursor_position);
if self.state.is_focused {
if is_clicked {
let text_layout = layout.children().next().unwrap();
let target = cursor_position.x - text_layout.bounds().x;
if target < 0.0 {
self.state.cursor_position = 0;
} else if self.is_secure {
self.state.cursor_position = find_cursor_position(
renderer,
target,
&self.value.secure(),
self.size.unwrap_or(renderer.default_size()),
0,
self.value.len(),
if target > 0.0 {
let value = if self.is_secure {
self.value.secure()
} else {
self.value.clone()
};
let size = self.size.unwrap_or(renderer.default_size());
let offset = renderer.offset(
text_layout.bounds(),
size,
&value,
&self.state,
);
} else {
self.state.cursor_position = find_cursor_position(
renderer,
target,
&self.value,
self.size.unwrap_or(renderer.default_size()),
target + offset,
&value,
size,
0,
self.value.len(),
);
}
}
self.state.is_focused = is_clicked;
}
Event::Keyboard(keyboard::Event::CharacterReceived(c))
if self.state.is_focused
@ -392,6 +397,22 @@ pub trait Renderer: crate::Renderer + Sized {
/// [`TextInput`]: struct.TextInput.html
fn measure_value(&self, value: &str, size: u16) -> f32;
/// Returns the current horizontal offset of the value of the
/// [`TextInput`].
///
/// This is the amount of horizontal scrolling applied when the [`Value`]
/// does not fit the [`TextInput`].
///
/// [`TextInput`]: struct.TextInput.html
/// [`Value`]: struct.Value.html
fn offset(
&self,
text_bounds: Rectangle,
size: u16,
value: &Value,
state: &State,
) -> f32;
/// Draws a [`TextInput`].
///
/// It receives:

View File

@ -30,6 +30,28 @@ impl text_input::Renderer for Renderer {
width
}
fn offset(
&self,
text_bounds: Rectangle,
size: u16,
value: &text_input::Value,
state: &text_input::State,
) -> f32 {
if state.is_focused() {
let (_, offset) = measure_cursor_and_scroll_offset(
self,
text_bounds,
value,
size,
state.cursor_position(value),
);
offset
} else {
0.0
}
}
fn draw(
&mut self,
bounds: Rectangle,
@ -91,11 +113,13 @@ impl text_input::Renderer for Renderer {
};
let (contents_primitive, offset) = if state.is_focused() {
let text_before_cursor =
value.until(state.cursor_position(value)).to_string();
let text_value_width =
self.measure_value(&text_before_cursor, size);
let (text_value_width, offset) = measure_cursor_and_scroll_offset(
self,
text_bounds,
value,
size,
state.cursor_position(value),
);
let cursor = Primitive::Quad {
bounds: Rectangle {
@ -112,11 +136,7 @@ impl text_input::Renderer for Renderer {
Primitive::Group {
primitives: vec![text_value, cursor],
},
Vector::new(
((text_value_width + 5.0) - text_bounds.width).max(0.0)
as u32,
0,
),
Vector::new(offset as u32, 0),
)
} else {
(text_value, Vector::new(0, 0))
@ -140,3 +160,20 @@ impl text_input::Renderer for Renderer {
)
}
}
fn measure_cursor_and_scroll_offset(
renderer: &Renderer,
text_bounds: Rectangle,
value: &text_input::Value,
size: u16,
cursor_index: usize,
) -> (f32, f32) {
use iced_native::text_input::Renderer;
let text_before_cursor = value.until(cursor_index).to_string();
let text_value_width = renderer.measure_value(&text_before_cursor, size);
let offset = ((text_value_width + 5.0) - text_bounds.width).max(0.0);
(text_value_width, offset)
}