25#include "table/strings.h"
32static std::string GetStringForWidget(
const Window *w,
const NWidgetCore *nwid,
bool secondary =
false)
35 if (nwid->GetIndex() < 0) {
36 if (stringid == STR_NULL)
return {};
38 return GetString(stringid + (secondary ? 1 : 0));
41 return w->
GetWidgetString(nwid->GetIndex(), stringid + (secondary ? 1 : 0));
130 case SA_RIGHT: p.
x = r.right + 1 - d.width;
break;
131 default: NOT_REACHED();
134 case SA_TOP: p.
y = r.top;
break;
136 case SA_BOTTOM: p.
y = r.bottom + 1 - d.height;
break;
137 default: NOT_REACHED();
154 int rev_base = mi + ma;
155 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
164 int height = ma + 1 - mi;
165 int slider_height = std::max(button_size, cap * height / count);
166 height -= slider_height;
169 ma = mi + slider_height - 1;
192 bool changed =
false;
197 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
200 button_size = NWidgetScrollbar::GetVerticalDimension().height;
202 if (pos < mi + button_size) {
205 if (_scroller_click_timeout <= 1) {
206 _scroller_click_timeout = 3;
210 }
else if (pos >= ma - button_size) {
214 if (_scroller_click_timeout <= 1) {
215 _scroller_click_timeout = 3;
224 }
else if (pos > end) {
227 _scrollbar_start_pos = start - mi - button_size;
228 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
230 _cursorpos_drag_start = _cursor.pos;
264 assert(scrollbar !=
nullptr);
291void DrawFrameRect(
int left,
int top,
int right,
int bottom, Colours colour, FrameFlags flags)
296 assert(colour < COLOUR_END);
304 Rect outer = {left, top, right, bottom};
308 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
309 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
310 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
311 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
314 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
315 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
316 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
317 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
318 interior = medium_dark;
321 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
331 d.height -= offset.
y;
351 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
352 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
382 DrawString(r_text.left, r_text.right, p.
y, text, text_colour, align,
false, fs);
396 if (str.empty())
return;
400 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
413 if (str.empty())
return;
417 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
445static inline void DrawMatrix(
const Rect &r, Colours colour,
bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
450 if (num_columns == 0) {
451 column_width = resize_x;
452 num_columns = r.
Width() / column_width;
454 column_width = r.
Width() / num_columns;
459 row_height = resize_y;
460 num_rows = r.
Height() / row_height;
462 row_height = r.
Height() / num_rows;
468 for (
int ctr = num_columns; ctr > 1; ctr--) {
474 for (
int ctr = num_rows; ctr > 1; ctr--) {
482 for (
int ctr = num_columns; ctr > 1; ctr--) {
488 for (
int ctr = num_rows; ctr > 1; ctr--) {
505 int height = NWidgetScrollbar::GetVerticalDimension().height;
520 int left = r.left + r.
Width() * 3 / 11;
521 int right = r.left + r.
Width() * 8 / 11;
546 int width = NWidgetScrollbar::GetHorizontalDimension().width;
560 int top = r.top + r.
Height() * 3 / 11;
561 int bottom = r.top + r.
Height() * 8 / 11;
620 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
621 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
624 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
625 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
628 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
629 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
684static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
688 }
else if (clicked) {
705 d.height -= offset.y;
722 bool company_owned = owner < MAX_COMPANIES;
732 if (str.empty())
return;
782 Rect outer = widget->GetCurrentRect();
787 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
788 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
789 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
790 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
808 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
819 return NWidgetScrollbar::GetVerticalDimension().width + 1;
822bool _draw_widget_outlines;
919 if (this->
index >= 0) widget_lookup[this->
index] =
this;
955 return (this->
type == tp) ? this :
nullptr;
958void NWidgetBase::ApplyAspectRatio()
1009 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1042 this->min_x = std::max(this->min_x,
min_x);
1043 this->min_y = std::max(this->min_y,
min_y);
1095 d.height *= max_lines;
1111 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1112 this->min_x =
min_x;
1113 this->min_y =
min_y;
1125 if (
min_y == this->min_y)
return false;
1126 this->min_y =
min_y;
1247 this->align =
align;
1275 if (this->
type == tp)
return this;
1276 for (
const auto &child_wid : this->
children) {
1278 if (nwid !=
nullptr)
return nwid;
1285 for (
const auto &child_wid : this->
children) {
1286 child_wid->AdjustPaddingForZoom();
1297 assert(wid !=
nullptr);
1299 this->
children.push_back(std::move(wid));
1305 for (
const auto &child_wid : this->
children) {
1306 child_wid->FillWidgetLookup(widget_lookup);
1312 for (
const auto &child_wid : this->
children) {
1316 DrawOutline(w,
this);
1323 for (
const auto &child_wid : this->
children) {
1325 if (nwid !=
nullptr)
return nwid;
1343 this->
fill_x = fill.width;
1344 this->
fill_y = fill.height;
1347 this->ApplyAspectRatio();
1358 for (
const auto &child_wid : this->
children) {
1359 child_wid->SetupSmallestSize(w);
1361 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1362 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1363 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1364 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1367 this->ApplyAspectRatio();
1378 for (
const auto &child_wid : this->
children) {
1379 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1380 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1381 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1383 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1384 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1385 uint child_pos_y = child_wid->padding.top;
1387 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1407 DrawOutline(w,
this);
1434class NWidgetLayer :
public NWidgetContainer {
1453 for (
const auto &child_wid : this->
children) {
1454 child_wid->SetupSmallestSize(w);
1456 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1457 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1458 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1459 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1462 this->ApplyAspectRatio();
1471 for (
const auto &child_wid : this->
children) {
1472 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1473 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1474 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1476 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1477 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1478 uint child_pos_y = child_wid->padding.top;
1480 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1487 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1491 DrawOutline(w,
this);
1550 uint max_vert_fill = 0;
1551 for (
const auto &child_wid : this->
children) {
1552 child_wid->SetupSmallestSize(w);
1553 longest = std::max(longest, child_wid->smallest_x);
1554 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1555 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1556 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1560 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1563 for (
const auto &child_wid : this->children) {
1564 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1565 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1566 if (step_size > 1 && child_height < cur_height) {
1567 uint remainder = (cur_height - child_height) % step_size;
1568 if (remainder > 0) {
1569 cur_height += step_size - remainder;
1570 assert(cur_height < max_smallest);
1579 for (
const auto &child_wid : this->children) {
1580 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1581 child_wid->ApplyAspectRatio();
1582 longest = std::max(longest, child_wid->smallest_x);
1585 for (
const auto &child_wid : this->children) {
1586 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1590 for (
const auto &child_wid : this->children) {
1591 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1592 if (child_wid->fill_x > 0) {
1593 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1595 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1597 if (child_wid->resize_x > 0) {
1613 for (
const auto &child_wid : this->
children) {
1614 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1633 int num_changing_childs = 0;
1634 uint biggest_stepsize = 0;
1635 for (
const auto &child_wid : this->children) {
1636 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1639 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1641 child_wid->current_x = child_wid->smallest_x;
1644 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1645 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1650 for (
const auto &child_wid : this->children) {
1651 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1652 if (hor_step == biggest_stepsize) {
1653 num_changing_childs++;
1659 while (biggest_stepsize > 0) {
1660 uint next_biggest_stepsize = 0;
1661 for (
const auto &child_wid : this->children) {
1662 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1663 if (hor_step > biggest_stepsize)
continue;
1664 if (hor_step == biggest_stepsize) {
1665 uint increment = additional_length / num_changing_childs;
1666 num_changing_childs--;
1667 if (hor_step > 1) increment -= increment % hor_step;
1668 child_wid->current_x = child_wid->smallest_x + increment;
1669 additional_length -= increment;
1672 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1674 biggest_stepsize = next_biggest_stepsize;
1678 for (
const auto &child_wid : this->children) {
1679 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1680 if (hor_step == biggest_stepsize) {
1681 num_changing_childs++;
1686 assert(num_changing_childs == 0);
1691 if (additional_length > 0) {
1702 uint position = rtl ? this->
current_x - pre : pre;
1703 for (
const auto &child_wid : this->children) {
1704 uint child_width = child_wid->current_x;
1705 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1706 uint child_y = y + child_wid->padding.top;
1708 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1709 if (child_wid->current_x != 0) {
1710 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1711 position = rtl ? position - padded_child_width : position + padded_child_width;
1733 uint max_hor_fill = 0;
1734 for (
const auto &child_wid : this->
children) {
1735 child_wid->SetupSmallestSize(w);
1736 highest = std::max(highest, child_wid->smallest_y);
1737 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1738 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1739 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1743 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1746 for (
const auto &child_wid : this->children) {
1747 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1748 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1749 if (step_size > 1 && child_width < cur_width) {
1750 uint remainder = (cur_width - child_width) % step_size;
1751 if (remainder > 0) {
1752 cur_width += step_size - remainder;
1753 assert(cur_width < max_smallest);
1762 for (
const auto &child_wid : this->children) {
1763 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1764 child_wid->ApplyAspectRatio();
1765 highest = std::max(highest, child_wid->smallest_y);
1768 for (
const auto &child_wid : this->children) {
1769 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1773 for (
const auto &child_wid : this->children) {
1774 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1775 if (child_wid->fill_y > 0) {
1776 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1778 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1780 if (child_wid->resize_y > 0) {
1796 for (
const auto &child_wid : this->
children) {
1797 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1807 int num_changing_childs = 0;
1808 uint biggest_stepsize = 0;
1809 for (
const auto &child_wid : this->children) {
1810 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1811 if (vert_step > 0) {
1813 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1815 child_wid->current_y = child_wid->smallest_y;
1818 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1819 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1824 for (
const auto &child_wid : this->children) {
1825 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1826 if (vert_step == biggest_stepsize) {
1827 num_changing_childs++;
1833 while (biggest_stepsize > 0) {
1834 uint next_biggest_stepsize = 0;
1835 for (
const auto &child_wid : this->children) {
1836 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1837 if (vert_step > biggest_stepsize)
continue;
1838 if (vert_step == biggest_stepsize) {
1839 uint increment = additional_length / num_changing_childs;
1840 num_changing_childs--;
1841 if (vert_step > 1) increment -= increment % vert_step;
1842 child_wid->current_y = child_wid->smallest_y + increment;
1843 additional_length -= increment;
1846 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1848 biggest_stepsize = next_biggest_stepsize;
1852 for (
const auto &child_wid : this->children) {
1853 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1854 if (vert_step == biggest_stepsize) {
1855 num_changing_childs++;
1860 assert(num_changing_childs == 0);
1865 if (additional_length > 0) {
1877 for (
const auto &child_wid : this->children) {
1878 uint child_height = child_wid->current_y;
1879 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1880 uint child_y = y + (this->
bottom_up ? position - child_height - child_wid->padding.top : position + child_wid->padding.top);
1882 child_wid->AssignSizePosition(sizing, child_x, child_y, child_wid->current_x, child_height, rtl);
1883 if (child_wid->current_y != 0) {
1884 uint padded_child_height = child_height + child_wid->padding.Vertical() + inter;
1885 position = this->
bottom_up ? position - padded_child_height : position + padded_child_height;
1905 this->ApplyAspectRatio();
1916 DrawOutline(w,
this);
1937 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1942 this->
sb->ScrollTowards(vpos);
1953 this->count =
count;
1955 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1963 count *= (this->
sb->IsVertical() ? this->
children.front()->smallest_y : this->children.front()->smallest_x) + this->
pip_inter;
1967 this->
sb->SetCapacity(this->
sb->IsVertical() ? this->current_y : this->current_x);
1968 this->
sb->SetStepSize(this->
sb->IsVertical() ? this->widget_h : this->widget_w);
1991 assert(this->
children.size() == 1);
1993 this->
children.front()->SetupSmallestSize(w);
2004 this->
fill_x = fill.width;
2005 this->
fill_y = fill.height;
2008 this->ApplyAspectRatio();
2040 int start_x, start_y, base_offs_x, base_offs_y;
2045 int widget_col = (rtl ?
2056 assert(child !=
nullptr);
2080 assert(child !=
nullptr);
2081 int start_x, start_y, base_offs_x, base_offs_y;
2084 int offs_y = base_offs_y;
2085 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2087 if (offs_y + child->
smallest_y <= 0)
continue;
2088 if (offs_y >= (
int)this->
current_y)
break;
2093 int offs_x = base_offs_x;
2096 if (offs_x + child->
smallest_x <= 0)
continue;
2097 if (offs_x >= (
int)this->
current_x)
continue;
2110 DrawOutline(w,
this);
2126 if (this->
sb !=
nullptr) {
2127 if (this->
sb->IsVertical()) {
2128 start_y = this->
sb->GetPosition() / this->
widget_h;
2129 base_offs_y += -this->
sb->GetPosition() + start_y * this->
widget_h;
2131 start_x = this->
sb->GetPosition() / this->widget_w;
2132 int sub_x = this->
sb->GetPosition() - start_x * this->widget_w;
2134 base_offs_x += sub_x;
2136 base_offs_x -= sub_x;
2154 this->child = std::move(child);
2155 if (this->child !=
nullptr) this->child->parent =
this;
2168 if (this->
child ==
nullptr) {
2169 this->
child = std::make_unique<NWidgetVertical>();
2172 this->
child->Add(std::move(nwid));
2187 if (this->
child ==
nullptr) {
2188 this->
child = std::make_unique<NWidgetVertical>();
2190 this->
child->parent =
this;
2191 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2206 if (this->
child ==
nullptr) {
2207 this->
child = std::make_unique<NWidgetVertical>();
2209 this->
child->parent =
this;
2210 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2215 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2221 if (this->
child !=
nullptr) {
2222 this->
child->SetupSmallestSize(w);
2232 if (w ==
nullptr)
return;
2235 std::string text = GetStringForWidget(w,
this);
2253 this->ApplyAspectRatio();
2260 std::string text = GetStringForWidget(w,
this);
2261 if (!text.empty()) {
2264 d =
maxdim(d, background);
2267 if (this->
index >= 0) {
2269 switch (this->
type) {
2270 default: NOT_REACHED();
2280 this->
fill_x = fill.width;
2281 this->
fill_y = fill.height;
2284 this->ApplyAspectRatio();
2292 if (this->
child !=
nullptr) {
2293 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2294 uint width = given_width - this->
child->padding.Horizontal();
2295 uint height = given_height - this->
child->padding.Vertical();
2296 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2303 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2310 Rect r = this->GetCurrentRect();
2313 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2315 switch (this->
type) {
2333 if (this->
child !=
nullptr) this->
child->Draw(w);
2339 DrawOutline(w,
this);
2346 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetFromPos(x, y);
2347 if (nwid ==
nullptr) nwid =
this;
2355 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetOfType(tp);
2356 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2368 this->ApplyAspectRatio();
2385 if (this->
disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2389 DrawOutline(w,
this);
2409 if (w->
viewport ==
nullptr)
return;
2453 int new_pos = list_position;
2523 const int count = sb.
GetCount() * resize_step;
2524 const int position = sb.
GetPosition() * resize_step;
2528 r.bottom = r.top + count;
2532 r.right += position;
2533 r.left = r.right - count;
2536 r.right = r.left + count;
2553 switch (this->type) {
2557 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2563 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2566 default: NOT_REACHED();
2575 switch (this->
type) {
2577 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2581 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2584 default: NOT_REACHED();
2595 Rect r = this->GetCurrentRect();
2598 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2614 DrawOutline(w,
this);
2617 void NWidgetScrollbar::InvalidateDimensionCache()
2623 Dimension NWidgetScrollbar::GetVerticalDimension()
2633 Dimension NWidgetScrollbar::GetHorizontalDimension()
2674NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours
colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp,
colour,
index, 1, 1, data, tip)
2683 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2687 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2693 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2708 case NWID_PUSHBUTTON_DROPDOWN:
2715 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2727 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2789 switch (this->
type) {
2858 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2891 padding.height + std::max(di.height, dt.height)
2942 case NWID_PUSHBUTTON_DROPDOWN: {
2963 this->
fill_x = fill.width;
2964 this->
fill_y = fill.height;
2967 this->ApplyAspectRatio();
2979 new_dpi.left += this->
pos_x;
2980 new_dpi.top += this->
pos_y;
2984 Rect r = this->GetCurrentRect();
2987 switch (this->
type) {
2990 if (this->
index == -1 && _draw_widget_outlines) {
3001 Colours button_colour = this->
widget_data.alternate_colour;
3002 if (button_colour == INVALID_COLOUR) button_colour = this->
colour;
3003 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3031 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
3032 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
3033 default: NOT_REACHED();
3048 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3053 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3090 case NWID_PUSHBUTTON_DROPDOWN:
3104 DrawOutline(w,
this);
3118 return pt.
x < button_width;
3121 return pt.
x >= button_left;
3145 switch (nwid.
type) {
3148 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3149 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3156 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3157 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3164 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3172 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3179 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3186 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3193 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3200 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3211 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3222 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3228 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3234 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3256 switch (nwid.
type) {
3257 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3295static 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)
3299 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3304 if (dest ==
nullptr)
return nwid_begin;
3335static 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)
3341 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3343 while (nwid_begin != nwid_end) {
3344 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3345 bool fill_sub =
false;
3346 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3349 if (sub_widget ==
nullptr)
break;
3357 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3358 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3359 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3360 parent = std::move(sub_widget);
3365 if (nwid_begin == nwid_end)
return nwid_begin;
3367 assert(nwid_begin < nwid_end);
3369 return std::next(nwid_begin);
3379std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3381 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3382 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3384 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3386 return std::move(container);
3400 auto nwid_begin = std::begin(nwid_parts);
3401 auto nwid_end = std::end(nwid_parts);
3403 *shade_select =
nullptr;
3406 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3408 assert(nwid !=
nullptr);
3412 auto root = std::make_unique<NWidgetVertical>();
3413 root->Add(std::move(nwid));
3414 if (nwid_begin == nwid_end)
return root;
3418 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3419 *shade_select = shade_stack.get();
3421 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3422 root->Add(std::move(shade_stack));
3427 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3442 assert(max_length >= 1);
3443 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3444 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3451 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3453 if (hor_length == max_length) {
3454 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3455 vert->Add(std::move(hor));
3459 if (hor ==
nullptr) {
3460 hor = std::make_unique<NWidgetHorizontal>();
3464 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3465 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3466 panel->SetFill(1, 1);
3467 if (resizable) panel->SetResize(1, 0);
3468 panel->SetToolTip(button_tooltip);
3469 hor->Add(std::move(panel));
3472 if (vert ==
nullptr)
return hor;
3474 if (hor_length > 0 && hor_length < max_length) {
3476 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3478 if (resizable) spc->SetResize(1, 0);
3479 hor->Add(std::move(spc));
3481 if (hor !=
nullptr) vert->Add(std::move(hor));
3492 assert(parent_window !=
nullptr);
3494 for (
auto &widget : this->
children) {
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.
TypedIndexContainer< std::array< Colours, MAX_COMPANIES >, CompanyID > _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 CentreBounds(int min, int max, int size)
Determine where to position a centred object.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
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.
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 DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
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.
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.
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
static constexpr PixelColour PC_BLACK
Black palette colour.
static constexpr PixelColour PC_WHITE
White palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
void DrawBoolButton(int x, int y, Colours button_colour, Colours background, bool state, bool clickable)
Draw a toggle button.
Functions for setting GUIs.
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
#define SETTING_BUTTON_HEIGHT
Height of setting buttons.
Types related to global configuration settings.
This file contains all sprite-related enums and defines.
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
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 constexpr PixelColour _string_colourmap[17]
Colour mapping for TextColour.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
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...
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
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.
Colour for pixel/line drawing.
Data stored about a string that can be modified in the GUI.
Padding dimensions to apply to each side of a Rect.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect CentreToHeight(int height) const
Centre a vertical dimension within this Rect.
int Height() const
Get height of Rect.
Rect WithY(int new_top, int new_bottom) const
Create a new Rect, replacing the top and bottom coordiates.
Rect WithX(int new_left, int new_right) const
Create a new Rect, replacing the left and right coordiates.
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.
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
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). INVALID_WIDGET if no widget has mouse ca...
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
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?
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
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.
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::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.
@ Normal
The normal zoom level.