OpenTTD Source  20241124-master-g9399a92a4f
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 "window_gui.h"
14 #include "viewport_func.h"
15 #include "zoom_func.h"
16 #include "strings_func.h"
17 #include "transparency.h"
18 #include "core/geometry_func.hpp"
19 #include "settings_type.h"
20 #include "querystring_gui.h"
21 
22 #include "table/sprites.h"
23 #include "table/strings.h"
24 #include "table/string_colours.h"
25 
26 #include "safeguards.h"
27 
29 
35 static inline RectPadding ScaleGUITrad(const RectPadding &r)
36 {
37  return {(uint8_t)ScaleGUITrad(r.left), (uint8_t)ScaleGUITrad(r.top), (uint8_t)ScaleGUITrad(r.right), (uint8_t)ScaleGUITrad(r.bottom)};
38 }
39 
45 static inline Dimension ScaleGUITrad(const Dimension &dim)
46 {
47  return {(uint)ScaleGUITrad(dim.width), (uint)ScaleGUITrad(dim.height)};
48 }
49 
55 {
56  Point offset;
57  Dimension d = GetSpriteSize(sprid, &offset, ZOOM_LVL_NORMAL);
58  d.width -= offset.x;
59  d.height -= offset.y;
60  return ScaleGUITrad(d);
61 }
62 
67 {
74  } else {
76  }
91 
97 }
98 
106 static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align)
107 {
108  Point p;
109  /* In case we have a RTL language we swap the alignment. */
110  if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
111  switch (align & SA_HOR_MASK) {
112  case SA_LEFT: p.x = r.left; break;
113  case SA_HOR_CENTER: p.x = CenterBounds(r.left, r.right, d.width); break;
114  case SA_RIGHT: p.x = r.right + 1 - d.width; break;
115  default: NOT_REACHED();
116  }
117  switch (align & SA_VERT_MASK) {
118  case SA_TOP: p.y = r.top; break;
119  case SA_VERT_CENTER: p.y = CenterBounds(r.top, r.bottom, d.height); break;
120  case SA_BOTTOM: p.y = r.bottom + 1 - d.height; break;
121  default: NOT_REACHED();
122  }
123  return p;
124 }
125 
135 static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom, bool horizontal)
136 {
137  /* Base for reversion */
138  int rev_base = top + bottom;
139  int button_size;
140  if (horizontal) {
141  button_size = NWidgetScrollbar::GetHorizontalDimension().width;
142  } else {
143  button_size = NWidgetScrollbar::GetVerticalDimension().height;
144  }
145  top += button_size; // top points to just below the up-button
146  bottom -= button_size; // bottom points to top of the down-button
147 
148  int height = (bottom - top);
149  int pos = sb->GetPosition();
150  int count = sb->GetCount();
151  int cap = sb->GetCapacity();
152 
153  if (count != 0) top += height * pos / count;
154 
155  if (cap > count) cap = count;
156  if (count != 0) bottom -= (count - pos - cap) * height / count;
157 
158  Point pt;
159  if (horizontal && _current_text_dir == TD_RTL) {
160  pt.x = rev_base - bottom;
161  pt.y = rev_base - top;
162  } else {
163  pt.x = top;
164  pt.y = bottom;
165  }
166  return pt;
167 }
168 
178 static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, int y, int mi, int ma)
179 {
180  int pos;
181  int button_size;
182  bool rtl = false;
183  bool changed = false;
184 
185  if (sb->type == NWID_HSCROLLBAR) {
186  pos = x;
187  rtl = _current_text_dir == TD_RTL;
188  button_size = NWidgetScrollbar::GetHorizontalDimension().width;
189  } else {
190  pos = y;
191  button_size = NWidgetScrollbar::GetVerticalDimension().height;
192  }
193  if (pos < mi + button_size) {
194  /* Pressing the upper button? */
196  if (_scroller_click_timeout <= 1) {
197  _scroller_click_timeout = 3;
198  changed = sb->UpdatePosition(rtl ? 1 : -1);
199  }
200  w->mouse_capture_widget = sb->index;
201  } else if (pos >= ma - button_size) {
202  /* Pressing the lower button? */
204 
205  if (_scroller_click_timeout <= 1) {
206  _scroller_click_timeout = 3;
207  changed = sb->UpdatePosition(rtl ? -1 : 1);
208  }
209  w->mouse_capture_widget = sb->index;
210  } else {
211  Point pt = HandleScrollbarHittest(sb, mi, ma, sb->type == NWID_HSCROLLBAR);
212 
213  if (pos < pt.x) {
214  changed = sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::SS_BIG);
215  } else if (pos > pt.y) {
216  changed = sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::SS_BIG);
217  } else {
218  _scrollbar_start_pos = pt.x - mi - button_size;
219  _scrollbar_size = ma - mi - button_size * 2;
220  w->mouse_capture_widget = sb->index;
221  _cursorpos_drag_start = _cursor.pos;
222  }
223  }
224 
225  if (changed) {
226  /* Position changed so refresh the window */
227  w->SetDirty();
228  } else {
229  /* No change so only refresh this scrollbar */
230  sb->SetDirty(w);
231  }
232 }
233 
242 void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
243 {
244  int mi, ma;
245 
246  if (nw->type == NWID_HSCROLLBAR) {
247  mi = nw->pos_x;
248  ma = nw->pos_x + nw->current_x;
249  } else {
250  mi = nw->pos_y;
251  ma = nw->pos_y + nw->current_y;
252  }
253  NWidgetScrollbar *scrollbar = dynamic_cast<NWidgetScrollbar*>(nw);
254  assert(scrollbar != nullptr);
255  ScrollbarClickPositioning(w, scrollbar, x, y, mi, ma);
256 }
257 
266 WidgetID GetWidgetFromPos(const Window *w, int x, int y)
267 {
268  NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
269  return (nw != nullptr) ? nw->index : -1;
270 }
271 
281 void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
282 {
283  assert(colour < COLOUR_END);
284 
285  uint dark = GetColourGradient(colour, SHADE_DARK);
286  uint medium_dark = GetColourGradient(colour, SHADE_LIGHT);
287  uint medium_light = GetColourGradient(colour, SHADE_LIGHTER);
288  uint light = GetColourGradient(colour, SHADE_LIGHTEST);
289 
290  if (flags & FR_TRANSPARENT) {
291  GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
292  } else {
293  uint interior;
294 
295  Rect outer = {left, top, right, bottom}; // Outside rectangle
296  Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel); // Inside rectangle
297 
298  if (flags & FR_LOWERED) {
299  GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark); // Left
300  GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark); // Top
301  GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light); // Right
302  GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light); // Bottom
303  interior = (flags & FR_DARKENED ? medium_dark : medium_light);
304  } else {
305  GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light); // Left
306  GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light); // Top
307  GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark); // Right
308  GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark); // Bottom
309  interior = medium_dark;
310  }
311  if (!(flags & FR_BORDERONLY)) {
312  GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior); // Inner
313  }
314  }
315 }
316 
317 void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, StringAlignment align)
318 {
319  Point offset;
320  Dimension d = GetSpriteSize(img, &offset);
321  d.width -= offset.x;
322  d.height -= offset.y;
323 
324  Point p = GetAlignedPosition(r, d, align);
325  DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
326 }
327 
337 static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
338 {
339  assert(img != 0);
340  DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
341 
342  if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2.
343  DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
344 }
345 
356 static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, TextColour colour, StringID str, StringAlignment align, FontSize fs)
357 {
358  if (str == STR_NULL) return;
359  if ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++;
360  Dimension d = GetStringBoundingBox(str, fs);
361  Point p = GetAlignedPosition(r, d, align);
362  DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
363 }
364 
373 static inline void DrawText(const Rect &r, TextColour colour, StringID str, StringAlignment align, FontSize fs)
374 {
375  Dimension d = GetStringBoundingBox(str, fs);
376  Point p = GetAlignedPosition(r, d, align);
377  if (str != STR_NULL) DrawString(r.left, r.right, p.y, str, colour, align, false, fs);
378 }
379 
389 static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
390 {
391  DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED);
392  if (str != STR_NULL) DrawString(r.Shrink(WidgetDimensions::scaled.inset), str, text_colour, align, false, fs);
393 }
394 
404 static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint16_t data, uint resize_x, uint resize_y)
405 {
406  DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
407 
408  int num_columns = GB(data, MAT_COL_START, MAT_COL_BITS); // Lower 8 bits of the widget data: Number of columns in the matrix.
409  int column_width; // Width of a single column in the matrix.
410  if (num_columns == 0) {
411  column_width = resize_x;
412  num_columns = r.Width() / column_width;
413  } else {
414  column_width = r.Width() / num_columns;
415  }
416 
417  int num_rows = GB(data, MAT_ROW_START, MAT_ROW_BITS); // Upper 8 bits of the widget data: Number of rows in the matrix.
418  int row_height; // Height of a single row in the matrix.
419  if (num_rows == 0) {
420  row_height = resize_y;
421  num_rows = r.Height() / row_height;
422  } else {
423  row_height = r.Height() / num_rows;
424  }
425 
426  int col = GetColourGradient(colour, SHADE_LIGHTER);
427 
428  int x = r.left;
429  for (int ctr = num_columns; ctr > 1; ctr--) {
430  x += column_width;
431  GfxFillRect(x, r.top + WidgetDimensions::scaled.bevel.top, x + WidgetDimensions::scaled.bevel.left - 1, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
432  }
433 
434  x = r.top;
435  for (int ctr = num_rows; ctr > 1; ctr--) {
436  x += row_height;
437  GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x, r.right - WidgetDimensions::scaled.bevel.right, x + WidgetDimensions::scaled.bevel.top - 1, col);
438  }
439 
440  col = GetColourGradient(colour, SHADE_NORMAL);
441 
442  x = r.left - 1;
443  for (int ctr = num_columns; ctr > 1; ctr--) {
444  x += column_width;
445  GfxFillRect(x - WidgetDimensions::scaled.bevel.right + 1, r.top + WidgetDimensions::scaled.bevel.top, x, r.bottom - WidgetDimensions::scaled.bevel.bottom, col);
446  }
447 
448  x = r.top - 1;
449  for (int ctr = num_rows; ctr > 1; ctr--) {
450  x += row_height;
451  GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x - WidgetDimensions::scaled.bevel.bottom + 1, r.right - WidgetDimensions::scaled.bevel.right, x, col);
452  }
453 }
454 
464 static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_clicked, bool bar_dragged, bool down_clicked, const Scrollbar *scrollbar)
465 {
466  int height = NWidgetScrollbar::GetVerticalDimension().height;
467 
468  /* draw up/down buttons */
469  DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
470  DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
471 
472  int c1 = GetColourGradient(colour, SHADE_DARK);
473  int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
474 
475  /* draw "shaded" background */
476  GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
477  GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c1, FILLRECT_CHECKER);
478 
479  /* track positions. These fractions are based on original 1x dimensions, but scale better. */
480  int left = r.left + r.Width() * 3 / 11; /* left track is positioned 3/11ths from the left */
481  int right = r.left + r.Width() * 8 / 11; /* right track is positioned 8/11ths from the left */
482  const uint8_t bl = WidgetDimensions::scaled.bevel.left;
483  const uint8_t br = WidgetDimensions::scaled.bevel.right;
484 
485  /* draw shaded lines */
486  GfxFillRect(left - bl, r.top + height, left - 1, r.bottom - height, c1);
487  GfxFillRect(left, r.top + height, left + br - 1, r.bottom - height, c2);
488  GfxFillRect(right - bl, r.top + height, right - 1, r.bottom - height, c1);
489  GfxFillRect(right, r.top + height, right + br - 1, r.bottom - height, c2);
490 
491  Point pt = HandleScrollbarHittest(scrollbar, r.top, r.bottom, false);
492  DrawFrameRect(r.left, pt.x, r.right, pt.y, colour, bar_dragged ? FR_LOWERED : FR_NONE);
493 }
494 
504 static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool left_clicked, bool bar_dragged, bool right_clicked, const Scrollbar *scrollbar)
505 {
506  int width = NWidgetScrollbar::GetHorizontalDimension().width;
507 
508  DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
509  DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
510 
511  int c1 = GetColourGradient(colour, SHADE_DARK);
512  int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
513 
514  /* draw "shaded" background */
515  GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
516  GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c1, FILLRECT_CHECKER);
517 
518  /* track positions. These fractions are based on original 1x dimensions, but scale better. */
519  int top = r.top + r.Height() * 3 / 11; /* top track is positioned 3/11ths from the top */
520  int bottom = r.top + r.Height() * 8 / 11; /* bottom track is positioned 8/11ths from the top */
521  const uint8_t bt = WidgetDimensions::scaled.bevel.top;
522  const uint8_t bb = WidgetDimensions::scaled.bevel.bottom;
523 
524  /* draw shaded lines */
525  GfxFillRect(r.left + width, top - bt, r.right - width, top - 1, c1);
526  GfxFillRect(r.left + width, top, r.right - width, top + bb - 1, c2);
527  GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom - 1, c1);
528  GfxFillRect(r.left + width, bottom, r.right - width, bottom + bb - 1, c2);
529 
530  /* draw actual scrollbar */
531  Point pt = HandleScrollbarHittest(scrollbar, r.left, r.right, true);
532  DrawFrameRect(pt.x, r.top, pt.y, r.bottom, colour, bar_dragged ? FR_LOWERED : FR_NONE);
533 }
534 
544 static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
545 {
546  int x2 = r.left; // by default the left side is the left side of the widget
547 
548  if (str != STR_NULL) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs);
549 
550  int c1 = GetColourGradient(colour, SHADE_DARK);
551  int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
552 
553  /* If the frame has text, adjust the top bar to fit half-way through */
554  Rect inner = r.Shrink(ScaleGUITrad(1));
555  if (str != STR_NULL) inner.top = r.top + GetCharacterHeight(FS_NORMAL) / 2;
556 
557  Rect outer = inner.Expand(WidgetDimensions::scaled.bevel);
558  Rect inside = inner.Shrink(WidgetDimensions::scaled.bevel);
559 
560  if (_current_text_dir == TD_LTR) {
561  /* Line from upper left corner to start of text */
562  GfxFillRect(outer.left, outer.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
563  GfxFillRect(inner.left, inner.top, r.left + WidgetDimensions::scaled.frametext.left - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
564 
565  /* Line from end of text to upper right corner */
566  GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
567  GfxFillRect(x2 + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
568  } else {
569  /* Line from upper left corner to start of text */
570  GfxFillRect(outer.left, outer.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inner.top - 1, c1);
571  GfxFillRect(inner.left, inner.top, x2 - WidgetDimensions::scaled.bevel.left - 1, inside.top - 1, c2);
572 
573  /* Line from end of text to upper right corner */
574  GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, outer.top, inner.right, inner.top - 1, c1);
575  GfxFillRect(r.right - WidgetDimensions::scaled.frametext.right + WidgetDimensions::scaled.bevel.right, inner.top, inside.right, inside.top - 1, c2);
576  }
577 
578  /* Line from upper left corner to bottom left corner */
579  GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
580  GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
581 
582  /* Line from upper right corner to bottom right corner */
583  GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
584  GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
585 
586  /* Line from bottom left corner to bottom right corner */
587  GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
588  GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
589 }
590 
597 static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
598 {
599  DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER);
600 }
601 
608 static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
609 {
610  DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER);
611 }
612 
619 static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
620 {
621  DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER);
622 }
623 
630 static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
631 {
632  DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER);
633 }
634 
643 static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
644 {
645  if (bevel) {
646  DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
647  } else if (clicked) {
649  }
650  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));
651 }
652 
658 static inline void DrawCloseBox(const Rect &r, Colours colour)
659 {
660  if (colour != COLOUR_WHITE) DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_NONE);
661  Point offset;
662  Dimension d = GetSpriteSize(SPR_CLOSEBOX, &offset);
663  d.width -= offset.x;
664  d.height -= offset.y;
665  int s = ScaleSpriteTrad(1); /* Offset to account for shadow of SPR_CLOSEBOX */
666  DrawSprite(SPR_CLOSEBOX, (colour != COLOUR_WHITE ? TC_BLACK : TC_SILVER) | (1U << PALETTE_TEXT_RECOLOUR), CenterBounds(r.left, r.right, d.width - s) - offset.x, CenterBounds(r.top, r.bottom, d.height - s) - offset.y);
667 }
668 
679 void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
680 {
681  bool company_owned = owner < MAX_COMPANIES;
682 
683  DrawFrameRect(r, colour, FR_BORDERONLY);
684  Rect ir = r.Shrink(WidgetDimensions::scaled.bevel);
685  DrawFrameRect(ir, colour, company_owned ? FR_LOWERED | FR_DARKENED | FR_BORDERONLY : FR_LOWERED | FR_DARKENED);
686 
687  if (company_owned) {
689  }
690 
691  if (str != STR_NULL) {
693  Point p = GetAlignedPosition(r, d, align);
694  DrawString(r.left + WidgetDimensions::scaled.captiontext.left, r.right - WidgetDimensions::scaled.captiontext.left, p.y, str, text_colour, align, false, fs);
695  }
696 }
697 
709 static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str, StringAlignment align)
710 {
711  int dd_width = NWidgetLeaf::dropdown_dimension.width;
712 
713  if (_current_text_dir == TD_LTR) {
714  DrawFrameRect(r.left, r.top, r.right - dd_width, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
715  DrawImageButtons(r.WithWidth(dd_width, true), WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
716  if (str != STR_NULL) {
717  DrawString(r.left + WidgetDimensions::scaled.dropdowntext.left, r.right - dd_width - WidgetDimensions::scaled.dropdowntext.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), str, TC_BLACK, align);
718  }
719  } else {
720  DrawFrameRect(r.left + dd_width, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE);
721  DrawImageButtons(r.WithWidth(dd_width, false), WWT_DROPDOWN, colour, clicked_dropdown, SPR_ARROW_DOWN, SA_CENTER);
722  if (str != STR_NULL) {
723  DrawString(r.left + dd_width + WidgetDimensions::scaled.dropdowntext.left, r.right - WidgetDimensions::scaled.dropdowntext.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), str, TC_BLACK, align);
724  }
725  }
726 }
727 
732 {
733  this->nested_root->Draw(this);
734 
735  if (this->flags & WF_WHITE_BORDER) {
736  DrawFrameRect(0, 0, this->width - 1, this->height - 1, COLOUR_WHITE, FR_BORDERONLY);
737  }
738 
739  if (this->flags & WF_HIGHLIGHTED) {
740  extern bool _window_highlight_colour;
741  for (const auto &pair : this->widget_lookup) {
742  const NWidgetBase *widget = pair.second;
743  if (!widget->IsHighlighted()) continue;
744 
745  Rect outer = widget->GetCurrentRect();
746  Rect inner = outer.Shrink(WidgetDimensions::scaled.bevel).Expand(1);
747 
748  int colour = _string_colourmap[_window_highlight_colour ? widget->GetHighlightColour() : TC_WHITE];
749 
750  GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
751  GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
752  GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
753  GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
754  }
755  }
756 }
757 
764 {
765  if (state == SBS_OFF) return;
766 
767  assert(!this->widget_lookup.empty());
768  Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
769 
770  /* Sort button uses the same sprites as vertical scrollbar */
771  Dimension dim = NWidgetScrollbar::GetVerticalDimension();
772 
773  DrawSpriteIgnorePadding(state == SBS_DOWN ? SPR_ARROW_DOWN : SPR_ARROW_UP, PAL_NONE, r.WithWidth(dim.width, _current_text_dir == TD_LTR), SA_CENTER);
774 }
775 
781 {
782  return NWidgetScrollbar::GetVerticalDimension().width + 1;
783 }
784 
785 bool _draw_widget_outlines;
786 
787 static void DrawOutline(const Window *, const NWidgetBase *wid)
788 {
789  if (!_draw_widget_outlines || wid->current_x == 0 || wid->current_y == 0) return;
790 
791  DrawRectOutline(wid->GetCurrentRect(), PC_WHITE, 1, 4);
792 }
793 
852 {
853  this->type = tp;
854 }
855 
856 /* ~NWidgetContainer() takes care of #next and #prev data members. */
857 
903 void NWidgetBase::SetDirty(const Window *w) const
904 {
905  int abs_left = w->left + this->pos_x;
906  int abs_top = w->top + this->pos_y;
907  AddDirtyBlock(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y);
908 }
909 
924 {
925  return (this->type == tp) ? this : nullptr;
926 }
927 
928 void NWidgetBase::ApplyAspectRatio()
929 {
930  if (this->aspect_ratio == 0) return;
931  if (this->smallest_x == 0 || this->smallest_y == 0) return;
932 
933  uint x = this->smallest_x;
934  uint y = this->smallest_y;
935  if (HasFlag(this->aspect_flags, AspectFlags::ResizeX)) x = std::max(this->smallest_x, static_cast<uint>(this->smallest_y * std::abs(this->aspect_ratio)));
936  if (HasFlag(this->aspect_flags, AspectFlags::ResizeY)) y = std::max(this->smallest_y, static_cast<uint>(this->smallest_x / std::abs(this->aspect_ratio)));
937 
938  this->smallest_x = x;
939  this->smallest_y = y;
940 }
941 
942 void NWidgetBase::AdjustPaddingForZoom()
943 {
944  this->padding = ScaleGUITrad(this->uz_padding);
945 }
946 
954 {
955  this->fill_x = fill_x;
956  this->fill_y = fill_y;
957 }
958 
964 void NWidgetResizeBase::SetAspect(float ratio, AspectFlags flags)
965 {
966  this->aspect_ratio = ratio;
967  this->aspect_flags = flags;
968 }
969 
976 void NWidgetResizeBase::SetAspect(int x_ratio, int y_ratio, AspectFlags flags)
977 {
978  this->SetAspect(static_cast<float>(x_ratio) / static_cast<float>(y_ratio), flags);
979 }
980 
981 void NWidgetResizeBase::AdjustPaddingForZoom()
982 {
983  if (!this->absolute) {
984  this->min_x = ScaleGUITrad(this->uz_min_x);
985  this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
986  }
987  NWidgetBase::AdjustPaddingForZoom();
988 }
989 
995 void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y)
996 {
997  this->uz_min_x = std::max(this->uz_min_x, min_x);
998  this->uz_min_y = std::max(this->uz_min_y, min_y);
999  this->min_x = ScaleGUITrad(this->uz_min_x);
1000  this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1001 }
1002 
1008 void NWidgetResizeBase::SetMinimalSizeAbsolute(uint min_x, uint min_y)
1009 {
1010  this->absolute = true;
1011  this->min_x = std::max(this->min_x, min_x);
1012  this->min_y = std::max(this->min_y, min_y);
1013 }
1014 
1021 void NWidgetResizeBase::SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
1022 {
1023  this->uz_text_lines = min_lines;
1024  this->uz_text_spacing = spacing;
1025  this->uz_text_size = size;
1026  this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing));
1027 }
1028 
1034 void NWidgetResizeBase::SetFill(uint fill_x, uint fill_y)
1035 {
1036  this->fill_x = fill_x;
1037  this->fill_y = fill_y;
1038 }
1039 
1045 void NWidgetResizeBase::SetResize(uint resize_x, uint resize_y)
1046 {
1047  this->resize_x = resize_x;
1048  this->resize_y = resize_y;
1049 }
1050 
1058 bool NWidgetResizeBase::UpdateMultilineWidgetSize(const std::string &str, int max_lines)
1059 {
1060  int y = GetStringHeight(str, this->current_x);
1061  if (y > max_lines * GetCharacterHeight(FS_NORMAL)) {
1062  /* Text at the current width is too tall, so try to guess a better width. */
1064  d.height *= max_lines;
1065  d.width /= 2;
1066  return this->UpdateSize(d.width, d.height);
1067  }
1068  return this->UpdateVerticalSize(y);
1069 }
1070 
1078 bool NWidgetResizeBase::UpdateSize(uint min_x, uint min_y)
1079 {
1080  if (min_x == this->min_x && min_y == this->min_y) return false;
1081  this->min_x = min_x;
1082  this->min_y = min_y;
1083  return true;
1084 }
1085 
1093 {
1094  if (min_y == this->min_y) return false;
1095  this->min_y = min_y;
1096  return true;
1097 }
1098 
1099 void NWidgetResizeBase::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1100 {
1101  this->StoreSizePosition(sizing, x, y, given_width, given_height);
1102 }
1103 
1114 NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, WidgetID index, uint fill_x, uint fill_y, uint32_t widget_data, StringID tool_tip) : NWidgetResizeBase(tp, fill_x, fill_y), index(index)
1115 {
1116  this->colour = colour;
1117  this->widget_data = widget_data;
1118  this->tool_tip = tool_tip;
1119  this->scrollbar_index = -1;
1120  this->text_colour = tp == WWT_CAPTION ? TC_WHITE : TC_BLACK;
1121  this->text_size = FS_NORMAL;
1122  this->align = SA_CENTER;
1123 }
1124 
1130 void NWidgetCore::SetDataTip(uint32_t widget_data, StringID tool_tip)
1131 {
1132  this->widget_data = widget_data;
1133  this->tool_tip = tool_tip;
1134 }
1135 
1142 {
1143  this->text_colour = colour;
1144  this->text_size = size;
1145 }
1146 
1152 {
1153  this->tool_tip = tool_tip;
1154 }
1155 
1161 {
1162  this->align = align;
1163 }
1164 
1166 {
1167  if (this->index >= 0) widget_lookup[this->index] = this;
1168 }
1169 
1171 {
1172  return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : nullptr;
1173 }
1174 
1176 {
1177  if (this->type == tp) return this;
1178  for (const auto &child_wid : this->children) {
1179  NWidgetBase *nwid = child_wid->GetWidgetOfType(tp);
1180  if (nwid != nullptr) return nwid;
1181  }
1182  return nullptr;
1183 }
1184 
1185 void NWidgetContainer::AdjustPaddingForZoom()
1186 {
1187  for (const auto &child_wid : this->children) {
1188  child_wid->AdjustPaddingForZoom();
1189  }
1190  NWidgetBase::AdjustPaddingForZoom();
1191 }
1192 
1197 void NWidgetContainer::Add(std::unique_ptr<NWidgetBase> &&wid)
1198 {
1199  assert(wid != nullptr);
1200  wid->parent = this;
1201  this->children.push_back(std::move(wid));
1202 }
1203 
1205 {
1206  for (const auto &child_wid : this->children) {
1207  child_wid->FillWidgetLookup(widget_lookup);
1208  }
1209 }
1210 
1212 {
1213  for (const auto &child_wid : this->children) {
1214  child_wid->Draw(w);
1215  }
1216 
1217  DrawOutline(w, this);
1218 }
1219 
1221 {
1222  if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1223 
1224  for (const auto &child_wid : this->children) {
1225  NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
1226  if (nwid != nullptr) return nwid;
1227  }
1228  return nullptr;
1229 }
1230 
1235 {
1236 }
1237 
1238 void NWidgetStacked::AdjustPaddingForZoom()
1239 {
1240  for (const auto &child_wid : this->children) {
1241  child_wid->AdjustPaddingForZoom();
1242  }
1243  NWidgetContainer::AdjustPaddingForZoom();
1244 }
1245 
1247 {
1248  /* Zero size plane selected */
1249  if (this->shown_plane >= SZSP_BEGIN) {
1250  Dimension size = {0, 0};
1251  Dimension padding = {0, 0};
1252  Dimension fill = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1253  Dimension resize = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
1254  /* Here we're primarily interested in the value of resize */
1255  if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
1256 
1257  this->smallest_x = size.width;
1258  this->smallest_y = size.height;
1259  this->fill_x = fill.width;
1260  this->fill_y = fill.height;
1261  this->resize_x = resize.width;
1262  this->resize_y = resize.height;
1263  this->ApplyAspectRatio();
1264  return;
1265  }
1266 
1267  /* First sweep, recurse down and compute minimal size and filling. */
1268  this->smallest_x = 0;
1269  this->smallest_y = 0;
1270  this->fill_x = this->IsEmpty() ? 0 : 1;
1271  this->fill_y = this->IsEmpty() ? 0 : 1;
1272  this->resize_x = this->IsEmpty() ? 0 : 1;
1273  this->resize_y = this->IsEmpty() ? 0 : 1;
1274  for (const auto &child_wid : this->children) {
1275  child_wid->SetupSmallestSize(w);
1276 
1277  this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1278  this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1279  this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1280  this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1281  this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1282  this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1283  this->ApplyAspectRatio();
1284  }
1285 }
1286 
1287 void NWidgetStacked::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1288 {
1289  assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1290  this->StoreSizePosition(sizing, x, y, given_width, given_height);
1291 
1292  if (this->shown_plane >= SZSP_BEGIN) return;
1293 
1294  for (const auto &child_wid : this->children) {
1295  uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1296  uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1297  uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1298 
1299  uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1300  uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1301  uint child_pos_y = child_wid->padding.top;
1302 
1303  child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1304  }
1305 }
1306 
1308 {
1309  /* We need to update widget_lookup later. */
1310  this->widget_lookup = &widget_lookup;
1311 
1312  if (this->index >= 0) widget_lookup[this->index] = this;
1313  NWidgetContainer::FillWidgetLookup(widget_lookup);
1314  /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1315  if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(widget_lookup);
1316 }
1317 
1319 {
1320  if (this->shown_plane >= SZSP_BEGIN) return;
1321 
1322  assert(static_cast<size_t>(this->shown_plane) < this->children.size());
1323  this->children[shown_plane]->Draw(w);
1324  DrawOutline(w, this);
1325 }
1326 
1328 {
1329  if (this->shown_plane >= SZSP_BEGIN) return nullptr;
1330 
1331  if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1332 
1333  if (static_cast<size_t>(this->shown_plane) >= this->children.size()) return nullptr;
1334  return this->children[shown_plane]->GetWidgetFromPos(x, y);
1335 }
1336 
1343 {
1344  if (this->shown_plane == plane) return false;
1345  this->shown_plane = plane;
1346  /* In case widget IDs are repeated, make sure Window::GetWidget works on displayed widgets. */
1347  if (static_cast<size_t>(this->shown_plane) < this->children.size()) this->children[shown_plane]->FillWidgetLookup(*this->widget_lookup);
1348  return true;
1349 }
1350 
1351 NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags) : NWidgetContainer(tp)
1352 {
1353  this->flags = flags;
1354 }
1355 
1356 void NWidgetPIPContainer::AdjustPaddingForZoom()
1357 {
1358  this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1359  this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1360  this->pip_post = ScaleGUITrad(this->uz_pip_post);
1361  NWidgetContainer::AdjustPaddingForZoom();
1362 }
1363 
1373 void NWidgetPIPContainer::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
1374 {
1375  this->uz_pip_pre = pip_pre;
1376  this->uz_pip_inter = pip_inter;
1377  this->uz_pip_post = pip_post;
1378 
1379  this->pip_pre = ScaleGUITrad(this->uz_pip_pre);
1380  this->pip_inter = ScaleGUITrad(this->uz_pip_inter);
1381  this->pip_post = ScaleGUITrad(this->uz_pip_post);
1382 }
1383 
1393 void NWidgetPIPContainer::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
1394 {
1395  this->pip_ratio_pre = pip_ratio_pre;
1396  this->pip_ratio_inter = pip_ratio_inter;
1397  this->pip_ratio_post = pip_ratio_post;
1398 }
1399 
1402 {
1403 }
1404 
1406 {
1407  this->smallest_x = 0; // Sum of minimal size of all children.
1408  this->smallest_y = 0; // Biggest child.
1409  this->fill_x = 0; // smallest non-zero child widget fill step.
1410  this->fill_y = 1; // smallest common child fill step.
1411  this->resize_x = 0; // smallest non-zero child widget resize step.
1412  this->resize_y = 1; // smallest common child resize step.
1413  this->gaps = 0;
1414 
1415  /* 1a. Forward call, collect longest/widest child length. */
1416  uint longest = 0; // Longest child found.
1417  uint max_vert_fill = 0; // Biggest vertical fill step.
1418  for (const auto &child_wid : this->children) {
1419  child_wid->SetupSmallestSize(w);
1420  longest = std::max(longest, child_wid->smallest_x);
1421  max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(ST_SMALLEST));
1422  this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1423  if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->gaps++;
1424  }
1425  if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1426  /* 1b. Make the container higher if needed to accommodate all children nicely. */
1427  [[maybe_unused]] uint max_smallest = this->smallest_y + 3 * max_vert_fill; // Upper limit to computing smallest height.
1428  uint cur_height = this->smallest_y;
1429  for (;;) {
1430  for (const auto &child_wid : this->children) {
1431  uint step_size = child_wid->GetVerticalStepSize(ST_SMALLEST);
1432  uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1433  if (step_size > 1 && child_height < cur_height) { // Small step sizes or already fitting children are not interesting.
1434  uint remainder = (cur_height - child_height) % step_size;
1435  if (remainder > 0) { // Child did not fit entirely, widen the container.
1436  cur_height += step_size - remainder;
1437  assert(cur_height < max_smallest); // Safeguard against infinite height expansion.
1438  /* Remaining children will adapt to the new cur_height, thus speeding up the computation. */
1439  }
1440  }
1441  }
1442  if (this->smallest_y == cur_height) break;
1443  this->smallest_y = cur_height; // Smallest height got changed, try again.
1444  }
1445  /* 2. For containers that must maintain equal width, extend child minimal size. */
1446  for (const auto &child_wid : this->children) {
1447  child_wid->smallest_y = this->smallest_y - child_wid->padding.Vertical();
1448  child_wid->ApplyAspectRatio();
1449  longest = std::max(longest, child_wid->smallest_x);
1450  }
1451  if (this->flags & NC_EQUALSIZE) {
1452  for (const auto &child_wid : this->children) {
1453  if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1454  }
1455  }
1456  /* 3. Compute smallest, fill, and resize values of the container. */
1457  for (const auto &child_wid : this->children) {
1458  this->smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1459  if (child_wid->fill_x > 0) {
1460  if (this->fill_x == 0 || this->fill_x > child_wid->fill_x) this->fill_x = child_wid->fill_x;
1461  }
1462  this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
1463 
1464  if (child_wid->resize_x > 0) {
1465  if (this->resize_x == 0 || this->resize_x > child_wid->resize_x) this->resize_x = child_wid->resize_x;
1466  }
1467  this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
1468  }
1469  if (this->fill_x == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_x = 1;
1470  /* 4. Increase by required PIP space. */
1471  this->smallest_x += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1472 }
1473 
1474 void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1475 {
1476  assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1477 
1478  /* Compute additional width given to us. */
1479  uint additional_length = given_width - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1480  for (const auto &child_wid : this->children) {
1481  if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1482  }
1483 
1484  this->StoreSizePosition(sizing, x, y, given_width, given_height);
1485 
1486  /* In principle, the additional horizontal space is distributed evenly over the available resizable children. Due to step sizes, this may not always be feasible.
1487  * To make resizing work as good as possible, first children with biggest step sizes are done. These may get less due to rounding down.
1488  * 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
1489  * of the child with the smallest non-zero stepsize.
1490  *
1491  * 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
1492  * size and position, and directly call child->AssignSizePosition() with the computed values.
1493  * 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
1494  * then we call the child.
1495  */
1496 
1497  /* First loop: Find biggest stepsize, find number of children that want a piece of the pie, handle vertical size for all children,
1498  * handle horizontal size for non-resizing children.
1499  */
1500  int num_changing_childs = 0; // Number of children that can change size.
1501  uint biggest_stepsize = 0;
1502  for (const auto &child_wid : this->children) {
1503  uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1504  if (hor_step > 0) {
1505  if (!(flags & NC_BIGFIRST)) num_changing_childs++;
1506  biggest_stepsize = std::max(biggest_stepsize, hor_step);
1507  } else {
1508  child_wid->current_x = child_wid->smallest_x;
1509  }
1510 
1511  uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1512  child_wid->current_y = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1513  }
1514 
1515  /* First.5 loop: count how many children are of the biggest step size. */
1516  if ((flags & NC_BIGFIRST) && biggest_stepsize > 0) {
1517  for (const auto &child_wid : this->children) {
1518  uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1519  if (hor_step == biggest_stepsize) {
1520  num_changing_childs++;
1521  }
1522  }
1523  }
1524 
1525  /* Second loop: Allocate the additional horizontal space over the resizing children, starting with the biggest resize steps. */
1526  while (biggest_stepsize > 0) {
1527  uint next_biggest_stepsize = 0;
1528  for (const auto &child_wid : this->children) {
1529  uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1530  if (hor_step > biggest_stepsize) continue; // Already done
1531  if (hor_step == biggest_stepsize) {
1532  uint increment = additional_length / num_changing_childs;
1533  num_changing_childs--;
1534  if (hor_step > 1) increment -= increment % hor_step;
1535  child_wid->current_x = child_wid->smallest_x + increment;
1536  additional_length -= increment;
1537  continue;
1538  }
1539  next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1540  }
1541  biggest_stepsize = next_biggest_stepsize;
1542 
1543  if (num_changing_childs == 0 && (flags & NC_BIGFIRST) && biggest_stepsize > 0) {
1544  /* Second.5 loop: count how many children are of the updated biggest step size. */
1545  for (const auto &child_wid : this->children) {
1546  uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1547  if (hor_step == biggest_stepsize) {
1548  num_changing_childs++;
1549  }
1550  }
1551  }
1552  }
1553  assert(num_changing_childs == 0);
1554 
1555  uint pre = this->pip_pre;
1556  uint inter = this->pip_inter;
1557 
1558  if (additional_length > 0) {
1559  /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1560  * which is never explicitly needed. */
1561  int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1562  if (r > 0) {
1563  pre += this->pip_ratio_pre * additional_length / r;
1564  if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1565  }
1566  }
1567 
1568  /* Third loop: Compute position and call the child. */
1569  uint position = rtl ? this->current_x - pre : pre; // Place to put next child relative to origin of the container.
1570  for (const auto &child_wid : this->children) {
1571  uint child_width = child_wid->current_x;
1572  uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1573  uint child_y = y + child_wid->padding.top;
1574 
1575  child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1576  if (child_wid->current_x != 0) {
1577  uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1578  position = rtl ? position - padded_child_width : position + padded_child_width;
1579  }
1580  }
1581 }
1582 
1585 {
1586  this->type = NWID_HORIZONTAL_LTR;
1587 }
1588 
1589 void NWidgetHorizontalLTR::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool)
1590 {
1591  NWidgetHorizontal::AssignSizePosition(sizing, x, y, given_width, given_height, false);
1592 }
1593 
1596 {
1597 }
1598 
1600 {
1601  this->smallest_x = 0; // Biggest child.
1602  this->smallest_y = 0; // Sum of minimal size of all children.
1603  this->fill_x = 1; // smallest common child fill step.
1604  this->fill_y = 0; // smallest non-zero child widget fill step.
1605  this->resize_x = 1; // smallest common child resize step.
1606  this->resize_y = 0; // smallest non-zero child widget resize step.
1607  this->gaps = 0;
1608 
1609  /* 1a. Forward call, collect longest/widest child length. */
1610  uint highest = 0; // Highest child found.
1611  uint max_hor_fill = 0; // Biggest horizontal fill step.
1612  for (const auto &child_wid : this->children) {
1613  child_wid->SetupSmallestSize(w);
1614  highest = std::max(highest, child_wid->smallest_y);
1615  max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(ST_SMALLEST));
1616  this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1617  if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->gaps++;
1618  }
1619  if (this->gaps > 0) this->gaps--; // Number of gaps is number of widgets less one.
1620  /* 1b. Make the container wider if needed to accommodate all children nicely. */
1621  [[maybe_unused]] uint max_smallest = this->smallest_x + 3 * max_hor_fill; // Upper limit to computing smallest height.
1622  uint cur_width = this->smallest_x;
1623  for (;;) {
1624  for (const auto &child_wid : this->children) {
1625  uint step_size = child_wid->GetHorizontalStepSize(ST_SMALLEST);
1626  uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1627  if (step_size > 1 && child_width < cur_width) { // Small step sizes or already fitting children are not interesting.
1628  uint remainder = (cur_width - child_width) % step_size;
1629  if (remainder > 0) { // Child did not fit entirely, widen the container.
1630  cur_width += step_size - remainder;
1631  assert(cur_width < max_smallest); // Safeguard against infinite width expansion.
1632  /* Remaining children will adapt to the new cur_width, thus speeding up the computation. */
1633  }
1634  }
1635  }
1636  if (this->smallest_x == cur_width) break;
1637  this->smallest_x = cur_width; // Smallest width got changed, try again.
1638  }
1639  /* 2. For containers that must maintain equal width, extend children minimal size. */
1640  for (const auto &child_wid : this->children) {
1641  child_wid->smallest_x = this->smallest_x - child_wid->padding.Horizontal();
1642  child_wid->ApplyAspectRatio();
1643  highest = std::max(highest, child_wid->smallest_y);
1644  }
1645  if (this->flags & NC_EQUALSIZE) {
1646  for (const auto &child_wid : this->children) {
1647  if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1648  }
1649  }
1650  /* 3. Compute smallest, fill, and resize values of the container. */
1651  for (const auto &child_wid : this->children) {
1652  this->smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1653  if (child_wid->fill_y > 0) {
1654  if (this->fill_y == 0 || this->fill_y > child_wid->fill_y) this->fill_y = child_wid->fill_y;
1655  }
1656  this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
1657 
1658  if (child_wid->resize_y > 0) {
1659  if (this->resize_y == 0 || this->resize_y > child_wid->resize_y) this->resize_y = child_wid->resize_y;
1660  }
1661  this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
1662  }
1663  if (this->fill_y == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_y = 1;
1664  /* 4. Increase by required PIP space. */
1665  this->smallest_y += this->pip_pre + this->gaps * this->pip_inter + this->pip_post;
1666 }
1667 
1668 void NWidgetVertical::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
1669 {
1670  assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1671 
1672  /* Compute additional height given to us. */
1673  uint additional_length = given_height - (this->pip_pre + this->gaps * this->pip_inter + this->pip_post);
1674  for (const auto &child_wid : this->children) {
1675  if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1676  }
1677 
1678  this->StoreSizePosition(sizing, x, y, given_width, given_height);
1679 
1680  /* Like the horizontal container, the vertical container also distributes additional height evenly, starting with the children with the biggest resize steps.
1681  * It also stores computed widths and heights into current_x and current_y values of the child.
1682  */
1683 
1684  /* 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. */
1685  int num_changing_childs = 0; // Number of children that can change size.
1686  uint biggest_stepsize = 0;
1687  for (const auto &child_wid : this->children) {
1688  uint vert_step = child_wid->GetVerticalStepSize(sizing);
1689  if (vert_step > 0) {
1690  if (!(flags & NC_BIGFIRST)) num_changing_childs++;
1691  biggest_stepsize = std::max(biggest_stepsize, vert_step);
1692  } else {
1693  child_wid->current_y = child_wid->smallest_y;
1694  }
1695 
1696  uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1697  child_wid->current_x = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1698  }
1699 
1700  /* First.5 loop: count how many children are of the biggest step size. */
1701  if ((this->flags & NC_BIGFIRST) && biggest_stepsize > 0) {
1702  for (const auto &child_wid : this->children) {
1703  uint vert_step = child_wid->GetVerticalStepSize(sizing);
1704  if (vert_step == biggest_stepsize) {
1705  num_changing_childs++;
1706  }
1707  }
1708  }
1709 
1710  /* Second loop: Allocate the additional vertical space over the resizing children, starting with the biggest resize steps. */
1711  while (biggest_stepsize > 0) {
1712  uint next_biggest_stepsize = 0;
1713  for (const auto &child_wid : this->children) {
1714  uint vert_step = child_wid->GetVerticalStepSize(sizing);
1715  if (vert_step > biggest_stepsize) continue; // Already done
1716  if (vert_step == biggest_stepsize) {
1717  uint increment = additional_length / num_changing_childs;
1718  num_changing_childs--;
1719  if (vert_step > 1) increment -= increment % vert_step;
1720  child_wid->current_y = child_wid->smallest_y + increment;
1721  additional_length -= increment;
1722  continue;
1723  }
1724  next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1725  }
1726  biggest_stepsize = next_biggest_stepsize;
1727 
1728  if (num_changing_childs == 0 && (flags & NC_BIGFIRST) && biggest_stepsize > 0) {
1729  /* Second.5 loop: count how many children are of the updated biggest step size. */
1730  for (const auto &child_wid : this->children) {
1731  uint vert_step = child_wid->GetVerticalStepSize(sizing);
1732  if (vert_step == biggest_stepsize) {
1733  num_changing_childs++;
1734  }
1735  }
1736  }
1737  }
1738  assert(num_changing_childs == 0);
1739 
1740  uint pre = this->pip_pre;
1741  uint inter = this->pip_inter;
1742 
1743  if (additional_length > 0) {
1744  /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post
1745  * which is never explicitly needed. */
1746  int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post;
1747  if (r > 0) {
1748  pre += this->pip_ratio_pre * additional_length / r;
1749  if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r;
1750  }
1751  }
1752 
1753  /* Third loop: Compute position and call the child. */
1754  uint position = pre; // Place to put next child relative to origin of the container.
1755  for (const auto &child_wid : this->children) {
1756  uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1757  uint child_height = child_wid->current_y;
1758 
1759  child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1760  if (child_wid->current_y != 0) {
1761  position += child_height + child_wid->padding.Vertical() + inter;
1762  }
1763  }
1764 }
1765 
1772 {
1773  this->SetMinimalSize(width, height);
1774  this->SetResize(0, 0);
1775 }
1776 
1778 {
1779  this->smallest_x = this->min_x;
1780  this->smallest_y = this->min_y;
1781  this->ApplyAspectRatio();
1782 }
1783 
1785 {
1786 }
1787 
1789 {
1790  /* Spacer widget is never normally visible. */
1791 
1792  if (_draw_widget_outlines && this->current_x != 0 && this->current_y != 0) {
1793  /* Spacers indicate a potential design issue, so get extra highlighting. */
1794  GfxFillRect(this->GetCurrentRect(), PC_WHITE, FILLRECT_CHECKER);
1795 
1796  DrawOutline(w, this);
1797  }
1798 }
1799 
1800 void NWidgetSpacer::SetDirty(const Window *) const
1801 {
1802  /* Spacer widget never need repainting. */
1803 }
1804 
1806 {
1807  return nullptr;
1808 }
1809 
1810 NWidgetMatrix::NWidgetMatrix(Colours colour, WidgetID index) : NWidgetPIPContainer(NWID_MATRIX, NC_EQUALSIZE), index(index), clicked(-1), count(-1)
1811 {
1812  this->colour = colour;
1813 }
1814 
1819 void NWidgetMatrix::SetClicked(int clicked)
1820 {
1821  this->clicked = clicked;
1822  if (this->clicked >= 0 && this->sb != nullptr && this->widgets_x != 0) {
1823  int vpos = (this->clicked / this->widgets_x) * this->widget_h; // Vertical position of the top.
1824  /* Need to scroll down -> Scroll to the bottom.
1825  * However, last entry has no 'this->pip_inter' underneath, and we must stay below this->sb->GetCount() */
1826  if (this->sb->GetPosition() < vpos) vpos += this->widget_h - this->pip_inter - 1;
1827  this->sb->ScrollTowards(vpos);
1828  }
1829 }
1830 
1837 {
1838  this->count = count;
1839 
1840  if (this->sb == nullptr || this->widgets_x == 0) return;
1841 
1842  /* We need to get the number of pixels the matrix is high/wide.
1843  * So, determine the number of rows/columns based on the number of
1844  * columns/rows (one is constant/unscrollable).
1845  * Then multiply that by the height of a widget, and add the pre
1846  * and post spacing "offsets". */
1847  count = CeilDiv(count, this->sb->IsVertical() ? this->widgets_x : this->widgets_y);
1848  count *= (this->sb->IsVertical() ? this->children.front()->smallest_y : this->children.front()->smallest_x) + this->pip_inter;
1849  if (count > 0) count -= this->pip_inter; // We counted an inter too much in the multiplication above
1850  count += this->pip_pre + this->pip_post;
1851  this->sb->SetCount(count);
1852  this->sb->SetCapacity(this->sb->IsVertical() ? this->current_y : this->current_x);
1853  this->sb->SetStepSize(this->sb->IsVertical() ? this->widget_h : this->widget_w);
1854 }
1855 
1861 {
1862  this->sb = sb;
1863 }
1864 
1870 {
1871  return this->current_element;
1872 }
1873 
1875 {
1876  assert(this->children.size() == 1);
1877 
1878  this->children.front()->SetupSmallestSize(w);
1879 
1880  Dimension padding = { (uint)this->pip_pre + this->pip_post, (uint)this->pip_pre + this->pip_post};
1881  Dimension size = {this->children.front()->smallest_x + padding.width, this->children.front()->smallest_y + padding.height};
1882  Dimension fill = {0, 0};
1883  Dimension resize = {this->pip_inter + this->children.front()->smallest_x, this->pip_inter + this->children.front()->smallest_y};
1884 
1885  if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
1886 
1887  this->smallest_x = size.width;
1888  this->smallest_y = size.height;
1889  this->fill_x = fill.width;
1890  this->fill_y = fill.height;
1891  this->resize_x = resize.width;
1892  this->resize_y = resize.height;
1893  this->ApplyAspectRatio();
1894 }
1895 
1896 void NWidgetMatrix::AssignSizePosition(SizingType, int x, int y, uint given_width, uint given_height, bool)
1897 {
1898  assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1899 
1900  this->pos_x = x;
1901  this->pos_y = y;
1902  this->current_x = given_width;
1903  this->current_y = given_height;
1904 
1905  /* Determine the size of the widgets, and the number of visible widgets on each of the axis. */
1906  this->widget_w = this->children.front()->smallest_x + this->pip_inter;
1907  this->widget_h = this->children.front()->smallest_y + this->pip_inter;
1908 
1909  /* Account for the pip_inter is between widgets, so we need to account for that when
1910  * the division assumes pip_inter is used for all widgets. */
1911  this->widgets_x = CeilDiv(this->current_x - this->pip_pre - this->pip_post + this->pip_inter, this->widget_w);
1912  this->widgets_y = CeilDiv(this->current_y - this->pip_pre - this->pip_post + this->pip_inter, this->widget_h);
1913 
1914  /* When resizing, update the scrollbar's count. E.g. with a vertical
1915  * scrollbar becoming wider or narrower means the amount of rows in
1916  * the scrollbar becomes respectively smaller or higher. */
1917  this->SetCount(this->count);
1918 }
1919 
1921 {
1922  if (this->index >= 0) widget_lookup[this->index] = this;
1923  NWidgetContainer::FillWidgetLookup(widget_lookup);
1924 }
1925 
1927 {
1928  /* Falls outside of the matrix widget. */
1929  if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return nullptr;
1930 
1931  int start_x, start_y, base_offs_x, base_offs_y;
1932  this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
1933 
1934  bool rtl = _current_text_dir == TD_RTL;
1935 
1936  int widget_col = (rtl ?
1937  -x + (int)this->pip_post + this->pos_x + base_offs_x + this->widget_w - 1 - (int)this->pip_inter :
1938  x - (int)this->pip_pre - this->pos_x - base_offs_x
1939  ) / this->widget_w;
1940 
1941  int widget_row = (y - base_offs_y - (int)this->pip_pre - this->pos_y) / this->widget_h;
1942 
1943  this->current_element = (widget_row + start_y) * this->widgets_x + start_x + widget_col;
1944  if (this->current_element >= this->count) return nullptr;
1945 
1946  NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
1947  assert(child != nullptr);
1949  this->pos_x + (rtl ? this->pip_post - widget_col * this->widget_w : this->pip_pre + widget_col * this->widget_w) + base_offs_x,
1950  this->pos_y + this->pip_pre + widget_row * this->widget_h + base_offs_y,
1951  child->smallest_x, child->smallest_y, rtl);
1952 
1953  return child->GetWidgetFromPos(x, y);
1954 }
1955 
1956 /* virtual */ void NWidgetMatrix::Draw(const Window *w)
1957 {
1958  /* Fill the background. */
1959  GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, SHADE_LIGHT));
1960 
1961  /* Set up a clipping area for the previews. */
1962  bool rtl = _current_text_dir == TD_RTL;
1963  DrawPixelInfo tmp_dpi;
1964  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;
1965 
1966  {
1967  AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi);
1968 
1969  /* Get the appropriate offsets so we can draw the right widgets. */
1970  NWidgetCore *child = dynamic_cast<NWidgetCore *>(this->children.front().get());
1971  assert(child != nullptr);
1972  int start_x, start_y, base_offs_x, base_offs_y;
1973  this->GetScrollOffsets(start_x, start_y, base_offs_x, base_offs_y);
1974 
1975  int offs_y = base_offs_y;
1976  for (int y = start_y; y < start_y + this->widgets_y + 1; y++, offs_y += this->widget_h) {
1977  /* Are we within bounds? */
1978  if (offs_y + child->smallest_y <= 0) continue;
1979  if (offs_y >= (int)this->current_y) break;
1980 
1981  /* We've passed our amount of widgets. */
1982  if (y * this->widgets_x >= this->count) break;
1983 
1984  int offs_x = base_offs_x;
1985  for (int x = start_x; x < start_x + this->widgets_x + 1; x++, offs_x += rtl ? -this->widget_w : this->widget_w) {
1986  /* Are we within bounds? */
1987  if (offs_x + child->smallest_x <= 0) continue;
1988  if (offs_x >= (int)this->current_x) continue;
1989 
1990  /* Do we have this many widgets? */
1991  this->current_element = y * this->widgets_x + x;
1992  if (this->current_element >= this->count) break;
1993 
1994  child->AssignSizePosition(ST_RESIZE, offs_x, offs_y, child->smallest_x, child->smallest_y, rtl);
1995  child->SetLowered(this->clicked == this->current_element);
1996  child->Draw(w);
1997  }
1998  }
1999  }
2000 
2001  DrawOutline(w, this);
2002 }
2003 
2011 void NWidgetMatrix::GetScrollOffsets(int &start_x, int &start_y, int &base_offs_x, int &base_offs_y)
2012 {
2013  base_offs_x = _current_text_dir == TD_RTL ? this->widget_w * (this->widgets_x - 1) : 0;
2014  base_offs_y = 0;
2015  start_x = 0;
2016  start_y = 0;
2017  if (this->sb != nullptr) {
2018  if (this->sb->IsVertical()) {
2019  start_y = this->sb->GetPosition() / this->widget_h;
2020  base_offs_y += -this->sb->GetPosition() + start_y * this->widget_h;
2021  } else {
2022  start_x = this->sb->GetPosition() / this->widget_w;
2023  int sub_x = this->sb->GetPosition() - start_x * this->widget_w;
2024  if (_current_text_dir == TD_RTL) {
2025  base_offs_x += sub_x;
2026  } else {
2027  base_offs_x -= sub_x;
2028  }
2029  }
2030  }
2031 }
2032 
2042 NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr<NWidgetPIPContainer> &&child) : NWidgetCore(tp, colour, index, 1, 1, 0x0, STR_NULL)
2043 {
2044  assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME);
2045  this->child = std::move(child);
2046  if (this->child != nullptr) this->child->parent = this;
2047  this->SetAlignment(SA_TOP | SA_LEFT);
2048 }
2049 
2057 void NWidgetBackground::Add(std::unique_ptr<NWidgetBase> &&nwid)
2058 {
2059  if (this->child == nullptr) {
2060  this->child = std::make_unique<NWidgetVertical>();
2061  }
2062  nwid->parent = this->child.get();
2063  this->child->Add(std::move(nwid));
2064 }
2065 
2076 void NWidgetBackground::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post)
2077 {
2078  if (this->child == nullptr) {
2079  this->child = std::make_unique<NWidgetVertical>();
2080  }
2081  this->child->parent = this;
2082  this->child->SetPIP(pip_pre, pip_inter, pip_post);
2083 }
2084 
2095 void NWidgetBackground::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post)
2096 {
2097  if (this->child == nullptr) {
2098  this->child = std::make_unique<NWidgetVertical>();
2099  }
2100  this->child->parent = this;
2101  this->child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2102 }
2103 
2104 void NWidgetBackground::AdjustPaddingForZoom()
2105 {
2106  if (child != nullptr) child->AdjustPaddingForZoom();
2107  NWidgetCore::AdjustPaddingForZoom();
2108 }
2109 
2111 {
2112  if (this->child != nullptr) {
2113  this->child->SetupSmallestSize(w);
2114 
2115  this->smallest_x = this->child->smallest_x;
2116  this->smallest_y = this->child->smallest_y;
2117  this->fill_x = this->child->fill_x;
2118  this->fill_y = this->child->fill_y;
2119  this->resize_x = this->child->resize_x;
2120  this->resize_y = this->child->resize_y;
2121 
2122  /* Don't apply automatic padding if there is no child widget. */
2123  if (w == nullptr) return;
2124 
2125  if (this->type == WWT_FRAME) {
2126  /* Account for the size of the frame's text if that exists */
2127  this->child->padding = WidgetDimensions::scaled.frametext;
2128  this->child->padding.top = std::max<uint8_t>(WidgetDimensions::scaled.frametext.top, this->widget_data != STR_NULL ? GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.frametext.top / 2 : 0);
2129 
2130  this->smallest_x += this->child->padding.Horizontal();
2131  this->smallest_y += this->child->padding.Vertical();
2132 
2133  if (this->index >= 0) w->SetStringParameters(this->index);
2134  this->smallest_x = std::max(this->smallest_x, GetStringBoundingBox(this->widget_data, this->text_size).width + WidgetDimensions::scaled.frametext.Horizontal());
2135  } else if (this->type == WWT_INSET) {
2136  /* Apply automatic padding for bevel thickness. */
2137  this->child->padding = WidgetDimensions::scaled.bevel;
2138 
2139  this->smallest_x += this->child->padding.Horizontal();
2140  this->smallest_y += this->child->padding.Vertical();
2141  }
2142  this->ApplyAspectRatio();
2143  } else {
2144  Dimension d = {this->min_x, this->min_y};
2145  Dimension fill = {this->fill_x, this->fill_y};
2146  Dimension resize = {this->resize_x, this->resize_y};
2147  if (w != nullptr) { // A non-nullptr window pointer acts as switch to turn dynamic widget size on.
2148  if (this->type == WWT_FRAME || this->type == WWT_INSET) {
2149  if (this->index >= 0) w->SetStringParameters(this->index);
2150  Dimension background = GetStringBoundingBox(this->widget_data, this->text_size);
2151  background.width += (this->type == WWT_FRAME) ? (WidgetDimensions::scaled.frametext.Horizontal()) : (WidgetDimensions::scaled.inset.Horizontal());
2152  d = maxdim(d, background);
2153  }
2154  if (this->index >= 0) {
2156  switch (this->type) {
2157  default: NOT_REACHED();
2161  }
2162  w->UpdateWidgetSize(this->index, d, padding, fill, resize);
2163  }
2164  }
2165  this->smallest_x = d.width;
2166  this->smallest_y = d.height;
2167  this->fill_x = fill.width;
2168  this->fill_y = fill.height;
2169  this->resize_x = resize.width;
2170  this->resize_y = resize.height;
2171  this->ApplyAspectRatio();
2172  }
2173 }
2174 
2175 void NWidgetBackground::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
2176 {
2177  this->StoreSizePosition(sizing, x, y, given_width, given_height);
2178 
2179  if (this->child != nullptr) {
2180  uint x_offset = (rtl ? this->child->padding.right : this->child->padding.left);
2181  uint width = given_width - this->child->padding.Horizontal();
2182  uint height = given_height - this->child->padding.Vertical();
2183  this->child->AssignSizePosition(sizing, x + x_offset, y + this->child->padding.top, width, height, rtl);
2184  }
2185 }
2186 
2188 {
2189  if (this->index >= 0) widget_lookup[this->index] = this;
2190  if (this->child != nullptr) this->child->FillWidgetLookup(widget_lookup);
2191 }
2192 
2194 {
2195  if (this->current_x == 0 || this->current_y == 0) return;
2196 
2197  Rect r = this->GetCurrentRect();
2198 
2199  const DrawPixelInfo *dpi = _cur_dpi;
2200  if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2201 
2202  switch (this->type) {
2203  case WWT_PANEL:
2204  assert(this->widget_data == 0);
2205  DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, this->IsLowered() ? FR_LOWERED : FR_NONE);
2206  break;
2207 
2208  case WWT_FRAME:
2209  if (this->index >= 0) w->SetStringParameters(this->index);
2210  DrawFrame(r, this->colour, this->text_colour, this->widget_data, this->align, this->text_size);
2211  break;
2212 
2213  case WWT_INSET:
2214  if (this->index >= 0) w->SetStringParameters(this->index);
2215  DrawInset(r, this->colour, this->text_colour, this->widget_data, this->align, this->text_size);
2216  break;
2217 
2218  default:
2219  NOT_REACHED();
2220  }
2221 
2222  if (this->index >= 0) w->DrawWidget(r, this->index);
2223  if (this->child != nullptr) this->child->Draw(w);
2224 
2225  if (this->IsDisabled()) {
2227  }
2228 
2229  DrawOutline(w, this);
2230 }
2231 
2233 {
2234  NWidgetCore *nwid = nullptr;
2235  if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) {
2236  if (this->child != nullptr) nwid = this->child->GetWidgetFromPos(x, y);
2237  if (nwid == nullptr) nwid = this;
2238  }
2239  return nwid;
2240 }
2241 
2243 {
2244  NWidgetBase *nwid = nullptr;
2245  if (this->child != nullptr) nwid = this->child->GetWidgetOfType(tp);
2246  if (nwid == nullptr && this->type == tp) nwid = this;
2247  return nwid;
2248 }
2249 
2250 NWidgetViewport::NWidgetViewport(WidgetID index) : NWidgetCore(NWID_VIEWPORT, INVALID_COLOUR, index, 1, 1, 0x0, STR_NULL)
2251 {
2252 }
2253 
2255 {
2256  this->smallest_x = this->min_x;
2257  this->smallest_y = this->min_y;
2258  this->ApplyAspectRatio();
2259 }
2260 
2262 {
2263  if (this->current_x == 0 || this->current_y == 0) return;
2264 
2265  if (this->disp_flags & ND_NO_TRANSPARENCY) {
2267  _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_TEXT); // Disable all transparency, except textual stuff
2268  w->DrawViewport();
2269  _transparency_opt = to_backup;
2270  } else {
2271  w->DrawViewport();
2272  }
2273 
2274  /* Optionally shade the viewport. */
2275  if (this->disp_flags & (ND_SHADE_GREY | ND_SHADE_DIMMED)) {
2277  }
2278 
2279  DrawOutline(w, this);
2280 }
2281 
2288 void NWidgetViewport::InitializeViewport(Window *w, std::variant<TileIndex, VehicleID> focus, ZoomLevel zoom)
2289 {
2290  InitializeWindowViewport(w, this->pos_x, this->pos_y, this->current_x, this->current_y, focus, zoom);
2291 }
2292 
2298 {
2299  Viewport *vp = w->viewport;
2300  if (vp != nullptr) {
2301  vp->left = w->left + this->pos_x;
2302  vp->top = w->top + this->pos_y;
2303  vp->width = this->current_x;
2304  vp->height = this->current_y;
2305 
2306  vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
2307  vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
2308  }
2309 }
2310 
2320 Scrollbar::size_type Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, WidgetID widget, int padding, int line_height) const
2321 {
2322  int pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
2323  if (pos != INT_MAX) pos += this->GetPosition();
2324  return (pos < 0 || pos >= this->GetCount()) ? Scrollbar::npos : pos;
2325 }
2326 
2341 EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16_t keycode) const
2342 {
2343  int new_pos = list_position;
2344  switch (keycode) {
2345  case WKC_UP:
2346  /* scroll up by one */
2347  new_pos--;
2348  break;
2349 
2350  case WKC_DOWN:
2351  /* scroll down by one */
2352  new_pos++;
2353  break;
2354 
2355  case WKC_PAGEUP:
2356  /* scroll up a page */
2357  new_pos -= this->GetCapacity();
2358  break;
2359 
2360  case WKC_PAGEDOWN:
2361  /* scroll down a page */
2362  new_pos += this->GetCapacity();
2363  break;
2364 
2365  case WKC_HOME:
2366  /* jump to beginning */
2367  new_pos = 0;
2368  break;
2369 
2370  case WKC_END:
2371  /* jump to end */
2372  new_pos = this->GetCount() - 1;
2373  break;
2374 
2375  default:
2376  return ES_NOT_HANDLED;
2377  }
2378 
2379  /* If there are no elements, there is nothing to scroll/update. */
2380  if (this->GetCount() != 0) {
2381  list_position = Clamp(new_pos, 0, this->GetCount() - 1);
2382  }
2383  return ES_HANDLED;
2384 }
2385 
2386 
2394 void Scrollbar::SetCapacityFromWidget(Window *w, WidgetID widget, int padding)
2395 {
2396  NWidgetBase *nwid = w->GetWidget<NWidgetBase>(widget);
2397  if (this->IsVertical()) {
2398  this->SetCapacity(((int)nwid->current_y - padding) / (int)nwid->resize_y);
2399  } else {
2400  this->SetCapacity(((int)nwid->current_x - padding) / (int)nwid->resize_x);
2401  }
2402 }
2403 
2411 Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
2412 {
2413  const int count = sb.GetCount() * resize_step;
2414  const int position = sb.GetPosition() * resize_step;
2415 
2416  if (sb.IsVertical()) {
2417  r.top -= position;
2418  r.bottom = r.top + count;
2419  } else {
2420  bool rtl = _current_text_dir == TD_RTL;
2421  if (rtl) {
2422  r.right += position;
2423  r.left = r.right - count;
2424  } else {
2425  r.left -= position;
2426  r.right = r.left + count;
2427  }
2428  }
2429 
2430  return r;
2431 }
2432 
2439 NWidgetScrollbar::NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index) : NWidgetCore(tp, colour, index, 1, 1, 0x0, STR_NULL), Scrollbar(tp != NWID_HSCROLLBAR)
2440 {
2441  assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR);
2442 
2443  switch (this->type) {
2444  case NWID_HSCROLLBAR:
2445  this->SetResize(1, 0);
2446  this->SetFill(1, 0);
2447  this->SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2448  break;
2449 
2450  case NWID_VSCROLLBAR:
2451  this->SetResize(0, 1);
2452  this->SetFill(0, 1);
2453  this->SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2454  break;
2455 
2456  default: NOT_REACHED();
2457  }
2458 }
2459 
2461 {
2462  this->min_x = 0;
2463  this->min_y = 0;
2464 
2465  switch (this->type) {
2466  case NWID_HSCROLLBAR:
2467  this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2468  break;
2469 
2470  case NWID_VSCROLLBAR:
2471  this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2472  break;
2473 
2474  default: NOT_REACHED();
2475  }
2476 
2477  this->smallest_x = this->min_x;
2478  this->smallest_y = this->min_y;
2479 }
2480 
2482 {
2483  if (this->current_x == 0 || this->current_y == 0) return;
2484 
2485  Rect r = this->GetCurrentRect();
2486 
2487  const DrawPixelInfo *dpi = _cur_dpi;
2488  if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return;
2489 
2490  bool up_lowered = HasBit(this->disp_flags, NDB_SCROLLBAR_UP);
2491  bool down_lowered = HasBit(this->disp_flags, NDB_SCROLLBAR_DOWN);
2492  bool middle_lowered = !(this->disp_flags & ND_SCROLLBAR_BTN) && w->mouse_capture_widget == this->index;
2493 
2494  if (this->type == NWID_HSCROLLBAR) {
2495  DrawHorizontalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2496  } else {
2497  DrawVerticalScrollbar(r, this->colour, up_lowered, middle_lowered, down_lowered, this);
2498  }
2499 
2500  if (this->IsDisabled()) {
2502  }
2503 
2504  DrawOutline(w, this);
2505 }
2506 
2507 /* static */ void NWidgetScrollbar::InvalidateDimensionCache()
2508 {
2509  vertical_dimension.width = vertical_dimension.height = 0;
2510  horizontal_dimension.width = horizontal_dimension.height = 0;
2511 }
2512 
2513 /* static */ Dimension NWidgetScrollbar::GetVerticalDimension()
2514 {
2515  if (vertical_dimension.width == 0) {
2516  vertical_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_UP), GetScaledSpriteSize(SPR_ARROW_DOWN));
2519  }
2520  return vertical_dimension;
2521 }
2522 
2523 /* static */ Dimension NWidgetScrollbar::GetHorizontalDimension()
2524 {
2525  if (horizontal_dimension.width == 0) {
2526  horizontal_dimension = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2529  }
2530  return horizontal_dimension;
2531 }
2532 
2535 
2538 {
2539  shadebox_dimension.width = shadebox_dimension.height = 0;
2540  debugbox_dimension.width = debugbox_dimension.height = 0;
2541  defsizebox_dimension.width = defsizebox_dimension.height = 0;
2542  stickybox_dimension.width = stickybox_dimension.height = 0;
2543  resizebox_dimension.width = resizebox_dimension.height = 0;
2544  closebox_dimension.width = closebox_dimension.height = 0;
2545  dropdown_dimension.width = dropdown_dimension.height = 0;
2546 }
2547 
2555 
2564 NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, uint32_t data, StringID tip) : NWidgetCore(tp, colour, index, 1, 1, data, tip)
2565 {
2566  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);
2567  this->min_x = 0;
2568  this->min_y = 0;
2569  this->SetResize(0, 0);
2570 
2571  switch (tp) {
2572  case WWT_EMPTY:
2573  break;
2574 
2575  case WWT_TEXT:
2576  this->SetFill(0, 0);
2578  break;
2579 
2580  case WWT_PUSHBTN:
2581  case WWT_IMGBTN:
2582  case WWT_PUSHIMGBTN:
2583  case WWT_IMGBTN_2:
2584  case WWT_TEXTBTN:
2585  case WWT_PUSHTXTBTN:
2586  case WWT_TEXTBTN_2:
2587  case WWT_LABEL:
2588  case WWT_MATRIX:
2589  case NWID_BUTTON_DROPDOWN:
2590  case NWID_PUSHBUTTON_DROPDOWN:
2591  this->SetFill(0, 0);
2592  break;
2593 
2594  case WWT_ARROWBTN:
2595  case WWT_PUSHARROWBTN:
2596  this->SetFill(0, 0);
2597  this->SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2598  break;
2599 
2600  case WWT_EDITBOX:
2601  this->SetFill(0, 0);
2602  break;
2603 
2604  case WWT_CAPTION:
2605  this->SetFill(1, 0);
2606  this->SetResize(1, 0);
2608  this->SetMinimalTextLines(1, WidgetDimensions::unscaled.captiontext.Vertical(), FS_NORMAL);
2609  this->SetDataTip(data, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2610  break;
2611 
2612  case WWT_STICKYBOX:
2613  this->SetFill(0, 0);
2615  this->SetDataTip(STR_NULL, STR_TOOLTIP_STICKY);
2616  this->SetAspect(this->min_x, this->min_y);
2617  break;
2618 
2619  case WWT_SHADEBOX:
2620  this->SetFill(0, 0);
2622  this->SetDataTip(STR_NULL, STR_TOOLTIP_SHADE);
2623  this->SetAspect(this->min_x, this->min_y);
2624  break;
2625 
2626  case WWT_DEBUGBOX:
2627  this->SetFill(0, 0);
2629  this->SetDataTip(STR_NULL, STR_TOOLTIP_DEBUG);
2630  this->SetAspect(this->min_x, this->min_y);
2631  break;
2632 
2633  case WWT_DEFSIZEBOX:
2634  this->SetFill(0, 0);
2636  this->SetDataTip(STR_NULL, STR_TOOLTIP_DEFSIZE);
2637  this->SetAspect(this->min_x, this->min_y);
2638  break;
2639 
2640  case WWT_RESIZEBOX:
2641  this->SetFill(0, 0);
2643  this->SetDataTip(RWV_SHOW_BEVEL, STR_TOOLTIP_RESIZE);
2644  break;
2645 
2646  case WWT_CLOSEBOX:
2647  this->SetFill(0, 0);
2649  this->SetDataTip(STR_NULL, STR_TOOLTIP_CLOSE_WINDOW);
2650  this->SetAspect(this->min_x, this->min_y);
2651  break;
2652 
2653  case WWT_DROPDOWN:
2654  this->SetFill(0, 0);
2656  this->SetAlignment(SA_TOP | SA_LEFT);
2657  break;
2658 
2659  default:
2660  NOT_REACHED();
2661  }
2662 }
2663 
2665 {
2666  Dimension padding = {0, 0};
2667  Dimension size = {this->min_x, this->min_y};
2668  Dimension fill = {this->fill_x, this->fill_y};
2669  Dimension resize = {this->resize_x, this->resize_y};
2670  switch (this->type) {
2671  case WWT_EMPTY: {
2672  break;
2673  }
2674  case WWT_MATRIX: {
2676  break;
2677  }
2678  case WWT_SHADEBOX: {
2680  if (NWidgetLeaf::shadebox_dimension.width == 0) {
2681  NWidgetLeaf::shadebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_SHADE), GetScaledSpriteSize(SPR_WINDOW_UNSHADE));
2683  NWidgetLeaf::shadebox_dimension.height += padding.height;
2684  }
2686  break;
2687  }
2688  case WWT_DEBUGBOX:
2691  if (NWidgetLeaf::debugbox_dimension.width == 0) {
2694  NWidgetLeaf::debugbox_dimension.height += padding.height;
2695  }
2697  } else {
2698  /* If the setting is disabled we don't want to see it! */
2699  size.width = 0;
2700  fill.width = 0;
2701  resize.width = 0;
2702  }
2703  break;
2704 
2705  case WWT_STICKYBOX: {
2707  if (NWidgetLeaf::stickybox_dimension.width == 0) {
2710  NWidgetLeaf::stickybox_dimension.height += padding.height;
2711  }
2713  break;
2714  }
2715 
2716  case WWT_DEFSIZEBOX: {
2718  if (NWidgetLeaf::defsizebox_dimension.width == 0) {
2721  NWidgetLeaf::defsizebox_dimension.height += padding.height;
2722  }
2724  break;
2725  }
2726 
2727  case WWT_RESIZEBOX: {
2729  if (NWidgetLeaf::resizebox_dimension.width == 0) {
2730  NWidgetLeaf::resizebox_dimension = maxdim(GetScaledSpriteSize(SPR_WINDOW_RESIZE_LEFT), GetScaledSpriteSize(SPR_WINDOW_RESIZE_RIGHT));
2732  NWidgetLeaf::resizebox_dimension.height += padding.height;
2733  }
2735  break;
2736  }
2737  case WWT_EDITBOX: {
2738  Dimension sprite_size = GetScaledSpriteSize(_current_text_dir == TD_RTL ? SPR_IMG_DELETE_RIGHT : SPR_IMG_DELETE_LEFT);
2739  size.width = std::max(size.width, ScaleGUITrad(30) + sprite_size.width);
2740  size.height = std::max(sprite_size.height, GetStringBoundingBox("_").height + WidgetDimensions::scaled.framerect.Vertical());
2741  }
2742  [[fallthrough]];
2743  case WWT_PUSHBTN: {
2745  break;
2746  }
2747  case WWT_IMGBTN:
2748  case WWT_IMGBTN_2:
2749  case WWT_PUSHIMGBTN: {
2752  if (this->type == WWT_IMGBTN_2) d2 = maxdim(d2, GetScaledSpriteSize(this->widget_data + 1));
2753  d2.width += padding.width;
2754  d2.height += padding.height;
2755  size = maxdim(size, d2);
2756  break;
2757  }
2758  case WWT_ARROWBTN:
2759  case WWT_PUSHARROWBTN: {
2761  Dimension d2 = maxdim(GetScaledSpriteSize(SPR_ARROW_LEFT), GetScaledSpriteSize(SPR_ARROW_RIGHT));
2762  d2.width += padding.width;
2763  d2.height += padding.height;
2764  size = maxdim(size, d2);
2765  break;
2766  }
2767 
2768  case WWT_CLOSEBOX: {
2770  if (NWidgetLeaf::closebox_dimension.width == 0) {
2773  NWidgetLeaf::closebox_dimension.height += padding.height;
2774  }
2776  break;
2777  }
2778  case WWT_TEXTBTN:
2779  case WWT_PUSHTXTBTN:
2780  case WWT_TEXTBTN_2: {
2782  if (this->index >= 0) w->SetStringParameters(this->index);
2784  d2.width += padding.width;
2785  d2.height += padding.height;
2786  size = maxdim(size, d2);
2787  break;
2788  }
2789  case WWT_LABEL:
2790  case WWT_TEXT: {
2791  if (this->index >= 0) w->SetStringParameters(this->index);
2792  size = maxdim(size, GetStringBoundingBox(this->widget_data, this->text_size));
2793  break;
2794  }
2795  case WWT_CAPTION: {
2797  if (this->index >= 0) w->SetStringParameters(this->index);
2799  d2.width += padding.width;
2800  d2.height += padding.height;
2801  size = maxdim(size, d2);
2802  break;
2803  }
2804  case WWT_DROPDOWN:
2805  case NWID_BUTTON_DROPDOWN:
2806  case NWID_PUSHBUTTON_DROPDOWN: {
2807  if (NWidgetLeaf::dropdown_dimension.width == 0) {
2811  }
2813  if (this->index >= 0) w->SetStringParameters(this->index);
2815  d2.width += padding.width;
2816  d2.height = std::max(d2.height + padding.height, NWidgetLeaf::dropdown_dimension.height);
2817  size = maxdim(size, d2);
2818  break;
2819  }
2820  default:
2821  NOT_REACHED();
2822  }
2823 
2824  if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
2825 
2826  this->smallest_x = size.width;
2827  this->smallest_y = size.height;
2828  this->fill_x = fill.width;
2829  this->fill_y = fill.height;
2830  this->resize_x = resize.width;
2831  this->resize_y = resize.height;
2832  this->ApplyAspectRatio();
2833 }
2834 
2836 {
2837  if (this->current_x == 0 || this->current_y == 0) return;
2838 
2839  /* Setup a clipping rectangle... for WWT_EMPTY or WWT_TEXT, an extra scaled pixel is allowed in case text shadow encroaches. */
2840  int extra = (this->type == WWT_EMPTY || this->type == WWT_TEXT) ? ScaleGUITrad(1) : 0;
2841  DrawPixelInfo new_dpi;
2842  if (!FillDrawPixelInfo(&new_dpi, this->pos_x, this->pos_y, this->current_x + extra, this->current_y + extra)) return;
2843  /* ...but keep coordinates relative to the window. */
2844  new_dpi.left += this->pos_x;
2845  new_dpi.top += this->pos_y;
2846 
2847  AutoRestoreBackup dpi_backup(_cur_dpi, &new_dpi);
2848 
2849  Rect r = this->GetCurrentRect();
2850 
2851  bool clicked = this->IsLowered();
2852  switch (this->type) {
2853  case WWT_EMPTY:
2854  /* WWT_EMPTY used as a spacer indicates a potential design issue. */
2855  if (this->index == -1 && _draw_widget_outlines) {
2857  }
2858  break;
2859 
2860  case WWT_PUSHBTN:
2861  assert(this->widget_data == 0);
2862  DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE);
2863  break;
2864 
2865  case WWT_IMGBTN:
2866  case WWT_PUSHIMGBTN:
2867  case WWT_IMGBTN_2:
2868  DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data, this->align);
2869  break;
2870 
2871  case WWT_TEXTBTN:
2872  case WWT_PUSHTXTBTN:
2873  case WWT_TEXTBTN_2:
2874  if (this->index >= 0) w->SetStringParameters(this->index);
2875  DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE);
2876  DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align, this->text_size);
2877  break;
2878 
2879  case WWT_ARROWBTN:
2880  case WWT_PUSHARROWBTN: {
2881  SpriteID sprite;
2882  switch (this->widget_data) {
2883  case AWV_DECREASE: sprite = _current_text_dir != TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
2884  case AWV_INCREASE: sprite = _current_text_dir == TD_RTL ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; break;
2885  case AWV_LEFT: sprite = SPR_ARROW_LEFT; break;
2886  case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break;
2887  default: NOT_REACHED();
2888  }
2889  DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align);
2890  break;
2891  }
2892 
2893  case WWT_LABEL:
2894  if (this->index >= 0) w->SetStringParameters(this->index);
2895  DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align, this->text_size);
2896  break;
2897 
2898  case WWT_TEXT:
2899  if (this->index >= 0) w->SetStringParameters(this->index);
2900  DrawText(r, this->text_colour, this->widget_data, this->align, this->text_size);
2901  break;
2902 
2903  case WWT_MATRIX:
2904  DrawMatrix(r, this->colour, clicked, this->widget_data, this->resize_x, this->resize_y);
2905  break;
2906 
2907  case WWT_EDITBOX: {
2908  const QueryString *query = w->GetQueryString(this->index);
2909  if (query != nullptr) query->DrawEditBox(w, this->index);
2910  break;
2911  }
2912 
2913  case WWT_CAPTION:
2914  if (this->index >= 0) w->SetStringParameters(this->index);
2915  DrawCaption(r, this->colour, w->owner, this->text_colour, this->widget_data, this->align, this->text_size);
2916  break;
2917 
2918  case WWT_SHADEBOX:
2919  assert(this->widget_data == 0);
2920  DrawShadeBox(r, this->colour, w->IsShaded());
2921  break;
2922 
2923  case WWT_DEBUGBOX:
2924  DrawDebugBox(r, this->colour, clicked);
2925  break;
2926 
2927  case WWT_STICKYBOX:
2928  assert(this->widget_data == 0);
2929  DrawStickyBox(r, this->colour, !!(w->flags & WF_STICKY));
2930  break;
2931 
2932  case WWT_DEFSIZEBOX:
2933  assert(this->widget_data == 0);
2934  DrawDefSizeBox(r, this->colour, clicked);
2935  break;
2936 
2937  case WWT_RESIZEBOX:
2938  DrawResizeBox(r, this->colour, this->pos_x < (w->width / 2), !!(w->flags & WF_SIZING), this->widget_data == 0);
2939  break;
2940 
2941  case WWT_CLOSEBOX:
2942  DrawCloseBox(r, this->colour);
2943  break;
2944 
2945  case WWT_DROPDOWN:
2946  if (this->index >= 0) w->SetStringParameters(this->index);
2947  DrawButtonDropdown(r, this->colour, false, clicked, this->widget_data, this->align);
2948  break;
2949 
2950  case NWID_BUTTON_DROPDOWN:
2951  case NWID_PUSHBUTTON_DROPDOWN:
2952  if (this->index >= 0) w->SetStringParameters(this->index);
2953  DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data, this->align);
2954  break;
2955 
2956  default:
2957  NOT_REACHED();
2958  }
2959  if (this->index >= 0) w->DrawWidget(r, this->index);
2960 
2961  if (this->IsDisabled()) {
2963  }
2964 
2965  DrawOutline(w, this);
2966 }
2967 
2976 {
2977  if (_current_text_dir == TD_LTR) {
2978  int button_width = this->pos_x + this->current_x - NWidgetLeaf::dropdown_dimension.width;
2979  return pt.x < button_width;
2980  } else {
2981  int button_left = this->pos_x + NWidgetLeaf::dropdown_dimension.width;
2982  return pt.x >= button_left;
2983  }
2984 }
2985 
2986 /* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
2987 
2994 {
2995  return tp > WPT_ATTRIBUTE_BEGIN && tp < WPT_ATTRIBUTE_END;
2996 }
2997 
3004 static void ApplyNWidgetPartAttribute(const NWidgetPart &nwid, NWidgetBase *dest)
3005 {
3006  switch (nwid.type) {
3007  case WPT_RESIZE: {
3008  NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3009  if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_RESIZE requires NWidgetResizeBase");
3010  assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3011  nwrb->SetResize(nwid.u.xy.x, nwid.u.xy.y);
3012  break;
3013  }
3014 
3015  case WPT_MINSIZE: {
3016  NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3017  if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINSIZE requires NWidgetResizeBase");
3018  assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
3019  nwrb->SetMinimalSize(nwid.u.xy.x, nwid.u.xy.y);
3020  break;
3021  }
3022 
3023  case WPT_MINTEXTLINES: {
3024  NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3025  if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINTEXTLINES requires NWidgetResizeBase");
3026  assert(nwid.u.text_lines.size >= FS_BEGIN && nwid.u.text_lines.size < FS_END);
3027  nwrb->SetMinimalTextLines(nwid.u.text_lines.lines, nwid.u.text_lines.spacing, nwid.u.text_lines.size);
3028  break;
3029  }
3030 
3031  case WPT_TEXTSTYLE: {
3032  NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3033  if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_TEXTSTYLE requires NWidgetCore");
3034  nwc->SetTextStyle(nwid.u.text_style.colour, nwid.u.text_style.size);
3035  break;
3036  }
3037 
3038  case WPT_ALIGNMENT: {
3039  NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3040  if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_ALIGNMENT requires NWidgetCore");
3041  nwc->SetAlignment(nwid.u.align.align);
3042  break;
3043  }
3044 
3045  case WPT_FILL: {
3046  NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
3047  if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_FILL requires NWidgetResizeBase");
3048  nwrb->SetFill(nwid.u.xy.x, nwid.u.xy.y);
3049  break;
3050  }
3051 
3052  case WPT_DATATIP: {
3053  NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3054  if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_DATATIP requires NWidgetCore");
3055  nwc->widget_data = nwid.u.data_tip.data;
3056  nwc->tool_tip = nwid.u.data_tip.tooltip;
3057  break;
3058  }
3059 
3060  case WPT_PADDING:
3061  if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_PADDING requires NWidgetBase");
3062  dest->SetPadding(nwid.u.padding);
3063  break;
3064 
3065  case WPT_PIPSPACE: {
3066  NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3067  if (nwc != nullptr) nwc->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3068 
3069  NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3070  if (nwb != nullptr) nwb->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3071 
3072  if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3073  break;
3074  }
3075 
3076  case WPT_PIPRATIO: {
3077  NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
3078  if (nwc != nullptr) nwc->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3079 
3080  NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
3081  if (nwb != nullptr) nwb->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
3082 
3083  if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3084  break;
3085  }
3086 
3087  case WPT_SCROLLBAR: {
3088  NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
3089  if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore");
3090  nwc->scrollbar_index = nwid.u.widget.index;
3091  break;
3092  }
3093 
3094  case WPT_ASPECT: {
3095  if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_ASPECT requires NWidgetBase");
3096  dest->aspect_ratio = nwid.u.aspect.ratio;
3097  dest->aspect_flags = nwid.u.aspect.flags;
3098  break;
3099  }
3100 
3101  default:
3102  NOT_REACHED();
3103  }
3104 }
3105 
3112 static std::unique_ptr<NWidgetBase> MakeNWidget(const NWidgetPart &nwid)
3113 {
3114  assert(!IsAttributeWidgetPartType(nwid.type));
3115  assert(nwid.type != WPT_ENDCONTAINER);
3116 
3117  switch (nwid.type) {
3118  case NWID_SPACER: return std::make_unique<NWidgetSpacer>(0, 0);
3119 
3120  case WWT_PANEL: [[fallthrough]];
3121  case WWT_INSET: [[fallthrough]];
3122  case WWT_FRAME: return std::make_unique<NWidgetBackground>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3123 
3124  case NWID_HORIZONTAL: return std::make_unique<NWidgetHorizontal>(nwid.u.cont_flags);
3125  case NWID_HORIZONTAL_LTR: return std::make_unique<NWidgetHorizontalLTR>(nwid.u.cont_flags);
3126  case NWID_VERTICAL: return std::make_unique<NWidgetVertical>(nwid.u.cont_flags);
3127  case NWID_SELECTION: return std::make_unique<NWidgetStacked>(nwid.u.widget.index);
3128  case NWID_MATRIX: return std::make_unique<NWidgetMatrix>(nwid.u.widget.colour, nwid.u.widget.index);
3129  case NWID_VIEWPORT: return std::make_unique<NWidgetViewport>(nwid.u.widget.index);
3130 
3131  case NWID_HSCROLLBAR: [[fallthrough]];
3132  case NWID_VSCROLLBAR: return std::make_unique<NWidgetScrollbar>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
3133 
3134  case WPT_FUNCTION: return nwid.u.func_ptr();
3135 
3136  default:
3137  assert((nwid.type & WWT_MASK) < WWT_LAST || (nwid.type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
3138  return std::make_unique<NWidgetLeaf>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index, 0x0, STR_NULL);
3139  }
3140 }
3141 
3155 static 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)
3156 {
3157  dest = nullptr;
3158 
3159  if (IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]] throw std::runtime_error("Expected non-attribute NWidgetPart type");
3160  if (nwid_begin->type == WPT_ENDCONTAINER) return nwid_begin;
3161 
3162  fill_dest = IsContainerWidgetType(nwid_begin->type);
3163  dest = MakeNWidget(*nwid_begin);
3164  if (dest == nullptr) return nwid_begin;
3165 
3166  ++nwid_begin;
3167 
3168  /* Once a widget is created, we're now looking for attributes. */
3169  while (nwid_begin != nwid_end && IsAttributeWidgetPartType(nwid_begin->type)) {
3170  ApplyNWidgetPartAttribute(*nwid_begin, dest.get());
3171  ++nwid_begin;
3172  }
3173 
3174  return nwid_begin;
3175 }
3176 
3183 {
3184  return tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
3185  || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION;
3186 }
3187 
3195 static 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)
3196 {
3197  /* If *parent == nullptr, only the first widget is read and returned. Otherwise, *parent must point to either
3198  * a #NWidgetContainer or a #NWidgetBackground object, and parts are added as much as possible. */
3199  NWidgetContainer *nwid_cont = dynamic_cast<NWidgetContainer *>(parent.get());
3200  NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(parent.get());
3201  assert(parent == nullptr || (nwid_cont != nullptr && nwid_parent == nullptr) || (nwid_cont == nullptr && nwid_parent != nullptr));
3202 
3203  while (nwid_begin != nwid_end) {
3204  std::unique_ptr<NWidgetBase> sub_widget = nullptr;
3205  bool fill_sub = false;
3206  nwid_begin = MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3207 
3208  /* Break out of loop when end reached */
3209  if (sub_widget == nullptr) break;
3210 
3211  /* If sub-widget is a container, recursively fill that container. */
3212  if (fill_sub && IsContainerWidgetType(sub_widget->type)) {
3213  nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, sub_widget);
3214  }
3215 
3216  /* Add sub_widget to parent container if available, otherwise return the widget to the caller. */
3217  if (nwid_cont != nullptr) nwid_cont->Add(std::move(sub_widget));
3218  if (nwid_parent != nullptr) nwid_parent->Add(std::move(sub_widget));
3219  if (nwid_cont == nullptr && nwid_parent == nullptr) {
3220  parent = std::move(sub_widget);
3221  return nwid_begin;
3222  }
3223  }
3224 
3225  if (nwid_begin == nwid_end) return nwid_begin; // Reached the end of the array of parts?
3226 
3227  assert(nwid_begin < nwid_end);
3228  assert(nwid_begin->type == WPT_ENDCONTAINER);
3229  return std::next(nwid_begin); // *nwid_begin is also 'used'
3230 }
3231 
3239 std::unique_ptr<NWidgetBase> MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3240 {
3241  if (container == nullptr) container = std::make_unique<NWidgetVertical>();
3242  [[maybe_unused]] auto nwid_part = MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3243 #ifdef WITH_ASSERT
3244  if (nwid_part != std::end(nwid_parts)) [[unlikely]] throw std::runtime_error("Did not consume all NWidgetParts");
3245 #endif
3246  return std::move(container);
3247 }
3248 
3258 std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(std::span<const NWidgetPart> nwid_parts, NWidgetStacked **shade_select)
3259 {
3260  auto nwid_begin = std::begin(nwid_parts);
3261  auto nwid_end = std::end(nwid_parts);
3262 
3263  *shade_select = nullptr;
3264 
3265  /* Read the first widget recursively from the array. */
3266  std::unique_ptr<NWidgetBase> nwid = nullptr;
3267  nwid_begin = MakeWidgetTree(nwid_begin, nwid_end, nwid);
3268  assert(nwid != nullptr);
3269 
3270  NWidgetHorizontal *hor_cont = dynamic_cast<NWidgetHorizontal *>(nwid.get());
3271 
3272  auto root = std::make_unique<NWidgetVertical>();
3273  root->Add(std::move(nwid));
3274  if (nwid_begin == nwid_end) return root; // There is no body at all.
3275 
3276  if (hor_cont != nullptr && hor_cont->GetWidgetOfType(WWT_CAPTION) != nullptr && hor_cont->GetWidgetOfType(WWT_SHADEBOX) != nullptr) {
3277  /* If the first widget has a title bar and a shade box, silently add a shade selection widget in the tree. */
3278  auto shade_stack = std::make_unique<NWidgetStacked>(-1);
3279  *shade_select = shade_stack.get();
3280  /* Load the remaining parts into the shade stack. */
3281  shade_stack->Add(MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3282  root->Add(std::move(shade_stack));
3283  return root;
3284  }
3285 
3286  /* Load the remaining parts into 'root'. */
3287  return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3288 }
3289 
3300 std::unique_ptr<NWidgetBase> MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
3301 {
3302  assert(max_length >= 1);
3303  std::unique_ptr<NWidgetVertical> vert = nullptr; // Storage for all rows.
3304  std::unique_ptr<NWidgetHorizontal> hor = nullptr; // Storage for buttons in one row.
3305  int hor_length = 0;
3306 
3307  Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_NORMAL);
3308  sprite_size.width += WidgetDimensions::unscaled.matrix.Horizontal();
3309  sprite_size.height += WidgetDimensions::unscaled.matrix.Vertical();
3310 
3311  for (WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3312  /* Ensure there is room in 'hor' for another button. */
3313  if (hor_length == max_length) {
3314  if (vert == nullptr) vert = std::make_unique<NWidgetVertical>();
3315  vert->Add(std::move(hor));
3316  hor = nullptr;
3317  hor_length = 0;
3318  }
3319  if (hor == nullptr) {
3320  hor = std::make_unique<NWidgetHorizontal>();
3321  hor_length = 0;
3322  }
3323 
3324  auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, button_colour, widnum);
3325  panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3326  panel->SetFill(1, 1);
3327  if (resizable) panel->SetResize(1, 0);
3328  panel->SetDataTip(0x0, button_tooltip);
3329  hor->Add(std::move(panel));
3330  hor_length++;
3331  }
3332  if (vert == nullptr) return hor; // All buttons fit in a single row.
3333 
3334  if (hor_length > 0 && hor_length < max_length) {
3335  /* Last row is partial, add a spacer at the end to force all buttons to the left. */
3336  auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3337  spc->SetFill(1, 1);
3338  if (resizable) spc->SetResize(1, 0);
3339  hor->Add(std::move(spc));
3340  }
3341  if (hor != nullptr) vert->Add(std::move(hor));
3342  return vert;
3343 }
Class for backupping variables and making sure they are restored later.
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Nested widget with a child.
Definition: widget_type.h:649
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition: widget.cpp:2232
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition: widget.cpp:2242
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:2175
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:2110
NWidgetBackground(WidgetType tp, Colours colour, WidgetID index, std::unique_ptr< NWidgetPIPContainer > &&child=nullptr)
Constructor parent nested widgets.
Definition: widget.cpp:2042
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:2076
std::unique_ptr< NWidgetPIPContainer > child
Child widget.
Definition: widget_type.h:668
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition: widget.cpp:2193
void Add(std::unique_ptr< NWidgetBase > &&nwid)
Add a child to the parent.
Definition: widget.cpp:2057
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition: widget.cpp:2187
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:2095
Baseclass for nested widgets.
Definition: widget_type.h:144
void StoreSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height)
Store size and position.
Definition: widget_type.h:286
float aspect_ratio
Desired aspect ratio of widget.
Definition: widget_type.h:245
virtual void SetDirty(const Window *w) const
Mark the widget as 'dirty' (in need of repaint).
Definition: widget.cpp:903
WidgetType type
Type of the widget / nested widget.
Definition: widget_type.h:232
uint resize_x
Horizontal resize step (0 means not resizable).
Definition: widget_type.h:235
uint fill_x
Horizontal fill stepsize (from initial size, 0 means not resizable).
Definition: widget_type.h:233
NWidgetBase * parent
Parent widget of this widget, automatically filled in when added to container.
Definition: widget_type.h:254
RectPadding uz_padding
Unscaled padding, for resize calculation.
Definition: widget_type.h:252
uint smallest_x
Smallest horizontal size of the widget in a filled window.
Definition: widget_type.h:240
AspectFlags aspect_flags
Which dimensions can be resized.
Definition: widget_type.h:246
virtual void Draw(const Window *w)=0
Draw the widgets of the tree.
uint current_x
Current horizontal size (after resizing).
Definition: widget_type.h:243
int pos_y
Vertical position of top-left corner of the widget in the window.
Definition: widget_type.h:249
int pos_x
Horizontal position of top-left corner of the widget in the window.
Definition: widget_type.h:248
void SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Set additional space (padding) around the widget.
Definition: widget_type.h:197
uint smallest_y
Smallest vertical size of the widget in a filled window.
Definition: widget_type.h:241
virtual NWidgetBase * GetWidgetOfType(WidgetType tp)
Retrieve a widget by its type.
Definition: widget.cpp:923
uint fill_y
Vertical fill stepsize (from initial size, 0 means not resizable).
Definition: widget_type.h:234
uint resize_y
Vertical resize step (0 means not resizable).
Definition: widget_type.h:236
RectPadding padding
Padding added to the widget. Managed by parent container widget. (parent container may swap left and ...
Definition: widget_type.h:251
NWidgetBase(WidgetType tp)
Base class constructor.
Definition: widget.cpp:851
uint current_y
Current vertical size (after resizing).
Definition: widget_type.h:244
Baseclass for container widgets.
Definition: widget_type.h:459
void Add(std::unique_ptr< NWidgetBase > &&wid)
Append widget wid to container.
Definition: widget.cpp:1197
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition: widget.cpp:1204
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition: widget.cpp:1211
bool IsEmpty()
Return whether the container is empty.
Definition: widget_type.h:471
std::vector< std::unique_ptr< NWidgetBase > > children
Child widgets in contaier.
Definition: widget_type.h:476
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition: widget.cpp:1220
NWidgetBase * GetWidgetOfType(WidgetType tp) override
Retrieve a widget by its type.
Definition: widget.cpp:1175
Base class for a 'real' widget.
Definition: widget_type.h:370
void SetToolTip(StringID tool_tip)
Set the tool tip of the nested widget.
Definition: widget.cpp:1151
bool IsDisabled() const
Return whether the widget is disabled.
Definition: widget_type.h:449
NWidgetDisplay disp_flags
Flags that affect display and interaction with the widget.
Definition: widget_type.h:390
void SetTextStyle(TextColour colour, FontSize size)
Set the text style of the nested widget.
Definition: widget.cpp:1141
uint32_t widget_data
Data of the widget.
Definition: widget_type.h:393
void SetAlignment(StringAlignment align)
Set the text/image alignment of the nested widget.
Definition: widget.cpp:1160
const WidgetID index
Index of the nested widget (-1 means 'not used').
Definition: widget_type.h:392
StringAlignment align
Alignment of text/image within widget.
Definition: widget_type.h:399
FontSize text_size
Size of text within widget.
Definition: widget_type.h:398
NWidgetCore(WidgetType tp, Colours colour, WidgetID index, uint fill_x, uint fill_y, uint32_t widget_data, StringID tool_tip)
Initialization of a 'real' widget.
Definition: widget.cpp:1114
void SetDataTip(uint32_t widget_data, StringID tool_tip)
Set data and tool tip of the nested widget.
Definition: widget.cpp:1130
WidgetID scrollbar_index
Index of an attached scrollbar.
Definition: widget_type.h:395
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition: widget.cpp:1170
TextColour text_colour
Colour of text within widget.
Definition: widget_type.h:397
Colours colour
Colour of this widget.
Definition: widget_type.h:391
void SetLowered(bool lowered)
Lower or raise the widget.
Definition: widget_type.h:428
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition: widget.cpp:1165
StringID tool_tip
Tooltip of the widget.
Definition: widget_type.h:394
bool IsLowered() const
Return whether the widget is lowered.
Definition: widget_type.h:434
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:1589
NWidgetHorizontalLTR(NWidContainerFlags flags=NC_NONE)
Horizontal left-to-right container widget.
Definition: widget.cpp:1584
Horizontal container.
Definition: widget_type.h:558
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:1405
NWidgetHorizontal(NWidContainerFlags flags=NC_NONE)
Horizontal container widget.
Definition: widget.cpp:1401
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:1474
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition: widget.cpp:2835
static void InvalidateDimensionCache()
Reset the cached dimensions.
Definition: widget.cpp:2537
static Dimension resizebox_dimension
Cached size of a resizebox widget.
Definition: widget_type.h:928
static Dimension shadebox_dimension
Cached size of a shadebox widget.
Definition: widget_type.h:931
NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, uint32_t data, StringID tip)
Nested leaf widget.
Definition: widget.cpp:2564
static Dimension closebox_dimension
Cached size of a closebox widget.
Definition: widget_type.h:929
static Dimension stickybox_dimension
Cached size of a stickybox widget.
Definition: widget_type.h:934
static Dimension defsizebox_dimension
Cached size of a defsizebox widget.
Definition: widget_type.h:933
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:2664
static Dimension dropdown_dimension
Cached size of a dropdown widget.
Definition: widget_type.h:927
static Dimension debugbox_dimension
Cached size of a debugbox widget.
Definition: widget_type.h:932
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:2975
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition: widget.cpp:1956
int count
Amount of valid elements.
Definition: widget_type.h:616
void SetClicked(int clicked)
Sets the clicked element in the matrix.
Definition: widget.cpp:1819
int GetCurrentElement() const
Get current element.
Definition: widget.cpp:1869
const WidgetID index
If non-negative, index in the Window::widget_lookup.
Definition: widget_type.h:613
Scrollbar * sb
The scrollbar we're associated with.
Definition: widget_type.h:618
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:1874
int widget_w
The width of the child widget including inter spacing.
Definition: widget_type.h:620
void SetScrollbar(Scrollbar *sb)
Assign a scrollbar to this matrix.
Definition: widget.cpp:1860
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:2011
int current_element
The element currently being processed.
Definition: widget_type.h:617
int widgets_x
The number of visible widgets in horizontal direction.
Definition: widget_type.h:622
int widget_h
The height of the child widget including inter spacing.
Definition: widget_type.h:621
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:1896
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition: widget.cpp:1920
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition: widget.cpp:1926
int clicked
The currently clicked element.
Definition: widget_type.h:615
void SetCount(int count)
Set the number of elements in this matrix.
Definition: widget.cpp:1836
int widgets_y
The number of visible widgets in vertical direction.
Definition: widget_type.h:623
Colours colour
Colour of this widget.
Definition: widget_type.h:614
Container with pre/inter/post child space.
Definition: widget_type.h:530
uint8_t gaps
Number of gaps between widgets.
Definition: widget_type.h:551
NWidContainerFlags flags
Flags of the container.
Definition: widget_type.h:539
uint8_t pip_pre
Amount of space before first widget.
Definition: widget_type.h:540
uint8_t uz_pip_pre
Unscaled space before first widget.
Definition: widget_type.h:547
uint8_t uz_pip_inter
Unscaled space between widgets.
Definition: widget_type.h:548
uint8_t pip_ratio_pre
Ratio of remaining space before first widget.
Definition: widget_type.h:543
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:1393
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:1373
uint8_t pip_post
Amount of space after last widget.
Definition: widget_type.h:542
uint8_t pip_ratio_inter
Ratio of remaining space between widgets.
Definition: widget_type.h:544
uint8_t pip_ratio_post
Ratio of remaining space after last widget.
Definition: widget_type.h:545
uint8_t uz_pip_post
Unscaled space after last widget.
Definition: widget_type.h:549
uint8_t pip_inter
Amount of space between widgets.
Definition: widget_type.h:541
Base class for a resizable nested widget.
Definition: widget_type.h:303
uint8_t uz_text_spacing
'Unscaled' text padding, stored for resize calculation.
Definition: widget_type.h:330
bool absolute
Set if minimum size is fixed and should not be resized.
Definition: widget_type.h:325
void SetMinimalSize(uint min_x, uint min_y)
Set minimal size of the widget.
Definition: widget.cpp:995
bool UpdateSize(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition: widget.cpp:1078
void SetAspect(float ratio, AspectFlags flags=AspectFlags::ResizeX)
Set desired aspect ratio of this widget.
Definition: widget.cpp:964
uint min_x
Minimal horizontal size of only this widget.
Definition: widget_type.h:322
FontSize uz_text_size
'Unscaled' font size, stored for resize calculation.
Definition: widget_type.h:331
NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y)
Constructor for resizable nested widgets.
Definition: widget.cpp:953
uint uz_min_x
Unscaled Minimal horizontal size of only this widget.
Definition: widget_type.h:326
uint8_t uz_text_lines
'Unscaled' text lines, stored for resize calculation.
Definition: widget_type.h:329
void SetFill(uint fill_x, uint fill_y)
Set the filling of the widget from initial size.
Definition: widget.cpp:1034
void SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size)
Set minimal text lines for the widget.
Definition: widget.cpp:1021
uint min_y
Minimal vertical size of only this widget.
Definition: widget_type.h:323
uint uz_min_y
Unscaled Minimal vertical size of only this widget.
Definition: widget_type.h:327
bool UpdateMultilineWidgetSize(const std::string &str, int max_lines)
Try to set optimum widget size for a multiline text widget.
Definition: widget.cpp:1058
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:1099
bool UpdateVerticalSize(uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition: widget.cpp:1092
void SetResize(uint resize_x, uint resize_y)
Set resize step of the widget.
Definition: widget.cpp:1045
void SetMinimalSizeAbsolute(uint min_x, uint min_y)
Set absolute (post-scaling) minimal size of the widget.
Definition: widget.cpp:1008
Nested widget to display and control a scrollbar in a window.
Definition: widget_type.h:896
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition: widget.cpp:2481
NWidgetScrollbar(WidgetType tp, Colours colour, WidgetID index)
Scrollbar widget.
Definition: widget.cpp:2439
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:2460
static Dimension horizontal_dimension
Cached size of horizontal scrollbar button.
Definition: widget_type.h:909
static Dimension vertical_dimension
Cached size of vertical scrollbar button.
Definition: widget_type.h:908
NWidgetSpacer(int width, int height)
Generic spacer widget.
Definition: widget.cpp:1771
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition: widget.cpp:1784
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition: widget.cpp:1805
void SetDirty(const Window *w) const override
Mark the widget as 'dirty' (in need of repaint).
Definition: widget.cpp:1800
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition: widget.cpp:1788
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:1777
Stacked widgets, widgets all occupying the same space in the window.
Definition: widget_type.h:498
int shown_plane
Plane being displayed (for NWID_SELECTION only).
Definition: widget_type.h:512
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:1246
const WidgetID index
If non-negative, index in the Window::widget_lookup.
Definition: widget_type.h:513
WidgetLookup * widget_lookup
Window's widget lookup, updated in SetDisplayedPlane().
Definition: widget_type.h:515
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:1287
NWidgetStacked(WidgetID index)
Widgets stacked on top of each other.
Definition: widget.cpp:1234
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition: widget.cpp:1318
bool SetDisplayedPlane(int plane)
Select which plane to show (for NWID_SELECTION only).
Definition: widget.cpp:1342
void FillWidgetLookup(WidgetLookup &widget_lookup) override
Fill the Window::widget_lookup with pointers to nested widgets in the tree.
Definition: widget.cpp:1307
NWidgetCore * GetWidgetFromPos(int x, int y) override
Retrieve a widget by its position.
Definition: widget.cpp:1327
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:1668
NWidgetVertical(NWidContainerFlags flags=NC_NONE)
Vertical container widget.
Definition: widget.cpp:1595
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:1599
void UpdateViewportCoordinates(Window *w)
Update the position and size of the viewport (after eg a resize).
Definition: widget.cpp:2297
void Draw(const Window *w) override
Draw the widgets of the tree.
Definition: widget.cpp:2261
void SetupSmallestSize(Window *w) override
Compute smallest size needed by the widget.
Definition: widget.cpp:2254
void InitializeViewport(Window *w, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize the viewport of the window.
Definition: widget.cpp:2288
Scrollbar data structure.
Definition: widget_type.h:694
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition: widget_type.h:731
void SetCount(size_t num)
Sets the number of elements in the list.
Definition: widget_type.h:780
bool IsVertical() const
Is the scrollbar vertical or not?
Definition: widget_type.h:759
bool UpdatePosition(int difference, ScrollbarStepping unit=SS_SMALL)
Updates the position of the first visible element by the given amount.
Definition: widget_type.h:824
void SetCapacity(size_t capacity)
Set the capacity of visible elements.
Definition: widget_type.h:794
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:2320
@ SS_BIG
Step in cap units.
Definition: widget_type.h:711
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:2394
size_type GetCount() const
Gets the number of elements in the list.
Definition: widget_type.h:722
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:2341
void ScrollTowards(size_type position)
Scroll towards the given position; if the item is visible nothing happens, otherwise it will be shown...
Definition: widget_type.h:841
void SetStepSize(size_t stepsize)
Set the distance to scroll when using the buttons or the wheel.
Definition: widget_type.h:768
size_type pos
Index of first visible item of the list.
Definition: widget_type.h:703
size_type GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:740
static constexpr uint WD_CAPTION_HEIGHT
Minimum height of a title bar.
Definition: window_gui.h:90
RectPadding defsizebox
Padding around image in defsizebox widget.
Definition: window_gui.h:48
RectPadding closebox
Padding around image in closebox widget.
Definition: window_gui.h:50
RectPadding framerect
Standard padding inside many panels.
Definition: window_gui.h:42
static constexpr uint WD_DROPDOWN_HEIGHT
Minimum height of a drop down widget.
Definition: window_gui.h:91
RectPadding captiontext
Padding for text within caption widget.
Definition: window_gui.h:51
RectPadding debugbox
Padding around image in debugbox widget.
Definition: window_gui.h:47
RectPadding frametext
Padding inside frame with text.
Definition: window_gui.h:43
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition: window_gui.h:68
RectPadding modalpopup
Spacing for popup warning/information windows.
Definition: window_gui.h:54
RectPadding hscrollbar
Padding inside horizontal scrollbar buttons.
Definition: window_gui.h:39
RectPadding shadebox
Padding around image in shadebox widget.
Definition: window_gui.h:45
RectPadding resizebox
Padding around image in resizebox widget.
Definition: window_gui.h:49
RectPadding vscrollbar
Padding inside vertical scrollbar buttons.
Definition: window_gui.h:38
RectPadding imgbtn
Padding around image button image.
Definition: window_gui.h:36
int vsep_normal
Normal vertical spacing.
Definition: window_gui.h:60
int vsep_wide
Wide vertical spacing.
Definition: window_gui.h:62
int hsep_wide
Wide horizontal spacing.
Definition: window_gui.h:64
static constexpr uint WD_CLOSEBOX_WIDTH
Minimum width of a close box widget.
Definition: window_gui.h:89
RectPadding fullbevel
Always-scaled bevel thickness.
Definition: window_gui.h:41
RectPadding inset
Padding inside inset container.
Definition: window_gui.h:37
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition: window_gui.h:67
static constexpr uint WD_RESIZEBOX_WIDTH
Minimum width of a resize box widget.
Definition: window_gui.h:88
static constexpr uint WD_STICKYBOX_WIDTH
Minimum width of a standard sticky box widget.
Definition: window_gui.h:85
RectPadding matrix
Padding of WWT_MATRIX items.
Definition: window_gui.h:44
int hsep_normal
Normal horizontal spacing.
Definition: window_gui.h:63
RectPadding dropdownlist
Padding of complete drop down list.
Definition: window_gui.h:53
static constexpr uint WD_DEBUGBOX_WIDTH
Minimum width of a standard debug box widget.
Definition: window_gui.h:86
RectPadding stickybox
Padding around image in stickybox widget.
Definition: window_gui.h:46
static constexpr uint WD_DEFSIZEBOX_WIDTH
Minimum width of a standard defsize box widget.
Definition: window_gui.h:87
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition: window_gui.h:40
RectPadding dropdowntext
Padding of drop down list item.
Definition: window_gui.h:52
static constexpr uint WD_SHADEBOX_WIDTH
Distances used in drawing widgets.
Definition: window_gui.h:84
int hsep_indent
Width of identation for tree layouts.
Definition: window_gui.h:65
Base class that provides memory initialization on dynamically created objects.
Definition: alloc_type.hpp:86
Colours _company_colours[MAX_COMPANIES]
NOSAVE: can be determined from company structs.
Definition: company_cmd.cpp:54
Functions related to companies.
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ MAX_COMPANIES
Maximum number of companies.
Definition: company_type.h:23
constexpr debug_inline bool HasFlag(const T x, const T y)
Checks if a value in a bitset enum is set.
Definition: enum_type.hpp:58
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 GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition: gfx.cpp:704
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition: gfx.cpp:922
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:851
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
Draw the outline of a Rect.
Definition: gfx.cpp:456
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:657
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:114
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:988
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:1548
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
Definition: gfx_func.h:166
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition: gfx_type.h:18
StringAlignment
How to align the to-be drawn text.
Definition: gfx_type.h:342
@ SA_TOP
Top align the text.
Definition: gfx_type.h:348
@ SA_LEFT
Left align the text.
Definition: gfx_type.h:343
@ SA_HOR_MASK
Mask for horizontal alignment.
Definition: gfx_type.h:346
@ SA_RIGHT
Right align the text (must be a single bit).
Definition: gfx_type.h:345
@ SA_HOR_CENTER
Horizontally center the text.
Definition: gfx_type.h:344
@ SA_VERT_MASK
Mask for vertical alignment.
Definition: gfx_type.h:351
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition: gfx_type.h:355
@ SA_BOTTOM
Bottom align the text.
Definition: gfx_type.h:350
@ SA_CENTER
Center both horizontally and vertically.
Definition: gfx_type.h:353
@ SA_VERT_CENTER
Vertically center the text.
Definition: gfx_type.h:349
FontSize
Available font sizes.
Definition: gfx_type.h:208
@ FS_BEGIN
First font.
Definition: gfx_type.h:215
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:209
uint32_t PaletteID
The number of the palette.
Definition: gfx_type.h:19
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition: gfx_type.h:260
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition: gfx_type.h:299
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
Definition: gfx_type.h:300
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:3239
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:3258
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition: window.cpp:940
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:1486
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.
Definition: math_func.hpp:252
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition: math_func.hpp:23
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
Definition: math_func.hpp:320
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:314
static const uint8_t PC_WHITE
White palette colour.
Definition: palette_func.h:70
static const uint8_t PC_BLACK
Black palette colour.
Definition: palette_func.h:67
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:56
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:1532
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition: sprites.h:1602
static const PaletteID PALETTE_NEWSPAPER
Recolour sprite for newspaper-greying.
Definition: sprites.h:1604
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.
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:56
Functions related to OTTD's strings.
@ TD_LTR
Text is written left-to-right by default.
Definition: strings_type.h:23
@ TD_RTL
Text is written right-to-left by default.
Definition: strings_type.h:24
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
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:125
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition: gfx_type.h:157
bool scale_bevels
bevels are scaled with GUI scale.
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
StringAlignment align
Alignment of text/image.
Definition: widget_type.h:1057
uint32_t data
Data value of the widget.
Definition: widget_type.h:1005
StringID tooltip
Tooltip of the widget.
Definition: widget_type.h:1006
uint8_t post
Amount of space before/between/after child widgets.
Definition: widget_type.h:1030
uint8_t lines
Number of text lines.
Definition: widget_type.h:1038
uint8_t spacing
Extra spacing around lines.
Definition: widget_type.h:1039
FontSize size
Font size of text lines.
Definition: widget_type.h:1040
TextColour colour
TextColour for DrawString.
Definition: widget_type.h:1048
FontSize size
Font size of text.
Definition: widget_type.h:1049
Colours colour
Widget colour.
Definition: widget_type.h:1014
WidgetID index
Index of the widget.
Definition: widget_type.h:1015
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:1075
WidgetType type
Type of the part.
Definition: widget_type.h:1076
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.
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.
Definition: viewport_type.h:22
int top
Screen coordinate top edge of the viewport.
Definition: viewport_type.h:24
int width
Screen width of the viewport.
Definition: viewport_type.h:25
ZoomLevel zoom
The zoom level of the viewport.
Definition: viewport_type.h:33
int virtual_width
width << zoom
Definition: viewport_type.h:30
int left
Screen coordinate left edge of the viewport.
Definition: viewport_type.h:23
int height
Screen height of the viewport.
Definition: viewport_type.h:26
int virtual_height
height << zoom
Definition: viewport_type.h:31
Data structure for an opened window.
Definition: window_gui.h:273
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition: widget.cpp:780
void DrawWidgets() const
Paint all widgets of a window.
Definition: widget.cpp:731
virtual void UpdateWidgetSize([[maybe_unused]] WidgetID widget, [[maybe_unused]] Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize)
Update size and resize step of a widget in the window.
Definition: window_gui.h:624
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 SetStringParameters([[maybe_unused]] WidgetID widget) const
Initialize string parameters for a widget.
Definition: window_gui.h:632
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition: widget.cpp:763
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
Definition: window_gui.h:853
ViewportData * viewport
Pointer to viewport data, if present.
Definition: window_gui.h:318
void DrawViewport() const
Draw the viewport of this window.
Definition: viewport.cpp:1832
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:563
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:334
virtual void DrawWidget([[maybe_unused]] const Rect &r, [[maybe_unused]] WidgetID widget) const
Draw the contents of a nested widget.
Definition: window_gui.h:610
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:213
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition: window_gui.h:977
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.
@ TO_TEXT
loading and cost/income text
Definition: transparency.h:31
@ TO_SIGNS
signs
Definition: transparency.h:23
uint TransparencyOptionBits
transparency option bits
Definition: transparency.h:36
NWidgetPartTextLines text_lines
Part with text line data.
Definition: widget_type.h:1083
NWidgetPartPIP pip
Part with pre/inter/post spaces.
Definition: widget_type.h:1082
NWidgetPartPaddings padding
Part with paddings.
Definition: widget_type.h:1081
NWidgetFunctionType * func_ptr
Part with a function call.
Definition: widget_type.h:1086
NWidContainerFlags cont_flags
Part with container flags.
Definition: widget_type.h:1087
NWidgetPartDataTip data_tip
Part with a data/tooltip.
Definition: widget_type.h:1079
NWidgetPartAlignment align
Part with internal alignment.
Definition: widget_type.h:1085
NWidgetPartAspect aspect
Part to set aspect ratio.
Definition: widget_type.h:1088
NWidgetPartTextStyle text_style
Part with text style data.
Definition: widget_type.h:1084
Point xy
Part with an x/y size.
Definition: widget_type.h:1078
NWidgetPartWidget widget
Part with a start of a widget.
Definition: widget_type.h:1080
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:222
Functions related to (drawing on) viewports.
static void DrawCloseBox(const Rect &r, Colours colour)
Draw a close box.
Definition: widget.cpp:658
static void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str, StringAlignment align)
Draw a button with a dropdown (WWT_DROPDOWN and NWID_BUTTON_DROPDOWN).
Definition: widget.cpp:709
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition: widget.cpp:281
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
Apply 'scroll' to a rect to be drawn in.
Definition: widget.cpp:2411
static void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked)
Draw a defsize box.
Definition: widget.cpp:619
bool IsContainerWidgetType(WidgetType tp)
Test if WidgetType is a container widget.
Definition: widget.cpp:3182
static void DrawDebugBox(const Rect &r, Colours colour, bool clicked)
Draw a NewGRF debug box.
Definition: widget.cpp:630
static void DrawStickyBox(const Rect &r, Colours colour, bool clicked)
Draw a sticky box.
Definition: widget.cpp:608
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:106
void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
Draw a caption bar.
Definition: widget.cpp:679
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:504
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
Definition: widget.cpp:54
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:3300
static void DrawLabel(const Rect &r, WidgetType type, bool clicked, TextColour colour, StringID str, StringAlignment align, FontSize fs)
Draw the label-part of a widget.
Definition: widget.cpp:356
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition: widget.cpp:66
static void DrawShadeBox(const Rect &r, Colours colour, bool clicked)
Draw a shade box.
Definition: widget.cpp:597
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:266
static void ApplyNWidgetPartAttribute(const NWidgetPart &nwid, NWidgetBase *dest)
Apply an attribute NWidgetPart to an NWidget.
Definition: widget.cpp:3004
static void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align)
Draw an image button.
Definition: widget.cpp:337
static bool IsAttributeWidgetPartType(WidgetType tp)
Test if (an NWidgetPart) WidgetType is an attribute widget part type.
Definition: widget.cpp:2993
static void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint16_t data, uint resize_x, uint resize_y)
Draw a matrix widget.
Definition: widget.cpp:404
static std::unique_ptr< NWidgetBase > MakeNWidget(const NWidgetPart &nwid)
Make NWidget from an NWidgetPart.
Definition: widget.cpp:3112
static void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
Draw a frame widget.
Definition: widget.cpp:544
static void DrawText(const Rect &r, TextColour colour, StringID str, StringAlignment align, FontSize fs)
Draw text.
Definition: widget.cpp:373
static 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)
Build a nested widget tree by recursively filling containers with nested widgets read from their part...
Definition: widget.cpp:3195
void ScrollbarClickHandler(Window *w, NWidgetCore *nw, int x, int y)
Special handling for the scrollbar widget type.
Definition: widget.cpp:242
static void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bool clicked, bool bevel)
Draw a resize box.
Definition: widget.cpp:643
static void DrawInset(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align, FontSize fs)
Draw an inset widget.
Definition: widget.cpp:389
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:135
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:178
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:464
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition: widget.cpp:35
@ SZSP_HORIZONTAL
Display plane with zero size vertically, and filling and resizing horizontally.
Definition: widget_type.h:482
@ SZSP_BEGIN
First zero-size plane.
Definition: widget_type.h:485
@ SZSP_VERTICAL
Display plane with zero size horizontally, and filling and resizing vertically.
Definition: widget_type.h:481
@ ND_SHADE_GREY
Bit value of the 'shade to grey' flag.
Definition: widget_type.h:356
@ ND_DROPDOWN_ACTIVE
Bit value of the 'dropdown active' flag.
Definition: widget_type.h:358
@ NDB_SCROLLBAR_DOWN
Down-button is lowered bit.
Definition: widget_type.h:347
@ ND_SHADE_DIMMED
Bit value of the 'dimmed colours' flag.
Definition: widget_type.h:357
@ ND_NO_TRANSPARENCY
Bit value of the 'no transparency' flag.
Definition: widget_type.h:355
@ NDB_SCROLLBAR_UP
Up-button is lowered bit.
Definition: widget_type.h:346
@ ND_SCROLLBAR_BTN
Bit value of the 'scrollbar up' or 'scrollbar down' flag.
Definition: widget_type.h:361
uint ComputeMaxSize(uint base, uint max_space, uint step)
Return the biggest possible size of a nested widget.
Definition: widget_type.h:944
@ AWV_LEFT
Force the arrow to the left.
Definition: widget_type.h:33
@ AWV_RIGHT
Force the arrow to the right.
Definition: widget_type.h:34
@ AWV_DECREASE
Arrow to the left or in case of RTL to the right.
Definition: widget_type.h:31
@ AWV_INCREASE
Arrow to the right or in case of RTL to the left.
Definition: widget_type.h:32
NWidContainerFlags
Nested widget container flags,.
Definition: widget_type.h:519
@ NC_EQUALSIZE
Value of the NCB_EQUALSIZE flag.
Definition: widget_type.h:524
@ NC_BIGFIRST
Value of the NCB_BIGFIRST flag.
Definition: widget_type.h:525
static constexpr uint8_t MAT_COL_BITS
Number of bits for the number of columns in the matrix.
Definition: widget_type.h:23
WidgetType
Window widget types, nested widget types, and nested widget part types.
Definition: widget_type.h:46
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:112
@ WWT_INSET
Pressed (inset) panel, most commonly used as combo box text area.
Definition: widget_type.h:51
@ WPT_FILL
Widget part for specifying fill.
Definition: widget_type.h:92
@ WPT_ALIGNMENT
Widget part for specifying text/image alignment.
Definition: widget_type.h:98
@ WWT_IMGBTN
(Toggle) Button with image
Definition: widget_type.h:52
@ WWT_PUSHBTN
Normal push-button (no toggle button) with custom drawing.
Definition: widget_type.h:111
@ WWT_IMGBTN_2
(Toggle) Button with diff image when clicked
Definition: widget_type.h:53
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
Definition: widget_type.h:113
@ WPT_MINSIZE
Widget part for specifying minimal size.
Definition: widget_type.h:90
@ WWT_PUSHARROWBTN
Normal push-button (no toggle button) with arrow caption.
Definition: widget_type.h:114
@ WWT_LABEL
Centered label.
Definition: widget_type.h:57
@ NWID_BUTTON_DROPDOWN
Button with a drop-down.
Definition: widget_type.h:82
@ NWID_SPACER
Invisible widget that takes some space.
Definition: widget_type.h:79
@ WWT_EDITBOX
a textbox for typing
Definition: widget_type.h:71
@ WWT_ARROWBTN
(Toggle) Button with an arrow
Definition: widget_type.h:54
@ NWID_HORIZONTAL
Horizontal container.
Definition: widget_type.h:75
@ WWT_TEXTBTN
(Toggle) Button with text
Definition: widget_type.h:55
@ WPT_SCROLLBAR
Widget part for attaching a scrollbar.
Definition: widget_type.h:99
@ WWT_PANEL
Simple depressed panel.
Definition: widget_type.h:50
@ WPT_ASPECT
Widget part for sepcifying aspect ratio.
Definition: widget_type.h:100
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition: widget_type.h:66
@ WPT_RESIZE
Widget part for specifying resizing.
Definition: widget_type.h:89
@ WWT_MATRIX
Grid of rows and columns.
Definition: widget_type.h:59
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:61
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition: widget_type.h:84
@ WPT_TEXTSTYLE
Widget part for specifying text colour.
Definition: widget_type.h:97
@ WPT_PADDING
Widget part for specifying a padding.
Definition: widget_type.h:94
@ NWID_VERTICAL
Vertical container.
Definition: widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition: widget_type.h:69
@ WWT_TEXTBTN_2
(Toggle) Button with diff text when clicked
Definition: widget_type.h:56
@ WWT_FRAME
Frame.
Definition: widget_type.h:60
@ WPT_MINTEXTLINES
Widget part for specifying minimal number of lines of text.
Definition: widget_type.h:91
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition: widget_type.h:48
@ WWT_LAST
Last Item. use WIDGETS_END to fill up padding!!
Definition: widget_type.h:72
@ WPT_ATTRIBUTE_END
End marker for attribute NWidgetPart types.
Definition: widget_type.h:101
@ NWID_HSCROLLBAR
Horizontal scrollbar.
Definition: widget_type.h:83
@ WPT_ENDCONTAINER
Widget part to denote end of a container.
Definition: widget_type.h:104
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition: widget_type.h:68
@ WPT_PIPRATIO
Widget part for specifying pre/inter/post ratio for containers.
Definition: widget_type.h:96
@ WPT_DATATIP
Widget part for specifying data and tooltip.
Definition: widget_type.h:93
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition: widget_type.h:65
@ WPT_ATTRIBUTE_BEGIN
Begin marker for attribute NWidgetPart types.
Definition: widget_type.h:88
@ WPT_PIPSPACE
Widget part for specifying pre/inter/post space for containers.
Definition: widget_type.h:95
@ NWID_MATRIX
Matrix container.
Definition: widget_type.h:78
@ NWID_VIEWPORT
Nested widget containing a viewport.
Definition: widget_type.h:81
@ WWT_DROPDOWN
Drop down list.
Definition: widget_type.h:70
@ WWT_TEXT
Pure simple text.
Definition: widget_type.h:58
@ NWID_HORIZONTAL_LTR
Horizontal container that doesn't change the order of the widgets for RTL languages.
Definition: widget_type.h:76
@ WPT_FUNCTION
Widget part for calling a user function.
Definition: widget_type.h:103
@ WWT_DEBUGBOX
NewGRF debug box (at top-right of a window, between WWT_CAPTION and WWT_SHADEBOX)
Definition: widget_type.h:63
@ NWID_SELECTION
Stacked widgets, only one visible at a time (eg in a panel with tabs).
Definition: widget_type.h:80
static constexpr uint8_t MAT_ROW_BITS
Number of bits for the number of rows in the matrix.
Definition: widget_type.h:27
std::map< WidgetID, class NWidgetBase * > WidgetLookup
Lookup between widget IDs and NWidget objects.
Definition: widget_type.h:136
SizingType
Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition()
Definition: widget_type.h:119
@ ST_RESIZE
Resize the nested widget tree.
Definition: widget_type.h:121
@ ST_SMALLEST
Initialize nested widget tree to smallest size. Also updates current_x and current_y.
Definition: widget_type.h:120
static constexpr uint8_t MAT_ROW_START
Lowest bit of the number of rows.
Definition: widget_type.h:26
static constexpr uint8_t MAT_COL_START
Bits of the WWT_MATRIX widget data.
Definition: widget_type.h:22
@ RWV_SHOW_BEVEL
Bevel of resize box is shown.
Definition: widget_type.h:39
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Definition: window.cpp:75
Functions, definitions and such used only by the GUI.
SortButtonState
State of a sort direction button.
Definition: window_gui.h:218
@ SBS_DOWN
Sort ascending.
Definition: window_gui.h:220
@ SBS_OFF
Do not sort (with this button).
Definition: window_gui.h:219
@ WF_WHITE_BORDER
Window white border counter bit mask.
Definition: window_gui.h:236
@ WF_SIZING
Window is being resized.
Definition: window_gui.h:233
@ WF_HIGHLIGHTED
Window has a widget that has a highlight.
Definition: window_gui.h:237
@ WF_STICKY
Window is made sticky by user.
Definition: window_gui.h:234
FrameFlags
Flags to describe the look of the frame.
Definition: window_gui.h:24
@ FR_DARKENED
If set the background is darker, allows for lowered frames with normal background colour when used wi...
Definition: window_gui.h:29
@ FR_BORDERONLY
Draw border only, no background.
Definition: window_gui.h:27
@ FR_LOWERED
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
Definition: window_gui.h:28
@ FR_TRANSPARENT
Makes the background transparent if set.
Definition: window_gui.h:26
int WidgetID
Widget ID.
Definition: window_type.h:18
EventState
State of handling an event.
Definition: window_type.h:743
@ ES_HANDLED
The passed event is handled.
Definition: window_type.h:744
@ ES_NOT_HANDLED
The passed event is not handled.
Definition: window_type.h:745
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_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
@ ZOOM_LVL_NORMAL
The normal zoom level.
Definition: zoom_type.h:21