Codechange: C++-ify the Layouter and related functions

They all now access a std::string_view, instead of a "const char *"
or std::string (in some cases).

Additionally, GetCharAtPosition and friends now return an index
instead of a "const char *", as it makes for a more clear interface.
pull/532/head
Patric Stout 1 year ago committed by Patric Stout
parent 61d1b330d1
commit 60399e17bd

@ -318,11 +318,11 @@ struct IConsoleWindow : Window
return r; return r;
} }
const char *GetTextCharacterAtPosition(const Point &pt) const override ptrdiff_t GetTextCharacterAtPosition(const Point &pt) const override
{ {
int delta = std::min<int>(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0); int delta = std::min<int>(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0);
if (!IsInsideMM(pt.y, this->height - this->line_height, this->height)) return nullptr; if (!IsInsideMM(pt.y, this->height - this->line_height, this->height)) return -1;
return GetCharAtPosition(_iconsole_cmdline.buf, pt.x - delta); return GetCharAtPosition(_iconsole_cmdline.buf, pt.x - delta);
} }

@ -642,7 +642,7 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
* @return In case of left or center alignment the right most pixel we have drawn to. * @return In case of left or center alignment the right most pixel we have drawn to.
* In case of right alignment the left most pixel we have drawn to. * In case of right alignment the left most pixel we have drawn to.
*/ */
int DrawString(int left, int right, int top, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize) int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
{ {
/* The string may contain control chars to change the font, just use the biggest font for clipping. */ /* The string may contain control chars to change the font, just use the biggest font for clipping. */
int max_height = std::max({FONT_HEIGHT_SMALL, FONT_HEIGHT_NORMAL, FONT_HEIGHT_LARGE, FONT_HEIGHT_MONO}); int max_height = std::max({FONT_HEIGHT_SMALL, FONT_HEIGHT_NORMAL, FONT_HEIGHT_LARGE, FONT_HEIGHT_MONO});
@ -661,28 +661,6 @@ int DrawString(int left, int right, int top, const char *str, TextColour colour,
return DrawLayoutLine(*layout.front(), top, left, right, align, underline, true); return DrawLayoutLine(*layout.front(), top, left, right, align, underline, true);
} }
/**
* Draw string, possibly truncated to make it fit in its allocated space
*
* @param left The left most position to draw on.
* @param right The right most position to draw on.
* @param top The top most position to draw on.
* @param str String to draw.
* @param colour Colour used for drawing the string, for details see _string_colourmap in
* table/palettes.h or docs/ottd-colourtext-palette.png or the enum TextColour in gfx_type.h
* @param align The alignment of the string when drawing left-to-right. In the
* case a right-to-left language is chosen this is inverted so it
* will be drawn in the right direction.
* @param underline Whether to underline what has been drawn or not.
* @param fontsize The size of the initial characters.
* @return In case of left or center alignment the right most pixel we have drawn to.
* In case of right alignment the left most pixel we have drawn to.
*/
int DrawString(int left, int right, int top, const std::string &str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
{
return DrawString(left, right, top, str.c_str(), colour, align, underline, fontsize);
}
/** /**
* Draw string, possibly truncated to make it fit in its allocated space * Draw string, possibly truncated to make it fit in its allocated space
* *
@ -713,7 +691,7 @@ int DrawString(int left, int right, int top, StringID str, TextColour colour, St
* @param maxw maximum string width * @param maxw maximum string width
* @return height of pixels of string when it is drawn * @return height of pixels of string when it is drawn
*/ */
int GetStringHeight(const char *str, int maxw, FontSize fontsize) int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
{ {
Layouter layout(str, maxw, TC_FROMSTRING, fontsize); Layouter layout(str, maxw, TC_FROMSTRING, fontsize);
return layout.GetBounds().height; return layout.GetBounds().height;
@ -765,7 +743,7 @@ Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestio
* @param suggestion Suggested bounding box. * @param suggestion Suggested bounding box.
* @return Bounding box for the multi-line string, may be bigger than \a suggestion. * @return Bounding box for the multi-line string, may be bigger than \a suggestion.
*/ */
Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &suggestion) Dimension GetStringMultiLineBoundingBox(std::string_view str, const Dimension &suggestion)
{ {
Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width)}; Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width)};
return box; return box;
@ -787,7 +765,7 @@ Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &sugges
* *
* @return If \a align is #SA_BOTTOM, the top to where we have written, else the bottom to where we have written. * @return If \a align is #SA_BOTTOM, the top to where we have written, else the bottom to where we have written.
*/ */
int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize) int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
{ {
int maxw = right - left + 1; int maxw = right - left + 1;
int maxh = bottom - top + 1; int maxh = bottom - top + 1;
@ -833,28 +811,6 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, const char *st
return ((align & SA_VERT_MASK) == SA_BOTTOM) ? first_line : last_line; return ((align & SA_VERT_MASK) == SA_BOTTOM) ? first_line : last_line;
} }
/**
* Draw string, possibly over multiple lines.
*
* @param left The left most position to draw on.
* @param right The right most position to draw on.
* @param top The top most position to draw on.
* @param bottom The bottom most position to draw on.
* @param str String to draw.
* @param colour Colour used for drawing the string, for details see _string_colourmap in
* table/palettes.h or docs/ottd-colourtext-palette.png or the enum TextColour in gfx_type.h
* @param align The horizontal and vertical alignment of the string.
* @param underline Whether to underline all strings
* @param fontsize The size of the initial characters.
*
* @return If \a align is #SA_BOTTOM, the top to where we have written, else the bottom to where we have written.
*/
int DrawStringMultiLine(int left, int right, int top, int bottom, const std::string &str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
{
return DrawStringMultiLine(left, right, top, bottom, str.c_str(), colour, align, underline, fontsize);
}
/** /**
* Draw string, possibly over multiple lines. * Draw string, possibly over multiple lines.
* *
@ -888,30 +844,15 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str,
* @param start_fontsize Fontsize to start the text with * @param start_fontsize Fontsize to start the text with
* @return string width and height in pixels * @return string width and height in pixels
*/ */
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize) Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
{ {
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize); Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
return layout.GetBounds(); return layout.GetBounds();
} }
/**
* Return the string dimension in pixels. The height and width are returned
* in a single Dimension value. TINYFONT, BIGFONT modifiers are only
* supported as the first character of the string. The returned dimensions
* are therefore a rough estimation correct for all the current strings
* but not every possible combination
* @param str string to calculate pixel-width
* @param start_fontsize Fontsize to start the text with
* @return string width and height in pixels
*/
Dimension GetStringBoundingBox(const std::string &str, FontSize start_fontsize)
{
return GetStringBoundingBox(str.c_str(), start_fontsize);
}
/** /**
* Get bounding box of a string. Uses parameters set by #SetDParam if needed. * Get bounding box of a string. Uses parameters set by #SetDParam if needed.
* Has the same restrictions as #GetStringBoundingBox(const char *str, FontSize start_fontsize). * Has the same restrictions as #GetStringBoundingBox(std::string_view str, FontSize start_fontsize).
* @param strid String to examine. * @param strid String to examine.
* @return Width and height of the bounding box for the string in pixels. * @return Width and height of the bounding box for the string in pixels.
*/ */
@ -946,10 +887,14 @@ uint GetStringListWidth(const StringID *list, FontSize fontsize)
* @param start_fontsize Font size to start the text with. * @param start_fontsize Font size to start the text with.
* @return Upper left corner of the glyph associated with the character. * @return Upper left corner of the glyph associated with the character.
*/ */
Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsize) Point GetCharPosInString(std::string_view str, const char *ch, FontSize start_fontsize)
{ {
/* Ensure "ch" is inside "str" or at the exact end. */
assert(ch >= str.data() && (ch - str.data()) <= static_cast<ptrdiff_t>(str.size()));
auto it_ch = str.begin() + (ch - str.data());
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize); Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
return layout.GetCharPosition(ch); return layout.GetCharPosition(it_ch);
} }
/** /**
@ -957,11 +902,11 @@ Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsiz
* @param str String to test. * @param str String to test.
* @param x Position relative to the start of the string. * @param x Position relative to the start of the string.
* @param start_fontsize Font size to start the text with. * @param start_fontsize Font size to start the text with.
* @return Pointer to the character at the position or nullptr if there is no character at the position. * @return Index of the character position or -1 if there is no character at the position.
*/ */
const char *GetCharAtPosition(const char *str, int x, FontSize start_fontsize) ptrdiff_t GetCharAtPosition(std::string_view str, int x, FontSize start_fontsize)
{ {
if (x < 0) return nullptr; if (x < 0) return -1;
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize); Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
return layout.GetCharAtPosition(x); return layout.GetCharAtPosition(x);

@ -95,11 +95,9 @@ void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub
void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, bool clicked, StringAlignment align); /* widget.cpp */ void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, bool clicked, StringAlignment align); /* widget.cpp */
std::unique_ptr<uint32[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel zoom = ZOOM_LVL_GUI); std::unique_ptr<uint32[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel zoom = ZOOM_LVL_GUI);
int DrawString(int left, int right, int top, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawString(int left, int right, int top, std::string_view str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL);
int DrawString(int left, int right, int top, const std::string &str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL);
int DrawString(int left, int right, int top, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawString(int left, int right, int top, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL);
int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL);
int DrawStringMultiLine(int left, int right, int top, int bottom, const std::string &str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL);
int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL);
void DrawCharCentered(WChar c, const Rect &r, TextColour colour); void DrawCharCentered(WChar c, const Rect &r, TextColour colour);
@ -110,12 +108,7 @@ void GfxDrawLine(int left, int top, int right, int bottom, int colour, int width
void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3); void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3);
/* Versions of DrawString/DrawStringMultiLine that accept a Rect instead of separate left, right, top and bottom parameters. */ /* Versions of DrawString/DrawStringMultiLine that accept a Rect instead of separate left, right, top and bottom parameters. */
static inline int DrawString(const Rect &r, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL) static inline int DrawString(const Rect &r, std::string_view str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL)
{
return DrawString(r.left, r.right, r.top, str, colour, align, underline, fontsize);
}
static inline int DrawString(const Rect &r, const std::string &str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL)
{ {
return DrawString(r.left, r.right, r.top, str, colour, align, underline, fontsize); return DrawString(r.left, r.right, r.top, str, colour, align, underline, fontsize);
} }
@ -125,12 +118,7 @@ static inline int DrawString(const Rect &r, StringID str, TextColour colour = TC
return DrawString(r.left, r.right, r.top, str, colour, align, underline, fontsize); return DrawString(r.left, r.right, r.top, str, colour, align, underline, fontsize);
} }
static inline int DrawStringMultiLine(const Rect &r, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL) static inline int DrawStringMultiLine(const Rect &r, std::string_view str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL)
{
return DrawStringMultiLine(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize);
}
static inline int DrawStringMultiLine(const Rect &r, const std::string &str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL)
{ {
return DrawStringMultiLine(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize); return DrawStringMultiLine(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize);
} }
@ -145,18 +133,17 @@ static inline void GfxFillRect(const Rect &r, int colour, FillRectMode mode = FI
GfxFillRect(r.left, r.top, r.right, r.bottom, colour, mode); GfxFillRect(r.left, r.top, r.right, r.bottom, colour, mode);
} }
Dimension GetStringBoundingBox(const char *str, FontSize start_fontsize = FS_NORMAL); Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize = FS_NORMAL);
Dimension GetStringBoundingBox(const std::string &str, FontSize start_fontsize = FS_NORMAL);
Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize = FS_NORMAL); Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize = FS_NORMAL);
uint GetStringListWidth(const StringID *list, FontSize fontsize = FS_NORMAL); uint GetStringListWidth(const StringID *list, FontSize fontsize = FS_NORMAL);
int GetStringHeight(const char *str, int maxw, FontSize fontsize = FS_NORMAL); int GetStringHeight(std::string_view str, int maxw, FontSize fontsize = FS_NORMAL);
int GetStringHeight(StringID str, int maxw); int GetStringHeight(StringID str, int maxw);
int GetStringLineCount(StringID str, int maxw); 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(std::string_view 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); Point GetCharPosInString(std::string_view str, const char *ch, FontSize start_fontsize = FS_NORMAL);
const char *GetCharAtPosition(const char *str, int x, FontSize start_fontsize = FS_NORMAL); ptrdiff_t GetCharAtPosition(std::string_view str, int x, FontSize start_fontsize = FS_NORMAL);
void DrawDirtyBlocks(); void DrawDirtyBlocks();
void AddDirtyBlock(int left, int top, int right, int bottom); void AddDirtyBlock(int left, int top, int right, int bottom);

