diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-09 20:46:03 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-09 20:46:03 +0200 |
commit | d72b54573599a8377d24d2812896ba2f8a11c6d3 (patch) | |
tree | 033013a65e8037b6447d46cd5792d0cb98eed234 | |
parent | cf8b7c568d1b537f833937ecf8dba19df379121e (diff) |
InputWidget: Usability improvements
Select all when search bar gets focus, so it's easy to enter a new search term.
One press of Escape will both unfocus and dismiss the search bar.
IssueID #65
-rw-r--r-- | src/ui/inputwidget.c | 58 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 1 | ||||
-rw-r--r-- | src/ui/window.c | 2 |
3 files changed, 36 insertions, 25 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index fbcbd4c2..4f9bd367 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -49,20 +49,25 @@ static void deinit_InputUndo_(iInputUndo *d) { | |||
49 | deinit_Array(&d->text); | 49 | deinit_Array(&d->text); |
50 | } | 50 | } |
51 | 51 | ||
52 | enum iInputWidgetFlag { | ||
53 | isSensitive_InputWidgetFlag = iBit(1), | ||
54 | enterPressed_InputWidgetFlag = iBit(2), | ||
55 | selectAllOnFocus_InputWidgetFlag = iBit(3), | ||
56 | notifyEdits_InputWidgetFlag = iBit(4), | ||
57 | eatEscape_InputWidgetFlag = iBit(5), | ||
58 | isMarking_InputWidgetFlag = iBit(6), | ||
59 | }; | ||
60 | |||
52 | struct Impl_InputWidget { | 61 | struct Impl_InputWidget { |
53 | iWidget widget; | 62 | iWidget widget; |
54 | enum iInputMode mode; | 63 | enum iInputMode mode; |
55 | iBool isSensitive; | 64 | int inFlags; |
56 | iBool enterPressed; | ||
57 | iBool selectAllOnFocus; | ||
58 | iBool notifyEdits; | ||
59 | size_t maxLen; | 65 | size_t maxLen; |
60 | iArray text; /* iChar[] */ | 66 | iArray text; /* iChar[] */ |
61 | iArray oldText; /* iChar[] */ | 67 | iArray oldText; /* iChar[] */ |
62 | iString hint; | 68 | iString hint; |
63 | size_t cursor; | 69 | size_t cursor; |
64 | size_t lastCursor; | 70 | size_t lastCursor; |
65 | iBool isMarking; | ||
66 | iRanges mark; | 71 | iRanges mark; |
67 | iArray undoStack; | 72 | iArray undoStack; |
68 | int font; | 73 | int font; |
@@ -96,11 +101,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { | |||
96 | d->font = uiInput_FontId; | 101 | d->font = uiInput_FontId; |
97 | d->cursor = 0; | 102 | d->cursor = 0; |
98 | d->lastCursor = 0; | 103 | d->lastCursor = 0; |
99 | d->isMarking = iFalse; | 104 | d->inFlags = eatEscape_InputWidgetFlag; |
100 | d->isSensitive = iFalse; | ||
101 | d->enterPressed = iFalse; | ||
102 | d->selectAllOnFocus = iFalse; | ||
103 | d->notifyEdits = iFalse; | ||
104 | iZap(d->mark); | 105 | iZap(d->mark); |
105 | setMaxLen_InputWidget(d, maxLen); | 106 | setMaxLen_InputWidget(d, maxLen); |
106 | /* Caller must arrange the width, but the height is fixed. */ | 107 | /* Caller must arrange the width, but the height is fixed. */ |
@@ -152,7 +153,7 @@ void setMode_InputWidget(iInputWidget *d, enum iInputMode mode) { | |||
152 | } | 153 | } |
153 | 154 | ||
154 | void setSensitive_InputWidget(iInputWidget *d, iBool isSensitive) { | 155 | void setSensitive_InputWidget(iInputWidget *d, iBool isSensitive) { |
155 | d->isSensitive = isSensitive; | 156 | iChangeFlags(d->inFlags, isSensitive_InputWidgetFlag, isSensitive); |
156 | } | 157 | } |
157 | 158 | ||
158 | const iString *text_InputWidget(const iInputWidget *d) { | 159 | const iString *text_InputWidget(const iInputWidget *d) { |
@@ -182,7 +183,7 @@ static const iChar sensitiveChar_ = 0x25cf; /* black circle */ | |||
182 | 183 | ||
183 | static iString *visText_InputWidget_(const iInputWidget *d) { | 184 | static iString *visText_InputWidget_(const iInputWidget *d) { |
184 | iString *text; | 185 | iString *text; |
185 | if (!d->isSensitive) { | 186 | if (~d->inFlags & isSensitive_InputWidgetFlag) { |
186 | text = newUnicodeN_String(constData_Array(&d->text), size_Array(&d->text)); | 187 | text = newUnicodeN_String(constData_Array(&d->text), size_Array(&d->text)); |
187 | } | 188 | } |
188 | else { | 189 | else { |
@@ -271,8 +272,8 @@ void begin_InputWidget(iInputWidget *d) { | |||
271 | showCursor_InputWidget_(d); | 272 | showCursor_InputWidget_(d); |
272 | refresh_Widget(w); | 273 | refresh_Widget(w); |
273 | d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); | 274 | d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); |
274 | d->enterPressed = iFalse; | 275 | d->inFlags &= ~enterPressed_InputWidgetFlag; |
275 | if (d->selectAllOnFocus) { | 276 | if (d->inFlags & selectAllOnFocus_InputWidgetFlag) { |
276 | d->mark = (iRanges){ 0, size_Array(&d->text) }; | 277 | d->mark = (iRanges){ 0, size_Array(&d->text) }; |
277 | } | 278 | } |
278 | else { | 279 | else { |
@@ -297,8 +298,11 @@ void end_InputWidget(iInputWidget *d, iBool accept) { | |||
297 | const char *id = cstr_String(id_Widget(as_Widget(d))); | 298 | const char *id = cstr_String(id_Widget(as_Widget(d))); |
298 | if (!*id) id = "_"; | 299 | if (!*id) id = "_"; |
299 | refresh_Widget(w); | 300 | refresh_Widget(w); |
300 | postCommand_Widget( | 301 | postCommand_Widget(w, |
301 | w, "input.ended id:%s enter:%d arg:%d", id, d->enterPressed ? 1 : 0, accept ? 1 : 0); | 302 | "input.ended id:%s enter:%d arg:%d", |
303 | id, | ||
304 | d->inFlags & enterPressed_InputWidgetFlag ? 1 : 0, | ||
305 | accept ? 1 : 0); | ||
302 | } | 306 | } |
303 | 307 | ||
304 | static void insertChar_InputWidget_(iInputWidget *d, iChar chr) { | 308 | static void insertChar_InputWidget_(iInputWidget *d, iChar chr) { |
@@ -351,11 +355,15 @@ void setCursor_InputWidget(iInputWidget *d, size_t pos) { | |||
351 | } | 355 | } |
352 | 356 | ||
353 | void setSelectAllOnFocus_InputWidget(iInputWidget *d, iBool selectAllOnFocus) { | 357 | void setSelectAllOnFocus_InputWidget(iInputWidget *d, iBool selectAllOnFocus) { |
354 | d->selectAllOnFocus = selectAllOnFocus; | 358 | iChangeFlags(d->inFlags, selectAllOnFocus_InputWidgetFlag, selectAllOnFocus); |
355 | } | 359 | } |
356 | 360 | ||
357 | void setNotifyEdits_InputWidget(iInputWidget *d, iBool notifyEdits) { | 361 | void setNotifyEdits_InputWidget(iInputWidget *d, iBool notifyEdits) { |
358 | d->notifyEdits = notifyEdits; | 362 | iChangeFlags(d->inFlags, notifyEdits_InputWidgetFlag, notifyEdits); |
363 | } | ||
364 | |||
365 | void setEatEscape_InputWidget(iInputWidget *d, iBool eatEscape) { | ||
366 | iChangeFlags(d->inFlags, eatEscape_InputWidgetFlag, eatEscape); | ||
359 | } | 367 | } |
360 | 368 | ||
361 | static iRanges mark_InputWidget_(const iInputWidget *d) { | 369 | static iRanges mark_InputWidget_(const iInputWidget *d) { |
@@ -366,7 +374,7 @@ static iRanges mark_InputWidget_(const iInputWidget *d) { | |||
366 | } | 374 | } |
367 | 375 | ||
368 | static void contentsWereChanged_InputWidget_(iInputWidget *d) { | 376 | static void contentsWereChanged_InputWidget_(iInputWidget *d) { |
369 | if (d->notifyEdits) { | 377 | if (d->inFlags & notifyEdits_InputWidgetFlag) { |
370 | postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d)))); | 378 | postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d)))); |
371 | } | 379 | } |
372 | } | 380 | } |
@@ -521,7 +529,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
521 | setFocus_Widget(w); | 529 | setFocus_Widget(w); |
522 | setCursor_InputWidget(d, coordIndex_InputWidget_(d, pos_Click(&d->click))); | 530 | setCursor_InputWidget(d, coordIndex_InputWidget_(d, pos_Click(&d->click))); |
523 | iZap(d->mark); | 531 | iZap(d->mark); |
524 | d->isMarking = iFalse; | 532 | d->inFlags &= ~isMarking_InputWidgetFlag; |
525 | return iTrue; | 533 | return iTrue; |
526 | case double_ClickResult: | 534 | case double_ClickResult: |
527 | case aborted_ClickResult: | 535 | case aborted_ClickResult: |
@@ -529,8 +537,8 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
529 | case drag_ClickResult: | 537 | case drag_ClickResult: |
530 | showCursor_InputWidget_(d); | 538 | showCursor_InputWidget_(d); |
531 | d->cursor = coordIndex_InputWidget_(d, pos_Click(&d->click)); | 539 | d->cursor = coordIndex_InputWidget_(d, pos_Click(&d->click)); |
532 | if (!d->isMarking) { | 540 | if (~d->inFlags & isMarking_InputWidgetFlag) { |
533 | d->isMarking = iTrue; | 541 | d->inFlags |= isMarking_InputWidgetFlag; |
534 | d->mark.start = d->cursor; | 542 | d->mark.start = d->cursor; |
535 | } | 543 | } |
536 | d->mark.end = d->cursor; | 544 | d->mark.end = d->cursor; |
@@ -577,13 +585,13 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
577 | switch (key) { | 585 | switch (key) { |
578 | case SDLK_RETURN: | 586 | case SDLK_RETURN: |
579 | case SDLK_KP_ENTER: | 587 | case SDLK_KP_ENTER: |
580 | d->enterPressed = iTrue; | 588 | d->inFlags |= enterPressed_InputWidgetFlag; |
581 | setFocus_Widget(NULL); | 589 | setFocus_Widget(NULL); |
582 | return iTrue; | 590 | return iTrue; |
583 | case SDLK_ESCAPE: | 591 | case SDLK_ESCAPE: |
584 | end_InputWidget(d, iFalse); | 592 | end_InputWidget(d, iFalse); |
585 | setFocus_Widget(NULL); | 593 | setFocus_Widget(NULL); |
586 | return iTrue; | 594 | return (d->inFlags & eatEscape_InputWidgetFlag) != 0; |
587 | case SDLK_BACKSPACE: | 595 | case SDLK_BACKSPACE: |
588 | if (!isEmpty_Range(&d->mark)) { | 596 | if (!isEmpty_Range(&d->mark)) { |
589 | pushUndo_InputWidget_(d); | 597 | pushUndo_InputWidget_(d); |
@@ -771,7 +779,7 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
771 | if (isFocused && d->cursorVis) { | 779 | if (isFocused && d->cursorVis) { |
772 | iString cur; | 780 | iString cur; |
773 | if (d->cursor < size_Array(&d->text)) { | 781 | if (d->cursor < size_Array(&d->text)) { |
774 | if (!d->isSensitive) { | 782 | if (~d->inFlags & isSensitive_InputWidgetFlag) { |
775 | initUnicodeN_String(&cur, constAt_Array(&d->text, d->cursor), 1); | 783 | initUnicodeN_String(&cur, constAt_Array(&d->text, d->cursor), 1); |
776 | } | 784 | } |
777 | else { | 785 | else { |
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index f146ea00..7fb8a8bd 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h | |||
@@ -41,6 +41,7 @@ void setTextCStr_InputWidget (iInputWidget *, const char *cstr); | |||
41 | void setCursor_InputWidget (iInputWidget *, size_t pos); | 41 | void setCursor_InputWidget (iInputWidget *, size_t pos); |
42 | void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); | 42 | void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); |
43 | void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits); | 43 | void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits); |
44 | void setEatEscape_InputWidget(iInputWidget *, iBool eatEscape); | ||
44 | void begin_InputWidget (iInputWidget *); | 45 | void begin_InputWidget (iInputWidget *); |
45 | void end_InputWidget (iInputWidget *, iBool accept); | 46 | void end_InputWidget (iInputWidget *, iBool accept); |
46 | void selectAll_InputWidget (iInputWidget *); | 47 | void selectAll_InputWidget (iInputWidget *); |
diff --git a/src/ui/window.c b/src/ui/window.c index 1bf9065e..867f535d 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -486,6 +486,8 @@ static void setupUserInterface_Window(iWindow *d) { | |||
486 | addChildFlags_Widget( | 486 | addChildFlags_Widget( |
487 | searchBar, iClob(new_LabelWidget("\U0001f50d Text", NULL)), frameless_WidgetFlag); | 487 | searchBar, iClob(new_LabelWidget("\U0001f50d Text", NULL)), frameless_WidgetFlag); |
488 | iInputWidget *input = new_InputWidget(0); | 488 | iInputWidget *input = new_InputWidget(0); |
489 | setSelectAllOnFocus_InputWidget(input, iTrue); | ||
490 | setEatEscape_InputWidget(input, iFalse); /* unfocus and close with one keypress */ | ||
489 | setId_Widget(addChildFlags_Widget(searchBar, iClob(input), expand_WidgetFlag), | 491 | setId_Widget(addChildFlags_Widget(searchBar, iClob(input), expand_WidgetFlag), |
490 | "find.input"); | 492 | "find.input"); |
491 | addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9f ", 'g', KMOD_PRIMARY, "find.next"))); | 493 | addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9f ", 'g', KMOD_PRIMARY, "find.next"))); |