support simple multiline text formatting and drawing

This commit is contained in:
Vadim Lopatin 2015-03-18 10:12:30 +03:00
parent 2155d8418b
commit cd8c617e05
1 changed files with 118 additions and 0 deletions

View File

@ -417,6 +417,21 @@ class Font : RefCountedObject {
}
}
/// measure multiline text with line splitting, returns width and height in pixels
Point measureMultilineText(const dchar[] text, int maxLines = 0, int maxWidth = 0, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
SimpleTextFormatter fmt;
FontRef fnt = FontRef(this);
return fmt.format(text, fnt, maxLines, maxWidth, tabSize, tabOffset, textFlags);
}
/// draws multiline text with line splitting
void drawMultilineText(DrawBuf buf, int x, int y, const dchar[] text, uint color, int maxLines = 0, int maxWidth = 0, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
SimpleTextFormatter fmt;
FontRef fnt = FontRef(this);
fmt.format(text, fnt, maxLines, maxWidth, tabSize, tabOffset, textFlags);
fmt.draw(buf, x, y, fnt, color);
}
/// get character glyph information
abstract Glyph * getCharGlyph(dchar ch, bool withImage = true);
@ -434,6 +449,109 @@ class Font : RefCountedObject {
}
alias FontRef = Ref!Font;
/// helper to split text into several lines and draw it
struct SimpleTextFormatter {
dstring[] _lines;
int _tabSize;
int _tabOffset;
uint _textFlags;
/// split text into lines and measure it; returns size in pixels
Point format(const dchar[] text, FontRef fnt, int maxLines = 0, int maxWidth = 0, int tabSize = 4, int tabOffset = 0, uint textFlags = 0) {
_tabSize = tabSize;
_tabOffset = tabOffset;
_textFlags = textFlags;
Point sz;
_lines.length = 0;
int lineHeight = fnt.height;
if (text.length == 0) {
sz.y = lineHeight;
return sz;
}
int[] widths;
int charsMeasured = fnt.measureText(text, widths, MAX_WIDTH_UNSPECIFIED, _tabSize, _tabOffset, _textFlags);
int lineStart = 0;
int lineStartX = 0;
int lastWordEnd = 0;
int lastWordEndX = 0;
dchar prevChar = 0;
for (int i = 0; i <= charsMeasured; i++) {
dchar ch = text[i];
if (ch == '\n' || i == charsMeasured) {
// split by EOL char or at end of text
dstring line = cast(dstring)text[lineStart .. i];
int lineEndX = (i == charsMeasured) || (i == lineStart) ? lineStartX : widths[i - 1];
int lineWidth = lineEndX - lineStartX;
sz.y += lineHeight;
if (sz.x < lineWidth)
sz.x = lineWidth;
_lines ~= line;
if (i == charsMeasured) // end of text reached
break;
// check max lines constraint
if (maxLines && _lines.length >= maxLines) // max lines reached
break;
lineStart = i + 1;
lineStartX = widths[i];
} else {
// split by width
int x = widths[i];
if (ch == '\t' || ch == ' ') {
// track last word end
if (prevChar != '\t' && prevChar != ' ' && prevChar != 0) {
lastWordEnd = i - 1;
lastWordEndX = widths[i - 1];
}
prevChar = ch;
continue;
}
if (maxWidth > 0 && x > maxWidth && x - lineStartX > maxWidth && i > lineStart) {
// need splitting
int lineEnd = i;
int lineEndX = widths[i - 1];
if (lastWordEnd > lineStart && lastWordEndX - lineStartX >= maxWidth / 3) {
// split on word bound
lineEnd = lastWordEnd;
lineEndX = widths[lastWordEnd - 1];
}
// add line
dstring line = cast(dstring)text[lineStart .. lastWordEnd];
int lineWidth = lineEndX - lineStartX;
sz.y += lineHeight;
if (sz.x < lineWidth)
sz.x = lineWidth;
_lines ~= line;
// check max lines constraint
if (maxLines && _lines.length >= maxLines) // max lines reached
break;
// find next line start
lineStart = lineEnd;
while(lineStart < text.length && (text[lineStart] == ' ' || text[lineStart] == '\t'))
lineStart++;
if (lineStart >= text.length)
break;
lineStartX = widths[lineStart - 1];
}
}
prevChar = ch;
}
return sz;
}
/// draw formatted text
void draw(DrawBuf buf, int x, int y, FontRef fnt, uint color) {
int lineHeight = fnt.height;
for (int i = 0; i < _lines.length; i++) {
dstring line = _lines[i];
fnt.drawText(buf, x, y, line, color, _tabSize, _tabOffset, _textFlags);
y += lineHeight;
}
}
}
/// font instance collection - utility class, for font manager implementations
struct FontList {
FontRef[] _list;