diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-21 13:42:58 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-21 13:42:58 +0300 |
commit | 37b331ce1b72969c13d1924064c578db999c4b4a (patch) | |
tree | d5675d8f45ee36aec7137d31ae5c8f3dcadf2345 /src | |
parent | 059d19065e613412cb9fccc06e23fd0685484905 (diff) |
InputWidget: Fixed and reliability
Cursor movement and pasting.
Diffstat (limited to 'src')
-rw-r--r-- | src/ui/inputwidget.c | 116 | ||||
-rw-r--r-- | src/ui/text.c | 14 | ||||
-rw-r--r-- | src/ui/uploadwidget.c | 2 |
3 files changed, 69 insertions, 63 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index c60aa474..16098d83 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -58,9 +58,9 @@ static void enableEditorKeysInMenus_(iBool enable) { | |||
58 | iDeclareType(InputLine) | 58 | iDeclareType(InputLine) |
59 | 59 | ||
60 | struct Impl_InputLine { | 60 | struct Impl_InputLine { |
61 | iString text; /* UTF-8 */ | 61 | iString text; |
62 | iRanges range; /* byte offset inside the entire content; for marking */ | 62 | iRanges range; /* byte offset inside the entire content; for marking */ |
63 | iRangei wrapLines; /* range of visual wrapped lines */ | 63 | iRangei wrapLines; /* range of visual wrapped lines */ |
64 | }; | 64 | }; |
65 | 65 | ||
66 | static void init_InputLine(iInputLine *d) { | 66 | static void init_InputLine(iInputLine *d) { |
@@ -323,17 +323,35 @@ static int visLineOffsetY_InputWidget_(const iInputWidget *d) { | |||
323 | return (line->wrapLines.start - d->visWrapLines.start) * lineHeight_Text(d->font); | 323 | return (line->wrapLines.start - d->visWrapLines.start) * lineHeight_Text(d->font); |
324 | } | 324 | } |
325 | 325 | ||
326 | static iWrapText wrap_InputWidget_(const iInputWidget *d, int y) { | ||
327 | return (iWrapText){ | ||
328 | .text = range_String(&line_InputWidget_(d, y)->text), | ||
329 | .maxWidth = width_Rect(contentBounds_InputWidget_(d)), | ||
330 | .mode = (d->inFlags & isUrl_InputWidgetFlag ? anyCharacter_WrapTextMode | ||
331 | : word_WrapTextMode), | ||
332 | }; | ||
333 | } | ||
334 | |||
335 | static iInt2 relativeCursorCoord_InputWidget_(const iInputWidget *d) { | ||
336 | /* Relative to the start of the line on which the cursor is. */ | ||
337 | iWrapText wt = wrap_InputWidget_(d, d->cursor.y); | ||
338 | wt.hitChar = wt.text.start + d->cursor.x; | ||
339 | measure_WrapText(&wt, d->font); | ||
340 | return wt.hitAdvance_out; | ||
341 | } | ||
342 | |||
326 | static void updateVisible_InputWidget_(iInputWidget *d) { | 343 | static void updateVisible_InputWidget_(iInputWidget *d) { |
327 | const int totalWraps = numWrapLines_InputWidget_(d); | 344 | const int totalWraps = numWrapLines_InputWidget_(d); |
328 | const int visWraps = iClamp(totalWraps, d->minWrapLines, d->maxWrapLines); | 345 | const int visWraps = iClamp(totalWraps, d->minWrapLines, d->maxWrapLines); |
329 | /* Resize the height of the editor. */ | 346 | /* Resize the height of the editor. */ |
330 | d->visWrapLines.end = d->visWrapLines.start + visWraps; | 347 | d->visWrapLines.end = d->visWrapLines.start + visWraps; |
331 | /* Determine which wraps are currently visible. */ | 348 | /* Determine which wraps are currently visible. */ |
349 | d->cursor.y = iMin(d->cursor.y, size_Array(&d->lines) - 1); | ||
332 | const iInputLine *curLine = constAt_Array(&d->lines, d->cursor.y); | 350 | const iInputLine *curLine = constAt_Array(&d->lines, d->cursor.y); |
333 | const int cursorY = curLine->wrapLines.start + | 351 | const int cursorY = curLine->wrapLines.start + |
334 | measureRange_Text(d->font, rangeSize_String(&curLine->text, d->cursor.x)) | 352 | relativeCursorCoord_InputWidget_(d).y / lineHeight_Text(d->font); |
335 | .advance.y / lineHeight_Text(d->font); | 353 | // measureRange_Text(d->font, rangeSize_String(&curLine->text, d->cursor.x)) |
336 | 354 | // .advance.y / lineHeight_Text(d->font); | |
337 | //int scrollY = d->visLineOffsetY / lineHeight_Text(d->font); | 355 | //int scrollY = d->visLineOffsetY / lineHeight_Text(d->font); |
338 | /* Scroll to cursor. */ | 356 | /* Scroll to cursor. */ |
339 | int delta = 0; | 357 | int delta = 0; |
@@ -528,12 +546,7 @@ static void updateMetrics_InputWidget_(iInputWidget *d) { | |||
528 | 546 | ||
529 | static void updateLine_InputWidget_(iInputWidget *d, iInputLine *line) { | 547 | static void updateLine_InputWidget_(iInputWidget *d, iInputLine *line) { |
530 | iAssert(endsWith_String(&line->text, "\n") || isLastLine_InputWidget_(d, line)); | 548 | iAssert(endsWith_String(&line->text, "\n") || isLastLine_InputWidget_(d, line)); |
531 | iWrapText wrapText = { | 549 | iWrapText wrapText = wrap_InputWidget_(d, indexOf_Array(&d->lines, line)); |
532 | .text = range_String(&line->text), | ||
533 | .maxWidth = width_Rect(contentBounds_InputWidget_(d)), | ||
534 | .mode = (d->inFlags & isUrl_InputWidgetFlag ? anyCharacter_WrapTextMode | ||
535 | : word_WrapTextMode), | ||
536 | }; | ||
537 | if (wrapText.maxWidth <= 0) { | 550 | if (wrapText.maxWidth <= 0) { |
538 | line->wrapLines.end = line->wrapLines.start + 1; | 551 | line->wrapLines.end = line->wrapLines.start + 1; |
539 | return; | 552 | return; |
@@ -1087,16 +1100,17 @@ static iBool findXBreaks_LineMover_(iWrapText *wrap, iRangecc wrappedText, | |||
1087 | 1100 | ||
1088 | } | 1101 | } |
1089 | 1102 | ||
1090 | static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir) { | 1103 | static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir, int horiz) { |
1091 | const iInputLine *line = cursorLine_InputWidget_(d); | 1104 | const iInputLine *line = cursorLine_InputWidget_(d); |
1092 | iRangecc text = range_String(&line->text); | 1105 | iRangecc text = range_String(&line->text); |
1093 | iWrapText wrapText = { | 1106 | // iWrapText wrapText = { |
1094 | .text = rangeSize_String(&line->text, d->cursor.x), | 1107 | // .text = rangeSize_String(&line->text, d->cursor.x), |
1095 | .maxWidth = width_Rect(contentBounds_InputWidget_(d)), | 1108 | // .maxWidth = width_Rect(contentBounds_InputWidget_(d)), |
1096 | .mode = (d->inFlags & isUrl_InputWidgetFlag ? anyCharacter_WrapTextMode | 1109 | // .mode = (d->inFlags & isUrl_InputWidgetFlag ? anyCharacter_WrapTextMode |
1097 | : word_WrapTextMode), | 1110 | // : word_WrapTextMode), |
1098 | }; | 1111 | // }; |
1099 | iInt2 relCoord = measure_WrapText(&wrapText, d->font).advance; | 1112 | //iInt2 relCoord = measure_WrapText(&wrapText, d->font).advance; |
1113 | iInt2 relCoord = relativeCursorCoord_InputWidget_(d); | ||
1100 | const int cursorWidth = measureN_Text(d->font, charPos_InputWidget_(d, d->cursor), 1).advance.x; | 1114 | const int cursorWidth = measureN_Text(d->font, charPos_InputWidget_(d, d->cursor), 1).advance.x; |
1101 | int relLine = relCoord.y / lineHeight_Text(d->font); | 1115 | int relLine = relCoord.y / lineHeight_Text(d->font); |
1102 | if ((dir < 0 && relLine > 0) || (dir > 0 && relLine < numWrapLines_InputLine_(line) - 1)) { | 1116 | if ((dir < 0 && relLine > 0) || (dir > 0 && relLine < numWrapLines_InputLine_(line) - 1)) { |
@@ -1113,14 +1127,17 @@ static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir) { | |||
1113 | line = cursorLine_InputWidget_(d); | 1127 | line = cursorLine_InputWidget_(d); |
1114 | relCoord.y = 0; | 1128 | relCoord.y = 0; |
1115 | } | 1129 | } |
1130 | else if (dir == 0 && horiz != 0) { | ||
1131 | relCoord.x = (horiz < 0 ? 0 : width_Widget(d)); | ||
1132 | } | ||
1116 | else { | 1133 | else { |
1117 | return iFalse; | 1134 | return iFalse; |
1118 | } | 1135 | } |
1119 | wrapText.text = range_String(&cursorLine_InputWidget_(d)->text); | 1136 | iWrapText wt = wrap_InputWidget_(d, d->cursor.y); |
1120 | wrapText.hitPoint = addY_I2(relCoord, 1); //arelCddX_I2(relCoord, cursorWidth), | 1137 | wt.hitPoint = addY_I2(relCoord, 1); /* never (0, 0) because that disables the hit test */ |
1121 | measure_WrapText(&wrapText, d->font); | 1138 | measure_WrapText(&wt, d->font); |
1122 | if (wrapText.hitChar_out) { | 1139 | if (wt.hitChar_out) { |
1123 | d->cursor.x = wrapText.hitChar_out - wrapText.text.start; | 1140 | d->cursor.x = wt.hitChar_out - wt.text.start; |
1124 | } | 1141 | } |
1125 | else { | 1142 | else { |
1126 | d->cursor.x = endX_InputWidget_(d, d->cursor.y); | 1143 | d->cursor.x = endX_InputWidget_(d, d->cursor.y); |
@@ -1134,32 +1151,8 @@ static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir) { | |||
1134 | } | 1151 | } |
1135 | }*/ | 1152 | }*/ |
1136 | //showCursor_InputWidget_(d); | 1153 | //showCursor_InputWidget_(d); |
1137 | setCursor_InputWidget(d, d->cursor); | 1154 | setCursor_InputWidget(d, d->cursor); /* mark, show */ |
1138 | return iTrue; | 1155 | return iTrue; |
1139 | |||
1140 | #if 0 | ||
1141 | const iInputLine *line = line_InputWidget_(d, d->cursorLine); | ||
1142 | int xPos1 = maxWidth_TextMetrics(measureN_Text(d->font, | ||
1143 | cstr_String(&line->text), | ||
1144 | d->cursor - line->offset)); | ||
1145 | int xPos2 = maxWidth_TextMetrics(measureN_Text(d->font, | ||
1146 | cstr_String(&line->text), | ||
1147 | iMin(d->cursor + 1 - line->offset, line->len))); | ||
1148 | const int xPos = (xPos1 + xPos2) / 2; | ||
1149 | size_t newCursor = iInvalidPos; | ||
1150 | const size_t numLines = size_Array(&d->lines); | ||
1151 | if (dir < 0 && d->cursorLine > 0) { | ||
1152 | newCursor = indexForRelativeX_InputWidget_(d, xPos, --line); | ||
1153 | } | ||
1154 | else if (dir > 0 && d->cursorLine < numLines - 1) { | ||
1155 | newCursor = indexForRelativeX_InputWidget_(d, xPos, ++line); | ||
1156 | } | ||
1157 | if (newCursor != iInvalidPos) { | ||
1158 | setCursor_InputWidget(d, newCursor); | ||
1159 | return iTrue; | ||
1160 | } | ||
1161 | #endif | ||
1162 | return iFalse; | ||
1163 | } | 1156 | } |
1164 | 1157 | ||
1165 | void setSensitiveContent_InputWidget(iInputWidget *d, iBool isSensitive) { | 1158 | void setSensitiveContent_InputWidget(iInputWidget *d, iBool isSensitive) { |
@@ -1449,7 +1442,8 @@ static void paste_InputWidget_(iInputWidget *d) { | |||
1449 | } | 1442 | } |
1450 | } | 1443 | } |
1451 | SDL_free(text); | 1444 | SDL_free(text); |
1452 | iConstForEach(String, i, paste) { insertChar_InputWidget_(d, i.value); } | 1445 | //iConstForEach(String, i, paste) { insertChar_InputWidget_(d, i.value); } |
1446 | insertRange_InputWidget_(d, range_String(paste)); | ||
1453 | contentsWereChanged_InputWidget_(d); | 1447 | contentsWereChanged_InputWidget_(d); |
1454 | } | 1448 | } |
1455 | } | 1449 | } |
@@ -1826,7 +1820,8 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1826 | case SDLK_RIGHT: { | 1820 | case SDLK_RIGHT: { |
1827 | const int dir = (key == SDLK_LEFT ? -1 : +1); | 1821 | const int dir = (key == SDLK_LEFT ? -1 : +1); |
1828 | if (mods & byLine_KeyModifier) { | 1822 | if (mods & byLine_KeyModifier) { |
1829 | setCursor_InputWidget(d, dir < 0 ? lineFirst : lineLast); | 1823 | //setCursor_InputWidget(d, dir < 0 ? lineFirst : lineLast); |
1824 | moveCursorByLine_InputWidget_(d, 0, dir); | ||
1830 | } | 1825 | } |
1831 | else if (mods & byWord_KeyModifier) { | 1826 | else if (mods & byWord_KeyModifier) { |
1832 | setCursor_InputWidget(d, skipWord_InputWidget_(d, d->cursor, dir)); | 1827 | setCursor_InputWidget(d, skipWord_InputWidget_(d, d->cursor, dir)); |
@@ -1847,7 +1842,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1847 | return processEvent_Widget(as_Widget(d), ev); | 1842 | return processEvent_Widget(as_Widget(d), ev); |
1848 | case SDLK_UP: | 1843 | case SDLK_UP: |
1849 | case SDLK_DOWN: | 1844 | case SDLK_DOWN: |
1850 | if (moveCursorByLine_InputWidget_(d, key == SDLK_UP ? -1 : +1)) { | 1845 | if (moveCursorByLine_InputWidget_(d, key == SDLK_UP ? -1 : +1, 0)) { |
1851 | refresh_Widget(d); | 1846 | refresh_Widget(d); |
1852 | return iTrue; | 1847 | return iTrue; |
1853 | } | 1848 | } |
@@ -1856,7 +1851,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1856 | case SDLK_PAGEUP: | 1851 | case SDLK_PAGEUP: |
1857 | case SDLK_PAGEDOWN: | 1852 | case SDLK_PAGEDOWN: |
1858 | for (int count = 0; count < 5; count++) { | 1853 | for (int count = 0; count < 5; count++) { |
1859 | moveCursorByLine_InputWidget_(d, key == SDLK_PAGEUP ? -1 : +1); | 1854 | moveCursorByLine_InputWidget_(d, key == SDLK_PAGEUP ? -1 : +1, 0); |
1860 | } | 1855 | } |
1861 | refresh_Widget(d); | 1856 | refresh_Widget(d); |
1862 | return iTrue; | 1857 | return iTrue; |
@@ -2050,16 +2045,17 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
2050 | /* Bar cursor. */ | 2045 | /* Bar cursor. */ |
2051 | curSize = init_I2(gap_UI / 2, lineHeight_Text(d->font)); | 2046 | curSize = init_I2(gap_UI / 2, lineHeight_Text(d->font)); |
2052 | // } | 2047 | // } |
2053 | const iInputLine *curLine = constCursorLine_InputWidget_(d); | 2048 | //const iInputLine *curLine = constCursorLine_InputWidget_(d); |
2054 | const iString * text = &curLine->text; | 2049 | //const iString * text = &curLine->text; |
2055 | /* The bounds include visible characters, while advance includes whitespace as well. | 2050 | /* The bounds include visible characters, while advance includes whitespace as well. |
2056 | Normally only the advance is needed, but if the cursor is at a newline, the advance | 2051 | Normally only the advance is needed, but if the cursor is at a newline, the advance |
2057 | will have reset back to zero. */ | 2052 | will have reset back to zero. */ |
2058 | wrapText.text = range_String(text); | 2053 | //wrapText.text = range_String(text); |
2059 | wrapText.hitChar = wrapText.text.start + d->cursor.x; | 2054 | //wrapText.hitChar = wrapText.text.start + d->cursor.x; |
2060 | //const int prefixSize = maxWidth_TextMetrics( | 2055 | //const int prefixSize = maxWidth_TextMetrics( |
2061 | measure_WrapText(&wrapText, d->font); | 2056 | //measure_WrapText(&wrapText, d->font); |
2062 | const iInt2 advance = wrapText.hitAdvance_out; | 2057 | //const iInt2 advance = wrapText.hitAdvance_out; |
2058 | const iInt2 advance = relativeCursorCoord_InputWidget_(d); | ||
2063 | // const iInt2 curPos = addX_I2(addY_I2(contentBounds.pos, lineHeight_Text(d->font) | 2059 | // const iInt2 curPos = addX_I2(addY_I2(contentBounds.pos, lineHeight_Text(d->font) |
2064 | // * d->cursorLine), | 2060 | // * d->cursorLine), |
2065 | // prefixSize + | 2061 | // prefixSize + |
diff --git a/src/ui/text.c b/src/ui/text.c index 8a82690f..27ed80ab 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -1561,8 +1561,18 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1561 | } | 1561 | } |
1562 | if (isHitPointOnThisLine && wrap->hitPoint.x >= orig.x + wrapAdvance) { | 1562 | if (isHitPointOnThisLine && wrap->hitPoint.x >= orig.x + wrapAdvance) { |
1563 | /* On the right side. */ | 1563 | /* On the right side. */ |
1564 | wrap->hitChar_out = sourcePtr_AttributedText_(&attrText, iMax(0, wrapResumePos - 1)); | 1564 | if (wrapResumePos == textLen) { |
1565 | wrap->hitGlyphNormX_out = 1.0f; | 1565 | wrap->hitChar_out = sourcePtr_AttributedText_(&attrText, wrapResumePos); |
1566 | } | ||
1567 | else { | ||
1568 | const char *hit = sourcePtr_AttributedText_(&attrText, iMax(0, wrapResumePos - 1)); | ||
1569 | while (hit > args->text.start) { | ||
1570 | if (!isSpace_Char(hit[-1])) break; | ||
1571 | hit--; | ||
1572 | } | ||
1573 | wrap->hitChar_out = hit; | ||
1574 | } | ||
1575 | wrap->hitGlyphNormX_out = 0.0f; | ||
1566 | } | 1576 | } |
1567 | } | 1577 | } |
1568 | else { | 1578 | else { |
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c index 8f84eb81..0f94d7dd 100644 --- a/src/ui/uploadwidget.c +++ b/src/ui/uploadwidget.c | |||
@@ -86,7 +86,7 @@ void init_UploadWidget(iUploadWidget *d) { | |||
86 | setFlags_Widget(page, arrangeSize_WidgetFlag, iTrue); | 86 | setFlags_Widget(page, arrangeSize_WidgetFlag, iTrue); |
87 | d->input = new_InputWidget(0); | 87 | d->input = new_InputWidget(0); |
88 | setFont_InputWidget(d->input, monospace_FontId); | 88 | setFont_InputWidget(d->input, monospace_FontId); |
89 | setLineLimits_InputWidget(d->input, 10, 20); | 89 | setLineLimits_InputWidget(d->input, 7, 20); |
90 | setHint_InputWidget(d->input, "${hint.upload.text}"); | 90 | setHint_InputWidget(d->input, "${hint.upload.text}"); |
91 | setEnterInsertsLF_InputWidget(d->input, iTrue); | 91 | setEnterInsertsLF_InputWidget(d->input, iTrue); |
92 | setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1)); | 92 | setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1)); |