Просмотр исходного кода

fix: Implement precise cursor positioning using Pango text measurement

Problem: Cursor misalignment near right margin due to fixed char_width calculations
- Fixed character width assumptions caused cumulative positioning errors
- Text rendering and cursor used different positioning logic
- Alignment degraded as cursor moved toward end of long lines

Solution: Use Pango's actual text measurement for cursor positioning
- Calculate exact cursor X position by measuring text up to cursor column
- Handle horizontal scrolling by measuring visible text portion correctly
- Use same Pango layout system for both text rendering and cursor positioning
- Remove dependency on fixed character width for cursor placement

Technical Implementation:
- Measure text from line start (or horizontal_offset) to cursor position
- Use layout->get_pixel_extents() for accurate width calculation
- Account for horizontal scrolling offset in text measurement
- Maintain proper padding and boundary checking

Result: Pixel-perfect cursor alignment across entire line width
✅ Cursor precisely highlights character at cursor position
✅ No cumulative positioning errors on long lines
✅ Consistent alignment from left margin to right margin
✅ Proper handling with horizontal scrolling enabled

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Bernardo Magri 1 месяц назад
Родитель
Сommit
932dfdc557
1 измененных файлов с 38 добавлено и 5 удалено
  1. 38 5
      src/gtk_editor.cpp

+ 38 - 5
src/gtk_editor.cpp

@@ -225,16 +225,49 @@ protected:
 
         // Render Cursor - Emacs-style blinking block cursor with color inversion
         if (cursor_visible_ && cursor.line >= static_cast<size_t>(start_line) && cursor.line < static_cast<size_t>(end_line)) {
-            // Account for horizontal scrolling and padding
-            double cursor_screen_x = PADDING_LEFT + (static_cast<int>(cursor.column) - horizontal_offset) * char_width_;
             int screen_y = cursor.line - start_line;
             double cursor_y = PADDING_TOP + screen_y * line_height_;
             
+            // Get the line text and calculate exact cursor position using Pango text measurement
+            size_t buffer_line_idx = cursor.line;
+            const auto& cursor_line_text = buffer.line(buffer_line_idx);
+            
+            // Calculate the exact X position by measuring text up to cursor position
+            double cursor_screen_x = PADDING_LEFT;
+            
+            // If we have horizontal scrolling, we need to account for the visible portion
+            if (horizontal_offset > 0 && cursor.column >= static_cast<size_t>(horizontal_offset)) {
+                
+                // Measure text from horizontal_offset to cursor position
+                if (horizontal_offset < static_cast<int>(cursor_line_text.length())) {
+                    std::string text_to_cursor = cursor_line_text.substr(
+                        horizontal_offset, 
+                        cursor.column - horizontal_offset
+                    );
+                    if (!text_to_cursor.empty()) {
+                        layout->set_text(text_to_cursor);
+                        Pango::Rectangle text_rect;
+                        layout->get_pixel_extents(text_rect, text_rect);
+                        cursor_screen_x = PADDING_LEFT + text_rect.get_width();
+                    }
+                }
+            } else if (horizontal_offset == 0) {
+                // No horizontal scrolling - measure from start of line to cursor
+                if (cursor.column > 0 && cursor.column <= cursor_line_text.length()) {
+                    std::string text_to_cursor = cursor_line_text.substr(0, cursor.column);
+                    layout->set_text(text_to_cursor);
+                    Pango::Rectangle text_rect;
+                    layout->get_pixel_extents(text_rect, text_rect);
+                    cursor_screen_x = PADDING_LEFT + text_rect.get_width();
+                }
+            } else {
+                // Cursor is before the visible area (shouldn't happen with proper scrolling)
+                cursor_screen_x = -char_width_; // Hide cursor
+            }
+            
             // Only render cursor if it's visible horizontally
             if (cursor_screen_x >= PADDING_LEFT && cursor_screen_x < (width - PADDING_RIGHT)) {
                 // Get the character under cursor for rendering with inverted colors
-                size_t buffer_line_idx = cursor.line;
-                const auto& cursor_line_text = buffer.line(buffer_line_idx);
                 char cursor_char = (cursor.column < cursor_line_text.length()) ? cursor_line_text[cursor.column] : ' ';
                 
                 // Draw block cursor background (inverted background color)
@@ -244,7 +277,7 @@ protected:
                 cr->fill();
                 
                 // Draw the character with inverted color (background color as foreground)
-                if (cursor_char != '\0') {
+                if (cursor_char != '\0' && cursor_char != ' ') {
                     Color cursor_fg = theme ? theme->get_bg_color(ThemeElement::Background) : Color(0, 0, 0);
                     cr->set_source_rgb(cursor_fg.r / 255.0, cursor_fg.g / 255.0, cursor_fg.b / 255.0);