diff options
-rw-r--r-- | po/en.po | 12 | ||||
-rw-r--r-- | res/lang/de.bin | bin | 24162 -> 24222 bytes | |||
-rw-r--r-- | res/lang/en.bin | bin | 22763 -> 22823 bytes | |||
-rw-r--r-- | res/lang/es.bin | bin | 25286 -> 25346 bytes | |||
-rw-r--r-- | res/lang/fi.bin | bin | 25291 -> 25351 bytes | |||
-rw-r--r-- | res/lang/fr.bin | bin | 26260 -> 26320 bytes | |||
-rw-r--r-- | res/lang/ia.bin | bin | 24890 -> 24950 bytes | |||
-rw-r--r-- | res/lang/ie.bin | bin | 24645 -> 24705 bytes | |||
-rw-r--r-- | res/lang/pl.bin | bin | 25823 -> 25883 bytes | |||
-rw-r--r-- | res/lang/ru.bin | bin | 37542 -> 37602 bytes | |||
-rw-r--r-- | res/lang/sr.bin | bin | 37199 -> 37259 bytes | |||
-rw-r--r-- | res/lang/tok.bin | bin | 23081 -> 23141 bytes | |||
-rw-r--r-- | res/lang/zh_Hans.bin | bin | 21842 -> 21902 bytes | |||
-rw-r--r-- | res/lang/zh_Hant.bin | bin | 22027 -> 22087 bytes | |||
-rw-r--r-- | src/app.c | 16 | ||||
-rw-r--r-- | src/defs.h | 2 | ||||
-rw-r--r-- | src/ui/color.h | 1 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 21 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 563 | ||||
-rw-r--r-- | src/ui/paint.c | 19 | ||||
-rw-r--r-- | src/ui/paint.h | 2 | ||||
-rw-r--r-- | src/ui/root.c | 33 | ||||
-rw-r--r-- | src/ui/touch.c | 9 | ||||
-rw-r--r-- | src/ui/util.c | 70 | ||||
-rw-r--r-- | src/ui/widget.c | 40 | ||||
-rw-r--r-- | src/ui/window.c | 10 | ||||
-rw-r--r-- | src/ui/window.h | 2 |
27 files changed, 591 insertions, 209 deletions
@@ -273,6 +273,18 @@ msgstr "Copy" | |||
273 | msgid "menu.paste" | 273 | msgid "menu.paste" |
274 | msgstr "Paste" | 274 | msgstr "Paste" |
275 | 275 | ||
276 | # keep this short (3x1 horiz layout) | ||
277 | msgid "menu.selectall" | ||
278 | msgstr "Select All" | ||
279 | |||
280 | # keep this short (3x1 horiz layout) | ||
281 | msgid "menu.delete" | ||
282 | msgstr "Delete" | ||
283 | |||
284 | # keep this short (3x1 horiz layout) | ||
285 | msgid "menu.undo" | ||
286 | msgstr "Undo" | ||
287 | |||
276 | msgid "menu.select.clear" | 288 | msgid "menu.select.clear" |
277 | msgstr "Clear Selection" | 289 | msgstr "Clear Selection" |
278 | 290 | ||
diff --git a/res/lang/de.bin b/res/lang/de.bin index ba87d002..f5a6b07e 100644 --- a/res/lang/de.bin +++ b/res/lang/de.bin | |||
Binary files differ | |||
diff --git a/res/lang/en.bin b/res/lang/en.bin index a7dfb866..df3c4025 100644 --- a/res/lang/en.bin +++ b/res/lang/en.bin | |||
Binary files differ | |||
diff --git a/res/lang/es.bin b/res/lang/es.bin index 7e7398d6..9f5a169f 100644 --- a/res/lang/es.bin +++ b/res/lang/es.bin | |||
Binary files differ | |||
diff --git a/res/lang/fi.bin b/res/lang/fi.bin index 607e52fd..ac3b99ef 100644 --- a/res/lang/fi.bin +++ b/res/lang/fi.bin | |||
Binary files differ | |||
diff --git a/res/lang/fr.bin b/res/lang/fr.bin index 955695ed..7d40e32c 100644 --- a/res/lang/fr.bin +++ b/res/lang/fr.bin | |||
Binary files differ | |||
diff --git a/res/lang/ia.bin b/res/lang/ia.bin index 61a18efc..d10ab85e 100644 --- a/res/lang/ia.bin +++ b/res/lang/ia.bin | |||
Binary files differ | |||
diff --git a/res/lang/ie.bin b/res/lang/ie.bin index 06ea7979..14349637 100644 --- a/res/lang/ie.bin +++ b/res/lang/ie.bin | |||
Binary files differ | |||
diff --git a/res/lang/pl.bin b/res/lang/pl.bin index 5fc5e24a..9ddd137f 100644 --- a/res/lang/pl.bin +++ b/res/lang/pl.bin | |||
Binary files differ | |||
diff --git a/res/lang/ru.bin b/res/lang/ru.bin index 1718f647..366c6ee5 100644 --- a/res/lang/ru.bin +++ b/res/lang/ru.bin | |||
Binary files differ | |||
diff --git a/res/lang/sr.bin b/res/lang/sr.bin index 60b7b600..870b0950 100644 --- a/res/lang/sr.bin +++ b/res/lang/sr.bin | |||
Binary files differ | |||
diff --git a/res/lang/tok.bin b/res/lang/tok.bin index 3298f0e8..dac5fb33 100644 --- a/res/lang/tok.bin +++ b/res/lang/tok.bin | |||
Binary files differ | |||
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin index 8c32a0c5..3407c485 100644 --- a/res/lang/zh_Hans.bin +++ b/res/lang/zh_Hans.bin | |||
Binary files differ | |||
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin index 68f7d3bc..b9001e2d 100644 --- a/res/lang/zh_Hant.bin +++ b/res/lang/zh_Hant.bin | |||
Binary files differ | |||
@@ -1069,11 +1069,11 @@ iLocalDef iBool isWaitingAllowed_App_(iApp *d) { | |||
1069 | return iFalse; | 1069 | return iFalse; |
1070 | } | 1070 | } |
1071 | #endif | 1071 | #endif |
1072 | #if defined (iPlatformMobile) | 1072 | //#if defined (iPlatformMobile) |
1073 | if (!isFinished_Anim(&d->window->rootOffset)) { | 1073 | // if (!isFinished_Anim(&d->window->rootOffset)) { |
1074 | return iFalse; | 1074 | // return iFalse; |
1075 | } | 1075 | // } |
1076 | #endif | 1076 | //#endif |
1077 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); | 1077 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); |
1078 | } | 1078 | } |
1079 | 1079 | ||
@@ -1318,7 +1318,7 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1318 | } | 1318 | } |
1319 | } | 1319 | } |
1320 | #if defined (LAGRANGE_ENABLE_IDLE_SLEEP) | 1320 | #if defined (LAGRANGE_ENABLE_IDLE_SLEEP) |
1321 | if (d->isIdling && !gotEvents && isFinished_Anim(&d->window->rootOffset)) { | 1321 | if (d->isIdling && !gotEvents /*&& isFinished_Anim(&d->window->rootOffset)*/) { |
1322 | /* This is where we spend most of our time when idle. 60 Hz still quite a lot but we | 1322 | /* This is where we spend most of our time when idle. 60 Hz still quite a lot but we |
1323 | can't wait too long after the user tries to interact again with the app. In any | 1323 | can't wait too long after the user tries to interact again with the app. In any |
1324 | case, on macOS SDL_WaitEvent() seems to use 10x more CPU time than sleeping. */ | 1324 | case, on macOS SDL_WaitEvent() seems to use 10x more CPU time than sleeping. */ |
@@ -1411,9 +1411,9 @@ void refresh_App(void) { | |||
1411 | #endif | 1411 | #endif |
1412 | if (!exchange_Atomic(&d->pendingRefresh, iFalse)) { | 1412 | if (!exchange_Atomic(&d->pendingRefresh, iFalse)) { |
1413 | /* Refreshing wasn't pending. */ | 1413 | /* Refreshing wasn't pending. */ |
1414 | if (isFinished_Anim(&d->window->rootOffset)) { | 1414 | // if (isFinished_Anim(&d->window->rootOffset)) { |
1415 | return; | 1415 | return; |
1416 | } | 1416 | // } |
1417 | } | 1417 | } |
1418 | // iTime draw; | 1418 | // iTime draw; |
1419 | // initCurrent_Time(&draw); | 1419 | // initCurrent_Time(&draw); |
@@ -151,6 +151,8 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) { | |||
151 | #define magnifyingGlass_Icon "\U0001f50d" | 151 | #define magnifyingGlass_Icon "\U0001f50d" |
152 | #define midEllipsis_Icon "\u00b7\u00b7\u00b7" | 152 | #define midEllipsis_Icon "\u00b7\u00b7\u00b7" |
153 | #define return_Icon "\u23ce" | 153 | #define return_Icon "\u23ce" |
154 | #define undo_Icon "\u23ea" | ||
155 | #define select_Icon "\u2b1a" | ||
154 | 156 | ||
155 | #if defined (iPlatformApple) | 157 | #if defined (iPlatformApple) |
156 | # define shift_Icon "\u21e7" | 158 | # define shift_Icon "\u21e7" |
diff --git a/src/ui/color.h b/src/ui/color.h index 37ec49eb..a1d863dc 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -183,6 +183,7 @@ iLocalDef iBool isRegularText_ColorId(enum iColorId d) { | |||
183 | #define mask_ColorId 0x7f | 183 | #define mask_ColorId 0x7f |
184 | #define permanent_ColorId 0x80 /* cannot be changed via escapes */ | 184 | #define permanent_ColorId 0x80 /* cannot be changed via escapes */ |
185 | #define fillBackground_ColorId 0x100 /* fill background with same color, but alpha 0 */ | 185 | #define fillBackground_ColorId 0x100 /* fill background with same color, but alpha 0 */ |
186 | #define opaque_ColorId 0x200 | ||
186 | 187 | ||
187 | #define asciiBase_ColorEscape 33 | 188 | #define asciiBase_ColorEscape 33 |
188 | #define asciiExtended_ColorEscape (128 - asciiBase_ColorEscape) | 189 | #define asciiExtended_ColorEscape (128 - asciiBase_ColorEscape) |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 4b3c2db0..8ea695d5 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -4597,23 +4597,6 @@ static void drawMedia_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { | |||
4597 | } | 4597 | } |
4598 | } | 4598 | } |
4599 | 4599 | ||
4600 | static void drawPin_(iPaint *p, iRect rangeRect, int dir) { | ||
4601 | const int pinColor = tmQuote_ColorId; | ||
4602 | const int height = height_Rect(rangeRect); | ||
4603 | iRect pin; | ||
4604 | if (dir == 0) { | ||
4605 | pin = (iRect){ add_I2(topLeft_Rect(rangeRect), init_I2(-gap_UI / 4, -gap_UI)), | ||
4606 | init_I2(gap_UI / 2, height + gap_UI) }; | ||
4607 | } | ||
4608 | else { | ||
4609 | pin = (iRect){ addX_I2(topRight_Rect(rangeRect), -gap_UI / 4), | ||
4610 | init_I2(gap_UI / 2, height + gap_UI) }; | ||
4611 | } | ||
4612 | fillRect_Paint(p, pin, pinColor); | ||
4613 | fillRect_Paint(p, initCentered_Rect(dir == 0 ? topMid_Rect(pin) : bottomMid_Rect(pin), | ||
4614 | init1_I2(gap_UI * 2)), pinColor); | ||
4615 | } | ||
4616 | |||
4617 | static void extend_GmRunRange_(iGmRunRange *runs) { | 4600 | static void extend_GmRunRange_(iGmRunRange *runs) { |
4618 | if (runs->start) { | 4601 | if (runs->start) { |
4619 | runs->start--; | 4602 | runs->start--; |
@@ -4857,8 +4840,8 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4857 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); | 4840 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); |
4858 | /* Selection range pins. */ | 4841 | /* Selection range pins. */ |
4859 | if (isTouchSelecting) { | 4842 | if (isTouchSelecting) { |
4860 | drawPin_(&ctx.paint, ctx.firstMarkRect, 0); | 4843 | drawPin_Paint(&ctx.paint, ctx.firstMarkRect, 0, tmQuote_ColorId); |
4861 | drawPin_(&ctx.paint, ctx.lastMarkRect, 1); | 4844 | drawPin_Paint(&ctx.paint, ctx.lastMarkRect, 1, tmQuote_ColorId); |
4862 | } | 4845 | } |
4863 | } | 4846 | } |
4864 | drawMedia_DocumentWidget_(d, &ctx.paint); | 4847 | drawMedia_DocumentWidget_(d, &ctx.paint); |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index ad630223..12eb490d 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -179,19 +179,23 @@ static void deinit_InputUndo_(iInputUndo *d) { | |||
179 | } | 179 | } |
180 | 180 | ||
181 | enum iInputWidgetFlag { | 181 | enum iInputWidgetFlag { |
182 | isSensitive_InputWidgetFlag = iBit(1), | 182 | isSensitive_InputWidgetFlag = iBit(1), |
183 | isUrl_InputWidgetFlag = iBit(2), /* affected by decoding preference */ | 183 | isUrl_InputWidgetFlag = iBit(2), /* affected by decoding preference */ |
184 | enterPressed_InputWidgetFlag = iBit(3), | 184 | enterPressed_InputWidgetFlag = iBit(3), |
185 | selectAllOnFocus_InputWidgetFlag = iBit(4), | 185 | selectAllOnFocus_InputWidgetFlag = iBit(4), |
186 | notifyEdits_InputWidgetFlag = iBit(5), | 186 | notifyEdits_InputWidgetFlag = iBit(5), |
187 | eatEscape_InputWidgetFlag = iBit(6), | 187 | eatEscape_InputWidgetFlag = iBit(6), |
188 | isMarking_InputWidgetFlag = iBit(7), | 188 | isMarking_InputWidgetFlag = iBit(7), |
189 | markWords_InputWidgetFlag = iBit(8), | 189 | markWords_InputWidgetFlag = iBit(8), |
190 | needUpdateBuffer_InputWidgetFlag = iBit(9), | 190 | needUpdateBuffer_InputWidgetFlag = iBit(9), |
191 | enterKeyEnabled_InputWidgetFlag = iBit(10), | 191 | enterKeyEnabled_InputWidgetFlag = iBit(10), |
192 | lineBreaksEnabled_InputWidgetFlag= iBit(11), | 192 | lineBreaksEnabled_InputWidgetFlag = iBit(11), |
193 | needBackup_InputWidgetFlag = iBit(12), | 193 | needBackup_InputWidgetFlag = iBit(12), |
194 | useReturnKeyBehavior_InputWidgetFlag = iBit(13), | 194 | useReturnKeyBehavior_InputWidgetFlag = iBit(13), |
195 | //touchBehavior_InputWidgetFlag = iBit(14), /* different behavior depending on interaction method */ | ||
196 | dragCursor_InputWidgetFlag = iBit(14), | ||
197 | dragMarkerStart_InputWidgetFlag = iBit(15), | ||
198 | dragMarkerEnd_InputWidgetFlag = iBit(16), | ||
195 | }; | 199 | }; |
196 | 200 | ||
197 | /*----------------------------------------------------------------------------------------------*/ | 201 | /*----------------------------------------------------------------------------------------------*/ |
@@ -217,6 +221,10 @@ struct Impl_InputWidget { | |||
217 | iArray undoStack; | 221 | iArray undoStack; |
218 | int font; | 222 | int font; |
219 | iClick click; | 223 | iClick click; |
224 | uint32_t tapStartTime; | ||
225 | uint32_t lastTapTime; | ||
226 | iInt2 lastTapPos; | ||
227 | int tapCount; | ||
220 | int wheelAccum; | 228 | int wheelAccum; |
221 | int cursorVis; | 229 | int cursorVis; |
222 | uint32_t timer; | 230 | uint32_t timer; |
@@ -460,14 +468,18 @@ static iWrapText wrap_InputWidget_(const iInputWidget *d, int y) { | |||
460 | }; | 468 | }; |
461 | } | 469 | } |
462 | 470 | ||
463 | static iInt2 relativeCursorCoord_InputWidget_(const iInputWidget *d) { | 471 | static iInt2 relativeCoord_InputWidget_(const iInputWidget *d, iInt2 pos) { |
464 | /* Relative to the start of the line on which the cursor is. */ | 472 | /* Relative to the start of the line on which the cursor is. */ |
465 | iWrapText wt = wrap_InputWidget_(d, d->cursor.y); | 473 | iWrapText wt = wrap_InputWidget_(d, pos.y); |
466 | wt.hitChar = wt.text.start + d->cursor.x; | 474 | wt.hitChar = wt.text.start + pos.x; |
467 | measure_WrapText(&wt, d->font); | 475 | measure_WrapText(&wt, d->font); |
468 | return wt.hitAdvance_out; | 476 | return wt.hitAdvance_out; |
469 | } | 477 | } |
470 | 478 | ||
479 | static iInt2 relativeCursorCoord_InputWidget_(const iInputWidget *d) { | ||
480 | return relativeCoord_InputWidget_(d, d->cursor); | ||
481 | } | ||
482 | |||
471 | static void updateVisible_InputWidget_(iInputWidget *d) { | 483 | static void updateVisible_InputWidget_(iInputWidget *d) { |
472 | const int totalWraps = numWrapLines_InputWidget_(d); | 484 | const int totalWraps = numWrapLines_InputWidget_(d); |
473 | const int visWraps = iClamp(totalWraps, d->minWrapLines, d->maxWrapLines); | 485 | const int visWraps = iClamp(totalWraps, d->minWrapLines, d->maxWrapLines); |
@@ -632,7 +644,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { | |||
632 | init_Widget(w); | 644 | init_Widget(w); |
633 | d->validator = NULL; | 645 | d->validator = NULL; |
634 | d->validatorContext = NULL; | 646 | d->validatorContext = NULL; |
635 | setFlags_Widget(w, focusable_WidgetFlag | hover_WidgetFlag | touchDrag_WidgetFlag, iTrue); | 647 | setFlags_Widget(w, focusable_WidgetFlag | hover_WidgetFlag, iTrue); |
636 | #if defined (iPlatformMobile) | 648 | #if defined (iPlatformMobile) |
637 | setFlags_Widget(w, extraPadding_WidgetFlag, iTrue); | 649 | setFlags_Widget(w, extraPadding_WidgetFlag, iTrue); |
638 | #endif | 650 | #endif |
@@ -662,6 +674,8 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { | |||
662 | splitToLines_(&iStringLiteral(""), &d->lines); | 674 | splitToLines_(&iStringLiteral(""), &d->lines); |
663 | setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); /* resizes its own height */ | 675 | setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); /* resizes its own height */ |
664 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 676 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
677 | d->lastTapTime = 0; | ||
678 | d->tapCount = 0; | ||
665 | d->wheelAccum = 0; | 679 | d->wheelAccum = 0; |
666 | d->timer = 0; | 680 | d->timer = 0; |
667 | d->cursorVis = 0; | 681 | d->cursorVis = 0; |
@@ -993,7 +1007,7 @@ void begin_InputWidget(iInputWidget *d) { | |||
993 | d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end }; | 1007 | d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end }; |
994 | d->cursor = cursorMax_InputWidget_(d); | 1008 | d->cursor = cursorMax_InputWidget_(d); |
995 | } | 1009 | } |
996 | else { | 1010 | else if (~d->inFlags & isMarking_InputWidgetFlag) { |
997 | iZap(d->mark); | 1011 | iZap(d->mark); |
998 | } | 1012 | } |
999 | enableEditorKeysInMenus_(iFalse); | 1013 | enableEditorKeysInMenus_(iFalse); |
@@ -1013,9 +1027,10 @@ void end_InputWidget(iInputWidget *d, iBool accept) { | |||
1013 | splitToLines_(&d->oldText, &d->lines); | 1027 | splitToLines_(&d->oldText, &d->lines); |
1014 | } | 1028 | } |
1015 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | 1029 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; |
1030 | d->inFlags &= ~isMarking_InputWidgetFlag; | ||
1016 | startOrStopCursorTimer_InputWidget_(d, iFalse); | 1031 | startOrStopCursorTimer_InputWidget_(d, iFalse); |
1017 | SDL_StopTextInput(); | 1032 | SDL_StopTextInput(); |
1018 | setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iFalse); | 1033 | setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag | touchDrag_WidgetFlag, iFalse); |
1019 | const char *id = cstr_String(id_Widget(as_Widget(d))); | 1034 | const char *id = cstr_String(id_Widget(as_Widget(d))); |
1020 | if (!*id) id = "_"; | 1035 | if (!*id) id = "_"; |
1021 | refresh_Widget(w); | 1036 | refresh_Widget(w); |
@@ -1445,88 +1460,31 @@ static iBool checkAcceptMods_InputWidget_(const iInputWidget *d, int mods) { | |||
1445 | return mods == 0; | 1460 | return mods == 0; |
1446 | } | 1461 | } |
1447 | 1462 | ||
1448 | static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | 1463 | enum iEventResult { |
1449 | iWidget *w = as_Widget(d); | 1464 | ignored_EventResult = 0, /* event was not processed */ |
1450 | /* Resize according to width immediately. */ | 1465 | false_EventResult = 1, /* event was processed but other widgets can still process it, too*/ |
1451 | if (d->lastUpdateWidth != w->rect.size.x) { | 1466 | true_EventResult = 2, /* event was processed and should not be passed on */ |
1452 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | 1467 | }; |
1453 | if (d->inFlags & isUrl_InputWidgetFlag) { | 1468 | |
1454 | /* Restore/omit the default scheme if necessary. */ | 1469 | static void markWordAtCursor_InputWidget_(iInputWidget *d) { |
1455 | setText_InputWidget(d, text_InputWidget(d)); | 1470 | d->mark.start = d->mark.end = cursorToIndex_InputWidget_(d, d->cursor); |
1456 | } | 1471 | extendRange_InputWidget_(d, &d->mark.start, -1); |
1457 | updateAllLinesAndResizeHeight_InputWidget_(d); | 1472 | extendRange_InputWidget_(d, &d->mark.end, +1); |
1458 | d->lastUpdateWidth = w->rect.size.x; | 1473 | d->initialMark = d->mark; |
1459 | } | 1474 | } |
1460 | if (isCommand_Widget(w, ev, "focus.gained")) { | 1475 | |
1461 | begin_InputWidget(d); | 1476 | static void showClipMenu_(iInt2 coord) { |
1462 | return iFalse; | 1477 | iWidget *clipMenu = findWidget_App("clipmenu"); |
1463 | } | 1478 | if (isVisible_Widget(clipMenu)) { |
1464 | else if (isEditing_InputWidget_(d) && (isCommand_UserEvent(ev, "window.focus.lost") || | 1479 | closeMenu_Widget(clipMenu); |
1465 | isCommand_UserEvent(ev, "window.focus.gained"))) { | ||
1466 | startOrStopCursorTimer_InputWidget_(d, isCommand_UserEvent(ev, "window.focus.gained")); | ||
1467 | d->cursorVis = 1; | ||
1468 | refresh_Widget(d); | ||
1469 | return iFalse; | ||
1470 | } | ||
1471 | else if (isCommand_UserEvent(ev, "keyroot.changed")) { | ||
1472 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | ||
1473 | } | ||
1474 | else if (isCommand_UserEvent(ev, "lang.changed")) { | ||
1475 | set_String(&d->hint, &d->srcHint); | ||
1476 | translate_Lang(&d->hint); | ||
1477 | return iFalse; | ||
1478 | } | ||
1479 | else if (isCommand_Widget(w, ev, "focus.lost")) { | ||
1480 | end_InputWidget(d, iTrue); | ||
1481 | return iFalse; | ||
1482 | } | ||
1483 | else if ((isCommand_UserEvent(ev, "copy") || isCommand_UserEvent(ev, "input.copy")) && | ||
1484 | isEditing_InputWidget_(d)) { | ||
1485 | copy_InputWidget_(d, argLabel_Command(command_UserEvent(ev), "cut")); | ||
1486 | return iTrue; | ||
1487 | } | ||
1488 | else if (isCommand_UserEvent(ev, "input.paste") && isEditing_InputWidget_(d)) { | ||
1489 | paste_InputWidget_(d); | ||
1490 | return iTrue; | ||
1491 | } | ||
1492 | else if (isCommand_UserEvent(ev, "theme.changed")) { | ||
1493 | if (d->buffered) { | ||
1494 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | ||
1495 | } | ||
1496 | return iFalse; | ||
1497 | } | ||
1498 | else if (isCommand_UserEvent(ev, "keyboard.changed")) { | ||
1499 | if (isFocused_Widget(d) && arg_Command(command_UserEvent(ev))) { | ||
1500 | iRect rect = bounds_Widget(w); | ||
1501 | rect.pos.y -= value_Anim(&get_Window()->rootOffset); | ||
1502 | const iInt2 visRoot = visibleSize_Root(w->root); | ||
1503 | if (bottom_Rect(rect) > visRoot.y) { | ||
1504 | setValue_Anim(&get_Window()->rootOffset, -(bottom_Rect(rect) - visRoot.y), 250); | ||
1505 | } | ||
1506 | } | ||
1507 | return iFalse; | ||
1508 | } | ||
1509 | else if (isCommand_UserEvent(ev, "text.insert")) { | ||
1510 | pushUndo_InputWidget_(d); | ||
1511 | deleteMarked_InputWidget_(d); | ||
1512 | insertChar_InputWidget_(d, arg_Command(command_UserEvent(ev))); | ||
1513 | contentsWereChanged_InputWidget_(d); | ||
1514 | return iTrue; | ||
1515 | } | ||
1516 | else if (isCommand_Widget(w, ev, "input.backup")) { | ||
1517 | if (d->inFlags & needBackup_InputWidgetFlag) { | ||
1518 | saveBackup_InputWidget_(d); | ||
1519 | } | ||
1520 | return iTrue; | ||
1521 | } | ||
1522 | else if (isMetricsChange_UserEvent(ev)) { | ||
1523 | updateMetrics_InputWidget_(d); | ||
1524 | // updateLinesAndResize_InputWidget_(d); | ||
1525 | } | 1480 | } |
1526 | else if (isFocused_Widget(d) && isCommand_UserEvent(ev, "copy")) { | 1481 | else { |
1527 | copy_InputWidget_(d, iFalse); | 1482 | openMenuFlags_Widget(clipMenu, coord, iFalse); |
1528 | return iTrue; | ||
1529 | } | 1483 | } |
1484 | } | ||
1485 | |||
1486 | static enum iEventResult processPointerEvents_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | ||
1487 | iWidget *w = as_Widget(d); | ||
1530 | if (ev->type == SDL_MOUSEMOTION && (isHover_Widget(d) || flags_Widget(w) & keepOnTop_WidgetFlag)) { | 1488 | if (ev->type == SDL_MOUSEMOTION && (isHover_Widget(d) || flags_Widget(w) & keepOnTop_WidgetFlag)) { |
1531 | const iInt2 coord = init_I2(ev->motion.x, ev->motion.y); | 1489 | const iInt2 coord = init_I2(ev->motion.x, ev->motion.y); |
1532 | const iInt2 inner = windowToInner_Widget(w, coord); | 1490 | const iInt2 inner = windowToInner_Widget(w, coord); |
@@ -1559,10 +1517,15 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1559 | d->visWrapLines.start += lineDelta; | 1517 | d->visWrapLines.start += lineDelta; |
1560 | d->visWrapLines.end += lineDelta; | 1518 | d->visWrapLines.end += lineDelta; |
1561 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | 1519 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; |
1562 | refresh_Widget(d); | 1520 | refresh_Widget(d); |
1563 | return iTrue; | 1521 | return true_EventResult; |
1564 | } | 1522 | } |
1565 | return iFalse; | 1523 | return false_EventResult; |
1524 | } | ||
1525 | if (ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT && | ||
1526 | contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { | ||
1527 | showClipMenu_(mouseCoord_Window(get_Window(), ev->button.which)); | ||
1528 | return iTrue; | ||
1566 | } | 1529 | } |
1567 | switch (processEvent_Click(&d->click, ev)) { | 1530 | switch (processEvent_Click(&d->click, ev)) { |
1568 | case none_ClickResult: | 1531 | case none_ClickResult: |
@@ -1584,10 +1547,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1584 | d->inFlags &= ~(isMarking_InputWidgetFlag | markWords_InputWidgetFlag); | 1547 | d->inFlags &= ~(isMarking_InputWidgetFlag | markWords_InputWidgetFlag); |
1585 | if (d->click.count == 2) { | 1548 | if (d->click.count == 2) { |
1586 | d->inFlags |= isMarking_InputWidgetFlag | markWords_InputWidgetFlag; | 1549 | d->inFlags |= isMarking_InputWidgetFlag | markWords_InputWidgetFlag; |
1587 | d->mark.start = d->mark.end = cursorToIndex_InputWidget_(d, d->cursor); | 1550 | markWordAtCursor_InputWidget_(d); |
1588 | extendRange_InputWidget_(d, &d->mark.start, -1); | ||
1589 | extendRange_InputWidget_(d, &d->mark.end, +1); | ||
1590 | d->initialMark = d->mark; | ||
1591 | refresh_Widget(w); | 1551 | refresh_Widget(w); |
1592 | } | 1552 | } |
1593 | if (d->click.count == 3) { | 1553 | if (d->click.count == 3) { |
@@ -1595,11 +1555,11 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1595 | } | 1555 | } |
1596 | } | 1556 | } |
1597 | refresh_Widget(d); | 1557 | refresh_Widget(d); |
1598 | return iTrue; | 1558 | return true_EventResult; |
1599 | } | 1559 | } |
1600 | case aborted_ClickResult: | 1560 | case aborted_ClickResult: |
1601 | d->inFlags &= ~isMarking_InputWidgetFlag; | 1561 | d->inFlags &= ~isMarking_InputWidgetFlag; |
1602 | return iTrue; | 1562 | return true_EventResult; |
1603 | case drag_ClickResult: | 1563 | case drag_ClickResult: |
1604 | d->cursor = coordCursor_InputWidget_(d, pos_Click(&d->click)); | 1564 | d->cursor = coordCursor_InputWidget_(d, pos_Click(&d->click)); |
1605 | showCursor_InputWidget_(d); | 1565 | showCursor_InputWidget_(d); |
@@ -1614,30 +1574,374 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1614 | d->mark.start = isFwd ? d->initialMark.start : d->initialMark.end; | 1574 | d->mark.start = isFwd ? d->initialMark.start : d->initialMark.end; |
1615 | } | 1575 | } |
1616 | refresh_Widget(w); | 1576 | refresh_Widget(w); |
1617 | return iTrue; | 1577 | return true_EventResult; |
1618 | case finished_ClickResult: | 1578 | case finished_ClickResult: |
1619 | d->inFlags &= ~isMarking_InputWidgetFlag; | 1579 | d->inFlags &= ~isMarking_InputWidgetFlag; |
1620 | return iTrue; | 1580 | return true_EventResult; |
1621 | } | 1581 | } |
1622 | if (ev->type == SDL_MOUSEMOTION && flags_Widget(w) & keepOnTop_WidgetFlag) { | 1582 | if (ev->type == SDL_MOUSEMOTION && flags_Widget(w) & keepOnTop_WidgetFlag) { |
1623 | const iInt2 coord = init_I2(ev->motion.x, ev->motion.y); | 1583 | const iInt2 coord = init_I2(ev->motion.x, ev->motion.y); |
1624 | if (contains_Click(&d->click, coord)) { | 1584 | if (contains_Click(&d->click, coord)) { |
1625 | return iTrue; | 1585 | return true_EventResult; |
1626 | } | 1586 | } |
1627 | } | 1587 | } |
1628 | if (ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT && | 1588 | return ignored_EventResult; |
1629 | contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { | 1589 | } |
1630 | iWidget *clipMenu = findWidget_App("clipmenu"); | 1590 | |
1631 | if (isVisible_Widget(clipMenu)) { | 1591 | static iInt2 touchCoordCursor_InputWidget_(const iInputWidget *d, iInt2 coord) { |
1632 | closeMenu_Widget(clipMenu); | 1592 | /* Clamp to the bounds so the cursor doesn't wrap at the ends. */ |
1593 | iRect bounds = shrunk_Rect(contentBounds_InputWidget_(d), one_I2()); | ||
1594 | bounds.size.y = iMini(numWrapLines_InputWidget_(d), d->maxWrapLines) * lineHeight_Text(d->font) - 2; | ||
1595 | return coordCursor_InputWidget_(d, min_I2(bottomRight_Rect(bounds), | ||
1596 | max_I2(coord, topLeft_Rect(bounds)))); | ||
1597 | } | ||
1598 | |||
1599 | static iBool isInsideMark_InputWidget_(const iInputWidget *d, size_t pos) { | ||
1600 | const iRanges mark = mark_InputWidget_(d); | ||
1601 | return contains_Range(&mark, pos); | ||
1602 | } | ||
1603 | |||
1604 | 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); | ||
1606 | const iInt2 b = sub_I2(uiCoord, topLeft_Rect(contentBounds_InputWidget_(d))); | ||
1607 | return dist_I2(a, b); | ||
1608 | } | ||
1609 | |||
1610 | static enum iEventResult processTouchEvents_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | ||
1611 | iWidget *w = as_Widget(d); | ||
1612 | /* | ||
1613 | + first tap to focus & select all/place cursor | ||
1614 | + focused tap to place cursor | ||
1615 | - drag cursor to move it | ||
1616 | - double-click to select a word | ||
1617 | - drag to move selection handles | ||
1618 | - long-press for context menu: copy, paste, delete, select all, deselect | ||
1619 | - double-click and hold to select words | ||
1620 | - triple-click to select all | ||
1621 | - drag/wheel elsewhere to scroll (contents or overflow), no change in focus | ||
1622 | */ | ||
1623 | // if (ev->type != SDL_MOUSEBUTTONUP && ev->type != SDL_MOUSEBUTTONDOWN && | ||
1624 | // ev->type != SDL_MOUSEWHEEL && ev->type != SDL_MOUSEMOTION && | ||
1625 | // !(ev->type == SDL_USEREVENT && ev->user.code == widgetTapBegins_UserEventCode) && | ||
1626 | // !(ev->type == SDL_USEREVENT && ev->user.code == widgetTouchEnds_UserEventCode)) { | ||
1627 | // return ignored_EventResult; | ||
1628 | // } | ||
1629 | if (isFocused_Widget(w)) { | ||
1630 | if (ev->type == SDL_USEREVENT && ev->user.code == widgetTapBegins_UserEventCode) { | ||
1631 | d->lastTapTime = d->tapStartTime; | ||
1632 | d->tapStartTime = SDL_GetTicks(); | ||
1633 | const int tapDist = dist_I2(latestPosition_Touch(), d->lastTapPos); | ||
1634 | d->lastTapPos = latestPosition_Touch(); | ||
1635 | printf("[%p] tap start time: %u (%u) %d\n", w, d->tapStartTime, d->tapStartTime - d->lastTapTime, tapDist); | ||
1636 | if (d->tapStartTime - d->lastTapTime < 400 && tapDist < gap_UI * 4) { | ||
1637 | d->tapCount++; | ||
1638 | printf("[%p] >> tap count: %d\n", w, d->tapCount); | ||
1639 | } | ||
1640 | else { | ||
1641 | d->tapCount = 0; | ||
1642 | } | ||
1643 | if (!isEmpty_Range(&d->mark)) { | ||
1644 | const int dist[2] = { | ||
1645 | distanceToPos_InputWidget_(d, latestPosition_Touch(), | ||
1646 | indexToCursor_InputWidget_(d, d->mark.start)), | ||
1647 | distanceToPos_InputWidget_(d, latestPosition_Touch(), | ||
1648 | indexToCursor_InputWidget_(d, d->mark.end)) | ||
1649 | }; | ||
1650 | if (dist[0] < dist[1]) { | ||
1651 | printf("[%p] begin marker start drag\n", w); | ||
1652 | d->inFlags |= dragMarkerStart_InputWidgetFlag; | ||
1653 | } | ||
1654 | else { | ||
1655 | printf("[%p] begin marker end drag\n", w); | ||
1656 | d->inFlags |= dragMarkerEnd_InputWidgetFlag; | ||
1657 | } | ||
1658 | d->inFlags |= isMarking_InputWidgetFlag; | ||
1659 | setFlags_Widget(w, touchDrag_WidgetFlag, iTrue); | ||
1660 | } | ||
1661 | else { | ||
1662 | const int dist = distanceToPos_InputWidget_(d, latestPosition_Touch(), d->cursor); | ||
1663 | printf("[%p] tap dist: %d\n", w, dist); | ||
1664 | if (dist < gap_UI * 10) { | ||
1665 | printf("[%p] begin cursor drag\n", w); | ||
1666 | setFlags_Widget(w, touchDrag_WidgetFlag, iTrue); | ||
1667 | d->inFlags |= dragCursor_InputWidgetFlag; | ||
1668 | // d->inFlags |= touchBehavior_InputWidgetFlag; | ||
1669 | // setMouseGrab_Widget(w); | ||
1670 | // return iTrue; | ||
1671 | } | ||
1672 | } | ||
1673 | // if (~d->inFlags & selectAllOnFocus_InputWidgetFlag) { | ||
1674 | // d->cursor = coordCursor_InputWidget_(d, pos_Click(&d->click)); | ||
1675 | // showCursor_InputWidget_(d); | ||
1676 | // } | ||
1677 | return true_EventResult; | ||
1678 | } | ||
1679 | } | ||
1680 | #if 0 | ||
1681 | else if (isFocused_Widget(w)) { | ||
1682 | if (ev->type == SDL_MOUSEMOTION) { | ||
1683 | if (~d->inFlags & touchBehavior_InputWidgetFlag) { | ||
1684 | const iInt2 curPos = relativeCursorCoord_InputWidget_(d); | ||
1685 | const iInt2 relClick = sub_I2(pos_Click(&d->click), | ||
1686 | topLeft_Rect(contentBounds_InputWidget_(d))); | ||
1687 | if (dist_I2(curPos, relClick) < gap_UI * 8) { | ||
1688 | printf("tap on cursor!\n"); | ||
1689 | setFlags_Widget(w, touchDrag_WidgetFlag, iTrue); | ||
1690 | d->inFlags |= touchBehavior_InputWidgetFlag; | ||
1691 | printf("[Input] begin cursor drag\n"); | ||
1692 | setMouseGrab_Widget(w); | ||
1693 | return iTrue; | ||
1694 | } | ||
1695 | } | ||
1696 | else if (ev->motion.x > 0 && ev->motion.y > 0) { | ||
1697 | printf("[Input] cursor being dragged\n"); | ||
1698 | iRect bounds = shrunk_Rect(contentBounds_InputWidget_(d), one_I2()); | ||
1699 | bounds.size.y = iMini(numWrapLines_InputWidget_(d), d->maxWrapLines) * lineHeight_Text(d->font) - 2; | ||
1700 | iInt2 mpos = init_I2(ev->motion.x, ev->motion.y); | ||
1701 | mpos = min_I2(bottomRight_Rect(bounds), max_I2(mpos, topLeft_Rect(bounds))); | ||
1702 | d->cursor = coordCursor_InputWidget_(d, mpos); | ||
1703 | showCursor_InputWidget_(d); | ||
1704 | refresh_Widget(w); | ||
1705 | return iTrue; | ||
1706 | } | ||
1633 | } | 1707 | } |
1634 | else { | 1708 | if (d->inFlags & touchBehavior_InputWidgetFlag) { |
1635 | openMenuFlags_Widget(clipMenu, | 1709 | if (ev->type == SDL_MOUSEBUTTONUP || |
1636 | mouseCoord_Window(get_Window(), ev->button.which), | 1710 | (ev->type == SDL_USEREVENT && ev->user.code == widgetTouchEnds_UserEventCode)) { |
1637 | iFalse); | 1711 | d->inFlags &= ~touchBehavior_InputWidgetFlag; |
1712 | setFlags_Widget(w, touchDrag_WidgetFlag, iFalse); | ||
1713 | setMouseGrab_Widget(NULL); | ||
1714 | printf("[Input] touch ends\n"); | ||
1715 | return iFalse; | ||
1716 | } | ||
1717 | } | ||
1718 | } | ||
1719 | #endif | ||
1720 | #if 1 | ||
1721 | if ((ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) && | ||
1722 | ev->button.button == SDL_BUTTON_RIGHT && contains_Widget(w, latestPosition_Touch())) { | ||
1723 | if (ev->type == SDL_MOUSEBUTTONDOWN) { | ||
1724 | /*if (isFocused_Widget(w)) { | ||
1725 | d->inFlags |= isMarking_InputWidgetFlag; | ||
1726 | d->cursor = touchCoordCursor_InputWidget_(d, latestPosition_Touch()); | ||
1727 | markWordAtCursor_InputWidget_(d); | ||
1728 | refresh_Widget(d); | ||
1729 | return true_EventResult; | ||
1730 | }*/ | ||
1731 | setFocus_Widget(w); | ||
1732 | d->inFlags |= isMarking_InputWidgetFlag; | ||
1733 | d->cursor = touchCoordCursor_InputWidget_(d, latestPosition_Touch()); | ||
1734 | markWordAtCursor_InputWidget_(d); | ||
1735 | d->cursor = indexToCursor_InputWidget_(d, d->mark.end); | ||
1736 | refresh_Widget(d); | ||
1737 | } | ||
1738 | return true_EventResult; | ||
1739 | } | ||
1740 | switch (processEvent_Click(&d->click, ev)) { | ||
1741 | case none_ClickResult: | ||
1742 | break; | ||
1743 | case started_ClickResult: { | ||
1744 | printf("[%p] started\n", w); | ||
1745 | /* | ||
1746 | const iInt2 curPos = relativeCursorCoord_InputWidget_(d); | ||
1747 | const iInt2 relClick = sub_I2(pos_Click(&d->click), | ||
1748 | topLeft_Rect(contentBounds_InputWidget_(d))); | ||
1749 | if (dist_I2(curPos, relClick) < gap_UI * 8) { | ||
1750 | printf("tap on cursor!\n"); | ||
1751 | setFlags_Widget(w, touchDrag_WidgetFlag, iTrue); | ||
1752 | } | ||
1753 | else { | ||
1754 | printf("tap elsewhere\n"); | ||
1755 | }*/ | ||
1756 | return true_EventResult; | ||
1757 | } | ||
1758 | case drag_ClickResult: | ||
1759 | printf("[%p] drag %d,%d\n", w, pos_Click(&d->click).x, pos_Click(&d->click).y); | ||
1760 | if (d->inFlags & dragCursor_InputWidgetFlag) { | ||
1761 | iZap(d->mark); | ||
1762 | d->cursor = touchCoordCursor_InputWidget_(d, pos_Click(&d->click)); | ||
1763 | showCursor_InputWidget_(d); | ||
1764 | refresh_Widget(w); | ||
1765 | } | ||
1766 | else if (d->inFlags & dragMarkerStart_InputWidgetFlag) { | ||
1767 | d->mark.start = cursorToIndex_InputWidget_(d, touchCoordCursor_InputWidget_(d, pos_Click(&d->click))); | ||
1768 | refresh_Widget(w); | ||
1769 | } | ||
1770 | else if (d->inFlags & dragMarkerEnd_InputWidgetFlag) { | ||
1771 | d->mark.end = cursorToIndex_InputWidget_(d, touchCoordCursor_InputWidget_(d, pos_Click(&d->click))); | ||
1772 | refresh_Widget(w); | ||
1773 | } | ||
1774 | return true_EventResult; | ||
1775 | // printf("[%p] aborted\n", w); | ||
1776 | // d->inFlags &= ~touchBehavior_InputWidgetFlag; | ||
1777 | // setFlags_Widget(w, touchDrag_WidgetFlag, iFalse); | ||
1778 | // return true_EventResult; | ||
1779 | case finished_ClickResult: | ||
1780 | case aborted_ClickResult: | ||
1781 | printf("[%p] ended\n", w); | ||
1782 | uint32_t tapElapsed = SDL_GetTicks() - d->tapStartTime; | ||
1783 | printf("tapElapsed: %u\n", tapElapsed); | ||
1784 | if (!isFocused_Widget(w)) { | ||
1785 | setFocus_Widget(w); | ||
1786 | d->lastTapPos = latestPosition_Touch(); | ||
1787 | d->tapStartTime = SDL_GetTicks(); | ||
1788 | d->tapCount = 0; | ||
1789 | d->cursor = touchCoordCursor_InputWidget_(d, pos_Click(&d->click)); | ||
1790 | showCursor_InputWidget_(d); | ||
1791 | } | ||
1792 | else if (!isEmpty_Range(&d->mark) && !isMoved_Click(&d->click)) { | ||
1793 | if (isInsideMark_InputWidget_(d, cursorToIndex_InputWidget_(d, touchCoordCursor_InputWidget_(d, latestPosition_Touch())))) { | ||
1794 | showClipMenu_(latestPosition_Touch()); | ||
1795 | } | ||
1796 | else { | ||
1797 | iZap(d->mark); | ||
1798 | d->cursor = touchCoordCursor_InputWidget_(d, pos_Click(&d->click)); | ||
1799 | } | ||
1800 | } | ||
1801 | else if (SDL_GetTicks() - d->lastTapTime > 1000 && | ||
1802 | d->tapCount == 0 && isEmpty_Range(&d->mark) && !isMoved_Click(&d->click) && | ||
1803 | distanceToPos_InputWidget_(d, latestPosition_Touch(), d->cursor) < gap_UI * 5) { | ||
1804 | showClipMenu_(latestPosition_Touch()); | ||
1805 | } | ||
1806 | else { | ||
1807 | if (~d->inFlags & isMarking_InputWidgetFlag) { | ||
1808 | iZap(d->mark); | ||
1809 | d->cursor = touchCoordCursor_InputWidget_(d, pos_Click(&d->click)); | ||
1810 | } | ||
1811 | } | ||
1812 | if (d->inFlags & (dragCursor_InputWidgetFlag | dragMarkerStart_InputWidgetFlag | | ||
1813 | dragMarkerEnd_InputWidgetFlag)) { | ||
1814 | printf("[%p] finished cursor/marker drag\n", w); | ||
1815 | d->inFlags &= ~(dragCursor_InputWidgetFlag | | ||
1816 | dragMarkerStart_InputWidgetFlag | | ||
1817 | dragMarkerEnd_InputWidgetFlag); | ||
1818 | setFlags_Widget(w, touchDrag_WidgetFlag, iFalse); | ||
1819 | } | ||
1820 | d->inFlags &= ~isMarking_InputWidgetFlag; | ||
1821 | showCursor_InputWidget_(d); | ||
1822 | refresh_Widget(w); | ||
1823 | #if 0 | ||
1824 | d->inFlags &= ~touchBehavior_InputWidgetFlag; | ||
1825 | if (flags_Widget(w) & touchDrag_WidgetFlag) { | ||
1826 | setFlags_Widget(w, touchDrag_WidgetFlag, iFalse); | ||
1827 | return true_EventResult; | ||
1828 | } | ||
1829 | if (!isMoved_Click(&d->click)) { | ||
1830 | if (!isFocused_Widget(w)) { | ||
1831 | setFocus_Widget(w); | ||
1832 | if (~d->inFlags & selectAllOnFocus_InputWidgetFlag) { | ||
1833 | d->cursor = coordCursor_InputWidget_(d, pos_Click(&d->click)); | ||
1834 | showCursor_InputWidget_(d); | ||
1835 | } | ||
1836 | } | ||
1837 | else { | ||
1838 | iZap(d->mark); | ||
1839 | d->cursor = coordCursor_InputWidget_(d, pos_Click(&d->click)); | ||
1840 | showCursor_InputWidget_(d); | ||
1841 | } | ||
1842 | } | ||
1843 | #endif | ||
1844 | return true_EventResult; | ||
1845 | } | ||
1846 | #endif | ||
1847 | // if ((ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) && | ||
1848 | // contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { | ||
1849 | // /* Eat all mouse clicks on the widget. */ | ||
1850 | // return true_EventResult; | ||
1851 | // } | ||
1852 | return ignored_EventResult; | ||
1853 | } | ||
1854 | |||
1855 | static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | ||
1856 | iWidget *w = as_Widget(d); | ||
1857 | /* Resize according to width immediately. */ | ||
1858 | if (d->lastUpdateWidth != w->rect.size.x) { | ||
1859 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | ||
1860 | if (d->inFlags & isUrl_InputWidgetFlag) { | ||
1861 | /* Restore/omit the default scheme if necessary. */ | ||
1862 | setText_InputWidget(d, text_InputWidget(d)); | ||
1638 | } | 1863 | } |
1864 | updateAllLinesAndResizeHeight_InputWidget_(d); | ||
1865 | d->lastUpdateWidth = w->rect.size.x; | ||
1866 | } | ||
1867 | if (isCommand_Widget(w, ev, "focus.gained")) { | ||
1868 | begin_InputWidget(d); | ||
1869 | return iFalse; | ||
1870 | } | ||
1871 | else if (isEditing_InputWidget_(d) && (isCommand_UserEvent(ev, "window.focus.lost") || | ||
1872 | isCommand_UserEvent(ev, "window.focus.gained"))) { | ||
1873 | startOrStopCursorTimer_InputWidget_(d, isCommand_UserEvent(ev, "window.focus.gained")); | ||
1874 | d->cursorVis = 1; | ||
1875 | refresh_Widget(d); | ||
1876 | return iFalse; | ||
1877 | } | ||
1878 | else if (isCommand_UserEvent(ev, "keyroot.changed")) { | ||
1879 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | ||
1880 | } | ||
1881 | else if (isCommand_UserEvent(ev, "lang.changed")) { | ||
1882 | set_String(&d->hint, &d->srcHint); | ||
1883 | translate_Lang(&d->hint); | ||
1884 | return iFalse; | ||
1885 | } | ||
1886 | else if (isCommand_Widget(w, ev, "focus.lost")) { | ||
1887 | end_InputWidget(d, iTrue); | ||
1888 | return iFalse; | ||
1889 | } | ||
1890 | else if ((isCommand_UserEvent(ev, "copy") || isCommand_UserEvent(ev, "input.copy")) && | ||
1891 | isEditing_InputWidget_(d)) { | ||
1892 | copy_InputWidget_(d, argLabel_Command(command_UserEvent(ev), "cut")); | ||
1893 | return iTrue; | ||
1894 | } | ||
1895 | else if (isCommand_UserEvent(ev, "input.paste") && isEditing_InputWidget_(d)) { | ||
1896 | paste_InputWidget_(d); | ||
1639 | return iTrue; | 1897 | return iTrue; |
1640 | } | 1898 | } |
1899 | else if (isCommand_UserEvent(ev, "theme.changed")) { | ||
1900 | if (d->buffered) { | ||
1901 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | ||
1902 | } | ||
1903 | return iFalse; | ||
1904 | } | ||
1905 | // else if (isCommand_UserEvent(ev, "keyboard.changed")) { | ||
1906 | // if (isFocused_Widget(d) && arg_Command(command_UserEvent(ev))) { | ||
1907 | // iRect rect = bounds_Widget(w); | ||
1908 | // rect.pos.y -= value_Anim(&get_Window()->rootOffset); | ||
1909 | // const iInt2 visRoot = visibleSize_Root(w->root); | ||
1910 | // if (bottom_Rect(rect) > visRoot.y) { | ||
1911 | // setValue_Anim(&get_Window()->rootOffset, -(bottom_Rect(rect) - visRoot.y), 250); | ||
1912 | // } | ||
1913 | // } | ||
1914 | // return iFalse; | ||
1915 | // } | ||
1916 | else if (isCommand_UserEvent(ev, "text.insert")) { | ||
1917 | pushUndo_InputWidget_(d); | ||
1918 | deleteMarked_InputWidget_(d); | ||
1919 | insertChar_InputWidget_(d, arg_Command(command_UserEvent(ev))); | ||
1920 | contentsWereChanged_InputWidget_(d); | ||
1921 | return iTrue; | ||
1922 | } | ||
1923 | else if (isCommand_Widget(w, ev, "input.backup")) { | ||
1924 | if (d->inFlags & needBackup_InputWidgetFlag) { | ||
1925 | saveBackup_InputWidget_(d); | ||
1926 | } | ||
1927 | return iTrue; | ||
1928 | } | ||
1929 | else if (isMetricsChange_UserEvent(ev)) { | ||
1930 | updateMetrics_InputWidget_(d); | ||
1931 | // updateLinesAndResize_InputWidget_(d); | ||
1932 | } | ||
1933 | else if (isFocused_Widget(d) && isCommand_UserEvent(ev, "copy")) { | ||
1934 | copy_InputWidget_(d, iFalse); | ||
1935 | return iTrue; | ||
1936 | } | ||
1937 | /* Click behavior depends on device type. */ { | ||
1938 | const int mbResult = (deviceType_App() == desktop_AppDeviceType | ||
1939 | ? processPointerEvents_InputWidget_(d, ev) | ||
1940 | : processTouchEvents_InputWidget_(d, ev)); | ||
1941 | if (mbResult) { | ||
1942 | return mbResult >> 1; | ||
1943 | } | ||
1944 | } | ||
1641 | if (ev->type == SDL_KEYUP && isFocused_Widget(w)) { | 1945 | if (ev->type == SDL_KEYUP && isFocused_Widget(w)) { |
1642 | return iTrue; | 1946 | return iTrue; |
1643 | } | 1947 | } |
@@ -1884,6 +2188,8 @@ struct Impl_MarkPainter { | |||
1884 | const iInputLine * line; | 2188 | const iInputLine * line; |
1885 | iInt2 pos; | 2189 | iInt2 pos; |
1886 | iRanges mark; | 2190 | iRanges mark; |
2191 | iRect firstMarkRect; | ||
2192 | iRect lastMarkRect; | ||
1887 | }; | 2193 | }; |
1888 | 2194 | ||
1889 | static iBool draw_MarkPainter_(iWrapText *wrapText, iRangecc wrappedText, int origin, int advance, | 2195 | static iBool draw_MarkPainter_(iWrapText *wrapText, iRangecc wrappedText, int origin, int advance, |
@@ -1922,7 +2228,11 @@ static iBool draw_MarkPainter_(iWrapText *wrapText, iRangecc wrappedText, int or | |||
1922 | } | 2228 | } |
1923 | rect.size.x = iMax(gap_UI / 3, rect.size.x); | 2229 | rect.size.x = iMax(gap_UI / 3, rect.size.x); |
1924 | mp->pos.y += lineHeight_Text(mp->d->font); | 2230 | mp->pos.y += lineHeight_Text(mp->d->font); |
1925 | fillRect_Paint(mp->paint, rect, uiMarked_ColorId); | 2231 | fillRect_Paint(mp->paint, rect, uiMarked_ColorId | opaque_ColorId); |
2232 | if (deviceType_App() != desktop_AppDeviceType) { | ||
2233 | if (isEmpty_Rect(mp->firstMarkRect)) mp->firstMarkRect = rect; | ||
2234 | mp->lastMarkRect = rect; | ||
2235 | } | ||
1926 | return iTrue; | 2236 | return iTrue; |
1927 | } | 2237 | } |
1928 | 2238 | ||
@@ -1962,6 +2272,7 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1962 | }; | 2272 | }; |
1963 | const iRangei visLines = visibleLineRange_InputWidget_(d); | 2273 | const iRangei visLines = visibleLineRange_InputWidget_(d); |
1964 | const int visLineOffsetY = visLineOffsetY_InputWidget_(d); | 2274 | const int visLineOffsetY = visLineOffsetY_InputWidget_(d); |
2275 | iRect markerRects[2]; | ||
1965 | /* If buffered, just draw the buffered copy. */ | 2276 | /* If buffered, just draw the buffered copy. */ |
1966 | if (d->buffered && !isFocused) { | 2277 | if (d->buffered && !isFocused) { |
1967 | /* Most input widgets will use this, since only one is focused at a time. */ | 2278 | /* Most input widgets will use this, since only one is focused at a time. */ |
@@ -1977,7 +2288,7 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1977 | .paint = &p, | 2288 | .paint = &p, |
1978 | .d = d, | 2289 | .d = d, |
1979 | .contentBounds = contentBounds, | 2290 | .contentBounds = contentBounds, |
1980 | .mark = mark_InputWidget_(d) | 2291 | .mark = mark_InputWidget_(d), |
1981 | }; | 2292 | }; |
1982 | wrapText.context = ▮ | 2293 | wrapText.context = ▮ |
1983 | wrapText.wrapFunc = isFocused ? draw_MarkPainter_ : NULL; /* mark is drawn under each line of text */ | 2294 | wrapText.wrapFunc = isFocused ? draw_MarkPainter_ : NULL; /* mark is drawn under each line of text */ |
@@ -1988,11 +2299,14 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1988 | marker.pos = drawPos; | 2299 | marker.pos = drawPos; |
1989 | addv_I2(&drawPos, draw_WrapText(&wrapText, d->font, drawPos, fg).advance); /* lines end with \n */ | 2300 | addv_I2(&drawPos, draw_WrapText(&wrapText, d->font, drawPos, fg).advance); /* lines end with \n */ |
1990 | } | 2301 | } |
2302 | markerRects[0] = marker.firstMarkRect; | ||
2303 | markerRects[1] = marker.lastMarkRect; | ||
1991 | wrapText.wrapFunc = NULL; | 2304 | wrapText.wrapFunc = NULL; |
1992 | wrapText.context = NULL; | 2305 | wrapText.context = NULL; |
1993 | } | 2306 | } |
1994 | /* Draw the insertion point. */ | 2307 | /* Draw the insertion point. */ |
1995 | if (isFocused && d->cursorVis && contains_Range(&visLines, d->cursor.y)) { | 2308 | if (isFocused && d->cursorVis && contains_Range(&visLines, d->cursor.y) && |
2309 | isEmpty_Range(&d->mark)) { | ||
1996 | iInt2 curSize; | 2310 | iInt2 curSize; |
1997 | iRangecc cursorChar = iNullRange; | 2311 | iRangecc cursorChar = iNullRange; |
1998 | int visWrapsAbove = 0; | 2312 | int visWrapsAbove = 0; |
@@ -2040,6 +2354,11 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
2040 | } | 2354 | } |
2041 | } | 2355 | } |
2042 | unsetClip_Paint(&p); | 2356 | unsetClip_Paint(&p); |
2357 | if (!isEmpty_Rect(markerRects[0])) { | ||
2358 | for (int i = 0; i < 2; ++i) { | ||
2359 | drawPin_Paint(&p, markerRects[i], i, uiTextCaution_ColorId); | ||
2360 | } | ||
2361 | } | ||
2043 | drawChildren_Widget(w); | 2362 | drawChildren_Widget(w); |
2044 | } | 2363 | } |
2045 | 2364 | ||
diff --git a/src/ui/paint.c b/src/ui/paint.c index 71ebb81d..89de47d4 100644 --- a/src/ui/paint.c +++ b/src/ui/paint.c | |||
@@ -33,7 +33,8 @@ iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) { | |||
33 | 33 | ||
34 | static void setColor_Paint_(const iPaint *d, int color) { | 34 | static void setColor_Paint_(const iPaint *d, int color) { |
35 | const iColor clr = get_Color(color & mask_ColorId); | 35 | const iColor clr = get_Color(color & mask_ColorId); |
36 | SDL_SetRenderDrawColor(renderer_Paint_(d), clr.r, clr.g, clr.b, clr.a * d->alpha / 255); | 36 | SDL_SetRenderDrawColor(renderer_Paint_(d), clr.r, clr.g, clr.b, |
37 | (color & opaque_ColorId ? 255 : clr.a) * d->alpha / 255); | ||
37 | } | 38 | } |
38 | 39 | ||
39 | void init_Paint(iPaint *d) { | 40 | void init_Paint(iPaint *d) { |
@@ -186,6 +187,22 @@ void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t n, int color) | |||
186 | free(offsetPoints); | 187 | free(offsetPoints); |
187 | } | 188 | } |
188 | 189 | ||
190 | void drawPin_Paint(iPaint *d, iRect rangeRect, int dir, int pinColor) { | ||
191 | const int height = height_Rect(rangeRect); | ||
192 | iRect pin; | ||
193 | if (dir == 0) { | ||
194 | pin = (iRect){ add_I2(topLeft_Rect(rangeRect), init_I2(-gap_UI / 4, -gap_UI)), | ||
195 | init_I2(gap_UI / 2, height + gap_UI) }; | ||
196 | } | ||
197 | else { | ||
198 | pin = (iRect){ addX_I2(topRight_Rect(rangeRect), -gap_UI / 4), | ||
199 | init_I2(gap_UI / 2, height + gap_UI) }; | ||
200 | } | ||
201 | fillRect_Paint(d, pin, pinColor); | ||
202 | fillRect_Paint(d, initCentered_Rect(dir == 0 ? topMid_Rect(pin) : bottomMid_Rect(pin), | ||
203 | init1_I2(gap_UI * 2)), pinColor); | ||
204 | } | ||
205 | |||
189 | iInt2 size_SDLTexture(SDL_Texture *d) { | 206 | iInt2 size_SDLTexture(SDL_Texture *d) { |
190 | iInt2 size; | 207 | iInt2 size; |
191 | SDL_QueryTexture(d, NULL, NULL, &size.x, &size.y); | 208 | SDL_QueryTexture(d, NULL, NULL, &size.x, &size.y); |
diff --git a/src/ui/paint.h b/src/ui/paint.h index e6701635..e894b62f 100644 --- a/src/ui/paint.h +++ b/src/ui/paint.h | |||
@@ -63,4 +63,6 @@ iLocalDef void drawVLine_Paint(const iPaint *d, iInt2 pos, int len, int color) { | |||
63 | drawLine_Paint(d, pos, addY_I2(pos, len), color); | 63 | drawLine_Paint(d, pos, addY_I2(pos, len), color); |
64 | } | 64 | } |
65 | 65 | ||
66 | void drawPin_Paint (iPaint *, iRect rangeRect, int dir, int pinColor); | ||
67 | |||
66 | iInt2 size_SDLTexture (SDL_Texture *); | 68 | iInt2 size_SDLTexture (SDL_Texture *); |
diff --git a/src/ui/root.c b/src/ui/root.c index 59f98aa4..91f9fbb3 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -1401,23 +1401,38 @@ void createUserInterface_Root(iRoot *d) { | |||
1401 | { "${menu.closetab.other}", 0, 0, "tabs.close toleft:1 toright:1" }, | 1401 | { "${menu.closetab.other}", 0, 0, "tabs.close toleft:1 toright:1" }, |
1402 | { barLeftArrow_Icon " ${menu.closetab.left}", 0, 0, "tabs.close toleft:1" }, | 1402 | { barLeftArrow_Icon " ${menu.closetab.left}", 0, 0, "tabs.close toleft:1" }, |
1403 | { barRightArrow_Icon " ${menu.closetab.right}", 0, 0, "tabs.close toright:1" }, | 1403 | { barRightArrow_Icon " ${menu.closetab.right}", 0, 0, "tabs.close toright:1" }, |
1404 | }, | 1404 | }, |
1405 | 6); | 1405 | 6); |
1406 | iWidget *barMenu = | 1406 | iWidget *barMenu = |
1407 | makeMenu_Widget(root, | 1407 | makeMenu_Widget(root, |
1408 | (iMenuItem[]){ | 1408 | (iMenuItem[]){ |
1409 | { leftHalf_Icon " ${menu.sidebar.left}", 0, 0, "sidebar.toggle" }, | 1409 | { leftHalf_Icon " ${menu.sidebar.left}", 0, 0, "sidebar.toggle" }, |
1410 | { rightHalf_Icon " ${menu.sidebar.right}", 0, 0, "sidebar2.toggle" }, | 1410 | { rightHalf_Icon " ${menu.sidebar.right}", 0, 0, "sidebar2.toggle" }, |
1411 | }, | 1411 | }, |
1412 | deviceType_App() == phone_AppDeviceType ? 1 : 2); | 1412 | deviceType_App() == phone_AppDeviceType ? 1 : 2); |
1413 | iWidget *clipMenu = makeMenu_Widget(root, | 1413 | iWidget *clipMenu = makeMenu_Widget(root, |
1414 | (iMenuItem[]){ | 1414 | #if defined (iPlatformMobile) |
1415 | { scissor_Icon " ${menu.cut}", 0, 0, "input.copy cut:1" }, | 1415 | (iMenuItem[]){ |
1416 | { clipCopy_Icon " ${menu.copy}", 0, 0, "input.copy" }, | 1416 | { ">>>" scissor_Icon " ${menu.cut}", 0, 0, "input.copy cut:1" }, |
1417 | { "---" }, | 1417 | { ">>>" clipCopy_Icon " ${menu.copy}", 0, 0, "input.copy" }, |
1418 | { clipboard_Icon " ${menu.paste}", 0, 0, "input.paste" }, | 1418 | { ">>>" clipboard_Icon " ${menu.paste}", 0, 0, "input.paste" }, |
1419 | }, | 1419 | { "---" }, |
1420 | 4); | 1420 | { ">>>" delete_Icon " " uiTextCaution_ColorEscape "${menu.delete}", 0, 0, "input.delete" }, |
1421 | { ">>>" select_Icon " ${menu.selectall}", 0, 0, "input.selectall" }, | ||
1422 | { ">>>" undo_Icon " ${menu.undo}", 0, 0, "input.undo" }, | ||
1423 | }, 7); | ||
1424 | #else | ||
1425 | (iMenuItem[]){ | ||
1426 | { scissor_Icon " ${menu.cut}", 0, 0, "input.copy cut:1" }, | ||
1427 | { clipCopy_Icon " ${menu.copy}", 0, 0, "input.copy" }, | ||
1428 | { clipboard_Icon " ${menu.paste}", 0, 0, "input.paste" }, | ||
1429 | { "---" }, | ||
1430 | { delete_Icon " " uiTextCaution_ColorEscape "${menu.delete}", 0, 0, "input.delete" }, | ||
1431 | { undo_Icon " ${menu.undo}", 0, 0, "input.undo" }, | ||
1432 | { "---" }, | ||
1433 | { select_Icon " ${menu.selectall}", 0, 0, "input.selectall" }, | ||
1434 | }, 8); | ||
1435 | #endif | ||
1421 | iWidget *splitMenu = makeMenu_Widget(root, (iMenuItem[]){ | 1436 | iWidget *splitMenu = makeMenu_Widget(root, (iMenuItem[]){ |
1422 | { "${menu.split.merge}", '1', 0, "ui.split arg:0" }, | 1437 | { "${menu.split.merge}", '1', 0, "ui.split arg:0" }, |
1423 | { "${menu.split.swap}", SDLK_x, 0, "ui.split swap:1" }, | 1438 | { "${menu.split.swap}", SDLK_x, 0, "ui.split swap:1" }, |
diff --git a/src/ui/touch.c b/src/ui/touch.c index 5fc8f245..61882739 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -293,6 +293,7 @@ static void update_TouchState_(void *ptr) { | |||
293 | } | 293 | } |
294 | if (elapsed > 50 && !touch->isTapBegun) { | 294 | if (elapsed > 50 && !touch->isTapBegun) { |
295 | /* Looks like a possible tap. */ | 295 | /* Looks like a possible tap. */ |
296 | touchState_()->currentTouchPos = initF3_I2(touch->pos[0]); | ||
296 | dispatchNotification_Touch_(touch, widgetTapBegins_UserEventCode); | 297 | dispatchNotification_Touch_(touch, widgetTapBegins_UserEventCode); |
297 | dispatchMotion_Touch_(touch->pos[0], 0); | 298 | dispatchMotion_Touch_(touch->pos[0], 0); |
298 | refresh_Widget(touch->affinity); | 299 | refresh_Widget(touch->affinity); |
@@ -471,13 +472,13 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
471 | } | 472 | } |
472 | iTouchState *d = touchState_(); | 473 | iTouchState *d = touchState_(); |
473 | iWindow *window = get_Window(); | 474 | iWindow *window = get_Window(); |
474 | if (!isFinished_Anim(&window->rootOffset)) { | 475 | // if (!isFinished_Anim(&window->rootOffset)) { |
475 | return iFalse; | 476 | // return iFalse; |
476 | } | 477 | // } |
477 | const iInt2 rootSize = size_Window(window); | 478 | const iInt2 rootSize = size_Window(window); |
478 | const SDL_TouchFingerEvent *fing = &ev->tfinger; | 479 | const SDL_TouchFingerEvent *fing = &ev->tfinger; |
479 | const iFloat3 pos = add_F3(init_F3(fing->x * rootSize.x, fing->y * rootSize.y, 0), /* pixels */ | 480 | const iFloat3 pos = add_F3(init_F3(fing->x * rootSize.x, fing->y * rootSize.y, 0), /* pixels */ |
480 | init_F3(0, -value_Anim(&window->rootOffset), 0)); | 481 | init_F3(0, 0 /*-value_Anim(&window->rootOffset)*/, 0)); |
481 | const uint32_t nowTime = SDL_GetTicks(); | 482 | const uint32_t nowTime = SDL_GetTicks(); |
482 | if (ev->type == SDL_FINGERDOWN) { | 483 | if (ev->type == SDL_FINGERDOWN) { |
483 | /* Register the new touch. */ | 484 | /* Register the new touch. */ |
diff --git a/src/ui/util.c b/src/ui/util.c index 48ed41a6..cfa8152c 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -706,23 +706,36 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
706 | setFrameColor_Widget(menu, uiSeparator_ColorId); | 706 | setFrameColor_Widget(menu, uiSeparator_ColorId); |
707 | } | 707 | } |
708 | iBool haveIcons = iFalse; | 708 | iBool haveIcons = iFalse; |
709 | iWidget *horizGroup = NULL; | ||
709 | for (size_t i = 0; i < n; ++i) { | 710 | for (size_t i = 0; i < n; ++i) { |
710 | const iMenuItem *item = &items[i]; | 711 | const iMenuItem *item = &items[i]; |
711 | if (!item->label) { | 712 | if (!item->label) { |
712 | break; | 713 | break; |
713 | } | 714 | } |
714 | if (equal_CStr(item->label, "---")) { | 715 | const char *labelText = item->label; |
716 | if (!startsWith_CStr(labelText, ">>>")) { | ||
717 | horizGroup = NULL; | ||
718 | } | ||
719 | if (equal_CStr(labelText, "---")) { | ||
715 | addChild_Widget(menu, iClob(makeMenuSeparator_())); | 720 | addChild_Widget(menu, iClob(makeMenuSeparator_())); |
716 | } | 721 | } |
717 | else { | 722 | else { |
718 | iBool isInfo = iFalse; | 723 | iBool isInfo = iFalse; |
719 | const char *labelText = item->label; | 724 | if (startsWith_CStr(labelText, ">>>")) { |
725 | labelText += 3; | ||
726 | if (!horizGroup) { | ||
727 | horizGroup = makeHDiv_Widget(); | ||
728 | setFlags_Widget(horizGroup, resizeHeightOfChildren_WidgetFlag, iFalse); | ||
729 | setFlags_Widget(horizGroup, arrangeHeight_WidgetFlag, iTrue); | ||
730 | addChild_Widget(menu, iClob(horizGroup)); | ||
731 | } | ||
732 | } | ||
720 | if (startsWith_CStr(labelText, "```")) { | 733 | if (startsWith_CStr(labelText, "```")) { |
721 | labelText += 3; | 734 | labelText += 3; |
722 | isInfo = iTrue; | 735 | isInfo = iTrue; |
723 | } | 736 | } |
724 | iLabelWidget *label = addChildFlags_Widget( | 737 | iLabelWidget *label = addChildFlags_Widget( |
725 | menu, | 738 | horizGroup ? horizGroup : menu, |
726 | iClob(newKeyMods_LabelWidget(labelText, item->key, item->kmods, item->command)), | 739 | iClob(newKeyMods_LabelWidget(labelText, item->key, item->kmods, item->command)), |
727 | noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag | | 740 | noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag | |
728 | drawKey_WidgetFlag | itemFlags); | 741 | drawKey_WidgetFlag | itemFlags); |
@@ -766,6 +779,34 @@ void openMenu_Widget(iWidget *d, iInt2 windowCoord) { | |||
766 | openMenuFlags_Widget(d, windowCoord, iTrue); | 779 | openMenuFlags_Widget(d, windowCoord, iTrue); |
767 | } | 780 | } |
768 | 781 | ||
782 | static void updateMenuItemFonts_Widget_(iWidget *d) { | ||
783 | const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); | ||
784 | const iBool isSlidePanel = (flags_Widget(d) & horizontalOffset_WidgetFlag) != 0; | ||
785 | iForEach(ObjectList, i, children_Widget(d)) { | ||
786 | if (isInstance_Object(i.object, &Class_LabelWidget)) { | ||
787 | iLabelWidget *label = i.object; | ||
788 | const iBool isCaution = startsWith_String(text_LabelWidget(label), uiTextCaution_ColorEscape); | ||
789 | if (isWrapped_LabelWidget(label)) { | ||
790 | continue; | ||
791 | } | ||
792 | if (deviceType_App() == desktop_AppDeviceType) { | ||
793 | setFont_LabelWidget(label, isCaution ? uiLabelBold_FontId : uiLabel_FontId); | ||
794 | } | ||
795 | else if (isPortraitPhone) { | ||
796 | if (!isSlidePanel) { | ||
797 | setFont_LabelWidget(label, isCaution ? defaultBigBold_FontId : defaultBig_FontId); | ||
798 | } | ||
799 | } | ||
800 | else { | ||
801 | setFont_LabelWidget(label, isCaution ? uiContentBold_FontId : uiContent_FontId); | ||
802 | } | ||
803 | } | ||
804 | else if (childCount_Widget(i.object)) { | ||
805 | updateMenuItemFonts_Widget_(i.object); | ||
806 | } | ||
807 | } | ||
808 | } | ||
809 | |||
769 | void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { | 810 | void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { |
770 | const iRect rootRect = rect_Root(d->root); | 811 | const iRect rootRect = rect_Root(d->root); |
771 | const iInt2 rootSize = rootRect.size; | 812 | const iInt2 rootSize = rootRect.size; |
@@ -788,28 +829,7 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { | |||
788 | } | 829 | } |
789 | d->rect.size.x = rootSize.x; | 830 | d->rect.size.x = rootSize.x; |
790 | } | 831 | } |
791 | /* Update item fonts. */ { | 832 | updateMenuItemFonts_Widget_(d); |
792 | iForEach(ObjectList, i, children_Widget(d)) { | ||
793 | if (isInstance_Object(i.object, &Class_LabelWidget)) { | ||
794 | iLabelWidget *label = i.object; | ||
795 | const iBool isCaution = startsWith_String(text_LabelWidget(label), uiTextCaution_ColorEscape); | ||
796 | if (isWrapped_LabelWidget(label)) { | ||
797 | continue; | ||
798 | } | ||
799 | if (deviceType_App() == desktop_AppDeviceType) { | ||
800 | setFont_LabelWidget(label, isCaution ? uiLabelBold_FontId : uiLabel_FontId); | ||
801 | } | ||
802 | else if (isPortraitPhone) { | ||
803 | if (!isSlidePanel) { | ||
804 | setFont_LabelWidget(label, isCaution ? defaultBigBold_FontId : defaultBig_FontId); | ||
805 | } | ||
806 | } | ||
807 | else { | ||
808 | setFont_LabelWidget(label, isCaution ? uiContentBold_FontId : uiContent_FontId); | ||
809 | } | ||
810 | } | ||
811 | } | ||
812 | } | ||
813 | arrange_Widget(d); | 833 | arrange_Widget(d); |
814 | if (isPortraitPhone) { | 834 | if (isPortraitPhone) { |
815 | if (isSlidePanel) { | 835 | if (isSlidePanel) { |
diff --git a/src/ui/widget.c b/src/ui/widget.c index 0765bf9f..1c0fb271 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -880,9 +880,9 @@ iInt2 localToWindow_Widget(const iWidget *d, iInt2 localCoord) { | |||
880 | applyVisualOffset_Widget_(w, &pos); | 880 | applyVisualOffset_Widget_(w, &pos); |
881 | addv_I2(&window, pos); | 881 | addv_I2(&window, pos); |
882 | } | 882 | } |
883 | #if defined (iPlatformMobile) | 883 | //#if defined (iPlatformMobile) |
884 | window.y += value_Anim(&get_Window()->rootOffset); | 884 | // window.y += value_Anim(&get_Window()->rootOffset); |
885 | #endif | 885 | //#endif |
886 | return window; | 886 | return window; |
887 | } | 887 | } |
888 | 888 | ||
@@ -1072,23 +1072,33 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
1072 | } | 1072 | } |
1073 | 1073 | ||
1074 | iBool scrollOverflow_Widget(iWidget *d, int delta) { | 1074 | iBool scrollOverflow_Widget(iWidget *d, int delta) { |
1075 | iRect bounds = boundsWithoutVisualOffset_Widget(d); | 1075 | iRect bounds = boundsWithoutVisualOffset_Widget(d); |
1076 | const iInt2 rootSize = size_Root(d->root); | 1076 | // const iInt2 rootSize = size_Root(d->root); |
1077 | const iRect winRect = safeRect_Root(d->root); | 1077 | const iRect winRect = adjusted_Rect(safeRect_Root(d->root), |
1078 | const int yTop = top_Rect(winRect); | 1078 | zero_I2(), |
1079 | const int yBottom = bottom_Rect(winRect); | 1079 | init_I2(0, -get_Window()->keyboardHeight)); |
1080 | const int yTop = top_Rect(winRect); | ||
1081 | const int yBottom = bottom_Rect(winRect); | ||
1080 | if (top_Rect(bounds) >= yTop && bottom_Rect(bounds) < yBottom) { | 1082 | if (top_Rect(bounds) >= yTop && bottom_Rect(bounds) < yBottom) { |
1081 | return iFalse; /* fits inside just fine */ | 1083 | return iFalse; /* fits inside just fine */ |
1082 | } | 1084 | } |
1083 | //const int safeBottom = rootSize.y - yBottom; | 1085 | //const int safeBottom = rootSize.y - yBottom; |
1084 | bounds.pos.y += delta; | 1086 | iRangei validPosRange = { bottom_Rect(winRect) - height_Rect(bounds), yTop }; |
1085 | const iRangei range = { bottom_Rect(winRect) - height_Rect(bounds), yTop }; | 1087 | if (validPosRange.start > validPosRange.end) { |
1088 | validPosRange.start = validPosRange.end; /* no room to scroll */ | ||
1089 | } | ||
1090 | if (delta) { | ||
1091 | if (delta < 0 && bounds.pos.y < validPosRange.start) { | ||
1092 | delta = 0; | ||
1093 | } | ||
1094 | if (delta > 0 && bounds.pos.y > validPosRange.end) { | ||
1095 | delta = 0; | ||
1096 | } | ||
1097 | bounds.pos.y += delta; | ||
1086 | // printf("range: %d ... %d\n", range.start, range.end); | 1098 | // printf("range: %d ... %d\n", range.start, range.end); |
1087 | if (range.start >= range.end) { | ||
1088 | bounds.pos.y = range.end; | ||
1089 | } | 1099 | } |
1090 | else { | 1100 | else { |
1091 | bounds.pos.y = iClamp(bounds.pos.y, range.start, range.end); | 1101 | bounds.pos.y = iClamp(bounds.pos.y, validPosRange.start, validPosRange.end); |
1092 | } | 1102 | } |
1093 | // if (delta >= 0) { | 1103 | // if (delta >= 0) { |
1094 | // bounds.pos.y = iMin(bounds.pos.y, yTop); | 1104 | // bounds.pos.y = iMin(bounds.pos.y, yTop); |
@@ -1454,7 +1464,7 @@ void setDrawBufferEnabled_Widget(iWidget *d, iBool enable) { | |||
1454 | 1464 | ||
1455 | static void beginBufferDraw_Widget_(const iWidget *d) { | 1465 | static void beginBufferDraw_Widget_(const iWidget *d) { |
1456 | if (d->drawBuf) { | 1466 | if (d->drawBuf) { |
1457 | printf("[%p] drawbuffer update %d\n", d, d->drawBuf->isValid); | 1467 | // printf("[%p] drawbuffer update %d\n", d, d->drawBuf->isValid); |
1458 | if (d->drawBuf->isValid) { | 1468 | if (d->drawBuf->isValid) { |
1459 | iAssert(!isEqual_I2(d->drawBuf->size, boundsForDraw_Widget_(d).size)); | 1469 | iAssert(!isEqual_I2(d->drawBuf->size, boundsForDraw_Widget_(d).size)); |
1460 | // printf(" drawBuf:%dx%d boundsForDraw:%dx%d\n", | 1470 | // printf(" drawBuf:%dx%d boundsForDraw:%dx%d\n", |
@@ -1503,7 +1513,7 @@ void draw_Widget(const iWidget *d) { | |||
1503 | endBufferDraw_Widget_(d); | 1513 | endBufferDraw_Widget_(d); |
1504 | } | 1514 | } |
1505 | if (d->drawBuf) { | 1515 | if (d->drawBuf) { |
1506 | iAssert(d->drawBuf->isValid); | 1516 | //iAssert(d->drawBuf->isValid); |
1507 | const iRect bounds = bounds_Widget(d); | 1517 | const iRect bounds = bounds_Widget(d); |
1508 | SDL_RenderCopy(renderer_Window(get_Window()), d->drawBuf->texture, NULL, | 1518 | SDL_RenderCopy(renderer_Window(get_Window()), d->drawBuf->texture, NULL, |
1509 | &(SDL_Rect){ bounds.pos.x, bounds.pos.y, | 1519 | &(SDL_Rect){ bounds.pos.x, bounds.pos.y, |
diff --git a/src/ui/window.c b/src/ui/window.c index 8034d858..ed2ec024 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -421,7 +421,7 @@ void init_Window(iWindow *d, iRect rect) { | |||
421 | d->ignoreClick = iFalse; | 421 | d->ignoreClick = iFalse; |
422 | d->focusGainedAt = 0; | 422 | d->focusGainedAt = 0; |
423 | d->keyboardHeight = 0; | 423 | d->keyboardHeight = 0; |
424 | init_Anim(&d->rootOffset, 0.0f); | 424 | // init_Anim(&d->rootOffset, 0.0f); |
425 | uint32_t flags = 0; | 425 | uint32_t flags = 0; |
426 | #if defined (iPlatformAppleDesktop) | 426 | #if defined (iPlatformAppleDesktop) |
427 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); | 427 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); |
@@ -1215,10 +1215,10 @@ iBool isOpenGLRenderer_Window(void) { | |||
1215 | void setKeyboardHeight_Window(iWindow *d, int height) { | 1215 | void setKeyboardHeight_Window(iWindow *d, int height) { |
1216 | if (d->keyboardHeight != height) { | 1216 | if (d->keyboardHeight != height) { |
1217 | d->keyboardHeight = height; | 1217 | d->keyboardHeight = height; |
1218 | if (height == 0) { | 1218 | // if (height == 0) { |
1219 | setFlags_Anim(&d->rootOffset, easeBoth_AnimFlag, iTrue); | 1219 | // setFlags_Anim(&d->rootOffset, easeBoth_AnimFlag, iTrue); |
1220 | setValue_Anim(&d->rootOffset, 0, 250); | 1220 | // setValue_Anim(&d->rootOffset, 0, 250); |
1221 | } | 1221 | // } |
1222 | postCommandf_App("keyboard.changed arg:%d", height); | 1222 | postCommandf_App("keyboard.changed arg:%d", height); |
1223 | postRefresh_App(); | 1223 | postRefresh_App(); |
1224 | } | 1224 | } |
diff --git a/src/ui/window.h b/src/ui/window.h index 63f7e5f2..a5b8f137 100644 --- a/src/ui/window.h +++ b/src/ui/window.h | |||
@@ -98,7 +98,7 @@ struct Impl_Window { | |||
98 | SDL_Cursor * cursors[SDL_NUM_SYSTEM_CURSORS]; | 98 | SDL_Cursor * cursors[SDL_NUM_SYSTEM_CURSORS]; |
99 | SDL_Cursor * pendingCursor; | 99 | SDL_Cursor * pendingCursor; |
100 | int loadAnimTimer; | 100 | int loadAnimTimer; |
101 | iAnim rootOffset; | 101 | // iAnim rootOffset; |
102 | int keyboardHeight; /* mobile software keyboards */ | 102 | int keyboardHeight; /* mobile software keyboards */ |
103 | }; | 103 | }; |
104 | 104 | ||