summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-24 06:48:54 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-24 06:48:54 +0300
commite0c9784b3d595bdc3d80f0d9f10f9588dfd11d67 (patch)
tree283b35a28f9ec3f4e7c430e231d2e49da572c8d7 /src/ui
parent3391e595259909a557d6d2d302f9b7a9d680c97a (diff)
Cleanup
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/inputwidget.c341
1 files changed, 23 insertions, 318 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 525e2a33..93dbfa9d 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -196,19 +196,19 @@ struct Impl_InputWidget {
196 iWidget widget; 196 iWidget widget;
197 enum iInputMode mode; 197 enum iInputMode mode;
198 int inFlags; 198 int inFlags;
199 size_t maxLen; /* characters */ 199 size_t maxLen; /* characters */
200 iArray lines; /* iInputLine[] */ 200 iArray lines; /* iInputLine[] */
201 iString oldText; /* for restoring if edits cancelled */ 201 iString oldText; /* for restoring if edits cancelled */
202 int lastUpdateWidth; 202 int lastUpdateWidth;
203 iString srcHint; 203 iString srcHint;
204 iString hint; 204 iString hint;
205 int leftPadding; 205 int leftPadding;
206 int rightPadding; 206 int rightPadding;
207 iInt2 cursor; /* cursor position: x = byte offset, y = line index */ 207 iInt2 cursor; /* cursor position: x = byte offset, y = line index */
208 iInt2 prevCursor; /* previous cursor position */ 208 iInt2 prevCursor; /* previous cursor position */
209 iRangei visWrapLines; /* which wrap lines are current visible */ 209 iRangei visWrapLines; /* which wrap lines are current visible */
210 int minWrapLines, maxWrapLines; /* min/max number of visible lines allowed */ 210 int minWrapLines, maxWrapLines; /* min/max number of visible lines allowed */
211 iRanges mark; 211 iRanges mark; /* TODO: would likely simplify things to use two Int2's for marking; no conversions needed */
212 iRanges initialMark; 212 iRanges initialMark;
213 iArray undoStack; 213 iArray undoStack;
214 int font; 214 int font;
@@ -325,34 +325,6 @@ static iRect contentBounds_InputWidget_(const iInputWidget *d) {
325 return bounds; 325 return bounds;
326} 326}
327 327
328#if 0
329static void updateCursorLine_InputWidget_(iInputWidget *d) {
330 iWidget *w = as_Widget(d);
331 d->cursorLine = 0;
332 iConstForEach(Array, i, &d->lines) {
333 const iInputLine *line = i.value;
334 if (line->offset > d->cursor) {
335 break;
336 }
337 d->cursorLine = index_ArrayConstIterator(&i);
338 }
339 /* May need to scroll to keep the cursor visible. */
340 iWidget *flow = findOverflowScrollable_Widget(w);
341 if (flow) {
342 const iRect rootRect = { rect_Root(w->root).pos, visibleSize_Root(w->root) };
343 int yCursor = contentBounds_InputWidget_(d).pos.y +
344 lineHeight_Text(d->font) * (int) d->cursorLine;
345 const int margin = lineHeight_Text(d->font) * 3;
346 if (yCursor < top_Rect(rootRect) + margin) {
347 scrollOverflow_Widget(flow, top_Rect(rootRect) + margin - yCursor);
348 }
349 else if (yCursor > bottom_Rect(rootRect) - margin * 3 / 2) {
350 scrollOverflow_Widget(flow, bottom_Rect(rootRect) - margin * 3 / 2 - yCursor);
351 }
352 }
353}
354#endif
355
356iLocalDef iBool isLastLine_InputWidget_(const iInputWidget *d, const iInputLine *line) { 328iLocalDef iBool isLastLine_InputWidget_(const iInputWidget *d, const iInputLine *line) {
357 return (const void *) line == constBack_Array(&d->lines); 329 return (const void *) line == constBack_Array(&d->lines);
358} 330}
@@ -441,8 +413,8 @@ static int visLineOffsetY_InputWidget_(const iInputWidget *d) {
441 return (line->wrapLines.start - d->visWrapLines.start) * lineHeight_Text(d->font); 413 return (line->wrapLines.start - d->visWrapLines.start) * lineHeight_Text(d->font);
442} 414}
443 415
444static const iChar sensitiveChar_ = 0x25cf; /* black circle */ 416static const iChar sensitiveChar_ = 0x25cf; /* black circle */
445static const char *sensitive_ = "\u25cf"; /* black circle */ 417static const char *sensitive_ = "\u25cf";
446 418
447static iWrapText wrap_InputWidget_(const iInputWidget *d, int y) { 419static iWrapText wrap_InputWidget_(const iInputWidget *d, int y) {
448 return (iWrapText){ 420 return (iWrapText){
@@ -472,9 +444,6 @@ static void updateVisible_InputWidget_(iInputWidget *d) {
472 const iInputLine *curLine = constAt_Array(&d->lines, d->cursor.y); 444 const iInputLine *curLine = constAt_Array(&d->lines, d->cursor.y);
473 const int cursorY = curLine->wrapLines.start + 445 const int cursorY = curLine->wrapLines.start +
474 relativeCursorCoord_InputWidget_(d).y / lineHeight_Text(d->font); 446 relativeCursorCoord_InputWidget_(d).y / lineHeight_Text(d->font);
475// measureRange_Text(d->font, rangeSize_String(&curLine->text, d->cursor.x))
476// .advance.y / lineHeight_Text(d->font);
477 //int scrollY = d->visLineOffsetY / lineHeight_Text(d->font);
478 /* Scroll to cursor. */ 447 /* Scroll to cursor. */
479 int delta = 0; 448 int delta = 0;
480 if (d->visWrapLines.end < cursorY + 1) { 449 if (d->visWrapLines.end < cursorY + 1) {
@@ -486,36 +455,10 @@ static void updateVisible_InputWidget_(iInputWidget *d) {
486 d->visWrapLines.start += delta; 455 d->visWrapLines.start += delta;
487 d->visWrapLines.end += delta; 456 d->visWrapLines.end += delta;
488 iAssert(contains_Range(&d->visWrapLines, cursorY)); 457 iAssert(contains_Range(&d->visWrapLines, cursorY));
489// iAssert(d->visWrapLines.start >= 0);
490// iAssert(d->visWrapLines.end <= totalWraps);
491
492// d->visLineOffsetY = scrollY * lineHeight_Text(d->font);
493#if 0
494// const int numContentLines = numContentLines_InputWidget_(d);
495 /* Expand or shrink the number of visible lines depending on how much content is available. */
496 //d->visLines.end = d->visLines.start + iClamp(numContentLines, d->minVisLines, d->maxVisLines);
497 const int maxVisLines = iMin(d->maxVisLines, size_Array(&d->lines));
498 d->visLines.end = d->visLines.start;
499
500 if (d->visLines.end > numContentLines) {
501 const int avail = d->visLines.start;
502 int offset = iMin(avail, d->visLines.end - numContentLines);
503 d->visLines.start -= offset;
504 d->visLines.end -= offset;
505 }
506 /* Keep the cursor visible. */
507 if (d->cursor.y < d->visLines.start) {
508 scrollVisLines_InputWidget_(d, d->cursor.y - d->visLines.start);
509 }
510 if (d->cursor.y > d->visLines.end - 1) {
511 scrollVisLines_InputWidget_(d, d->cursor.y - d->visLines.end - 1);
512 }
513#endif
514} 458}
515 459
516static void showCursor_InputWidget_(iInputWidget *d) { 460static void showCursor_InputWidget_(iInputWidget *d) {
517 d->cursorVis = 2; 461 d->cursorVis = 2;
518// updateCursorLine_InputWidget_(d);
519 updateVisible_InputWidget_(d); 462 updateVisible_InputWidget_(d);
520} 463}
521 464
@@ -542,7 +485,6 @@ static void updateSizeForFixedLength_InputWidget_(iInputWidget *d) {
542} 485}
543 486
544static iString *text_InputWidget_(const iInputWidget *d) { 487static iString *text_InputWidget_(const iInputWidget *d) {
545 //return newUnicodeN_String(constData_Array(&d->text), size_Array(&d->text));
546 iString *text = new_String(); 488 iString *text = new_String();
547 mergeLines_(&d->lines, text); 489 mergeLines_(&d->lines, text);
548 return text; 490 return text;
@@ -558,99 +500,10 @@ static size_t length_InputWidget_(const iInputWidget *d) {
558 return len; 500 return len;
559} 501}
560 502
561#if 0
562static iString *visText_InputWidget_(const iInputWidget *d) {
563 iString *text;
564 if (~d->inFlags & isSensitive_InputWidgetFlag) {
565 text = text_InputWidget_(d);
566 }
567 else {
568 text = new_String();
569// iAssert(d->length == length_InputWidget_(d));
570 const size_t len = length_InputWidget_(d);
571 for (size_t i = 0; i < len; i++) {
572 appendCStr_String(text, sensitive_);
573 }
574 }
575 return text;
576}
577
578static void clearLines_InputWidget_(iInputWidget *d) {
579 iForEach(Array, i, &d->lines) {
580 deinit_InputLine(i.value);
581 }
582 clear_Array(&d->lines);
583}
584#endif
585
586#if 0
587static void updateLines_InputWidget_(iInputWidget *d) {
588 d->lastUpdateWidth = d->widget.rect.size.x;
589 clearLines_InputWidget_(d);
590 if (d->maxLen) {
591 /* Everything on a single line. */
592 iInputLine line;
593 init_InputLine(&line);
594 iString *u8 = visText_InputWidget_(d);
595 set_String(&line.text, u8);
596 line.len = length_String(u8);
597 delete_String(u8);
598 pushBack_Array(&d->lines, &line);
599 updateCursorLine_InputWidget_(d);
600 return;
601 }
602 /* Word-wrapped lines. */
603 iString *u8 = visText_InputWidget_(d);
604 size_t charPos = 0;
605 iRangecc content = range_String(u8);
606 const int wrapWidth = contentBounds_InputWidget_(d).size.x;
607 while (wrapWidth > 0 && content.end != content.start) {
608 const char *endPos;
609 if (d->inFlags & isUrl_InputWidgetFlag) {
610 tryAdvanceNoWrap_Text(d->font, content, wrapWidth, &endPos);
611 }
612 else {
613 tryAdvance_Text(d->font, content, wrapWidth, &endPos);
614 }
615 const iRangecc part = (iRangecc){ content.start, endPos };
616 iInputLine line;
617 init_InputLine(&line);
618 setRange_String(&line.text, part);
619 line.offset = charPos;
620 line.len = length_String(&line.text);
621 pushBack_Array(&d->lines, &line);
622 charPos += line.len;
623 content.start = endPos;
624 }
625 if (isEmpty_Array(&d->lines) || endsWith_String(u8, "\n")) {
626 /* Always at least one empty line. */
627 iInputLine line;
628 init_InputLine(&line);
629 line.offset = charPos;
630 pushBack_Array(&d->lines, &line);
631 }
632 else {
633 iAssert(charPos == length_String(u8));
634 }
635 delete_String(u8);
636 updateCursorLine_InputWidget_(d);
637}
638#endif
639
640static int contentHeight_InputWidget_(const iInputWidget *d) { 503static int contentHeight_InputWidget_(const iInputWidget *d) {
641 return size_Range(&d->visWrapLines) * lineHeight_Text(d->font); 504 return size_Range(&d->visWrapLines) * lineHeight_Text(d->font);
642} 505}
643 506
644#if 0
645static int contentHeight_InputWidget_(const iInputWidget *d, iBool forLayout) {
646 int numLines = d->curVisLines;
647 if (forLayout) {
648 numLines = iMin(numLines, d->numLayoutLines);
649 }
650 return numLines * lineHeight_Text(d->font);
651}
652#endif
653
654static void updateMetrics_InputWidget_(iInputWidget *d) { 507static void updateMetrics_InputWidget_(iInputWidget *d) {
655 iWidget *w = as_Widget(d); 508 iWidget *w = as_Widget(d);
656 updateSizeForFixedLength_InputWidget_(d); 509 updateSizeForFixedLength_InputWidget_(d);
@@ -701,7 +554,6 @@ static void updateAllLinesAndResizeHeight_InputWidget_(iInputWidget *d) {
701 updateLineRangesStartingFrom_InputWidget_(d, 0); 554 updateLineRangesStartingFrom_InputWidget_(d, 0);
702 updateVisible_InputWidget_(d); 555 updateVisible_InputWidget_(d);
703 if (oldWraps != numWrapLines_InputWidget_(d)) { 556 if (oldWraps != numWrapLines_InputWidget_(d)) {
704// d->click.minHeight = contentHeight_InputWidget_(d, iFalse);
705 updateMetrics_InputWidget_(d); 557 updateMetrics_InputWidget_(d);
706 } 558 }
707} 559}
@@ -716,7 +568,6 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
716 setFlags_Widget(w, extraPadding_WidgetFlag, iTrue); 568 setFlags_Widget(w, extraPadding_WidgetFlag, iTrue);
717#endif 569#endif
718 init_Array(&d->lines, sizeof(iInputLine)); 570 init_Array(&d->lines, sizeof(iInputLine));
719 //init_Array(&d->oldText, sizeof(iChar));
720 init_String(&d->oldText); 571 init_String(&d->oldText);
721 init_Array(&d->lines, sizeof(iInputLine)); 572 init_Array(&d->lines, sizeof(iInputLine));
722 init_String(&d->srcHint); 573 init_String(&d->srcHint);
@@ -727,10 +578,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
727 d->rightPadding = 0; 578 d->rightPadding = 0;
728 d->cursor = zero_I2(); 579 d->cursor = zero_I2();
729 d->prevCursor = zero_I2(); 580 d->prevCursor = zero_I2();
730 //d->lastCursor = 0;
731 //d->cursorLine = 0;
732 d->lastUpdateWidth = 0; 581 d->lastUpdateWidth = 0;
733 //d->verticalMoveX = -1; /* TODO: Use this. */
734 d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag; 582 d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag;
735 if (deviceType_App() != desktop_AppDeviceType) { 583 if (deviceType_App() != desktop_AppDeviceType) {
736 d->inFlags |= enterKeyInsertsLineFeed_InputWidgetFlag; 584 d->inFlags |= enterKeyInsertsLineFeed_InputWidgetFlag;
@@ -739,11 +587,9 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
739 setMaxLen_InputWidget(d, maxLen); 587 setMaxLen_InputWidget(d, maxLen);
740 d->visWrapLines.start = 0; 588 d->visWrapLines.start = 0;
741 d->visWrapLines.end = 1; 589 d->visWrapLines.end = 1;
742// d->visLineOffsetY = 0;
743 d->maxWrapLines = maxLen > 0 ? 1 : 20; /* TODO: Choose maximum dynamically? */ 590 d->maxWrapLines = maxLen > 0 ? 1 : 20; /* TODO: Choose maximum dynamically? */
744 d->minWrapLines = 1; 591 d->minWrapLines = 1;
745 splitToLines_(&iStringLiteral(""), &d->lines); 592 splitToLines_(&iStringLiteral(""), &d->lines);
746 //d->maxLayoutLines = iInvalidSize;
747 setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); /* resizes its own height */ 593 setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); /* resizes its own height */
748 init_Click(&d->click, d, SDL_BUTTON_LEFT); 594 init_Click(&d->click, d, SDL_BUTTON_LEFT);
749 d->timer = 0; 595 d->timer = 0;
@@ -751,7 +597,6 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
751 d->buffered = NULL; 597 d->buffered = NULL;
752 d->backupPath = NULL; 598 d->backupPath = NULL;
753 d->backupTimer = 0; 599 d->backupTimer = 0;
754 //updateLines_InputWidget_(d);
755 updateMetrics_InputWidget_(d); 600 updateMetrics_InputWidget_(d);
756} 601}
757 602
@@ -970,12 +815,8 @@ void setText_InputWidget(iInputWidget *d, const iString *text) {
970#endif 815#endif
971 } 816 }
972 clearUndo_InputWidget_(d); 817 clearUndo_InputWidget_(d);
973 //clear_Array(&d->text);
974 iString *nfcText = collect_String(copy_String(text)); 818 iString *nfcText = collect_String(copy_String(text));
975 normalize_String(nfcText); 819 normalize_String(nfcText);
976// iConstForEach(String, i, nfcText) {
977// pushBack_Array(&d->text, &i.value);
978// }
979 splitToLines_(nfcText, &d->lines); 820 splitToLines_(nfcText, &d->lines);
980 iAssert(!isEmpty_Array(&d->lines)); 821 iAssert(!isEmpty_Array(&d->lines));
981 iForEach(Array, i, &d->lines) { 822 iForEach(Array, i, &d->lines) {
@@ -993,8 +834,6 @@ void setText_InputWidget(iInputWidget *d, const iString *text) {
993 if (!isFocused_Widget(d)) { 834 if (!isFocused_Widget(d)) {
994 d->inFlags |= needUpdateBuffer_InputWidgetFlag; 835 d->inFlags |= needUpdateBuffer_InputWidgetFlag;
995 } 836 }
996// updateLinesAndResize_InputWidget_(d);
997// updateAllLinesAndResizeHeight_InputWidget_(d);
998 updateVisible_InputWidget_(d); 837 updateVisible_InputWidget_(d);
999 refresh_Widget(as_Widget(d)); 838 refresh_Widget(as_Widget(d));
1000} 839}
@@ -1018,7 +857,6 @@ static uint32_t cursorTimer_(uint32_t interval, void *w) {
1018} 857}
1019 858
1020static size_t cursorToIndex_InputWidget_(const iInputWidget *d, iInt2 pos) { 859static size_t cursorToIndex_InputWidget_(const iInputWidget *d, iInt2 pos) {
1021 /* TODO: Lazy linear position updates. */
1022 if (pos.y < 0) { 860 if (pos.y < 0) {
1023 return 0; 861 return 0;
1024 } 862 }
@@ -1031,7 +869,6 @@ static size_t cursorToIndex_InputWidget_(const iInputWidget *d, iInt2 pos) {
1031} 869}
1032 870
1033static iInt2 indexToCursor_InputWidget_(const iInputWidget *d, size_t index) { 871static iInt2 indexToCursor_InputWidget_(const iInputWidget *d, size_t index) {
1034 /* TODO: Lazy linear position updates. */
1035 /* TODO: The lines are sorted; this could use a binary search. */ 872 /* TODO: The lines are sorted; this could use a binary search. */
1036 iConstForEach(Array, i, &d->lines) { 873 iConstForEach(Array, i, &d->lines) {
1037 const iInputLine *line = i.value; 874 const iInputLine *line = i.value;
@@ -1059,7 +896,6 @@ void begin_InputWidget(iInputWidget *d) {
1059 } 896 }
1060 invalidateBuffered_InputWidget_(d); 897 invalidateBuffered_InputWidget_(d);
1061 setFlags_Widget(w, hidden_WidgetFlag | disabled_WidgetFlag, iFalse); 898 setFlags_Widget(w, hidden_WidgetFlag | disabled_WidgetFlag, iFalse);
1062 //setCopy_Array(&d->oldText, &d->text);
1063 mergeLines_(&d->lines, &d->oldText); 899 mergeLines_(&d->lines, &d->oldText);
1064 if (d->mode == overwrite_InputMode) { 900 if (d->mode == overwrite_InputMode) {
1065 d->cursor = zero_I2(); 901 d->cursor = zero_I2();
@@ -1068,15 +904,8 @@ void begin_InputWidget(iInputWidget *d) {
1068 d->cursor.y = iMin(d->cursor.y, size_Array(&d->lines) - 1); 904 d->cursor.y = iMin(d->cursor.y, size_Array(&d->lines) - 1);
1069 d->cursor.x = iMin(d->cursor.x, cursorLine_InputWidget_(d)->range.end); 905 d->cursor.x = iMin(d->cursor.x, cursorLine_InputWidget_(d)->range.end);
1070 } 906 }
1071// updateCursorLine_InputWidget_(d);
1072 SDL_StartTextInput(); 907 SDL_StartTextInput();
1073 setFlags_Widget(w, selected_WidgetFlag, iTrue); 908 setFlags_Widget(w, selected_WidgetFlag, iTrue);
1074#if 0
1075 if (d->maxLayoutLines != iInvalidSize) {
1076 /* This will extend beyond the arranged region. */
1077 setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue);
1078 }
1079#endif
1080 showCursor_InputWidget_(d); 909 showCursor_InputWidget_(d);
1081 refresh_Widget(w); 910 refresh_Widget(w);
1082 d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); 911 d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d);
@@ -1099,7 +928,6 @@ void end_InputWidget(iInputWidget *d, iBool accept) {
1099 } 928 }
1100 enableEditorKeysInMenus_(iTrue); 929 enableEditorKeysInMenus_(iTrue);
1101 if (!accept) { 930 if (!accept) {
1102 //setCopy_Array(&d->text, &d->oldText);
1103 /* Overwrite the edited lines. */ 931 /* Overwrite the edited lines. */
1104 splitToLines_(&d->oldText, &d->lines); 932 splitToLines_(&d->oldText, &d->lines);
1105 } 933 }
@@ -1110,7 +938,6 @@ void end_InputWidget(iInputWidget *d, iBool accept) {
1110 setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iFalse); 938 setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iFalse);
1111 const char *id = cstr_String(id_Widget(as_Widget(d))); 939 const char *id = cstr_String(id_Widget(as_Widget(d)));
1112 if (!*id) id = "_"; 940 if (!*id) id = "_";
1113// updateLinesAndResize_InputWidget_(d);
1114 refresh_Widget(w); 941 refresh_Widget(w);
1115 postCommand_Widget(w, 942 postCommand_Widget(w,
1116 "input.ended id:%s enter:%d arg:%d", 943 "input.ended id:%s enter:%d arg:%d",
@@ -1130,7 +957,6 @@ static void textOfLinesWasChanged_InputWidget_(iInputWidget *d, iRangei lineRang
1130} 957}
1131 958
1132static void insertRange_InputWidget_(iInputWidget *d, iRangecc range) { 959static void insertRange_InputWidget_(iInputWidget *d, iRangecc range) {
1133 iWidget *w = as_Widget(d);
1134 iRangecc nextRange = { range.end, range.end }; 960 iRangecc nextRange = { range.end, range.end };
1135 const int firstModified = d->cursor.y; 961 const int firstModified = d->cursor.y;
1136 for (;; range = nextRange) { 962 for (;; range = nextRange) {
@@ -1174,21 +1000,6 @@ static void insertRange_InputWidget_(iInputWidget *d, iRangecc range) {
1174 } 1000 }
1175 } 1001 }
1176 textOfLinesWasChanged_InputWidget_(d, (iRangei){ firstModified, d->cursor.y + 1 }); 1002 textOfLinesWasChanged_InputWidget_(d, (iRangei){ firstModified, d->cursor.y + 1 });
1177#if 0
1178 else if (d->maxLen == 0 || d->cursor < d->maxLen) {
1179 if (d->cursor >= size_Array(&d->text)) {
1180 resize_Array(&d->text, d->cursor + 1);
1181 }
1182 set_Array(&d->text, d->cursor++, &chr);
1183 if (d->maxLen > 1 && d->cursor == d->maxLen) {
1184 iWidget *nextFocus = findFocusable_Widget(w, forward_WidgetFocusDir);
1185 setFocus_Widget(nextFocus == w ? NULL : nextFocus);
1186 }
1187 else if (d->maxLen == 1) {
1188 d->cursor = 0;
1189 }
1190 }
1191#endif
1192 showCursor_InputWidget_(d); 1003 showCursor_InputWidget_(d);
1193 refresh_Widget(as_Widget(d)); 1004 refresh_Widget(as_Widget(d));
1194} 1005}
@@ -1223,52 +1034,11 @@ void setCursor_InputWidget(iInputWidget *d, iInt2 pos) {
1223 showCursor_InputWidget_(d); 1034 showCursor_InputWidget_(d);
1224} 1035}
1225 1036
1226#if 0
1227static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const iInputLine *line) {
1228 size_t index = line->range.start;
1229 if (x <= 0) {
1230 return index;
1231 }
1232 const char *endPos;
1233 tryAdvanceNoWrap_Text(d->font, range_String(&line->text), x, &endPos);
1234 if (endPos == constEnd_String(&line->text)) {
1235 index = line->range.end - 1;
1236 }
1237 else {
1238#if 0
1239 /* Need to know the actual character index. */
1240 /* TODO: tryAdvance could tell us this directly with an extra return value */
1241 iConstForEach(String, i, &line->text) {
1242 if (i.pos >= endPos) break;
1243 index++;
1244 }
1245#endif
1246 index = line->range.start + (endPos - constBegin_String(&line->text));
1247 }
1248#if 0
1249 if (!isLastLine_InputWidget_(d, line) && index == line->offset + line->len) {
1250 index = iMax(index - 1, line->offset);
1251 }
1252#endif
1253 return iMax(index, line->range.start);
1254}
1255#endif
1256
1257static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir, int horiz) { 1037static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir, int horiz) {
1258 const iInputLine *line = cursorLine_InputWidget_(d); 1038 const iInputLine *line = cursorLine_InputWidget_(d);
1259 iRangecc text = range_String(&line->text); 1039 iInt2 relCoord = relativeCursorCoord_InputWidget_(d);
1260// iWrapText wrapText = { 1040 int relLine = relCoord.y / lineHeight_Text(d->font);
1261// .text = rangeSize_String(&line->text, d->cursor.x),
1262// .maxWidth = width_Rect(contentBounds_InputWidget_(d)),
1263// .mode = (d->inFlags & isUrl_InputWidgetFlag ? anyCharacter_WrapTextMode
1264// : word_WrapTextMode),
1265// };
1266 //iInt2 relCoord = measure_WrapText(&wrapText, d->font).advance;
1267 iInt2 relCoord = relativeCursorCoord_InputWidget_(d);
1268 const int cursorWidth = measureN_Text(d->font, charPos_InputWidget_(d, d->cursor), 1).advance.x;
1269 int relLine = relCoord.y / lineHeight_Text(d->font);
1270 if ((dir < 0 && relLine > 0) || (dir > 0 && relLine < numWrapLines_InputLine_(line) - 1)) { 1041 if ((dir < 0 && relLine > 0) || (dir > 0 && relLine < numWrapLines_InputLine_(line) - 1)) {
1271 /* We can just change the cursor X value, but we'll have to check where the cursor lands. */
1272 relCoord.y += dir * lineHeight_Text(d->font); 1042 relCoord.y += dir * lineHeight_Text(d->font);
1273 } 1043 }
1274 else if (dir < 0 && d->cursor.y > 0) { 1044 else if (dir < 0 && d->cursor.y > 0) {
@@ -1278,7 +1048,6 @@ static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir, int horiz)
1278 } 1048 }
1279 else if (dir > 0 && d->cursor.y < size_Array(&d->lines) - 1) { 1049 else if (dir > 0 && d->cursor.y < size_Array(&d->lines) - 1) {
1280 d->cursor.y++; 1050 d->cursor.y++;
1281 line = cursorLine_InputWidget_(d);
1282 relCoord.y = 0; 1051 relCoord.y = 0;
1283 } 1052 }
1284 else if (dir == 0 && horiz != 0) { 1053 else if (dir == 0 && horiz != 0) {
@@ -1296,15 +1065,13 @@ static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir, int horiz)
1296 else { 1065 else {
1297 d->cursor.x = endX_InputWidget_(d, d->cursor.y); 1066 d->cursor.x = endX_InputWidget_(d, d->cursor.y);
1298 } 1067 }
1299 /* 1068 if (wt.hitGlyphNormX_out > 0.5f && d->cursor.x < endX_InputWidget_(d, d->cursor.y)) {
1300 if (wrapText.hitGlyphNormX_out > 0.5f && d->cursor.x < endX_InputWidget_(d, d->cursor.y)) {
1301 iChar ch; 1069 iChar ch;
1302 int n = decodeBytes_MultibyteChar(wrapText.text.start + d->cursor.x, wrapText.text.end, &ch); 1070 int n = decodeBytes_MultibyteChar(wt.text.start + d->cursor.x, wt.text.end, &ch);
1303 if (n > 0) { 1071 if (ch != '\n' && n > 0) {
1304 d->cursor.x += n; 1072 d->cursor.x += n;
1305 } 1073 }
1306 }*/ 1074 }
1307 //showCursor_InputWidget_(d);
1308 setCursor_InputWidget(d, d->cursor); /* mark, show */ 1075 setCursor_InputWidget(d, d->cursor); /* mark, show */
1309 return iTrue; 1076 return iTrue;
1310} 1077}
@@ -1342,7 +1109,6 @@ static void contentsWereChanged_InputWidget_(iInputWidget *d) {
1342 if (d->validator) { 1109 if (d->validator) {
1343 d->validator(d, d->validatorContext); /* this may change the contents */ 1110 d->validator(d, d->validatorContext); /* this may change the contents */
1344 } 1111 }
1345// updateLinesAndResize_InputWidget_(d);
1346 if (d->inFlags & notifyEdits_InputWidgetFlag) { 1112 if (d->inFlags & notifyEdits_InputWidgetFlag) {
1347 postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d)))); 1113 postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d))));
1348 } 1114 }
@@ -1512,8 +1278,6 @@ static iInt2 coordCursor_InputWidget_(const iInputWidget *d, iInt2 coord) {
1512static iBool copy_InputWidget_(iInputWidget *d, iBool doCut) { 1278static iBool copy_InputWidget_(iInputWidget *d, iBool doCut) {
1513 if (!isEmpty_Range(&d->mark)) { 1279 if (!isEmpty_Range(&d->mark)) {
1514 const iRanges m = mark_InputWidget_(d); 1280 const iRanges m = mark_InputWidget_(d);
1515// iString *str = collect_String(newUnicodeN_String(constAt_Array(&d->text, m.start),
1516// size_Range(&m)));
1517 iString *str = collectNew_String(); 1281 iString *str = collectNew_String();
1518 mergeLinesRange_(&d->lines, m, str); 1282 mergeLinesRange_(&d->lines, m, str);
1519 SDL_SetClipboardText( 1283 SDL_SetClipboardText(
@@ -1544,22 +1308,11 @@ static void paste_InputWidget_(iInputWidget *d) {
1544 } 1308 }
1545 } 1309 }
1546 SDL_free(text); 1310 SDL_free(text);
1547 //iConstForEach(String, i, paste) { insertChar_InputWidget_(d, i.value); }
1548 insertRange_InputWidget_(d, range_String(paste)); 1311 insertRange_InputWidget_(d, range_String(paste));
1549 contentsWereChanged_InputWidget_(d); 1312 contentsWereChanged_InputWidget_(d);
1550 } 1313 }
1551} 1314}
1552 1315
1553#if 0
1554static iRanges lineRange_InputWidget_(const iInputWidget *d) {
1555 if (isEmpty_Array(&d->lines)) {
1556 return (iRanges){ 0, 0 };
1557 }
1558 const iInputLine *line = line_InputWidget_(d, d->cursorLine);
1559 return (iRanges){ line->offset, line->offset + line->len };
1560}
1561#endif
1562
1563static void extendRange_InputWidget_(iInputWidget *d, size_t *index, int dir) { 1316static void extendRange_InputWidget_(iInputWidget *d, size_t *index, int dir) {
1564 iInt2 pos = indexToCursor_InputWidget_(d, *index); 1317 iInt2 pos = indexToCursor_InputWidget_(d, *index);
1565 if (dir < 0) { 1318 if (dir < 0) {
@@ -1617,7 +1370,6 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1617 setText_InputWidget(d, text_InputWidget(d)); 1370 setText_InputWidget(d, text_InputWidget(d));
1618 } 1371 }
1619#endif 1372#endif
1620// updateLinesAndResize_InputWidget_(d);
1621 updateAllLinesAndResizeHeight_InputWidget_(d); 1373 updateAllLinesAndResizeHeight_InputWidget_(d);
1622 d->lastUpdateWidth = w->rect.size.x; 1374 d->lastUpdateWidth = w->rect.size.x;
1623 } 1375 }
@@ -1880,9 +1632,8 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1880 deleteMarked_InputWidget_(d); 1632 deleteMarked_InputWidget_(d);
1881 contentsWereChanged_InputWidget_(d); 1633 contentsWereChanged_InputWidget_(d);
1882 } 1634 }
1883 else if (!isEqual_I2(d->cursor, curMax)) {//} < size_Array(&d->text)) { 1635 else if (!isEqual_I2(d->cursor, curMax)) {
1884 pushUndo_InputWidget_(d); 1636 pushUndo_InputWidget_(d);
1885 //remove_Array(&d->text, d->cursor);
1886 deleteIndexRange_InputWidget_(d, (iRanges){ 1637 deleteIndexRange_InputWidget_(d, (iRanges){
1887 cursorToIndex_InputWidget_(d, d->cursor), 1638 cursorToIndex_InputWidget_(d, d->cursor),
1888 cursorToIndex_InputWidget_(d, movedCursor_InputWidget_(d, d->cursor, +1, 0)) 1639 cursorToIndex_InputWidget_(d, movedCursor_InputWidget_(d, d->cursor, +1, 0))
@@ -1948,7 +1699,6 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1948 case SDLK_RIGHT: { 1699 case SDLK_RIGHT: {
1949 const int dir = (key == SDLK_LEFT ? -1 : +1); 1700 const int dir = (key == SDLK_LEFT ? -1 : +1);
1950 if (mods & byLine_KeyModifier) { 1701 if (mods & byLine_KeyModifier) {
1951 //setCursor_InputWidget(d, dir < 0 ? lineFirst : lineLast);
1952 moveCursorByLine_InputWidget_(d, 0, dir); 1702 moveCursorByLine_InputWidget_(d, 0, dir);
1953 } 1703 }
1954 else if (mods & byWord_KeyModifier) { 1704 else if (mods & byWord_KeyModifier) {
@@ -1995,7 +1745,6 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1995 else if (ev->type == SDL_TEXTINPUT && isFocused_Widget(w)) { 1745 else if (ev->type == SDL_TEXTINPUT && isFocused_Widget(w)) {
1996 pushUndo_InputWidget_(d); 1746 pushUndo_InputWidget_(d);
1997 deleteMarked_InputWidget_(d); 1747 deleteMarked_InputWidget_(d);
1998 const int firstInsertLine = d->cursor.y;
1999 insertRange_InputWidget_(d, range_CStr(ev->text.text)); 1748 insertRange_InputWidget_(d, range_CStr(ev->text.text));
2000 contentsWereChanged_InputWidget_(d); 1749 contentsWereChanged_InputWidget_(d);
2001 return iTrue; 1750 return iTrue;
@@ -2006,12 +1755,12 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
2006iDeclareType(MarkPainter) 1755iDeclareType(MarkPainter)
2007 1756
2008struct Impl_MarkPainter { 1757struct Impl_MarkPainter {
2009 iPaint *paint; 1758 iPaint * paint;
2010 const iInputWidget *d; 1759 const iInputWidget *d;
2011 iRect contentBounds; 1760 iRect contentBounds;
2012 const iInputLine *line; 1761 const iInputLine * line;
2013 iInt2 pos; 1762 iInt2 pos;
2014 iRanges mark; 1763 iRanges mark;
2015}; 1764};
2016 1765
2017static iBool draw_MarkPainter_(iWrapText *wrapText, iRangecc wrappedText, int origin, int advance, 1766static iBool draw_MarkPainter_(iWrapText *wrapText, iRangecc wrappedText, int origin, int advance,
@@ -2034,7 +1783,7 @@ static iBool draw_MarkPainter_(iWrapText *wrapText, iRangecc wrappedText, int or
2034 iRect rect = { addX_I2(mp->pos, origin), init_I2(advance, lineHeight_Text(mp->d->font)) }; 1783 iRect rect = { addX_I2(mp->pos, origin), init_I2(advance, lineHeight_Text(mp->d->font)) };
2035 if (mark.end < lineRange.end) { 1784 if (mark.end < lineRange.end) {
2036 /* Calculate where the mark ends. */ 1785 /* Calculate where the mark ends. */
2037 const iRangecc markedPrefix = { //cstr, cstr + mark.end - lineRange.start }; 1786 const iRangecc markedPrefix = {
2038 wrappedText.start, 1787 wrappedText.start,
2039 wrappedText.start + mark.end - lineRange.start 1788 wrappedText.start + mark.end - lineRange.start
2040 }; 1789 };
@@ -2042,7 +1791,7 @@ static iBool draw_MarkPainter_(iWrapText *wrapText, iRangecc wrappedText, int or
2042 } 1791 }
2043 if (mark.start > lineRange.start) { 1792 if (mark.start > lineRange.start) {
2044 /* Calculate where the mark starts. */ 1793 /* Calculate where the mark starts. */
2045 const iRangecc unmarkedPrefix = { //cstr, cstr + mark.start - lineRange.start 1794 const iRangecc unmarkedPrefix = {
2046 wrappedText.start, 1795 wrappedText.start,
2047 wrappedText.start + mark.start - lineRange.start 1796 wrappedText.start + mark.start - lineRange.start
2048 }; 1797 };
@@ -2118,34 +1867,6 @@ static void draw_InputWidget_(const iInputWidget *d) {
2118 } 1867 }
2119 wrapText.wrapFunc = NULL; 1868 wrapText.wrapFunc = NULL;
2120 wrapText.context = NULL; 1869 wrapText.context = NULL;
2121#if 0
2122 iConstForEach(Array, i, &d->lines) {
2123 const iInputLine *line = i.value;
2124 const iBool isLast = index_ArrayConstIterator(&i) == size_Array(&d->lines) - 1;
2125 const iInputLine *nextLine = isLast ? NULL : (line + 1);
2126 const iRanges lineRange = { line->offset,
2127 nextLine ? nextLine->offset : size_Array(&d->text) };
2128 if (isFocused && !isEmpty_Range(&d->mark)) {
2129 /* Draw the selected range. */
2130 const iRanges mark = mark_InputWidget_(d);
2131 if (mark.start < lineRange.end && mark.end > lineRange.start) {
2132 const int m1 = maxWidth_TextMetrics(measureN_Text(d->font,
2133 cstr_String(&line->text),
2134 iMax(lineRange.start, mark.start) - line->offset));
2135 const int m2 = maxWidth_TextMetrics(measureN_Text(d->font,
2136 cstr_String(&line->text),
2137 iMin(lineRange.end, mark.end) - line->offset));
2138 fillRect_Paint(&p,
2139 (iRect){ addX_I2(drawPos, iMin(m1, m2)),
2140 init_I2(iMax(gap_UI / 3, iAbs(m2 - m1)),
2141 lineHeight_Text(d->font)) },
2142 uiMarked_ColorId);
2143 }
2144 }
2145 drawRange_Text(d->font, drawPos, fg, range_String(&line->text));
2146 drawPos.y += lineHeight_Text(d->font);
2147 }
2148#endif
2149 } 1870 }
2150 unsetClip_Paint(&p); 1871 unsetClip_Paint(&p);
2151 /* Draw the insertion point. */ 1872 /* Draw the insertion point. */
@@ -2180,23 +1901,7 @@ static void draw_InputWidget_(const iInputWidget *d) {
2180 /* Bar cursor. */ 1901 /* Bar cursor. */
2181 curSize = init_I2(gap_UI / 2, lineHeight_Text(d->font)); 1902 curSize = init_I2(gap_UI / 2, lineHeight_Text(d->font));
2182 } 1903 }
2183 //const iInputLine *curLine = constCursorLine_InputWidget_(d);
2184 //const iString * text = &curLine->text;
2185 /* The bounds include visible characters, while advance includes whitespace as well.
2186 Normally only the advance is needed, but if the cursor is at a newline, the advance
2187 will have reset back to zero. */
2188 //wrapText.text = range_String(text);
2189 //wrapText.hitChar = wrapText.text.start + d->cursor.x;
2190 //const int prefixSize = maxWidth_TextMetrics(
2191 //measure_WrapText(&wrapText, d->font);
2192 //const iInt2 advance = wrapText.hitAdvance_out;
2193 const iInt2 advance = relativeCursorCoord_InputWidget_(d); 1904 const iInt2 advance = relativeCursorCoord_InputWidget_(d);
2194 // const iInt2 curPos = addX_I2(addY_I2(contentBounds.pos, lineHeight_Text(d->font)
2195 // * d->cursorLine),
2196 // prefixSize +
2197 // (d->mode == insert_InputMode ? -curSize.x / 2 :
2198 // 0));
2199 //printf("%d -> tm.advance: %d, %d\n", d->cursor.x, tm.advance.x, tm.advance.y);
2200 const iInt2 curPos = add_I2(addY_I2(topLeft_Rect(contentBounds), visLineOffsetY + 1905 const iInt2 curPos = add_I2(addY_I2(topLeft_Rect(contentBounds), visLineOffsetY +
2201 visWrapsAbove * lineHeight_Text(d->font)), 1906 visWrapsAbove * lineHeight_Text(d->font)),
2202 addX_I2(advance, 1907 addX_I2(advance,
@@ -2204,7 +1909,7 @@ static void draw_InputWidget_(const iInputWidget *d) {
2204 const iRect curRect = { curPos, curSize }; 1909 const iRect curRect = { curPos, curSize };
2205 fillRect_Paint(&p, curRect, uiInputCursor_ColorId); 1910 fillRect_Paint(&p, curRect, uiInputCursor_ColorId);
2206 if (d->mode == overwrite_InputMode) { 1911 if (d->mode == overwrite_InputMode) {
2207 /* The `gap_UI` offsets below are a hack. They are used because for some reason the 1912 /* The `gap_UI` offset below is a hack. They are used because for some reason the
2208 cursor rect and the glyph inside don't quite position like during `run_Text_()`. */ 1913 cursor rect and the glyph inside don't quite position like during `run_Text_()`. */
2209 drawRange_Text(d->font, 1914 drawRange_Text(d->font,
2210 addX_I2(curPos, iMin(1, gap_UI / 8)), 1915 addX_I2(curPos, iMin(1, gap_UI / 8)),