summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-08-06 12:09:11 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-08-06 12:09:11 +0300
commit4c176d97043e33bb9d2db264f0ecb4912e32d553 (patch)
treea4f77b9e543e6c433abfce1793dbf78bc265a977
parent018bbd0f90809108c04019d7f661f157250edc1c (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.c75
-rw-r--r--src/ui/inputwidget.h1
-rw-r--r--src/ui/root.c6
-rw-r--r--src/ui/uploadwidget.c1
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
570static 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
582static 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
569void init_InputWidget(iInputWidget *d, size_t maxLen) { 592void 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
764void setUseReturnKeyBehavior_InputWidget(iInputWidget *d, iBool useReturnKeyBehavior) {
765 iChangeFlags(d->inFlags, useReturnKeyBehavior_InputWidgetFlag, useReturnKeyBehavior);
766}
767
743void setHint_InputWidget(iInputWidget *d, const char *hintText) { 768void 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
869static 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
881static size_t cursorToIndex_InputWidget_(const iInputWidget *d, iInt2 pos) { 894static 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
1398static 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
1405static 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
1386static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { 1412static 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
52void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); 52void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context);
53void setLineBreaksEnabled_InputWidget(iInputWidget *, iBool lineBreaksEnabled); 53void setLineBreaksEnabled_InputWidget(iInputWidget *, iBool lineBreaksEnabled);
54void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); 54void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled);
55void setUseReturnKeyBehavior_InputWidget(iInputWidget *, iBool useReturnKeyBehavior);
55void setBackupFileName_InputWidget (iInputWidget *, const char *fileName); 56void setBackupFileName_InputWidget (iInputWidget *, const char *fileName);
56void begin_InputWidget (iInputWidget *); 57void begin_InputWidget (iInputWidget *);
57void end_InputWidget (iInputWidget *, iBool accept); 58void 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));