summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/inputwidget.c20
-rw-r--r--src/ui/text.c26
-rw-r--r--src/ui/text_simple.c35
-rw-r--r--src/ui/uploadwidget.c4
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
540static void updateLineRangesStartingFrom_InputWidget_(iInputWidget *d, int y) { 541static 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
1297static iBool copy_InputWidget_(iInputWidget *d, iBool doCut) { 1300static 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 = &marker; 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
1826iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { 1826iInt2 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
1837iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { 1836iInt2 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
2020iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { 2018iTextMetrics 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),