diff options
-rw-r--r-- | src/ui/inputwidget.c | 138 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 1 |
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 | ||
471 | static iInt2 relativeCoord_InputWidget_(const iInputWidget *d, iInt2 pos) { | 471 | static 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 | |||
488 | static 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 | ||
496 | static 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 | |||
479 | static iInt2 relativeCursorCoord_InputWidget_(const iInputWidget *d) { | 515 | static iInt2 relativeCursorCoord_InputWidget_(const iInputWidget *d) { |
480 | return relativeCoord_InputWidget_(d, d->cursor); | 516 | return relativeCoordOnLine_InputWidget_(d, d->cursor); |
481 | } | 517 | } |
482 | 518 | ||
483 | static void updateVisible_InputWidget_(iInputWidget *d) { | 519 | static 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 | ||
809 | int font_InputWidget(const iInputWidget *d) { | ||
810 | return d->font; | ||
811 | } | ||
812 | |||
773 | iInputWidgetContentPadding contentPadding_InputWidget(const iInputWidget *d) { | 813 | iInputWidgetContentPadding 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 | ||
842 | static 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 | |||
859 | static void updateBuffered_InputWidget_(iInputWidget *d) { | 882 | static 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 | ||
1604 | static int distanceToPos_InputWidget_(const iInputWidget *d, iInt2 uiCoord, iInt2 textPos) { | 1600 | static 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 | ||
1610 | static enum iEventResult processTouchEvents_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | 1609 | static 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 *); | |||
68 | int maxLines_InputWidget (const iInputWidget *); | 68 | int maxLines_InputWidget (const iInputWidget *); |
69 | iInputWidgetContentPadding contentPadding_InputWidget (const iInputWidget *); | 69 | iInputWidgetContentPadding contentPadding_InputWidget (const iInputWidget *); |
70 | const iString * text_InputWidget (const iInputWidget *); | 70 | const iString * text_InputWidget (const iInputWidget *); |
71 | int font_InputWidget (const iInputWidget *); | ||
71 | 72 | ||
72 | iLocalDef const char *cstrText_InputWidget(const iInputWidget *d) { | 73 | iLocalDef const char *cstrText_InputWidget(const iInputWidget *d) { |
73 | return cstr_String(text_InputWidget(d)); | 74 | return cstr_String(text_InputWidget(d)); |