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