From 33a521f1f52845d1a056f37afc62336b7bf6081c Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 23 May 2018 15:28:17 +0200 Subject: gl: Multi-line text input fields. --- platform/gl/gl-annotate.c | 2 +- platform/gl/gl-app.h | 5 +- platform/gl/gl-file.c | 6 +- platform/gl/gl-input.c | 265 ++++++++++++++++++++++++++++++++++++---------- platform/gl/gl-main.c | 4 +- 5 files changed, 218 insertions(+), 64 deletions(-) (limited to 'platform/gl') diff --git a/platform/gl/gl-annotate.c b/platform/gl/gl-annotate.c index 35fbc766..39f3c82d 100644 --- a/platform/gl/gl-annotate.c +++ b/platform/gl/gl-annotate.c @@ -355,7 +355,7 @@ static void do_annotate_contents(void) } ui_label("Contents:"); - if (ui_input(&input, 0) >= UI_INPUT_EDIT) + if (ui_input(&input, 0, 5) >= UI_INPUT_EDIT) pdf_set_annot_contents(ctx, selected_annot, input.text); } diff --git a/platform/gl/gl-app.h b/platform/gl/gl-app.h index 675ece0e..0cc330d6 100644 --- a/platform/gl/gl-app.h +++ b/platform/gl/gl-app.h @@ -138,8 +138,9 @@ enum struct input { - char text[256]; + char text[16*1024]; char *end, *p, *q; + int scroll; }; struct list @@ -174,7 +175,7 @@ int ui_slider(int *value, int min, int max, int width); int ui_select(const void *id, const char *current, const char *options[], int n); void ui_input_init(struct input *input, const char *text); -int ui_input(struct input *input, int width); +int ui_input(struct input *input, int width, int height); void ui_scrollbar(int x0, int y0, int x1, int y1, int *value, int page_size, int max); void ui_list_begin(struct list *list, int count, int req_w, int req_h); diff --git a/platform/gl/gl-file.c b/platform/gl/gl-file.c index ec74f5fc..6fc34f5d 100644 --- a/platform/gl/gl-file.c +++ b/platform/gl/gl-file.c @@ -334,7 +334,7 @@ int ui_open_file(char filename[PATH_MAX]) ui_spacer(); } ui_layout(ALL, X, CENTER, 0, 0); - if (ui_input(&fc.input_dir, 0) == UI_INPUT_ACCEPT) + if (ui_input(&fc.input_dir, 0, 1) == UI_INPUT_ACCEPT) load_dir(fc.input_dir.text); } ui_panel_end(); @@ -450,7 +450,7 @@ int ui_save_file(char filename[PATH_MAX], void (*extra_panel)(void)) ui_panel_end(); ui_layout(T, X, NW, 2, 2); - if (ui_input(&fc.input_dir, 0) == UI_INPUT_ACCEPT) + if (ui_input(&fc.input_dir, 0, 1) == UI_INPUT_ACCEPT) load_dir(fc.input_dir.text); ui_layout(T, X, NW, 2, 2); @@ -469,7 +469,7 @@ int ui_save_file(char filename[PATH_MAX], void (*extra_panel)(void)) bump_file_version(-1); ui_spacer(); ui_layout(ALL, X, CENTER, 0, 0); - ui_input(&fc.input_file, 0); + ui_input(&fc.input_file, 0, 1); } ui_panel_end(); diff --git a/platform/gl/gl-input.c b/platform/gl/gl-input.c index 73243865..56fcf74d 100644 --- a/platform/gl/gl-input.c +++ b/platform/gl/gl-input.c @@ -1,6 +1,51 @@ #include "gl-app.h" #include +#include + +struct line { char *a, *b; }; + +static int break_lines(char *a, char *end, struct line *lines, int maxlines, int width) +{ + char *next, *b = a; + int c, n = 0; + float x = 0, w = 0; + while (b < end) + { + next = b + fz_chartorune(&c, b); + if (c == '\n' || c == '\r') + { + if (n + 1 < maxlines) + { + lines[n].a = a; + lines[n].b = b; + ++n; + a = next; + x = 0; + } + } + else + { + w = ui_measure_character(c); + if (x + w > width && (n + 1 < maxlines)) + { + lines[n].a = a; + lines[n].b = b; + ++n; + a = b; + x = w; + } + else + { + x += w; + } + } + b = next; + } + lines[n].a = a; + lines[n].b = b; + return n + 1; +} static void draw_string_part(float x, float y, const char *s, const char *e) { @@ -41,6 +86,14 @@ static char *find_string_location(char *s, char *e, float w, float x) return e; } +static char *find_input_location(struct line *lines, int n, float left, float top, float x, float y) +{ + int i = 0; + if (y > top) i = (y - top) / ui.lineheight; + if (i >= n) i = n - 1; + return find_string_location(lines[i].a, lines[i].b, left, x); +} + static inline int myisalnum(char *s) { int cat, c; @@ -53,6 +106,50 @@ static inline int myisalnum(char *s) return 0; } +static char *home_line(char *p, char *start) +{ + while (p > start) + { + if (p[-1] == '\n' || p[-1] == '\r') + return p; + --p; + } + return p; +} + +static char *end_line(char *p, char *end) +{ + while (p < end) + { + if (p[0] == '\n' || p[0] == '\r') + return p; + ++p; + } + return p; +} + +static char *up_line(char *p, char *start) +{ + while (p > start) + { + --p; + if (*p == '\n' || *p == '\r') + return p; + } + return p; +} + +static char *down_line(char *p, char *end) +{ + while (p < end) + { + if (*p == '\n' || *p == '\r') + return p+1; + ++p; + } + return p; +} + static char *prev_char(char *p, char *start) { --p; @@ -108,7 +205,7 @@ static void ui_input_paste(struct input *input, const char *buf, int n) input->q = input->p; } -static int ui_input_key(struct input *input) +static int ui_input_key(struct input *input, int multiline) { switch (ui.key) { @@ -165,42 +262,36 @@ static int ui_input_key(struct input *input) } break; case KEY_UP: + if (ui.mod & GLUT_ACTIVE_SHIFT) + input->q = up_line(input->q, input->text); + else + input->p = input->q = up_line(input->p, input->text); + break; + case KEY_DOWN: + if (ui.mod & GLUT_ACTIVE_SHIFT) + input->q = down_line(input->q, input->end); + else + input->p = input->q = down_line(input->q, input->end); + break; case KEY_HOME: if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT) - { input->q = input->text; - } + else if (ui.mod == GLUT_ACTIVE_SHIFT) + input->q = home_line(input->q, input->text); else if (ui.mod == GLUT_ACTIVE_CTRL) - { input->p = input->q = input->text; - } - else if (ui.mod == GLUT_ACTIVE_SHIFT) - { - input->q = input->text; - } else if (ui.mod == 0) - { - input->p = input->q = input->text; - } + input->p = input->q = home_line(input->p, input->text); break; - case KEY_DOWN: case KEY_END: if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT) - { input->q = input->end; - } + else if (ui.mod == GLUT_ACTIVE_SHIFT) + input->q = end_line(input->q, input->end); else if (ui.mod == GLUT_ACTIVE_CTRL) - { input->p = input->q = input->end; - } - else if (ui.mod == GLUT_ACTIVE_SHIFT) - { - input->q = input->end; - } else if (ui.mod == 0) - { - input->p = input->q = input->end; - } + input->p = input->q = end_line(input->p, input->end); break; case KEY_DELETE: if (input->p != input->q) @@ -218,8 +309,13 @@ static int ui_input_key(struct input *input) ui.focus = NULL; return UI_INPUT_NONE; case KEY_ENTER: - ui.focus = NULL; - return UI_INPUT_ACCEPT; + if (!multiline) + { + ui.focus = NULL; + return UI_INPUT_ACCEPT; + } + ui_input_paste(input, "\n", 1); + break; case KEY_BACKSPACE: if (input->p != input->q) ui_input_delete_selection(input); @@ -293,16 +389,42 @@ void ui_input_init(struct input *input, const char *text) input->end = input->text + strlen(input->text); input->p = input->text; input->q = input->end; + input->scroll = 0; } -int ui_input(struct input *input, int width) +int ui_input(struct input *input, int width, int height) { + struct line lines[500]; fz_irect area; - float ax, px, qx; + float ax, bx; + int ay, sy; char *p, *q; int state; + int i, n; + + if (ui.focus == input) + state = ui_input_key(input, height > 1); + else + state = UI_INPUT_NONE; + + area = ui_pack(width, ui.lineheight * height + 6); + ui_draw_bevel_rect(area, UI_COLOR_TEXT_BG, 1); + fz_expand_irect(&area, -2); + + if (height > 1) + area.x1 -= ui.lineheight; - area = ui_pack(width, ui.lineheight + 6); + n = break_lines(input->text, input->end, lines, nelem(lines), area.x1-area.x0-2); + + if (height > 1) + ui_scrollbar(area.x1, area.y0, area.x1+ui.lineheight, area.y1, &input->scroll, 1, fz_maxi(0, n-height)+1); + else + input->scroll = 0; + + ax = area.x0 + 2; + bx = area.x1 - 2; + ay = area.y0 + 1; + sy = input->scroll * ui.lineheight; if (ui_mouse_inside(&area)) { @@ -311,46 +433,77 @@ int ui_input(struct input *input, int width) ui.cursor = GLUT_CURSOR_TEXT; if (!ui.active && ui.down) { - input->p = find_string_location(input->text, input->end, area.x0 + 3, ui.x); + input->p = find_input_location(lines, n, ax, ay-sy, ui.x, ui.y); ui.active = input; } } if (ui.active == input) { - input->q = find_string_location(input->text, input->end, area.x0 + 3, ui.x); + input->q = find_input_location(lines, n, ax, ay-sy, ui.x, ui.y); ui.focus = input; } - if (ui.focus == input) - state = ui_input_key(input); - else - state = UI_INPUT_NONE; - - ui_draw_bevel_rect(area, UI_COLOR_TEXT_BG, 1); - p = input->p < input->q ? input->p : input->q; q = input->p > input->q ? input->p : input->q; - ax = area.x0 + 4; - px = ax + measure_string_part(input->text, p); - qx = px + measure_string_part(p, q); - - if (ui.focus == input) + for (i = input->scroll; i < n && i < input->scroll+height; ++i) { - glColorHex(UI_COLOR_TEXT_SEL_BG); - glRectf(px, area.y0 + 3, qx+1, area.y1 - 3); - glColorHex(UI_COLOR_TEXT_FG); - draw_string_part(ax, area.y0 + 3, input->text, p); - glColorHex(UI_COLOR_TEXT_SEL_FG); - draw_string_part(px, area.y0 + 3, p, q); - glColorHex(UI_COLOR_TEXT_FG); - draw_string_part(qx, area.y0 + 3, q, input->end); - } - else - { - glColorHex(UI_COLOR_TEXT_FG); - draw_string_part(ax, area.y0 + 3, input->text, input->end); + char *a = lines[i].a, *b = lines[i].b; + if (ui.focus == input) + { + if (p >= a && p <= b && q >= a && q <= b) + { + float px = ax + measure_string_part(a, p); + float qx = px + measure_string_part(p, q); + glColorHex(UI_COLOR_TEXT_SEL_BG); + glRectf(px, ay, qx+1, ay + ui.lineheight); + glColorHex(UI_COLOR_TEXT_FG); + draw_string_part(ax, ay, a, p); + glColorHex(UI_COLOR_TEXT_SEL_FG); + draw_string_part(px, ay, p, q); + glColorHex(UI_COLOR_TEXT_FG); + draw_string_part(qx, ay, q, b); + } + else if (p < a && q >= a && q <= b) + { + float qx = ax + measure_string_part(a, q); + glColorHex(UI_COLOR_TEXT_SEL_BG); + glRectf(ax, ay, qx+1, ay + ui.lineheight); + glColorHex(UI_COLOR_TEXT_SEL_FG); + draw_string_part(ax, ay, a, q); + glColorHex(UI_COLOR_TEXT_FG); + draw_string_part(qx, ay, q, b); + } + else if (p >= a && p <= b && q > b) + { + float px = ax + measure_string_part(a, p); + glColorHex(UI_COLOR_TEXT_SEL_BG); + glRectf(px, ay, bx, ay + ui.lineheight); + glColorHex(UI_COLOR_TEXT_FG); + draw_string_part(ax, ay, a, p); + glColorHex(UI_COLOR_TEXT_SEL_FG); + draw_string_part(px, ay, p, b); + } + else if (p < a && q > b) + { + glColorHex(UI_COLOR_TEXT_SEL_BG); + glRectf(ax, ay, bx, ay + ui.lineheight); + glColorHex(UI_COLOR_TEXT_SEL_FG); + draw_string_part(ax, ay, a, b); + } + else + { + glColorHex(UI_COLOR_TEXT_FG); + draw_string_part(ax, ay, a, b); + } + } + else + { + glColorHex(UI_COLOR_TEXT_FG); + draw_string_part(ax, ay, a, b); + } + ay += ui.lineheight; } return state; diff --git a/platform/gl/gl-main.c b/platform/gl/gl-main.c index c3c95618..15adb14d 100644 --- a/platform/gl/gl-main.c +++ b/platform/gl/gl-main.c @@ -608,7 +608,7 @@ static void password_dialog(void) { ui_layout(T, X, NW, 2, 2); ui_label("Password:"); - is = ui_input(&input_password, 200); + is = ui_input(&input_password, 200, 1); ui_layout(B, X, NW, 2, 2); ui_panel_begin(0, ui.gridsize, 0, 0, 0); @@ -1135,7 +1135,7 @@ static void do_canvas(void) ui_layout(L, NONE, W, 2, 0); ui_label("Search:"); ui_layout(ALL, X, E, 2, 0); - if (ui_input(&search_input, 0) == UI_INPUT_ACCEPT) + if (ui_input(&search_input, 0, 1) == UI_INPUT_ACCEPT) { showsearch = 0; search_page = -1; -- cgit v1.2.3