Skip to content

Commit 21c4c84

Browse files
committed
Wide character support in bar meter text
1 parent 9706893 commit 21c4c84

File tree

1 file changed

+121
-46
lines changed

1 file changed

+121
-46
lines changed

Meter.c

Lines changed: 121 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -109,71 +109,146 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
109109
}
110110
x++;
111111

112-
// The text in the bar is right aligned;
113-
// Pad with maximal spaces and then calculate needed starting position offset
114-
RichString_begin(bar);
115-
RichString_appendChr(&bar, 0, ' ', w);
116-
RichString_appendWide(&bar, 0, this->txtBuffer);
117-
118-
int startPos = RichString_sizeVal(bar) - w;
119-
if (startPos > w) {
120-
// Text is too large for bar
121-
// Truncate meter text at a space character
122-
for (int pos = 2 * w; pos > w; pos--) {
123-
if (RichString_getCharVal(bar, pos) == ' ') {
124-
while (pos > w && RichString_getCharVal(bar, pos - 1) == ' ')
125-
pos--;
126-
startPos = pos - w;
127-
break;
112+
// Calculate the number of terminal columns needed for the meter text.
113+
114+
// The text in the bar is right aligned
115+
116+
MBStringDecoderState state;
117+
memset(&state, 0, sizeof(state));
118+
state.str = this->txtBuffer;
119+
state.maxLen = sizeof(this->txtBuffer) - 1;
120+
121+
int nColsLeft = w; // pun intended
122+
int savedCols = nColsLeft;
123+
size_t len = 0;
124+
size_t savedLen = 0;
125+
126+
while (String_decodeNextWChar(&state)) {
127+
if (state.ch == 0)
128+
break;
129+
130+
if (state.ch == ' ') {
131+
savedLen = len;
132+
savedCols = nColsLeft;
133+
}
134+
135+
#ifdef HAVE_LIBNCURSESW
136+
int nCols = wcwidth((wchar_t)state.ch);
137+
if (nCols < 0) {
138+
assert(nCols >= 0);
139+
break;
140+
}
141+
#else
142+
int nCols = 1;
143+
#endif
144+
145+
if (nCols > nColsLeft) {
146+
// Text is too large for bar
147+
// Truncate meter text at a space character
148+
if (savedLen > 0) {
149+
len = savedLen;
150+
nColsLeft = savedCols;
128151
}
152+
break;
129153
}
130154

131-
// If still too large, print the start not the end
132-
startPos = MINIMUM(startPos, w);
133-
}
155+
nColsLeft -= nCols;
156+
157+
if (state.ch == ' ') {
158+
continue;
159+
}
160+
161+
#ifdef HAVE_LIBNCURSESW
162+
// If the character takes zero columns, include the character in the
163+
// substring if the working encoding is UTF-8, and ignore it otherwise.
164+
if (nCols <= 0 && !CRT_utf8) {
165+
continue;
166+
}
167+
#endif
134168

135-
assert(startPos >= 0);
136-
assert(startPos <= w);
137-
assert(startPos + w <= RichString_sizeVal(bar));
169+
len = (size_t)(state.str - this->txtBuffer);
170+
}
138171

139-
int blockSizes[10];
172+
RichString_begin(bar);
173+
RichString_appendChr(&bar, 0, ' ', nColsLeft);
174+
RichString_appendnWide(&bar, 0, this->txtBuffer, len);
140175

141-
// First draw in the bar[] buffer...
176+
size_t charPos = 0;
142177
int offset = 0;
143178
for (uint8_t i = 0; i < this->curItems; i++) {
179+
if (!(this->total > 0.0)) {
180+
break;
181+
}
182+
if (offset >= w) {
183+
break;
184+
}
185+
144186
double value = this->values[i];
145-
if (isPositive(value) && this->total > 0.0) {
146-
value = MINIMUM(value, this->total);
147-
blockSizes[i] = ceil((value / this->total) * w);
148-
blockSizes[i] = MINIMUM(blockSizes[i], w - offset);
149-
} else {
150-
blockSizes[i] = 0;
187+
if (!isPositive(value)) {
188+
continue;
151189
}
152-
int nextOffset = offset + blockSizes[i];
153-
for (int j = offset; j < nextOffset; j++)
154-
if (RichString_getCharVal(bar, startPos + j) == ' ') {
190+
value = MINIMUM(value, this->total);
191+
int blockSize = ceil((value / this->total) * w);
192+
blockSize = MINIMUM(blockSize, w - offset);
193+
if (blockSize < 1) {
194+
continue;
195+
}
196+
197+
int nextOffset = offset + blockSize;
198+
assert(offset < nextOffset);
199+
200+
size_t startPos = charPos;
201+
while (true) {
202+
if (offset >= nextOffset) {
203+
#ifdef HAVE_LIBNCURSESW
204+
if (!CRT_utf8) {
205+
break;
206+
}
207+
#else
208+
break;
209+
#endif
210+
}
211+
212+
#ifdef HAVE_LIBNCURSESW
213+
wchar_t ch = RichString_getCharVal(bar, charPos);
214+
if (ch == 0)
215+
break;
216+
217+
int nCols = wcwidth(ch);
218+
assert(nCols >= 0);
219+
220+
if (offset >= nextOffset && nCols > 0) {
221+
// This break condition is for UTF-8.
222+
break;
223+
}
224+
#else
225+
char ch = RichString_getCharVal(bar, charPos);
226+
int nCols = 1;
227+
228+
assert(offset < nextOffset);
229+
#endif
230+
if (ch == ' ') {
155231
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
156232
assert(i < strlen(BarMeterMode_characters));
157-
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
233+
RichString_setChar(&bar, charPos, BarMeterMode_characters[i]);
158234
} else {
159-
RichString_setChar(&bar, startPos + j, '|');
235+
RichString_setChar(&bar, charPos, '|');
160236
}
161237
}
162-
offset = nextOffset;
163-
}
164238

165-
// ...then print the buffer.
166-
offset = 0;
167-
for (uint8_t i = 0; i < this->curItems; i++) {
239+
offset += nCols;
240+
charPos++;
241+
}
242+
168243
int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
169-
RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]);
170-
RichString_printoffnVal(bar, y, x + offset, startPos + offset, blockSizes[i]);
171-
offset += blockSizes[i];
244+
RichString_setAttrn(&bar, CRT_colors[attr], startPos, charPos - startPos);
172245
}
173-
if (offset < w) {
174-
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
175-
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
246+
247+
len = RichString_sizeVal(bar);
248+
if (charPos < len) {
249+
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], charPos, len - charPos);
176250
}
251+
RichString_printVal(bar, y, x);
177252

178253
RichString_delete(&bar);
179254

0 commit comments

Comments
 (0)