diff options
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/inputwidget.c | 20 | ||||
-rw-r--r-- | src/ui/text.c | 26 | ||||
-rw-r--r-- | src/ui/text_simple.c | 35 | ||||
-rw-r--r-- | src/ui/uploadwidget.c | 4 |
4 files changed, 66 insertions, 19 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 6246078e..8ed52022 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -535,6 +535,7 @@ static void updateLine_InputWidget_(iInputWidget *d, iInputLine *line) { | |||
535 | } | 535 | } |
536 | const iTextMetrics tm = measure_WrapText(&wrapText, d->font); | 536 | const iTextMetrics tm = measure_WrapText(&wrapText, d->font); |
537 | line->wrapLines.end = line->wrapLines.start + height_Rect(tm.bounds) / lineHeight_Text(d->font); | 537 | line->wrapLines.end = line->wrapLines.start + height_Rect(tm.bounds) / lineHeight_Text(d->font); |
538 | iAssert(!isEmpty_Range(&line->wrapLines)); | ||
538 | } | 539 | } |
539 | 540 | ||
540 | static void updateLineRangesStartingFrom_InputWidget_(iInputWidget *d, int y) { | 541 | static void updateLineRangesStartingFrom_InputWidget_(iInputWidget *d, int y) { |
@@ -776,6 +777,7 @@ static iRangei visibleLineRange_InputWidget_(const iInputWidget *d) { | |||
776 | } | 777 | } |
777 | else break; | 778 | else break; |
778 | } | 779 | } |
780 | iAssert(isEmpty_Range(&vis) || (vis.start >= 0 && vis.end >= vis.start)); | ||
779 | return vis; | 781 | return vis; |
780 | } | 782 | } |
781 | 783 | ||
@@ -1193,6 +1195,7 @@ static iBool deleteMarked_InputWidget_(iInputWidget *d) { | |||
1193 | if (!isEmpty_Range(&m)) { | 1195 | if (!isEmpty_Range(&m)) { |
1194 | deleteIndexRange_InputWidget_(d, m); | 1196 | deleteIndexRange_InputWidget_(d, m); |
1195 | setCursor_InputWidget(d, indexToCursor_InputWidget_(d, m.start)); | 1197 | setCursor_InputWidget(d, indexToCursor_InputWidget_(d, m.start)); |
1198 | iZap(d->mark); /* setCursor thinks we're marking when Shift is down */ | ||
1196 | return iTrue; | 1199 | return iTrue; |
1197 | } | 1200 | } |
1198 | return iFalse; | 1201 | return iFalse; |
@@ -1296,8 +1299,8 @@ static iInt2 coordCursor_InputWidget_(const iInputWidget *d, iInt2 coord) { | |||
1296 | 1299 | ||
1297 | static iBool copy_InputWidget_(iInputWidget *d, iBool doCut) { | 1300 | static iBool copy_InputWidget_(iInputWidget *d, iBool doCut) { |
1298 | if (!isEmpty_Range(&d->mark)) { | 1301 | if (!isEmpty_Range(&d->mark)) { |
1299 | const iRanges m = mark_InputWidget_(d); | 1302 | const iRanges m = mark_InputWidget_(d); |
1300 | iString *str = collectNew_String(); | 1303 | iString * str = collectNew_String(); |
1301 | mergeLinesRange_(&d->lines, m, str); | 1304 | mergeLinesRange_(&d->lines, m, str); |
1302 | SDL_SetClipboardText( | 1305 | SDL_SetClipboardText( |
1303 | cstr_String(d->inFlags & isUrl_InputWidgetFlag ? canonicalUrl_String(str) : str)); | 1306 | cstr_String(d->inFlags & isUrl_InputWidgetFlag ? canonicalUrl_String(str) : str)); |
@@ -1866,7 +1869,7 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1866 | drawRange_Text(d->font, drawPos, uiAnnotation_ColorId, range_String(&d->hint)); | 1869 | drawRange_Text(d->font, drawPos, uiAnnotation_ColorId, range_String(&d->hint)); |
1867 | } | 1870 | } |
1868 | else { | 1871 | else { |
1869 | /* TODO: Make a function out of this. */ | 1872 | iAssert(~d->inFlags & isSensitive_InputWidgetFlag || size_Range(&visLines) == 1); |
1870 | drawPos.y += visLineOffsetY; | 1873 | drawPos.y += visLineOffsetY; |
1871 | iMarkPainter marker = { | 1874 | iMarkPainter marker = { |
1872 | .paint = &p, | 1875 | .paint = &p, |
@@ -1874,14 +1877,13 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1874 | .contentBounds = contentBounds, | 1877 | .contentBounds = contentBounds, |
1875 | .mark = mark_InputWidget_(d) | 1878 | .mark = mark_InputWidget_(d) |
1876 | }; | 1879 | }; |
1877 | iAssert(~d->inFlags & isSensitive_InputWidgetFlag || size_Range(&visLines) == 1); | 1880 | wrapText.context = ▮ |
1881 | wrapText.wrapFunc = isFocused ? draw_MarkPainter_ : NULL; /* mark is drawn under each line of text */ | ||
1878 | for (size_t vis = visLines.start; vis < visLines.end; vis++) { | 1882 | for (size_t vis = visLines.start; vis < visLines.end; vis++) { |
1879 | const iInputLine *line = constAt_Array(&d->lines, vis); | 1883 | const iInputLine *line = constAt_Array(&d->lines, vis); |
1880 | wrapText.text = range_String(&line->text); | 1884 | wrapText.text = range_String(&line->text); |
1881 | wrapText.wrapFunc = isFocused ? draw_MarkPainter_ : NULL; /* mark is drawn under each line of text */ | 1885 | marker.line = line; |
1882 | wrapText.context = ▮ | 1886 | marker.pos = drawPos; |
1883 | marker.line = line; | ||
1884 | marker.pos = drawPos; | ||
1885 | addv_I2(&drawPos, draw_WrapText(&wrapText, d->font, drawPos, fg).advance); /* lines end with \n */ | 1887 | addv_I2(&drawPos, draw_WrapText(&wrapText, d->font, drawPos, fg).advance); /* lines end with \n */ |
1886 | } | 1888 | } |
1887 | wrapText.wrapFunc = NULL; | 1889 | wrapText.wrapFunc = NULL; |
diff --git a/src/ui/text.c b/src/ui/text.c index 866ae7fa..0a8386e4 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -1824,7 +1824,6 @@ static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int adv | |||
1824 | } | 1824 | } |
1825 | 1825 | ||
1826 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { | 1826 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { |
1827 | /* FIXME: Get rid of this. Caller could use WrapText directly? */ | ||
1828 | iWrapText wrap = { .mode = word_WrapTextMode, | 1827 | iWrapText wrap = { .mode = word_WrapTextMode, |
1829 | .text = text, | 1828 | .text = text, |
1830 | .maxWidth = width, | 1829 | .maxWidth = width, |
@@ -1835,8 +1834,7 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) | |||
1835 | } | 1834 | } |
1836 | 1835 | ||
1837 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { | 1836 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { |
1838 | /* TODO: "NoWrap" means words aren't wrapped; the line is broken at nearest character. */ | 1837 | /* "NoWrap" means words aren't wrapped; the line is broken at nearest character. */ |
1839 | /* FIXME: Get rid of this. Caller could use WrapText directly? */ | ||
1840 | iWrapText wrap = { .mode = anyCharacter_WrapTextMode, | 1838 | iWrapText wrap = { .mode = anyCharacter_WrapTextMode, |
1841 | .text = text, | 1839 | .text = text, |
1842 | .maxWidth = width, | 1840 | .maxWidth = width, |
@@ -2019,6 +2017,27 @@ iTextMetrics measure_WrapText(iWrapText *d, int fontId) { | |||
2019 | 2017 | ||
2020 | iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { | 2018 | iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { |
2021 | iTextMetrics tm; | 2019 | iTextMetrics tm; |
2020 | #if !defined (LAGRANGE_ENABLE_HARFBUZZ) | ||
2021 | /* In simple mode, each line must be wrapped first so we can break at the right points | ||
2022 | and do wrap notifications before drawing. */ | ||
2023 | iRangecc text = d->text; | ||
2024 | iZap(tm); | ||
2025 | d->wrapRange_ = (iRangecc){ d->text.start, d->text.start }; | ||
2026 | const iInt2 orig = pos; | ||
2027 | while (!isEmpty_Range(&text)) { | ||
2028 | const char *endPos; | ||
2029 | const int width = d->mode == word_WrapTextMode | ||
2030 | ? tryAdvance_Text(fontId, text, d->maxWidth, &endPos).x | ||
2031 | : tryAdvanceNoWrap_Text(fontId, text, d->maxWidth, &endPos).x; | ||
2032 | notify_WrapText_(d, endPos, 0, width, iFalse); | ||
2033 | drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endPos }); | ||
2034 | text.start = endPos; | ||
2035 | pos.y += lineHeight_Text(fontId); | ||
2036 | tm.bounds.size.x = iMax(tm.bounds.size.x, width); | ||
2037 | tm.bounds.size.y = pos.y - orig.y; | ||
2038 | } | ||
2039 | tm.advance = sub_I2(pos, orig); | ||
2040 | #else | ||
2022 | tm.bounds = run_Font_(font_Text_(fontId), | 2041 | tm.bounds = run_Font_(font_Text_(fontId), |
2023 | &(iRunArgs){ .mode = draw_RunMode | runFlagsFromId_(fontId) | | 2042 | &(iRunArgs){ .mode = draw_RunMode | runFlagsFromId_(fontId) | |
2024 | (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) | | 2043 | (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) | |
@@ -2029,6 +2048,7 @@ iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { | |||
2029 | .color = color & mask_ColorId, | 2048 | .color = color & mask_ColorId, |
2030 | .cursorAdvance_out = &tm.advance, | 2049 | .cursorAdvance_out = &tm.advance, |
2031 | }); | 2050 | }); |
2051 | #endif | ||
2032 | return tm; | 2052 | return tm; |
2033 | } | 2053 | } |
2034 | 2054 | ||
diff --git a/src/ui/text_simple.c b/src/ui/text_simple.c index b843f2e8..3afc631a 100644 --- a/src/ui/text_simple.c +++ b/src/ui/text_simple.c | |||
@@ -60,8 +60,8 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
60 | /* This function shapes text using a simplified, incomplete algorithm. It works for English | 60 | /* This function shapes text using a simplified, incomplete algorithm. It works for English |
61 | and other non-complex LTR scripts. Composed glyphs are not supported (must rely on text | 61 | and other non-complex LTR scripts. Composed glyphs are not supported (must rely on text |
62 | being in a pre-composed form). This algorithm is used if HarfBuzz is not available. */ | 62 | being in a pre-composed form). This algorithm is used if HarfBuzz is not available. */ |
63 | iRect bounds = zero_Rect(); | ||
64 | const iInt2 orig = args->pos; | 63 | const iInt2 orig = args->pos; |
64 | iRect bounds = { orig, init_I2(0, d->height) }; | ||
65 | float xpos = orig.x; | 65 | float xpos = orig.x; |
66 | float xposMax = xpos; | 66 | float xposMax = xpos; |
67 | float monoAdvance = 0; | 67 | float monoAdvance = 0; |
@@ -73,16 +73,20 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
73 | const int xposLimit = (wrap && wrap->maxWidth ? orig.x + wrap->maxWidth : 0); | 73 | const int xposLimit = (wrap && wrap->maxWidth ? orig.x + wrap->maxWidth : 0); |
74 | const enum iRunMode mode = args->mode; | 74 | const enum iRunMode mode = args->mode; |
75 | const char * lastWordEnd = args->text.start; | 75 | const char * lastWordEnd = args->text.start; |
76 | iAssert(xposLimit == 0 || isMeasuring_(mode)); | 76 | //iAssert(xposLimit == 0 || isMeasuring_(mode)); |
77 | iAssert(args->text.end >= args->text.start); | 77 | iAssert(args->text.end >= args->text.start); |
78 | if (wrap) { | 78 | if (wrap) { |
79 | wrap->wrapRange_ = args->text; | 79 | wrap->wrapRange_ = args->text; |
80 | wrap->hitAdvance_out = zero_I2(); | ||
81 | wrap->hitChar_out = NULL; | ||
82 | wrap->hitGlyphNormX_out = 0.0f; | ||
80 | } | 83 | } |
81 | // if (args->continueFrom_out) { | 84 | // if (args->continueFrom_out) { |
82 | // *args->continueFrom_out = args->text.end; | 85 | // *args->continueFrom_out = args->text.end; |
83 | // } | 86 | // } |
84 | iChar prevCh = 0; | 87 | const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2()); |
85 | const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode); | 88 | const iBool checkHitChar = wrap && wrap->hitChar; |
89 | const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode); | ||
86 | if (isMonospaced) { | 90 | if (isMonospaced) { |
87 | monoAdvance = glyph_Font_(d, 'M')->advance; | 91 | monoAdvance = glyph_Font_(d, 'M')->advance; |
88 | } | 92 | } |
@@ -91,10 +95,21 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
91 | SDL_SetRenderDrawColor(text_.render, initial.r, initial.g, initial.b, 0); | 95 | SDL_SetRenderDrawColor(text_.render, initial.r, initial.g, initial.b, 0); |
92 | } | 96 | } |
93 | /* Text rendering is not very straightforward! Let's dive in... */ | 97 | /* Text rendering is not very straightforward! Let's dive in... */ |
98 | iChar prevCh = 0; | ||
94 | const char *chPos; | 99 | const char *chPos; |
95 | for (chPos = args->text.start; chPos != args->text.end; ) { | 100 | for (chPos = args->text.start; chPos != args->text.end; ) { |
96 | iAssert(chPos < args->text.end); | 101 | iAssert(chPos < args->text.end); |
97 | const char *currentPos = chPos; | 102 | const char *currentPos = chPos; |
103 | const iBool isHitPointOnThisLine = (checkHitPoint && wrap->hitPoint.y >= ypos && | ||
104 | wrap->hitPoint.y < ypos + d->height); | ||
105 | if (checkHitChar && currentPos == wrap->hitChar) { | ||
106 | wrap->hitAdvance_out = sub_I2(init_I2(xpos, ypos), orig); | ||
107 | } | ||
108 | /* Check if the hit point is on the left side of the line. */ | ||
109 | if (isHitPointOnThisLine && !wrap->hitChar_out && wrap->hitPoint.x < orig.x) { | ||
110 | wrap->hitChar_out = currentPos; | ||
111 | wrap->hitGlyphNormX_out = 0.0f; | ||
112 | } | ||
98 | if (*chPos == 0x1b) { /* ANSI escape. */ | 113 | if (*chPos == 0x1b) { /* ANSI escape. */ |
99 | chPos++; | 114 | chPos++; |
100 | iRegExpMatch m; | 115 | iRegExpMatch m; |
@@ -213,6 +228,13 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
213 | glyph = glyph_Font_(d, ch); /* cache may have been reset */ | 228 | glyph = glyph_Font_(d, ch); /* cache may have been reset */ |
214 | } | 229 | } |
215 | int x2 = x1 + glyph->rect[hoff].size.x; | 230 | int x2 = x1 + glyph->rect[hoff].size.x; |
231 | if (isHitPointOnThisLine) { | ||
232 | if (wrap->hitPoint.x >= x1) { /* may also be off to the right */ | ||
233 | wrap->hitChar_out = currentPos; | ||
234 | wrap->hitGlyphNormX_out = | ||
235 | wrap->hitPoint.x < x2 ? (wrap->hitPoint.x - x1) / glyph->advance : 1.0f; | ||
236 | } | ||
237 | } | ||
216 | /* Out of the allotted space on the line? */ | 238 | /* Out of the allotted space on the line? */ |
217 | if (xposLimit > 0 && x2 > xposLimit) { | 239 | if (xposLimit > 0 && x2 > xposLimit) { |
218 | iAssert(wrap); | 240 | iAssert(wrap); |
@@ -340,6 +362,9 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
340 | } | 362 | } |
341 | } | 363 | } |
342 | notify_WrapText_(wrap, chPos, 0, xpos - orig.x, iFalse); | 364 | notify_WrapText_(wrap, chPos, 0, xpos - orig.x, iFalse); |
365 | if (checkHitChar && wrap->hitChar == args->text.end) { | ||
366 | wrap->hitAdvance_out = sub_I2(init_I2(xpos, ypos), orig); | ||
367 | } | ||
343 | if (args->cursorAdvance_out) { | 368 | if (args->cursorAdvance_out) { |
344 | *args->cursorAdvance_out = sub_I2(init_I2(xpos, ypos), orig); | 369 | *args->cursorAdvance_out = sub_I2(init_I2(xpos, ypos), orig); |
345 | } | 370 | } |
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c index 7f2a68bf..7bfa73bd 100644 --- a/src/ui/uploadwidget.c +++ b/src/ui/uploadwidget.c | |||
@@ -68,8 +68,8 @@ static void updateInputMaxHeight_UploadWidget_(iUploadWidget *d) { | |||
68 | /* Calculate how many lines fits vertically in the view. */ | 68 | /* Calculate how many lines fits vertically in the view. */ |
69 | const iInt2 inputPos = topLeft_Rect(bounds_Widget(as_Widget(d->input))); | 69 | const iInt2 inputPos = topLeft_Rect(bounds_Widget(as_Widget(d->input))); |
70 | const int footerHeight = height_Widget(d->token) + | 70 | const int footerHeight = height_Widget(d->token) + |
71 | height_Widget(findChild_Widget(w, "dialogbuttons")) + | 71 | height_Widget(findChild_Widget(w, "dialogbuttons")) + |
72 | 6 * gap_UI; | 72 | 6 * gap_UI; |
73 | const int avail = bottom_Rect(safeRect_Root(w->root)) - footerHeight; | 73 | const int avail = bottom_Rect(safeRect_Root(w->root)) - footerHeight; |
74 | setLineLimits_InputWidget(d->input, | 74 | setLineLimits_InputWidget(d->input, |
75 | minLines_InputWidget(d->input), | 75 | minLines_InputWidget(d->input), |