[pango/line-breaker: 18/18] more wip: editing api




commit d18414c7f64f2d4a911c3305d7ed2481bea13724
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Jan 15 10:33:14 2022 -0500

    more wip: editing api

 pango/pango-line.c  | 159 +++++++++++++++++++++++++++++++++++++++++++-
 pango/pango-lines.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 336 insertions(+), 9 deletions(-)
---
diff --git a/pango/pango-line.c b/pango/pango-line.c
index 63903f87..76426f72 100644
--- a/pango/pango-line.c
+++ b/pango/pango-line.c
@@ -138,10 +138,167 @@ pango_line_index_to_x (PangoLine *line,
 
 gboolean
 pango_line_x_to_index (PangoLine *line,
-                       int        x,
+                       int        x_pos,
                        int       *index,
                        int       *trailing)
 {
+  GSList *tmp_list;
+  gint start_pos = 0;
+  gint first_index = 0; /* line->start_index */
+  gint first_offset;
+  gint last_index;      /* start of last grapheme in line */
+  gint last_offset;
+  gint end_index;       /* end iterator for line */
+  gint end_offset;      /* end iterator for line */
+  gint last_trailing;
+  gboolean suppress_last_trailing;
+
+  /* Find the last index in the line */
+  first_index = line->start_index;
+
+  if (line->length == 0)
+    {
+      if (index)
+        *index = first_index;
+      if (trailing)
+        *trailing = 0;
+
+      return FALSE;
+    }
+
+  g_assert (line->length > 0);
+
+  first_offset = g_utf8_pointer_to_offset (line->data->text, line->data->text + line->start_index);
+
+  end_index = first_index + line->length;
+  end_offset = first_offset + g_utf8_pointer_to_offset (line->data->text + first_index, line->data->text + 
end_index);
+
+  last_index = end_index;
+  last_offset = end_offset;
+  last_trailing = 0;
+ do
+    {
+      last_index = g_utf8_prev_char (line->data->text + last_index) - line->data->text;
+      last_offset--;
+      last_trailing++;
+    }
+  while (last_offset > first_offset && !line->data->log_attrs[last_offset].is_cursor_position);
+
+  /* This is a HACK. If a program only keeps track of cursor (etc)
+   * indices and not the trailing flag, then the trailing index of the
+   * last character on a wrapped line is identical to the leading
+   * index of the next line. So, we fake it and set the trailing flag
+   * to zero.
+   *
+   * That is, if the text is "now is the time", and is broken between
+   * 'now' and 'is'
+   *
+   * Then when the cursor is actually at:
+   *
+   * n|o|w| |i|s|
+   *              ^
+   * we lie and say it is at:
+   *
+   * n|o|w| |i|s|
+   *            ^
+   *
+   * So the cursor won't appear on the next line before 'the'.
+   *
+   * Actually, any program keeping cursor
+   * positions with wrapped lines should distinguish leading and
+   * trailing cursors.
+   */
+  if (line->wrapped)
+    suppress_last_trailing = TRUE;
+  else
+    suppress_last_trailing = FALSE;
+
+  if (x_pos < 0)
+    {
+      /* pick the leftmost char */
+      if (index)
+        *index = (line->direction == PANGO_DIRECTION_LTR) ? first_index : last_index;
+      /* and its leftmost edge */
+      if (trailing)
+        *trailing = (line->direction == PANGO_DIRECTION_LTR || suppress_last_trailing) ? 0 : last_trailing;
+
+      return FALSE;
+    }
+
+  tmp_list = line->runs;
+  while (tmp_list)
+    {
+      PangoLayoutRun *run = tmp_list->data;
+      int logical_width;
+
+      logical_width = pango_glyph_string_get_width (run->glyphs);
+
+      if (x_pos >= start_pos && x_pos < start_pos + logical_width)
+        {
+          int offset;
+          gboolean char_trailing;
+          int grapheme_start_index;
+          int grapheme_start_offset;
+          int grapheme_end_offset;
+          int pos;
+          int char_index;
+
+          pango_glyph_string_x_to_index (run->glyphs,
+                                         line->data->text + run->item->offset, run->item->length,
+                                         &run->item->analysis,
+                                         x_pos - start_pos,
+                                         &pos, &char_trailing);
+
+          char_index = run->item->offset + pos;
+
+          /* Convert from characters to graphemes */
+
+          offset = g_utf8_pointer_to_offset (line->data->text, line->data->text + char_index);
+
+          grapheme_start_offset = offset;
+          grapheme_start_index = char_index;
+          while (grapheme_start_offset > first_offset &&
+                 !line->data->log_attrs[grapheme_start_offset].is_cursor_position)
+            {
+              grapheme_start_index = g_utf8_prev_char (line->data->text + grapheme_start_index) - 
line->data->text;
+              grapheme_start_offset--;
+            }
+
+          grapheme_end_offset = offset;
+          do
+            {
+              grapheme_end_offset++;
+            }
+
+          while (grapheme_end_offset < end_offset &&
+                 !line->data->log_attrs[grapheme_end_offset].is_cursor_position);
+
+          if (index)
+            *index = grapheme_start_index;
+          if (trailing)
+            {
+              if ((grapheme_end_offset == end_offset && suppress_last_trailing) ||
+                  offset + char_trailing <= (grapheme_start_offset + grapheme_end_offset) / 2)
+                *trailing = 0;
+              else
+                *trailing = grapheme_end_offset - grapheme_start_offset;
+            }
+
+          return TRUE;
+        }
+
+      start_pos += logical_width;
+      tmp_list = tmp_list->next;
+    }
+
+  /* pick the rightmost char */
+  if (index)
+    *index = (line->direction == PANGO_DIRECTION_LTR) ? last_index : first_index;
+
+  /* and its rightmost edge */
+  if (trailing)
+    *trailing = (line->direction == PANGO_DIRECTION_LTR && !suppress_last_trailing) ? last_trailing : 0;
+
   return FALSE;
 }
 
diff --git a/pango/pango-lines.c b/pango/pango-lines.c
index 26dcc7d6..0f5a9df1 100644
--- a/pango/pango-lines.c
+++ b/pango/pango-lines.c
@@ -225,12 +225,179 @@ pango_lines_index_to_pos (PangoLines     *lines,
     }
 }
 
