OpenTTD Source 15.3
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
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
69{
70 Point offset;
71 Dimension d = GetSpriteSize(sprid, &offset, ZoomLevel::Normal);
72 d.width -= offset.x;
73 d.height -= offset.y;
74 return ScaleGUITrad(d);
75}
76
84{
86 uint x = std::max(d.width, d.height);
87 return {x, x};
88}
89
92
97{
104 } else {
106 }
121
127
129}
130
138static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align)
139{
140 Point p;
141 /* In case we have a RTL language we swap the alignment. */
142 if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
143 switch (align & SA_HOR_MASK) {
144 case SA_LEFT: p.x = r.left; break;
145 case SA_HOR_CENTER: p.x = CentreBounds(r.left, r.right, d.width); break;
146 case SA_RIGHT: p.x = r.right + 1 - d.width; break;
147 default: NOT_REACHED();
148 }
149 switch (align & SA_VERT_MASK) {
150 case SA_TOP: p.y = r.top; break;
151 case SA_VERT_CENTER: p.y = CentreBounds(r.top, r.bottom, d.height); break;
152 case SA_BOTTOM: p.y = r.bottom + 1 - d.height; break;
153 default: NOT_REACHED();
154 }
155 return p;
156}
157
167static std::pair<int, int> HandleScrollbarHittest(const Scrollbar *sb, int mi, int ma, bool horizontal)
168{
169 /* Base for reversion */
170 int rev_base = mi + ma;
171 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
172
173 mi += button_size; // now points to just after the up/left-button
174 ma -= button_size; // now points to just before the down/right-button
175
176 int count = sb->GetCount();
177 int cap = sb->GetCapacity();
178
179 if (count > cap) {
180 int height = ma + 1 - mi;
181 int slider_height = std::max(button_size, cap * height / count);
182 height -= slider_height;
183
184 mi += height * sb->GetPosition() / (count - cap);
185 ma = mi + slider_height - 1;
186 }
187
188 /* Reverse coordinates for RTL. */
189 if (horizontal && _current_text_dir == TD_RTL) return {rev_base - ma, rev_base - mi};
190
191 return {mi, ma};
192}
193
203static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, int y, int mi, int ma)
204{
205 int pos;
206 int button_size;
207 bool rtl = false;
208 bool changed = false;
209
210 if (sb->type == NWID_HSCROLLBAR) {
211 pos = x;
212 rtl = _current_text_dir == TD_RTL;
213 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
214 } else {
215 pos = y;
216 button_size = NWidgetScrollbar::GetVerticalDimension().height;
217 }
218 if (pos < mi + button_size) {
219 /* Pressing the upper button? */
221 if (_scroller_click_timeout <= 1) {
222 _scroller_click_timeout = 3;
223 changed = sb->UpdatePosition(rtl ? 1 : -1);
224 }
225 w->mouse_capture_widget = sb->GetIndex();
226 } else if (pos >= ma - button_size) {
227 /* Pressing the lower button? */
229
230 if (_scroller_click_timeout <= 1) {
231 _scroller_click_timeout = 3;
232 changed = sb->UpdatePosition(rtl ? -1 : 1);
233 }
234 w->mouse_capture_widget = sb->GetIndex();
235 } else {
236 auto [start, end] = HandleScrollbarHittest(sb, mi, ma, sb->type == NWID_HSCROLLBAR);
237
238 if (pos < start) {
239 changed = sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::SS_BIG);
240 } else if (pos > end) {
241 changed = sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::SS_BIG);
242 } else {
243 _scrollbar_start_pos = start - mi - button_size;
244 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
245 w->mouse_capture_widget = sb->GetIndex();
246 _cursorpos_drag_start = _cursor.pos;
247 }
248 }
249
250 if (changed) {
251 /* Position changed so refresh the window */
252 w->OnScrollbarScroll(sb->GetIndex());
253 w->SetDirty();
254 } else {
255 /* No change so only refresh this scrollbar */
256 sb->SetDirty(w);
257 }
258}
259
268void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
269{
270 int mi, ma;
271
272 if (nw->type == NWID_HSCROLLBAR) {
273 mi = nw->pos_x;
274 ma = nw->pos_x + nw->current_x;
275 } else {
276 mi = nw->pos_y;
277 ma = nw->pos_y + nw->current_y;
278 }
279 NWidgetScrollbar *scrollbar = dynamic_cast<NWidgetScrollbar*>(nw);
280 assert(scrollbar != nullptr);
281 ScrollbarClickPositioning(w, scrollbar, x, y, mi, ma);
282}
283
292WidgetID GetWidgetFromPos(const Window *w, int x, int y)
293{
294 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
295 return (nw != nullptr) ? nw->GetIndex() : INVALID_WIDGET;
296}
297
307void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
308{
309 if (flags.Test(FrameFlag::Transparent)) {
310 GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
311 } else {
312 assert(colour < COLOUR_END);
313
314 const PixelColour dark = GetColourGradient(colour, SHADE_DARK);
315 const PixelColour medium_dark = GetColourGradient(colour, SHADE_LIGHT);
316 const PixelColour medium_light = GetColourGradient(colour, SHADE_LIGHTER);
317 const PixelColour light = GetColourGradient(colour, SHADE_LIGHTEST);
318 PixelColour interior;
319
320 Rect outer = {left, top, right, bottom}; // Outside rectangle
321 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
322
323 if (flags.Test(FrameFlag::Lowered)) {
324 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark); // Left
325 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark); // Top
326 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light); // Right
327 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light); // Bottom
328 interior = (flags.Test(FrameFlag::Darkened) ? medium_dark : medium_light);
329 } else {
330 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light); // Left
331 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light); // Top
332 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark); // Right
333 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark); // Bottom
334 interior = medium_dark;
335 }
336 if (!flags.Test(FrameFlag::BorderOnly)) {
337 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior); // Inner
338 }
339 }
340}
341
342void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, StringAlignment align)
343{
344 Point offset;
345 Dimension d = GetSpriteSize(img, &offset);
346 d.width -= offset.x;
347 d.height -= offset.y;
348
349 Point p = GetAlignedPosition(r, d, align);
350 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
351}
352
362static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
363{
364 assert(img != 0);
365 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
366
367 if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2.
368 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
369}
370
382static inline void DrawImageTextButtons(const Rect &r, Colours colour, bool clicked, SpriteID img, TextColour text_colour, const std::string &text, StringAlignment align, FontSize fs)
383{
384 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
385
386 bool rtl = _current_text_dir == TD_RTL;
387 int image_width = img != 0 ? std::max<int>(GetSquareScaledSpriteSize(img).width, r.Shrink(WidgetDimensions::scaled.framerect).Height()) : 0;
388 Rect r_img = r.Shrink(WidgetDimensions::scaled.framerect).WithWidth(image_width, rtl);
389 Rect r_text = r.Shrink(WidgetDimensions::scaled.framerect).Indent(image_width + WidgetDimensions::scaled.hsep_wide, rtl);
390
391 if (img != 0) {
392 DrawSpriteIgnorePadding(img, PAL_NONE, r_img, SA_HOR_CENTER | (align & SA_VERT_MASK));
393 }
394
395 if (!text.empty()) {
396 Dimension d = GetStringBoundingBox(text, fs);
397 Point p = GetAlignedPosition(r_text, d, align);
398 DrawString(r_text.left, r_text.right, p.y, text, text_colour, align, false, fs);
399 }
400}
401
410static inline void DrawLabel(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
411{
412 if (str.empty()) return;
413
414 Dimension d = GetStringBoundingBox(str, fs);
415 Point p = GetAlignedPosition(r, d, align);
416 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
417}
418
427static inline void DrawText(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
428{
429 if (str.empty()) return;
430
431 Dimension d = GetStringBoundingBox(str, fs);
432 Point p = GetAlignedPosition(r, d, align);
433 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
434}
435
445static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
446{
448 if (!str.empty()) DrawString(r.Shrink(WidgetDimensions::scaled.inset), str, text_colour, align, false, fs);
449}
450
461static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
462{
463 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
464
465 int column_width; // Width of a single column in the matrix.
466 if (num_columns == 0) {
467 column_width = resize_x;
468 num_columns = r.Width() / column_width;
469 } else {
470 column_width = r.Width() / num_columns;
471 }
472
473 int row_height; // Height of a single row in the matrix.
474 if (num_rows == 0) {
475 row_height = resize_y;
476 num_rows = r.Height() / row_height;
477 } else {
478 row_height = r.Height() / num_rows;
479 }
480
481 PixelColour col = GetColourGradient(colour, SHADE_LIGHTER);
482
483 int x = r.left;
484 for (int ctr = num_columns; ctr > 1; ctr--) {
485 x += column_width;
486 GfxFillRect(x, r.top + WidgetDimensions::scaled.bevel.top, x + WidgetDimensions::scaled.bevel.left - 1, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
487 }
488
489 x = r.top;
490 for (int ctr = num_rows; ctr > 1; ctr--) {
491 x += row_height;
493 }
494
495 col = GetColourGradient(colour, SHADE_NORMAL);
496
497 x = r.left - 1;
498 for (int ctr = num_columns; ctr > 1; ctr--) {
499 x += column_width;
500 GfxFillRect(x - WidgetDimensions::scaled.bevel.right + 1, r.top + WidgetDimensions::scaled.bevel.top, x, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
501 }
502
503 x = r.top - 1;
504 for (int ctr = num_rows; ctr > 1; ctr--) {
505 x += row_height;
506 GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x - WidgetDimensions::scaled.bevel.bottom + 1, r.right - WidgetDimensions::scaled.bevel.right, x, col);
507 }
508}
509
519static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
520{
521 int height = NWidgetScrollbar::GetVerticalDimension().height;
522
523 /* draw up/down buttons */
524 DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
525 DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
526
527 PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
528 PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
529
530 /* draw "shaded" background */
531 Rect bg = r.Shrink(0, height);
532 GfxFillRect(bg, c2);
534
535 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
536 int left = r.left + r.Width() * 3 / 11; /* left track is positioned 3/11ths from the left */
537 int right = r.left + r.Width() * 8 / 11; /* right track is positioned 8/11ths from the left */
538 const uint8_t bl = WidgetDimensions::scaled.bevel.left;
539 const uint8_t br = WidgetDimensions::scaled.bevel.right;
540
541 /* draw shaded lines */
542 GfxFillRect(bg.WithX(left - bl, left - 1), c1);
543 GfxFillRect(bg.WithX(left, left + br - 1), c2);
544 GfxFillRect(bg.WithX(right - bl, right - 1), c1);
545 GfxFillRect(bg.WithX(right, right + br - 1), c2);
546
547 auto [top, bottom] = HandleScrollbarHittest(scrollbar, r.top, r.bottom, false);
548 DrawFrameRect(r.left, top, r.right, bottom, colour, bar_dragged ? FrameFlag::Lowered : FrameFlags{});
549}
550
560static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
561{
562 int width = NWidgetScrollbar::GetHorizontalDimension().width;
563
564 DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
565 DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
566
567 PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
568 PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
569
570 /* draw "shaded" background */
571 Rect bg = r.Shrink(width, 0);
572 GfxFillRect(bg, c2);
574
575 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
576 int top = r.top + r.Height() * 3 / 11; /* top track is positioned 3/11ths from the top */
577 int bottom = r.top + r.Height() * 8 / 11; /* bottom track is positioned 8/11ths from the top */
578 const uint8_t bt = WidgetDimensions::scaled.bevel.top;
579 const uint8_t bb = WidgetDimensions::scaled.bevel.bottom;
580
581 /* draw shaded lines */
582 GfxFillRect(bg.WithY(top - bt, top - 1), c1);
583 GfxFillRect(bg.WithY(top, top + bb - 1), c2);
584 GfxFillRect(bg.WithY(bottom - bt, bottom - 1), c1);
585 GfxFillRect(bg.WithY(bottom, bottom + bb - 1), c2);
586
587 /* draw actual scrollbar */
588 auto [left, right] = HandleScrollbarHittest(scrollbar, r.left, r.right, true);
589 DrawFrameRect(left, r.top, right, r.bottom, colour, bar_dragged ? FrameFlag::Lowered : FrameFlags{});
590}
591
601static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
602{
603 int x2 = r.left; // by default the left side is the left side of the widget
604
605 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);
606
607 PixelColour c1 = GetColourGradient(colour, SHADE_DARK);
608 PixelColour c2 = GetColourGradient(colour, SHADE_LIGHTEST);
609
610 /* If the frame has text, adjust the top bar to fit half-way through */
611 Rect inner = r.Shrink(ScaleGUITrad(1));
612 if (!str.empty()) inner.top = r.top + GetCharacterHeight(FS_NORMAL) / 2;
613
614 Rect outer = inner.Expand(WidgetDimensions::scaled.bevel);
615 Rect inside = inner.Shrink(WidgetDimensions::scaled.bevel);
616
617 if (_current_text_dir == TD_LTR) {
618 /* Line from upper left corner to start of text */
619 GfxFillRect(outer.left, outer.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
620 GfxFillRect(inner.left, inner.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
621
622 /* Line from end of text to upper right corner */
623 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
624 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
625 } else {
626 /* Line from upper left corner to start of text */
627 GfxFillRect(outer.left, outer.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
628 GfxFillRect(inner.left, inner.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
629
630 /* Line from end of text to upper right corner */
631 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
632 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
633 }
634
635 /* Line from upper left corner to bottom left corner */
636 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
637 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
638
639 /* Line from upper right corner to bottom right corner */
640 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
641 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
642
643 /* Line from bottom left corner to bottom right corner */
644 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
645 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
646}
647
654static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
655{
656 DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER);
657}
658
665static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
666{
667 DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER);
668}
669
676static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
677{
678 DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER);
679}
680
687static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
688{
689 DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER);
690}
691
700static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
701{
702 if (bevel) {
703 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
704 } else if (clicked) {
706 }
707 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));
708}
709
715static inline void DrawCloseBox(const Rect &r, Colours colour)
716{
717 if (colour != COLOUR_WHITE) DrawFrameRect(r, colour, {});
718 Point offset;
719 Dimension d = GetSpriteSize(SPR_CLOSEBOX, &offset);
720 d.width -= offset.x;
721 d.height -= offset.y;
722 int s = ScaleSpriteTrad(1); /* Offset to account for shadow of SPR_CLOSEBOX */
723 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);
724}
725
736void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
737{
738 bool company_owned = owner < MAX_COMPANIES;
739
743
744 if (company_owned) {
746 }
747
748 if (str.empty()) return;
749
751 Point p = GetAlignedPosition(r, d, align);
752 DrawString(r.left + WidgetDimensions::scaled.captiontext.left, r.right - WidgetDimensions::scaled.captiontext.left, p.y, str, text_colour, align, false, fs);
753}
754
766static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, std::string_view str, StringAlignment align)
767{
768 bool rtl = _current_text_dir == TD_RTL;
769
770 Rect text = r.Indent(NWidgetLeaf::dropdown_dimension.width, !rtl);
771 DrawFrameRect(text, colour, clicked_button ? FrameFlag::Lowered : FrameFlags{});
772 if (!str.empty()) {
773 text = text.CentreToHeight(GetCharacterHeight(FS_NORMAL)).Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero);
774 DrawString(text, str, TC_BLACK, align);
775 }
776
777 Rect button = r.WithWidth(NWidgetLeaf::dropdown_dimension.width, !rtl);
778 DrawImageButtons(button, WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
779}
780
785{
786 this->nested_root->Draw(this);
787
789 DrawFrameRect(0, 0, this->width - 1, this->height - 1, COLOUR_WHITE, FrameFlag::BorderOnly);
790 }
791
793 extern bool _window_highlight_colour;
794 for (const auto &pair : this->widget_lookup) {
795 const NWidgetBase *widget = pair.second;
796 if (!widget->IsHighlighted()) continue;
797
798 Rect outer = widget->GetCurrentRect();
799 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1);
800
801 PixelColour colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE];
802
803 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
804 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
805 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
806 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
807 }
808 }
809}
810
817{
818 if (state == SBS_OFF) return;
819
820 assert(!this->widget_lookup.empty());
821 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
822
823 /* Sort button uses the same sprites as vertical scrollbar */
824 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
825
826 DrawSpriteIgnorePadding(state == SBS_DOWN ? SPR_ARROW_DOWN : SPR_ARROW_UP, PAL_NONE, r.WithWidth(dim.width, _current_text_dir == TD_LTR), SA_CENTER);
827}
828
834{
835 return NWidgetScrollbar::GetVerticalDimension().width + 1;
836}
837
838bool _draw_widget_outlines;
839
840static void DrawOutline(const Window *, const NWidgetBase *wid)
841{
842 if (!_draw_widget_outlines || wid->current_x == 0 || wid->current_y == 0) return;
843
844 DrawRectOutline(wid->GetCurrentRect(), PC_WHITE, 1, 4);
845}
846
934{
935 if (this->index >= 0) widget_lookup[this->index] = this;
936}
937
949void NWidgetBase::SetDirty(const Window *w) const
950{
951 int abs_left = w->left + this->pos_x;
952 int abs_top = w->top + this->pos_y;
953 AddDirtyBlock(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
954}
955
970{
971 return (this->type == tp) ? this : nullptr;
972}
973
974void NWidgetBase::ApplyAspectRatio()
975{
976 if (this->aspect_ratio == 0) return;
977 if (this->smallest_x == 0 || this->smallest_y == 0) return;
978
979 uint x = this->smallest_x;
980 uint y = this->smallest_y;
981 if (this->aspect_flags.Test(AspectFlag::ResizeX)) x = std::max(this->smallest_x, static_cast<uint>(this->smallest_y * std::abs(this->aspect_ratio)));
982 if (this->aspect_flags.Test(AspectFlag::ResizeY)) y = std::max(this->smallest_y, static_cast<uint>(this->smallest_x / std::abs(this->aspect_ratio)));
983
984 this->smallest_x = x;
985 this->smallest_y = y;
986}
987
988void NWidgetBase::AdjustPaddingForZoom()
989{
990 this->padding = ScaleGUITrad(this->uz_padding);
991}
992
999NWidgetResizeBase::NWidgetResizeBase(WidgetType tp, WidgetID index, uint fill_x, uint fill_y) : NWidgetBase(tp, index)
1000{
1001 this->fill_x = fill_x;
1002 this->fill_y = fill_y;
1003}
1004
1011{
1012 this->aspect_ratio = ratio;
1013 this->aspect_flags = flags;
1014}
1015
1022void NWidgetResizeBase::SetAspect(int x_ratio, int y_ratio, AspectFlags flags)
1023{
1024 this->SetAspect(static_cast<float>(x_ratio) / static_cast<float>(y_ratio), flags);
1025}
1026
1027void NWidgetResizeBase::AdjustPaddingForZoom()
1028{
1029 if (!this->absolute) {
1030 this->min_x = ScaleGUITrad(this->uz_min_x);
1031 this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1032 if (this->toolbar_size > 0) {
1033 this->min_x = std::max(this->min_x, this->toolbar_size * _toolbar_image_size.width + WidgetDimensions::scaled.imgbtn.Horizontal());
1034 this->min_y = std::max(this->min_y, _toolbar_image_size.height + WidgetDimensions::scaled.imgbtn.Vertical());
1035 }
1036 }
1037 NWidgetBase::AdjustPaddingForZoom();
1038}
1039
1045void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y)
1046{
1047 this->uz_min_x = std::max(this->uz_min_x, min_x);
1048 this->uz_min_y = std::max(this->uz_min_y, min_y);
1049}
1050
1056{
1057 this->toolbar_size = toolbar_size;
1058}
1059
1066{
1067 this->absolute = true;
1068 this->min_x = std::max(this->min_x, min_x);
1069 this->min_y = std::max(this->min_y, min_y);
1070}
1071
1078void NWidgetResizeBase::SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
1079{
1080 this->uz_text_lines = min_lines;
1081 this->uz_text_spacing = spacing;
1082 this->uz_text_size = size;
1083}
1084
1090void NWidgetResizeBase::SetFill(uint fill_x, uint fill_y)
1091{
1092 this->fill_x = fill_x;
1093 this->fill_y = fill_y;
1094}
1095
1101void NWidgetResizeBase::SetResize(uint resize_x, uint resize_y)
1102{
1103 this->resize_x = resize_x;
1104 this->resize_y = resize_y;
1105}
1106
1114bool NWidgetResizeBase::UpdateMultilineWidgetSize(const std::string &str, int max_lines)
1115{
1116 int y = GetStringHeight(str, this->current_x);
1117 if (y > max_lines * GetCharacterHeight(FS_NORMAL)) {
1118 /* Text at the current width is too tall, so try to guess a better width. */
1120 d.height *= max_lines;
1121 d.width /= 2;
1122 return this->UpdateSize(d.width, d.height);
1123 }
1124 return this->UpdateVerticalSize(y);
1125}
1126
1134bool NWidgetResizeBase::UpdateSize(uint min_x, uint min_y)
1135{
1136 if (min_x == this->min_x && min_y == this->min_y) return false;
1137 this->min_x = min_x;
1138 this->min_y = min_y;
1139 return true;
1140}
1141
1149{
1150 if (min_y == this->min_y) return false;
1151 this->min_y = min_y;
1152 return true;
1153}
1154
1155void NWidgetResizeBase::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1156{
1157 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1158}
1159
1170NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, WidgetID index, uint fill_x, uint fill_y, const WidgetData &widget_data, StringID tool_tip) : NWidgetResizeBase(tp, index, fill_x, fill_y)
1171{
1172 this->colour = colour;
1173 this->widget_data = widget_data;
1174 this->SetToolTip(tool_tip);
1175 this->text_colour = tp == WWT_CAPTION ? TC_WHITE : TC_BLACK;
1176}
1177
1183{
1184 this->widget_data.string = string;
1185}
1186
1193{
1194 this->SetString(string);
1195 this->SetToolTip(tool_tip);
1196}
1197
1203{
1204 this->widget_data.sprite = sprite;
1205}
1206
1213{
1214 this->SetSprite(sprite);
1215 this->SetToolTip(tool_tip);
1216}
1217
1223void NWidgetCore::SetMatrixDimension(uint32_t columns, uint32_t rows)
1224{
1225 this->widget_data.matrix = { columns, rows };
1226}
1227
1233{
1234 this->widget_data.resize_widget_type = type;
1235}
1236
1243{
1244 this->text_colour = colour;
1245 this->text_size = size;
1246}
1247
1253{
1254 this->tool_tip = tool_tip;
1255}
1256
1262{
1263 return this->tool_tip;
1264}
1265
1271{
1272 this->align = align;
1273}
1274
1280{
1281 return this->widget_data.string;
1282}
1283
1289{
1290 return this->scrollbar_index;
1291}
1292
1294{
1295 return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : nullptr;
1296}
1297
1299{
1300 if (this->type == tp) return this;
1301 for (const auto &child_wid : this->children) {
1302 NWidgetBase *nwid = child_wid->GetWidgetOfType(tp);
1303 if (nwid != nullptr) return nwid;
1304 }
1305 return nullptr;
1306}
1307
1308void NWidgetContainer::AdjustPaddingForZoom()
1309{
1310 for (const auto &child_wid : this->children) {
1311 child_wid->AdjustPaddingForZoom();
1312 }
1313 NWidgetBase::AdjustPaddingForZoom();
1314}
1315
1320void NWidgetContainer::Add(std::unique_ptr<NWidgetBase> &&wid)
1321{
1322 assert(wid != nullptr);
1323 wid->parent = this;
1324 this->children.push_back(std::move(wid));
1325}
1326
1328{
1329 this->NWidgetBase::FillWidgetLookup(widget_lookup);
1330 for (const auto &child_wid : this->children) {
1331 child_wid->FillWidgetLookup(widget_lookup);
1332 }
1333}
1334
1336{
1337 for (const auto &child_wid : this->children) {
1338 child_wid->Draw(w);
1339 }
1340
1341 DrawOutline(w, this);
1342}
1343
1345{
1346 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1347
1348 for (const auto &child_wid : this->children) {
1349 NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
1350 if (nwid != nullptr) return nwid;
1351 }
1352 return nullptr;
1353}
1354
1356{
1357 /* Zero size plane selected */
1358 if (this->shown_plane >= SZSP_BEGIN) {
1359 Dimension size = {0, 0};
1360 Dimension padding = {0, 0};
1361 Dimension fill = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1362 Dimension resize = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1363 /* Here we're primarily interested in the value of resize */
1364 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
1365
1366 this->smallest_x = size.width;
1367 this->smallest_y = size.height;
1368 this->fill_x = fill.width;
1369 this->fill_y = fill.height;
1370 this->resize_x = resize.width;
1371 this->resize_y = resize.height;
1372 this->ApplyAspectRatio();
1373 return;
1374 }
1375
1376 /* First sweep, recurse down and compute minimal size and filling. */
1377 this->smallest_x = 0;
1378 this->smallest_y = 0;
1379 this->fill_x = this->IsEmpty() ? 0 : 1;
1380 this->fill_y = this->IsEmpty() ? 0 : 1;
1381 this->resize_x = this->IsEmpty() ? 0 : 1;
1382 this->resize_y = this->IsEmpty() ? 0 : 1;
1383 for (const auto &child_wid : this->children) {
1384 child_wid->SetupSmallestSize(w);
1385
1386 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1387 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1388 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1389 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1390 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1391 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1392 this->ApplyAspectRatio();
1393 }
1394}
1395
1396void NWidgetStacked::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1397{
1398 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1399 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1400
1401 if (this->shown_plane >= SZSP_BEGIN) return;
1402
1403 for (const auto &child_wid : this->children) {
1404 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1405 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1406 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1407
1408 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1409 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1410 uint child_pos_y = child_wid->padding.top;
1411
1412 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1413 }
1414}
1415
1417{
1418 /* We need to update widget_lookup later. */
1419 this->widget_lookup = &widget_lookup;
1420
1421 this->NWidgetContainer::FillWidgetLookup(widget_lookup);
1422 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1423 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(widget_lookup);
1424}
1425
1427{
1428 if (this->shown_plane >= SZSP_BEGIN) return;
1429
1430 assert(static_cast<size_t>(this->shown_plane) < this->children.size());
1431 this->children[shown_plane]->Draw(w);
1432 DrawOutline(w, this);
1433}
1434
1436{
1437 if (this->shown_plane >= SZSP_BEGIN) return nullptr;
1438
1439 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1440
1441 if (static_cast<size_t>(this->shown_plane) >= this->children.size()) return nullptr;
1442 return this->children[shown_plane]->GetWidgetFromPos(x, y);
1443}
1444
1451{
1452 if (this->shown_plane == plane) return false;
1453 this->shown_plane = plane;
1454 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1455 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(*this->widget_lookup);
1456 return true;
1457}
1458
1460public:
1462
1463 void SetupSmallestSize(Window *w) override;
1464 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override;
1465
1466 void Draw(const Window *w) override;
1467};
1468
1470{
1471 /* First sweep, recurse down and compute minimal size and filling. */
1472 this->smallest_x = 0;
1473 this->smallest_y = 0;
1474 this->fill_x = this->IsEmpty() ? 0 : 1;
1475 this->fill_y = this->IsEmpty() ? 0 : 1;
1476 this->resize_x = this->IsEmpty() ? 0 : 1;
1477 this->resize_y = this->IsEmpty() ? 0 : 1;
1478 for (const auto &child_wid : this->children) {
1479 child_wid->SetupSmallestSize(w);
1480
1481 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1482 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1483 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1484 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1485 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1486 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1487 this->ApplyAspectRatio();
1488 }
1489}
1490
1491void NWidgetLayer::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1492{
1493 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1494 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1495
1496 for (const auto &child_wid : this->children) {
1497 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1498 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1499 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1500
1501 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1502 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1503 uint child_pos_y = child_wid->padding.top;
1504
1505 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1506 }
1507}
1508
1510{
1511 /* Draw in reverse order, as layers are arranged top-down. */
1512 for (auto it = std::rbegin(this->children); it != std::rend(this->children); ++it) {
1513 (*it)->Draw(w);
1514 }
1515
1516 DrawOutline(w, this);
1517}
1518
1519void NWidgetPIPContainer::AdjustPaddingForZoom()
1520{
1521 this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1522 this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1523 this->pip_post = ScaleGUITrad(this->uz_pip_post);
1524 NWidgetContainer::AdjustPaddingForZoom();
1525}
1526
1536void NWidgetPIPContainer::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
1537{
1538 this->uz_pip_pre = pip_pre;
1539 this->uz_pip_inter = pip_inter;
1540 this->uz_pip_post = pip_post;
1541
1542 this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1543 this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1544 this->pip_post = ScaleGUITrad(this->uz_pip_post);
1545}
1546
1556void NWidgetPIPContainer::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
1557{
1558 this->pip_ratio_pre = pip_ratio_pre;
1559 this->pip_ratio_inter = pip_ratio_inter;
1560 this->pip_ratio_post = pip_ratio_post;
1561}
1562
1564{
1565 this->smallest_x = 0; // Sum of minimal size of all children.
1566 this->smallest_y = 0; // Biggest child.
1567 this->fill_x = 0; // smallest non-zero child widget fill step.
1568 this->fill_y = 1; // smallest common child fill step.
1569 this->resize_x = 0; // smallest non-zero child widget resize step.
1570 this->resize_y = 1; // smallest common child resize step.
1571 this->gaps = 0;
1572
1573 /* 1a. Forward call, collect longest/widest child length. */
1574 uint longest = 0; // Longest child found.
1575 uint max_vert_fill = 0; // Biggest vertical fill step.
1576 for (const auto &child_wid : this->children) {
1577 child_wid->SetupSmallestSize(w);
1578 longest = std::max(longest, child_wid->smallest_x);
1579 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(ST_SMALLEST));
1580 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1581 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->gaps++;
1582 }
1583 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1584 /* 1b. Make the container higher if needed to accommodate all children nicely. */
1585 [[maybe_unused]] uint max_smallest = this->smallest_y + 3 * max_vert_fill; // Upper limit to computing smallest height.
1586 uint cur_height = this->smallest_y;
1587 for (;;) {
1588 for (const auto &child_wid : this->children) {
1589 uint step_size = child_wid->GetVerticalStepSize(ST_SMALLEST);
1590 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1591 if (step_size > 1 && child_height < cur_height) { // Small step sizes or already fitting children are not interesting.
1592 uint remainder = (cur_height - child_height) % step_size;
1593 if (remainder > 0) { // Child did not fit entirely, widen the container.
1594 cur_height += step_size - remainder;
1595 assert(cur_height < max_smallest); // Safeguard against infinite height expansion.
1596 /* Remaining children will adapt to the new cur_height, thus speeding up the computation. */
1597 }
1598 }
1599 }
1600 if (this->smallest_y == cur_height) break;
1601 this->smallest_y = cur_height; // Smallest height got changed, try again.
1602 }
1603 /* 2. For containers that must maintain equal width, extend child minimal size. */
1604 for (const auto &child_wid : this->children) {
1605 child_wid->smallest_y = this->smallest_y - child_wid->padding.Vertical();
1606 child_wid->ApplyAspectRatio();
1607 longest = std::max(longest, child_wid->smallest_x);
1608 }
1610 for (const auto &child_wid : this->children) {
1611 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1612 }
1613 }
1614 /* 3. Compute smallest, fill, and resize values of the container. */
1615 for (const auto &child_wid : this->children) {
1616 this->smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1617 if (child_wid->fill_x > 0) {
1618 if (this->fill_x == 0 || this->fill_x > child_wid->fill_x) this->fill_x = child_wid->fill_x;
1619 }
1620 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1621
1622 if (child_wid->resize_x > 0) {
1623 if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x;
1624 }
1625 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1626 }
1627 if (this->fill_x == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_x = 1;
1628 /* 4. Increase by required PIP space. */
1629 this->smallest_x += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1630}
1631
1632void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1633{
1634 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1635
1636 /* Compute additional width given to us. */
1637 uint additional_length = given_width - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1638 for (const auto &child_wid : this->children) {
1639 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1640 }
1641
1642 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1643
1644 /* In principle, the additional horizontal space is distributed evenly over the available resizable children. Due to step sizes, this may not always be feasible.
1645 * To make resizing work as good as possible, first children with biggest step sizes are done. These may get less due to rounding down.
1646 * 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
1647 * of the child with the smallest non-zero stepsize.
1648 *
1649 * 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
1650 * size and position, and directly call child->AssignSizePosition() with the computed values.
1651 * 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
1652 * then we call the child.
1653 */
1654
1655 /* First loop: Find biggest stepsize, find number of children that want a piece of the pie, handle vertical size for all children,
1656 * handle horizontal size for non-resizing children.
1657 */
1658 int num_changing_childs = 0; // Number of children that can change size.
1659 uint biggest_stepsize = 0;
1660 for (const auto &child_wid : this->children) {
1661 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1662 if (hor_step > 0) {
1663 if (!flags.Test(NWidContainerFlag::BigFirst)) num_changing_childs++;
1664 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1665 } else {
1666 child_wid->current_x = child_wid->smallest_x;
1667 }
1668
1669 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1670 child_wid->current_y = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1671 }
1672
1673 /* First.5 loop: count how many children are of the biggest step size. */
1674 if (flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1675 for (const auto &child_wid : this->children) {
1676 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1677 if (hor_step == biggest_stepsize) {
1678 num_changing_childs++;
1679 }
1680 }
1681 }
1682
1683 /* Second loop: Allocate the additional horizontal space over the resizing children, starting with the biggest resize steps. */
1684 while (biggest_stepsize > 0) {
1685 uint next_biggest_stepsize = 0;
1686 for (const auto &child_wid : this->children) {
1687 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1688 if (hor_step > biggest_stepsize) continue; // Already done
1689 if (hor_step == biggest_stepsize) {
1690 uint increment = additional_length / num_changing_childs;
1691 num_changing_childs--;
1692 if (hor_step > 1) increment -= increment % hor_step;
1693 child_wid->current_x = child_wid->smallest_x + increment;
1694 additional_length -= increment;
1695 continue;
1696 }
1697 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1698 }
1699 biggest_stepsize = next_biggest_stepsize;
1700
1701 if (num_changing_childs == 0 && flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1702 /* Second.5 loop: count how many children are of the updated biggest step size. */
1703 for (const auto &child_wid : this->children) {
1704 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1705 if (hor_step == biggest_stepsize) {
1706 num_changing_childs++;
1707 }
1708 }
1709 }
1710 }
1711 assert(num_changing_childs == 0);
1712
1713 uint pre = this->pip_pre;
1714 uint inter = this->pip_inter;
1715
1716 if (additional_length > 0) {
1717 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1718 * which is never explicitly needed. */
1719 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1720 if (r > 0) {
1721 pre += this->pip_ratio_pre * additional_length / r;
1722 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1723 }
1724 }
1725
1726 /* Third loop: Compute position and call the child. */
1727 uint position = rtl ? this->current_x - pre : pre; // Place to put next child relative to origin of the container.
1728 for (const auto &child_wid : this->children) {
1729 uint child_width = child_wid->current_x;
1730 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1731 uint child_y = y + child_wid->padding.top;
1732
1733 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1734 if (child_wid->current_x != 0) {
1735 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1736 position = rtl ? position - padded_child_width : position + padded_child_width;
1737 }
1738 }
1739}
1740
1741void NWidgetHorizontalLTR::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1742{
1743 NWidgetHorizontal::AssignSizePosition(sizing, x, y, given_width, given_height, false);
1744}
1745
1747{
1748 this->smallest_x = 0; // Biggest child.
1749 this->smallest_y = 0; // Sum of minimal size of all children.
1750 this->fill_x = 1; // smallest common child fill step.
1751 this->fill_y = 0; // smallest non-zero child widget fill step.
1752 this->resize_x = 1; // smallest common child resize step.
1753 this->resize_y = 0; // smallest non-zero child widget resize step.
1754 this->gaps = 0;
1755
1756 /* 1a. Forward call, collect longest/widest child length. */
1757 uint highest = 0; // Highest child found.
1758 uint max_hor_fill = 0; // Biggest horizontal fill step.
1759 for (const auto &child_wid : this->children) {
1760 child_wid->SetupSmallestSize(w);
1761 highest = std::max(highest, child_wid->smallest_y);
1762 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(ST_SMALLEST));
1763 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1764 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->gaps++;
1765 }
1766 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1767 /* 1b. Make the container wider if needed to accommodate all children nicely. */
1768 [[maybe_unused]] uint max_smallest = this->smallest_x + 3 * max_hor_fill; // Upper limit to computing smallest height.
1769 uint cur_width = this->smallest_x;
1770 for (;;) {
1771 for (const auto &child_wid : this->children) {
1772 uint step_size = child_wid->GetHorizontalStepSize(ST_SMALLEST);
1773 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1774 if (step_size > 1 && child_width < cur_width) { // Small step sizes or already fitting children are not interesting.
1775 uint remainder = (cur_width - child_width) % step_size;
1776 if (remainder > 0) { // Child did not fit entirely, widen the container.
1777 cur_width += step_size - remainder;
1778 assert(cur_width < max_smallest); // Safeguard against infinite width expansion.
1779 /* Remaining children will adapt to the new cur_width, thus speeding up the computation. */
1780 }
1781 }
1782 }
1783 if (this->smallest_x == cur_width) break;
1784 this->smallest_x = cur_width; // Smallest width got changed, try again.
1785 }
1786 /* 2. For containers that must maintain equal width, extend children minimal size. */
1787 for (const auto &child_wid : this->children) {
1788 child_wid->smallest_x = this->smallest_x - child_wid->padding.Horizontal();
1789 child_wid->ApplyAspectRatio();
1790 highest = std::max(highest, child_wid->smallest_y);
1791 }
1793 for (const auto &child_wid : this->children) {
1794 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1795 }
1796 }
1797 /* 3. Compute smallest, fill, and resize values of the container. */
1798 for (const auto &child_wid : this->children) {
1799 this->smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1800 if (child_wid->fill_y > 0) {
1801 if (this->fill_y == 0 || this->fill_y > child_wid->fill_y) this->fill_y = child_wid->fill_y;
1802 }
1803 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1804
1805 if (child_wid->resize_y > 0) {
1806 if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y;
1807 }
1808 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1809 }
1810 if (this->fill_y == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_y = 1;
1811 /* 4. Increase by required PIP space. */
1812 this->smallest_y += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1813}
1814
1815void NWidgetVertical::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1816{
1817 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1818
1819 /* Compute additional height given to us. */
1820 uint additional_length = given_height - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1821 for (const auto &child_wid : this->children) {
1822 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1823 }
1824
1825 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1826
1827 /* Like the horizontal container, the vertical container also distributes additional height evenly, starting with the children with the biggest resize steps.
1828 * It also stores computed widths and heights into current_x and current_y values of the child.
1829 */
1830
1831 /* 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. */
1832 int num_changing_childs = 0; // Number of children that can change size.
1833 uint biggest_stepsize = 0;
1834 for (const auto &child_wid : this->children) {
1835 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1836 if (vert_step > 0) {
1837 if (!flags.Test(NWidContainerFlag::BigFirst)) num_changing_childs++;
1838 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1839 } else {
1840 child_wid->current_y = child_wid->smallest_y;
1841 }
1842
1843 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1844 child_wid->current_x = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1845 }
1846
1847 /* First.5 loop: count how many children are of the biggest step size. */
1848 if (this->flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1849 for (const auto &child_wid : this->children) {
1850 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1851 if (vert_step == biggest_stepsize) {
1852 num_changing_childs++;
1853 }
1854 }
1855 }
1856
1857 /* Second loop: Allocate the additional vertical space over the resizing children, starting with the biggest resize steps. */
1858 while (biggest_stepsize > 0) {
1859 uint next_biggest_stepsize = 0;
1860 for (const auto &child_wid : this->children) {
1861 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1862 if (vert_step > biggest_stepsize) continue; // Already done
1863 if (vert_step == biggest_stepsize) {
1864 uint increment = additional_length / num_changing_childs;
1865 num_changing_childs--;
1866 if (vert_step > 1) increment -= increment % vert_step;
1867 child_wid->current_y = child_wid->smallest_y + increment;
1868 additional_length -= increment;
1869 continue;
1870 }
1871 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1872 }
1873 biggest_stepsize = next_biggest_stepsize;
1874
1875 if (num_changing_childs == 0 && flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1876 /* Second.5 loop: count how many children are of the updated biggest step size. */
1877 for (const auto &child_wid : this->children) {
1878 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1879 if (vert_step == biggest_stepsize) {
1880 num_changing_childs++;
1881 }
1882 }
1883 }
1884 }
1885 assert(num_changing_childs == 0);
1886
1887 uint pre = this->pip_pre;
1888 uint inter = this->pip_inter;
1889
1890 if (additional_length > 0) {
1891 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1892 * which is never explicitly needed. */
1893 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1894 if (r > 0) {
1895 pre += this->pip_ratio_pre * additional_length / r;
1896 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1897 }
1898 }
1899
1900 /* Third loop: Compute position and call the child. */
1901 uint position = pre; // Place to put next child relative to origin of the container.
1902 for (const auto &child_wid : this->children) {
1903 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1904 uint child_height = child_wid->current_y;
1905
1906 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1907 if (child_wid->current_y != 0) {
1908 position += child_height + child_wid->padding.Vertical() + inter;
1909 }
1910 }
1911}
1912
1919{
1920 this->SetMinimalSize(width, height);
1921 this->SetResize(0, 0);
1922}
1923
1925{
1926 this->smallest_x = this->min_x;
1927 this->smallest_y = this->min_y;
1928 this->ApplyAspectRatio();
1929}
1930
1932{
1933 /* Spacer widget is never normally visible. */
1934
1935 if (_draw_widget_outlines && this->current_x != 0 && this->current_y != 0) {
1936 /* Spacers indicate a potential design issue, so get extra highlighting. */
1937 GfxFillRect(this->GetCurrentRect(), PC_WHITE, FILLRECT_CHECKER);
1938
1939 DrawOutline(w, this);
1940 }
1941}
1942
1944{
1945 /* Spacer widget never need repainting. */
1946}
1947
1949{
1950 return nullptr;
1951}
1952
1958{
1959 this->clicked = clicked;
1960 if (this->clicked >= 0 && this->sb != nullptr && this->widgets_x != 0) {
1961 int vpos = (this->clicked / this->widgets_x) * this->widget_h; // Vertical position of the top.
1962 /* Need to scroll down -> Scroll to the bottom.
1963 * However, last entry has no 'this->pip_inter' underneath, and we must stay below this->sb->GetCount() */
1964 if (this->sb->GetPosition() < vpos) vpos += this->widget_h - this->pip_inter - 1;
1965 this->sb->ScrollTowards(vpos);
1966 }
1967}
1968
1975{
1976 this->count = count;
1977
1978 if (this->sb == nullptr || this->widgets_x == 0) return;
1979
1980 /* We need to get the number of pixels the matrix is high/wide.
1981 * So, determine the number of rows/columns based on the number of
1982 * columns/rows (one is constant/unscrollable).
1983 * Then multiply that by the height of a widget, and add the pre
1984 * and post spacing "offsets". */
1985 count = CeilDiv(count, this->sb->IsVertical() ? this->widgets_x : this->widgets_y);
1986 count *= (this->sb->IsVertical() ? this->children.front()->smallest_y : this->children.front()->smallest_x) + this->pip_inter;
1987 if (count > 0) count -= this->pip_inter; // We counted an inter too much in the multiplication above
1988 count += this->pip_pre + this->pip_post;
1989 this->sb->SetCount(count);
1990 this->sb->SetCapacity(this->sb->IsVertical() ? this->current_y : this->current_x);
1991 this->sb->SetStepSize(this->sb->IsVertical() ? this->widget_h : this->widget_w);
1992}
1993
1999{
2000 this->sb = sb;
2001}
2002
2008{
2009 return this->current_element;
2010}
2011
2013{
2014 assert(this->children.size() == 1);
2015
2016 this->children.front()->SetupSmallestSize(w);
2017
2018 Dimension padding = { (uint)this->pip_pre + this->pip_post, (uint)this->pip_pre + this->pip_post};
2019 Dimension size = {this->children.front()->smallest_x + padding.width, this->children.front()->smallest_y + padding.height};
2020 Dimension fill = {0, 0};
2021 Dimension resize = {this->pip_inter + this->children.front()->smallest_x, this->pip_inter + this->children.front()->smallest_y};
2022
2023 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
2024
2025 this->smallest_x = size.width;
2026 this->smallest_y = size.height;
2027 this->fill_x = fill.width;
2028 this->fill_y = fill.height;
2029 this->resize_x = resize.width;
2030 this->resize_y = resize.height;
2031 this->ApplyAspectRatio();
2032}
2033
2034void NWidgetMatrix::AssignSizePosition(SizingType, int x, int y, uint given_width, uint given_height, bool)
2035{
2036 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
2037
2038 this->pos_x = x;
2039 this->pos_y = y;
2040 this->current_x = given_width;
2041 this->current_y = given_height;
2042
2043 /* Determine the size of the widgets, and the number of visible widgets on each of the axis. */
2044 this->widget_w = this->children.front()->smallest_x + this->pip_inter;
2045 this->widget_h = this->children.front()->smallest_y + this->pip_inter;
2046
2047 /* Account for the pip_inter is between widgets, so we need to account for that when
2048 * the division assumes pip_inter is used for all widgets. */
2049 this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
2050 this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
2051
2052 /* When resizing, update the scrollbar's count. E.g. with a vertical
2053 * scrollbar becoming wider or narrower means the amount of rows in
2054 * the scrollbar becomes respectively smaller or higher. */
2055 this->SetCount(this->count);
2056}
2057
2059{
2060 /* Falls outside of the matrix widget. */
2061 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
2062
2063 int start_x, start_y, base_offs_x, base_offs_y;
2064 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2065
2066 bool rtl = _current_text_dir == TD_RTL;
2067
2068 int widget_col = (rtl ?
2069 -x + (int)this->pip_post + this->pos_x + base_offs_x + this->widget_w - 1 - (int)this->pip_inter :
2070 x - (int)this->pip_pre - this->pos_x - base_offs_x
2071 ) / this->widget_w;
2072
2073 int widget_row = (y - base_offs_y - (int)this->pip_pre - this->pos_y) / this->widget_h;
2074
2075 this->current_element = (widget_row + start_y) * this->widgets_x + start_x + widget_col;
2076 if (this->current_element >= this->count) return nullptr;
2077
2078 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2079 assert(child != nullptr);
2081 this->pos_x + (rtl ? this->pip_post - widget_col * this->widget_w : this->pip_pre + widget_col * this->widget_w) + base_offs_x,
2082 this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
2083 child->smallest_x, child->smallest_y, rtl);
2084
2085 return child->GetWidgetFromPos(x, y);
2086}
2087
2088/* virtual */ void NWidgetMatrix::Draw(const Window *w)
2089{
2090 /* Fill the background. */
2091 GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, SHADE_LIGHT));
2092
2093 /* Set up a clipping area for the previews. */
2094 bool rtl = _current_text_dir == TD_RTL;
2095 DrawPixelInfo tmp_dpi;
2096 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;
2097
2098 {
2099 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
2100
2101 /* Get the appropriate offsets so we can draw the right widgets. */
2102 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2103 assert(child != nullptr);
2104 int start_x, start_y, base_offs_x, base_offs_y;
2105 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2106
2107 int offs_y = base_offs_y;
2108 for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
2109 /* Are we within bounds? */
2110 if (offs_y + child->smallest_y <= 0) continue;
2111 if (offs_y >= (int)this->current_y) break;
2112
2113 /* We've passed our amount of widgets. */
2114 if (y * this->widgets_x >= this->count) break;
2115
2116 int offs_x = base_offs_x;
2117 for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
2118 /* Are we within bounds? */
2119 if (offs_x + child->smallest_x <= 0) continue;
2120 if (offs_x >= (int)this->current_x) continue;
2121
2122 /* Do we have this many widgets? */
2123 this->current_element = y * this->widgets_x + x;
2124 if (this->current_element >= this->count) break;
2125
2126 child->AssignSizePosition(ST_RESIZE, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
2127 child->SetLowered(this->clicked == this->current_element);
2128 child->Draw(w);
2129 }
2130 }
2131 }
2132
2133 DrawOutline(w, this);
2134}
2135
2143void NWidgetMatrix::GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
2144{
2145 base_offs_x = _current_text_dir == TD_RTL ? this->widget_w * (this->widgets_x - 1) : 0;
2146 base_offs_y = 0;
2147 start_x = 0;
2148 start_y = 0;
2149 if (this->sb != nullptr) {
2150 if (this->sb->IsVertical()) {
2151 start_y = this->sb->GetPosition() / this->widget_h;
2152 base_offs_y += -this->sb->GetPosition() + start_y * this->widget_h;
2153 } else {
2154 start_x = this->sb->GetPosition() / this->widget_w;
2155 int sub_x = this->sb->GetPosition() - start_x * this->widget_w;
2156 if (_current_text_dir == TD_RTL) {
2157 base_offs_x += sub_x;
2158 } else {
2159 base_offs_x -= sub_x;
2160 }
2161 }
2162 }
2163}
2164
2174NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr<NWidgetPIPContainer> &&child) : NWidgetCore(tp, colour, index, 1, 1, {}, STR_NULL)
2175{
2176 assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
2177 this->child = std::move(child);
2178 if (this->child != nullptr) this->child->parent = this;
2179 this->SetAlignment(SA_TOP | SA_LEFT);
2180}
2181
2189void NWidgetBackground::Add(std::unique_ptr<NWidgetBase> &&nwid)
2190{
2191 if (this->child == nullptr) {
2192 this->child = std::make_unique<NWidgetVertical>();
2193 }
2194 nwid->parent = this->child.get();
2195 this->child->Add(std::move(nwid));
2196}
2197
2208void NWidgetBackground::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
2209{
2210 if (this->child == nullptr) {
2211 this->child = std::make_unique<NWidgetVertical>();
2212 }
2213 this->child->parent = this;
2214 this->child->SetPIP(pip_pre, pip_inter, pip_post);
2215}
2216
2227void NWidgetBackground::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
2228{
2229 if (this->child == nullptr) {
2230 this->child = std::make_unique<NWidgetVertical>();
2231 }
2232 this->child->parent = this;
2233 this->child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2234}
2235
2236void NWidgetBackground::AdjustPaddingForZoom()
2237{
2238 if (child != nullptr) child->AdjustPaddingForZoom();
2239 NWidgetCore::AdjustPaddingForZoom();
2240}
2241
2243{
2244 if (this->child != nullptr) {
2245 this->child->SetupSmallestSize(w);
2246
2247 this->smallest_x = this->child->smallest_x;
2248 this->smallest_y = this->child->smallest_y;
2249 this->fill_x = this->child->fill_x;
2250 this->fill_y = this->child->fill_y;
2251 this->resize_x = this->child->resize_x;
2252 this->resize_y = this->child->resize_y;
2253
2254 /* Don't apply automatic padding if there is no child widget. */
2255 if (w == nullptr) return;
2256
2257 if (this->type == WWT_FRAME) {
2258 std::string text = GetStringForWidget(w, this);
2259 Dimension text_size = text.empty() ? Dimension{0, 0} : GetStringBoundingBox(text, this->text_size);
2260
2261 /* Account for the size of the frame's text if that exists */
2262 this->child->padding = WidgetDimensions::scaled.frametext;
2263 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);
2264
2265 this->smallest_x += this->child->padding.Horizontal();
2266 this->smallest_y += this->child->padding.Vertical();
2267
2268 this->smallest_x = std::max(this->smallest_x, text_size.width + WidgetDimensions::scaled.frametext.Horizontal());
2269 } else if (this->type == WWT_INSET) {
2270 /* Apply automatic padding for bevel thickness. */
2271 this->child->padding = WidgetDimensions::scaled.bevel;
2272
2273 this->smallest_x += this->child->padding.Horizontal();
2274 this->smallest_y += this->child->padding.Vertical();
2275 }
2276 this->ApplyAspectRatio();
2277 } else {
2278 Dimension d = {this->min_x, this->min_y};
2279 Dimension fill = {this->fill_x, this->fill_y};
2280 Dimension resize = {this->resize_x, this->resize_y};
2281 if (w != nullptr) { // A non-nullptr window pointer acts as switch to turn dynamic widget size on.
2282 if (this->type == WWT_FRAME || this->type == WWT_INSET) {
2283 std::string text = GetStringForWidget(w, this);
2284 if (!text.empty()) {
2285 Dimension background = GetStringBoundingBox(text, this->text_size);
2286 background.width += (this->type == WWT_FRAME) ? (WidgetDimensions::scaled.frametext.Horizontal()) : (WidgetDimensions::scaled.inset.Horizontal());
2287 d = maxdim(d, background);
2288 }
2289 }
2290 if (this->index >= 0) {
2292 switch (this->type) {
2293 default: NOT_REACHED();
2297 }
2298 w->UpdateWidgetSize(this->index, d, padding, fill, resize);
2299 }
2300 }
2301 this->smallest_x = d.width;
2302 this->smallest_y = d.height;
2303 this->fill_x = fill.width;
2304 this->fill_y = fill.height;
2305 this->resize_x = resize.width;
2306 this->resize_y = resize.height;
2307 this->ApplyAspectRatio();
2308 }
2309}
2310
2311void NWidgetBackground::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
2312{
2313 this->StoreSizePosition(sizing, x, y, given_width, given_height);
2314
2315 if (this->child != nullptr) {
2316 uint x_offset = (rtl ? this->child->padding.right : this->child->padding.left);
2317 uint width = given_width - this->child->padding.Horizontal();
2318 uint height = given_height - this->child->padding.Vertical();
2319 this->child->AssignSizePosition(sizing, x + x_offset, y + this->child->padding.top, width, height, rtl);
2320 }
2321}
2322
2324{
2325 this->NWidgetCore::FillWidgetLookup(widget_lookup);
2326 if (this->child != nullptr) this->child->FillWidgetLookup(widget_lookup);
2327}
2328
2330{
2331 if (this->current_x == 0 || this->current_y == 0) return;
2332
2333 Rect r = this->GetCurrentRect();
2334
2335 const DrawPixelInfo *dpi = _cur_dpi;
2336 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2337
2338 switch (this->type) {
2339 case WWT_PANEL:
2341 break;
2342
2343 case WWT_FRAME:
2344 DrawFrame(r, this->colour, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
2345 break;
2346
2347 case WWT_INSET:
2348 DrawInset(r, this->colour, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
2349 break;
2350
2351 default:
2352 NOT_REACHED();
2353 }
2354
2355 if (this->index >= 0) w->DrawWidget(r, this->index);
2356 if (this->child != nullptr) this->child->Draw(w);
2357
2358 if (this->IsDisabled()) {
2360 }
2361
2362 DrawOutline(w, this);
2363}
2364
2366{
2367 NWidgetCore *nwid = nullptr;
2368 if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) {
2369 if (this->child != nullptr) nwid = this->child->GetWidgetFromPos(x, y);
2370 if (nwid == nullptr) nwid = this;
2371 }
2372 return nwid;
2373}
2374
2376{
2377 NWidgetBase *nwid = nullptr;
2378 if (this->child != nullptr) nwid = this->child->GetWidgetOfType(tp);
2379 if (nwid == nullptr && this->type == tp) nwid = this;
2380 return nwid;
2381}
2382
2383NWidgetViewport::NWidgetViewport(WidgetID index) : NWidgetCore(NWID_VIEWPORT, INVALID_COLOUR, index, 1, 1, {}, STR_NULL)
2384{
2385}
2386
2388{
2389 this->smallest_x = this->min_x;
2390 this->smallest_y = this->min_y;
2391 this->ApplyAspectRatio();
2392}
2393
2395{
2396 if (this->current_x == 0 || this->current_y == 0) return;
2397
2400 _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_TEXT); // Disable all transparency, except textual stuff
2401 w->DrawViewport();
2402 _transparency_opt = to_backup;
2403 } else {
2404 w->DrawViewport();
2405 }
2406
2407 /* Optionally shade the viewport. */
2408 if (this->disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2410 }
2411
2412 DrawOutline(w, this);
2413}
2414
2421void NWidgetViewport::InitializeViewport(Window *w, std::variant<TileIndex, VehicleID> focus, ZoomLevel zoom)
2422{
2423 InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, focus, zoom);
2424}
2425
2431{
2432 if (w->viewport == nullptr) return;
2433
2434 Viewport &vp = *w->viewport;
2435 vp.left = w->left + this->pos_x;
2436 vp.top = w->top + this->pos_y;
2437 vp.width = this->current_x;
2438 vp.height = this->current_y;
2439
2440 vp.virtual_width = ScaleByZoom(vp.width, vp.zoom);
2442}
2443
2453Scrollbar::size_type Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, WidgetID widget, int padding, int line_height) const
2454{
2455 int pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
2456 if (pos != INT_MAX) pos += this->GetPosition();
2457 return (pos < 0 || pos >= this->GetCount()) ? Scrollbar::npos : pos;
2458}
2459
2474EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
2475{
2476 int new_pos = list_position;
2477 switch (keycode) {
2478 case WKC_UP:
2479 /* scroll up by one */
2480 new_pos--;
2481 break;
2482
2483 case WKC_DOWN:
2484 /* scroll down by one */
2485 new_pos++;
2486 break;
2487
2488 case WKC_PAGEUP:
2489 /* scroll up a page */
2490 new_pos -= this->GetCapacity();
2491 break;
2492
2493 case WKC_PAGEDOWN:
2494 /* scroll down a page */
2495 new_pos += this->GetCapacity();
2496 break;
2497
2498 case WKC_HOME:
2499 /* jump to beginning */
2500 new_pos = 0;
2501 break;
2502
2503 case WKC_END:
2504 /* jump to end */
2505 new_pos = this->GetCount() - 1;
2506 break;
2507
2508 default:
2509 return ES_NOT_HANDLED;
2510 }
2511
2512 /* If there are no elements, there is nothing to scroll/update. */
2513 if (this->GetCount() != 0) {
2514 list_position = Clamp(new_pos, 0, this->GetCount() - 1);
2515 }
2516 return ES_HANDLED;
2517}
2518
2519
2528{
2529 NWidgetBase *nwid = w->GetWidget<NWidgetBase>(widget);
2530 if (this->IsVertical()) {
2531 this->SetCapacity(((int)nwid->current_y - padding) / (int)nwid->resize_y);
2532 } else {
2533 this->SetCapacity(((int)nwid->current_x - padding) / (int)nwid->resize_x);
2534 }
2535}
2536
2544Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
2545{
2546 const int count = sb.GetCount() * resize_step;
2547 const int position = sb.GetPosition() * resize_step;
2548
2549 if (sb.IsVertical()) {
2550 r.top -= position;
2551 r.bottom = r.top + count;
2552 } else {
2553 bool rtl = _current_text_dir == TD_RTL;
2554 if (rtl) {
2555 r.right += position;
2556 r.left = r.right - count;
2557 } else {
2558 r.left -= position;
2559 r.right = r.left + count;
2560 }
2561 }
2562
2563 return r;
2564}
2565
2572NWidgetScrollbar::NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index) : NWidgetCore(tp, colour, index, 1, 1, {}, STR_NULL), Scrollbar(tp != NWID_HSCROLLBAR)
2573{
2574 assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR);
2575
2576 switch (this->type) {
2577 case NWID_HSCROLLBAR:
2578 this->SetResize(1, 0);
2579 this->SetFill(1, 0);
2580 this->SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2581 break;
2582
2583 case NWID_VSCROLLBAR:
2584 this->SetResize(0, 1);
2585 this->SetFill(0, 1);
2586 this->SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2587 break;
2588
2589 default: NOT_REACHED();
2590 }
2591}
2592
2594{
2595 this->min_x = 0;
2596 this->min_y = 0;
2597
2598 switch (this->type) {
2599 case NWID_HSCROLLBAR:
2600 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2601 break;
2602
2603 case NWID_VSCROLLBAR:
2604 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2605 break;
2606
2607 default: NOT_REACHED();
2608 }
2609
2610 this->smallest_x = this->min_x;
2611 this->smallest_y = this->min_y;
2612}
2613
2615{
2616 if (this->current_x == 0 || this->current_y == 0) return;
2617
2618 Rect r = this->GetCurrentRect();
2619
2620 const DrawPixelInfo *dpi = _cur_dpi;
2621 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2622
2623 bool up_lowered = this->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp);
2624 bool down_lowered = this->disp_flags.Test(NWidgetDisplayFlag::ScrollbarDown);
2625 bool middle_lowered = !this->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown}) && w->mouse_capture_widget == this->index;
2626
2627 if (this->type == NWID_HSCROLLBAR) {
2628 DrawHorizontalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2629 } else {
2630 DrawVerticalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2631 }
2632
2633 if (this->IsDisabled()) {
2635 }
2636
2637 DrawOutline(w, this);
2638}
2639
2640/* static */ void NWidgetScrollbar::InvalidateDimensionCache()
2641{
2642 vertical_dimension.width = vertical_dimension.height = 0;
2643 horizontal_dimension.width = horizontal_dimension.height = 0;
2644}
2645
2646/* static */ Dimension NWidgetScrollbar::GetVerticalDimension()
2647{
2648 if (vertical_dimension.width == 0) {
2649 vertical_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_UP), GetScaledSpriteSize(SPR_ARROW_DOWN));
2652 }
2653 return vertical_dimension;
2654}
2655
2656/* static */ Dimension NWidgetScrollbar::GetHorizontalDimension()
2657{
2658 if (horizontal_dimension.width == 0) {
2659 horizontal_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2662 }
2663 return horizontal_dimension;
2664}
2665
2668
2671{
2672 shadebox_dimension.width = shadebox_dimension.height = 0;
2673 debugbox_dimension.width = debugbox_dimension.height = 0;
2674 defsizebox_dimension.width = defsizebox_dimension.height = 0;
2675 stickybox_dimension.width = stickybox_dimension.height = 0;
2676 resizebox_dimension.width = resizebox_dimension.height = 0;
2677 closebox_dimension.width = closebox_dimension.height = 0;
2678 dropdown_dimension.width = dropdown_dimension.height = 0;
2679}
2680
2688
2697NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, const WidgetData &data, StringID tip) : NWidgetCore(tp, colour, index, 1, 1, data, tip)
2698{
2699 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);
2700 this->min_x = 0;
2701 this->min_y = 0;
2702 this->SetResize(0, 0);
2703
2704 switch (tp) {
2705 case WWT_EMPTY:
2706 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_EMPTY should not have a colour");
2707 break;
2708
2709 case WWT_TEXT:
2710 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_TEXT should not have a colour");
2711 this->SetFill(0, 0);
2713 break;
2714
2715 case WWT_LABEL:
2716 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_LABEL should not have a colour");
2717 [[fallthrough]];
2718
2719 case WWT_PUSHBTN:
2720 case WWT_IMGBTN:
2721 case WWT_PUSHIMGBTN:
2722 case WWT_IMGBTN_2:
2723 case WWT_TEXTBTN:
2724 case WWT_PUSHTXTBTN:
2725 case WWT_TEXTBTN_2:
2726 case WWT_IMGTEXTBTN:
2727 case WWT_PUSHIMGTEXTBTN:
2728 case WWT_BOOLBTN:
2729 case WWT_MATRIX:
2731 case NWID_PUSHBUTTON_DROPDOWN:
2732 this->SetFill(0, 0);
2733 break;
2734
2735 case WWT_ARROWBTN:
2736 case WWT_PUSHARROWBTN:
2737 this->SetFill(0, 0);
2738 this->SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2739 break;
2740
2741 case WWT_EDITBOX:
2742 this->SetFill(0, 0);
2743 break;
2744
2745 case WWT_CAPTION:
2746 this->SetFill(1, 0);
2747 this->SetResize(1, 0);
2749 this->SetMinimalTextLines(1, WidgetDimensions::unscaled.captiontext.Vertical(), FS_NORMAL);
2750 this->SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2751 break;
2752
2753 case WWT_STICKYBOX:
2754 this->SetFill(0, 0);
2756 this->SetToolTip(STR_TOOLTIP_STICKY);
2757 this->SetAspect(this->min_x, this->min_y);
2758 break;
2759
2760 case WWT_SHADEBOX:
2761 this->SetFill(0, 0);
2763 this->SetToolTip(STR_TOOLTIP_SHADE);
2764 this->SetAspect(this->min_x, this->min_y);
2765 break;
2766
2767 case WWT_DEBUGBOX:
2768 this->SetFill(0, 0);
2770 this->SetToolTip(STR_TOOLTIP_DEBUG);
2771 this->SetAspect(this->min_x, this->min_y);
2772 break;
2773
2774 case WWT_DEFSIZEBOX:
2775 this->SetFill(0, 0);
2777 this->SetToolTip(STR_TOOLTIP_DEFSIZE);
2778 this->SetAspect(this->min_x, this->min_y);
2779 break;
2780
2781 case WWT_RESIZEBOX:
2782 this->SetFill(0, 0);
2785 this->SetToolTip(STR_TOOLTIP_RESIZE);
2786 break;
2787
2788 case WWT_CLOSEBOX:
2789 this->SetFill(0, 0);
2791 this->SetToolTip(STR_TOOLTIP_CLOSE_WINDOW);
2792 this->SetAspect(this->min_x, this->min_y);
2793 break;
2794
2795 case WWT_DROPDOWN:
2796 this->SetFill(0, 0);
2798 this->SetAlignment(SA_TOP | SA_LEFT);
2799 break;
2800
2801 default:
2802 NOT_REACHED();
2803 }
2804}
2805
2807{
2808 Dimension padding = {0, 0};
2809 Dimension size = {this->min_x, this->min_y};
2810 Dimension fill = {this->fill_x, this->fill_y};
2811 Dimension resize = {this->resize_x, this->resize_y};
2812 switch (this->type) {
2813 case WWT_EMPTY: {
2814 break;
2815 }
2816 case WWT_MATRIX: {
2818 break;
2819 }
2820 case WWT_SHADEBOX: {
2822 if (NWidgetLeaf::shadebox_dimension.width == 0) {
2823 NWidgetLeaf::shadebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_SHADE), GetScaledSpriteSize(SPR_WINDOW_UNSHADE));
2826 }
2828 break;
2829 }
2830 case WWT_DEBUGBOX:
2833 if (NWidgetLeaf::debugbox_dimension.width == 0) {
2837 }
2839 } else {
2840 /* If the setting is disabled we don't want to see it! */
2841 size.width = 0;
2842 fill.width = 0;
2843 resize.width = 0;
2844 }
2845 break;
2846
2847 case WWT_STICKYBOX: {
2849 if (NWidgetLeaf::stickybox_dimension.width == 0) {
2853 }
2855 break;
2856 }
2857
2858 case WWT_DEFSIZEBOX: {
2860 if (NWidgetLeaf::defsizebox_dimension.width == 0) {
2864 }
2866 break;
2867 }
2868
2869 case WWT_RESIZEBOX: {
2871 if (NWidgetLeaf::resizebox_dimension.width == 0) {
2872 NWidgetLeaf::resizebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_RESIZE_LEFT), GetScaledSpriteSize(SPR_WINDOW_RESIZE_RIGHT));
2875 }
2877 break;
2878 }
2879 case WWT_EDITBOX: {
2880 Dimension sprite_size = GetScaledSpriteSize(_current_text_dir == TD_RTL ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
2881 size.width = std::max(size.width, ScaleGUITrad(30) + sprite_size.width);
2882 size.height = std::max(sprite_size.height, GetStringBoundingBox("_").height + WidgetDimensions::scaled.framerect.Vertical());
2883 }
2884 [[fallthrough]];
2885 case WWT_PUSHBTN: {
2887 break;
2888 }
2889
2890 case WWT_BOOLBTN:
2891 size.width = SETTING_BUTTON_WIDTH;
2892 size.height = SETTING_BUTTON_HEIGHT;
2893 break;
2894
2895 case WWT_IMGBTN:
2896 case WWT_IMGBTN_2:
2897 case WWT_PUSHIMGBTN: {
2899 Dimension d2 = GetScaledSpriteSize(this->widget_data.sprite);
2900 if (this->type == WWT_IMGBTN_2) d2 = maxdim(d2, GetScaledSpriteSize(this->widget_data.sprite + 1));
2901 d2.width += padding.width;
2902 d2.height += padding.height;
2903 size = maxdim(size, d2);
2904 break;
2905 }
2906
2907 case WWT_IMGTEXTBTN:
2908 case WWT_PUSHIMGTEXTBTN: {
2911 Dimension dt = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2912 Dimension d2{
2913 padding.width + di.width + WidgetDimensions::scaled.hsep_wide + dt.width,
2914 padding.height + std::max(di.height, dt.height)
2915 };
2916 size = maxdim(size, d2);
2917 break;
2918 }
2919
2920 case WWT_ARROWBTN:
2921 case WWT_PUSHARROWBTN: {
2923 Dimension d2 = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2924 d2.width += padding.width;
2925 d2.height += padding.height;
2926 size = maxdim(size, d2);
2927 break;
2928 }
2929
2930 case WWT_CLOSEBOX: {
2932 if (NWidgetLeaf::closebox_dimension.width == 0) {
2936 }
2938 break;
2939 }
2940 case WWT_TEXTBTN:
2941 case WWT_PUSHTXTBTN:
2942 case WWT_TEXTBTN_2: {
2944 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2945 d2.width += padding.width;
2946 d2.height += padding.height;
2947 size = maxdim(size, d2);
2948 break;
2949 }
2950 case WWT_LABEL:
2951 case WWT_TEXT: {
2952 size = maxdim(size, GetStringBoundingBox(GetStringForWidget(w, this), this->text_size));
2953 break;
2954 }
2955 case WWT_CAPTION: {
2957 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2958 d2.width += padding.width;
2959 d2.height += padding.height;
2960 size = maxdim(size, d2);
2961 break;
2962 }
2963 case WWT_DROPDOWN:
2965 case NWID_PUSHBUTTON_DROPDOWN: {
2966 if (NWidgetLeaf::dropdown_dimension.width == 0) {
2970 }
2972 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2973 d2.width += padding.width;
2974 d2.height = std::max(d2.height + padding.height, NWidgetLeaf::dropdown_dimension.height);
2975 size = maxdim(size, d2);
2976 break;
2977 }
2978 default:
2979 NOT_REACHED();
2980 }
2981
2982 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
2983
2984 this->smallest_x = size.width;
2985 this->smallest_y = size.height;
2986 this->fill_x = fill.width;
2987 this->fill_y = fill.height;
2988 this->resize_x = resize.width;
2989 this->resize_y = resize.height;
2990 this->ApplyAspectRatio();
2991}
2992
2994{
2995 if (this->current_x == 0 || this->current_y == 0) return;
2996
2997 /* Setup a clipping rectangle... for WWT_EMPTY or WWT_TEXT, an extra scaled pixel is allowed in case text shadow encroaches. */
2998 int extra = (this->type == WWT_EMPTY || this->type == WWT_TEXT) ? ScaleGUITrad(1) : 0;
2999 DrawPixelInfo new_dpi;
3000 if (!FillDrawPixelInfo(&new_dpi, this->pos_x, this->pos_y, this->current_x + extra, this->current_y + extra)) return;
3001 /* ...but keep coordinates relative to the window. */
3002 new_dpi.left += this->pos_x;
3003 new_dpi.top += this->pos_y;
3004
3005 AutoRestoreBackup dpi_backup(_cur_dpi, &new_dpi);
3006
3007 Rect r = this->GetCurrentRect();
3008
3009 bool clicked = this->IsLowered();
3010 switch (this->type) {
3011 case WWT_EMPTY:
3012 /* WWT_EMPTY used as a spacer indicates a potential design issue. */
3013 if (this->index == -1 && _draw_widget_outlines) {
3015 }
3016 break;
3017
3018 case WWT_PUSHBTN:
3019 DrawFrameRect(r, this->colour, clicked ? FrameFlag::Lowered : FrameFlags{});
3020 break;
3021
3022 case WWT_BOOLBTN: {
3024 Colours button_colour = this->widget_data.alternate_colour;
3025 if (button_colour == INVALID_COLOUR) button_colour = this->colour;
3026 DrawBoolButton(pt.x, pt.y, button_colour, this->colour, clicked, !this->IsDisabled());
3027 break;
3028 }
3029
3030 case WWT_IMGBTN:
3031 case WWT_PUSHIMGBTN:
3032 case WWT_IMGBTN_2:
3033 DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data.sprite, this->align);
3034 break;
3035
3036 case WWT_TEXTBTN:
3037 case WWT_PUSHTXTBTN:
3038 case WWT_TEXTBTN_2:
3039 DrawFrameRect(r, this->colour, clicked ? FrameFlag::Lowered : FrameFlags{});
3040 DrawLabel(r, this->text_colour, GetStringForWidget(w, this, (type & WWT_MASK) == WWT_TEXTBTN_2 && clicked), this->align, this->text_size);
3041 break;
3042
3043 case WWT_IMGTEXTBTN:
3044 case WWT_PUSHIMGTEXTBTN:
3045 DrawImageTextButtons(r, this->colour, clicked, this->widget_data.sprite, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3046 break;
3047
3048 case WWT_ARROWBTN:
3049 case WWT_PUSHARROWBTN: {
3050 SpriteID sprite;
3051 switch (this->widget_data.arrow_widget_type) {
3052 case AWV_DECREASE: sprite = _current_text_dir != TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
3053 case AWV_INCREASE: sprite = _current_text_dir == TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
3054 case AWV_LEFT: sprite = SPR_ARROW_LEFT; break;
3055 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break;
3056 default: NOT_REACHED();
3057 }
3058 DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align);
3059 break;
3060 }
3061
3062 case WWT_LABEL:
3063 DrawLabel(r, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3064 break;
3065
3066 case WWT_TEXT:
3067 DrawText(r, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3068 break;
3069
3070 case WWT_MATRIX:
3071 DrawMatrix(r, this->colour, clicked, this->widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3072 break;
3073
3074 case WWT_EDITBOX: {
3075 const QueryString *query = w->GetQueryString(this->index);
3076 if (query != nullptr) query->DrawEditBox(w, this->index);
3077 break;
3078 }
3079
3080 case WWT_CAPTION:
3081 DrawCaption(r, this->colour, w->owner, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3082 break;
3083
3084 case WWT_SHADEBOX:
3085 DrawShadeBox(r, this->colour, w->IsShaded());
3086 break;
3087
3088 case WWT_DEBUGBOX:
3089 DrawDebugBox(r, this->colour, clicked);
3090 break;
3091
3092 case WWT_STICKYBOX:
3094 break;
3095
3096 case WWT_DEFSIZEBOX:
3097 DrawDefSizeBox(r, this->colour, clicked);
3098 break;
3099
3100 case WWT_RESIZEBOX:
3101 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 == RWV_SHOW_BEVEL);
3102 break;
3103
3104 case WWT_CLOSEBOX:
3105 DrawCloseBox(r, this->colour);
3106 break;
3107
3108 case WWT_DROPDOWN:
3109 DrawButtonDropdown(r, this->colour, false, clicked, GetStringForWidget(w, this), this->align);
3110 break;
3111
3113 case NWID_PUSHBUTTON_DROPDOWN:
3114 DrawButtonDropdown(r, this->colour, clicked, this->disp_flags.Test(NWidgetDisplayFlag::DropdownActive), GetStringForWidget(w, this), this->align);
3115 break;
3116
3117 default:
3118 NOT_REACHED();
3119 }
3120 if (this->index >= 0) w->DrawWidget(r, this->index);
3121
3122 if (this->IsDisabled() && this->type != WWT_BOOLBTN) {
3123 /* WWT_BOOLBTN is excluded as it draws its own disabled state. */
3125 }
3126
3127 DrawOutline(w, this);
3128}
3129
3138{
3139 if (_current_text_dir == TD_LTR) {
3140 int button_width = this->pos_x + this->current_x - NWidgetLeaf::dropdown_dimension.width;
3141 return pt.x < button_width;
3142 } else {
3143 int button_left = this->pos_x + NWidgetLeaf::dropdown_dimension.width;
3144 return pt.x >= button_left;
3145 }
3146}
3147
3148/* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
3149
3156{
3157 return tp > WPT_ATTRIBUTE_BEGIN && tp < WPT_ATTRIBUTE_END;
3158}
3159
3167{
3168 switch (nwid.type) {
3169 case WPT_RESIZE: {
3170 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3171 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_RESIZE requires NWidgetResizeBase");
3172 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3173 nwrb->SetResize(nwid.u.xy.x, nwid.u.xy.y);
3174 break;
3175 }
3176
3177 case WPT_MINSIZE: {
3178 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3179 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINSIZE requires NWidgetResizeBase");
3180 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3181 nwrb->SetMinimalSize(nwid.u.xy.x, nwid.u.xy.y);
3182 break;
3183 }
3184
3185 case WPT_MINTEXTLINES: {
3186 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3187 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINTEXTLINES requires NWidgetResizeBase");
3188 assert(nwid.u.text_lines.size >= FS_BEGIN && nwid.u.text_lines.size < FS_END);
3190 break;
3191 }
3192
3193 case WPT_TOOLBARSIZE: {
3194 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3195 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_TOOLBARSIZE requires NWidgetResizeBase");
3196 assert(nwid.u.xy.x >= 0);
3197 nwrb->SetToolbarMinimalSize(nwid.u.xy.x);
3198 break;
3199 }
3200
3201 case WPT_TEXTSTYLE: {
3202 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3203 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_TEXTSTYLE requires NWidgetCore");
3204 nwc->SetTextStyle(nwid.u.text_style.colour, nwid.u.text_style.size);
3205 break;
3206 }
3207
3208 case WPT_ALIGNMENT: {
3209 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3210 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_ALIGNMENT requires NWidgetCore");
3211 nwc->SetAlignment(nwid.u.align.align);
3212 break;
3213 }
3214
3215 case WPT_FILL: {
3216 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3217 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_FILL requires NWidgetResizeBase");
3218 nwrb->SetFill(nwid.u.xy.x, nwid.u.xy.y);
3219 break;
3220 }
3221
3222 case WPT_DATATIP: {
3223 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3224 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_DATATIP requires NWidgetCore");
3225 nwc->widget_data = nwid.u.data_tip.data;
3226 nwc->SetToolTip(nwid.u.data_tip.tooltip);
3227 break;
3228 }
3229
3230 case WPT_PADDING:
3231 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_PADDING requires NWidgetBase");
3232 dest->SetPadding(nwid.u.padding);
3233 break;
3234
3235 case WPT_PIPSPACE: {
3236 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3237 if (nwc != nullptr) nwc->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3238
3239 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3240 if (nwb != nullptr) nwb->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3241
3242 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3243 break;
3244 }
3245
3246 case WPT_PIPRATIO: {
3247 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3248 if (nwc != nullptr) nwc->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3249
3250 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3251 if (nwb != nullptr) nwb->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3252
3253 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3254 break;
3255 }
3256
3257 case WPT_SCROLLBAR: {
3258 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3259 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore");
3260 nwc->scrollbar_index = nwid.u.widget.index;
3261 break;
3262 }
3263
3264 case WPT_ASPECT: {
3265 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_ASPECT requires NWidgetBase");
3266 dest->aspect_ratio = nwid.u.aspect.ratio;
3267 dest->aspect_flags = nwid.u.aspect.flags;
3268 break;
3269 }
3270
3271 default:
3272 NOT_REACHED();
3273 }
3274}
3275
3282static std::unique_ptr<NWidgetBase> MakeNWidget(const NWidgetPart &nwid)
3283{
3284 assert(!IsAttributeWidgetPartType(nwid.type));
3285 assert(nwid.type != WPT_ENDCONTAINER);
3286
3287 switch (nwid.type) {
3288 case NWID_SPACER: return std::make_unique<NWidgetSpacer>(0, 0);
3289
3290 case WWT_PANEL: [[fallthrough]];
3291 case WWT_INSET: [[fallthrough]];
3292 case WWT_FRAME: return std::make_unique<NWidgetBackground>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3293
3294 case NWID_HORIZONTAL: return std::make_unique<NWidgetHorizontal>(nwid.u.container.flags, nwid.u.container.index);
3295 case NWID_HORIZONTAL_LTR: return std::make_unique<NWidgetHorizontalLTR>(nwid.u.container.flags, nwid.u.container.index);
3296 case NWID_VERTICAL: return std::make_unique<NWidgetVertical>(nwid.u.container.flags, nwid.u.container.index);
3297 case NWID_SELECTION: return std::make_unique<NWidgetStacked>(nwid.u.widget.index);
3298 case NWID_MATRIX: return std::make_unique<NWidgetMatrix>(nwid.u.widget.colour, nwid.u.widget.index);
3299 case NWID_VIEWPORT: return std::make_unique<NWidgetViewport>(nwid.u.widget.index);
3300 case NWID_LAYER: return std::make_unique<NWidgetLayer>(nwid.u.widget.index);
3301
3302 case NWID_HSCROLLBAR: [[fallthrough]];
3303 case NWID_VSCROLLBAR: return std::make_unique<NWidgetScrollbar>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3304
3305 case WPT_FUNCTION: return nwid.u.func_ptr();
3306
3307 default:
3308 assert((nwid.type & WWT_MASK) < WWT_LAST || (nwid.type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
3309 return std::make_unique<NWidgetLeaf>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index, WidgetData{}, STR_NULL);
3310 }
3311}
3312
3326static 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)
3327{
3328 dest = nullptr;
3329
3330 if (IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]] throw std::runtime_error("Expected non-attribute NWidgetPart type");
3331 if (nwid_begin->type == WPT_ENDCONTAINER) return nwid_begin;
3332
3333 fill_dest = IsContainerWidgetType(nwid_begin->type);
3334 dest = MakeNWidget(*nwid_begin);
3335 if (dest == nullptr) return nwid_begin;
3336
3337 ++nwid_begin;
3338
3339 /* Once a widget is created, we're now looking for attributes. */
3340 while (nwid_begin != nwid_end && IsAttributeWidgetPartType(nwid_begin->type)) {
3341 ApplyNWidgetPartAttribute(*nwid_begin, dest.get());
3342 ++nwid_begin;
3343 }
3344
3345 return nwid_begin;
3346}
3347
3354{
3355 return tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
3356 || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION || tp == NWID_LAYER;
3357}
3358
3366static 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)
3367{
3368 /* If *parent == nullptr, only the first widget is read and returned. Otherwise, *parent must point to either
3369 * a #NWidgetContainer or a #NWidgetBackground object, and parts are added as much as possible. */
3370 NWidgetContainer *nwid_cont = dynamic_cast<NWidgetContainer *>(parent.get());
3371 NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(parent.get());
3372 assert(parent == nullptr || (nwid_cont != nullptr && nwid_parent == nullptr) || (nwid_cont == nullptr && nwid_parent != nullptr));
3373
3374 while (nwid_begin != nwid_end) {
3375 std::unique_ptr<NWidgetBase> sub_widget = nullptr;
3376 bool fill_sub = false;
3377 nwid_begin = MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3378
3379 /* Break out of loop when end reached */
3380 if (sub_widget == nullptr) break;
3381
3382 /* If sub-widget is a container, recursively fill that container. */
3383 if (fill_sub && IsContainerWidgetType(sub_widget->type)) {
3384 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, sub_widget);
3385 }
3386
3387 /* Add sub_widget to parent container if available, otherwise return the widget to the caller. */
3388 if (nwid_cont != nullptr) nwid_cont->Add(std::move(sub_widget));
3389 if (nwid_parent != nullptr) nwid_parent->Add(std::move(sub_widget));
3390 if (nwid_cont == nullptr && nwid_parent == nullptr) {
3391 parent = std::move(sub_widget);
3392 return nwid_begin;
3393 }
3394 }
3395
3396 if (nwid_begin == nwid_end) return nwid_begin; // Reached the end of the array of parts?
3397
3398 assert(nwid_begin < nwid_end);
3399 assert(nwid_begin->type == WPT_ENDCONTAINER);
3400 return std::next(nwid_begin); // *nwid_begin is also 'used'
3401}
3402
3410std::unique_ptr<NWidgetBase> MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3411{
3412 if (container == nullptr) container = std::make_unique<NWidgetVertical>();
3413 [[maybe_unused]] auto nwid_part = MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3414#ifdef WITH_ASSERT
3415 if (nwid_part != std::end(nwid_parts)) [[unlikely]] throw std::runtime_error("Did not consume all NWidgetParts");
3416#endif
3417 return std::move(container);
3418}
3419
3429std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(std::span<const NWidgetPart> nwid_parts, NWidgetStacked **shade_select)
3430{
3431 auto nwid_begin = std::begin(nwid_parts);
3432 auto nwid_end = std::end(nwid_parts);
3433
3434 *shade_select = nullptr;
3435
3436 /* Read the first widget recursively from the array. */
3437 std::unique_ptr<NWidgetBase> nwid = nullptr;
3438 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, nwid);
3439 assert(nwid != nullptr);
3440
3441 NWidgetHorizontal *hor_cont = dynamic_cast<NWidgetHorizontal *>(nwid.get());
3442
3443 auto root = std::make_unique<NWidgetVertical>();
3444 root->Add(std::move(nwid));
3445 if (nwid_begin == nwid_end) return root; // There is no body at all.
3446
3447 if (hor_cont != nullptr && hor_cont->GetWidgetOfType(WWT_CAPTION) != nullptr && hor_cont->GetWidgetOfType(WWT_SHADEBOX) != nullptr) {
3448 /* If the first widget has a title bar and a shade box, silently add a shade selection widget in the tree. */
3449 auto shade_stack = std::make_unique<NWidgetStacked>(INVALID_WIDGET);
3450 *shade_select = shade_stack.get();
3451 /* Load the remaining parts into the shade stack. */
3452 shade_stack->Add(MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3453 root->Add(std::move(shade_stack));
3454 return root;
3455 }
3456
3457 /* Load the remaining parts into 'root'. */
3458 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3459}
3460
3471std::unique_ptr<NWidgetBase> MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
3472{
3473 assert(max_length >= 1);
3474 std::unique_ptr<NWidgetVertical> vert = nullptr; // Storage for all rows.
3475 std::unique_ptr<NWidgetHorizontal> hor = nullptr; // Storage for buttons in one row.
3476 int hor_length = 0;
3477
3478 Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZoomLevel::Normal);
3479 sprite_size.width += WidgetDimensions::unscaled.matrix.Horizontal();
3480 sprite_size.height += WidgetDimensions::unscaled.matrix.Vertical();
3481
3482 for (WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3483 /* Ensure there is room in 'hor' for another button. */
3484 if (hor_length == max_length) {
3485 if (vert == nullptr) vert = std::make_unique<NWidgetVertical>();
3486 vert->Add(std::move(hor));
3487 hor = nullptr;
3488 hor_length = 0;
3489 }
3490 if (hor == nullptr) {
3491 hor = std::make_unique<NWidgetHorizontal>();
3492 hor_length = 0;
3493 }
3494
3495 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, button_colour, widnum);
3496 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3497 panel->SetFill(1, 1);
3498 if (resizable) panel->SetResize(1, 0);
3499 panel->SetToolTip(button_tooltip);
3500 hor->Add(std::move(panel));
3501 hor_length++;
3502 }
3503 if (vert == nullptr) return hor; // All buttons fit in a single row.
3504
3505 if (hor_length > 0 && hor_length < max_length) {
3506 /* Last row is partial, add a spacer at the end to force all buttons to the left. */
3507 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3508 spc->SetFill(1, 1);
3509 if (resizable) spc->SetResize(1, 0);
3510 hor->Add(std::move(spc));
3511 }
3512 if (hor != nullptr) vert->Add(std::move(hor));
3513 return vert;
3514}
3515
3522{
3523 assert(parent_window != nullptr);
3524 if (parent_window->nested_focus != nullptr) {
3525 for (auto &widget : this->children) {
3526 if (parent_window->nested_focus == widget.get()) {
3527 parent_window->UnfocusFocusedWidget();
3528 }
3529 }
3530 }
3531}
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.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Enum-as-bit-set wrapper.
Nested widget with a child.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2365
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:2375
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:2311
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2242
NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr< NWidgetPIPContainer > &&child=nullptr)
Constructor parent nested widgets.
Definition widget.cpp:2174
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:2208
std::unique_ptr< NWidgetPIPContainer > child
Child widget.
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2329
void Add(std::unique_ptr< NWidgetBase > &&nwid)
Add a child to the parent.
Definition widget.cpp:2189
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:2323
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:2227
Baseclass for nested widgets.
void StoreSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height)
Store size and position.
float aspect_ratio
Desired aspect ratio of widget.
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:949
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.
virtual void Draw(const Window *w)=0
Draw the widgets of the tree.
uint current_x
Current horizontal size (after resizing).
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
void SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Set additional space (padding) around the widget.
uint smallest_y
Smallest vertical size of the widget in a filled window.
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:969
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:933
Baseclass for container widgets.
void Add(std::unique_ptr< NWidgetBase > &&wid)
Append widget wid to container.
Definition widget.cpp:1320
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1327
void UnfocusWidgets(Window *parent_window)
Unfocuses the focused widget of the window, if the focused widget is contained inside the container.
Definition widget.cpp:3521
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1335
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:1344
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:1298
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:1252
bool IsDisabled() const
Return whether the widget is disabled.
void SetSprite(SpriteID sprite)
Set sprite of the nested widget.
Definition widget.cpp:1202
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:1242
WidgetID GetScrollbarIndex() const
Get the WidgetID of this nested widget's scrollbar.
Definition widget.cpp:1288
void SetAlignment(StringAlignment align)
Set the text/image alignment of the nested widget.
Definition widget.cpp:1270
void SetResizeWidgetType(ResizeWidgetValues type)
Set the resize widget type of the nested widget.
Definition widget.cpp:1232
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:1170
void SetSpriteTip(SpriteID sprite, StringID tool_tip)
Set sprite and tool tip of the nested widget.
Definition widget.cpp:1212
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:1279
WidgetID scrollbar_index
Index of an attached scrollbar.
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1261
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1293
void SetString(StringID string)
Set string of the nested widget.
Definition widget.cpp:1182
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:1223
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:1192
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:1741
Horizontal container.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1563
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:1632
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:1491
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1469
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1509
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2993
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition widget.cpp:2670
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:2697
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:2806
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:3137
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2088
int count
Amount of valid elements.
void SetClicked(int clicked)
Sets the clicked element in the matrix.
Definition widget.cpp:1957
int GetCurrentElement() const
Get current element.
Definition widget.cpp:2007
Scrollbar * sb
The scrollbar we're associated with.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2012
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:1998
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:2143
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:2034
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2058
int clicked
The currently clicked element.
void SetCount(int count)
Set the number of elements in this matrix.
Definition widget.cpp:1974
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:1556
uint8_t uz_pip_inter
Unscaled space between widgets.
uint8_t pip_ratio_pre
Ratio of remaining space before first widget.
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:1536
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:1045
bool UpdateSize(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1134
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:999
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:1090
void SetToolbarMinimalSize(uint8_t toolbar_size)
Set minimal size of the widget in toolbar-icon-relative width.
Definition widget.cpp:1055
void SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
Set minimal text lines for the widget.
Definition widget.cpp:1078
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:1114
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:1155
bool UpdateVerticalSize(uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1148
void SetResize(uint resize_x, uint resize_y)
Set resize step of the widget.
Definition widget.cpp:1101
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:1010
void SetMinimalSizeAbsolute(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1065
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:2614
NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index)
Scrollbar widget.
Definition widget.cpp:2572
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2593
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:1918
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1948
void SetDirty(const Window *w) const override
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:1943
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1931
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1924
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:1355
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:1396
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1426
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition widget.cpp:1450
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1416
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1435
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:1815
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1746
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition widget.cpp:2430
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2394
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2387
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition widget.cpp:2421
Scrollbar data structure.
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
void SetCount(size_t num)
Sets the number of elements in the list.
bool IsVertical() const
Is the scrollbar vertical or not?
bool UpdatePosition(int difference, ScrollbarStepping unit=SS_SMALL)
Updates the position of the first visible element by the given amount.
void SetCapacity(size_t capacity)
Set the capacity of visible elements.
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition widget.cpp:2453
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:2527
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:2474
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
void SetStepSize(size_t stepsize)
Set the distance to scroll when using the buttons or the wheel.
size_type pos
Index of first visible item of the list.
size_type GetPosition() const
Gets the position of the first visible element in the list.
@ SS_BIG
Step in cap units.
static constexpr uint WD_CAPTION_HEIGHT
Minimum height of a title bar.
Definition window_gui.h:87
RectPadding defsizebox
Padding around image in defsizebox widget.
Definition window_gui.h:46
RectPadding closebox
Padding around image in closebox widget.
Definition window_gui.h:48
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:40
static constexpr uint WD_DROPDOWN_HEIGHT
Minimum height of a drop down widget.
Definition window_gui.h:88
RectPadding captiontext
Padding for text within caption widget.
Definition window_gui.h:49
RectPadding debugbox
Padding around image in debugbox widget.
Definition window_gui.h:45
RectPadding frametext
Padding inside frame with text.
Definition window_gui.h:41
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
RectPadding modalpopup
Spacing for popup warning/information windows.
Definition window_gui.h:52
RectPadding hscrollbar
Padding inside horizontal scrollbar buttons.
Definition window_gui.h:37
RectPadding shadebox
Padding around image in shadebox widget.
Definition window_gui.h:43
RectPadding resizebox
Padding around image in resizebox widget.
Definition window_gui.h:47
RectPadding vscrollbar
Padding inside vertical scrollbar buttons.
Definition window_gui.h:36
RectPadding imgbtn
Padding around image button image.
Definition window_gui.h:34
int vsep_normal
Normal vertical spacing.
Definition window_gui.h:58
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:60
int hsep_wide
Wide horizontal spacing.
Definition window_gui.h:62
static constexpr uint WD_CLOSEBOX_WIDTH
Minimum width of a close box widget.
Definition window_gui.h:86
RectPadding fullbevel
Always-scaled bevel thickness.
Definition window_gui.h:39
RectPadding inset
Padding inside inset container.
Definition window_gui.h:35
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
RectPadding matrix
Padding of WWT_MATRIX items.
Definition window_gui.h:42
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:61
RectPadding dropdownlist
Padding of complete drop down list.
Definition window_gui.h:51
static constexpr uint WD_DEBUGBOX_WIDTH
Minimum width of a standard debug box widget.
Definition window_gui.h:83
RectPadding stickybox
Padding around image in stickybox widget.
Definition window_gui.h:44
static constexpr uint WD_DEFSIZEBOX_WIDTH
Minimum width of a standard defsize box widget.
Definition window_gui.h:84
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
RectPadding dropdowntext
Padding of drop down list item.
Definition window_gui.h:50
static constexpr uint WD_SHADEBOX_WIDTH
Distances used in drawing widgets.
Definition window_gui.h:81
int hsep_indent
Width of indentation for tree layouts.
Definition window_gui.h:63
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:715
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:968
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:897
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:668
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:463
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:1034
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:115
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:1568
Dimension GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
Definition widget.cpp:83
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
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
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:3429
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:3410
constexpr NWidgetPart SetAlignment(StringAlignment align)
Widget part function for setting the alignment of text/images.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:968
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:1516
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:388
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...
GUISettings gui
settings related to the GUI
T y
Y coordinate.
T x
X coordinate.
Point pos
logical mouse position
Definition gfx_type.h:125
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
bool scale_bevels
bevels are scaled with GUI scale.
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
StringAlignment align
Alignment of text/image.
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:621
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:833
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:784
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:506
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:720
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:816
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:471
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
Definition window_gui.h:860
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:607
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:560
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:335
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:212
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:983
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:715
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:307
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition widget.cpp:2544
void ApplyNWidgetPartAttribute(const NWidgetPart &nwid, NWidgetBase *dest)
Apply an attribute NWidgetPart to an NWidget.
Definition widget.cpp:3166
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:167
static void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
Draw a defsize box.
Definition widget.cpp:676
Dimension GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
Definition widget.cpp:83
bool IsContainerWidgetType(WidgetType tp)
Test if WidgetType is a container widget.
Definition widget.cpp:3353
static void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
Draw a NewGRF debug box.
Definition widget.cpp:687
static void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
Draw a sticky box.
Definition widget.cpp:665
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:601
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:382
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:138
static Dimension _toolbar_image_size
Cached dimension of maximal toolbar sprite size.
Definition widget.cpp:90
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:461
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:560
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:766
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
static std::unique_ptr< NWidgetBase > MakeNWidget(const NWidgetPart &nwid)
Make NWidget from an NWidgetPart.
Definition widget.cpp:3282
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:96
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:736
static void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
Draw a shade box.
Definition widget.cpp:654
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:292
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:362
static bool IsAttributeWidgetPartType(WidgetType tp)
Test if (an NWidgetPart) WidgetType is an attribute widget part type.
Definition widget.cpp:3155
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:410
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:268
static void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
Draw a resize box.
Definition widget.cpp:700
static void DrawText(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
Draw text.
Definition widget.cpp:427
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:203
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:445
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:3366
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:519
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:3471
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.
@ ShadeDimmed
Display dimmed colours in the viewport.
@ ScrollbarUp
Up-button is lowered bit.
@ DropdownActive
Dropdown menu of the button dropdown widget is active.
@ NoTransparency
Viewport is never transparent.
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()
@ ST_RESIZE
Resize the nested widget tree.
@ ST_SMALLEST
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
@ AWV_LEFT
Force the arrow to the left.
Definition widget_type.h:22
@ AWV_RIGHT
Force the arrow to the right.
Definition widget_type.h:23
@ AWV_DECREASE
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:20
@ AWV_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.
ResizeWidgetValues
WidgetData values for a resize box widget.
Definition widget_type.h:27
@ RWV_SHOW_BEVEL
Bevel of resize box is shown.
Definition widget_type.h:28
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:307
@ Transparent
Makes the background transparent if set.
@ BorderOnly
Draw border only, no background.
@ Darkened
If set the background is darker, allows for lowered frames with normal background colour when used wi...
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
@ SizingLeft
Window is being resized towards the left.
@ Highlighted
Window has a widget that has a highlight.
@ SizingRight
Window is being resized towards the right.
@ WhiteBorder
Window white border counter bit mask.
@ Sticky
Window is made sticky by user.
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.