Skip to content

Commit 3043fb4

Browse files
committed
Wide character support in bar meter text
1 parent 5542a99 commit 3043fb4

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
@@ -110,71 +110,146 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
110110
attrset(CRT_colors[RESET_COLOR]); // Clear the bold attribute
111111
x++;
112112

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

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

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

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

142-
// First draw in the bar[] buffer...
177+
size_t charPos = 0;
143178
int offset = 0;
144179
for (uint8_t i = 0; i < this->curItems; i++) {
180+
if (!(this->total > 0.0)) {
181+
break;
182+
}
183+
if (offset >= w) {
184+
break;
185+
}
186+
145187
double value = this->values[i];
146-
if (isPositive(value) && this->total > 0.0) {
147-
value = MINIMUM(value, this->total);
148-
blockSizes[i] = ceil((value / this->total) * w);
149-
blockSizes[i] = MINIMUM(blockSizes[i], w - offset);
150-
} else {
151-
blockSizes[i] = 0;
188+
if (!isPositive(value)) {
189+
continue;
152190
}
153-
int nextOffset = offset + blockSizes[i];
154-
for (int j = offset; j < nextOffset; j++)
155-
if (RichString_getCharVal(bar, startPos + j) == ' ') {
191+
value = MINIMUM(value, this->total);
192+
int blockSize = ceil((value / this->total) * w);
193+
blockSize = MINIMUM(blockSize, w - offset);
194+
if (blockSize < 1) {
195+
continue;
196+
}
197+
198+
int nextOffset = offset + blockSize;
199+
assert(offset < nextOffset);
200+
201+
size_t startPos = charPos;
202+
while (true) {
203+
if (offset >= nextOffset) {
204+
#ifdef HAVE_LIBNCURSESW
205+
if (!CRT_utf8) {
206+
break;
207+
}
208+
#else
209+
break;
210+
#endif
211+
}
212+
213+
#ifdef HAVE_LIBNCURSESW
214+
wchar_t ch = RichString_getCharVal(bar, charPos);
215+
if (ch == 0)
216+
break;
217+
218+
int nCols = wcwidth(ch);
219+
assert(nCols >= 0);
220+
221+
if (offset >= nextOffset && nCols > 0) {
222+
// This break condition is for UTF-8.
223+
break;
224+
}
225+
#else
226+
char ch = RichString_getCharVal(bar, charPos);
227+
int nCols = 1;
228+
229+
assert(offset < nextOffset);
230+
#endif
231+
if (ch == ' ') {
156232
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
157233
assert(i < strlen(BarMeterMode_characters));
158-
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
234+
RichString_setChar(&bar, charPos, BarMeterMode_characters[i]);
159235
} else {
160-
RichString_setChar(&bar, startPos + j, '|');
236+
RichString_setChar(&bar, charPos, '|');
161237
}
162238
}
163-
offset = nextOffset;
164-
}
165239

166-
// ...then print the buffer.
167-
offset = 0;
168-
for (uint8_t i = 0; i < this->curItems; i++) {
240+
offset += nCols;
241+
charPos++;
242+
}
243+
169244
int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
170-
RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]);
171-
RichString_printoffnVal(bar, y, x + offset, startPos + offset, blockSizes[i]);
172-
offset += blockSizes[i];
245+
RichString_setAttrn(&bar, CRT_colors[attr], startPos, charPos - startPos);
173246
}
174-
if (offset < w) {
175-
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
176-
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
247+
248+
len = RichString_sizeVal(bar);
249+
if (charPos < len) {
250+
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], charPos, len - charPos);
177251
}
252+
RichString_printVal(bar, y, x);
178253

179254
RichString_delete(&bar);
180255

0 commit comments

Comments
 (0)