@ -59,7 +59,7 @@ Font::Font(FontSize size, TextColour colour) :
* @tparam T The type of layouter we want. * @tparam T The type of layouter we want.
*/ */
template <typename T> template <typename T>
static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str, FontState &state) static inline void GetLayouter(Layouter::LineCacheItem &line, std::string_view str, FontState &state)
{ {
if (line.buffer != nullptr) free(line.buffer); if (line.buffer != nullptr) free(line.buffer);
@ -72,15 +72,18 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str,
line.buffer = buff_begin; line.buffer = buff_begin;
fontMapping.clear(); fontMapping.clear();
auto cur = str.begin();
/* /*
* Go through the whole string while adding Font instances to the font map * Go through the whole string while adding Font instances to the font map
* whenever the font changes, and convert the wide characters into a format * whenever the font changes, and convert the wide characters into a format
* usable by ParagraphLayout. * usable by ParagraphLayout.
*/ */
for (; buff < buffer_last;) { for (; buff < buffer_last && cur != str.end();) {
WChar c = Utf8Consume(const_cast<const char **>(&str)); WChar c = Utf8Consume(cur);
if (c == '\0' || c == '\n') { if (c == '\0' || c == '\n') {
break; /* Caller should already have filtered out these characters. */
NOT_REACHED();
} else if (c >= SCC_BLUE && c <= SCC_BLACK) { } else if (c >= SCC_BLUE && c <= SCC_BLACK) {
state.SetColour((TextColour)(c - SCC_BLUE)); state.SetColour((TextColour)(c - SCC_BLUE));
} else if (c == SCC_PUSH_COLOUR) { } else if (c == SCC_PUSH_COLOUR) {
@ -123,65 +126,51 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str,
* @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) : string(str) Layouter::Layouter(std::string_view str, int maxw, TextColour colour, FontSize fontsize) : string(str)
{ {
FontState state(colour, fontsize); FontState state(colour, fontsize);
WChar c = 0;
do { while (true) {
/* Scan string for end of line */ auto line_length = str.find_first_of('\n');
const char *lineend = str; auto str_line = str.substr(0, line_length);
for (;;) {
size_t len = Utf8Decode(&c, lineend);
if (c == '\0' || c == '\n') break;
lineend += len;
}
LineCacheItem& line = GetCachedParagraphLayout(str, lineend - str, state); LineCacheItem &line = GetCachedParagraphLayout(str_line, state);
if (line.layout != nullptr) { if (line.layout != nullptr) {
/* Line is in cache */
str = lineend + 1;
state = line.state_after; state = line.state_after;
line.layout->Reflow(); line.layout->Reflow();
} else { } else {
/* Line is new, layout it */ /* Line is new, layout it */
FontState old_state = state; FontState old_state = state;
#if (defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) || defined(WITH_UNISCRIBE) || defined(WITH_COCOA)
const char *old_str = str;
#endif
#if defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ) #if defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)
if (line.layout == nullptr) { if (line.layout == nullptr) {
GetLayouter<ICUParagraphLayoutFactory>(line, str, state); GetLayouter<ICUParagraphLayoutFactory>(line, str_line, state);
if (line.layout == nullptr) { if (line.layout == nullptr) {
state = old_state; state = old_state;
str = old_str;
} }
} }
#endif #endif
#ifdef WITH_UNISCRIBE #ifdef WITH_UNISCRIBE
if (line.layout == nullptr) { if (line.layout == nullptr) {
GetLayouter<UniscribeParagraphLayoutFactory>(line, str, state); GetLayouter<UniscribeParagraphLayoutFactory>(line, str_line, state);
if (line.layout == nullptr) { if (line.layout == nullptr) {
state = old_state; state = old_state;
str = old_str;
} }
} }
#endif #endif
#ifdef WITH_COCOA #ifdef WITH_COCOA
if (line.layout == nullptr) { if (line.layout == nullptr) {
GetLayouter<CoreTextParagraphLayoutFactory>(line, str, state); GetLayouter<CoreTextParagraphLayoutFactory>(line, str_line, state);
if (line.layout == nullptr) { if (line.layout == nullptr) {
state = old_state; state = old_state;
str = old_str;
} }
} }
#endif #endif
if (line.layout == nullptr) { if (line.layout == nullptr) {
GetLayouter<FallbackParagraphLayoutFactory>(line, str, state); GetLayouter<FallbackParagraphLayoutFactory>(line, str_line, state);
} }
} }
@ -191,7 +180,15 @@ Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsi
if (l == nullptr) break; if (l == nullptr) break;
this->push_back(std::move(l)); this->push_back(std::move(l));
} }
} while (c != '\0');
/* Break out if this was the last line. */
if (line_length == std::string_view::npos) {
break;
}
/* Go to the next line. */
str.remove_prefix(line_length + 1);
}
} }
/** /**
@ -210,21 +207,18 @@ Dimension Layouter::GetBounds()
/** /**
* Get the position of a character in the layout. * Get the position of a character in the layout.
* @param ch Character to get the position of. * @param ch Character to get the position of. Must be an iterator of the string passed to the constructor.
* @return Upper left corner of the character relative to the start of the string. * @return Upper left corner of the character relative to the start of the string.
* @note Will only work right for single-line strings. * @note Will only work right for single-line strings.
*/ */
Point Layouter::GetCharPosition(const char *ch) const Point Layouter::GetCharPosition(std::string_view::const_iterator ch) const
{ {
/* Find the code point index which corresponds to the char /* Find the code point index which corresponds to the char
* pointer into our UTF-8 source string. */ * pointer into our UTF-8 source string. */
size_t index = 0; size_t index = 0;
const char *str = this->string; auto str = this->string.begin();
while (str < ch) { while (str < ch && str != this->string.end()) {
WChar c; WChar c = Utf8Consume(str);
size_t len = Utf8Decode(&c, str);
if (c == '\0' || c == '\n') break;
str += len;
index += this->front()->GetInternalCharLength(c); index += this->front()->GetInternalCharLength(c);
} }
@ -233,7 +227,7 @@ Point Layouter::GetCharPosition(const char *ch) const
const auto &line = this->front(); const auto &line = this->front();
/* Pointer to the end-of-string/line marker? Return total line width. */ /* Pointer to the end-of-string/line marker? Return total line width. */
if (*ch == '\0' || *ch == '\n') { if (ch == this->string.end() || *ch == '\0' || *ch == '\n') {
Point p = { line->GetWidth(), 0 }; Point p = { line->GetWidth(), 0 };
return p; return p;
} }
@ -259,9 +253,9 @@ Point Layouter::GetCharPosition(const char *ch) const
/** /**
* Get the character that is at a position. * Get the character that is at a position.
* @param x Position in the string. * @param x Position in the string.
* @return Pointer to the character at the position or nullptr if no character is at the position. * @return Index of the position or -1 if no character is at the position.
*/ */
const char *Layouter::GetCharAtPosition(int x) const ptrdiff_t Layouter::GetCharAtPosition(int x) const
{ {
const auto &line = this->front(); const auto &line = this->front();
@ -280,17 +274,18 @@ const char *Layouter::GetCharAtPosition(int x) const
size_t index = run.GetGlyphToCharMap()[i]; size_t index = run.GetGlyphToCharMap()[i];
size_t cur_idx = 0; size_t cur_idx = 0;
for (const char *str = this->string; *str != '\0'; ) { int char_index = 0;
if (cur_idx == index) return str; for (auto str = this->string.begin(); str != this->string.end(); char_index++) {
if (cur_idx == index) return char_index;
WChar c = Utf8Consume(&str); WChar c = Utf8Consume(str);
cur_idx += line->GetInternalCharLength(c); cur_idx += line->GetInternalCharLength(c);
} }
} }
} }
} }
return nullptr; return -1;
} }
/** /**
@ -332,18 +327,17 @@ void Layouter::ResetFontCache(FontSize size)
* Get reference to cache item. * Get reference to cache item.
* If the item does not exist yet, it is default constructed. * If the item does not exist yet, it is default constructed.
* @param str Source string of the line (including colour and font size codes). * @param str Source string of the line (including colour and font size codes).
* @param len Length of \a str in bytes (no termination).
* @param state State of the font at the beginning of the line. * @param state State of the font at the beginning of the line.
* @return Reference to cache item. * @return Reference to cache item.
*/ */
Layouter::LineCacheItem &Layouter::GetCachedParagraphLayout(const char *str, size_t len, const FontState &state) Layouter::LineCacheItem &Layouter::GetCachedParagraphLayout(std::string_view str, const FontState &state)
{ {
if (linecache == nullptr) { if (linecache == nullptr) {
/* Create linecache on first access to avoid trouble with initialisation order of static variables. */ /* Create linecache on first access to avoid trouble with initialisation order of static variables. */
linecache = new LineCache(); linecache = new LineCache();
} }
if (auto match = linecache->find(LineCacheQuery{state, std::string_view{str, len}}); if (auto match = linecache->find(LineCacheQuery{state, str});
match != linecache->end()) { match != linecache->end()) {
return match->second; return match->second;
} }
@ -351,7 +345,7 @@ Layouter::LineCacheItem &Layouter::GetCachedParagraphLayout(const char *str, siz
/* Create missing entry */ /* Create missing entry */
LineCacheKey key; LineCacheKey key;
key.state_before = state; key.state_before = state;
key.str.assign(str, len); key.str.assign(str);
return (*linecache)[key]; return (*linecache)[key];
} }

@ -124,7 +124,7 @@ public:
* It also accounts for the memory allocations and frees. * It also accounts for the memory allocations and frees.
*/ */
class Layouter : public std::vector<std::unique_ptr<const ParagraphLayouter::Line>> { class Layouter : public std::vector<std::unique_ptr<const ParagraphLayouter::Line>> {
const char *string; ///< Pointer to the original string. std::string_view string; ///< Pointer to the original string.
/** Key into the linecache */ /** Key into the linecache */
struct LineCacheKey { struct LineCacheKey {
@ -168,17 +168,17 @@ private:
typedef std::map<LineCacheKey, LineCacheItem, LineCacheCompare> LineCache; typedef std::map<LineCacheKey, LineCacheItem, LineCacheCompare> LineCache;
static LineCache *linecache; static LineCache *linecache;
static LineCacheItem &GetCachedParagraphLayout(const char *str, size_t len, const FontState &state); static LineCacheItem &GetCachedParagraphLayout(std::string_view str, const FontState &state);
typedef SmallMap<TextColour, Font *> FontColourMap; typedef SmallMap<TextColour, Font *> FontColourMap;
static FontColourMap fonts[FS_END]; static FontColourMap fonts[FS_END];
public: public:
static Font *GetFont(FontSize size, TextColour colour); static Font *GetFont(FontSize size, TextColour colour);
Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL); Layouter(std::string_view str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL);
Dimension GetBounds(); Dimension GetBounds();
Point GetCharPosition(const char *ch) const; Point GetCharPosition(std::string_view::const_iterator ch) const;
const char *GetCharAtPosition(int x) const; ptrdiff_t GetCharAtPosition(int x) const;
static void ResetFontCache(FontSize size); static void ResetFontCache(FontSize size);
static void ResetLineCache(); static void ResetLineCache();

@ -896,9 +896,9 @@ Rect QueryString::GetBoundingRect(const Window *w, int wid, const char *from, co
* @param w Window the edit box is in. * @param w Window the edit box is in.
* @param wid Widget index. * @param wid Widget index.
* @param pt Position to test. * @param pt Position to test.
* @return Pointer to the character at the position or nullptr if no character is at the position. * @return Index of the character position or -1 if no character is at the position.
*/ */
const char *QueryString::GetCharAtPosition(const Window *w, int wid, const Point &pt) const ptrdiff_t QueryString::GetCharAtPosition(const Window *w, int wid, const Point &pt) const
{ {
const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid); const NWidgetLeaf *wi = w->GetWidget<NWidgetLeaf>(wid);
@ -910,7 +910,7 @@ const char *QueryString::GetCharAtPosition(const Window *w, int wid, const Point
Rect r = wi->GetCurrentRect().Indent(clearbtn_width, !rtl).Shrink(WidgetDimensions::scaled.framerect); Rect r = wi->GetCurrentRect().Indent(clearbtn_width, !rtl).Shrink(WidgetDimensions::scaled.framerect);
if (!IsInsideMM(pt.y, r.top, r.bottom)) return nullptr; if (!IsInsideMM(pt.y, r.top, r.bottom)) return -1;
/* Clamp caret position to be inside our current width. */ /* Clamp caret position to be inside our current width. */
const Textbuf *tb = &this->text; const Textbuf *tb = &this->text;

@ -54,7 +54,7 @@ public:
Point GetCaretPosition(const Window *w, int wid) const; Point GetCaretPosition(const Window *w, int wid) const;
Rect GetBoundingRect(const Window *w, int wid, const char *from, const char *to) const; Rect GetBoundingRect(const Window *w, int wid, const char *from, const char *to) const;
const char *GetCharAtPosition(const Window *w, int wid, const Point &pt) const; ptrdiff_t GetCharAtPosition(const Window *w, int wid, const Point &pt) const;
/** /**
* Get the current text. * Get the current text.

@ -1057,10 +1057,11 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel
Point pt = { (int)view_pt.x, (int)[ self frame ].size.height - (int)view_pt.y }; Point pt = { (int)view_pt.x, (int)[ self frame ].size.height - (int)view_pt.y };
const char *ch = _focused_window->GetTextCharacterAtPosition(pt); auto index = _focused_window->GetTextCharacterAtPosition(pt);
if (ch == nullptr) return NSNotFound; if (index == -1) return NSNotFound;
return CountUtf16Units(_focused_window->GetFocusedText(), ch); auto text = _focused_window->GetFocusedText();
return CountUtf16Units(text, text + index);
} }
/** Get the bounding rect for the given range. */ /** Get the bounding rect for the given range. */

@ -440,15 +440,15 @@ void Window::UpdateQueryStringSize()
/** /**
* Get the character that is rendered at a position by the focused edit box. * Get the character that is rendered at a position by the focused edit box.
* @param pt The position to test. * @param pt The position to test.
* @return Pointer to the character at the position or nullptr if no character is at the position. * @return Index of the character position or -1 if no character is at the position.
*/ */
/* virtual */ const char *Window::GetTextCharacterAtPosition(const Point &pt) const /* virtual */ ptrdiff_t Window::GetTextCharacterAtPosition(const Point &pt) const
{ {
if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) { if (this->nested_focus != nullptr && this->nested_focus->type == WWT_EDITBOX) {
return this->GetQueryString(this->nested_focus->index)->GetCharAtPosition(this, this->nested_focus->index, pt); return this->GetQueryString(this->nested_focus->index)->GetCharAtPosition(this, this->nested_focus->index, pt);
} }
return nullptr; return -1;
} }
/** /**

@ -281,7 +281,7 @@ public:
virtual const char *GetMarkedText(size_t *length) const; virtual const char *GetMarkedText(size_t *length) const;
virtual Point GetCaretPosition() const; virtual Point GetCaretPosition() const;
virtual Rect GetTextBoundingRect(const char *from, const char *to) const; virtual Rect GetTextBoundingRect(const char *from, const char *to) const;
virtual const char *GetTextCharacterAtPosition(const Point &pt) const; virtual ptrdiff_t GetTextCharacterAtPosition(const Point &pt) const;
void InitNested(WindowNumber number = 0); void InitNested(WindowNumber number = 0);
void CreateNestedTree(bool fill_nested = true); void CreateNestedTree(bool fill_nested = true);

Loading…
Cancel
Save