From 202690de40057ee224eb3dcaedad57204f8db37c Mon Sep 17 00:00:00 2001 From: Ben Whittaker Date: Mon, 4 Oct 2021 15:48:43 -0400 Subject: [PATCH] Layout: Implement text wrapping --- modules/Layout.js | 39 ++++++++++++++++++++++++++++---- tests/Layout/tests/wrapping.bmp | Bin 0 -> 15562 bytes tests/Layout/tests/wrapping.js | 7 ++++++ 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 tests/Layout/tests/wrapping.bmp create mode 100644 tests/Layout/tests/wrapping.js diff --git a/modules/Layout.js b/modules/Layout.js index 5ac0cab16..69f257b3b 100644 --- a/modules/Layout.js +++ b/modules/Layout.js @@ -36,6 +36,8 @@ layoutObject has: * A `id` field. If specified the object is added with this name to the returned `layout` object, so can be referenced as `layout.foo` * A `font` field, eg `6x8` or `30%` to use a percentage of screen height +* A `wrap` field to enable line wrapping. Requires some combination of `width`/`height` + and `fillx`/`filly` to be set. Not compatible with text rotation. * A `col` field, eg `#f00` for red * A `bgCol` field for background color (will automatically fill on render) * A `halign` field to set horizontal alignment. `-1`=left, `1`=right, `0`=center @@ -152,6 +154,24 @@ Layout.prototype.remove = function (l) { } }; +function wrappedLines(str, maxWidth) { + var lines = []; + for (var unwrappedLine of str.split("\n")) { + var words = unwrappedLine.split(" "); + var line = words.shift(); + for (var word of words) { + if (g.stringWidth(line + " " + word) > maxWidth) { + lines.push(line); + line = word; + } else { + line += " " + word; + } + } + lines.push(line); + } + return lines; +} + function prepareLazyRender(l, rectsToClear, drawList, rects, parentBg) { var bgCol = l.bgCol == null ? parentBg : g.toColor(l.bgCol); if (bgCol != parentBg || l.type == "txt" || l.type == "btn" || l.type == "img" || l.type == "custom") { @@ -187,7 +207,14 @@ Layout.prototype.render = function (l) { var cb = { "":function(){}, "txt":function(l){ - g.setFont(l.font,l.fsz).setFontAlign(0,0,l.r).drawString(l.label, l.x+(l.w>>1), l.y+(l.h>>1)); + if (l.wrap) { + g.setFont(l.font,l.fsz).setFontAlign(0,-1); + var lines = wrappedLines(l.label, l.w); + var y = l.y+((l.h-g.getFontHeight()*lines.length)>>1); + lines.forEach((line, i) => g.drawString(line, l.x+(l.w>>1), y+g.getFontHeight()*i)); + } else { + g.setFont(l.font,l.fsz).setFontAlign(0,0,l.r).drawString(l.label, l.x+(l.w>>1), l.y+(l.h>>1)); + } }, "btn":function(l){ var x = l.x+(0|l.pad); var y = l.y+(0|l.pad); @@ -305,9 +332,13 @@ Layout.prototype.update = function() { l.font = f[0]; l.fsz = f[1]; } - g.setFont(l.font,l.fsz); - l._h = g.getFontHeight(); - l._w = g.stringWidth(l.label); + if (l.wrap) { + l._h = l._w = 0; + } else { + g.setFont(l.font,l.fsz); + l._h = g.getFontHeight(); + l._w = g.stringWidth(l.label); + } }, "btn": function(l) { l._h = 24; l._w = 14 + l.label.length*8; diff --git a/tests/Layout/tests/wrapping.bmp b/tests/Layout/tests/wrapping.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a0d80cc5be495c0a20ea7178ea36ff16e032372d GIT binary patch literal 15562 zcmeHN%Wd2+5M_WI1E>`jkb@EA6d*n1;9^{YOYkvAmk^|al(EM>7*O7OGo&ajwbF`G z5GUly)M!XP4j+d@ef;U`&yPXr3-S;6`;Onc@SeK+&-agyk1sDTcMnhD?pt^W;W?b2 z!jBMe{S?k2{0a}}@OnPKLT;%sa1;aG6u1lg^{1{sv8eAMBgZB6NL|m2bx@?klve+Q zYHT%wOSS!VfKVk0&<1s9N?a$U2_2G@ZWbF@V!7(5e@HxJwX_*72LZdhGZPF~_DUa7 z%>dRhHO}#fy1`yAV%w9KvND-q(SS(_3K9yAa(n9_xJN!D zy(wHgIdmsJK7^ijn^aFrkEbg_+oj_?OP1A`3ksa#L~7mxq7#<%v3folv@FXuA2`E|LhMvJ#d zx5giB3k*1VDJ6ls27k(Qg4*h>H%|36P9ZQza;q5QO0^!7H6!j|?~b?vkM?q#o7gMW z2757bA2@gr{+I`;{t{fe5Z^{xmM&bE72RkrL%I0_*{}=otGfuhnb_hF+%M6Ze|*EM z3&*98K}|ELGC)$1{A3q<5{YUb1rAOSmf|4F|R+C0q<}&wGXe*FxL8Eg+Vj zs}C~ddi>evXAR#JM6W6^_oXwk6%j5XyWY;w}ZX#m;DNcWEyzu1R!laq2#DPfIFgEkeI)1_j= zMX5dWtinaKWrHmKS{O>85NM71RLD)8np$K)oS1825m>N-h7eW%D3Ae@T8&kWtqXR? z0pL)UNcVPzD|@v%gv%^Tn{X8%ZOI9f_Lgvs4YSeuRCiD_7J!G$5Qs!k{52jhCJ5uX zWhm#Iljta+r6RnkxreaQZAnekmHbRl1FmL$8MoBwc!-mEi#CocMeWt|xXD^vi(wpz zY)!(@RSm91U0@|)H(aw9xYL=fv)5xz^;Qwb2_P51I!3y;TUr8` z(Ouvcm&ItRR&{cL3oj~p{)|~s%?JS52CjEeMDcGJ(BioLbrb+|T#T3zl~bZjGeb75 z*J#SBbI1RVb9?v1