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);
289void DrawFrameRect(
int left,
int top,
int right,
int bottom, Colours colour, FrameFlags flags)
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;
518 int left = r.left + r.
Width() * 3 / 11;
519 int right = r.left + r.
Width() * 8 / 11;
544 int width = NWidgetScrollbar::GetHorizontalDimension().width;
558 int top = r.top + r.
Height() * 3 / 11;
559 int bottom = r.top + r.
Height() * 8 / 11;
618 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
619 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
622 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
623 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
626 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
627 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
682static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
686 }
else if (clicked) {
703 d.height -= offset.y;
720 bool company_owned = owner < MAX_COMPANIES;
730 if (str.empty())
return;
780 Rect outer = widget->GetCurrentRect();
785 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
786 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
787 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
788 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
806 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
817 return NWidgetScrollbar::GetVerticalDimension().width + 1;
820bool _draw_widget_outlines;
917 if (this->
index >= 0) widget_lookup[this->
index] =
this;
953 return (this->
type == tp) ? this :
nullptr;
956void NWidgetBase::ApplyAspectRatio()
1007 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1040 this->min_x = std::max(this->min_x,
min_x);
1041 this->min_y = std::max(this->min_y,
min_y);
1093 d.height *= max_lines;
1109 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1110 this->min_x =
min_x;
1111 this->min_y =
min_y;
1123 if (
min_y == this->min_y)
return false;
1124 this->min_y =
min_y;
1245 this->align =
align;
1273 if (this->
type == tp)
return this;
1274 for (
const auto &child_wid : this->
children) {
1276 if (nwid !=
nullptr)
return nwid;
1283 for (
const auto &child_wid : this->
children) {
1284 child_wid->AdjustPaddingForZoom();
1295 assert(wid !=
nullptr);
1297 this->
children.push_back(std::move(wid));
1303 for (
const auto &child_wid : this->
children) {
1304 child_wid->FillWidgetLookup(widget_lookup);
1310 for (
const auto &child_wid : this->
children) {
1314 DrawOutline(w,
this);
1321 for (
const auto &child_wid : this->
children) {
1323 if (nwid !=
nullptr)
return nwid;
1341 this->
fill_x = fill.width;
1342 this->
fill_y = fill.height;
1345 this->ApplyAspectRatio();
1356 for (
const auto &child_wid : this->
children) {
1357 child_wid->SetupSmallestSize(w);
1359 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1360 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1361 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1362 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1365 this->ApplyAspectRatio();
1376 for (
const auto &child_wid : this->
children) {
1377 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1378 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1379 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1381 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1382 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1383 uint child_pos_y = child_wid->padding.top;
1385 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1405 DrawOutline(w,
this);
1432class NWidgetLayer :
public NWidgetContainer {
1451 for (
const auto &child_wid : this->
children) {
1452 child_wid->SetupSmallestSize(w);
1454 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1455 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1456 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1457 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1460 this->ApplyAspectRatio();
1469 for (
const auto &child_wid : this->
children) {
1470 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1471 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1472 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1474 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1475 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1476 uint child_pos_y = child_wid->padding.top;
1478 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1485 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1489 DrawOutline(w,
this);
1548 uint max_vert_fill = 0;
1549 for (
const auto &child_wid : this->
children) {
1550 child_wid->SetupSmallestSize(w);
1551 longest = std::max(longest, child_wid->smallest_x);
1552 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1553 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1554 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1558 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1561 for (
const auto &child_wid : this->children) {
1562 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1563 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1564 if (step_size > 1 && child_height < cur_height) {
1565 uint remainder = (cur_height - child_height) % step_size;
1566 if (remainder > 0) {
1567 cur_height += step_size - remainder;
1568 assert(cur_height < max_smallest);
1577 for (
const auto &child_wid : this->children) {
1578 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1579 child_wid->ApplyAspectRatio();
1580 longest = std::max(longest, child_wid->smallest_x);
1583 for (
const auto &child_wid : this->children) {
1584 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1588 for (
const auto &child_wid : this->children) {
1589 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1590 if (child_wid->fill_x > 0) {
1591 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1593 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1595 if (child_wid->resize_x > 0) {
1611 for (
const auto &child_wid : this->
children) {
1612 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1631 int num_changing_childs = 0;
1632 uint biggest_stepsize = 0;
1633 for (
const auto &child_wid : this->children) {
1634 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1637 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1639 child_wid->current_x = child_wid->smallest_x;
1642 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1643 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1648 for (
const auto &child_wid : this->children) {
1649 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1650 if (hor_step == biggest_stepsize) {
1651 num_changing_childs++;
1657 while (biggest_stepsize > 0) {
1658 uint next_biggest_stepsize = 0;
1659 for (
const auto &child_wid : this->children) {
1660 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1661 if (hor_step > biggest_stepsize)
continue;
1662 if (hor_step == biggest_stepsize) {
1663 uint increment = additional_length / num_changing_childs;
1664 num_changing_childs--;
1665 if (hor_step > 1) increment -= increment % hor_step;
1666 child_wid->current_x = child_wid->smallest_x + increment;
1667 additional_length -= increment;
1670 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1672 biggest_stepsize = next_biggest_stepsize;
1676 for (
const auto &child_wid : this->children) {
1677 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1678 if (hor_step == biggest_stepsize) {
1679 num_changing_childs++;
1684 assert(num_changing_childs == 0);
1689 if (additional_length > 0) {
1700 uint position = rtl ? this->
current_x - pre : pre;
1701 for (
const auto &child_wid : this->children) {
1702 uint child_width = child_wid->current_x;
1703 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1704 uint child_y = y + child_wid->padding.top;
1706 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1707 if (child_wid->current_x != 0) {
1708 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1709 position = rtl ? position - padded_child_width : position + padded_child_width;
1731 uint max_hor_fill = 0;
1732 for (
const auto &child_wid : this->
children) {
1733 child_wid->SetupSmallestSize(w);
1734 highest = std::max(highest, child_wid->smallest_y);
1735 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1736 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1737 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1741 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1744 for (
const auto &child_wid : this->children) {
1745 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1746 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1747 if (step_size > 1 && child_width < cur_width) {
1748 uint remainder = (cur_width - child_width) % step_size;
1749 if (remainder > 0) {
1750 cur_width += step_size - remainder;
1751 assert(cur_width < max_smallest);
1760 for (
const auto &child_wid : this->children) {
1761 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1762 child_wid->ApplyAspectRatio();
1763 highest = std::max(highest, child_wid->smallest_y);
1766 for (
const auto &child_wid : this->children) {
1767 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1771 for (
const auto &child_wid : this->children) {
1772 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1773 if (child_wid->fill_y > 0) {
1774 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1776 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1778 if (child_wid->resize_y > 0) {
1794 for (
const auto &child_wid : this->
children) {
1795 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1805 int num_changing_childs = 0;
1806 uint biggest_stepsize = 0;
1807 for (
const auto &child_wid : this->children) {
1808 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1809 if (vert_step > 0) {
1811 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1813 child_wid->current_y = child_wid->smallest_y;
1816 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1817 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1822 for (
const auto &child_wid : this->children) {
1823 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1824 if (vert_step == biggest_stepsize) {
1825 num_changing_childs++;
1831 while (biggest_stepsize > 0) {
1832 uint next_biggest_stepsize = 0;
1833 for (
const auto &child_wid : this->children) {
1834 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1835 if (vert_step > biggest_stepsize)
continue;
1836 if (vert_step == biggest_stepsize) {
1837 uint increment = additional_length / num_changing_childs;
1838 num_changing_childs--;
1839 if (vert_step > 1) increment -= increment % vert_step;
1840 child_wid->current_y = child_wid->smallest_y + increment;
1841 additional_length -= increment;
1844 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1846 biggest_stepsize = next_biggest_stepsize;
1850 for (
const auto &child_wid : this->children) {
1851 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1852 if (vert_step == biggest_stepsize) {
1853 num_changing_childs++;
1858 assert(num_changing_childs == 0);
1863 if (additional_length > 0) {
1874 uint position = pre;
1875 for (
const auto &child_wid : this->children) {
1876 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1877 uint child_height = child_wid->current_y;
1879 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1880 if (child_wid->current_y != 0) {
1881 position += child_height + child_wid->padding.Vertical() + inter;
1901 this->ApplyAspectRatio();
1912 DrawOutline(w,
this);
1933 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1938 this->
sb->ScrollTowards(vpos);
1949 this->count =
count;
1951 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1959 count *= (this->
sb->IsVertical() ? this->
children.front()->smallest_y : this->children.front()->smallest_x) + this->
pip_inter;
1963 this->
sb->SetCapacity(this->
sb->IsVertical() ? this->current_y : this->current_x);
1964 this->
sb->SetStepSize(this->
sb->IsVertical() ? this->widget_h : this->widget_w);
1987 assert(this->
children.size() == 1);
1989 this->
children.front()->SetupSmallestSize(w);
2000 this->
fill_x = fill.width;
2001 this->
fill_y = fill.height;
2004 this->ApplyAspectRatio();
2036 int start_x, start_y, base_offs_x, base_offs_y;
2041 int widget_col = (rtl ?
2052 assert(child !=
nullptr);
2076 assert(child !=
nullptr);
2077 int start_x, start_y, base_offs_x, base_offs_y;
2080 int offs_y = base_offs_y;
2081 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2083 if (offs_y + child->
smallest_y <= 0)
continue;
2084 if (offs_y >= (
int)this->
current_y)
break;
2089 int offs_x = base_offs_x;
2092 if (offs_x + child->
smallest_x <= 0)
continue;
2093 if (offs_x >= (
int)this->
current_x)
continue;
2106 DrawOutline(w,
this);
2122 if (this->
sb !=
nullptr) {
2123 if (this->
sb->IsVertical()) {
2124 start_y = this->
sb->GetPosition() / this->
widget_h;
2125 base_offs_y += -this->
sb->GetPosition() + start_y * this->
widget_h;
2127 start_x = this->
sb->GetPosition() / this->widget_w;
2128 int sub_x = this->
sb->GetPosition() - start_x * this->widget_w;
2130 base_offs_x += sub_x;
2132 base_offs_x -= sub_x;
2150 this->child = std::move(child);
2151 if (this->child !=
nullptr) this->child->parent =
this;
2164 if (this->
child ==
nullptr) {
2165 this->
child = std::make_unique<NWidgetVertical>();
2168 this->
child->Add(std::move(nwid));
2183 if (this->
child ==
nullptr) {
2184 this->
child = std::make_unique<NWidgetVertical>();
2186 this->
child->parent =
this;
2187 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2202 if (this->
child ==
nullptr) {
2203 this->
child = std::make_unique<NWidgetVertical>();
2205 this->
child->parent =
this;
2206 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2211 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2217 if (this->
child !=
nullptr) {
2218 this->
child->SetupSmallestSize(w);
2228 if (w ==
nullptr)
return;
2231 std::string text = GetStringForWidget(w,
this);
2249 this->ApplyAspectRatio();
2256 std::string text = GetStringForWidget(w,
this);
2257 if (!text.empty()) {
2260 d =
maxdim(d, background);
2263 if (this->
index >= 0) {
2265 switch (this->
type) {
2266 default: NOT_REACHED();
2276 this->
fill_x = fill.width;
2277 this->
fill_y = fill.height;
2280 this->ApplyAspectRatio();
2288 if (this->
child !=
nullptr) {
2289 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2290 uint width = given_width - this->
child->padding.Horizontal();
2291 uint height = given_height - this->
child->padding.Vertical();
2292 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2299 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2306 Rect r = this->GetCurrentRect();
2309 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2311 switch (this->
type) {
2329 if (this->
child !=
nullptr) this->
child->Draw(w);
2335 DrawOutline(w,
this);
2342 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetFromPos(x, y);
2343 if (nwid ==
nullptr) nwid =
this;
2351 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetOfType(tp);
2352 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2364 this->ApplyAspectRatio();
2381 if (this->
disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2385 DrawOutline(w,
this);
2405 if (w->
viewport ==
nullptr)
return;
2449 int new_pos = list_position;
2519 const int count = sb.
GetCount() * resize_step;
2520 const int position = sb.
GetPosition() * resize_step;
2524 r.bottom = r.top + count;
2528 r.right += position;
2529 r.left = r.right - count;
2532 r.right = r.left + count;
2549 switch (this->type) {
2553 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2559 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2562 default: NOT_REACHED();
2571 switch (this->
type) {
2573 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2577 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2580 default: NOT_REACHED();
2591 Rect r = this->GetCurrentRect();
2594 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2610 DrawOutline(w,
this);
2613 void NWidgetScrollbar::InvalidateDimensionCache()
2619 Dimension NWidgetScrollbar::GetVerticalDimension()
2629 Dimension NWidgetScrollbar::GetHorizontalDimension()
2670NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours
colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp,
colour,
index, 1, 1, data, tip)
2679 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2683 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2689 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2704 case NWID_PUSHBUTTON_DROPDOWN:
2711 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2723 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2785 switch (this->
type) {
2854 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2887 padding.height + std::max(di.height, dt.height)
2938 case NWID_PUSHBUTTON_DROPDOWN: {
2959 this->
fill_x = fill.width;
2960 this->
fill_y = fill.height;
2963 this->ApplyAspectRatio();
2975 new_dpi.left += this->
pos_x;
2976 new_dpi.top += this->
pos_y;
2980 Rect r = this->GetCurrentRect();
2983 switch (this->
type) {
2986 if (this->
index == -1 && _draw_widget_outlines) {
2997 Colours button_colour = this->
widget_data.alternate_colour;
2998 if (button_colour == INVALID_COLOUR) button_colour = this->
colour;
2999 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3027 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
3028 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
3029 default: NOT_REACHED();
3044 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3049 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3086 case NWID_PUSHBUTTON_DROPDOWN:
3100 DrawOutline(w,
this);
3114 return pt.
x < button_width;
3117 return pt.
x >= button_left;
3141 switch (nwid.
type) {
3144 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3145 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3152 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3153 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3160 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3168 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3175 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3182 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3189 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3196 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3207 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3218 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3224 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3230 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3252 switch (nwid.
type) {
3253 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3291static 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)
3295 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3300 if (dest ==
nullptr)
return nwid_begin;
3331static 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)
3337 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3339 while (nwid_begin != nwid_end) {
3340 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3341 bool fill_sub =
false;
3342 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3345 if (sub_widget ==
nullptr)
break;
3353 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3354 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3355 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3356 parent = std::move(sub_widget);
3361 if (nwid_begin == nwid_end)
return nwid_begin;
3363 assert(nwid_begin < nwid_end);
3365 return std::next(nwid_begin);
3375std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3377 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3378 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3380 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3382 return std::move(container);
3396 auto nwid_begin = std::begin(nwid_parts);
3397 auto nwid_end = std::end(nwid_parts);
3399 *shade_select =
nullptr;
3402 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3404 assert(nwid !=
nullptr);
3408 auto root = std::make_unique<NWidgetVertical>();
3409 root->Add(std::move(nwid));
3410 if (nwid_begin == nwid_end)
return root;
3414 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3415 *shade_select = shade_stack.get();
3417 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3418 root->Add(std::move(shade_stack));
3423 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3438 assert(max_length >= 1);
3439 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3440 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3447 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3449 if (hor_length == max_length) {
3450 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3451 vert->Add(std::move(hor));
3455 if (hor ==
nullptr) {
3456 hor = std::make_unique<NWidgetHorizontal>();
3460 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3461 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3462 panel->SetFill(1, 1);
3463 if (resizable) panel->SetResize(1, 0);
3464 panel->SetToolTip(button_tooltip);
3465 hor->Add(std::move(panel));
3468 if (vert ==
nullptr)
return hor;
3470 if (hor_length > 0 && hor_length < max_length) {
3472 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3474 if (resizable) spc->SetResize(1, 0);
3475 hor->Add(std::move(spc));
3477 if (hor !=
nullptr) vert->Add(std::move(hor));
3488 assert(parent_window !=
nullptr);
3490 for (
auto &widget : this->
children) {
Class for backupping variables and making sure they are restored later.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
TypedIndexContainer< std::array< Colours, MAX_COMPANIES >, CompanyID > _company_colours
NOSAVE: can be determined from company structs.
Functions related to companies.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
FontSize
Available font sizes.
@ FS_NORMAL
Index of the normal font in the font tables.
StringAlignment
How to align the to-be drawn text.
@ SA_TOP
Top align the text.
@ SA_LEFT
Left align the text.
@ SA_HOR_MASK
Mask for horizontal alignment.
@ SA_RIGHT
Right align the text (must be a single bit).
@ SA_HOR_CENTER
Horizontally center the text.
@ SA_VERT_MASK
Mask for vertical alignment.
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
@ SA_BOTTOM
Bottom align the text.
@ SA_CENTER
Center both horizontally and vertically.
@ SA_VERT_CENTER
Vertically center the text.
uint32_t PaletteID
The number of the palette.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
static constexpr PixelColour PC_BLACK
Black palette colour.
static constexpr PixelColour PC_WHITE
White palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
void DrawBoolButton(int x, int y, Colours button_colour, Colours background, bool state, bool clickable)
Draw a toggle button.
Functions for setting GUIs.
#define SETTING_BUTTON_WIDTH
Width of setting buttons.
#define SETTING_BUTTON_HEIGHT
Height of setting buttons.
Types related to global configuration settings.
This file contains all sprite-related enums and defines.
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
static const PaletteID PALETTE_NEWSPAPER
Recolour sprite for newspaper-greying.
Definition of base types and functions in a cross-platform compatible way.
The colour translation of GRF's strings.
static constexpr PixelColour _string_colourmap[17]
Colour mapping for TextColour.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
TextDirection _current_text_dir
Text direction of the currently selected language.
Functions related to OTTD's strings.
Types related to strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_LTR
Text is written left-to-right by default.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
uint8_t lines
Number of text lines.
uint8_t spacing
Extra spacing around lines.
FontSize size
Font size of text lines.
TextColour colour
TextColour for DrawString.
FontSize size
Font size of text.
Colour for pixel/line drawing.
Data stored about a string that can be modified in the GUI.
Padding dimensions to apply to each side of a Rect.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect CentreToHeight(int height) const
Centre a vertical dimension within this Rect.
int Height() const
Get height of Rect.
Rect WithY(int new_top, int new_bottom) const
Create a new Rect, replacing the top and bottom coordiates.
Rect WithX(int new_left, int new_right) const
Create a new Rect, replacing the left and right coordiates.
Rect Expand(int s) const
Copy and expand Rect by s pixels.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
Data structure for an opened window.
virtual void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
Update size and resize step of a widget in the window.
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
void DrawWidgets() const
Paint all widgets of a window.
std::unique_ptr< ViewportData > viewport
Pointer to viewport data, if present.
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). INVALID_WIDGET if no widget has mouse ca...
virtual void OnScrollbarScroll(WidgetID widget)
Notify window that a scrollbar position has been updated.
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
void UnfocusFocusedWidget()
Makes no widget on this window have focus.
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
void DrawViewport() const
Draw the viewport of this window.
virtual void DrawWidget(const Rect &r, WidgetID widget) const
Draw the contents of a nested widget.
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
int left
x position of left edge of the window
bool IsShaded() const
Is window shaded currently?
const NWidgetCore * nested_focus
Currently focused nested widget, or nullptr if no nested widget has focus.
int top
y position of top edge of the window
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
WindowFlags flags
Window flags.
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
int height
Height of the window (number of pixels down in y direction).
int width
width of the window (number of pixels to the right in x direction)
Functions related to transparency.
TransparencyOptionBits _transparency_opt
The bits that should be transparent.
uint TransparencyOptionBits
transparency option bits
@ TO_TEXT
loading and cost/income text
void InitializeWindowViewport(Window *w, int x, int y, int width, int height, std::variant< TileIndex, VehicleID > focus, ZoomLevel zoom)
Initialize viewport of the window for use.
Functions related to (drawing on) viewports.
bool _window_highlight_colour
If false, highlight is white, otherwise the by the widget defined colour.
Functions, definitions and such used only by the GUI.
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
@ Transparent
Makes the background transparent if set.
@ BorderOnly
Draw border only, no background.
@ Darkened
If set the background is darker, allows for lowered frames with normal background colour when used wi...
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed).
@ SizingLeft
Window is being resized towards the left.
@ Highlighted
Window has a widget that has a highlight.
@ SizingRight
Window is being resized towards the right.
@ WhiteBorder
Window white border counter bit mask.
@ Sticky
Window is made sticky by user.
SortButtonState
State of a sort direction button.
@ SBS_DOWN
Sort ascending.
@ SBS_OFF
Do not sort (with this button).
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
static constexpr WidgetID INVALID_WIDGET
An invalid widget index.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::Min) When shifting right,...
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
ZoomLevel
All zoom levels we know.
@ Normal
The normal zoom level.