diff options
-rw-r--r-- | src/ui/inputwidget.c | 117 | ||||
-rw-r--r-- | src/ui/text.c | 51 | ||||
-rw-r--r-- | src/ui/text.h | 7 |
3 files changed, 125 insertions, 50 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 5f86f5bf..51447f21 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -455,31 +455,46 @@ void setContentPadding_InputWidget(iInputWidget *d, int left, int right) { | |||
455 | refresh_Widget(d); | 455 | refresh_Widget(d); |
456 | } | 456 | } |
457 | 457 | ||
458 | static iBool isHintVisible_InputWidget_(const iInputWidget *d) { | ||
459 | return !isEmpty_String(&d->hint) && size_Array(&d->lines) == 1 && | ||
460 | isEmpty_String(&line_InputWidget_(d, 0)->text); | ||
461 | } | ||
462 | |||
458 | static void updateBuffered_InputWidget_(iInputWidget *d) { | 463 | static void updateBuffered_InputWidget_(iInputWidget *d) { |
459 | invalidateBuffered_InputWidget_(d); | 464 | invalidateBuffered_InputWidget_(d); |
460 | iString *bufText = NULL; | 465 | if (isHintVisible_InputWidget_(d)) { |
466 | d->buffered = new_TextBuf(d->font, uiAnnotation_ColorId, cstr_String(&d->hint)); | ||
467 | } | ||
468 | else { | ||
469 | iString *bufText = NULL; | ||
461 | #if 0 | 470 | #if 0 |
462 | if (d->inFlags & isUrl_InputWidgetFlag && as_Widget(d)->root == win->keyRoot) { | 471 | if (d->inFlags & isUrl_InputWidgetFlag && as_Widget(d)->root == win->keyRoot) { |
463 | /* TODO: Move this omitting to `updateLines_`? */ | 472 | /* TODO: Move this omitting to `updateLines_`? */ |
464 | /* Highlight the host name. */ | 473 | /* Highlight the host name. */ |
465 | iUrl parts; | 474 | iUrl parts; |
466 | const iString *text = collect_String(utf32toUtf8_InputWidget_(d)); | 475 | const iString *text = collect_String(utf32toUtf8_InputWidget_(d)); |
467 | init_Url(&parts, text); | 476 | init_Url(&parts, text); |
468 | if (!isEmpty_Range(&parts.host)) { | 477 | if (!isEmpty_Range(&parts.host)) { |
469 | bufText = new_String(); | 478 | bufText = new_String(); |
470 | appendRange_String(bufText, (iRangecc){ constBegin_String(text), parts.host.start }); | 479 | appendRange_String(bufText, (iRangecc){ constBegin_String(text), parts.host.start }); |
471 | appendCStr_String(bufText, uiTextStrong_ColorEscape); | 480 | appendCStr_String(bufText, uiTextStrong_ColorEscape); |
472 | appendRange_String(bufText, parts.host); | 481 | appendRange_String(bufText, parts.host); |
473 | appendCStr_String(bufText, restore_ColorEscape); | 482 | appendCStr_String(bufText, restore_ColorEscape); |
474 | appendRange_String(bufText, (iRangecc){ parts.host.end, constEnd_String(text) }); | 483 | appendRange_String(bufText, (iRangecc){ parts.host.end, constEnd_String(text) }); |
484 | } | ||
475 | } | 485 | } |
476 | } | ||
477 | #endif | 486 | #endif |
478 | if (!bufText) { | 487 | if (!bufText) { |
479 | bufText = visText_InputWidget_(d); | 488 | bufText = visText_InputWidget_(d); |
489 | } | ||
490 | const int maxWidth = contentBounds_InputWidget_(d).size.x; | ||
491 | const int fg = uiInputText_ColorId; | ||
492 | const char *text = cstr_String(bufText); | ||
493 | d->buffered = | ||
494 | (d->inFlags & isUrl_InputWidgetFlag ? newBound_TextBuf(d->font, fg, maxWidth, text) | ||
495 | : newWrap_TextBuf (d->font, fg, maxWidth, text)); | ||
496 | delete_String(bufText); | ||
480 | } | 497 | } |
481 | d->buffered = new_TextBuf(d->font, uiInputText_ColorId, cstr_String(bufText)); | ||
482 | delete_String(bufText); | ||
483 | d->inFlags &= ~needUpdateBuffer_InputWidgetFlag; | 498 | d->inFlags &= ~needUpdateBuffer_InputWidgetFlag; |
484 | } | 499 | } |
485 | 500 | ||
@@ -707,6 +722,7 @@ void setSensitiveContent_InputWidget(iInputWidget *d, iBool isSensitive) { | |||
707 | 722 | ||
708 | void setUrlContent_InputWidget(iInputWidget *d, iBool isUrl) { | 723 | void setUrlContent_InputWidget(iInputWidget *d, iBool isUrl) { |
709 | iChangeFlags(d->inFlags, isUrl_InputWidgetFlag, isUrl); | 724 | iChangeFlags(d->inFlags, isUrl_InputWidgetFlag, isUrl); |
725 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | ||
710 | } | 726 | } |
711 | 727 | ||
712 | void setSelectAllOnFocus_InputWidget(iInputWidget *d, iBool selectAllOnFocus) { | 728 | void setSelectAllOnFocus_InputWidget(iInputWidget *d, iBool selectAllOnFocus) { |
@@ -963,6 +979,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
963 | updateLinesAndResize_InputWidget_(d); | 979 | updateLinesAndResize_InputWidget_(d); |
964 | } | 980 | } |
965 | else if (isResize_UserEvent(ev)) { | 981 | else if (isResize_UserEvent(ev)) { |
982 | d->inFlags |= needUpdateBuffer_InputWidgetFlag; | ||
966 | if (d->inFlags & isUrl_InputWidgetFlag) { | 983 | if (d->inFlags & isUrl_InputWidgetFlag) { |
967 | /* Restore/omit the default scheme if necessary. */ | 984 | /* Restore/omit the default scheme if necessary. */ |
968 | setText_InputWidget(d, text_InputWidget(d)); | 985 | setText_InputWidget(d, text_InputWidget(d)); |
@@ -1260,7 +1277,7 @@ static iBool isWhite_(const iString *str) { | |||
1260 | static void draw_InputWidget_(const iInputWidget *d) { | 1277 | static void draw_InputWidget_(const iInputWidget *d) { |
1261 | const iWidget *w = constAs_Widget(d); | 1278 | const iWidget *w = constAs_Widget(d); |
1262 | iRect bounds = adjusted_Rect(bounds_InputWidget_(d), padding_(), neg_I2(padding_())); | 1279 | iRect bounds = adjusted_Rect(bounds_InputWidget_(d), padding_(), neg_I2(padding_())); |
1263 | iBool isHint = iFalse; | 1280 | iBool isHint = isHintVisible_InputWidget_(d); |
1264 | const iBool isFocused = isFocused_Widget(w); | 1281 | const iBool isFocused = isFocused_Widget(w); |
1265 | const iBool isHover = isHover_Widget(w) && | 1282 | const iBool isHover = isHover_Widget(w) && |
1266 | contains_InputWidget_(d, mouseCoord_Window(get_Window())); | 1283 | contains_InputWidget_(d, mouseCoord_Window(get_Window())); |
@@ -1290,33 +1307,43 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
1290 | const int fg = isHint ? uiAnnotation_ColorId | 1307 | const int fg = isHint ? uiAnnotation_ColorId |
1291 | : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId | 1308 | : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId |
1292 | : uiInputText_ColorId; | 1309 | : uiInputText_ColorId; |
1293 | /* TODO: If buffered, just draw the buffered copy. */ | 1310 | /* If buffered, just draw the buffered copy. */ |
1294 | iConstForEach(Array, i, &d->lines) { | 1311 | if (d->buffered && !isFocused) { //&& !isFocused/* && !isHint*/) { |
1295 | const iInputLine *line = i.value; | 1312 | /* Most input widgets will use this, since only one is focused at a time. */ |
1296 | const iBool isLast = index_ArrayConstIterator(&i) == size_Array(&d->lines) - 1; | 1313 | draw_TextBuf(d->buffered, topLeft_Rect(contentBounds), white_ColorId); |
1297 | const iInputLine *nextLine = isLast ? NULL : (line + 1); | 1314 | } |
1298 | const iRanges lineRange = { line->offset, | 1315 | else if (isHint) { |
1299 | nextLine ? nextLine->offset : size_Array(&d->text) }; | 1316 | drawRange_Text(d->font, topLeft_Rect(contentBounds), uiAnnotation_ColorId, |
1300 | if (isFocused && !isEmpty_Range(&d->mark)) { | 1317 | range_String(&d->hint)); |
1301 | /* Draw the selected range. */ | 1318 | } |
1302 | const iRanges mark = mark_InputWidget_(d); | 1319 | else { |
1303 | if (mark.start < lineRange.end && mark.end > lineRange.start) { | 1320 | iConstForEach(Array, i, &d->lines) { |
1304 | const int m1 = advanceN_Text(d->font, | 1321 | const iInputLine *line = i.value; |
1305 | cstr_String(&line->text), | 1322 | const iBool isLast = index_ArrayConstIterator(&i) == size_Array(&d->lines) - 1; |
1306 | iMax(lineRange.start, mark.start) - line->offset) | 1323 | const iInputLine *nextLine = isLast ? NULL : (line + 1); |
1307 | .x; | 1324 | const iRanges lineRange = { line->offset, |
1308 | const int m2 = advanceN_Text(d->font, | 1325 | nextLine ? nextLine->offset : size_Array(&d->text) }; |
1309 | cstr_String(&line->text), | 1326 | if (isFocused && !isEmpty_Range(&d->mark)) { |
1310 | iMin(lineRange.end, mark.end) - line->offset) | 1327 | /* Draw the selected range. */ |
1311 | .x; | 1328 | const iRanges mark = mark_InputWidget_(d); |
1312 | fillRect_Paint(&p, | 1329 | if (mark.start < lineRange.end && mark.end > lineRange.start) { |
1313 | (iRect){ addX_I2(drawPos, iMin(m1, m2)), | 1330 | const int m1 = advanceN_Text(d->font, |
1314 | init_I2(iAbs(m2 - m1), lineHeight_Text(d->font)) }, | 1331 | cstr_String(&line->text), |
1315 | uiMarked_ColorId); | 1332 | iMax(lineRange.start, mark.start) - line->offset) |
1333 | .x; | ||
1334 | const int m2 = advanceN_Text(d->font, | ||
1335 | cstr_String(&line->text), | ||
1336 | iMin(lineRange.end, mark.end) - line->offset) | ||
1337 | .x; | ||
1338 | fillRect_Paint(&p, | ||
1339 | (iRect){ addX_I2(drawPos, iMin(m1, m2)), | ||
1340 | init_I2(iAbs(m2 - m1), lineHeight_Text(d->font)) }, | ||
1341 | uiMarked_ColorId); | ||
1342 | } | ||
1316 | } | 1343 | } |
1344 | drawRange_Text(d->font, drawPos, fg, range_String(&line->text)); | ||
1345 | drawPos.y += lineHeight_Text(d->font); | ||
1317 | } | 1346 | } |
1318 | drawRange_Text(d->font, drawPos, fg, range_String(&line->text)); | ||
1319 | drawPos.y += lineHeight_Text(d->font); | ||
1320 | } | 1347 | } |
1321 | // if (d->buffered && !isFocused && !isHint) { | 1348 | // if (d->buffered && !isFocused && !isHint) { |
1322 | // /* Most input widgets will use this, since only one is focused at a time. */ | 1349 | // /* Most input widgets will use this, since only one is focused at a time. */ |
diff --git a/src/ui/text.c b/src/ui/text.c index aacc8d3d..6c9e23cb 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -1404,9 +1404,21 @@ iString *renderBlockChars_Text(const iBlock *fontData, int height, enum iTextBlo | |||
1404 | 1404 | ||
1405 | iDefineTypeConstructionArgs(TextBuf, (int font, int color, const char *text), font, color, text) | 1405 | iDefineTypeConstructionArgs(TextBuf, (int font, int color, const char *text), font, color, text) |
1406 | 1406 | ||
1407 | void init_TextBuf(iTextBuf *d, int font, int color, const char *text) { | 1407 | static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iBool doWrap, const char *text) { |
1408 | SDL_Renderer *render = text_.render; | 1408 | SDL_Renderer *render = text_.render; |
1409 | d->size = advance_Text(font, text); | 1409 | if (maxWidth == 0) { |
1410 | d->size = advance_Text(font, text); | ||
1411 | } | ||
1412 | else { | ||
1413 | d->size = zero_I2(); | ||
1414 | iRangecc content = range_CStr(text); | ||
1415 | while (!isEmpty_Range(&content)) { | ||
1416 | const iInt2 size = (doWrap ? tryAdvance_Text(font, content, maxWidth, &content.start) | ||
1417 | : tryAdvanceNoWrap_Text(font, content, maxWidth, &content.start)); | ||
1418 | d->size.x = iMax(d->size.x, size.x); | ||
1419 | d->size.y += size.y; | ||
1420 | } | ||
1421 | } | ||
1410 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); | 1422 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); |
1411 | if (d->size.x * d->size.y) { | 1423 | if (d->size.x * d->size.y) { |
1412 | d->texture = SDL_CreateTexture(render, | 1424 | d->texture = SDL_CreateTexture(render, |
@@ -1424,17 +1436,50 @@ void init_TextBuf(iTextBuf *d, int font, int color, const char *text) { | |||
1424 | SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */ | 1436 | SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */ |
1425 | SDL_SetRenderDrawColor(text_.render, 0, 0, 0, 0); | 1437 | SDL_SetRenderDrawColor(text_.render, 0, 0, 0, 0); |
1426 | SDL_RenderClear(text_.render); | 1438 | SDL_RenderClear(text_.render); |
1427 | draw_Text_(font, zero_I2(), color | fillBackground_ColorId, range_CStr(text)); | 1439 | const int fg = color | fillBackground_ColorId; |
1440 | iRangecc range = range_CStr(text); | ||
1441 | if (maxWidth == 0) { | ||
1442 | draw_Text_(font, zero_I2(), fg, range); | ||
1443 | } | ||
1444 | else if (doWrap) { | ||
1445 | drawWrapRange_Text(font, zero_I2(), maxWidth, fg, range); | ||
1446 | } | ||
1447 | else { | ||
1448 | iInt2 pos = zero_I2(); | ||
1449 | while (!isEmpty_Range(&range)) { | ||
1450 | const char *endp; | ||
1451 | tryAdvanceNoWrap_Text(font, range, maxWidth, &endp); | ||
1452 | draw_Text_(font, pos, fg, (iRangecc){ range.start, endp }); | ||
1453 | range.start = endp; | ||
1454 | pos.y += lineHeight_Text(font); | ||
1455 | } | ||
1456 | } | ||
1428 | SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND); | 1457 | SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND); |
1429 | SDL_SetRenderTarget(render, oldTarget); | 1458 | SDL_SetRenderTarget(render, oldTarget); |
1430 | SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); | 1459 | SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); |
1431 | } | 1460 | } |
1432 | } | 1461 | } |
1433 | 1462 | ||
1463 | void init_TextBuf(iTextBuf *d, int font, int color, const char *text) { | ||
1464 | initWrap_TextBuf_(d, font, color, 0, iFalse, text); | ||
1465 | } | ||
1466 | |||
1434 | void deinit_TextBuf(iTextBuf *d) { | 1467 | void deinit_TextBuf(iTextBuf *d) { |
1435 | SDL_DestroyTexture(d->texture); | 1468 | SDL_DestroyTexture(d->texture); |
1436 | } | 1469 | } |
1437 | 1470 | ||
1471 | iTextBuf *newBound_TextBuf(int font, int color, int boundWidth, const char *text) { | ||
1472 | iTextBuf *d = iMalloc(TextBuf); | ||
1473 | initWrap_TextBuf_(d, font, color, boundWidth, iFalse, text); | ||
1474 | return d; | ||
1475 | } | ||
1476 | |||
1477 | iTextBuf *newWrap_TextBuf(int font, int color, int wrapWidth, const char *text) { | ||
1478 | iTextBuf *d = iMalloc(TextBuf); | ||
1479 | initWrap_TextBuf_(d, font, color, wrapWidth, iTrue, text); | ||
1480 | return d; | ||
1481 | } | ||
1482 | |||
1438 | void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) { | 1483 | void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) { |
1439 | const iColor clr = get_Color(color); | 1484 | const iColor clr = get_Color(color); |
1440 | SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b); | 1485 | SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b); |
diff --git a/src/ui/text.h b/src/ui/text.h index c6091599..044ddd32 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -170,10 +170,13 @@ iString * renderBlockChars_Text (const iBlock *fontData, int height, enum iT | |||
170 | 170 | ||
171 | iDeclareType(TextBuf) | 171 | iDeclareType(TextBuf) |
172 | iDeclareTypeConstructionArgs(TextBuf, int font, int color, const char *text) | 172 | iDeclareTypeConstructionArgs(TextBuf, int font, int color, const char *text) |
173 | 173 | ||
174 | struct Impl_TextBuf { | 174 | struct Impl_TextBuf { |
175 | SDL_Texture *texture; | 175 | SDL_Texture *texture; |
176 | iInt2 size; | 176 | iInt2 size; |
177 | }; | 177 | }; |
178 | 178 | ||
179 | void draw_TextBuf (const iTextBuf *, iInt2 pos, int color); | 179 | iTextBuf * newBound_TextBuf(int font, int color, int boundWidth, const char *text); /* does not word wrap */ |
180 | iTextBuf * newWrap_TextBuf (int font, int color, int wrapWidth, const char *text); | ||
181 | |||
182 | void draw_TextBuf (const iTextBuf *, iInt2 pos, int color); | ||