summaryrefslogtreecommitdiff
path: root/src/ui/inputwidget.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-05-12 09:06:11 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-05-12 09:06:11 +0300
commit4e84a9ff371e69ba828f8678c5cec89eea45d2ab (patch)
tree332a76e480b197c1bc814ff8dd4d9b46ebe4431e /src/ui/inputwidget.c
parent892d8dedc46713ddf916e82405f06fc2c9c46ae4 (diff)
InputWidget: Expanding while editing
The navbar layout cannot accommodate more than one line of text, but allow the editor to expand while writing text.
Diffstat (limited to 'src/ui/inputwidget.c')
-rw-r--r--src/ui/inputwidget.c61
1 files changed, 50 insertions, 11 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 22dd4c92..0fd80ca5 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -104,6 +104,7 @@ struct Impl_InputWidget {
104 enum iInputMode mode; 104 enum iInputMode mode;
105 int inFlags; 105 int inFlags;
106 size_t maxLen; 106 size_t maxLen;
107 size_t maxLayoutLines;
107 iArray text; /* iChar[] */ 108 iArray text; /* iChar[] */
108 iArray oldText; /* iChar[] */ 109 iArray oldText; /* iChar[] */
109 iArray lines; 110 iArray lines;
@@ -162,7 +163,8 @@ iLocalDef iInt2 padding_(void) {
162} 163}
163 164
164static iRect contentBounds_InputWidget_(const iInputWidget *d) { 165static iRect contentBounds_InputWidget_(const iInputWidget *d) {
165 const iWidget *w = constAs_Widget(d); 166 const iWidget *w = constAs_Widget(d);
167 const iRect widgetBounds = bounds_Widget(w);
166 iRect bounds = adjusted_Rect(bounds_Widget(w), 168 iRect bounds = adjusted_Rect(bounds_Widget(w),
167 addX_I2(padding_(), d->leftPadding), 169 addX_I2(padding_(), d->leftPadding),
168 neg_I2(addX_I2(padding_(), d->rightPadding))); 170 neg_I2(addX_I2(padding_(), d->rightPadding)));
@@ -233,7 +235,12 @@ static void updateLines_InputWidget_(iInputWidget *d) {
233 const int wrapWidth = contentBounds_InputWidget_(d).size.x; 235 const int wrapWidth = contentBounds_InputWidget_(d).size.x;
234 while (wrapWidth > 0 && content.end != content.start) { 236 while (wrapWidth > 0 && content.end != content.start) {
235 const char *endPos; 237 const char *endPos;
236 tryAdvance_Text(d->font, content, wrapWidth, &endPos); 238 if (d->inFlags & isUrl_InputWidgetFlag) {
239 tryAdvanceNoWrap_Text(d->font, content, wrapWidth, &endPos);
240 }
241 else {
242 tryAdvance_Text(d->font, content, wrapWidth, &endPos);
243 }
237 const iRangecc part = (iRangecc){ content.start, endPos }; 244 const iRangecc part = (iRangecc){ content.start, endPos };
238 iInputLine line; 245 iInputLine line;
239 init_InputLine(&line); 246 init_InputLine(&line);
@@ -256,15 +263,19 @@ static void updateLines_InputWidget_(iInputWidget *d) {
256 updateCursorLine_InputWidget_(d); 263 updateCursorLine_InputWidget_(d);
257} 264}
258 265
259static int contentHeight_InputWidget_(const iInputWidget *d) { 266static int contentHeight_InputWidget_(const iInputWidget *d, iBool forLayout) {
260 return iMax(1, size_Array(&d->lines)) * lineHeight_Text(d->font); 267 size_t numLines = iMax(1, size_Array(&d->lines));
268 if (forLayout) {
269 numLines = iMin(numLines, d->maxLayoutLines);
270 }
271 return numLines * lineHeight_Text(d->font);
261} 272}
262 273
263static void updateMetrics_InputWidget_(iInputWidget *d) { 274static void updateMetrics_InputWidget_(iInputWidget *d) {
264 iWidget *w = as_Widget(d); 275 iWidget *w = as_Widget(d);
265 updateSizeForFixedLength_InputWidget_(d); 276 updateSizeForFixedLength_InputWidget_(d);
266 /* Caller must arrange the width, but the height is fixed. */ 277 /* Caller must arrange the width, but the height is fixed. */
267 w->rect.size.y = contentHeight_InputWidget_(d) + 3 * padding_().y; /* TODO: Why 3x? */ 278 w->rect.size.y = contentHeight_InputWidget_(d, iTrue) + 3 * padding_().y; /* TODO: Why 3x? */
268 if (flags_Widget(w) & extraPadding_WidgetFlag) { 279 if (flags_Widget(w) & extraPadding_WidgetFlag) {
269 w->rect.size.y += 2 * gap_UI; 280 w->rect.size.y += 2 * gap_UI;
270 } 281 }
@@ -276,6 +287,7 @@ static void updateLinesAndResize_InputWidget_(iInputWidget *d) {
276 const size_t oldCount = size_Array(&d->lines); 287 const size_t oldCount = size_Array(&d->lines);
277 updateLines_InputWidget_(d); 288 updateLines_InputWidget_(d);
278 if (oldCount != size_Array(&d->lines)) { 289 if (oldCount != size_Array(&d->lines)) {
290 d->click.minHeight = contentHeight_InputWidget_(d, iFalse);
279 updateMetrics_InputWidget_(d); 291 updateMetrics_InputWidget_(d);
280 } 292 }
281} 293}
@@ -303,6 +315,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
303 d->inFlags = eatEscape_InputWidgetFlag; 315 d->inFlags = eatEscape_InputWidgetFlag;
304 iZap(d->mark); 316 iZap(d->mark);
305 setMaxLen_InputWidget(d, maxLen); 317 setMaxLen_InputWidget(d, maxLen);
318 d->maxLayoutLines = iInvalidSize;
306 setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); 319 setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue);
307 init_Click(&d->click, d, SDL_BUTTON_LEFT); 320 init_Click(&d->click, d, SDL_BUTTON_LEFT);
308 d->timer = 0; 321 d->timer = 0;
@@ -405,6 +418,11 @@ void setMaxLen_InputWidget(iInputWidget *d, size_t maxLen) {
405 updateSizeForFixedLength_InputWidget_(d); 418 updateSizeForFixedLength_InputWidget_(d);
406} 419}
407 420
421void setMaxLayoutLines_InputWidget(iInputWidget *d, size_t maxLayoutLines) {
422 d->maxLayoutLines = maxLayoutLines;
423 updateMetrics_InputWidget_(d);
424}
425
408void setHint_InputWidget(iInputWidget *d, const char *hintText) { 426void setHint_InputWidget(iInputWidget *d, const char *hintText) {
409 /* Keep original for retranslations. */ 427 /* Keep original for retranslations. */
410 setCStr_String(&d->srcHint, hintText); 428 setCStr_String(&d->srcHint, hintText);
@@ -532,7 +550,7 @@ void begin_InputWidget(iInputWidget *d) {
532 } 550 }
533 updateCursorLine_InputWidget_(d); 551 updateCursorLine_InputWidget_(d);
534 SDL_StartTextInput(); 552 SDL_StartTextInput();
535 setFlags_Widget(w, selected_WidgetFlag, iTrue); 553 setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iTrue);
536 showCursor_InputWidget_(d); 554 showCursor_InputWidget_(d);
537 refresh_Widget(w); 555 refresh_Widget(w);
538 d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d); 556 d->timer = SDL_AddTimer(refreshInterval_InputWidget_, cursorTimer_, d);
@@ -560,7 +578,7 @@ void end_InputWidget(iInputWidget *d, iBool accept) {
560 SDL_RemoveTimer(d->timer); 578 SDL_RemoveTimer(d->timer);
561 d->timer = 0; 579 d->timer = 0;
562 SDL_StopTextInput(); 580 SDL_StopTextInput();
563 setFlags_Widget(w, selected_WidgetFlag, iFalse); 581 setFlags_Widget(w, selected_WidgetFlag | keepOnTop_WidgetFlag, iFalse);
564 const char *id = cstr_String(id_Widget(as_Widget(d))); 582 const char *id = cstr_String(id_Widget(as_Widget(d)));
565 if (!*id) id = "_"; 583 if (!*id) id = "_";
566 updateLinesAndResize_InputWidget_(d); 584 updateLinesAndResize_InputWidget_(d);
@@ -866,6 +884,20 @@ static void extendRange_InputWidget_(iInputWidget *d, size_t *pos, int dir) {
866 } 884 }
867} 885}
868 886
887static iRect bounds_InputWidget_(const iInputWidget *d) {
888 const iWidget *w = constAs_Widget(d);
889 iRect bounds = bounds_Widget(w);
890 if (!isFocused_Widget(d)) {
891 return bounds;
892 }
893 bounds.size.y = contentHeight_InputWidget_(d, iFalse) + 3 * padding_().y;
894 return bounds;
895}
896
897static iBool contains_InputWidget_(const iInputWidget *d, iInt2 coord) {
898 return contains_Rect(bounds_InputWidget_(d), coord);
899}
900
869static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { 901static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
870 iWidget *w = as_Widget(d); 902 iWidget *w = as_Widget(d);
871 if (isCommand_Widget(w, ev, "focus.gained")) { 903 if (isCommand_Widget(w, ev, "focus.gained")) {
@@ -925,8 +957,9 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
925 copy_InputWidget_(d, iFalse); 957 copy_InputWidget_(d, iFalse);
926 return iTrue; 958 return iTrue;
927 } 959 }
928 if (ev->type == SDL_MOUSEMOTION && isHover_Widget(d)) { 960 if (ev->type == SDL_MOUSEMOTION && (isHover_Widget(d) || flags_Widget(w) & keepOnTop_WidgetFlag)) {
929 const iInt2 inner = windowToInner_Widget(w, init_I2(ev->motion.x, ev->motion.y)); 961 const iInt2 coord = init_I2(ev->motion.x, ev->motion.y);
962 const iInt2 inner = windowToInner_Widget(w, coord);
930 setCursor_Window(get_Window(), 963 setCursor_Window(get_Window(),
931 inner.x >= 2 * gap_UI + d->leftPadding && 964 inner.x >= 2 * gap_UI + d->leftPadding &&
932 inner.x < width_Widget(w) - d->rightPadding 965 inner.x < width_Widget(w) - d->rightPadding
@@ -976,6 +1009,12 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
976 d->inFlags &= ~isMarking_InputWidgetFlag; 1009 d->inFlags &= ~isMarking_InputWidgetFlag;
977 return iTrue; 1010 return iTrue;
978 } 1011 }
1012 if (ev->type == SDL_MOUSEMOTION && flags_Widget(w) & keepOnTop_WidgetFlag) {
1013 const iInt2 coord = init_I2(ev->motion.x, ev->motion.y);
1014 if (contains_Click(&d->click, coord)) {
1015 return iTrue;
1016 }
1017 }
979 if (ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT && 1018 if (ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT &&
980 contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { 1019 contains_Widget(w, init_I2(ev->button.x, ev->button.y))) {
981 iWidget *clipMenu = findWidget_App("clipmenu"); 1020 iWidget *clipMenu = findWidget_App("clipmenu");
@@ -1193,11 +1232,11 @@ static iBool isWhite_(const iString *str) {
1193 1232
1194static void draw_InputWidget_(const iInputWidget *d) { 1233static void draw_InputWidget_(const iInputWidget *d) {
1195 const iWidget *w = constAs_Widget(d); 1234 const iWidget *w = constAs_Widget(d);
1196 iRect bounds = adjusted_Rect(bounds_Widget(w), padding_(), neg_I2(padding_())); 1235 iRect bounds = adjusted_Rect(bounds_InputWidget_(d), padding_(), neg_I2(padding_()));
1197 iBool isHint = iFalse; 1236 iBool isHint = iFalse;
1198 const iBool isFocused = isFocused_Widget(w); 1237 const iBool isFocused = isFocused_Widget(w);
1199 const iBool isHover = isHover_Widget(w) && 1238 const iBool isHover = isHover_Widget(w) &&
1200 contains_Widget(w, mouseCoord_Window(get_Window())); 1239 contains_InputWidget_(d, mouseCoord_Window(get_Window()));
1201 if (d->inFlags & needUpdateBuffer_InputWidgetFlag) { 1240 if (d->inFlags & needUpdateBuffer_InputWidgetFlag) {
1202 updateBuffered_InputWidget_(iConstCast(iInputWidget *, d)); 1241 updateBuffered_InputWidget_(iConstCast(iInputWidget *, d));
1203 } 1242 }