Skip to content

Commit e1f171a

Browse files
committed
Wide character support in bar meter text
1 parent e74c4da commit e1f171a

1 file changed

Lines changed: 72 additions & 51 deletions

File tree

Meter.c

Lines changed: 72 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
9898

9999
const char* ptr = caption;
100100
int nCols = String_mbswidth(&ptr, 256, captionWidth);
101-
int len = (int)(ptr - caption);
102-
mvprintw(y, x, "%-*.*s", len + captionWidth - nCols, len, caption);
101+
int captionLen = (int)(ptr - caption);
102+
mvprintw(y, x, "%-*.*s", captionLen + captionWidth - nCols, captionLen, caption);
103103
}
104104
w -= captionWidth;
105105

@@ -125,71 +125,92 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
125125
attrset(CRT_colors[RESET_COLOR]); // Clear the bold attribute
126126
x++;
127127

128-
// The text in the bar is right aligned;
129-
// Pad with maximal spaces and then calculate needed starting position offset
130-
RichString_begin(bar);
131-
RichString_appendChr(&bar, 0, ' ', w);
132-
RichString_appendWide(&bar, 0, this->txtBuffer);
133-
134-
int startPos = RichString_sizeVal(bar) - w;
135-
if (startPos > w) {
136-
// Text is too large for bar
137-
// Truncate meter text at a space character
138-
for (int pos = 2 * w; pos > w; pos--) {
139-
if (RichString_getCharVal(bar, pos) == ' ') {
140-
while (pos > w && RichString_getCharVal(bar, pos - 1) == ' ')
141-
pos--;
142-
startPos = pos - w;
143-
break;
144-
}
145-
}
128+
// Calculate the number of terminal columns needed for the meter text.
129+
// The text in the bar is right aligned
146130

147-
// If still too large, print the start not the end
148-
startPos = MINIMUM(startPos, w);
131+
RichString_begin(bar);
132+
{
133+
const char* ptr = this->txtBuffer;
134+
int padWidth = w - String_lineBreakWidth(&ptr, sizeof(this->txtBuffer) - 1, w, ' ');
135+
RichString_appendChr(&bar, 0, ' ', padWidth);
136+
RichString_appendnWide(&bar, 0, this->txtBuffer, (size_t)(ptr - this->txtBuffer));
149137
}
150138

151-
assert(startPos >= 0);
152-
assert(startPos <= w);
153-
assert(startPos + w <= RichString_sizeVal(bar));
154-
155-
int blockSizes[10];
139+
#ifdef HAVE_LIBNCURSESW
140+
// If the character takes zero columns, include the character in the
141+
// substring if the working encoding is UTF-8, and ignore it otherwise.
142+
// In Unicode, combining characters are always placed after the base
143+
// character, but some legacy 8-bit encodings instead place combining
144+
// characters before the base character.
145+
const bool isUnicode = CRT_utf8;
146+
#else
147+
const bool isUnicode = false;
148+
#endif
156149

157-
// First draw in the bar[] buffer...
158150
int offset = 0;
151+
size_t len = RichString_sizeVal(bar);
152+
size_t charPos = 0;
159153
for (uint8_t i = 0; i < this->curItems; i++) {
154+
if (!(this->total > 0.0))
155+
break;
156+
if (offset >= w)
157+
break;
158+
160159
double value = this->values[i];
161-
if (isPositive(value) && this->total > 0.0) {
162-
value = MINIMUM(value, this->total);
163-
blockSizes[i] = ceil((value / this->total) * w);
164-
blockSizes[i] = MINIMUM(blockSizes[i], w - offset);
165-
} else {
166-
blockSizes[i] = 0;
167-
}
168-
int nextOffset = offset + blockSizes[i];
169-
for (int j = offset; j < nextOffset; j++)
170-
if (RichString_getCharVal(bar, startPos + j) == ' ') {
160+
if (!isPositive(value))
161+
continue;
162+
value = MINIMUM(value, this->total);
163+
int blockSize = ceil((value / this->total) * w);
164+
blockSize = MINIMUM(blockSize, w - offset);
165+
if (blockSize < 1)
166+
continue;
167+
168+
int nextOffset = offset + blockSize;
169+
170+
size_t startPos = charPos;
171+
while (charPos < len && (offset < nextOffset || isUnicode)) {
172+
assert(offset <= nextOffset);
173+
174+
#ifdef HAVE_LIBNCURSESW
175+
wchar_t ch = RichString_getCharVal(bar, charPos);
176+
#else
177+
char ch = RichString_getCharVal(bar, charPos);
178+
#endif
179+
assert(ch != 0);
180+
181+
#ifdef HAVE_LIBNCURSESW
182+
int nCols = wcwidth(ch);
183+
assert(nCols >= 0);
184+
185+
if (offset >= nextOffset && nCols > 0) {
186+
// This break condition is for UTF-8.
187+
break;
188+
}
189+
#else
190+
const int nCols = 1;
191+
#endif
192+
if (ch == ' ') {
171193
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
172194
assert(i < strlen(BarMeterMode_characters));
173-
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
195+
RichString_setChar(&bar, charPos, BarMeterMode_characters[i]);
174196
} else {
175-
RichString_setChar(&bar, startPos + j, '|');
197+
RichString_setChar(&bar, charPos, '|');
176198
}
177199
}
178-
offset = nextOffset;
179-
}
180200

181-
// ...then print the buffer.
182-
offset = 0;
183-
for (uint8_t i = 0; i < this->curItems; i++) {
201+
offset += nCols;
202+
charPos++;
203+
}
204+
if (charPos <= startPos)
205+
continue;
206+
184207
int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
185-
RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]);
186-
RichString_printoffnVal(bar, y, x + offset, startPos + offset, blockSizes[i]);
187-
offset += blockSizes[i];
208+
RichString_setAttrn(&bar, CRT_colors[attr], startPos, charPos - startPos);
188209
}
189-
if (offset < w) {
190-
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
191-
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
210+
if (charPos < len) {
211+
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], charPos, len - charPos);
192212
}
213+
RichString_printVal(bar, y, x);
193214

194215
RichString_delete(&bar);
195216

0 commit comments

Comments
 (0)