23#include "table/strings.h"
112 case SA_LEFT: p.x = r.left;
break;
114 case SA_RIGHT: p.x = r.right + 1 - d.width;
break;
115 default: NOT_REACHED();
118 case SA_TOP: p.y = r.top;
break;
120 case SA_BOTTOM: p.y = r.bottom + 1 - d.height;
break;
121 default: NOT_REACHED();
138 int rev_base = top + bottom;
141 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
143 button_size = NWidgetScrollbar::GetVerticalDimension().height;
146 bottom -= button_size;
152 int height = (bottom - top);
153 int slider_height = std::max(button_size, cap * height / count);
154 height -= slider_height;
157 bottom = top + slider_height;
162 pt.x = rev_base - bottom;
163 pt.y = rev_base - top;
185 bool changed =
false;
190 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
193 button_size = NWidgetScrollbar::GetVerticalDimension().height;
195 if (pos < mi + button_size) {
198 if (_scroller_click_timeout <= 1) {
199 _scroller_click_timeout = 3;
203 }
else if (pos >= ma - button_size) {
207 if (_scroller_click_timeout <= 1) {
208 _scroller_click_timeout = 3;
217 }
else if (pos > pt.y) {
220 _scrollbar_start_pos = pt.x - mi - button_size;
221 _scrollbar_size = ma - mi - button_size * 2 - (pt.y - pt.x);
223 _cursorpos_drag_start = _cursor.
pos;
256 assert(scrollbar !=
nullptr);
271 return (nw !=
nullptr) ? nw->
GetIndex() : -1;
288 assert(colour < COLOUR_END);
296 Rect outer = {left, top, right, bottom};
300 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
301 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
302 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
303 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
306 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
307 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
308 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
309 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
310 interior = medium_dark;
313 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
323 d.height -= offset.y;
326 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
343 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
344 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
359 if (str == STR_NULL)
return;
363 DrawString(r.left, r.right, p.y, str, colour, align,
false, fs);
378 if (str != STR_NULL)
DrawString(r.left, r.right, p.y, str, colour, align,
false, fs);
392 DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, {FrameFlag::Lowered, FrameFlag::Darkened});
406static inline void DrawMatrix(
const Rect &r, Colours colour,
bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
411 if (num_columns == 0) {
412 column_width = resize_x;
413 num_columns = r.
Width() / column_width;
415 column_width = r.
Width() / num_columns;
420 row_height = resize_y;
421 num_rows = r.
Height() / row_height;
423 row_height = r.
Height() / num_rows;
429 for (
int ctr = num_columns; ctr > 1; ctr--) {
435 for (
int ctr = num_rows; ctr > 1; ctr--) {
443 for (
int ctr = num_columns; ctr > 1; ctr--) {
449 for (
int ctr = num_rows; ctr > 1; ctr--) {
466 int height = NWidgetScrollbar::GetVerticalDimension().height;
476 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
480 int left = r.left + r.
Width() * 3 / 11;
481 int right = r.left + r.
Width() * 8 / 11;
486 GfxFillRect(left - bl, r.top + height, left - 1, r.bottom - height, c1);
487 GfxFillRect(left, r.top + height, left + br - 1, r.bottom - height, c2);
488 GfxFillRect(right - bl, r.top + height, right - 1, r.bottom - height, c1);
489 GfxFillRect(right, r.top + height, right + br - 1, r.bottom - height, c2);
506 int width = NWidgetScrollbar::GetHorizontalDimension().width;
515 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
519 int top = r.top + r.
Height() * 3 / 11;
520 int bottom = r.top + r.
Height() * 8 / 11;
525 GfxFillRect(r.left + width, top - bt, r.right - width, top - 1, c1);
526 GfxFillRect(r.left + width, top, r.right - width, top + bb - 1, c2);
527 GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom - 1, c1);
528 GfxFillRect(r.left + width, bottom, r.right - width, bottom + bb - 1, c2);
579 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
580 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
583 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
584 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
587 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
588 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
643static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
647 }
else if (clicked) {
660 if (colour != COLOUR_WHITE)
DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, {});
664 d.height -= offset.y;
691 if (str != STR_NULL) {
716 if (str != STR_NULL) {
722 if (str != STR_NULL) {
743 if (!widget->IsHighlighted())
continue;
771 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
782 return NWidgetScrollbar::GetVerticalDimension().width + 1;
785bool _draw_widget_outlines;
925 return (this->
type == tp) ? this :
nullptr;
928void NWidgetBase::ApplyAspectRatio()
942void NWidgetBase::AdjustPaddingForZoom()
978 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
981void NWidgetResizeBase::AdjustPaddingForZoom()
987 NWidgetBase::AdjustPaddingForZoom();
1011 this->min_x = std::max(this->min_x,
min_x);
1012 this->min_y = std::max(this->min_y,
min_y);
1064 d.height *= max_lines;
1080 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1081 this->min_x =
min_x;
1082 this->min_y =
min_y;
1094 if (
min_y == this->min_y)
return false;
1095 this->min_y =
min_y;
1219 this->align =
align;
1251 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1261 if (this->
type == tp)
return this;
1262 for (
const auto &child_wid : this->
children) {
1264 if (nwid !=
nullptr)
return nwid;
1269void NWidgetContainer::AdjustPaddingForZoom()
1271 for (
const auto &child_wid : this->
children) {
1272 child_wid->AdjustPaddingForZoom();
1274 NWidgetBase::AdjustPaddingForZoom();
1283 assert(wid !=
nullptr);
1285 this->
children.push_back(std::move(wid));
1290 for (
const auto &child_wid : this->
children) {
1297 for (
const auto &child_wid : this->
children) {
1301 DrawOutline(w,
this);
1308 for (
const auto &child_wid : this->
children) {
1310 if (nwid !=
nullptr)
return nwid;
1335 this->
fill_x = fill.width;
1336 this->
fill_y = fill.height;
1339 this->ApplyAspectRatio();
1350 for (
const auto &child_wid : this->
children) {
1351 child_wid->SetupSmallestSize(w);
1353 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1354 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1355 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1356 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1359 this->ApplyAspectRatio();
1370 for (
const auto &child_wid : this->
children) {
1371 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1372 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1373 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1375 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1376 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1377 uint child_pos_y = child_wid->padding.top;
1379 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1388 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1400 DrawOutline(w,
this);
1450 for (
const auto &child_wid : this->
children) {
1453 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1454 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1455 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1456 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1459 this->ApplyAspectRatio();
1468 for (
const auto &child_wid : this->
children) {
1469 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1470 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1471 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1473 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1474 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1475 uint child_pos_y = child_wid->padding.top;
1477 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1484 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1488 DrawOutline(w,
this);
1493 this->flags = flags;
1496void NWidgetPIPContainer::AdjustPaddingForZoom()
1501 NWidgetContainer::AdjustPaddingForZoom();
1557 uint max_vert_fill = 0;
1558 for (
const auto &child_wid : this->
children) {
1560 longest = std::max(longest, child_wid->smallest_x);
1561 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1562 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1563 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1567 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1570 for (
const auto &child_wid : this->
children) {
1571 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1572 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1573 if (step_size > 1 && child_height < cur_height) {
1574 uint remainder = (cur_height - child_height) % step_size;
1575 if (remainder > 0) {
1576 cur_height += step_size - remainder;
1577 assert(cur_height < max_smallest);
1586 for (
const auto &child_wid : this->
children) {
1587 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1588 child_wid->ApplyAspectRatio();
1589 longest = std::max(longest, child_wid->smallest_x);
1592 for (
const auto &child_wid : this->
children) {
1593 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1597 for (
const auto &child_wid : this->
children) {
1598 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1599 if (child_wid->fill_x > 0) {
1600 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1602 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1604 if (child_wid->resize_x > 0) {
1620 for (
const auto &child_wid : this->
children) {
1621 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1640 int num_changing_childs = 0;
1641 uint biggest_stepsize = 0;
1642 for (
const auto &child_wid : this->
children) {
1643 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1646 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1648 child_wid->current_x = child_wid->smallest_x;
1651 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1652 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1657 for (
const auto &child_wid : this->
children) {
1658 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1659 if (hor_step == biggest_stepsize) {
1660 num_changing_childs++;
1666 while (biggest_stepsize > 0) {
1667 uint next_biggest_stepsize = 0;
1668 for (
const auto &child_wid : this->
children) {
1669 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1670 if (hor_step > biggest_stepsize)
continue;
1671 if (hor_step == biggest_stepsize) {
1672 uint increment = additional_length / num_changing_childs;
1673 num_changing_childs--;
1674 if (hor_step > 1) increment -= increment % hor_step;
1675 child_wid->current_x = child_wid->smallest_x + increment;
1676 additional_length -= increment;
1679 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1681 biggest_stepsize = next_biggest_stepsize;
1683 if (num_changing_childs == 0 && (
flags &
NC_BIGFIRST) && biggest_stepsize > 0) {
1685 for (
const auto &child_wid : this->
children) {
1686 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1687 if (hor_step == biggest_stepsize) {
1688 num_changing_childs++;
1693 assert(num_changing_childs == 0);
1698 if (additional_length > 0) {
1709 uint position = rtl ? this->
current_x - pre : pre;
1710 for (
const auto &child_wid : this->
children) {
1711 uint child_width = child_wid->current_x;
1712 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1713 uint child_y = y + child_wid->padding.top;
1715 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1716 if (child_wid->current_x != 0) {
1717 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1718 position = rtl ? position - padded_child_width : position + padded_child_width;
1751 uint max_hor_fill = 0;
1752 for (
const auto &child_wid : this->
children) {
1754 highest = std::max(highest, child_wid->smallest_y);
1755 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1756 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1757 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1761 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1764 for (
const auto &child_wid : this->
children) {
1765 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1766 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1767 if (step_size > 1 && child_width < cur_width) {
1768 uint remainder = (cur_width - child_width) % step_size;
1769 if (remainder > 0) {
1770 cur_width += step_size - remainder;
1771 assert(cur_width < max_smallest);
1780 for (
const auto &child_wid : this->
children) {
1781 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1782 child_wid->ApplyAspectRatio();
1783 highest = std::max(highest, child_wid->smallest_y);
1786 for (
const auto &child_wid : this->
children) {
1787 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1791 for (
const auto &child_wid : this->
children) {
1792 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1793 if (child_wid->fill_y > 0) {
1794 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1796 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1798 if (child_wid->resize_y > 0) {
1814 for (
const auto &child_wid : this->
children) {
1815 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1825 int num_changing_childs = 0;
1826 uint biggest_stepsize = 0;
1827 for (
const auto &child_wid : this->
children) {
1828 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1829 if (vert_step > 0) {
1831 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1833 child_wid->current_y = child_wid->smallest_y;
1836 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1837 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1842 for (
const auto &child_wid : this->
children) {
1843 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1844 if (vert_step == biggest_stepsize) {
1845 num_changing_childs++;
1851 while (biggest_stepsize > 0) {
1852 uint next_biggest_stepsize = 0;
1853 for (
const auto &child_wid : this->
children) {
1854 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1855 if (vert_step > biggest_stepsize)
continue;
1856 if (vert_step == biggest_stepsize) {
1857 uint increment = additional_length / num_changing_childs;
1858 num_changing_childs--;
1859 if (vert_step > 1) increment -= increment % vert_step;
1860 child_wid->current_y = child_wid->smallest_y + increment;
1861 additional_length -= increment;
1864 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1866 biggest_stepsize = next_biggest_stepsize;
1868 if (num_changing_childs == 0 && (
flags &
NC_BIGFIRST) && biggest_stepsize > 0) {
1870 for (
const auto &child_wid : this->
children) {
1871 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1872 if (vert_step == biggest_stepsize) {
1873 num_changing_childs++;
1878 assert(num_changing_childs == 0);
1883 if (additional_length > 0) {
1894 uint position = pre;
1895 for (
const auto &child_wid : this->
children) {
1896 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1897 uint child_height = child_wid->current_y;
1899 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1900 if (child_wid->current_y != 0) {
1901 position += child_height + child_wid->padding.Vertical() + inter;
1921 this->ApplyAspectRatio();
1936 DrawOutline(w,
this);
1952 this->colour = colour;
1962 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1978 this->count =
count;
1980 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
2016 assert(this->
children.size() == 1);
2018 this->
children.front()->SetupSmallestSize(w);
2021 Dimension size = {this->
children.front()->smallest_x + padding.width, this->
children.front()->smallest_y + padding.height};
2029 this->
fill_x = fill.width;
2030 this->
fill_y = fill.height;
2033 this->ApplyAspectRatio();
2062 if (this->
index >= 0) widget_lookup[this->
index] =
this;
2071 int start_x, start_y, base_offs_x, base_offs_y;
2076 int widget_col = (rtl ?
2087 assert(child !=
nullptr);
2111 assert(child !=
nullptr);
2112 int start_x, start_y, base_offs_x, base_offs_y;
2115 int offs_y = base_offs_y;
2116 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2118 if (offs_y + child->
smallest_y <= 0)
continue;
2119 if (offs_y >= (
int)this->
current_y)
break;
2124 int offs_x = base_offs_x;
2127 if (offs_x + child->
smallest_x <= 0)
continue;
2128 if (offs_x >= (
int)this->
current_x)
continue;
2141 DrawOutline(w,
this);
2157 if (this->
sb !=
nullptr) {
2165 base_offs_x += sub_x;
2167 base_offs_x -= sub_x;
2185 this->child = std::move(child);
2186 if (this->child !=
nullptr) this->child->parent =
this;
2199 if (this->
child ==
nullptr) {
2200 this->
child = std::make_unique<NWidgetVertical>();
2202 nwid->parent = this->
child.get();
2203 this->
child->Add(std::move(nwid));
2218 if (this->
child ==
nullptr) {
2219 this->
child = std::make_unique<NWidgetVertical>();
2221 this->
child->parent =
this;
2222 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2237 if (this->
child ==
nullptr) {
2238 this->
child = std::make_unique<NWidgetVertical>();
2240 this->
child->parent =
this;
2241 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2244void NWidgetBackground::AdjustPaddingForZoom()
2246 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2247 NWidgetCore::AdjustPaddingForZoom();
2252 if (this->
child !=
nullptr) {
2253 this->
child->SetupSmallestSize(w);
2263 if (w ==
nullptr)
return;
2277 this->
child->padding = WidgetDimensions::scaled.
bevel;
2282 this->ApplyAspectRatio();
2292 d =
maxdim(d, background);
2294 if (this->
index >= 0) {
2296 switch (this->
type) {
2297 default: NOT_REACHED();
2307 this->
fill_x = fill.width;
2308 this->
fill_y = fill.height;
2311 this->ApplyAspectRatio();
2319 if (this->
child !=
nullptr) {
2320 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2321 uint width = given_width - this->
child->padding.Horizontal();
2322 uint height = given_height - this->
child->padding.Vertical();
2323 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2329 if (this->
index >= 0) widget_lookup[this->
index] =
this;
2330 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2337 Rect r = this->GetCurrentRect();
2340 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2342 switch (this->
type) {
2362 if (this->
child !=
nullptr) this->
child->Draw(w);
2368 DrawOutline(w,
this);
2376 if (nwid ==
nullptr) nwid =
this;
2385 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2397 this->ApplyAspectRatio();
2418 DrawOutline(w,
this);
2439 if (vp !=
nullptr) {
2463 return (pos < 0 || pos >= this->
GetCount()) ? Scrollbar::npos :
pos;
2482 int new_pos = list_position;
2552 const int count = sb.
GetCount() * resize_step;
2553 const int position = sb.
GetPosition() * resize_step;
2557 r.bottom = r.top + count;
2561 r.right += position;
2562 r.left = r.right - count;
2565 r.right = r.left + count;
2582 switch (this->
type) {
2586 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2592 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2595 default: NOT_REACHED();
2604 switch (this->
type) {
2606 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2610 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2613 default: NOT_REACHED();
2624 Rect r = this->GetCurrentRect();
2627 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2643 DrawOutline(w,
this);
2646 void NWidgetScrollbar::InvalidateDimensionCache()
2652 Dimension NWidgetScrollbar::GetVerticalDimension()
2662 Dimension NWidgetScrollbar::GetHorizontalDimension()
2703NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp, colour, index, 1, 1, data, tip)
2712 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2716 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2722 if (colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2734 case NWID_PUSHBUTTON_DROPDOWN:
2741 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2753 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2815 switch (this->
type) {
2884 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2951 case NWID_PUSHBUTTON_DROPDOWN: {
2973 this->
fill_x = fill.width;
2974 this->
fill_y = fill.height;
2977 this->ApplyAspectRatio();
2989 new_dpi.left += this->
pos_x;
2990 new_dpi.top += this->
pos_y;
2994 Rect r = this->GetCurrentRect();
2997 switch (this->
type) {
3000 if (this->
index == -1 && _draw_widget_outlines) {
3029 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
3030 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
3031 default: NOT_REACHED();
3048 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3053 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3059 DrawCaption(r, this->
colour, w->
owner, this->text_colour, this->GetString(), this->align, this->text_size);
3092 case NWID_PUSHBUTTON_DROPDOWN:
3106 DrawOutline(w,
this);
3119 int button_width = this->
pos_x + this->
current_x - NWidgetLeaf::dropdown_dimension.width;
3120 return pt.x < button_width;
3122 int button_left = this->
pos_x + NWidgetLeaf::dropdown_dimension.width;
3123 return pt.x >= button_left;
3147 switch (nwid.
type) {
3150 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3151 assert(nwid.u.
xy.x >= 0 && nwid.u.
xy.y >= 0);
3158 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3159 assert(nwid.u.
xy.x >= 0 && nwid.u.
xy.y >= 0);
3166 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3174 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3181 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3188 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3195 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3202 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3213 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3224 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3230 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3236 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3258 switch (nwid.
type) {
3259 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3297static 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)
3301 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3306 if (dest ==
nullptr)
return nwid_begin;
3337static 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)
3343 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3345 while (nwid_begin != nwid_end) {
3346 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3347 bool fill_sub =
false;
3348 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3351 if (sub_widget ==
nullptr)
break;
3359 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3360 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3361 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3362 parent = std::move(sub_widget);
3367 if (nwid_begin == nwid_end)
return nwid_begin;
3369 assert(nwid_begin < nwid_end);
3371 return std::next(nwid_begin);
3381std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3383 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3384 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3386 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3388 return std::move(container);
3402 auto nwid_begin = std::begin(nwid_parts);
3403 auto nwid_end = std::end(nwid_parts);
3405 *shade_select =
nullptr;
3408 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3410 assert(nwid !=
nullptr);
3414 auto root = std::make_unique<NWidgetVertical>();
3415 root->Add(std::move(nwid));
3416 if (nwid_begin == nwid_end)
return root;
3420 auto shade_stack = std::make_unique<NWidgetStacked>(-1);
3421 *shade_select = shade_stack.get();
3423 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3424 root->Add(std::move(shade_stack));
3429 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3444 assert(max_length >= 1);
3445 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3446 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3453 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3455 if (hor_length == max_length) {
3456 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3457 vert->Add(std::move(hor));
3461 if (hor ==
nullptr) {
3462 hor = std::make_unique<NWidgetHorizontal>();
3466 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3467 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3468 panel->SetFill(1, 1);
3469 if (resizable) panel->SetResize(1, 0);
3470 panel->SetToolTip(button_tooltip);
3471 hor->Add(std::move(panel));
3474 if (vert ==
nullptr)
return hor;
3476 if (hor_length > 0 && hor_length < max_length) {
3478 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3480 if (resizable) spc->SetResize(1, 0);
3481 hor->Add(std::move(spc));
3483 if (hor !=
nullptr) vert->Add(std::move(hor));
Class for backupping variables and making sure they are restored later.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr bool Test(Tenum value) const
Test if the enum value is set.
Base class that provides memory initialization on dynamically created objects.
Colours _company_colours[MAX_COMPANIES]
NOSAVE: can be determined from company structs.
Functions related to companies.
Owner
Enum for all companies/owners.
@ MAX_COMPANIES
Maximum number of 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 GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
Draw the outline of a Rect.
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 GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
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.
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
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.
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
static const uint8_t PC_WHITE
White palette colour.
static const uint8_t PC_BLACK
Black 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.
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 const uint8_t _string_colourmap[17]
Colour mapping for TextColour.
TextDirection _current_text_dir
Text direction of the currently selected language.
Functions related to OTTD's 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.
Coordinates of a point in 2D.
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.
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.
virtual void SetStringParameters(WidgetID widget) const
Initialize string parameters for a widget.
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
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?
ViewportData * viewport
Pointer to viewport data, if present.
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.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_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.
@ ZOOM_LVL_NORMAL
The normal zoom level.