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.)

This commit is contained in:
Michael Drake 2013-01-13 14:51:26 +00:00
parent 6938efc2a0
commit 740559ab52
1 changed files with 102 additions and 91 deletions

View File

@ -38,16 +38,9 @@
#define MARGIN_RIGHT 4 #define MARGIN_RIGHT 4
#define CARET_COLOR 0x0000FF #define CARET_COLOR 0x0000FF
/* background color for readonly textarea */ /* background color for readonly textarea */
#define READONLY_BG 0xD9D9D9
#define BACKGROUND_COL 0xFFFFFF
#define BORDER_COLOR 0x000000 #define BORDER_COLOR 0x000000
#define SELECTION_COL 0xFFDDDD #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 = { static plot_style_t pstyle_stroke_border = {
.stroke_type = PLOT_OP_TYPE_SOLID, .stroke_type = PLOT_OP_TYPE_SOLID,
.stroke_colour = BORDER_COLOR, .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 rect *clip, const struct redraw_context *ctx)
{ {
const struct plotter_table *plot = ctx->plot; const struct plotter_table *plot = ctx->plot;
int line0, line1, line; int line0, line1, line, left, right;
int chars, offset, text_y_offset, text_y_offset_baseline; int chars, text_y_offset, text_y_offset_baseline;
unsigned int c_pos, c_len, b_start, b_end, line_len; unsigned int c_pos, c_len, c_len_part, b_start, b_end, line_len;
unsigned int sel_start, sel_end;
char *line_text; char *line_text;
struct rect r; struct rect r, s;
plot_style_t plot_style_fill_bg = { bool selected = false;
.fill_type = PLOT_OP_TYPE_SOLID, plot_font_style_t *fstyle;
.fill_colour = BACKGROUND_COL, 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; r = *clip;
@ -899,9 +898,6 @@ void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
/* Nothing to redraw */ /* Nothing to redraw */
return; 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; line0 = (r.y0 - y + ta->scroll_y) / ta->line_height - 1;
line1 = (r.y1 - 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->clip(&r);
plot->rectangle(r.x0, r.y0, r.x1, r.y1, &plot_style_fill_bg); plot->rectangle(r.x0, r.y0, r.x1, r.y1, &plot_style_fill_bg);
plot->rectangle(x, y, plot->rectangle(x, y,
x + ta->vis_width - 1, y + ta->vis_height - 1, x + ta->vis_width - 1, y + ta->vis_height - 1,
&pstyle_stroke_border); &pstyle_stroke_border);
if (r.x0 < x + MARGIN_LEFT) if (r.x0 < x + MARGIN_LEFT)
r.x0 = x + MARGIN_LEFT; r.x0 = x + MARGIN_LEFT;
if (r.x1 > x + ta->vis_width - MARGIN_RIGHT) if (r.x1 > x + ta->vis_width - MARGIN_RIGHT)
r.x1 = x + ta->vis_width - MARGIN_RIGHT; r.x1 = x + ta->vis_width - MARGIN_RIGHT;
plot->clip(&r);
if (line0 > 0) if (line0 > 0)
c_pos = utf8_bounded_length(ta->text, 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; 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) && for (line = line0; (line <= line1) &&
(y + line * ta->line_height <= r.y1 + ta->scroll_y); (y + line * ta->line_height <= r.y1 + ta->scroll_y);
line++) { line++) {
if (ta->lines[line].b_length == 0) if (ta->lines[line].b_length == 0)
continue; continue;
plot->clip(&r);
c_len = utf8_bounded_length( c_len = utf8_bounded_length(
&(ta->text[ta->lines[line].b_start]), &(ta->text[ta->lines[line].b_start]),
ta->lines[line].b_length); ta->lines[line].b_length);
/* if there is a newline between the lines count it too */ b_end = 0;
if (line < ta->line_count - 1 && ta->lines[line + 1].b_start != right = x + MARGIN_LEFT;
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 do {
selection (ta->sel_end = -1) */ sel_start = ta->sel_start;
if (ta->sel_end != -1 && sel_end = ta->sel_end;
c_pos < (unsigned)ta->sel_end && /* get length of part of line */
c_pos + c_len > (unsigned)ta->sel_start) { 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;
/* offset from the beginning of the line */ } else if (sel_start <= c_pos &&
offset = ta->sel_start - c_pos; sel_end > c_pos + c_len) {
chars = ta->sel_end - c_pos - /* rest of line selected */
(offset > 0 ? offset:0); 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;
} else {
assert(0);
}
line_text = &(ta->text[ta->lines[line].b_start]); line_text = &(ta->text[ta->lines[line].b_start]);
line_len = ta->lines[line].b_length; line_len = ta->lines[line].b_length;
if (offset > 0) { /* find b_start and b_end for this part of the line */
b_start = b_end;
/* find byte start of the selected part */ chars = c_len_part;
for (b_start = 0; offset > 0; offset--) for (b_end = b_start; chars > 0; chars--)
b_start = utf8_next(line_text, b_end = utf8_next(line_text, line_len, b_end);
line_len,
b_start); /* find clip left/right for this part of line */
nsfont.font_width(&ta->fstyle, line_text, left = right;
b_start, &r.x0); nsfont.font_width(&ta->fstyle, line_text,
r.x0 += x + MARGIN_LEFT; b_end, &right);
} right += x + MARGIN_LEFT;
else {
r.x0 = x + MARGIN_LEFT; /* set clip */
b_start = 0; 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);
} }
/* 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);
if (chars >= 0) { c_pos += c_len_part;
c_len -= c_len_part;
/* find byte end of the selected part */ } while (c_pos < c_pos + c_len);
for (b_end = b_start; chars > 0 &&
b_end < line_len;
chars--) {
b_end = utf8_next(line_text, line_len,
b_end);
}
}
else
b_end = ta->lines[line].b_length;
b_end -= b_start; /* if there is a newline between the lines, skip it */
nsfont.font_width(&ta->fstyle, if (line < ta->line_count - 1 && ta->lines[line + 1].b_start !=
&(ta->text[ta->lines[line].b_start + ta->lines[line].b_start +
b_start]), ta->lines[line].b_length)
b_end, &r.x1); c_pos++;
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;
r.y0 = y + line * ta->line_height + text_y_offset_baseline;
ta->fstyle.background =
(ta->flags & TEXTAREA_READONLY) ?
READONLY_BG : BACKGROUND_COL,
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);
} }
plot->clip(&r);
x -= ta->scroll_x; x -= ta->scroll_x;
y -= ta->scroll_y; y -= ta->scroll_y;
@ -1099,10 +1110,10 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
case KEY_SELECT_ALL: case KEY_SELECT_ALL:
caret = ta->text_utf8_len; caret = ta->text_utf8_len;
ta->sel_start = 0; ta->sel_start = 0;
ta->sel_end = ta->text_utf8_len; ta->sel_end = ta->text_utf8_len;
redraw = true; redraw = true;
break; break;
case KEY_COPY_SELECTION: case KEY_COPY_SELECTION:
if (ta->sel_start != -1) { if (ta->sel_start != -1) {
if (!textarea_replace_text(ta, if (!textarea_replace_text(ta,