Implement textual hit testing
This commit is contained in:
parent
8333b8f88c
commit
aa63841e2c
13 changed files with 341 additions and 75 deletions
|
|
@ -61,8 +61,8 @@ mod debug;
|
|||
mod debug;
|
||||
|
||||
pub use iced_core::{
|
||||
menu, Align, Background, Color, Font, HorizontalAlignment, Length, Menu,
|
||||
Padding, Point, Rectangle, Size, Vector, VerticalAlignment,
|
||||
menu, Align, Background, Color, Font, HitTestResult, HorizontalAlignment,
|
||||
Length, Menu, Padding, Point, Rectangle, Size, Vector, VerticalAlignment,
|
||||
};
|
||||
pub use iced_futures::{executor, futures, Command};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{
|
||||
button, checkbox, column, container, pane_grid, progress_bar, radio, row,
|
||||
scrollable, slider, text, text_input, toggler, Color, Element, Font,
|
||||
HorizontalAlignment, Layout, Padding, Point, Rectangle, Renderer, Size,
|
||||
VerticalAlignment,
|
||||
HitTestResult, HorizontalAlignment, Layout, Padding, Point, Rectangle,
|
||||
Renderer, Size, Vector, VerticalAlignment,
|
||||
};
|
||||
|
||||
/// A renderer that does nothing.
|
||||
|
|
@ -67,6 +67,18 @@ impl text::Renderer for Null {
|
|||
(0.0, 20.0)
|
||||
}
|
||||
|
||||
fn hit_test(
|
||||
&self,
|
||||
_contents: &str,
|
||||
_size: f32,
|
||||
_font: Self::Font,
|
||||
_bounds: Size,
|
||||
_point: Point,
|
||||
_nearest_only: bool,
|
||||
) -> HitTestResult {
|
||||
HitTestResult::NearestCharOffset(0, Vector::new(0., 0.))
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
_defaults: &Self::Defaults,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Write some text for your users to read.
|
||||
use crate::{
|
||||
layout, Color, Element, Hasher, HorizontalAlignment, Layout, Length, Point,
|
||||
Rectangle, Size, VerticalAlignment, Widget,
|
||||
layout, Color, Element, Hasher, HitTestResult, HorizontalAlignment, Layout,
|
||||
Length, Point, Rectangle, Size, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use std::hash::Hash;
|
||||
|
|
@ -179,6 +179,23 @@ pub trait Renderer: crate::Renderer {
|
|||
bounds: Size,
|
||||
) -> (f32, f32);
|
||||
|
||||
/// Tests whether the provided point is within the boundaries of [`Text`]
|
||||
/// laid out with the given parameters, returning information about
|
||||
/// the nearest character.
|
||||
///
|
||||
/// If nearest_only is true, the hit test does not consider whether the
|
||||
/// the point is interior to any glyph bounds, returning only the character
|
||||
/// with the nearest centeroid.
|
||||
fn hit_test(
|
||||
&self,
|
||||
contents: &str,
|
||||
size: f32,
|
||||
font: Self::Font,
|
||||
bounds: Size,
|
||||
point: Point,
|
||||
nearest_only: bool,
|
||||
) -> HitTestResult;
|
||||
|
||||
/// Draws a [`Text`] fragment.
|
||||
///
|
||||
/// It receives:
|
||||
|
|
|
|||
|
|
@ -707,15 +707,15 @@ pub trait Renderer: text::Renderer + Sized {
|
|||
|
||||
let offset = self.offset(text_bounds, font, size, &value, &state);
|
||||
|
||||
find_cursor_position(
|
||||
self,
|
||||
&value,
|
||||
self.hit_test(
|
||||
&value.to_string(),
|
||||
size.into(),
|
||||
font,
|
||||
size,
|
||||
x + offset,
|
||||
0,
|
||||
value.len(),
|
||||
Size::INFINITY,
|
||||
Point::new(x + offset, text_bounds.height / 2.0),
|
||||
true,
|
||||
)
|
||||
.cursor()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -803,62 +803,6 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Reduce allocations
|
||||
fn find_cursor_position<Renderer: self::Renderer>(
|
||||
renderer: &Renderer,
|
||||
value: &Value,
|
||||
font: Renderer::Font,
|
||||
size: u16,
|
||||
target: f32,
|
||||
start: usize,
|
||||
end: usize,
|
||||
) -> usize {
|
||||
if start >= end {
|
||||
if start == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let prev = value.until(start - 1);
|
||||
let next = value.until(start);
|
||||
|
||||
let prev_width = renderer.measure_value(&prev.to_string(), size, font);
|
||||
let next_width = renderer.measure_value(&next.to_string(), size, font);
|
||||
|
||||
if next_width - target > target - prev_width {
|
||||
return start - 1;
|
||||
} else {
|
||||
return start;
|
||||
}
|
||||
}
|
||||
|
||||
let index = (end - start) / 2;
|
||||
let subvalue = value.until(start + index);
|
||||
|
||||
let width = renderer.measure_value(&subvalue.to_string(), size, font);
|
||||
|
||||
if width > target {
|
||||
find_cursor_position(
|
||||
renderer,
|
||||
value,
|
||||
font,
|
||||
size,
|
||||
target,
|
||||
start,
|
||||
start + index,
|
||||
)
|
||||
} else {
|
||||
find_cursor_position(
|
||||
renderer,
|
||||
value,
|
||||
font,
|
||||
size,
|
||||
target,
|
||||
start + index + 1,
|
||||
end,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
mod platform {
|
||||
use crate::keyboard;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue