Use Option to encode empty text case in hit test methods
This commit is contained in:
parent
93fec8d273
commit
643500bbdf
10 changed files with 59 additions and 52 deletions
|
|
@ -14,14 +14,14 @@ pub enum Hit {
|
||||||
|
|
||||||
impl Hit {
|
impl Hit {
|
||||||
/// Computes the cursor position corresponding to this [`HitTestResult`] .
|
/// Computes the cursor position corresponding to this [`HitTestResult`] .
|
||||||
pub fn cursor(&self) -> usize {
|
pub fn cursor(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::CharOffset(i) => *i,
|
Self::CharOffset(i) => i,
|
||||||
Self::NearestCharOffset(i, delta) => {
|
Self::NearestCharOffset(i, delta) => {
|
||||||
if delta.x > f32::EPSILON {
|
if delta.x > f32::EPSILON {
|
||||||
i + 1
|
i + 1
|
||||||
} else {
|
} else {
|
||||||
*i
|
i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ impl backend::Text for Backend {
|
||||||
bounds: Size,
|
bounds: Size,
|
||||||
point: iced_native::Point,
|
point: iced_native::Point,
|
||||||
nearest_only: bool,
|
nearest_only: bool,
|
||||||
) -> text::Hit {
|
) -> Option<text::Hit> {
|
||||||
self.text_pipeline.hit_test(
|
self.text_pipeline.hit_test(
|
||||||
contents,
|
contents,
|
||||||
size,
|
size,
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ impl Pipeline {
|
||||||
bounds: iced_native::Size,
|
bounds: iced_native::Size,
|
||||||
point: iced_native::Point,
|
point: iced_native::Point,
|
||||||
nearest_only: bool,
|
nearest_only: bool,
|
||||||
) -> Hit {
|
) -> Option<Hit> {
|
||||||
use glow_glyph::GlyphCruncher;
|
use glow_glyph::GlyphCruncher;
|
||||||
|
|
||||||
let glow_glyph::FontId(font_id) = self.find_font(font);
|
let glow_glyph::FontId(font_id) = self.find_font(font);
|
||||||
|
|
@ -182,23 +182,25 @@ impl Pipeline {
|
||||||
if !nearest_only {
|
if !nearest_only {
|
||||||
for (idx, bounds) in bounds.clone() {
|
for (idx, bounds) in bounds.clone() {
|
||||||
if bounds.contains(point) {
|
if bounds.contains(point) {
|
||||||
return Hit::CharOffset(char_index(idx));
|
return Some(Hit::CharOffset(char_index(idx)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (idx, nearest) = bounds.fold(
|
let (idx, nearest) = bounds.fold(
|
||||||
(0usize, iced_native::Point::ORIGIN),
|
(None, iced_native::Point::ORIGIN),
|
||||||
|acc: (usize, iced_native::Point), (idx, bounds)| {
|
|best, (idx, bounds)| {
|
||||||
if bounds.center().distance(point) < acc.1.distance(point) {
|
let center = bounds.center();
|
||||||
(idx, bounds.center())
|
|
||||||
|
if center.distance(point) < best.1.distance(point) {
|
||||||
|
(Some(idx), center)
|
||||||
} else {
|
} else {
|
||||||
acc
|
best
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Hit::NearestCharOffset(char_index(idx), (point - nearest).into())
|
idx.map(|idx| Hit::NearestCharOffset(char_index(idx), point - nearest))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trim_measurement_cache(&mut self) {
|
pub fn trim_measurement_cache(&mut self) {
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ pub trait Text {
|
||||||
bounds: Size,
|
bounds: Size,
|
||||||
point: Point,
|
point: Point,
|
||||||
nearest_only: bool,
|
nearest_only: bool,
|
||||||
) -> text::Hit;
|
) -> Option<text::Hit>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A graphics backend that supports image rendering.
|
/// A graphics backend that supports image rendering.
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ where
|
||||||
bounds: Size,
|
bounds: Size,
|
||||||
point: Point,
|
point: Point,
|
||||||
nearest_only: bool,
|
nearest_only: bool,
|
||||||
) -> text::Hit {
|
) -> Option<text::Hit> {
|
||||||
self.backend().hit_test(
|
self.backend().hit_test(
|
||||||
content,
|
content,
|
||||||
size,
|
size,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
button, checkbox, column, container, pane_grid, progress_bar, radio, row,
|
button, checkbox, column, container, pane_grid, progress_bar, radio, row,
|
||||||
scrollable, slider, text, text_input, toggler, Color, Element, Font,
|
scrollable, slider, text, text_input, toggler, Color, Element, Font,
|
||||||
HorizontalAlignment, Layout, Padding, Point, Rectangle, Renderer, Size,
|
HorizontalAlignment, Layout, Padding, Point, Rectangle, Renderer, Size,
|
||||||
Vector, VerticalAlignment,
|
VerticalAlignment,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A renderer that does nothing.
|
/// A renderer that does nothing.
|
||||||
|
|
@ -75,8 +75,8 @@ impl text::Renderer for Null {
|
||||||
_bounds: Size,
|
_bounds: Size,
|
||||||
_point: Point,
|
_point: Point,
|
||||||
_nearest_only: bool,
|
_nearest_only: bool,
|
||||||
) -> text::Hit {
|
) -> Option<text::Hit> {
|
||||||
text::Hit::NearestCharOffset(0, Vector::new(0., 0.))
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ pub trait Renderer: crate::Renderer {
|
||||||
bounds: Size,
|
bounds: Size,
|
||||||
point: Point,
|
point: Point,
|
||||||
nearest_only: bool,
|
nearest_only: bool,
|
||||||
) -> Hit;
|
) -> Option<Hit>;
|
||||||
|
|
||||||
/// Draws a [`Text`] fragment.
|
/// Draws a [`Text`] fragment.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -268,41 +268,42 @@ where
|
||||||
|
|
||||||
match click.kind() {
|
match click.kind() {
|
||||||
click::Kind::Single => {
|
click::Kind::Single => {
|
||||||
if target > 0.0 {
|
let position = if target > 0.0 {
|
||||||
let value = if self.is_secure {
|
let value = if self.is_secure {
|
||||||
self.value.secure()
|
self.value.secure()
|
||||||
} else {
|
} else {
|
||||||
self.value.clone()
|
self.value.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let position = renderer.find_cursor_position(
|
renderer.find_cursor_position(
|
||||||
text_layout.bounds(),
|
text_layout.bounds(),
|
||||||
self.font,
|
self.font,
|
||||||
self.size,
|
self.size,
|
||||||
&value,
|
&value,
|
||||||
&self.state,
|
&self.state,
|
||||||
target,
|
target,
|
||||||
);
|
)
|
||||||
|
|
||||||
self.state.cursor.move_to(position);
|
|
||||||
} else {
|
} else {
|
||||||
self.state.cursor.move_to(0);
|
None
|
||||||
}
|
};
|
||||||
|
|
||||||
|
self.state.cursor.move_to(position.unwrap_or(0));
|
||||||
self.state.is_dragging = true;
|
self.state.is_dragging = true;
|
||||||
}
|
}
|
||||||
click::Kind::Double => {
|
click::Kind::Double => {
|
||||||
if self.is_secure {
|
if self.is_secure {
|
||||||
self.state.cursor.select_all(&self.value);
|
self.state.cursor.select_all(&self.value);
|
||||||
} else {
|
} else {
|
||||||
let position = renderer.find_cursor_position(
|
let position = renderer
|
||||||
text_layout.bounds(),
|
.find_cursor_position(
|
||||||
self.font,
|
text_layout.bounds(),
|
||||||
self.size,
|
self.font,
|
||||||
&self.value,
|
self.size,
|
||||||
&self.state,
|
&self.value,
|
||||||
target,
|
&self.state,
|
||||||
);
|
target,
|
||||||
|
)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
self.state.cursor.select_range(
|
self.state.cursor.select_range(
|
||||||
self.value.previous_start_of_word(position),
|
self.value.previous_start_of_word(position),
|
||||||
|
|
@ -341,14 +342,16 @@ where
|
||||||
self.value.clone()
|
self.value.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let position = renderer.find_cursor_position(
|
let position = renderer
|
||||||
text_layout.bounds(),
|
.find_cursor_position(
|
||||||
self.font,
|
text_layout.bounds(),
|
||||||
self.size,
|
self.font,
|
||||||
&value,
|
self.size,
|
||||||
&self.state,
|
&value,
|
||||||
target,
|
&self.state,
|
||||||
);
|
target,
|
||||||
|
)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
self.state.cursor.select_range(
|
self.state.cursor.select_range(
|
||||||
self.state.cursor.start(&value),
|
self.state.cursor.start(&value),
|
||||||
|
|
@ -702,7 +705,7 @@ pub trait Renderer: text::Renderer + Sized {
|
||||||
value: &Value,
|
value: &Value,
|
||||||
state: &State,
|
state: &State,
|
||||||
x: f32,
|
x: f32,
|
||||||
) -> usize {
|
) -> Option<usize> {
|
||||||
let size = size.unwrap_or(self.default_size());
|
let size = size.unwrap_or(self.default_size());
|
||||||
|
|
||||||
let offset = self.offset(text_bounds, font, size, &value, &state);
|
let offset = self.offset(text_bounds, font, size, &value, &state);
|
||||||
|
|
@ -715,7 +718,7 @@ pub trait Renderer: text::Renderer + Sized {
|
||||||
Point::new(x + offset, text_bounds.height / 2.0),
|
Point::new(x + offset, text_bounds.height / 2.0),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.cursor()
|
.map(text::Hit::cursor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -284,7 +284,7 @@ impl backend::Text for Backend {
|
||||||
bounds: Size,
|
bounds: Size,
|
||||||
point: iced_native::Point,
|
point: iced_native::Point,
|
||||||
nearest_only: bool,
|
nearest_only: bool,
|
||||||
) -> text::Hit {
|
) -> Option<text::Hit> {
|
||||||
self.text_pipeline.hit_test(
|
self.text_pipeline.hit_test(
|
||||||
contents,
|
contents,
|
||||||
size,
|
size,
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ impl Pipeline {
|
||||||
bounds: iced_native::Size,
|
bounds: iced_native::Size,
|
||||||
point: iced_native::Point,
|
point: iced_native::Point,
|
||||||
nearest_only: bool,
|
nearest_only: bool,
|
||||||
) -> Hit {
|
) -> Option<Hit> {
|
||||||
use wgpu_glyph::GlyphCruncher;
|
use wgpu_glyph::GlyphCruncher;
|
||||||
|
|
||||||
let wgpu_glyph::FontId(font_id) = self.find_font(font);
|
let wgpu_glyph::FontId(font_id) = self.find_font(font);
|
||||||
|
|
@ -190,23 +190,25 @@ impl Pipeline {
|
||||||
if !nearest_only {
|
if !nearest_only {
|
||||||
for (idx, bounds) in bounds.clone() {
|
for (idx, bounds) in bounds.clone() {
|
||||||
if bounds.contains(point) {
|
if bounds.contains(point) {
|
||||||
return Hit::CharOffset(char_index(idx));
|
return Some(Hit::CharOffset(char_index(idx)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (idx, nearest) = bounds.fold(
|
let (idx, nearest) = bounds.fold(
|
||||||
(0usize, iced_native::Point::ORIGIN),
|
(None, iced_native::Point::ORIGIN),
|
||||||
|acc: (usize, iced_native::Point), (idx, bounds)| {
|
|best, (idx, bounds)| {
|
||||||
if bounds.center().distance(point) < acc.1.distance(point) {
|
let center = bounds.center();
|
||||||
(idx, bounds.center())
|
|
||||||
|
if center.distance(point) < best.1.distance(point) {
|
||||||
|
(Some(idx), center)
|
||||||
} else {
|
} else {
|
||||||
acc
|
best
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Hit::NearestCharOffset(char_index(idx), (point - nearest).into())
|
idx.map(|idx| Hit::NearestCharOffset(char_index(idx), point - nearest))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trim_measurement_cache(&mut self) {
|
pub fn trim_measurement_cache(&mut self) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue