UI: Allow TextViews to use wordwrap.

But they must have a fixed width.
This commit is contained in:
Unknown W. Brackets 2016-07-04 11:46:21 -07:00
parent 30e99206c9
commit a2494b4ff2
7 changed files with 108 additions and 24 deletions

View File

@ -335,14 +335,14 @@ void DrawBuffer::DrawImage2GridH(ImageID atlas_image, float x1, float y1, float
class AtlasWordWrapper : public WordWrapper {
public:
// Note: maxW may be height if rotated.
AtlasWordWrapper(AtlasFont &atlasfont, float scale, const char *str, float maxW) : WordWrapper(str, maxW), atlasfont_(atlasfont), scale_(scale) {
AtlasWordWrapper(const AtlasFont &atlasfont, float scale, const char *str, float maxW) : WordWrapper(str, maxW), atlasfont_(atlasfont), scale_(scale) {
}
protected:
float MeasureWidth(const char *str, size_t bytes) override;
AtlasFont &atlasfont_;
float scale_;
const AtlasFont &atlasfont_;
const float scale_;
};
float AtlasWordWrapper::MeasureWidth(const char *str, size_t bytes) {
@ -393,6 +393,16 @@ void DrawBuffer::MeasureTextCount(int font, const char *text, int count, float *
if (h) *h = atlasfont.height * fontscaley * lines;
}
void DrawBuffer::MeasureTextRect(int font, const char *text, int count, const Bounds &bounds, float *w, float *h, int align) {
std::string toMeasure = std::string(text, count);
if (align & FLAG_WRAP_TEXT) {
AtlasWordWrapper wrapper(*atlas->fonts[font], fontscalex, toMeasure.c_str(), bounds.w);
toMeasure = wrapper.Wrapped();
}
MeasureTextCount(font, toMeasure.c_str(), (int)toMeasure.length(), w, h);
}
void DrawBuffer::MeasureText(int font, const char *text, float *w, float *h) {
return MeasureTextCount(font, text, (int)strlen(text), w, h);
}
@ -428,7 +438,12 @@ void DrawBuffer::DrawTextRect(int font, const char *text, float x, float y, floa
y += h;
}
DrawText(font, text, x, y, color, align);
std::string toDraw = text;
if (align & FLAG_WRAP_TEXT) {
AtlasWordWrapper wrapper(*atlas->fonts[font], fontscalex, toDraw.c_str(), w);
toDraw = wrapper.Wrapped();
}
DrawText(font, toDraw.c_str(), x, y, color, align);
}
// ROTATE_* doesn't yet work right.

View File

@ -38,7 +38,8 @@ enum {
// Avoids using system font drawing as it's too slow.
// Not actually used here but is reserved for whatever system wraps DrawBuffer.
FLAG_DYNAMIC_ASCII = 2048,
FLAG_NO_PREFIX = 4096 // means to not process ampersands
FLAG_NO_PREFIX = 4096, // means to not process ampersands
FLAG_WRAP_TEXT = 8192,
};
class Thin3DShaderSet;
@ -126,7 +127,8 @@ public:
// NOTE: Count is in plain chars not utf-8 chars!
void MeasureTextCount(int font, const char *text, int count, float *w, float *h);
void MeasureTextRect(int font, const char *text, int count, const Bounds &bounds, float *w, float *h, int align = 0);
void DrawTextRect(int font, const char *text, float x, float y, float w, float h, Color color = 0xFFFFFFFF, int align = 0);
void DrawText(int font, const char *text, float x, float y, Color color = 0xFFFFFFFF, int align = 0);
void DrawTextShadow(int font, const char *text, float x, float y, Color color = 0xFFFFFFFF, int align = 0);

View File

@ -148,13 +148,42 @@ void TextDrawer::MeasureString(const char *str, size_t len, float *w, float *h)
*h = size.cy * fontScaleY_;
}
void TextDrawer::MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align) {
auto iter = fontMap_.find(fontHash_);
if (iter != fontMap_.end()) {
SelectObject(ctx_->hDC, iter->second->hFont);
}
std::string toMeasure = std::string(str, len);
if (align & FLAG_WRAP_TEXT) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w);
}
std::vector<std::string> lines;
SplitString(toMeasure, '\n', lines);
float total_w = 0.0f;
float total_h = 0.0f;
for (size_t i = 0; i < lines.size(); i++) {
SIZE size;
std::wstring wstr = ConvertUTF8ToWString(lines[i].length() == 0 ? " " : lines[i]);
GetTextExtentPoint32(ctx_->hDC, wstr.c_str(), (int)wstr.size(), &size);
if (total_w < size.cx * fontScaleX_) {
total_w = size.cx * fontScaleX_;
}
total_h += size.cy * fontScaleY_;
}
*w = total_w;
*h = total_h;
}
void TextDrawer::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) {
if (!strlen(str))
return;
uint32_t stringHash = hash::Fletcher((const uint8_t *)str, strlen(str));
uint32_t entryHash = stringHash ^ fontHash_;
target.Flush(true);
TextStringEntry *entry;
@ -288,6 +317,25 @@ void TextDrawer::MeasureString(const char *str, size_t len, float *w, float *h)
#endif
}
void TextDrawer::MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align) {
std::string toMeasure = std::string(str, len);
if (align & FLAG_WRAP_TEXT) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w);
}
#ifdef USING_QT_UI
QFont* font = fontMap_.find(fontHash_)->second;
QFontMetrics fm(*font);
QSize size = fm.size(0, QString::fromUtf8(toMeasure.c_str(), (int)toMeasure.size()));
*w = (float)size.width() * fontScaleX_;
*h = (float)size.height() * fontScaleY_;
#else
*w = 0;
*h = 0;
#endif
}
void TextDrawer::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) {
if (!strlen(str))
return;
@ -351,6 +399,11 @@ void TextDrawer::DrawString(DrawBuffer &target, const char *str, float x, float
#endif
void TextDrawer::WrapString(std::string &out, const char *str, float maxW) {
TextDrawerWordWrapper wrapper(this, str, maxW);
out = wrapper.Wrapped();
}
void TextDrawer::SetFontScale(float xscale, float yscale) {
fontScaleX_ = xscale;
fontScaleY_ = xscale;
@ -370,7 +423,13 @@ void TextDrawer::DrawStringRect(DrawBuffer &target, const char *str, const Bound
y = bounds.y2();
}
DrawString(target, str, x, y, color, align);
std::string toDraw = str;
if (align & FLAG_WRAP_TEXT) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toDraw, str, rotated ? bounds.h : bounds.w);
}
DrawString(target, toDraw.c_str(), x, y, color, align);
}
void TextDrawer::OncePerFrame() {

View File

@ -50,6 +50,7 @@ public:
void SetFontScale(float xscale, float yscale);
void MeasureString(const char *str, float *w, float *h);
void MeasureString(const char *str, size_t len, float *w, float *h);
void MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT);
void DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT);
void DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align);
// Use for housekeeping like throwing out old strings.
@ -58,6 +59,8 @@ public:
private:
Thin3DContext *thin3d_;
void WrapString(std::string &out, const char *str, float maxWidth);
int frameCount_;
float fontScaleX_;
float fontScaleY_;

