1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00

UTF-8: Fix scrolling of input fields.

form_state.state_cell is no longer used for FC_TEXT, FC_PASSWORD, nor FC_FILE.
Instead, get_link_cursor_offset() computes the cell with utf8_ptr2chars
(a new function) or utf8_ptr2cells.  This shouldn't slow down ELinks too
much, as it's done only for the selected link and only once per redraw.

The left side of a scrolled input field is always aligned at a
character boundary.  The right side might not be.
This commit is contained in:
Kalle Olavi Niemitalo 2006-08-27 23:41:41 +03:00 committed by Kalle Olavi Niemitalo
parent 92845d0b56
commit 8d77387f6f
3 changed files with 121 additions and 36 deletions

View File

@ -342,7 +342,8 @@ draw_form_entry(struct terminal *term, struct document_view *doc_view,
switch (fc->type) {
unsigned char *s;
#ifdef CONFIG_UTF_8
unsigned char *text, *end;
unsigned char *text, *end, *last_in_view;
int retried;
#endif /* CONFIG_UTF_8 */
int len;
int i, x, y;
@ -379,38 +380,118 @@ draw_form_entry(struct terminal *term, struct document_view *doc_view,
break;
#ifdef CONFIG_UTF_8
utf_8:
end = NULL; /* Shut up the compiler. */
int_bounds(&fs->vpos, fs->state_cell - fc->size + 1, fs->state_cell);
if (fc->type == FC_PASSWORD)
len = utf8_ptr2chars(fs->value + fs->vpos, NULL);
else
len = utf8_ptr2cells(fs->value + fs->vpos, NULL);
retried = 0;
retry_after_scroll:
text = fs->value;
end = strchr(text, '\0');
if (!text) text = "";
len = strlen(text);
int_bounds(&fs->state, 0, len);
int_bounds(&fs->vpos, 0, fs->state);
end = text + len;
text += fs->vpos;
last_in_view = NULL;
for (i = 0; i < fc->size; i++, x++) {
for (i = 0; i < fc->size; ) {
unicode_val_T data;
int cells, cell;
unsigned char *maybe_in_view = text;
if (!col_is_in_box(box, x)) continue;
if (fs->value && i >= -fs->vpos && i < len) {
if (fc->type != FC_PASSWORD)
data = utf_8_to_unicode(&text, end);
else
data = '*';
} else
data = utf_8_to_unicode(&text, end);
if (data == UCS_NO_CHAR) /* end of string */
data = '_';
else if (fc->type == FC_PASSWORD)
data = '*';
if (unicode_to_cell(data) == 2) {
if (i + 1 < fc->size) {
draw_char_data(term, x++, y, data);
data = UCS_NO_CHAR;
i++;
} else
data = ' ';
cells = unicode_to_cell(data);
if (i + cells <= fc->size) {
last_in_view = maybe_in_view;
if (colspan_is_in_box(box, x + i, cells)) {
/* The character fits completely.
* Draw the character, and mark any
* further cells with UCS_NO_CHAR. */
draw_char_data(term, x + i, y, data);
for (cell = 1; cell < cells; cell++)
draw_char_data(term, x + i + cell,
y, UCS_NO_CHAR);
goto drew_char;
}
}
/* The character does not fit completely.
* Write spaces to the cells that do fit. */
for (cell = 0; cell < cells; cell++) {
if (col_is_in_box(box, x + i + cell)
&& i + cell < fc->size)
draw_char_data(term,
x + i + cell,
y, ' ');
}
drew_char:
i += cells;
}
/* The int_bounds calls above ensured that the
* insertion point cannot be at the left side
* of the scrolled-visible part of the text.
* However it can still be at the right side.
* Check whether we need to change fs->vpos.
*
* This implementation attempts to follow
* these rules:
* - If the insertion point is at the end of
* the string, leave at least one empty cell
* so that there is a place for the cursor.
* - If a character follows the insertion
* point, make that character fully visible;
* note the character may be double-width.
* - If fc->size < 2, it is not possible to
* make a double-width character fully
* visible. In this case, it is OK if the
* output is ugly, but ELinks must not fall
* into an infinite loop or crash.
* - The length of the string should not affect
* how long this function takes. The width
* of the widget naturally will.
* - Optimize the case where fields are drawn
* several times without being modified.
*
* It follows that:
* - If the "for i" loop above hit UCS_NO_CHAR,
* then there is no need to scroll.
* - When the string ends with a double-width
* character that fits in only partially,
* then text==end, but the field may have
* to be scrolled. */
if (fs->value && last_in_view
&& last_in_view < fs->value + fs->state) {
unsigned char *ptr = fs->value + fs->state;
int cells = fc->size;
enum utf8_step how = (fc->type == FC_PASSWORD)
? utf8_step_characters
: utf8_step_cells_fewer;
/* The insertion point is at the right
* side of the scrolled-visible part
* of the text. Decide a new fs->vpos
* by counting cells backwards from
* @ptr. But first advance @ptr past
* the character that follows the
* insertion point, so that it will be
* fully displayed. If there is no
* such character, reserve one cell
* for the cursor anyway. */
if (utf_8_to_unicode(&ptr, end) == UCS_NO_CHAR)
--cells;
ptr = utf8_step_backward(ptr, fs->value,
cells, how, NULL);
if (fs->vpos != ptr - fs->value) {
fs->vpos = ptr - fs->value;
retried = 1;
goto retry_after_scroll;
}
draw_char_data(term, x, y, data);
}
break;
#endif /* CONFIG_UTF_8 */

View File

@ -52,16 +52,14 @@ struct form_state {
* in @form_control.labels. */
int state;
#ifdef CONFIG_UTF_8
/* For FC_TEXT, FC_PASSWORD, and FC_FILE, @state_cell is the
* number of cells needed for the text before the insertion
* point. (For FC_PASSWORD, each character is one cell.)
* When CONFIG_UTF_8 is not defined, @state_cell is assumed
* to be the same as @state. */
/* For FC_TEXT, FC_PASSWORD, and FC_FILE, @state_cell is not
* used. There is still code that updates it; such code
* should be removed. */
int state_cell;
#endif /* CONFIG_UTF_8 */
/* For FC_TEXT, FC_PASSWORD, and FC_FILE, @vpos is the number
* of cells that scrolling has hidden at the left. Thus, the
* X offset of the cursor is @state_cell - @vpos. */
/* For FC_TEXT, FC_PASSWORD, and FC_FILE, @vpos is the index
* of the first displayed byte in @value. It should never be
* in the middle of a character. */
int vpos;
int vypos;

View File

@ -128,11 +128,17 @@ get_link_cursor_offset(struct document_view *doc_view, struct link *link)
case LINK_FIELD:
fc = get_link_form_control(link);
fs = find_form_state(doc_view, fc);
if (!fs)
if (!fs || !fs->value)
return 0;
#ifdef CONFIG_UTF_8
else if (utf8) {
return fs->state_cell - fs->vpos;
unsigned char *scroll = fs->value + fs->vpos;
unsigned char *point = fs->value + fs->state;
if (fs->type == FC_PASSWORD)
return utf8_ptr2chars(scroll, point);
else
return utf8_ptr2cells(scroll, point);
}
#endif /* CONFIG_UTF_8 */
else