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));
128 case SA_RIGHT: p.
x = r.right + 1 - d.width;
break;
129 default: NOT_REACHED();
132 case SA_TOP: p.
y = r.top;
break;
134 case SA_BOTTOM: p.
y = r.bottom + 1 - d.height;
break;
135 default: NOT_REACHED();
152 int rev_base = mi + ma;
153 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
162 int height = ma + 1 - mi;
163 int slider_height = std::max(button_size, cap * height / count);
164 height -= slider_height;
167 ma = mi + slider_height - 1;
190 bool changed =
false;
195 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
198 button_size = NWidgetScrollbar::GetVerticalDimension().height;
200 if (pos < mi + button_size) {
203 if (_scroller_click_timeout <= 1) {
204 _scroller_click_timeout = 3;
208 }
else if (pos >= ma - button_size) {
212 if (_scroller_click_timeout <= 1) {
213 _scroller_click_timeout = 3;
222 }
else if (pos > end) {
225 _scrollbar_start_pos = start - mi - button_size;
226 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
228 _cursorpos_drag_start = _cursor.
pos;
262 assert(scrollbar !=
nullptr);
294 assert(colour < COLOUR_END);
302 Rect outer = {left, top, right, bottom};
306 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
307 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
308 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
309 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
312 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
313 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
314 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
315 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
316 interior = medium_dark;
319 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
329 d.height -= offset.
y;
349 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
350 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
380 DrawString(r_text.left, r_text.right, p.
y, text, text_colour, align,
false, fs);
394 if (str.empty())
return;
398 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
411 if (str.empty())
return;
415 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
443static inline void DrawMatrix(
const Rect &r, Colours colour,
bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
448 if (num_columns == 0) {
449 column_width = resize_x;
450 num_columns = r.
Width() / column_width;
452 column_width = r.
Width() / num_columns;
457 row_height = resize_y;
458 num_rows = r.
Height() / row_height;
460 row_height = r.
Height() / num_rows;
466 for (
int ctr = num_columns; ctr > 1; ctr--) {
472 for (
int ctr = num_rows; ctr > 1; ctr--) {
480 for (
int ctr = num_columns; ctr > 1; ctr--) {
486 for (
int ctr = num_rows; ctr > 1; ctr--) {
503 int height = NWidgetScrollbar::GetVerticalDimension().height;
513 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
517 int left = r.left + r.
Width() * 3 / 11;
518 int right = r.left + r.
Width() * 8 / 11;
523 GfxFillRect(left - bl, r.top + height, left - 1, r.bottom - height, c1);
524 GfxFillRect(left, r.top + height, left + br - 1, r.bottom - height, c2);
525 GfxFillRect(right - bl, r.top + height, right - 1, r.bottom - height, c1);
526 GfxFillRect(right, r.top + height, right + br - 1, r.bottom - height, c2);
543 int width = NWidgetScrollbar::GetHorizontalDimension().width;
552 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
556 int top = r.top + r.
Height() * 3 / 11;
557 int bottom = r.top + r.
Height() * 8 / 11;
562 GfxFillRect(r.left + width, top - bt, r.right - width, top - 1, c1);
563 GfxFillRect(r.left + width, top, r.right - width, top + bb - 1, c2);
564 GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom - 1, c1);
565 GfxFillRect(r.left + width, bottom, r.right - width, bottom + bb - 1, c2);
616 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
617 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
620 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
621 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
624 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
625 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
680static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
684 }
else if (clicked) {
701 d.height -= offset.y;
718 bool company_owned = owner < MAX_COMPANIES;
728 if (str.empty())
return;
776 if (!widget->IsHighlighted())
continue;
778 Rect outer = widget->GetCurrentRect();
783 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
784 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
785 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
786 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
801 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
804 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
815 return NWidgetScrollbar::GetVerticalDimension().width + 1;
818bool _draw_widget_outlines;
915 if (this->
index >= 0) widget_lookup[this->
index] =
this;
951 return (this->
type == tp) ? this :
nullptr;
954void NWidgetBase::ApplyAspectRatio()
968void NWidgetBase::AdjustPaddingForZoom()
1004 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1007void NWidgetResizeBase::AdjustPaddingForZoom()
1013 NWidgetBase::AdjustPaddingForZoom();
1037 this->min_x = std::max(this->min_x,
min_x);
1038 this->min_y = std::max(this->min_y,
min_y);
1090 d.height *= max_lines;
1106 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1107 this->min_x =
min_x;
1108 this->min_y =
min_y;
1120 if (
min_y == this->min_y)
return false;
1121 this->min_y =
min_y;
1242 this->align =
align;
1270 if (this->
type == tp)
return this;
1271 for (
const auto &child_wid : this->
children) {
1273 if (nwid !=
nullptr)
return nwid;
1278void NWidgetContainer::AdjustPaddingForZoom()
1280 for (
const auto &child_wid : this->
children) {
1281 child_wid->AdjustPaddingForZoom();
1283 NWidgetBase::AdjustPaddingForZoom();
1292 assert(wid !=
nullptr);
1294 this->
children.push_back(std::move(wid));
1300 for (
const auto &child_wid : this->
children) {
1301 child_wid->FillWidgetLookup(widget_lookup);
1307 for (
const auto &child_wid : this->
children) {
1311 DrawOutline(w,
this);
1318 for (
const auto &child_wid : this->
children) {
1320 if (nwid !=
nullptr)
return nwid;
1338 this->
fill_x = fill.width;
1339 this->
fill_y = fill.height;
1342 this->ApplyAspectRatio();
1353 for (
const auto &child_wid : this->
children) {
1354 child_wid->SetupSmallestSize(w);
1356 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1357 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1358 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1359 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1362 this->ApplyAspectRatio();
1373 for (
const auto &child_wid : this->
children) {
1374 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1375 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1376 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1378 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1379 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1380 uint child_pos_y = child_wid->padding.top;
1382 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1402 DrawOutline(w,
this);
1448 for (
const auto &child_wid : this->
children) {
1449 child_wid->SetupSmallestSize(w);
1451 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1452 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1453 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1454 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1457 this->ApplyAspectRatio();
1466 for (
const auto &child_wid : this->
children) {
1467 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1468 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1469 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1471 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1472 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1473 uint child_pos_y = child_wid->padding.top;
1475 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1482 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1486 DrawOutline(w,
this);
1489void NWidgetPIPContainer::AdjustPaddingForZoom()
1494 NWidgetContainer::AdjustPaddingForZoom();
1545 uint max_vert_fill = 0;
1546 for (
const auto &child_wid : this->
children) {
1547 child_wid->SetupSmallestSize(w);
1548 longest = std::max(longest, child_wid->smallest_x);
1549 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1550 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1551 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1555 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1558 for (
const auto &child_wid : this->
children) {
1559 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1560 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1561 if (step_size > 1 && child_height < cur_height) {
1562 uint remainder = (cur_height - child_height) % step_size;
1563 if (remainder > 0) {
1564 cur_height += step_size - remainder;
1565 assert(cur_height < max_smallest);
1574 for (
const auto &child_wid : this->
children) {
1575 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1576 child_wid->ApplyAspectRatio();
1577 longest = std::max(longest, child_wid->smallest_x);
1580 for (
const auto &child_wid : this->
children) {
1581 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1585 for (
const auto &child_wid : this->
children) {
1586 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1587 if (child_wid->fill_x > 0) {
1588 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1590 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1592 if (child_wid->resize_x > 0) {
1608 for (
const auto &child_wid : this->
children) {
1609 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1628 int num_changing_childs = 0;
1629 uint biggest_stepsize = 0;
1630 for (
const auto &child_wid : this->
children) {
1631 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1634 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1636 child_wid->current_x = child_wid->smallest_x;
1639 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1640 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1645 for (
const auto &child_wid : this->
children) {
1646 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1647 if (hor_step == biggest_stepsize) {
1648 num_changing_childs++;
1654 while (biggest_stepsize > 0) {
1655 uint next_biggest_stepsize = 0;
1656 for (
const auto &child_wid : this->
children) {
1657 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1658 if (hor_step > biggest_stepsize)
continue;
1659 if (hor_step == biggest_stepsize) {
1660 uint increment = additional_length / num_changing_childs;
1661 num_changing_childs--;
1662 if (hor_step > 1) increment -= increment % hor_step;
1663 child_wid->current_x = child_wid->smallest_x + increment;
1664 additional_length -= increment;
1667 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1669 biggest_stepsize = next_biggest_stepsize;
1673 for (
const auto &child_wid : this->
children) {
1674 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1675 if (hor_step == biggest_stepsize) {
1676 num_changing_childs++;
1681 assert(num_changing_childs == 0);
1686 if (additional_length > 0) {
1697 uint position = rtl ? this->
current_x - pre : pre;
1698 for (
const auto &child_wid : this->
children) {
1699 uint child_width = child_wid->current_x;
1700 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1701 uint child_y = y + child_wid->padding.top;
1703 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1704 if (child_wid->current_x != 0) {
1705 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1706 position = rtl ? position - padded_child_width : position + padded_child_width;
1728 uint max_hor_fill = 0;
1729 for (
const auto &child_wid : this->
children) {
1730 child_wid->SetupSmallestSize(w);
1731 highest = std::max(highest, child_wid->smallest_y);
1732 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1733 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1734 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1738 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1741 for (
const auto &child_wid : this->
children) {
1742 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1743 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1744 if (step_size > 1 && child_width < cur_width) {
1745 uint remainder = (cur_width - child_width) % step_size;
1746 if (remainder > 0) {
1747 cur_width += step_size - remainder;
1748 assert(cur_width < max_smallest);
1757 for (
const auto &child_wid : this->
children) {
1758 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1759 child_wid->ApplyAspectRatio();
1760 highest = std::max(highest, child_wid->smallest_y);
1763 for (
const auto &child_wid : this->
children) {
1764 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1768 for (
const auto &child_wid : this->
children) {
1769 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1770 if (child_wid->fill_y > 0) {
1771 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1773 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1775 if (child_wid->resize_y > 0) {
1791 for (
const auto &child_wid : this->
children) {
1792 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1802 int num_changing_childs = 0;
1803 uint biggest_stepsize = 0;
1804 for (
const auto &child_wid : this->
children) {
1805 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1806 if (vert_step > 0) {
1808 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1810 child_wid->current_y = child_wid->smallest_y;
1813 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1814 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1819 for (
const auto &child_wid : this->
children) {
1820 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1821 if (vert_step == biggest_stepsize) {
1822 num_changing_childs++;
1828 while (biggest_stepsize > 0) {
1829 uint next_biggest_stepsize = 0;
1830 for (
const auto &child_wid : this->
children) {
1831 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1832 if (vert_step > biggest_stepsize)
continue;
1833 if (vert_step == biggest_stepsize) {
1834 uint increment = additional_length / num_changing_childs;
1835 num_changing_childs--;
1836 if (vert_step > 1) increment -= increment % vert_step;
1837 child_wid->current_y = child_wid->smallest_y + increment;
1838 additional_length -= increment;
1841 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1843 biggest_stepsize = next_biggest_stepsize;
1847 for (
const auto &child_wid : this->
children) {
1848 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1849 if (vert_step == biggest_stepsize) {
1850 num_changing_childs++;
1855 assert(num_changing_childs == 0);
1860 if (additional_length > 0) {
1871 uint position = pre;
1872 for (
const auto &child_wid : this->
children) {
1873 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1874 uint child_height = child_wid->current_y;
1876 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1877 if (child_wid->current_y != 0) {
1878 position += child_height + child_wid->padding.Vertical() + inter;
1898 this->ApplyAspectRatio();
1909 DrawOutline(w,
this);
1930 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1946 this->count =
count;
1948 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1984 assert(this->
children.size() == 1);
1986 this->
children.front()->SetupSmallestSize(w);
1989 Dimension size = {this->
children.front()->smallest_x + padding.width, this->
children.front()->smallest_y + padding.height};
1997 this->
fill_x = fill.width;
1998 this->
fill_y = fill.height;
2001 this->ApplyAspectRatio();
2033 int start_x, start_y, base_offs_x, base_offs_y;
2038 int widget_col = (rtl ?
2049 assert(child !=
nullptr);
2073 assert(child !=
nullptr);
2074 int start_x, start_y, base_offs_x, base_offs_y;
2077 int offs_y = base_offs_y;
2078 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2080 if (offs_y + child->
smallest_y <= 0)
continue;
2081 if (offs_y >= (
int)this->
current_y)
break;
2086 int offs_x = base_offs_x;
2089 if (offs_x + child->
smallest_x <= 0)
continue;
2090 if (offs_x >= (
int)this->
current_x)
continue;
2103 DrawOutline(w,
this);
2119 if (this->
sb !=
nullptr) {
2127 base_offs_x += sub_x;
2129 base_offs_x -= sub_x;
2147 this->child = std::move(child);
2148 if (this->child !=
nullptr) this->child->parent =
this;
2161 if (this->
child ==
nullptr) {
2162 this->
child = std::make_unique<NWidgetVertical>();
2165 this->
child->Add(std::move(nwid));
2180 if (this->
child ==
nullptr) {
2181 this->
child = std::make_unique<NWidgetVertical>();
2183 this->
child->parent =
this;
2184 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2199 if (this->
child ==
nullptr) {
2200 this->
child = std::make_unique<NWidgetVertical>();
2202 this->
child->parent =
this;
2203 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2206void NWidgetBackground::AdjustPaddingForZoom()
2208 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2209 NWidgetCore::AdjustPaddingForZoom();
2214 if (this->
child !=
nullptr) {
2215 this->
child->SetupSmallestSize(w);
2225 if (w ==
nullptr)
return;
2228 std::string text = GetStringForWidget(w,
this);
2241 this->
child->padding = WidgetDimensions::scaled.
bevel;
2246 this->ApplyAspectRatio();
2253 std::string text = GetStringForWidget(w,
this);
2254 if (!text.empty()) {
2257 d =
maxdim(d, background);
2260 if (this->
index >= 0) {
2262 switch (this->
type) {
2263 default: NOT_REACHED();
2273 this->
fill_x = fill.width;
2274 this->
fill_y = fill.height;
2277 this->ApplyAspectRatio();
2285 if (this->
child !=
nullptr) {
2286 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2287 uint width = given_width - this->
child->padding.Horizontal();
2288 uint height = given_height - this->
child->padding.Vertical();
2289 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2296 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2303 Rect r = this->GetCurrentRect();
2306 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2308 switch (this->
type) {
2326 if (this->
child !=
nullptr) this->
child->Draw(w);
2332 DrawOutline(w,
this);
2340 if (nwid ==
nullptr) nwid =
this;
2349 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2361 this->ApplyAspectRatio();
2378 if (this->
disp_flags.
Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2382 DrawOutline(w,
this);
2402 if (w->
viewport ==
nullptr)
return;
2427 return (pos < 0 || pos >= this->
GetCount()) ? Scrollbar::npos :
pos;
2446 int new_pos = list_position;
2516 const int count = sb.
GetCount() * resize_step;
2517 const int position = sb.
GetPosition() * resize_step;
2521 r.bottom = r.top + count;
2525 r.right += position;
2526 r.left = r.right - count;
2529 r.right = r.left + count;
2546 switch (this->
type) {
2550 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2556 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2559 default: NOT_REACHED();
2568 switch (this->
type) {
2570 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2574 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2577 default: NOT_REACHED();
2588 Rect r = this->GetCurrentRect();
2591 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2607 DrawOutline(w,
this);
2610 void NWidgetScrollbar::InvalidateDimensionCache()
2616 Dimension NWidgetScrollbar::GetVerticalDimension()
2626 Dimension NWidgetScrollbar::GetHorizontalDimension()
2667NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp, colour, index, 1, 1, data, tip)
2676 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2680 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2686 if (colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2701 case NWID_PUSHBUTTON_DROPDOWN:
2708 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2720 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2782 switch (this->
type) {
2851 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2884 padding.height + std::max(di.height, dt.height)
2935 case NWID_PUSHBUTTON_DROPDOWN: {
2956 this->
fill_x = fill.width;
2957 this->
fill_y = fill.height;
2960 this->ApplyAspectRatio();
2972 new_dpi.left += this->
pos_x;
2973 new_dpi.top += this->
pos_y;
2977 Rect r = this->GetCurrentRect();
2980 switch (this->
type) {
2983 if (this->
index == -1 && _draw_widget_outlines) {
2994 Colours button_colour = this->
widget_data.alternate_colour;
2995 if (button_colour == INVALID_COLOUR) button_colour = this->
colour;
2996 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3024 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
3025 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
3026 default: NOT_REACHED();
3041 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3046 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3083 case NWID_PUSHBUTTON_DROPDOWN:
3097 DrawOutline(w,
this);
3110 int button_width = this->
pos_x + this->
current_x - NWidgetLeaf::dropdown_dimension.width;
3111 return pt.
x < button_width;
3113 int button_left = this->
pos_x + NWidgetLeaf::dropdown_dimension.width;
3114 return pt.
x >= button_left;
3138 switch (nwid.
type) {
3141 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3142 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3149 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3150 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3157 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3165 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3172 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3179 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3186 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3193 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3204 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3215 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3221 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3227 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3249 switch (nwid.
type) {
3250 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3288static 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)
3292 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3297 if (dest ==
nullptr)
return nwid_begin;
3328static 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)
3334 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3336 while (nwid_begin != nwid_end) {
3337 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3338 bool fill_sub =
false;
3339 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3342 if (sub_widget ==
nullptr)
break;
3350 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3351 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3352 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3353 parent = std::move(sub_widget);
3358 if (nwid_begin == nwid_end)
return nwid_begin;
3360 assert(nwid_begin < nwid_end);
3362 return std::next(nwid_begin);
3372std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3374 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3375 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3377 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3379 return std::move(container);
3393 auto nwid_begin = std::begin(nwid_parts);
3394 auto nwid_end = std::end(nwid_parts);
3396 *shade_select =
nullptr;
3399 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3401 assert(nwid !=
nullptr);
3405 auto root = std::make_unique<NWidgetVertical>();
3406 root->Add(std::move(nwid));
3407 if (nwid_begin == nwid_end)
return root;
3411 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3412 *shade_select = shade_stack.get();
3414 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3415 root->Add(std::move(shade_stack));
3420 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3435 assert(max_length >= 1);
3436 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3437 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3444 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3446 if (hor_length == max_length) {
3447 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3448 vert->Add(std::move(hor));
3452 if (hor ==
nullptr) {
3453 hor = std::make_unique<NWidgetHorizontal>();
3457 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3458 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3459 panel->SetFill(1, 1);
3460 if (resizable) panel->SetResize(1, 0);
3461 panel->SetToolTip(button_tooltip);
3462 hor->Add(std::move(panel));
3465 if (vert ==
nullptr)
return hor;
3467 if (hor_length > 0 && hor_length < max_length) {
3469 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3471 if (resizable) spc->SetResize(1, 0);
3472 hor->Add(std::move(spc));
3474 if (hor !=
nullptr) vert->Add(std::move(hor));
Class for backupping variables and making sure they are restored later.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
TypedIndexContainer< std::array< Colours, MAX_COMPANIES >, CompanyID > _company_colours
NOSAVE: can be determined from company structs.
Functions related to companies.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
FontSize
Available font sizes.
@ FS_NORMAL
Index of the normal font in the font tables.
StringAlignment
How to align the to-be drawn text.
@ SA_TOP
Top align the text.
@ SA_LEFT
Left align the text.
@ SA_HOR_MASK
Mask for horizontal alignment.
@ SA_RIGHT
Right align the text (must be a single bit).
@ SA_HOR_CENTER
Horizontally center the text.
@ SA_VERT_MASK
Mask for vertical alignment.
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
@ SA_BOTTOM
Bottom align the text.
@ SA_CENTER
Center both horizontally and vertically.
@ SA_VERT_CENTER
Vertically center the text.
uint32_t PaletteID
The number of the palette.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
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...
GUISettings gui
settings related to the GUI
Point pos
logical mouse position
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
bool scale_bevels
bevels are scaled with GUI scale.
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
uint8_t lines
Number of text lines.
uint8_t spacing
Extra spacing around lines.
FontSize size
Font size of text lines.
TextColour colour
TextColour for DrawString.
FontSize size
Font size of text.
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.
Rect CentreTo(int width, int height) const
Centre a dimension within this Rect.
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.
int Height() const
Get height of Rect.
Rect Expand(int s) const
Copy and expand Rect by s pixels.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
Data structure for an opened window.
virtual void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
Update size and resize step of a widget in the window.
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
void DrawWidgets() const
Paint all widgets of a window.
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.
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?
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.