From da461914b3814691ae1390ecd8f5e4e5b8e97bea Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Jul 26 2020 15:33:57 +0000 Subject: text layout --- diff --git a/demo/src/common.c b/demo/src/common.c index 6ce297c..fa50e2e 100644 --- a/demo/src/common.c +++ b/demo/src/common.c @@ -69,6 +69,7 @@ void commonInit() { void commonDraw() { pushDrawingState(); + noFill(); textFontDefault(); textSize(16); text(buffer, 16, 48); diff --git a/demo/src/font.c b/demo/src/font.c index a8326b3..c109d22 100644 --- a/demo/src/font.c +++ b/demo/src/font.c @@ -21,9 +21,11 @@ void fontDraw() { double x = 512; double y = 256; + fill("1 0 0 0.5"); textSize(64*(1+sin(worldGetSeconds()/4))); text("Here is the\nleft aligned\ntext. VAW.", x, y); + noFill(); textFont(font); textAlign(HALIGN_RIGHT, VALIGN_TOP); text("Here is the\nright aligned\ntext. VAW.", x, y); diff --git a/src/font.c b/src/font.c index d6a3bab..aced0b1 100644 --- a/src/font.c +++ b/src/font.c @@ -50,6 +50,7 @@ struct _HeliFontInstance { struct _HeliCharDesc { HeliGlyph *glyph; + int charpos; double pos; double x, y; HeliCharDesc *next; @@ -62,6 +63,14 @@ typedef struct _HeliLineDesc { double height; } HeliLineDesc; +struct _TextLayout { + Font font; + int linesCount; + HeliLineDesc *lines; + HeliCharDesc *chars; + double l, t, r, b; +}; + struct _Font { HeliFontInstance *instance; }; @@ -270,11 +279,9 @@ Font createFont(const char *path) { if (!heliInitialized) return NULL; if (!path) path = blankPath; Font f = calloc(1, sizeof(*f)); - f->instance = (HeliFontInstance*)heliStringmapGet(&fontCache, path); - if (!f->instance) { - f->instance = loadFont(path); - heliStringmapAdd(&fontCache, path, f->instance, (HeliFreeCallback)&unloadFont); - } + HeliPair *item = heliStringmapGet(&fontCache, path); + if (!item) item = heliStringmapAdd(&fontCache, path, loadFont(path), (HeliFreeCallback)&unloadFont); + f->instance = (HeliFontInstance*)item->value; ++f->instance->refcount; heliObjectRegister(f, (HeliFreeCallback)&fontDestroy); return f; @@ -304,38 +311,43 @@ void textSize(double size) { heliDrawingGetState()->fontSize = size; } -void text(const char *text, double x, double y) { - resetPath(); +static void expand(double *min, double *max, double x) { + if (*min > x) *min = x; + if (*max < x) *max = x; +} + + +TextLayout createTextLayout(const char *text) { + TextLayout layout = calloc(1, sizeof(*layout)); - if (!text || !text[0]) return; + if (!text || !text[0]) return layout; HeliDrawingState *drawingState = heliDrawingGetState(); - double fontScale = drawingState->fontSize/FONT_BASE_SIZE; - if (fontScale < HELI_PRECISION) return; - if (drawingState->colorStroke[3] < HELI_PRECISION) return; - if (!defaultFont) defaultFont = createFont(NULL); HeliFontInstance *f = drawingState->font ? drawingState->font->instance : NULL; HeliFontInstance *ff = defaultFont ? defaultFont->instance : NULL; if (!f) f = ff; if (!ff) ff = f; - if (!f) return; + if (!f) return layout; if (!f->face) f = ff; if (!ff->face) ff = f; - if (!f->face) return; + if (!f->face) return layout; - int linesCount = 1; int linesAllocated = 256; - HeliLineDesc *lines = calloc(linesAllocated, sizeof(HeliLineDesc)); - HeliLineDesc *line = lines; + layout->linesCount = 1; + layout->lines = calloc(linesAllocated, sizeof(HeliLineDesc)); + HeliLineDesc *line = layout->lines; line->height = f->height; - HeliCharDesc *chars = calloc(strlen(text), sizeof(HeliCharDesc)); + layout->chars = calloc(strlen(text) + 1, sizeof(HeliCharDesc)); + if (defaultFont && defaultFont->instance != f) + layout->font = createFont(f->path); // gather glyphs const char *c = text; while(1) { + const char *charpos = c; int code = FONT_BADCHAR_CODE; while((*c & 0xC0) == 0x80) ++c; if ((*c & 0x80) == 0x00) { @@ -355,38 +367,19 @@ void text(const char *text, double x, double y) { } ++c; - if (code == 0) break; - if (code == 10 || code == 13) { - if ((code == 10 && *c == 13) || (code == 13 && *c == 10)) ++c; - if (linesCount >= linesAllocated) { - linesAllocated += linesAllocated/4; - lines = realloc(lines, linesAllocated*sizeof(HeliLineDesc)); - memset(lines + linesCount, 0, (linesAllocated - linesCount)*sizeof(HeliLineDesc)); - line = &lines[linesCount - 1]; - } - ++line; - ++linesCount; - line->begin = line->end = (line - 1)->end; - line->height = f->height; - continue; - } - HeliGlyph *glyph = &blankGlyph; - if (!glyph->fi) glyph = getGlyph(f, code); - if (!glyph->fi) glyph = getGlyph(ff, code); + int code2 = (code == 0 || code == 10 || code == 13) ? 32 : code; + if (!glyph->fi) glyph = getGlyph(f, code2); + if (!glyph->fi) glyph = getGlyph(ff, code2); if (!glyph->fi) glyph = getGlyph(f, FONT_BADCHAR_CODE); if (!glyph->fi) glyph = getGlyph(ff, FONT_BADCHAR_CODE); if (!glyph->fi) continue; - HeliCharDesc *cc = &chars[line->end++]; + HeliCharDesc *cc = &layout->chars[line->end++]; + cc->charpos = charpos - text; cc->glyph = glyph; if (line->height < glyph->fi->height) line->height = glyph->fi->height; - if (line->end - 1 <= line->begin) { - line->l = cc->glyph->l; - line->t = cc->glyph->t; - line->r = cc->glyph->r; - line->b = cc->glyph->b; - } else { + if (line->begin < line->end - 1) { HeliCharDesc *cp = cc - 1; cc->pos = cp->pos + cp->glyph->advance; FT_Vector kerning = {}; @@ -398,23 +391,39 @@ void text(const char *text, double x, double y) { FT_KERNING_UNFITTED, &kerning )) cc->pos += kerning.x/64.0; - if (line->l > cc->glyph->l + cc->pos) line->l = cc->glyph->l + cc->pos; - if (line->r < cc->glyph->r + cc->pos) line->r = cc->glyph->r + cc->pos; - if (line->t > cc->glyph->t) line->t = cc->glyph->t; - if (line->b < cc->glyph->b) line->b = cc->glyph->b; } + expand(&line->l, &line->r, cc->pos + cc->glyph->l); + expand(&line->l, &line->r, cc->pos + cc->glyph->r); + expand(&line->t, &line->b, cc->glyph->t); + expand(&line->t, &line->b, cc->glyph->b); + expand(&line->t, &line->b, -glyph->fi->height); + + if (code == 10 || code == 13) { + if ((code == 10 && *c == 13) || (code == 13 && *c == 10)) ++c; + if (layout->linesCount >= linesAllocated) { + linesAllocated += linesAllocated/4; + layout->lines = realloc(layout->lines, linesAllocated*sizeof(HeliLineDesc)); + memset(layout->lines + layout->linesCount, 0, (linesAllocated - layout->linesCount)*sizeof(HeliLineDesc)); + line = &layout->lines[layout->linesCount - 1]; + } + ++line; + ++layout->linesCount; + line->begin = line->end = (line - 1)->end; + line->height = f->height; + } + if (code == 0) break; } - int charsCount = lines[linesCount-1].end; - if (charsCount <= 0) return; + int charsCount = layout->lines[layout->linesCount-1].end; + if (charsCount <= 0) return layout; // bounds double l = 0; - double t = -lines->height; + double t = -layout->lines->height; double r = 0; double b = 0; - for(int i = 0; i < linesCount; ++i) { - HeliLineDesc *line = &lines[i]; + for(int i = 0; i < layout->linesCount; ++i) { + HeliLineDesc *line = layout->lines + i; if (i > 0) line->y = (line-1)->y + line->height; if (l > line->l) l = line->l; if (r < line->r) r = line->r; @@ -423,26 +432,35 @@ void text(const char *text, double x, double y) { } // alignment + double dx = drawingState->horAlign == HALIGN_RIGHT ? -r + : drawingState->horAlign == HALIGN_CENTER ? -0.5*(l + r) + : -l; double dy = drawingState->vertAlign == VALIGN_BOTTOM ? -b : drawingState->vertAlign == VALIGN_CENTER ? -0.5*(t + b) : -t; - for(int i = 0; i < linesCount; ++i) { - HeliLineDesc *line = &lines[i]; + for(int i = 0; i < layout->linesCount; ++i) { + HeliLineDesc *line = layout->lines + i; line->x = drawingState->horAlign == HALIGN_RIGHT ? -line->r : drawingState->horAlign == HALIGN_CENTER ? -0.5*(line->l + line->r) : -line->l; line->y += dy; for(int j = line->begin; j < line->end; ++j) { - HeliCharDesc *cc = &chars[j]; + HeliCharDesc *cc = layout->chars + j; cc->x = cc->pos + line->x; cc->y = line->y; } } + // bounds + layout->l = l + dx; + layout->t = t + dy; + layout->r = r + dx; + layout->b = b + dy; + // sort by textures for(int i = 0; i < charsCount-1; ++i) - chars[i].next = &chars[i+1]; - HeliCharDesc *first = NULL, *last = NULL, *current = chars; + layout->chars[i].next = &layout->chars[i+1]; + HeliCharDesc *first = NULL, *last = NULL, *current = layout->chars; while(current) { if (!current->next) { current = current->next = first; @@ -456,8 +474,45 @@ void text(const char *text, double x, double y) { current = current->next; } } + + return layout; +} + + +void textLayoutDestroy(TextLayout layout) { + if (layout) { + free(layout->lines); + free(layout->chars); + if (layout->font) fontDestroy(layout->font); + } + free(layout); +} + + +void textLayoutDrawSubstr(TextLayout layout, double x, double y, int start, int length) { + resetPath(); + + if (layout->linesCount <= 0) return; + assert(layout->lines); + assert(layout->chars); + int charsCount = layout->lines[layout->linesCount - 1].end; + if (charsCount <= 0) return; + + int textEnd = layout->chars[charsCount - 1].charpos; + int begin = start; + if (begin < 0) begin = 0; + long long endll = (long long)begin + length; // to avoid overflow on length == INT_MAX and begin > 0 + if (endll > textEnd) endll = textEnd; + int end = (int)endll; + if (begin >= end) return; + + HeliDrawingState *drawingState = heliDrawingGetState(); - // draw + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + if (fontScale <= HELI_PRECISION) return; + if ( drawingState->colorStroke[3] <= HELI_PRECISION + && drawingState->colorFill[3] <= HELI_PRECISION ) return; + const double borderK = 0.5*FONT_MAP_BORDERSIZE/FONT_MAP_GLYPHWORKSIZE; const double texStep = 1.0/FONT_MAP_CNT; const double texOffset = -0.5*FONT_MAP_BORDERSIZE/FONT_MAP_TEXSIZE; @@ -466,52 +521,199 @@ void text(const char *text, double x, double y) { glPushMatrix(); glTranslated(x, y, 0); glScaled(fontScale, fontScale, 1); - glColor4dv(drawingState->colorStroke); - HeliFontMap *currentMap = NULL; - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - for(HeliCharDesc *cc = chars; cc; cc = cc->next) { - if ( !cc->glyph->map - || cc->glyph->r - cc->glyph->l <= HELI_PRECISION - || cc->glyph->b - cc->glyph->t <= HELI_PRECISION ) - continue; - if (currentMap != cc->glyph->map) { - glEnd(); - currentMap = cc->glyph->map; - glBindTexture(GL_TEXTURE_2D, currentMap->texid); - glBegin(GL_QUADS); + + if (drawingState->colorFill[3] > HELI_PRECISION) { + double strokeAlpha = drawingState->colorStroke[3]; + drawingState->colorStroke[3] = 0; + for(int i = 0; i < layout->linesCount; ++i) { + HeliLineDesc *line = layout->lines + i; + int found = FALSE; + double l = 0, r = 0; + for(int j = line->begin; j < line->end; ++j) { + HeliCharDesc *cc = layout->chars + j; + if (cc->charpos < begin || cc->charpos > end) continue; + if (!found) { l = r = cc->x; found = TRUE; } + expand(&l, &r, cc->x); + if (cc->glyph && cc->charpos < end) { + expand(&l, &r, cc->x + cc->glyph->l); + expand(&l, &r, cc->x + cc->glyph->r); + } + } + if (found) + rect(l, line->t + line->y, r-l, line->b - line->t); } - - double dx = (cc->glyph->r - cc->glyph->l)*borderK; - double dy = (cc->glyph->b - cc->glyph->t)*borderK; - double l = cc->x + cc->glyph->l - dx; - double t = cc->y + cc->glyph->t - dy; - double r = cc->x + cc->glyph->r + dx; - double b = cc->y + cc->glyph->b + dy; - - int gi = cc->glyph - cc->glyph->map->glyphs; - double tl = (gi % FONT_MAP_CNT)*texStep + texOffset; - double tt = (gi / FONT_MAP_CNT)*texStep + texOffset; - double tr = tl + texStep; - double tb = tt + texStep; - - glTexCoord2d(tl, tt); glVertex2d(l, t); - glTexCoord2d(tr, tt); glVertex2d(r, t); - glTexCoord2d(tr, tb); glVertex2d(r, b); - glTexCoord2d(tl, tb); glVertex2d(l, b); + drawingState->colorStroke[3] = strokeAlpha; } - glEnd(); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); + + if (drawingState->colorStroke[3] > HELI_PRECISION) { + glColor4dv(drawingState->colorStroke); + HeliFontMap *currentMap = NULL; + glEnable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + for(HeliCharDesc *cc = layout->chars; cc; cc = cc->next) { + if (cc->charpos < begin || cc->charpos >= end) continue; + if ( !cc->glyph->map + || cc->glyph->r - cc->glyph->l <= HELI_PRECISION + || cc->glyph->b - cc->glyph->t <= HELI_PRECISION ) + continue; + if (currentMap != cc->glyph->map) { + glEnd(); + currentMap = cc->glyph->map; + glBindTexture(GL_TEXTURE_2D, currentMap->texid); + glBegin(GL_QUADS); + } + + double dx = (cc->glyph->r - cc->glyph->l)*borderK; + double dy = (cc->glyph->b - cc->glyph->t)*borderK; + double l = cc->x + cc->glyph->l - dx; + double t = cc->y + cc->glyph->t - dy; + double r = cc->x + cc->glyph->r + dx; + double b = cc->y + cc->glyph->b + dy; + + int gi = cc->glyph - cc->glyph->map->glyphs; + double tl = (gi % FONT_MAP_CNT)*texStep + texOffset; + double tt = (gi / FONT_MAP_CNT)*texStep + texOffset; + double tr = tl + texStep; + double tb = tt + texStep; + + glTexCoord2d(tl, tt); glVertex2d(l, t); + glTexCoord2d(tr, tt); glVertex2d(r, t); + glTexCoord2d(tr, tb); glVertex2d(r, b); + glTexCoord2d(tl, tb); glVertex2d(l, b); + } + glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + } + glPopMatrix(); glDisable(GL_MULTISAMPLE); glColor4d(1, 1, 1, 1); } +void textLayoutDrawFrom(TextLayout layout, double x, double y, int start) + { textLayoutDrawSubstr(layout, x, y, start, INT_MAX); } +void textLayoutDraw(TextLayout layout, double x, double y) + { textLayoutDrawFrom(layout, x, y, 0); } + + +void text(const char *text, double x, double y) { + resetPath(); + if (!text || !text[0]) return; + + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + if (fontScale < HELI_PRECISION) return; + if ( drawingState->colorStroke[3] < HELI_PRECISION + && drawingState->colorFill[3] < HELI_PRECISION ) return; + + TextLayout layout = createTextLayout(text); + textLayoutDraw(layout, x, y); + textLayoutDestroy(layout); +} + + +double textLayoutGetWidth(TextLayout layout) { + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + return (layout->r - layout->l)*fontScale; +} + + +double textLayoutGetHeight(TextLayout layout) { + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + return (layout->b - layout->t)*fontScale; +} + + void heliFontFinish() { heliArrayDestroy(&fontCache); if (ftInitialized && ftLibrary) FT_Done_FreeType(ftLibrary); ftLibrary = NULL; ftInitialized = FALSE; } + +static void findChar(TextLayout layout, int cursor, int *lineIndex, int *charIndex) { + *lineIndex = 0; + *charIndex = 0; + if (!layout || layout->linesCount <= 0 || !layout->lines || !layout->chars) return; + + *lineIndex = layout->linesCount - 1; + *charIndex = layout->lines[*lineIndex].end - 1; + for(int i = 0; i < layout->linesCount; ++i) { + HeliLineDesc *line = layout->lines + i; + if (line->begin < line->end) { + if (layout->chars[line->end-1].charpos >= cursor) { + *lineIndex = i; + *charIndex = line->end-1; + for(int j = line->begin; j < line->end; ++j) + if (layout->chars[j].charpos >= cursor) + { *charIndex = j; break; } + return; + } + } + } +} + +static int moveCursorVert(TextLayout layout, int cursor, int offset) { + if (!layout || layout->linesCount <= 0 || !layout->lines || !layout->chars) return 0; + + int li = 0, ci = 0; + findChar(layout, cursor, &li, &ci); + + li += offset; + if (li < 0) + return 0; + if (li >= layout->linesCount) + return layout->chars[ layout->lines[layout->linesCount-1].end - 1 ].charpos; + + double x = layout->chars[ci].x; + HeliLineDesc *line = layout->lines + li; + for(int j = line->begin; j < line->end; ++j) { + HeliCharDesc *cc1 = layout->chars + j; + double xx = layout->chars[j].x; + if (xx + HELI_PRECISION >= x || j == line->end - 1) { + if (j > line->begin) { + HeliCharDesc *cc0 = cc1 - 1; + if (fabs(cc0->x - x) < fabs(xx - x)) return cc0->charpos; + } + return cc1->charpos; + } + } + return cursor; +} + +int textLayoutCursorUp(TextLayout layout, int cursor) + { return moveCursorVert(layout, cursor, -1); } + +int textLayoutCursorDown(TextLayout layout, int cursor) + { return moveCursorVert(layout, cursor, 1); } + +double textLayoutCursorGetX(TextLayout layout, int cursor) { + if (!layout || layout->linesCount <= 0 || !layout->lines || !layout->chars) return 0; + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + int li = 0, ci = 0; + findChar(layout, cursor, &li, &ci); + return layout->chars[ci].x * fontScale; +} + +double textLayoutCursorGetY(TextLayout layout, int cursor) { + if (!layout || layout->linesCount <= 0 || !layout->lines || !layout->chars) return 0; + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + int li = 0, ci = 0; + findChar(layout, cursor, &li, &ci); + return layout->chars[ci].y * fontScale; +} + +double textLayoutCursorGetHeight(TextLayout layout, int cursor) { + if (!layout || layout->linesCount <= 0 || !layout->lines || !layout->chars) return 0; + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + int li = 0, ci = 0; + findChar(layout, cursor, &li, &ci); + return layout->lines[li].height * fontScale; +} diff --git a/src/font.h b/src/font.h index 27e72e2..2f5bd1a 100644 --- a/src/font.h +++ b/src/font.h @@ -6,6 +6,7 @@ typedef struct _Font *Font; +typedef struct _TextLayout *TextLayout; typedef enum _HAlign { @@ -30,5 +31,18 @@ void text(const char *text, double x, double y); void textAlign(HAlign hor, VAlign vert); void textSize(double size); +TextLayout createTextLayout(const char *text); +void textLayoutDestroy(TextLayout layout); +void textLayoutDraw(TextLayout layout, double x, double y); +void textLayoutDrawFrom(TextLayout layout, double x, double y, int start); +void textLayoutDrawSubstr(TextLayout layout, double x, double y, int start, int length); +double textLayoutGetWidth(TextLayout layout); +double textLayoutGetHeight(TextLayout layout); +int textLayoutCursorUp(TextLayout layout, int cursor); +int textLayoutCursorDown(TextLayout layout, int cursor); +double textLayoutCursorGetX(TextLayout layout, int cursor); +double textLayoutCursorGetY(TextLayout layout, int cursor); +double textLayoutCursorGetHeight(TextLayout layout, int cursor); + #endif diff --git a/src/test.c b/src/test.c index 713f2cd..c44876d 100644 --- a/src/test.c +++ b/src/test.c @@ -8,7 +8,7 @@ void heliDoTests() { } #else -static printStringset(HeliArray *a) { +static void printStringset(HeliArray *a) { for(int i = 0; i < a->count; ++i) printf(" %s\n", (const char*)a->items[i].key); } diff --git a/src/world.c b/src/world.c index 1a81002..351faff 100644 --- a/src/world.c +++ b/src/world.c @@ -473,6 +473,8 @@ void worldRun() { if (init()) { heliInitialized = TRUE; + heliDoTests(); + if (initCallback) initCallback(); run(); if (deinitCallback) deinitCallback(); diff --git a/src/worldui.c b/src/worldui.c index 945c9ea..ddd91e5 100644 --- a/src/worldui.c +++ b/src/worldui.c @@ -113,6 +113,8 @@ static void paste(HeliDialog *dialog) { static void draw(HeliDialog *dialog) { pushDrawingState(); + const double time = SDL_GetTicks()*1e-3; + const double w = worldGetWidth(); const double h = worldGetHeight(); const double border = 16; @@ -136,6 +138,8 @@ static void draw(HeliDialog *dialog) { const char *strokeColor = "white"; const char *fillColor = "0.3 0.3 0.3"; + const char *selTextColor = "black"; + const char *selFillColor = "white"; strokeWeight(1); textFontDefault(); textSize(16); @@ -146,9 +150,39 @@ static void draw(HeliDialog *dialog) { noFill(); stroke(strokeColor); - cliprect(l, t, r - l, b - t); textAlign(HALIGN_LEFT, VALIGN_TOP); - text(dialog->answer, l + dialog->scrollX, t + dialog->scrollY); + TextLayout layout = createTextLayout(dialog->answer); + double tx = l + dialog->scrollX; + double ty = t + dialog->scrollY; + double cx = textLayoutCursorGetX(layout, dialog->pos) + tx; + double cy = textLayoutCursorGetY(layout, dialog->pos) + ty; + double ch = textLayoutCursorGetHeight(layout, dialog->pos); + cliprect(l-1, t-1, r-l+2, b-t+2); + if (dialog->pos == dialog->selPos) { + textLayoutDraw(layout, tx, ty); + } else { + int p0 = dialog->pos < dialog->selPos ? dialog->pos : dialog->selPos; + int p1 = dialog->pos < dialog->selPos ? dialog->selPos : dialog->pos; + fill(selFillColor); + stroke(selTextColor); + textLayoutDrawSubstr(layout, tx, ty, p0, p1 - p0); + noFill(); + stroke(strokeColor); + textLayoutDrawSubstr(layout, tx, ty, 0, p0); + textLayoutDrawFrom(layout, tx, ty, p1); + } + stroke(rgba(1, 1, 1, 0.5 + 0.5*sin(time/0.5*2*PI))); + line(cx, cy, cx, cy - ch); + int shift = keyDown("left shift") || keyDown("right shift"); + if (keyWentDown("up")) { + dialog->pos = textLayoutCursorUp(layout, dialog->pos); + if (!shift) dialog->selPos = dialog->pos; + } + if (keyWentDown("down")) { + dialog->pos = textLayoutCursorDown(layout, dialog->pos); + if (!shift) dialog->selPos = dialog->pos; + } + textLayoutDestroy(layout); noClip(); noFill(); @@ -211,13 +245,25 @@ void heliDialogDraw(HeliDialog *dialog) { if (!shift) dialog->selPos = dialog->pos; } - if (ctrl && keyWentDown("home")) { - dialog->pos = 0; + if (keyWentDown("home")) { + if (ctrl) { + dialog->pos = 0; + } else { + while( dialog->pos > 0 + && dialog->answer[dialog->pos-1] != '\r' + && dialog->answer[dialog->pos-1] != '\n' ) --dialog->pos; + } if (!shift) dialog->selPos = dialog->pos; } - if (ctrl && keyWentDown("end")) { - dialog->pos = strlen(dialog->answer); + if (keyWentDown("end")) { + if (ctrl) { + dialog->pos = strlen(dialog->answer); + } else { + while( dialog->answer[dialog->pos] != 0 + && dialog->answer[dialog->pos] != '\r' + && dialog->answer[dialog->pos] != '\n' ) ++dialog->pos; + } if (!shift) dialog->selPos = dialog->pos; } @@ -230,6 +276,8 @@ void heliDialogDraw(HeliDialog *dialog) { } } + if (ctrl && keyWentDown("a")) + { dialog->selPos = 0; dialog->pos = strlen(dialog->answer); } if (ctrl && keyWentDown("c")) copy(dialog); if (ctrl && keyWentDown("insert"))