input: Fix to position cursor at correct offset on mouse down or select. (#580)
Ref: https://github.com/zed-industries/zed/pull/23603 --- ## TODO Here still have a little bug in multiple lines and soft wrapped line. Only the first line can get correct position, the wrapped line is always got the offset +1, so the first char of that soft wrapped line, we can't position the cursor at the first.
This commit is contained in:
parent
1689f4208f
commit
68de692290
1 changed files with 51 additions and 9 deletions
|
|
@ -12,7 +12,7 @@ use unicode_segmentation::*;
|
|||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
actions, div, point, px, AnyElement, AppContext, Bounds, ClickEvent, ClipboardItem,
|
||||
Context as _, Entity, EventEmitter, FocusHandle, FocusableView, Half, InteractiveElement as _,
|
||||
Context as _, Entity, EventEmitter, FocusHandle, FocusableView, InteractiveElement as _,
|
||||
IntoElement, KeyBinding, KeyDownEvent, Model, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||
MouseUpEvent, ParentElement as _, Pixels, Point, Rems, Render, ScrollHandle, ScrollWheelEvent,
|
||||
SharedString, Styled as _, UTF16Selection, ViewContext, ViewInputHandler, WindowContext,
|
||||
|
|
@ -776,9 +776,9 @@ impl TextInput {
|
|||
}
|
||||
|
||||
if event.modifiers.shift {
|
||||
self.select_to(self.previous_boundary(offset), cx);
|
||||
self.select_to(offset, cx);
|
||||
} else {
|
||||
self.move_to(self.previous_boundary(offset), cx)
|
||||
self.move_to(offset, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -896,6 +896,39 @@ impl TextInput {
|
|||
}
|
||||
}
|
||||
|
||||
// Fork of `closest_index_for_x` method from `gpui::text::Layout`
|
||||
// https://github.com/zed-industries/zed/blob/d1be419fff415329b38f26aff90488700702c82a/crates/gpui/src/text_system/line_layout.rs#L74
|
||||
fn closest_index_for_x(&self, line: &WrappedLine, x: Pixels) -> usize {
|
||||
let mut prev_index = 0;
|
||||
let mut prev_x = px(0.);
|
||||
|
||||
for run in line.unwrapped_layout.runs.iter() {
|
||||
for glyph in run.glyphs.iter() {
|
||||
if glyph.position.x >= x {
|
||||
if glyph.position.x - x < x - prev_x {
|
||||
return glyph.index;
|
||||
} else {
|
||||
return prev_index;
|
||||
}
|
||||
}
|
||||
prev_index = glyph.index;
|
||||
prev_x = glyph.position.x;
|
||||
}
|
||||
}
|
||||
|
||||
// HOTFIX:
|
||||
//
|
||||
// Wait https://github.com/zed-industries/zed/pull/23603
|
||||
if line.unwrapped_layout.len == 1 {
|
||||
if x > line.width() / 2.0 {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
line.unwrapped_layout.len
|
||||
}
|
||||
|
||||
fn index_for_mouse_position(&self, position: Point<Pixels>, _: &WindowContext) -> usize {
|
||||
// If the text is empty, always return 0
|
||||
if self.text.is_empty() {
|
||||
|
|
@ -926,16 +959,25 @@ impl TextInput {
|
|||
|
||||
for line in lines.iter() {
|
||||
let line_origin = self.line_origin_with_y_offset(&mut y_offset, &line, line_height);
|
||||
let mut pos = inner_position - line_origin;
|
||||
// Ignore the y position in single line mode, only check x position.
|
||||
let pos = inner_position - line_origin;
|
||||
let closest_index = self.closest_index_for_x(line, pos.x);
|
||||
|
||||
// Return offset by use closest_index_for_x if is single line mode.
|
||||
if self.is_single_line() {
|
||||
pos.y = line_height.half();
|
||||
return closest_index;
|
||||
}
|
||||
|
||||
let index_result = line.index_for_position(pos, line_height);
|
||||
if let Ok(v) = index_result {
|
||||
// Add 1 for place cursor after the character.
|
||||
index += v + 1;
|
||||
// FIXME: If the line have soft wrap, only first line can get correct position (closest_index is correct).
|
||||
// The second line or more, the position is always at offset +1,
|
||||
// but if we mouse down on left half or character, we expect the cursor is at offset.
|
||||
// We need find a way to improve.
|
||||
if pos.y > line_height / 2. {
|
||||
index += v + 1;
|
||||
} else {
|
||||
index += closest_index;
|
||||
}
|
||||
break;
|
||||
} else if let Ok(_) = line.index_for_position(point(px(0.), pos.y), line_height) {
|
||||
// Click in the this line but not in the text, move cursor to the end of the line.
|
||||
|
|
@ -1168,7 +1210,7 @@ impl TextInput {
|
|||
}
|
||||
|
||||
let offset = self.index_for_mouse_position(event.position, cx);
|
||||
self.select_to(self.previous_boundary(offset), cx);
|
||||
self.select_to(offset, cx);
|
||||
}
|
||||
|
||||
fn is_valid_input(&self, new_text: &str) -> bool {
|
||||
|
|
|
|||
Loading…
Reference in a new issue