OpenTTD Source 20250524-master-gc366e6a48e
widget.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "core/backup_type.hpp"
12#include "company_func.h"
13#include "settings_gui.h"
14#include "strings_type.h"
15#include "window_gui.h"
16#include "viewport_func.h"
17#include "zoom_func.h"
18#include "strings_func.h"
19#include "transparency.h"
21#include "settings_type.h"
22#include "querystring_gui.h"
23
24#include "table/sprites.h"
25#include "table/strings.h"
27
28#include "safeguards.h"
29
31
32static std::string GetStringForWidget(const Window *w, const NWidgetCore *nwid, bool secondary = false)
33{
34 StringID stringid = nwid->GetString();
35 if (nwid->GetIndex() < 0) {
36 if (stringid == STR_NULL) return {};
37
38 return GetString(stringid + (secondary ? 1 : 0));
39 }
40
41 return w->GetWidgetString(nwid->GetIndex(), stringid + (secondary ? 1 : 0));
42}
43
49static inline RectPadding ScaleGUITrad(const RectPadding &r)
50{
51 return {(uint8_t)ScaleGUITrad(r.left), (uint8_t)ScaleGUITrad(r.top), (uint8_t)ScaleGUITrad(r.right), (uint8_t)ScaleGUITrad(r.bottom)};
52}
53
59static inline Dimension ScaleGUITrad(const Dimension &dim)
60{
61 return {(uint)ScaleGUITrad(dim.width), (uint)ScaleGUITrad(dim.height)};
62}
63
69{
70 Point offset;
71 Dimension d = GetSpriteSize(sprid, &offset, ZoomLevel::Normal);
72 d.width -= offset.x;
73 d.height -= offset.y;
74 return ScaleGUITrad(d);
75}
76
81{
88 } else {
90 }
105
111}
112
120static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align)
121{
122 Point p;
123 /* In case we have a RTL language we swap the alignment. */
124 if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
125 switch (align & SA_HOR_MASK) {
126 case SA_LEFT: p.x = r.left; break;
127 case SA_HOR_CENTER: p.x = CentreBounds(r.left, r.right, d.width); break;
128 case SA_RIGHT: p.x = r.right + 1 - d.width; break;
129 default: NOT_REACHED();
130 }
131 switch (align & SA_VERT_MASK) {
132 case SA_TOP: p.y = r.top; break;
133 case SA_VERT_CENTER: p.y = CentreBounds(r.top, r.bottom, d.height); break;
134 case SA_BOTTOM: p.y = r.bottom + 1 - d.height; break;
135 default: NOT_REACHED();
136 }
137 return p;
138}
139
149static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom, bool horizontal)
150{
151 /* Base for reversion */
152 int rev_base = top + bottom;
153 int button_size;
154 if (horizontal) {
155 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
156 } else {
157 button_size = NWidgetScrollbar::GetVerticalDimension().height;
158 }
159 top += button_size; // top points to just below the up-button
160 bottom -= button_size; // bottom points to just before the down-button
161
162 int count = sb->GetCount();
163 int cap = sb->GetCapacity();
164
165 if (count > cap) {
166 int height = bottom + 1 - top;
167 int slider_height = std::max(button_size, cap * height / count);
168 height -= slider_height;
169
170 top += height * sb->GetPosition() / (count - cap);
171 bottom = top + slider_height - 1;
172 }
173
174 Point pt;
175 if (horizontal && _current_text_dir == TD_RTL) {
176 pt.x = rev_base - bottom;
177 pt.y = rev_base - top;
178 } else {
179 pt.x = top;
180 pt.y = bottom;
181 }
182 return pt;
183}
184
194static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, int y, int mi, int ma)
195{
196 int pos;
197 int button_size;
198 bool rtl = false;
199 bool changed = false;
200
201 if (sb->type == NWID_HSCROLLBAR) {
202 pos = x;
203 rtl = _current_text_dir == TD_RTL;
204 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
205 } else {
206 pos = y;
207 button_size = NWidgetScrollbar::GetVerticalDimension().height;
208 }
209 if (pos < mi + button_size) {
210 /* Pressing the upper button? */
212 if (_scroller_click_timeout <= 1) {
213 _scroller_click_timeout = 3;
214 changed = sb->UpdatePosition(rtl ? 1 : -1);
215 }
217 } else if (pos >= ma - button_size) {
218 /* Pressing the lower button? */
220
221 if (_scroller_click_timeout <= 1) {
222 _scroller_click_timeout = 3;
223 changed = sb->UpdatePosition(rtl ? -1 : 1);
224 }
226 } else {
227 Point pt = HandleScrollbarHittest(sb, mi, ma, sb->type == NWID_HSCROLLBAR);
228
229 if (pos < pt.x) {
230 changed = sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::SS_BIG);
231 } else if (pos > pt.y) {
232 changed = sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::SS_BIG);
233 } else {
234 _scrollbar_start_pos = pt.x - mi - button_size;
235 _scrollbar_size = ma - mi - button_size * 2 - (pt.y - pt.x);
237 _cursorpos_drag_start = _cursor.pos;
238 }
239 }
240
241 if (changed) {
242 /* Position changed so refresh the window */
243 w->OnScrollbarScroll(sb->GetIndex());
244 w->SetDirty();
245 } else {
246 /* No change so only refresh this scrollbar */
247 sb->SetDirty(w);
248 }
249}
250
259void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
260{
261 int mi, ma;
262
263 if (nw->type == NWID_HSCROLLBAR) {
264 mi = nw->pos_x;
265 ma = nw->pos_x + nw->current_x;
266 } else {
267 mi = nw->pos_y;
268 ma = nw->pos_y + nw->current_y;
269 }
270 NWidgetScrollbar *scrollbar = dynamic_cast<NWidgetScrollbar*>(nw);
271 assert(scrollbar != nullptr);
272 ScrollbarClickPositioning(w, scrollbar, x, y, mi, ma);
273}
274
283WidgetID GetWidgetFromPos(const Window *w, int x, int y)
284{
285 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
286 return (nw != nullptr) ? nw->GetIndex() : -1;
287}
288
298void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
299{
300 if (flags.Test(FrameFlag::Transparent)) {
301 GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
302 } else {
303 assert(colour < COLOUR_END);
304
305 const uint dark = GetColourGradient(colour, SHADE_DARK);
306 const uint medium_dark = GetColourGradient(colour, SHADE_LIGHT);
307 const uint medium_light = GetColourGradient(colour, SHADE_LIGHTER);
308 const uint light = GetColourGradient(colour, SHADE_LIGHTEST);
309 uint interior;
310
311 Rect outer = {left, top, right, bottom}; // Outside rectangle
312 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
313
314 if (flags.Test(FrameFlag::Lowered)) {
315 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark); // Left
316 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark); // Top
317 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light); // Right
318 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light); // Bottom
319 interior = (flags.Test(FrameFlag::Darkened) ? medium_dark : medium_light);
320 } else {
321 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light); // Left
322 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light); // Top
323 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark); // Right
324 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark); // Bottom
325 interior = medium_dark;
326 }
327 if (!flags.Test(FrameFlag::BorderOnly)) {
328 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior); // Inner
329 }
330 }
331}
332
333void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, StringAlignment align)
334{
335 Point offset;
336 Dimension d = GetSpriteSize(img, &offset);
337 d.width -= offset.x;
338 d.height -= offset.y;
339
340 Point p = GetAlignedPosition(r, d, align);
341 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
342}
343
353static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
354{
355 assert(img != 0);
356 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FrameFlag::Lowered : FrameFlags{});
357
358 if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2.
359 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
360}
361
373static inline void DrawImageTextButtons(const Rect &r, Colours colour, bool clicked, SpriteID img, TextColour text_colour, const std::string &text, StringAlignment align, FontSize fs)
374{
375 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FrameFlag::Lowered : FrameFlags{});
376
377 bool rtl = _current_text_dir == TD_RTL;
378 int image_width = img != 0 ? GetScaledSpriteSize(img).width : 0;
379 Rect r_img = r.Shrink(WidgetDimensions::scaled.framerect).WithWidth(image_width, rtl);
380 Rect r_text = r.Shrink(WidgetDimensions::scaled.framerect).Indent(image_width + WidgetDimensions::scaled.hsep_wide, rtl);
381
382 if (img != 0) {
383 DrawSpriteIgnorePadding(img, PAL_NONE, r_img, SA_HOR_CENTER | (align & SA_VERT_MASK));
384 }
385
386 if (!text.empty()) {
387 Dimension d = GetStringBoundingBox(text, fs);
388 Point p = GetAlignedPosition(r_text, d, align);
389 DrawString(r_text.left, r_text.right, p.y, text, text_colour, align, false, fs);
390 }
391}
392
401static inline void DrawLabel(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
402{
403 if (str.empty()) return;
404
405 Dimension d = GetStringBoundingBox(str, fs);
406 Point p = GetAlignedPosition(r, d, align);
407 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
408}
409
418static inline void DrawText(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
419{
420 if (str.empty()) return;
421
422 Dimension d = GetStringBoundingBox(str, fs);
423 Point p = GetAlignedPosition(r, d, align);
424 DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
425}
426
436static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
437{
438 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, {FrameFlag::Lowered, FrameFlag::Darkened});
439 if (!str.empty()) DrawString(r.Shrink(WidgetDimensions::scaled.inset), str, text_colour, align, false, fs);
440}
441
452static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
453{
454 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FrameFlag::Lowered : FrameFlags{});
455
456 int column_width; // Width of a single column in the matrix.
457 if (num_columns == 0) {
458 column_width = resize_x;
459 num_columns = r.Width() / column_width;
460 } else {
461 column_width = r.Width() / num_columns;
462 }
463
464 int row_height; // Height of a single row in the matrix.
465 if (num_rows == 0) {
466 row_height = resize_y;
467 num_rows = r.Height() / row_height;
468 } else {
469 row_height = r.Height() / num_rows;
470 }
471
472 int col = GetColourGradient(colour, SHADE_LIGHTER);
473
474 int x = r.left;
475 for (int ctr = num_columns; ctr > 1; ctr--) {
476 x += column_width;
477 GfxFillRect(x, r.top + WidgetDimensions::scaled.bevel.top, x + WidgetDimensions::scaled.bevel.left - 1, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
478 }
479
480 x = r.top;
481 for (int ctr = num_rows; ctr > 1; ctr--) {
482 x += row_height;
484 }
485
486 col = GetColourGradient(colour, SHADE_NORMAL);
487
488 x = r.left - 1;
489 for (int ctr = num_columns; ctr > 1; ctr--) {
490 x += column_width;
491 GfxFillRect(x - WidgetDimensions::scaled.bevel.right + 1, r.top + WidgetDimensions::scaled.bevel.top, x, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
492 }
493
494 x = r.top - 1;
495 for (int ctr = num_rows; ctr > 1; ctr--) {
496 x += row_height;
497 GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x - WidgetDimensions::scaled.bevel.bottom + 1, r.right - WidgetDimensions::scaled.bevel.right, x, col);
498 }
499}
500
510static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
511{
512 int height = NWidgetScrollbar::GetVerticalDimension().height;
513
514 /* draw up/down buttons */
515 DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
516 DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
517
518 int c1 = GetColourGradient(colour, SHADE_DARK);
519 int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
520
521 /* draw "shaded" background */
522 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
523 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c1, FILLRECT_CHECKER);
524
525 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
526 int left = r.left + r.Width() * 3 / 11; /* left track is positioned 3/11ths from the left */
527 int right = r.left + r.Width() * 8 / 11; /* right track is positioned 8/11ths from the left */
528 const uint8_t bl = WidgetDimensions::scaled.bevel.left;
529 const uint8_t br = WidgetDimensions::scaled.bevel.right;
530
531 /* draw shaded lines */
532 GfxFillRect(left - bl, r.top + height, left - 1, r.bottom - height, c1);
533 GfxFillRect(left, r.top + height, left + br - 1, r.bottom - height, c2);
534 GfxFillRect(right - bl, r.top + height, right - 1, r.bottom - height, c1);
535 GfxFillRect(right, r.top + height, right + br - 1, r.bottom - height, c2);
536
537 Point pt = HandleScrollbarHittest(scrollbar, r.top, r.bottom, false);
538 DrawFrameRect(r.left, pt.x, r.right, pt.y, colour, bar_dragged ? FrameFlag::Lowered : FrameFlags{});
539}
540
550static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
551{
552 int width = NWidgetScrollbar::GetHorizontalDimension().width;
553
554 DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
555 DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
556
557 int c1 = GetColourGradient(colour, SHADE_DARK);
558 int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
559
560 /* draw "shaded" background */
561 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
562 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c1, FILLRECT_CHECKER);
563
564 /* track positions. These fractions are based on original 1x dimensions, but scale better. */
565 int top = r.top + r.Height() * 3 / 11; /* top track is positioned 3/11ths from the top */
566 int bottom = r.top + r.Height() * 8 / 11; /* bottom track is positioned 8/11ths from the top */
567 const uint8_t bt = WidgetDimensions::scaled.bevel.top;
568 const uint8_t bb = WidgetDimensions::scaled.bevel.bottom;
569
570 /* draw shaded lines */
571 GfxFillRect(r.left + width, top - bt, r.right - width, top - 1, c1);
572 GfxFillRect(r.left + width, top, r.right - width, top + bb - 1, c2);
573 GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom - 1, c1);
574 GfxFillRect(r.left + width, bottom, r.right - width, bottom + bb - 1, c2);
575
576 /* draw actual scrollbar */
577 Point pt = HandleScrollbarHittest(scrollbar, r.left, r.right, true);
578 DrawFrameRect(pt.x, r.top, pt.y, r.bottom, colour, bar_dragged ? FrameFlag::Lowered : FrameFlags{});
579}
580
590static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
591{
592 int x2 = r.left; // by default the left side is the left side of the widget
593
594 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);
595
596 int c1 = GetColourGradient(colour, SHADE_DARK);
597 int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
598
599 /* If the frame has text, adjust the top bar to fit half-way through */
600 Rect inner = r.Shrink(ScaleGUITrad(1));
601 if (!str.empty()) inner.top = r.top + GetCharacterHeight(FS_NORMAL) / 2;
602
603 Rect outer = inner.Expand(WidgetDimensions::scaled.bevel);
604 Rect inside = inner.Shrink(WidgetDimensions::scaled.bevel);
605
606 if (_current_text_dir == TD_LTR) {
607 /* Line from upper left corner to start of text */
608 GfxFillRect(outer.left, outer.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
609 GfxFillRect(inner.left, inner.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
610
611 /* Line from end of text to upper right corner */
612 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
613 GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
614 } else {
615 /* Line from upper left corner to start of text */
616 GfxFillRect(outer.left, outer.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
617 GfxFillRect(inner.left, inner.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
618
619 /* Line from end of text to upper right corner */
620 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
621 GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
622 }
623
624 /* Line from upper left corner to bottom left corner */
625 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
626 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
627
628 /* Line from upper right corner to bottom right corner */
629 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
630 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
631
632 /* Line from bottom left corner to bottom right corner */
633 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
634 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
635}
636
643static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
644{
645 DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER);
646}
647
654static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
655{
656 DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER);
657}
658
665static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
666{
667 DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER);
668}
669
676static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
677{
678 DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER);
679}
680
689static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
690{
691 if (bevel) {
692 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FrameFlag::Lowered : FrameFlags{});
693 } else if (clicked) {
695 }
696 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));
697}
698
704static inline void DrawCloseBox(const Rect &r, Colours colour)
705{
706 if (colour != COLOUR_WHITE) DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, {});
707 Point offset;
708 Dimension d = GetSpriteSize(SPR_CLOSEBOX, &offset);
709 d.width -= offset.x;
710 d.height -= offset.y;
711 int s = ScaleSpriteTrad(1); /* Offset to account for shadow of SPR_CLOSEBOX */
712 DrawSprite(SPR_CLOSEBOX, (colour != COLOUR_WHITE ? TC_BLACK : TC_SILVER) | (1U << PALETTE_TEXT_RECOLOUR), CentreBounds(r.left, r.right, d.width - s) - offset.x, CentreBounds(r.top, r.bottom, d.height - s) - offset.y);
713}
714
725void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, std::string_view str, StringAlignment align, FontSize fs)
726{
727 bool company_owned = owner < MAX_COMPANIES;
728
732
733 if (company_owned) {
735 }
736
737 if (str.empty()) return;
738
740 Point p = GetAlignedPosition(r, d, align);
741 DrawString(r.left + WidgetDimensions::scaled.captiontext.left, r.right - WidgetDimensions::scaled.captiontext.left, p.y, str, text_colour, align, false, fs);
742}
743
755static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, std::string_view str, StringAlignment align)
756{
757 int dd_width = NWidgetLeaf::dropdown_dimension.width;
758
759 if (_current_text_dir == TD_LTR) {
760 DrawFrameRect(r.left, r.top, r.right - dd_width, r.bottom, colour, clicked_button ? FrameFlag::Lowered : FrameFlags{});
761 DrawImageButtons(r.WithWidth(dd_width, true), WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
762 if (!str.empty()) {
763 DrawString(r.left + WidgetDimensions::scaled.dropdowntext.left, r.right - dd_width - WidgetDimensions::scaled.dropdowntext.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), str, TC_BLACK, align);
764 }
765 } else {
766 DrawFrameRect(r.left + dd_width, r.top, r.right, r.bottom, colour, clicked_button ? FrameFlag::Lowered : FrameFlags{});
767 DrawImageButtons(r.WithWidth(dd_width, false), WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
768 if (!str.empty()) {
769 DrawString(r.left + dd_width + WidgetDimensions::scaled.dropdowntext.left, r.right - WidgetDimensions::scaled.dropdowntext.right, CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), str, TC_BLACK, align);
770 }
771 }
772}
773
778{
779 this->nested_root->Draw(this);
780
782 DrawFrameRect(0, 0, this->width - 1, this->height - 1, COLOUR_WHITE, FrameFlag::BorderOnly);
783 }
784
786 extern bool _window_highlight_colour;
787 for (const auto &pair : this->widget_lookup) {
788 const NWidgetBase *widget = pair.second;
789 if (!widget->IsHighlighted()) continue;
790
791 Rect outer = widget->GetCurrentRect();
792 Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1);
793
794 int colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE];
795
796 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
797 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
798 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
799 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
800 }
801 }
802}
803
810{
811 if (state == SBS_OFF) return;
812
813 assert(!this->widget_lookup.empty());
814 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
815
816 /* Sort button uses the same sprites as vertical scrollbar */
817 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
818
819 DrawSpriteIgnorePadding(state == SBS_DOWN ? SPR_ARROW_DOWN : SPR_ARROW_UP, PAL_NONE, r.WithWidth(dim.width, _current_text_dir == TD_LTR), SA_CENTER);
820}
821
827{
828 return NWidgetScrollbar::GetVerticalDimension().width + 1;
829}
830
831bool _draw_widget_outlines;
832
833static void DrawOutline(const Window *, const NWidgetBase *wid)
834{
835 if (!_draw_widget_outlines || wid->current_x == 0 || wid->current_y == 0) return;
836
837 DrawRectOutline(wid->GetCurrentRect(), PC_WHITE, 1, 4);
838}
839
938void NWidgetBase::SetDirty(const Window *w) const
939{
940 int abs_left = w->left + this->pos_x;
941 int abs_top = w->top + this->pos_y;
942 AddDirtyBlock(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
943}
944
959{
960 return (this->type == tp) ? this : nullptr;
961}
962
963void NWidgetBase::ApplyAspectRatio()
964{
965 if (this->aspect_ratio == 0) return;
966 if (this->smallest_x == 0 || this->smallest_y == 0) return;
967
968 uint x = this->smallest_x;
969 uint y = this->smallest_y;
970 if (this->aspect_flags.Test(AspectFlag::ResizeX)) x = std::max(this->smallest_x, static_cast<uint>(this->smallest_y * std::abs(this->aspect_ratio)));
971 if (this->aspect_flags.Test(AspectFlag::ResizeY)) y = std::max(this->smallest_y, static_cast<uint>(this->smallest_x / std::abs(this->aspect_ratio)));
972
973 this->smallest_x = x;
974 this->smallest_y = y;
975}
976
977void NWidgetBase::AdjustPaddingForZoom()
978{
979 this->padding = ScaleGUITrad(this->uz_padding);
980}
981
989{
990 this->fill_x = fill_x;
991 this->fill_y = fill_y;
992}
993
1000{
1001 this->aspect_ratio = ratio;
1002 this->aspect_flags = flags;
1003}
1004
1011void NWidgetResizeBase::SetAspect(int x_ratio, int y_ratio, AspectFlags flags)
1012{
1013 this->SetAspect(static_cast<float>(x_ratio) / static_cast<float>(y_ratio), flags);
1014}
1015
1016void NWidgetResizeBase::AdjustPaddingForZoom()
1017{
1018 if (!this->absolute) {
1019 this->min_x = ScaleGUITrad(this->uz_min_x);
1020 this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1021 }
1022 NWidgetBase::AdjustPaddingForZoom();
1023}
1024
1030void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y)
1031{
1032 this->uz_min_x = std::max(this->uz_min_x, min_x);
1033 this->uz_min_y = std::max(this->uz_min_y, min_y);
1034 this->min_x = ScaleGUITrad(this->uz_min_x);
1035 this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1036}
1037
1044{
1045 this->absolute = true;
1046 this->min_x = std::max(this->min_x, min_x);
1047 this->min_y = std::max(this->min_y, min_y);
1048}
1049
1056void NWidgetResizeBase::SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
1057{
1058 this->uz_text_lines = min_lines;
1059 this->uz_text_spacing = spacing;
1060 this->uz_text_size = size;
1061 this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1062}
1063
1069void NWidgetResizeBase::SetFill(uint fill_x, uint fill_y)
1070{
1071 this->fill_x = fill_x;
1072 this->fill_y = fill_y;
1073}
1074
1080void NWidgetResizeBase::SetResize(uint resize_x, uint resize_y)
1081{
1082 this->resize_x = resize_x;
1083 this->resize_y = resize_y;
1084}
1085
1093bool NWidgetResizeBase::UpdateMultilineWidgetSize(const std::string &str, int max_lines)
1094{
1095 int y = GetStringHeight(str, this->current_x);
1096 if (y > max_lines * GetCharacterHeight(FS_NORMAL)) {
1097 /* Text at the current width is too tall, so try to guess a better width. */
1099 d.height *= max_lines;
1100 d.width /= 2;
1101 return this->UpdateSize(d.width, d.height);
1102 }
1103 return this->UpdateVerticalSize(y);
1104}
1105
1113bool NWidgetResizeBase::UpdateSize(uint min_x, uint min_y)
1114{
1115 if (min_x == this->min_x && min_y == this->min_y) return false;
1116 this->min_x = min_x;
1117 this->min_y = min_y;
1118 return true;
1119}
1120
1128{
1129 if (min_y == this->min_y) return false;
1130 this->min_y = min_y;
1131 return true;
1132}
1133
1134void NWidgetResizeBase::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1135{
1136 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1137}
1138
1149NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, WidgetID index, uint fill_x, uint fill_y, const WidgetData &widget_data, StringID tool_tip) : NWidgetResizeBase(tp, fill_x, fill_y), index(index)
1150{
1151 this->colour = colour;
1152 this->widget_data = widget_data;
1153 this->SetToolTip(tool_tip);
1154 this->text_colour = tp == WWT_CAPTION ? TC_WHITE : TC_BLACK;
1155}
1156
1162{
1163 this->widget_data.string = string;
1164}
1165
1172{
1173 this->SetString(string);
1174 this->SetToolTip(tool_tip);
1175}
1176
1182{
1183 this->widget_data.sprite = sprite;
1184}
1185
1192{
1193 this->SetSprite(sprite);
1194 this->SetToolTip(tool_tip);
1195}
1196
1202void NWidgetCore::SetMatrixDimension(uint32_t columns, uint32_t rows)
1203{
1204 this->widget_data.matrix = { columns, rows };
1205}
1206
1212{
1213 this->widget_data.resize_widget_type = type;
1214}
1215
1222{
1223 this->text_colour = colour;
1224 this->text_size = size;
1225}
1226
1232{
1233 this->tool_tip = tool_tip;
1234}
1235
1241{
1242 return this->tool_tip;
1243}
1244
1250{
1251 this->align = align;
1252}
1253
1259{
1260 return this->widget_data.string;
1261}
1262
1268{
1269 return this->index;
1270}
1271
1277{
1278 return this->scrollbar_index;
1279}
1280
1282{
1283 if (this->index >= 0) widget_lookup[this->index] = this;
1284}
1285
1287{
1288 return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : nullptr;
1289}
1290
1292{
1293 if (this->type == tp) return this;
1294 for (const auto &child_wid : this->children) {
1295 NWidgetBase *nwid = child_wid->GetWidgetOfType(tp);
1296 if (nwid != nullptr) return nwid;
1297 }
1298 return nullptr;
1299}
1300
1301void NWidgetContainer::AdjustPaddingForZoom()
1302{
1303 for (const auto &child_wid : this->children) {
1304 child_wid->AdjustPaddingForZoom();
1305 }
1306 NWidgetBase::AdjustPaddingForZoom();
1307}
1308
1313void NWidgetContainer::Add(std::unique_ptr<NWidgetBase> &&wid)
1314{
1315 assert(wid != nullptr);
1316 wid->parent = this;
1317 this->children.push_back(std::move(wid));
1318}
1319
1321{
1322 for (const auto &child_wid : this->children) {
1323 child_wid->FillWidgetLookup(widget_lookup);
1324 }
1325}
1326
1328{
1329 for (const auto &child_wid : this->children) {
1330 child_wid->Draw(w);
1331 }
1332
1333 DrawOutline(w, this);
1334}
1335
1337{
1338 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1339
1340 for (const auto &child_wid : this->children) {
1341 NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
1342 if (nwid != nullptr) return nwid;
1343 }
1344 return nullptr;
1345}
1346
1348{
1349 /* Zero size plane selected */
1350 if (this->shown_plane >= SZSP_BEGIN) {
1351 Dimension size = {0, 0};
1352 Dimension padding = {0, 0};
1353 Dimension fill = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1354 Dimension resize = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1355 /* Here we're primarily interested in the value of resize */
1356 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
1357
1358 this->smallest_x = size.width;
1359 this->smallest_y = size.height;
1360 this->fill_x = fill.width;
1361 this->fill_y = fill.height;
1362 this->resize_x = resize.width;
1363 this->resize_y = resize.height;
1364 this->ApplyAspectRatio();
1365 return;
1366 }
1367
1368 /* First sweep, recurse down and compute minimal size and filling. */
1369 this->smallest_x = 0;
1370 this->smallest_y = 0;
1371 this->fill_x = this->IsEmpty() ? 0 : 1;
1372 this->fill_y = this->IsEmpty() ? 0 : 1;
1373 this->resize_x = this->IsEmpty() ? 0 : 1;
1374 this->resize_y = this->IsEmpty() ? 0 : 1;
1375 for (const auto &child_wid : this->children) {
1376 child_wid->SetupSmallestSize(w);
1377
1378 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1379 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1380 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1381 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1382 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1383 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1384 this->ApplyAspectRatio();
1385 }
1386}
1387
1388void NWidgetStacked::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1389{
1390 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1391 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1392
1393 if (this->shown_plane >= SZSP_BEGIN) return;
1394
1395 for (const auto &child_wid : this->children) {
1396 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1397 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1398 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1399
1400 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1401 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1402 uint child_pos_y = child_wid->padding.top;
1403
1404 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1405 }
1406}
1407
1409{
1410 /* We need to update widget_lookup later. */
1411 this->widget_lookup = &widget_lookup;
1412
1413 if (this->index >= 0) widget_lookup[this->index] = this;
1415 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1416 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(widget_lookup);
1417}
1418
1420{
1421 if (this->shown_plane >= SZSP_BEGIN) return;
1422
1423 assert(static_cast<size_t>(this->shown_plane) < this->children.size());
1424 this->children[shown_plane]->Draw(w);
1425 DrawOutline(w, this);
1426}
1427
1429{
1430 if (this->shown_plane >= SZSP_BEGIN) return nullptr;
1431
1432 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1433
1434 if (static_cast<size_t>(this->shown_plane) >= this->children.size()) return nullptr;
1435 return this->children[shown_plane]->GetWidgetFromPos(x, y);
1436}
1437
1444{
1445 if (this->shown_plane == plane) return false;
1446 this->shown_plane = plane;
1447 /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1448 if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(*this->widget_lookup);
1449 return true;
1450}
1451
1453public:
1455
1456 void SetupSmallestSize(Window *w) override;
1457 void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override;
1458
1459 void Draw(const Window *w) override;
1460
1462};
1463
1465{
1466 /* First sweep, recurse down and compute minimal size and filling. */
1467 this->smallest_x = 0;
1468 this->smallest_y = 0;
1469 this->fill_x = this->IsEmpty() ? 0 : 1;
1470 this->fill_y = this->IsEmpty() ? 0 : 1;
1471 this->resize_x = this->IsEmpty() ? 0 : 1;
1472 this->resize_y = this->IsEmpty() ? 0 : 1;
1473 for (const auto &child_wid : this->children) {
1474 child_wid->SetupSmallestSize(w);
1475
1476 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1477 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1478 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1479 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1480 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1481 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1482 this->ApplyAspectRatio();
1483 }
1484}
1485
1486void NWidgetLayer::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1487{
1488 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1489 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1490
1491 for (const auto &child_wid : this->children) {
1492 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1493 uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1494 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1495
1496 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1497 uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1498 uint child_pos_y = child_wid->padding.top;
1499
1500 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1501 }
1502}
1503
1505{
1506 /* Draw in reverse order, as layers are arranged top-down. */
1507 for (auto it = std::rbegin(this->children); it != std::rend(this->children); ++it) {
1508 (*it)->Draw(w);
1509 }
1510
1511 DrawOutline(w, this);
1512}
1513
1514NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags) : NWidgetContainer(tp)
1515{
1516 this->flags = flags;
1517}
1518
1519void NWidgetPIPContainer::AdjustPaddingForZoom()
1520{
1521 this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1522 this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1523 this->pip_post = ScaleGUITrad(this->uz_pip_post);
1524 NWidgetContainer::AdjustPaddingForZoom();
1525}
1526
1536void NWidgetPIPContainer::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
1537{
1538 this->uz_pip_pre = pip_pre;
1539 this->uz_pip_inter = pip_inter;
1540 this->uz_pip_post = pip_post;
1541
1542 this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1543 this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1544 this->pip_post = ScaleGUITrad(this->uz_pip_post);
1545}
1546
1556void NWidgetPIPContainer::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
1557{
1558 this->pip_ratio_pre = pip_ratio_pre;
1559 this->pip_ratio_inter = pip_ratio_inter;
1560 this->pip_ratio_post = pip_ratio_post;
1561}
1562
1567
1569{
1570 this->smallest_x = 0; // Sum of minimal size of all children.
1571 this->smallest_y = 0; // Biggest child.
1572 this->fill_x = 0; // smallest non-zero child widget fill step.
1573 this->fill_y = 1; // smallest common child fill step.
1574 this->resize_x = 0; // smallest non-zero child widget resize step.
1575 this->resize_y = 1; // smallest common child resize step.
1576 this->gaps = 0;
1577
1578 /* 1a. Forward call, collect longest/widest child length. */
1579 uint longest = 0; // Longest child found.
1580 uint max_vert_fill = 0; // Biggest vertical fill step.
1581 for (const auto &child_wid : this->children) {
1582 child_wid->SetupSmallestSize(w);
1583 longest = std::max(longest, child_wid->smallest_x);
1584 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(ST_SMALLEST));
1585 this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1586 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->gaps++;
1587 }
1588 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1589 /* 1b. Make the container higher if needed to accommodate all children nicely. */
1590 [[maybe_unused]] uint max_smallest = this->smallest_y + 3 * max_vert_fill; // Upper limit to computing smallest height.
1591 uint cur_height = this->smallest_y;
1592 for (;;) {
1593 for (const auto &child_wid : this->children) {
1594 uint step_size = child_wid->GetVerticalStepSize(ST_SMALLEST);
1595 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1596 if (step_size > 1 && child_height < cur_height) { // Small step sizes or already fitting children are not interesting.
1597 uint remainder = (cur_height - child_height) % step_size;
1598 if (remainder > 0) { // Child did not fit entirely, widen the container.
1599 cur_height += step_size - remainder;
1600 assert(cur_height < max_smallest); // Safeguard against infinite height expansion.
1601 /* Remaining children will adapt to the new cur_height, thus speeding up the computation. */
1602 }
1603 }
1604 }
1605 if (this->smallest_y == cur_height) break;
1606 this->smallest_y = cur_height; // Smallest height got changed, try again.
1607 }
1608 /* 2. For containers that must maintain equal width, extend child minimal size. */
1609 for (const auto &child_wid : this->children) {
1610 child_wid->smallest_y = this->smallest_y - child_wid->padding.Vertical();
1611 child_wid->ApplyAspectRatio();
1612 longest = std::max(longest, child_wid->smallest_x);
1613 }
1615 for (const auto &child_wid : this->children) {
1616 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1617 }
1618 }
1619 /* 3. Compute smallest, fill, and resize values of the container. */
1620 for (const auto &child_wid : this->children) {
1621 this->smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1622 if (child_wid->fill_x > 0) {
1623 if (this->fill_x == 0 || this->fill_x > child_wid->fill_x) this->fill_x = child_wid->fill_x;
1624 }
1625 this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1626
1627 if (child_wid->resize_x > 0) {
1628 if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x;
1629 }
1630 this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1631 }
1632 if (this->fill_x == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_x = 1;
1633 /* 4. Increase by required PIP space. */
1634 this->smallest_x += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1635}
1636
1637void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1638{
1639 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1640
1641 /* Compute additional width given to us. */
1642 uint additional_length = given_width - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1643 for (const auto &child_wid : this->children) {
1644 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1645 }
1646
1647 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1648
1649 /* In principle, the additional horizontal space is distributed evenly over the available resizable children. Due to step sizes, this may not always be feasible.
1650 * To make resizing work as good as possible, first children with biggest step sizes are done. These may get less due to rounding down.
1651 * 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
1652 * of the child with the smallest non-zero stepsize.
1653 *
1654 * 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
1655 * size and position, and directly call child->AssignSizePosition() with the computed values.
1656 * 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
1657 * then we call the child.
1658 */
1659
1660 /* First loop: Find biggest stepsize, find number of children that want a piece of the pie, handle vertical size for all children,
1661 * handle horizontal size for non-resizing children.
1662 */
1663 int num_changing_childs = 0; // Number of children that can change size.
1664 uint biggest_stepsize = 0;
1665 for (const auto &child_wid : this->children) {
1666 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1667 if (hor_step > 0) {
1668 if (!flags.Test(NWidContainerFlag::BigFirst)) num_changing_childs++;
1669 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1670 } else {
1671 child_wid->current_x = child_wid->smallest_x;
1672 }
1673
1674 uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1675 child_wid->current_y = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1676 }
1677
1678 /* First.5 loop: count how many children are of the biggest step size. */
1679 if (flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1680 for (const auto &child_wid : this->children) {
1681 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1682 if (hor_step == biggest_stepsize) {
1683 num_changing_childs++;
1684 }
1685 }
1686 }
1687
1688 /* Second loop: Allocate the additional horizontal space over the resizing children, starting with the biggest resize steps. */
1689 while (biggest_stepsize > 0) {
1690 uint next_biggest_stepsize = 0;
1691 for (const auto &child_wid : this->children) {
1692 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1693 if (hor_step > biggest_stepsize) continue; // Already done
1694 if (hor_step == biggest_stepsize) {
1695 uint increment = additional_length / num_changing_childs;
1696 num_changing_childs--;
1697 if (hor_step > 1) increment -= increment % hor_step;
1698 child_wid->current_x = child_wid->smallest_x + increment;
1699 additional_length -= increment;
1700 continue;
1701 }
1702 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1703 }
1704 biggest_stepsize = next_biggest_stepsize;
1705
1706 if (num_changing_childs == 0 && flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1707 /* Second.5 loop: count how many children are of the updated biggest step size. */
1708 for (const auto &child_wid : this->children) {
1709 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1710 if (hor_step == biggest_stepsize) {
1711 num_changing_childs++;
1712 }
1713 }
1714 }
1715 }
1716 assert(num_changing_childs == 0);
1717
1718 uint pre = this->pip_pre;
1719 uint inter = this->pip_inter;
1720
1721 if (additional_length > 0) {
1722 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1723 * which is never explicitly needed. */
1724 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1725 if (r > 0) {
1726 pre += this->pip_ratio_pre * additional_length / r;
1727 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1728 }
1729 }
1730
1731 /* Third loop: Compute position and call the child. */
1732 uint position = rtl ? this->current_x - pre : pre; // Place to put next child relative to origin of the container.
1733 for (const auto &child_wid : this->children) {
1734 uint child_width = child_wid->current_x;
1735 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1736 uint child_y = y + child_wid->padding.top;
1737
1738 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1739 if (child_wid->current_x != 0) {
1740 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1741 position = rtl ? position - padded_child_width : position + padded_child_width;
1742 }
1743 }
1744}
1745
1751
1752void NWidgetHorizontalLTR::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1753{
1754 NWidgetHorizontal::AssignSizePosition(sizing, x, y, given_width, given_height, false);
1755}
1756
1761
1763{
1764 this->smallest_x = 0; // Biggest child.
1765 this->smallest_y = 0; // Sum of minimal size of all children.
1766 this->fill_x = 1; // smallest common child fill step.
1767 this->fill_y = 0; // smallest non-zero child widget fill step.
1768 this->resize_x = 1; // smallest common child resize step.
1769 this->resize_y = 0; // smallest non-zero child widget resize step.
1770 this->gaps = 0;
1771
1772 /* 1a. Forward call, collect longest/widest child length. */
1773 uint highest = 0; // Highest child found.
1774 uint max_hor_fill = 0; // Biggest horizontal fill step.
1775 for (const auto &child_wid : this->children) {
1776 child_wid->SetupSmallestSize(w);
1777 highest = std::max(highest, child_wid->smallest_y);
1778 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(ST_SMALLEST));
1779 this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1780 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->gaps++;
1781 }
1782 if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1783 /* 1b. Make the container wider if needed to accommodate all children nicely. */
1784 [[maybe_unused]] uint max_smallest = this->smallest_x + 3 * max_hor_fill; // Upper limit to computing smallest height.
1785 uint cur_width = this->smallest_x;
1786 for (;;) {
1787 for (const auto &child_wid : this->children) {
1788 uint step_size = child_wid->GetHorizontalStepSize(ST_SMALLEST);
1789 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1790 if (step_size > 1 && child_width < cur_width) { // Small step sizes or already fitting children are not interesting.
1791 uint remainder = (cur_width - child_width) % step_size;
1792 if (remainder > 0) { // Child did not fit entirely, widen the container.
1793 cur_width += step_size - remainder;
1794 assert(cur_width < max_smallest); // Safeguard against infinite width expansion.
1795 /* Remaining children will adapt to the new cur_width, thus speeding up the computation. */
1796 }
1797 }
1798 }
1799 if (this->smallest_x == cur_width) break;
1800 this->smallest_x = cur_width; // Smallest width got changed, try again.
1801 }
1802 /* 2. For containers that must maintain equal width, extend children minimal size. */
1803 for (const auto &child_wid : this->children) {
1804 child_wid->smallest_x = this->smallest_x - child_wid->padding.Horizontal();
1805 child_wid->ApplyAspectRatio();
1806 highest = std::max(highest, child_wid->smallest_y);
1807 }
1809 for (const auto &child_wid : this->children) {
1810 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1811 }
1812 }
1813 /* 3. Compute smallest, fill, and resize values of the container. */
1814 for (const auto &child_wid : this->children) {
1815 this->smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1816 if (child_wid->fill_y > 0) {
1817 if (this->fill_y == 0 || this->fill_y > child_wid->fill_y) this->fill_y = child_wid->fill_y;
1818 }
1819 this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1820
1821 if (child_wid->resize_y > 0) {
1822 if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y;
1823 }
1824 this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1825 }
1826 if (this->fill_y == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_y = 1;
1827 /* 4. Increase by required PIP space. */
1828 this->smallest_y += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1829}
1830
1831void NWidgetVertical::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1832{
1833 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1834
1835 /* Compute additional height given to us. */
1836 uint additional_length = given_height - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1837 for (const auto &child_wid : this->children) {
1838 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1839 }
1840
1841 this->StoreSizePosition(sizing, x, y, given_width, given_height);
1842
1843 /* Like the horizontal container, the vertical container also distributes additional height evenly, starting with the children with the biggest resize steps.
1844 * It also stores computed widths and heights into current_x and current_y values of the child.
1845 */
1846
1847 /* 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. */
1848 int num_changing_childs = 0; // Number of children that can change size.
1849 uint biggest_stepsize = 0;
1850 for (const auto &child_wid : this->children) {
1851 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1852 if (vert_step > 0) {
1853 if (!flags.Test(NWidContainerFlag::BigFirst)) num_changing_childs++;
1854 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1855 } else {
1856 child_wid->current_y = child_wid->smallest_y;
1857 }
1858
1859 uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1860 child_wid->current_x = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1861 }
1862
1863 /* First.5 loop: count how many children are of the biggest step size. */
1864 if (this->flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1865 for (const auto &child_wid : this->children) {
1866 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1867 if (vert_step == biggest_stepsize) {
1868 num_changing_childs++;
1869 }
1870 }
1871 }
1872
1873 /* Second loop: Allocate the additional vertical space over the resizing children, starting with the biggest resize steps. */
1874 while (biggest_stepsize > 0) {
1875 uint next_biggest_stepsize = 0;
1876 for (const auto &child_wid : this->children) {
1877 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1878 if (vert_step > biggest_stepsize) continue; // Already done
1879 if (vert_step == biggest_stepsize) {
1880 uint increment = additional_length / num_changing_childs;
1881 num_changing_childs--;
1882 if (vert_step > 1) increment -= increment % vert_step;
1883 child_wid->current_y = child_wid->smallest_y + increment;
1884 additional_length -= increment;
1885 continue;
1886 }
1887 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1888 }
1889 biggest_stepsize = next_biggest_stepsize;
1890
1891 if (num_changing_childs == 0 && flags.Test(NWidContainerFlag::BigFirst) && biggest_stepsize > 0) {
1892 /* Second.5 loop: count how many children are of the updated biggest step size. */
1893 for (const auto &child_wid : this->children) {
1894 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1895 if (vert_step == biggest_stepsize) {
1896 num_changing_childs++;
1897 }
1898 }
1899 }
1900 }
1901 assert(num_changing_childs == 0);
1902
1903 uint pre = this->pip_pre;
1904 uint inter = this->pip_inter;
1905
1906 if (additional_length > 0) {
1907 /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1908 * which is never explicitly needed. */
1909 int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1910 if (r > 0) {
1911 pre += this->pip_ratio_pre * additional_length / r;
1912 if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1913 }
1914 }
1915
1916 /* Third loop: Compute position and call the child. */
1917 uint position = pre; // Place to put next child relative to origin of the container.
1918 for (const auto &child_wid : this->children) {
1919 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1920 uint child_height = child_wid->current_y;
1921
1922 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1923 if (child_wid->current_y != 0) {
1924 position += child_height + child_wid->padding.Vertical() + inter;
1925 }
1926 }
1927}
1928
1935{
1936 this->SetMinimalSize(width, height);
1937 this->SetResize(0, 0);
1938}
1939
1941{
1942 this->smallest_x = this->min_x;
1943 this->smallest_y = this->min_y;
1944 this->ApplyAspectRatio();
1945}
1946
1950
1952{
1953 /* Spacer widget is never normally visible. */
1954
1955 if (_draw_widget_outlines && this->current_x != 0 && this->current_y != 0) {
1956 /* Spacers indicate a potential design issue, so get extra highlighting. */
1957 GfxFillRect(this->GetCurrentRect(), PC_WHITE, FILLRECT_CHECKER);
1958
1959 DrawOutline(w, this);
1960 }
1961}
1962
1964{
1965 /* Spacer widget never need repainting. */
1966}
1967
1969{
1970 return nullptr;
1971}
1972
1973NWidgetMatrix::NWidgetMatrix(Colours colour, WidgetID index) : NWidgetPIPContainer(NWID_MATRIX, NWidContainerFlag::EqualSize), index(index)
1974{
1975 this->colour = colour;
1976}
1977
1983{
1984 this->clicked = clicked;
1985 if (this->clicked >= 0 && this->sb != nullptr && this->widgets_x != 0) {
1986 int vpos = (this->clicked / this->widgets_x) * this->widget_h; // Vertical position of the top.
1987 /* Need to scroll down -> Scroll to the bottom.
1988 * However, last entry has no 'this->pip_inter' underneath, and we must stay below this->sb->GetCount() */
1989 if (this->sb->GetPosition() < vpos) vpos += this->widget_h - this->pip_inter - 1;
1990 this->sb->ScrollTowards(vpos);
1991 }
1992}
1993
2000{
2001 this->count = count;
2002
2003 if (this->sb == nullptr || this->widgets_x == 0) return;
2004
2005 /* We need to get the number of pixels the matrix is high/wide.
2006 * So, determine the number of rows/columns based on the number of
2007 * columns/rows (one is constant/unscrollable).
2008 * Then multiply that by the height of a widget, and add the pre
2009 * and post spacing "offsets". */
2010 count = CeilDiv(count, this->sb->IsVertical() ? this->widgets_x : this->widgets_y);
2011 count *= (this->sb->IsVertical() ? this->children.front()->smallest_y : this->children.front()->smallest_x) + this->pip_inter;
2012 if (count > 0) count -= this->pip_inter; // We counted an inter too much in the multiplication above
2013 count += this->pip_pre + this->pip_post;
2014 this->sb->SetCount(count);
2015 this->sb->SetCapacity(this->sb->IsVertical() ? this->current_y : this->current_x);
2016 this->sb->SetStepSize(this->sb->IsVertical() ? this->widget_h : this->widget_w);
2017}
2018
2024{
2025 this->sb = sb;
2026}
2027
2033{
2034 return this->current_element;
2035}
2036
2038{
2039 assert(this->children.size() == 1);
2040
2041 this->children.front()->SetupSmallestSize(w);
2042
2043 Dimension padding = { (uint)this->pip_pre + this->pip_post, (uint)this->pip_pre + this->pip_post};
2044 Dimension size = {this->children.front()->smallest_x + padding.width, this->children.front()->smallest_y + padding.height};
2045 Dimension fill = {0, 0};
2046 Dimension resize = {this->pip_inter + this->children.front()->smallest_x, this->pip_inter + this->children.front()->smallest_y};
2047
2048 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
2049
2050 this->smallest_x = size.width;
2051 this->smallest_y = size.height;
2052 this->fill_x = fill.width;
2053 this->fill_y = fill.height;
2054 this->resize_x = resize.width;
2055 this->resize_y = resize.height;
2056 this->ApplyAspectRatio();
2057}
2058
2059void NWidgetMatrix::AssignSizePosition(SizingType, int x, int y, uint given_width, uint given_height, bool)
2060{
2061 assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
2062
2063 this->pos_x = x;
2064 this->pos_y = y;
2065 this->current_x = given_width;
2066 this->current_y = given_height;
2067
2068 /* Determine the size of the widgets, and the number of visible widgets on each of the axis. */
2069 this->widget_w = this->children.front()->smallest_x + this->pip_inter;
2070 this->widget_h = this->children.front()->smallest_y + this->pip_inter;
2071
2072 /* Account for the pip_inter is between widgets, so we need to account for that when
2073 * the division assumes pip_inter is used for all widgets. */
2074 this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
2075 this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
2076
2077 /* When resizing, update the scrollbar's count. E.g. with a vertical
2078 * scrollbar becoming wider or narrower means the amount of rows in
2079 * the scrollbar becomes respectively smaller or higher. */
2080 this->SetCount(this->count);
2081}
2082
2084{
2085 if (this->index >= 0) widget_lookup[this->index] = this;
2087}
2088
2090{
2091 /* Falls outside of the matrix widget. */
2092 if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
2093
2094 int start_x, start_y, base_offs_x, base_offs_y;
2095 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2096
2097 bool rtl = _current_text_dir == TD_RTL;
2098
2099 int widget_col = (rtl ?
2100 -x + (int)this->pip_post + this->pos_x + base_offs_x + this->widget_w - 1 - (int)this->pip_inter :
2101 x - (int)this->pip_pre - this->pos_x - base_offs_x
2102 ) / this->widget_w;
2103
2104 int widget_row = (y - base_offs_y - (int)this->pip_pre - this->pos_y) / this->widget_h;
2105
2106 this->current_element = (widget_row + start_y) * this->widgets_x + start_x + widget_col;
2107 if (this->current_element >= this->count) return nullptr;
2108
2109 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2110 assert(child != nullptr);
2112 this->pos_x + (rtl ? this->pip_post - widget_col * this->widget_w : this->pip_pre + widget_col * this->widget_w) + base_offs_x,
2113 this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
2114 child->smallest_x, child->smallest_y, rtl);
2115
2116 return child->GetWidgetFromPos(x, y);
2117}
2118
2119/* virtual */ void NWidgetMatrix::Draw(const Window *w)
2120{
2121 /* Fill the background. */
2122 GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, SHADE_LIGHT));
2123
2124 /* Set up a clipping area for the previews. */
2125 bool rtl = _current_text_dir == TD_RTL;
2126 DrawPixelInfo tmp_dpi;
2127 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;
2128
2129 {
2130 AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
2131
2132 /* Get the appropriate offsets so we can draw the right widgets. */
2133 NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
2134 assert(child != nullptr);
2135 int start_x, start_y, base_offs_x, base_offs_y;
2136 this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
2137
2138 int offs_y = base_offs_y;
2139 for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
2140 /* Are we within bounds? */
2141 if (offs_y + child->smallest_y <= 0) continue;
2142 if (offs_y >= (int)this->current_y) break;
2143
2144 /* We've passed our amount of widgets. */
2145 if (y * this->widgets_x >= this->count) break;
2146
2147 int offs_x = base_offs_x;
2148 for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
2149 /* Are we within bounds? */
2150 if (offs_x + child->smallest_x <= 0) continue;
2151 if (offs_x >= (int)this->current_x) continue;
2152
2153 /* Do we have this many widgets? */
2154 this->current_element = y * this->widgets_x + x;
2155 if (this->current_element >= this->count) break;
2156
2157 child->AssignSizePosition(ST_RESIZE, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
2158 child->SetLowered(this->clicked == this->current_element);
2159 child->Draw(w);
2160 }
2161 }
2162 }
2163
2164 DrawOutline(w, this);
2165}
2166
2174void NWidgetMatrix::GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
2175{
2176 base_offs_x = _current_text_dir == TD_RTL ? this->widget_w * (this->widgets_x - 1) : 0;
2177 base_offs_y = 0;
2178 start_x = 0;
2179 start_y = 0;
2180 if (this->sb != nullptr) {
2181 if (this->sb->IsVertical()) {
2182 start_y = this->sb->GetPosition() / this->widget_h;
2183 base_offs_y += -this->sb->GetPosition() + start_y * this->widget_h;
2184 } else {
2185 start_x = this->sb->GetPosition() / this->widget_w;
2186 int sub_x = this->sb->GetPosition() - start_x * this->widget_w;
2187 if (_current_text_dir == TD_RTL) {
2188 base_offs_x += sub_x;
2189 } else {
2190 base_offs_x -= sub_x;
2191 }
2192 }
2193 }
2194}
2195
2205NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr<NWidgetPIPContainer> &&child) : NWidgetCore(tp, colour, index, 1, 1, {}, STR_NULL)
2206{
2207 assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
2208 this->child = std::move(child);
2209 if (this->child != nullptr) this->child->parent = this;
2210 this->SetAlignment(SA_TOP | SA_LEFT);
2211}
2212
2220void NWidgetBackground::Add(std::unique_ptr<NWidgetBase> &&nwid)
2221{
2222 if (this->child == nullptr) {
2223 this->child = std::make_unique<NWidgetVertical>();
2224 }
2225 nwid->parent = this->child.get();
2226 this->child->Add(std::move(nwid));
2227}
2228
2239void NWidgetBackground::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
2240{
2241 if (this->child == nullptr) {
2242 this->child = std::make_unique<NWidgetVertical>();
2243 }
2244 this->child->parent = this;
2245 this->child->SetPIP(pip_pre, pip_inter, pip_post);
2246}
2247
2258void NWidgetBackground::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
2259{
2260 if (this->child == nullptr) {
2261 this->child = std::make_unique<NWidgetVertical>();
2262 }
2263 this->child->parent = this;
2264 this->child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2265}
2266
2267void NWidgetBackground::AdjustPaddingForZoom()
2268{
2269 if (child != nullptr) child->AdjustPaddingForZoom();
2270 NWidgetCore::AdjustPaddingForZoom();
2271}
2272
2274{
2275 if (this->child != nullptr) {
2276 this->child->SetupSmallestSize(w);
2277
2278 this->smallest_x = this->child->smallest_x;
2279 this->smallest_y = this->child->smallest_y;
2280 this->fill_x = this->child->fill_x;
2281 this->fill_y = this->child->fill_y;
2282 this->resize_x = this->child->resize_x;
2283 this->resize_y = this->child->resize_y;
2284
2285 /* Don't apply automatic padding if there is no child widget. */
2286 if (w == nullptr) return;
2287
2288 if (this->type == WWT_FRAME) {
2289 std::string text = GetStringForWidget(w, this);
2290 Dimension text_size = text.empty() ? Dimension{0, 0} : GetStringBoundingBox(text, this->text_size);
2291
2292 /* Account for the size of the frame's text if that exists */
2293 this->child->padding = WidgetDimensions::scaled.frametext;
2294 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);
2295
2296 this->smallest_x += this->child->padding.Horizontal();
2297 this->smallest_y += this->child->padding.Vertical();
2298
2299 this->smallest_x = std::max(this->smallest_x, text_size.width + WidgetDimensions::scaled.frametext.Horizontal());
2300 } else if (this->type == WWT_INSET) {
2301 /* Apply automatic padding for bevel thickness. */
2302 this->child->padding = WidgetDimensions::scaled.bevel;
2303
2304 this->smallest_x += this->child->padding.Horizontal();
2305 this->smallest_y += this->child->padding.Vertical();
2306 }
2307 this->ApplyAspectRatio();
2308 } else {
2309 Dimension d = {this->min_x, this->min_y};
2310 Dimension fill = {this->fill_x, this->fill_y};
2311 Dimension resize = {this->resize_x, this->resize_y};
2312 if (w != nullptr) { // A non-nullptr window pointer acts as switch to turn dynamic widget size on.
2313 if (this->type == WWT_FRAME || this->type == WWT_INSET) {
2314 std::string text = GetStringForWidget(w, this);
2315 Dimension background = GetStringBoundingBox(text, this->text_size);
2316 background.width += (this->type == WWT_FRAME) ? (WidgetDimensions::scaled.frametext.Horizontal()) : (WidgetDimensions::scaled.inset.Horizontal());
2317 d = maxdim(d, background);
2318 }
2319 if (this->index >= 0) {
2321 switch (this->type) {
2322 default: NOT_REACHED();
2326 }
2327 w->UpdateWidgetSize(this->index, d, padding, fill, resize);
2328 }
2329 }
2330 this->smallest_x = d.width;
2331 this->smallest_y = d.height;
2332 this->fill_x = fill.width;
2333 this->fill_y = fill.height;
2334 this->resize_x = resize.width;
2335 this->resize_y = resize.height;
2336 this->ApplyAspectRatio();
2337 }
2338}
2339
2340void NWidgetBackground::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
2341{
2342 this->StoreSizePosition(sizing, x, y, given_width, given_height);
2343
2344 if (this->child != nullptr) {
2345 uint x_offset = (rtl ? this->child->padding.right : this->child->padding.left);
2346 uint width = given_width - this->child->padding.Horizontal();
2347 uint height = given_height - this->child->padding.Vertical();
2348 this->child->AssignSizePosition(sizing, x + x_offset, y + this->child->padding.top, width, height, rtl);
2349 }
2350}
2351
2353{
2354 if (this->index >= 0) widget_lookup[this->index] = this;
2355 if (this->child != nullptr) this->child->FillWidgetLookup(widget_lookup);
2356}
2357
2359{
2360 if (this->current_x == 0 || this->current_y == 0) return;
2361
2362 Rect r = this->GetCurrentRect();
2363
2364 const DrawPixelInfo *dpi = _cur_dpi;
2365 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2366
2367 switch (this->type) {
2368 case WWT_PANEL:
2369 DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, this->IsLowered() ? FrameFlag::Lowered : FrameFlags{});
2370 break;
2371
2372 case WWT_FRAME:
2373 DrawFrame(r, this->colour, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
2374 break;
2375
2376 case WWT_INSET:
2377 DrawInset(r, this->colour, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
2378 break;
2379
2380 default:
2381 NOT_REACHED();
2382 }
2383
2384 if (this->index >= 0) w->DrawWidget(r, this->index);
2385 if (this->child != nullptr) this->child->Draw(w);
2386
2387 if (this->IsDisabled()) {
2389 }
2390
2391 DrawOutline(w, this);
2392}
2393
2395{
2396 NWidgetCore *nwid = nullptr;
2397 if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) {
2398 if (this->child != nullptr) nwid = this->child->GetWidgetFromPos(x, y);
2399 if (nwid == nullptr) nwid = this;
2400 }
2401 return nwid;
2402}
2403
2405{
2406 NWidgetBase *nwid = nullptr;
2407 if (this->child != nullptr) nwid = this->child->GetWidgetOfType(tp);
2408 if (nwid == nullptr && this->type == tp) nwid = this;
2409 return nwid;
2410}
2411
2412NWidgetViewport::NWidgetViewport(WidgetID index) : NWidgetCore(NWID_VIEWPORT, INVALID_COLOUR, index, 1, 1, {}, STR_NULL)
2413{
2414}
2415
2417{
2418 this->smallest_x = this->min_x;
2419 this->smallest_y = this->min_y;
2420 this->ApplyAspectRatio();
2421}
2422
2424{
2425 if (this->current_x == 0 || this->current_y == 0) return;
2426
2429 _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_TEXT); // Disable all transparency, except textual stuff
2430 w->DrawViewport();
2431 _transparency_opt = to_backup;
2432 } else {
2433 w->DrawViewport();
2434 }
2435
2436 /* Optionally shade the viewport. */
2437 if (this->disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2439 }
2440
2441 DrawOutline(w, this);
2442}
2443
2450void NWidgetViewport::InitializeViewport(Window *w, std::variant<TileIndex, VehicleID> focus, ZoomLevel zoom)
2451{
2452 InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, focus, zoom);
2453}
2454
2460{
2461 if (w->viewport == nullptr) return;
2462
2463 Viewport &vp = *w->viewport;
2464 vp.left = w->left + this->pos_x;
2465 vp.top = w->top + this->pos_y;
2466 vp.width = this->current_x;
2467 vp.height = this->current_y;
2468
2469 vp.virtual_width = ScaleByZoom(vp.width, vp.zoom);
2471}
2472
2482Scrollbar::size_type Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, WidgetID widget, int padding, int line_height) const
2483{
2484 int pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
2485 if (pos != INT_MAX) pos += this->GetPosition();
2486 return (pos < 0 || pos >= this->GetCount()) ? Scrollbar::npos : pos;
2487}
2488
2503EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
2504{
2505 int new_pos = list_position;
2506 switch (keycode) {
2507 case WKC_UP:
2508 /* scroll up by one */
2509 new_pos--;
2510 break;
2511
2512 case WKC_DOWN:
2513 /* scroll down by one */
2514 new_pos++;
2515 break;
2516
2517 case WKC_PAGEUP:
2518 /* scroll up a page */
2519 new_pos -= this->GetCapacity();
2520 break;
2521
2522 case WKC_PAGEDOWN:
2523 /* scroll down a page */
2524 new_pos += this->GetCapacity();
2525 break;
2526
2527 case WKC_HOME:
2528 /* jump to beginning */
2529 new_pos = 0;
2530 break;
2531
2532 case WKC_END:
2533 /* jump to end */
2534 new_pos = this->GetCount() - 1;
2535 break;
2536
2537 default:
2538 return ES_NOT_HANDLED;
2539 }
2540
2541 /* If there are no elements, there is nothing to scroll/update. */
2542 if (this->GetCount() != 0) {
2543 list_position = Clamp(new_pos, 0, this->GetCount() - 1);
2544 }
2545 return ES_HANDLED;
2546}
2547
2548
2557{
2558 NWidgetBase *nwid = w->GetWidget<NWidgetBase>(widget);
2559 if (this->IsVertical()) {
2560 this->SetCapacity(((int)nwid->current_y - padding) / (int)nwid->resize_y);
2561 } else {
2562 this->SetCapacity(((int)nwid->current_x - padding) / (int)nwid->resize_x);
2563 }
2564}
2565
2573Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
2574{
2575 const int count = sb.GetCount() * resize_step;
2576 const int position = sb.GetPosition() * resize_step;
2577
2578 if (sb.IsVertical()) {
2579 r.top -= position;
2580 r.bottom = r.top + count;
2581 } else {
2582 bool rtl = _current_text_dir == TD_RTL;
2583 if (rtl) {
2584 r.right += position;
2585 r.left = r.right - count;
2586 } else {
2587 r.left -= position;
2588 r.right = r.left + count;
2589 }
2590 }
2591
2592 return r;
2593}
2594
2601NWidgetScrollbar::NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index) : NWidgetCore(tp, colour, index, 1, 1, {}, STR_NULL), Scrollbar(tp != NWID_HSCROLLBAR)
2602{
2603 assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR);
2604
2605 switch (this->type) {
2606 case NWID_HSCROLLBAR:
2607 this->SetResize(1, 0);
2608 this->SetFill(1, 0);
2609 this->SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2610 break;
2611
2612 case NWID_VSCROLLBAR:
2613 this->SetResize(0, 1);
2614 this->SetFill(0, 1);
2615 this->SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2616 break;
2617
2618 default: NOT_REACHED();
2619 }
2620}
2621
2623{
2624 this->min_x = 0;
2625 this->min_y = 0;
2626
2627 switch (this->type) {
2628 case NWID_HSCROLLBAR:
2629 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2630 break;
2631
2632 case NWID_VSCROLLBAR:
2633 this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2634 break;
2635
2636 default: NOT_REACHED();
2637 }
2638
2639 this->smallest_x = this->min_x;
2640 this->smallest_y = this->min_y;
2641}
2642
2644{
2645 if (this->current_x == 0 || this->current_y == 0) return;
2646
2647 Rect r = this->GetCurrentRect();
2648
2649 const DrawPixelInfo *dpi = _cur_dpi;
2650 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2651
2652 bool up_lowered = this->disp_flags.Test(NWidgetDisplayFlag::ScrollbarUp);
2653 bool down_lowered = this->disp_flags.Test(NWidgetDisplayFlag::ScrollbarDown);
2654 bool middle_lowered = !this->disp_flags.Any({NWidgetDisplayFlag::ScrollbarUp, NWidgetDisplayFlag::ScrollbarDown}) && w->mouse_capture_widget == this->index;
2655
2656 if (this->type == NWID_HSCROLLBAR) {
2657 DrawHorizontalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2658 } else {
2659 DrawVerticalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2660 }
2661
2662 if (this->IsDisabled()) {
2664 }
2665
2666 DrawOutline(w, this);
2667}
2668
2669/* static */ void NWidgetScrollbar::InvalidateDimensionCache()
2670{
2671 vertical_dimension.width = vertical_dimension.height = 0;
2672 horizontal_dimension.width = horizontal_dimension.height = 0;
2673}
2674
2675/* static */ Dimension NWidgetScrollbar::GetVerticalDimension()
2676{
2677 if (vertical_dimension.width == 0) {
2678 vertical_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_UP), GetScaledSpriteSize(SPR_ARROW_DOWN));
2681 }
2682 return vertical_dimension;
2683}
2684
2685/* static */ Dimension NWidgetScrollbar::GetHorizontalDimension()
2686{
2687 if (horizontal_dimension.width == 0) {
2688 horizontal_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2691 }
2692 return horizontal_dimension;
2693}
2694
2697
2700{
2701 shadebox_dimension.width = shadebox_dimension.height = 0;
2702 debugbox_dimension.width = debugbox_dimension.height = 0;
2703 defsizebox_dimension.width = defsizebox_dimension.height = 0;
2704 stickybox_dimension.width = stickybox_dimension.height = 0;
2705 resizebox_dimension.width = resizebox_dimension.height = 0;
2706 closebox_dimension.width = closebox_dimension.height = 0;
2707 dropdown_dimension.width = dropdown_dimension.height = 0;
2708}
2709
2717
2726NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, const WidgetData &data, StringID tip) : NWidgetCore(tp, colour, index, 1, 1, data, tip)
2727{
2728 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);
2729 this->min_x = 0;
2730 this->min_y = 0;
2731 this->SetResize(0, 0);
2732
2733 switch (tp) {
2734 case WWT_EMPTY:
2735 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_EMPTY should not have a colour");
2736 break;
2737
2738 case WWT_TEXT:
2739 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_TEXT should not have a colour");
2740 this->SetFill(0, 0);
2742 break;
2743
2744 case WWT_LABEL:
2745 if (colour != INVALID_COLOUR) [[unlikely]] throw std::runtime_error("WWT_LABEL should not have a colour");
2746 [[fallthrough]];
2747
2748 case WWT_PUSHBTN:
2749 case WWT_IMGBTN:
2750 case WWT_PUSHIMGBTN:
2751 case WWT_IMGBTN_2:
2752 case WWT_TEXTBTN:
2753 case WWT_PUSHTXTBTN:
2754 case WWT_TEXTBTN_2:
2755 case WWT_IMGTEXTBTN:
2756 case WWT_PUSHIMGTEXTBTN:
2757 case WWT_BOOLBTN:
2758 case WWT_MATRIX:
2760 case NWID_PUSHBUTTON_DROPDOWN:
2761 this->SetFill(0, 0);
2762 break;
2763
2764 case WWT_ARROWBTN:
2765 case WWT_PUSHARROWBTN:
2766 this->SetFill(0, 0);
2767 this->SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2768 break;
2769
2770 case WWT_EDITBOX:
2771 this->SetFill(0, 0);
2772 break;
2773
2774 case WWT_CAPTION:
2775 this->SetFill(1, 0);
2776 this->SetResize(1, 0);
2778 this->SetMinimalTextLines(1, WidgetDimensions::unscaled.captiontext.Vertical(), FS_NORMAL);
2779 this->SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2780 break;
2781
2782 case WWT_STICKYBOX:
2783 this->SetFill(0, 0);
2785 this->SetToolTip(STR_TOOLTIP_STICKY);
2786 this->SetAspect(this->min_x, this->min_y);
2787 break;
2788
2789 case WWT_SHADEBOX:
2790 this->SetFill(0, 0);
2792 this->SetToolTip(STR_TOOLTIP_SHADE);
2793 this->SetAspect(this->min_x, this->min_y);
2794 break;
2795
2796 case WWT_DEBUGBOX:
2797 this->SetFill(0, 0);
2799 this->SetToolTip(STR_TOOLTIP_DEBUG);
2800 this->SetAspect(this->min_x, this->min_y);
2801 break;
2802
2803 case WWT_DEFSIZEBOX:
2804 this->SetFill(0, 0);
2806 this->SetToolTip(STR_TOOLTIP_DEFSIZE);
2807 this->SetAspect(this->min_x, this->min_y);
2808 break;
2809
2810 case WWT_RESIZEBOX:
2811 this->SetFill(0, 0);
2814 this->SetToolTip(STR_TOOLTIP_RESIZE);
2815 break;
2816
2817 case WWT_CLOSEBOX:
2818 this->SetFill(0, 0);
2820 this->SetToolTip(STR_TOOLTIP_CLOSE_WINDOW);
2821 this->SetAspect(this->min_x, this->min_y);
2822 break;
2823
2824 case WWT_DROPDOWN:
2825 this->SetFill(0, 0);
2827 this->SetAlignment(SA_TOP | SA_LEFT);
2828 break;
2829
2830 default:
2831 NOT_REACHED();
2832 }
2833}
2834
2836{
2837 Dimension padding = {0, 0};
2838 Dimension size = {this->min_x, this->min_y};
2839 Dimension fill = {this->fill_x, this->fill_y};
2840 Dimension resize = {this->resize_x, this->resize_y};
2841 switch (this->type) {
2842 case WWT_EMPTY: {
2843 break;
2844 }
2845 case WWT_MATRIX: {
2847 break;
2848 }
2849 case WWT_SHADEBOX: {
2851 if (NWidgetLeaf::shadebox_dimension.width == 0) {
2852 NWidgetLeaf::shadebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_SHADE), GetScaledSpriteSize(SPR_WINDOW_UNSHADE));
2855 }
2857 break;
2858 }
2859 case WWT_DEBUGBOX:
2862 if (NWidgetLeaf::debugbox_dimension.width == 0) {
2866 }
2868 } else {
2869 /* If the setting is disabled we don't want to see it! */
2870 size.width = 0;
2871 fill.width = 0;
2872 resize.width = 0;
2873 }
2874 break;
2875
2876 case WWT_STICKYBOX: {
2878 if (NWidgetLeaf::stickybox_dimension.width == 0) {
2882 }
2884 break;
2885 }
2886
2887 case WWT_DEFSIZEBOX: {
2889 if (NWidgetLeaf::defsizebox_dimension.width == 0) {
2893 }
2895 break;
2896 }
2897
2898 case WWT_RESIZEBOX: {
2900 if (NWidgetLeaf::resizebox_dimension.width == 0) {
2901 NWidgetLeaf::resizebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_RESIZE_LEFT), GetScaledSpriteSize(SPR_WINDOW_RESIZE_RIGHT));
2904 }
2906 break;
2907 }
2908 case WWT_EDITBOX: {
2909 Dimension sprite_size = GetScaledSpriteSize(_current_text_dir == TD_RTL ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
2910 size.width = std::max(size.width, ScaleGUITrad(30) + sprite_size.width);
2911 size.height = std::max(sprite_size.height, GetStringBoundingBox("_").height + WidgetDimensions::scaled.framerect.Vertical());
2912 }
2913 [[fallthrough]];
2914 case WWT_PUSHBTN: {
2916 break;
2917 }
2918
2919 case WWT_BOOLBTN:
2920 size.width = SETTING_BUTTON_WIDTH;
2921 size.height = SETTING_BUTTON_HEIGHT;
2922 break;
2923
2924 case WWT_IMGBTN:
2925 case WWT_IMGBTN_2:
2926 case WWT_PUSHIMGBTN: {
2928 Dimension d2 = GetScaledSpriteSize(this->widget_data.sprite);
2929 if (this->type == WWT_IMGBTN_2) d2 = maxdim(d2, GetScaledSpriteSize(this->widget_data.sprite + 1));
2930 d2.width += padding.width;
2931 d2.height += padding.height;
2932 size = maxdim(size, d2);
2933 break;
2934 }
2935
2936 case WWT_IMGTEXTBTN:
2937 case WWT_PUSHIMGTEXTBTN: {
2939 Dimension di = GetScaledSpriteSize(this->widget_data.sprite);
2940 Dimension dt = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2941 Dimension d2{
2942 padding.width + di.width + WidgetDimensions::scaled.hsep_wide + dt.width,
2943 padding.height + std::max(di.height, dt.height)
2944 };
2945 size = maxdim(size, d2);
2946 break;
2947 }
2948
2949 case WWT_ARROWBTN:
2950 case WWT_PUSHARROWBTN: {
2952 Dimension d2 = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2953 d2.width += padding.width;
2954 d2.height += padding.height;
2955 size = maxdim(size, d2);
2956 break;
2957 }
2958
2959 case WWT_CLOSEBOX: {
2961 if (NWidgetLeaf::closebox_dimension.width == 0) {
2965 }
2967 break;
2968 }
2969 case WWT_TEXTBTN:
2970 case WWT_PUSHTXTBTN:
2971 case WWT_TEXTBTN_2: {
2973 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2974 d2.width += padding.width;
2975 d2.height += padding.height;
2976 size = maxdim(size, d2);
2977 break;
2978 }
2979 case WWT_LABEL:
2980 case WWT_TEXT: {
2981 size = maxdim(size, GetStringBoundingBox(GetStringForWidget(w, this), this->text_size));
2982 break;
2983 }
2984 case WWT_CAPTION: {
2986 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
2987 d2.width += padding.width;
2988 d2.height += padding.height;
2989 size = maxdim(size, d2);
2990 break;
2991 }
2992 case WWT_DROPDOWN:
2994 case NWID_PUSHBUTTON_DROPDOWN: {
2995 if (NWidgetLeaf::dropdown_dimension.width == 0) {
2999 }
3001 Dimension d2 = GetStringBoundingBox(GetStringForWidget(w, this), this->text_size);
3002 d2.width += padding.width;
3003 d2.height = std::max(d2.height + padding.height, NWidgetLeaf::dropdown_dimension.height);
3004 size = maxdim(size, d2);
3005 break;
3006 }
3007 default:
3008 NOT_REACHED();
3009 }
3010
3011 if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
3012
3013 this->smallest_x = size.width;
3014 this->smallest_y = size.height;
3015 this->fill_x = fill.width;
3016 this->fill_y = fill.height;
3017 this->resize_x = resize.width;
3018 this->resize_y = resize.height;
3019 this->ApplyAspectRatio();
3020}
3021
3023{
3024 if (this->current_x == 0 || this->current_y == 0) return;
3025
3026 /* Setup a clipping rectangle... for WWT_EMPTY or WWT_TEXT, an extra scaled pixel is allowed in case text shadow encroaches. */
3027 int extra = (this->type == WWT_EMPTY || this->type == WWT_TEXT) ? ScaleGUITrad(1) : 0;
3028 DrawPixelInfo new_dpi;
3029 if (!FillDrawPixelInfo(&new_dpi, this->pos_x, this->pos_y, this->current_x + extra, this->current_y + extra)) return;
3030 /* ...but keep coordinates relative to the window. */
3031 new_dpi.left += this->pos_x;
3032 new_dpi.top += this->pos_y;
3033
3034 AutoRestoreBackup dpi_backup(_cur_dpi, &new_dpi);
3035
3036 Rect r = this->GetCurrentRect();
3037
3038 bool clicked = this->IsLowered();
3039 switch (this->type) {
3040 case WWT_EMPTY:
3041 /* WWT_EMPTY used as a spacer indicates a potential design issue. */
3042 if (this->index == -1 && _draw_widget_outlines) {
3044 }
3045 break;
3046
3047 case WWT_PUSHBTN:
3048 DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FrameFlag::Lowered : FrameFlags{});
3049 break;
3050
3051 case WWT_BOOLBTN: {
3053 Colours button_colour = this->widget_data.alternate_colour;
3054 if (button_colour == INVALID_COLOUR) button_colour = this->colour;
3055 DrawBoolButton(pt.x, pt.y, button_colour, this->colour, clicked, !this->IsDisabled());
3056 break;
3057 }
3058
3059 case WWT_IMGBTN:
3060 case WWT_PUSHIMGBTN:
3061 case WWT_IMGBTN_2:
3062 DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data.sprite, this->align);
3063 break;
3064
3065 case WWT_TEXTBTN:
3066 case WWT_PUSHTXTBTN:
3067 case WWT_TEXTBTN_2:
3068 DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FrameFlag::Lowered : FrameFlags{});
3069 DrawLabel(r, this->text_colour, GetStringForWidget(w, this, (type & WWT_MASK) == WWT_TEXTBTN_2 && clicked), this->align, this->text_size);
3070 break;
3071
3072 case WWT_IMGTEXTBTN:
3073 case WWT_PUSHIMGTEXTBTN:
3074 DrawImageTextButtons(r, this->colour, clicked, this->widget_data.sprite, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3075 break;
3076
3077 case WWT_ARROWBTN:
3078 case WWT_PUSHARROWBTN: {
3079 SpriteID sprite;
3080 switch (this->widget_data.arrow_widget_type) {
3081 case AWV_DECREASE: sprite = _current_text_dir != TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
3082 case AWV_INCREASE: sprite = _current_text_dir == TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
3083 case AWV_LEFT: sprite = SPR_ARROW_LEFT; break;
3084 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break;
3085 default: NOT_REACHED();
3086 }
3087 DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align);
3088 break;
3089 }
3090
3091 case WWT_LABEL:
3092 DrawLabel(r, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3093 break;
3094
3095 case WWT_TEXT:
3096 DrawText(r, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3097 break;
3098
3099 case WWT_MATRIX:
3100 DrawMatrix(r, this->colour, clicked, this->widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3101 break;
3102
3103 case WWT_EDITBOX: {
3104 const QueryString *query = w->GetQueryString(this->index);
3105 if (query != nullptr) query->DrawEditBox(w, this->index);
3106 break;
3107 }
3108
3109 case WWT_CAPTION:
3110 DrawCaption(r, this->colour, w->owner, this->text_colour, GetStringForWidget(w, this), this->align, this->text_size);
3111 break;
3112
3113 case WWT_SHADEBOX:
3114 DrawShadeBox(r, this->colour, w->IsShaded());
3115 break;
3116
3117 case WWT_DEBUGBOX:
3118 DrawDebugBox(r, this->colour, clicked);
3119 break;
3120
3121 case WWT_STICKYBOX:
3123 break;
3124
3125 case WWT_DEFSIZEBOX:
3126 DrawDefSizeBox(r, this->colour, clicked);
3127 break;
3128
3129 case WWT_RESIZEBOX:
3130 DrawResizeBox(r, this->colour, this->pos_x < (w->width / 2), w->flags.Test(WindowFlag::SizingLeft) || w->flags.Test(WindowFlag::SizingRight), this->widget_data.resize_widget_type == RWV_SHOW_BEVEL);
3131 break;
3132
3133 case WWT_CLOSEBOX:
3134 DrawCloseBox(r, this->colour);
3135 break;
3136
3137 case WWT_DROPDOWN:
3138 DrawButtonDropdown(r, this->colour, false, clicked, GetStringForWidget(w, this), this->align);
3139 break;
3140
3142 case NWID_PUSHBUTTON_DROPDOWN:
3143 DrawButtonDropdown(r, this->colour, clicked, this->disp_flags.Test(NWidgetDisplayFlag::DropdownActive), GetStringForWidget(w, this), this->align);
3144 break;
3145
3146 default:
3147 NOT_REACHED();
3148 }
3149 if (this->index >= 0) w->DrawWidget(r, this->index);
3150
3151 if (this->IsDisabled() && this->type != WWT_BOOLBTN) {
3152 /* WWT_BOOLBTN is excluded as it draws its own disabled state. */
3154 }
3155
3156 DrawOutline(w, this);
3157}
3158
3167{
3168 if (_current_text_dir == TD_LTR) {
3169 int button_width = this->pos_x + this->current_x - NWidgetLeaf::dropdown_dimension.width;
3170 return pt.x < button_width;
3171 } else {
3172 int button_left = this->pos_x + NWidgetLeaf::dropdown_dimension.width;
3173 return pt.x >= button_left;
3174 }
3175}
3176
3177/* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
3178
3185{
3186 return tp > WPT_ATTRIBUTE_BEGIN && tp < WPT_ATTRIBUTE_END;
3187}
3188
3196{
3197 switch (nwid.type) {
3198 case WPT_RESIZE: {
3199 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3200 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_RESIZE requires NWidgetResizeBase");
3201 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3202 nwrb->SetResize(nwid.u.xy.x, nwid.u.xy.y);
3203 break;
3204 }
3205
3206 case WPT_MINSIZE: {
3207 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3208 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINSIZE requires NWidgetResizeBase");
3209 assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3210 nwrb->SetMinimalSize(nwid.u.xy.x, nwid.u.xy.y);
3211 break;
3212 }
3213
3214 case WPT_MINTEXTLINES: {
3215 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3216 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINTEXTLINES requires NWidgetResizeBase");
3217 assert(nwid.u.text_lines.size >= FS_BEGIN && nwid.u.text_lines.size < FS_END);
3219 break;
3220 }
3221
3222 case WPT_TEXTSTYLE: {
3223 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3224 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_TEXTSTYLE requires NWidgetCore");
3225 nwc->SetTextStyle(nwid.u.text_style.colour, nwid.u.text_style.size);
3226 break;
3227 }
3228
3229 case WPT_ALIGNMENT: {
3230 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3231 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_ALIGNMENT requires NWidgetCore");
3232 nwc->SetAlignment(nwid.u.align.align);
3233 break;
3234 }
3235
3236 case WPT_FILL: {
3237 NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3238 if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_FILL requires NWidgetResizeBase");
3239 nwrb->SetFill(nwid.u.xy.x, nwid.u.xy.y);
3240 break;
3241 }
3242
3243 case WPT_DATATIP: {
3244 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3245 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_DATATIP requires NWidgetCore");
3246 nwc->widget_data = nwid.u.data_tip.data;
3247 nwc->SetToolTip(nwid.u.data_tip.tooltip);
3248 break;
3249 }
3250
3251 case WPT_PADDING:
3252 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_PADDING requires NWidgetBase");
3253 dest->SetPadding(nwid.u.padding);
3254 break;
3255
3256 case WPT_PIPSPACE: {
3257 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3258 if (nwc != nullptr) nwc->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3259
3260 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3261 if (nwb != nullptr) nwb->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3262
3263 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3264 break;
3265 }
3266
3267 case WPT_PIPRATIO: {
3268 NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3269 if (nwc != nullptr) nwc->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3270
3271 NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3272 if (nwb != nullptr) nwb->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3273
3274 if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3275 break;
3276 }
3277
3278 case WPT_SCROLLBAR: {
3279 NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3280 if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore");
3281 nwc->scrollbar_index = nwid.u.widget.index;
3282 break;
3283 }
3284
3285 case WPT_ASPECT: {
3286 if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_ASPECT requires NWidgetBase");
3287 dest->aspect_ratio = nwid.u.aspect.ratio;
3288 dest->aspect_flags = nwid.u.aspect.flags;
3289 break;
3290 }
3291
3292 default:
3293 NOT_REACHED();
3294 }
3295}
3296
3303static std::unique_ptr<NWidgetBase> MakeNWidget(const NWidgetPart &nwid)
3304{
3305 assert(!IsAttributeWidgetPartType(nwid.type));
3306 assert(nwid.type != WPT_ENDCONTAINER);
3307
3308 switch (nwid.type) {
3309 case NWID_SPACER: return std::make_unique<NWidgetSpacer>(0, 0);
3310
3311 case WWT_PANEL: [[fallthrough]];
3312 case WWT_INSET: [[fallthrough]];
3313 case WWT_FRAME: return std::make_unique<NWidgetBackground>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3314
3315 case NWID_HORIZONTAL: return std::make_unique<NWidgetHorizontal>(nwid.u.cont_flags);
3316 case NWID_HORIZONTAL_LTR: return std::make_unique<NWidgetHorizontalLTR>(nwid.u.cont_flags);
3317 case NWID_VERTICAL: return std::make_unique<NWidgetVertical>(nwid.u.cont_flags);
3318 case NWID_SELECTION: return std::make_unique<NWidgetStacked>(nwid.u.widget.index);
3319 case NWID_MATRIX: return std::make_unique<NWidgetMatrix>(nwid.u.widget.colour, nwid.u.widget.index);
3320 case NWID_VIEWPORT: return std::make_unique<NWidgetViewport>(nwid.u.widget.index);
3321 case NWID_LAYER: return std::make_unique<NWidgetLayer>(nwid.u.widget.index);
3322
3323 case NWID_HSCROLLBAR: [[fallthrough]];
3324 case NWID_VSCROLLBAR: return std::make_unique<NWidgetScrollbar>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3325
3326 case WPT_FUNCTION: return nwid.u.func_ptr();
3327
3328 default:
3329 assert((nwid.type & WWT_MASK) < WWT_LAST || (nwid.type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
3330 return std::make_unique<NWidgetLeaf>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index, WidgetData{}, STR_NULL);
3331 }
3332}
3333
3347static std::span<const NWidgetPart>::iterator MakeNWidget(std::span<const NWidgetPart>::iterator nwid_begin, std::span<const NWidgetPart>::iterator nwid_end, std::unique_ptr<NWidgetBase> &dest, bool &fill_dest)
3348{
3349 dest = nullptr;
3350
3351 if (IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]] throw std::runtime_error("Expected non-attribute NWidgetPart type");
3352 if (nwid_begin->type == WPT_ENDCONTAINER) return nwid_begin;
3353
3354 fill_dest = IsContainerWidgetType(nwid_begin->type);
3355 dest = MakeNWidget(*nwid_begin);
3356 if (dest == nullptr) return nwid_begin;
3357
3358 ++nwid_begin;
3359
3360 /* Once a widget is created, we're now looking for attributes. */
3361 while (nwid_begin != nwid_end && IsAttributeWidgetPartType(nwid_begin->type)) {
3362 ApplyNWidgetPartAttribute(*nwid_begin, dest.get());
3363 ++nwid_begin;
3364 }
3365
3366 return nwid_begin;
3367}
3368
3375{
3376 return tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
3377 || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION || tp == NWID_LAYER;
3378}
3379
3387static std::span<const NWidgetPart>::iterator MakeWidgetTree(std::span<const NWidgetPart>::iterator nwid_begin, std::span<const NWidgetPart>::iterator nwid_end, std::unique_ptr<NWidgetBase> &parent)
3388{
3389 /* If *parent == nullptr, only the first widget is read and returned. Otherwise, *parent must point to either
3390 * a #NWidgetContainer or a #NWidgetBackground object, and parts are added as much as possible. */
3391 NWidgetContainer *nwid_cont = dynamic_cast<NWidgetContainer *>(parent.get());
3392 NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(parent.get());
3393 assert(parent == nullptr || (nwid_cont != nullptr && nwid_parent == nullptr) || (nwid_cont == nullptr && nwid_parent != nullptr));
3394
3395 while (nwid_begin != nwid_end) {
3396 std::unique_ptr<NWidgetBase> sub_widget = nullptr;
3397 bool fill_sub = false;
3398 nwid_begin = MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3399
3400 /* Break out of loop when end reached */
3401 if (sub_widget == nullptr) break;
3402
3403 /* If sub-widget is a container, recursively fill that container. */
3404 if (fill_sub && IsContainerWidgetType(sub_widget->type)) {
3405 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, sub_widget);
3406 }
3407
3408 /* Add sub_widget to parent container if available, otherwise return the widget to the caller. */
3409 if (nwid_cont != nullptr) nwid_cont->Add(std::move(sub_widget));
3410 if (nwid_parent != nullptr) nwid_parent->Add(std::move(sub_widget));
3411 if (nwid_cont == nullptr && nwid_parent == nullptr) {
3412 parent = std::move(sub_widget);
3413 return nwid_begin;
3414 }
3415 }
3416
3417 if (nwid_begin == nwid_end) return nwid_begin; // Reached the end of the array of parts?
3418
3419 assert(nwid_begin < nwid_end);
3420 assert(nwid_begin->type == WPT_ENDCONTAINER);
3421 return std::next(nwid_begin); // *nwid_begin is also 'used'
3422}
3423
3431std::unique_ptr<NWidgetBase> MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3432{
3433 if (container == nullptr) container = std::make_unique<NWidgetVertical>();
3434 [[maybe_unused]] auto nwid_part = MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3435#ifdef WITH_ASSERT
3436 if (nwid_part != std::end(nwid_parts)) [[unlikely]] throw std::runtime_error("Did not consume all NWidgetParts");
3437#endif
3438 return std::move(container);
3439}
3440
3450std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(std::span<const NWidgetPart> nwid_parts, NWidgetStacked **shade_select)
3451{
3452 auto nwid_begin = std::begin(nwid_parts);
3453 auto nwid_end = std::end(nwid_parts);
3454
3455 *shade_select = nullptr;
3456
3457 /* Read the first widget recursively from the array. */
3458 std::unique_ptr<NWidgetBase> nwid = nullptr;
3459 nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, nwid);
3460 assert(nwid != nullptr);
3461
3462 NWidgetHorizontal *hor_cont = dynamic_cast<NWidgetHorizontal *>(nwid.get());
3463
3464 auto root = std::make_unique<NWidgetVertical>();
3465 root->Add(std::move(nwid));
3466 if (nwid_begin == nwid_end) return root; // There is no body at all.
3467
3468 if (hor_cont != nullptr && hor_cont->GetWidgetOfType(WWT_CAPTION) != nullptr && hor_cont->GetWidgetOfType(WWT_SHADEBOX) != nullptr) {
3469 /* If the first widget has a title bar and a shade box, silently add a shade selection widget in the tree. */
3470 auto shade_stack = std::make_unique<NWidgetStacked>(-1);
3471 *shade_select = shade_stack.get();
3472 /* Load the remaining parts into the shade stack. */
3473 shade_stack->Add(MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3474 root->Add(std::move(shade_stack));
3475 return root;
3476 }
3477
3478 /* Load the remaining parts into 'root'. */
3479 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3480}
3481
3492std::unique_ptr<NWidgetBase> MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
3493{
3494 assert(max_length >= 1);
3495 std::unique_ptr<NWidgetVertical> vert = nullptr; // Storage for all rows.
3496 std::unique_ptr<NWidgetHorizontal> hor = nullptr; // Storage for buttons in one row.
3497 int hor_length = 0;
3498
3499 Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZoomLevel::Normal);
3500 sprite_size.width += WidgetDimensions::unscaled.matrix.Horizontal();
3501 sprite_size.height += WidgetDimensions::unscaled.matrix.Vertical();
3502
3503 for (WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3504 /* Ensure there is room in 'hor' for another button. */
3505 if (hor_length == max_length) {
3506 if (vert == nullptr) vert = std::make_unique<NWidgetVertical>();
3507 vert->Add(std::move(hor));
3508 hor = nullptr;
3509 hor_length = 0;
3510 }
3511 if (hor == nullptr) {
3512 hor = std::make_unique<NWidgetHorizontal>();
3513 hor_length = 0;
3514 }
3515
3516 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, button_colour, widnum);
3517 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3518 panel->SetFill(1, 1);
3519 if (resizable) panel->SetResize(1, 0);
3520 panel->SetToolTip(button_tooltip);
3521 hor->Add(std::move(panel));
3522 hor_length++;
3523 }
3524 if (vert == nullptr) return hor; // All buttons fit in a single row.
3525
3526 if (hor_length > 0 && hor_length < max_length) {
3527 /* Last row is partial, add a spacer at the end to force all buttons to the left. */
3528 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3529 spc->SetFill(1, 1);
3530 if (resizable) spc->SetResize(1, 0);
3531 hor->Add(std::move(spc));
3532 }
3533 if (hor != nullptr) vert->Add(std::move(hor));
3534 return vert;
3535}
Class for backupping variables and making sure they are restored later.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Enum-as-bit-set wrapper.
Nested widget with a child.
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2394
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:2404
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:2340
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2273
NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr< NWidgetPIPContainer > &&child=nullptr)
Constructor parent nested widgets.
Definition widget.cpp:2205
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:2239
std::unique_ptr< NWidgetPIPContainer > child
Child widget.
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2358
void Add(std::unique_ptr< NWidgetBase > &&nwid)
Add a child to the parent.
Definition widget.cpp:2220
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:2352
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:2258
Baseclass for nested widgets.
void StoreSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height)
Store size and position.
float aspect_ratio
Desired aspect ratio of widget.
virtual void SetupSmallestSize(Window *w)=0
Compute smallest size needed by the widget.
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:938
WidgetType type
Type of the widget / nested widget.
uint resize_x
Horizontal resize step (0 means not resizable).
uint fill_x
Horizontal fill stepsize (from initial size, 0 means not resizable).
NWidgetBase * parent
Parent widget of this widget, automatically filled in when added to container.
virtual void FillWidgetLookup(WidgetLookup &widget_lookup)=0
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
RectPadding uz_padding
Unscaled padding, for resize calculation.
uint smallest_x
Smallest horizontal size of the widget in a filled window.
AspectFlags aspect_flags
Which dimensions can be resized.
virtual void Draw(const Window *w)=0
Draw the widgets of the tree.
uint current_x
Current horizontal size (after resizing).
int pos_y
Vertical position of top-left corner of the widget in the window.
int pos_x
Horizontal position of top-left corner of the widget in the window.
void SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Set additional space (padding) around the widget.
uint smallest_y
Smallest vertical size of the widget in a filled window.
virtual NWidgetBase * GetWidgetOfType(WidgetType tp)
Retrieve a widget by its type.
Definition widget.cpp:958
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).
Baseclass for container widgets.
void Add(std::unique_ptr< NWidgetBase > &&wid)
Append widget wid to container.
Definition widget.cpp:1313
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1320
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1327
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:1336
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition widget.cpp:1291
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:1231
bool IsDisabled() const
Return whether the widget is disabled.
void SetSprite(SpriteID sprite)
Set sprite of the nested widget.
Definition widget.cpp:1181
WidgetID GetIndex() const
Get the WidgetID of this nested widget.
Definition widget.cpp:1267
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:1221
WidgetID GetScrollbarIndex() const
Get the WidgetID of this nested widget's scrollbar.
Definition widget.cpp:1276
void SetAlignment(StringAlignment align)
Set the text/image alignment of the nested widget.
Definition widget.cpp:1249
void SetResizeWidgetType(ResizeWidgetValues type)
Set the resize widget type of the nested widget.
Definition widget.cpp:1211
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:1149
void SetSpriteTip(SpriteID sprite, StringID tool_tip)
Set sprite and tool tip of the nested widget.
Definition widget.cpp:1191
const WidgetID index
Index of the nested widget (-1 means 'not used').
StringAlignment align
Alignment of text/image within widget.
FontSize text_size
Size of text within widget.
StringID GetString() const
Get the string that has been set for this nested widget.
Definition widget.cpp:1258
WidgetID scrollbar_index
Index of an attached scrollbar.
StringID GetToolTip() const
Get the tool tip of the nested widget.
Definition widget.cpp:1240
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1286
void SetString(StringID string)
Set string of the nested widget.
Definition widget.cpp:1161
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:1202
void SetLowered(bool lowered)
Lower or raise the widget.
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1281
void SetStringTip(StringID string, StringID tool_tip)
Set string and tool tip of the nested widget.
Definition widget.cpp:1171
StringID tool_tip
Tooltip of the widget.
bool IsLowered() const
Return whether the widget is lowered.
NWidgetHorizontalLTR(NWidContainerFlags flags={})
Horizontal left-to-right container widget.
Definition widget.cpp:1747
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:1752
Horizontal container.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1568
NWidgetHorizontal(NWidContainerFlags flags={})
Horizontal container widget.
Definition widget.cpp:1564
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:1637
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:1486
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1464
const WidgetID index
If non-negative, index in the Window::widget_lookup.
Definition widget.cpp:1461
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1504
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:3022
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition widget.cpp:2699
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:2726
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:2835
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:3166
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2119
int count
Amount of valid elements.
void SetClicked(int clicked)
Sets the clicked element in the matrix.
Definition widget.cpp:1982
int GetCurrentElement() const
Get current element.
Definition widget.cpp:2032
const WidgetID index
If non-negative, index in the Window::widget_lookup.
Scrollbar * sb
The scrollbar we're associated with.
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2037
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:2023
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:2174
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:2059
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:2083
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:2089
int clicked
The currently clicked element.
void SetCount(int count)
Set the number of elements in this matrix.
Definition widget.cpp:1999
int widgets_y
The number of visible widgets in vertical direction.
Colours colour
Colour of this widget.
Container with pre/inter/post child space.
uint8_t gaps
Number of gaps between widgets.
NWidContainerFlags flags
Flags of the container.
uint8_t pip_pre
Amount of space before first widget.
uint8_t uz_pip_pre
Unscaled space before first widget.
uint8_t uz_pip_inter
Unscaled space between widgets.
uint8_t pip_ratio_pre
Ratio of remaining space before first widget.
void SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_rato_post)
Set additional pre/inter/post space for the container.
Definition widget.cpp:1556
void SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
Set additional pre/inter/post space for the container.
Definition widget.cpp:1536
uint8_t pip_post
Amount of space after last widget.
uint8_t pip_ratio_inter
Ratio of remaining space between widgets.
uint8_t pip_ratio_post
Ratio of remaining space after last widget.
uint8_t uz_pip_post
Unscaled space after last widget.
uint8_t pip_inter
Amount of space between widgets.
Base class for a resizable nested widget.
uint8_t uz_text_spacing
'Unscaled' text padding, stored for resize calculation.
bool absolute
Set if minimum size is fixed and should not be resized.
void SetMinimalSize(uint min_x, uint min_y)
Set minimal size of the widget.
Definition widget.cpp:1030
bool UpdateSize(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1113
uint min_x
Minimal horizontal size of only this widget.
FontSize uz_text_size
'Unscaled' font size, stored for resize calculation.
NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y)
Constructor for resizable nested widgets.
Definition widget.cpp:988
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:1069
void SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
Set minimal text lines for the widget.
Definition widget.cpp:1056
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:1093
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:1134
bool UpdateVerticalSize(uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1127
void SetResize(uint resize_x, uint resize_y)
Set resize step of the widget.
Definition widget.cpp:1080
void SetAspect(float ratio, AspectFlags flags=AspectFlag::ResizeX)
Set desired aspect ratio of this widget.
Definition widget.cpp:999
void SetMinimalSizeAbsolute(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition widget.cpp:1043
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:2643
NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index)
Scrollbar widget.
Definition widget.cpp:2601
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2622
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:1934
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1947
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1968
void SetDirty(const Window *w) const override
Mark the widget as 'dirty' (in need of repaint).
Definition widget.cpp:1963
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1951
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1940
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:1347
const WidgetID index
If non-negative, index in the Window::widget_lookup.
WidgetLookup * widget_lookup
Window's widget lookup, updated in SetDisplayedPlane().
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override
Assign size and position to the widget.
Definition widget.cpp:1388
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:1419
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition widget.cpp:1443
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition widget.cpp:1408
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition widget.cpp:1428
NWidgetVertical(NWidContainerFlags flags={})
Vertical container widget.
Definition widget.cpp:1758
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:1831
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:1762
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition widget.cpp:2459
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition widget.cpp:2423
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition widget.cpp:2416
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition widget.cpp:2450
Scrollbar data structure.
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
void SetCount(size_t num)
Sets the number of elements in the list.
bool IsVertical() const
Is the scrollbar vertical or not?
bool UpdatePosition(int difference, ScrollbarStepping unit=SS_SMALL)
Updates the position of the first visible element by the given amount.
void SetCapacity(size_t capacity)
Set the capacity of visible elements.
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition widget.cpp:2482
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:2556
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:2503
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
void SetStepSize(size_t stepsize)
Set the distance to scroll when using the buttons or the wheel.
size_type pos
Index of first visible item of the list.
size_type GetPosition() const
Gets the position of the first visible element in the list.
@ SS_BIG
Step in cap units.
static constexpr uint WD_CAPTION_HEIGHT
Minimum height of a title bar.
Definition window_gui.h:87
RectPadding defsizebox
Padding around image in defsizebox widget.
Definition window_gui.h:46
RectPadding closebox
Padding around image in closebox widget.
Definition window_gui.h:48
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:40
static constexpr uint WD_DROPDOWN_HEIGHT
Minimum height of a drop down widget.
Definition window_gui.h:88
RectPadding captiontext
Padding for text within caption widget.
Definition window_gui.h:49
RectPadding debugbox
Padding around image in debugbox widget.
Definition window_gui.h:45
RectPadding frametext
Padding inside frame with text.
Definition window_gui.h:41
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
RectPadding modalpopup
Spacing for popup warning/information windows.
Definition window_gui.h:52
RectPadding hscrollbar
Padding inside horizontal scrollbar buttons.
Definition window_gui.h:37
RectPadding shadebox
Padding around image in shadebox widget.
Definition window_gui.h:43
RectPadding resizebox
Padding around image in resizebox widget.
Definition window_gui.h:47
RectPadding vscrollbar
Padding inside vertical scrollbar buttons.
Definition window_gui.h:36
RectPadding imgbtn
Padding around image button image.
Definition window_gui.h:34
int vsep_normal
Normal vertical spacing.
Definition window_gui.h:58
int vsep_wide
Wide vertical spacing.
Definition window_gui.h:60
int hsep_wide
Wide horizontal spacing.
Definition window_gui.h:62
static constexpr uint WD_CLOSEBOX_WIDTH
Minimum width of a close box widget.
Definition window_gui.h:86
RectPadding fullbevel
Always-scaled bevel thickness.
Definition window_gui.h:39
RectPadding inset
Padding inside inset container.
Definition window_gui.h:35
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
static constexpr uint WD_RESIZEBOX_WIDTH
Minimum width of a resize box widget.
Definition window_gui.h:85
static constexpr uint WD_STICKYBOX_WIDTH
Minimum width of a standard sticky box widget.
Definition window_gui.h:82
RectPadding matrix
Padding of WWT_MATRIX items.
Definition window_gui.h:42
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:61
RectPadding dropdownlist
Padding of complete drop down list.
Definition window_gui.h:51
static constexpr uint WD_DEBUGBOX_WIDTH
Minimum width of a standard debug box widget.
Definition window_gui.h:83
RectPadding stickybox
Padding around image in stickybox widget.
Definition window_gui.h:44
static constexpr uint WD_DEFSIZEBOX_WIDTH
Minimum width of a standard defsize box widget.
Definition window_gui.h:84
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
RectPadding dropdowntext
Padding of drop down list item.
Definition window_gui.h:50
static constexpr uint WD_SHADEBOX_WIDTH
Distances used in drawing widgets.
Definition window_gui.h:81
int hsep_indent
Width of indentation for tree layouts.
Definition window_gui.h:63
ReferenceThroughBaseContainer< std::array< Colours, MAX_COMPANIES > > _company_colours
NOSAVE: can be determined from company structs.
Functions related to companies.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
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:705
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:958
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:887
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:457
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:658
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:115
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:1024
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:1554
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
FontSize
Available font sizes.
Definition gfx_type.h:250
@ FS_BEGIN
First font.
Definition gfx_type.h:257
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:251
StringAlignment
How to align the to-be drawn text.
Definition gfx_type.h:382
@ SA_TOP
Top align the text.
Definition gfx_type.h:388
@ SA_LEFT
Left align the text.
Definition gfx_type.h:383
@ SA_HOR_MASK
Mask for horizontal alignment.
Definition gfx_type.h:386
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:385
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:384
@ SA_VERT_MASK
Mask for vertical alignment.
Definition gfx_type.h:391
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:395
@ SA_BOTTOM
Bottom align the text.
Definition gfx_type.h:390
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:393
@ SA_VERT_CENTER
Vertically center the text.
Definition gfx_type.h:389
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:302
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:341
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
Definition gfx_type.h:342
std::unique_ptr< NWidgetBase > MakeWindowNWidgetTree(std::span< const NWidgetPart > nwid_parts, NWidgetStacked **shade_select)
Make a nested widget tree for a window from a parts array.
Definition widget.cpp:3450
std::unique_ptr< NWidgetBase > MakeNWidgets(std::span< const NWidgetPart > nwid_parts, std::unique_ptr< NWidgetBase > &&container)
Construct a nested widget tree from an array of parts.
Definition widget.cpp:3431
constexpr NWidgetPart SetAlignment(StringAlignment align)
Widget part function for setting the alignment of text/images.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:955
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:1502
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
static const uint8_t PC_WHITE
White palette colour.
static const uint8_t PC_BLACK
Black palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:59
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:1536
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition sprites.h:1606
static const PaletteID PALETTE_NEWSPAPER
Recolour sprite for newspaper-greying.
Definition sprites.h:1608
Definition of base types and functions in a cross-platform compatible way.
The colour translation of GRF's strings.
static const uint8_t _string_colourmap[17]
Colour mapping for TextColour.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:415
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:57
Functions related to OTTD's strings.
Types related to strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_LTR
Text is written left-to-right by default.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
GUISettings gui
settings related to the GUI
Point pos
logical mouse position
Definition gfx_type.h:126
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:158
bool scale_bevels
bevels are scaled with GUI scale.
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
StringAlignment align
Alignment of text/image.
WidgetData data
Data value of the widget.
StringID tooltip
Tooltip of the widget.
uint8_t post
Amount of space before/between/after child widgets.
uint8_t lines
Number of text lines.
uint8_t spacing
Extra spacing around lines.
FontSize size
Font size of text lines.
TextColour colour
TextColour for DrawString.
FontSize size
Font size of text.
Colours colour
Widget colour.
WidgetID index
Index of the widget.
Partial widget specification to allow NWidgets to be written nested.
WidgetType type
Type of the part.
Coordinates of a point in 2D.
Data stored about a string that can be modified in the GUI.
Padding dimensions to apply to each side of a Rect.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
int Height() const
Get height of Rect.
Rect Expand(int s) const
Copy and expand Rect by s pixels.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
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:620
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition widget.cpp:826
void DrawWidgets() const
Paint all widgets of a window.
Definition widget.cpp:777
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:503
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
Definition window_gui.h:326
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
Definition window_gui.h:719
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition widget.cpp:809
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
Definition window_gui.h:859
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:606
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:559
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:332
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:211
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:982
WindowFlags flags
Window flags.
Definition window_gui.h:300
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
Definition window_gui.h:321
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:312
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:311
Functions related to transparency.
TransparencyOptionBits _transparency_opt
The bits that should be transparent.
uint TransparencyOptionBits
transparency option bits
@ TO_TEXT
loading and cost/income text
@ TO_SIGNS
signs
NWidgetPartTextLines text_lines
Part with text line data.
NWidgetPartPIP pip
Part with pre/inter/post spaces.
NWidgetPartPaddings padding
Part with paddings.
NWidgetFunctionType * func_ptr
Part with a function call.
NWidContainerFlags cont_flags
Part with container flags.
NWidgetPartDataTip data_tip
Part with a data/tooltip.
NWidgetPartAlignment align
Part with internal alignment.
NWidgetPartAspect aspect
Part to set aspect ratio.
NWidgetPartTextStyle text_style
Part with text style data.
Point xy
Part with an x/y size.
NWidgetPartWidget widget
Part with a start of a widget.
void InitializeWindowViewport(Window *w, int x, int y, int width, int height, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize viewport of the window for use.
Definition viewport.cpp:218
Functions related to (drawing on) viewports.
static void DrawCloseBox(const Rect &r, Colours colour)
Draw a close box.
Definition widget.cpp:704
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:298
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition widget.cpp:2573
void ApplyNWidgetPartAttribute(const NWidgetPart &nwid, NWidgetBase *dest)
Apply an attribute NWidgetPart to an NWidget.
Definition widget.cpp:3195
static void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
Draw a defsize box.
Definition widget.cpp:665
bool IsContainerWidgetType(WidgetType tp)
Test if WidgetType is a container widget.
Definition widget.cpp:3374
static void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
Draw a NewGRF debug box.
Definition widget.cpp:676
static void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
Draw a sticky box.
Definition widget.cpp:654
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:590
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:373
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:120
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:452
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:550
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:755
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition widget.cpp:68
static std::unique_ptr< NWidgetBase > MakeNWidget(const NWidgetPart &nwid)
Make NWidget from an NWidgetPart.
Definition widget.cpp:3303
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:80
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:725
static void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
Draw a shade box.
Definition widget.cpp:643
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:283
static void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
Draw an image button.
Definition widget.cpp:353
static bool IsAttributeWidgetPartType(WidgetType tp)
Test if (an NWidgetPart) WidgetType is an attribute widget part type.
Definition widget.cpp:3184
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:401
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition widget.cpp:259
static void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
Draw a resize box.
Definition widget.cpp:689
static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom, bool horizontal)
Compute the vertical position of the draggable part of scrollbar.
Definition widget.cpp:149
static void DrawText(const Rect &r, TextColour colour, std::string_view str, StringAlignment align, FontSize fs)
Draw text.
Definition widget.cpp:418
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:194
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:436
static std::span< constNWidgetPart >::iterator MakeWidgetTree(std::span< const NWidgetPart >::iterator nwid_begin, std::span< const NWidgetPart >::iterator nwid_end, std::unique_ptr< NWidgetBase > &parent)
Build a nested widget tree by recursively filling containers with nested widgets read from their part...
Definition widget.cpp:3387
static void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
Draw a vertical scrollbar.
Definition widget.cpp:510
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
std::unique_ptr< NWidgetBase > MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
Make a number of rows with button-like graphics, for enabling/disabling each company.
Definition widget.cpp:3492
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition widget_type.h:36
@ 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:41
@ WPT_FILL
Widget part for specifying fill.
Definition widget_type.h:85
@ 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:91
@ WWT_IMGBTN
(Toggle) Button with image
Definition widget_type.h:42
@ 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:43
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WPT_MINSIZE
Widget part for specifying minimal size.
Definition widget_type.h:83
@ WWT_PUSHARROWBTN
Normal push-button (no toggle button) with arrow caption.
@ WWT_LABEL
Centered label.
Definition widget_type.h:49
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition widget_type.h:75
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:71
@ WWT_EDITBOX
a textbox for typing
Definition widget_type.h:63
@ WWT_ARROWBTN
(Toggle) Button with an arrow
Definition widget_type.h:44
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:67
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:45
@ WPT_SCROLLBAR
Widget part for attaching a scrollbar.
Definition widget_type.h:92
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:40
@ WPT_ASPECT
Widget part for specifying aspect ratio.
Definition widget_type.h:93
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:58
@ WPT_RESIZE
Widget part for specifying resizing.
Definition widget_type.h:82
@ WWT_IMGTEXTBTN
(Toggle) Button with image and text
Definition widget_type.h:48
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:51
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:56
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:53
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:77
@ WPT_TEXTSTYLE
Widget part for specifying text colour.
Definition widget_type.h:90
@ WPT_PADDING
Widget part for specifying a padding.
Definition widget_type.h:87
@ WWT_BOOLBTN
Standard boolean toggle button.
Definition widget_type.h:47
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:69
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:61
@ WWT_TEXTBTN_2
(Toggle) Button with diff text when clicked
Definition widget_type.h:46
@ WWT_FRAME
Frame.
Definition widget_type.h:52
@ WPT_MINTEXTLINES
Widget part for specifying minimal number of lines of text.
Definition widget_type.h:84
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:38
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition widget_type.h:64
@ WPT_ATTRIBUTE_END
End marker for attribute NWidgetPart types.
Definition widget_type.h:94
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition widget_type.h:76
@ 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:60
@ WPT_PIPRATIO
Widget part for specifying pre/inter/post ratio for containers.
Definition widget_type.h:89
@ WPT_DATATIP
Widget part for specifying data and tooltip.
Definition widget_type.h:86
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:57
@ WPT_ATTRIBUTE_BEGIN
Begin marker for attribute NWidgetPart types.
Definition widget_type.h:81
@ WPT_PIPSPACE
Widget part for specifying pre/inter/post space for containers.
Definition widget_type.h:88
@ NWID_MATRIX
Matrix container.
Definition widget_type.h:70
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition widget_type.h:74
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:62
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:50
@ NWID_HORIZONTAL_LTR
Horizontal container that doesn't change the order of the widgets for RTL languages.
Definition widget_type.h:68
@ 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:55
@ NWID_LAYER
Layered widgets, all visible together.
Definition widget_type.h:73
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition widget_type.h:72
@ ScrollbarDown
Down-button is lowered bit.
@ ShadeDimmed
Display dimmed colours in the viewport.
@ ScrollbarUp
Up-button is lowered bit.
@ DropdownActive
Dropdown menu of the button dropdown widget is active.
@ NoTransparency
Viewport is never transparent.
uint ComputeMaxSize(uint base, uint max_space, uint step)
Return the biggest possible size of a nested widget.
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
@ SZSP_BEGIN
First zero-size plane.
@ SZSP_VERTICAL
Display plane with zero size horizontally, and filling and resizing vertically.
NWidContainerFlag
Nested widget container flags,.
@ EqualSize
Containers should keep all their (resizing) children equally large.
@ BigFirst
Allocate space to biggest resize first.
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition()
@ ST_RESIZE
Resize the nested widget tree.
@ ST_SMALLEST
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
@ AWV_LEFT
Force the arrow to the left.
Definition widget_type.h:23
@ AWV_RIGHT
Force the arrow to the right.
Definition widget_type.h:24
@ AWV_DECREASE
Arrow to the left or in case of RTL to the right.
Definition widget_type.h:21
@ AWV_INCREASE
Arrow to the right or in case of RTL to the left.
Definition widget_type.h:22
std::map< WidgetID, class NWidgetBase * > WidgetLookup
Lookup between widget IDs and NWidget objects.
ResizeWidgetValues
WidgetData values for a resize box widget.
Definition widget_type.h:28
@ RWV_SHOW_BEVEL
Bevel of resize box is shown.
Definition widget_type.h:29
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition window.cpp:77
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:298
@ Transparent
Makes the background transparent if set.
@ BorderOnly
Draw border only, no background.
@ Darkened
If set the background is darker, allows for lowered frames with normal background colour when used wi...
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
@ SizingLeft
Window is being resized towards the left.
@ Highlighted
Window has a widget that has a highlight.
@ SizingRight
Window is being resized towards the right.
@ WhiteBorder
Window white border counter bit mask.
@ Sticky
Window is made sticky by user.
SortButtonState
State of a sort direction button.
Definition window_gui.h:216
@ SBS_DOWN
Sort ascending.
Definition window_gui.h:218
@ SBS_OFF
Do not sort (with this button).
Definition window_gui.h:217
int WidgetID
Widget ID.
Definition window_type.h:20
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
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:16
@ Normal
The normal zoom level.