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));
88 uint x = std::max(d.width, d.height);
148 default: NOT_REACHED();
154 default: NOT_REACHED();
171 int rev_base = mi + ma;
172 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
181 int height = ma + 1 - mi;
182 int slider_height = std::max(button_size, cap * height / count);
183 height -= slider_height;
186 ma = mi + slider_height - 1;
209 bool changed =
false;
214 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
217 button_size = NWidgetScrollbar::GetVerticalDimension().height;
219 if (pos < mi + button_size) {
222 if (_scroller_click_timeout <= 1) {
223 _scroller_click_timeout = 3;
227 }
else if (pos >= ma - button_size) {
231 if (_scroller_click_timeout <= 1) {
232 _scroller_click_timeout = 3;
241 }
else if (pos > end) {
244 _scrollbar_start_pos = start - mi - button_size;
245 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
247 _cursorpos_drag_start = _cursor.pos;
281 assert(scrollbar !=
nullptr);
321 Rect outer = {left, top, right, bottom};
325 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
326 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
327 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
328 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
331 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
332 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
333 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
334 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
335 interior = medium_dark;
338 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
355 d.height -= offset.
y;
375 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
406 DrawString(r_text.left, r_text.right, p.
y, text, text_colour, align,
false, fs);
420 if (str.empty())
return;
424 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
437 if (str.empty())
return;
441 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
469static inline void DrawMatrix(
const Rect &r,
Colours colour,
bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
474 if (num_columns == 0) {
475 column_width = resize_x;
476 num_columns = r.
Width() / column_width;
478 column_width = r.
Width() / num_columns;
483 row_height = resize_y;
484 num_rows = r.
Height() / row_height;
486 row_height = r.
Height() / num_rows;
492 for (
int ctr = num_columns; ctr > 1; ctr--) {
498 for (
int ctr = num_rows; ctr > 1; ctr--) {
506 for (
int ctr = num_columns; ctr > 1; ctr--) {
512 for (
int ctr = num_rows; ctr > 1; ctr--) {
529 int height = NWidgetScrollbar::GetVerticalDimension().height;
544 int left = r.left + r.
Width() * 3 / 11;
545 int right = r.left + r.
Width() * 8 / 11;
570 int width = NWidgetScrollbar::GetHorizontalDimension().width;
584 int top = r.top + r.
Height() * 3 / 11;
585 int bottom = r.top + r.
Height() * 8 / 11;
644 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
645 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
648 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
649 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
652 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
653 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
712 }
else if (clicked) {
729 d.height -= offset.y;
746 bool company_owned = owner < MAX_COMPANIES;
756 if (str.empty())
return;
806 Rect outer = widget->GetCurrentRect();
811 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
812 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
813 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
814 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
830 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
841 return NWidgetScrollbar::GetVerticalDimension().width + 1;
844bool _draw_widget_outlines;
941 if (this->
index >= 0) widget_lookup[this->
index] =
this;
977 return (this->
type == tp) ? this :
nullptr;
980void NWidgetBase::ApplyAspectRatio()
1031 assert(x_ratio > 0 && y_ratio > 0);
1032 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1076 this->min_x = std::max(this->min_x,
min_x);
1077 this->min_y = std::max(this->min_y,
min_y);
1128 d.height *= max_lines;
1144 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1145 this->min_x =
min_x;
1146 this->min_y =
min_y;
1158 if (
min_y == this->min_y)
return false;
1159 this->min_y =
min_y;
1280 this->align =
align;
1308 if (this->
type == tp)
return this;
1309 for (
const auto &child_wid : this->
children) {
1311 if (nwid !=
nullptr)
return nwid;
1318 for (
const auto &child_wid : this->
children) {
1319 child_wid->AdjustPaddingForZoom();
1330 assert(wid !=
nullptr);
1332 this->
children.push_back(std::move(wid));
1338 for (
const auto &child_wid : this->
children) {
1339 child_wid->FillWidgetLookup(widget_lookup);
1345 for (
const auto &child_wid : this->
children) {
1349 DrawOutline(w,
this);
1356 for (
const auto &child_wid : this->
children) {
1358 if (nwid !=
nullptr)
return nwid;
1376 this->
fill_x = fill.width;
1377 this->
fill_y = fill.height;
1380 this->ApplyAspectRatio();
1391 for (
const auto &child_wid : this->
children) {
1392 child_wid->SetupSmallestSize(w);
1394 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1395 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1396 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1397 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1400 this->ApplyAspectRatio();
1411 for (
const auto &child_wid : this->
children) {
1412 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1413 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1414 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1416 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1417 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1418 uint child_pos_y = child_wid->padding.top;
1420 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1440 DrawOutline(w,
this);
1467class NWidgetLayer :
public NWidgetContainer {
1486 for (
const auto &child_wid : this->
children) {
1487 child_wid->SetupSmallestSize(w);
1489 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1490 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1491 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1492 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1495 this->ApplyAspectRatio();
1504 for (
const auto &child_wid : this->
children) {
1505 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1506 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1507 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1509 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1510 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1511 uint child_pos_y = child_wid->padding.top;
1513 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1520 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1524 DrawOutline(w,
this);
1583 uint max_vert_fill = 0;
1584 for (
const auto &child_wid : this->
children) {
1585 child_wid->SetupSmallestSize(w);
1586 longest = std::max(longest, child_wid->smallest_x);
1588 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1589 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1593 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1596 for (
const auto &child_wid : this->children) {
1598 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1599 if (step_size > 1 && child_height < cur_height) {
1600 uint remainder = (cur_height - child_height) % step_size;
1601 if (remainder > 0) {
1602 cur_height += step_size - remainder;
1603 assert(cur_height < max_smallest);
1612 for (
const auto &child_wid : this->children) {
1613 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1614 child_wid->ApplyAspectRatio();
1615 longest = std::max(longest, child_wid->smallest_x);
1618 for (
const auto &child_wid : this->children) {
1619 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1623 for (
const auto &child_wid : this->children) {
1624 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1625 if (child_wid->fill_x > 0) {
1626 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1628 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1630 if (child_wid->resize_x > 0) {
1646 for (
const auto &child_wid : this->
children) {
1647 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1666 int num_changing_childs = 0;
1667 uint biggest_stepsize = 0;
1668 for (
const auto &child_wid : this->children) {
1669 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1672 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1674 child_wid->current_x = child_wid->smallest_x;
1677 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1678 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1681 for (
const auto &child_wid : this->children) {
1682 auto [pref_x, _] = child_wid->GetPreferredSizeForSize(child_wid->current_x, child_wid->current_y);
1683 additional_length += child_wid->current_x - pref_x;
1684 child_wid->current_x = pref_x;
1689 for (
const auto &child_wid : this->children) {
1690 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1691 if (hor_step == biggest_stepsize) {
1692 num_changing_childs++;
1698 while (biggest_stepsize > 0) {
1699 uint next_biggest_stepsize = 0;
1700 for (
const auto &child_wid : this->children) {
1701 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1702 if (hor_step > biggest_stepsize)
continue;
1703 if (hor_step == biggest_stepsize) {
1704 uint increment = additional_length / num_changing_childs;
1705 num_changing_childs--;
1706 if (hor_step > 1) increment -= increment % hor_step;
1707 child_wid->current_x = child_wid->smallest_x + increment;
1708 additional_length -= increment;
1711 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1713 biggest_stepsize = next_biggest_stepsize;
1717 for (
const auto &child_wid : this->children) {
1718 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1719 if (hor_step == biggest_stepsize) {
1720 num_changing_childs++;
1725 assert(num_changing_childs == 0);
1730 if (additional_length > 0) {
1741 uint position = rtl ? this->
current_x - pre : pre;
1742 for (
const auto &child_wid : this->children) {
1743 uint child_width = child_wid->current_x;
1744 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1745 uint child_y = y + child_wid->padding.top;
1747 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1748 if (child_wid->current_x != 0) {
1749 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1750 position = rtl ? position - padded_child_width : position + padded_child_width;
1772 uint max_hor_fill = 0;
1773 for (
const auto &child_wid : this->
children) {
1774 child_wid->SetupSmallestSize(w);
1775 highest = std::max(highest, child_wid->smallest_y);
1777 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1778 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1782 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1785 for (
const auto &child_wid : this->children) {
1787 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1788 if (step_size > 1 && child_width < cur_width) {
1789 uint remainder = (cur_width - child_width) % step_size;
1790 if (remainder > 0) {
1791 cur_width += step_size - remainder;
1792 assert(cur_width < max_smallest);
1801 for (
const auto &child_wid : this->children) {
1802 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1803 child_wid->ApplyAspectRatio();
1804 highest = std::max(highest, child_wid->smallest_y);
1807 for (
const auto &child_wid : this->children) {
1808 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1812 for (
const auto &child_wid : this->children) {
1813 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1814 if (child_wid->fill_y > 0) {
1815 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1817 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1819 if (child_wid->resize_y > 0) {
1835 for (
const auto &child_wid : this->
children) {
1836 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1846 int num_changing_childs = 0;
1847 uint biggest_stepsize = 0;
1848 for (
const auto &child_wid : this->children) {
1849 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1850 if (vert_step > 0) {
1852 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1854 child_wid->current_y = child_wid->smallest_y;
1857 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1858 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1861 for (
const auto &child_wid : this->children) {
1862 auto [_, pref_y] = child_wid->GetPreferredSizeForSize(child_wid->current_x, child_wid->current_y);
1863 additional_length += child_wid->current_y - pref_y;
1864 child_wid->current_y = pref_y;
1869 for (
const auto &child_wid : this->children) {
1870 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1871 if (vert_step == biggest_stepsize) {
1872 num_changing_childs++;
1878 while (biggest_stepsize > 0) {
1879 uint next_biggest_stepsize = 0;
1880 for (
const auto &child_wid : this->children) {
1881 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1882 if (vert_step > biggest_stepsize)
continue;
1883 if (vert_step == biggest_stepsize) {
1884 uint increment = additional_length / num_changing_childs;
1885 num_changing_childs--;
1886 if (vert_step > 1) increment -= increment % vert_step;
1887 child_wid->current_y = child_wid->smallest_y + increment;
1888 additional_length -= increment;
1891 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1893 biggest_stepsize = next_biggest_stepsize;
1897 for (
const auto &child_wid : this->children) {
1898 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1899 if (vert_step == biggest_stepsize) {
1900 num_changing_childs++;
1905 assert(num_changing_childs == 0);
1910 if (additional_length > 0) {
1922 for (
const auto &child_wid : this->children) {
1923 uint child_height = child_wid->current_y;
1924 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1925 uint child_y = y + (this->
bottom_up ? position - child_height - child_wid->padding.top : position + child_wid->padding.top);
1927 child_wid->AssignSizePosition(sizing, child_x, child_y, child_wid->current_x, child_height, rtl);
1928 if (child_wid->current_y != 0) {
1929 uint padded_child_height = child_height + child_wid->padding.Vertical() + inter;
1930 position = this->
bottom_up ? position - padded_child_height : position + padded_child_height;
1950 this->ApplyAspectRatio();
1961 DrawOutline(w,
this);
1982 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1987 this->
sb->ScrollTowards(vpos);
1998 this->count =
count;
2000 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
2008 count *= (this->
sb->IsVertical() ? this->
children.front()->smallest_y : this->children.front()->smallest_x) + this->
pip_inter;
2012 this->
sb->SetCapacity(this->
sb->IsVertical() ? this->current_y : this->current_x);
2013 this->
sb->SetStepSize(this->
sb->IsVertical() ? this->widget_h : this->widget_w);
2036 assert(this->
children.size() == 1);
2038 this->
children.front()->SetupSmallestSize(w);
2049 this->
fill_x = fill.width;
2050 this->
fill_y = fill.height;
2053 this->ApplyAspectRatio();
2085 int start_x, start_y, base_offs_x, base_offs_y;
2090 int widget_col = (rtl ?
2101 assert(child !=
nullptr);
2125 assert(child !=
nullptr);
2126 int start_x, start_y, base_offs_x, base_offs_y;
2129 int offs_y = base_offs_y;
2130 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2132 if (offs_y + child->
smallest_y <= 0)
continue;
2133 if (offs_y >= (
int)this->
current_y)
break;
2138 int offs_x = base_offs_x;
2141 if (offs_x + child->
smallest_x <= 0)
continue;
2142 if (offs_x >= (
int)this->
current_x)
continue;
2155 DrawOutline(w,
this);
2171 if (this->
sb !=
nullptr) {
2172 if (this->
sb->IsVertical()) {
2173 start_y = this->
sb->GetPosition() / this->
widget_h;
2174 base_offs_y += -this->
sb->GetPosition() + start_y * this->
widget_h;
2176 start_x = this->
sb->GetPosition() / this->widget_w;
2177 int sub_x = this->
sb->GetPosition() - start_x * this->widget_w;
2179 base_offs_x += sub_x;
2181 base_offs_x -= sub_x;
2199 this->child = std::move(child);
2200 if (this->child !=
nullptr) this->child->parent =
this;
2213 if (this->
child ==
nullptr) {
2214 this->
child = std::make_unique<NWidgetVertical>();
2217 this->
child->Add(std::move(nwid));
2232 if (this->
child ==
nullptr) {
2233 this->
child = std::make_unique<NWidgetVertical>();
2235 this->
child->parent =
this;
2236 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2251 if (this->
child ==
nullptr) {
2252 this->
child = std::make_unique<NWidgetVertical>();
2254 this->
child->parent =
this;
2255 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2260 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2266 if (this->
child !=
nullptr) {
2267 this->
child->SetupSmallestSize(w);
2277 if (w ==
nullptr)
return;
2280 std::string text = GetStringForWidget(w,
this);
2298 this->ApplyAspectRatio();
2305 std::string text = GetStringForWidget(w,
this);
2306 if (!text.empty()) {
2309 d =
maxdim(d, background);
2312 if (this->
index >= 0) {
2314 switch (this->
type) {
2315 default: NOT_REACHED();
2325 this->
fill_x = fill.width;
2326 this->
fill_y = fill.height;
2329 this->ApplyAspectRatio();
2337 if (this->
child !=
nullptr) {
2338 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2339 uint width = given_width - this->
child->padding.Horizontal();
2340 uint height = given_height - this->
child->padding.Vertical();
2341 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2348 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2355 Rect r = this->GetCurrentRect();
2358 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2360 switch (this->
type) {
2378 if (this->
child !=
nullptr) this->
child->Draw(w);
2384 DrawOutline(w,
this);
2391 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetFromPos(x, y);
2392 if (nwid ==
nullptr) nwid =
this;
2400 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetOfType(tp);
2401 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2413 this->ApplyAspectRatio();
2429 if (this->
disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2433 DrawOutline(w,
this);
2453 if (w->
viewport ==
nullptr)
return;
2497 int new_pos = list_position;
2567 const int count = sb.
GetCount() * resize_step;
2568 const int position = sb.
GetPosition() * resize_step;
2572 r.bottom = r.top + count;
2576 r.right += position;
2577 r.left = r.right - count;
2580 r.right = r.left + count;
2597 switch (this->type) {
2601 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2607 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2610 default: NOT_REACHED();
2619 switch (this->
type) {
2621 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2625 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2628 default: NOT_REACHED();
2639 Rect r = this->GetCurrentRect();
2642 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2658 DrawOutline(w,
this);
2661 void NWidgetScrollbar::InvalidateDimensionCache()
2667 Dimension NWidgetScrollbar::GetVerticalDimension()
2677 Dimension NWidgetScrollbar::GetHorizontalDimension()
2718NWidgetLeaf::NWidgetLeaf(
WidgetType tp,
Colours colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp,
colour,
index, 1, 1, data, tip)
2727 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2731 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2737 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2752 case NWID_PUSHBUTTON_DROPDOWN:
2759 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2771 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2833 switch (this->
type) {
2902 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2935 padding.height + std::max(di.height, dt.height)
2986 case NWID_PUSHBUTTON_DROPDOWN: {
3007 this->
fill_x = fill.width;
3008 this->
fill_y = fill.height;
3011 this->ApplyAspectRatio();
3023 new_dpi.left += this->
pos_x;
3024 new_dpi.top += this->
pos_y;
3028 Rect r = this->GetCurrentRect();
3031 switch (this->
type) {
3034 if (this->
index == -1 && _draw_widget_outlines) {
3047 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3077 default: NOT_REACHED();
3092 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3097 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3134 case NWID_PUSHBUTTON_DROPDOWN:
3148 DrawOutline(w,
this);
3162 return pt.
x < button_width;
3165 return pt.
x >= button_left;
3189 switch (nwid.
type) {
3192 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3193 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3200 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3201 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3208 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3216 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TOOLBARSIZE requires NWidgetResizeBase");
3217 assert(nwid.u.
xy.
x >= 0);
3224 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3231 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3238 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3245 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3252 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3263 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3274 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3280 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3286 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3308 switch (nwid.
type) {
3309 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3347static std::span<const NWidgetPart>::iterator
MakeNWidget(std::span<const NWidgetPart>::iterator nwid_begin, std::span<const NWidgetPart>::iterator nwid_end, std::unique_ptr<NWidgetBase> &dest,
bool &fill_dest)
3351 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3356 if (dest ==
nullptr)
return nwid_begin;
3387static std::span<const NWidgetPart>::iterator
MakeWidgetTree(std::span<const NWidgetPart>::iterator nwid_begin, std::span<const NWidgetPart>::iterator nwid_end, std::unique_ptr<NWidgetBase> &parent)
3393 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3395 while (nwid_begin != nwid_end) {
3396 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3397 bool fill_sub =
false;
3398 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3401 if (sub_widget ==
nullptr)
break;
3409 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3410 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3411 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3412 parent = std::move(sub_widget);
3417 if (nwid_begin == nwid_end)
return nwid_begin;
3419 assert(nwid_begin < nwid_end);
3421 return std::next(nwid_begin);
3431std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3433 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3434 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3436 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3438 return std::move(container);
3452 auto nwid_begin = std::begin(nwid_parts);
3453 auto nwid_end = std::end(nwid_parts);
3455 *shade_select =
nullptr;
3458 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3460 assert(nwid !=
nullptr);
3464 auto root = std::make_unique<NWidgetVertical>();
3465 root->Add(std::move(nwid));
3466 if (nwid_begin == nwid_end)
return root;
3470 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3471 *shade_select = shade_stack.get();
3473 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3474 root->Add(std::move(shade_stack));
3479 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3494 assert(max_length >= 1);
3495 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3496 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3503 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3505 if (hor_length == max_length) {
3506 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3507 vert->Add(std::move(hor));
3511 if (hor ==
nullptr) {
3512 hor = std::make_unique<NWidgetHorizontal>();
3516 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3517 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3518 panel->SetFill(1, 1);
3519 if (resizable) panel->SetResize(1, 0);
3520 panel->SetToolTip(button_tooltip);
3521 hor->Add(std::move(panel));
3524 if (vert ==
nullptr)
return hor;
3526 if (hor_length > 0 && hor_length < max_length) {
3528 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3530 if (resizable) spc->SetResize(1, 0);
3531 hor->Add(std::move(spc));
3533 if (hor !=
nullptr) vert->Add(std::move(hor));
3544 assert(parent_window !=
nullptr);
3546 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.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
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.
@ ForceRight
Force align to the right.
@ Centre
Align to the centre.
@ Start
Align to the start, LTR/RTL aware.
@ ForceLeft
Force align to the left.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
@ Bottom
Align to the bottom.
@ Middle
Align to the middle.
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, 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.
int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, Alignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
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 GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, Alignment align)
Draw a sprite within a Rect, ignoring the sprite's padding.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
FontSize
Available font sizes.
@ Begin
Marker for the first font in the enumeration.
@ End
Marker for the end of the enumerations.
@ Normal
Index of the normal font in the font tables.
uint32_t PaletteID
The number of the palette.
Colours
One of 16 base colours used for companies and windows/widgets.
@ End
End-of-array marker.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
@ Recolour
Apply a recolour sprite to the screen content.
@ Checker
Draw only every second pixel, used for greying-out.
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, Shade shade)
Get colour gradient palette index.
@ Darker
Darker colour shade.
@ Lightest
Lightest colour shade.
@ Lighter
Lighter colour shade.
@ Normal
Normal colour shade.
@ Light
Light colour shade.
static constexpr PixelColour PC_BLACK
Black palette colour.
static constexpr PixelColour PC_WHITE
White palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
@ Invalid
broken savegame (used internally)
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 const SpriteID SPR_ARROW_RIGHT
static const SpriteID SPR_WINDOW_SHADE
Shade the window icon.
static const SpriteID SPR_WINDOW_DEBUG
NewGRF debug window icon.
static const SpriteID SPR_PIN_UP
Pin icon.
static const SpriteID SPR_WINDOW_UNSHADE
Unshade the window icon.
static const SpriteID SPR_COMPANY_ICON
Icon showing company colour.
static const SpriteID SPR_ARROW_LEFT
static const SpriteID SPR_WINDOW_RESIZE_RIGHT
Resize icon to the right.
static const SpriteID SPR_WINDOW_RESIZE_LEFT
Resize icon to the left.
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
static const SpriteID SPR_ARROW_DOWN
static const SpriteID SPR_ARROW_UP
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.
StrongType::Typedef< uint32_t, struct StringIDTag, StrongType::Compare, StrongType::Integer > 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.
Horizontal and vertical alignment.
AlignmentH ResolveRTL() const
Resolve horizontal alignment for the current text direction.
AlignmentV v
Vertical alignment.
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 coordinates.
Rect WithX(int new_left, int new_right) const
Create a new Rect, replacing the left and right coordinates.
Rect Expand(int s) const
Copy and expand Rect by s pixels.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
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 UnfocusFocusedWidget()
Makes no widget on this window have focus.
void DrawSortButton(WidgetID widget, bool descending) const
Draw a sort button's up or down arrow symbol.
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.
EnumBitSet< TransparencyOption, uint32_t > TransparencyOptions
Bitset of TransparencyOption elements.
TransparencyOptions _transparency_opt
The bits that should be transparent.
@ 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.
EnumBitSet< FrameFlag, uint8_t > FrameFlags
Bitset of FrameFlag elements.
@ 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.
EventState
State of handling an event.
@ Handled
The passed event is handled.
@ NotHandled
The passed event is not handled.
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
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.