OpenTTD Source 20260108-master-g8ba1860eaa
error_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "stdafx.h"
12#include "landscape.h"
13#include "newgrf_text.h"
14#include "error.h"
15#include "viewport_func.h"
16#include "gfx_func.h"
17#include "string_func.h"
18#include "company_base.h"
19#include "company_func.h"
21#include "strings_func.h"
22#include "zoom_func.h"
23#include "window_func.h"
24#include "console_func.h"
25#include "window_gui.h"
26#include "timer/timer.h"
27#include "timer/timer_window.h"
28
30
31#include "table/sprites.h"
32#include "table/strings.h"
33
34#include "safeguards.h"
35
36static constexpr std::initializer_list<NWidgetPart> _nested_errmsg_widgets = {
38 NWidget(WWT_CLOSEBOX, COLOUR_RED),
39 NWidget(WWT_CAPTION, COLOUR_RED, WID_EM_CAPTION), SetStringTip(STR_ERROR_MESSAGE_CAPTION),
41 NWidget(WWT_PANEL, COLOUR_RED),
42 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_EM_MESSAGE), SetPadding(WidgetDimensions::unscaled.modalpopup), SetFill(1, 0), SetMinimalSize(236, 0),
44};
45
46static WindowDesc _errmsg_desc(
47 WDP_MANUAL, {}, 0, 0,
49 {},
50 _nested_errmsg_widgets
51);
52
53static constexpr std::initializer_list<NWidgetPart> _nested_errmsg_face_widgets = {
55 NWidget(WWT_CLOSEBOX, COLOUR_RED),
58 NWidget(WWT_PANEL, COLOUR_RED),
60 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_EM_FACE), SetPadding(2, 0, 2, 2), SetFill(0, 1), SetMinimalSize(92, 119),
61 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_EM_MESSAGE), SetPadding(WidgetDimensions::unscaled.modalpopup), SetFill(1, 1), SetMinimalSize(236, 0),
64};
65
66static WindowDesc _errmsg_face_desc(
67 WDP_MANUAL, {}, 0, 0,
69 {},
70 _nested_errmsg_face_widgets
71);
72
82ErrorMessageData::ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical, int x, int y, EncodedString &&extra_msg, CompanyID company) :
83 is_critical(is_critical),
84 summary_msg(std::move(summary_msg)),
85 detailed_msg(std::move(detailed_msg)),
86 extra_msg(std::move(extra_msg)),
87 position(x, y),
88 company(company)
89{
90 assert(!this->summary_msg.empty());
91}
92
97
100private:
101 uint height_summary = 0;
103 uint height_extra = 0;
104
105 TimeoutTimer<TimerWindow> display_timeout = {std::chrono::seconds(_settings_client.gui.errmsg_duration), [this]() {
106 this->Close();
107 }};
108
109public:
110 ErrmsgWindow(const ErrorMessageData &data) :
111 Window(data.HasFace() ? _errmsg_face_desc : _errmsg_desc),
112 ErrorMessageData(data)
113 {
114 this->InitNested();
115 }
116
117 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
118 {
119 switch (widget) {
120 case WID_EM_MESSAGE: {
121 this->height_summary = GetStringHeight(this->summary_msg.GetDecodedString(), size.width);
122 this->height_detailed = (this->detailed_msg.empty()) ? 0 : GetStringHeight(this->detailed_msg.GetDecodedString(), size.width);
123 this->height_extra = (this->extra_msg.empty()) ? 0 : GetStringHeight(this->extra_msg.GetDecodedString(), size.width);
124
125 uint panel_height = this->height_summary;
126 if (!this->detailed_msg.empty()) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide;
127 if (!this->extra_msg.empty()) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide;
128
129 size.height = std::max(size.height, panel_height);
130 break;
131 }
132 case WID_EM_FACE:
133 size = maxdim(size, GetScaledSpriteSize(SPR_GRADIENT));
134 break;
135 }
136 }
137
138 Point OnInitialPosition([[maybe_unused]] int16_t sm_width, [[maybe_unused]] int16_t sm_height, [[maybe_unused]] int window_number) override
139 {
140 /* Position (0, 0) given, center the window. */
141 if (this->position.x == 0 && this->position.y == 0) {
142 Point pt = {(_screen.width - sm_width) >> 1, (_screen.height - sm_height) >> 1};
143 return pt;
144 }
145
146 constexpr int distance_to_cursor = 200;
147
148 /* Position the error window just above the cursor. This makes the
149 * error window clearly visible, without being in the way of what
150 * the user is doing. */
151 Point pt;
152 pt.x = _cursor.pos.x - sm_width / 2;
153 pt.y = _cursor.pos.y - (distance_to_cursor + sm_height);
154
155 if (pt.y < GetMainViewTop()) {
156 /* Window didn't fit above cursor, so place it below. */
157 pt.y = _cursor.pos.y + distance_to_cursor;
158 }
159
160 return pt;
161 }
162
168 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
169 {
170 /* If company gets shut down, while displaying an error about it, remove the error message. */
171 if (this->company != CompanyID::Invalid() && !Company::IsValidID(this->company)) this->Close();
172 }
173
174 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
175 {
176 if (widget == WID_EM_CAPTION && this->company != CompanyID::Invalid()) return GetString(STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY, this->company);
177
178 return this->Window::GetWidgetString(widget, stringid);
179 }
180
181 void DrawWidget(const Rect &r, WidgetID widget) const override
182 {
183 switch (widget) {
184 case WID_EM_FACE: {
185 const Company *c = Company::Get(this->company);
187 break;
188 }
189
190 case WID_EM_MESSAGE:
191 if (this->detailed_msg.empty()) {
193 } else if (this->extra_msg.empty()) {
194 /* Extra space when message is shorter than company face window */
195 int extra = (r.Height() - this->height_summary - this->height_detailed - WidgetDimensions::scaled.vsep_wide) / 2;
196
197 /* Note: NewGRF supplied error message often do not start with a colour code, so default to white. */
198 DrawStringMultiLineWithClipping(r.WithHeight(this->height_summary + extra, false), this->summary_msg.GetDecodedString(), TC_WHITE, SA_CENTER);
199 DrawStringMultiLineWithClipping(r.WithHeight(this->height_detailed + extra, true), this->detailed_msg.GetDecodedString(), TC_WHITE, SA_CENTER);
200 } else {
201 /* Extra space when message is shorter than company face window */
202 int extra = (r.Height() - this->height_summary - this->height_detailed - this->height_extra - (WidgetDimensions::scaled.vsep_wide * 2)) / 3;
203
204 /* Note: NewGRF supplied error message often do not start with a colour code, so default to white. */
205 Rect top_section = r.WithHeight(this->height_summary + extra, false);
206 Rect bottom_section = r.WithHeight(this->height_extra + extra, true);
207 Rect middle_section = top_section.WithY(top_section.bottom, bottom_section.top);
210 DrawStringMultiLineWithClipping(bottom_section, this->extra_msg.GetDecodedString(), TC_WHITE, SA_CENTER);
211 }
212
213 break;
214
215 default:
216 break;
217 }
218 }
219
220 void OnPaint() override
221 {
222 /* Start the timeout if not already started and the message is not critical. This is handled during OnPaint so that any delay between
223 * creating the window and displaying it does not affect how long the message is visible. */
224 if (!this->is_critical && this->display_timeout.HasFired()) {
225 this->display_timeout.Reset();
226 }
227
228 this->Window::OnPaint();
229 }
230
231 void OnMouseLoop() override
232 {
233 /* Disallow closing the window too easily, if timeout is disabled */
234 if (_right_button_down && !this->is_critical) this->Close();
235 }
236
237 void Close([[maybe_unused]] int data = 0) override
238 {
241 this->Window::Close();
242 }
243
249 {
250 return this->is_critical;
251 }
252};
253
258{
260 _error_list.clear();
261}
262
265{
267 if (!_error_list.empty()) {
268 new ErrmsgWindow(_error_list.front());
269 _error_list.pop_front();
270 }
271}
272
279{
280 ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
281 if (_window_system_initialized && w != nullptr) {
282 if (w->IsCritical()) _error_list.push_front(*w);
284 w->Close();
285 }
286}
287
296void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
297{
298 EncodedString error = std::move(cc.GetEncodedMessage());
299 if (error.empty()) error = GetEncodedStringIfValid(cc.GetErrorMessage());
300
301 ShowErrorMessage(std::move(summary_msg), std::move(error), WL_INFO, x, y,
303}
304
314void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x, int y, EncodedString &&extra_msg, CompanyID company)
315{
316 if (wl != WL_INFO) {
317 /* Print message to console */
318
319 std::string message = summary_msg.GetDecodedString();
320 if (!detailed_msg.empty()) {
321 message += " ";
322 message += detailed_msg.GetDecodedString();
323 }
324 if (!extra_msg.empty()) {
325 message += " ";
326 message += extra_msg.GetDecodedString();
327 }
328
329 IConsolePrint(wl == WL_WARNING ? CC_WARNING : CC_ERROR, message);
330 }
331
332 bool is_critical = wl == WL_CRITICAL;
333
334 if (_game_mode == GM_BOOTSTRAP) return;
335 if (_settings_client.gui.errmsg_duration == 0 && !is_critical) return;
336
337 ErrorMessageData data(std::move(summary_msg), std::move(detailed_msg), is_critical, x, y, std::move(extra_msg), company);
338
339 ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
340 if (w != nullptr) {
341 if (w->IsCritical()) {
342 /* A critical error is currently shown. */
343 if (wl == WL_CRITICAL) {
344 /* Push another critical error in the queue of errors,
345 * but do not put other errors in the queue. */
346 _error_list.push_back(std::move(data));
347 }
348 return;
349 }
350 /* A non-critical error was shown. */
351 w->Close();
352 }
353 new ErrmsgWindow(data);
354}
355
356
362{
363 ErrmsgWindow *w = dynamic_cast<ErrmsgWindow *>(FindWindowById(WC_ERRMSG, 0));
364 if (w == nullptr) return false;
365 w->Close();
366 return true;
367}
368
375{
376 _error_list.splice(_error_list.end(), datas);
377}
378
385{
386 _error_list.push_back(data);
387}
Common return value for all commands.
EncodedString & GetEncodedMessage()
Get the last encoded error message.
StringID GetErrorMessage() const
Returns the error message of a command.
CompanyID GetErrorOwner() const
Get the originator owner for this error.
StringID GetExtraErrorMessage() const
Returns the extra error message of a command.
Container for an encoded string, created by GetEncodedString.
std::string GetDecodedString() const
Decode the encoded string.
Definition strings.cpp:207
The data of the error message.
Definition error.h:31
ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical=false, int x=0, int y=0, EncodedString &&extra_msg={}, CompanyID company=CompanyID::Invalid())
Display an error message in a window.
Definition error_gui.cpp:82
bool HasFace() const
Check whether error window shall display a company manager face.
Definition error.h:44
CompanyID company
Company belonging to the face being shown. CompanyID::Invalid() if no face present.
Definition error.h:38
Point position
Position of the error message window.
Definition error.h:37
EncodedString detailed_msg
Detailed error message showed in second line. Can be INVALID_STRING_ID.
Definition error.h:35
EncodedString extra_msg
Extra error message shown in third line. Can be INVALID_STRING_ID.
Definition error.h:36
EncodedString summary_msg
General error message showed in first line. Must be valid.
Definition error.h:34
bool is_critical
Whether the error message is critical.
Definition error.h:33
A timeout timer will fire once after the interval.
Definition timer.h:116
void Reset()
Reset the timer, so it will fire again after the timeout.
Definition timer.h:140
bool HasFired() const
Check whether the timeout occurred.
Definition timer.h:171
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:60
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
Definition of stuff that is very close to a company, like the company struct itself.
Functions related to companies.
void DrawCompanyManagerFace(const CompanyManagerFace &cmf, Colours colour, const Rect &r)
Draws the face of a company manager's face.
Functionality related to the company manager's face.
void IConsolePrint(TextColour colour_code, const std::string &string)
Handle the printing of text entered into the console or redirected there by any other means.
Definition console.cpp:90
Console functions used outside of the console code.
static const TextColour CC_WARNING
Colour for warning lines.
static const TextColour CC_ERROR
Colour for error lines.
Functions related to errors.
std::list< ErrorMessageData > ErrorList
Define a queue with errors.
Definition error.h:48
WarningLevel
Message severity/type.
Definition error.h:23
@ WL_WARNING
Other information.
Definition error.h:25
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
@ WL_INFO
Used for DoCommand-like (and some non-fatal AI GUI) errors/information.
Definition error.h:24
static ErrorList _error_list
The actual queue with errors.
Definition error_gui.cpp:94
void UnshowCriticalError()
Unshow the critical error.
void ClearErrorMessages()
Clear all errors from the queue.
void ScheduleErrorMessage(ErrorList &datas)
Schedule a list of errors.
bool _window_system_initialized
Whether the window system is initialized or not.
Definition error_gui.cpp:96
bool HideActiveErrorMessage()
Close active error message window.
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
void ShowFirstError()
Show the first error of the queue.
Types related to the error widgets.
@ WID_EM_CAPTION
Caption of the window.
@ WID_EM_MESSAGE
Error message.
@ WID_EM_FACE
Error title.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition gfx.cpp:715
bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw a multiline string, possibly over multiple lines, if the region is within the current display cl...
Definition gfx.cpp:870
bool _right_button_down
Is right mouse button pressed?
Definition gfx.cpp:43
Functions related to the gfx engine.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
Functions related to OTTD's landscape.
Header of Action 04 "universal holder" structure and functions.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
This file contains all sprite-related enums and defines.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
Functions related to OTTD's strings.
static EncodedString GetEncodedStringIfValid(StringID str)
Encode a string with no parameters into an encoded string, if the string id is valid.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
GUISettings gui
settings related to the GUI
Colours colour
Company colour.
CompanyManagerFace face
Face description of the president.
T y
Y coordinate.
T x
X coordinate.
Point pos
logical mouse position
Definition gfx_type.h:125
Dimensions (a width and height) of a rectangle in 2D.
Window class for displaying an error message window.
Definition error_gui.cpp:99
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
bool IsCritical()
Check whether the currently shown error message was critical or not.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void Close(int data=0) override
Hide the window and all its child windows, and mark them for a later deletion.
uint height_extra
Height of the extra_msg string in pixels in the WID_EM_MESSAGE widget.
void OnMouseLoop() override
Called for every mouse loop run, which is at least once per (game) tick.
Point OnInitialPosition(int16_t sm_width, int16_t sm_height, int window_number) override
Compute the initial position of the window.
void OnPaint() override
The window must be repainted.
uint height_detailed
Height of the detailed_msg string in pixels in the WID_EM_MESSAGE widget.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
uint height_summary
Height of the summary_msg string in pixels in the WID_EM_MESSAGE widget.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
uint8_t errmsg_duration
duration of error message
static Titem * Get(auto index)
Returns Titem with given index.
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
int Height() const
Get height of Rect.
Rect WithY(int new_top, int new_bottom) const
Create a new Rect, replacing the top and bottom coordiates.
High level window description.
Definition window_gui.h:168
Data structure for an opened window.
Definition window_gui.h:274
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1104
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:506
ResizeInfo resize
Resize information.
Definition window_gui.h:315
virtual void OnPaint()
The window must be repainted.
Definition window_gui.h:596
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1822
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
Definition of Interval and OneShot timers.
Definition of the Window system.
void SetRedErrorSquare(TileIndex tile)
Set a tile to display a red error square.
Functions related to (drawing on) viewports.
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:52
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:60
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
int GetMainViewTop()
Return the top of the main view available for general use.
Definition window.cpp:2138
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1153
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ WDP_MANUAL
Manually align the window (so no automatic location finding)
Definition window_gui.h:143
int WidgetID
Widget ID.
Definition window_type.h:20
@ WC_ERRMSG
Error message; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:50
Functions related to zooming.