summaryrefslogtreecommitdiff
path: root/platform/gl/gl-input.c
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2018-05-23 15:28:17 +0200
committerRobin Watts <robin.watts@artifex.com>2018-06-22 16:48:47 +0100
commit33a521f1f52845d1a056f37afc62336b7bf6081c (patch)
treebd49673be7e00c95e2022fa13d98b2d261074a60 /platform/gl/gl-input.c
parent7a30381733d7b03d109b85dcfb7b9c71a2d25b21 (diff)
downloadmupdf-33a521f1f52845d1a056f37afc62336b7bf6081c.tar.xz
gl: Multi-line text input fields.
Diffstat (limited to 'platform/gl/gl-input.c')
-rw-r--r--platform/gl/gl-input.c265
1 files changed, 209 insertions, 56 deletions
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 <string.h>
+#include <stdio.h>
+
+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;