diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-05-14 07:24:32 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-05-14 07:24:32 +0300 |
commit | fe59111e9671fba8d1c64def9831716860ea5e44 (patch) | |
tree | fdb44222af394b4924ade2715205a813f1db2f1b | |
parent | 713dcca5da21897fd8f5d7519112429273d7233e (diff) |
InputWidget: Fixed cursor moving; scroll the dialog
Up/down movement sometimes ended up in the wrong cursor position.
Now the nearest overflow-scrollable parent scrolls to keep the cursor visible.
-rw-r--r-- | src/ui/inputwidget.c | 123 | ||||
-rw-r--r-- | src/ui/text.c | 4 | ||||
-rw-r--r-- | src/ui/touch.c | 15 | ||||
-rw-r--r-- | src/ui/widget.c | 22 | ||||
-rw-r--r-- | src/ui/widget.h | 2 |
5 files changed, 107 insertions, 59 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 51447f21..6639f957 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -42,8 +42,12 @@ static const size_t maxUndo_InputWidget_ = 64; | |||
42 | 42 | ||
43 | static void enableEditorKeysInMenus_(iBool enable) { | 43 | static void enableEditorKeysInMenus_(iBool enable) { |
44 | #if defined (iPlatformAppleDesktop) | 44 | #if defined (iPlatformAppleDesktop) |
45 | enableMenuItemsByKey_MacOS(SDLK_LEFT, KMOD_PRIMARY, enable); | 45 | enableMenuItemsByKey_MacOS(SDLK_LEFT, KMOD_PRIMARY, enable); |
46 | enableMenuItemsByKey_MacOS(SDLK_RIGHT, KMOD_PRIMARY, enable); | 46 | enableMenuItemsByKey_MacOS(SDLK_RIGHT, KMOD_PRIMARY, enable); |
47 | enableMenuItemsByKey_MacOS(SDLK_UP, KMOD_PRIMARY, enable); | ||
48 | enableMenuItemsByKey_MacOS(SDLK_DOWN, KMOD_PRIMARY, enable); | ||
49 | enableMenuItemsByKey_MacOS(SDLK_UP, KMOD_PRIMARY | KMOD_SHIFT, enable); | ||
50 | enableMenuItemsByKey_MacOS(SDLK_DOWN, KMOD_PRIMARY | KMOD_SHIFT, enable); | ||
47 | #else | 51 | #else |
48 | iUnused(enable); | 52 | iUnused(enable); |
49 | #endif | 53 | #endif |
@@ -84,6 +88,7 @@ iDeclareType(InputLine) | |||
84 | 88 | ||
85 | struct Impl_InputLine { | 89 | struct Impl_InputLine { |
86 | size_t offset; /* character position from the beginning */ | 90 | size_t offset; /* character position from the beginning */ |
91 | size_t len; /* length as characters */ | ||
87 | iString text; /* UTF-8 */ | 92 | iString text; /* UTF-8 */ |
88 | }; | 93 | }; |
89 | 94 | ||
@@ -138,7 +143,23 @@ static void clearUndo_InputWidget_(iInputWidget *d) { | |||
138 | clear_Array(&d->undoStack); | 143 | clear_Array(&d->undoStack); |
139 | } | 144 | } |
140 | 145 | ||
146 | iLocalDef iInt2 padding_(void) { | ||
147 | return init_I2(gap_UI / 2, gap_UI / 2); | ||
148 | } | ||
149 | |||
150 | static iRect contentBounds_InputWidget_(const iInputWidget *d) { | ||
151 | const iWidget *w = constAs_Widget(d); | ||
152 | // const iRect widgetBounds = bounds_Widget(w); | ||
153 | iRect bounds = adjusted_Rect(bounds_Widget(w), | ||
154 | addX_I2(padding_(), d->leftPadding), | ||
155 | neg_I2(addX_I2(padding_(), d->rightPadding))); | ||
156 | shrink_Rect(&bounds, init_I2(gap_UI * (flags_Widget(w) & tight_WidgetFlag ? 1 : 2), 0)); | ||
157 | bounds.pos.y += padding_().y / 2; | ||
158 | return bounds; | ||
159 | } | ||
160 | |||
141 | static void updateCursorLine_InputWidget_(iInputWidget *d) { | 161 | static void updateCursorLine_InputWidget_(iInputWidget *d) { |
162 | iWidget *w = as_Widget(d); | ||
142 | d->cursorLine = 0; | 163 | d->cursorLine = 0; |
143 | iConstForEach(Array, i, &d->lines) { | 164 | iConstForEach(Array, i, &d->lines) { |
144 | const iInputLine *line = i.value; | 165 | const iInputLine *line = i.value; |
@@ -147,6 +168,20 @@ static void updateCursorLine_InputWidget_(iInputWidget *d) { | |||
147 | } | 168 | } |
148 | d->cursorLine = index_ArrayConstIterator(&i); | 169 | d->cursorLine = index_ArrayConstIterator(&i); |
149 | } | 170 | } |
171 | /* May need to scroll to keep the cursor visible. */ | ||
172 | iWidget *flow = findOverflowScrollable_Widget(w); | ||
173 | if (flow) { | ||
174 | const iRect rootRect = rect_Root(w->root); | ||
175 | int yCursor = contentBounds_InputWidget_(d).pos.y + | ||
176 | lineHeight_Text(d->font) * d->cursorLine; | ||
177 | const int margin = lineHeight_Text(d->font) * 3; | ||
178 | if (yCursor < top_Rect(rootRect) + margin) { | ||
179 | scrollOverflow_Widget(flow, top_Rect(rootRect) + margin - yCursor); | ||
180 | } | ||
181 | else if (yCursor > bottom_Rect(rootRect) - margin * 3 / 2) { | ||
182 | scrollOverflow_Widget(flow, bottom_Rect(rootRect) - margin * 3 / 2 - yCursor); | ||
183 | } | ||
184 | } | ||
150 | } | 185 | } |
151 | 186 | ||
152 | static void showCursor_InputWidget_(iInputWidget *d) { | 187 | static void showCursor_InputWidget_(iInputWidget *d) { |
@@ -161,21 +196,6 @@ static void invalidateBuffered_InputWidget_(iInputWidget *d) { | |||
161 | } | 196 | } |
162 | } | 197 | } |
163 | 198 | ||
164 | iLocalDef iInt2 padding_(void) { | ||
165 | return init_I2(gap_UI / 2, gap_UI / 2); | ||
166 | } | ||
167 | |||
168 | static iRect contentBounds_InputWidget_(const iInputWidget *d) { | ||
169 | const iWidget *w = constAs_Widget(d); | ||
170 | const iRect widgetBounds = bounds_Widget(w); | ||
171 | iRect bounds = adjusted_Rect(bounds_Widget(w), | ||
172 | addX_I2(padding_(), d->leftPadding), | ||
173 | neg_I2(addX_I2(padding_(), d->rightPadding))); | ||
174 | shrink_Rect(&bounds, init_I2(gap_UI * (flags_Widget(w) & tight_WidgetFlag ? 1 : 2), 0)); | ||
175 | bounds.pos.y += padding_().y / 2; | ||
176 | return bounds; | ||
177 | } | ||
178 | |||
179 | static void updateSizeForFixedLength_InputWidget_(iInputWidget *d) { | 199 | static void updateSizeForFixedLength_InputWidget_(iInputWidget *d) { |
180 | if (d->maxLen) { | 200 | if (d->maxLen) { |
181 | /* Set a fixed size based on maximum possible width of the text. */ | 201 | /* Set a fixed size based on maximum possible width of the text. */ |
@@ -226,6 +246,7 @@ static void updateLines_InputWidget_(iInputWidget *d) { | |||
226 | init_InputLine(&line); | 246 | init_InputLine(&line); |
227 | iString *u8 = visText_InputWidget_(d); | 247 | iString *u8 = visText_InputWidget_(d); |
228 | set_String(&line.text, u8); | 248 | set_String(&line.text, u8); |
249 | line.len = length_String(u8); | ||
229 | delete_String(u8); | 250 | delete_String(u8); |
230 | pushBack_Array(&d->lines, &line); | 251 | pushBack_Array(&d->lines, &line); |
231 | updateCursorLine_InputWidget_(d); | 252 | updateCursorLine_InputWidget_(d); |
@@ -249,14 +270,16 @@ static void updateLines_InputWidget_(iInputWidget *d) { | |||
249 | init_InputLine(&line); | 270 | init_InputLine(&line); |
250 | setRange_String(&line.text, part); | 271 | setRange_String(&line.text, part); |
251 | line.offset = charPos; | 272 | line.offset = charPos; |
273 | line.len = length_String(&line.text); | ||
252 | pushBack_Array(&d->lines, &line); | 274 | pushBack_Array(&d->lines, &line); |
253 | charPos += length_String(&line.text); | 275 | charPos += line.len; |
254 | content.start = endPos; | 276 | content.start = endPos; |
255 | } | 277 | } |
256 | if (isEmpty_Array(&d->lines) || endsWith_String(u8, "\n")) { | 278 | if (isEmpty_Array(&d->lines) || endsWith_String(u8, "\n")) { |
257 | /* Always at least one empty line. */ | 279 | /* Always at least one empty line. */ |
258 | iInputLine line; | 280 | iInputLine line; |
259 | init_InputLine(&line); | 281 | init_InputLine(&line); |
282 | line.offset = charPos; | ||
260 | pushBack_Array(&d->lines, &line); | 283 | pushBack_Array(&d->lines, &line); |
261 | } | 284 | } |
262 | else { | 285 | else { |
@@ -578,7 +601,11 @@ void begin_InputWidget(iInputWidget *d) { | |||
578 | } | 601 | } |
579 | updateCursorLine_InputWidget_(d); | 602 | updateCursorLine_InputWidget_(d); |
580 | SDL_StartTextInput(); | 603 | SDL_StartTextInput(); |
581 | setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iTrue); | 604 | setFlags_Widget(w, selected_WidgetFlag, iTrue); |
605 | if (d->maxLayoutLines != iInvalidSize) { | ||
606 | /* This will extend beyond the arranged region. */ | ||
607 | setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue); | ||
608 | } | ||
582 | showCursor_InputWidget_(d); | 609 | showCursor_InputWidget_(d); |
583 | refresh_Widget(w); | 610 | refresh_Widget(w); |
584 | d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); | 611 | d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); |
@@ -672,6 +699,10 @@ void setCursor_InputWidget(iInputWidget *d, size_t pos) { | |||
672 | showCursor_InputWidget_(d); | 699 | showCursor_InputWidget_(d); |
673 | } | 700 | } |
674 | 701 | ||
702 | iLocalDef iBool isLastLine_InputWidget_(const iInputWidget *d, const iInputLine *line) { | ||
703 | return (const void *) line == constAt_Array(&d->lines, size_Array(&d->lines) - 1); | ||
704 | } | ||
705 | |||
675 | static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const iInputLine *line) { | 706 | static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const iInputLine *line) { |
676 | if (x <= 0) { | 707 | if (x <= 0) { |
677 | return line->offset; | 708 | return line->offset; |
@@ -680,7 +711,7 @@ static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const | |||
680 | tryAdvanceNoWrap_Text(d->font, range_String(&line->text), x, &endPos); | 711 | tryAdvanceNoWrap_Text(d->font, range_String(&line->text), x, &endPos); |
681 | size_t index = line->offset; | 712 | size_t index = line->offset; |
682 | if (endPos == constEnd_String(&line->text)) { | 713 | if (endPos == constEnd_String(&line->text)) { |
683 | index += length_String(&line->text); | 714 | index += line->len; |
684 | } | 715 | } |
685 | else { | 716 | else { |
686 | /* Need to know the actual character index. */ | 717 | /* Need to know the actual character index. */ |
@@ -690,6 +721,9 @@ static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const | |||
690 | index++; | 721 | index++; |
691 | } | 722 | } |
692 | } | 723 | } |
724 | if (!isLastLine_InputWidget_(d, line) && index == line->offset + line->len) { | ||
725 | index = iMax(index - 1, line->offset); | ||
726 | } | ||
693 | return index; | 727 | return index; |
694 | } | 728 | } |
695 | 729 | ||
@@ -705,11 +739,6 @@ static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir) { | |||
705 | newCursor = indexForRelativeX_InputWidget_(d, xPos, ++line); | 739 | newCursor = indexForRelativeX_InputWidget_(d, xPos, ++line); |
706 | } | 740 | } |
707 | if (newCursor != iInvalidPos) { | 741 | if (newCursor != iInvalidPos) { |
708 | /* Clamp it to the current line. */ | ||
709 | newCursor = iMax(newCursor, line->offset); | ||
710 | newCursor = iMin(newCursor, line->offset + length_String(&line->text) - | ||
711 | /* last line is allowed to go to the cursorMax */ | ||
712 | ((const void *) line < constAt_Array(&d->lines, numLines - 1) ? 1 : 0)); | ||
713 | setCursor_InputWidget(d, newCursor); | 742 | setCursor_InputWidget(d, newCursor); |
714 | return iTrue; | 743 | return iTrue; |
715 | } | 744 | } |
@@ -896,7 +925,7 @@ static iRanges lineRange_InputWidget_(const iInputWidget *d) { | |||
896 | return (iRanges){ 0, 0 }; | 925 | return (iRanges){ 0, 0 }; |
897 | } | 926 | } |
898 | const iInputLine *line = line_InputWidget_(d, d->cursorLine); | 927 | const iInputLine *line = line_InputWidget_(d, d->cursorLine); |
899 | return (iRanges){ line->offset, line->offset + length_String(&line->text) }; | 928 | return (iRanges){ line->offset, line->offset + line->len }; |
900 | } | 929 | } |
901 | 930 | ||
902 | static void extendRange_InputWidget_(iInputWidget *d, size_t *pos, int dir) { | 931 | static void extendRange_InputWidget_(iInputWidget *d, size_t *pos, int dir) { |
@@ -1094,6 +1123,17 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1094 | return iTrue; | 1123 | return iTrue; |
1095 | } | 1124 | } |
1096 | } | 1125 | } |
1126 | #if defined (iPlatformApple) | ||
1127 | if (mods == KMOD_PRIMARY || mods == (KMOD_PRIMARY | KMOD_SHIFT)) { | ||
1128 | switch (key) { | ||
1129 | case SDLK_UP: | ||
1130 | case SDLK_DOWN: | ||
1131 | setCursor_InputWidget(d, key == SDLK_UP ? 0 : curMax); | ||
1132 | refresh_Widget(d); | ||
1133 | return iTrue; | ||
1134 | } | ||
1135 | } | ||
1136 | #endif | ||
1097 | d->lastCursor = d->cursor; | 1137 | d->lastCursor = d->cursor; |
1098 | switch (key) { | 1138 | switch (key) { |
1099 | case SDLK_INSERT: | 1139 | case SDLK_INSERT: |
@@ -1187,7 +1227,12 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1187 | break; | 1227 | break; |
1188 | case SDLK_HOME: | 1228 | case SDLK_HOME: |
1189 | case SDLK_END: | 1229 | case SDLK_END: |
1190 | setCursor_InputWidget(d, key == SDLK_HOME ? lineFirst : lineLast); | 1230 | if (mods == KMOD_PRIMARY || mods == (KMOD_PRIMARY | KMOD_SHIFT)) { |
1231 | setCursor_InputWidget(d, key == SDLK_HOME ? 0 : curMax); | ||
1232 | } | ||
1233 | else { | ||
1234 | setCursor_InputWidget(d, key == SDLK_HOME ? lineFirst : lineLast); | ||
1235 | } | ||
1191 | refresh_Widget(w); | 1236 | refresh_Widget(w); |
1192 | return iTrue; | 1237 | return iTrue; |
1193 | case SDLK_a: | 1238 | case SDLK_a: |
@@ -1233,19 +1278,20 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1233 | /* Allow focus switching. */ | 1278 | /* Allow focus switching. */ |
1234 | return processEvent_Widget(as_Widget(d), ev); | 1279 | return processEvent_Widget(as_Widget(d), ev); |
1235 | case SDLK_UP: | 1280 | case SDLK_UP: |
1236 | if (moveCursorByLine_InputWidget_(d, -1)) { | ||
1237 | refresh_Widget(d); | ||
1238 | return iTrue; | ||
1239 | } | ||
1240 | /* For moving to lookup from url entry. */ | ||
1241 | return processEvent_Widget(as_Widget(d), ev); | ||
1242 | case SDLK_DOWN: | 1281 | case SDLK_DOWN: |
1243 | if (moveCursorByLine_InputWidget_(d, +1)) { | 1282 | if (moveCursorByLine_InputWidget_(d, key == SDLK_UP ? -1 : +1)) { |
1244 | refresh_Widget(d); | 1283 | refresh_Widget(d); |
1245 | return iTrue; | 1284 | return iTrue; |
1246 | } | 1285 | } |
1247 | /* For moving to lookup from url entry. */ | 1286 | /* For moving to lookup from url entry. */ |
1248 | return processEvent_Widget(as_Widget(d), ev); | 1287 | return processEvent_Widget(as_Widget(d), ev); |
1288 | case SDLK_PAGEUP: | ||
1289 | case SDLK_PAGEDOWN: | ||
1290 | for (int count = 0; count < 5; count++) { | ||
1291 | moveCursorByLine_InputWidget_(d, key == SDLK_PAGEUP ? -1 : +1); | ||
1292 | } | ||
1293 | refresh_Widget(d); | ||
1294 | return iTrue; | ||
1249 | } | 1295 | } |
1250 | if (mods & (KMOD_PRIMARY | KMOD_SECONDARY)) { | 1296 | if (mods & (KMOD_PRIMARY | KMOD_SECONDARY)) { |
1251 | return iFalse; | 1297 | return iFalse; |
@@ -1265,6 +1311,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1265 | return processEvent_Widget(w, ev); | 1311 | return processEvent_Widget(w, ev); |
1266 | } | 1312 | } |
1267 | 1313 | ||
1314 | #if 0 | ||
1268 | static iBool isWhite_(const iString *str) { | 1315 | static iBool isWhite_(const iString *str) { |
1269 | iConstForEach(String, i, str) { | 1316 | iConstForEach(String, i, str) { |
1270 | if (!isSpace_Char(i.value)) { | 1317 | if (!isSpace_Char(i.value)) { |
@@ -1273,6 +1320,7 @@ static iBool isWhite_(const iString *str) { | |||
1273 | } | 1320 | } |
1274 | return iTrue; | 1321 | return iTrue; |
1275 | } | 1322 | } |
1323 | #endif | ||
1276 | 1324 | ||
1277 | static void draw_InputWidget_(const iInputWidget *d) { | 1325 | static void draw_InputWidget_(const iInputWidget *d) { |
1278 | const iWidget *w = constAs_Widget(d); | 1326 | const iWidget *w = constAs_Widget(d); |
@@ -1337,7 +1385,8 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1337 | .x; | 1385 | .x; |
1338 | fillRect_Paint(&p, | 1386 | fillRect_Paint(&p, |
1339 | (iRect){ addX_I2(drawPos, iMin(m1, m2)), | 1387 | (iRect){ addX_I2(drawPos, iMin(m1, m2)), |
1340 | init_I2(iAbs(m2 - m1), lineHeight_Text(d->font)) }, | 1388 | init_I2(iMax(gap_UI / 3, iAbs(m2 - m1)), |
1389 | lineHeight_Text(d->font)) }, | ||
1341 | uiMarked_ColorId); | 1390 | uiMarked_ColorId); |
1342 | } | 1391 | } |
1343 | } | 1392 | } |
@@ -1405,13 +1454,7 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1405 | drawChildren_Widget(w); | 1454 | drawChildren_Widget(w); |
1406 | } | 1455 | } |
1407 | 1456 | ||
1408 | //static void sizeChanged_InputWidget_(iInputWidget *d) { | ||
1409 | // printf("[InputWidget] %p: size changed, updating layout\n", d); | ||
1410 | // updateLinesAndResize_InputWidget_(d, iFalse); | ||
1411 | //} | ||
1412 | |||
1413 | iBeginDefineSubclass(InputWidget, Widget) | 1457 | iBeginDefineSubclass(InputWidget, Widget) |
1414 | .processEvent = (iAny *) processEvent_InputWidget_, | 1458 | .processEvent = (iAny *) processEvent_InputWidget_, |
1415 | .draw = (iAny *) draw_InputWidget_, | 1459 | .draw = (iAny *) draw_InputWidget_, |
1416 | // .sizeChanged = (iAny *) sizeChanged_InputWidget_, | ||
1417 | iEndDefineSubclass(InputWidget) | 1460 | iEndDefineSubclass(InputWidget) |
diff --git a/src/ui/text.c b/src/ui/text.c index 6c9e23cb..eca0952d 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -1269,7 +1269,7 @@ int drawWrapRange_Text(int fontId, iInt2 pos, int maxWidth, int color, iRangecc | |||
1269 | const iInt2 adv = tryAdvance_Text(fontId, text, maxWidth, &endp); | 1269 | const iInt2 adv = tryAdvance_Text(fontId, text, maxWidth, &endp); |
1270 | drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endp }); | 1270 | drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endp }); |
1271 | text.start = endp; | 1271 | text.start = endp; |
1272 | pos.y += adv.y; | 1272 | pos.y += iMax(adv.y, lineHeight_Text(fontId)); |
1273 | } | 1273 | } |
1274 | return pos.y; | 1274 | return pos.y; |
1275 | } | 1275 | } |
@@ -1416,7 +1416,7 @@ static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iB | |||
1416 | const iInt2 size = (doWrap ? tryAdvance_Text(font, content, maxWidth, &content.start) | 1416 | const iInt2 size = (doWrap ? tryAdvance_Text(font, content, maxWidth, &content.start) |
1417 | : tryAdvanceNoWrap_Text(font, content, maxWidth, &content.start)); | 1417 | : tryAdvanceNoWrap_Text(font, content, maxWidth, &content.start)); |
1418 | d->size.x = iMax(d->size.x, size.x); | 1418 | d->size.x = iMax(d->size.x, size.x); |
1419 | d->size.y += size.y; | 1419 | d->size.y += iMax(size.y, lineHeight_Text(font)); |
1420 | } | 1420 | } |
1421 | } | 1421 | } |
1422 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); | 1422 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); |
diff --git a/src/ui/touch.c b/src/ui/touch.c index b2c52526..c5634788 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -338,19 +338,6 @@ static void update_TouchState_(void *ptr) { | |||
338 | } | 338 | } |
339 | } | 339 | } |
340 | 340 | ||
341 | static iWidget *findOverflowScrollable_Widget_(iWidget *d) { | ||
342 | const iInt2 rootSize = size_Root(d->root); | ||
343 | for (iWidget *w = d; w; w = parent_Widget(w)) { | ||
344 | if (flags_Widget(w) & overflowScrollable_WidgetFlag) { | ||
345 | if (height_Widget(w) > rootSize.y && !hasVisibleChildOnTop_Widget(w)) { | ||
346 | return w; | ||
347 | } | ||
348 | return NULL; | ||
349 | } | ||
350 | } | ||
351 | return NULL; | ||
352 | } | ||
353 | |||
354 | static iWidget *findSlidePanel_Widget_(iWidget *d) { | 341 | static iWidget *findSlidePanel_Widget_(iWidget *d) { |
355 | for (iWidget *w = d; w; w = parent_Widget(w)) { | 342 | for (iWidget *w = d; w; w = parent_Widget(w)) { |
356 | if (isVisible_Widget(w) && flags_Widget(w) & horizontalOffset_WidgetFlag) { | 343 | if (isVisible_Widget(w) && flags_Widget(w) & horizontalOffset_WidgetFlag) { |
@@ -544,7 +531,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
544 | divvf_F3(&touch->accum, 6); | 531 | divvf_F3(&touch->accum, 6); |
545 | divfv_I2(&pixels, 6); | 532 | divfv_I2(&pixels, 6); |
546 | /* Allow scrolling a scrollable widget. */ | 533 | /* Allow scrolling a scrollable widget. */ |
547 | iWidget *flow = findOverflowScrollable_Widget_(touch->affinity); | 534 | iWidget *flow = findOverflowScrollable_Widget(touch->affinity); |
548 | if (flow) { | 535 | if (flow) { |
549 | touch->affinity = flow; | 536 | touch->affinity = flow; |
550 | } | 537 | } |
diff --git a/src/ui/widget.c b/src/ui/widget.c index fa05e8d1..18f98050 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -927,7 +927,7 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
927 | return iFalse; | 927 | return iFalse; |
928 | } | 928 | } |
929 | 929 | ||
930 | static iBool scrollOverflow_Widget_(iWidget *d, int delta) { | 930 | iBool scrollOverflow_Widget(iWidget *d, int delta) { |
931 | iRect bounds = bounds_Widget(d); | 931 | iRect bounds = bounds_Widget(d); |
932 | const iInt2 rootSize = size_Root(d->root); | 932 | const iInt2 rootSize = size_Root(d->root); |
933 | const iRect winRect = safeRect_Root(d->root); | 933 | const iRect winRect = safeRect_Root(d->root); |
@@ -980,7 +980,7 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
980 | if (!isPerPixel_MouseWheelEvent(&ev->wheel)) { | 980 | if (!isPerPixel_MouseWheelEvent(&ev->wheel)) { |
981 | step *= lineHeight_Text(uiLabel_FontId); | 981 | step *= lineHeight_Text(uiLabel_FontId); |
982 | } | 982 | } |
983 | if (scrollOverflow_Widget_(d, step)) { | 983 | if (scrollOverflow_Widget(d, step)) { |
984 | return iTrue; | 984 | return iTrue; |
985 | } | 985 | } |
986 | } | 986 | } |
@@ -989,7 +989,7 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
989 | if (d->flags & overflowScrollable_WidgetFlag && | 989 | if (d->flags & overflowScrollable_WidgetFlag && |
990 | ~d->flags & visualOffset_WidgetFlag && | 990 | ~d->flags & visualOffset_WidgetFlag && |
991 | isCommand_UserEvent(ev, "widget.overflow")) { | 991 | isCommand_UserEvent(ev, "widget.overflow")) { |
992 | scrollOverflow_Widget_(d, 0); /* check bounds */ | 992 | scrollOverflow_Widget(d, 0); /* check bounds */ |
993 | } | 993 | } |
994 | if (ev->user.code == command_UserEventCode && d->commandHandler && | 994 | if (ev->user.code == command_UserEventCode && d->commandHandler && |
995 | d->commandHandler(d, ev->user.data1)) { | 995 | d->commandHandler(d, ev->user.data1)) { |
@@ -1321,6 +1321,22 @@ iAny *findParentClass_Widget(const iWidget *d, const iAnyClass *class) { | |||
1321 | return i; | 1321 | return i; |
1322 | } | 1322 | } |
1323 | 1323 | ||
1324 | iAny *findOverflowScrollable_Widget(iWidget *d) { | ||
1325 | const iRect rootRect = rect_Root(d->root); | ||
1326 | for (iWidget *w = d; w; w = parent_Widget(w)) { | ||
1327 | if (flags_Widget(w) & overflowScrollable_WidgetFlag) { | ||
1328 | const iRect bounds = boundsWithoutVisualOffset_Widget(w); | ||
1329 | if ((bottom_Rect(bounds) > bottom_Rect(rootRect) || | ||
1330 | top_Rect(bounds) < top_Rect(rootRect)) && | ||
1331 | !hasVisibleChildOnTop_Widget(w)) { | ||
1332 | return w; | ||
1333 | } | ||
1334 | return NULL; | ||
1335 | } | ||
1336 | } | ||
1337 | return NULL; | ||
1338 | } | ||
1339 | |||
1324 | size_t childCount_Widget(const iWidget *d) { | 1340 | size_t childCount_Widget(const iWidget *d) { |
1325 | if (!d->children) return 0; | 1341 | if (!d->children) return 0; |
1326 | return size_ObjectList(d->children); | 1342 | return size_ObjectList(d->children); |
diff --git a/src/ui/widget.h b/src/ui/widget.h index 5c05e917..13f400d1 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -193,6 +193,7 @@ iAny * findChild_Widget (const iWidget *, const char *id); | |||
193 | const iPtrArray *findChildren_Widget (const iWidget *, const char *id); | 193 | const iPtrArray *findChildren_Widget (const iWidget *, const char *id); |
194 | iAny * findParentClass_Widget (const iWidget *, const iAnyClass *class); | 194 | iAny * findParentClass_Widget (const iWidget *, const iAnyClass *class); |
195 | iAny * findFocusable_Widget (const iWidget *startFrom, enum iWidgetFocusDir focusDir); | 195 | iAny * findFocusable_Widget (const iWidget *startFrom, enum iWidgetFocusDir focusDir); |
196 | iAny * findOverflowScrollable_Widget (iWidget *); | ||
196 | size_t childCount_Widget (const iWidget *); | 197 | size_t childCount_Widget (const iWidget *); |
197 | void draw_Widget (const iWidget *); | 198 | void draw_Widget (const iWidget *); |
198 | void drawBackground_Widget (const iWidget *); | 199 | void drawBackground_Widget (const iWidget *); |
@@ -262,6 +263,7 @@ iAny * child_Widget (iWidget *, size_t index); /* O(n) */ | |||
262 | size_t childIndex_Widget (const iWidget *, const iAnyObject *child); /* O(n) */ | 263 | size_t childIndex_Widget (const iWidget *, const iAnyObject *child); /* O(n) */ |
263 | void arrange_Widget (iWidget *); | 264 | void arrange_Widget (iWidget *); |
264 | void resetSize_Widget (iWidget *); | 265 | void resetSize_Widget (iWidget *); |
266 | iBool scrollOverflow_Widget (iWidget *, int delta); /* moves the widget */ | ||
265 | iBool dispatchEvent_Widget (iWidget *, const SDL_Event *); | 267 | iBool dispatchEvent_Widget (iWidget *, const SDL_Event *); |
266 | iBool processEvent_Widget (iWidget *, const SDL_Event *); | 268 | iBool processEvent_Widget (iWidget *, const SDL_Event *); |
267 | void postCommand_Widget (const iAnyObject *, const char *cmd, ...); | 269 | void postCommand_Widget (const iAnyObject *, const char *cmd, ...); |