Make hit detection public
This commit is contained in:
parent
e32cfedfcf
commit
c031944b45
1 changed files with 36 additions and 27 deletions
|
|
@ -88,16 +88,16 @@ pub struct TextLayoutRun<'a> {
|
||||||
pub line_y: i32,
|
pub line_y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TextLayoutRunIter<'a> {
|
pub struct TextLayoutRunIter<'a, 'b> {
|
||||||
buffer: &'a TextBuffer<'a>,
|
buffer: &'b TextBuffer<'a>,
|
||||||
line_i: usize,
|
line_i: usize,
|
||||||
layout_i: usize,
|
layout_i: usize,
|
||||||
line_y: i32,
|
line_y: i32,
|
||||||
total_layout: i32,
|
total_layout: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TextLayoutRunIter<'a> {
|
impl<'a, 'b> TextLayoutRunIter<'a, 'b> {
|
||||||
pub fn new(buffer: &'a TextBuffer<'a>) -> Self {
|
pub fn new(buffer: &'b TextBuffer<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer,
|
buffer,
|
||||||
line_i: 0,
|
line_i: 0,
|
||||||
|
|
@ -108,8 +108,8 @@ impl<'a> TextLayoutRunIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for TextLayoutRunIter<'a> {
|
impl<'a, 'b> Iterator for TextLayoutRunIter<'a, 'b> {
|
||||||
type Item = TextLayoutRun<'a>;
|
type Item = TextLayoutRun<'b>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
while let Some(line) = self.buffer.lines.get(self.line_i) {
|
while let Some(line) = self.buffer.lines.get(self.line_i) {
|
||||||
|
|
@ -759,7 +759,12 @@ impl<'a> TextBuffer<'a> {
|
||||||
TextAction::Click { x, y } => {
|
TextAction::Click { x, y } => {
|
||||||
self.select_opt = None;
|
self.select_opt = None;
|
||||||
|
|
||||||
self.click(x, y);
|
if let Some(new_cursor) = self.hit(x, y) {
|
||||||
|
if new_cursor != self.cursor {
|
||||||
|
self.cursor = new_cursor;
|
||||||
|
self.redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
TextAction::Drag { x, y } => {
|
TextAction::Drag { x, y } => {
|
||||||
if self.select_opt.is_none() {
|
if self.select_opt.is_none() {
|
||||||
|
|
@ -767,7 +772,12 @@ impl<'a> TextBuffer<'a> {
|
||||||
self.redraw = true;
|
self.redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.click(x, y);
|
if let Some(new_cursor) = self.hit(x, y) {
|
||||||
|
if new_cursor != self.cursor {
|
||||||
|
self.cursor = new_cursor;
|
||||||
|
self.redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
TextAction::Scroll { lines } => {
|
TextAction::Scroll { lines } => {
|
||||||
self.scroll += lines;
|
self.scroll += lines;
|
||||||
|
|
@ -778,25 +788,26 @@ impl<'a> TextBuffer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn click(&mut self, mouse_x: i32, mouse_y: i32) {
|
/// Convert x, y position to TextCursor (hit detection)
|
||||||
|
pub fn hit(&self, x: i32, y: i32) -> Option<TextCursor> {
|
||||||
let instant = Instant::now();
|
let instant = Instant::now();
|
||||||
|
|
||||||
let font_size = self.metrics.font_size;
|
let font_size = self.metrics.font_size;
|
||||||
let line_height = self.metrics.line_height;
|
let line_height = self.metrics.line_height;
|
||||||
|
|
||||||
let mut new_cursor = self.cursor;
|
let mut new_cursor_opt = None;
|
||||||
|
|
||||||
for run in self.layout_runs() {
|
for run in self.layout_runs() {
|
||||||
let line_y = run.line_y;
|
let line_y = run.line_y;
|
||||||
|
|
||||||
if mouse_y >= line_y - font_size
|
if y >= line_y - font_size
|
||||||
&& mouse_y < line_y - font_size + line_height
|
&& y < line_y - font_size + line_height
|
||||||
{
|
{
|
||||||
let mut new_cursor_glyph = run.glyphs.len();
|
let mut new_cursor_glyph = run.glyphs.len();
|
||||||
let mut new_cursor_char = 0;
|
let mut new_cursor_char = 0;
|
||||||
'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
|
'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
|
||||||
if mouse_x >= glyph.x as i32
|
if x >= glyph.x as i32
|
||||||
&& mouse_x <= (glyph.x + glyph.w) as i32
|
&& x <= (glyph.x + glyph.w) as i32
|
||||||
{
|
{
|
||||||
new_cursor_glyph = glyph_i;
|
new_cursor_glyph = glyph_i;
|
||||||
|
|
||||||
|
|
@ -805,12 +816,12 @@ impl<'a> TextBuffer<'a> {
|
||||||
let mut egc_x = glyph.x;
|
let mut egc_x = glyph.x;
|
||||||
let egc_w = glyph.w / (total as f32);
|
let egc_w = glyph.w / (total as f32);
|
||||||
for (egc_i, egc) in cluster.grapheme_indices(true) {
|
for (egc_i, egc) in cluster.grapheme_indices(true) {
|
||||||
if mouse_x >= egc_x as i32
|
if x >= egc_x as i32
|
||||||
&& mouse_x <= (egc_x + egc_w) as i32
|
&& x <= (egc_x + egc_w) as i32
|
||||||
{
|
{
|
||||||
new_cursor_char = egc_i;
|
new_cursor_char = egc_i;
|
||||||
|
|
||||||
let right_half = mouse_x >= (egc_x + egc_w / 2.0) as i32;
|
let right_half = x >= (egc_x + egc_w / 2.0) as i32;
|
||||||
if right_half != glyph.rtl {
|
if right_half != glyph.rtl {
|
||||||
// If clicking on last half of glyph, move cursor past glyph
|
// If clicking on last half of glyph, move cursor past glyph
|
||||||
new_cursor_char += egc.len();
|
new_cursor_char += egc.len();
|
||||||
|
|
@ -820,7 +831,7 @@ impl<'a> TextBuffer<'a> {
|
||||||
egc_x += egc_w;
|
egc_x += egc_w;
|
||||||
}
|
}
|
||||||
|
|
||||||
let right_half = mouse_x >= (glyph.x + glyph.w / 2.0) as i32;
|
let right_half = x >= (glyph.x + glyph.w / 2.0) as i32;
|
||||||
if right_half != glyph.rtl {
|
if right_half != glyph.rtl {
|
||||||
// If clicking on last half of glyph, move cursor past glyph
|
// If clicking on last half of glyph, move cursor past glyph
|
||||||
new_cursor_char = cluster.len();
|
new_cursor_char = cluster.len();
|
||||||
|
|
@ -829,8 +840,7 @@ impl<'a> TextBuffer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new_cursor.line = run.line_i;
|
let mut new_cursor = TextCursor::new(run.line_i, 0);
|
||||||
new_cursor.index = 0;
|
|
||||||
|
|
||||||
match run.glyphs.get(new_cursor_glyph) {
|
match run.glyphs.get(new_cursor_glyph) {
|
||||||
Some(glyph) => {
|
Some(glyph) => {
|
||||||
|
|
@ -859,21 +869,20 @@ impl<'a> TextBuffer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_cursor_opt = Some(new_cursor);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_cursor != self.cursor {
|
|
||||||
self.cursor = new_cursor;
|
|
||||||
self.redraw = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let duration = instant.elapsed();
|
let duration = instant.elapsed();
|
||||||
log::trace!("click({}, {}): {:?}", mouse_x, mouse_y, duration);
|
log::trace!("click({}, {}): {:?}", x, y, duration);
|
||||||
|
|
||||||
|
new_cursor_opt
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the visible layout runs for rendering and other tasks
|
/// Get the visible layout runs for rendering and other tasks
|
||||||
pub fn layout_runs(&self) -> TextLayoutRunIter {
|
pub fn layout_runs<'b>(&'b self) -> TextLayoutRunIter<'a, 'b> {
|
||||||
TextLayoutRunIter::new(self)
|
TextLayoutRunIter::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue