24#include "table/strings.h"
31static std::string GetStringForWidget(
const Window *w,
const NWidgetCore *nwid,
bool secondary =
false)
35 if (stringid == STR_NULL)
return {};
37 return GetString(stringid + (secondary ? 1 : 0));
125 case SA_LEFT: p.x = r.left;
break;
127 case SA_RIGHT: p.x = r.right + 1 - d.width;
break;
128 default: NOT_REACHED();
131 case SA_TOP: p.y = r.top;
break;
133 case SA_BOTTOM: p.y = r.bottom + 1 - d.height;
break;
134 default: NOT_REACHED();
151 int rev_base = top + bottom;
154 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
156 button_size = NWidgetScrollbar::GetVerticalDimension().height;
159 bottom -= button_size;
165 int height = (bottom - top);
166 int slider_height = std::max(button_size, cap * height / count);
167 height -= slider_height;
170 bottom = top + slider_height;
175 pt.x = rev_base - bottom;
176 pt.y = rev_base - top;
198 bool changed =
false;
203 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
206 button_size = NWidgetScrollbar::GetVerticalDimension().height;
208 if (pos < mi + button_size) {
211 if (_scroller_click_timeout <= 1) {
212 _scroller_click_timeout = 3;
216 }
else if (pos >= ma - button_size) {
220 if (_scroller_click_timeout <= 1) {
221 _scroller_click_timeout = 3;
230 }
else if (pos > pt.y) {
233 _scrollbar_start_pos = pt.x - mi - button_size;
234 _scrollbar_size = ma - mi - button_size * 2 - (pt.y - pt.x);
236 _cursorpos_drag_start = _cursor.
pos;
269 assert(scrollbar !=
nullptr);
284 return (nw !=
nullptr) ? nw->
GetIndex() : -1;
301 assert(colour < COLOUR_END);
309 Rect outer = {left, top, right, bottom};
313 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
314 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
315 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
316 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
319 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
320 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
321 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
322 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
323 interior = medium_dark;
326 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
336 d.height -= offset.y;
339 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
356 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
357 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
370 if (str.empty())
return;
374 DrawString(r.left, r.right, p.y, str, colour, align,
false, fs);
387 if (str.empty())
return;
391 DrawString(r.left, r.right, p.y, str, colour, align,
false, fs);
405 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, {FrameFlag::Lowered, FrameFlag::Darkened});
419static inline void DrawMatrix(
const Rect &r, Colours colour,
bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
424 if (num_columns == 0) {
425 column_width = resize_x;
426 num_columns = r.
Width() / column_width;
428 column_width = r.
Width() / num_columns;
433 row_height = resize_y;
434 num_rows = r.
Height() / row_height;
436 row_height = r.
Height() / num_rows;
442 for (
int ctr = num_columns; ctr > 1; ctr--) {
448 for (
int ctr = num_rows; ctr > 1; ctr--) {
456 for (
int ctr = num_columns; ctr > 1; ctr--) {
462 for (
int ctr = num_rows; ctr > 1; ctr--) {
479 int height = NWidgetScrollbar::GetVerticalDimension().height;
489 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
493 int left = r.left + r.
Width() * 3 / 11;
494 int right = r.left + r.
Width() * 8 / 11;
499 GfxFillRect(left - bl, r.top + height, left - 1, r.bottom - height, c1);
500 GfxFillRect(left, r.top + height, left + br - 1, r.bottom - height, c2);
501 GfxFillRect(right - bl, r.top + height, right - 1, r.bottom - height, c1);
502 GfxFillRect(right, r.top + height, right + br - 1, r.bottom - height, c2);
519 int width = NWidgetScrollbar::GetHorizontalDimension().width;
528 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
532 int top = r.top + r.
Height() * 3 / 11;
533 int bottom = r.top + r.
Height() * 8 / 11;
538 GfxFillRect(r.left + width, top - bt, r.right - width, top - 1, c1);
539 GfxFillRect(r.left + width, top, r.right - width, top + bb - 1, c2);
540 GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom - 1, c1);
541 GfxFillRect(r.left + width, bottom, r.right - width, bottom + bb - 1, c2);
592 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
593 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
596 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
597 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
600 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
601 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
656static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
660 }
else if (clicked) {
673 if (colour != COLOUR_WHITE)
DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, {});
677 d.height -= offset.y;
694 bool company_owned = owner < MAX_COMPANIES;
704 if (str.empty())
return;
756 if (!widget->IsHighlighted())
continue;
784 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
795 return NWidgetScrollbar::GetVerticalDimension().width + 1;
798bool _draw_widget_outlines;
927 return (this->
type == tp) ? this :
nullptr;
930void NWidgetBase::ApplyAspectRatio()
944void NWidgetBase::AdjustPaddingForZoom()
980 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
983void NWidgetResizeBase::AdjustPaddingForZoom()
989 NWidgetBase::AdjustPaddingForZoom();
1013 this->min_x = std::max(this->min_x,
min_x);
1014 this->min_y = std::max(this->min_y,
min_y);
1066 d.height *= max_lines;
1082 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1083 this->min_x =
min_x;
1084 this->min_y =
min_y;
1096 if (
min_y == this->min_y)
return false;
1097 this->min_y =
min_y;
1218 this->align =
align;
1250 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1260 if (this->
type == tp)
return this;
1261 for (
const auto &child_wid : this->
children) {
1263 if (nwid !=
nullptr)
return nwid;
1268void NWidgetContainer::AdjustPaddingForZoom()
1270 for (
const auto &child_wid : this->
children) {
1271 child_wid->AdjustPaddingForZoom();
1273 NWidgetBase::AdjustPaddingForZoom();
1282 assert(wid !=
nullptr);
1284 this->
children.push_back(std::move(wid));
1289 for (
const auto &child_wid : this->
children) {
1296 for (
const auto &child_wid : this->
children) {
1300 DrawOutline(w,
this);
1307 for (
const auto &child_wid : this->
children) {
1309 if (nwid !=
nullptr)
return nwid;
1327 this->
fill_x = fill.width;
1328 this->
fill_y = fill.height;
1331 this->ApplyAspectRatio();
1342 for (
const auto &child_wid : this->
children) {
1343 child_wid->SetupSmallestSize(w);
1345 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1346 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1347 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1348 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1351 this->ApplyAspectRatio();
1362 for (
const auto &child_wid : this->
children) {
1363 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1364 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1365 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1367 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1368 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1369 uint child_pos_y = child_wid->padding.top;
1371 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1380 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1392 DrawOutline(w,
this);
1440 for (
const auto &child_wid : this->
children) {
1441 child_wid->SetupSmallestSize(w);
1443 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1444 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1445 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1446 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1449 this->ApplyAspectRatio();
1458 for (
const auto &child_wid : this->
children) {
1459 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1460 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1461 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1463 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1464 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1465 uint child_pos_y = child_wid->padding.top;
1467 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1474 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1478 DrawOutline(w,
this);
1483 this->flags = flags;
1486void NWidgetPIPContainer::AdjustPaddingForZoom()
1491 NWidgetContainer::AdjustPaddingForZoom();
1547 uint max_vert_fill = 0;
1548 for (
const auto &child_wid : this->
children) {
1550 longest = std::max(longest, child_wid->smallest_x);
1551 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1552 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1553 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1557 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1560 for (
const auto &child_wid : this->
children) {
1561 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1562 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1563 if (step_size > 1 && child_height < cur_height) {
1564 uint remainder = (cur_height - child_height) % step_size;
1565 if (remainder > 0) {
1566 cur_height += step_size - remainder;
1567 assert(cur_height < max_smallest);
1576 for (
const auto &child_wid : this->
children) {
1577 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1578 child_wid->ApplyAspectRatio();
1579 longest = std::max(longest, child_wid->smallest_x);
1582 for (
const auto &child_wid : this->
children) {
1583 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1587 for (
const auto &child_wid : this->
children) {
1588 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1589 if (child_wid->fill_x > 0) {
1590 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1592 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1594 if (child_wid->resize_x > 0) {
1610 for (
const auto &child_wid : this->
children) {
1611 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1630 int num_changing_childs = 0;
1631 uint biggest_stepsize = 0;
1632 for (
const auto &child_wid : this->
children) {
1633 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1636 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1638 child_wid->current_x = child_wid->smallest_x;
1641 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1642 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1647 for (
const auto &child_wid : this->
children) {
1648 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1649 if (hor_step == biggest_stepsize) {
1650 num_changing_childs++;
1656 while (biggest_stepsize > 0) {
1657 uint next_biggest_stepsize = 0;
1658 for (
const auto &child_wid : this->
children) {
1659 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1660 if (hor_step > biggest_stepsize)
continue;
1661 if (hor_step == biggest_stepsize) {
1662 uint increment = additional_length / num_changing_childs;
1663 num_changing_childs--;
1664 if (hor_step > 1) increment -= increment % hor_step;
1665 child_wid->current_x = child_wid->smallest_x + increment;
1666 additional_length -= increment;
1669 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1671 biggest_stepsize = next_biggest_stepsize;
1675 for (
const auto &child_wid : this->
children) {
1676 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1677 if (hor_step == biggest_stepsize) {
1678 num_changing_childs++;
1683 assert(num_changing_childs == 0);
1688 if (additional_length > 0) {
1699 uint position = rtl ? this->
current_x - pre : pre;
1700 for (
const auto &child_wid : this->
children) {
1701 uint child_width = child_wid->current_x;
1702 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1703 uint child_y = y + child_wid->padding.top;
1705 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1706 if (child_wid->current_x != 0) {
1707 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1708 position = rtl ? position - padded_child_width : position + padded_child_width;
1741 uint max_hor_fill = 0;
1742 for (
const auto &child_wid : this->
children) {
1744 highest = std::max(highest, child_wid->smallest_y);
1745 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1746 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1747 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1751 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1754 for (
const auto &child_wid : this->
children) {
1755 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1756 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1757 if (step_size > 1 && child_width < cur_width) {
1758 uint remainder = (cur_width - child_width) % step_size;
1759 if (remainder > 0) {
1760 cur_width += step_size - remainder;
1761 assert(cur_width < max_smallest);
1770 for (
const auto &child_wid : this->
children) {
1771 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1772 child_wid->ApplyAspectRatio();
1773 highest = std::max(highest, child_wid->smallest_y);
1776 for (
const auto &child_wid : this->
children) {
1777 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1781 for (
const auto &child_wid : this->
children) {
1782 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1783 if (child_wid->fill_y > 0) {
1784 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1786 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1788 if (child_wid->resize_y > 0) {
1804 for (
const auto &child_wid : this->
children) {
1805 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1815 int num_changing_childs = 0;
1816 uint biggest_stepsize = 0;
1817 for (
const auto &child_wid : this->
children) {
1818 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1819 if (vert_step > 0) {
1821 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1823 child_wid->current_y = child_wid->smallest_y;
1826 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1827 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1832 for (
const auto &child_wid : this->
children) {
1833 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1834 if (vert_step == biggest_stepsize) {
1835 num_changing_childs++;
1841 while (biggest_stepsize > 0) {
1842 uint next_biggest_stepsize = 0;
1843 for (
const auto &child_wid : this->
children) {
1844 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1845 if (vert_step > biggest_stepsize)
continue;
1846 if (vert_step == biggest_stepsize) {
1847 uint increment = additional_length / num_changing_childs;
1848 num_changing_childs--;
1849 if (vert_step > 1) increment -= increment % vert_step;
1850 child_wid->current_y = child_wid->smallest_y + increment;
1851 additional_length -= increment;
1854 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1856 biggest_stepsize = next_biggest_stepsize;
1860 for (
const auto &child_wid : this->
children) {
1861 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1862 if (vert_step == biggest_stepsize) {
1863 num_changing_childs++;
1868 assert(num_changing_childs == 0);
1873 if (additional_length > 0) {
1884 uint position = pre;
1885 for (
const auto &child_wid : this->
children) {
1886 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1887 uint child_height = child_wid->current_y;
1889 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1890 if (child_wid->current_y != 0) {
1891 position += child_height + child_wid->padding.Vertical() + inter;
1911 this->ApplyAspectRatio();
1926 DrawOutline(w,
this);
1942 this->colour = colour;
1952 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1968 this->count =
count;
1970 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
2006 assert(this->
children.size() == 1);
2008 this->
children.front()->SetupSmallestSize(w);
2011 Dimension size = {this->
children.front()->smallest_x + padding.width, this->
children.front()->smallest_y + padding.height};
2019 this->
fill_x = fill.width;
2020 this->
fill_y = fill.height;
2023 this->ApplyAspectRatio();
2052 if (this->
index >= 0) widget_lookup[this->
index] =
this;
2061 int start_x, start_y, base_offs_x, base_offs_y;
2066 int widget_col = (rtl ?
2077 assert(child !=
nullptr);
2101 assert(child !=
nullptr);
2102 int start_x, start_y, base_offs_x, base_offs_y;
2105 int offs_y = base_offs_y;
2106 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2108 if (offs_y + child->
smallest_y <= 0)
continue;
2109 if (offs_y >= (
int)this->
current_y)
break;
2114 int offs_x = base_offs_x;
2117 if (offs_x + child->
smallest_x <= 0)
continue;
2118 if (offs_x >= (
int)this->
current_x)
continue;
2131 DrawOutline(w,
this);
2147 if (this->
sb !=
nullptr) {
2155 base_offs_x += sub_x;
2157 base_offs_x -= sub_x;
2175 this->child = std::move(child);
2176 if (this->child !=
nullptr) this->child->parent =
this;
2189 if (this->
child ==
nullptr) {
2190 this->
child = std::make_unique<NWidgetVertical>();
2193 this->
child->Add(std::move(nwid));
2208 if (this->
child ==
nullptr) {
2209 this->
child = std::make_unique<NWidgetVertical>();
2211 this->
child->parent =
this;
2212 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2227 if (this->
child ==
nullptr) {
2228 this->
child = std::make_unique<NWidgetVertical>();
2230 this->
child->parent =
this;
2231 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2234void NWidgetBackground::AdjustPaddingForZoom()
2236 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2237 NWidgetCore::AdjustPaddingForZoom();
2242 if (this->
child !=
nullptr) {
2243 this->
child->SetupSmallestSize(w);
2253 if (w ==
nullptr)
return;
2263 std::string text = GetStringForWidget(w,
this);
2267 this->
child->padding = WidgetDimensions::scaled.
bevel;
2272 this->ApplyAspectRatio();
2279 std::string text = GetStringForWidget(w,
this);
2282 d =
maxdim(d, background);
2284 if (this->
index >= 0) {
2286 switch (this->
type) {
2287 default: NOT_REACHED();
2297 this->
fill_x = fill.width;
2298 this->
fill_y = fill.height;
2301 this->ApplyAspectRatio();
2309 if (this->
child !=
nullptr) {
2310 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2311 uint width = given_width - this->
child->padding.Horizontal();
2312 uint height = given_height - this->
child->padding.Vertical();
2313 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2319 if (this->
index >= 0) widget_lookup[this->
index] =
this;
2320 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2327 Rect r = this->GetCurrentRect();
2330 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2332 switch (this->
type) {
2350 if (this->
child !=
nullptr) this->
child->Draw(w);
2356 DrawOutline(w,
this);
2364 if (nwid ==
nullptr) nwid =
this;
2373 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2385 this->ApplyAspectRatio();
2402 if (this->
disp_flags.
Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2406 DrawOutline(w,
this);
2427 if (vp !=
nullptr) {
2451 return (pos < 0 || pos >= this->
GetCount()) ? Scrollbar::npos :
pos;
2470 int new_pos = list_position;
2540 const int count = sb.
GetCount() * resize_step;
2541 const int position = sb.
GetPosition() * resize_step;
2545 r.bottom = r.top + count;
2549 r.right += position;
2550 r.left = r.right - count;
2553 r.right = r.left + count;
2570 switch (this->
type) {
2574 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2580 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2583 default: NOT_REACHED();
2592 switch (this->
type) {
2594 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2598 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2601 default: NOT_REACHED();
2612 Rect r = this->GetCurrentRect();
2615 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2631 DrawOutline(w,
this);
2634 void NWidgetScrollbar::InvalidateDimensionCache()
2640 Dimension NWidgetScrollbar::GetVerticalDimension()
2650 Dimension NWidgetScrollbar::GetHorizontalDimension()
2691NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp, colour, index, 1, 1, data, tip)
2700 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2704 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2710 if (colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2722 case NWID_PUSHBUTTON_DROPDOWN:
2729 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2741 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2803 switch (this->
type) {
2872 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2936 case NWID_PUSHBUTTON_DROPDOWN: {
2957 this->
fill_x = fill.width;
2958 this->
fill_y = fill.height;
2961 this->ApplyAspectRatio();
2973 new_dpi.left += this->
pos_x;
2974 new_dpi.top += this->
pos_y;
2978 Rect r = this->GetCurrentRect();
2981 switch (this->
type) {
2984 if (this->
index == -1 && _draw_widget_outlines) {
3012 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
3013 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
3014 default: NOT_REACHED();
3029 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3034 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3071 case NWID_PUSHBUTTON_DROPDOWN:
3084 DrawOutline(w,
this);
3097 int button_width = this->
pos_x + this->
current_x - NWidgetLeaf::dropdown_dimension.width;
3098 return pt.x < button_width;
3100 int button_left = this->
pos_x + NWidgetLeaf::dropdown_dimension.width;
3101 return pt.x >= button_left;
3125 switch (nwid.
type) {
3128 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3129 assert(nwid.u.
xy.x >= 0 && nwid.u.
xy.y >= 0);
3136 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3137 assert(nwid.u.
xy.x >= 0 && nwid.u.
xy.y >= 0);
3144 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3152 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3159 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3166 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3173 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3180 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3191 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3202 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3208 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3214 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3236 switch (nwid.
type) {
3237 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3275static 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)
3279 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3284 if (dest ==
nullptr)
return nwid_begin;
3315static 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)
3321 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3323 while (nwid_begin != nwid_end) {
3324 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3325 bool fill_sub =
false;
3326 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3329 if (sub_widget ==
nullptr)
break;
3337 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3338 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3339 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3340 parent = std::move(sub_widget);
3345 if (nwid_begin == nwid_end)
return nwid_begin;
3347 assert(nwid_begin < nwid_end);
3349 return std::next(nwid_begin);
3359std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3361 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3362 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3364 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3366 return std::move(container);
3380 auto nwid_begin = std::begin(nwid_parts);
3381 auto nwid_end = std::end(nwid_parts);
3383 *shade_select =
nullptr;
3386 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3388 assert(nwid !=
nullptr);
3392 auto root = std::make_unique<NWidgetVertical>();
3393 root->Add(std::move(nwid));
3394 if (nwid_begin == nwid_end)
return root;
3398 auto shade_stack = std::make_unique<NWidgetStacked>(-1);
3399 *shade_select = shade_stack.get();
3401 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3402 root->Add(std::move(shade_stack));
3407 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3422 assert(max_length >= 1);
3423 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3424 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3431 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3433 if (hor_length == max_length) {
3434 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3435 vert->Add(std::move(hor));
3439 if (hor ==
nullptr) {
3440 hor = std::make_unique<NWidgetHorizontal>();
3444 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3445 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3446 panel->SetFill(1, 1);
3447 if (resizable) panel->SetResize(1, 0);
3448 panel->SetToolTip(button_tooltip);
3449 hor->Add(std::move(panel));
3452 if (vert ==
nullptr)
return hor;
3454 if (hor_length > 0 && hor_length < max_length) {
3456 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3458 if (resizable) spc->SetResize(1, 0);
3459 hor->Add(std::move(spc));
3461 if (hor !=
nullptr) vert->Add(std::move(hor));
Class for backupping variables and making sure they are restored later.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
ReferenceThroughBaseContainer< std::array< Colours, MAX_COMPANIES > > _company_colours
NOSAVE: can be determined from company structs.
Functions related to companies.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
Draw the outline of a Rect.
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.
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.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
FontSize
Available font sizes.
@ FS_NORMAL
Index of the normal font in the font tables.
StringAlignment
How to align the to-be drawn text.
@ SA_TOP
Top align the text.
@ SA_LEFT
Left align the text.
@ SA_HOR_MASK
Mask for horizontal alignment.
@ SA_RIGHT
Right align the text (must be a single bit).
@ SA_HOR_CENTER
Horizontally center the text.
@ SA_VERT_MASK
Mask for vertical alignment.
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
@ SA_BOTTOM
Bottom align the text.
@ SA_CENTER
Center both horizontally and vertically.
@ SA_VERT_CENTER
Vertically center the text.
uint32_t PaletteID
The number of the palette.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
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.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
static const uint8_t PC_WHITE
White palette colour.
static const uint8_t PC_BLACK
Black palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
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.
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
static const PaletteID PALETTE_NEWSPAPER
Recolour sprite for newspaper-greying.
Definition of base types and functions in a cross-platform compatible way.
The colour translation of GRF's strings.
static const uint8_t _string_colourmap[17]
Colour mapping for TextColour.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
TextDirection _current_text_dir
Text direction of the currently selected language.
Functions related to OTTD's strings.
Types related to strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_LTR
Text is written left-to-right by default.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
GUISettings gui
settings related to the GUI
Point pos
logical mouse position
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
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
uint8_t lines
Number of text lines.
uint8_t spacing
Extra spacing around lines.
FontSize size
Font size of text lines.
TextColour colour
TextColour for DrawString.
FontSize size
Font size of text.
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.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
Data structure for an opened window.
virtual void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
Update size and resize step of a widget in the window.
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
void DrawWidgets() const
Paint all widgets of a window.
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
ViewportData * viewport
Pointer to viewport data, if present.
void DrawViewport() const
Draw the viewport of this window.
virtual void DrawWidget(const Rect &r, WidgetID widget) const
Draw the contents of a nested widget.
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
int left
x position of left edge of the window
bool IsShaded() const
Is window shaded currently?
int top
y position of top edge of the window
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
WindowFlags flags
Window flags.
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
int height
Height of the window (number of pixels down in y direction)
int width
width of the window (number of pixels to the right in x direction)
Functions related to transparency.
TransparencyOptionBits _transparency_opt
The bits that should be transparent.
uint TransparencyOptionBits
transparency option bits
@ TO_TEXT
loading and cost/income text
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.
Functions related to (drawing on) viewports.
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Functions, definitions and such used only by the GUI.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
@ Transparent
Makes the background transparent if set.
@ BorderOnly
Draw border only, no background.
@ Darkened
If set the background is darker, allows for lowered frames with normal background colour when used wi...
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
@ SizingLeft
Window is being resized towards the left.
@ Highlighted
Window has a widget that has a highlight.
@ SizingRight
Window is being resized towards the right.
@ WhiteBorder
Window white border counter bit mask.
@ Sticky
Window is made sticky by user.
SortButtonState
State of a sort direction button.
@ SBS_DOWN
Sort ascending.
@ SBS_OFF
Do not sort (with this button).
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_MIN) When shifting right,...
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
ZoomLevel
All zoom levels we know.
@ ZOOM_LVL_NORMAL
The normal zoom level.