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);
309void DrawFrameRect(
int left,
int top,
int right,
int bottom, Colours colour, FrameFlags flags)
314 assert(colour < COLOUR_END);
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);
702static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
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);
826 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
837 return NWidgetScrollbar::GetVerticalDimension().width + 1;
840bool _draw_widget_outlines;
937 if (this->
index >= 0) widget_lookup[this->
index] =
this;
973 return (this->
type == tp) ? this :
nullptr;
976void NWidgetBase::ApplyAspectRatio()
1027 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1071 this->min_x = std::max(this->min_x,
min_x);
1072 this->min_y = std::max(this->min_y,
min_y);
1123 d.height *= max_lines;
1139 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1140 this->min_x =
min_x;
1141 this->min_y =
min_y;
1153 if (
min_y == this->min_y)
return false;
1154 this->min_y =
min_y;
1275 this->align =
align;
1303 if (this->
type == tp)
return this;
1304 for (
const auto &child_wid : this->
children) {
1306 if (nwid !=
nullptr)
return nwid;
1313 for (
const auto &child_wid : this->
children) {
1314 child_wid->AdjustPaddingForZoom();
1325 assert(wid !=
nullptr);
1327 this->
children.push_back(std::move(wid));
1333 for (
const auto &child_wid : this->
children) {
1334 child_wid->FillWidgetLookup(widget_lookup);
1340 for (
const auto &child_wid : this->
children) {
1344 DrawOutline(w,
this);
1351 for (
const auto &child_wid : this->
children) {
1353 if (nwid !=
nullptr)
return nwid;
1371 this->
fill_x = fill.width;
1372 this->
fill_y = fill.height;
1375 this->ApplyAspectRatio();
1386 for (
const auto &child_wid : this->
children) {
1387 child_wid->SetupSmallestSize(w);
1389 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1390 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1391 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1392 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1395 this->ApplyAspectRatio();
1406 for (
const auto &child_wid : this->
children) {
1407 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1408 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1409 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1411 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1412 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1413 uint child_pos_y = child_wid->padding.top;
1415 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1435 DrawOutline(w,
this);
1462class NWidgetLayer :
public NWidgetContainer {
1481 for (
const auto &child_wid : this->
children) {
1482 child_wid->SetupSmallestSize(w);
1484 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1485 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1486 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1487 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1490 this->ApplyAspectRatio();
1499 for (
const auto &child_wid : this->
children) {
1500 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1501 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1502 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1504 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1505 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1506 uint child_pos_y = child_wid->padding.top;
1508 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1515 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1519 DrawOutline(w,
this);
1578 uint max_vert_fill = 0;
1579 for (
const auto &child_wid : this->
children) {
1580 child_wid->SetupSmallestSize(w);
1581 longest = std::max(longest, child_wid->smallest_x);
1583 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1584 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1588 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1591 for (
const auto &child_wid : this->children) {
1593 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1594 if (step_size > 1 && child_height < cur_height) {
1595 uint remainder = (cur_height - child_height) % step_size;
1596 if (remainder > 0) {
1597 cur_height += step_size - remainder;
1598 assert(cur_height < max_smallest);
1607 for (
const auto &child_wid : this->children) {
1608 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1609 child_wid->ApplyAspectRatio();
1610 longest = std::max(longest, child_wid->smallest_x);
1613 for (
const auto &child_wid : this->children) {
1614 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1618 for (
const auto &child_wid : this->children) {
1619 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1620 if (child_wid->fill_x > 0) {
1621 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1623 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1625 if (child_wid->resize_x > 0) {
1641 for (
const auto &child_wid : this->
children) {
1642 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1661 int num_changing_childs = 0;
1662 uint biggest_stepsize = 0;
1663 for (
const auto &child_wid : this->children) {
1664 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1667 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1669 child_wid->current_x = child_wid->smallest_x;
1672 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1673 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
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++;
1687 while (biggest_stepsize > 0) {
1688 uint next_biggest_stepsize = 0;
1689 for (
const auto &child_wid : this->children) {
1690 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1691 if (hor_step > biggest_stepsize)
continue;
1692 if (hor_step == biggest_stepsize) {
1693 uint increment = additional_length / num_changing_childs;
1694 num_changing_childs--;
1695 if (hor_step > 1) increment -= increment % hor_step;
1696 child_wid->current_x = child_wid->smallest_x + increment;
1697 additional_length -= increment;
1700 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1702 biggest_stepsize = next_biggest_stepsize;
1706 for (
const auto &child_wid : this->children) {
1707 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1708 if (hor_step == biggest_stepsize) {
1709 num_changing_childs++;
1714 assert(num_changing_childs == 0);
1719 if (additional_length > 0) {
1730 uint position = rtl ? this->
current_x - pre : pre;
1731 for (
const auto &child_wid : this->children) {
1732 uint child_width = child_wid->current_x;
1733 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1734 uint child_y = y + child_wid->padding.top;
1736 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1737 if (child_wid->current_x != 0) {
1738 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1739 position = rtl ? position - padded_child_width : position + padded_child_width;
1761 uint max_hor_fill = 0;
1762 for (
const auto &child_wid : this->
children) {
1763 child_wid->SetupSmallestSize(w);
1764 highest = std::max(highest, child_wid->smallest_y);
1766 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1767 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1771 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1774 for (
const auto &child_wid : this->children) {
1776 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1777 if (step_size > 1 && child_width < cur_width) {
1778 uint remainder = (cur_width - child_width) % step_size;
1779 if (remainder > 0) {
1780 cur_width += step_size - remainder;
1781 assert(cur_width < max_smallest);
1790 for (
const auto &child_wid : this->children) {
1791 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1792 child_wid->ApplyAspectRatio();
1793 highest = std::max(highest, child_wid->smallest_y);
1796 for (
const auto &child_wid : this->children) {
1797 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1801 for (
const auto &child_wid : this->children) {
1802 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1803 if (child_wid->fill_y > 0) {
1804 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1806 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1808 if (child_wid->resize_y > 0) {
1824 for (
const auto &child_wid : this->
children) {
1825 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1835 int num_changing_childs = 0;
1836 uint biggest_stepsize = 0;
1837 for (
const auto &child_wid : this->children) {
1838 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1839 if (vert_step > 0) {
1841 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1843 child_wid->current_y = child_wid->smallest_y;
1846 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1847 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
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++;
1861 while (biggest_stepsize > 0) {
1862 uint next_biggest_stepsize = 0;
1863 for (
const auto &child_wid : this->children) {
1864 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1865 if (vert_step > biggest_stepsize)
continue;
1866 if (vert_step == biggest_stepsize) {
1867 uint increment = additional_length / num_changing_childs;
1868 num_changing_childs--;
1869 if (vert_step > 1) increment -= increment % vert_step;
1870 child_wid->current_y = child_wid->smallest_y + increment;
1871 additional_length -= increment;
1874 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1876 biggest_stepsize = next_biggest_stepsize;
1880 for (
const auto &child_wid : this->children) {
1881 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1882 if (vert_step == biggest_stepsize) {
1883 num_changing_childs++;
1888 assert(num_changing_childs == 0);
1893 if (additional_length > 0) {
1905 for (
const auto &child_wid : this->children) {
1906 uint child_height = child_wid->current_y;
1907 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1908 uint child_y = y + (this->
bottom_up ? position - child_height - child_wid->padding.top : position + child_wid->padding.top);
1910 child_wid->AssignSizePosition(sizing, child_x, child_y, child_wid->current_x, child_height, rtl);
1911 if (child_wid->current_y != 0) {
1912 uint padded_child_height = child_height + child_wid->padding.Vertical() + inter;
1913 position = this->
bottom_up ? position - padded_child_height : position + padded_child_height;
1933 this->ApplyAspectRatio();
1944 DrawOutline(w,
this);
1965 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1970 this->
sb->ScrollTowards(vpos);
1981 this->count =
count;
1983 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1991 count *= (this->
sb->IsVertical() ? this->
children.front()->smallest_y : this->children.front()->smallest_x) + this->
pip_inter;
1995 this->
sb->SetCapacity(this->
sb->IsVertical() ? this->current_y : this->current_x);
1996 this->
sb->SetStepSize(this->
sb->IsVertical() ? this->widget_h : this->widget_w);
2019 assert(this->
children.size() == 1);
2021 this->
children.front()->SetupSmallestSize(w);
2032 this->
fill_x = fill.width;
2033 this->
fill_y = fill.height;
2036 this->ApplyAspectRatio();
2068 int start_x, start_y, base_offs_x, base_offs_y;
2073 int widget_col = (rtl ?
2084 assert(child !=
nullptr);
2108 assert(child !=
nullptr);
2109 int start_x, start_y, base_offs_x, base_offs_y;
2112 int offs_y = base_offs_y;
2113 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2115 if (offs_y + child->
smallest_y <= 0)
continue;
2116 if (offs_y >= (
int)this->
current_y)
break;
2121 int offs_x = base_offs_x;
2124 if (offs_x + child->
smallest_x <= 0)
continue;
2125 if (offs_x >= (
int)this->
current_x)
continue;
2138 DrawOutline(w,
this);
2154 if (this->
sb !=
nullptr) {
2155 if (this->
sb->IsVertical()) {
2156 start_y = this->
sb->GetPosition() / this->
widget_h;
2157 base_offs_y += -this->
sb->GetPosition() + start_y * this->
widget_h;
2159 start_x = this->
sb->GetPosition() / this->widget_w;
2160 int sub_x = this->
sb->GetPosition() - start_x * this->widget_w;
2162 base_offs_x += sub_x;
2164 base_offs_x -= sub_x;
2182 this->child = std::move(child);
2183 if (this->child !=
nullptr) this->child->parent =
this;
2196 if (this->
child ==
nullptr) {
2197 this->
child = std::make_unique<NWidgetVertical>();
2200 this->
child->Add(std::move(nwid));
2215 if (this->
child ==
nullptr) {
2216 this->
child = std::make_unique<NWidgetVertical>();
2218 this->
child->parent =
this;
2219 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2234 if (this->
child ==
nullptr) {
2235 this->
child = std::make_unique<NWidgetVertical>();
2237 this->
child->parent =
this;
2238 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2243 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2249 if (this->
child !=
nullptr) {
2250 this->
child->SetupSmallestSize(w);
2260 if (w ==
nullptr)
return;
2263 std::string text = GetStringForWidget(w,
this);
2281 this->ApplyAspectRatio();
2288 std::string text = GetStringForWidget(w,
this);
2289 if (!text.empty()) {
2292 d =
maxdim(d, background);
2295 if (this->
index >= 0) {
2297 switch (this->
type) {
2298 default: NOT_REACHED();
2308 this->
fill_x = fill.width;
2309 this->
fill_y = fill.height;
2312 this->ApplyAspectRatio();
2320 if (this->
child !=
nullptr) {
2321 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2322 uint width = given_width - this->
child->padding.Horizontal();
2323 uint height = given_height - this->
child->padding.Vertical();
2324 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2331 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2338 Rect r = this->GetCurrentRect();
2341 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2343 switch (this->
type) {
2361 if (this->
child !=
nullptr) this->
child->Draw(w);
2367 DrawOutline(w,
this);
2374 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetFromPos(x, y);
2375 if (nwid ==
nullptr) nwid =
this;
2383 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetOfType(tp);
2384 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2396 this->ApplyAspectRatio();
2413 if (this->
disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2417 DrawOutline(w,
this);
2437 if (w->
viewport ==
nullptr)
return;
2481 int new_pos = list_position;
2551 const int count = sb.
GetCount() * resize_step;
2552 const int position = sb.
GetPosition() * resize_step;
2556 r.bottom = r.top + count;
2560 r.right += position;
2561 r.left = r.right - count;
2564 r.right = r.left + count;
2581 switch (this->type) {
2585 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2591 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2594 default: NOT_REACHED();
2603 switch (this->
type) {
2605 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2609 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2612 default: NOT_REACHED();
2623 Rect r = this->GetCurrentRect();
2626 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2642 DrawOutline(w,
this);
2645 void NWidgetScrollbar::InvalidateDimensionCache()
2651 Dimension NWidgetScrollbar::GetVerticalDimension()
2661 Dimension NWidgetScrollbar::GetHorizontalDimension()
2702NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours
colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp,
colour,
index, 1, 1, data, tip)
2711 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2715 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2721 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2736 case NWID_PUSHBUTTON_DROPDOWN:
2743 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2755 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2817 switch (this->
type) {
2886 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2919 padding.height + std::max(di.height, dt.height)
2970 case NWID_PUSHBUTTON_DROPDOWN: {
2991 this->
fill_x = fill.width;
2992 this->
fill_y = fill.height;
2995 this->ApplyAspectRatio();
3007 new_dpi.left += this->
pos_x;
3008 new_dpi.top += this->
pos_y;
3012 Rect r = this->GetCurrentRect();
3015 switch (this->
type) {
3018 if (this->
index == -1 && _draw_widget_outlines) {
3029 Colours button_colour = this->
widget_data.alternate_colour;
3030 if (button_colour == INVALID_COLOUR) button_colour = this->
colour;
3031 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3061 default: NOT_REACHED();
3076 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3081 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3118 case NWID_PUSHBUTTON_DROPDOWN:
3132 DrawOutline(w,
this);
3146 return pt.
x < button_width;
3149 return pt.
x >= button_left;
3173 switch (nwid.
type) {
3176 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3177 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3184 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3185 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3192 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3200 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TOOLBARSIZE requires NWidgetResizeBase");
3201 assert(nwid.u.
xy.
x >= 0);
3208 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3215 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3222 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3229 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3236 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3247 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3258 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3264 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3270 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3292 switch (nwid.
type) {
3293 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3331static 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)
3335 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3340 if (dest ==
nullptr)
return nwid_begin;
3371static 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)
3377 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3379 while (nwid_begin != nwid_end) {
3380 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3381 bool fill_sub =
false;
3382 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3385 if (sub_widget ==
nullptr)
break;
3393 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3394 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3395 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3396 parent = std::move(sub_widget);
3401 if (nwid_begin == nwid_end)
return nwid_begin;
3403 assert(nwid_begin < nwid_end);
3405 return std::next(nwid_begin);
3415std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3417 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3418 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3420 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3422 return std::move(container);
3436 auto nwid_begin = std::begin(nwid_parts);
3437 auto nwid_end = std::end(nwid_parts);
3439 *shade_select =
nullptr;
3442 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3444 assert(nwid !=
nullptr);
3448 auto root = std::make_unique<NWidgetVertical>();
3449 root->Add(std::move(nwid));
3450 if (nwid_begin == nwid_end)
return root;
3454 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3455 *shade_select = shade_stack.get();
3457 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3458 root->Add(std::move(shade_stack));
3463 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3478 assert(max_length >= 1);
3479 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3480 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3487 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3489 if (hor_length == max_length) {
3490 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3491 vert->Add(std::move(hor));
3495 if (hor ==
nullptr) {
3496 hor = std::make_unique<NWidgetHorizontal>();
3500 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3501 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3502 panel->SetFill(1, 1);
3503 if (resizable) panel->SetResize(1, 0);
3504 panel->SetToolTip(button_tooltip);
3505 hor->Add(std::move(panel));
3508 if (vert ==
nullptr)
return hor;
3510 if (hor_length > 0 && hor_length < max_length) {
3512 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3514 if (resizable) spc->SetResize(1, 0);
3515 hor->Add(std::move(spc));
3517 if (hor !=
nullptr) vert->Add(std::move(hor));
3528 assert(parent_window !=
nullptr);
3530 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 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.
@ 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.