diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index acfda71049..ab66588c2f 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -239,7 +239,7 @@ public: for (auto it = first; it != last; ++it) { const BridgeSpec *b = it->spec; DrawSpriteIgnorePadding(b->sprite, b->pal, tr.WithWidth(this->icon_width, rtl), SA_HOR_CENTER | SA_BOTTOM); - DrawStringMultiLine(tr.Indent(this->icon_width + WidgetDimensions::scaled.hsep_normal, rtl), GetBridgeSelectString(*it)); + DrawStringMultiLineWithClipping(tr.Indent(this->icon_width + WidgetDimensions::scaled.hsep_normal, rtl), GetBridgeSelectString(*it)); tr = tr.Translate(0, this->resize.step_height); } break; diff --git a/src/error_gui.cpp b/src/error_gui.cpp index 1a22c27267..0814850aea 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -194,14 +194,14 @@ public: case WID_EM_MESSAGE: if (this->detailed_msg.empty()) { - DrawStringMultiLine(r, this->summary_msg.GetDecodedString(), TC_FROMSTRING, SA_CENTER); + DrawStringMultiLineWithClipping(r, this->summary_msg.GetDecodedString(), TC_FROMSTRING, SA_CENTER); } else if (this->extra_msg.empty()) { /* Extra space when message is shorter than company face window */ int extra = (r.Height() - this->height_summary - this->height_detailed - WidgetDimensions::scaled.vsep_wide) / 2; /* Note: NewGRF supplied error message often do not start with a colour code, so default to white. */ - DrawStringMultiLine(r.WithHeight(this->height_summary + extra, false), this->summary_msg.GetDecodedString(), TC_WHITE, SA_CENTER); - DrawStringMultiLine(r.WithHeight(this->height_detailed + extra, true), this->detailed_msg.GetDecodedString(), TC_WHITE, SA_CENTER); + DrawStringMultiLineWithClipping(r.WithHeight(this->height_summary + extra, false), this->summary_msg.GetDecodedString(), TC_WHITE, SA_CENTER); + DrawStringMultiLineWithClipping(r.WithHeight(this->height_detailed + extra, true), this->detailed_msg.GetDecodedString(), TC_WHITE, SA_CENTER); } else { /* Extra space when message is shorter than company face window */ int extra = (r.Height() - this->height_summary - this->height_detailed - this->height_extra - (WidgetDimensions::scaled.vsep_wide * 2)) / 3; @@ -210,9 +210,9 @@ public: Rect top_section = r.WithHeight(this->height_summary + extra, false); Rect bottom_section = r.WithHeight(this->height_extra + extra, true); Rect middle_section = { top_section.left, top_section.bottom, top_section.right, bottom_section.top }; - DrawStringMultiLine(top_section, this->summary_msg.GetDecodedString(), TC_WHITE, SA_CENTER); - DrawStringMultiLine(middle_section, this->detailed_msg.GetDecodedString(), TC_WHITE, SA_CENTER); - DrawStringMultiLine(bottom_section, this->extra_msg.GetDecodedString(), TC_WHITE, SA_CENTER); + DrawStringMultiLineWithClipping(top_section, this->summary_msg.GetDecodedString(), TC_WHITE, SA_CENTER); + DrawStringMultiLineWithClipping(middle_section, this->detailed_msg.GetDecodedString(), TC_WHITE, SA_CENTER); + DrawStringMultiLineWithClipping(bottom_section, this->extra_msg.GetDecodedString(), TC_WHITE, SA_CENTER); } break; diff --git a/src/gfx.cpp b/src/gfx.cpp index 13d9776d12..364e9955b5 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -839,6 +839,41 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, return DrawStringMultiLine(left, right, top, bottom, GetString(str), colour, align, underline, fontsize); } +/** + * Draw a multiline string, possibly over multiple lines, if the region is within the current display clipping area. + * @note With clipping, it is not possible to determine how tall the rendered text will be, as it's not layouted. + * Regulard DrawStringMultiLine must be used if the height needs to be known. + * + * @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 true iff the string was drawn. + */ +bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, 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. */ + int max_height = std::max({GetCharacterHeight(FS_SMALL), GetCharacterHeight(FS_NORMAL), GetCharacterHeight(FS_LARGE), GetCharacterHeight(FS_MONO)}); + + /* Funny glyphs may extent outside the usual bounds, so relax the clipping somewhat. */ + int extra = max_height / 2; + + if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > bottom + extra || + _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) { + return false; + } + + DrawStringMultiLine(left, right, top, bottom, str, colour, align, underline, fontsize); + return true; +} + /** * Return the string dimension in pixels. The height and width are returned * in a single Dimension value. TINYFONT, BIGFONT modifiers are only diff --git a/src/gfx_func.h b/src/gfx_func.h index 0a3ab5b5c2..ed9d2c2cec 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -99,6 +99,7 @@ int DrawString(int left, int right, int top, std::string_view str, TextColour co 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, 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, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); +bool DrawStringMultiLineWithClipping(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); void DrawCharCentered(char32_t c, const Rect &r, TextColour colour); @@ -129,6 +130,11 @@ inline int DrawStringMultiLine(const Rect &r, StringID str, TextColour colour = return DrawStringMultiLine(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize); } +inline bool DrawStringMultiLineWithClipping(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 DrawStringMultiLineWithClipping(r.left, r.right, r.top, r.bottom, str, colour, align, underline, fontsize); +} + inline void GfxFillRect(const Rect &r, int colour, FillRectMode mode = FILLRECT_OPAQUE) { GfxFillRect(r.left, r.top, r.right, r.bottom, colour, mode); diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 011e1d41c2..38d1876301 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -454,11 +454,11 @@ protected: TimerGameEconomy::Year year = this->year; for (int i = 0; i < this->num_on_x_axis; i++) { if (rtl) { - DrawStringMultiLine(x + x_sep, x, y, this->height, + DrawStringMultiLineWithClipping(x + x_sep, x, y, this->height, GetString(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + month, year), GRAPH_AXIS_LABEL_COLOUR, SA_LEFT); } else { - DrawStringMultiLine(x, x + x_sep, y, this->height, + DrawStringMultiLineWithClipping(x, x + x_sep, y, this->height, GetString(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + month, year), GRAPH_AXIS_LABEL_COLOUR, SA_LEFT); } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 4f2c76fdc7..70e3553733 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -928,7 +928,7 @@ struct GameOptionsWindow : Window { /* Draw the 'some search results are hidden' notice. */ if (this->warn_missing != WHR_NONE) { - DrawStringMultiLine(panel.WithHeight(this->warn_lines * GetCharacterHeight(FS_NORMAL)), + DrawStringMultiLineWithClipping(panel.WithHeight(this->warn_lines * GetCharacterHeight(FS_NORMAL)), GetString(warn_str, _game_settings_restrict_dropdown[this->filter.min_cat]), TC_BLACK, SA_CENTER); } diff --git a/src/story_gui.cpp b/src/story_gui.cpp index c74e840185..54707cc653 100644 --- a/src/story_gui.cpp +++ b/src/story_gui.cpp @@ -712,7 +712,7 @@ public: switch (ce.pe->type) { case SPET_TEXT: - y_offset = DrawStringMultiLine(ce.bounds.left, ce.bounds.right, ce.bounds.top - scrollpos, ce.bounds.bottom - scrollpos, + DrawStringMultiLineWithClipping(ce.bounds.left, ce.bounds.right, ce.bounds.top - scrollpos, ce.bounds.bottom - scrollpos, ce.pe->text.GetDecodedString(), TC_BLACK, SA_TOP | SA_LEFT); break; diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp index 4b76254054..1046af2813 100644 --- a/src/textfile_gui.cpp +++ b/src/textfile_gui.cpp @@ -592,7 +592,7 @@ void TextfileWindow::AfterLoadMarkdown() int y_offset = (line.top - pos) * line_height; if (IsWidgetLowered(WID_TF_WRAPTEXT)) { - DrawStringMultiLine(fr.left, fr.right, y_offset, fr.bottom, line.text, line.colour, SA_TOP | SA_LEFT, false, FS_MONO); + DrawStringMultiLineWithClipping(fr.left, fr.right, y_offset, y_offset + (line.bottom - line.top) * line_height, line.text, line.colour, SA_TOP | SA_LEFT, false, FS_MONO); } else { DrawString(fr.left, fr.right, y_offset, line.text, line.colour, SA_TOP | SA_LEFT, false, FS_MONO); }