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 case SA_RIGHT: p.
x = r.right + 1 - d.width;
break;
149 default: NOT_REACHED();
152 case SA_TOP: p.
y = r.top;
break;
154 case SA_BOTTOM: p.
y = r.bottom + 1 - d.height;
break;
155 default: NOT_REACHED();
172 int rev_base = mi + ma;
173 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
182 int height = ma + 1 - mi;
183 int slider_height = std::max(button_size, cap * height / count);
184 height -= slider_height;
187 ma = mi + slider_height - 1;
210 bool changed =
false;
215 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
218 button_size = NWidgetScrollbar::GetVerticalDimension().height;
220 if (pos < mi + button_size) {
223 if (_scroller_click_timeout <= 1) {
224 _scroller_click_timeout = 3;
228 }
else if (pos >= ma - button_size) {
232 if (_scroller_click_timeout <= 1) {
233 _scroller_click_timeout = 3;
242 }
else if (pos > end) {
245 _scrollbar_start_pos = start - mi - button_size;
246 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
248 _cursorpos_drag_start = _cursor.pos;
282 assert(scrollbar !=
nullptr);
322 Rect outer = {left, top, right, bottom};
326 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
327 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
328 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
329 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
332 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
333 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
334 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
335 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
336 interior = medium_dark;
339 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
349 d.height -= offset.
y;
369 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
370 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
400 DrawString(r_text.left, r_text.right, p.
y, text, text_colour, align,
false, fs);
414 if (str.empty())
return;
418 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
431 if (str.empty())
return;
435 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
463static inline void DrawMatrix(
const Rect &r,
Colours colour,
bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
468 if (num_columns == 0) {
469 column_width = resize_x;
470 num_columns = r.
Width() / column_width;
472 column_width = r.
Width() / num_columns;
477 row_height = resize_y;
478 num_rows = r.
Height() / row_height;
480 row_height = r.
Height() / num_rows;
486 for (
int ctr = num_columns; ctr > 1; ctr--) {
492 for (
int ctr = num_rows; ctr > 1; ctr--) {
500 for (
int ctr = num_columns; ctr > 1; ctr--) {
506 for (
int ctr = num_rows; ctr > 1; ctr--) {
523 int height = NWidgetScrollbar::GetVerticalDimension().height;
538 int left = r.left + r.
Width() * 3 / 11;
539 int right = r.left + r.
Width() * 8 / 11;
564 int width = NWidgetScrollbar::GetHorizontalDimension().width;
578 int top = r.top + r.
Height() * 3 / 11;
579 int bottom = r.top + r.
Height() * 8 / 11;
638 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
639 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
642 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
643 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
646 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
647 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
706 }
else if (clicked) {
723 d.height -= offset.y;
740 bool company_owned = owner < MAX_COMPANIES;
750 if (str.empty())
return;
800 Rect outer = widget->GetCurrentRect();
805 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
806 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
807 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
808 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
824 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
835 return NWidgetScrollbar::GetVerticalDimension().width + 1;
838bool _draw_widget_outlines;
935 if (this->
index >= 0) widget_lookup[this->
index] =
this;
971 return (this->
type == tp) ? this :
nullptr;
974void NWidgetBase::ApplyAspectRatio()
1025 assert(x_ratio > 0 && y_ratio > 0);
1026 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1070 this->min_x = std::max(this->min_x,
min_x);
1071 this->min_y = std::max(this->min_y,
min_y);
1122 d.height *= max_lines;
1138 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1139 this->min_x =
min_x;
1140 this->min_y =
min_y;
1152 if (
min_y == this->min_y)
return false;
1153 this->min_y =
min_y;
1274 this->align =
align;
1302 if (this->
type == tp)
return this;
1303 for (
const auto &child_wid : this->
children) {
1305 if (nwid !=
nullptr)
return nwid;
1312 for (
const auto &child_wid : this->
children) {
1313 child_wid->AdjustPaddingForZoom();
1324 assert(wid !=
nullptr);
1326 this->
children.push_back(std::move(wid));
1332 for (
const auto &child_wid : this->
children) {
1333 child_wid->FillWidgetLookup(widget_lookup);
1339 for (
const auto &child_wid : this->
children) {
1343 DrawOutline(w,
this);
1350 for (
const auto &child_wid : this->
children) {
1352 if (nwid !=
nullptr)
return nwid;
1370 this->
fill_x = fill.width;
1371 this->
fill_y = fill.height;
1374 this->ApplyAspectRatio();
1385 for (
const auto &child_wid : this->
children) {
1386 child_wid->SetupSmallestSize(w);
1388 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1389 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1390 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1391 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1394 this->ApplyAspectRatio();
1405 for (
const auto &child_wid : this->
children) {
1406 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1407 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1408 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1410 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1411 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1412 uint child_pos_y = child_wid->padding.top;
1414 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1434 DrawOutline(w,
this);
1461class NWidgetLayer :
public NWidgetContainer {
1480 for (
const auto &child_wid : this->
children) {
1481 child_wid->SetupSmallestSize(w);
1483 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1484 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1485 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1486 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1489 this->ApplyAspectRatio();
1498 for (
const auto &child_wid : this->
children) {
1499 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1500 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1501 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1503 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1504 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1505 uint child_pos_y = child_wid->padding.top;
1507 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1514 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1518 DrawOutline(w,
this);
1577 uint max_vert_fill = 0;
1578 for (
const auto &child_wid : this->
children) {
1579 child_wid->SetupSmallestSize(w);
1580 longest = std::max(longest, child_wid->smallest_x);
1582 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1583 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1587 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1590 for (
const auto &child_wid : this->children) {
1592 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1593 if (step_size > 1 && child_height < cur_height) {
1594 uint remainder = (cur_height - child_height) % step_size;
1595 if (remainder > 0) {
1596 cur_height += step_size - remainder;
1597 assert(cur_height < max_smallest);
1606 for (
const auto &child_wid : this->children) {
1607 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1608 child_wid->ApplyAspectRatio();
1609 longest = std::max(longest, child_wid->smallest_x);
1612 for (
const auto &child_wid : this->children) {
1613 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1617 for (
const auto &child_wid : this->children) {
1618 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1619 if (child_wid->fill_x > 0) {
1620 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1622 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1624 if (child_wid->resize_x > 0) {
1640 for (
const auto &child_wid : this->
children) {
1641 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1660 int num_changing_childs = 0;
1661 uint biggest_stepsize = 0;
1662 for (
const auto &child_wid : this->children) {
1663 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1666 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1668 child_wid->current_x = child_wid->smallest_x;
1671 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1672 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1677 for (
const auto &child_wid : this->children) {
1678 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1679 if (hor_step == biggest_stepsize) {
1680 num_changing_childs++;
1686 while (biggest_stepsize > 0) {
1687 uint next_biggest_stepsize = 0;
1688 for (
const auto &child_wid : this->children) {
1689 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1690 if (hor_step > biggest_stepsize)
continue;
1691 if (hor_step == biggest_stepsize) {
1692 uint increment = additional_length / num_changing_childs;
1693 num_changing_childs--;
1694 if (hor_step > 1) increment -= increment % hor_step;
1695 child_wid->current_x = child_wid->smallest_x + increment;
1696 additional_length -= increment;
1699 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1701 biggest_stepsize = next_biggest_stepsize;
1705 for (
const auto &child_wid : this->children) {
1706 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1707 if (hor_step == biggest_stepsize) {
1708 num_changing_childs++;
1713 assert(num_changing_childs == 0);
1718 if (additional_length > 0) {
1729 uint position = rtl ? this->
current_x - pre : pre;
1730 for (
const auto &child_wid : this->children) {
1731 uint child_width = child_wid->current_x;
1732 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1733 uint child_y = y + child_wid->padding.top;
1735 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1736 if (child_wid->current_x != 0) {
1737 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1738 position = rtl ? position - padded_child_width : position + padded_child_width;
1760 uint max_hor_fill = 0;
1761 for (
const auto &child_wid : this->
children) {
1762 child_wid->SetupSmallestSize(w);
1763 highest = std::max(highest, child_wid->smallest_y);
1765 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1766 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1770 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1773 for (
const auto &child_wid : this->children) {
1775 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1776 if (step_size > 1 && child_width < cur_width) {
1777 uint remainder = (cur_width - child_width) % step_size;
1778 if (remainder > 0) {
1779 cur_width += step_size - remainder;
1780 assert(cur_width < max_smallest);
1789 for (
const auto &child_wid : this->children) {
1790 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1791 child_wid->ApplyAspectRatio();
1792 highest = std::max(highest, child_wid->smallest_y);
1795 for (
const auto &child_wid : this->children) {
1796 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1800 for (
const auto &child_wid : this->children) {
1801 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1802 if (child_wid->fill_y > 0) {
1803 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1805 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1807 if (child_wid->resize_y > 0) {
1823 for (
const auto &child_wid : this->
children) {
1824 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1834 int num_changing_childs = 0;
1835 uint biggest_stepsize = 0;
1836 for (
const auto &child_wid : this->children) {
1837 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1838 if (vert_step > 0) {
1840 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1842 child_wid->current_y = child_wid->smallest_y;
1845 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1846 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1851 for (
const auto &child_wid : this->children) {
1852 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1853 if (vert_step == biggest_stepsize) {
1854 num_changing_childs++;
1860 while (biggest_stepsize > 0) {
1861 uint next_biggest_stepsize = 0;
1862 for (
const auto &child_wid : this->children) {
1863 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1864 if (vert_step > biggest_stepsize)
continue;
1865 if (vert_step == biggest_stepsize) {
1866 uint increment = additional_length / num_changing_childs;
1867 num_changing_childs--;
1868 if (vert_step > 1) increment -= increment % vert_step;
1869 child_wid->current_y = child_wid->smallest_y + increment;
1870 additional_length -= increment;
1873 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1875 biggest_stepsize = next_biggest_stepsize;
1879 for (
const auto &child_wid : this->children) {
1880 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1881 if (vert_step == biggest_stepsize) {
1882 num_changing_childs++;
1887 assert(num_changing_childs == 0);
1892 if (additional_length > 0) {
1904 for (
const auto &child_wid : this->children) {
1905 uint child_height = child_wid->current_y;
1906 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1907 uint child_y = y + (this->
bottom_up ? position - child_height - child_wid->padding.top : position + child_wid->padding.top);
1909 child_wid->AssignSizePosition(sizing, child_x, child_y, child_wid->current_x, child_height, rtl);
1910 if (child_wid->current_y != 0) {
1911 uint padded_child_height = child_height + child_wid->padding.Vertical() + inter;
1912 position = this->
bottom_up ? position - padded_child_height : position + padded_child_height;
1932 this->ApplyAspectRatio();
1943 DrawOutline(w,
this);
1964 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1969 this->
sb->ScrollTowards(vpos);
1980 this->count =
count;
1982 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1990 count *= (this->
sb->IsVertical() ? this->
children.front()->smallest_y : this->children.front()->smallest_x) + this->
pip_inter;
1994 this->
sb->SetCapacity(this->
sb->IsVertical() ? this->current_y : this->current_x);
1995 this->
sb->SetStepSize(this->
sb->IsVertical() ? this->widget_h : this->widget_w);
2018 assert(this->
children.size() == 1);
2020 this->
children.front()->SetupSmallestSize(w);
2031 this->
fill_x = fill.width;
2032 this->
fill_y = fill.height;
2035 this->ApplyAspectRatio();
2067 int start_x, start_y, base_offs_x, base_offs_y;
2072 int widget_col = (rtl ?
2083 assert(child !=
nullptr);
2107 assert(child !=
nullptr);
2108 int start_x, start_y, base_offs_x, base_offs_y;
2111 int offs_y = base_offs_y;
2112 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2114 if (offs_y + child->
smallest_y <= 0)
continue;
2115 if (offs_y >= (
int)this->
current_y)
break;
2120 int offs_x = base_offs_x;
2123 if (offs_x + child->
smallest_x <= 0)
continue;
2124 if (offs_x >= (
int)this->
current_x)
continue;
2137 DrawOutline(w,
this);
2153 if (this->
sb !=
nullptr) {
2154 if (this->
sb->IsVertical()) {
2155 start_y = this->
sb->GetPosition() / this->
widget_h;
2156 base_offs_y += -this->
sb->GetPosition() + start_y * this->
widget_h;
2158 start_x = this->
sb->GetPosition() / this->widget_w;
2159 int sub_x = this->
sb->GetPosition() - start_x * this->widget_w;
2161 base_offs_x += sub_x;
2163 base_offs_x -= sub_x;
2181 this->child = std::move(child);
2182 if (this->child !=
nullptr) this->child->parent =
this;
2195 if (this->
child ==
nullptr) {
2196 this->
child = std::make_unique<NWidgetVertical>();
2199 this->
child->Add(std::move(nwid));
2214 if (this->
child ==
nullptr) {
2215 this->
child = std::make_unique<NWidgetVertical>();
2217 this->
child->parent =
this;
2218 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2233 if (this->
child ==
nullptr) {
2234 this->
child = std::make_unique<NWidgetVertical>();
2236 this->
child->parent =
this;
2237 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2242 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2248 if (this->
child !=
nullptr) {
2249 this->
child->SetupSmallestSize(w);
2259 if (w ==
nullptr)
return;
2262 std::string text = GetStringForWidget(w,
this);
2280 this->ApplyAspectRatio();
2287 std::string text = GetStringForWidget(w,
this);
2288 if (!text.empty()) {
2291 d =
maxdim(d, background);
2294 if (this->
index >= 0) {
2296 switch (this->
type) {
2297 default: NOT_REACHED();
2307 this->
fill_x = fill.width;
2308 this->
fill_y = fill.height;
2311 this->ApplyAspectRatio();
2319 if (this->
child !=
nullptr) {
2320 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2321 uint width = given_width - this->
child->padding.Horizontal();
2322 uint height = given_height - this->
child->padding.Vertical();
2323 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2330 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2337 Rect r = this->GetCurrentRect();
2340 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2342 switch (this->
type) {
2360 if (this->
child !=
nullptr) this->
child->Draw(w);
2366 DrawOutline(w,
this);
2373 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetFromPos(x, y);
2374 if (nwid ==
nullptr) nwid =
this;
2382 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetOfType(tp);
2383 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2395 this->ApplyAspectRatio();
2411 if (this->
disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2415 DrawOutline(w,
this);
2435 if (w->
viewport ==
nullptr)
return;
2479 int new_pos = list_position;
2549 const int count = sb.
GetCount() * resize_step;
2550 const int position = sb.
GetPosition() * resize_step;
2554 r.bottom = r.top + count;
2558 r.right += position;
2559 r.left = r.right - count;
2562 r.right = r.left + count;
2579 switch (this->type) {
2583 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2589 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2592 default: NOT_REACHED();
2601 switch (this->
type) {
2603 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2607 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2610 default: NOT_REACHED();
2621 Rect r = this->GetCurrentRect();
2624 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2640 DrawOutline(w,
this);
2643 void NWidgetScrollbar::InvalidateDimensionCache()
2649 Dimension NWidgetScrollbar::GetVerticalDimension()
2659 Dimension NWidgetScrollbar::GetHorizontalDimension()
2700NWidgetLeaf::NWidgetLeaf(
WidgetType tp,
Colours colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp,
colour,
index, 1, 1, data, tip)
2709 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2713 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2719 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2734 case NWID_PUSHBUTTON_DROPDOWN:
2741 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2753 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2815 switch (this->
type) {
2884 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2917 padding.height + std::max(di.height, dt.height)
2968 case NWID_PUSHBUTTON_DROPDOWN: {
2989 this->
fill_x = fill.width;
2990 this->
fill_y = fill.height;
2993 this->ApplyAspectRatio();
3005 new_dpi.left += this->
pos_x;
3006 new_dpi.top += this->
pos_y;
3010 Rect r = this->GetCurrentRect();
3013 switch (this->
type) {
3016 if (this->
index == -1 && _draw_widget_outlines) {
3029 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3059 default: NOT_REACHED();
3074 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3079 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3116 case NWID_PUSHBUTTON_DROPDOWN:
3130 DrawOutline(w,
this);
3144 return pt.
x < button_width;
3147 return pt.
x >= button_left;
3171 switch (nwid.
type) {
3174 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3175 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3182 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3183 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3190 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3198 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TOOLBARSIZE requires NWidgetResizeBase");
3199 assert(nwid.u.
xy.
x >= 0);
3206 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3213 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3220 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3227 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3234 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3245 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3256 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3262 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3268 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3290 switch (nwid.
type) {
3291 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3329static 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)
3333 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3338 if (dest ==
nullptr)
return nwid_begin;
3369static 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)
3375 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3377 while (nwid_begin != nwid_end) {
3378 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3379 bool fill_sub =
false;
3380 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3383 if (sub_widget ==
nullptr)
break;
3391 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3392 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3393 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3394 parent = std::move(sub_widget);
3399 if (nwid_begin == nwid_end)
return nwid_begin;
3401 assert(nwid_begin < nwid_end);
3403 return std::next(nwid_begin);
3413std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3415 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3416 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3418 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3420 return std::move(container);
3434 auto nwid_begin = std::begin(nwid_parts);
3435 auto nwid_end = std::end(nwid_parts);
3437 *shade_select =
nullptr;
3440 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3442 assert(nwid !=
nullptr);
3446 auto root = std::make_unique<NWidgetVertical>();
3447 root->Add(std::move(nwid));
3448 if (nwid_begin == nwid_end)
return root;
3452 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3453 *shade_select = shade_stack.get();
3455 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3456 root->Add(std::move(shade_stack));
3461 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3476 assert(max_length >= 1);
3477 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3478 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3485 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3487 if (hor_length == max_length) {
3488 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3489 vert->Add(std::move(hor));
3493 if (hor ==
nullptr) {
3494 hor = std::make_unique<NWidgetHorizontal>();
3498 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3499 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3500 panel->SetFill(1, 1);
3501 if (resizable) panel->SetResize(1, 0);
3502 panel->SetToolTip(button_tooltip);
3503 hor->Add(std::move(panel));
3506 if (vert ==
nullptr)
return hor;
3508 if (hor_length > 0 && hor_length < max_length) {
3510 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3512 if (resizable) spc->SetResize(1, 0);
3513 hor->Add(std::move(spc));
3515 if (hor !=
nullptr) vert->Add(std::move(hor));
3526 assert(parent_window !=
nullptr);
3528 for (
auto &widget : this->
children) {
Class for backupping variables and making sure they are restored later.
@ Invalid
Invalid town production effect.
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.
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, ExtendedTextColour 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 GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
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.
@ 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.
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.
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.
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 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.