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;
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;
778 if (!widget->IsHighlighted())
continue;
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);
803 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
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()
970void NWidgetBase::AdjustPaddingForZoom()
1006 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1009void NWidgetResizeBase::AdjustPaddingForZoom()
1015 NWidgetBase::AdjustPaddingForZoom();
1039 this->min_x = std::max(this->min_x,
min_x);
1040 this->min_y = std::max(this->min_y,
min_y);
1092 d.height *= max_lines;
1108 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1109 this->min_x =
min_x;
1110 this->min_y =
min_y;
1122 if (
min_y == this->min_y)
return false;
1123 this->min_y =
min_y;
1244 this->align =
align;
1272 if (this->
type == tp)
return this;
1273 for (
const auto &child_wid : this->
children) {
1275 if (nwid !=
nullptr)
return nwid;
1280void NWidgetContainer::AdjustPaddingForZoom()
1282 for (
const auto &child_wid : this->
children) {
1283 child_wid->AdjustPaddingForZoom();
1285 NWidgetBase::AdjustPaddingForZoom();
1294 assert(wid !=
nullptr);
1296 this->
children.push_back(std::move(wid));
1302 for (
const auto &child_wid : this->
children) {
1303 child_wid->FillWidgetLookup(widget_lookup);
1309 for (
const auto &child_wid : this->
children) {
1313 DrawOutline(w,
this);
1320 for (
const auto &child_wid : this->
children) {
1322 if (nwid !=
nullptr)
return nwid;
1340 this->
fill_x = fill.width;
1341 this->
fill_y = fill.height;
1344 this->ApplyAspectRatio();
1355 for (
const auto &child_wid : this->
children) {
1356 child_wid->SetupSmallestSize(w);
1358 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1359 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1360 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1361 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1364 this->ApplyAspectRatio();
1375 for (
const auto &child_wid : this->
children) {
1376 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1377 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1378 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1380 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1381 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1382 uint child_pos_y = child_wid->padding.top;
1384 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1404 DrawOutline(w,
this);
1450 for (
const auto &child_wid : this->
children) {
1451 child_wid->SetupSmallestSize(w);
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);
1491void NWidgetPIPContainer::AdjustPaddingForZoom()
1496 NWidgetContainer::AdjustPaddingForZoom();
1547 uint max_vert_fill = 0;
1548 for (
const auto &child_wid : this->
children) {
1549 child_wid->SetupSmallestSize(w);
1550 longest = std::max(longest, child_wid->smallest_x);
1551 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1552 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1553 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1557 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1560 for (
const auto &child_wid : this->
children) {
1561 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1562 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1563 if (step_size > 1 && child_height < cur_height) {
1564 uint remainder = (cur_height - child_height) % step_size;
1565 if (remainder > 0) {
1566 cur_height += step_size - remainder;
1567 assert(cur_height < max_smallest);
1576 for (
const auto &child_wid : this->
children) {
1577 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1578 child_wid->ApplyAspectRatio();
1579 longest = std::max(longest, child_wid->smallest_x);
1582 for (
const auto &child_wid : this->
children) {
1583 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1587 for (
const auto &child_wid : this->
children) {
1588 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1589 if (child_wid->fill_x > 0) {
1590 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1592 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1594 if (child_wid->resize_x > 0) {
1610 for (
const auto &child_wid : this->
children) {
1611 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1630 int num_changing_childs = 0;
1631 uint biggest_stepsize = 0;
1632 for (
const auto &child_wid : this->
children) {
1633 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1636 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1638 child_wid->current_x = child_wid->smallest_x;
1641 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1642 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1647 for (
const auto &child_wid : this->
children) {
1648 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1649 if (hor_step == biggest_stepsize) {
1650 num_changing_childs++;
1656 while (biggest_stepsize > 0) {
1657 uint next_biggest_stepsize = 0;
1658 for (
const auto &child_wid : this->
children) {
1659 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1660 if (hor_step > biggest_stepsize)
continue;
1661 if (hor_step == biggest_stepsize) {
1662 uint increment = additional_length / num_changing_childs;
1663 num_changing_childs--;
1664 if (hor_step > 1) increment -= increment % hor_step;
1665 child_wid->current_x = child_wid->smallest_x + increment;
1666 additional_length -= increment;
1669 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1671 biggest_stepsize = next_biggest_stepsize;
1675 for (
const auto &child_wid : this->
children) {
1676 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1677 if (hor_step == biggest_stepsize) {
1678 num_changing_childs++;
1683 assert(num_changing_childs == 0);
1688 if (additional_length > 0) {
1699 uint position = rtl ? this->
current_x - pre : pre;
1700 for (
const auto &child_wid : this->
children) {
1701 uint child_width = child_wid->current_x;
1702 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1703 uint child_y = y + child_wid->padding.top;
1705 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1706 if (child_wid->current_x != 0) {
1707 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1708 position = rtl ? position - padded_child_width : position + padded_child_width;
1730 uint max_hor_fill = 0;
1731 for (
const auto &child_wid : this->
children) {
1732 child_wid->SetupSmallestSize(w);
1733 highest = std::max(highest, child_wid->smallest_y);
1734 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1735 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1736 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1740 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1743 for (
const auto &child_wid : this->
children) {
1744 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1745 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1746 if (step_size > 1 && child_width < cur_width) {
1747 uint remainder = (cur_width - child_width) % step_size;
1748 if (remainder > 0) {
1749 cur_width += step_size - remainder;
1750 assert(cur_width < max_smallest);
1759 for (
const auto &child_wid : this->
children) {
1760 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1761 child_wid->ApplyAspectRatio();
1762 highest = std::max(highest, child_wid->smallest_y);
1765 for (
const auto &child_wid : this->
children) {
1766 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1770 for (
const auto &child_wid : this->
children) {
1771 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1772 if (child_wid->fill_y > 0) {
1773 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1775 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1777 if (child_wid->resize_y > 0) {
1793 for (
const auto &child_wid : this->
children) {
1794 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1804 int num_changing_childs = 0;
1805 uint biggest_stepsize = 0;
1806 for (
const auto &child_wid : this->
children) {
1807 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1808 if (vert_step > 0) {
1810 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1812 child_wid->current_y = child_wid->smallest_y;
1815 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1816 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1821 for (
const auto &child_wid : this->
children) {
1822 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1823 if (vert_step == biggest_stepsize) {
1824 num_changing_childs++;
1830 while (biggest_stepsize > 0) {
1831 uint next_biggest_stepsize = 0;
1832 for (
const auto &child_wid : this->
children) {
1833 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1834 if (vert_step > biggest_stepsize)
continue;
1835 if (vert_step == biggest_stepsize) {
1836 uint increment = additional_length / num_changing_childs;
1837 num_changing_childs--;
1838 if (vert_step > 1) increment -= increment % vert_step;
1839 child_wid->current_y = child_wid->smallest_y + increment;
1840 additional_length -= increment;
1843 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1845 biggest_stepsize = next_biggest_stepsize;
1849 for (
const auto &child_wid : this->
children) {
1850 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1851 if (vert_step == biggest_stepsize) {
1852 num_changing_childs++;
1857 assert(num_changing_childs == 0);
1862 if (additional_length > 0) {
1873 uint position = pre;
1874 for (
const auto &child_wid : this->
children) {
1875 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1876 uint child_height = child_wid->current_y;
1878 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1879 if (child_wid->current_y != 0) {
1880 position += child_height + child_wid->padding.Vertical() + inter;
1900 this->ApplyAspectRatio();
1911 DrawOutline(w,
this);
1932 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1948 this->count =
count;
1950 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1986 assert(this->
children.size() == 1);
1988 this->
children.front()->SetupSmallestSize(w);
1991 Dimension size = {this->
children.front()->smallest_x + padding.width, this->
children.front()->smallest_y + padding.height};
1999 this->
fill_x = fill.width;
2000 this->
fill_y = fill.height;
2003 this->ApplyAspectRatio();
2035 int start_x, start_y, base_offs_x, base_offs_y;
2040 int widget_col = (rtl ?
2051 assert(child !=
nullptr);
2075 assert(child !=
nullptr);
2076 int start_x, start_y, base_offs_x, base_offs_y;
2079 int offs_y = base_offs_y;
2080 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2082 if (offs_y + child->
smallest_y <= 0)
continue;
2083 if (offs_y >= (
int)this->
current_y)
break;
2088 int offs_x = base_offs_x;
2091 if (offs_x + child->
smallest_x <= 0)
continue;
2092 if (offs_x >= (
int)this->
current_x)
continue;
2105 DrawOutline(w,
this);
2121 if (this->
sb !=
nullptr) {
2129 base_offs_x += sub_x;
2131 base_offs_x -= sub_x;
2149 this->child = std::move(child);
2150 if (this->child !=
nullptr) this->child->parent =
this;
2163 if (this->
child ==
nullptr) {
2164 this->
child = std::make_unique<NWidgetVertical>();
2167 this->
child->Add(std::move(nwid));
2182 if (this->
child ==
nullptr) {
2183 this->
child = std::make_unique<NWidgetVertical>();
2185 this->
child->parent =
this;
2186 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2201 if (this->
child ==
nullptr) {
2202 this->
child = std::make_unique<NWidgetVertical>();
2204 this->
child->parent =
this;
2205 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2208void NWidgetBackground::AdjustPaddingForZoom()
2210 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2211 NWidgetCore::AdjustPaddingForZoom();
2216 if (this->
child !=
nullptr) {
2217 this->
child->SetupSmallestSize(w);
2227 if (w ==
nullptr)
return;
2230 std::string text = GetStringForWidget(w,
this);
2243 this->
child->padding = WidgetDimensions::scaled.
bevel;
2248 this->ApplyAspectRatio();
2255 std::string text = GetStringForWidget(w,
this);
2256 if (!text.empty()) {
2259 d =
maxdim(d, background);
2262 if (this->
index >= 0) {
2264 switch (this->
type) {
2265 default: NOT_REACHED();
2275 this->
fill_x = fill.width;
2276 this->
fill_y = fill.height;
2279 this->ApplyAspectRatio();
2287 if (this->
child !=
nullptr) {
2288 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2289 uint width = given_width - this->
child->padding.Horizontal();
2290 uint height = given_height - this->
child->padding.Vertical();
2291 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2298 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2305 Rect r = this->GetCurrentRect();
2308 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2310 switch (this->
type) {
2328 if (this->
child !=
nullptr) this->
child->Draw(w);
2334 DrawOutline(w,
this);
2342 if (nwid ==
nullptr) nwid =
this;
2351 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2363 this->ApplyAspectRatio();
2380 if (this->
disp_flags.
Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2384 DrawOutline(w,
this);
2404 if (w->
viewport ==
nullptr)
return;
2429 return (pos < 0 || pos >= this->
GetCount()) ? Scrollbar::npos :
pos;
2448 int new_pos = list_position;
2518 const int count = sb.
GetCount() * resize_step;
2519 const int position = sb.
GetPosition() * resize_step;
2523 r.bottom = r.top + count;
2527 r.right += position;
2528 r.left = r.right - count;
2531 r.right = r.left + count;
2548 switch (this->
type) {
2552 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2558 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2561 default: NOT_REACHED();
2570 switch (this->
type) {
2572 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2576 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2579 default: NOT_REACHED();
2590 Rect r = this->GetCurrentRect();
2593 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2609 DrawOutline(w,
this);
2612 void NWidgetScrollbar::InvalidateDimensionCache()
2618 Dimension NWidgetScrollbar::GetVerticalDimension()
2628 Dimension NWidgetScrollbar::GetHorizontalDimension()
2669NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp, colour, index, 1, 1, data, tip)
2678 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2682 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2688 if (colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2703 case NWID_PUSHBUTTON_DROPDOWN:
2710 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2722 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2784 switch (this->
type) {
2853 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2886 padding.height + std::max(di.height, dt.height)
2937 case NWID_PUSHBUTTON_DROPDOWN: {
2958 this->
fill_x = fill.width;
2959 this->
fill_y = fill.height;
2962 this->ApplyAspectRatio();
2974 new_dpi.left += this->
pos_x;
2975 new_dpi.top += this->
pos_y;
2979 Rect r = this->GetCurrentRect();
2982 switch (this->
type) {
2985 if (this->
index == -1 && _draw_widget_outlines) {
2996 Colours button_colour = this->
widget_data.alternate_colour;
2997 if (button_colour == INVALID_COLOUR) button_colour = this->
colour;
2998 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3026 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
3027 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
3028 default: NOT_REACHED();
3043 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3048 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3085 case NWID_PUSHBUTTON_DROPDOWN:
3099 DrawOutline(w,
this);
3112 int button_width = this->
pos_x + this->
current_x - NWidgetLeaf::dropdown_dimension.width;
3113 return pt.
x < button_width;
3115 int button_left = this->
pos_x + NWidgetLeaf::dropdown_dimension.width;
3116 return pt.
x >= button_left;
3140 switch (nwid.
type) {
3143 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3144 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3151 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3152 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3159 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3167 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3174 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3181 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3188 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3195 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3206 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3217 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3223 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3229 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3251 switch (nwid.
type) {
3252 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3290static 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)
3294 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3299 if (dest ==
nullptr)
return nwid_begin;
3330static 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)
3336 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3338 while (nwid_begin != nwid_end) {
3339 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3340 bool fill_sub =
false;
3341 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3344 if (sub_widget ==
nullptr)
break;
3352 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3353 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3354 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3355 parent = std::move(sub_widget);
3360 if (nwid_begin == nwid_end)
return nwid_begin;
3362 assert(nwid_begin < nwid_end);
3364 return std::next(nwid_begin);
3374std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3376 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3377 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3379 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3381 return std::move(container);
3395 auto nwid_begin = std::begin(nwid_parts);
3396 auto nwid_end = std::end(nwid_parts);
3398 *shade_select =
nullptr;
3401 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3403 assert(nwid !=
nullptr);
3407 auto root = std::make_unique<NWidgetVertical>();
3408 root->Add(std::move(nwid));
3409 if (nwid_begin == nwid_end)
return root;
3413 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3414 *shade_select = shade_stack.get();
3416 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3417 root->Add(std::move(shade_stack));
3422 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3437 assert(max_length >= 1);
3438 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3439 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3446 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3448 if (hor_length == max_length) {
3449 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3450 vert->Add(std::move(hor));
3454 if (hor ==
nullptr) {
3455 hor = std::make_unique<NWidgetHorizontal>();
3459 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3460 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3461 panel->SetFill(1, 1);
3462 if (resizable) panel->SetResize(1, 0);
3463 panel->SetToolTip(button_tooltip);
3464 hor->Add(std::move(panel));
3467 if (vert ==
nullptr)
return hor;
3469 if (hor_length > 0 && hor_length < max_length) {
3471 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3473 if (resizable) spc->SetResize(1, 0);
3474 hor->Add(std::move(spc));
3476 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.
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.
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.