View File

@ -138,8 +138,23 @@ void UIContext::MeasureTextCount(const UI::FontStyle &style, const char *str, in
Draw()->MeasureTextCount(style.atlasFont, str, count, x, y);
} else {
textDrawer_->SetFontScale(fontScaleX_, fontScaleY_);
std::string subset(str, count);
textDrawer_->MeasureString(subset.c_str(), x, y);
textDrawer_->MeasureString(str, count, x, y);
}
}
void UIContext::MeasureTextRect(const UI::FontStyle &style, const char *str, int count, const Bounds &bounds, float *x, float *y, int align) const {
if ((align & FLAG_WRAP_TEXT) == 0) {
MeasureTextCount(style, str, count, x, y, align);
return;
}
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
float sizeFactor = (float)style.sizePts / 24.0f;
Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor);
Draw()->MeasureTextRect(style.atlasFont, str, count, bounds, x, y, align);
} else {
textDrawer_->SetFontScale(fontScaleX_, fontScaleY_);
textDrawer_->MeasureStringRect(str, count, bounds, x, y, align);
}
}

View File

@ -61,6 +61,7 @@ public:
void SetFontScale(float scaleX, float scaleY);
void MeasureTextCount(const UI::FontStyle &style, const char *str, int count, float *x, float *y, int align = 0) const;
void MeasureText(const UI::FontStyle &style, const char *str, float *x, float *y, int align = 0) const;
void MeasureTextRect(const UI::FontStyle &style, const char *str, int count, const Bounds &bounds, float *x, float *y, int align = 0) const;
void DrawText(const char *str, float x, float y, uint32_t color, int align = 0);
void DrawTextShadow(const char *str, float x, float y, uint32_t color, int align = 0);
void DrawTextRect(const char *str, const Bounds &bounds, uint32_t color, int align = 0);

View File

@ -630,20 +630,9 @@ void Thin3DTextureView::Draw(UIContext &dc) {
}
void TextView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
// MeasureText doesn't seem to work with line breaks, so do something more sophisticated.
std::vector<std::string> lines;
SplitString(text_, '\n', lines);
float total_w = 0.f;
float total_h = 0.f;
for (size_t i = 0; i < lines.size(); i++) {
float temp_w, temp_h;
dc.MeasureText(small_ ? dc.theme->uiFontSmall : dc.theme->uiFont, lines[i].c_str(), &temp_w, &temp_h);
if (temp_w > total_w)
total_w = temp_w;
total_h += temp_h;
}
w = total_w;
h = total_h;
// We don't have the bounding w/h yet, so stick with hardset layout params.
Bounds bounds(0, 0, layoutParams_->width, layoutParams_->height);
dc.MeasureTextRect(small_ ? dc.theme->uiFontSmall : dc.theme->uiFont, text_.c_str(), (int)text_.length(), bounds, &w, &h, textAlign_);
}
void TextView::Draw(UIContext &dc) {