-static void
-pango_lines_get_line_yrange (PangoLines *lines,
-                             int         index,
-                             int        *first_y,
-                             int        *last_y)
+static gboolean
+pango_lines_x_to_index (PangoLines *lines,
+                        int         num,
+                        int         x_pos,
+                        int        *index,
+                        int        *trailing)
 {
+  PangoLine *line;
+  PangoLine *next_line;
+  int xx, yy;
+  GSList *tmp_list;
+  gint start_pos = 0;
+  gint first_index = 0; /* line->start_index */
+  gint first_offset;
+  gint last_index;      /* start of last grapheme in line */
+  gint last_offset;
+  gint end_index;       /* end iterator for line */
+  gint end_offset;      /* end iterator for line */
+  gint last_trailing;
+  gboolean suppress_last_trailing;
+
+  pango_lines_get_line (lines, num, &line, &xx, &yy);
+
+  /* Find the last index in the line */
+  first_index = line->start_index;
+
+  if (line->length == 0)
+    {
+      if (index)
+        *index = first_index;
+      if (trailing)
+        *trailing = 0;
+
+      return FALSE;
+    }
+
+  g_assert (line->length > 0);
+
+  first_offset = g_utf8_pointer_to_offset (line->data->text, line->data->text + line->start_index);
+
+  end_index = first_index + line->length;
+  end_offset = first_offset + g_utf8_pointer_to_offset (line->data->text + first_index, line->data->text + 
end_index);
+
+  last_index = end_index;
+  last_offset = end_offset;
+  last_trailing = 0;
+
+  do
+    {
+      last_index = g_utf8_prev_char (line->data->text + last_index) - line->data->text;
+      last_offset--;
+      last_trailing++;
+    }
+  while (last_offset > first_offset && !line->data->log_attrs[last_offset].is_cursor_position);
+
+  /* This is a HACK. If a program only keeps track of cursor (etc)
+   * indices and not the trailing flag, then the trailing index of the
+   * last character on a wrapped line is identical to the leading
+   * index of the next line. So, we fake it and set the trailing flag
+   * to zero.
+   *
+   * That is, if the text is "now is the time", and is broken between
+   * 'now' and 'is'
+   *
+   * Then when the cursor is actually at:
+   *
+   * n|o|w| |i|s|
+   *              ^
+   * we lie and say it is at:
+   *
+   * n|o|w| |i|s|
+   *            ^
+   *
+   * So the cursor won't appear on the next line before 'the'.
+   *
+   * Actually, any program keeping cursor
+   * positions with wrapped lines should distinguish leading and
+   * trailing cursors.
+   */
+  if (pango_lines_get_line (lines, num + 1, &next_line, &xx, &yy))
+    {
+      if (line->start_index + line->length == next_line->start_index)
+        suppress_last_trailing = TRUE;
+      else
+        suppress_last_trailing = FALSE;
+    }
+
+  if (x_pos < 0)
+    {
+      /* pick the leftmost char */
+      if (index)
+        *index = (line->direction == PANGO_DIRECTION_LTR) ? first_index : last_index;
+      /* and its leftmost edge */
+      if (trailing)
+        *trailing = (line->direction == PANGO_DIRECTION_LTR || suppress_last_trailing) ? 0 : last_trailing;
+
+      return FALSE;
+    }
+
+  tmp_list = line->runs;
+  while (tmp_list)
+    {
+      PangoLayoutRun *run = tmp_list->data;
+      int logical_width;
+
+      logical_width = pango_glyph_string_get_width (run->glyphs);
+
+      if (x_pos >= start_pos && x_pos < start_pos + logical_width)
+        {
+          int offset;
+          gboolean char_trailing;
+          int grapheme_start_index;
+          int grapheme_start_offset;
+          int grapheme_end_offset;
+          int pos;
+          int char_index;
+
+          pango_glyph_string_x_to_index (run->glyphs,
+                                         line->data->text + run->item->offset, run->item->length,
+                                         &run->item->analysis,
+                                         x_pos - start_pos,
+                                         &pos, &char_trailing);
+
+          char_index = run->item->offset + pos;
+
+          /* Convert from characters to graphemes */
+
+          offset = g_utf8_pointer_to_offset (line->data->text, line->data->text + char_index);
+
+          grapheme_start_offset = offset;
+          grapheme_start_index = char_index;
+          while (grapheme_start_offset > first_offset &&
+                 !line->data->log_attrs[grapheme_start_offset].is_cursor_position)
+            {
+              grapheme_start_index = g_utf8_prev_char (line->data->text + grapheme_start_index) - 
line->data->text;
+              grapheme_start_offset--;
+            }
+
+          grapheme_end_offset = offset;
+          do
+            {
+              grapheme_end_offset++;
+            }
+          while (grapheme_end_offset < end_offset &&
+                 !line->data->log_attrs[grapheme_end_offset].is_cursor_position);
+
+          if (index)
+            *index = grapheme_start_index;
+          if (trailing)
+            {
+              if ((grapheme_end_offset == end_offset && suppress_last_trailing) ||
+                  offset + char_trailing <= (grapheme_start_offset + grapheme_end_offset) / 2)
+                *trailing = 0;
+              else
+                *trailing = grapheme_end_offset - grapheme_start_offset;
+            }
+
+          return TRUE;
+        }
+
+      start_pos += logical_width;
+      tmp_list = tmp_list->next;
+    }
+
+  /* pick the rightmost char */
+  if (index)
+    *index = (line->direction == PANGO_DIRECTION_LTR) ? last_index : first_index;
+
+  /* and its rightmost edge */
+  if (trailing)
+    *trailing = (line->direction == PANGO_DIRECTION_LTR && !suppress_last_trailing) ? last_trailing : 0;
+
+  return FALSE;
 }
 
 gboolean
