mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r25651) -Fix: Textbuf caret rendering for complex scripts (e.g. Tamil).
This commit is contained in:
parent
9d7ec75fc0
commit
33f3cf3a5d
14
src/gfx.cpp
14
src/gfx.cpp
|
@ -707,6 +707,20 @@ Dimension GetStringBoundingBox(StringID strid)
|
||||||
return GetStringBoundingBox(buffer);
|
return GetStringBoundingBox(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the leading corner of a character in a single-line string relative
|
||||||
|
* to the start of the string.
|
||||||
|
* @param str String containing the character.
|
||||||
|
* @param ch Pointer to the character in the string.
|
||||||
|
* @param start_fontsize Font size to start the text with.
|
||||||
|
* @return Upper left corner of the glyph associated with the character.
|
||||||
|
*/
|
||||||
|
Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsize)
|
||||||
|
{
|
||||||
|
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
|
||||||
|
return layout.GetCharPosition(ch);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw single character horizontally centered around (x,y)
|
* Draw single character horizontally centered around (x,y)
|
||||||
* @param c Character (glyph) to draw
|
* @param c Character (glyph) to draw
|
||||||
|
|
|
@ -126,6 +126,7 @@ int GetStringLineCount(StringID str, int maxw);
|
||||||
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion);
|
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion);
|
||||||
Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &suggestion);
|
Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &suggestion);
|
||||||
void LoadStringWidthTable(bool monospace = false);
|
void LoadStringWidthTable(bool monospace = false);
|
||||||
|
Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsize = FS_NORMAL);
|
||||||
|
|
||||||
void DrawDirtyBlocks();
|
void DrawDirtyBlocks();
|
||||||
void SetDirtyBlocks(int left, int top, int right, int bottom);
|
void SetDirtyBlocks(int left, int top, int right, int bottom);
|
||||||
|
|
|
@ -423,7 +423,7 @@ ParagraphLayout *Layouter::GetParagraphLayout(WChar *buff, WChar *buff_end, Font
|
||||||
* @param colour The colour of the font.
|
* @param colour The colour of the font.
|
||||||
* @param fontsize The size of font to use.
|
* @param fontsize The size of font to use.
|
||||||
*/
|
*/
|
||||||
Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsize)
|
Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsize) : string(str)
|
||||||
{
|
{
|
||||||
FontState state(colour, fontsize);
|
FontState state(colour, fontsize);
|
||||||
WChar c = 0;
|
WChar c = 0;
|
||||||
|
@ -512,6 +512,59 @@ Dimension Layouter::GetBounds()
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position of a character in the layout.
|
||||||
|
* @param ch Character to get the position of.
|
||||||
|
* @return Upper left corner of the character relative to the start of the string.
|
||||||
|
* @note Will only work right for single-line strings.
|
||||||
|
*/
|
||||||
|
Point Layouter::GetCharPosition(const char *ch) const
|
||||||
|
{
|
||||||
|
/* Find the code point index which corresponds to the char
|
||||||
|
* pointer into our UTF-8 source string. */
|
||||||
|
size_t index = 0;
|
||||||
|
const char *str = this->string;
|
||||||
|
while (str < ch) {
|
||||||
|
WChar c;
|
||||||
|
size_t len = Utf8Decode(&c, str);
|
||||||
|
if (c == '\0' || c == '\n') break;
|
||||||
|
str += len;
|
||||||
|
#ifdef WITH_ICU
|
||||||
|
/* ICU uses UTF-16 internally which means we need to account for surrogate pairs. */
|
||||||
|
index += len < 4 ? 1 : 2;
|
||||||
|
#else
|
||||||
|
index++;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str == ch) {
|
||||||
|
/* Valid character. */
|
||||||
|
const ParagraphLayout::Line *line = *this->Begin();
|
||||||
|
|
||||||
|
/* Pointer to the end-of-string/line marker? Return total line width. */
|
||||||
|
if (*ch == '\0' || *ch == '\n') {
|
||||||
|
Point p = { line->getWidth(), 0 };
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scan all runs until we've found our code point index. */
|
||||||
|
for (int run_index = 0; run_index < line->countRuns(); run_index++) {
|
||||||
|
const ParagraphLayout::VisualRun *run = line->getVisualRun(run_index);
|
||||||
|
|
||||||
|
for (int i = 0; i < run->getGlyphCount(); i++) {
|
||||||
|
/* Matching glyph? Return position. */
|
||||||
|
if ((size_t)run->getGlyphToCharMap()[i] == index) {
|
||||||
|
Point p = { run->getPositions()[i * 2], run->getPositions()[i * 2 + 1] };
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Point p = { 0, 0 };
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a static font instance.
|
* Get a static font instance.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -168,6 +168,8 @@ class Layouter : public AutoDeleteSmallVector<ParagraphLayout::Line *, 4> {
|
||||||
typedef WChar CharType; ///< The type of character used within the layouter.
|
typedef WChar CharType; ///< The type of character used within the layouter.
|
||||||
#endif /* WITH_ICU */
|
#endif /* WITH_ICU */
|
||||||
|
|
||||||
|
const char *string; ///< Pointer to the original string.
|
||||||
|
|
||||||
size_t AppendToBuffer(CharType *buff, const CharType *buffer_last, WChar c);
|
size_t AppendToBuffer(CharType *buff, const CharType *buffer_last, WChar c);
|
||||||
ParagraphLayout *GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping);
|
ParagraphLayout *GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping);
|
||||||
|
|
||||||
|
@ -209,6 +211,7 @@ class Layouter : public AutoDeleteSmallVector<ParagraphLayout::Line *, 4> {
|
||||||
public:
|
public:
|
||||||
Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL);
|
Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL);
|
||||||
Dimension GetBounds();
|
Dimension GetBounds();
|
||||||
|
Point GetCharPosition(const char *ch) const;
|
||||||
|
|
||||||
static void ResetFontCache(FontSize size);
|
static void ResetFontCache(FontSize size);
|
||||||
static void ResetLineCache();
|
static void ResetLineCache();
|
||||||
|
|
|
@ -81,18 +81,17 @@ void Textbuf::DelChar(bool backspace)
|
||||||
if (backspace) s = Utf8PrevChar(s);
|
if (backspace) s = Utf8PrevChar(s);
|
||||||
|
|
||||||
uint16 len = (uint16)Utf8Decode(&c, s);
|
uint16 len = (uint16)Utf8Decode(&c, s);
|
||||||
uint width = GetCharacterWidth(FS_NORMAL, c);
|
|
||||||
|
|
||||||
this->pixels -= width;
|
|
||||||
if (backspace) {
|
|
||||||
this->caretpos -= len;
|
|
||||||
this->caretxoffs -= width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Move the remaining characters over the marker */
|
/* Move the remaining characters over the marker */
|
||||||
memmove(s, s + len, this->bytes - (s - this->buf) - len);
|
memmove(s, s + len, this->bytes - (s - this->buf) - len);
|
||||||
this->bytes -= len;
|
this->bytes -= len;
|
||||||
this->chars--;
|
this->chars--;
|
||||||
|
|
||||||
|
this->UpdateWidth();
|
||||||
|
if (backspace) {
|
||||||
|
this->caretpos -= len;
|
||||||
|
this->UpdateCaretPosition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,17 +158,16 @@ void Textbuf::DeleteAll()
|
||||||
*/
|
*/
|
||||||
bool Textbuf::InsertChar(WChar key)
|
bool Textbuf::InsertChar(WChar key)
|
||||||
{
|
{
|
||||||
const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
|
|
||||||
uint16 len = (uint16)Utf8CharLen(key);
|
uint16 len = (uint16)Utf8CharLen(key);
|
||||||
if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) {
|
if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) {
|
||||||
memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos);
|
memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos);
|
||||||
Utf8Encode(this->buf + this->caretpos, key);
|
Utf8Encode(this->buf + this->caretpos, key);
|
||||||
this->chars++;
|
this->chars++;
|
||||||
this->bytes += len;
|
this->bytes += len;
|
||||||
this->pixels += charwidth;
|
this->UpdateWidth();
|
||||||
|
|
||||||
this->caretpos += len;
|
this->caretpos += len;
|
||||||
this->caretxoffs += charwidth;
|
this->UpdateCaretPosition();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -187,7 +185,7 @@ bool Textbuf::InsertClipboard()
|
||||||
|
|
||||||
if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false;
|
if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false;
|
||||||
|
|
||||||
uint16 pixels = 0, bytes = 0, chars = 0;
|
uint16 bytes = 0, chars = 0;
|
||||||
WChar c;
|
WChar c;
|
||||||
for (const char *ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
|
for (const char *ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
|
||||||
if (!IsValidChar(c, this->afilter)) break;
|
if (!IsValidChar(c, this->afilter)) break;
|
||||||
|
@ -196,9 +194,6 @@ bool Textbuf::InsertClipboard()
|
||||||
if (this->bytes + bytes + len > this->max_bytes) break;
|
if (this->bytes + bytes + len > this->max_bytes) break;
|
||||||
if (this->chars + chars + 1 > this->max_chars) break;
|
if (this->chars + chars + 1 > this->max_chars) break;
|
||||||
|
|
||||||
byte char_pixels = GetCharacterWidth(FS_NORMAL, c);
|
|
||||||
|
|
||||||
pixels += char_pixels;
|
|
||||||
bytes += len;
|
bytes += len;
|
||||||
chars++;
|
chars++;
|
||||||
}
|
}
|
||||||
|
@ -207,8 +202,6 @@ bool Textbuf::InsertClipboard()
|
||||||
|
|
||||||
memmove(this->buf + this->caretpos + bytes, this->buf + this->caretpos, this->bytes - this->caretpos);
|
memmove(this->buf + this->caretpos + bytes, this->buf + this->caretpos, this->bytes - this->caretpos);
|
||||||
memcpy(this->buf + this->caretpos, utf8_buf, bytes);
|
memcpy(this->buf + this->caretpos, utf8_buf, bytes);
|
||||||
this->pixels += pixels;
|
|
||||||
this->caretxoffs += pixels;
|
|
||||||
|
|
||||||
this->bytes += bytes;
|
this->bytes += bytes;
|
||||||
this->chars += chars;
|
this->chars += chars;
|
||||||
|
@ -217,6 +210,9 @@ bool Textbuf::InsertClipboard()
|
||||||
assert(this->chars <= this->max_chars);
|
assert(this->chars <= this->max_chars);
|
||||||
this->buf[this->bytes - 1] = '\0'; // terminating zero
|
this->buf[this->bytes - 1] = '\0'; // terminating zero
|
||||||
|
|
||||||
|
this->UpdateWidth();
|
||||||
|
this->UpdateCaretPosition();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +238,7 @@ WChar Textbuf::MoveCaretLeft()
|
||||||
const char *s = Utf8PrevChar(this->buf + this->caretpos);
|
const char *s = Utf8PrevChar(this->buf + this->caretpos);
|
||||||
Utf8Decode(&c, s);
|
Utf8Decode(&c, s);
|
||||||
this->caretpos = s - this->buf;
|
this->caretpos = s - this->buf;
|
||||||
this->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
|
this->UpdateCaretPosition();
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
@ -267,12 +263,24 @@ WChar Textbuf::MoveCaretRight()
|
||||||
|
|
||||||
WChar c;
|
WChar c;
|
||||||
this->caretpos += (uint16)Utf8Decode(&c, this->buf + this->caretpos);
|
this->caretpos += (uint16)Utf8Decode(&c, this->buf + this->caretpos);
|
||||||
this->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
|
this->UpdateCaretPosition();
|
||||||
|
|
||||||
Utf8Decode(&c, this->buf + this->caretpos);
|
Utf8Decode(&c, this->buf + this->caretpos);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Update pixel width of the text. */
|
||||||
|
void Textbuf::UpdateWidth()
|
||||||
|
{
|
||||||
|
this->pixels = GetStringBoundingBox(this->buf, FS_NORMAL).width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update pixel position of the caret. */
|
||||||
|
void Textbuf::UpdateCaretPosition()
|
||||||
|
{
|
||||||
|
this->caretxoffs = this->chars > 1 ? GetCharPosInString(this->buf, this->buf + this->caretpos, FS_NORMAL).x : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle text navigation with arrow keys left/right.
|
* Handle text navigation with arrow keys left/right.
|
||||||
* This defines where the caret will blink and the next character interaction will occur
|
* This defines where the caret will blink and the next character interaction will occur
|
||||||
|
@ -336,12 +344,12 @@ bool Textbuf::MovePos(uint16 keycode)
|
||||||
|
|
||||||
case WKC_HOME:
|
case WKC_HOME:
|
||||||
this->caretpos = 0;
|
this->caretpos = 0;
|
||||||
this->caretxoffs = 0;
|
this->UpdateCaretPosition();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case WKC_END:
|
case WKC_END:
|
||||||
this->caretpos = this->bytes - 1;
|
this->caretpos = this->bytes - 1;
|
||||||
this->caretxoffs = this->pixels;
|
this->UpdateCaretPosition();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -418,21 +426,20 @@ void Textbuf::UpdateSize()
|
||||||
{
|
{
|
||||||
const char *buf = this->buf;
|
const char *buf = this->buf;
|
||||||
|
|
||||||
this->pixels = 0;
|
|
||||||
this->chars = this->bytes = 1; // terminating zero
|
this->chars = this->bytes = 1; // terminating zero
|
||||||
|
|
||||||
WChar c;
|
WChar c;
|
||||||
while ((c = Utf8Consume(&buf)) != '\0') {
|
while ((c = Utf8Consume(&buf)) != '\0') {
|
||||||
this->pixels += GetCharacterWidth(FS_NORMAL, c);
|
|
||||||
this->bytes += Utf8CharLen(c);
|
this->bytes += Utf8CharLen(c);
|
||||||
this->chars++;
|
this->chars++;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(this->bytes <= this->max_bytes);
|
assert(this->bytes <= this->max_bytes);
|
||||||
assert(this->chars <= this->max_chars);
|
assert(this->chars <= this->max_chars);
|
||||||
|
|
||||||
this->caretpos = this->bytes - 1;
|
this->caretpos = this->bytes - 1;
|
||||||
this->caretxoffs = this->pixels;
|
this->UpdateWidth();
|
||||||
|
|
||||||
|
this->UpdateCaretPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -69,6 +69,8 @@ private:
|
||||||
bool CanMoveCaretRight();
|
bool CanMoveCaretRight();
|
||||||
WChar MoveCaretRight();
|
WChar MoveCaretRight();
|
||||||
|
|
||||||
|
void UpdateWidth();
|
||||||
|
void UpdateCaretPosition();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* TEXTBUF_TYPE_H */
|
#endif /* TEXTBUF_TYPE_H */
|
||||||
|
|
Loading…
Reference in New Issue