mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-14 11:01:38 +00:00
PPGe: Show ellipsis for overly long lines.
To match PPGE_LINE_USE_ELLIPSIS when using TextDrawer.
This commit is contained in:
parent
6652fe261f
commit
08a6047768
@ -703,6 +703,8 @@ void PPGeMeasureText(float *w, float *h, int *n,
|
||||
float mw, mh;
|
||||
textDrawer->SetFontScale(scale, scale);
|
||||
int dtalign = (WrapType & PPGE_LINE_WRAP_WORD) ? FLAG_WRAP_TEXT : 0;
|
||||
if (WrapType & PPGE_LINE_USE_ELLIPSIS)
|
||||
dtalign |= FLAG_ELLIPSIZE_TEXT;
|
||||
Bounds b(0, 0, wrapWidth <= 0 ? 480.0f : wrapWidth, 272.0f);
|
||||
textDrawer->MeasureStringRect(text, strlen(text), b, &mw, &mh, dtalign);
|
||||
|
||||
@ -792,6 +794,7 @@ int GetPow2(int x) {
|
||||
|
||||
static PPGeTextDrawerImage PPGeGetTextImage(const char *text, int align, float scale, float maxWidth, bool wrap) {
|
||||
int tdalign = (align & PPGE_ALIGN_HCENTER) ? ALIGN_HCENTER : 0;
|
||||
tdalign |= FLAG_ELLIPSIZE_TEXT;
|
||||
if (wrap) {
|
||||
tdalign |= FLAG_WRAP_TEXT;
|
||||
}
|
||||
@ -806,7 +809,6 @@ static PPGeTextDrawerImage PPGeGetTextImage(const char *text, int align, float s
|
||||
} else {
|
||||
std::vector<uint8_t> bitmapData;
|
||||
textDrawer->SetFontScale(scale, scale);
|
||||
// TODO: Ellipsis on long lines...
|
||||
Bounds b(0, 0, maxWidth, 272.0f);
|
||||
textDrawer->DrawStringBitmapRect(bitmapData, im.entry, Draw::DataFormat::R8_UNORM, text, b, tdalign);
|
||||
|
||||
|
@ -365,7 +365,7 @@ void DrawBuffer::DrawImage2GridH(ImageID atlas_image, float x1, float y1, float
|
||||
class AtlasWordWrapper : public WordWrapper {
|
||||
public:
|
||||
// Note: maxW may be height if rotated.
|
||||
AtlasWordWrapper(const 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, int flags) : WordWrapper(str, maxW, flags), atlasfont_(atlasfont), scale_(scale) {
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -442,14 +442,15 @@ void DrawBuffer::MeasureTextRect(FontID font_id, const char *text, int count, co
|
||||
}
|
||||
|
||||
std::string toMeasure = std::string(text, count);
|
||||
if (align & FLAG_WRAP_TEXT) {
|
||||
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
|
||||
if (wrap) {
|
||||
const AtlasFont *font = atlas->getFont(font_id);
|
||||
if (!font) {
|
||||
*w = 0.0f;
|
||||
*h = 0.0f;
|
||||
return;
|
||||
}
|
||||
AtlasWordWrapper wrapper(*font, fontscalex, toMeasure.c_str(), bounds.w);
|
||||
AtlasWordWrapper wrapper(*font, fontscalex, toMeasure.c_str(), bounds.w, wrap);
|
||||
toMeasure = wrapper.Wrapped();
|
||||
}
|
||||
MeasureTextCount(font_id, toMeasure.c_str(), (int)toMeasure.length(), w, h);
|
||||
@ -491,8 +492,9 @@ void DrawBuffer::DrawTextRect(FontID font, const char *text, float x, float y, f
|
||||
}
|
||||
|
||||
std::string toDraw = text;
|
||||
if (align & FLAG_WRAP_TEXT) {
|
||||
AtlasWordWrapper wrapper(*atlas->getFont(font), fontscalex, toDraw.c_str(), w);
|
||||
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
|
||||
if (wrap) {
|
||||
AtlasWordWrapper wrapper(*atlas->getFont(font), fontscalex, toDraw.c_str(), w, wrap);
|
||||
toDraw = wrapper.Wrapped();
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ enum {
|
||||
FLAG_DYNAMIC_ASCII = 2048,
|
||||
FLAG_NO_PREFIX = 4096, // means to not process ampersands
|
||||
FLAG_WRAP_TEXT = 8192,
|
||||
FLAG_ELLIPSIZE_TEXT = 16384,
|
||||
};
|
||||
|
||||
namespace Draw {
|
||||
|
@ -24,8 +24,8 @@ float TextDrawerWordWrapper::MeasureWidth(const char *str, size_t bytes) {
|
||||
return w;
|
||||
}
|
||||
|
||||
void TextDrawer::WrapString(std::string &out, const char *str, float maxW) {
|
||||
TextDrawerWordWrapper wrapper(this, str, maxW);
|
||||
void TextDrawer::WrapString(std::string &out, const char *str, float maxW, int flags) {
|
||||
TextDrawerWordWrapper wrapper(this, str, maxW, flags);
|
||||
out = wrapper.Wrapped();
|
||||
}
|
||||
|
||||
@ -59,9 +59,10 @@ void TextDrawer::DrawStringRect(DrawBuffer &target, const char *str, const Bound
|
||||
}
|
||||
|
||||
std::string toDraw = str;
|
||||
if (align & FLAG_WRAP_TEXT) {
|
||||
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
|
||||
if (wrap) {
|
||||
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
|
||||
WrapString(toDraw, str, rotated ? bounds.h : bounds.w);
|
||||
WrapString(toDraw, str, rotated ? bounds.h : bounds.w, wrap);
|
||||
}
|
||||
|
||||
DrawString(target, toDraw.c_str(), x, y, color, align);
|
||||
@ -69,9 +70,10 @@ void TextDrawer::DrawStringRect(DrawBuffer &target, const char *str, const Bound
|
||||
|
||||
void TextDrawer::DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, const Bounds &bounds, int align) {
|
||||
std::string toDraw = str;
|
||||
if (align & FLAG_WRAP_TEXT) {
|
||||
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
|
||||
if (wrap) {
|
||||
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
|
||||
WrapString(toDraw, str, rotated ? bounds.h : bounds.w);
|
||||
WrapString(toDraw, str, rotated ? bounds.h : bounds.w, wrap);
|
||||
}
|
||||
|
||||
DrawStringBitmap(bitmapData, entry, texFormat, toDraw.c_str(), align);
|
||||
|
@ -78,7 +78,7 @@ protected:
|
||||
|
||||
Draw::DrawContext *draw_;
|
||||
virtual void ClearCache() = 0;
|
||||
void WrapString(std::string &out, const char *str, float maxWidth);
|
||||
void WrapString(std::string &out, const char *str, float maxWidth, int flags);
|
||||
|
||||
struct CacheKey {
|
||||
bool operator < (const CacheKey &other) const {
|
||||
@ -102,7 +102,7 @@ protected:
|
||||
|
||||
class TextDrawerWordWrapper : public WordWrapper {
|
||||
public:
|
||||
TextDrawerWordWrapper(TextDrawer *drawer, const char *str, float maxW) : WordWrapper(str, maxW), drawer_(drawer) {
|
||||
TextDrawerWordWrapper(TextDrawer *drawer, const char *str, float maxW, int flags) : WordWrapper(str, maxW, flags), drawer_(drawer) {
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -115,9 +115,10 @@ void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bou
|
||||
}
|
||||
|
||||
std::string toMeasure = std::string(str, len);
|
||||
if (align & FLAG_WRAP_TEXT) {
|
||||
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
|
||||
if (wrap) {
|
||||
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
|
||||
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w);
|
||||
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w, wrap);
|
||||
}
|
||||
|
||||
std::vector<std::string> lines;
|
||||
|
@ -76,9 +76,10 @@ void TextDrawerQt::MeasureString(const char *str, size_t len, float *w, float *h
|
||||
|
||||
void TextDrawerQt::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) {
|
||||
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
|
||||
if (wrap) {
|
||||
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
|
||||
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w);
|
||||
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w, wrap);
|
||||
}
|
||||
|
||||
QFont* font = fontMap_.find(fontHash_)->second;
|
||||
|
@ -154,9 +154,10 @@ void TextDrawerWin32::MeasureStringRect(const char *str, size_t len, const Bound
|
||||
}
|
||||
|
||||
std::string toMeasure = std::string(str, len);
|
||||
if (align & FLAG_WRAP_TEXT) {
|
||||
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
|
||||
if (wrap) {
|
||||
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
|
||||
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w);
|
||||
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w, wrap);
|
||||
}
|
||||
|
||||
TEXTMETRIC metrics{};
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <cstring>
|
||||
#include "gfx_es2/draw_buffer.h"
|
||||
#include "util/text/utf8.h"
|
||||
#include "util/text/wrap_text.h"
|
||||
|
||||
@ -75,39 +76,58 @@ std::string WordWrapper::Wrapped() {
|
||||
}
|
||||
|
||||
bool WordWrapper::WrapBeforeWord() {
|
||||
if (x_ + wordWidth_ > maxW_ && out_.size() > 0) {
|
||||
if (IsShy(out_[out_.size() - 1])) {
|
||||
// Soft hyphen, replace it with a real hyphen since we wrapped at it.
|
||||
// TODO: There's an edge case here where the hyphen might not fit.
|
||||
out_[out_.size() - 1] = '-';
|
||||
if (flags_ & FLAG_WRAP_TEXT) {
|
||||
if (x_ + wordWidth_ > maxW_ && !out_.empty()) {
|
||||
if (IsShy(out_[out_.size() - 1])) {
|
||||
// Soft hyphen, replace it with a real hyphen since we wrapped at it.
|
||||
// TODO: There's an edge case here where the hyphen might not fit.
|
||||
out_[out_.size() - 1] = '-';
|
||||
}
|
||||
out_ += "\n";
|
||||
lastLineStart_ = out_.size();
|
||||
x_ = 0.0f;
|
||||
forceEarlyWrap_ = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (flags_ & FLAG_ELLIPSIZE_TEXT) {
|
||||
if (x_ + wordWidth_ > maxW_) {
|
||||
if (!out_.empty() && IsSpace(out_[out_.size() - 1])) {
|
||||
out_[out_.size() - 1] = '.';
|
||||
out_ += "..";
|
||||
} else {
|
||||
out_ += "...";
|
||||
}
|
||||
x_ = maxW_;
|
||||
}
|
||||
out_ += "\n";
|
||||
lastLineStart_ = out_.size();
|
||||
x_ = 0.0f;
|
||||
forceEarlyWrap_ = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WordWrapper::AppendWord(int endIndex, bool addNewline) {
|
||||
int nextWordIndex = lastIndex_;
|
||||
int lastWordStartIndex = lastIndex_;
|
||||
if (WrapBeforeWord()) {
|
||||
// Advance to the first non-whitespace UTF-8 character in the following word (if any) to prevent starting the new line with a whitespace
|
||||
UTF8 utf8Word(str_, nextWordIndex);
|
||||
while (nextWordIndex < endIndex) {
|
||||
UTF8 utf8Word(str_, lastWordStartIndex);
|
||||
while (lastWordStartIndex < endIndex) {
|
||||
const uint32_t c = utf8Word.next();
|
||||
if (!IsSpace(c)) {
|
||||
break;
|
||||
}
|
||||
nextWordIndex = utf8Word.byteIndex();
|
||||
lastWordStartIndex = utf8Word.byteIndex();
|
||||
}
|
||||
}
|
||||
|
||||
// This will include the newline.
|
||||
out_.append(str_ + nextWordIndex, str_ + endIndex);
|
||||
if (addNewline) {
|
||||
if (x_ < maxW_) {
|
||||
out_.append(str_ + lastWordStartIndex, str_ + endIndex);
|
||||
} else {
|
||||
scanForNewline_ = true;
|
||||
}
|
||||
if (addNewline && (flags_ & FLAG_WRAP_TEXT)) {
|
||||
out_ += "\n";
|
||||
lastLineStart_ = out_.size();
|
||||
scanForNewline_ = false;
|
||||
} else {
|
||||
// We may have appended a newline - check.
|
||||
size_t pos = out_.substr(lastLineStart_).find_last_of("\n");
|
||||
@ -129,6 +149,10 @@ void WordWrapper::Wrap() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags_ & FLAG_ELLIPSIZE_TEXT) {
|
||||
ellipsisWidth_ = MeasureWidth("...", 3);
|
||||
}
|
||||
|
||||
for (UTF8 utf(str_); !utf.end(); ) {
|
||||
int beforeIndex = utf.byteIndex();
|
||||
uint32_t c = utf.next();
|
||||
@ -142,6 +166,13 @@ void WordWrapper::Wrap() {
|
||||
wordWidth_ = 0.0f;
|
||||
// We wrapped once, so stop forcing.
|
||||
forceEarlyWrap_ = false;
|
||||
scanForNewline_ = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scanForNewline_) {
|
||||
// We're discarding the rest of the characters until a newline (no wrapping.)
|
||||
lastIndex_ = afterIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -175,13 +206,33 @@ void WordWrapper::Wrap() {
|
||||
}
|
||||
// Now, add the word so far (without this latest character) and break.
|
||||
AppendWord(beforeIndex, true);
|
||||
x_ = 0.0f;
|
||||
if (lastLineStart_ != out_.size()) {
|
||||
x_ = MeasureWidth(out_.c_str() + lastLineStart_, out_.size() - lastLineStart_);
|
||||
} else {
|
||||
x_ = 0.0f;
|
||||
}
|
||||
wordWidth_ = 0.0f;
|
||||
forceEarlyWrap_ = false;
|
||||
// The current character will be handled as part of the next word.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((flags_ & FLAG_ELLIPSIZE_TEXT) && wordWidth_ > 0.0f && x_ + newWordWidth + ellipsisWidth_ > maxW_) {
|
||||
if ((flags_ & FLAG_WRAP_TEXT) == 0) {
|
||||
// Now, add the word so far (without this latest character) and show the ellipsis.
|
||||
AppendWord(beforeIndex, true);
|
||||
if (lastLineStart_ != out_.size()) {
|
||||
x_ = MeasureWidth(out_.c_str() + lastLineStart_, out_.size() - lastLineStart_);
|
||||
} else {
|
||||
x_ = 0.0f;
|
||||
}
|
||||
wordWidth_ = 0.0f;
|
||||
forceEarlyWrap_ = false;
|
||||
// The current character will be handled as part of the next word.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
wordWidth_ = newWordWidth;
|
||||
|
||||
// Is this the end of a word via punctuation / CJK?
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
class WordWrapper {
|
||||
public:
|
||||
WordWrapper(const char *str, float maxW)
|
||||
: str_(str), maxW_(maxW) {
|
||||
WordWrapper(const char *str, float maxW, int flags)
|
||||
: str_(str), maxW_(maxW), flags_(flags) {
|
||||
}
|
||||
|
||||
std::string Wrapped();
|
||||
@ -23,7 +23,9 @@ protected:
|
||||
|
||||
const char *const str_;
|
||||
const float maxW_;
|
||||
const int flags_;
|
||||
std::string out_;
|
||||
|
||||
// Index of last output / start of current word.
|
||||
int lastIndex_ = 0;
|
||||
// Index of last line start.
|
||||
@ -32,6 +34,10 @@ protected:
|
||||
float x_ = 0.0f;
|
||||
// Most recent width of word since last index.
|
||||
float wordWidth_ = 0.0f;
|
||||
// Width of "..." when flag is set, zero otherwise.
|
||||
float ellipsisWidth_ = 0.0f;
|
||||
// Force the next word to cut partially and wrap.
|
||||
bool forceEarlyWrap_ = false;
|
||||
// Skip all characters until the next newline.
|
||||
bool scanForNewline_ = false;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user