@@ -240,6 +407,7 @@ pango_lines_pos_to_index (PangoLines *lines,
                           int        *index,
                           int        *trailing)
 {
+  PangoLine *line = NULL;
   PangoLine *prev_line = NULL;
   PangoLine *found = NULL;
   int found_line_x = 0;
@@ -253,13 +421,15 @@ pango_lines_pos_to_index (PangoLines *lines,
   for (int i = 0; i < lines->lines->len; i++)
     {
       Line *l = &g_array_index (lines->lines, Line, i);
-      PangoLine *line = l->line;
       Line *next_l;
       PangoRectangle line_logical;
       int first_y, last_y;
 
+      line = l->line;
+
       pango_line_get_extents (line, &line_logical);
-      pango_lines_get_line_yrange (lines, i, &first_y, &last_y);
+      first_y = line_logical.y;
+      last_y = line_logical.y + line_logical.height;
 
       if (y < first_y)
         {
@@ -303,7 +473,7 @@ pango_lines_pos_to_index (PangoLines *lines,
       found_line_x = prev_line_x;
     }
 
-  retval = pango_line_x_to_index (found, found_line_x, index, trailing);
+  retval = pango_line_x_to_index (line, found_line_x, index, trailing);
 
   if (outside)
     retval = FALSE;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]