diff --git a/Cargo.lock b/Cargo.lock index e44d683b..144cf5ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,7 +474,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools 0.11.0", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", diff --git a/crates/ui/src/input/input.rs b/crates/ui/src/input/input.rs index b334dee4..cb9a545f 100644 --- a/crates/ui/src/input/input.rs +++ b/crates/ui/src/input/input.rs @@ -929,6 +929,61 @@ impl TextInput { line.unwrapped_layout.len } + /// Get the closest index for position + /// + /// Fork: + /// https://github.com/zed-industries/zed/blob/1c322c0f2daa50f46644832a6a1cdf15bcf921ea/crates/gpui/src/text_system/line_layout.rs#L272 + /// + /// Wait PR https://github.com/zed-industries/zed/pull/23668 + pub fn closest_index_for_position( + &self, + line: &WrappedLine, + position: Point, + line_height: Pixels, + ) -> Result { + let wrapped_line_ix = (position.y / line_height) as usize; + + let wrapped_line_start_index; + let wrapped_line_start_x; + if wrapped_line_ix > 0 { + let Some(line_start_boundary) = line.wrap_boundaries.get(wrapped_line_ix - 1) else { + return Err(0); + }; + let run = &line.unwrapped_layout.runs[line_start_boundary.run_ix]; + let glyph = &run.glyphs[line_start_boundary.glyph_ix]; + wrapped_line_start_index = glyph.index; + wrapped_line_start_x = glyph.position.x; + } else { + wrapped_line_start_index = 0; + wrapped_line_start_x = Pixels::ZERO; + }; + + let wrapped_line_end_index; + let wrapped_line_end_x; + if wrapped_line_ix < line.wrap_boundaries.len() { + let next_wrap_boundary_ix = wrapped_line_ix; + let next_wrap_boundary = line.wrap_boundaries[next_wrap_boundary_ix]; + let run = &line.unwrapped_layout.runs[next_wrap_boundary.run_ix]; + let glyph = &run.glyphs[next_wrap_boundary.glyph_ix]; + wrapped_line_end_index = glyph.index; + wrapped_line_end_x = glyph.position.x; + } else { + wrapped_line_end_index = line.unwrapped_layout.len; + wrapped_line_end_x = line.unwrapped_layout.width; + }; + + let mut position_in_unwrapped_line = position; + position_in_unwrapped_line.x += wrapped_line_start_x; + if position_in_unwrapped_line.x < wrapped_line_start_x { + Err(wrapped_line_start_index) + } else if position_in_unwrapped_line.x >= wrapped_line_end_x { + Err(wrapped_line_end_index) + } else { + // HOTFIX + Ok(self.closest_index_for_x(&line, position_in_unwrapped_line.x)) + } + } + fn index_for_mouse_position(&self, position: Point, _: &WindowContext) -> usize { // If the text is empty, always return 0 if self.text.is_empty() { @@ -967,22 +1022,15 @@ impl TextInput { return closest_index; } - let index_result = line.index_for_position(pos, line_height); + let index_result = self.closest_index_for_position(line, pos, line_height); if let Ok(v) = index_result { - // 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; - } + index += v; 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. // The fallback index is saved in Err from `index_for_position` method. index += index_result.unwrap_err(); + println!("------------------ 2"); break; } else if line.len() == 0 { // empty line @@ -995,6 +1043,7 @@ impl TextInput { break; } } else { + println!("------------------ 3 {}", line.len()); index += line.len(); }