OpenTTD Source 20241224-master-gf74b0cf984
widget.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 <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "company_func.h"
13#include "window_gui.h"
14#include "viewport_func.h"
15#include "zoom_func.h"
16#include "strings_func.h"
17#include "transparency.h"
19#include "settings_type.h"
20#include "querystring_gui.h"
21
22#include "table/sprites.h"
23#include "table/strings.h"
25
26#include "safeguards.h"
27
29
35static inline RectPadding ScaleGUITrad(const RectPadding &r)
36{
37 return {(uint8_t)ScaleGUITrad(r.left), (uint8_t)ScaleGUITrad(r.top), (uint8_t)ScaleGUITrad(r.right), (uint8_t)ScaleGUITrad(r.bottom)};
38}
39
45static inline Dimension ScaleGUITrad(const Dimension &dim)
46{
47 return {(uint)ScaleGUITrad(dim.width), (uint)ScaleGUITrad(dim.height)};
48}
49
55{
56 Point offset;
57 Dimension d = GetSpriteSize(sprid, &offset, ZOOM_LVL_NORMAL);
58 d.width -= offset.x;
59 d.height -= offset.y;
60 return ScaleGUITrad(d);
61}
62
67{
74 } else {
76 }
91
97}
98
106static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align)
107{
108 Point p;
109 /* In case we have a RTL language we swap the alignment. */
110 if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
111 switch (align & SA_HOR_MASK) {
112 case SA_LEFT: p.x = r.left; break;
113 case SA_HOR_CENTER: p.x = CenterBounds(r.left, r.right, d.width); break;
114 case SA_RIGHT: p.x = r.right + 1 - d.width; break;
115 default: NOT_REACHED();
116 }
117 switch (align & SA_VERT_MASK) {
118 case SA_TOP: p.y = r.top; break;
119 case SA_VERT_CENTER: p.y = CenterBounds(r.top, r.bottom, d.height); break;
120 case SA_BOTTOM: p.y = r.bottom + 1 - d.height; break;
121 default: NOT_REACHED();
122 }
123 return p;
124}
125
135static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom, bool horizontal)
136{
137 /* Base for reversion */
138 int rev_base = top + bottom;
139 int button_size;
140 if (horizontal) {
141 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
142 } else {
143 button_size = NWidgetScrollbar::GetVerticalDimension().height;
144 }
145 top += button_size; // top points to just below the up-button
146 bottom -= button_size; // bottom points to top of the down-button
147
148 int count = sb->GetCount();
149 int cap = sb->GetCapacity();
150
151 if (count > cap) {
152 int height = (bottom - top);
153 int slider_height = std::max(button_size, cap * height / count);
154 height -= slider_height;
155
156 top += height * sb->GetPosition() / (count - cap);
157 bottom = top + slider_height;
158 }
159
160 Point pt;
161 if (horizontal && _current_text_dir == TD_RTL) {
162 pt.x = rev_base - bottom;
163 pt.y = rev_base - top;
164 } else {
165 pt.x = top;
166 pt.y = bottom;
167 }
168 return pt;
169}
170
180static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, int y, int mi, int ma)
181{
182 int pos;
183 int button_size;
184 bool rtl = false;
185 bool changed = false;
186
187 if (sb->type == NWID_HSCROLLBAR) {
188 pos = x;
189 rtl = _current_text_dir == TD_RTL;
190 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
191 } else {
192 pos = y;
193 button_size = NWidgetScrollbar::GetVerticalDimension().height;
194 }
195 if (pos < mi + button_size) {
196 /* Pressing the upper button? */
198 if (_scroller_click_timeout <= 1) {
199 _scroller_click_timeout = 3;
200 changed = sb->UpdatePosition(rtl ? 1 : -1);
201 }
203 } else if (pos >= ma - button_size) {
204 /* Pressing the lower button? */
206
207 if (_scroller_click_timeout <= 1) {
208 _scroller_click_timeout = 3;
209 changed = sb->UpdatePosition(rtl ? -1 : 1);
210 }
212 } else {
213 Point pt = HandleScrollbarHittest(sb, mi, ma, sb->type == NWID_HSCROLLBAR);
214
215 if (pos < pt.x) {
216 changed = sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::SS_BIG);
217 } else if (pos > pt.y) {
218 changed = sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::SS_BIG);
219 } else {
220 _scrollbar_start_pos = pt.x - mi - button_size;
221 _scrollbar_size = ma - mi - button_size * 2 - (pt.y - pt.x);
223 _cursorpos_drag_start = _cursor.pos;
224 }
225 }
226
227 if (changed) {
228 /* Position changed so refresh the window */
229 w->SetDirty();
230 } else {
231 /* No change so only refresh this scrollbar */
232 sb->SetDirty(w);
233 }
234}
235
244void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
245{
246 int mi, ma;
247
248 if (nw->type == NWID_HSCROLLBAR) {
249 mi = nw->pos_x;
250 ma = nw->pos_x + nw->current_x;
251 } else {
252 mi = nw->pos_y;
253 ma = nw->pos_y + nw->current_y;
254 }
255 NWidgetScrollbar *scrollbar = dynamic_cast<NWidgetScrollbar*>(nw);
256 assert(scrollbar != nullptr);
257 ScrollbarClickPositioning(w, scrollbar, x, y, mi, ma);
258}
259
268WidgetID GetWidgetFromPos(const Window *w, int x, int y)
269{
270 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
271 return (nw != nullptr) ? nw->index : -1;
272}
273
283void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
284{
285 if (flags & FR_TRANSPARENT) {
286 GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
287 } else {
288 assert(colour < COLOUR_END);
289
290 const uint dark = GetColourGradient(colour, SHADE_DARK);
291 const uint medium_dark = GetColourGradient(colour, SHADE_LIGHT);
292 const uint medium_light = GetColourGradient(colour, SHADE_LIGHTER);
293 const uint light = GetColourGradient(colour, SHADE_LIGHTEST);
294 uint interior;
295
296 Rect outer = {left, top, right, bottom}; // Outside rectangle
297 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
298
299 if (flags & FR_LOWERED) {
300 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark); // Left
301 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark); // Top
302 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light); // Right
303 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light); // Bottom
304 interior = (flags & FR_DARKENED ? medium_dark : medium_light);
305 } else {
306 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light); // Left
307 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light); // Top
308 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark); // Right
309 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark); // Bottom
310 interior = medium_dark;
311 }
312 if (!(flags & FR_BORDERONLY)) {
313 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior); // Inner
314 }
315 }
316}
317
318void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, StringAlignment align)
319{
320 Point offset;
321 Dimension d = GetSpriteSize(img, &offset);
322 d.width -= offset.x;
323 d.height -= offset.y;
324
325 Point p = GetAlignedPosition(r, d, align);
326 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
327}
328
338static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
339{
340 assert(img != 0);
341 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
342
343 if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2.
344 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
345}
346
357static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, TextColour colour, StringID str, StringAlignment align, FontSize fs)
358{
359 if (str == STR_NULL) return;
360 if ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++;
361 Dimension d = GetStringBoundingBox(str, fs);
362 Point p = GetAlignedPosition(r, d, align);
363 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
364}
365
374static inline void DrawText(const Rect &r, TextColour colour, StringID str, StringAlignment align, FontSize fs)
375{
376 Dimension d = GetStringBoundingBox(str, fs);
377 Point p = GetAlignedPosition(r, d, align);
378 if (str != STR_NULL) DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
379}
380
390static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
391{
392 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED);
393 if (str != STR_NULL) DrawString(r.Shrink(WidgetDimensions::scaled.inset), str, text_colour, align, false, fs);
394}
395
405static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint16_t data, uint resize_x, uint resize_y)
406{
407 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
408
409 int num_columns = GB(data, MAT_COL_START, MAT_COL_BITS); // Lower 8 bits of the widget data: Number of columns in the matrix.
410 int column_width; // Width of a single column in the matrix.
411 if (num_columns == 0) {
412 column_width = resize_x;
413 num_columns = r.Width() / column_width;
414 } else {
415 column_width = r.Width() / num_columns;
416 }
417
418 int num_rows = GB(data, MAT_ROW_START, MAT_ROW_BITS); // Upper 8 bits of the widget data: Number of rows in the matrix.
419 int row_height; // Height of a single row in the matrix.
420 if (num_rows == 0) {
421 row_height = resize_y;
422 num_rows = r.Height() / row_height;
423 } else {
424 row_height = r.Height() / num_rows;
425 }
426
427 int col = GetColourGradient(colour, SHADE_LIGHTER);
428
429 int x = r.left;
430 for (int ctr = num_columns; ctr > 1; ctr--) {
431 x += column_width;
432 GfxFillRect(x, r.top + WidgetDimensions::scaled.bevel.top, x + WidgetDimensions::scaled.bevel.left - 1, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
433 }
434
435 x = r.top;
436 for (int ctr = num_rows; ctr > 1; ctr--) {
437 x += row_height;
439 }
440
441 col = GetColourGradient(colour, SHADE_NORMAL);
442
443 x = r.left - 1;
444 for (int ctr = num_columns; ctr > 1; ctr--) {
445 x += column_width;
446 GfxFillRect(x - WidgetDimensions::scaled.bevel.right + 1, r.top + WidgetDimensions::scaled.bevel.top, x, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
447 }
448
449 x = r.top - 1;
450 for (int ctr = num_rows; ctr > 1; ctr--) {
451 x += row_height;
452 GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x - WidgetDimensions::scaled.bevel.bottom + 1, r.right - WidgetDimensions::scaled.bevel.right, x, col);
453 }
454}
455
465static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
466{
467 int height = NWidgetScrollbar::GetVerticalDimension().height;
468
469 /* draw up/down buttons */
470 DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
471 DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
472
473 int c1 = GetColourGradient(colour, SHADE_DARK);
474 int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
475
476 /* draw "shaded" background */
477 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
478 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c1, FILLRECT_CHECKER);
479
480 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
481 int left = r.left + r.Width() * 3 / 11; /* left track is positioned 3/11ths from the left */
482 int right = r.left + r.Width() * 8 / 11; /* right track is positioned 8/11ths from the left */
483 const uint8_t bl = WidgetDimensions::scaled.bevel.left;
484 const uint8_t br = WidgetDimensions::scaled.bevel.right;
485
486 /* draw shaded lines */
487 GfxFillRect(left - bl, r.top + height, left - 1, r.bottom - height, c1);
488 GfxFillRect(left, r.top + height, left + br - 1, r.bottom - height, c2);
489 GfxFillRect(right - bl, r.top + height, right - 1, r.bottom - height, c1);
490 GfxFillRect(right, r.top + height, right + br - 1, r.bottom - height, c2);
491
492 Point pt = HandleScrollbarHittest(scrollbar, r.top, r.bottom, false);
493 DrawFrameRect(r.left, pt.x, r.right, pt.y, colour, bar_dragged ? FR_LOWERED : FR_NONE);
494}
495
505static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
506{
507 int width = NWidgetScrollbar::GetHorizontalDimension().width;
508
509 DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
510 DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
511
512 int c1 = GetColourGradient(colour, SHADE_DARK);
513 int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
514
515 /* draw "shaded" background */
516 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
517 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c1, FILLRECT_CHECKER);
518
519 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
520 int top = r.top + r.Height() * 3 / 11; /* top track is positioned 3/11ths from the top */
521 int bottom = r.top + r.Height() * 8 / 11; /* bottom track is positioned 8/11ths from the top */
522 const uint8_t bt = WidgetDimensions::scaled.bevel.top;
523 const uint8_t bb = WidgetDimensions::scaled.bevel.bottom;
524
525 /* draw shaded lines */
526 GfxFillRect(r.left + width, top - bt, r.right - width, top - 1, c1);
527 GfxFillRect(r.left + width, top, r.right - width, top + bb - 1, c2);
528 GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom - 1, c1);
529 GfxFillRect(r.left + width, bottom, r.right - width, bottom + bb - 1, c2);
530
531 /* draw actual scrollbar */
532 Point pt = HandleScrollbarHittest(scrollbar, r.left, r.right, true);
533 DrawFrameRect(pt.x, r.top, pt.y, r.bottom, colour, bar_dragged ? FR_LOWERED : FR_NONE);
534}
535
545static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
546{
547 int x2 = r.left; // by default the left side is the left side of the widget
548
549 if (str != STR_NULL) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs);
550
551 int c1 = GetColourGradient(colour, SHADE_DARK);
552 int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
553
554 /* If the frame has text, adjust the top bar to fit half-way through */
555 Rect inner = r.Shrink(ScaleGUITrad(1));
556 if (str != STR_NULL) inner.top = r.top + GetCharacterHeight(FS_NORMAL) / 2;
557
558 Rect outer = inner.Expand(WidgetDimensions::scaled.bevel);
559 Rect inside = inner.Shrink(WidgetDimensions::scaled.bevel);
560
561 if (_current_text_dir == TD_LTR) {
562 /* Line from upper left corner to start of text */
563 GfxFillRect(outer.left, outer.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
564 GfxFillRect(inner.left, inner.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
565
566 /* Line from end of text to upper right corner */
567 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
568 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
569 } else {
570 /* Line from upper left corner to start of text */
571 GfxFillRect(outer.left, outer.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
572 GfxFillRect(inner.left, inner.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
573
574 /* Line from end of text to upper right corner */
575 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
576 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
577 }
578
579 /* Line from upper left corner to bottom left corner */
580 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
581 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
582
583 /* Line from upper right corner to bottom right corner */
584 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
585 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
586
587 /* Line from bottom left corner to bottom right corner */
588 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
589 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
590}
591
598static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
599{
600 DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER);
601}
602
609static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
610{
611 DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER);
612}
613
620static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
621{
622 DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER);
623}
624
631static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
632{
633 DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER);
634}
635
644static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
645{
646 if (bevel) {
647 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
648 } else if (clicked) {
650 }
651 DrawSpriteIgnorePadding(at_left ? SPR_WINDOW_RESIZE_LEFT : SPR_WINDOW_RESIZE_RIGHT, PAL_NONE, r.Shrink(ScaleGUITrad(2)), at_left ? (SA_LEFT | SA_BOTTOM | SA_FORCE) : (SA_RIGHT | SA_BOTTOM | SA_FORCE));
652}
653
659static inline void DrawCloseBox(const Rect &r, Colours colour)
660{
661 if (colour != COLOUR_WHITE) DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_NONE);
662 Point offset;
663 Dimension d = GetSpriteSize(SPR_CLOSEBOX, &offset);
664 d.width -= offset.x;
665 d.height -= offset.y;
666 int s = ScaleSpriteTrad(1); /* Offset to account for shadow of SPR_CLOSEBOX */
667 DrawSprite(SPR_CLOSEBOX, (colour != COLOUR_WHITE ? TC_BLACK : TC_SILVER) | (1U << PALETTE_TEXT_RECOLOUR), CenterBounds(r.left, r.right, d.width - s) - offset.x, CenterBounds(r.top, r.bottom, d.height - s) - offset.y);
668}
669
680void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
681{
682 bool company_owned = owner < MAX_COMPANIES;
683
684 DrawFrameRect(r, colour, FR_BORDERONLY);
686 DrawFrameRect(ir, colour, company_owned ? FR_LOWERED | FR_DARKENED | FR_BORDERONLY : FR_LOWERED | FR_DARKENED);
687
688 if (company_owned) {
690 }
691
692 if (str != STR_NULL) {
694 Point p = GetAlignedPosition(r, d, align);
695 DrawString(r.left + WidgetDimensions::scaled.captiontext.left, r.right - WidgetDimensions::scaled.captiontext.left, p.y, str, text_colour, align, false, fs);
696 }
697}
698
710static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str, StringAlignment align)
711{
712 int dd_width = NWidgetLeaf::dropdown_dimension.width;
713
714 if (_current_text_dir == TD_LTR) {
715 DrawFrameRect(r.left, r.top, r.right - dd_width, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
716 DrawImageButtons(r.WithWidth(dd_width, true), WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
717 if (str != STR_NULL) {
718 DrawString(r.left + WidgetDimensions::scaled.dropdowntext.left, r.right - dd_width - WidgetDimensions::scaled.dropdowntext.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), str, TC_BLACK, align);
719 }
720 } else {
721 DrawFrameRect(r.left + dd_width, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
722 DrawImageButtons(r.WithWidth(dd_width, false), WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
723 if (str != STR_NULL) {
724 DrawString(r.left + dd_width + WidgetDimensions::scaled.dropdowntext.left, r.right - WidgetDimensions::scaled.dropdowntext.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), str, TC_BLACK, align);
725 }
726 }
727}
728
733{
734 this->nested_root->Draw(this);
735
736 if (this->flags & WF_WHITE_BORDER) {
737 DrawFrameRect(0, 0, this->width - 1, this->height - 1, COLOUR_WHITE, FR_BORDERONLY);
738 }
739
740 if (this->flags & WF_HIGHLIGHTED) {
741 extern bool _window_highlight_colour;
742 for (const auto &pair : this->widget_lookup) {
743 const NWidgetBase *widget = pair.second;
744 if (!widget->IsHighlighted()) continue;
745
746 Rect outer = widget->GetCurrentRect();
748
749 int colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE];
750
751 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
752 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
753 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
754 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
755 }
756 }
757}
758
765{
766 if (state == SBS_OFF) return;
767
768 assert(!this->widget_lookup.empty());
769 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
770
771 /* Sort button uses the same sprites as vertical scrollbar */
772 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
773
774 DrawSpriteIgnorePadding(state == SBS_DOWN ? SPR_ARROW_DOWN : SPR_ARROW_UP, PAL_NONE, r.WithWidth(dim.width, _current_text_dir == TD_LTR), SA_CENTER);
775}
776
782{
783 return NWidgetScrollbar::GetVerticalDimension().width + 1;
784}
785
786bool _draw_widget_outlines;
787
788static void DrawOutline(const Window *, const NWidgetBase *wid)
789{
790 if (!_draw_widget_outlines || wid->current_x == 0 || wid->current_y == 0) return;
791
792 DrawRectOutline(wid->GetCurrentRect(), PC_WHITE, 1, 4);
793}
794
856
857/* ~NWidgetContainer() takes care of #next and #prev data members. */
858
904void NWidgetBase::SetDirty(const Window *w) const
905{
906 int abs_left = w->left + this->pos_x;
907 int abs_top = w->top + this->pos_y;
908 AddDirtyBlock(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
909}
910
925{
926 return (this->type == tp) ? this : nullptr;
927}
928
929void NWidgetBase::ApplyAspectRatio()
930{
931 if (this->aspect_ratio == 0) return;
932 if (this->smallest_x == 0 || this->smallest_y == 0) return;
933
934 uint x = this->smallest_x;
935 uint y = this->smallest_y;
936 if (HasFlag(this->aspect_flags, AspectFlags::ResizeX)) x = std::max(this->smallest_x, static_cast<uint>(this->smallest_y * std::abs(this->aspect_ratio)));
937 if (HasFlag(this->aspect_flags, AspectFlags::ResizeY)) y = std::max(this->smallest_y, static_cast<uint>(this->smallest_x / std::abs(this->aspect_ratio)));
938
939 this->smallest_x = x;
940 this->smallest_y = y;
941}
942
943void NWidgetBase::AdjustPaddingForZoom()
944{
945 this->padding = ScaleGUITrad(this->uz_padding);
946}
947
955{
956 this->fill_x = fill_x;
957 this->fill_y = fill_y;
958}
959
965void NWidgetResizeBase::SetAspect(float ratio, AspectFlags flags)
966{
967 this->aspect_ratio = ratio;
968 this->aspect_flags = flags;
969}
970
977void NWidgetResizeBase::SetAspect(int x_ratio, int y_ratio, AspectFlags flags)
978{
979 this->SetAspect(static_cast<float>(x_ratio) / static_cast<float>(y_ratio), flags);
980}
981
982void NWidgetResizeBase::AdjustPaddingForZoom()
983{
984 if (!this->absolute) {
985 this->min_x = ScaleGUITrad(this->uz_min_x);
987 }
988 NWidgetBase::AdjustPaddingForZoom();
989}
990
996void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y)
997{
998 this->uz_min_x = std::max(this->uz_min_x, min_x);
999 this->uz_min_y = std::max(this->uz_min_y, min_y);
1000 this->min_x = ScaleGUITrad(this->uz_min_x);
1001 this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1002}
1003
1010{
1011 this->absolute = true;
1012 this->min_x = std::max(this->min_x, min_x);
1013 this->min_y = std::max(this->min_y, min_y);
1014}
1015
1022void NWidgetResizeBase::SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
1023{
1024 this->uz_text_lines = min_lines;
1025 this->uz_text_spacing = spacing;
1026 this->uz_text_size = size;
1027 this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1028}
1029
1035void NWidgetResizeBase::SetFill(uint fill_x, uint fill_y)
1036{
1037 this->fill_x = fill_x;
1038 this->fill_y = fill_y;
1039}
1040
1046void NWidgetResizeBase::SetResize(uint resize_x, uint resize_y)
1047{
1048 this->resize_x = resize_x;
1049 this->resize_y = resize_y;
1050}
1051
1059bool NWidgetResizeBase::UpdateMultilineWidgetSize(const std::string &str, int max_lines)
1060{
1061 int y = GetStringHeight(str, this->current_x);
1062 if (y > max_lines * GetCharacterHeight(FS_NORMAL)) {
1063 /* Text at the current width is too tall, so try to guess a better width. */
1065 d.height *= max_lines;
1066 d.width /= 2;
1067 return this->UpdateSize(d.width, d.height);
1068 }
1069 return this->UpdateVerticalSize(y);
1070}
1071
1079bool NWidgetResizeBase::UpdateSize(uint min_x, uint min_y)
1080{
1081 if (min_x == this->min_x && min_y == this->min_y) return false;
1082 this->min_x = min_x;
1083 this->min_y = min_y;
1084 return true;
1085}
1086
1094{
1095 if (min_y == this->min_y) return false;
1096 this->min_y = min_y;
1097 return true;
1098}
1099
1100void NWidgetResizeBase::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1101{
1102 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1103}
1104
1115NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, WidgetID index, uint fill_x, uint fill_y, uint32_t widget_data, StringID tool_tip) : NWidgetResizeBase(tp, fill_x, fill_y), index(index)
1116{
1117 this->colour = colour;
1118 this->widget_data = widget_data;
1119 this->tool_tip = tool_tip;
1120 this->scrollbar_index = -1;
1121 this->text_colour = tp == WWT_CAPTION ? TC_WHITE : TC_BLACK;
1122 this->text_size = FS_NORMAL;
1123 this->align = SA_CENTER;
1124}
1125
1131void NWidgetCore::SetDataTip(uint32_t widget_data, StringID tool_tip)
1132{
1133 this->widget_data = widget_data;
1134 this->tool_tip = tool_tip;
1135}
1136
1143{
1144 this->text_colour = colour;
1145 this->text_size = size;
1146}
1147
1153{
1154 this->tool_tip = tool_tip;
1155}
1156
1162{
1163 this->align = align;
1164}
1165
1167{
1168 if (this->index >= 0) widget_lookup[this->index] = this;
1169}
1170
1172{
1173 return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : nullptr;
1174}
1175
1177{
1178 if (this->type == tp) return this;
1179 for (const auto &child_wid : this->children) {
1180 NWidgetBase *nwid = child_wid->GetWidgetOfType(tp);
1181 if (nwid != nullptr) return nwid;
1182 }
1183 return nullptr;
1184}
1185
1186void NWidgetContainer::AdjustPaddingForZoom()
1187{
1188 for (const auto &child_wid : this->children) {
1189 child_wid->AdjustPaddingForZoom();
1190 }
1191 NWidgetBase::AdjustPaddingForZoom();
1192}
1193
1198void NWidgetContainer::Add(std::unique_ptr<NWidgetBase> &&wid)
1199{
1200 assert(wid != nullptr);
1201 wid->parent = this;
1202 this->children.push_back(std::move(wid));
1203}
1204
1206{
1207 for (const auto &child_wid : this->children) {
1208 child_wid->FillWidgetLookup(widget_lookup);
1209 }
1210}
1211
1213{
1214 for (const auto &child_wid : this->children) {
1215 child_wid->Draw(w);
1216 }
1217
1218 DrawOutline(w, this);
1219}
1220
1222{
1223 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1224
1225 for (const auto &child_wid : this->children) {
1226 NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
1227 if (nwid != nullptr) return nwid;
1228 }
1229 return nullptr;
1230}
1231
1238
1240{
1241 /* Zero size plane selected */
1242 if (this->shown_plane >= SZSP_BEGIN) {
1243 Dimension size = {0, 0};
1244 Dimension padding = {0, 0};
1245 Dimension fill = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1246 Dimension resize = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1247 /* Here we're primarily interested in the value of resize */
1248 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
1249
1250 this->smallest_x = size.width;
1251 this->smallest_y = size.height;
1252 this->fill_x = fill.width;
1253 this->fill_y = fill.height;
1254 this->resize_x = resize.width;
1255 this->resize_y = resize.height;
1256 this->ApplyAspectRatio();
1257 return;
1258 }
1259
1260 /* First sweep, recurse down and compute minimal size and filling. */
1261 this->smallest_x = 0;
1262 this->smallest_y = 0;
1263 this->fill_x = this->IsEmpty() ? 0 : 1;
1264 this->fill_y = this->IsEmpty() ? 0 : 1;
1265 this->resize_x = this->IsEmpty() ? 0 : 1;
1266 this->resize_y = this->IsEmpty() ? 0 : 1;
1267 for (const auto &child_wid : this->children) {
1268 child_wid->SetupSmallestSize(w);
1269
1270 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1271 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1272 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1273 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1274 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1275 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1276 this->ApplyAspectRatio();
1277 }
1278}
1279
1280void NWidgetStacked::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1281{
1282 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1283 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1284
1285 if (this->shown_plane >= SZSP_BEGIN) return;
1286
1287 for (const auto &child_wid : this->children) {
1288 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1289 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1290 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1291
1292 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1293 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1294 uint child_pos_y = child_wid->padding.top;
1295
1296 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1297 }
1298}
1299
1301{
1302 /* We need to update widget_lookup later. */
1303 this->widget_lookup = &widget_lookup;
1304
1305 if (this->index >= 0) widget_lookup[this->index] = this;
1307 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1308 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(widget_lookup);
1309}
1310
1312{
1313 if (this->shown_plane >= SZSP_BEGIN) return;
1314
1315 assert(static_cast<size_t>(this->shown_plane) < this->children.size());
1316 this->children[shown_plane]->Draw(w);
1317 DrawOutline(w, this);
1318}
1319
1321{
1322 if (this->shown_plane >= SZSP_BEGIN) return nullptr;
1323
1324 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1325
1326 if (static_cast<size_t>(this->shown_plane) >= this->children.size()) return nullptr;
1327 return this->children[shown_plane]->GetWidgetFromPos(x, y);
1328}
1329
1336{
1337 if (this->shown_plane == plane) return false;
1338 this->shown_plane = plane;
1339 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1340 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(*this->widget_lookup);
1341 return true;
1342}
1343
1345public:
1347
1348 void SetupSmallestSize(Window *w) override;
1349 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override;
1350
1351 void Draw(const Window *w) override;
1352
1354};
1355
1356NWidgetLayer::NWidgetLayer(WidgetID index) : NWidgetContainer(NWID_LAYER), index(index) {}
1357
1359{
1360 /* First sweep, recurse down and compute minimal size and filling. */
1361 this->smallest_x = 0;
1362 this->smallest_y = 0;
1363 this->fill_x = this->IsEmpty() ? 0 : 1;
1364 this->fill_y = this->IsEmpty() ? 0 : 1;
1365 this->resize_x = this->IsEmpty() ? 0 : 1;
1366 this->resize_y = this->IsEmpty() ? 0 : 1;
1367 for (const auto &child_wid : this->children) {
1368 child_wid->SetupSmallestSize(w);
1369
1370 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1371 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1372 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1373 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1374 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1375 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1376 this->ApplyAspectRatio();
1377 }
1378}
1379
1380void NWidgetLayer::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1381{
1382 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1383 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1384
1385 for (const auto &child_wid : this->children) {
1386 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1387 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1388 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1389
1390 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1391 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1392 uint child_pos_y = child_wid->padding.top;
1393
1394 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1395 }
1396}
1397
1399{
1400 /* Draw in reverse order, as layers are arranged top-down. */
1401 for (auto it = std::rbegin(this->children); it != std::rend(this->children); ++it) {
1402 (*it)->Draw(w);
1403 }
1404
1405 DrawOutline(w, this);
1406}
1407
1408NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags) : NWidgetContainer(tp)
1409{
1410 this->flags = flags;
1411}
1412
1413void NWidgetPIPContainer::AdjustPaddingForZoom()
1414{
1415 this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1416 this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1417 this->pip_post = ScaleGUITrad(this->uz_pip_post);
1418 NWidgetContainer::AdjustPaddingForZoom();
1419}
1420
1430void NWidgetPIPContainer::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
1431{
1432 this->uz_pip_pre = pip_pre;
1433 this->uz_pip_inter = pip_inter;
1434 this->uz_pip_post = pip_post;
1435
1436 this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1437 this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1438 this->pip_post = ScaleGUITrad(this->uz_pip_post);
1439}
1440
1450void NWidgetPIPContainer::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
1451{
1452 this->pip_ratio_pre = pip_ratio_pre;
1453 this->pip_ratio_inter = pip_ratio_inter;
1454 this->pip_ratio_post = pip_ratio_post;
1455}
1456
1461
1463{
1464 this->smallest_x = 0; // Sum of minimal size of all children.
1465 this->smallest_y = 0; // Biggest child.
1466 this->fill_x = 0; // smallest non-zero child widget fill step.
1467 this->fill_y = 1; // smallest common child fill step.
1468 this->resize_x = 0; // smallest non-zero child widget resize step.
1469 this->resize_y = 1; // smallest common child resize step.
1470 this->gaps = 0;
1471
1472 /* 1a. Forward call, collect longest/widest child length. */
1473 uint longest = 0; // Longest child found.
1474 uint max_vert_fill = 0; // Biggest vertical fill step.
1475 for (const auto &child_wid : this->children) {
1476 child_wid->SetupSmallestSize(w);
1477 longest = std::max(longest, child_wid->smallest_x);
1478 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(ST_SMALLEST));
1479 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1480 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->gaps++;
1481 }
1482 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1483 /* 1b. Make the container higher if needed to accommodate all children nicely. */
1484 [[maybe_unused]] uint max_smallest = this->smallest_y + 3 * max_vert_fill; // Upper limit to computing smallest height.
1485 uint cur_height = this->smallest_y;
1486 for (;;) {
1487 for (const auto &child_wid : this->children) {
1488 uint step_size = child_wid->GetVerticalStepSize(ST_SMALLEST);
1489 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1490 if (step_size > 1 && child_height < cur_height) { // Small step sizes or already fitting children are not interesting.
1491 uint remainder = (cur_height - child_height) % step_size;
1492 if (remainder > 0) { // Child did not fit entirely, widen the container.
1493 cur_height += step_size - remainder;
1494 assert(cur_height < max_smallest); // Safeguard against infinite height expansion.
1495 /* Remaining children will adapt to the new cur_height, thus speeding up the computation. */
1496 }
1497 }
1498 }
1499 if (this->smallest_y == cur_height) break;
1500 this->smallest_y = cur_height; // Smallest height got changed, try again.
1501 }
1502 /* 2. For containers that must maintain equal width, extend child minimal size. */
1503 for (const auto &child_wid : this->children) {
1504 child_wid->smallest_y = this->smallest_y - child_wid->padding.Vertical();
1505 child_wid->ApplyAspectRatio();
1506 longest = std::max(longest, child_wid->smallest_x);
1507 }
1508 if (this->flags & NC_EQUALSIZE) {
1509 for (const auto &child_wid : this->children) {
1510 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1511 }
1512 }
1513 /* 3. Compute smallest, fill, and resize values of the container. */
1514 for (const auto &child_wid : this->children) {
1515 this->smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1516 if (child_wid->fill_x > 0) {
1517 if (this->fill_x == 0 || this->fill_x > child_wid->fill_x) this->fill_x = child_wid->fill_x;
1518 }
1519 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1520
1521 if (child_wid->resize_x > 0) {
1522 if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x;
1523 }
1524 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1525 }
1526 if (this->fill_x == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_x = 1;
1527 /* 4. Increase by required PIP space. */
1528 this->smallest_x += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1529}
1530
1531void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1532{
1533 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1534
1535 /* Compute additional width given to us. */
1536 uint additional_length = given_width - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1537 for (const auto &child_wid : this->children) {
1538 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1539 }
1540
1541 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1542
1543 /* In principle, the additional horizontal space is distributed evenly over the available resizable children. Due to step sizes, this may not always be feasible.
1544 * To make resizing work as good as possible, first children with biggest step sizes are done. These may get less due to rounding down.
1545 * This additional space is then given to children with smaller step sizes. This will give a good result when resize steps of each child is a multiple
1546 * of the child with the smallest non-zero stepsize.
1547 *
1548 * Since child sizes are computed out of order, positions cannot be calculated until all sizes are known. That means it is not possible to compute the child
1549 * size and position, and directly call child->AssignSizePosition() with the computed values.
1550 * Instead, computed child widths and heights are stored in child->current_x and child->current_y values. That is allowed, since this method overwrites those values
1551 * then we call the child.
1552 */
1553
1554 /* First loop: Find biggest stepsize, find number of children that want a piece of the pie, handle vertical size for all children,
1555 * handle horizontal size for non-resizing children.
1556 */
1557 int num_changing_childs = 0; // Number of children that can change size.
1558 uint biggest_stepsize = 0;
1559 for (const auto &child_wid : this->children) {
1560 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1561 if (hor_step > 0) {
1562 if (!(flags & NC_BIGFIRST)) num_changing_childs++;
1563 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1564 } else {
1565 child_wid->current_x = child_wid->smallest_x;
1566 }
1567
1568 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1569 child_wid->current_y = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1570 }
1571
1572 /* First.5 loop: count how many children are of the biggest step size. */
1573 if ((flags & NC_BIGFIRST) && biggest_stepsize > 0) {
1574 for (const auto &child_wid : this->children) {
1575 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1576 if (hor_step == biggest_stepsize) {
1577 num_changing_childs++;
1578 }
1579 }
1580 }
1581
1582 /* Second loop: Allocate the additional horizontal space over the resizing children, starting with the biggest resize steps. */
1583 while (biggest_stepsize > 0) {
1584 uint next_biggest_stepsize = 0;
1585 for (const auto &child_wid : this->children) {
1586 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1587 if (hor_step > biggest_stepsize) continue; // Already done
1588 if (hor_step == biggest_stepsize) {
1589 uint increment = additional_length / num_changing_childs;
1590 num_changing_childs--;
1591 if (hor_step > 1) increment -= increment % hor_step;
1592 child_wid->current_x = child_wid->smallest_x + increment;
1593 additional_length -= increment;
1594 continue;
1595 }
1596 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1597 }
1598 biggest_stepsize = next_biggest_stepsize;
1599
1600 if (num_changing_childs == 0 && (flags & NC_BIGFIRST) && biggest_stepsize > 0) {
1601 /* Second.5 loop: count how many children are of the updated biggest step size. */
1602 for (const auto &child_wid : this->children) {
1603 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1604 if (hor_step == biggest_stepsize) {
1605 num_changing_childs++;
1606 }
1607 }
1608 }
1609 }
1610 assert(num_changing_childs == 0);
1611
1612 uint pre = this->pip_pre;
1613 uint inter = this->pip_inter;
1614
1615 if (additional_length > 0) {
1616 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1617 * which is never explicitly needed. */
1618 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1619 if (r > 0) {
1620 pre += this->pip_ratio_pre * additional_length / r;
1621 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1622 }
1623 }
1624
1625 /* Third loop: Compute position and call the child. */
1626 uint position = rtl ? this->current_x - pre : pre; // Place to put next child relative to origin of the container.
1627 for (const auto &child_wid : this->children) {
1628 uint child_width = child_wid->current_x;
1629 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1630 uint child_y = y + child_wid->padding.top;
1631
1632 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1633 if (child_wid->current_x != 0) {
1634 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1635 position = rtl ? position - padded_child_width : position + padded_child_width;
1636 }
1637 }
1638}
1639
1645
1646void NWidgetHorizontalLTR::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1647{
1648 NWidgetHorizontal::AssignSizePosition(sizing, x, y, given_width, given_height, false);
1649}
1650
1655
1657{
1658 this->smallest_x = 0; // Biggest child.
1659 this->smallest_y = 0; // Sum of minimal size of all children.
1660 this->fill_x = 1; // smallest common child fill step.
1661 this->fill_y = 0; // smallest non-zero child widget fill step.
1662 this->resize_x = 1; // smallest common child resize step.
1663 this->resize_y = 0; // smallest non-zero child widget resize step.
1664 this->gaps = 0;
1665
1666 /* 1a. Forward call, collect longest/widest child length. */
1667 uint highest = 0; // Highest child found.
1668 uint max_hor_fill = 0; // Biggest horizontal fill step.
1669 for (const auto &child_wid : this->children) {
1670 child_wid->SetupSmallestSize(w);
1671 highest = std::max(highest, child_wid->smallest_y);
1672 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(ST_SMALLEST));
1673 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1674 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->gaps++;
1675 }
1676 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1677 /* 1b. Make the container wider if needed to accommodate all children nicely. */
1678 [[maybe_unused]] uint max_smallest = this->smallest_x + 3 * max_hor_fill; // Upper limit to computing smallest height.
1679 uint cur_width = this->smallest_x;
1680 for (;;) {
1681 for (const auto &child_wid : this->children) {
1682 uint step_size = child_wid->GetHorizontalStepSize(ST_SMALLEST);
1683 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1684 if (step_size > 1 && child_width < cur_width) { // Small step sizes or already fitting children are not interesting.
1685 uint remainder = (cur_width - child_width) % step_size;
1686 if (remainder > 0) { // Child did not fit entirely, widen the container.
1687 cur_width += step_size - remainder;
1688 assert(cur_width < max_smallest); // Safeguard against infinite width expansion.
1689 /* Remaining children will adapt to the new cur_width, thus speeding up the computation. */
1690 }
1691 }
1692 }
1693 if (this->smallest_x == cur_width) break;
1694 this->smallest_x = cur_width; // Smallest width got changed, try again.
1695 }
1696 /* 2. For containers that must maintain equal width, extend children minimal size. */
1697 for (const auto &child_wid : this->children) {
1698 child_wid->smallest_x = this->smallest_x - child_wid->padding.Horizontal();
1699 child_wid->ApplyAspectRatio();
1700 highest = std::max(highest, child_wid->smallest_y);
1701 }
1702 if (this->flags & NC_EQUALSIZE) {
1703 for (const auto &child_wid : this->children) {
1704 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1705 }
1706 }
1707 /* 3. Compute smallest, fill, and resize values of the container. */
1708 for (const auto &child_wid : this->children) {
1709 this->smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1710 if (child_wid->fill_y > 0) {
1711 if (this->fill_y == 0 || this->fill_y > child_wid->fill_y) this->fill_y = child_wid->fill_y;
1712 }
1713 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1714
1715 if (child_wid->resize_y > 0) {
1716 if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y;
1717 }
1718 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1719 }
1720 if (this->fill_y == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_y = 1;
1721 /* 4. Increase by required PIP space. */
1722 this->smallest_y += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1723}
1724
1725void NWidgetVertical::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1726{
1727 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1728
1729 /* Compute additional height given to us. */
1730 uint additional_length = given_height - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1731 for (const auto &child_wid : this->children) {
1732 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1733 }
1734
1735 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1736
1737 /* Like the horizontal container, the vertical container also distributes additional height evenly, starting with the children with the biggest resize steps.
1738 * It also stores computed widths and heights into current_x and current_y values of the child.
1739 */
1740
1741 /* First loop: Find biggest stepsize, find number of children that want a piece of the pie, handle horizontal size for all children, handle vertical size for non-resizing child. */
1742 int num_changing_childs = 0; // Number of children that can change size.
1743 uint biggest_stepsize = 0;
1744 for (const auto &child_wid : this->children) {
1745 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1746 if (vert_step > 0) {
1747 if (!(flags & NC_BIGFIRST)) num_changing_childs++;
1748 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1749 } else {
1750 child_wid->current_y = child_wid->smallest_y;
1751 }
1752
1753 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1754 child_wid->current_x = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1755 }
1756
1757 /* First.5 loop: count how many children are of the biggest step size. */
1758 if ((this->flags & NC_BIGFIRST) && biggest_stepsize > 0) {
1759 for (const auto &child_wid : this->children) {
1760 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1761 if (vert_step == biggest_stepsize) {
1762 num_changing_childs++;
1763 }
1764 }
1765 }
1766
1767 /* Second loop: Allocate the additional vertical space over the resizing children, starting with the biggest resize steps. */
1768 while (biggest_stepsize > 0) {
1769 uint next_biggest_stepsize = 0;
1770 for (const auto &child_wid : this->children) {
1771 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1772 if (vert_step > biggest_stepsize) continue; // Already done
1773 if (vert_step == biggest_stepsize) {
1774 uint increment = additional_length / num_changing_childs;
1775 num_changing_childs--;
1776 if (vert_step > 1) increment -= increment % vert_step;
1777 child_wid->current_y = child_wid->smallest_y + increment;
1778 additional_length -= increment;
1779 continue;
1780 }
1781 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1782 }
1783 biggest_stepsize = next_biggest_stepsize;
1784
1785 if (num_changing_childs == 0 && (flags & NC_BIGFIRST) && biggest_stepsize > 0) {
1786 /* Second.5 loop: count how many children are of the updated biggest step size. */
1787 for (const auto &child_wid : this->children) {
1788 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1789 if (vert_step == biggest_stepsize) {
1790 num_changing_childs++;
1791 }
1792 }
1793 }
1794 }
1795 assert(num_changing_childs == 0);
1796
1797 uint pre = this->pip_pre;
1798 uint inter = this->pip_inter;
1799
1800 if (additional_length > 0) {
1801 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1802 * which is never explicitly needed. */
1803 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1804 if (r > 0) {
1805 pre += this->pip_ratio_pre * additional_length / r;
1806 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1807 }
1808 }
1809
1810 /* Third loop: Compute position and call the child. */
1811 uint position = pre; // Place to put next child relative to origin of the container.
1812 for (const auto &child_wid : this->children) {
1813 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1814 uint child_height = child_wid->current_y;
1815
1816 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1817 if (child_wid->current_y != 0) {
1818 position += child_height + child_wid->padding.Vertical() + inter;
1819 }
1820 }
1821}
1822
1829{
1830 this->SetMinimalSize(width, height);
1831 this->SetResize(0, 0);
1832}
1833
1835{
1836 this->smallest_x = this->min_x;
1837 this->smallest_y = this->min_y;
1838 this->ApplyAspectRatio();
1839}
1840
1844
1846{
1847 /* Spacer widget is never normally visible. */
1848
1849 if (_draw_widget_outlines && this->current_x != 0 && this->current_y != 0) {
1850 /* Spacers indicate a potential design issue, so get extra highlighting. */
1851 GfxFillRect(this->GetCurrentRect(), PC_WHITE, FILLRECT_CHECKER);
1852
1853 DrawOutline(w, this);
1854 }
1855}
1856
1858{
1859 /* Spacer widget never need repainting. */
1860}
1861
1863{
1864 return nullptr;
1865}
1866
1867NWidgetMatrix::NWidgetMatrix(Colours colour, WidgetID index) : NWidgetPIPContainer(NWID_MATRIX, NC_EQUALSIZE), index(index), clicked(-1), count(-1)
1868{
1869 this->colour = colour;
1870}
1871
1877{
1878 this->clicked = clicked;
1879 if (this->clicked >= 0 && this->sb != nullptr && this->widgets_x != 0) {
1880 int vpos = (this->clicked / this->widgets_x) * this->widget_h; // Vertical position of the top.
1881 /* Need to scroll down -> Scroll to the bottom.
1882 * However, last entry has no 'this->pip_inter' underneath, and we must stay below this->sb->GetCount() */
1883 if (this->sb->GetPosition() < vpos) vpos += this->widget_h - this->pip_inter - 1;
1884 this->sb->ScrollTowards(vpos);
1885 }
1886}
1887
1894{
1895 this->count = count;
1896
1897 if (this->sb == nullptr || this->widgets_x == 0) return;
1898
1899 /* We need to get the number of pixels the matrix is high/wide.
1900 * So, determine the number of rows/columns based on the number of
1901 * columns/rows (one is constant/unscrollable).
1902 * Then multiply that by the height of a widget, and add the pre
1903 * and post spacing "offsets". */
1904 count = CeilDiv(count, this->sb->IsVertical() ? this->widgets_x : this->widgets_y);
1905 count *= (this->sb->IsVertical() ? this->children.front()->smallest_y : this->children.front()->smallest_x) + this->pip_inter;
1906 if (count > 0) count -= this->pip_inter; // We counted an inter too much in the multiplication above
1907 count += this->pip_pre + this->pip_post;
1908 this->sb->SetCount(count);
1909 this->sb->SetCapacity(this->sb->IsVertical() ? this->current_y : this->current_x);
1910 this->sb->SetStepSize(this->sb->IsVertical() ? this->widget_h : this->widget_w);
1911}
1912
1918{
1919 this->sb = sb;
1920}
1921
1927{
1928 return this->current_element;
1929}
1930
1932{
1933 assert(this->children.size() == 1);
1934
1935 this->children.front()->SetupSmallestSize(w);
1936
1937 Dimension padding = { (uint)this->pip_pre + this->pip_post, (uint)this->pip_pre + this->pip_post};
1938 Dimension size = {this->children.front()->smallest_x + padding.width, this->children.front()->smallest_y + padding.height};
1939 Dimension fill = {0, 0};
1940 Dimension resize = {this->pip_inter + this->children.front()->smallest_x, this->pip_inter + this->children.front()->smallest_y};
1941
1942 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
1943
1944 this->smallest_x = size.width;
1945 this->smallest_y = size.height;
1946 this->fill_x = fill.width;
1947 this->fill_y = fill.height;
1948 this->resize_x = resize.width;
1949 this->resize_y = resize.height;
1950 this->ApplyAspectRatio();
1951}
1952
1953void NWidgetMatrix::AssignSizePosition(SizingType, int x, int y, uint given_width, uint given_height, bool)
1954{
1955 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1956
1957 this->pos_x = x;
1958 this->pos_y = y;
1959 this->current_x = given_width;
1960 this->current_y = given_height;
1961
1962 /* Determine the size of the widgets, and the number of visible widgets on each of the axis. */
1963 this->widget_w = this->children.front()->smallest_x + this->pip_inter;
1964 this->widget_h = this->children.front()->smallest_y + this->pip_inter;
1965
1966 /* Account for the pip_inter is between widgets, so we need to account for that when
1967 * the division assumes pip_inter is used for all widgets. */
1968 this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
1969 this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
1970
1971 /* When resizing, update the scrollbar's count. E.g. with a vertical
1972 * scrollbar becoming wider or narrower means the amount of rows in
1973 * the scrollbar becomes respectively smaller or higher. */
1974 this->SetCount(this->count);
1975}
1976
1978{
1979 if (this->index >= 0) widget_lookup[this->index] = this;
1981}
1982
1984{
1985 /* Falls outside of the matrix widget. */
1986 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1987
1988 int start_x, start_y, base_offs_x, base_offs_y;
1989 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
1990
1991 bool rtl = _current_text_dir == TD_RTL;
1992
1993 int widget_col = (rtl ?
1994 -x + (int)this->pip_post + this->pos_x + base_offs_x + this->widget_w - 1 - (int)this->pip_inter :
1995 x - (int)this->pip_pre - this->pos_x - base_offs_x
1996 ) / this->widget_w;
1997
1998 int widget_row = (y - base_offs_y - (int)this->pip_pre - this->pos_y) / this->widget_h;
1999
2000 this->current_element = (widget_row + start_y) * this->widgets_x + start_x + widget_col;
2001 if (this->current_element >= this->count) return nullptr;
2002
2003 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2004 assert(child != nullptr);
2006 this->pos_x + (rtl ? this->pip_post - widget_col * this->widget_w : this->pip_pre + widget_col * this->widget_w) + base_offs_x,
2007 this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
2008 child->smallest_x, child->smallest_y, rtl);
2009
2010 return child->GetWidgetFromPos(x, y);
2011}
2012
2013/* virtual */ void NWidgetMatrix::Draw(const Window *w)
2014{
2015 /* Fill the background. */
2016 GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, SHADE_LIGHT));
2017
2018 /* Set up a clipping area for the previews. */
2019 bool rtl = _current_text_dir == TD_RTL;
2020 DrawPixelInfo tmp_dpi;
2021 if (!FillDrawPixelInfo(&tmp_dpi, this->pos_x + (rtl ? this->pip_post : this->pip_pre), this->pos_y + this->pip_pre, this->current_x - this->pip_pre - this->pip_post, this->current_y - this->pip_pre - this->pip_post)) return;
2022
2023 {
2024 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
2025
2026 /* Get the appropriate offsets so we can draw the right widgets. */
2027 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2028 assert(child != nullptr);
2029 int start_x, start_y, base_offs_x, base_offs_y;
2030 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2031
2032 int offs_y = base_offs_y;
2033 for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
2034 /* Are we within bounds? */
2035 if (offs_y + child->smallest_y <= 0) continue;
2036 if (offs_y >= (int)this->current_y) break;
2037
2038 /* We've passed our amount of widgets. */
2039 if (y * this->widgets_x >= this->count) break;
2040
2041 int offs_x = base_offs_x;
2042 for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
2043 /* Are we within bounds? */
2044 if (offs_x + child->smallest_x <= 0) continue;
2045 if (offs_x >= (int)this->current_x) continue;
2046
2047 /* Do we have this many widgets? */
2048 this->current_element = y * this->widgets_x + x;
2049 if (this->current_element >= this->count) break;
2050
2051 child->AssignSizePosition(ST_RESIZE, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
2052 child->SetLowered(this->clicked == this->current_element);
2053 child->Draw(w);
2054 }
2055 }
2056 }
2057
2058 DrawOutline(w, this);
2059}
2060
2068void NWidgetMatrix::GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
2069{
2070 base_offs_x = _current_text_dir == TD_RTL ? this->widget_w * (this->widgets_x - 1) : 0;
2071 base_offs_y = 0;
2072 start_x = 0;
2073 start_y = 0;
2074 if (this->sb != nullptr) {
2075 if (this->sb->IsVertical()) {
2076 start_y = this->sb->GetPosition() / this->widget_h;
2077 base_offs_y += -this->sb->GetPosition() + start_y * this->widget_h;
2078 } else {
2079 start_x = this->sb->GetPosition() / this->widget_w;
2080 int sub_x = this->sb->GetPosition() - start_x * this->widget_w;
2081 if (_current_text_dir == TD_RTL) {
2082 base_offs_x += sub_x;
2083 } else {
2084 base_offs_x -= sub_x;
2085 }
2086 }
2087 }
2088}
2089
2099NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr<NWidgetPIPContainer> &&child) : NWidgetCore(tp, colour, index, 1, 1, 0x0, STR_NULL)
2100{
2101 assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
2102 this->child = std::move(child);
2103 if (this->child != nullptr) this->child->parent = this;
2104 this->SetAlignment(SA_TOP | SA_LEFT);
2105}
2106
2114void NWidgetBackground::Add(std::unique_ptr<NWidgetBase> &&nwid)
2115{
2116 if (this->child == nullptr) {
2117 this->child = std::make_unique<NWidgetVertical>();
2118 }
2119 nwid->parent = this->child.get();
2120 this->child->Add(std::move(nwid));
2121}
2122
2133void NWidgetBackground::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
2134{
2135 if (this->child == nullptr) {
2136 this->child = std::make_unique<NWidgetVertical>();
2137 }
2138 this->child->parent = this;
2139 this->child->SetPIP(pip_pre, pip_inter, pip_post);
2140}
2141
2152void NWidgetBackground::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
2153{
2154 if (this->child == nullptr) {
2155 this->child = std::make_unique<NWidgetVertical>();
2156 }
2157 this->child->parent = this;
2158 this->child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2159}
2160
2161void NWidgetBackground::AdjustPaddingForZoom()
2162{
2163 if (child != nullptr) child->AdjustPaddingForZoom();
2164 NWidgetCore::AdjustPaddingForZoom();
2165}
2166
2168{
2169 if (this->child != nullptr) {
2170 this->child->SetupSmallestSize(w);
2171
2172 this->smallest_x = this->child->smallest_x;
2173 this->smallest_y = this->child->smallest_y;
2174 this->fill_x = this->child->fill_x;
2175 this->fill_y = this->child->fill_y;
2176 this->resize_x = this->child->resize_x;
2177 this->resize_y = this->child->resize_y;
2178
2179 /* Don't apply automatic padding if there is no child widget. */
2180 if (w == nullptr) return;
2181
2182 if (this->type == WWT_FRAME) {
2183 /* Account for the size of the frame's text if that exists */
2184 this->child->padding = WidgetDimensions::scaled.frametext;
2185 this->child->padding.top = std::max<uint8_t>(WidgetDimensions::scaled.frametext.top, this->widget_data != STR_NULL ? GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.frametext.top / 2 : 0);
2186
2187 this->smallest_x += this->child->padding.Horizontal();
2188 this->smallest_y += this->child->padding.Vertical();
2189
2190 if (this->index >= 0) w->SetStringParameters(this->index);
2191 this->smallest_x = std::max(this->smallest_x, GetStringBoundingBox(this->widget_data, this->text_size).width + WidgetDimensions::scaled.frametext.Horizontal());
2192 } else if (this->type == WWT_INSET) {
2193 /* Apply automatic padding for bevel thickness. */
2194 this->child->padding = WidgetDimensions::scaled.bevel;
2195
2196 this->smallest_x += this->child->padding.Horizontal();
2197 this->smallest_y += this->child->padding.Vertical();
2198 }
2199 this->ApplyAspectRatio();
2200 } else {
2201 Dimension d = {this->min_x, this->min_y};
2202 Dimension fill = {this->fill_x, this->fill_y};
2203 Dimension resize = {this->resize_x, this->resize_y};
2204 if (w != nullptr) { // A non-nullptr window pointer acts as switch to turn dynamic widget size on.
2205 if (this->type == WWT_FRAME || this->type == WWT_INSET) {
2206 if (this->index >= 0) w->SetStringParameters(this->index);
2207 Dimension background = GetStringBoundingBox(this->widget_data, this->text_size);
2208 background.width += (this->type == WWT_FRAME) ? (WidgetDimensions::scaled.frametext.Horizontal()) : (WidgetDimensions::scaled.inset.Horizontal());
2209 d = maxdim(d, background);
2210 }
2211 if (this->index >= 0) {
2213 switch (this->type) {
2214 default: NOT_REACHED();
2218 }
2219 w->UpdateWidgetSize(this->index, d, padding, fill, resize);
2220 }
2221 }
2222 this->smallest_x = d.width;
2223 this->smallest_y = d.height;
2224 this->fill_x = fill.width;
2225 this->fill_y = fill.height;
2226 this->resize_x = resize.width;
2227 this->resize_y = resize.height;
2228 this->ApplyAspectRatio();
2229 }
2230}
2231
2232void NWidgetBackground::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
2233{
2234 this->StoreSizePosition(sizing, x, y, given_width, given_height);
2235
2236 if (this->child != nullptr) {
2237 uint x_offset = (rtl ? this->child->padding.right : this->child->padding.left);
2238 uint width = given_width - this->child->padding.Horizontal();
2239 uint height = given_height - this->child->padding.Vertical();
2240 this->child->AssignSizePosition(sizing, x + x_offset, y + this->child->padding.top, width, height, rtl);
2241 }
2242}
2243
2245{
2246 if (this->index >= 0) widget_lookup[this->index] = this;
2247 if (this->child != nullptr) this->child->FillWidgetLookup(widget_lookup);
2248}
2249
2251{
2252 if (this->current_x == 0 || this->current_y == 0) return;
2253
2254 Rect r = this->GetCurrentRect();
2255
2256 const DrawPixelInfo *dpi = _cur_dpi;
2257 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2258
2259 switch (this->type) {
2260 case WWT_PANEL:
2261 assert(this->widget_data == 0);
2262 DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, this->IsLowered() ? FR_LOWERED : FR_NONE);
2263 break;
2264
2265 case WWT_FRAME:
2266 if (this->index >= 0) w->SetStringParameters(this->index);
2267 DrawFrame(r, this->colour, this->text_colour, this->widget_data, this->align, this->text_size);
2268 break;
2269
2270 case WWT_INSET:
2271 if (this->index >= 0) w->SetStringParameters(this->index);
2272 DrawInset(r, this->colour, this->text_colour, this->widget_data, this->align, this->text_size);
2273 break;
2274
2275 default:
2276 NOT_REACHED();
2277 }
2278
2279 if (this->index >= 0) w->DrawWidget(r, this->index);
2280 if (this->child != nullptr) this->child->Draw(w);
2281
2282 if (this->IsDisabled()) {
2284 }
2285
2286 DrawOutline(w, this);
2287}
2288
2290{
2291 NWidgetCore *nwid = nullptr;
2292 if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) {
2293 if (this->child != nullptr) nwid = this->child->GetWidgetFromPos(x, y);
2294 if (nwid == nullptr) nwid = this;
2295 }
2296 return nwid;
2297}
2298
2300{
2301 NWidgetBase *nwid = nullptr;
2302 if (this->child != nullptr) nwid = this->child->GetWidgetOfType(tp);
2303 if (nwid == nullptr && this->type == tp) nwid = this;
2304 return nwid;
2305}
2306
2307NWidgetViewport::NWidgetViewport(WidgetID index) : NWidgetCore(NWID_VIEWPORT, INVALID_COLOUR, index, 1, 1, 0x0, STR_NULL)
2308{
2309}
2310
2312{
2313 this->smallest_x = this->min_x;
2314 this->smallest_y = this->min_y;
2315 this->ApplyAspectRatio();
2316}
2317
2319{
2320 if (this->current_x == 0 || this->current_y == 0) return;
2321
2322 if (this->disp_flags & ND_NO_TRANSPARENCY) {
2324 _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_TEXT); // Disable all transparency, except textual stuff
2325 w->DrawViewport();
2326 _transparency_opt = to_backup;
2327 } else {
2328 w->DrawViewport();
2329 }
2330
2331 /* Optionally shade the viewport. */
2332 if (this->disp_flags & (ND_SHADE_GREY | ND_SHADE_DIMMED)) {
2334 }
2335
2336 DrawOutline(w, this);
2337}
2338
2345void NWidgetViewport::InitializeViewport(Window *w, std::variant<TileIndex, VehicleID> focus, ZoomLevel zoom)
2346{
2347 InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, focus, zoom);
2348}
2349
2355{
2356 Viewport *vp = w->viewport;
2357 if (vp != nullptr) {
2358 vp->left = w->left + this->pos_x;
2359 vp->top = w->top + this->pos_y;
2360 vp->width = this->current_x;
2361 vp->height = this->current_y;
2362
2363 vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
2364 vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
2365 }
2366}
2367
2377Scrollbar::size_type Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, WidgetID widget, int padding, int line_height) const
2378{
2379 int pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
2380 if (pos != INT_MAX) pos += this->GetPosition();
2381 return (pos < 0 || pos >= this->GetCount()) ? Scrollbar::npos : pos;
2382}
2383
2398EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
2399{
2400 int new_pos = list_position;
2401 switch (keycode) {
2402 case WKC_UP:
2403 /* scroll up by one */
2404 new_pos--;
2405 break;
2406
2407 case WKC_DOWN:
2408 /* scroll down by one */
2409 new_pos++;
2410 break;
2411
2412 case WKC_PAGEUP:
2413 /* scroll up a page */
2414 new_pos -= this->GetCapacity();
2415 break;
2416
2417 case WKC_PAGEDOWN:
2418 /* scroll down a page */
2419 new_pos += this->GetCapacity();
2420 break;
2421
2422 case WKC_HOME:
2423 /* jump to beginning */
2424 new_pos = 0;
2425 break;
2426
2427 case WKC_END:
2428 /* jump to end */
2429 new_pos = this->GetCount() - 1;
2430 break;
2431
2432 default:
2433 return ES_NOT_HANDLED;
2434 }
2435
2436 /* If there are no elements, there is nothing to scroll/update. */
2437 if (this->GetCount() != 0) {
2438 list_position = Clamp(new_pos, 0, this->GetCount() - 1);
2439 }
2440 return ES_HANDLED;
2441}
2442
2443
2452{
2453 NWidgetBase *nwid = w->GetWidget<NWidgetBase>(widget);
2454 if (this->IsVertical()) {
2455 this->SetCapacity(((int)nwid->current_y - padding) / (int)nwid->resize_y);
2456 } else {
2457 this->SetCapacity(((int)nwid->current_x - padding) / (int)nwid->resize_x);
2458 }
2459}
2460
2468Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
2469{
2470 const int count = sb.GetCount() * resize_step;
2471 const int position = sb.GetPosition() * resize_step;
2472
2473 if (sb.IsVertical()) {
2474 r.top -= position;
2475 r.bottom = r.top + count;
2476 } else {
2477 bool rtl = _current_text_dir == TD_RTL;
2478 if (rtl) {
2479 r.right += position;
2480 r.left = r.right - count;
2481 } else {
2482 r.left -= position;
2483 r.right = r.left + count;
2484 }
2485 }
2486
2487 return r;
2488}
2489
2496NWidgetScrollbar::NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index) : NWidgetCore(tp, colour, index, 1, 1, 0x0, STR_NULL), Scrollbar(tp != NWID_HSCROLLBAR)
2497{
2498 assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR);
2499
2500 switch (this->type) {
2501 case NWID_HSCROLLBAR:
2502 this->SetResize(1, 0);
2503 this->SetFill(1, 0);
2504 this->SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2505 break;
2506
2507 case NWID_VSCROLLBAR:
2508 this->SetResize(0, 1);
2509 this->SetFill(0, 1);
2510 this->SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2511 break;
2512
2513 default: NOT_REACHED();
2514 }
2515}
2516
2518{
2519 this->min_x = 0;
2520 this->min_y = 0;
2521
2522 switch (this->type) {
2523 case NWID_HSCROLLBAR:
2524 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2525 break;
2526
2527 case NWID_VSCROLLBAR:
2528 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2529 break;
2530
2531 default: NOT_REACHED();
2532 }
2533
2534 this->smallest_x = this->min_x;
2535 this->smallest_y = this->min_y;
2536}
2537
2539{
2540 if (this->current_x == 0 || this->current_y == 0) return;
2541
2542 Rect r = this->GetCurrentRect();
2543
2544 const DrawPixelInfo *dpi = _cur_dpi;
2545 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2546
2547 bool up_lowered = HasBit(this->disp_flags, NDB_SCROLLBAR_UP);
2548 bool down_lowered = HasBit(this->disp_flags, NDB_SCROLLBAR_DOWN);
2549 bool middle_lowered = !(this->disp_flags & ND_SCROLLBAR_BTN) && w->mouse_capture_widget == this->index;
2550
2551 if (this->type == NWID_HSCROLLBAR) {
2552 DrawHorizontalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2553 } else {
2554 DrawVerticalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2555 }
2556
2557 if (this->IsDisabled()) {
2559 }
2560
2561 DrawOutline(w, this);
2562}
2563
2564/* static */ void NWidgetScrollbar::InvalidateDimensionCache()
2565{
2566 vertical_dimension.width = vertical_dimension.height = 0;
2567 horizontal_dimension.width = horizontal_dimension.height = 0;
2568}
2569
2570/* static */ Dimension NWidgetScrollbar::GetVerticalDimension()
2571{
2572 if (vertical_dimension.width == 0) {
2573 vertical_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_UP), GetScaledSpriteSize(SPR_ARROW_DOWN));
2576 }
2577 return vertical_dimension;
2578}
2579
2580/* static */ Dimension NWidgetScrollbar::GetHorizontalDimension()
2581{
2582 if (horizontal_dimension.width == 0) {
2583 horizontal_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2586 }
2587 return horizontal_dimension;
2588}
2589
2592
2595{
2596 shadebox_dimension.width = shadebox_dimension.height = 0;
2597 debugbox_dimension.width = debugbox_dimension.height = 0;
2598 defsizebox_dimension.width = defsizebox_dimension.height = 0;
2599 stickybox_dimension.width = stickybox_dimension.height = 0;
2600 resizebox_dimension.width = resizebox_dimension.height = 0;
2601 closebox_dimension.width = closebox_dimension.height = 0;
2602 dropdown_dimension.width = dropdown_dimension.height = 0;
2603}
2604
2612
2621NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, uint32_t data, StringID tip) : NWidgetCore(tp, colour, index, 1, 1, data, tip)
2622{
2623 assert(index >= 0 || tp == WWT_LABEL || tp == WWT_TEXT || tp == WWT_CAPTION || tp == WWT_RESIZEBOX || tp == WWT_SHADEBOX || tp == WWT_DEFSIZEBOX || tp == WWT_DEBUGBOX || tp == WWT_STICKYBOX || tp == WWT_CLOSEBOX);
2624 this->min_x = 0;
2625 this->min_y = 0;
2626 this->SetResize(0, 0);
2627
2628 switch (tp) {
2629 case WWT_EMPTY:
2630 break;
2631
2632 case WWT_TEXT:
2633 this->SetFill(0, 0);
2635 break;
2636
2637 case WWT_PUSHBTN:
2638 case WWT_IMGBTN:
2639 case WWT_PUSHIMGBTN:
2640 case WWT_IMGBTN_2:
2641 case WWT_TEXTBTN:
2642 case WWT_PUSHTXTBTN:
2643 case WWT_TEXTBTN_2:
2644 case WWT_LABEL:
2645 case WWT_MATRIX:
2647 case NWID_PUSHBUTTON_DROPDOWN:
2648 this->SetFill(0, 0);
2649 break;
2650
2651 case WWT_ARROWBTN:
2652 case WWT_PUSHARROWBTN:
2653 this->SetFill(0, 0);
2654 this->SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2655 break;
2656
2657 case WWT_EDITBOX:
2658 this->SetFill(0, 0);
2659 break;
2660
2661 case WWT_CAPTION:
2662 this->SetFill(1, 0);
2663 this->SetResize(1, 0);
2665 this->SetMinimalTextLines(1, WidgetDimensions::unscaled.captiontext.Vertical(), FS_NORMAL);
2666 this->SetDataTip(data, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2667 break;
2668
2669 case WWT_STICKYBOX:
2670 this->SetFill(0, 0);
2672 this->SetDataTip(STR_NULL, STR_TOOLTIP_STICKY);
2673 this->SetAspect(this->min_x, this->min_y);
2674 break;
2675
2676 case WWT_SHADEBOX:
2677 this->SetFill(0, 0);
2679 this->SetDataTip(STR_NULL, STR_TOOLTIP_SHADE);
2680 this->SetAspect(this->min_x, this->min_y);
2681 break;
2682
2683 case WWT_DEBUGBOX:
2684 this->SetFill(0, 0);
2686 this->SetDataTip(STR_NULL, STR_TOOLTIP_DEBUG);
2687 this->SetAspect(this->min_x, this->min_y);
2688 break;
2689
2690 case WWT_DEFSIZEBOX:
2691 this->SetFill(0, 0);
2693 this->SetDataTip(STR_NULL, STR_TOOLTIP_DEFSIZE);
2694 this->SetAspect(this->min_x, this->min_y);
2695 break;
2696
2697 case WWT_RESIZEBOX:
2698 this->SetFill(0, 0);
2700 this->SetDataTip(RWV_SHOW_BEVEL, STR_TOOLTIP_RESIZE);
2701 break;
2702
2703 case WWT_CLOSEBOX:
2704 this->SetFill(0, 0);
2706 this->SetDataTip(STR_NULL, STR_TOOLTIP_CLOSE_WINDOW);
2707 this->SetAspect(this->min_x, this->min_y);
2708 break;
2709
2710 case WWT_DROPDOWN:
2711 this->SetFill(0, 0);
2713 this->SetAlignment(SA_TOP | SA_LEFT);
2714 break;
2715
2716 default:
2717 NOT_REACHED();
2718 }
2719}
2720
2722{
2723 Dimension padding = {0, 0};
2724 Dimension size = {this->min_x, this->min_y};
2725 Dimension fill = {this->fill_x, this->fill_y};
2726 Dimension resize = {this->resize_x, this->resize_y};
2727 switch (this->type) {
2728 case WWT_EMPTY: {
2729 break;
2730 }
2731 case WWT_MATRIX: {
2733 break;
2734 }
2735 case WWT_SHADEBOX: {
2737 if (NWidgetLeaf::shadebox_dimension.width == 0) {
2738 NWidgetLeaf::shadebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_SHADE), GetScaledSpriteSize(SPR_WINDOW_UNSHADE));
2741 }
2743 break;
2744 }
2745 case WWT_DEBUGBOX:
2748 if (NWidgetLeaf::debugbox_dimension.width == 0) {
2752 }
2754 } else {
2755 /* If the setting is disabled we don't want to see it! */
2756 size.width = 0;
2757 fill.width = 0;
2758 resize.width = 0;
2759 }
2760 break;
2761
2762 case WWT_STICKYBOX: {
2764 if (NWidgetLeaf::stickybox_dimension.width == 0) {
2768 }
2770 break;
2771 }
2772
2773 case WWT_DEFSIZEBOX: {
2775 if (NWidgetLeaf::defsizebox_dimension.width == 0) {
2779 }
2781 break;
2782 }
2783
2784 case WWT_RESIZEBOX: {
2786 if (NWidgetLeaf::resizebox_dimension.width == 0) {
2787 NWidgetLeaf::resizebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_RESIZE_LEFT), GetScaledSpriteSize(SPR_WINDOW_RESIZE_RIGHT));
2790 }
2792 break;
2793 }
2794 case WWT_EDITBOX: {
2795 Dimension sprite_size = GetScaledSpriteSize(_current_text_dir == TD_RTL ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
2796 size.width = std::max(size.width, ScaleGUITrad(30) + sprite_size.width);
2797 size.height = std::max(sprite_size.height, GetStringBoundingBox("_").height + WidgetDimensions::scaled.framerect.Vertical());
2798 }
2799 [[fallthrough]];
2800 case WWT_PUSHBTN: {
2802 break;
2803 }
2804 case WWT_IMGBTN:
2805 case WWT_IMGBTN_2:
2806 case WWT_PUSHIMGBTN: {
2809 if (this->type == WWT_IMGBTN_2) d2 = maxdim(d2, GetScaledSpriteSize(this->widget_data + 1));
2810 d2.width += padding.width;
2811 d2.height += padding.height;
2812 size = maxdim(size, d2);
2813 break;
2814 }
2815 case WWT_ARROWBTN:
2816 case WWT_PUSHARROWBTN: {
2818 Dimension d2 = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2819 d2.width += padding.width;
2820 d2.height += padding.height;
2821 size = maxdim(size, d2);
2822 break;
2823 }
2824
2825 case WWT_CLOSEBOX: {
2827 if (NWidgetLeaf::closebox_dimension.width == 0) {
2831 }
2833 break;
2834 }
2835 case WWT_TEXTBTN:
2836 case WWT_PUSHTXTBTN:
2837 case WWT_TEXTBTN_2: {
2839 if (this->index >= 0) w->SetStringParameters(this->index);
2841 d2.width += padding.width;
2842 d2.height += padding.height;
2843 size = maxdim(size, d2);
2844 break;
2845 }
2846 case WWT_LABEL:
2847 case WWT_TEXT: {
2848 if (this->index >= 0) w->SetStringParameters(this->index);
2849 size = maxdim(size, GetStringBoundingBox(this->widget_data, this->text_size));
2850 break;
2851 }
2852 case WWT_CAPTION: {
2854 if (this->index >= 0) w->SetStringParameters(this->index);
2856 d2.width += padding.width;
2857 d2.height += padding.height;
2858 size = maxdim(size, d2);
2859 break;
2860 }
2861 case WWT_DROPDOWN:
2863 case NWID_PUSHBUTTON_DROPDOWN: {
2864 if (NWidgetLeaf::dropdown_dimension.width == 0) {
2868 }
2870 if (this->index >= 0) w->SetStringParameters(this->index);
2872 d2.width += padding.width;
2873 d2.height = std::max(d2.height + padding.height, NWidgetLeaf::dropdown_dimension.height);
2874 size = maxdim(size, d2);
2875 break;
2876 }
2877 default:
2878 NOT_REACHED();
2879 }
2880
2881 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
2882
2883 this->smallest_x = size.width;
2884 this->smallest_y = size.height;
2885 this->fill_x = fill.width;
2886 this->fill_y = fill.height;
2887 this->resize_x = resize.width;
2888 this->resize_y = resize.height;
2889 this->ApplyAspectRatio();
2890}
2891
2893{
2894 if (this->current_x == 0 || this->current_y == 0) return;
2895
2896 /* Setup a clipping rectangle... for WWT_EMPTY or WWT_TEXT, an extra scaled pixel is allowed in case text shadow encroaches. */
2897 int extra = (this->type == WWT_EMPTY || this->type == WWT_TEXT) ? ScaleGUITrad(1) : 0;
2898 DrawPixelInfo new_dpi;
2899 if (!FillDrawPixelInfo(&new_dpi, this->pos_x, this->pos_y, this->current_x + extra, this->current_y + extra)) return;
2900 /* ...but keep coordinates relative to the window. */
2901 new_dpi.left += this->pos_x;
2902 new_dpi.top += this->pos_y;
2903
2904 AutoRestoreBackup dpi_backup(_cur_dpi, &new_dpi);
2905
2906 Rect r = this->GetCurrentRect();
2907
2908 bool clicked = this->IsLowered();
2909 switch (this->type) {
2910 case WWT_EMPTY:
2911 /* WWT_EMPTY used as a spacer indicates a potential design issue. */
2912 if (this->index == -1 && _draw_widget_outlines) {
2914 }
2915 break;
2916
2917 case WWT_PUSHBTN:
2918 assert(this->widget_data == 0);
2919 DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE);
2920 break;
2921
2922 case WWT_IMGBTN:
2923 case WWT_PUSHIMGBTN:
2924 case WWT_IMGBTN_2:
2925 DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data, this->align);
2926 break;
2927
2928 case WWT_TEXTBTN:
2929 case WWT_PUSHTXTBTN:
2930 case WWT_TEXTBTN_2:
2931 if (this->index >= 0) w->SetStringParameters(this->index);
2932 DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE);
2933 DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align, this->text_size);
2934 break;
2935
2936 case WWT_ARROWBTN:
2937 case WWT_PUSHARROWBTN: {
2938 SpriteID sprite;
2939 switch (this->widget_data) {
2940 case AWV_DECREASE: sprite = _current_text_dir != TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
2941 case AWV_INCREASE: sprite = _current_text_dir == TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
2942 case AWV_LEFT: sprite = SPR_ARROW_LEFT; break;
2943 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break;
2944 default: NOT_REACHED();
2945 }
2946 DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align);
2947 break;
2948 }
2949
2950 case WWT_LABEL:
2951 if (this->index >= 0) w->SetStringParameters(this->index);
2952 DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align, this->text_size);
2953 break;
2954
2955 case WWT_TEXT:
2956 if (this->index >= 0) w->SetStringParameters(this->index);
2957 DrawText(r, this->text_colour, this->widget_data, this->align, this->text_size);
2958 break;
2959
2960 case WWT_MATRIX:
2961 DrawMatrix(r, this->colour, clicked, this->widget_data, this->resize_x, this->resize_y);
2962 break;
2963
2964 case WWT_EDITBOX: {
2965 const QueryString *query = w->GetQueryString(this->index);
2966 if (query != nullptr) query->DrawEditBox(w, this->index);
2967 break;
2968 }
2969
2970 case WWT_CAPTION:
2971 if (this->index >= 0) w->SetStringParameters(this->index);
2972 DrawCaption(r, this->colour, w->owner, this->text_colour, this->widget_data, this->align, this->text_size);
2973 break;
2974
2975 case WWT_SHADEBOX:
2976 assert(this->widget_data == 0);
2977 DrawShadeBox(r, this->colour, w->IsShaded());
2978 break;
2979
2980 case WWT_DEBUGBOX:
2981 DrawDebugBox(r, this->colour, clicked);
2982 break;
2983
2984 case WWT_STICKYBOX:
2985 assert(this->widget_data == 0);
2986 DrawStickyBox(r, this->colour, !!(w->flags & WF_STICKY));
2987 break;
2988
2989 case WWT_DEFSIZEBOX:
2990 assert(this->widget_data == 0);
2991 DrawDefSizeBox(r, this->colour, clicked);
2992 break;
2993
2994 case WWT_RESIZEBOX:
2995 DrawResizeBox(r, this->colour, this->pos_x < (w->width / 2), !!(w->flags & WF_SIZING), this->widget_data == 0);
2996 break;
2997
2998 case WWT_CLOSEBOX:
2999 DrawCloseBox(r, this->colour);
3000 break;
3001
3002 case WWT_DROPDOWN:
3003 if (this->index >= 0) w->SetStringParameters(this->index);
3004 DrawButtonDropdown(r, this->colour, false, clicked, this->widget_data, this->align);
3005 break;
3006
3008 case NWID_PUSHBUTTON_DROPDOWN:
3009 if (this->index >= 0) w->SetStringParameters(this->index);
3010 DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data, this->align);
3011 break;
3012
3013 default:
3014 NOT_REACHED();
3015 }
3016 if (this->index >= 0) w->DrawWidget(r, this->index);
3017
3018 if (this->IsDisabled()) {
3020 }
3021
3022 DrawOutline(w, this);
3023}
3024
3033{
3034 if (_current_text_dir == TD_LTR) {
3035 int button_width = this->pos_x + this->current_x - NWidgetLeaf::dropdown_dimension.width;
3036 return pt.x < button_width;
3037 } else {
3038 int button_left = this->pos_x + NWidgetLeaf::dropdown_dimension.width;
3039 return pt.x >= button_left;
3040 }
3041}
3042
3043/* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
3044
3051{
3052 return tp > WPT_ATTRIBUTE_BEGIN && tp < WPT_ATTRIBUTE_END;
3053}
3054
3062{
3063 switch (nwid.type) {
3064 case WPT_RESIZE: {
3065 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3066 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_RESIZE requires NWidgetResizeBase");
3067 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3068 nwrb->SetResize(nwid.u.xy.x, nwid.u.xy.y);
3069 break;
3070 }
3071
3072 case WPT_MINSIZE: {
3073 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3074 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINSIZE requires NWidgetResizeBase");
3075 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3076 nwrb->SetMinimalSize(nwid.u.xy.x, nwid.u.xy.y);
3077 break;
3078 }
3079
3080 case WPT_MINTEXTLINES: {
3081 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3082 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINTEXTLINES requires NWidgetResizeBase");
3083 assert(nwid.u.text_lines.size >= FS_BEGIN && nwid.u.text_lines.size < FS_END);
3085 break;
3086 }
3087
3088 case WPT_TEXTSTYLE: {
3089 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3090 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_TEXTSTYLE requires NWidgetCore");
3091 nwc->SetTextStyle(nwid.u.text_style.colour, nwid.u.text_style.size);
3092 break;
3093 }
3094
3095 case WPT_ALIGNMENT: {
3096 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3097 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_ALIGNMENT requires NWidgetCore");
3098 nwc->SetAlignment(nwid.u.align.align);
3099 break;
3100 }
3101
3102 case WPT_FILL: {
3103 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3104 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_FILL requires NWidgetResizeBase");
3105 nwrb->SetFill(nwid.u.xy.x, nwid.u.xy.y);
3106 break;
3107 }
3108
3109 case WPT_DATATIP: {
3110 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3111 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_DATATIP requires NWidgetCore");
3112 nwc->widget_data = nwid.u.data_tip.data;
3113 nwc->tool_tip = nwid.u.data_tip.tooltip;
3114 break;
3115 }
3116
3117 case WPT_PADDING:
3118 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_PADDING requires NWidgetBase");
3119 dest->SetPadding(nwid.u.padding);
3120 break;
3121
3122 case WPT_PIPSPACE: {
3123 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3124 if (nwc != nullptr) nwc->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3125
3126 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3127 if (nwb != nullptr) nwb->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3128
3129 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3130 break;
3131 }
3132
3133 case WPT_PIPRATIO: {
3134 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3135 if (nwc != nullptr) nwc->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3136
3137 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3138 if (nwb != nullptr) nwb->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3139
3140 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3141 break;
3142 }
3143
3144 case WPT_SCROLLBAR: {
3145 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3146 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore");
3147 nwc->scrollbar_index = nwid.u.widget.index;
3148 break;
3149 }
3150
3151 case WPT_ASPECT: {
3152 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_ASPECT requires NWidgetBase");
3153 dest->aspect_ratio = nwid.u.aspect.ratio;
3154 dest->aspect_flags = nwid.u.aspect.flags;
3155 break;
3156 }
3157
3158 default:
3159 NOT_REACHED();
3160 }
3161}
3162
3169static std::unique_ptr<NWidgetBase> MakeNWidget(const NWidgetPart &nwid)
3170{
3171 assert(!IsAttributeWidgetPartType(nwid.type));
3172 assert(nwid.type != WPT_ENDCONTAINER);
3173
3174 switch (nwid.type) {
3175 case NWID_SPACER: return std::make_unique<NWidgetSpacer>(0, 0);
3176
3177 case WWT_PANEL: [[fallthrough]];
3178 case WWT_INSET: [[fallthrough]];
3179 case WWT_FRAME: return std::make_unique<NWidgetBackground>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3180
3181 case NWID_HORIZONTAL: return std::make_unique<NWidgetHorizontal>(nwid.u.cont_flags);
3182 case NWID_HORIZONTAL_LTR: return std::make_unique<NWidgetHorizontalLTR>(nwid.u.cont_flags);
3183 case NWID_VERTICAL: return std::make_unique<NWidgetVertical>(nwid.u.cont_flags);
3184 case NWID_SELECTION: return std::make_unique<NWidgetStacked>(nwid.u.widget.index);
3185 case NWID_MATRIX: return std::make_unique<NWidgetMatrix>(nwid.u.widget.colour, nwid.u.widget.index);
3186 case NWID_VIEWPORT: return std::make_unique<NWidgetViewport>(nwid.u.widget.index);
3187 case NWID_LAYER: return std::make_unique<NWidgetLayer>(nwid.u.widget.index);
3188
3189 case NWID_HSCROLLBAR: [[fallthrough]];
3190 case NWID_VSCROLLBAR: return std::make_unique<NWidgetScrollbar>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3191
3192 case WPT_FUNCTION: return nwid.u.func_ptr();
3193
3194 default:
3195 assert((nwid.type & WWT_MASK) < WWT_LAST || (nwid.type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
3196 return std::make_unique<NWidgetLeaf>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index, 0x0, STR_NULL);
3197 }
3198}
3199
3213static std::span<const NWidgetPart>::iterator MakeNWidget(std::span<const NWidgetPart>::iterator nwid_begin, std::span<const NWidgetPart>::iterator nwid_end, std::unique_ptr<NWidgetBase> &dest, bool &fill_dest)
3214{
3215 dest = nullptr;
3216
3217 if (IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]] throw std::runtime_error("Expected non-attribute NWidgetPart type");
3218 if (nwid_begin->type == WPT_ENDCONTAINER) return nwid_begin;
3219
3220 fill_dest = IsContainerWidgetType(nwid_begin->type);
3221 dest = MakeNWidget(*nwid_begin);
3222 if (dest == nullptr) return nwid_begin;
3223
3224 ++nwid_begin;
3225
3226 /* Once a widget is created, we're now looking for attributes. */
3227 while (nwid_begin != nwid_end && IsAttributeWidgetPartType(nwid_begin->type)) {
3228 ApplyNWidgetPartAttribute(*nwid_begin, dest.get());
3229 ++nwid_begin;
3230 }
3231
3232 return nwid_begin;
3233}
3234
3241{
3242 return tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
3243 || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION || tp == NWID_LAYER;
3244}
3245
3253static std::span<const NWidgetPart>::iterator MakeWidgetTree(std::span<const NWidgetPart>::iterator nwid_begin, std::span<const NWidgetPart>::iterator nwid_end, std::unique_ptr<NWidgetBase> &parent)
3254{
3255 /* If *parent == nullptr, only the first widget is read and returned. Otherwise, *parent must point to either
3256 * a #NWidgetContainer or a #NWidgetBackground object, and parts are added as much as possible. */
3257 NWidgetContainer *nwid_cont = dynamic_cast<NWidgetContainer *>(parent.get());
3258 NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(parent.get());
3259 assert(parent == nullptr || (nwid_cont != nullptr && nwid_parent == nullptr) || (nwid_cont == nullptr && nwid_parent != nullptr));
3260
3261 while (nwid_begin != nwid_end) {
3262 std::unique_ptr<NWidgetBase> sub_widget = nullptr;
3263 bool fill_sub = false;
3264 nwid_begin = MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3265
3266 /* Break out of loop when end reached */
3267 if (sub_widget == nullptr) break;
3268
3269 /* If sub-widget is a container, recursively fill that container. */
3270 if (fill_sub && IsContainerWidgetType(sub_widget->type)) {
3271 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, sub_widget);
3272 }
3273
3274 /* Add sub_widget to parent container if available, otherwise return the widget to the caller. */
3275 if (nwid_cont != nullptr) nwid_cont->Add(std::move(sub_widget));
3276 if (nwid_parent != nullptr) nwid_parent->Add(std::move(sub_widget));
3277 if (nwid_cont == nullptr && nwid_parent == nullptr) {
3278 parent = std::move(sub_widget);
3279 return nwid_begin;
3280 }
3281 }
3282
3283 if (nwid_begin == nwid_end) return nwid_begin; // Reached the end of the array of parts?
3284
3285 assert(nwid_begin < nwid_end);
3286 assert(nwid_begin->type == WPT_ENDCONTAINER);
3287 return std::next(nwid_begin); // *nwid_begin is also 'used'
3288}
3289
3297std::unique_ptr<NWidgetBase> MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3298{
3299 if (container == nullptr) container = std::make_unique<NWidgetVertical>();
3300 [[maybe_unused]] auto nwid_part = MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3301#ifdef WITH_ASSERT
3302 if (nwid_part != std::end(nwid_parts)) [[unlikely]] throw std::runtime_error("Did not consume all NWidgetParts");
3303#endif
3304 return std::move(container);
3305}
3306
3316std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(std::span<const NWidgetPart> nwid_parts, NWidgetStacked **shade_select)
3317{
3318 auto nwid_begin = std::begin(nwid_parts);
3319 auto nwid_end = std::end(nwid_parts);
3320
3321 *shade_select = nullptr;
3322
3323 /* Read the first widget recursively from the array. */
3324 std::unique_ptr<NWidgetBase> nwid = nullptr;
3325 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, nwid);
3326 assert(nwid != nullptr);
3327
3328 NWidgetHorizontal *hor_cont = dynamic_cast<NWidgetHorizontal *>(nwid.get());
3329
3330 auto root = std::make_unique<NWidgetVertical>();
3331 root->Add(std::move(nwid));
3332 if (nwid_begin == nwid_end) return root; // There is no body at all.
3333
3334 if (hor_cont != nullptr && hor_cont->GetWidgetOfType(WWT_CAPTION) != nullptr && hor_cont->GetWidgetOfType(WWT_SHADEBOX) != nullptr) {
3335 /* If the first widget has a title bar and a shade box, silently add a shade selection widget in the tree. */
3336 auto shade_stack = std::make_unique<NWidgetStacked>(-1);
3337 *shade_select = shade_stack.get();
3338 /* Load the remaining parts into the shade stack. */
3339 shade_stack->Add(MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3340 root->Add(std::move(shade_stack));
3341 return root;
3342 }
3343
3344 /* Load the remaining parts into 'root'. */
3345 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3346}
3347
3358std::unique_ptr<NWidgetBase> MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
3359{
3360 assert(max_length >= 1);
3361 std::unique_ptr<NWidgetVertical> vert = nullptr; // Storage for all rows.
3362 std::unique_ptr<NWidgetHorizontal> hor = nullptr; // Storage for buttons in one row.
3363 int hor_length = 0;
3364
3365 Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_NORMAL);
3366 sprite_size.width += WidgetDimensions::unscaled.matrix.Horizontal();
3367 sprite_size.height += WidgetDimensions::unscaled.matrix.Vertical();
3368
3369 for (WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3370 /* Ensure there is room in 'hor' for another button. */
3371 if (hor_length == max_length) {
3372 if (vert == nullptr) vert = std::make_unique<NWidgetVertical>();
3373 vert->Add(std::move(hor));
3374 hor = nullptr;
3375 hor_length = 0;
3376 }
3377 if (hor == nullptr) {
3378 hor = std::make_unique<NWidgetHorizontal>();
3379 hor_length = 0;
3380 }
3381
3382 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, button_colour, widnum);
3383 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3384 panel->SetFill(1, 1);
3385 if (resizable) panel->SetResize(1, 0);
3386 panel->SetDataTip(0x0, button_tooltip);
3387 hor->Add(std::move(panel));
3388 hor_length++;
3389 }
3390 if (vert == nullptr) return hor; // All buttons fit in a single row.
3391
3392 if (hor_length > 0 && hor_length < max_length) {
3393 /* Last row is partial, add a spacer at the end to force all buttons to the left. */
3394 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3395 spc->SetFill(1, 1);
3396 if (resizable) spc->SetResize(1, 0);
3397 hor->Add(std::move(spc));
3398 }
3399 if (hor != nullptr) vert->Add(std::move(hor));
3400 return vert;
3401}
Class for backupping variables and making sure they are restored later.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Nested widget with a child.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2289
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:2299
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:2232
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2167
NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr< NWidgetPIPContainer > &&child=nullptr)
Constructor parent nested widgets.
Definition widget.cpp:2099
void SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
Set additional pre/inter/post space for the background widget.
Definition widget.cpp:2133
std::unique_ptr< NWidgetPIPContainer > child
Child widget.
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2250
void Add(std::unique_ptr< NWidgetBase > &&nwid)
Add a child to the parent.
Definition widget.cpp:2114
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:2244
void SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
Set additional pre/inter/post space ratios for the background widget.
Definition widget.cpp:2152
Baseclass for nested widgets.
void StoreSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height)
Store size and position.
float aspect_ratio
Desired aspect ratio of widget.
virtual void SetupSmallestSize(Window *w)=0
Compute smallest size needed by the widget.
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:904
WidgetType type
Type of the widget / nested widget.
uint resize_x
Horizontal resize step (0 means not resizable).
uint fill_x
Horizontal fill stepsize (from initial size, 0 means not resizable).
NWidgetBase * parent
Parent widget of this widget, automatically filled in when added to container.
virtual void FillWidgetLookup(WidgetLookup &widget_lookup)=0
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
RectPadding uz_padding
Unscaled padding, for resize calculation.
uint smallest_x
Smallest horizontal size of the widget in a filled window.
AspectFlags aspect_flags
Which dimensions can be resized.
virtual void Draw(const Window *w)=0
Draw the widgets of the tree.
uint current_x
Current horizontal size (after resizing).
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
void SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Set additional space (padding) around the widget.
uint smallest_y
Smallest vertical size of the widget in a filled window.
virtual NWidgetBase * GetWidgetOfType(WidgetType tp)
Retrieve a widget by its type.
Definition widget.cpp:924
uint fill_y
Vertical fill stepsize (from initial size, 0 means not resizable).
uint resize_y
Vertical resize step (0 means not resizable).
RectPadding padding
Padding added to the widget. Managed by parent container widget. (parent container may swap left and ...
NWidgetBase(WidgetType tp)
Base class constructor.
Definition widget.cpp:852
uint current_y
Current vertical size (after resizing).
Baseclass for container widgets.
void Add(std::unique_ptr< NWidgetBase > &&wid)
Append widget wid to container.
Definition widget.cpp:1198
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1205
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1212
bool IsEmpty()
Return whether the container is empty.
std::vector< std::unique_ptr< NWidgetBase > > children
Child widgets in contaier.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1221
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:1176
Base class for a 'real' widget.
void SetToolTip(StringID tool_tip)
Set the tool tip of the nested widget.
Definition widget.cpp:1152
bool IsDisabled() const
Return whether the widget is disabled.
NWidgetDisplay disp_flags
Flags that affect display and interaction with the widget.
void SetTextStyle(TextColour colour, FontSize size)
Set the text style of the nested widget.
Definition widget.cpp:1142
uint32_t widget_data
Data of the widget.
void SetAlignment(StringAlignment align)
Set the text/image alignment of the nested widget.
Definition widget.cpp:1161
const WidgetID index
Index of the nested widget (-1 means 'not used').
StringAlignment align
Alignment of text/image within widget.
FontSize text_size
Size of text within widget.
NWidgetCore(WidgetType tp, Colours colour, WidgetID index, uint fill_x, uint fill_y, uint32_t widget_data, StringID tool_tip)
Initialization of a 'real' widget.
Definition widget.cpp:1115
void SetDataTip(uint32_t widget_data, StringID tool_tip)
Set data and tool tip of the nested widget.
Definition widget.cpp:1131
WidgetID scrollbar_index
Index of an attached scrollbar.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1171
TextColour text_colour
Colour of text within widget.
Colours colour
Colour of this widget.
void SetLowered(bool lowered)
Lower or raise the widget.
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1166
StringID tool_tip
Tooltip of the widget.
bool IsLowered() const
Return whether the widget is lowered.
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:1646
NWidgetHorizontalLTR(NWidContainerFlags flags=NC_NONE)
Horizontal left-to-right container widget.
Definition widget.cpp:1641
Horizontal container.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1462
NWidgetHorizontal(NWidContainerFlags flags=NC_NONE)
Horizontal container widget.
Definition widget.cpp:1458
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:1531
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:1380
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1358
const WidgetID index
If non-negative, index in the Window::widget_lookup.
Definition widget.cpp:1353
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1398
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2892
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition widget.cpp:2594
static Dimension resizebox_dimension
Cached size of a resizebox widget.
static Dimension shadebox_dimension
Cached size of a shadebox widget.
NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, uint32_t data, StringID tip)
Nested leaf widget.
Definition widget.cpp:2621
static Dimension closebox_dimension
Cached size of a closebox widget.
static Dimension stickybox_dimension
Cached size of a stickybox widget.
static Dimension defsizebox_dimension
Cached size of a defsizebox widget.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2721
static Dimension dropdown_dimension
Cached size of a dropdown widget.
static Dimension debugbox_dimension
Cached size of a debugbox widget.
bool ButtonHit(const Point &pt)
For a NWID_BUTTON_DROPDOWN, test whether pt refers to the button or to the drop-down.
Definition widget.cpp:3032
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2013
int count
Amount of valid elements.
void SetClicked(int clicked)
Sets the clicked element in the matrix.
Definition widget.cpp:1876
int GetCurrentElement() const
Get current element.
Definition widget.cpp:1926
const WidgetID index
If non-negative, index in the Window::widget_lookup.
Scrollbar * sb
The scrollbar we're associated with.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1931
int widget_w
The width of the child widget including inter spacing.
void SetScrollbar(Scrollbar *sb)
Assign a scrollbar to this matrix.
Definition widget.cpp:1917
void GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
Get the different offsets that are influenced by scrolling.
Definition widget.cpp:2068
int current_element
The element currently being processed.
int widgets_x
The number of visible widgets in horizontal direction.
int widget_h
The height of the child widget including inter spacing.
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:1953
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1977
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1983
int clicked
The currently clicked element.
void SetCount(int count)
Set the number of elements in this matrix.
Definition widget.cpp:1893
int widgets_y
The number of visible widgets in vertical direction.
Colours colour
Colour of this widget.
Container with pre/inter/post child space.
uint8_t gaps
Number of gaps between widgets.
NWidContainerFlags flags
Flags of the container.
uint8_t pip_pre
Amount of space before first widget.
uint8_t uz_pip_pre
Unscaled space before first widget.
uint8_t uz_pip_inter
Unscaled space between widgets.
uint8_t pip_ratio_pre
Ratio of remaining space before first widget.
void SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_rato_post)
Set additional pre/inter/post space for the container.
Definition widget.cpp:1450
void SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
Set additional pre/inter/post space for the container.
Definition widget.cpp:1430
uint8_t pip_post
Amount of space after last widget.
uint8_t pip_ratio_inter
Ratio of remaining space between widgets.
uint8_t pip_ratio_post
Ratio of remaining space after last widget.
uint8_t uz_pip_post
Unscaled space after last widget.
uint8_t pip_inter
Amount of space between widgets.
Base class for a resizable nested widget.
uint8_t uz_text_spacing
'Unscaled' text padding, stored for resize calculation.
bool absolute
Set if minimum size is fixed and should not be resized.
void SetMinimalSize(uint min_x, uint min_y)
Set minimal size of the widget.
Definition widget.cpp:996
bool UpdateSize(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1079
void SetAspect(float ratio, AspectFlags flags=AspectFlags::ResizeX)
Set desired aspect ratio of this widget.
Definition widget.cpp:965
uint min_x
Minimal horizontal size of only this widget.
FontSize uz_text_size
'Unscaled' font size, stored for resize calculation.
NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y)
Constructor for resizable nested widgets.
Definition widget.cpp:954
uint uz_min_x
Unscaled Minimal horizontal size of only this widget.
uint8_t uz_text_lines
'Unscaled' text lines, stored for resize calculation.
void SetFill(uint fill_x, uint fill_y)
Set the filling of the widget from initial size.
Definition widget.cpp:1035
void SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
Set minimal text lines for the widget.
Definition widget.cpp:1022
uint min_y
Minimal vertical size of only this widget.
uint uz_min_y
Unscaled Minimal vertical size of only this widget.
bool UpdateMultilineWidgetSize(const std::string &str, int max_lines)
Try to set optimum widget size for a multiline text widget.
Definition widget.cpp:1059
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:1100
bool UpdateVerticalSize(uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1093
void SetResize(uint resize_x, uint resize_y)
Set resize step of the widget.
Definition widget.cpp:1046
void SetMinimalSizeAbsolute(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1009
Nested widget to display and control a scrollbar in a window.
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2538
NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index)
Scrollbar widget.
Definition widget.cpp:2496
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2517
static Dimension horizontal_dimension
Cached size of horizontal scrollbar button.
static Dimension vertical_dimension
Cached size of vertical scrollbar button.
NWidgetSpacer(int width, int height)
Generic spacer widget.
Definition widget.cpp:1828
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1841
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1862
void SetDirty(const Window *w) const override
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:1857
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1845
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1834
Stacked widgets, widgets all occupying the same space in the window.
int shown_plane
Plane being displayed (for NWID_SELECTION only).
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1239
const WidgetID index
If non-negative, index in the Window::widget_lookup.
WidgetLookup * widget_lookup
Window's widget lookup, updated in SetDisplayedPlane().
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:1280
NWidgetStacked(WidgetID index)
Widgets stacked on top of each other.
Definition widget.cpp:1235
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1311
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition widget.cpp:1335
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1300
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1320
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:1725
NWidgetVertical(NWidContainerFlags flags=NC_NONE)
Vertical container widget.
Definition widget.cpp:1652
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1656
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition widget.cpp:2354
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2318
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2311
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition widget.cpp:2345
Scrollbar data structure.
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
void SetCount(size_t num)
Sets the number of elements in the list.
bool IsVertical() const
Is the scrollbar vertical or not?
bool UpdatePosition(int difference, ScrollbarStepping unit=SS_SMALL)
Updates the position of the first visible element by the given amount.
void SetCapacity(size_t capacity)
Set the capacity of visible elements.
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition widget.cpp:2377
@ SS_BIG
Step in cap units.
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition widget.cpp:2451
size_type GetCount() const
Gets the number of elements in the list.
EventState UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
Update the given list position as if it were on this scroll bar when the given keycode was pressed.
Definition widget.cpp:2398
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
void SetStepSize(size_t stepsize)
Set the distance to scroll when using the buttons or the wheel.
size_type pos
Index of first visible item of the list.
size_type GetPosition() const
Gets the position of the first visible element in the list.
static constexpr uint WD_CAPTION_HEIGHT
Minimum height of a title bar.
Definition window_gui.h:90
RectPadding defsizebox
Padding around image in defsizebox widget.
Definition window_gui.h:48
RectPadding closebox
Padding around image in closebox widget.
Definition window_gui.h:50
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:42
static constexpr uint WD_DROPDOWN_HEIGHT
Minimum height of a drop down widget.
Definition window_gui.h:91
RectPadding captiontext
Padding for text within caption widget.
Definition window_gui.h:51
RectPadding debugbox
Padding around image in debugbox widget.
Definition window_gui.h:47
RectPadding frametext
Padding inside frame with text.
Definition window_gui.h:43
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:28
RectPadding modalpopup
Spacing for popup warning/information windows.
Definition window_gui.h:54
RectPadding hscrollbar
Padding inside horizontal scrollbar buttons.
Definition window_gui.h:39
RectPadding shadebox
Padding around image in shadebox widget.
Definition window_gui.h:45
RectPadding resizebox
Padding around image in resizebox widget.
Definition window_gui.h:49
RectPadding vscrollbar
Padding inside vertical scrollbar buttons.
Definition window_gui.h:38
RectPadding imgbtn
Padding around image button image.
Definition window_gui.h:36
int vsep_normal
Normal vertical spacing.
Definition window_gui.h:60
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:62
int hsep_wide
Wide horizontal spacing.
Definition window_gui.h:64
static constexpr uint WD_CLOSEBOX_WIDTH
Minimum width of a close box widget.
Definition window_gui.h:89
RectPadding fullbevel
Always-scaled bevel thickness.
Definition window_gui.h:41
RectPadding inset
Padding inside inset container.
Definition window_gui.h:37
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:96
static constexpr uint WD_RESIZEBOX_WIDTH
Minimum width of a resize box widget.
Definition window_gui.h:88
static constexpr uint WD_STICKYBOX_WIDTH
Minimum width of a standard sticky box widget.
Definition window_gui.h:85
RectPadding matrix
Padding of WWT_MATRIX items.
Definition window_gui.h:44
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:63
RectPadding dropdownlist
Padding of complete drop down list.
Definition window_gui.h:53
static constexpr uint WD_DEBUGBOX_WIDTH
Minimum width of a standard debug box widget.
Definition window_gui.h:86
RectPadding stickybox
Padding around image in stickybox widget.
Definition window_gui.h:46
static constexpr uint WD_DEFSIZEBOX_WIDTH
Minimum width of a standard defsize box widget.
Definition window_gui.h:87
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:40
RectPadding dropdowntext
Padding of drop down list item.
Definition window_gui.h:52
static constexpr uint WD_SHADEBOX_WIDTH
Distances used in drawing widgets.
Definition window_gui.h:84
int hsep_indent
Width of identation for tree layouts.
Definition window_gui.h:65
Base class that provides memory initialization on dynamically created objects.
Colours _company_colours[MAX_COMPANIES]
NOSAVE: can be determined from company structs.
Functions related to companies.
Owner
Enum for all companies/owners.
@ MAX_COMPANIES
Maximum number of companies.
debug_inline constexpr bool HasFlag(const T x, const T y)
Checks if a value in a bitset enum is set.
Definition enum_type.hpp:58
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
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:704
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:922
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:851
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:456
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:657
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:114
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:988
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Definition gfx.cpp:1548
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:54
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
Definition gfx_func.h:166
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:18
StringAlignment
How to align the to-be drawn text.
Definition gfx_type.h:342
@ SA_TOP
Top align the text.
Definition gfx_type.h:348
@ SA_LEFT
Left align the text.
Definition gfx_type.h:343
@ SA_HOR_MASK
Mask for horizontal alignment.
Definition gfx_type.h:346
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:345
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:344
@ SA_VERT_MASK
Mask for vertical alignment.
Definition gfx_type.h:351
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:355
@ SA_BOTTOM
Bottom align the text.
Definition gfx_type.h:350
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:353
@ SA_VERT_CENTER
Vertically center the text.
Definition gfx_type.h:349
FontSize
Available font sizes.
Definition gfx_type.h:208
@ FS_BEGIN
First font.
Definition gfx_type.h:215
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:209
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:19
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:260
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:299
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
Definition gfx_type.h:300
std::unique_ptr< NWidgetBase > MakeWindowNWidgetTree(std::span< const NWidgetPart > nwid_parts, NWidgetStacked **shade_select)
Make a nested widget tree for a window from a parts array.
Definition widget.cpp:3316
std::unique_ptr< NWidgetBase > MakeNWidgets(std::span< const NWidgetPart > nwid_parts, std::unique_ptr< NWidgetBase > &&container)
Construct a nested widget tree from an array of parts.
Definition widget.cpp:3297
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:940
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
Definition gfx.cpp:1486
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:314
static const uint8_t PC_WHITE
White palette colour.
static const uint8_t PC_BLACK
Black palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
Types related to global configuration settings.
This file contains all sprite-related enums and defines.
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
Definition sprites.h:1532
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition sprites.h:1602
static const PaletteID PALETTE_NEWSPAPER
Recolour sprite for newspaper-greying.
Definition sprites.h:1604
Definition of base types and functions in a cross-platform compatible way.
The colour translation of GRF's strings.
static const uint8_t _string_colourmap[17]
Colour mapping for TextColour.
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
Functions related to OTTD's strings.
@ TD_LTR
Text is written left-to-right by default.
@ TD_RTL
Text is written right-to-left by default.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
GUISettings gui
settings related to the GUI
Point pos
logical mouse position
Definition gfx_type.h:125
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
bool scale_bevels
bevels are scaled with GUI scale.
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
StringAlignment align
Alignment of text/image.
uint32_t data
Data value of the widget.
StringID tooltip
Tooltip of the widget.
uint8_t post
Amount of space before/between/after child widgets.
uint8_t lines
Number of text lines.
uint8_t spacing
Extra spacing around lines.
FontSize size
Font size of text lines.
TextColour colour
TextColour for DrawString.
FontSize size
Font size of text.
Colours colour
Widget colour.
WidgetID index
Index of the widget.
Partial widget specification to allow NWidgets to be written nested.
WidgetType type
Type of the part.
Coordinates of a point in 2D.
Data stored about a string that can be modified in the GUI.
Padding dimensions to apply to each side of a Rect.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
int Height() const
Get height of Rect.
Rect Expand(int s) const
Copy and expand Rect by s pixels.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
Data structure for an opened window.
Definition window_gui.h:273
virtual void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
Update size and resize step of a widget in the window.
Definition window_gui.h:624
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:781
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:732
virtual void SetStringParameters(WidgetID widget) const
Initialize string parameters for a widget.
Definition window_gui.h:632
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
Definition window_gui.h:326
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:764
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
Definition window_gui.h:853
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
void DrawViewport() const
Draw the viewport of this window.
virtual void DrawWidget(const Rect &r, WidgetID widget) const
Draw the contents of a nested widget.
Definition window_gui.h:610
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:316
int left
x position of left edge of the window
Definition window_gui.h:309
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:563
int top
y position of top edge of the window
Definition window_gui.h:310
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:334
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:322
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:213
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:977
WindowFlags flags
Window flags.
Definition window_gui.h:300
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:321
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
Functions related to transparency.
TransparencyOptionBits _transparency_opt
The bits that should be transparent.
@ TO_TEXT
loading and cost/income text
@ TO_SIGNS
signs
uint TransparencyOptionBits
transparency option bits
NWidgetPartTextLines text_lines
Part with text line data.
NWidgetPartPIP pip
Part with pre/inter/post spaces.
NWidgetPartPaddings padding
Part with paddings.
NWidgetFunctionType * func_ptr
Part with a function call.
NWidContainerFlags cont_flags
Part with container flags.
NWidgetPartDataTip data_tip
Part with a data/tooltip.
NWidgetPartAlignment align
Part with internal alignment.
NWidgetPartAspect aspect
Part to set aspect ratio.
NWidgetPartTextStyle text_style
Part with text style data.
Point xy
Part with an x/y size.
NWidgetPartWidget widget
Part with a start of a widget.
void InitializeWindowViewport(Window *w, int x, int y, int width, int height, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize viewport of the window for use.
Definition viewport.cpp:222
Functions related to (drawing on) viewports.
static void DrawCloseBox(const Rect &r, Colours colour)
Draw a close box.
Definition widget.cpp:659
static void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str, StringAlignment align)
Draw a button with a dropdown (WWT_DROPDOWN and NWID_BUTTON_DROPDOWN).
Definition widget.cpp:710
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:283
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition widget.cpp:2468
static void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
Draw a defsize box.
Definition widget.cpp:620
bool IsContainerWidgetType(WidgetType tp)
Test if WidgetType is a container widget.
Definition widget.cpp:3240
static void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
Draw a NewGRF debug box.
Definition widget.cpp:631
static void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
Draw a sticky box.
Definition widget.cpp:609
static Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align)
Calculate x and y coordinates for an aligned object within a window.
Definition widget.cpp:106
void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
Draw a caption bar.
Definition widget.cpp:680
static void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
Draw a horizontal scrollbar.
Definition widget.cpp:505
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:54
static std::unique_ptr< NWidgetBase > MakeNWidget(const NWidgetPart &nwid)
Make NWidget from an NWidgetPart.
Definition widget.cpp:3169
static void DrawLabel(const Rect &r, WidgetType type, bool clicked, TextColour colour, StringID str, StringAlignment align, FontSize fs)
Draw the label-part of a widget.
Definition widget.cpp:357
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:66
static void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
Draw a shade box.
Definition widget.cpp:598
WidgetID GetWidgetFromPos(const Window *w, int x, int y)
Returns the index for the widget located at the given position relative to the window.
Definition widget.cpp:268
static void ApplyNWidgetPartAttribute(const NWidgetPart &nwid, NWidgetBase *dest)
Apply an attribute NWidgetPart to an NWidget.
Definition widget.cpp:3061
static void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
Draw an image button.
Definition widget.cpp:338
static bool IsAttributeWidgetPartType(WidgetType tp)
Test if (an NWidgetPart) WidgetType is an attribute widget part type.
Definition widget.cpp:3050
static void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint16_t data, uint resize_x, uint resize_y)
Draw a matrix widget.
Definition widget.cpp:405
static void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
Draw a frame widget.
Definition widget.cpp:545
static void DrawText(const Rect &r, TextColour colour, StringID str, StringAlignment align, FontSize fs)
Draw text.
Definition widget.cpp:374
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:244
static void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
Draw a resize box.
Definition widget.cpp:644
static void DrawInset(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
Draw an inset widget.
Definition widget.cpp:390
static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom, bool horizontal)
Compute the vertical position of the draggable part of scrollbar.
Definition widget.cpp:135
static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, int y, int mi, int ma)
Compute new position of the scrollbar after a click and updates the window flags.
Definition widget.cpp:180
static std::span< constNWidgetPart >::iterator MakeWidgetTree(std::span< const NWidgetPart >::iterator nwid_begin, std::span< const NWidgetPart >::iterator nwid_end, std::unique_ptr< NWidgetBase > &parent)
Build a nested widget tree by recursively filling containers with nested widgets read from their part...
Definition widget.cpp:3253
static void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
Draw a vertical scrollbar.
Definition widget.cpp:465
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:35
std::unique_ptr< NWidgetBase > MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
Make a number of rows with button-like graphics, for enabling/disabling each company.
Definition widget.cpp:3358
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ SZSP_BEGIN
First zero-size plane.
@ SZSP_VERTICAL
Display plane with zero size horizontally, and filling and resizing vertically.
@ ND_SHADE_GREY
Bit value of the 'shade to grey' flag.
@ ND_DROPDOWN_ACTIVE
Bit value of the 'dropdown active' flag.
@ NDB_SCROLLBAR_DOWN
Down-button is lowered bit.
@ ND_SHADE_DIMMED
Bit value of the 'dimmed colours' flag.
@ ND_NO_TRANSPARENCY
Bit value of the 'no transparency' flag.
@ NDB_SCROLLBAR_UP
Up-button is lowered bit.
@ ND_SCROLLBAR_BTN
Bit value of the 'scrollbar up' or 'scrollbar down' flag.
uint ComputeMaxSize(uint base, uint max_space, uint step)
Return the biggest possible size of a nested widget.
@ AWV_LEFT
Force the arrow to the left.
Definition widget_type.h:33
@ AWV_RIGHT
Force the arrow to the right.
Definition widget_type.h:34
@ AWV_DECREASE
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:31
@ AWV_INCREASE
Arrow to the right or in case of RTL to the left.
Definition widget_type.h:32
NWidContainerFlags
Nested widget container flags,.
@ NC_EQUALSIZE
Value of the NCB_EQUALSIZE flag.
@ NC_BIGFIRST
Value of the NCB_BIGFIRST flag.
static constexpr uint8_t MAT_COL_BITS
Number of bits for the number of columns in the matrix.
Definition widget_type.h:23
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h:46
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h:51
@ WPT_FILL
Widget part for specifying fill.
Definition widget_type.h:93
@ WPT_ALIGNMENT
Widget part for specifying text/image alignment.
Definition widget_type.h:99
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:52
@ WWT_PUSHBTN
Normal push-button (no toggle button) with custom drawing.
@ WWT_IMGBTN_2
(Toggle) Button with diff image when clicked
Definition widget_type.h:53
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WPT_MINSIZE
Widget part for specifying minimal size.
Definition widget_type.h:91
@ WWT_PUSHARROWBTN
Normal push-button (no toggle button) with arrow caption.
@ WWT_LABEL
Centered label.
Definition widget_type.h:57
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:83
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:79
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:71
@ WWT_ARROWBTN
(Toggle) Button with an arrow
Definition widget_type.h:54
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:75
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:55
@ WPT_SCROLLBAR
Widget part for attaching a scrollbar.
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:50
@ WPT_ASPECT
Widget part for sepcifying aspect ratio.
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:66
@ WPT_RESIZE
Widget part for specifying resizing.
Definition widget_type.h:90
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:59
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:61
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:85
@ WPT_TEXTSTYLE
Widget part for specifying text colour.
Definition widget_type.h:98
@ WPT_PADDING
Widget part for specifying a padding.
Definition widget_type.h:95
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:69
@ WWT_TEXTBTN_2
(Toggle) Button with diff text when clicked
Definition widget_type.h:56
@ WWT_FRAME
Frame.
Definition widget_type.h:60
@ WPT_MINTEXTLINES
Widget part for specifying minimal number of lines of text.
Definition widget_type.h:92
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:48
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition widget_type.h:72
@ WPT_ATTRIBUTE_END
End marker for attribute NWidgetPart types.
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:84
@ WPT_ENDCONTAINER
Widget part to denote end of a container.
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:68
@ WPT_PIPRATIO
Widget part for specifying pre/inter/post ratio for containers.
Definition widget_type.h:97
@ WPT_DATATIP
Widget part for specifying data and tooltip.
Definition widget_type.h:94
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:65
@ WPT_ATTRIBUTE_BEGIN
Begin marker for attribute NWidgetPart types.
Definition widget_type.h:89
@ WPT_PIPSPACE
Widget part for specifying pre/inter/post space for containers.
Definition widget_type.h:96
@ NWID_MATRIX
Matrix container.
Definition widget_type.h:78
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:82
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:70
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:58
@ NWID_HORIZONTAL_LTR
Horizontal container that doesn't change the order of the widgets for RTL languages.
Definition widget_type.h:76
@ WPT_FUNCTION
Widget part for calling a user function.
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition widget_type.h:63
@ NWID_LAYER
Layered widgets, all visible together.
Definition widget_type.h:81
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:80
static constexpr uint8_t MAT_ROW_BITS
Number of bits for the number of rows in the matrix.
Definition widget_type.h:27
std::map< WidgetID, class NWidgetBase * > WidgetLookup
Lookup between widget IDs and NWidget objects.
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition()
@ ST_RESIZE
Resize the nested widget tree.
@ ST_SMALLEST
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
static constexpr uint8_t MAT_ROW_START
Lowest bit of the number of rows.
Definition widget_type.h:26
static constexpr uint8_t MAT_COL_START
Bits of the WWT_MATRIX widget data.
Definition widget_type.h:22
@ RWV_SHOW_BEVEL
Bevel of resize box is shown.
Definition widget_type.h:39
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition window.cpp:75
Functions, definitions and such used only by the GUI.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:283
SortButtonState
State of a sort direction button.
Definition window_gui.h:218
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:220
@ SBS_OFF
Do not sort (with this button).
Definition window_gui.h:219
@ WF_WHITE_BORDER
Window white border counter bit mask.
Definition window_gui.h:236
@ WF_SIZING
Window is being resized.
Definition window_gui.h:233
@ WF_HIGHLIGHTED
Window has a widget that has a highlight.
Definition window_gui.h:237
@ WF_STICKY
Window is made sticky by user.
Definition window_gui.h:234
FrameFlags
Flags to describe the look of the frame.
Definition window_gui.h:24
@ FR_DARKENED
If set the background is darker, allows for lowered frames with normal background colour when used wi...
Definition window_gui.h:29
@ FR_BORDERONLY
Draw border only, no background.
Definition window_gui.h:27
@ FR_LOWERED
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
Definition window_gui.h:28
@ FR_TRANSPARENT
Makes the background transparent if set.
Definition window_gui.h:26
int WidgetID
Widget ID.
Definition window_type.h:18
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_MIN) When shifting right,...
Definition zoom_func.h:22
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107
ZoomLevel
All zoom levels we know.
Definition zoom_type.h:16
@ ZOOM_LVL_NORMAL
The normal zoom level.
Definition zoom_type.h:21