OpenTTD Source 20260731-master-g77ba2b244a
widget.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "company_func.h"
13#include "settings_gui.h"
14#include "strings_type.h"
15#include "window_gui.h"
16#include "viewport_func.h"
17#include "zoom_func.h"
18#include "strings_func.h"
19#include "transparency.h"
21#include "settings_type.h"
22#include "querystring_gui.h"
23
24#include "table/sprites.h"
25#include "table/strings.h"
27
28#include "safeguards.h"
29
31
32static std::string GetStringForWidget(const Window *w, const NWidgetCore *nwid, bool secondary = false)
33{
34 StringID stringid = nwid->GetString();
35 if (nwid->GetIndex() < 0) {
36 if (stringid == STR_NULL) return {};
37
38 return GetString(stringid + (secondary ? 1 : 0));
39 }
40
41 return w->GetWidgetString(nwid->GetIndex(), stringid + (secondary ? 1 : 0));
42}
43
49static inline RectPadding ScaleGUITrad(const RectPadding &r)
50{
51 return {(uint8_t)ScaleGUITrad(r.left), (uint8_t)ScaleGUITrad(r.top), (uint8_t)ScaleGUITrad(r.right), (uint8_t)ScaleGUITrad(r.bottom)};
52}
53
59static inline Dimension ScaleGUITrad(const Dimension &dim)
60{
61 return {(uint)ScaleGUITrad(dim.width), (uint)ScaleGUITrad(dim.height)};
62}
63
71{
72 Point offset;
73 Dimension d = GetSpriteSize(sprid, &offset, ZoomLevel::Normal);
74 d.width -= offset.x;
75 d.height -= offset.y;
76 return ScaleGUITrad(d);
77}
78
86{
88 uint x = std::max(d.width, d.height);
89 return {x, x};
90}
91
94
99{
104 if (_settings_client.gui.scale_bevels) {
106 } else {
108 }
123
129
131}
132
140static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, Alignment align)
141{
142 Point p;
143 /* In case we have a RTL language we swap the alignment. */
144 switch (align.ResolveRTL()) {
145 case AlignmentH::ForceLeft: p.x = r.left; break;
146 case AlignmentH::Centre: p.x = CentreBounds(r.left, r.right, d.width); break;
147 case AlignmentH::ForceRight: p.x = r.right + 1 - d.width; break;
148 default: NOT_REACHED();
149 }
150 switch (align.v) {
151 case AlignmentV::Top: p.y = r.top; break;
152 case AlignmentV::Middle: p.y = CentreBounds(r.top, r.bottom, d.height); break;
153 case AlignmentV::Bottom: p.y = r.bottom + 1 - d.height; break;
154 default: NOT_REACHED();
155 }
156 return p;
157}
158
168static std::pair<int, int> HandleScrollbarHittest(const Scrollbar *sb, int mi, int ma, bool horizontal)
169{
170 /* Base for reversion */
171 int rev_base = mi + ma;
172 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
173
174 mi += button_size; // now points to just after the up/left-button
175 ma -= button_size; // now points to just before the down/right-button
176
177 int count = sb->GetCount();
178 int cap = sb->GetCapacity();
179
180 if (count > cap) {
181 int height = ma + 1 - mi;
182 int slider_height = std::max(button_size, cap * height / count);
183 height -= slider_height;
184
185 mi += height * sb->GetPosition() / (count - cap);
186 ma = mi + slider_height - 1;
187 }
188
189 /* Reverse coordinates for RTL. */
190 if (horizontal && _current_text_dir == TD_RTL) return {rev_base - ma, rev_base - mi};
191
192 return {mi, ma};
193}
194
204static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, int y, int mi, int ma)
205{
206 int pos;
207 int button_size;
208 bool rtl = false;
209 bool changed = false;
210
211 if (sb->type == NWID_HSCROLLBAR) {
212 pos = x;
213 rtl = _current_text_dir == TD_RTL;
214 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
215 } else {
216 pos = y;
217 button_size = NWidgetScrollbar::GetVerticalDimension().height;
218 }
219 if (pos < mi + button_size) {
220 /* Pressing the upper button? */
222 if (_scroller_click_timeout <= 1) {
223 _scroller_click_timeout = 3;
224 changed = sb->UpdatePosition(rtl ? 1 : -1);
225 }
226 w->mouse_capture_widget = sb->GetIndex();
227 } else if (pos >= ma - button_size) {
228 /* Pressing the lower button? */
230
231 if (_scroller_click_timeout <= 1) {
232 _scroller_click_timeout = 3;
233 changed = sb->UpdatePosition(rtl ? -1 : 1);
234 }
235 w->mouse_capture_widget = sb->GetIndex();
236 } else {
237 auto [start, end] = HandleScrollbarHittest(sb, mi, ma, sb->type == NWID_HSCROLLBAR);
238
239 if (pos < start) {
240 changed = sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::Stepping::Big);
241 } else if (pos > end) {
242 changed = sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::Stepping::Big);
243 } else {
244 _scrollbar_start_pos = start - mi - button_size;
245 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
246 w->mouse_capture_widget = sb->GetIndex();
247 _cursorpos_drag_start = _cursor.pos;
248 }
249 }
250
251 if (changed) {
252 /* Position changed so refresh the window */
253 w->OnScrollbarScroll(sb->GetIndex());
254 w->SetDirty();
255 } else {
256 /* No change so only refresh this scrollbar */
257 sb->SetDirty(w);
258 }
259}
260
269void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
270{
271 int mi, ma;
272
273 if (nw->type == NWID_HSCROLLBAR) {
274 mi = nw->pos_x;
275 ma = nw->pos_x + nw->current_x;
276 } else {
277 mi = nw->pos_y;
278 ma = nw->pos_y + nw->current_y;
279 }
280 NWidgetScrollbar *scrollbar = dynamic_cast<NWidgetScrollbar*>(nw);
281 assert(scrollbar != nullptr);
282 ScrollbarClickPositioning(w, scrollbar, x, y, mi, ma);
283}
284
293WidgetID GetWidgetFromPos(const Window *w, int x, int y)
294{
295 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
296 return (nw != nullptr) ? nw->GetIndex() : INVALID_WIDGET;
297}
298
308void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
309{
310 if (flags.Test(FrameFlag::Transparent)) {
312 } else {
313 assert(colour < Colours::End);
314
315 const PixelColour dark = GetColourGradient(colour, Shade::Dark);
316 const PixelColour medium_dark = GetColourGradient(colour, Shade::Light);
317 const PixelColour medium_light = GetColourGradient(colour, Shade::Lighter);
318 const PixelColour light = GetColourGradient(colour, Shade::Lightest);
319 PixelColour interior;
320
321 Rect outer = {left, top, right, bottom}; // Outside rectangle
322 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
323
324 if (flags.Test(FrameFlag::Lowered)) {
325 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark); // Left
326 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark); // Top
327 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light); // Right
328 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light); // Bottom
329 interior = (flags.Test(FrameFlag::Darkened) ? medium_dark : medium_light);
330 } else {
331 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light); // Left
332 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light); // Top
333 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark); // Right
334 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark); // Bottom
335 interior = medium_dark;
336 }
337 if (!flags.Test(FrameFlag::BorderOnly)) {
338 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior); // Inner
339 }
340 }
341}
342
351{
352 Point offset;
353 Dimension d = GetSpriteSize(img, &offset);
354 d.width -= offset.x;
355 d.height -= offset.y;
356
357 Point p = GetAlignedPosition(r, d, align);
358 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
359}
360
370static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, Alignment align)
371{
372 assert(img != 0);
373 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
374
375 if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2.
376 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
377}
378
390static inline void DrawImageTextButtons(const Rect &r, Colours colour, bool clicked, SpriteID img, TextColour text_colour, const std::string &text, Alignment align, FontSize fs)
391{
392 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
393
394 bool rtl = _current_text_dir == TD_RTL;
395 int image_width = img != 0 ? std::max<int>(GetSquareScaledSpriteSize(img).width, r.Shrink(WidgetDimensions::scaled.framerect).Height()) : 0;
396 Rect r_img = r.Shrink(WidgetDimensions::scaled.framerect).WithWidth(image_width, rtl);
397 Rect r_text = r.Shrink(WidgetDimensions::scaled.framerect).Indent(image_width + WidgetDimensions::scaled.hsep_wide, rtl);
398
399 if (img != 0) {
400 DrawSpriteIgnorePadding(img, PAL_NONE, r_img, {AlignmentH::Centre, align.v});
401 }
402
403 if (!text.empty()) {
404 Dimension d = GetStringBoundingBox(text, fs);
405 Point p = GetAlignedPosition(r_text, d, align);
406 DrawString(r_text.left, r_text.right, p.y, text, text_colour, align, false, fs);
407 }
408}
409
418static inline void DrawLabel(const Rect &r, TextColour colour, std::string_view str, Alignment align, FontSize fs)
419{
420 if (str.empty()) return;
421
422 Dimension d = GetStringBoundingBox(str, fs);
423 Point p = GetAlignedPosition(r, d, align);
424 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
425}
426
435static inline void DrawText(const Rect &r, TextColour colour, std::string_view str, Alignment align, FontSize fs)
436{
437 if (str.empty()) return;
438
439 Dimension d = GetStringBoundingBox(str, fs);
440 Point p = GetAlignedPosition(r, d, align);
441 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
442}
443
453static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, Alignment align, FontSize fs)
454{
456 if (!str.empty()) DrawString(r.Shrink(WidgetDimensions::scaled.inset), str, text_colour, align, false, fs);
457}
458
469static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
470{
471 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
472
473 int column_width; // Width of a single column in the matrix.
474 if (num_columns == 0) {
475 column_width = resize_x;
476 num_columns = r.Width() / column_width;
477 } else {
478 column_width = r.Width() / num_columns;
479 }
480
481 int row_height; // Height of a single row in the matrix.
482 if (num_rows == 0) {
483 row_height = resize_y;
484 num_rows = r.Height() / row_height;
485 } else {
486 row_height = r.Height() / num_rows;
487 }
488
490
491 int x = r.left;
492 for (int ctr = num_columns; ctr > 1; ctr--) {
493 x += column_width;
494 GfxFillRect(x, r.top + WidgetDimensions::scaled.bevel.top, x + WidgetDimensions::scaled.bevel.left - 1, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
495 }
496
497 x = r.top;
498 for (int ctr = num_rows; ctr > 1; ctr--) {
499 x += row_height;
500 GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x, r.right - WidgetDimensions::scaled.bevel.right, x + WidgetDimensions::scaled.bevel.top - 1, col);
501 }
502
503 col = GetColourGradient(colour, Shade::Normal);
504
505 x = r.left - 1;
506 for (int ctr = num_columns; ctr > 1; ctr--) {
507 x += column_width;
508 GfxFillRect(x - WidgetDimensions::scaled.bevel.right + 1, r.top + WidgetDimensions::scaled.bevel.top, x, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
509 }
510
511 x = r.top - 1;
512 for (int ctr = num_rows; ctr > 1; ctr--) {
513 x += row_height;
514 GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x - WidgetDimensions::scaled.bevel.bottom + 1, r.right - WidgetDimensions::scaled.bevel.right, x, col);
515 }
516}
517
527static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
528{
529 int height = NWidgetScrollbar::GetVerticalDimension().height;
530
531 /* draw up/down buttons */
532 DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, {AlignmentH::Centre, AlignmentV::Middle});
533 DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, {AlignmentH::Centre, AlignmentV::Middle});
534
537
538 /* draw "shaded" background */
539 Rect bg = r.Shrink(0, height);
540 GfxFillRect(bg, c2);
542
543 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
544 int left = r.left + r.Width() * 3 / 11; /* left track is positioned 3/11ths from the left */
545 int right = r.left + r.Width() * 8 / 11; /* right track is positioned 8/11ths from the left */
546 const uint8_t bl = WidgetDimensions::scaled.bevel.left;
547 const uint8_t br = WidgetDimensions::scaled.bevel.right;
548
549 /* draw shaded lines */
550 GfxFillRect(bg.WithX(left - bl, left - 1), c1);
551 GfxFillRect(bg.WithX(left, left + br - 1), c2);
552 GfxFillRect(bg.WithX(right - bl, right - 1), c1);
553 GfxFillRect(bg.WithX(right, right + br - 1), c2);
554
555 auto [top, bottom] = HandleScrollbarHittest(scrollbar, r.top, r.bottom, false);
556 DrawFrameRect(r.left, top, r.right, bottom, colour, bar_dragged ? FrameFlag::Lowered : FrameFlags{});
557}
558
568static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
569{
570 int width = NWidgetScrollbar::GetHorizontalDimension().width;
571
572 DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, {AlignmentH::Centre, AlignmentV::Middle});
573 DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, {AlignmentH::Centre, AlignmentV::Middle});
574
577
578 /* draw "shaded" background */
579 Rect bg = r.Shrink(width, 0);
580 GfxFillRect(bg, c2);
582
583 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
584 int top = r.top + r.Height() * 3 / 11; /* top track is positioned 3/11ths from the top */
585 int bottom = r.top + r.Height() * 8 / 11; /* bottom track is positioned 8/11ths from the top */
586 const uint8_t bt = WidgetDimensions::scaled.bevel.top;
587 const uint8_t bb = WidgetDimensions::scaled.bevel.bottom;
588
589 /* draw shaded lines */
590 GfxFillRect(bg.WithY(top - bt, top - 1), c1);
591 GfxFillRect(bg.WithY(top, top + bb - 1), c2);
592 GfxFillRect(bg.WithY(bottom - bt, bottom - 1), c1);
593 GfxFillRect(bg.WithY(bottom, bottom + bb - 1), c2);
594
595 /* draw actual scrollbar */
596 auto [left, right] = HandleScrollbarHittest(scrollbar, r.left, r.right, true);
597 DrawFrameRect(left, r.top, right, r.bottom, colour, bar_dragged ? FrameFlag::Lowered : FrameFlags{});
598}
599
609static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, Alignment align, FontSize fs)
610{
611 int x2 = r.left; // by default the left side is the left side of the widget
612
613 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);
614
617
618 /* If the frame has text, adjust the top bar to fit half-way through */
619 Rect inner = r.Shrink(ScaleGUITrad(1));
620 if (!str.empty()) inner.top = r.top + GetCharacterHeight(FontSize::Normal) / 2;
621
622 Rect outer = inner.Expand(WidgetDimensions::scaled.bevel);
623 Rect inside = inner.Shrink(WidgetDimensions::scaled.bevel);
624
625 if (_current_text_dir == TD_LTR) {
626 /* Line from upper left corner to start of text */
627 GfxFillRect(outer.left, outer.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
628 GfxFillRect(inner.left, inner.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
629
630 /* Line from end of text to upper right corner */
631 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
632 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
633 } else {
634 /* Line from upper left corner to start of text */
635 GfxFillRect(outer.left, outer.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
636 GfxFillRect(inner.left, inner.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
637
638 /* Line from end of text to upper right corner */
639 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
640 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
641 }
642
643 /* Line from upper left corner to bottom left corner */
644 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
645 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
646
647 /* Line from upper right corner to bottom right corner */
648 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
649 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
650
651 /* Line from bottom left corner to bottom right corner */
652 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
653 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
654}
655
662static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
663{
665}
666
673static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
674{
675 DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, {AlignmentH::Centre, AlignmentV::Middle});
676}
677
684static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
685{
686 DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, {AlignmentH::Centre, AlignmentV::Middle});
687}
688
695static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
696{
698}
699
708static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
709{
710 if (bevel) {
711 DrawFrameRect(r, colour, clicked ? FrameFlag::Lowered : FrameFlags{});
712 } else if (clicked) {
714 }
716}
717
723static inline void DrawCloseBox(const Rect &r, Colours colour)
724{
725 if (colour != Colours::White) DrawFrameRect(r, colour, {});
726 Point offset;
727 Dimension d = GetSpriteSize(SPR_CLOSEBOX, &offset);
728 d.width -= offset.x;
729 d.height -= offset.y;
730 int s = ScaleSpriteTrad(1); // Offset to account for shadow of SPR_CLOSEBOX.
731 DrawSprite(SPR_CLOSEBOX, to_underlying(colour != Colours::White ? TextColour::Black : TextColour::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);
732}
733
744void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, std::string_view str, Alignment align, FontSize fs)
745{
746 bool company_owned = owner < MAX_COMPANIES;
747
751
752 if (company_owned) {
754 }
755
756 if (str.empty()) return;
757
759 Point p = GetAlignedPosition(r, d, align);
760 DrawString(r.left + WidgetDimensions::scaled.captiontext.left, r.right - WidgetDimensions::scaled.captiontext.left, p.y, str, text_colour, align, false, fs);
761}
762
774static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, std::string_view str, Alignment align)
775{
776 bool rtl = _current_text_dir == TD_RTL;
777
778 Rect text = r.Indent(NWidgetLeaf::dropdown_dimension.width, !rtl);
779 DrawFrameRect(text, colour, clicked_button ? FrameFlag::Lowered : FrameFlags{});
780 if (!str.empty()) {
781 text = text.CentreToHeight(GetCharacterHeight(FontSize::Normal)).Shrink(WidgetDimensions::scaled.dropdowntext, RectPadding::zero);
782 DrawString(text, str, TextColour::Black, align);
783 }
784
785 Rect button = r.WithWidth(NWidgetLeaf::dropdown_dimension.width, !rtl);
787}
788
793{
794 this->nested_root->Draw(this);
795
796 if (this->flags.Test(WindowFlag::WhiteBorder)) {
797 DrawFrameRect(0, 0, this->width - 1, this->height - 1, Colours::White, FrameFlag::BorderOnly);
798 }
799
800 if (this->flags.Test(WindowFlag::Highlighted)) {
801 extern bool _window_highlight_colour;
802 for (const auto &pair : this->widget_lookup) {
803 const NWidgetBase *widget = pair.second;
804 if (!widget->IsHighlighted()) continue;
805
806 Rect outer = widget->GetCurrentRect();
807 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1);
808
810
811 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
812 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
813 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
814 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
815 }
816 }
817}
818
824void Window::DrawSortButton(WidgetID widget, bool descending) const
825{
826 assert(!this->widget_lookup.empty());
827 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
828
829 /* Sort button uses the same sprites as vertical scrollbar */
830 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
831
832 DrawSpriteIgnorePadding(descending ? SPR_ARROW_DOWN : SPR_ARROW_UP, PAL_NONE, r.WithWidth(dim.width, _current_text_dir == TD_LTR), {AlignmentH::Centre, AlignmentV::Middle});
833}
834
840{
841 return NWidgetScrollbar::GetVerticalDimension().width + 1;
842}
843
844bool _draw_widget_outlines;
845
846static void DrawOutline(const Window *, const NWidgetBase *wid)
847{
848 if (!_draw_widget_outlines || wid->current_x == 0 || wid->current_y == 0) return;
849
850 DrawRectOutline(wid->GetCurrentRect(), PC_WHITE, 1, 4);
851}
852
905
919
933
940{
941 if (this->index >= 0) widget_lookup[this->index] = this;
942}
943
950
955void NWidgetBase::SetDirty(const Window *w) const
956{
957 int abs_left = w->left + this->pos_x;
958 int abs_top = w->top + this->pos_y;
959 AddDirtyBlock(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
960}
961
969
976{
977 return (this->type == tp) ? this : nullptr;
978}
979
980void NWidgetBase::ApplyAspectRatio()
981{
982 if (this->aspect_ratio == 0) return;
983 if (this->smallest_x == 0 || this->smallest_y == 0) return;
984
985 uint x = this->smallest_x;
986 uint y = this->smallest_y;
987 if (this->aspect_flags.Test(AspectFlag::ResizeX)) x = std::max(this->smallest_x, static_cast<uint>(this->smallest_y * std::abs(this->aspect_ratio)));
988 if (this->aspect_flags.Test(AspectFlag::ResizeY)) y = std::max(this->smallest_y, static_cast<uint>(this->smallest_x / std::abs(this->aspect_ratio)));
989
990 this->smallest_x = x;
991 this->smallest_y = y;
992}
993
998
1007{
1008 this->fill_x = fill_x;
1009 this->fill_y = fill_y;
1010}
1011
1018{
1019 this->aspect_ratio = ratio;
1020 this->aspect_flags = flags;
1021}
1022
1029void NWidgetResizeBase::SetAspect(int x_ratio, int y_ratio, AspectFlags flags)
1030{
1031 assert(x_ratio > 0 && y_ratio > 0);
1032 this->SetAspect(static_cast<float>(x_ratio) / static_cast<float>(y_ratio), flags);
1033}
1034
1036{
1037 if (!this->absolute) {
1038 this->min_x = ScaleGUITrad(this->uz_min_x);
1039 this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1040 if (this->toolbar_size > 0) {
1041 this->min_x = std::max(this->min_x, this->toolbar_size * _toolbar_image_size.width + WidgetDimensions::scaled.imgbtn.Horizontal());
1042 this->min_y = std::max(this->min_y, _toolbar_image_size.height + WidgetDimensions::scaled.imgbtn.Vertical());
1043 }
1044 }
1046}
1047
1054{
1055 this->uz_min_x = std::max(this->uz_min_x, min_x);
1056 this->uz_min_y = std::max(this->uz_min_y, min_y);
1057}
1058
1064{
1065 this->toolbar_size = toolbar_size;
1066}
1067
1074{
1075 this->absolute = true;
1076 this->min_x = std::max(this->min_x, min_x);
1077 this->min_y = std::max(this->min_y, min_y);
1078}
1079
1086void NWidgetResizeBase::SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
1087{
1088 this->uz_text_lines = min_lines;
1089 this->uz_text_spacing = spacing;
1090 this->uz_text_size = size;
1091}
1092
1099{
1100 this->fill_x = fill_x;
1101 this->fill_y = fill_y;
1102}
1103
1110{
1111 this->resize_x = resize_x;
1112 this->resize_y = resize_y;
1113}
1114
1122bool NWidgetResizeBase::UpdateMultilineWidgetSize(const std::string &str, int max_lines)
1123{
1124 int y = GetStringHeight(str, this->current_x);
1125 if (y > max_lines * GetCharacterHeight(FontSize::Normal)) {
1126 /* Text at the current width is too tall, so try to guess a better width. */
1128 d.height *= max_lines;
1129 d.width /= 2;
1130 return this->UpdateSize(d.width, d.height);
1131 }
1132 return this->UpdateVerticalSize(y);
1133}
1134
1143{
1144 if (min_x == this->min_x && min_y == this->min_y) return false;
1145 this->min_x = min_x;
1146 this->min_y = min_y;
1147 return true;
1148}
1149
1157{
1158 if (min_y == this->min_y) return false;
1159 this->min_y = min_y;
1160 return true;
1161}
1162
1163void NWidgetResizeBase::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1164{
1165 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1166}
1167
1179{
1180 this->colour = colour;
1181 this->widget_data = widget_data;
1182 this->SetToolTip(tool_tip);
1184}
1185
1191{
1192 this->widget_data.string = string;
1193}
1194
1201{
1202 this->SetString(string);
1203 this->SetToolTip(tool_tip);
1204}
1205
1211{
1212 this->widget_data.sprite = sprite;
1213}
1214
1221{
1222 this->SetSprite(sprite);
1223 this->SetToolTip(tool_tip);
1224}
1225
1231void NWidgetCore::SetMatrixDimension(uint32_t columns, uint32_t rows)
1232{
1233 this->widget_data.matrix = { columns, rows };
1234}
1235
1241{
1242 this->widget_data.resize_widget_type = type;
1243}
1244
1251{
1252 this->text_colour = colour;
1253 this->text_size = size;
1254}
1255
1261{
1262 this->tool_tip = tool_tip;
1263}
1264
1270{
1271 return this->tool_tip;
1272}
1273
1279{
1280 this->align = align;
1281}
1282
1288{
1289 return this->widget_data.string;
1290}
1291
1297{
1298 return this->scrollbar_index;
1299}
1300
1302{
1303 return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : nullptr;
1304}
1305
1307{
1308 if (this->type == tp) return this;
1309 for (const auto &child_wid : this->children) {
1310 NWidgetBase *nwid = child_wid->GetWidgetOfType(tp);
1311 if (nwid != nullptr) return nwid;
1312 }
1313 return nullptr;
1314}
1315
1317{
1318 for (const auto &child_wid : this->children) {
1319 child_wid->AdjustPaddingForZoom();
1320 }
1322}
1323
1328void NWidgetContainer::Add(std::unique_ptr<NWidgetBase> &&wid)
1329{
1330 assert(wid != nullptr);
1331 wid->parent = this;
1332 this->children.push_back(std::move(wid));
1333}
1334
1336{
1337 this->NWidgetBase::FillWidgetLookup(widget_lookup);
1338 for (const auto &child_wid : this->children) {
1339 child_wid->FillWidgetLookup(widget_lookup);
1340 }
1341}
1342
1344{
1345 for (const auto &child_wid : this->children) {
1346 child_wid->Draw(w);
1347 }
1348
1349 DrawOutline(w, this);
1350}
1351
1353{
1354 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1355
1356 for (const auto &child_wid : this->children) {
1357 NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
1358 if (nwid != nullptr) return nwid;
1359 }
1360 return nullptr;
1361}
1362
1364{
1365 /* Zero size plane selected */
1366 if (this->shown_plane >= SZSP_BEGIN) {
1367 Dimension size = {0, 0};
1368 Dimension padding = {0, 0};
1369 Dimension fill = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1370 Dimension resize = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1371 /* Here we're primarily interested in the value of resize */
1372 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
1373
1374 this->smallest_x = size.width;
1375 this->smallest_y = size.height;
1376 this->fill_x = fill.width;
1377 this->fill_y = fill.height;
1378 this->resize_x = resize.width;
1379 this->resize_y = resize.height;
1380 this->ApplyAspectRatio();
1381 return;
1382 }
1383
1384 /* First sweep, recurse down and compute minimal size and filling. */
1385 this->smallest_x = 0;
1386 this->smallest_y = 0;
1387 this->fill_x = this->IsEmpty() ? 0 : 1;
1388 this->fill_y = this->IsEmpty() ? 0 : 1;
1389 this->resize_x = this->IsEmpty() ? 0 : 1;
1390 this->resize_y = this->IsEmpty() ? 0 : 1;
1391 for (const auto &child_wid : this->children) {
1392 child_wid->SetupSmallestSize(w);
1393
1394 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1395 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1396 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1397 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1398 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1399 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1400 this->ApplyAspectRatio();
1401 }
1402}
1403
1404void NWidgetStacked::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1405{
1406 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1407 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1408
1409 if (this->shown_plane >= SZSP_BEGIN) return;
1410
1411 for (const auto &child_wid : this->children) {
1412 uint hor_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1413 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1414 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1415
1416 uint vert_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1417 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1418 uint child_pos_y = child_wid->padding.top;
1419
1420 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1421 }
1422}
1423
1425{
1426 /* We need to update widget_lookup later. */
1427 this->widget_lookup = &widget_lookup;
1428
1429 this->NWidgetContainer::FillWidgetLookup(widget_lookup);
1430 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1431 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(widget_lookup);
1432}
1433
1435{
1436 if (this->shown_plane >= SZSP_BEGIN) return;
1437
1438 assert(static_cast<size_t>(this->shown_plane) < this->children.size());
1439 this->children[shown_plane]->Draw(w);
1440 DrawOutline(w, this);
1441}
1442
1444{
1445 if (this->shown_plane >= SZSP_BEGIN) return nullptr;
1446
1447 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1448
1449 if (static_cast<size_t>(this->shown_plane) >= this->children.size()) return nullptr;
1450 return this->children[shown_plane]->GetWidgetFromPos(x, y);
1451}
1452
1459{
1460 if (this->shown_plane == plane) return false;
1461 this->shown_plane = plane;
1462 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1463 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(*this->widget_lookup);
1464 return true;
1465}
1466
1467class NWidgetLayer : public NWidgetContainer {
1468public:
1469 NWidgetLayer(WidgetID index) : NWidgetContainer(NWID_LAYER, index) {}
1470
1471 void SetupSmallestSize(Window *w) override;
1472 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override;
1473
1474 void Draw(const Window *w) override;
1475};
1476
1478{
1479 /* First sweep, recurse down and compute minimal size and filling. */
1480 this->smallest_x = 0;
1481 this->smallest_y = 0;
1482 this->fill_x = this->IsEmpty() ? 0 : 1;
1483 this->fill_y = this->IsEmpty() ? 0 : 1;
1484 this->resize_x = this->IsEmpty() ? 0 : 1;
1485 this->resize_y = this->IsEmpty() ? 0 : 1;
1486 for (const auto &child_wid : this->children) {
1487 child_wid->SetupSmallestSize(w);
1488
1489 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1490 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1491 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1492 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1493 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1494 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1495 this->ApplyAspectRatio();
1496 }
1497}
1498
1499void NWidgetLayer::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1500{
1501 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1502 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1503
1504 for (const auto &child_wid : this->children) {
1505 uint hor_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1506 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1507 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1508
1509 uint vert_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1510 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1511 uint child_pos_y = child_wid->padding.top;
1512
1513 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1514 }
1515}
1516
1518{
1519 /* Draw in reverse order, as layers are arranged top-down. */
1520 for (auto it = std::rbegin(this->children); it != std::rend(this->children); ++it) {
1521 (*it)->Draw(w);
1522 }
1523
1524 DrawOutline(w, this);
1525}
1526
1534
1545{
1546 this->uz_pip_pre = pip_pre;
1547 this->uz_pip_inter = pip_inter;
1548 this->uz_pip_post = pip_post;
1549
1550 this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1551 this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1552 this->pip_post = ScaleGUITrad(this->uz_pip_post);
1553}
1554
1565{
1566 this->pip_ratio_pre = pip_ratio_pre;
1567 this->pip_ratio_inter = pip_ratio_inter;
1568 this->pip_ratio_post = pip_ratio_post;
1569}
1570
1572{
1573 this->smallest_x = 0; // Sum of minimal size of all children.
1574 this->smallest_y = 0; // Biggest child.
1575 this->fill_x = 0; // smallest non-zero child widget fill step.
1576 this->fill_y = 1; // smallest common child fill step.
1577 this->resize_x = 0; // smallest non-zero child widget resize step.
1578 this->resize_y = 1; // smallest common child resize step.
1579 this->gaps = 0;
1580
1581 /* 1a. Forward call, collect longest/widest child length. */
1582 uint longest = 0; // Longest child found.
1583 uint max_vert_fill = 0; // Biggest vertical fill step.
1584 for (const auto &child_wid : this->children) {
1585 child_wid->SetupSmallestSize(w);
1586 longest = std::max(longest, child_wid->smallest_x);
1587 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(SizingType::Smallest));
1588 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1589 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->gaps++;
1590 }
1591 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1592 /* 1b. Make the container higher if needed to accommodate all children nicely. */
1593 [[maybe_unused]] uint max_smallest = this->smallest_y + 3 * max_vert_fill; // Upper limit to computing smallest height.
1594 uint cur_height = this->smallest_y;
1595 for (;;) {
1596 for (const auto &child_wid : this->children) {
1597 uint step_size = child_wid->GetVerticalStepSize(SizingType::Smallest);
1598 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1599 if (step_size > 1 && child_height < cur_height) { // Small step sizes or already fitting children are not interesting.
1600 uint remainder = (cur_height - child_height) % step_size;
1601 if (remainder > 0) { // Child did not fit entirely, widen the container.
1602 cur_height += step_size - remainder;
1603 assert(cur_height < max_smallest); // Safeguard against infinite height expansion.
1604 /* Remaining children will adapt to the new cur_height, thus speeding up the computation. */
1605 }
1606 }
1607 }
1608 if (this->smallest_y == cur_height) break;
1609 this->smallest_y = cur_height; // Smallest height got changed, try again.
1610 }
1611 /* 2. For containers that must maintain equal width, extend child minimal size. */
1612 for (const auto &child_wid : this->children) {
1613 child_wid->smallest_y = this->smallest_y - child_wid->padding.Vertical();
1614 child_wid->ApplyAspectRatio();
1615 longest = std::max(longest, child_wid->smallest_x);
1616 }
1617 if (this->flags.Test(NWidContainerFlag::EqualSize)) {
1618 for (const auto &child_wid : this->children) {
1619 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1620 }
1621 }
1622 /* 3. Compute smallest, fill, and resize values of the container. */
1623 for (const auto &child_wid : this->children) {
1624 this->smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1625 if (child_wid->fill_x > 0) {
1626 if (this->fill_x == 0 || this->fill_x > child_wid->fill_x) this->fill_x = child_wid->fill_x;
1627 }
1628 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1629
1630 if (child_wid->resize_x > 0) {
1631 if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x;
1632 }
1633 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1634 }
1635 if (this->fill_x == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_x = 1;
1636 /* 4. Increase by required PIP space. */
1637 this->smallest_x += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1638}
1639
1640void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1641{
1642 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1643
1644 /* Compute additional width given to us. */
1645 uint additional_length = given_width - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1646 for (const auto &child_wid : this->children) {
1647 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1648 }
1649
1650 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1651
1652 /* In principle, the additional horizontal space is distributed evenly over the available resizable children. Due to step sizes, this may not always be feasible.
1653 * To make resizing work as good as possible, first children with biggest step sizes are done. These may get less due to rounding down.
1654 * 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
1655 * of the child with the smallest non-zero stepsize.
1656 *
1657 * 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
1658 * size and position, and directly call child->AssignSizePosition() with the computed values.
1659 * 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
1660 * then we call the child.
1661 */
1662
1663 /* First loop: Find biggest stepsize, find number of children that want a piece of the pie, handle vertical size for all children,
1664 * handle horizontal size for non-resizing children.
1665 */
1666 int num_changing_childs = 0; // Number of children that can change size.
1667 uint biggest_stepsize = 0;
1668 for (const auto &child_wid : this->children) {
1669 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1670 if (hor_step > 0) {
1671 if (!flags.Test(NWidContainerFlag::BigFirst)) num_changing_childs++;
1672 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1673 } else {
1674 child_wid->current_x = child_wid->smallest_x;
1675 }
1676
1677 uint vert_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1678 child_wid->current_y = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1679 }
1680
1681 for (const auto &child_wid : this->children) {
1682 auto [pref_x, _] = child_wid->GetPreferredSizeForSize(child_wid->current_x, child_wid->current_y);
1683 additional_length += child_wid->current_x - pref_x;
1684 child_wid->current_x = pref_x;
1685 }
1686
1687 /* First.5 loop: count how many children are of the biggest step size. */
1688 if (flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1689 for (const auto &child_wid : this->children) {
1690 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1691 if (hor_step == biggest_stepsize) {
1692 num_changing_childs++;
1693 }
1694 }
1695 }
1696
1697 /* Second loop: Allocate the additional horizontal space over the resizing children, starting with the biggest resize steps. */
1698 while (biggest_stepsize > 0) {
1699 uint next_biggest_stepsize = 0;
1700 for (const auto &child_wid : this->children) {
1701 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1702 if (hor_step > biggest_stepsize) continue; // Already done
1703 if (hor_step == biggest_stepsize) {
1704 uint increment = additional_length / num_changing_childs;
1705 num_changing_childs--;
1706 if (hor_step > 1) increment -= increment % hor_step;
1707 child_wid->current_x = child_wid->smallest_x + increment;
1708 additional_length -= increment;
1709 continue;
1710 }
1711 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1712 }
1713 biggest_stepsize = next_biggest_stepsize;
1714
1715 if (num_changing_childs == 0 && flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1716 /* Second.5 loop: count how many children are of the updated biggest step size. */
1717 for (const auto &child_wid : this->children) {
1718 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1719 if (hor_step == biggest_stepsize) {
1720 num_changing_childs++;
1721 }
1722 }
1723 }
1724 }
1725 assert(num_changing_childs == 0);
1726
1727 uint pre = this->pip_pre;
1728 uint inter = this->pip_inter;
1729
1730 if (additional_length > 0) {
1731 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1732 * which is never explicitly needed. */
1733 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1734 if (r > 0) {
1735 pre += this->pip_ratio_pre * additional_length / r;
1736 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1737 }
1738 }
1739
1740 /* Third loop: Compute position and call the child. */
1741 uint position = rtl ? this->current_x - pre : pre; // Place to put next child relative to origin of the container.
1742 for (const auto &child_wid : this->children) {
1743 uint child_width = child_wid->current_x;
1744 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1745 uint child_y = y + child_wid->padding.top;
1746
1747 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1748 if (child_wid->current_x != 0) {
1749 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1750 position = rtl ? position - padded_child_width : position + padded_child_width;
1751 }
1752 }
1753}
1754
1755void NWidgetHorizontalLTR::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1756{
1757 NWidgetHorizontal::AssignSizePosition(sizing, x, y, given_width, given_height, false);
1758}
1759
1761{
1762 this->smallest_x = 0; // Biggest child.
1763 this->smallest_y = 0; // Sum of minimal size of all children.
1764 this->fill_x = 1; // smallest common child fill step.
1765 this->fill_y = 0; // smallest non-zero child widget fill step.
1766 this->resize_x = 1; // smallest common child resize step.
1767 this->resize_y = 0; // smallest non-zero child widget resize step.
1768 this->gaps = 0;
1769
1770 /* 1a. Forward call, collect longest/widest child length. */
1771 uint highest = 0; // Highest child found.
1772 uint max_hor_fill = 0; // Biggest horizontal fill step.
1773 for (const auto &child_wid : this->children) {
1774 child_wid->SetupSmallestSize(w);
1775 highest = std::max(highest, child_wid->smallest_y);
1776 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(SizingType::Smallest));
1777 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1778 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->gaps++;
1779 }
1780 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1781 /* 1b. Make the container wider if needed to accommodate all children nicely. */
1782 [[maybe_unused]] uint max_smallest = this->smallest_x + 3 * max_hor_fill; // Upper limit to computing smallest height.
1783 uint cur_width = this->smallest_x;
1784 for (;;) {
1785 for (const auto &child_wid : this->children) {
1786 uint step_size = child_wid->GetHorizontalStepSize(SizingType::Smallest);
1787 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1788 if (step_size > 1 && child_width < cur_width) { // Small step sizes or already fitting children are not interesting.
1789 uint remainder = (cur_width - child_width) % step_size;
1790 if (remainder > 0) { // Child did not fit entirely, widen the container.
1791 cur_width += step_size - remainder;
1792 assert(cur_width < max_smallest); // Safeguard against infinite width expansion.
1793 /* Remaining children will adapt to the new cur_width, thus speeding up the computation. */
1794 }
1795 }
1796 }
1797 if (this->smallest_x == cur_width) break;
1798 this->smallest_x = cur_width; // Smallest width got changed, try again.
1799 }
1800 /* 2. For containers that must maintain equal width, extend children minimal size. */
1801 for (const auto &child_wid : this->children) {
1802 child_wid->smallest_x = this->smallest_x - child_wid->padding.Horizontal();
1803 child_wid->ApplyAspectRatio();
1804 highest = std::max(highest, child_wid->smallest_y);
1805 }
1806 if (this->flags.Test(NWidContainerFlag::EqualSize)) {
1807 for (const auto &child_wid : this->children) {
1808 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1809 }
1810 }
1811 /* 3. Compute smallest, fill, and resize values of the container. */
1812 for (const auto &child_wid : this->children) {
1813 this->smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1814 if (child_wid->fill_y > 0) {
1815 if (this->fill_y == 0 || this->fill_y > child_wid->fill_y) this->fill_y = child_wid->fill_y;
1816 }
1817 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1818
1819 if (child_wid->resize_y > 0) {
1820 if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y;
1821 }
1822 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1823 }
1824 if (this->fill_y == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_y = 1;
1825 /* 4. Increase by required PIP space. */
1826 this->smallest_y += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1827}
1828
1829void NWidgetVertical::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1830{
1831 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1832
1833 /* Compute additional height given to us. */
1834 uint additional_length = given_height - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1835 for (const auto &child_wid : this->children) {
1836 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1837 }
1838
1839 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1840
1841 /* Like the horizontal container, the vertical container also distributes additional height evenly, starting with the children with the biggest resize steps.
1842 * It also stores computed widths and heights into current_x and current_y values of the child.
1843 */
1844
1845 /* 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. */
1846 int num_changing_childs = 0; // Number of children that can change size.
1847 uint biggest_stepsize = 0;
1848 for (const auto &child_wid : this->children) {
1849 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1850 if (vert_step > 0) {
1851 if (!flags.Test(NWidContainerFlag::BigFirst)) num_changing_childs++;
1852 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1853 } else {
1854 child_wid->current_y = child_wid->smallest_y;
1855 }
1856
1857 uint hor_step = (sizing == SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1858 child_wid->current_x = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1859 }
1860
1861 for (const auto &child_wid : this->children) {
1862 auto [_, pref_y] = child_wid->GetPreferredSizeForSize(child_wid->current_x, child_wid->current_y);
1863 additional_length += child_wid->current_y - pref_y;
1864 child_wid->current_y = pref_y;
1865 }
1866
1867 /* First.5 loop: count how many children are of the biggest step size. */
1868 if (this->flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1869 for (const auto &child_wid : this->children) {
1870 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1871 if (vert_step == biggest_stepsize) {
1872 num_changing_childs++;
1873 }
1874 }
1875 }
1876
1877 /* Second loop: Allocate the additional vertical space over the resizing children, starting with the biggest resize steps. */
1878 while (biggest_stepsize > 0) {
1879 uint next_biggest_stepsize = 0;
1880 for (const auto &child_wid : this->children) {
1881 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1882 if (vert_step > biggest_stepsize) continue; // Already done
1883 if (vert_step == biggest_stepsize) {
1884 uint increment = additional_length / num_changing_childs;
1885 num_changing_childs--;
1886 if (vert_step > 1) increment -= increment % vert_step;
1887 child_wid->current_y = child_wid->smallest_y + increment;
1888 additional_length -= increment;
1889 continue;
1890 }
1891 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1892 }
1893 biggest_stepsize = next_biggest_stepsize;
1894
1895 if (num_changing_childs == 0 && flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1896 /* Second.5 loop: count how many children are of the updated biggest step size. */
1897 for (const auto &child_wid : this->children) {
1898 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1899 if (vert_step == biggest_stepsize) {
1900 num_changing_childs++;
1901 }
1902 }
1903 }
1904 }
1905 assert(num_changing_childs == 0);
1906
1907 uint pre = this->pip_pre;
1908 uint inter = this->pip_inter;
1909
1910 if (additional_length > 0) {
1911 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1912 * which is never explicitly needed. */
1913 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1914 if (r > 0) {
1915 pre += this->pip_ratio_pre * additional_length / r;
1916 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1917 }
1918 }
1919
1920 /* Third loop: Compute position and call the child. */
1921 uint position = this->bottom_up ? this->current_y - pre : pre; // Place to put next child relative to origin of the container.
1922 for (const auto &child_wid : this->children) {
1923 uint child_height = child_wid->current_y;
1924 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1925 uint child_y = y + (this->bottom_up ? position - child_height - child_wid->padding.top : position + child_wid->padding.top);
1926
1927 child_wid->AssignSizePosition(sizing, child_x, child_y, child_wid->current_x, child_height, rtl);
1928 if (child_wid->current_y != 0) {
1929 uint padded_child_height = child_height + child_wid->padding.Vertical() + inter;
1930 position = this->bottom_up ? position - padded_child_height : position + padded_child_height;
1931 }
1932 }
1933}
1934
1941{
1942 this->SetMinimalSize(width, height);
1943 this->SetResize(0, 0);
1944}
1945
1947{
1948 this->smallest_x = this->min_x;
1949 this->smallest_y = this->min_y;
1950 this->ApplyAspectRatio();
1951}
1952
1954{
1955 /* Spacer widget is never normally visible. */
1956
1957 if (_draw_widget_outlines && this->current_x != 0 && this->current_y != 0) {
1958 /* Spacers indicate a potential design issue, so get extra highlighting. */
1959 GfxFillRect(this->GetCurrentRect(), PC_WHITE, FillRectMode::Checker);
1960
1961 DrawOutline(w, this);
1962 }
1963}
1964
1966{
1967 /* Spacer widget never need repainting. */
1968}
1969
1971{
1972 return nullptr;
1973}
1974
1980{
1981 this->clicked = clicked;
1982 if (this->clicked >= 0 && this->sb != nullptr && this->widgets_x != 0) {
1983 int vpos = (this->clicked / this->widgets_x) * this->widget_h; // Vertical position of the top.
1984 /* Need to scroll down -> Scroll to the bottom.
1985 * However, last entry has no 'this->pip_inter' underneath, and we must stay below this->sb->GetCount() */
1986 if (this->sb->GetPosition() < vpos) vpos += this->widget_h - this->pip_inter - 1;
1987 this->sb->ScrollTowards(vpos);
1988 }
1989}
1990
1997{
1998 this->count = count;
1999
2000 if (this->sb == nullptr || this->widgets_x == 0) return;
2001
2002 /* We need to get the number of pixels the matrix is high/wide.
2003 * So, determine the number of rows/columns based on the number of
2004 * columns/rows (one is constant/unscrollable).
2005 * Then multiply that by the height of a widget, and add the pre
2006 * and post spacing "offsets". */
2007 count = CeilDiv(count, this->sb->IsVertical() ? this->widgets_x : this->widgets_y);
2008 count *= (this->sb->IsVertical() ? this->children.front()->smallest_y : this->children.front()->smallest_x) + this->pip_inter;
2009 if (count > 0) count -= this->pip_inter; // We counted an inter too much in the multiplication above
2010 count += this->pip_pre + this->pip_post;
2011 this->sb->SetCount(count);
2012 this->sb->SetCapacity(this->sb->IsVertical() ? this->current_y : this->current_x);
2013 this->sb->SetStepSize(this->sb->IsVertical() ? this->widget_h : this->widget_w);
2014}
2015
2021{
2022 this->sb = sb;
2023}
2024
2030{
2031 return this->current_element;
2032}
2033
2035{
2036 assert(this->children.size() == 1);
2037
2038 this->children.front()->SetupSmallestSize(w);
2039
2040 Dimension padding = { (uint)this->pip_pre + this->pip_post, (uint)this->pip_pre + this->pip_post};
2041 Dimension size = {this->children.front()->smallest_x + padding.width, this->children.front()->smallest_y + padding.height};
2042 Dimension fill = {0, 0};
2043 Dimension resize = {this->pip_inter + this->children.front()->smallest_x, this->pip_inter + this->children.front()->smallest_y};
2044
2045 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
2046
2047 this->smallest_x = size.width;
2048 this->smallest_y = size.height;
2049 this->fill_x = fill.width;
2050 this->fill_y = fill.height;
2051 this->resize_x = resize.width;
2052 this->resize_y = resize.height;
2053 this->ApplyAspectRatio();
2054}
2055
2056void NWidgetMatrix::AssignSizePosition(SizingType, int x, int y, uint given_width, uint given_height, bool)
2057{
2058 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
2059
2060 this->pos_x = x;
2061 this->pos_y = y;
2062 this->current_x = given_width;
2063 this->current_y = given_height;
2064
2065 /* Determine the size of the widgets, and the number of visible widgets on each of the axis. */
2066 this->widget_w = this->children.front()->smallest_x + this->pip_inter;
2067 this->widget_h = this->children.front()->smallest_y + this->pip_inter;
2068
2069 /* Account for the pip_inter is between widgets, so we need to account for that when
2070 * the division assumes pip_inter is used for all widgets. */
2071 this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
2072 this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
2073
2074 /* When resizing, update the scrollbar's count. E.g. with a vertical
2075 * scrollbar becoming wider or narrower means the amount of rows in
2076 * the scrollbar becomes respectively smaller or higher. */
2077 this->SetCount(this->count);
2078}
2079
2081{
2082 /* Falls outside of the matrix widget. */
2083 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
2084
2085 int start_x, start_y, base_offs_x, base_offs_y;
2086 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2087
2088 bool rtl = _current_text_dir == TD_RTL;
2089
2090 int widget_col = (rtl ?
2091 -x + (int)this->pip_post + this->pos_x + base_offs_x + this->widget_w - 1 - (int)this->pip_inter :
2092 x - (int)this->pip_pre - this->pos_x - base_offs_x
2093 ) / this->widget_w;
2094
2095 int widget_row = (y - base_offs_y - (int)this->pip_pre - this->pos_y) / this->widget_h;
2096
2097 this->current_element = (widget_row + start_y) * this->widgets_x + start_x + widget_col;
2098 if (this->current_element >= this->count) return nullptr;
2099
2100 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2101 assert(child != nullptr);
2103 this->pos_x + (rtl ? this->pip_post - widget_col * this->widget_w : this->pip_pre + widget_col * this->widget_w) + base_offs_x,
2104 this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
2105 child->smallest_x, child->smallest_y, rtl);
2106
2107 return child->GetWidgetFromPos(x, y);
2108}
2109
2110/* virtual */ void NWidgetMatrix::Draw(const Window *w)
2111{
2112 /* Fill the background. */
2113 GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, Shade::Light));
2114
2115 /* Set up a clipping area for the previews. */
2116 bool rtl = _current_text_dir == TD_RTL;
2117 DrawPixelInfo tmp_dpi;
2118 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;
2119
2120 {
2121 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
2122
2123 /* Get the appropriate offsets so we can draw the right widgets. */
2124 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2125 assert(child != nullptr);
2126 int start_x, start_y, base_offs_x, base_offs_y;
2127 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2128
2129 int offs_y = base_offs_y;
2130 for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
2131 /* Are we within bounds? */
2132 if (offs_y + child->smallest_y <= 0) continue;
2133 if (offs_y >= (int)this->current_y) break;
2134
2135 /* We've passed our amount of widgets. */
2136 if (y * this->widgets_x >= this->count) break;
2137
2138 int offs_x = base_offs_x;
2139 for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
2140 /* Are we within bounds? */
2141 if (offs_x + child->smallest_x <= 0) continue;
2142 if (offs_x >= (int)this->current_x) continue;
2143
2144 /* Do we have this many widgets? */
2145 this->current_element = y * this->widgets_x + x;
2146 if (this->current_element >= this->count) break;
2147
2148 child->AssignSizePosition(SizingType::Resize, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
2149 child->SetLowered(this->clicked == this->current_element);
2150 child->Draw(w);
2151 }
2152 }
2153 }
2154
2155 DrawOutline(w, this);
2156}
2157
2165void NWidgetMatrix::GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
2166{
2167 base_offs_x = _current_text_dir == TD_RTL ? this->widget_w * (this->widgets_x - 1) : 0;
2168 base_offs_y = 0;
2169 start_x = 0;
2170 start_y = 0;
2171 if (this->sb != nullptr) {
2172 if (this->sb->IsVertical()) {
2173 start_y = this->sb->GetPosition() / this->widget_h;
2174 base_offs_y += -this->sb->GetPosition() + start_y * this->widget_h;
2175 } else {
2176 start_x = this->sb->GetPosition() / this->widget_w;
2177 int sub_x = this->sb->GetPosition() - start_x * this->widget_w;
2178 if (_current_text_dir == TD_RTL) {
2179 base_offs_x += sub_x;
2180 } else {
2181 base_offs_x -= sub_x;
2182 }
2183 }
2184 }
2185}
2186
2196NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr<NWidgetPIPContainer> &&child) : NWidgetCore(tp, colour, index, 1, 1, {}, STR_NULL)
2197{
2198 assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
2199 this->child = std::move(child);
2200 if (this->child != nullptr) this->child->parent = this;
2202}
2203
2211void NWidgetBackground::Add(std::unique_ptr<NWidgetBase> &&nwid)
2212{
2213 if (this->child == nullptr) {
2214 this->child = std::make_unique<NWidgetVertical>();
2215 }
2216 nwid->parent = this->child.get();
2217 this->child->Add(std::move(nwid));
2218}
2219
2230void NWidgetBackground::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
2231{
2232 if (this->child == nullptr) {
2233 this->child = std::make_unique<NWidgetVertical>();
2234 }
2235 this->child->parent = this;
2236 this->child->SetPIP(pip_pre, pip_inter, pip_post);
2237}
2238
2249void NWidgetBackground::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
2250{
2251 if (this->child == nullptr) {
2252 this->child = std::make_unique<NWidgetVertical>();
2253 }
2254 this->child->parent = this;
2255 this->child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2256}
2257
2259{
2260 if (child != nullptr) child->AdjustPaddingForZoom();
2262}
2263
2265{
2266 if (this->child != nullptr) {
2267 this->child->SetupSmallestSize(w);
2268
2269 this->smallest_x = this->child->smallest_x;
2270 this->smallest_y = this->child->smallest_y;
2271 this->fill_x = this->child->fill_x;
2272 this->fill_y = this->child->fill_y;
2273 this->resize_x = this->child->resize_x;
2274 this->resize_y = this->child->resize_y;
2275
2276 /* Don't apply automatic padding if there is no child widget. */
2277 if (w == nullptr) return;
2278
2279 if (this->type == WWT_FRAME) {
2280 std::string text = GetStringForWidget(w, this);
2281 Dimension text_size = text.empty() ? Dimension{0, 0} : GetStringBoundingBox(text, this->text_size);
2282
2283 /* Account for the size of the frame's text if that exists */
2284 this->child->padding = WidgetDimensions::scaled.frametext;
2285 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);
2286
2287 this->smallest_x += this->child->padding.Horizontal();
2288 this->smallest_y += this->child->padding.Vertical();
2289
2290 this->smallest_x = std::max(this->smallest_x, text_size.width + WidgetDimensions::scaled.frametext.Horizontal());
2291 } else if (this->type == WWT_INSET) {
2292 /* Apply automatic padding for bevel thickness. */
2293 this->child->padding = WidgetDimensions::scaled.bevel;
2294
2295 this->smallest_x += this->child->padding.Horizontal();
2296 this->smallest_y += this->child->padding.Vertical();
2297 }
2298 this->ApplyAspectRatio();
2299 } else {
2300 Dimension d = {this->min_x, this->min_y};
2301 Dimension fill = {this->fill_x, this->fill_y};
2302 Dimension resize = {this->resize_x, this->resize_y};
2303 if (w != nullptr) { // A non-nullptr window pointer acts as switch to turn dynamic widget size on.
2304 if (this->type == WWT_FRAME || this->type == WWT_INSET) {
2305 std::string text = GetStringForWidget(w, this);
2306 if (!text.empty()) {
2307 Dimension background = GetStringBoundingBox(text, this->text_size);
2308 background.width += (this->type == WWT_FRAME) ? (WidgetDimensions::scaled.frametext.Horizontal()) : (WidgetDimensions::scaled.inset.Horizontal());
2309 d = maxdim(d, background);
2310 }
2311 }
2312 if (this->index >= 0) {
2314 switch (this->type) {
2315 default: NOT_REACHED();
2316 case WWT_PANEL: padding = {WidgetDimensions::scaled.framerect.Horizontal(), WidgetDimensions::scaled.framerect.Vertical()}; break;
2317 case WWT_FRAME: padding = {WidgetDimensions::scaled.frametext.Horizontal(), WidgetDimensions::scaled.frametext.Vertical()}; break;
2318 case WWT_INSET: padding = {WidgetDimensions::scaled.inset.Horizontal(), WidgetDimensions::scaled.inset.Vertical()}; break;
2319 }
2320 w->UpdateWidgetSize(this->index, d, padding, fill, resize);
2321 }
2322 }
2323 this->smallest_x = d.width;
2324 this->smallest_y = d.height;
2325 this->fill_x = fill.width;
2326 this->fill_y = fill.height;
2327 this->resize_x = resize.width;
2328 this->resize_y = resize.height;
2329 this->ApplyAspectRatio();
2330 }
2331}
2332
2333void NWidgetBackground::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
2334{
2335 this->StoreSizePosition(sizing, x, y, given_width, given_height);
2336
2337 if (this->child != nullptr) {
2338 uint x_offset = (rtl ? this->child->padding.right : this->child->padding.left);
2339 uint width = given_width - this->child->padding.Horizontal();
2340 uint height = given_height - this->child->padding.Vertical();
2341 this->child->AssignSizePosition(sizing, x + x_offset, y + this->child->padding.top, width, height, rtl);
2342 }
2343}
2344
2346{
2347 this->NWidgetCore::FillWidgetLookup(widget_lookup);
2348 if (this->child != nullptr) this->child->FillWidgetLookup(widget_lookup);
2349}
2350
2352{
2353 if (this->current_x == 0 || this->current_y == 0) return;
2354
2355 Rect r = this->GetCurrentRect();
2356
2357 const DrawPixelInfo *dpi = _cur_dpi;
2358 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2359
2360 switch (this->type) {
2361 case WWT_PANEL:
2363 break;
2364
2365 case WWT_FRAME:
2366 DrawFrame(r, this->colour, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
2367 break;
2368
2369 case WWT_INSET:
2370 DrawInset(r, this->colour, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
2371 break;
2372
2373 default:
2374 NOT_REACHED();
2375 }
2376
2377 if (this->index >= 0) w->DrawWidget(r, this->index);
2378 if (this->child != nullptr) this->child->Draw(w);
2379
2380 if (this->IsDisabled()) {
2382 }
2383
2384 DrawOutline(w, this);
2385}
2386
2388{
2389 NWidgetCore *nwid = nullptr;
2390 if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) {
2391 if (this->child != nullptr) nwid = this->child->GetWidgetFromPos(x, y);
2392 if (nwid == nullptr) nwid = this;
2393 }
2394 return nwid;
2395}
2396
2398{
2399 NWidgetBase *nwid = nullptr;
2400 if (this->child != nullptr) nwid = this->child->GetWidgetOfType(tp);
2401 if (nwid == nullptr && this->type == tp) nwid = this;
2402 return nwid;
2403}
2404
2405NWidgetViewport::NWidgetViewport(WidgetID index) : NWidgetCore(NWID_VIEWPORT, Colours::Invalid, index, 1, 1, {}, STR_NULL)
2406{
2407}
2408
2410{
2411 this->smallest_x = this->min_x;
2412 this->smallest_y = this->min_y;
2413 this->ApplyAspectRatio();
2414}
2415
2417{
2418 if (this->current_x == 0 || this->current_y == 0) return;
2419
2422 _transparency_opt &= TransparencyOptions{TransparencyOption::Signs, TransparencyOption::Text}; // Disable all transparency, except textual stuff
2423 w->DrawViewport();
2424 } else {
2425 w->DrawViewport();
2426 }
2427
2428 /* Optionally shade the viewport. */
2429 if (this->disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2431 }
2432
2433 DrawOutline(w, this);
2434}
2435
2442void NWidgetViewport::InitializeViewport(Window *w, std::variant<TileIndex, VehicleID> focus, ZoomLevel zoom)
2443{
2444 InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, focus, zoom);
2445}
2446
2452{
2453 if (w->viewport == nullptr) return;
2454
2455 Viewport &vp = *w->viewport;
2456 vp.left = w->left + this->pos_x;
2457 vp.top = w->top + this->pos_y;
2458 vp.width = this->current_x;
2459 vp.height = this->current_y;
2460
2461 vp.virtual_width = ScaleByZoom(vp.width, vp.zoom);
2463}
2464
2474Scrollbar::size_type Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, WidgetID widget, int padding, int line_height) const
2475{
2476 int pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
2477 if (pos != INT_MAX) pos += this->GetPosition();
2478 return (pos < 0 || pos >= this->GetCount()) ? Scrollbar::npos : pos;
2479}
2480
2495EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
2496{
2497 int new_pos = list_position;
2498 switch (keycode) {
2499 case WKC_UP:
2500 /* scroll up by one */
2501 new_pos--;
2502 break;
2503
2504 case WKC_DOWN:
2505 /* scroll down by one */
2506 new_pos++;
2507 break;
2508
2509 case WKC_PAGEUP:
2510 /* scroll up a page */
2511 new_pos -= this->GetCapacity();
2512 break;
2513
2514 case WKC_PAGEDOWN:
2515 /* scroll down a page */
2516 new_pos += this->GetCapacity();
2517 break;
2518
2519 case WKC_HOME:
2520 /* jump to beginning */
2521 new_pos = 0;
2522 break;
2523
2524 case WKC_END:
2525 /* jump to end */
2526 new_pos = this->GetCount() - 1;
2527 break;
2528
2529 default:
2531 }
2532
2533 /* If there are no elements, there is nothing to scroll/update. */
2534 if (this->GetCount() != 0) {
2535 list_position = Clamp(new_pos, 0, this->GetCount() - 1);
2536 }
2537 return EventState::Handled;
2538}
2539
2540
2549{
2550 NWidgetBase *nwid = w->GetWidget<NWidgetBase>(widget);
2551 if (this->IsVertical()) {
2552 this->SetCapacity(((int)nwid->current_y - padding) / (int)nwid->resize_y);
2553 } else {
2554 this->SetCapacity(((int)nwid->current_x - padding) / (int)nwid->resize_x);
2555 }
2556}
2557
2565Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
2566{
2567 const int count = sb.GetCount() * resize_step;
2568 const int position = sb.GetPosition() * resize_step;
2569
2570 if (sb.IsVertical()) {
2571 r.top -= position;
2572 r.bottom = r.top + count;
2573 } else {
2574 bool rtl = _current_text_dir == TD_RTL;
2575 if (rtl) {
2576 r.right += position;
2577 r.left = r.right - count;
2578 } else {
2579 r.left -= position;
2580 r.right = r.left + count;
2581 }
2582 }
2583
2584 return r;
2585}
2586
2594{
2595 assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR);
2596
2597 switch (this->type) {
2598 case NWID_HSCROLLBAR:
2599 this->SetResize(1, 0);
2600 this->SetFill(1, 0);
2601 this->SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2602 break;
2603
2604 case NWID_VSCROLLBAR:
2605 this->SetResize(0, 1);
2606 this->SetFill(0, 1);
2607 this->SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2608 break;
2609
2610 default: NOT_REACHED();
2611 }
2612}
2613
2615{
2616 this->min_x = 0;
2617 this->min_y = 0;
2618
2619 switch (this->type) {
2620 case NWID_HSCROLLBAR:
2621 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2622 break;
2623
2624 case NWID_VSCROLLBAR:
2625 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2626 break;
2627
2628 default: NOT_REACHED();
2629 }
2630
2631 this->smallest_x = this->min_x;
2632 this->smallest_y = this->min_y;
2633}
2634
2636{
2637 if (this->current_x == 0 || this->current_y == 0) return;
2638
2639 Rect r = this->GetCurrentRect();
2640
2641 const DrawPixelInfo *dpi = _cur_dpi;
2642 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2643
2644 bool up_lowered = this->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp);
2645 bool down_lowered = this->disp_flags.Test(NWidgetDisplayFlag::ScrollbarDown);
2646 bool middle_lowered = !this->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown}) && w->mouse_capture_widget == this->index;
2647
2648 if (this->type == NWID_HSCROLLBAR) {
2649 DrawHorizontalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2650 } else {
2651 DrawVerticalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2652 }
2653
2654 if (this->IsDisabled()) {
2656 }
2657
2658 DrawOutline(w, this);
2659}
2660
2661/* static */ void NWidgetScrollbar::InvalidateDimensionCache()
2662{
2663 vertical_dimension.width = vertical_dimension.height = 0;
2664 horizontal_dimension.width = horizontal_dimension.height = 0;
2665}
2666
2667/* static */ Dimension NWidgetScrollbar::GetVerticalDimension()
2668{
2669 if (vertical_dimension.width == 0) {
2673 }
2674 return vertical_dimension;
2675}
2676
2677/* static */ Dimension NWidgetScrollbar::GetHorizontalDimension()
2678{
2679 if (horizontal_dimension.width == 0) {
2683 }
2684 return horizontal_dimension;
2685}
2686
2689
2692{
2693 shadebox_dimension.width = shadebox_dimension.height = 0;
2694 debugbox_dimension.width = debugbox_dimension.height = 0;
2695 defsizebox_dimension.width = defsizebox_dimension.height = 0;
2696 stickybox_dimension.width = stickybox_dimension.height = 0;
2697 resizebox_dimension.width = resizebox_dimension.height = 0;
2698 closebox_dimension.width = closebox_dimension.height = 0;
2699 dropdown_dimension.width = dropdown_dimension.height = 0;
2700}
2701
2709
2719{
2720 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);
2721 this->min_x = 0;
2722 this->min_y = 0;
2723 this->SetResize(0, 0);
2724
2725 switch (tp) {
2726 case WWT_EMPTY:
2727 if (colour != Colours::Invalid) [[unlikely]] throw std::runtime_error("WWT_EMPTY should not have a colour");
2728 break;
2729
2730 case WWT_TEXT:
2731 if (colour != Colours::Invalid) [[unlikely]] throw std::runtime_error("WWT_TEXT should not have a colour");
2732 this->SetFill(0, 0);
2734 break;
2735
2736 case WWT_LABEL:
2737 if (colour != Colours::Invalid) [[unlikely]] throw std::runtime_error("WWT_LABEL should not have a colour");
2738 [[fallthrough]];
2739
2740 case WWT_PUSHBTN:
2741 case WWT_IMGBTN:
2742 case WWT_PUSHIMGBTN:
2743 case WWT_IMGBTN_2:
2744 case WWT_TEXTBTN:
2745 case WWT_PUSHTXTBTN:
2746 case WWT_TEXTBTN_2:
2747 case WWT_IMGTEXTBTN:
2748 case WWT_PUSHIMGTEXTBTN:
2749 case WWT_BOOLBTN:
2750 case WWT_MATRIX:
2752 case NWID_PUSHBUTTON_DROPDOWN:
2753 this->SetFill(0, 0);
2754 break;
2755
2756 case WWT_ARROWBTN:
2757 case WWT_PUSHARROWBTN:
2758 this->SetFill(0, 0);
2759 this->SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2760 break;
2761
2762 case WWT_EDITBOX:
2763 this->SetFill(0, 0);
2764 break;
2765
2766 case WWT_CAPTION:
2767 this->SetFill(1, 0);
2768 this->SetResize(1, 0);
2770 this->SetMinimalTextLines(1, WidgetDimensions::unscaled.captiontext.Vertical(), FontSize::Normal);
2771 this->SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2772 break;
2773
2774 case WWT_STICKYBOX:
2775 this->SetFill(0, 0);
2777 this->SetToolTip(STR_TOOLTIP_STICKY);
2778 this->SetAspect(this->uz_min_x, this->uz_min_y);
2779 break;
2780
2781 case WWT_SHADEBOX:
2782 this->SetFill(0, 0);
2784 this->SetToolTip(STR_TOOLTIP_SHADE);
2785 this->SetAspect(this->uz_min_x, this->uz_min_y);
2786 break;
2787
2788 case WWT_DEBUGBOX:
2789 this->SetFill(0, 0);
2791 this->SetToolTip(STR_TOOLTIP_DEBUG);
2792 this->SetAspect(this->uz_min_x, this->uz_min_y);
2793 break;
2794
2795 case WWT_DEFSIZEBOX:
2796 this->SetFill(0, 0);
2798 this->SetToolTip(STR_TOOLTIP_DEFSIZE);
2799 this->SetAspect(this->uz_min_x, this->uz_min_y);
2800 break;
2801
2802 case WWT_RESIZEBOX:
2803 this->SetFill(0, 0);
2806 this->SetToolTip(STR_TOOLTIP_RESIZE);
2807 break;
2808
2809 case WWT_CLOSEBOX:
2810 this->SetFill(0, 0);
2812 this->SetToolTip(STR_TOOLTIP_CLOSE_WINDOW);
2813 this->SetAspect(this->uz_min_x, this->uz_min_y);
2814 break;
2815
2816 case WWT_DROPDOWN:
2817 this->SetFill(0, 0);
2820 break;
2821
2822 default:
2823 NOT_REACHED();
2824 }
2825}
2826
2828{
2829 Dimension padding = {0, 0};
2830 Dimension size = {this->min_x, this->min_y};
2831 Dimension fill = {this->fill_x, this->fill_y};
2832 Dimension resize = {this->resize_x, this->resize_y};
2833 switch (this->type) {
2834 case WWT_EMPTY: {
2835 break;
2836 }
2837 case WWT_MATRIX: {
2838 padding = {WidgetDimensions::scaled.matrix.Horizontal(), WidgetDimensions::scaled.matrix.Vertical()};
2839 break;
2840 }
2841 case WWT_SHADEBOX: {
2842 padding = {WidgetDimensions::scaled.shadebox.Horizontal(), WidgetDimensions::scaled.shadebox.Vertical()};
2843 if (NWidgetLeaf::shadebox_dimension.width == 0) {
2847 }
2849 break;
2850 }
2851 case WWT_DEBUGBOX:
2852 if (_settings_client.gui.newgrf_developer_tools && w->IsNewGRFInspectable()) {
2853 padding = {WidgetDimensions::scaled.debugbox.Horizontal(), WidgetDimensions::scaled.debugbox.Vertical()};
2854 if (NWidgetLeaf::debugbox_dimension.width == 0) {
2858 }
2860 } else {
2861 /* If the setting is disabled we don't want to see it! */
2862 size.width = 0;
2863 fill.width = 0;
2864 resize.width = 0;
2865 }
2866 break;
2867
2868 case WWT_STICKYBOX: {
2869 padding = {WidgetDimensions::scaled.stickybox.Horizontal(), WidgetDimensions::scaled.stickybox.Vertical()};
2870 if (NWidgetLeaf::stickybox_dimension.width == 0) {
2874 }
2876 break;
2877 }
2878
2879 case WWT_DEFSIZEBOX: {
2880 padding = {WidgetDimensions::scaled.defsizebox.Horizontal(), WidgetDimensions::scaled.defsizebox.Vertical()};
2881 if (NWidgetLeaf::defsizebox_dimension.width == 0) {
2885 }
2887 break;
2888 }
2889
2890 case WWT_RESIZEBOX: {
2891 padding = {WidgetDimensions::scaled.resizebox.Horizontal(), WidgetDimensions::scaled.resizebox.Vertical()};
2892 if (NWidgetLeaf::resizebox_dimension.width == 0) {
2896 }
2898 break;
2899 }
2900 case WWT_EDITBOX: {
2901 Dimension sprite_size = GetScaledSpriteSize(_current_text_dir == TD_RTL ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
2902 size.width = std::max(size.width, ScaleGUITrad(30) + sprite_size.width);
2903 size.height = std::max(sprite_size.height, GetStringBoundingBox("_").height + WidgetDimensions::scaled.framerect.Vertical());
2904 }
2905 [[fallthrough]];
2906 case WWT_PUSHBTN: {
2907 padding = {WidgetDimensions::scaled.frametext.Horizontal(), WidgetDimensions::scaled.framerect.Vertical()};
2908 break;
2909 }
2910
2911 case WWT_BOOLBTN:
2912 size.width = SETTING_BUTTON_WIDTH;
2913 size.height = SETTING_BUTTON_HEIGHT;
2914 break;
2915
2916 case WWT_IMGBTN:
2917 case WWT_IMGBTN_2:
2918 case WWT_PUSHIMGBTN: {
2919 padding = {WidgetDimensions::scaled.imgbtn.Horizontal(), WidgetDimensions::scaled.imgbtn.Vertical()};
2920 Dimension d2 = GetScaledSpriteSize(this->widget_data.sprite);
2921 if (this->type == WWT_IMGBTN_2) d2 = maxdim(d2, GetScaledSpriteSize(this->widget_data.sprite + 1));
2922 d2.width += padding.width;
2923 d2.height += padding.height;
2924 size = maxdim(size, d2);
2925 break;
2926 }
2927
2928 case WWT_IMGTEXTBTN:
2929 case WWT_PUSHIMGTEXTBTN: {
2930 padding = {WidgetDimensions::scaled.framerect.Horizontal(), WidgetDimensions::scaled.framerect.Vertical()};
2932 Dimension dt = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2933 Dimension d2{
2934 padding.width + 2 * (di.width + WidgetDimensions::scaled.hsep_wide) + dt.width,
2935 padding.height + std::max(di.height, dt.height)
2936 };
2937 size = maxdim(size, d2);
2938 break;
2939 }
2940
2941 case WWT_ARROWBTN:
2942 case WWT_PUSHARROWBTN: {
2943 padding = {WidgetDimensions::scaled.imgbtn.Horizontal(), WidgetDimensions::scaled.imgbtn.Vertical()};
2945 d2.width += padding.width;
2946 d2.height += padding.height;
2947 size = maxdim(size, d2);
2948 break;
2949 }
2950
2951 case WWT_CLOSEBOX: {
2952 padding = {WidgetDimensions::scaled.closebox.Horizontal(), WidgetDimensions::scaled.closebox.Vertical()};
2953 if (NWidgetLeaf::closebox_dimension.width == 0) {
2957 }
2959 break;
2960 }
2961 case WWT_TEXTBTN:
2962 case WWT_PUSHTXTBTN:
2963 case WWT_TEXTBTN_2: {
2964 padding = {WidgetDimensions::scaled.framerect.Horizontal(), WidgetDimensions::scaled.framerect.Vertical()};
2965 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2966 d2.width += padding.width;
2967 d2.height += padding.height;
2968 size = maxdim(size, d2);
2969 break;
2970 }
2971 case WWT_LABEL:
2972 case WWT_TEXT: {
2973 size = maxdim(size, GetStringBoundingBox(GetStringForWidget(w, this), this->text_size));
2974 break;
2975 }
2976 case WWT_CAPTION: {
2977 padding = {WidgetDimensions::scaled.captiontext.Horizontal(), WidgetDimensions::scaled.captiontext.Vertical()};
2978 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2979 d2.width += padding.width;
2980 d2.height += padding.height;
2981 size = maxdim(size, d2);
2982 break;
2983 }
2984 case WWT_DROPDOWN:
2986 case NWID_PUSHBUTTON_DROPDOWN: {
2987 if (NWidgetLeaf::dropdown_dimension.width == 0) {
2989 NWidgetLeaf::dropdown_dimension.width += WidgetDimensions::scaled.vscrollbar.Horizontal();
2990 NWidgetLeaf::dropdown_dimension.height += WidgetDimensions::scaled.vscrollbar.Vertical();
2991 }
2992 padding = {WidgetDimensions::scaled.dropdowntext.Horizontal() + NWidgetLeaf::dropdown_dimension.width + WidgetDimensions::scaled.fullbevel.Horizontal(), WidgetDimensions::scaled.dropdowntext.Vertical()};
2993 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2994 d2.width += padding.width;
2995 d2.height = std::max(d2.height + padding.height, NWidgetLeaf::dropdown_dimension.height);
2996 size = maxdim(size, d2);
2997 break;
2998 }
2999 default:
3000 NOT_REACHED();
3001 }
3002
3003 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
3004
3005 this->smallest_x = size.width;
3006 this->smallest_y = size.height;
3007 this->fill_x = fill.width;
3008 this->fill_y = fill.height;
3009 this->resize_x = resize.width;
3010 this->resize_y = resize.height;
3011 this->ApplyAspectRatio();
3012}
3013
3015{
3016 if (this->current_x == 0 || this->current_y == 0) return;
3017
3018 /* Setup a clipping rectangle... for WWT_EMPTY or WWT_TEXT, an extra scaled pixel is allowed in case text shadow encroaches. */
3019 int extra = (this->type == WWT_EMPTY || this->type == WWT_TEXT) ? ScaleGUITrad(1) : 0;
3020 DrawPixelInfo new_dpi;
3021 if (!FillDrawPixelInfo(&new_dpi, this->pos_x, this->pos_y, this->current_x + extra, this->current_y + extra)) return;
3022 /* ...but keep coordinates relative to the window. */
3023 new_dpi.left += this->pos_x;
3024 new_dpi.top += this->pos_y;
3025
3026 AutoRestoreBackup dpi_backup(_cur_dpi, &new_dpi);
3027
3028 Rect r = this->GetCurrentRect();
3029
3030 bool clicked = this->IsLowered();
3031 switch (this->type) {
3032 case WWT_EMPTY:
3033 /* WWT_EMPTY used as a spacer indicates a potential design issue. */
3034 if (this->index == -1 && _draw_widget_outlines) {
3036 }
3037 break;
3038
3039 case WWT_PUSHBTN:
3040 DrawFrameRect(r, this->colour, clicked ? FrameFlag::Lowered : FrameFlags{});
3041 break;
3042
3043 case WWT_BOOLBTN: {
3045 Colours button_colour = this->widget_data.alternate_colour;
3046 if (button_colour == Colours::Invalid) button_colour = this->colour;
3047 DrawBoolButton(pt.x, pt.y, button_colour, this->colour, clicked, !this->IsDisabled());
3048 break;
3049 }
3050
3051 case WWT_IMGBTN:
3052 case WWT_PUSHIMGBTN:
3053 case WWT_IMGBTN_2:
3054 DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data.sprite, this->align);
3055 break;
3056
3057 case WWT_TEXTBTN:
3058 case WWT_PUSHTXTBTN:
3059 case WWT_TEXTBTN_2:
3060 DrawFrameRect(r, this->colour, clicked ? FrameFlag::Lowered : FrameFlags{});
3061 DrawLabel(r, this->text_colour, GetStringForWidget(w, this, (type & WWT_MASK) == WWT_TEXTBTN_2 && clicked), this->align, this->text_size);
3062 break;
3063
3064 case WWT_IMGTEXTBTN:
3065 case WWT_PUSHIMGTEXTBTN:
3066 DrawImageTextButtons(r, this->colour, clicked, this->widget_data.sprite, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3067 break;
3068
3069 case WWT_ARROWBTN:
3070 case WWT_PUSHARROWBTN: {
3071 SpriteID sprite;
3072 switch (this->widget_data.arrow_widget_type) {
3075 case ArrowWidgetType::Left: sprite = SPR_ARROW_LEFT; break;
3076 case ArrowWidgetType::Right: sprite = SPR_ARROW_RIGHT; break;
3077 default: NOT_REACHED();
3078 }
3079 DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align);
3080 break;
3081 }
3082
3083 case WWT_LABEL:
3084 DrawLabel(r, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3085 break;
3086
3087 case WWT_TEXT:
3088 DrawText(r, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3089 break;
3090
3091 case WWT_MATRIX:
3092 DrawMatrix(r, this->colour, clicked, this->widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3093 break;
3094
3095 case WWT_EDITBOX: {
3096 const QueryString *query = w->GetQueryString(this->index);
3097 if (query != nullptr) query->DrawEditBox(w, this->index);
3098 break;
3099 }
3100
3101 case WWT_CAPTION:
3102 DrawCaption(r, this->colour, w->owner, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3103 break;
3104
3105 case WWT_SHADEBOX:
3106 DrawShadeBox(r, this->colour, w->IsShaded());
3107 break;
3108
3109 case WWT_DEBUGBOX:
3110 DrawDebugBox(r, this->colour, clicked);
3111 break;
3112
3113 case WWT_STICKYBOX:
3115 break;
3116
3117 case WWT_DEFSIZEBOX:
3118 DrawDefSizeBox(r, this->colour, clicked);
3119 break;
3120
3121 case WWT_RESIZEBOX:
3122 DrawResizeBox(r, this->colour, this->pos_x < (w->width / 2), w->flags.Test(WindowFlag::SizingLeft) || w->flags.Test(WindowFlag::SizingRight), this->widget_data.resize_widget_type == ResizeWidgetType::ShowBevel);
3123 break;
3124
3125 case WWT_CLOSEBOX:
3126 DrawCloseBox(r, this->colour);
3127 break;
3128
3129 case WWT_DROPDOWN:
3130 DrawButtonDropdown(r, this->colour, false, clicked, GetStringForWidget(w, this), this->align);
3131 break;
3132
3134 case NWID_PUSHBUTTON_DROPDOWN:
3135 DrawButtonDropdown(r, this->colour, clicked, this->disp_flags.Test(NWidgetDisplayFlag::DropdownActive), GetStringForWidget(w, this), this->align);
3136 break;
3137
3138 default:
3139 NOT_REACHED();
3140 }
3141 if (this->index >= 0) w->DrawWidget(r, this->index);
3142
3143 if (this->IsDisabled() && this->type != WWT_BOOLBTN) {
3144 /* WWT_BOOLBTN is excluded as it draws its own disabled state. */
3146 }
3147
3148 DrawOutline(w, this);
3149}
3150
3159{
3160 if (_current_text_dir == TD_LTR) {
3161 int button_width = this->pos_x + this->current_x - NWidgetLeaf::dropdown_dimension.width;
3162 return pt.x < button_width;
3163 } else {
3164 int button_left = this->pos_x + NWidgetLeaf::dropdown_dimension.width;
3165 return pt.x >= button_left;
3166 }
3167}
3168
3169/* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
3170
3177{
3178 return tp > WPT_ATTRIBUTE_BEGIN && tp < WPT_ATTRIBUTE_END;
3179}
3180
3188{
3189 switch (nwid.type) {
3190 case WPT_RESIZE: {
3191 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3192 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_RESIZE requires NWidgetResizeBase");
3193 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3194 nwrb->SetResize(nwid.u.xy.x, nwid.u.xy.y);
3195 break;
3196 }
3197
3198 case WPT_MINSIZE: {
3199 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3200 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINSIZE requires NWidgetResizeBase");
3201 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3202 nwrb->SetMinimalSize(nwid.u.xy.x, nwid.u.xy.y);
3203 break;
3204 }
3205
3206 case WPT_MINTEXTLINES: {
3207 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3208 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINTEXTLINES requires NWidgetResizeBase");
3209 assert(nwid.u.text_lines.size >= FontSize::Begin && nwid.u.text_lines.size < FontSize::End);
3211 break;
3212 }
3213
3214 case WPT_TOOLBARSIZE: {
3215 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3216 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_TOOLBARSIZE requires NWidgetResizeBase");
3217 assert(nwid.u.xy.x >= 0);
3218 nwrb->SetToolbarMinimalSize(nwid.u.xy.x);
3219 break;
3220 }
3221
3222 case WPT_TEXTSTYLE: {
3223 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3224 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_TEXTSTYLE requires NWidgetCore");
3225 nwc->SetTextStyle(nwid.u.text_style.colour, nwid.u.text_style.size);
3226 break;
3227 }
3228
3229 case WPT_ALIGNMENT: {
3230 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3231 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_ALIGNMENT requires NWidgetCore");
3232 nwc->SetAlignment(nwid.u.align.align);
3233 break;
3234 }
3235
3236 case WPT_FILL: {
3237 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3238 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_FILL requires NWidgetResizeBase");
3239 nwrb->SetFill(nwid.u.xy.x, nwid.u.xy.y);
3240 break;
3241 }
3242
3243 case WPT_DATATIP: {
3244 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3245 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_DATATIP requires NWidgetCore");
3246 nwc->widget_data = nwid.u.data_tip.data;
3247 nwc->SetToolTip(nwid.u.data_tip.tooltip);
3248 break;
3249 }
3250
3251 case WPT_PADDING:
3252 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_PADDING requires NWidgetBase");
3253 dest->SetPadding(nwid.u.padding);
3254 break;
3255
3256 case WPT_PIPSPACE: {
3257 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3258 if (nwc != nullptr) nwc->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3259
3260 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3261 if (nwb != nullptr) nwb->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3262
3263 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3264 break;
3265 }
3266
3267 case WPT_PIPRATIO: {
3268 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3269 if (nwc != nullptr) nwc->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3270
3271 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3272 if (nwb != nullptr) nwb->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3273
3274 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3275 break;
3276 }
3277
3278 case WPT_SCROLLBAR: {
3279 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3280 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore");
3281 nwc->scrollbar_index = nwid.u.widget.index;
3282 break;
3283 }
3284
3285 case WPT_ASPECT: {
3286 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_ASPECT requires NWidgetBase");
3287 dest->aspect_ratio = nwid.u.aspect.ratio;
3288 dest->aspect_flags = nwid.u.aspect.flags;
3289 break;
3290 }
3291
3292 default:
3293 NOT_REACHED();
3294 }
3295}
3296
3303static std::unique_ptr<NWidgetBase> MakeNWidget(const NWidgetPart &nwid)
3304{
3305 assert(!IsAttributeWidgetPartType(nwid.type));
3306 assert(nwid.type != WPT_ENDCONTAINER);
3307
3308 switch (nwid.type) {
3309 case NWID_SPACER: return std::make_unique<NWidgetSpacer>(0, 0);
3310
3311 case WWT_PANEL: [[fallthrough]];
3312 case WWT_INSET: [[fallthrough]];
3313 case WWT_FRAME: return std::make_unique<NWidgetBackground>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3314
3315 case NWID_HORIZONTAL: return std::make_unique<NWidgetHorizontal>(nwid.u.container.flags, nwid.u.container.index);
3316 case NWID_HORIZONTAL_LTR: return std::make_unique<NWidgetHorizontalLTR>(nwid.u.container.flags, nwid.u.container.index);
3317 case NWID_VERTICAL: return std::make_unique<NWidgetVertical>(nwid.u.container.flags, nwid.u.container.index);
3318 case NWID_SELECTION: return std::make_unique<NWidgetStacked>(nwid.u.widget.index);
3319 case NWID_MATRIX: return std::make_unique<NWidgetMatrix>(nwid.u.widget.colour, nwid.u.widget.index);
3320 case NWID_VIEWPORT: return std::make_unique<NWidgetViewport>(nwid.u.widget.index);
3321 case NWID_LAYER: return std::make_unique<NWidgetLayer>(nwid.u.widget.index);
3322
3323 case NWID_HSCROLLBAR: [[fallthrough]];
3324 case NWID_VSCROLLBAR: return std::make_unique<NWidgetScrollbar>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3325
3326 case WPT_FUNCTION: return nwid.u.func_ptr();
3327
3328 default:
3329 assert((nwid.type & WWT_MASK) < WWT_LAST || (nwid.type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
3330 return std::make_unique<NWidgetLeaf>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index, WidgetData{}, STR_NULL);
3331 }
3332}
3333
3347static 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)
3348{
3349 dest = nullptr;
3350
3351 if (IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]] throw std::runtime_error("Expected non-attribute NWidgetPart type");
3352 if (nwid_begin->type == WPT_ENDCONTAINER) return nwid_begin;
3353
3354 fill_dest = IsContainerWidgetType(nwid_begin->type);
3355 dest = MakeNWidget(*nwid_begin);
3356 if (dest == nullptr) return nwid_begin;
3357
3358 ++nwid_begin;
3359
3360 /* Once a widget is created, we're now looking for attributes. */
3361 while (nwid_begin != nwid_end && IsAttributeWidgetPartType(nwid_begin->type)) {
3362 ApplyNWidgetPartAttribute(*nwid_begin, dest.get());
3363 ++nwid_begin;
3364 }
3365
3366 return nwid_begin;
3367}
3368
3375{
3376 return tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
3377 || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION || tp == NWID_LAYER;
3378}
3379
3387static 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)
3388{
3389 /* If *parent == nullptr, only the first widget is read and returned. Otherwise, *parent must point to either
3390 * a #NWidgetContainer or a #NWidgetBackground object, and parts are added as much as possible. */
3391 NWidgetContainer *nwid_cont = dynamic_cast<NWidgetContainer *>(parent.get());
3392 NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(parent.get());
3393 assert(parent == nullptr || (nwid_cont != nullptr && nwid_parent == nullptr) || (nwid_cont == nullptr && nwid_parent != nullptr));
3394
3395 while (nwid_begin != nwid_end) {
3396 std::unique_ptr<NWidgetBase> sub_widget = nullptr;
3397 bool fill_sub = false;
3398 nwid_begin = MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3399
3400 /* Break out of loop when end reached */
3401 if (sub_widget == nullptr) break;
3402
3403 /* If sub-widget is a container, recursively fill that container. */
3404 if (fill_sub && IsContainerWidgetType(sub_widget->type)) {
3405 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, sub_widget);
3406 }
3407
3408 /* Add sub_widget to parent container if available, otherwise return the widget to the caller. */
3409 if (nwid_cont != nullptr) nwid_cont->Add(std::move(sub_widget));
3410 if (nwid_parent != nullptr) nwid_parent->Add(std::move(sub_widget));
3411 if (nwid_cont == nullptr && nwid_parent == nullptr) {
3412 parent = std::move(sub_widget);
3413 return nwid_begin;
3414 }
3415 }
3416
3417 if (nwid_begin == nwid_end) return nwid_begin; // Reached the end of the array of parts?
3418
3419 assert(nwid_begin < nwid_end);
3420 assert(nwid_begin->type == WPT_ENDCONTAINER);
3421 return std::next(nwid_begin); // *nwid_begin is also 'used'
3422}
3423
3431std::unique_ptr<NWidgetBase> MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3432{
3433 if (container == nullptr) container = std::make_unique<NWidgetVertical>();
3434 [[maybe_unused]] auto nwid_part = MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3435#ifdef WITH_ASSERT
3436 if (nwid_part != std::end(nwid_parts)) [[unlikely]] throw std::runtime_error("Did not consume all NWidgetParts");
3437#endif
3438 return std::move(container);
3439}
3440
3450std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(std::span<const NWidgetPart> nwid_parts, NWidgetStacked **shade_select)
3451{
3452 auto nwid_begin = std::begin(nwid_parts);
3453 auto nwid_end = std::end(nwid_parts);
3454
3455 *shade_select = nullptr;
3456
3457 /* Read the first widget recursively from the array. */
3458 std::unique_ptr<NWidgetBase> nwid = nullptr;
3459 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, nwid);
3460 assert(nwid != nullptr);
3461
3462 NWidgetHorizontal *hor_cont = dynamic_cast<NWidgetHorizontal *>(nwid.get());
3463
3464 auto root = std::make_unique<NWidgetVertical>();
3465 root->Add(std::move(nwid));
3466 if (nwid_begin == nwid_end) return root; // There is no body at all.
3467
3468 if (hor_cont != nullptr && hor_cont->GetWidgetOfType(WWT_CAPTION) != nullptr && hor_cont->GetWidgetOfType(WWT_SHADEBOX) != nullptr) {
3469 /* If the first widget has a title bar and a shade box, silently add a shade selection widget in the tree. */
3470 auto shade_stack = std::make_unique<NWidgetStacked>(INVALID_WIDGET);
3471 *shade_select = shade_stack.get();
3472 /* Load the remaining parts into the shade stack. */
3473 shade_stack->Add(MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3474 root->Add(std::move(shade_stack));
3475 return root;
3476 }
3477
3478 /* Load the remaining parts into 'root'. */
3479 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3480}
3481
3492std::unique_ptr<NWidgetBase> MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
3493{
3494 assert(max_length >= 1);
3495 std::unique_ptr<NWidgetVertical> vert = nullptr; // Storage for all rows.
3496 std::unique_ptr<NWidgetHorizontal> hor = nullptr; // Storage for buttons in one row.
3497 int hor_length = 0;
3498
3500 sprite_size.width += WidgetDimensions::unscaled.matrix.Horizontal();
3501 sprite_size.height += WidgetDimensions::unscaled.matrix.Vertical();
3502
3503 for (WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3504 /* Ensure there is room in 'hor' for another button. */
3505 if (hor_length == max_length) {
3506 if (vert == nullptr) vert = std::make_unique<NWidgetVertical>();
3507 vert->Add(std::move(hor));
3508 hor = nullptr;
3509 hor_length = 0;
3510 }
3511 if (hor == nullptr) {
3512 hor = std::make_unique<NWidgetHorizontal>();
3513 hor_length = 0;
3514 }
3515
3516 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, button_colour, widnum);
3517 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3518 panel->SetFill(1, 1);
3519 if (resizable) panel->SetResize(1, 0);
3520 panel->SetToolTip(button_tooltip);
3521 hor->Add(std::move(panel));
3522 hor_length++;
3523 }
3524 if (vert == nullptr) return hor; // All buttons fit in a single row.
3525
3526 if (hor_length > 0 && hor_length < max_length) {
3527 /* Last row is partial, add a spacer at the end to force all buttons to the left. */
3528 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3529 spc->SetFill(1, 1);
3530 if (resizable) spc->SetResize(1, 0);
3531 hor->Add(std::move(spc));
3532 }
3533 if (hor != nullptr) vert->Add(std::move(hor));
3534 return vert;
3535}
3536
3543{
3544 assert(parent_window != nullptr);
3545 if (parent_window->nested_focus != nullptr) {
3546 for (auto &widget : this->children) {
3547 if (parent_window->nested_focus == widget.get()) {
3548 parent_window->UnfocusFocusedWidget();
3549 }
3550 }
3551 }
3552}
Class for backupping variables and making sure they are restored later.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
Nested widget with a child.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2387
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:2397
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:2333
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2264
void AdjustPaddingForZoom() override
Adjust the padding based on the user interface zoom.
Definition widget.cpp:2258
NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr< NWidgetPIPContainer > &&child=nullptr)
Constructor parent nested widgets.
Definition widget.cpp:2196
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:2230
std::unique_ptr< NWidgetPIPContainer > child
Child widget.
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2351
void Add(std::unique_ptr< NWidgetBase > &&nwid)
Add a child to the parent.
Definition widget.cpp:2211
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:2345
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:2249
Baseclass for nested widgets.
void StoreSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height)
Store size and position.
virtual TextColour GetHighlightColour() const
Get the colour of the highlighted text.
float aspect_ratio
Desired aspect ratio of widget.
virtual bool IsHighlighted() const
Whether the widget is currently highlighted or not.
virtual void AdjustPaddingForZoom()
Adjust the padding based on the user interface zoom.
Definition widget.cpp:994
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:955
WidgetType type
Type of the widget / nested widget.
uint resize_x
Horizontal resize step (0 means not resizable).
uint fill_x
Horizontal fill stepsize (from initial size, 0 means not resizable).
NWidgetBase * parent
Parent widget of this widget, automatically filled in when added to container.
RectPadding uz_padding
Unscaled padding, for resize calculation.
uint smallest_x
Smallest horizontal size of the widget in a filled window.
AspectFlags aspect_flags
Which dimensions can be resized.
uint current_x
Current horizontal size (after resizing).
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
virtual void Draw(const Window *w)=0
Draw the widgets of the tree.
void SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Set additional space (padding) around the widget.
uint smallest_y
Smallest vertical size of the widget in a filled window.
const WidgetID index
Index of the nested widget (INVALID_WIDGET means 'not used').
virtual NWidgetBase * GetWidgetOfType(WidgetType tp)
Retrieve a widget by its type.
Definition widget.cpp:975
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:939
Baseclass for container widgets.
void Add(std::unique_ptr< NWidgetBase > &&wid)
Append widget wid to container.
Definition widget.cpp:1328
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1335
void UnfocusWidgets(Window *parent_window)
Unfocuses the focused widget of the window, if the focused widget is contained inside the container.
Definition widget.cpp:3542
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1343
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:1352
void AdjustPaddingForZoom() override
Adjust the padding based on the user interface zoom.
Definition widget.cpp:1316
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:1306
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:1260
bool IsDisabled() const
Return whether the widget is disabled.
void SetSprite(SpriteID sprite)
Set sprite of the nested widget.
Definition widget.cpp:1210
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:1250
void SetAlignment(Alignment align)
Set the text/image alignment of the nested widget.
Definition widget.cpp:1278
WidgetID GetScrollbarIndex() const
Get the WidgetID of this nested widget's scrollbar.
Definition widget.cpp:1296
void SetResizeWidgetType(ResizeWidgetType type)
Set the resize widget type of the nested widget.
Definition widget.cpp:1240
Alignment align
Alignment of text/image within widget.
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:1178
void SetSpriteTip(SpriteID sprite, StringID tool_tip)
Set sprite and tool tip of the nested widget.
Definition widget.cpp:1220
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:1287
WidgetID scrollbar_index
Index of an attached scrollbar.
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1269
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1301
void SetString(StringID string)
Set string of the nested widget.
Definition widget.cpp:1190
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:1231
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:1200
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:1755
Horizontal container.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1571
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:1640
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:1499
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1477
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1517
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:3014
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition widget.cpp:2691
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:2718
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:2827
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:3158
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2110
int count
Amount of valid elements.
void SetClicked(int clicked)
Sets the clicked element in the matrix.
Definition widget.cpp:1979
int GetCurrentElement() const
Get current element.
Definition widget.cpp:2029
Scrollbar * sb
The scrollbar we're associated with.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2034
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:2020
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:2165
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:2056
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2080
int clicked
The currently clicked element.
void SetCount(int count)
Set the number of elements in this matrix.
Definition widget.cpp:1996
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:1564
uint8_t uz_pip_inter
Unscaled space between widgets.
uint8_t pip_ratio_pre
Ratio of remaining space before first widget.
void AdjustPaddingForZoom() override
Adjust the padding based on the user interface zoom.
Definition widget.cpp:1527
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:1544
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:1053
bool UpdateSize(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1142
void AdjustPaddingForZoom() override
Adjust the padding based on the user interface zoom.
Definition widget.cpp:1035
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:1006
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:1098
void SetToolbarMinimalSize(uint8_t toolbar_size)
Set minimal size of the widget in toolbar-icon-relative width.
Definition widget.cpp:1063
void SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
Set minimal text lines for the widget.
Definition widget.cpp:1086
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:1122
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:1163
bool UpdateVerticalSize(uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1156
void SetResize(uint resize_x, uint resize_y)
Set resize step of the widget.
Definition widget.cpp:1109
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:1017
void SetMinimalSizeAbsolute(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1073
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:2635
NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index)
Scrollbar widget.
Definition widget.cpp:2593
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2614
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:1940
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1970
void SetDirty(const Window *w) const override
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:1965
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1953
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1946
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:1363
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:1404
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1434
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition widget.cpp:1458
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1424
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1443
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:1829
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1760
bool bottom_up
Set to flow the widget from bottom-to-top instead of top-to-bottom.
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition widget.cpp:2451
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2416
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2409
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition widget.cpp:2442
Scrollbar data structure.
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
bool IsVertical() const
Is the scrollbar vertical or not?
bool UpdatePosition(int difference, Scrollbar::Stepping unit=Stepping::Small)
Updates the position of the first visible element by the given amount.
@ Big
Step in cap units.
void SetCapacity(size_t capacity)
Set the capacity of visible elements.
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition widget.cpp:2474
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:2548
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:2495
size_type pos
Index of first visible item of the list.
size_type GetPosition() const
Gets the position of the first visible element in the list.
static constexpr uint WD_CAPTION_HEIGHT
Minimum height of a title bar.
Definition window_gui.h:89
static constexpr uint WD_DROPDOWN_HEIGHT
Minimum height of a drop down widget.
Definition window_gui.h:90
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
RectPadding hscrollbar
Padding inside horizontal scrollbar buttons.
Definition window_gui.h:39
RectPadding vscrollbar
Padding inside vertical scrollbar buttons.
Definition window_gui.h:38
static constexpr uint WD_CLOSEBOX_WIDTH
Minimum width of a close box widget.
Definition window_gui.h:88
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:95
static constexpr uint WD_RESIZEBOX_WIDTH
Minimum width of a resize box widget.
Definition window_gui.h:87
static constexpr uint WD_STICKYBOX_WIDTH
Minimum width of a standard sticky box widget.
Definition window_gui.h:84
static constexpr uint WD_DEBUGBOX_WIDTH
Minimum width of a standard debug box widget.
Definition window_gui.h:85
static constexpr uint WD_DEFSIZEBOX_WIDTH
Minimum width of a standard defsize box widget.
Definition window_gui.h:86
static constexpr uint WD_SHADEBOX_WIDTH
Distances used in drawing widgets.
Definition window_gui.h:83
TypedIndexContainer< std::array< Colours, MAX_COMPANIES >, CompanyID > _company_colours
NOSAVE: can be determined from company structs.
Functions related to companies.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:88
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
@ ForceRight
Force align to the right.
@ Centre
Align to the centre.
@ Start
Align to the start, LTR/RTL aware.
@ ForceLeft
Force align to the left.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
@ Bottom
Align to the bottom.
@ Top
Align to the top.
@ Middle
Align to the middle.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition gfx.cpp:716
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:971
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:899
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:464
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1037
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, Alignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:668
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:1572
Dimension GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
Definition widget.cpp:85
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:70
void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, Alignment align)
Draw a sprite within a Rect, ignoring the sprite's padding.
Definition widget.cpp:350
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
@ Begin
Marker for the first font in the enumeration.
Definition gfx_type.h:255
@ End
Marker for the end of the enumerations.
Definition gfx_type.h:254
@ Normal
Index of the normal font in the font tables.
Definition gfx_type.h:249
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
Colours
One of 16 base colours used for companies and windows/widgets.
Definition gfx_type.h:283
@ White
White.
Definition gfx_type.h:300
@ Invalid
Invalid marker.
Definition gfx_type.h:302
@ End
End-of-array marker.
Definition gfx_type.h:301
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:315
@ White
White colour.
Definition gfx_type.h:330
@ Black
Black colour.
Definition gfx_type.h:334
@ Silver
Silver colour.
Definition gfx_type.h:319
@ Recolour
Apply a recolour sprite to the screen content.
Definition gfx_type.h:394
@ Checker
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:393
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
std::unique_ptr< NWidgetBase > MakeWindowNWidgetTree(std::span< const NWidgetPart > nwid_parts, NWidgetStacked **shade_select)
Make a nested widget tree for a window from a parts array.
Definition widget.cpp:3450
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:3431
constexpr NWidgetPart SetToolTip(StringID tip)
Widget part function for setting tooltip and clearing the widget data.
constexpr NWidgetPart SetAlignment(Alignment align)
Widget part function for setting the alignment of text/images.
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
Definition window.cpp:975
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:1520
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
PixelColour GetColourGradient(Colours colour, Shade shade)
Get colour gradient palette index.
Definition palette.cpp:393
@ Darker
Darker colour shade.
@ Lightest
Lightest colour shade.
@ Lighter
Lighter colour shade.
@ Normal
Normal colour shade.
@ Light
Light colour shade.
@ Dark
Dark colour shade.
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.
@ Invalid
broken savegame (used internally)
Definition saveload.h:434
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 const SpriteID SPR_ARROW_RIGHT
Definition sprites.h:88
static const SpriteID SPR_WINDOW_SHADE
Shade the window icon.
Definition sprites.h:79
static const SpriteID SPR_WINDOW_DEBUG
NewGRF debug window icon.
Definition sprites.h:81
static const SpriteID SPR_PIN_UP
Pin icon.
Definition sprites.h:92
static const SpriteID SPR_WINDOW_UNSHADE
Unshade the window icon.
Definition sprites.h:80
static const SpriteID SPR_COMPANY_ICON
Icon showing company colour.
Definition sprites.h:385
static const SpriteID SPR_ARROW_LEFT
Definition sprites.h:87
static const SpriteID SPR_WINDOW_RESIZE_RIGHT
Resize icon to the right.
Definition sprites.h:77
static const SpriteID SPR_WINDOW_RESIZE_LEFT
Resize icon to the left.
Definition sprites.h:78
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
Definition sprites.h:1718
static const SpriteID SPR_ARROW_DOWN
Definition sprites.h:85
static const SpriteID SPR_ARROW_UP
Definition sprites.h:86
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition sprites.h:1790
static const PaletteID PALETTE_NEWSPAPER
Recolour sprite for newspaper-greying.
Definition sprites.h:1792
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.
StrongType::Typedef< uint32_t, struct StringIDTag, StrongType::Compare, StrongType::Integer > 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.
Horizontal and vertical alignment.
AlignmentH ResolveRTL() const
Resolve horizontal alignment for the current text direction.
AlignmentV v
Vertical alignment.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
T y
Y coordinate.
T x
X coordinate.
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
Alignment 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:307
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 coordinates.
Rect WithX(int new_left, int new_right) const
Create a new Rect, replacing the left and right coordinates.
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:273
virtual void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
Update size and resize step of a widget in the window.
Definition window_gui.h:623
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:839
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:792
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:513
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:326
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
Definition window_gui.h:723
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
Definition window.cpp:478
void DrawSortButton(WidgetID widget, bool descending) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:824
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
Definition window_gui.h:866
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:609
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition window_gui.h:316
int left
x position of left edge of the window
Definition window_gui.h:309
bool IsShaded() const
Is window shaded currently?
Definition window_gui.h:562
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
Definition window_gui.h:319
int top
y position of top edge of the window
Definition window_gui.h:310
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
Definition window.cpp:342
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
Definition window_gui.h:322
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:218
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:989
WindowFlags flags
Window flags.
Definition window_gui.h:300
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:321
int height
Height of the window (number of pixels down in y direction).
Definition window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
Functions related to transparency.
EnumBitSet< TransparencyOption, uint32_t > TransparencyOptions
Bitset of TransparencyOption elements.
TransparencyOptions _transparency_opt
The bits that should be transparent.
@ Text
loading and cost/income text
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 DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, std::string_view str, Alignment align)
Draw a button with a dropdown (WWT_DROPDOWN and NWID_BUTTON_DROPDOWN).
Definition widget.cpp:774
static void DrawCloseBox(const Rect &r, Colours colour)
Draw a close box.
Definition widget.cpp:723
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:308
static Point GetAlignedPosition(const Rect &r, const Dimension &d, Alignment align)
Calculate x and y coordinates for an aligned object within a window.
Definition widget.cpp:140
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition widget.cpp:2565
void ApplyNWidgetPartAttribute(const NWidgetPart &nwid, NWidgetBase *dest)
Apply an attribute NWidgetPart to an NWidget.
Definition widget.cpp:3187
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:168
static void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
Draw a defsize box.
Definition widget.cpp:684
Dimension GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
Definition widget.cpp:85
bool IsContainerWidgetType(WidgetType tp)
Test if WidgetType is a container widget.
Definition widget.cpp:3374
static void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
Draw a NewGRF debug box.
Definition widget.cpp:695
static void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
Draw a sticky box.
Definition widget.cpp:673
static Dimension _toolbar_image_size
Cached dimension of maximal toolbar sprite size.
Definition widget.cpp:92
static void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
Draw a matrix widget.
Definition widget.cpp:469
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:568
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:70
static std::unique_ptr< NWidgetBase > MakeNWidget(const NWidgetPart &nwid)
Make NWidget from an NWidgetPart.
Definition widget.cpp:3303
static void DrawText(const Rect &r, TextColour colour, std::string_view str, Alignment align, FontSize fs)
Draw text.
Definition widget.cpp:435
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:98
static void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
Draw a shade box.
Definition widget.cpp:662
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:293
Dimension GetToolbarMaximalImageSize()
Get maximal square size of a toolbar image.
static void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, Alignment align, FontSize fs)
Draw a frame widget.
Definition widget.cpp:609
static void DrawImageTextButtons(const Rect &r, Colours colour, bool clicked, SpriteID img, TextColour text_colour, const std::string &text, Alignment align, FontSize fs)
Draw a button with image and rext.
Definition widget.cpp:390
static bool IsAttributeWidgetPartType(WidgetType tp)
Test if (an NWidgetPart) WidgetType is an attribute widget part type.
Definition widget.cpp:3176
static void DrawInset(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, Alignment align, FontSize fs)
Draw an inset widget.
Definition widget.cpp:453
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:269
static void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, Alignment align)
Draw an image button.
Definition widget.cpp:370
static void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
Draw a resize box.
Definition widget.cpp:708
void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, std::string_view str, Alignment align, FontSize fs)
Draw a caption bar.
Definition widget.cpp:744
void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, Alignment align)
Draw a sprite within a Rect, ignoring the sprite's padding.
Definition widget.cpp:350
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:204
static void DrawLabel(const Rect &r, TextColour colour, std::string_view str, Alignment align, FontSize fs)
Draw the label-part of a widget.
Definition widget.cpp:418
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:3387
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:527
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:3492
EnumBitSet< AspectFlag, uint8_t > AspectFlags
Bitset of AspectFlag elements.
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h:35
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition widget_type.h:40
@ WPT_FILL
Widget part for specifying fill.
Definition widget_type.h:84
@ WWT_PUSHIMGTEXTBTN
Normal push-button (no toggle button) with image and text caption.
@ WPT_ALIGNMENT
Widget part for specifying text/image alignment.
Definition widget_type.h:90
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:41
@ WWT_PUSHBTN
Normal push-button (no toggle button) with custom drawing.
@ WWT_IMGBTN_2
(Toggle) Button with diff image when clicked
Definition widget_type.h:42
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WPT_MINSIZE
Widget part for specifying minimal size.
Definition widget_type.h:82
@ WWT_PUSHARROWBTN
Normal push-button (no toggle button) with arrow caption.
@ WWT_LABEL
Centered label.
Definition widget_type.h:48
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:74
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:70
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:62
@ WWT_ARROWBTN
(Toggle) Button with an arrow
Definition widget_type.h:43
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:44
@ WPT_SCROLLBAR
Widget part for attaching a scrollbar.
Definition widget_type.h:91
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
@ WPT_ASPECT
Widget part for specifying aspect ratio.
Definition widget_type.h:92
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX).
Definition widget_type.h:57
@ WPT_RESIZE
Widget part for specifying resizing.
Definition widget_type.h:81
@ WWT_IMGTEXTBTN
(Toggle) Button with image and text
Definition widget_type.h:47
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX).
Definition widget_type.h:55
@ WPT_TOOLBARSIZE
Widget part for specifying minimal size in terms of toolbar images.
Definition widget_type.h:93
@ WWT_CAPTION
Window caption (window title between closebox and stickybox).
Definition widget_type.h:52
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
@ WPT_TEXTSTYLE
Widget part for specifying text colour.
Definition widget_type.h:89
@ WPT_PADDING
Widget part for specifying a padding.
Definition widget_type.h:86
@ WWT_BOOLBTN
Standard boolean toggle button.
Definition widget_type.h:46
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
@ WWT_CLOSEBOX
Close box (at top-left of a window).
Definition widget_type.h:60
@ WWT_TEXTBTN_2
(Toggle) Button with diff text when clicked
Definition widget_type.h:45
@ WWT_FRAME
Frame.
Definition widget_type.h:51
@ WPT_MINTEXTLINES
Widget part for specifying minimal number of lines of text.
Definition widget_type.h:83
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition widget_type.h:63
@ WPT_ATTRIBUTE_END
End marker for attribute NWidgetPart types.
Definition widget_type.h:94
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:75
@ WPT_ENDCONTAINER
Widget part to denote end of a container.
Definition widget_type.h:97
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window).
Definition widget_type.h:59
@ WPT_PIPRATIO
Widget part for specifying pre/inter/post ratio for containers.
Definition widget_type.h:88
@ WPT_DATATIP
Widget part for specifying data and tooltip.
Definition widget_type.h:85
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX).
Definition widget_type.h:56
@ WPT_ATTRIBUTE_BEGIN
Begin marker for attribute NWidgetPart types.
Definition widget_type.h:80
@ WPT_PIPSPACE
Widget part for specifying pre/inter/post space for containers.
Definition widget_type.h:87
@ NWID_MATRIX
Matrix container.
Definition widget_type.h:69
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:73
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:61
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:49
@ NWID_HORIZONTAL_LTR
Horizontal container that doesn't change the order of the widgets for RTL languages.
Definition widget_type.h:67
@ WPT_FUNCTION
Widget part for calling a user function.
Definition widget_type.h:96
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX).
Definition widget_type.h:54
@ NWID_LAYER
Layered widgets, all visible together.
Definition widget_type.h:72
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:71
@ ScrollbarDown
Down-button is lowered bit.
@ NoTransparency
Viewport is never transparent.
@ ShadeDimmed
Display dimmed colours in the viewport.
@ DropdownActive
Dropdown menu of the button dropdown widget is active.
@ ScrollbarUp
Up-button is lowered bit.
uint ComputeMaxSize(uint base, uint max_space, uint step)
Return the biggest possible size of a nested widget.
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ SZSP_BEGIN
First zero-size plane.
@ SZSP_VERTICAL
Display plane with zero size horizontally, and filling and resizing vertically.
@ EqualSize
Containers should keep all their (resizing) children equally large.
@ BigFirst
Allocate space to biggest resize first.
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition().
@ Resize
Resize the nested widget tree.
@ Smallest
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
@ Right
Force the arrow to the right.
Definition widget_type.h:23
@ Left
Force the arrow to the left.
Definition widget_type.h:22
@ Decrease
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:20
@ Increase
Arrow to the right or in case of RTL to the left.
Definition widget_type.h:21
std::map< WidgetID, class NWidgetBase * > WidgetLookup
Lookup between widget IDs and NWidget objects.
ResizeWidgetType
WidgetData values for a resize box widget.
Definition widget_type.h:27
@ ShowBevel
Bevel of resize box is shown.
Definition widget_type.h:28
@ ResizeY
Resize vertically to reach desired aspect ratio.
@ ResizeX
Resize horizontally to reach desired aspect ratio.
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition window.cpp:73
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:308
EnumBitSet< FrameFlag, uint8_t > FrameFlags
Bitset of FrameFlag elements.
Definition window_gui.h:32
@ Transparent
Makes the background transparent if set.
Definition window_gui.h:25
@ BorderOnly
Draw border only, no background.
Definition window_gui.h:26
@ Darkened
If set the background is darker, allows for lowered frames with normal background colour when used wi...
Definition window_gui.h:28
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed).
Definition window_gui.h:27
@ SizingLeft
Window is being resized towards the left.
Definition window_gui.h:228
@ Highlighted
Window has a widget that has a highlight.
Definition window_gui.h:233
@ SizingRight
Window is being resized towards the right.
Definition window_gui.h:227
@ WhiteBorder
Window white border counter bit mask.
Definition window_gui.h:232
@ Sticky
Window is made sticky by user.
Definition window_gui.h:230
int WidgetID
Widget ID.
Definition window_type.h:21
EventState
State of handling an event.
@ Handled
The passed event is handled.
@ NotHandled
The passed event is not handled.
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
Definition window_type.h:24
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::Min) When shifting right,...
Definition zoom_func.h:22
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107
ZoomLevel
All zoom levels we know.
Definition zoom_type.h:20
@ Normal
The normal zoom level.
Definition zoom_type.h:26