summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-15 18:18:45 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-15 18:18:45 +0300
commitaed6837cc1e1bfb9ccb238900ad7387444b624eb (patch)
tree3b502166a9b9cd69b46ea6c11949be82bedaff53
parent9f7bbeecba762213c7dec4555e0cbb1da2b2ea66 (diff)
Fixing regressions text metrics, InputWidget
`run_Font_` was moving the Y cursor position twice for each line break. Checking for the HarfBuzz UNSAFE_TO_BREAK flag leads to some unexpected behavior near edges of words. The old `tryAdvanceNoWrap` method should return the maximum horizontal advance of the text, and not the cursor position's advance. `draw_WrapText` used the wrong foreground color. `TextBuf` now uses WrapText to do all the measuring and drawing, making things much simpler.
-rw-r--r--res/about/version.gmi2
-rw-r--r--src/ui/inputwidget.c68
-rw-r--r--src/ui/text.c23
-rw-r--r--src/ui/text.h4
4 files changed, 42 insertions, 55 deletions
diff --git a/res/about/version.gmi b/res/about/version.gmi
index 89c9bedc..a39d792b 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -18,7 +18,7 @@
18* Preferences have been reorganized. There is a new Fonts page, and some General settings have been moved to the Style page. All color-related settings are on the Colors page, including UI theme colors. 18* Preferences have been reorganized. There is a new Fonts page, and some General settings have been moved to the Style page. All color-related settings are on the Colors page, including UI theme colors.
19* Opened links are updated to reflect visited status even when opened in a background tab or to the side. 19* Opened links are updated to reflect visited status even when opened in a background tab or to the side.
20* Unicode Byte Order Mark is ignored in the beginning of a page. 20* Unicode Byte Order Mark is ignored in the beginning of a page.
21* Right-to-left paragraphs are aligned to the right. 21* Right-to-left paragraphs are aligned to the right. Link icons and list/quote symbols are moved to the right margin.
22* Large lede paragraph font is not applied if the paragraph has too many lines. 22* Large lede paragraph font is not applied if the paragraph has too many lines.
23* Adjusted kerning of Nunito. 23* Adjusted kerning of Nunito.
24* Fixed sizing of characters in the Noto Symbols fonts. 24* Fixed sizing of characters in the Noto Symbols fonts.
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index b51b3985..29003ad5 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -747,7 +747,13 @@ static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const
747 747
748static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir) { 748static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir) {
749 const iInputLine *line = line_InputWidget_(d, d->cursorLine); 749 const iInputLine *line = line_InputWidget_(d, d->cursorLine);
750 int xPos = measureN_Text(d->font, cstr_String(&line->text), d->cursor - line->offset).advance.x; 750 int xPos1 = maxWidth_TextMetrics(measureN_Text(d->font,
751 cstr_String(&line->text),
752 d->cursor - line->offset));
753 int xPos2 = maxWidth_TextMetrics(measureN_Text(d->font,
754 cstr_String(&line->text),
755 iMin(d->cursor + 1 - line->offset, line->len)));
756 const int xPos = (xPos1 + xPos2) / 2;
751 size_t newCursor = iInvalidPos; 757 size_t newCursor = iInvalidPos;
752 const size_t numLines = size_Array(&d->lines); 758 const size_t numLines = size_Array(&d->lines);
753 if (dir < 0 && d->cursorLine > 0) { 759 if (dir < 0 && d->cursorLine > 0) {
@@ -861,32 +867,6 @@ static size_t skipWord_InputWidget_(const iInputWidget *d, size_t pos, int dir)
861 return pos; 867 return pos;
862} 868}
863 869
864#if 0
865static iInt2 textOrigin_InputWidget_(const iInputWidget *d) { //}, const char *visText) {
866// const iWidget *w = constAs_Widget(d);
867 iRect bounds = contentBounds_InputWidget_(d);/* adjusted_Rect(bounds_Widget(w),
868 addX_I2(padding_(), d->leftPadding),
869 neg_I2(addX_I2(padding_(), d->rightPadding)));*/
870// const iInt2 emSize = advance_Text(d->font, "M");
871// const int textWidth = advance_Text(d->font, visText).x;
872// const int cursorX = advanceN_Text(d->font, visText, d->cursor).x;
873// int xOff = 0;
874// shrink_Rect(&bounds, init_I2(gap_UI * (flags_Widget(w) & tight_WidgetFlag ? 1 : 2), 0));
875/* if (d->maxLen == 0) {
876 if (textWidth > width_Rect(bounds) - emSize.x) {
877 xOff = width_Rect(bounds) - emSize.x - textWidth;
878 }
879 if (cursorX + xOff < width_Rect(bounds) / 2) {
880 xOff = width_Rect(bounds) / 2 - cursorX;
881 }
882 xOff = iMin(xOff, 0);
883 }*/
884// const int yOff = 0.3f * lineHeight_Text(d->font); // (height_Rect(bounds) - lineHeight_Text(d->font)) / 2;
885// return addY_I2(topLeft_Rect(bounds), yOff);
886
887}
888#endif
889
890static size_t coordIndex_InputWidget_(const iInputWidget *d, iInt2 coord) { 870static size_t coordIndex_InputWidget_(const iInputWidget *d, iInt2 coord) {
891 const iInt2 pos = sub_I2(coord, contentBounds_InputWidget_(d).pos); 871 const iInt2 pos = sub_I2(coord, contentBounds_InputWidget_(d).pos);
892 const size_t lineNumber = iMin(iMax(0, pos.y) / lineHeight_Text(d->font), 872 const size_t lineNumber = iMin(iMax(0, pos.y) / lineHeight_Text(d->font),
@@ -1342,17 +1322,6 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1342 return processEvent_Widget(w, ev); 1322 return processEvent_Widget(w, ev);
1343} 1323}
1344 1324
1345#if 0
1346static iBool isWhite_(const iString *str) {
1347 iConstForEach(String, i, str) {
1348 if (!isSpace_Char(i.value)) {
1349 return iFalse;
1350 }
1351 }
1352 return iTrue;
1353}
1354#endif
1355
1356static void draw_InputWidget_(const iInputWidget *d) { 1325static void draw_InputWidget_(const iInputWidget *d) {
1357 const iWidget *w = constAs_Widget(d); 1326 const iWidget *w = constAs_Widget(d);
1358 iRect bounds = adjusted_Rect(bounds_InputWidget_(d), padding_(), neg_I2(padding_())); 1327 iRect bounds = adjusted_Rect(bounds_InputWidget_(d), padding_(), neg_I2(padding_()));
@@ -1400,14 +1369,12 @@ static void draw_InputWidget_(const iInputWidget *d) {
1400 /* Draw the selected range. */ 1369 /* Draw the selected range. */
1401 const iRanges mark = mark_InputWidget_(d); 1370 const iRanges mark = mark_InputWidget_(d);
1402 if (mark.start < lineRange.end && mark.end > lineRange.start) { 1371 if (mark.start < lineRange.end && mark.end > lineRange.start) {
1403 const int m1 = measureN_Text(d->font, 1372 const int m1 = maxWidth_TextMetrics(measureN_Text(d->font,
1404 cstr_String(&line->text), 1373 cstr_String(&line->text),
1405 iMax(lineRange.start, mark.start) - line->offset) 1374 iMax(lineRange.start, mark.start) - line->offset));
1406 .advance.x; 1375 const int m2 = maxWidth_TextMetrics(measureN_Text(d->font,
1407 const int m2 = measureN_Text(d->font,
1408 cstr_String(&line->text), 1376 cstr_String(&line->text),
1409 iMin(lineRange.end, mark.end) - line->offset) 1377 iMin(lineRange.end, mark.end) - line->offset));
1410 .advance.x;
1411 fillRect_Paint(&p, 1378 fillRect_Paint(&p,
1412 (iRect){ addX_I2(drawPos, iMin(m1, m2)), 1379 (iRect){ addX_I2(drawPos, iMin(m1, m2)),
1413 init_I2(iMax(gap_UI / 3, iAbs(m2 - m1)), 1380 init_I2(iMax(gap_UI / 3, iAbs(m2 - m1)),
@@ -1446,15 +1413,20 @@ static void draw_InputWidget_(const iInputWidget *d) {
1446 } 1413 }
1447 const iInputLine *curLine = line_InputWidget_(d, d->cursorLine); 1414 const iInputLine *curLine = line_InputWidget_(d, d->cursorLine);
1448 const iString * text = &curLine->text; 1415 const iString * text = &curLine->text;
1449 /* The `gap_UI` offsets below are a hack. They are used because for some reason the 1416 /* The bounds include visible characters, while advance includes whitespace as well.
1450 cursor rect and the glyph inside don't quite position like during `run_Text_()`. */ 1417 Normally only the advance is needed, but if the cursor is at a newline, the advance
1451 const int prefixSize = measureN_Text(d->font, cstr_String(text), d->cursor - curLine->offset).advance.x; 1418 will have reset back to zero. */
1419 const int prefixSize = maxWidth_TextMetrics(measureN_Text(d->font,
1420 cstr_String(text),
1421 d->cursor - curLine->offset));
1452 const iInt2 curPos = addX_I2(addY_I2(contentBounds.pos, lineHeight_Text(d->font) * d->cursorLine), 1422 const iInt2 curPos = addX_I2(addY_I2(contentBounds.pos, lineHeight_Text(d->font) * d->cursorLine),
1453 prefixSize + 1423 prefixSize +
1454 (d->mode == insert_InputMode ? -curSize.x / 2 : 0)); 1424 (d->mode == insert_InputMode ? -curSize.x / 2 : 0));
1455 const iRect curRect = { curPos, curSize }; 1425 const iRect curRect = { curPos, curSize };
1456 fillRect_Paint(&p, curRect, uiInputCursor_ColorId); 1426 fillRect_Paint(&p, curRect, uiInputCursor_ColorId);
1457 if (d->mode == overwrite_InputMode) { 1427 if (d->mode == overwrite_InputMode) {
1428 /* The `gap_UI` offsets below are a hack. They are used because for some reason the
1429 cursor rect and the glyph inside don't quite position like during `run_Text_()`. */
1458 draw_Text(d->font, 1430 draw_Text(d->font,
1459 addX_I2(curPos, iMin(1, gap_UI / 8)), 1431 addX_I2(curPos, iMin(1, gap_UI / 8)),
1460 uiInputCursorText_ColorId, 1432 uiInputCursorText_ColorId,
diff --git a/src/ui/text.c b/src/ui/text.c
index 0a3b7b2e..b283d700 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -1450,7 +1450,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1450 wrapResumePos = run->logical.end; 1450 wrapResumePos = run->logical.end;
1451 wrapRuns.end = runIndex; 1451 wrapRuns.end = runIndex;
1452 wrapResumeRunIndex = runIndex + 1; 1452 wrapResumeRunIndex = runIndex + 1;
1453 yCursor += d->height; 1453 //yCursor += d->height;
1454 break; 1454 break;
1455 } 1455 }
1456 wrapResumeRunIndex = runCount; 1456 wrapResumeRunIndex = runCount;
@@ -1489,10 +1489,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1489 prevCh = ch; 1489 prevCh = ch;
1490 } 1490 }
1491 else { 1491 else {
1492 if (~glyphFlags & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) { 1492 //if (~glyphFlags & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) {
1493 safeBreakPos = logPos; 1493 safeBreakPos = logPos;
1494 breakAdvance = wrapAdvance; 1494 breakAdvance = wrapAdvance;
1495 } 1495 //}
1496 } 1496 }
1497 /* Out of room? */ 1497 /* Out of room? */
1498 if (wrapAdvance + xOffset + glyph->d[0].x + glyph->rect[0].size.x > 1498 if (wrapAdvance + xOffset + glyph->d[0].x + glyph->rect[0].size.x >
@@ -1787,7 +1787,8 @@ iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **e
1787 .maxWidth = width, 1787 .maxWidth = width,
1788 .wrapFunc = cbAdvanceOneLine_, 1788 .wrapFunc = cbAdvanceOneLine_,
1789 .context = endPos }; 1789 .context = endPos };
1790 return measure_WrapText(&wrap, fontId).bounds.size; 1790 iTextMetrics tm = measure_WrapText(&wrap, fontId);
1791 return init_I2(maxWidth_TextMetrics(tm), tm.bounds.size.y);
1791} 1792}
1792 1793
1793iTextMetrics measureN_Text(int fontId, const char *text, size_t n) { 1794iTextMetrics measureN_Text(int fontId, const char *text, size_t n) {
@@ -1969,7 +1970,7 @@ void draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) {
1969 .text = d->text, 1970 .text = d->text,
1970 .pos = pos, 1971 .pos = pos,
1971 .wrap = d, 1972 .wrap = d,
1972 .color = color, 1973 .color = color & mask_ColorId,
1973 }); 1974 });
1974} 1975}
1975 1976
@@ -2088,6 +2089,13 @@ iDefineTypeConstructionArgs(TextBuf, (int font, int color, const char *text), fo
2088 2089
2089static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iBool doWrap, const char *text) { 2090static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iBool doWrap, const char *text) {
2090 SDL_Renderer *render = text_.render; 2091 SDL_Renderer *render = text_.render;
2092 iWrapText wrapText = {
2093 .text = range_CStr(text),
2094 .maxWidth = maxWidth,
2095 .mode = (doWrap ? word_WrapTextMode : anyCharacter_WrapTextMode),
2096 };
2097 d->size = measure_WrapText(&wrapText, font).bounds.size;
2098#if 0
2091 if (maxWidth == 0) { 2099 if (maxWidth == 0) {
2092 d->size = measure_Text(font, text).bounds.size; 2100 d->size = measure_Text(font, text).bounds.size;
2093 } 2101 }
@@ -2101,6 +2109,7 @@ static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iB
2101 d->size.y += iMax(size.y, lineHeight_Text(font)); 2109 d->size.y += iMax(size.y, lineHeight_Text(font));
2102 } 2110 }
2103 } 2111 }
2112#endif
2104 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 2113 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
2105 if (d->size.x * d->size.y) { 2114 if (d->size.x * d->size.y) {
2106 d->texture = SDL_CreateTexture(render, 2115 d->texture = SDL_CreateTexture(render,
@@ -2119,7 +2128,7 @@ static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iB
2119 SDL_SetRenderDrawColor(render, 255, 255, 255, 0); 2128 SDL_SetRenderDrawColor(render, 255, 255, 255, 0);
2120 SDL_RenderClear(render); 2129 SDL_RenderClear(render);
2121 SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */ 2130 SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */
2122 const int fg = color | fillBackground_ColorId; 2131#if 0
2123 iRangecc range = range_CStr(text); 2132 iRangecc range = range_CStr(text);
2124 if (maxWidth == 0) { 2133 if (maxWidth == 0) {
2125 draw_Text_(font, zero_I2(), fg, range); 2134 draw_Text_(font, zero_I2(), fg, range);
@@ -2137,6 +2146,8 @@ static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iB
2137 pos.y += lineHeight_Text(font); 2146 pos.y += lineHeight_Text(font);
2138 } 2147 }
2139 } 2148 }
2149#endif
2150 draw_WrapText(&wrapText, font, zero_I2(), color | fillBackground_ColorId);
2140 SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND); 2151 SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND);
2141 SDL_SetRenderTarget(render, oldTarget); 2152 SDL_SetRenderTarget(render, oldTarget);
2142 SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); 2153 SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);
diff --git a/src/ui/text.h b/src/ui/text.h
index 09d92ce0..d779dd96 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -159,6 +159,10 @@ struct Impl_TextMetrics {
159 iInt2 advance; /* cursor offset */ 159 iInt2 advance; /* cursor offset */
160}; 160};
161 161
162iLocalDef int maxWidth_TextMetrics(const iTextMetrics d) {
163 return iMax(width_Rect(d.bounds), d.advance.x);
164}
165
162iTextMetrics measureRange_Text (int fontId, iRangecc text); 166iTextMetrics measureRange_Text (int fontId, iRangecc text);
163iTextMetrics measureWrapRange_Text (int fontId, int maxWidth, iRangecc text); 167iTextMetrics measureWrapRange_Text (int fontId, int maxWidth, iRangecc text);
164iTextMetrics measureN_Text (int fontId, const char *text, size_t n); /* `n` in characters */ 168iTextMetrics measureN_Text (int fontId, const char *text, size_t n); /* `n` in characters */