From 740559ab52c0f7cf4123eddd4a1d96196843a8cb Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 13 Jan 2013 14:51:26 +0000 Subject: Rewrite redraw to handle selection properly. (Now text can change colour inside selection, rather than just rendering a rectangle under the normal line of text. This removes colour restrictions, allowing e.g. inversion for selected text.) --- desktop/textarea.c | 197 ++++++++++++++++++++++++++++------------------------- 1 file changed, 104 insertions(+), 93 deletions(-) diff --git a/desktop/textarea.c b/desktop/textarea.c index 9b14773cf..606618c6e 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -38,16 +38,9 @@ #define MARGIN_RIGHT 4 #define CARET_COLOR 0x0000FF /* background color for readonly textarea */ -#define READONLY_BG 0xD9D9D9 -#define BACKGROUND_COL 0xFFFFFF #define BORDER_COLOR 0x000000 #define SELECTION_COL 0xFFDDDD -static plot_style_t pstyle_fill_selection = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = SELECTION_COL, -}; - static plot_style_t pstyle_stroke_border = { .stroke_type = PLOT_OP_TYPE_SOLID, .stroke_colour = BORDER_COLOR, @@ -878,15 +871,21 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, const struct rect *clip, const struct redraw_context *ctx) { const struct plotter_table *plot = ctx->plot; - int line0, line1, line; - int chars, offset, text_y_offset, text_y_offset_baseline; - unsigned int c_pos, c_len, b_start, b_end, line_len; + int line0, line1, line, left, right; + int chars, text_y_offset, text_y_offset_baseline; + unsigned int c_pos, c_len, c_len_part, b_start, b_end, line_len; + unsigned int sel_start, sel_end; char *line_text; - struct rect r; - plot_style_t plot_style_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = BACKGROUND_COL, - }; + struct rect r, s; + bool selected = false; + plot_font_style_t *fstyle; + plot_style_t plot_style_fill_bg = { + .stroke_type = PLOT_OP_TYPE_NONE, + .stroke_width = 0, + .stroke_colour = NS_TRANSPARENT, + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = ta->fstyle.background + }; r = *clip; @@ -899,9 +898,6 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, /* Nothing to redraw */ return; - if (ta->flags & TEXTAREA_READONLY) - plot_style_fill_bg.fill_colour = READONLY_BG; - line0 = (r.y0 - y + ta->scroll_y) / ta->line_height - 1; line1 = (r.y1 - y + ta->scroll_y) / ta->line_height + 1; @@ -928,14 +924,13 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, plot->clip(&r); plot->rectangle(r.x0, r.y0, r.x1, r.y1, &plot_style_fill_bg); plot->rectangle(x, y, - x + ta->vis_width - 1, y + ta->vis_height - 1, - &pstyle_stroke_border); + x + ta->vis_width - 1, y + ta->vis_height - 1, + &pstyle_stroke_border); if (r.x0 < x + MARGIN_LEFT) r.x0 = x + MARGIN_LEFT; if (r.x1 > x + ta->vis_width - MARGIN_RIGHT) r.x1 = x + ta->vis_width - MARGIN_RIGHT; - plot->clip(&r); if (line0 > 0) c_pos = utf8_bounded_length(ta->text, @@ -953,97 +948,113 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg, text_y_offset_baseline = (ta->vis_height * 3 + 2) / 4; } + plot_style_fill_bg.fill_colour = ta->sel_fstyle.background; + for (line = line0; (line <= line1) && (y + line * ta->line_height <= r.y1 + ta->scroll_y); line++) { if (ta->lines[line].b_length == 0) continue; + plot->clip(&r); + c_len = utf8_bounded_length( &(ta->text[ta->lines[line].b_start]), ta->lines[line].b_length); - /* if there is a newline between the lines count it too */ - if (line < ta->line_count - 1 && ta->lines[line + 1].b_start != - ta->lines[line].b_start + - ta->lines[line].b_length) - c_len++; - - /* check if a part of the line is selected, won't happen if no - selection (ta->sel_end = -1) */ - if (ta->sel_end != -1 && - c_pos < (unsigned)ta->sel_end && - c_pos + c_len > (unsigned)ta->sel_start) { + b_end = 0; + right = x + MARGIN_LEFT; + + do { + sel_start = ta->sel_start; + sel_end = ta->sel_end; + /* get length of part of line */ + if (ta->sel_end == -1 || ta->sel_end == ta->sel_start || + sel_end <= c_pos || + sel_start > c_pos + c_len) { + /* rest of line unselected */ + selected = false; + c_len_part = c_len; + fstyle = &ta->fstyle; + + } else if (sel_start <= c_pos && + sel_end > c_pos + c_len) { + /* rest of line selected */ + selected = true; + c_len_part = c_len; + fstyle = &ta->sel_fstyle; + + } else if (sel_start > c_pos) { + /* next part of line unselected */ + selected = false; + c_len_part = sel_start - c_pos; + fstyle = &ta->fstyle; + + } else if (sel_end > c_pos) { + /* next part of line selected */ + selected = true; + c_len_part = sel_end - c_pos; + fstyle = &ta->sel_fstyle; - /* offset from the beginning of the line */ - offset = ta->sel_start - c_pos; - chars = ta->sel_end - c_pos - - (offset > 0 ? offset:0); + } else { + assert(0); + } line_text = &(ta->text[ta->lines[line].b_start]); line_len = ta->lines[line].b_length; - if (offset > 0) { - - /* find byte start of the selected part */ - for (b_start = 0; offset > 0; offset--) - b_start = utf8_next(line_text, - line_len, - b_start); - nsfont.font_width(&ta->fstyle, line_text, - b_start, &r.x0); - r.x0 += x + MARGIN_LEFT; - } - else { - r.x0 = x + MARGIN_LEFT; - b_start = 0; - } - - - if (chars >= 0) { - - /* find byte end of the selected part */ - for (b_end = b_start; chars > 0 && - b_end < line_len; - chars--) { - b_end = utf8_next(line_text, line_len, - b_end); - } + /* find b_start and b_end for this part of the line */ + b_start = b_end; + + chars = c_len_part; + for (b_end = b_start; chars > 0; chars--) + b_end = utf8_next(line_text, line_len, b_end); + + /* find clip left/right for this part of line */ + left = right; + nsfont.font_width(&ta->fstyle, line_text, + b_end, &right); + right += x + MARGIN_LEFT; + + /* set clip */ + s = r; + if (s.x0 < left) + s.x0 = left; + if (s.x1 > right) + s.x1 = right; + plot->clip(&s); + + if (selected) { + /* draw selection fill */ + plot->rectangle(s.x0 - ta->scroll_x, + y + line * ta->line_height + 1 - + ta->scroll_y + text_y_offset, + s.x1 - ta->scroll_x, + y + (line + 1) * ta->line_height + 1 - + ta->scroll_y + text_y_offset, + &plot_style_fill_bg); } - else - b_end = ta->lines[line].b_length; - - b_end -= b_start; - nsfont.font_width(&ta->fstyle, - &(ta->text[ta->lines[line].b_start + - b_start]), - b_end, &r.x1); - r.x1 += r.x0; - plot->rectangle(r.x0 - ta->scroll_x, y + - line * ta->line_height + - 1 - ta->scroll_y + text_y_offset, - r.x1 - ta->scroll_x, - y + (line + 1) * ta->line_height - - 1 - ta->scroll_y + text_y_offset, - &pstyle_fill_selection); - - } - c_pos += c_len; + /* draw text */ + plot->text(x + MARGIN_LEFT - ta->scroll_x, + y + line * ta->line_height + + text_y_offset_baseline - ta->scroll_y, + ta->text + ta->lines[line].b_start, + ta->lines[line].b_length, fstyle); - r.y0 = y + line * ta->line_height + text_y_offset_baseline; + c_pos += c_len_part; + c_len -= c_len_part; - ta->fstyle.background = - (ta->flags & TEXTAREA_READONLY) ? - READONLY_BG : BACKGROUND_COL, + } while (c_pos < c_pos + c_len); - plot->text(x + MARGIN_LEFT - ta->scroll_x, - r.y0 - ta->scroll_y, - ta->text + ta->lines[line].b_start, - ta->lines[line].b_length, - &ta->fstyle); + /* if there is a newline between the lines, skip it */ + if (line < ta->line_count - 1 && ta->lines[line + 1].b_start != + ta->lines[line].b_start + + ta->lines[line].b_length) + c_pos++; } + plot->clip(&r); x -= ta->scroll_x; y -= ta->scroll_y; @@ -1099,10 +1110,10 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) case KEY_SELECT_ALL: caret = ta->text_utf8_len; - ta->sel_start = 0; + ta->sel_start = 0; ta->sel_end = ta->text_utf8_len; - redraw = true; - break; + redraw = true; + break; case KEY_COPY_SELECTION: if (ta->sel_start != -1) { if (!textarea_replace_text(ta, -- cgit v1.2.3