GUI: Handle ellipsis shortening of text when drawing u32 strings

This commit is contained in:
aryanrawlani28 2020-06-12 01:04:38 +05:30 committed by Eugene Sandulenko
parent 5f2af6b93e
commit ad78777bc6
3 changed files with 95 additions and 10 deletions

View File

@ -1115,7 +1115,7 @@ drawString(const Graphics::Font *font, const Common::U32String &text, const Comm
if (!drawArea.isEmpty()) {
Surface textAreaSurface = _activeSurface->getSubArea(drawArea);
font->drawString(&textAreaSurface, text, area.left - drawArea.left, offset - drawArea.top, area.width() - deltax, _fgColor, alignH, deltax);
font->drawString(&textAreaSurface, text, area.left - drawArea.left, offset - drawArea.top, area.width() - deltax, _fgColor, alignH, deltax, ellipsis);
}
}

View File

@ -334,17 +334,23 @@ Common::Rect Font::getBoundingBox(const Common::String &input, int x, int y, con
return getBoundingBoxImpl(*this, str, x, y, w, align, deltax);
}
Common::Rect Font::getBoundingBox(const Common::U32String &str, int x, int y, const int w, TextAlign align) const {
Common::Rect Font::getBoundingBox(const Common::U32String &input, int x, int y, const int w, TextAlign align, int deltax, bool useEllipsis) const {
// In case no width was given we cannot any alignment apart from left
// alignment.
if (w == 0) {
if (useEllipsis) {
warning("Font::getBoundingBox: Requested ellipsis when no width was specified");
}
if (align != kTextAlignLeft) {
warning("Font::getBoundingBox: Requested text alignment when no width was specified");
}
useEllipsis = false;
align = kTextAlignLeft;
}
const Common::U32String str = useEllipsis ? handleEllipsis(input, w) : input;
return getBoundingBoxImpl(*this, str, x, y, w, align, 0);
}
@ -369,8 +375,9 @@ void Font::drawString(Surface *dst, const Common::String &str, int x, int y, int
drawStringImpl(*this, dst, renderStr, x, y, w, color, align, deltax);
}
void Font::drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax) const {
drawStringImpl(*this, dst, str, x, y, w, color, align, deltax);
void Font::drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const {
Common::U32String renderStr = useEllipsis ? handleEllipsis(str, w) : str;
drawStringImpl(*this, dst, renderStr, x, y, w, color, align, deltax);
}
void Font::drawString(ManagedSurface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const {
@ -380,10 +387,10 @@ void Font::drawString(ManagedSurface *dst, const Common::String &str, int x, int
}
}
void Font::drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax) const {
drawString(&dst->_innerSurface, str, x, y, w, color, align, deltax);
void Font::drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const {
drawString(&dst->_innerSurface, str, x, y, w, color, align, deltax, useEllipsis);
if (w != 0) {
dst->addDirtyRect(getBoundingBox(str, x, y, w, align));
dst->addDirtyRect(getBoundingBox(str, x, y, w, align, useEllipsis));
}
}
@ -477,4 +484,81 @@ TextAlign convertTextAlignH(TextAlign alignH, bool rtl) {
}
}
Common::U32String Font::handleEllipsis(const Common::U32String &input, int w) const {
Common::U32String s = input;
int width = getStringWidth(s);
bool hasEllipsisAtEnd = false;
if (s.size() > 3 && s[s.size() - 1] == '.' && s[s.size() - 2] == '.' && s[s.size() - 3] == '.') {
hasEllipsisAtEnd = true;
}
if (width > w && hasEllipsisAtEnd) {
// String is too wide. Check whether it ends in an ellipsis
// ("..."). If so, remove that and try again!
s.deleteLastChar();
s.deleteLastChar();
s.deleteLastChar();
width = getStringWidth(s);
}
if (width > w) {
Common::U32String str;
Common::U32String ellipsis("...");
// String is too wide. So we shorten it "intelligently" by
// replacing parts of the string by an ellipsis. There are
// three possibilities for this: replace the start, the end, or
// the middle of the string. What is best really depends on the
// context; but unless we want to make this configurable,
// replacing the middle seems to be a good compromise.
const int ellipsisWidth = getStringWidth(ellipsis);
// SLOW algorithm to remove enough of the middle. But it is good enough
// for now.
const int halfWidth = (w - ellipsisWidth) / 2;
int w2 = 0;
Common::String::unsigned_type last = 0;
uint i = 0;
for (; i < s.size(); ++i) {
const Common::String::unsigned_type cur = s[i];
int charWidth = getCharWidth(cur) + getKerningOffset(last, cur);
if (w2 + charWidth > halfWidth)
break;
last = cur;
w2 += charWidth;
str += cur;
}
// At this point we know that the first 'i' chars are together 'w2'
// pixels wide. We took the first i-1, and add "..." to them.
str += ellipsis;
last = '.';
// The original string is width wide. Of those we already skipped past
// w2 pixels, which means (width - w2) remain.
// The new str is (w2+ellipsisWidth) wide, so we can accommodate about
// (w - (w2+ellipsisWidth)) more pixels.
// Thus we skip ((width - w2) - (w - (w2+ellipsisWidth))) =
// (width + ellipsisWidth - w)
int skip = width + ellipsisWidth - w;
for (; i < s.size() && skip > 0; ++i) {
const Common::String::unsigned_type cur = s[i];
skip -= getCharWidth(cur) + getKerningOffset(last, cur);
last = cur;
}
// Append the remaining chars, if any
for (; i < s.size(); ++i) {
str += s[i];
}
return str;
}
return s;
}
} // End of namespace Graphics

View File

@ -128,7 +128,7 @@ public:
* @return The actual area where the string is drawn.
*/
Common::Rect getBoundingBox(const Common::String &str, int x = 0, int y = 0, const int w = 0, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = false) const;
Common::Rect getBoundingBox(const Common::U32String &str, int x = 0, int _y = 0, const int w = 0, TextAlign align = kTextAlignLeft) const;
Common::Rect getBoundingBox(const Common::U32String &str, int x = 0, int _y = 0, const int w = 0, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = false) const;
/**
* Draw a character at a specific point on a surface.
@ -155,9 +155,9 @@ public:
// TODO: Add doxygen comments to this
void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const;
void drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0) const;
void drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const;
void drawString(ManagedSurface *dst, const Common::String &str, int x, int _y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const;
void drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0) const;
void drawString(ManagedSurface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const;
/**
* Compute and return the width the string str has when rendered using this font.
@ -191,6 +191,7 @@ public:
private:
Common::String handleEllipsis(const Common::String &str, int w) const;
Common::U32String handleEllipsis(const Common::U32String &str, int w) const;
};
} // End of namespace Graphics