Skip to content

Commit 24b74d3

Browse files
committed
Wide character support in bar meter text
1 parent 0ddf994 commit 24b74d3

1 file changed

Lines changed: 74 additions & 51 deletions

File tree

Meter.c

Lines changed: 74 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,94 @@ 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 charPos = 0;
159152
for (uint8_t i = 0; i < this->curItems; i++) {
153+
if (!(this->total > 0.0))
154+
break;
155+
if (offset >= w)
156+
break;
157+
160158
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) == ' ') {
159+
if (!isPositive(value))
160+
continue;
161+
value = MINIMUM(value, this->total);
162+
int blockSize = ceil((value / this->total) * w);
163+
blockSize = MINIMUM(blockSize, w - offset);
164+
if (blockSize < 1)
165+
continue;
166+
167+
int nextOffset = offset + blockSize;
168+
169+
size_t startPos = charPos;
170+
do {
171+
assert(offset <= nextOffset);
172+
173+
#ifdef HAVE_LIBNCURSESW
174+
wchar_t ch = RichString_getCharVal(bar, charPos);
175+
#else
176+
char ch = RichString_getCharVal(bar, charPos);
177+
#endif
178+
if (ch == 0)
179+
break;
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+
} while (offset < nextOffset || isUnicode);
204+
184205
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];
206+
RichString_setAttrn(&bar, CRT_colors[attr], startPos, charPos - startPos);
188207
}
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);
208+
209+
{
210+
size_t len = RichString_sizeVal(bar);
211+
if (charPos < len) {
212+
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], charPos, len - charPos);
213+
}
192214
}
215+
RichString_printVal(bar, y, x);
193216

194217
RichString_delete(&bar);
195218

0 commit comments

Comments
 (0)