Skip to content

Commit e8e8af2

Browse files
committed
Wide character support in bar meter text
1 parent 598b859 commit e8e8af2

File tree

1 file changed

+128
-47
lines changed

1 file changed

+128
-47
lines changed

Meter.c

Lines changed: 128 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,21 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
8989

9090
// Draw the caption
9191
const int captionWidth = 3;
92+
if (w < captionWidth)
93+
return;
94+
9295
Meter_drawCaptionFixedWidth(this, x, y, captionWidth);
9396
x += captionWidth;
9497
w -= captionWidth;
9598

9699
// Draw the bar borders
100+
if (w < 1)
101+
return;
102+
97103
attrset(CRT_colors[BAR_BORDER]);
98104
mvaddch(y, x, '[');
99105
w--;
100-
mvaddch(y, x + MAXIMUM(w, 0), ']');
106+
mvaddch(y, x + w, ']');
101107
w--;
102108
attrset(CRT_colors[RESET_COLOR]);
103109

@@ -107,71 +113,146 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
107113
return;
108114
}
109115

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

129-
// If still too large, print the start not the end
130-
startPos = MINIMUM(startPos, w);
131-
}
159+
nColsLeft -= nCols;
132160

133-
assert(startPos >= 0);
134-
assert(startPos <= w);
135-
assert(startPos + w <= RichString_sizeVal(bar));
161+
if (state.ch == ' ') {
162+
continue;
163+
}
136164

137-
int blockSizes[10];
165+
#ifdef HAVE_LIBNCURSESW
166+
// If the character takes zero columns, include the character in the
167+
// substring if the working encoding is UTF-8, and ignore it otherwise.
168+
if (nCols <= 0 && !CRT_utf8) {
169+
continue;
170+
}
171+
#endif
172+
173+
len = (size_t)(state.str - this->txtBuffer);
174+
}
175+
176+
RichString_begin(bar);
177+
RichString_appendChr(&bar, 0, ' ', nColsLeft);
178+
RichString_appendnWide(&bar, 0, this->txtBuffer, len);
138179

139-
// First draw in the bar[] buffer...
180+
size_t charPos = 0;
140181
int offset = 0;
141182
for (uint8_t i = 0; i < this->curItems; i++) {
183+
if (!(this->total > 0.0)) {
184+
break;
185+
}
186+
if (offset >= w) {
187+
break;
188+
}
189+
142190
double value = this->values[i];
143-
if (isPositive(value) && this->total > 0.0) {
144-
value = MINIMUM(value, this->total);
145-
blockSizes[i] = ceil((value / this->total) * w);
146-
blockSizes[i] = MINIMUM(blockSizes[i], w - offset);
147-
} else {
148-
blockSizes[i] = 0;
191+
if (!isPositive(value)) {
192+
continue;
149193
}
150-
int nextOffset = offset + blockSizes[i];
151-
for (int j = offset; j < nextOffset; j++)
152-
if (RichString_getCharVal(bar, startPos + j) == ' ') {
194+
value = MINIMUM(value, this->total);
195+
int blockSize = ceil((value / this->total) * w);
196+
blockSize = MINIMUM(blockSize, w - offset);
197+
if (blockSize < 1) {
198+
continue;
199+
}
200+
201+
int nextOffset = offset + blockSize;
202+
assert(offset < nextOffset);
203+
204+
size_t startPos = charPos;
205+
while (true) {
206+
if (offset >= nextOffset) {
207+
#ifdef HAVE_LIBNCURSESW
208+
if (!CRT_utf8) {
209+
break;
210+
}
211+
#else
212+
break;
213+
#endif
214+
}
215+
216+
#ifdef HAVE_LIBNCURSESW
217+
wchar_t ch = RichString_getCharVal(bar, charPos);
218+
if (ch == 0)
219+
break;
220+
221+
int nCols = wcwidth(ch);
222+
assert(nCols >= 0);
223+
224+
if (offset >= nextOffset && nCols > 0) {
225+
// This break condition is for UTF-8.
226+
break;
227+
}
228+
#else
229+
char ch = RichString_getCharVal(bar, charPos);
230+
int nCols = 1;
231+
232+
assert(offset < nextOffset);
233+
#endif
234+
if (ch == ' ') {
153235
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
154236
assert(i < strlen(BarMeterMode_characters));
155-
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
237+
RichString_setChar(&bar, charPos, BarMeterMode_characters[i]);
156238
} else {
157-
RichString_setChar(&bar, startPos + j, '|');
239+
RichString_setChar(&bar, charPos, '|');
158240
}
159241
}
160-
offset = nextOffset;
161-
}
162242

163-
// ...then print the buffer.
164-
offset = 0;
165-
for (uint8_t i = 0; i < this->curItems; i++) {
243+
offset += nCols;
244+
charPos++;
245+
}
246+
166247
int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
167-
RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]);
168-
RichString_printoffnVal(bar, y, x + offset, startPos + offset, blockSizes[i]);
169-
offset += blockSizes[i];
248+
RichString_setAttrn(&bar, CRT_colors[attr], startPos, charPos - startPos);
170249
}
171-
if (offset < w) {
172-
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
173-
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
250+
251+
len = RichString_sizeVal(bar);
252+
if (charPos < len) {
253+
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], charPos, len - charPos);
174254
}
255+
RichString_printVal(bar, y, x);
175256

176257
RichString_delete(&bar);
177258

0 commit comments

Comments
 (0)