OpenTTD Source 20260401-master-g3efaeb0eea
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "company_func.h"
13#include "settings_gui.h"
14#include "strings_type.h"
15#include "window_gui.h"
16#include "viewport_func.h"
17#include "zoom_func.h"
18#include "strings_func.h"
19#include "transparency.h"
21#include "settings_type.h"
22#include "querystring_gui.h"
23
24#include "table/sprites.h"
25#include "table/strings.h"
27
28#include "safeguards.h"
29
31
32static std::string GetStringForWidget(const Window *w, const NWidgetCore *nwid, bool secondary = false)
33{
34 StringID stringid = nwid->GetString();
35 if (nwid->GetIndex() < 0) {
36 if (stringid == STR_NULL) return {};
37
38 return GetString(stringid + (secondary ? 1 : 0));
39 }
40
41 return w->GetWidgetString(nwid->GetIndex(), stringid + (secondary ? 1 : 0));
42}
43
49static inline RectPadding ScaleGUITrad(const RectPadding &r)
50{
51 return {(uint8_t)ScaleGUITrad(r.left), (uint8_t)ScaleGUITrad(r.top), (uint8_t)ScaleGUITrad(r.right), (uint8_t)ScaleGUITrad(r.bottom)};
52}
53
59static inline Dimension ScaleGUITrad(const Dimension &dim)
60{
61 return {(uint)ScaleGUITrad(dim.width), (uint)ScaleGUITrad(dim.height)};
62}
63
71{
72 Point offset;
73 Dimension d = GetSpriteSize(sprid, &offset, ZoomLevel::Normal);
74 d.width -= offset.x;
75 d.height -= offset.y;
76 return ScaleGUITrad(d);
77}
78
86{
88 uint x = std::max(d.width, d.height);
89 return {x, x};
90}
91
94
99{
104 if (_settings_client.gui.scale_bevels) {
106 } else {
108 }
123
129
131}
132
140static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align)
141{
142 Point p;
143 /* In case we have a RTL language we swap the alignment. */
144 if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
145 switch (align & SA_HOR_MASK) {
146 case SA_LEFT: p.x = r.left; break;
147 case SA_HOR_CENTER: p.x = CentreBounds(r.left, r.right, d.width); break;
148 case SA_RIGHT: p.x = r.right + 1 - d.width; break;
149 default: NOT_REACHED();
150 }
151 switch (align & SA_VERT_MASK) {
152 case SA_TOP: p.y = r.top; break;
153 case SA_VERT_CENTER: p.y = CentreBounds(r.top, r.bottom, d.height); break;
154 case SA_BOTTOM: p.y = r.bottom + 1 - d.height; break;
155 default: NOT_REACHED();
156 }
157 return p;
158}
159
169static std::pair<int, int> HandleScrollbarHittest(const Scrollbar *sb, int mi, int ma, bool horizontal)
170{
171 /* Base for reversion */
172 int rev_base = mi + ma;
173 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
174
175 mi += button_size; // now points to just after the up/left-button
176 ma -= button_size; // now points to just before the down/right-button
177
178 int count = sb->GetCount();
179 int cap = sb->GetCapacity();
180
181 if (count > cap) {
182 int height = ma + 1 - mi;
183 int slider_height = std::max(button_size, cap * height / count);
184 height -= slider_height;
185
186 mi += height * sb->GetPosition() / (count - cap);
187 ma = mi + slider_height - 1;
188 }
189
190 /* Reverse coordinates for RTL. */
191 if (horizontal && _current_text_dir == TD_RTL) return {rev_base - ma, rev_base - mi};
192
193 return {mi, ma};
194}
195
205static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, int y, int mi, int ma)
206{
207 int pos;
208 int button_size;
209 bool rtl = false;
210 bool changed = false;
211
212 if (sb->type == NWID_HSCROLLBAR) {
213 pos = x;
214 rtl = _current_text_dir == TD_RTL;
215 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
216 } else {
217 pos = y;
218 button_size = NWidgetScrollbar::GetVerticalDimension().height;
219 }
220 if (pos < mi + button_size) {
221 /* Pressing the upper button? */
223 if (_scroller_click_timeout <= 1) {
224 _scroller_click_timeout = 3;
225 changed = sb->UpdatePosition(rtl ? 1 : -1);
226 }
227 w->mouse_capture_widget = sb->GetIndex();
228 } else if (pos >= ma - button_size) {
229 /* Pressing the lower button? */
231
232 if (_scroller_click_timeout <= 1) {
233 _scroller_click_timeout = 3;
234 changed = sb->UpdatePosition(rtl ? -1 : 1);
235 }
236 w->mouse_capture_widget = sb->GetIndex();
237 } else {
238 auto [start, end] = HandleScrollbarHittest(sb, mi, ma, sb->type == NWID_HSCROLLBAR);
239
240 if (pos < start) {
241 changed = sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::Stepping::Big);
242 } else if (pos > end) {
243 changed = sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::Stepping::Big);
244 } else {
245 _scrollbar_start_pos = start - mi - button_size;
246 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
247 w->mouse_capture_widget = sb->GetIndex();
248 _cursorpos_drag_start = _cursor.pos;
249 }
250 }
251
252 if (changed) {
253 /* Position changed so refresh the window */
254 w->OnScrollbarScroll(sb->GetIndex());
255 w->SetDirty();
256 } else {
257 /* No change so only refresh this scrollbar */
258 sb->SetDirty(w);
259 }
260}
261
270void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
271{
272 int mi, ma;
273
274 if (nw->type == NWID_HSCROLLBAR) {
275 mi = nw->pos_x;
276 ma = nw->pos_x + nw->current_x;
277 } else {
278 mi = nw->pos_y;
279 ma = nw->pos_y + nw->current_y;
280 }
281 NWidgetScrollbar *scrollbar = dynamic_cast<NWidgetScrollbar*>(nw);
282 assert(scrollbar != nullptr);
283 ScrollbarClickPositioning(w, scrollbar, x, y, mi, ma);
284}
285
294WidgetID GetWidgetFromPos(const Window *w, int x, int y)
295{
296 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
297 return (nw != nullptr) ? nw->GetIndex() : INVALID_WIDGET;
298}
299
309void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
310{
311 if (flags.Test(FrameFlag::Transparent)) {
312 GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
313 } else {
314 assert(colour < COLOUR_END);
315
316 const PixelColour dark = GetColourGradient(colour, SHADE_DARK);
317 const PixelColour medium_dark = GetColourGradient(colour, SHADE_LIGHT);
318 const PixelColour medium_light = GetColourGradient(colour, SHADE_LIGHTER);
319 const PixelColour light = GetColourGradient(colour, SHADE_LIGHTEST);
320 PixelColour interior;
321
322 Rect outer = {left, top, right, bottom}; // Outside rectangle
323 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
324
325 if (flags.Test(FrameFlag::Lowered)) {
326 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark); // Left
327 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark); // Top
328 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light); // Right
329 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light); // Bottom
330 interior = (flags.Test(FrameFlag::Darkened) ? medium_dark : medium_light);
331 } else {
332 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light); // Left
333 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light); // Top
334 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark); // Right
335 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark); // Bottom
336 interior = medium_dark;
337 }
338 if (!flags.Test(FrameFlag::BorderOnly)) {
339 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior); // Inner
340 }
341 }
342}
343
344void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, StringAlignment align)
345{
346 Point offset;
347 Dimension d = GetSpriteSize(img, &offset);
348 d.width -= offset.x;
349 d.height -= offset.y;
350
351 Point p = GetAlignedPosition(r, d, align);
352 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
353}
354
364static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
365{
366 assert(img != 0);
367 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
368
369 if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2.
370 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
371}
372
384static inline void DrawImageTextButtons(const Rect &r, Colours colour, bool clicked, SpriteID img, TextColour text_colour, const std::string &text, StringAlignment align, FontSize fs)
385{
386 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
387
388 bool rtl = _current_text_dir == TD_RTL;
389 int image_width = img != 0 ? std::max<int>(GetSquareScaledSpriteSize(img).width, r.Shrink(WidgetDimensions::scaled.framerect).Height()) : 0;
390 Rect r_img = r.Shrink(WidgetDimensions::scaled.framerect).WithWidth(image_width, rtl);
391 Rect r_text = r.Shrink(WidgetDimensions::scaled.framerect).Indent(image_width + WidgetDimensions::scaled.hsep_wide, rtl);
392
393 if (img != 0) {
394 DrawSpriteIgnorePadding(img, PAL_NONE, r_img, SA_HOR_CENTER | (align & SA_VERT_MASK));
395 }
396
397 if (!text.empty()) {
398 Dimension d = GetStringBoundingBox(text, fs);
399 Point p = GetAlignedPosition(r_text, d, align);
400 DrawString(r_text.left, r_text.right, p.y, text, text_colour, align, false, fs);
401 }
402}
403
412static inline void DrawLabel(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
413{
414 if (str.empty()) return;
415
416 Dimension d = GetStringBoundingBox(str, fs);
417 Point p = GetAlignedPosition(r, d, align);
418 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
419}
420
429static inline void DrawText(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
430{
431 if (str.empty()) return;
432
433 Dimension d = GetStringBoundingBox(str, fs);
434 Point p = GetAlignedPosition(r, d, align);
435 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
436}
437
447static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
448{
450 if (!str.empty()) DrawString(r.Shrink(WidgetDimensions::scaled.inset), str, text_colour, align, false, fs);
451}
452
463static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
464{
465 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
466
467 int column_width; // Width of a single column in the matrix.
468 if (num_columns == 0) {
469 column_width = resize_x;
470 num_columns = r.Width() / column_width;
471 } else {
472 column_width = r.Width() / num_columns;
473 }
474
475 int row_height; // Height of a single row in the matrix.
476 if (num_rows == 0) {
477 row_height = resize_y;
478 num_rows = r.Height() / row_height;
479 } else {
480 row_height = r.Height() / num_rows;
481 }
482
483 PixelColour col = GetColourGradient(colour, SHADE_LIGHTER);
484
485 int x = r.left;
486 for (int ctr = num_columns; ctr > 1; ctr--) {
487 x += column_width;
488 GfxFillRect(x, r.top + WidgetDimensions::scaled.bevel.top, x + WidgetDimensions::scaled.bevel.left - 1, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
489 }
490
491 x = r.top;
492 for (int ctr = num_rows; ctr > 1; ctr--) {
493 x += row_height;
494 GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x, r.right - WidgetDimensions::scaled.bevel.right, x + WidgetDimensions::scaled.bevel.top - 1, col);
495 }
496
497 col = GetColourGradient(colour, SHADE_NORMAL);
498
499 x = r.left - 1;
500 for (int ctr = num_columns; ctr > 1; ctr--) {
501 x += column_width;
502 GfxFillRect(x - WidgetDimensions::scaled.bevel.right + 1, r.top + WidgetDimensions::scaled.bevel.top, x, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
503 }
504
505 x = r.top - 1;
506 for (int ctr = num_rows; ctr > 1; ctr--) {
507 x += row_height;
508 GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x - WidgetDimensions::scaled.bevel.bottom + 1, r.right - WidgetDimensions::scaled.bevel.right, x, col);
509 }
510}
511
521static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
522{
523 int height = NWidgetScrollbar::GetVerticalDimension().height;
524
525 /* draw up/down buttons */
526 DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
527 DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
528
529 PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
530 PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
531
532 /* draw "shaded" background */
533 Rect bg = r.Shrink(0, height);
534 GfxFillRect(bg, c2);
536
537 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
538 int left = r.left + r.Width() * 3 / 11; /* left track is positioned 3/11ths from the left */
539 int right = r.left + r.Width() * 8 / 11; /* right track is positioned 8/11ths from the left */
540 const uint8_t bl = WidgetDimensions::scaled.bevel.left;
541 const uint8_t br = WidgetDimensions::scaled.bevel.right;
542
543 /* draw shaded lines */
544 GfxFillRect(bg.WithX(left - bl, left - 1), c1);
545 GfxFillRect(bg.WithX(left, left + br - 1), c2);
546 GfxFillRect(bg.WithX(right - bl, right - 1), c1);
547 GfxFillRect(bg.WithX(right, right + br - 1), c2);
548
549 auto [top, bottom] = HandleScrollbarHittest(scrollbar, r.top, r.bottom, false);
550 DrawFrameRect(r.left, top, r.right, bottom, colour, bar_dragged ? FrameFlag::Lowered : FrameFlags{});
551}
552
562static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
563{
564 int width = NWidgetScrollbar::GetHorizontalDimension().width;
565
566 DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
567 DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
568
569 PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
570 PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
571
572 /* draw "shaded" background */
573 Rect bg = r.Shrink(width, 0);
574 GfxFillRect(bg, c2);
576
577 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
578 int top = r.top + r.Height() * 3 / 11; /* top track is positioned 3/11ths from the top */
579 int bottom = r.top + r.Height() * 8 / 11; /* bottom track is positioned 8/11ths from the top */
580 const uint8_t bt = WidgetDimensions::scaled.bevel.top;
581 const uint8_t bb = WidgetDimensions::scaled.bevel.bottom;
582
583 /* draw shaded lines */
584 GfxFillRect(bg.WithY(top - bt, top - 1), c1);
585 GfxFillRect(bg.WithY(top, top + bb - 1), c2);
586 GfxFillRect(bg.WithY(bottom - bt, bottom - 1), c1);
587 GfxFillRect(bg.WithY(bottom, bottom + bb - 1), c2);
588
589 /* draw actual scrollbar */
590 auto [left, right] = HandleScrollbarHittest(scrollbar, r.left, r.right, true);
591 DrawFrameRect(left, r.top, right, r.bottom, colour, bar_dragged ? FrameFlag::Lowered : FrameFlags{});
592}
593
603static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
604{
605 int x2 = r.left; // by default the left side is the left side of the widget
606
607 if (!str.empty()) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs);
608
609 PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
610 PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
611
612 /* If the frame has text, adjust the top bar to fit half-way through */
613 Rect inner = r.Shrink(ScaleGUITrad(1));
614 if (!str.empty()) inner.top = r.top + GetCharacterHeight(FS_NORMAL) / 2;
615
616 Rect outer = inner.Expand(WidgetDimensions::scaled.bevel);
617 Rect inside = inner.Shrink(WidgetDimensions::scaled.bevel);
618
619 if (_current_text_dir == TD_LTR) {
620 /* Line from upper left corner to start of text */
621 GfxFillRect(outer.left, outer.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
622 GfxFillRect(inner.left, inner.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
623
624 /* Line from end of text to upper right corner */
625 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
626 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
627 } else {
628 /* Line from upper left corner to start of text */
629 GfxFillRect(outer.left, outer.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
630 GfxFillRect(inner.left, inner.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
631
632 /* Line from end of text to upper right corner */
633 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
634 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
635 }
636
637 /* Line from upper left corner to bottom left corner */
638 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
639 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
640
641 /* Line from upper right corner to bottom right corner */
642 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
643 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
644
645 /* Line from bottom left corner to bottom right corner */
646 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
647 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
648}
649
656static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
657{
658 DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER);
659}
660
667static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
668{
669 DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER);
670}
671
678static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
679{
680 DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER);
681}
682
689static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
690{
691 DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER);
692}
693
702static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
703{
704 if (bevel) {
705 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
706 } else if (clicked) {
707 GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(colour, SHADE_LIGHTER));
708 }
709 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));
710}
711
717static inline void DrawCloseBox(const Rect &r, Colours colour)
718{
719 if (colour != COLOUR_WHITE) DrawFrameRect(r, colour, {});
720 Point offset;
721 Dimension d = GetSpriteSize(SPR_CLOSEBOX, &offset);
722 d.width -= offset.x;
723 d.height -= offset.y;
724 int s = ScaleSpriteTrad(1); // Offset to account for shadow of SPR_CLOSEBOX.
725 DrawSprite(SPR_CLOSEBOX, (colour != COLOUR_WHITE ? TC_BLACK : TC_SILVER) | (1U << PALETTE_TEXT_RECOLOUR), CentreBounds(r.left, r.right, d.width - s) - offset.x, CentreBounds(r.top, r.bottom, d.height - s) - offset.y);
726}
727
738void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
739{
740 bool company_owned = owner < MAX_COMPANIES;
741
745
746 if (company_owned) {
748 }
749
750 if (str.empty()) return;
751
753 Point p = GetAlignedPosition(r, d, align);
754 DrawString(r.left + WidgetDimensions::scaled.captiontext.left, r.right - WidgetDimensions::scaled.captiontext.left, p.y, str, text_colour, align, false, fs);
755}
756
768static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, std::string_view str, StringAlignment align)
769{
770 bool rtl = _current_text_dir == TD_RTL;
771
772 Rect text = r.Indent(NWidgetLeaf::dropdown_dimension.width, !rtl);
773 DrawFrameRect(text, colour, clicked_button ? FrameFlag::Lowered : FrameFlags{});
774 if (!str.empty()) {
775 text = text.CentreToHeight(GetCharacterHeight(FS_NORMAL)).Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero);
776 DrawString(text, str, TC_BLACK, align);
777 }
778
779 Rect button = r.WithWidth(NWidgetLeaf::dropdown_dimension.width, !rtl);
780 DrawImageButtons(button, WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
781}
782
787{
788 this->nested_root->Draw(this);
789
790 if (this->flags.Test(WindowFlag::WhiteBorder)) {
791 DrawFrameRect(0, 0, this->width - 1, this->height - 1, COLOUR_WHITE, FrameFlag::BorderOnly);
792 }
793
794 if (this->flags.Test(WindowFlag::Highlighted)) {
795 extern bool _window_highlight_colour;
796 for (const auto &pair : this->widget_lookup) {
797 const NWidgetBase *widget = pair.second;
798 if (!widget->IsHighlighted()) continue;
799
800 Rect outer = widget->GetCurrentRect();
801 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1);
802
804
805 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
806 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
807 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
808 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
809 }
810 }
811}
812
819{
820 if (state == SBS_OFF) return;
821
822 assert(!this->widget_lookup.empty());
823 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
824
825 /* Sort button uses the same sprites as vertical scrollbar */
826 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
827
828 DrawSpriteIgnorePadding(state == SBS_DOWN ? SPR_ARROW_DOWN : SPR_ARROW_UP, PAL_NONE, r.WithWidth(dim.width, _current_text_dir == TD_LTR), SA_CENTER);
829}
830
836{
837 return NWidgetScrollbar::GetVerticalDimension().width + 1;
838}
839
840bool _draw_widget_outlines;
841
842static void DrawOutline(const Window *, const NWidgetBase *wid)
843{
844 if (!_draw_widget_outlines || wid->current_x == 0 || wid->current_y == 0) return;
845
846 DrawRectOutline(wid->GetCurrentRect(), PC_WHITE, 1, 4);
847}
848
901
915
929
936{
937 if (this->index >= 0) widget_lookup[this->index] = this;
938}
939
946
951void NWidgetBase::SetDirty(const Window *w) const
952{
953 int abs_left = w->left + this->pos_x;
954 int abs_top = w->top + this->pos_y;
955 AddDirtyBlock(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
956}
957
965
972{
973 return (this->type == tp) ? this : nullptr;
974}
975
976void NWidgetBase::ApplyAspectRatio()
977{
978 if (this->aspect_ratio == 0) return;
979 if (this->smallest_x == 0 || this->smallest_y == 0) return;
980
981 uint x = this->smallest_x;
982 uint y = this->smallest_y;
983 if (this->aspect_flags.Test(AspectFlag::ResizeX)) x = std::max(this->smallest_x, static_cast<uint>(this->smallest_y * std::abs(this->aspect_ratio)));
984 if (this->aspect_flags.Test(AspectFlag::ResizeY)) y = std::max(this->smallest_y, static_cast<uint>(this->smallest_x / std::abs(this->aspect_ratio)));
985
986 this->smallest_x = x;
987 this->smallest_y = y;
988}
989
994
1003{
1004 this->fill_x = fill_x;
1005 this->fill_y = fill_y;
1006}
1007
1013void NWidgetResizeBase::SetAspect(float ratio, AspectFlags flags)
1014{
1015 this->aspect_ratio = ratio;
1016 this->aspect_flags = flags;
1017}
1018
1025void NWidgetResizeBase::SetAspect(int x_ratio, int y_ratio, AspectFlags flags)
1026{
1027 this->SetAspect(static_cast<float>(x_ratio) / static_cast<float>(y_ratio), flags);
1028}
1029
1031{
1032 if (!this->absolute) {
1033 this->min_x = ScaleGUITrad(this->uz_min_x);
1034 this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1035 if (this->toolbar_size > 0) {
1036 this->min_x = std::max(this->min_x, this->toolbar_size * _toolbar_image_size.width + WidgetDimensions::scaled.imgbtn.Horizontal());
1037 this->min_y = std::max(this->min_y, _toolbar_image_size.height + WidgetDimensions::scaled.imgbtn.Vertical());
1038 }
1039 }
1041}
1042
1049{
1050 this->uz_min_x = std::max(this->uz_min_x, min_x);
1051 this->uz_min_y = std::max(this->uz_min_y, min_y);
1052}
1053
1059{
1060 this->toolbar_size = toolbar_size;
1061}
1062
1069{
1070 this->absolute = true;
1071 this->min_x = std::max(this->min_x, min_x);
1072 this->min_y = std::max(this->min_y, min_y);
1073}
1074
1081void NWidgetResizeBase::SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
1082{
1083 this->uz_text_lines = min_lines;
1084 this->uz_text_spacing = spacing;
1085 this->uz_text_size = size;
1086}
1087
1094{
1095 this->fill_x = fill_x;
1096 this->fill_y = fill_y;
1097}
1098
1105{
1106 this->resize_x = resize_x;
1107 this->resize_y = resize_y;
1108}
1109
1117bool NWidgetResizeBase::UpdateMultilineWidgetSize(const std::string &str, int max_lines)
1118{
1119 int y = GetStringHeight(str, this->current_x);
1120 if (y > max_lines * GetCharacterHeight(FS_NORMAL)) {
1121 /* Text at the current width is too tall, so try to guess a better width. */
1123 d.height *= max_lines;
1124 d.width /= 2;
1125 return this->UpdateSize(d.width, d.height);
1126 }
1127 return this->UpdateVerticalSize(y);
1128}
1129
1138{
1139 if (min_x == this->min_x && min_y == this->min_y) return false;
1140 this->min_x = min_x;
1141 this->min_y = min_y;
1142 return true;
1143}
1144
1152{
1153 if (min_y == this->min_y) return false;
1154 this->min_y = min_y;
1155 return true;
1156}
1157
1158void NWidgetResizeBase::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1159{
1160 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1161}
1162
1174{
1175 this->colour = colour;
1176 this->widget_data = widget_data;
1177 this->SetToolTip(tool_tip);
1178 this->text_colour = tp == WWT_CAPTION ? TC_WHITE : TC_BLACK;
1179}
1180
1186{
1187 this->widget_data.string = string;
1188}
1189
1196{
1197 this->SetString(string);
1198 this->SetToolTip(tool_tip);
1199}
1200
1206{
1207 this->widget_data.sprite = sprite;
1208}
1209
1216{
1217 this->SetSprite(sprite);
1218 this->SetToolTip(tool_tip);
1219}
1220
1226void NWidgetCore::SetMatrixDimension(uint32_t columns, uint32_t rows)
1227{
1228 this->widget_data.matrix = { columns, rows };
1229}
1230
1236{
1237 this->widget_data.resize_widget_type = type;
1238}
1239
1246{
1247 this->text_colour = colour;
1248 this->text_size = size;
1249}
1250
1256{
1257 this->tool_tip = tool_tip;
1258}
1259
1265{
1266 return this->tool_tip;
1267}
1268
1274{
1275 this->align = align;
1276}
1277
1283{
1284 return this->widget_data.string;
1285}
1286
1292{
1293 return this->scrollbar_index;
1294}
1295
1297{
1298 return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : nullptr;
1299}
1300
1302{
1303 if (this->type == tp) return this;
1304 for (const auto &child_wid : this->children) {
1305 NWidgetBase *nwid = child_wid->GetWidgetOfType(tp);
1306 if (nwid != nullptr) return nwid;
1307 }
1308 return nullptr;
1309}
1310
1312{
1313 for (const auto &child_wid : this->children) {
1314 child_wid->AdjustPaddingForZoom();
1315 }
1317}
1318
1323void NWidgetContainer::Add(std::unique_ptr<NWidgetBase> &&wid)
1324{
1325 assert(wid != nullptr);
1326 wid->parent = this;
1327 this->children.push_back(std::move(wid));
1328}
1329
1331{
1332 this->NWidgetBase::FillWidgetLookup(widget_lookup);
1333 for (const auto &child_wid : this->children) {
1334 child_wid->FillWidgetLookup(widget_lookup);
1335 }
1336}
1337
1339{
1340 for (const auto &child_wid : this->children) {
1341 child_wid->Draw(w);
1342 }
1343
1344 DrawOutline(w, this);
1345}
1346
1348{
1349 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1350
1351 for (const auto &child_wid : this->children) {
1352 NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
1353 if (nwid != nullptr) return nwid;
1354 }
1355 return nullptr;
1356}
1357
1359{
1360 /* Zero size plane selected */
1361 if (this->shown_plane >= SZSP_BEGIN) {
1362 Dimension size = {0, 0};
1363 Dimension padding = {0, 0};
1364 Dimension fill = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1365 Dimension resize = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1366 /* Here we're primarily interested in the value of resize */
1367 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
1368
1369 this->smallest_x = size.width;
1370 this->smallest_y = size.height;
1371 this->fill_x = fill.width;
1372 this->fill_y = fill.height;
1373 this->resize_x = resize.width;
1374 this->resize_y = resize.height;
1375 this->ApplyAspectRatio();
1376 return;
1377 }
1378
1379 /* First sweep, recurse down and compute minimal size and filling. */
1380 this->smallest_x = 0;
1381 this->smallest_y = 0;
1382 this->fill_x = this->IsEmpty() ? 0 : 1;
1383 this->fill_y = this->IsEmpty() ? 0 : 1;
1384 this->resize_x = this->IsEmpty() ? 0 : 1;
1385 this->resize_y = this->IsEmpty() ? 0 : 1;
1386 for (const auto &child_wid : this->children) {
1387 child_wid->SetupSmallestSize(w);
1388
1389 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1390 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1391 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1392 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1393 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1394 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1395 this->ApplyAspectRatio();
1396 }
1397}
1398
1399void NWidgetStacked::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1400{
1401 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1402 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1403
1404 if (this->shown_plane >= SZSP_BEGIN) return;
1405
1406 for (const auto &child_wid : this->children) {
1407 uint hor_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1408 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1409 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1410
1411 uint vert_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1412 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1413 uint child_pos_y = child_wid->padding.top;
1414
1415 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1416 }
1417}
1418
1420{
1421 /* We need to update widget_lookup later. */
1422 this->widget_lookup = &widget_lookup;
1423
1424 this->NWidgetContainer::FillWidgetLookup(widget_lookup);
1425 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1426 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(widget_lookup);
1427}
1428
1430{
1431 if (this->shown_plane >= SZSP_BEGIN) return;
1432
1433 assert(static_cast<size_t>(this->shown_plane) < this->children.size());
1434 this->children[shown_plane]->Draw(w);
1435 DrawOutline(w, this);
1436}
1437
1439{
1440 if (this->shown_plane >= SZSP_BEGIN) return nullptr;
1441
1442 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1443
1444 if (static_cast<size_t>(this->shown_plane) >= this->children.size()) return nullptr;
1445 return this->children[shown_plane]->GetWidgetFromPos(x, y);
1446}
1447
1454{
1455 if (this->shown_plane == plane) return false;
1456 this->shown_plane = plane;
1457 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1458 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(*this->widget_lookup);
1459 return true;
1460}
1461
1462class NWidgetLayer : public NWidgetContainer {
1463public:
1464 NWidgetLayer(WidgetID index) : NWidgetContainer(NWID_LAYER, index) {}
1465
1466 void SetupSmallestSize(Window *w) override;
1467 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override;
1468
1469 void Draw(const Window *w) override;
1470};
1471
1473{
1474 /* First sweep, recurse down and compute minimal size and filling. */
1475 this->smallest_x = 0;
1476 this->smallest_y = 0;
1477 this->fill_x = this->IsEmpty() ? 0 : 1;
1478 this->fill_y = this->IsEmpty() ? 0 : 1;
1479 this->resize_x = this->IsEmpty() ? 0 : 1;
1480 this->resize_y = this->IsEmpty() ? 0 : 1;
1481 for (const auto &child_wid : this->children) {
1482 child_wid->SetupSmallestSize(w);
1483
1484 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1485 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1486 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1487 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1488 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1489 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1490 this->ApplyAspectRatio();
1491 }
1492}
1493
1494void NWidgetLayer::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1495{
1496 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1497 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1498
1499 for (const auto &child_wid : this->children) {
1500 uint hor_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1501 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1502 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1503
1504 uint vert_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1505 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1506 uint child_pos_y = child_wid->padding.top;
1507
1508 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1509 }
1510}
1511
1513{
1514 /* Draw in reverse order, as layers are arranged top-down. */
1515 for (auto it = std::rbegin(this->children); it != std::rend(this->children); ++it) {
1516 (*it)->Draw(w);
1517 }
1518
1519 DrawOutline(w, this);
1520}
1521
1529
1540{
1541 this->uz_pip_pre = pip_pre;
1542 this->uz_pip_inter = pip_inter;
1543 this->uz_pip_post = pip_post;
1544
1545 this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1546 this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1547 this->pip_post = ScaleGUITrad(this->uz_pip_post);
1548}
1549
1560{
1561 this->pip_ratio_pre = pip_ratio_pre;
1562 this->pip_ratio_inter = pip_ratio_inter;
1563 this->pip_ratio_post = pip_ratio_post;
1564}
1565
1567{
1568 this->smallest_x = 0; // Sum of minimal size of all children.
1569 this->smallest_y = 0; // Biggest child.
1570 this->fill_x = 0; // smallest non-zero child widget fill step.
1571 this->fill_y = 1; // smallest common child fill step.
1572 this->resize_x = 0; // smallest non-zero child widget resize step.
1573 this->resize_y = 1; // smallest common child resize step.
1574 this->gaps = 0;
1575
1576 /* 1a. Forward call, collect longest/widest child length. */
1577 uint longest = 0; // Longest child found.
1578 uint max_vert_fill = 0; // Biggest vertical fill step.
1579 for (const auto &child_wid : this->children) {
1580 child_wid->SetupSmallestSize(w);
1581 longest = std::max(longest, child_wid->smallest_x);
1582 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(SizingType::Smallest));
1583 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1584 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->gaps++;
1585 }
1586 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1587 /* 1b. Make the container higher if needed to accommodate all children nicely. */
1588 [[maybe_unused]] uint max_smallest = this->smallest_y + 3 * max_vert_fill; // Upper limit to computing smallest height.
1589 uint cur_height = this->smallest_y;
1590 for (;;) {
1591 for (const auto &child_wid : this->children) {
1592 uint step_size = child_wid->GetVerticalStepSize(SizingType::Smallest);
1593 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1594 if (step_size > 1 && child_height < cur_height) { // Small step sizes or already fitting children are not interesting.
1595 uint remainder = (cur_height - child_height) % step_size;
1596 if (remainder > 0) { // Child did not fit entirely, widen the container.
1597 cur_height += step_size - remainder;
1598 assert(cur_height < max_smallest); // Safeguard against infinite height expansion.
1599 /* Remaining children will adapt to the new cur_height, thus speeding up the computation. */
1600 }
1601 }
1602 }
1603 if (this->smallest_y == cur_height) break;
1604 this->smallest_y = cur_height; // Smallest height got changed, try again.
1605 }
1606 /* 2. For containers that must maintain equal width, extend child minimal size. */
1607 for (const auto &child_wid : this->children) {
1608 child_wid->smallest_y = this->smallest_y - child_wid->padding.Vertical();
1609 child_wid->ApplyAspectRatio();
1610 longest = std::max(longest, child_wid->smallest_x);
1611 }
1612 if (this->flags.Test(NWidContainerFlag::EqualSize)) {
1613 for (const auto &child_wid : this->children) {
1614 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1615 }
1616 }
1617 /* 3. Compute smallest, fill, and resize values of the container. */
1618 for (const auto &child_wid : this->children) {
1619 this->smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1620 if (child_wid->fill_x > 0) {
1621 if (this->fill_x == 0 || this->fill_x > child_wid->fill_x) this->fill_x = child_wid->fill_x;
1622 }
1623 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1624
1625 if (child_wid->resize_x > 0) {
1626 if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x;
1627 }
1628 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1629 }
1630 if (this->fill_x == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_x = 1;
1631 /* 4. Increase by required PIP space. */
1632 this->smallest_x += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1633}
1634
1635void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1636{
1637 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1638
1639 /* Compute additional width given to us. */
1640 uint additional_length = given_width - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1641 for (const auto &child_wid : this->children) {
1642 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1643 }
1644
1645 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1646
1647 /* In principle, the additional horizontal space is distributed evenly over the available resizable children. Due to step sizes, this may not always be feasible.
1648 * To make resizing work as good as possible, first children with biggest step sizes are done. These may get less due to rounding down.
1649 * 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
1650 * of the child with the smallest non-zero stepsize.
1651 *
1652 * 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
1653 * size and position, and directly call child->AssignSizePosition() with the computed values.
1654 * 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
1655 * then we call the child.
1656 */
1657
1658 /* First loop: Find biggest stepsize, find number of children that want a piece of the pie, handle vertical size for all children,
1659 * handle horizontal size for non-resizing children.
1660 */
1661 int num_changing_childs = 0; // Number of children that can change size.
1662 uint biggest_stepsize = 0;
1663 for (const auto &child_wid : this->children) {
1664 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1665 if (hor_step > 0) {
1666 if (!flags.Test(NWidContainerFlag::BigFirst)) num_changing_childs++;
1667 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1668 } else {
1669 child_wid->current_x = child_wid->smallest_x;
1670 }
1671
1672 uint vert_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1673 child_wid->current_y = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1674 }
1675
1676 /* First.5 loop: count how many children are of the biggest step size. */
1677 if (flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1678 for (const auto &child_wid : this->children) {
1679 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1680 if (hor_step == biggest_stepsize) {
1681 num_changing_childs++;
1682 }
1683 }
1684 }
1685
1686 /* Second loop: Allocate the additional horizontal space over the resizing children, starting with the biggest resize steps. */
1687 while (biggest_stepsize > 0) {
1688 uint next_biggest_stepsize = 0;
1689 for (const auto &child_wid : this->children) {
1690 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1691 if (hor_step > biggest_stepsize) continue; // Already done
1692 if (hor_step == biggest_stepsize) {
1693 uint increment = additional_length / num_changing_childs;
1694 num_changing_childs--;
1695 if (hor_step > 1) increment -= increment % hor_step;
1696 child_wid->current_x = child_wid->smallest_x + increment;
1697 additional_length -= increment;
1698 continue;
1699 }
1700 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1701 }
1702 biggest_stepsize = next_biggest_stepsize;
1703
1704 if (num_changing_childs == 0 && flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1705 /* Second.5 loop: count how many children are of the updated biggest step size. */
1706 for (const auto &child_wid : this->children) {
1707 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1708 if (hor_step == biggest_stepsize) {
1709 num_changing_childs++;
1710 }
1711 }
1712 }
1713 }
1714 assert(num_changing_childs == 0);
1715
1716 uint pre = this->pip_pre;
1717 uint inter = this->pip_inter;
1718
1719 if (additional_length > 0) {
1720 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1721 * which is never explicitly needed. */
1722 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1723 if (r > 0) {
1724 pre += this->pip_ratio_pre * additional_length / r;
1725 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1726 }
1727 }
1728
1729 /* Third loop: Compute position and call the child. */
1730 uint position = rtl ? this->current_x - pre : pre; // Place to put next child relative to origin of the container.
1731 for (const auto &child_wid : this->children) {
1732 uint child_width = child_wid->current_x;
1733 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1734 uint child_y = y + child_wid->padding.top;
1735
1736 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1737 if (child_wid->current_x != 0) {
1738 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1739 position = rtl ? position - padded_child_width : position + padded_child_width;
1740 }
1741 }
1742}
1743
1744void NWidgetHorizontalLTR::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1745{
1746 NWidgetHorizontal::AssignSizePosition(sizing, x, y, given_width, given_height, false);
1747}
1748
1750{
1751 this->smallest_x = 0; // Biggest child.
1752 this->smallest_y = 0; // Sum of minimal size of all children.
1753 this->fill_x = 1; // smallest common child fill step.
1754 this->fill_y = 0; // smallest non-zero child widget fill step.
1755 this->resize_x = 1; // smallest common child resize step.
1756 this->resize_y = 0; // smallest non-zero child widget resize step.
1757 this->gaps = 0;
1758
1759 /* 1a. Forward call, collect longest/widest child length. */
1760 uint highest = 0; // Highest child found.
1761 uint max_hor_fill = 0; // Biggest horizontal fill step.
1762 for (const auto &child_wid : this->children) {
1763 child_wid->SetupSmallestSize(w);
1764 highest = std::max(highest, child_wid->smallest_y);
1765 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(SizingType::Smallest));
1766 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1767 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->gaps++;
1768 }
1769 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1770 /* 1b. Make the container wider if needed to accommodate all children nicely. */
1771 [[maybe_unused]] uint max_smallest = this->smallest_x + 3 * max_hor_fill; // Upper limit to computing smallest height.
1772 uint cur_width = this->smallest_x;
1773 for (;;) {
1774 for (const auto &child_wid : this->children) {
1775 uint step_size = child_wid->GetHorizontalStepSize(SizingType::Smallest);
1776 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1777 if (step_size > 1 && child_width < cur_width) { // Small step sizes or already fitting children are not interesting.
1778 uint remainder = (cur_width - child_width) % step_size;
1779 if (remainder > 0) { // Child did not fit entirely, widen the container.
1780 cur_width += step_size - remainder;
1781 assert(cur_width < max_smallest); // Safeguard against infinite width expansion.
1782 /* Remaining children will adapt to the new cur_width, thus speeding up the computation. */
1783 }
1784 }
1785 }
1786 if (this->smallest_x == cur_width) break;
1787 this->smallest_x = cur_width; // Smallest width got changed, try again.
1788 }
1789 /* 2. For containers that must maintain equal width, extend children minimal size. */
1790 for (const auto &child_wid : this->children) {
1791 child_wid->smallest_x = this->smallest_x - child_wid->padding.Horizontal();
1792 child_wid->ApplyAspectRatio();
1793 highest = std::max(highest, child_wid->smallest_y);
1794 }
1795 if (this->flags.Test(NWidContainerFlag::EqualSize)) {
1796 for (const auto &child_wid : this->children) {
1797 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1798 }
1799 }
1800 /* 3. Compute smallest, fill, and resize values of the container. */
1801 for (const auto &child_wid : this->children) {
1802 this->smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1803 if (child_wid->fill_y > 0) {
1804 if (this->fill_y == 0 || this->fill_y > child_wid->fill_y) this->fill_y = child_wid->fill_y;
1805 }
1806 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1807
1808 if (child_wid->resize_y > 0) {
1809 if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y;
1810 }
1811 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1812 }
1813 if (this->fill_y == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_y = 1;
1814 /* 4. Increase by required PIP space. */
1815 this->smallest_y += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1816}
1817
1818void NWidgetVertical::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1819{
1820 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1821
1822 /* Compute additional height given to us. */
1823 uint additional_length = given_height - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1824 for (const auto &child_wid : this->children) {
1825 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1826 }
1827
1828 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1829
1830 /* Like the horizontal container, the vertical container also distributes additional height evenly, starting with the children with the biggest resize steps.
1831 * It also stores computed widths and heights into current_x and current_y values of the child.
1832 */
1833
1834 /* 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. */
1835 int num_changing_childs = 0; // Number of children that can change size.
1836 uint biggest_stepsize = 0;
1837 for (const auto &child_wid : this->children) {
1838 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1839 if (vert_step > 0) {
1840 if (!flags.Test(NWidContainerFlag::BigFirst)) num_changing_childs++;
1841 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1842 } else {
1843 child_wid->current_y = child_wid->smallest_y;
1844 }
1845
1846 uint hor_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1847 child_wid->current_x = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1848 }
1849
1850 /* First.5 loop: count how many children are of the biggest step size. */
1851 if (this->flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1852 for (const auto &child_wid : this->children) {
1853 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1854 if (vert_step == biggest_stepsize) {
1855 num_changing_childs++;
1856 }
1857 }
1858 }
1859
1860 /* Second loop: Allocate the additional vertical space over the resizing children, starting with the biggest resize steps. */
1861 while (biggest_stepsize > 0) {
1862 uint next_biggest_stepsize = 0;
1863 for (const auto &child_wid : this->children) {
1864 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1865 if (vert_step > biggest_stepsize) continue; // Already done
1866 if (vert_step == biggest_stepsize) {
1867 uint increment = additional_length / num_changing_childs;
1868 num_changing_childs--;
1869 if (vert_step > 1) increment -= increment % vert_step;
1870 child_wid->current_y = child_wid->smallest_y + increment;
1871 additional_length -= increment;
1872 continue;
1873 }
1874 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1875 }
1876 biggest_stepsize = next_biggest_stepsize;
1877
1878 if (num_changing_childs == 0 && flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1879 /* Second.5 loop: count how many children are of the updated biggest step size. */
1880 for (const auto &child_wid : this->children) {
1881 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1882 if (vert_step == biggest_stepsize) {
1883 num_changing_childs++;
1884 }
1885 }
1886 }
1887 }
1888 assert(num_changing_childs == 0);
1889
1890 uint pre = this->pip_pre;
1891 uint inter = this->pip_inter;
1892
1893 if (additional_length > 0) {
1894 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1895 * which is never explicitly needed. */
1896 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1897 if (r > 0) {
1898 pre += this->pip_ratio_pre * additional_length / r;
1899 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1900 }
1901 }
1902
1903 /* Third loop: Compute position and call the child. */
1904 uint position = this->bottom_up ? this->current_y - pre : pre; // Place to put next child relative to origin of the container.
1905 for (const auto &child_wid : this->children) {
1906 uint child_height = child_wid->current_y;
1907 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1908 uint child_y = y + (this->bottom_up ? position - child_height - child_wid->padding.top : position + child_wid->padding.top);
1909
1910 child_wid->AssignSizePosition(sizing, child_x, child_y, child_wid->current_x, child_height, rtl);
1911 if (child_wid->current_y != 0) {
1912 uint padded_child_height = child_height + child_wid->padding.Vertical() + inter;
1913 position = this->bottom_up ? position - padded_child_height : position + padded_child_height;
1914 }
1915 }
1916}
1917
1924{
1925 this->SetMinimalSize(width, height);
1926 this->SetResize(0, 0);
1927}
1928
1930{
1931 this->smallest_x = this->min_x;
1932 this->smallest_y = this->min_y;
1933 this->ApplyAspectRatio();
1934}
1935
1937{
1938 /* Spacer widget is never normally visible. */
1939
1940 if (_draw_widget_outlines && this->current_x != 0 && this->current_y != 0) {
1941 /* Spacers indicate a potential design issue, so get extra highlighting. */
1942 GfxFillRect(this->GetCurrentRect(), PC_WHITE, FILLRECT_CHECKER);
1943
1944 DrawOutline(w, this);
1945 }
1946}
1947
1949{
1950 /* Spacer widget never need repainting. */
1951}
1952
1954{
1955 return nullptr;
1956}
1957
1963{
1964 this->clicked = clicked;
1965 if (this->clicked >= 0 && this->sb != nullptr && this->widgets_x != 0) {
1966 int vpos = (this->clicked / this->widgets_x) * this->widget_h; // Vertical position of the top.
1967 /* Need to scroll down -> Scroll to the bottom.
1968 * However, last entry has no 'this->pip_inter' underneath, and we must stay below this->sb->GetCount() */
1969 if (this->sb->GetPosition() < vpos) vpos += this->widget_h - this->pip_inter - 1;
1970 this->sb->ScrollTowards(vpos);
1971 }
1972}
1973
1980{
1981 this->count = count;
1982
1983 if (this->sb == nullptr || this->widgets_x == 0) return;
1984
1985 /* We need to get the number of pixels the matrix is high/wide.
1986 * So, determine the number of rows/columns based on the number of
1987 * columns/rows (one is constant/unscrollable).
1988 * Then multiply that by the height of a widget, and add the pre
1989 * and post spacing "offsets". */
1990 count = CeilDiv(count, this->sb->IsVertical() ? this->widgets_x : this->widgets_y);
1991 count *= (this->sb->IsVertical() ? this->children.front()->smallest_y : this->children.front()->smallest_x) + this->pip_inter;
1992 if (count > 0) count -= this->pip_inter; // We counted an inter too much in the multiplication above
1993 count += this->pip_pre + this->pip_post;
1994 this->sb->SetCount(count);
1995 this->sb->SetCapacity(this->sb->IsVertical() ? this->current_y : this->current_x);
1996 this->sb->SetStepSize(this->sb->IsVertical() ? this->widget_h : this->widget_w);
1997}
1998
2004{
2005 this->sb = sb;
2006}
2007
2013{
2014 return this->current_element;
2015}
2016
2018{
2019 assert(this->children.size() == 1);
2020
2021 this->children.front()->SetupSmallestSize(w);
2022
2023 Dimension padding = { (uint)this->pip_pre + this->pip_post, (uint)this->pip_pre + this->pip_post};
2024 Dimension size = {this->children.front()->smallest_x + padding.width, this->children.front()->smallest_y + padding.height};
2025 Dimension fill = {0, 0};
2026 Dimension resize = {this->pip_inter + this->children.front()->smallest_x, this->pip_inter + this->children.front()->smallest_y};
2027
2028 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
2029
2030 this->smallest_x = size.width;
2031 this->smallest_y = size.height;
2032 this->fill_x = fill.width;
2033 this->fill_y = fill.height;
2034 this->resize_x = resize.width;
2035 this->resize_y = resize.height;
2036 this->ApplyAspectRatio();
2037}
2038
2039void NWidgetMatrix::AssignSizePosition(SizingType, int x, int y, uint given_width, uint given_height, bool)
2040{
2041 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
2042
2043 this->pos_x = x;
2044 this->pos_y = y;
2045 this->current_x = given_width;
2046 this->current_y = given_height;
2047
2048 /* Determine the size of the widgets, and the number of visible widgets on each of the axis. */
2049 this->widget_w = this->children.front()->smallest_x + this->pip_inter;
2050 this->widget_h = this->children.front()->smallest_y + this->pip_inter;
2051
2052 /* Account for the pip_inter is between widgets, so we need to account for that when
2053 * the division assumes pip_inter is used for all widgets. */
2054 this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
2055 this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
2056
2057 /* When resizing, update the scrollbar's count. E.g. with a vertical
2058 * scrollbar becoming wider or narrower means the amount of rows in
2059 * the scrollbar becomes respectively smaller or higher. */
2060 this->SetCount(this->count);
2061}
2062
2064{
2065 /* Falls outside of the matrix widget. */
2066 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
2067
2068 int start_x, start_y, base_offs_x, base_offs_y;
2069 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2070
2071 bool rtl = _current_text_dir == TD_RTL;
2072
2073 int widget_col = (rtl ?
2074 -x + (int)this->pip_post + this->pos_x + base_offs_x + this->widget_w - 1 - (int)this->pip_inter :
2075 x - (int)this->pip_pre - this->pos_x - base_offs_x
2076 ) / this->widget_w;
2077
2078 int widget_row = (y - base_offs_y - (int)this->pip_pre - this->pos_y) / this->widget_h;
2079
2080 this->current_element = (widget_row + start_y) * this->widgets_x + start_x + widget_col;
2081 if (this->current_element >= this->count) return nullptr;
2082
2083 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2084 assert(child != nullptr);
2086 this->pos_x + (rtl ? this->pip_post - widget_col * this->widget_w : this->pip_pre + widget_col * this->widget_w) + base_offs_x,
2087 this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
2088 child->smallest_x, child->smallest_y, rtl);
2089
2090 return child->GetWidgetFromPos(x, y);
2091}
2092
2093/* virtual */ void NWidgetMatrix::Draw(const Window *w)
2094{
2095 /* Fill the background. */
2096 GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, SHADE_LIGHT));
2097
2098 /* Set up a clipping area for the previews. */
2099 bool rtl = _current_text_dir == TD_RTL;
2100 DrawPixelInfo tmp_dpi;
2101 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;
2102
2103 {
2104 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
2105
2106 /* Get the appropriate offsets so we can draw the right widgets. */
2107 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2108 assert(child != nullptr);
2109 int start_x, start_y, base_offs_x, base_offs_y;
2110 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2111
2112 int offs_y = base_offs_y;
2113 for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
2114 /* Are we within bounds? */
2115 if (offs_y + child->smallest_y <= 0) continue;
2116 if (offs_y >= (int)this->current_y) break;
2117
2118 /* We've passed our amount of widgets. */
2119 if (y * this->widgets_x >= this->count) break;
2120
2121 int offs_x = base_offs_x;
2122 for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
2123 /* Are we within bounds? */
2124 if (offs_x + child->smallest_x <= 0) continue;
2125 if (offs_x >= (int)this->current_x) continue;
2126
2127 /* Do we have this many widgets? */
2128 this->current_element = y * this->widgets_x + x;
2129 if (this->current_element >= this->count) break;
2130
2131 child->AssignSizePosition(SizingType::Resize, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
2132 child->SetLowered(this->clicked == this->current_element);
2133 child->Draw(w);
2134 }
2135 }
2136 }
2137
2138 DrawOutline(w, this);
2139}
2140
2148void NWidgetMatrix::GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
2149{
2150 base_offs_x = _current_text_dir == TD_RTL ? this->widget_w * (this->widgets_x - 1) : 0;
2151 base_offs_y = 0;
2152 start_x = 0;
2153 start_y = 0;
2154 if (this->sb != nullptr) {
2155 if (this->sb->IsVertical()) {
2156 start_y = this->sb->GetPosition() / this->widget_h;
2157 base_offs_y += -this->sb->GetPosition() + start_y * this->widget_h;
2158 } else {
2159 start_x = this->sb->GetPosition() / this->widget_w;
2160 int sub_x = this->sb->GetPosition() - start_x * this->widget_w;
2161 if (_current_text_dir == TD_RTL) {
2162 base_offs_x += sub_x;
2163 } else {
2164 base_offs_x -= sub_x;
2165 }
2166 }
2167 }
2168}
2169
2179NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr<NWidgetPIPContainer> &&child) : NWidgetCore(tp, colour, index, 1, 1, {}, STR_NULL)
2180{
2181 assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
2182 this->child = std::move(child);
2183 if (this->child != nullptr) this->child->parent = this;
2184 this->SetAlignment(SA_TOP | SA_LEFT);
2185}
2186
2194void NWidgetBackground::Add(std::unique_ptr<NWidgetBase> &&nwid)
2195{
2196 if (this->child == nullptr) {
2197 this->child = std::make_unique<NWidgetVertical>();
2198 }
2199 nwid->parent = this->child.get();
2200 this->child->Add(std::move(nwid));
2201}
2202
2213void NWidgetBackground::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
2214{
2215 if (this->child == nullptr) {
2216 this->child = std::make_unique<NWidgetVertical>();
2217 }
2218 this->child->parent = this;
2219 this->child->SetPIP(pip_pre, pip_inter, pip_post);
2220}
2221
2232void NWidgetBackground::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
2233{
2234 if (this->child == nullptr) {
2235 this->child = std::make_unique<NWidgetVertical>();
2236 }
2237 this->child->parent = this;
2238 this->child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2239}
2240
2242{
2243 if (child != nullptr) child->AdjustPaddingForZoom();
2245}
2246
2248{
2249 if (this->child != nullptr) {
2250 this->child->SetupSmallestSize(w);
2251
2252 this->smallest_x = this->child->smallest_x;
2253 this->smallest_y = this->child->smallest_y;
2254 this->fill_x = this->child->fill_x;
2255 this->fill_y = this->child->fill_y;
2256 this->resize_x = this->child->resize_x;
2257 this->resize_y = this->child->resize_y;
2258
2259 /* Don't apply automatic padding if there is no child widget. */
2260 if (w == nullptr) return;
2261
2262 if (this->type == WWT_FRAME) {
2263 std::string text = GetStringForWidget(w, this);
2264 Dimension text_size = text.empty() ? Dimension{0, 0} : GetStringBoundingBox(text, this->text_size);
2265
2266 /* Account for the size of the frame's text if that exists */
2267 this->child->padding = WidgetDimensions::scaled.frametext;
2268 this->child->padding.top = std::max<uint8_t>(WidgetDimensions::scaled.frametext.top, text_size.height != 0 ? text_size.height + WidgetDimensions::scaled.frametext.top / 2 : 0);
2269
2270 this->smallest_x += this->child->padding.Horizontal();
2271 this->smallest_y += this->child->padding.Vertical();
2272
2273 this->smallest_x = std::max(this->smallest_x, text_size.width + WidgetDimensions::scaled.frametext.Horizontal());
2274 } else if (this->type == WWT_INSET) {
2275 /* Apply automatic padding for bevel thickness. */
2276 this->child->padding = WidgetDimensions::scaled.bevel;
2277
2278 this->smallest_x += this->child->padding.Horizontal();
2279 this->smallest_y += this->child->padding.Vertical();
2280 }
2281 this->ApplyAspectRatio();
2282 } else {
2283 Dimension d = {this->min_x, this->min_y};
2284 Dimension fill = {this->fill_x, this->fill_y};
2285 Dimension resize = {this->resize_x, this->resize_y};
2286 if (w != nullptr) { // A non-nullptr window pointer acts as switch to turn dynamic widget size on.
2287 if (this->type == WWT_FRAME || this->type == WWT_INSET) {
2288 std::string text = GetStringForWidget(w, this);
2289 if (!text.empty()) {
2290 Dimension background = GetStringBoundingBox(text, this->text_size);
2291 background.width += (this->type == WWT_FRAME) ? (WidgetDimensions::scaled.frametext.Horizontal()) : (WidgetDimensions::scaled.inset.Horizontal());
2292 d = maxdim(d, background);
2293 }
2294 }
2295 if (this->index >= 0) {
2297 switch (this->type) {
2298 default: NOT_REACHED();
2299 case WWT_PANEL: padding = {WidgetDimensions::scaled.framerect.Horizontal(), WidgetDimensions::scaled.framerect.Vertical()}; break;
2300 case WWT_FRAME: padding = {WidgetDimensions::scaled.frametext.Horizontal(), WidgetDimensions::scaled.frametext.Vertical()}; break;
2301 case WWT_INSET: padding = {WidgetDimensions::scaled.inset.Horizontal(), WidgetDimensions::scaled.inset.Vertical()}; break;
2302 }
2303 w->UpdateWidgetSize(this->index, d, padding, fill, resize);
2304 }
2305 }
2306 this->smallest_x = d.width;
2307 this->smallest_y = d.height;
2308 this->fill_x = fill.width;
2309 this->fill_y = fill.height;
2310 this->resize_x = resize.width;
2311 this->resize_y = resize.height;
2312 this->ApplyAspectRatio();
2313 }
2314}
2315
2316void NWidgetBackground::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
2317{
2318 this->StoreSizePosition(sizing, x, y, given_width, given_height);
2319
2320 if (this->child != nullptr) {
2321 uint x_offset = (rtl ? this->child->padding.right : this->child->padding.left);
2322 uint width = given_width - this->child->padding.Horizontal();
2323 uint height = given_height - this->child->padding.Vertical();
2324 this->child->AssignSizePosition(sizing, x + x_offset, y + this->child->padding.top, width, height, rtl);
2325 }
2326}
2327
2329{
2330 this->NWidgetCore::FillWidgetLookup(widget_lookup);
2331 if (this->child != nullptr) this->child->FillWidgetLookup(widget_lookup);
2332}
2333
2335{
2336 if (this->current_x == 0 || this->current_y == 0) return;
2337
2338 Rect r = this->GetCurrentRect();
2339
2340 const DrawPixelInfo *dpi = _cur_dpi;
2341 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2342
2343 switch (this->type) {
2344 case WWT_PANEL:
2345 DrawFrameRect(r, this->colour, this->IsLowered() ? FrameFlag::Lowered : FrameFlags{});
2346 break;
2347
2348 case WWT_FRAME:
2349 DrawFrame(r, this->colour, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
2350 break;
2351
2352 case WWT_INSET:
2353 DrawInset(r, this->colour, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
2354 break;
2355
2356 default:
2357 NOT_REACHED();
2358 }
2359
2360 if (this->index >= 0) w->DrawWidget(r, this->index);
2361 if (this->child != nullptr) this->child->Draw(w);
2362
2363 if (this->IsDisabled()) {
2365 }
2366
2367 DrawOutline(w, this);
2368}
2369
2371{
2372 NWidgetCore *nwid = nullptr;
2373 if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) {
2374 if (this->child != nullptr) nwid = this->child->GetWidgetFromPos(x, y);
2375 if (nwid == nullptr) nwid = this;
2376 }
2377 return nwid;
2378}
2379
2381{
2382 NWidgetBase *nwid = nullptr;
2383 if (this->child != nullptr) nwid = this->child->GetWidgetOfType(tp);
2384 if (nwid == nullptr && this->type == tp) nwid = this;
2385 return nwid;
2386}
2387
2388NWidgetViewport::NWidgetViewport(WidgetID index) : NWidgetCore(NWID_VIEWPORT, INVALID_COLOUR, index, 1, 1, {}, STR_NULL)
2389{
2390}
2391
2393{
2394 this->smallest_x = this->min_x;
2395 this->smallest_y = this->min_y;
2396 this->ApplyAspectRatio();
2397}
2398
2400{
2401 if (this->current_x == 0 || this->current_y == 0) return;
2402
2405 _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_TEXT); // Disable all transparency, except textual stuff
2406 w->DrawViewport();
2407 _transparency_opt = to_backup;
2408 } else {
2409 w->DrawViewport();
2410 }
2411
2412 /* Optionally shade the viewport. */
2413 if (this->disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2415 }
2416
2417 DrawOutline(w, this);
2418}
2419
2426void NWidgetViewport::InitializeViewport(Window *w, std::variant<TileIndex, VehicleID> focus, ZoomLevel zoom)
2427{
2428 InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, focus, zoom);
2429}
2430
2436{
2437 if (w->viewport == nullptr) return;
2438
2439 Viewport &vp = *w->viewport;
2440 vp.left = w->left + this->pos_x;
2441 vp.top = w->top + this->pos_y;
2442 vp.width = this->current_x;
2443 vp.height = this->current_y;
2444
2445 vp.virtual_width = ScaleByZoom(vp.width, vp.zoom);
2447}
2448
2458Scrollbar::size_type Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, WidgetID widget, int padding, int line_height) const
2459{
2460 int pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
2461 if (pos != INT_MAX) pos += this->GetPosition();
2462 return (pos < 0 || pos >= this->GetCount()) ? Scrollbar::npos : pos;
2463}
2464
2479EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
2480{
2481 int new_pos = list_position;
2482 switch (keycode) {
2483 case WKC_UP:
2484 /* scroll up by one */
2485 new_pos--;
2486 break;
2487
2488 case WKC_DOWN:
2489 /* scroll down by one */
2490 new_pos++;
2491 break;
2492
2493 case WKC_PAGEUP:
2494 /* scroll up a page */
2495 new_pos -= this->GetCapacity();
2496 break;
2497
2498 case WKC_PAGEDOWN:
2499 /* scroll down a page */
2500 new_pos += this->GetCapacity();
2501 break;
2502
2503 case WKC_HOME:
2504 /* jump to beginning */
2505 new_pos = 0;
2506 break;
2507
2508 case WKC_END:
2509 /* jump to end */
2510 new_pos = this->GetCount() - 1;
2511 break;
2512
2513 default:
2514 return ES_NOT_HANDLED;
2515 }
2516
2517 /* If there are no elements, there is nothing to scroll/update. */
2518 if (this->GetCount() != 0) {
2519 list_position = Clamp(new_pos, 0, this->GetCount() - 1);
2520 }
2521 return ES_HANDLED;
2522}
2523
2524
2533{
2534 NWidgetBase *nwid = w->GetWidget<NWidgetBase>(widget);
2535 if (this->IsVertical()) {
2536 this->SetCapacity(((int)nwid->current_y - padding) / (int)nwid->resize_y);
2537 } else {
2538 this->SetCapacity(((int)nwid->current_x - padding) / (int)nwid->resize_x);
2539 }
2540}
2541
2549Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
2550{
2551 const int count = sb.GetCount() * resize_step;
2552 const int position = sb.GetPosition() * resize_step;
2553
2554 if (sb.IsVertical()) {
2555 r.top -= position;
2556 r.bottom = r.top + count;
2557 } else {
2558 bool rtl = _current_text_dir == TD_RTL;
2559 if (rtl) {
2560 r.right += position;
2561 r.left = r.right - count;
2562 } else {
2563 r.left -= position;
2564 r.right = r.left + count;
2565 }
2566 }
2567
2568 return r;
2569}
2570
2578{
2579 assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR);
2580
2581 switch (this->type) {
2582 case NWID_HSCROLLBAR:
2583 this->SetResize(1, 0);
2584 this->SetFill(1, 0);
2585 this->SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2586 break;
2587
2588 case NWID_VSCROLLBAR:
2589 this->SetResize(0, 1);
2590 this->SetFill(0, 1);
2591 this->SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2592 break;
2593
2594 default: NOT_REACHED();
2595 }
2596}
2597
2599{
2600 this->min_x = 0;
2601 this->min_y = 0;
2602
2603 switch (this->type) {
2604 case NWID_HSCROLLBAR:
2605 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2606 break;
2607
2608 case NWID_VSCROLLBAR:
2609 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2610 break;
2611
2612 default: NOT_REACHED();
2613 }
2614
2615 this->smallest_x = this->min_x;
2616 this->smallest_y = this->min_y;
2617}
2618
2620{
2621 if (this->current_x == 0 || this->current_y == 0) return;
2622
2623 Rect r = this->GetCurrentRect();
2624
2625 const DrawPixelInfo *dpi = _cur_dpi;
2626 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2627
2628 bool up_lowered = this->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp);
2629 bool down_lowered = this->disp_flags.Test(NWidgetDisplayFlag::ScrollbarDown);
2630 bool middle_lowered = !this->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown}) && w->mouse_capture_widget == this->index;
2631
2632 if (this->type == NWID_HSCROLLBAR) {
2633 DrawHorizontalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2634 } else {
2635 DrawVerticalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2636 }
2637
2638 if (this->IsDisabled()) {
2640 }
2641
2642 DrawOutline(w, this);
2643}
2644
2645/* static */ void NWidgetScrollbar::InvalidateDimensionCache()
2646{
2647 vertical_dimension.width = vertical_dimension.height = 0;
2648 horizontal_dimension.width = horizontal_dimension.height = 0;
2649}
2650
2651/* static */ Dimension NWidgetScrollbar::GetVerticalDimension()
2652{
2653 if (vertical_dimension.width == 0) {
2654 vertical_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_UP), GetScaledSpriteSize(SPR_ARROW_DOWN));
2657 }
2658 return vertical_dimension;
2659}
2660
2661/* static */ Dimension NWidgetScrollbar::GetHorizontalDimension()
2662{
2663 if (horizontal_dimension.width == 0) {
2664 horizontal_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2667 }
2668 return horizontal_dimension;
2669}
2670
2673
2676{
2677 shadebox_dimension.width = shadebox_dimension.height = 0;
2678 debugbox_dimension.width = debugbox_dimension.height = 0;
2679 defsizebox_dimension.width = defsizebox_dimension.height = 0;
2680 stickybox_dimension.width = stickybox_dimension.height = 0;
2681 resizebox_dimension.width = resizebox_dimension.height = 0;
2682 closebox_dimension.width = closebox_dimension.height = 0;
2683 dropdown_dimension.width = dropdown_dimension.height = 0;
2684}
2685
2693
2702NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, const WidgetData &data, StringID tip) : NWidgetCore(tp, colour, index, 1, 1, data, tip)
2703{
2704 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);
2705 this->min_x = 0;
2706 this->min_y = 0;
2707 this->SetResize(0, 0);
2708
2709 switch (tp) {
2710 case WWT_EMPTY:
2711 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_EMPTY should not have a colour");
2712 break;
2713
2714 case WWT_TEXT:
2715 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_TEXT should not have a colour");
2716 this->SetFill(0, 0);
2718 break;
2719
2720 case WWT_LABEL:
2721 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_LABEL should not have a colour");
2722 [[fallthrough]];
2723
2724 case WWT_PUSHBTN:
2725 case WWT_IMGBTN:
2726 case WWT_PUSHIMGBTN:
2727 case WWT_IMGBTN_2:
2728 case WWT_TEXTBTN:
2729 case WWT_PUSHTXTBTN:
2730 case WWT_TEXTBTN_2:
2731 case WWT_IMGTEXTBTN:
2732 case WWT_PUSHIMGTEXTBTN:
2733 case WWT_BOOLBTN:
2734 case WWT_MATRIX:
2736 case NWID_PUSHBUTTON_DROPDOWN:
2737 this->SetFill(0, 0);
2738 break;
2739
2740 case WWT_ARROWBTN:
2741 case WWT_PUSHARROWBTN:
2742 this->SetFill(0, 0);
2743 this->SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2744 break;
2745
2746 case WWT_EDITBOX:
2747 this->SetFill(0, 0);
2748 break;
2749
2750 case WWT_CAPTION:
2751 this->SetFill(1, 0);
2752 this->SetResize(1, 0);
2754 this->SetMinimalTextLines(1, WidgetDimensions::unscaled.captiontext.Vertical(), FS_NORMAL);
2755 this->SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2756 break;
2757
2758 case WWT_STICKYBOX:
2759 this->SetFill(0, 0);
2761 this->SetToolTip(STR_TOOLTIP_STICKY);
2762 this->SetAspect(this->min_x, this->min_y);
2763 break;
2764
2765 case WWT_SHADEBOX:
2766 this->SetFill(0, 0);
2768 this->SetToolTip(STR_TOOLTIP_SHADE);
2769 this->SetAspect(this->min_x, this->min_y);
2770 break;
2771
2772 case WWT_DEBUGBOX:
2773 this->SetFill(0, 0);
2775 this->SetToolTip(STR_TOOLTIP_DEBUG);
2776 this->SetAspect(this->min_x, this->min_y);
2777 break;
2778
2779 case WWT_DEFSIZEBOX:
2780 this->SetFill(0, 0);
2782 this->SetToolTip(STR_TOOLTIP_DEFSIZE);
2783 this->SetAspect(this->min_x, this->min_y);
2784 break;
2785
2786 case WWT_RESIZEBOX:
2787 this->SetFill(0, 0);
2790 this->SetToolTip(STR_TOOLTIP_RESIZE);
2791 break;
2792
2793 case WWT_CLOSEBOX:
2794 this->SetFill(0, 0);
2796 this->SetToolTip(STR_TOOLTIP_CLOSE_WINDOW);
2797 this->SetAspect(this->min_x, this->min_y);
2798 break;
2799
2800 case WWT_DROPDOWN:
2801 this->SetFill(0, 0);
2803 this->SetAlignment(SA_TOP | SA_LEFT);
2804 break;
2805
2806 default:
2807 NOT_REACHED();
2808 }
2809}
2810
2812{
2813 Dimension padding = {0, 0};
2814 Dimension size = {this->min_x, this->min_y};
2815 Dimension fill = {this->fill_x, this->fill_y};
2816 Dimension resize = {this->resize_x, this->resize_y};
2817 switch (this->type) {
2818 case WWT_EMPTY: {
2819 break;
2820 }
2821 case WWT_MATRIX: {
2822 padding = {WidgetDimensions::scaled.matrix.Horizontal(), WidgetDimensions::scaled.matrix.Vertical()};
2823 break;
2824 }
2825 case WWT_SHADEBOX: {
2826 padding = {WidgetDimensions::scaled.shadebox.Horizontal(), WidgetDimensions::scaled.shadebox.Vertical()};
2827 if (NWidgetLeaf::shadebox_dimension.width == 0) {
2828 NWidgetLeaf::shadebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_SHADE), GetScaledSpriteSize(SPR_WINDOW_UNSHADE));
2831 }
2833 break;
2834 }
2835 case WWT_DEBUGBOX:
2836 if (_settings_client.gui.newgrf_developer_tools && w->IsNewGRFInspectable()) {
2837 padding = {WidgetDimensions::scaled.debugbox.Horizontal(), WidgetDimensions::scaled.debugbox.Vertical()};
2838 if (NWidgetLeaf::debugbox_dimension.width == 0) {
2842 }
2844 } else {
2845 /* If the setting is disabled we don't want to see it! */
2846 size.width = 0;
2847 fill.width = 0;
2848 resize.width = 0;
2849 }
2850 break;
2851
2852 case WWT_STICKYBOX: {
2853 padding = {WidgetDimensions::scaled.stickybox.Horizontal(), WidgetDimensions::scaled.stickybox.Vertical()};
2854 if (NWidgetLeaf::stickybox_dimension.width == 0) {
2858 }
2860 break;
2861 }
2862
2863 case WWT_DEFSIZEBOX: {
2864 padding = {WidgetDimensions::scaled.defsizebox.Horizontal(), WidgetDimensions::scaled.defsizebox.Vertical()};
2865 if (NWidgetLeaf::defsizebox_dimension.width == 0) {
2869 }
2871 break;
2872 }
2873
2874 case WWT_RESIZEBOX: {
2875 padding = {WidgetDimensions::scaled.resizebox.Horizontal(), WidgetDimensions::scaled.resizebox.Vertical()};
2876 if (NWidgetLeaf::resizebox_dimension.width == 0) {
2877 NWidgetLeaf::resizebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_RESIZE_LEFT), GetScaledSpriteSize(SPR_WINDOW_RESIZE_RIGHT));
2880 }
2882 break;
2883 }
2884 case WWT_EDITBOX: {
2885 Dimension sprite_size = GetScaledSpriteSize(_current_text_dir == TD_RTL ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
2886 size.width = std::max(size.width, ScaleGUITrad(30) + sprite_size.width);
2887 size.height = std::max(sprite_size.height, GetStringBoundingBox("_").height + WidgetDimensions::scaled.framerect.Vertical());
2888 }
2889 [[fallthrough]];
2890 case WWT_PUSHBTN: {
2891 padding = {WidgetDimensions::scaled.frametext.Horizontal(), WidgetDimensions::scaled.framerect.Vertical()};
2892 break;
2893 }
2894
2895 case WWT_BOOLBTN:
2896 size.width = SETTING_BUTTON_WIDTH;
2897 size.height = SETTING_BUTTON_HEIGHT;
2898 break;
2899
2900 case WWT_IMGBTN:
2901 case WWT_IMGBTN_2:
2902 case WWT_PUSHIMGBTN: {
2903 padding = {WidgetDimensions::scaled.imgbtn.Horizontal(), WidgetDimensions::scaled.imgbtn.Vertical()};
2904 Dimension d2 = GetScaledSpriteSize(this->widget_data.sprite);
2905 if (this->type == WWT_IMGBTN_2) d2 = maxdim(d2, GetScaledSpriteSize(this->widget_data.sprite + 1));
2906 d2.width += padding.width;
2907 d2.height += padding.height;
2908 size = maxdim(size, d2);
2909 break;
2910 }
2911
2912 case WWT_IMGTEXTBTN:
2913 case WWT_PUSHIMGTEXTBTN: {
2914 padding = {WidgetDimensions::scaled.framerect.Horizontal(), WidgetDimensions::scaled.framerect.Vertical()};
2916 Dimension dt = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2917 Dimension d2{
2918 padding.width + 2 * (di.width + WidgetDimensions::scaled.hsep_wide) + dt.width,
2919 padding.height + std::max(di.height, dt.height)
2920 };
2921 size = maxdim(size, d2);
2922 break;
2923 }
2924
2925 case WWT_ARROWBTN:
2926 case WWT_PUSHARROWBTN: {
2927 padding = {WidgetDimensions::scaled.imgbtn.Horizontal(), WidgetDimensions::scaled.imgbtn.Vertical()};
2928 Dimension d2 = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2929 d2.width += padding.width;
2930 d2.height += padding.height;
2931 size = maxdim(size, d2);
2932 break;
2933 }
2934
2935 case WWT_CLOSEBOX: {
2936 padding = {WidgetDimensions::scaled.closebox.Horizontal(), WidgetDimensions::scaled.closebox.Vertical()};
2937 if (NWidgetLeaf::closebox_dimension.width == 0) {
2941 }
2943 break;
2944 }
2945 case WWT_TEXTBTN:
2946 case WWT_PUSHTXTBTN:
2947 case WWT_TEXTBTN_2: {
2948 padding = {WidgetDimensions::scaled.framerect.Horizontal(), WidgetDimensions::scaled.framerect.Vertical()};
2949 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2950 d2.width += padding.width;
2951 d2.height += padding.height;
2952 size = maxdim(size, d2);
2953 break;
2954 }
2955 case WWT_LABEL:
2956 case WWT_TEXT: {
2957 size = maxdim(size, GetStringBoundingBox(GetStringForWidget(w, this), this->text_size));
2958 break;
2959 }
2960 case WWT_CAPTION: {
2961 padding = {WidgetDimensions::scaled.captiontext.Horizontal(), WidgetDimensions::scaled.captiontext.Vertical()};
2962 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2963 d2.width += padding.width;
2964 d2.height += padding.height;
2965 size = maxdim(size, d2);
2966 break;
2967 }
2968 case WWT_DROPDOWN:
2970 case NWID_PUSHBUTTON_DROPDOWN: {
2971 if (NWidgetLeaf::dropdown_dimension.width == 0) {
2973 NWidgetLeaf::dropdown_dimension.width += WidgetDimensions::scaled.vscrollbar.Horizontal();
2974 NWidgetLeaf::dropdown_dimension.height += WidgetDimensions::scaled.vscrollbar.Vertical();
2975 }
2976 padding = {WidgetDimensions::scaled.dropdowntext.Horizontal() + NWidgetLeaf::dropdown_dimension.width + WidgetDimensions::scaled.fullbevel.Horizontal(), WidgetDimensions::scaled.dropdowntext.Vertical()};
2977 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2978 d2.width += padding.width;
2979 d2.height = std::max(d2.height + padding.height, NWidgetLeaf::dropdown_dimension.height);
2980 size = maxdim(size, d2);
2981 break;
2982 }
2983 default:
2984 NOT_REACHED();
2985 }
2986
2987 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
2988
2989 this->smallest_x = size.width;
2990 this->smallest_y = size.height;
2991 this->fill_x = fill.width;
2992 this->fill_y = fill.height;
2993 this->resize_x = resize.width;
2994 this->resize_y = resize.height;
2995 this->ApplyAspectRatio();
2996}
2997
2999{
3000 if (this->current_x == 0 || this->current_y == 0) return;
3001
3002 /* Setup a clipping rectangle... for WWT_EMPTY or WWT_TEXT, an extra scaled pixel is allowed in case text shadow encroaches. */
3003 int extra = (this->type == WWT_EMPTY || this->type == WWT_TEXT) ? ScaleGUITrad(1) : 0;
3004 DrawPixelInfo new_dpi;
3005 if (!FillDrawPixelInfo(&new_dpi, this->pos_x, this->pos_y, this->current_x + extra, this->current_y + extra)) return;
3006 /* ...but keep coordinates relative to the window. */
3007 new_dpi.left += this->pos_x;
3008 new_dpi.top += this->pos_y;
3009
3010 AutoRestoreBackup dpi_backup(_cur_dpi, &new_dpi);
3011
3012 Rect r = this->GetCurrentRect();
3013
3014 bool clicked = this->IsLowered();
3015 switch (this->type) {
3016 case WWT_EMPTY:
3017 /* WWT_EMPTY used as a spacer indicates a potential design issue. */
3018 if (this->index == -1 && _draw_widget_outlines) {
3020 }
3021 break;
3022
3023 case WWT_PUSHBTN:
3024 DrawFrameRect(r, this->colour, clicked ? FrameFlag::Lowered : FrameFlags{});
3025 break;
3026
3027 case WWT_BOOLBTN: {
3029 Colours button_colour = this->widget_data.alternate_colour;
3030 if (button_colour == INVALID_COLOUR) button_colour = this->colour;
3031 DrawBoolButton(pt.x, pt.y, button_colour, this->colour, clicked, !this->IsDisabled());
3032 break;
3033 }
3034
3035 case WWT_IMGBTN:
3036 case WWT_PUSHIMGBTN:
3037 case WWT_IMGBTN_2:
3038 DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data.sprite, this->align);
3039 break;
3040
3041 case WWT_TEXTBTN:
3042 case WWT_PUSHTXTBTN:
3043 case WWT_TEXTBTN_2:
3044 DrawFrameRect(r, this->colour, clicked ? FrameFlag::Lowered : FrameFlags{});
3045 DrawLabel(r, this->text_colour, GetStringForWidget(w, this, (type & WWT_MASK) == WWT_TEXTBTN_2 && clicked), this->align, this->text_size);
3046 break;
3047
3048 case WWT_IMGTEXTBTN:
3049 case WWT_PUSHIMGTEXTBTN:
3050 DrawImageTextButtons(r, this->colour, clicked, this->widget_data.sprite, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3051 break;
3052
3053 case WWT_ARROWBTN:
3054 case WWT_PUSHARROWBTN: {
3055 SpriteID sprite;
3056 switch (this->widget_data.arrow_widget_type) {
3057 case ArrowWidgetType::Decrease: sprite = _current_text_dir != TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
3058 case ArrowWidgetType::Increase: sprite = _current_text_dir == TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
3059 case ArrowWidgetType::Left: sprite = SPR_ARROW_LEFT; break;
3060 case ArrowWidgetType::Right: sprite = SPR_ARROW_RIGHT; break;
3061 default: NOT_REACHED();
3062 }
3063 DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align);
3064 break;
3065 }
3066
3067 case WWT_LABEL:
3068 DrawLabel(r, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3069 break;
3070
3071 case WWT_TEXT:
3072 DrawText(r, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3073 break;
3074
3075 case WWT_MATRIX:
3076 DrawMatrix(r, this->colour, clicked, this->widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3077 break;
3078
3079 case WWT_EDITBOX: {
3080 const QueryString *query = w->GetQueryString(this->index);
3081 if (query != nullptr) query->DrawEditBox(w, this->index);
3082 break;
3083 }
3084
3085 case WWT_CAPTION:
3086 DrawCaption(r, this->colour, w->owner, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3087 break;
3088
3089 case WWT_SHADEBOX:
3090 DrawShadeBox(r, this->colour, w->IsShaded());
3091 break;
3092
3093 case WWT_DEBUGBOX:
3094 DrawDebugBox(r, this->colour, clicked);
3095 break;
3096
3097 case WWT_STICKYBOX:
3099 break;
3100
3101 case WWT_DEFSIZEBOX:
3102 DrawDefSizeBox(r, this->colour, clicked);
3103 break;
3104
3105 case WWT_RESIZEBOX:
3106 DrawResizeBox(r, this->colour, this->pos_x < (w->width / 2), w->flags.Test(WindowFlag::SizingLeft) || w->flags.Test(WindowFlag::SizingRight), this->widget_data.resize_widget_type == ResizeWidgetType::ShowBevel);
3107 break;
3108
3109 case WWT_CLOSEBOX:
3110 DrawCloseBox(r, this->colour);
3111 break;
3112
3113 case WWT_DROPDOWN:
3114 DrawButtonDropdown(r, this->colour, false, clicked, GetStringForWidget(w, this), this->align);
3115 break;
3116
3118 case NWID_PUSHBUTTON_DROPDOWN:
3119 DrawButtonDropdown(r, this->colour, clicked, this->disp_flags.Test(NWidgetDisplayFlag::DropdownActive), GetStringForWidget(w, this), this->align);
3120 break;
3121
3122 default:
3123 NOT_REACHED();
3124 }
3125 if (this->index >= 0) w->DrawWidget(r, this->index);
3126
3127 if (this->IsDisabled() && this->type != WWT_BOOLBTN) {
3128 /* WWT_BOOLBTN is excluded as it draws its own disabled state. */
3130 }
3131
3132 DrawOutline(w, this);
3133}
3134
3143{
3144 if (_current_text_dir == TD_LTR) {
3145 int button_width = this->pos_x + this->current_x - NWidgetLeaf::dropdown_dimension.width;
3146 return pt.x < button_width;
3147 } else {
3148 int button_left = this->pos_x + NWidgetLeaf::dropdown_dimension.width;
3149 return pt.x >= button_left;
3150 }
3151}
3152
3153/* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
3154
3161{
3162 return tp > WPT_ATTRIBUTE_BEGIN && tp < WPT_ATTRIBUTE_END;
3163}
3164
3172{
3173 switch (nwid.type) {
3174 case WPT_RESIZE: {
3175 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3176 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_RESIZE requires NWidgetResizeBase");
3177 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3178 nwrb->SetResize(nwid.u.xy.x, nwid.u.xy.y);
3179 break;
3180 }
3181
3182 case WPT_MINSIZE: {
3183 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3184 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINSIZE requires NWidgetResizeBase");
3185 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3186 nwrb->SetMinimalSize(nwid.u.xy.x, nwid.u.xy.y);
3187 break;
3188 }
3189
3190 case WPT_MINTEXTLINES: {
3191 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3192 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINTEXTLINES requires NWidgetResizeBase");
3193 assert(nwid.u.text_lines.size >= FS_BEGIN && nwid.u.text_lines.size < FS_END);
3195 break;
3196 }
3197
3198 case WPT_TOOLBARSIZE: {
3199 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3200 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_TOOLBARSIZE requires NWidgetResizeBase");
3201 assert(nwid.u.xy.x >= 0);
3202 nwrb->SetToolbarMinimalSize(nwid.u.xy.x);
3203 break;
3204 }
3205
3206 case WPT_TEXTSTYLE: {
3207 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3208 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_TEXTSTYLE requires NWidgetCore");
3209 nwc->SetTextStyle(nwid.u.text_style.colour, nwid.u.text_style.size);
3210 break;
3211 }
3212
3213 case WPT_ALIGNMENT: {
3214 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3215 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_ALIGNMENT requires NWidgetCore");
3216 nwc->SetAlignment(nwid.u.align.align);
3217 break;
3218 }
3219
3220 case WPT_FILL: {
3221 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3222 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_FILL requires NWidgetResizeBase");
3223 nwrb->SetFill(nwid.u.xy.x, nwid.u.xy.y);
3224 break;
3225 }
3226
3227 case WPT_DATATIP: {
3228 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3229 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_DATATIP requires NWidgetCore");
3230 nwc->widget_data = nwid.u.data_tip.data;
3231 nwc->SetToolTip(nwid.u.data_tip.tooltip);
3232 break;
3233 }
3234
3235 case WPT_PADDING:
3236 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_PADDING requires NWidgetBase");
3237 dest->SetPadding(nwid.u.padding);
3238 break;
3239
3240 case WPT_PIPSPACE: {
3241 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3242 if (nwc != nullptr) nwc->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3243
3244 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3245 if (nwb != nullptr) nwb->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3246
3247 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3248 break;
3249 }
3250
3251 case WPT_PIPRATIO: {
3252 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3253 if (nwc != nullptr) nwc->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3254
3255 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3256 if (nwb != nullptr) nwb->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3257
3258 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3259 break;
3260 }
3261
3262 case WPT_SCROLLBAR: {
3263 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3264 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore");
3265 nwc->scrollbar_index = nwid.u.widget.index;
3266 break;
3267 }
3268
3269 case WPT_ASPECT: {
3270 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_ASPECT requires NWidgetBase");
3271 dest->aspect_ratio = nwid.u.aspect.ratio;
3272 dest->aspect_flags = nwid.u.aspect.flags;
3273 break;
3274 }
3275
3276 default:
3277 NOT_REACHED();
3278 }
3279}
3280
3287static std::unique_ptr<NWidgetBase> MakeNWidget(const NWidgetPart &nwid)
3288{
3289 assert(!IsAttributeWidgetPartType(nwid.type));
3290 assert(nwid.type != WPT_ENDCONTAINER);
3291
3292 switch (nwid.type) {
3293 case NWID_SPACER: return std::make_unique<NWidgetSpacer>(0, 0);
3294
3295 case WWT_PANEL: [[fallthrough]];
3296 case WWT_INSET: [[fallthrough]];
3297 case WWT_FRAME: return std::make_unique<NWidgetBackground>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3298
3299 case NWID_HORIZONTAL: return std::make_unique<NWidgetHorizontal>(nwid.u.container.flags, nwid.u.container.index);
3300 case NWID_HORIZONTAL_LTR: return std::make_unique<NWidgetHorizontalLTR>(nwid.u.container.flags, nwid.u.container.index);
3301 case NWID_VERTICAL: return std::make_unique<NWidgetVertical>(nwid.u.container.flags, nwid.u.container.index);
3302 case NWID_SELECTION: return std::make_unique<NWidgetStacked>(nwid.u.widget.index);
3303 case NWID_MATRIX: return std::make_unique<NWidgetMatrix>(nwid.u.widget.colour, nwid.u.widget.index);
3304 case NWID_VIEWPORT: return std::make_unique<NWidgetViewport>(nwid.u.widget.index);
3305 case NWID_LAYER: return std::make_unique<NWidgetLayer>(nwid.u.widget.index);
3306
3307 case NWID_HSCROLLBAR: [[fallthrough]];
3308 case NWID_VSCROLLBAR: return std::make_unique<NWidgetScrollbar>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3309
3310 case WPT_FUNCTION: return nwid.u.func_ptr();
3311
3312 default:
3313 assert((nwid.type & WWT_MASK) < WWT_LAST || (nwid.type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
3314 return std::make_unique<NWidgetLeaf>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index, WidgetData{}, STR_NULL);
3315 }
3316}
3317
3331static 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)
3332{
3333 dest = nullptr;
3334
3335 if (IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]] throw std::runtime_error("Expected non-attribute NWidgetPart type");
3336 if (nwid_begin->type == WPT_ENDCONTAINER) return nwid_begin;
3337
3338 fill_dest = IsContainerWidgetType(nwid_begin->type);
3339 dest = MakeNWidget(*nwid_begin);
3340 if (dest == nullptr) return nwid_begin;
3341
3342 ++nwid_begin;
3343
3344 /* Once a widget is created, we're now looking for attributes. */
3345 while (nwid_begin != nwid_end && IsAttributeWidgetPartType(nwid_begin->type)) {
3346 ApplyNWidgetPartAttribute(*nwid_begin, dest.get());
3347 ++nwid_begin;
3348 }
3349
3350 return nwid_begin;
3351}
3352
3359{
3360 return tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
3361 || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION || tp == NWID_LAYER;
3362}
3363
3371static 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)
3372{
3373 /* If *parent == nullptr, only the first widget is read and returned. Otherwise, *parent must point to either
3374 * a #NWidgetContainer or a #NWidgetBackground object, and parts are added as much as possible. */
3375 NWidgetContainer *nwid_cont = dynamic_cast<NWidgetContainer *>(parent.get());
3376 NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(parent.get());
3377 assert(parent == nullptr || (nwid_cont != nullptr && nwid_parent == nullptr) || (nwid_cont == nullptr && nwid_parent != nullptr));
3378
3379 while (nwid_begin != nwid_end) {
3380 std::unique_ptr<NWidgetBase> sub_widget = nullptr;
3381 bool fill_sub = false;
3382 nwid_begin = MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3383
3384 /* Break out of loop when end reached */
3385 if (sub_widget == nullptr) break;
3386
3387 /* If sub-widget is a container, recursively fill that container. */
3388 if (fill_sub && IsContainerWidgetType(sub_widget->type)) {
3389 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, sub_widget);
3390 }
3391
3392 /* Add sub_widget to parent container if available, otherwise return the widget to the caller. */
3393 if (nwid_cont != nullptr) nwid_cont->Add(std::move(sub_widget));
3394 if (nwid_parent != nullptr) nwid_parent->Add(std::move(sub_widget));
3395 if (nwid_cont == nullptr && nwid_parent == nullptr) {
3396 parent = std::move(sub_widget);
3397 return nwid_begin;
3398 }
3399 }
3400
3401 if (nwid_begin == nwid_end) return nwid_begin; // Reached the end of the array of parts?
3402
3403 assert(nwid_begin < nwid_end);
3404 assert(nwid_begin->type == WPT_ENDCONTAINER);
3405 return std::next(nwid_begin); // *nwid_begin is also 'used'
3406}
3407
3415std::unique_ptr<NWidgetBase> MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3416{
3417 if (container == nullptr) container = std::make_unique<NWidgetVertical>();
3418 [[maybe_unused]] auto nwid_part = MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3419#ifdef WITH_ASSERT
3420 if (nwid_part != std::end(nwid_parts)) [[unlikely]] throw std::runtime_error("Did not consume all NWidgetParts");
3421#endif
3422 return std::move(container);
3423}
3424
3434std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(std::span<const NWidgetPart> nwid_parts, NWidgetStacked **shade_select)
3435{
3436 auto nwid_begin = std::begin(nwid_parts);
3437 auto nwid_end = std::end(nwid_parts);
3438
3439 *shade_select = nullptr;
3440
3441 /* Read the first widget recursively from the array. */
3442 std::unique_ptr<NWidgetBase> nwid = nullptr;
3443 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, nwid);
3444 assert(nwid != nullptr);
3445
3446 NWidgetHorizontal *hor_cont = dynamic_cast<NWidgetHorizontal *>(nwid.get());
3447
3448 auto root = std::make_unique<NWidgetVertical>();
3449 root->Add(std::move(nwid));
3450 if (nwid_begin == nwid_end) return root; // There is no body at all.
3451
3452 if (hor_cont != nullptr && hor_cont->GetWidgetOfType(WWT_CAPTION) != nullptr && hor_cont->GetWidgetOfType(WWT_SHADEBOX) != nullptr) {
3453 /* If the first widget has a title bar and a shade box, silently add a shade selection widget in the tree. */
3454 auto shade_stack = std::make_unique<NWidgetStacked>(INVALID_WIDGET);
3455 *shade_select = shade_stack.get();
3456 /* Load the remaining parts into the shade stack. */
3457 shade_stack->Add(MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3458 root->Add(std::move(shade_stack));
3459 return root;
3460 }
3461
3462 /* Load the remaining parts into 'root'. */
3463 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3464}
3465
3476std::unique_ptr<NWidgetBase> MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
3477{
3478 assert(max_length >= 1);
3479 std::unique_ptr<NWidgetVertical> vert = nullptr; // Storage for all rows.
3480 std::unique_ptr<NWidgetHorizontal> hor = nullptr; // Storage for buttons in one row.
3481 int hor_length = 0;
3482
3483 Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZoomLevel::Normal);
3484 sprite_size.width += WidgetDimensions::unscaled.matrix.Horizontal();
3485 sprite_size.height += WidgetDimensions::unscaled.matrix.Vertical();
3486
3487 for (WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3488 /* Ensure there is room in 'hor' for another button. */
3489 if (hor_length == max_length) {
3490 if (vert == nullptr) vert = std::make_unique<NWidgetVertical>();
3491 vert->Add(std::move(hor));
3492 hor = nullptr;
3493 hor_length = 0;
3494 }
3495 if (hor == nullptr) {
3496 hor = std::make_unique<NWidgetHorizontal>();
3497 hor_length = 0;
3498 }
3499
3500 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, button_colour, widnum);
3501 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3502 panel->SetFill(1, 1);
3503 if (resizable) panel->SetResize(1, 0);
3504 panel->SetToolTip(button_tooltip);
3505 hor->Add(std::move(panel));
3506 hor_length++;
3507 }
3508 if (vert == nullptr) return hor; // All buttons fit in a single row.
3509
3510 if (hor_length > 0 && hor_length < max_length) {
3511 /* Last row is partial, add a spacer at the end to force all buttons to the left. */
3512 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3513 spc->SetFill(1, 1);
3514 if (resizable) spc->SetResize(1, 0);
3515 hor->Add(std::move(spc));
3516 }
3517 if (hor != nullptr) vert->Add(std::move(hor));
3518 return vert;
3519}
3520
3527{
3528 assert(parent_window != nullptr);
3529 if (parent_window->nested_focus != nullptr) {
3530 for (auto &widget : this->children) {
3531 if (parent_window->nested_focus == widget.get()) {
3532 parent_window->UnfocusFocusedWidget();
3533 }
3534 }
3535 }
3536}
Class for backupping variables and making sure they are restored later.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
Nested widget with a child.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2370
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:2380
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:2316
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2247
void AdjustPaddingForZoom() override
Adjust the padding based on the user interface zoom.
Definition widget.cpp:2241
NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr< NWidgetPIPContainer > &&child=nullptr)
Constructor parent nested widgets.
Definition widget.cpp:2179
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:2213
std::unique_ptr< NWidgetPIPContainer > child
Child widget.
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2334
void Add(std::unique_ptr< NWidgetBase > &&nwid)
Add a child to the parent.
Definition widget.cpp:2194
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:2328
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:2232
Baseclass for nested widgets.
void StoreSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height)
Store size and position.
virtual TextColour GetHighlightColour() const
Get the colour of the highlighted text.
float aspect_ratio
Desired aspect ratio of widget.
virtual bool IsHighlighted() const
Whether the widget is currently highlighted or not.
virtual void AdjustPaddingForZoom()
Adjust the padding based on the user interface zoom.
Definition widget.cpp:990
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:951
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.
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.
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.
virtual void Draw(const Window *w)=0
Draw the widgets of the tree.
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.
const WidgetID index
Index of the nested widget (INVALID_WIDGET means 'not used').
virtual NWidgetBase * GetWidgetOfType(WidgetType tp)
Retrieve a widget by its type.
Definition widget.cpp:971
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 ...
uint current_y
Current vertical size (after resizing).
virtual void FillWidgetLookup(WidgetLookup &widget_lookup)
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:935
Baseclass for container widgets.
void Add(std::unique_ptr< NWidgetBase > &&wid)
Append widget wid to container.
Definition widget.cpp:1323
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1330
void UnfocusWidgets(Window *parent_window)
Unfocuses the focused widget of the window, if the focused widget is contained inside the container.
Definition widget.cpp:3526
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1338
bool IsEmpty()
Return whether the container is empty.
std::vector< std::unique_ptr< NWidgetBase > > children
Child widgets in container.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1347
void AdjustPaddingForZoom() override
Adjust the padding based on the user interface zoom.
Definition widget.cpp:1311
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:1301
Base class for a 'real' widget.
WidgetData widget_data
Data of the widget.
void SetToolTip(StringID tool_tip)
Set the tool tip of the nested widget.
Definition widget.cpp:1255
bool IsDisabled() const
Return whether the widget is disabled.
void SetSprite(SpriteID sprite)
Set sprite of the nested widget.
Definition widget.cpp:1205
NWidgetDisplayFlags 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:1245
WidgetID GetScrollbarIndex() const
Get the WidgetID of this nested widget's scrollbar.
Definition widget.cpp:1291
void SetAlignment(StringAlignment align)
Set the text/image alignment of the nested widget.
Definition widget.cpp:1273
void SetResizeWidgetType(ResizeWidgetType type)
Set the resize widget type of the nested widget.
Definition widget.cpp:1235
NWidgetCore(WidgetType tp, Colours colour, WidgetID index, uint fill_x, uint fill_y, const WidgetData &widget_data, StringID tool_tip)
Initialization of a 'real' widget.
Definition widget.cpp:1173
void SetSpriteTip(SpriteID sprite, StringID tool_tip)
Set sprite and tool tip of the nested widget.
Definition widget.cpp:1215
StringAlignment align
Alignment of text/image within widget.
FontSize text_size
Size of text within widget.
StringID GetString() const
Get the string that has been set for this nested widget.
Definition widget.cpp:1282
WidgetID scrollbar_index
Index of an attached scrollbar.
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1264
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1296
void SetString(StringID string)
Set string of the nested widget.
Definition widget.cpp:1185
TextColour text_colour
Colour of text within widget.
Colours colour
Colour of this widget.
void SetMatrixDimension(uint32_t columns, uint32_t rows)
Set the matrix dimension.
Definition widget.cpp:1226
void SetLowered(bool lowered)
Lower or raise the widget.
void SetStringTip(StringID string, StringID tool_tip)
Set string and tool tip of the nested widget.
Definition widget.cpp:1195
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:1744
Horizontal container.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1566
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:1635
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:1494
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1472
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1512
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2998
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition widget.cpp:2675
static Dimension resizebox_dimension
Cached size of a resizebox widget.
static Dimension shadebox_dimension
Cached size of a shadebox widget.
static Dimension closebox_dimension
Cached size of a closebox widget.
static Dimension stickybox_dimension
Cached size of a stickybox widget.
NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, const WidgetData &data, StringID tip)
Nested leaf widget.
Definition widget.cpp:2702
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:2811
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:3142
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2093
int count
Amount of valid elements.
void SetClicked(int clicked)
Sets the clicked element in the matrix.
Definition widget.cpp:1962
int GetCurrentElement() const
Get current element.
Definition widget.cpp:2012
Scrollbar * sb
The scrollbar we're associated with.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2017
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:2003
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:2148
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:2039
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2063
int clicked
The currently clicked element.
void SetCount(int count)
Set the number of elements in this matrix.
Definition widget.cpp:1979
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.
void SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
Set additional pre/inter/post space for the container.
Definition widget.cpp:1559
uint8_t uz_pip_inter
Unscaled space between widgets.
uint8_t pip_ratio_pre
Ratio of remaining space before first widget.
void AdjustPaddingForZoom() override
Adjust the padding based on the user interface zoom.
Definition widget.cpp:1522
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:1539
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:1048
bool UpdateSize(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1137
void AdjustPaddingForZoom() override
Adjust the padding based on the user interface zoom.
Definition widget.cpp:1030
uint min_x
Minimal horizontal size of only this widget.
FontSize uz_text_size
'Unscaled' font size, stored for resize calculation.
NWidgetResizeBase(WidgetType tp, WidgetID index, uint fill_x, uint fill_y)
Constructor for resizable nested widgets.
Definition widget.cpp:1002
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:1093
void SetToolbarMinimalSize(uint8_t toolbar_size)
Set minimal size of the widget in toolbar-icon-relative width.
Definition widget.cpp:1058
void SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
Set minimal text lines for the widget.
Definition widget.cpp:1081
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:1117
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:1158
bool UpdateVerticalSize(uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1151
void SetResize(uint resize_x, uint resize_y)
Set resize step of the widget.
Definition widget.cpp:1104
uint8_t toolbar_size
Minimal size in terms of toolbar images.
void SetAspect(float ratio, AspectFlags flags=AspectFlag::ResizeX)
Set desired aspect ratio of this widget.
Definition widget.cpp:1013
void SetMinimalSizeAbsolute(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1068
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:2619
NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index)
Scrollbar widget.
Definition widget.cpp:2577
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2598
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:1923
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1953
void SetDirty(const Window *w) const override
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:1948
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1936
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1929
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:1358
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:1399
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1429
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition widget.cpp:1453
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1419
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1438
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:1818
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1749
bool bottom_up
Set to flow the widget from bottom-to-top instead of top-to-bottom.
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition widget.cpp:2435
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2399
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2392
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition widget.cpp:2426
Scrollbar data structure.
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
bool IsVertical() const
Is the scrollbar vertical or not?
bool UpdatePosition(int difference, Scrollbar::Stepping unit=Stepping::Small)
Updates the position of the first visible element by the given amount.
@ Big
Step in cap units.
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:2458
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:2532
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:2479
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:87
static constexpr uint WD_DROPDOWN_HEIGHT
Minimum height of a drop down widget.
Definition window_gui.h:88
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
RectPadding hscrollbar
Padding inside horizontal scrollbar buttons.
Definition window_gui.h:37
RectPadding vscrollbar
Padding inside vertical scrollbar buttons.
Definition window_gui.h:36
static constexpr uint WD_CLOSEBOX_WIDTH
Minimum width of a close box widget.
Definition window_gui.h:86
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
static constexpr uint WD_RESIZEBOX_WIDTH
Minimum width of a resize box widget.
Definition window_gui.h:85
static constexpr uint WD_STICKYBOX_WIDTH
Minimum width of a standard sticky box widget.
Definition window_gui.h:82
static constexpr uint WD_DEBUGBOX_WIDTH
Minimum width of a standard debug box widget.
Definition window_gui.h:83
static constexpr uint WD_DEFSIZEBOX_WIDTH
Minimum width of a standard defsize box widget.
Definition window_gui.h:84
static constexpr uint WD_SHADEBOX_WIDTH
Distances used in drawing widgets.
Definition window_gui.h:81
TypedIndexContainer< std::array< Colours, MAX_COMPANIES >, CompanyID > _company_colours
NOSAVE: can be determined from company structs.
Functions related to companies.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition gfx.cpp:717
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:972
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:900
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:669
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:464
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:1038
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
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:1573
Dimension GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
Definition widget.cpp:85
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:70
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
FontSize
Available font sizes.
Definition gfx_type.h:248
@ FS_BEGIN
First font.
Definition gfx_type.h:255
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
StringAlignment
How to align the to-be drawn text.
Definition gfx_type.h:387
@ SA_TOP
Top align the text.
Definition gfx_type.h:393
@ SA_LEFT
Left align the text.
Definition gfx_type.h:388
@ SA_HOR_MASK
Mask for horizontal alignment.
Definition gfx_type.h:391
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:390
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:389
@ SA_VERT_MASK
Mask for vertical alignment.
Definition gfx_type.h:396
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:400
@ SA_BOTTOM
Bottom align the text.
Definition gfx_type.h:395
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
@ SA_VERT_CENTER
Vertically center the text.
Definition gfx_type.h:394
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:307
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:346
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
Definition gfx_type.h:347
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
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:3434
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:3415
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart SetAlignment(StringAlignment align)
Widget part function for setting the alignment of text/images.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:980
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:1521
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
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
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:393
static constexpr PixelColour PC_BLACK
Black palette colour.
static constexpr PixelColour PC_WHITE
White 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:60
void DrawBoolButton(int x, int y, Colours button_colour, Colours background, bool state, bool clickable)
Draw a toggle button.
Functions for setting GUIs.
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
#define SETTING_BUTTON_HEIGHT
Height of setting buttons.
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:1546
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition sprites.h:1616
static const PaletteID PALETTE_NEWSPAPER
Recolour sprite for newspaper-greying.
Definition sprites.h:1618
Definition of base types and functions in a cross-platform compatible way.
The colour translation of GRF's strings.
static constexpr PixelColour _string_colourmap[17]
Colour mapping for TextColour.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
Functions related to OTTD's strings.
Types related to strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_LTR
Text is written left-to-right by default.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
T y
Y coordinate.
T x
X coordinate.
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
StringAlignment align
Alignment of text/image.
WidgetData 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.
Colour for pixel/line drawing.
Definition gfx_type.h:405
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.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect CentreToHeight(int height) const
Centre a vertical dimension within this Rect.
int Height() const
Get height of Rect.
Rect WithY(int new_top, int new_bottom) const
Create a new Rect, replacing the top and bottom coordiates.
Rect WithX(int new_left, int new_right) const
Create a new Rect, replacing the left and right coordiates.
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
Container with the data associated to a single widget.
Data structure for an opened window.
Definition window_gui.h:274
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:835
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:786
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:319
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:518
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). INVALID_WIDGET if no widget has mouse ca...
Definition window_gui.h:327
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
Definition window_gui.h:724
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:818
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:483
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
Definition window_gui.h:867
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:317
int left
x position of left edge of the window
Definition window_gui.h:310
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:563
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:320
int top
y position of top edge of the window
Definition window_gui.h:311
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:347
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:323
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:223
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:990
WindowFlags flags
Window flags.
Definition window_gui.h:301
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:322
int height
Height of the window (number of pixels down in y direction).
Definition window_gui.h:313
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
Functions related to transparency.
TransparencyOptionBits _transparency_opt
The bits that should be transparent.
uint TransparencyOptionBits
transparency option bits
@ TO_TEXT
loading and cost/income text
@ TO_SIGNS
signs
NWidgetPartContainer container
Part with container flags.
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.
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:218
Functions related to (drawing on) viewports.
static void DrawCloseBox(const Rect &r, Colours colour)
Draw a close box.
Definition widget.cpp:717
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:309
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition widget.cpp:2549
void ApplyNWidgetPartAttribute(const NWidgetPart &nwid, NWidgetBase *dest)
Apply an attribute NWidgetPart to an NWidget.
Definition widget.cpp:3171
static std::pair< int, int > HandleScrollbarHittest(const Scrollbar *sb, int mi, int ma, bool horizontal)
Compute the vertical position of the draggable part of scrollbar.
Definition widget.cpp:169
static void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
Draw a defsize box.
Definition widget.cpp:678
Dimension GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
Definition widget.cpp:85
bool IsContainerWidgetType(WidgetType tp)
Test if WidgetType is a container widget.
Definition widget.cpp:3358
static void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
Draw a NewGRF debug box.
Definition widget.cpp:689
static void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
Draw a sticky box.
Definition widget.cpp:667
static void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
Draw a frame widget.
Definition widget.cpp:603
static void DrawImageTextButtons(const Rect &r, Colours colour, bool clicked, SpriteID img, TextColour text_colour, const std::string &text, StringAlignment align, FontSize fs)
Draw a button with image and rext.
Definition widget.cpp:384
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:140
static Dimension _toolbar_image_size
Cached dimension of maximal toolbar sprite size.
Definition widget.cpp:92
static void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
Draw a matrix widget.
Definition widget.cpp:463
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:562
static void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, std::string_view str, StringAlignment align)
Draw a button with a dropdown (WWT_DROPDOWN and NWID_BUTTON_DROPDOWN).
Definition widget.cpp:768
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:70
static std::unique_ptr< NWidgetBase > MakeNWidget(const NWidgetPart &nwid)
Make NWidget from an NWidgetPart.
Definition widget.cpp:3287
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:98
void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
Draw a caption bar.
Definition widget.cpp:738
static void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
Draw a shade box.
Definition widget.cpp:656
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:294
Dimension GetToolbarMaximalImageSize()
Get maximal square size of a toolbar image.
static void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
Draw an image button.
Definition widget.cpp:364
static bool IsAttributeWidgetPartType(WidgetType tp)
Test if (an NWidgetPart) WidgetType is an attribute widget part type.
Definition widget.cpp:3160
static void DrawLabel(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
Draw the label-part of a widget.
Definition widget.cpp:412
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:270
static void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
Draw a resize box.
Definition widget.cpp:702
static void DrawText(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
Draw text.
Definition widget.cpp:429
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:205
static void DrawInset(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
Draw an inset widget.
Definition widget.cpp:447
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:3371
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:521
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
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:3476
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h:35
@ 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:40
@ WPT_FILL
Widget part for specifying fill.
Definition widget_type.h:84
@ WWT_PUSHIMGTEXTBTN
Normal push-button (no toggle button) with image and text caption.
@ WPT_ALIGNMENT
Widget part for specifying text/image alignment.
Definition widget_type.h:90
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:41
@ 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:42
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WPT_MINSIZE
Widget part for specifying minimal size.
Definition widget_type.h:82
@ WWT_PUSHARROWBTN
Normal push-button (no toggle button) with arrow caption.
@ WWT_LABEL
Centered label.
Definition widget_type.h:48
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:74
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:70
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ WWT_ARROWBTN
(Toggle) Button with an arrow
Definition widget_type.h:43
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:44
@ WPT_SCROLLBAR
Widget part for attaching a scrollbar.
Definition widget_type.h:91
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WPT_ASPECT
Widget part for specifying aspect ratio.
Definition widget_type.h:92
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WPT_RESIZE
Widget part for specifying resizing.
Definition widget_type.h:81
@ WWT_IMGTEXTBTN
(Toggle) Button with image and text
Definition widget_type.h:47
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WPT_TOOLBARSIZE
Widget part for specifying minimal size in terms of toolbar images.
Definition widget_type.h:93
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ WPT_TEXTSTYLE
Widget part for specifying text colour.
Definition widget_type.h:89
@ WPT_PADDING
Widget part for specifying a padding.
Definition widget_type.h:86
@ WWT_BOOLBTN
Standard boolean toggle button.
Definition widget_type.h:46
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_TEXTBTN_2
(Toggle) Button with diff text when clicked
Definition widget_type.h:45
@ WWT_FRAME
Frame.
Definition widget_type.h:51
@ WPT_MINTEXTLINES
Widget part for specifying minimal number of lines of text.
Definition widget_type.h:83
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition widget_type.h:63
@ WPT_ATTRIBUTE_END
End marker for attribute NWidgetPart types.
Definition widget_type.h:94
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:75
@ WPT_ENDCONTAINER
Widget part to denote end of a container.
Definition widget_type.h:97
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WPT_PIPRATIO
Widget part for specifying pre/inter/post ratio for containers.
Definition widget_type.h:88
@ WPT_DATATIP
Widget part for specifying data and tooltip.
Definition widget_type.h:85
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ WPT_ATTRIBUTE_BEGIN
Begin marker for attribute NWidgetPart types.
Definition widget_type.h:80
@ WPT_PIPSPACE
Widget part for specifying pre/inter/post space for containers.
Definition widget_type.h:87
@ NWID_MATRIX
Matrix container.
Definition widget_type.h:69
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:73
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:61
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:49
@ NWID_HORIZONTAL_LTR
Horizontal container that doesn't change the order of the widgets for RTL languages.
Definition widget_type.h:67
@ WPT_FUNCTION
Widget part for calling a user function.
Definition widget_type.h:96
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX).
Definition widget_type.h:54
@ NWID_LAYER
Layered widgets, all visible together.
Definition widget_type.h:72
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
@ ScrollbarDown
Down-button is lowered bit.
@ NoTransparency
Viewport is never transparent.
@ ShadeDimmed
Display dimmed colours in the viewport.
@ DropdownActive
Dropdown menu of the button dropdown widget is active.
@ ScrollbarUp
Up-button is lowered bit.
uint ComputeMaxSize(uint base, uint max_space, uint step)
Return the biggest possible size of a nested widget.
@ 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.
@ EqualSize
Containers should keep all their (resizing) children equally large.
@ BigFirst
Allocate space to biggest resize first.
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition().
@ Resize
Resize the nested widget tree.
@ Smallest
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
@ Right
Force the arrow to the right.
Definition widget_type.h:23
@ Left
Force the arrow to the left.
Definition widget_type.h:22
@ Decrease
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:20
@ Increase
Arrow to the right or in case of RTL to the left.
Definition widget_type.h:21
std::map< WidgetID, class NWidgetBase * > WidgetLookup
Lookup between widget IDs and NWidget objects.
ResizeWidgetType
WidgetData values for a resize box widget.
Definition widget_type.h:27
@ ShowBevel
Bevel of resize box is shown.
Definition widget_type.h:28
@ ResizeY
Resize vertically to reach desired aspect ratio.
@ ResizeX
Resize horizontally to reach desired aspect ratio.
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition window.cpp:78
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:309
@ Transparent
Makes the background transparent if set.
Definition window_gui.h:25
@ BorderOnly
Draw border only, no background.
Definition window_gui.h:26
@ Darkened
If set the background is darker, allows for lowered frames with normal background colour when used wi...
Definition window_gui.h:28
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed).
Definition window_gui.h:27
@ SizingLeft
Window is being resized towards the left.
Definition window_gui.h:231
@ Highlighted
Window has a widget that has a highlight.
Definition window_gui.h:236
@ SizingRight
Window is being resized towards the right.
Definition window_gui.h:230
@ WhiteBorder
Window white border counter bit mask.
Definition window_gui.h:235
@ Sticky
Window is made sticky by user.
Definition window_gui.h:233
SortButtonState
State of a sort direction button.
Definition window_gui.h:217
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:219
@ SBS_OFF
Do not sort (with this button).
Definition window_gui.h:218
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
Definition window_type.h:23
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::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:20
@ Normal
The normal zoom level.
Definition zoom_type.h:26