| @@ -802,6 +802,13 @@ BND_EXPORT void bndRadioButton(NVGcontext *ctx, | |||||
| float x, float y, float w, float h, int flags, BNDwidgetState state, | float x, float y, float w, float h, int flags, BNDwidgetState state, | ||||
| int iconid, const char *label); | int iconid, const char *label); | ||||
| // Calculate the corresponding text position for given coordinates px/py | |||||
| // in a text field. | |||||
| // See bndTextField for more info. | |||||
| BND_EXPORT int bndTextFieldTextPosition(NVGcontext *ctx, float x, float y, float w, float h, | |||||
| int iconid, const char *text, int px, int py); | |||||
| // Draw a text field with its lower left origin at (x,y) and size of (w,h), | // Draw a text field with its lower left origin at (x,y) and size of (w,h), | ||||
| // where flags is one or multiple flags from BNDcornerFlags and state denotes | // where flags is one or multiple flags from BNDcornerFlags and state denotes | ||||
| // the widgets current UI state. | // the widgets current UI state. | ||||
| @@ -1044,6 +1051,12 @@ BND_EXPORT void bndIconLabelValue(NVGcontext *ctx, float x, float y, float w, fl | |||||
| BND_EXPORT void bndNodeIconLabel(NVGcontext *ctx, float x, float y, float w, float h, | BND_EXPORT void bndNodeIconLabel(NVGcontext *ctx, float x, float y, float w, float h, | ||||
| int iconid, NVGcolor color, NVGcolor shadowColor, int align, | int iconid, NVGcolor color, NVGcolor shadowColor, int align, | ||||
| float fontsize, const char *label); | float fontsize, const char *label); | ||||
| // Calculate the corresponding text position for given coordinates px/py | |||||
| // in an iconLabel. | |||||
| // See bndIconLabelCaret for more info. | |||||
| BND_EXPORT int bndIconLabelTextPosition(NVGcontext *ctx, float x, float y, float w, float h, | |||||
| int iconid, float fontsize, const char *label, int px, int py); | |||||
| // Draw an optional icon specified by <iconid>, an optional label and | // Draw an optional icon specified by <iconid>, an optional label and | ||||
| // a caret with given fontsize and color within a widget box. | // a caret with given fontsize and color within a widget box. | ||||
| @@ -1208,6 +1221,9 @@ static double bnd_fmax ( double a, double b ) | |||||
| // max glyphs for position testing | // max glyphs for position testing | ||||
| #define BND_MAX_GLYPHS 1024 | #define BND_MAX_GLYPHS 1024 | ||||
| // max rows for position testing | |||||
| #define BND_MAX_ROWS 32 | |||||
| // text distance from bottom | // text distance from bottom | ||||
| #define BND_TEXT_PAD_DOWN 7 | #define BND_TEXT_PAD_DOWN 7 | ||||
| @@ -1445,6 +1461,12 @@ void bndRadioButton(NVGcontext *ctx, | |||||
| BND_LABEL_FONT_SIZE, label, NULL); | BND_LABEL_FONT_SIZE, label, NULL); | ||||
| } | } | ||||
| int bndTextFieldTextPosition(NVGcontext *ctx, float x, float y, float w, float h, | |||||
| int iconid, const char *text, int px, int py) { | |||||
| return bndIconLabelTextPosition(ctx, x, y, w, h, | |||||
| iconid, BND_LABEL_FONT_SIZE, text, px, py); | |||||
| } | |||||
| void bndTextField(NVGcontext *ctx, | void bndTextField(NVGcontext *ctx, | ||||
| float x, float y, float w, float h, int flags, BNDwidgetState state, | float x, float y, float w, float h, int flags, BNDwidgetState state, | ||||
| int iconid, const char *text, int cbegin, int cend) { | int iconid, const char *text, int cbegin, int cend) { | ||||
| @@ -2131,17 +2153,17 @@ void bndIconLabelValue(NVGcontext *ctx, float x, float y, float w, float h, | |||||
| + nvgTextBounds(ctx, 1, 1, value, NULL, NULL); | + nvgTextBounds(ctx, 1, 1, value, NULL, NULL); | ||||
| x += ((w-BND_PAD_RIGHT-pleft)-width)*0.5f; | x += ((w-BND_PAD_RIGHT-pleft)-width)*0.5f; | ||||
| } | } | ||||
| y += h-BND_TEXT_PAD_DOWN; | |||||
| y += BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN; | |||||
| nvgText(ctx, x, y, label, NULL); | nvgText(ctx, x, y, label, NULL); | ||||
| x += label_width; | x += label_width; | ||||
| nvgText(ctx, x, y, BND_LABEL_SEPARATOR, NULL); | nvgText(ctx, x, y, BND_LABEL_SEPARATOR, NULL); | ||||
| x += sep_width; | x += sep_width; | ||||
| nvgText(ctx, x, y, value, NULL); | |||||
| nvgText(ctx, x, y, value, NULL); | |||||
| } else { | } else { | ||||
| nvgTextAlign(ctx, | nvgTextAlign(ctx, | ||||
| (align==BND_LEFT)?(NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE): | (align==BND_LEFT)?(NVG_ALIGN_LEFT|NVG_ALIGN_BASELINE): | ||||
| (NVG_ALIGN_CENTER|NVG_ALIGN_BASELINE)); | (NVG_ALIGN_CENTER|NVG_ALIGN_BASELINE)); | ||||
| nvgTextBox(ctx,x+pleft,y+h-BND_TEXT_PAD_DOWN, | |||||
| nvgTextBox(ctx,x+pleft,y+BND_WIDGET_HEIGHT-BND_TEXT_PAD_DOWN, | |||||
| w-BND_PAD_RIGHT-pleft,label, NULL); | w-BND_PAD_RIGHT-pleft,label, NULL); | ||||
| } | } | ||||
| } else if (iconid >= 0) { | } else if (iconid >= 0) { | ||||
| @@ -2171,10 +2193,70 @@ void bndNodeIconLabel(NVGcontext *ctx, float x, float y, float w, float h, | |||||
| } | } | ||||
| } | } | ||||
| int bndIconLabelTextPosition(NVGcontext *ctx, float x, float y, float w, float h, | |||||
| int iconid, float fontsize, const char *label, int px, int py) { | |||||
| float bounds[4]; | |||||
| float pleft = BND_TEXT_RADIUS; | |||||
| if (!label) return -1; | |||||
| if (iconid >= 0) | |||||
| pleft += BND_ICON_SHEET_RES; | |||||
| if (bnd_font < 0) return -1; | |||||
| x += pleft; | |||||
| y += BND_WIDGET_HEIGHT - BND_TEXT_PAD_DOWN; | |||||
| nvgFontFaceId(ctx, bnd_font); | |||||
| nvgFontSize(ctx, fontsize); | |||||
| nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE); | |||||
| w -= BND_TEXT_RADIUS + pleft; | |||||
| float asc, desc, lh; | |||||
| static NVGtextRow rows[BND_MAX_ROWS]; | |||||
| int nrows = nvgTextBreakLines( | |||||
| ctx, label, NULL, w, rows, BND_MAX_ROWS); | |||||
| if (nrows == 0) return 0; | |||||
| nvgTextBoxBounds(ctx, x, y, w, label, NULL, bounds); | |||||
| nvgTextMetrics(ctx, &asc, &desc, &lh); | |||||
| // calculate vertical position | |||||
| int row = bnd_clamp((int)((float)(py - bounds[1]) / lh), 0, nrows - 1); | |||||
| // search horizontal position | |||||
| static NVGglyphPosition glyphs[BND_MAX_GLYPHS]; | |||||
| int nglyphs = nvgTextGlyphPositions( | |||||
| ctx, x, y, rows[row].start, rows[row].end + 1, glyphs, BND_MAX_GLYPHS); | |||||
| int col, p = 0; | |||||
| for (col = 0; col < nglyphs && glyphs[col].x < px; ++col) | |||||
| p = glyphs[col].str - label; | |||||
| // see if we should move one character further | |||||
| if (col > 0 && col < nglyphs && glyphs[col].x - px < px - glyphs[col - 1].x) | |||||
| p = glyphs[col].str - label; | |||||
| return p; | |||||
| } | |||||
| static void bndCaretPosition(NVGcontext *ctx, float x, float y, | |||||
| float desc, float lineHeight, const char *caret, NVGtextRow *rows,int nrows, | |||||
| int *cr, float *cx, float *cy) { | |||||
| static NVGglyphPosition glyphs[BND_MAX_GLYPHS]; | |||||
| int r,nglyphs; | |||||
| for (r=0; r < nrows && rows[r].end < caret; ++r); | |||||
| *cr = r; | |||||
| *cx = x; | |||||
| *cy = y-lineHeight-desc + r*lineHeight; | |||||
| if (nrows == 0) return; | |||||
| *cx = rows[r].minx; | |||||
| nglyphs = nvgTextGlyphPositions( | |||||
| ctx, x, y, rows[r].start, rows[r].end+1, glyphs, BND_MAX_GLYPHS); | |||||
| for (int i=0; i < nglyphs; ++i) { | |||||
| *cx=glyphs[i].x; | |||||
| if (glyphs[i].str == caret) break; | |||||
| } | |||||
| } | |||||
| void bndIconLabelCaret(NVGcontext *ctx, float x, float y, float w, float h, | void bndIconLabelCaret(NVGcontext *ctx, float x, float y, float w, float h, | ||||
| int iconid, NVGcolor color, float fontsize, const char *label, | int iconid, NVGcolor color, float fontsize, const char *label, | ||||
| NVGcolor caretcolor, int cbegin, int cend) { | NVGcolor caretcolor, int cbegin, int cend) { | ||||
| float bounds[4]; | |||||
| float pleft = BND_TEXT_RADIUS; | float pleft = BND_TEXT_RADIUS; | ||||
| if (!label) return; | if (!label) return; | ||||
| if (iconid >= 0) { | if (iconid >= 0) { | ||||
| @@ -2194,70 +2276,37 @@ void bndIconLabelCaret(NVGcontext *ctx, float x, float y, float w, float h, | |||||
| w -= BND_TEXT_RADIUS+pleft; | w -= BND_TEXT_RADIUS+pleft; | ||||
| if (cend >= cbegin) { | if (cend >= cbegin) { | ||||
| #if 1 | |||||
| float c0,c1; | |||||
| const char *cb;const char *ce; | |||||
| static NVGglyphPosition glyphs[BND_MAX_GLYPHS]; | |||||
| int nglyphs = nvgTextGlyphPositions( | |||||
| ctx, x, y, label, label+cend+1, glyphs, BND_MAX_GLYPHS); | |||||
| c0=glyphs[0].x; | |||||
| c1=glyphs[nglyphs-1].x; | |||||
| cb = label+cbegin; ce = label+cend; | |||||
| // TODO: this is slow | |||||
| for (int i=0; i < nglyphs; ++i) { | |||||
| if (glyphs[i].str == cb) | |||||
| c0 = glyphs[i].x; | |||||
| if (glyphs[i].str == ce) | |||||
| c1 = glyphs[i].x; | |||||
| } | |||||
| int c0r,c1r; | |||||
| float c0x,c0y,c1x,c1y; | |||||
| float desc,lh; | |||||
| static NVGtextRow rows[BND_MAX_ROWS]; | |||||
| int nrows = nvgTextBreakLines( | |||||
| ctx, label, label+cend+1, w, rows, BND_MAX_ROWS); | |||||
| nvgTextMetrics(ctx, NULL, &desc, &lh); | |||||
| bndCaretPosition(ctx, x, y, desc, lh, label+cbegin, | |||||
| rows, nrows, &c0r, &c0x, &c0y); | |||||
| bndCaretPosition(ctx, x, y, desc, lh, label+cend, | |||||
| rows, nrows, &c1r, &c1x, &c1y); | |||||
| nvgTextBounds(ctx,x,y,label,NULL, bounds); | |||||
| nvgBeginPath(ctx); | |||||
| if (cbegin == cend) { | |||||
| nvgFillColor(ctx, nvgRGBf(0.337,0.502,0.761)); | |||||
| nvgRect(ctx, c0-1, bounds[1], 2, bounds[3]-bounds[1]); | |||||
| } else { | |||||
| nvgFillColor(ctx, caretcolor); | |||||
| nvgRect(ctx, c0-1, bounds[1], c1-c0+1, bounds[3]-bounds[1]); | |||||
| } | |||||
| nvgFill(ctx); | |||||
| #else | |||||
| float c0,c1; | |||||
| const char *cb; | |||||
| const char *ce; | |||||
| const char *line; | |||||
| int numlines; | |||||
| cb = label+cbegin; ce = label+cend; | |||||
| line = label; | |||||
| NVGtextRow rows[2]; | |||||
| numlines = nvgTextBreakLines(ctx, line, NULL, w, rows, 2); | |||||
| /* | |||||
| int nglyphs = nvgTextGlyphPositions( | |||||
| ctx, x, y, label, label+cend+1, glyphs, BND_MAX_GLYPHS); | |||||
| c0=glyphs[0].x; | |||||
| c1=glyphs[nglyphs-1].x; | |||||
| // TODO: this is slow | |||||
| for (int i=0; i < nglyphs; ++i) { | |||||
| if (glyphs[i].str == cb) | |||||
| c0 = glyphs[i].x; | |||||
| if (glyphs[i].str == ce) | |||||
| c1 = glyphs[i].x; | |||||
| } | |||||
| nvgTextBounds(ctx,x,y,label,NULL, bounds); | |||||
| nvgBeginPath(ctx); | nvgBeginPath(ctx); | ||||
| if (cbegin == cend) { | if (cbegin == cend) { | ||||
| nvgFillColor(ctx, nvgRGBf(0.337,0.502,0.761)); | nvgFillColor(ctx, nvgRGBf(0.337,0.502,0.761)); | ||||
| nvgRect(ctx, c0-1, bounds[1], 2, bounds[3]-bounds[1]); | |||||
| nvgRect(ctx, c0x-1, c0y, 2, lh+1); | |||||
| } else { | } else { | ||||
| nvgFillColor(ctx, caretcolor); | nvgFillColor(ctx, caretcolor); | ||||
| nvgRect(ctx, c0-1, bounds[1], c1-c0+1, bounds[3]-bounds[1]); | |||||
| if (c0r == c1r) { | |||||
| nvgRect(ctx, c0x-1, c0y, c1x-c0x+1, lh+1); | |||||
| } else { | |||||
| int blk=c1r-c0r-1; | |||||
| nvgRect(ctx, c0x-1, c0y, x+w-c0x+1, lh+1); | |||||
| nvgRect(ctx, x, c1y, c1x-x+1, lh+1); | |||||
| if (blk) | |||||
| nvgRect(ctx, x, c0y+lh, w, blk*lh+1); | |||||
| } | |||||
| } | } | ||||
| nvgFill(ctx); | nvgFill(ctx); | ||||
| */ | |||||
| #endif | |||||
| } | } | ||||
| nvgBeginPath(ctx); | nvgBeginPath(ctx); | ||||