diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-08-06 12:09:11 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-08-06 12:09:11 +0300 |
commit | 4c176d97043e33bb9d2db264f0ecb4912e32d553 (patch) | |
tree | a4f77b9e543e6c433abfce1793dbf78bc265a977 | |
parent | 018bbd0f90809108c04019d7f661f157250edc1c (diff) |
InputWidget: Return key, input focus behavior
In the long-form text entry mode, don't use the user's configured Return key behavior, since that's meant for shorter input fields.
Input fields no longer lose focus when the window loses input focus, but the cursor will stop blinking. This makes it easier to resume typing after switching window focus, and also on macOS the symbol picker is easier to use.
-rw-r--r-- | src/ui/inputwidget.c | 75 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 1 | ||||
-rw-r--r-- | src/ui/root.c | 6 | ||||
-rw-r--r-- | src/ui/uploadwidget.c | 1 |
4 files changed, 59 insertions, 24 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index d31130f5..f1f368b6 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -190,6 +190,7 @@ enum iInputWidgetFlag { | |||
190 | enterKeyEnabled_InputWidgetFlag = iBit(10), | 190 | enterKeyEnabled_InputWidgetFlag = iBit(10), |
191 | lineBreaksEnabled_InputWidgetFlag= iBit(11), | 191 | lineBreaksEnabled_InputWidgetFlag= iBit(11), |
192 | needBackup_InputWidgetFlag = iBit(12), | 192 | needBackup_InputWidgetFlag = iBit(12), |
193 | useReturnKeyBehavior_InputWidgetFlag = iBit(13), | ||
193 | }; | 194 | }; |
194 | 195 | ||
195 | /*----------------------------------------------------------------------------------------------*/ | 196 | /*----------------------------------------------------------------------------------------------*/ |
@@ -566,6 +567,28 @@ static void updateAllLinesAndResizeHeight_InputWidget_(iInputWidget *d) { | |||
566 | } | 567 | } |
567 | } | 568 | } |
568 | 569 | ||
570 | static uint32_t cursorTimer_(uint32_t interval, void *w) { | ||
571 | iInputWidget *d = w; | ||
572 | if (d->cursorVis > 1) { | ||
573 | d->cursorVis--; | ||
574 | } | ||
575 | else { | ||
576 | d->cursorVis ^= 1; | ||
577 | } | ||
578 | refresh_Widget(w); | ||
579 | return interval; | ||
580 | } | ||
581 | |||
582 | static void startOrStopCursorTimer_InputWidget_(iInputWidget *d, iBool doStart) { | ||
583 | if (doStart && !d->timer) { | ||
584 | d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); | ||
585 | } | ||
586 | else if (!doStart && d->timer) { | ||
587 | SDL_RemoveTimer(d->timer); | ||
588 | d->timer = 0; | ||
589 | } | ||
590 | } | ||
591 | |||
569 | void init_InputWidget(iInputWidget *d, size_t maxLen) { | 592 | void init_InputWidget(iInputWidget *d, size_t maxLen) { |
570 | iWidget *w = &d->widget; | 593 | iWidget *w = &d->widget; |
571 | init_Widget(w); | 594 | init_Widget(w); |
@@ -588,7 +611,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { | |||
588 | d->prevCursor = zero_I2(); | 611 | d->prevCursor = zero_I2(); |
589 | d->lastUpdateWidth = 0; | 612 | d->lastUpdateWidth = 0; |
590 | d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag | | 613 | d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag | |
591 | lineBreaksEnabled_InputWidgetFlag; | 614 | lineBreaksEnabled_InputWidgetFlag | useReturnKeyBehavior_InputWidgetFlag; |
592 | // if (deviceType_App() != desktop_AppDeviceType) { | 615 | // if (deviceType_App() != desktop_AppDeviceType) { |
593 | // d->inFlags |= enterKeyInsertsLineFeed_InputWidgetFlag; | 616 | // d->inFlags |= enterKeyInsertsLineFeed_InputWidgetFlag; |
594 | // } | 617 | // } |
@@ -627,9 +650,7 @@ void deinit_InputWidget(iInputWidget *d) { | |||
627 | delete_TextBuf(d->buffered); | 650 | delete_TextBuf(d->buffered); |
628 | clearUndo_InputWidget_(d); | 651 | clearUndo_InputWidget_(d); |
629 | deinit_Array(&d->undoStack); | 652 | deinit_Array(&d->undoStack); |
630 | if (d->timer) { | 653 | startOrStopCursorTimer_InputWidget_(d, iFalse); |
631 | SDL_RemoveTimer(d->timer); | ||
632 | } | ||
633 | deinit_String(&d->srcHint); | 654 | deinit_String(&d->srcHint); |
634 | deinit_String(&d->hint); | 655 | deinit_String(&d->hint); |
635 | deinit_String(&d->oldText); | 656 | deinit_String(&d->oldText); |
@@ -740,6 +761,10 @@ void setEnterKeyEnabled_InputWidget(iInputWidget *d, iBool enterKeyEnabled) { | |||
740 | iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled); | 761 | iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled); |
741 | } | 762 | } |
742 | 763 | ||
764 | void setUseReturnKeyBehavior_InputWidget(iInputWidget *d, iBool useReturnKeyBehavior) { | ||
765 | iChangeFlags(d->inFlags, useReturnKeyBehavior_InputWidgetFlag, useReturnKeyBehavior); | ||
766 | } | ||
767 | |||
743 | void setHint_InputWidget(iInputWidget *d, const char *hintText) { | 768 | void setHint_InputWidget(iInputWidget *d, const char *hintText) { |
744 | /* Keep original for retranslations. */ | 769 | /* Keep original for retranslations. */ |
745 | setCStr_String(&d->srcHint, hintText); | 770 | setCStr_String(&d->srcHint, hintText); |
@@ -866,18 +891,6 @@ void setTextCStr_InputWidget(iInputWidget *d, const char *cstr) { | |||
866 | delete_String(str); | 891 | delete_String(str); |
867 | } | 892 | } |
868 | 893 | ||
869 | static uint32_t cursorTimer_(uint32_t interval, void *w) { | ||
870 | iInputWidget *d = w; | ||
871 | if (d->cursorVis > 1) { | ||
872 | d->cursorVis--; | ||
873 | } | ||
874 | else { | ||
875 | d->cursorVis ^= 1; | ||
876 | } | ||
877 | refresh_Widget(w); | ||
878 | return interval; | ||
879 | } | ||
880 | |||
881 | static size_t cursorToIndex_InputWidget_(const iInputWidget *d, iInt2 pos) { | 894 | static size_t cursorToIndex_InputWidget_(const iInputWidget *d, iInt2 pos) { |
882 | if (pos.y < 0) { | 895 | if (pos.y < 0) { |
883 | return 0; | 896 | return 0; |
@@ -930,7 +943,7 @@ void begin_InputWidget(iInputWidget *d) { | |||
930 | setFlags_Widget(w, selected_WidgetFlag, iTrue); | 943 | setFlags_Widget(w, selected_WidgetFlag, iTrue); |
931 | showCursor_InputWidget_(d); | 944 | showCursor_InputWidget_(d); |
932 | refresh_Widget(w); | 945 | refresh_Widget(w); |
933 | d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); | 946 | startOrStopCursorTimer_InputWidget_(d, iTrue); |
934 | d->inFlags &= ~enterPressed_InputWidgetFlag; | 947 | d->inFlags &= ~enterPressed_InputWidgetFlag; |
935 | if (d->inFlags & selectAllOnFocus_InputWidgetFlag) { | 948 | if (d->inFlags & selectAllOnFocus_InputWidgetFlag) { |
936 | d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end }; | 949 | d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end }; |
@@ -955,8 +968,7 @@ void end_InputWidget(iInputWidget *d, iBool accept) { | |||
955 | splitToLines_(&d->oldText, &d->lines); | 968 | splitToLines_(&d->oldText, &d->lines); |
956 | } | 969 | } |
957 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | 970 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; |
958 | SDL_RemoveTimer(d->timer); | 971 | startOrStopCursorTimer_InputWidget_(d, iFalse); |
959 | d->timer = 0; | ||
960 | SDL_StopTextInput(); | 972 | SDL_StopTextInput(); |
961 | setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iFalse); | 973 | setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iFalse); |
962 | const char *id = cstr_String(id_Widget(as_Widget(d))); | 974 | const char *id = cstr_String(id_Widget(as_Widget(d))); |
@@ -1383,6 +1395,20 @@ static iBool isArrowUpDownConsumed_InputWidget_(const iInputWidget *d) { | |||
1383 | return d->maxWrapLines > 1; | 1395 | return d->maxWrapLines > 1; |
1384 | } | 1396 | } |
1385 | 1397 | ||
1398 | static iBool checkLineBreakMods_InputWidget_(const iInputWidget *d, int mods) { | ||
1399 | if (d->inFlags & useReturnKeyBehavior_InputWidgetFlag) { | ||
1400 | return mods == lineBreakKeyMod_ReturnKeyBehavior(prefs_App()->returnKey); | ||
1401 | } | ||
1402 | return mods == 0; | ||
1403 | } | ||
1404 | |||
1405 | static iBool checkAcceptMods_InputWidget_(const iInputWidget *d, int mods) { | ||
1406 | if (d->inFlags & useReturnKeyBehavior_InputWidgetFlag) { | ||
1407 | return mods == acceptKeyMod_ReturnKeyBehavior(prefs_App()->returnKey); | ||
1408 | } | ||
1409 | return mods == 0; | ||
1410 | } | ||
1411 | |||
1386 | static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | 1412 | static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { |
1387 | iWidget *w = as_Widget(d); | 1413 | iWidget *w = as_Widget(d); |
1388 | /* Resize according to width immediately. */ | 1414 | /* Resize according to width immediately. */ |
@@ -1401,6 +1427,13 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1401 | begin_InputWidget(d); | 1427 | begin_InputWidget(d); |
1402 | return iFalse; | 1428 | return iFalse; |
1403 | } | 1429 | } |
1430 | else if (isEditing_InputWidget_(d) && (isCommand_UserEvent(ev, "window.focus.lost") || | ||
1431 | isCommand_UserEvent(ev, "window.focus.gained"))) { | ||
1432 | startOrStopCursorTimer_InputWidget_(d, isCommand_UserEvent(ev, "window.focus.gained")); | ||
1433 | d->cursorVis = 1; | ||
1434 | refresh_Widget(d); | ||
1435 | return iFalse; | ||
1436 | } | ||
1404 | else if (isCommand_UserEvent(ev, "keyroot.changed")) { | 1437 | else if (isCommand_UserEvent(ev, "keyroot.changed")) { |
1405 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | 1438 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; |
1406 | } | 1439 | } |
@@ -1617,7 +1650,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1617 | if (~d->inFlags & isSensitive_InputWidgetFlag && | 1650 | if (~d->inFlags & isSensitive_InputWidgetFlag && |
1618 | ~d->inFlags & isUrl_InputWidgetFlag && | 1651 | ~d->inFlags & isUrl_InputWidgetFlag && |
1619 | d->inFlags & lineBreaksEnabled_InputWidgetFlag && d->maxLen == 0) { | 1652 | d->inFlags & lineBreaksEnabled_InputWidgetFlag && d->maxLen == 0) { |
1620 | if (mods == lineBreakKeyMod_ReturnKeyBehavior(prefs_App()->returnKey)) { | 1653 | if (checkLineBreakMods_InputWidget_(d, mods)) { |
1621 | pushUndo_InputWidget_(d); | 1654 | pushUndo_InputWidget_(d); |
1622 | deleteMarked_InputWidget_(d); | 1655 | deleteMarked_InputWidget_(d); |
1623 | insertChar_InputWidget_(d, '\n'); | 1656 | insertChar_InputWidget_(d, '\n'); |
@@ -1626,7 +1659,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1626 | } | 1659 | } |
1627 | } | 1660 | } |
1628 | if (d->inFlags & enterKeyEnabled_InputWidgetFlag && | 1661 | if (d->inFlags & enterKeyEnabled_InputWidgetFlag && |
1629 | (mods == acceptKeyMod_ReturnKeyBehavior(prefs_App()->returnKey) || | 1662 | (checkAcceptMods_InputWidget_(d, mods) || |
1630 | (~d->inFlags & lineBreaksEnabled_InputWidgetFlag))) { | 1663 | (~d->inFlags & lineBreaksEnabled_InputWidgetFlag))) { |
1631 | d->inFlags |= enterPressed_InputWidgetFlag; | 1664 | d->inFlags |= enterPressed_InputWidgetFlag; |
1632 | setFocus_Widget(NULL); | 1665 | setFocus_Widget(NULL); |
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index f7e46e8c..0d327ca6 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h | |||
@@ -52,6 +52,7 @@ void setLineLimits_InputWidget (iInputWidget *, int minLines, int maxLi | |||
52 | void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); | 52 | void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); |
53 | void setLineBreaksEnabled_InputWidget(iInputWidget *, iBool lineBreaksEnabled); | 53 | void setLineBreaksEnabled_InputWidget(iInputWidget *, iBool lineBreaksEnabled); |
54 | void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); | 54 | void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); |
55 | void setUseReturnKeyBehavior_InputWidget(iInputWidget *, iBool useReturnKeyBehavior); | ||
55 | void setBackupFileName_InputWidget (iInputWidget *, const char *fileName); | 56 | void setBackupFileName_InputWidget (iInputWidget *, const char *fileName); |
56 | void begin_InputWidget (iInputWidget *); | 57 | void begin_InputWidget (iInputWidget *); |
57 | void end_InputWidget (iInputWidget *, iBool accept); | 58 | void end_InputWidget (iInputWidget *, iBool accept); |
diff --git a/src/ui/root.c b/src/ui/root.c index c61f1b35..a8b9f998 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -358,9 +358,9 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { | |||
358 | return iTrue; | 358 | return iTrue; |
359 | } | 359 | } |
360 | else if (equal_Command(cmd, "window.focus.lost")) { | 360 | else if (equal_Command(cmd, "window.focus.lost")) { |
361 | #if !defined (iPlatformMobile) /* apps don't share input focus on mobile */ | 361 | //#if !defined (iPlatformMobile) /* apps don't share input focus on mobile */ |
362 | setFocus_Widget(NULL); | 362 | // setFocus_Widget(NULL); |
363 | #endif | 363 | //#endif |
364 | setTextColor_LabelWidget(findWidget_App("winbar.app"), uiAnnotation_ColorId); | 364 | setTextColor_LabelWidget(findWidget_App("winbar.app"), uiAnnotation_ColorId); |
365 | setTextColor_LabelWidget(findWidget_App("winbar.title"), uiAnnotation_ColorId); | 365 | setTextColor_LabelWidget(findWidget_App("winbar.title"), uiAnnotation_ColorId); |
366 | return iFalse; | 366 | return iFalse; |
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c index 2780f86a..57b6b6b7 100644 --- a/src/ui/uploadwidget.c +++ b/src/ui/uploadwidget.c | |||
@@ -116,6 +116,7 @@ void init_UploadWidget(iUploadWidget *d) { | |||
116 | setId_Widget(as_Widget(d->input), "upload.text"); | 116 | setId_Widget(as_Widget(d->input), "upload.text"); |
117 | setFont_InputWidget(d->input, monospace_FontId); | 117 | setFont_InputWidget(d->input, monospace_FontId); |
118 | setLineLimits_InputWidget(d->input, 7, 20); | 118 | setLineLimits_InputWidget(d->input, 7, 20); |
119 | setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */ | ||
119 | setHint_InputWidget(d->input, "${hint.upload.text}"); | 120 | setHint_InputWidget(d->input, "${hint.upload.text}"); |
120 | setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1)); | 121 | setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1)); |
121 | addChild_Widget(page, iClob(d->input)); | 122 | addChild_Widget(page, iClob(d->input)); |