summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/inputwidget.c138
-rw-r--r--src/ui/inputwidget.h1
2 files changed, 85 insertions, 54 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 12eb490d..146b46b6 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -468,16 +468,52 @@ static iWrapText wrap_InputWidget_(const iInputWidget *d, int y) {
468 }; 468 };
469} 469}
470 470
471static iInt2 relativeCoord_InputWidget_(const iInputWidget *d, iInt2 pos) { 471static iRangei visibleLineRange_InputWidget_(const iInputWidget *d) {
472 /* Relative to the start of the line on which the cursor is. */ 472 iRangei vis = { -1, -1 };
473 /* Determine which lines are in the potentially visible range. */
474 for (int i = 0; i < size_Array(&d->lines); i++) {
475 const iInputLine *line = constAt_Array(&d->lines, i);
476 if (vis.start < 0 && line->wrapLines.end > d->visWrapLines.start) {
477 vis.start = vis.end = i;
478 }
479 if (line->wrapLines.start < d->visWrapLines.end) {
480 vis.end = i + 1;
481 }
482 else break;
483 }
484 iAssert(isEmpty_Range(&vis) || (vis.start >= 0 && vis.end >= vis.start));
485 return vis;
486}
487
488static iInt2 relativeCoordOnLine_InputWidget_(const iInputWidget *d, iInt2 pos) {
489 /* Relative to the start of the line on which the position is. */
473 iWrapText wt = wrap_InputWidget_(d, pos.y); 490 iWrapText wt = wrap_InputWidget_(d, pos.y);
474 wt.hitChar = wt.text.start + pos.x; 491 wt.hitChar = wt.text.start + pos.x;
475 measure_WrapText(&wt, d->font); 492 measure_WrapText(&wt, d->font);
476 return wt.hitAdvance_out; 493 return wt.hitAdvance_out;
477} 494}
478 495
496static iInt2 cursorToWindowCoord_InputWidget_(const iInputWidget *d, iInt2 pos, iBool *isInsideBounds) {
497 /* Maps a cursor XY position to a window coordinate. */
498 const iRect bounds = contentBounds_InputWidget_(d);
499 iInt2 wc = addY_I2(topLeft_Rect(bounds), visLineOffsetY_InputWidget_(d));
500 iRangei visLines = visibleLineRange_InputWidget_(d);
501 if (!contains_Range(&visLines, pos.y)) {
502 /* This line is not visible. */
503 *isInsideBounds = iFalse;
504 return zero_I2();
505 }
506 for (int i = visLines.start; i < pos.y; i++) {
507 wc.y += lineHeight_Text(d->font) * numWrapLines_InputLine_(line_InputWidget_(d, i));
508 }
509 const iInputLine *line = line_InputWidget_(d, pos.y);
510 addv_I2(&wc, relativeCoordOnLine_InputWidget_(d, pos));
511 *isInsideBounds = contains_Rect(bounds, wc);
512 return wc;
513}
514
479static iInt2 relativeCursorCoord_InputWidget_(const iInputWidget *d) { 515static iInt2 relativeCursorCoord_InputWidget_(const iInputWidget *d) {
480 return relativeCoord_InputWidget_(d, d->cursor); 516 return relativeCoordOnLine_InputWidget_(d, d->cursor);
481} 517}
482 518
483static void updateVisible_InputWidget_(iInputWidget *d) { 519static void updateVisible_InputWidget_(iInputWidget *d) {
@@ -770,6 +806,10 @@ const iString *text_InputWidget(const iInputWidget *d) {
770 return collectNew_String(); 806 return collectNew_String();
771} 807}
772 808
809int font_InputWidget(const iInputWidget *d) {
810 return d->font;
811}
812
773iInputWidgetContentPadding contentPadding_InputWidget(const iInputWidget *d) { 813iInputWidgetContentPadding contentPadding_InputWidget(const iInputWidget *d) {
774 return (iInputWidgetContentPadding){ d->leftPadding, d->rightPadding }; 814 return (iInputWidgetContentPadding){ d->leftPadding, d->rightPadding };
775} 815}
@@ -839,23 +879,6 @@ static iBool isHintVisible_InputWidget_(const iInputWidget *d) {
839 return !isEmpty_String(&d->hint) && isEmpty_InputWidget_(d); 879 return !isEmpty_String(&d->hint) && isEmpty_InputWidget_(d);
840} 880}
841 881
842static iRangei visibleLineRange_InputWidget_(const iInputWidget *d) {
843 iRangei vis = { -1, -1 };
844 /* Determine which lines are in the potentially visible range. */
845 for (int i = 0; i < size_Array(&d->lines); i++) {
846 const iInputLine *line = constAt_Array(&d->lines, i);
847 if (vis.start < 0 && line->wrapLines.end > d->visWrapLines.start) {
848 vis.start = vis.end = i;
849 }
850 if (line->wrapLines.start < d->visWrapLines.end) {
851 vis.end = i + 1;
852 }
853 else break;
854 }
855 iAssert(isEmpty_Range(&vis) || (vis.start >= 0 && vis.end >= vis.start));
856 return vis;
857}
858
859static void updateBuffered_InputWidget_(iInputWidget *d) { 882static void updateBuffered_InputWidget_(iInputWidget *d) {
860 invalidateBuffered_InputWidget_(d); 883 invalidateBuffered_InputWidget_(d);
861 if (isHintVisible_InputWidget_(d)) { 884 if (isHintVisible_InputWidget_(d)) {
@@ -1332,9 +1355,10 @@ static iInt2 coordCursor_InputWidget_(const iInputWidget *d, iInt2 coord) {
1332 if (relCoord.y < 0) { 1355 if (relCoord.y < 0) {
1333 return zero_I2(); 1356 return zero_I2();
1334 } 1357 }
1335 if (relCoord.y >= height_Rect(bounds)) { 1358// if (relCoord.y >= height_Rect(bounds)) {
1336 return cursorMax_InputWidget_(d); 1359// printf("relCoord > bounds.h\n"); fflush(stdout);
1337 } 1360// return cursorMax_InputWidget_(d);
1361// }
1338 iWrapText wrapText = { 1362 iWrapText wrapText = {
1339 .maxWidth = d->maxLen == 0 ? width_Rect(bounds) : unlimitedWidth_InputWidget_, 1363 .maxWidth = d->maxLen == 0 ? width_Rect(bounds) : unlimitedWidth_InputWidget_,
1340 .mode = (d->inFlags & isUrl_InputWidgetFlag ? anyCharacter_WrapTextMode : word_WrapTextMode), 1364 .mode = (d->inFlags & isUrl_InputWidgetFlag ? anyCharacter_WrapTextMode : word_WrapTextMode),
@@ -1494,34 +1518,6 @@ static enum iEventResult processPointerEvents_InputWidget_(iInputWidget *d, cons
1494 ? SDL_SYSTEM_CURSOR_IBEAM 1518 ? SDL_SYSTEM_CURSOR_IBEAM
1495 : SDL_SYSTEM_CURSOR_ARROW); 1519 : SDL_SYSTEM_CURSOR_ARROW);
1496 } 1520 }
1497 if (ev->type == SDL_MOUSEWHEEL && contains_Widget(w, coord_MouseWheelEvent(&ev->wheel))) {
1498 const int lineHeight = lineHeight_Text(d->font);
1499 if (isPerPixel_MouseWheelEvent(&ev->wheel)) {
1500 d->wheelAccum -= ev->wheel.y;
1501 }
1502 else {
1503 d->wheelAccum -= ev->wheel.y * 3 * lineHeight;
1504 }
1505 int lineDelta = d->wheelAccum / lineHeight;
1506 if (lineDelta < 0) {
1507 lineDelta = iMax(lineDelta, -d->visWrapLines.start);
1508 if (!lineDelta) d->wheelAccum = 0;
1509 }
1510 else if (lineDelta > 0) {
1511 lineDelta = iMin(lineDelta,
1512 lastLine_InputWidget_(d)->wrapLines.end - d->visWrapLines.end);
1513 if (!lineDelta) d->wheelAccum = 0;
1514 }
1515 if (lineDelta) {
1516 d->wheelAccum -= lineDelta * lineHeight;
1517 d->visWrapLines.start += lineDelta;
1518 d->visWrapLines.end += lineDelta;
1519 d->inFlags |= needUpdateBuffer_InputWidgetFlag;
1520 refresh_Widget(d);
1521 return true_EventResult;
1522 }
1523 return false_EventResult;
1524 }
1525 if (ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT && 1521 if (ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT &&
1526 contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { 1522 contains_Widget(w, init_I2(ev->button.x, ev->button.y))) {
1527 showClipMenu_(mouseCoord_Window(get_Window(), ev->button.which)); 1523 showClipMenu_(mouseCoord_Window(get_Window(), ev->button.which));
@@ -1602,9 +1598,12 @@ static iBool isInsideMark_InputWidget_(const iInputWidget *d, size_t pos) {
1602} 1598}
1603 1599
1604static int distanceToPos_InputWidget_(const iInputWidget *d, iInt2 uiCoord, iInt2 textPos) { 1600static int distanceToPos_InputWidget_(const iInputWidget *d, iInt2 uiCoord, iInt2 textPos) {
1605 const iInt2 a = addY_I2(relativeCoord_InputWidget_(d, textPos), lineHeight_Text(d->font) / 2); 1601 iBool isInside;
1606 const iInt2 b = sub_I2(uiCoord, topLeft_Rect(contentBounds_InputWidget_(d))); 1602 const iInt2 winCoord = cursorToWindowCoord_InputWidget_(d, textPos, &isInside);
1607 return dist_I2(a, b); 1603 if (!isInside) {
1604 return INT_MAX;
1605 }
1606 return dist_I2(addY_I2(winCoord, lineHeight_Text(d->font) / 2), uiCoord);
1608} 1607}
1609 1608
1610static enum iEventResult processTouchEvents_InputWidget_(iInputWidget *d, const SDL_Event *ev) { 1609static enum iEventResult processTouchEvents_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
@@ -1934,6 +1933,37 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1934 copy_InputWidget_(d, iFalse); 1933 copy_InputWidget_(d, iFalse);
1935 return iTrue; 1934 return iTrue;
1936 } 1935 }
1936 if (ev->type == SDL_MOUSEWHEEL && contains_Widget(w, coord_MouseWheelEvent(&ev->wheel))) {
1937 if (numWrapLines_InputWidget_(d) <= size_Range(&d->visWrapLines)) {
1938 return ignored_EventResult;
1939 }
1940 const int lineHeight = lineHeight_Text(d->font);
1941 if (isPerPixel_MouseWheelEvent(&ev->wheel)) {
1942 d->wheelAccum -= ev->wheel.y;
1943 }
1944 else {
1945 d->wheelAccum -= ev->wheel.y * 3 * lineHeight;
1946 }
1947 int lineDelta = d->wheelAccum / lineHeight;
1948 if (lineDelta < 0) {
1949 lineDelta = iMax(lineDelta, -d->visWrapLines.start);
1950 if (!lineDelta) d->wheelAccum = 0;
1951 }
1952 else if (lineDelta > 0) {
1953 lineDelta = iMin(lineDelta,
1954 lastLine_InputWidget_(d)->wrapLines.end - d->visWrapLines.end);
1955 if (!lineDelta) d->wheelAccum = 0;
1956 }
1957 if (lineDelta) {
1958 d->wheelAccum -= lineDelta * lineHeight;
1959 d->visWrapLines.start += lineDelta;
1960 d->visWrapLines.end += lineDelta;
1961 d->inFlags |= needUpdateBuffer_InputWidgetFlag;
1962 refresh_Widget(d);
1963 return true_EventResult;
1964 }
1965 return false_EventResult;
1966 }
1937 /* Click behavior depends on device type. */ { 1967 /* Click behavior depends on device type. */ {
1938 const int mbResult = (deviceType_App() == desktop_AppDeviceType 1968 const int mbResult = (deviceType_App() == desktop_AppDeviceType
1939 ? processPointerEvents_InputWidget_(d, ev) 1969 ? processPointerEvents_InputWidget_(d, ev)
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h
index 0d327ca6..f70c81af 100644
--- a/src/ui/inputwidget.h
+++ b/src/ui/inputwidget.h
@@ -68,6 +68,7 @@ int minLines_InputWidget (const iInputWidget *);
68int maxLines_InputWidget (const iInputWidget *); 68int maxLines_InputWidget (const iInputWidget *);
69iInputWidgetContentPadding contentPadding_InputWidget (const iInputWidget *); 69iInputWidgetContentPadding contentPadding_InputWidget (const iInputWidget *);
70const iString * text_InputWidget (const iInputWidget *); 70const iString * text_InputWidget (const iInputWidget *);
71int font_InputWidget (const iInputWidget *);
71 72
72iLocalDef const char *cstrText_InputWidget(const iInputWidget *d) { 73iLocalDef const char *cstrText_InputWidget(const iInputWidget *d) {
73 return cstr_String(text_InputWidget(d)); 74 return cstr_String(text_InputWidget(d));