[pango/line-breaker: 18/18] more wip: editing api
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pango/line-breaker: 18/18] more wip: editing api
- Date: Sat, 15 Jan 2022 15:37:30 +0000 (UTC)
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]