Skip to content

Commit

Permalink
wrapString: Allow wrapping between images
Browse files Browse the repository at this point in the history
  • Loading branch information
gfwilliams committed Nov 10, 2023
1 parent f5a683a commit 19868ef
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 31 deletions.
2 changes: 1 addition & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
Bangle.js2: Fix spurious tap events when HRM enabled by changing threshold
Reinstate `if (0);"test"` fix after f87a53c accidentally reverted it
Bangle.js2: Do a soft reset every time we start the SPL06 pressure sensor (stops occasional sensor lockup)
Graphics: wrapString will now wrap lines on comma, dot and dash
Graphics: wrapString will now wrap lines on comma, dot, dash, and between images

2v19 : Fix Object.values/entries for numeric keys after 2v18 regression (fix #2375)
nRF52: for SD>5 use static buffers for advertising and scan response data (#2367)
Expand Down
78 changes: 48 additions & 30 deletions libs/graphics/jswrap_graphics.c
Original file line number Diff line number Diff line change
Expand Up @@ -2355,57 +2355,77 @@ JsVar *jswrap_graphics_wrapString(JsVar *parent, JsVar *str, int maxWidth) {
int spaceWidth = _jswrap_graphics_getCharWidth(&gfx, &info, ' ');
int wordWidth = 0;
int lineWidth = 0;
bool lineHasSpaceAfter = false;
bool canSplitAfter = false;
char wordBreakCharacter = 0; // character that came before wordStartIdx (if space we may have left it off)
int wordStartIdx = 0;
int wordIdxAtMaxWidth = 0; // index just before the word width>maxWidth
int wordWidthAtMaxWidth = 0; // index just before the word width>maxWidth
bool endOfText = false;
bool wasNewLine = false;

/* What we do is try and step over one whole word/image at a time, then when we've got that
(with wordStartIdx at the start) we see whether we have space to append to currentLine,
or whether we have to start a new line.
This is all a bit of a mess but it appears to work as intended now. */
JsvStringIterator it;
jsvStringIteratorNewUTF8(&it, str, 0);

while (jsvStringIteratorHasChar(&it) || endOfText) {
int ch = jsvStringIteratorGetUTF8CharAndNext(&it);
bool canBreakOnCh = endOfText || ch=='\n' || ch==' ';
bool canBreakAfterCh = ch==',' || ch=='.' || ch=='-';
if (canBreakOnCh || canBreakAfterCh) { // newline or space
if (canBreakOnCh || canSplitAfter) { // is breakable - newline,space,dash, image before
int currentPos = (int)jsvStringIteratorGetIndex(&it);
bool includeCh = canBreakAfterCh;
if ((lineWidth + spaceWidth + wordWidth <= maxWidth) &&
!wasNewLine) {
// all on one line
if (lineHasSpaceAfter) {
jsvAppendString(currentLine, " ");
// all on one line, just append the last word
if (wordBreakCharacter && (lineWidth || wordBreakCharacter!=' ')) {
// add the space/etc before (but not a space at the start of a newline)
jsvAppendCharacter(currentLine, wordBreakCharacter);
lineWidth += spaceWidth;
wordBreakCharacter = 0;
}
jsvAppendStringVar(currentLine, str, wordStartIdx, currentPos-(wordStartIdx+ (includeCh?0:1)));
lineHasSpaceAfter = !canBreakAfterCh;
jsvAppendStringVar(currentLine, str, wordStartIdx, currentPos-(wordStartIdx+1));
lineWidth += wordWidth;
} else { // doesn't fit on one line - move to new line
} else { // doesn't fit on one line - put word on new line
lineWidth = wordWidth;
if (jsvGetStringLength(currentLine) || wasNewLine) {
if (jsvGetStringLength(currentLine) || wasNewLine)
jsvArrayPush(lines, currentLine);
}
jsvUnLock(currentLine);
if (wordIdxAtMaxWidth) {
// word is too long to fit on a line, split it
// jsvNewFromStringVar will create a unicode string is str was a unicode string
jsvArrayPushAndUnLock(lines, jsvNewFromStringVar(str, wordStartIdx, wordIdxAtMaxWidth-(wordStartIdx+1)));
wordStartIdx = wordIdxAtMaxWidth-1;
lineWidth -= wordWidthAtMaxWidth;
currentLine = 0;
// if the word is too big to fit in the line, split it until it fits
while (wordWidth > maxWidth) {
int width = 0;
currentLine = jsvNewFromEmptyString();
while (wordStartIdx < currentPos) {
char wordCh = jsvGetCharInString(str, wordStartIdx);
int w = _jswrap_graphics_getCharWidth(&gfx, &info, wordCh);
if (width+w < maxWidth) {
wordStartIdx++;
wordWidth -= w;
lineWidth -= w;
width += w;
jsvAppendCharacter(currentLine, wordCh);
} else
break;
}
jsvArrayPush(lines, currentLine);
jsvUnLock(currentLine);
}
// Add the remaining bit of word
currentLine = jsvNewFromStringVar(str, wordStartIdx, currentPos-(wordStartIdx+1));
lineHasSpaceAfter = !canBreakAfterCh;
// jsvNewFromStringVar will create a unicode string is str was a unicode string
if (wasNewLine) wordBreakCharacter = ' ';
}
if (canSplitAfter && !canBreakOnCh) currentPos--; // include the current ch in the next word
if (canBreakOnCh && ch>0)
wordBreakCharacter = (char)ch;
// we're now starting a new word
wordWidth = 0;
wordIdxAtMaxWidth = 0;
wordStartIdx = currentPos;
wasNewLine = ch=='\n';
canSplitAfter = false;
if (endOfText) break;
continue;
if (ch!=0) continue; // allow us to handle images next
}
canSplitAfter = false;
#ifndef SAVE_ON_FLASH
if (ch==0) { // If images are described in-line in the string, render them
GfxDrawImageInfo img;
Expand All @@ -2415,17 +2435,15 @@ JsVar *jswrap_graphics_wrapString(JsVar *parent, JsVar *str, int maxWidth) {
_jswrap_graphics_freeImageInfo(&img);
// string iterator now points to the next char after image
wordWidth += img.width;
canSplitAfter = true;
if (!jsvStringIteratorHasChar(&it)) endOfText=true;
}
continue;
}
#endif
int w = _jswrap_graphics_getCharWidth(&gfx, &info, ch);
if (wordWidth <= maxWidth && wordWidth+w>maxWidth) {
wordIdxAtMaxWidth = (int)jsvStringIteratorGetIndex(&it);
wordWidthAtMaxWidth = wordWidth;
}
wordWidth += w;
wordWidth += _jswrap_graphics_getCharWidth(&gfx, &info, ch);
if (ch==',' || ch=='.' || ch=='-' || ch=='-' || ch==':')
canSplitAfter = true;
if (!jsvStringIteratorHasChar(&it)) endOfText=true;
}
jsvStringIteratorFree(&it);
Expand Down

0 comments on commit 19868ef

Please sign in to comment.