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));
86 uint x = std::max(d.width, d.height);
146 case SA_RIGHT: p.
x = r.right + 1 - d.width;
break;
147 default: NOT_REACHED();
150 case SA_TOP: p.
y = r.top;
break;
152 case SA_BOTTOM: p.
y = r.bottom + 1 - d.height;
break;
153 default: NOT_REACHED();
170 int rev_base = mi + ma;
171 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
180 int height = ma + 1 - mi;
181 int slider_height = std::max(button_size, cap * height / count);
182 height -= slider_height;
185 ma = mi + slider_height - 1;
208 bool changed =
false;
213 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
216 button_size = NWidgetScrollbar::GetVerticalDimension().height;
218 if (pos < mi + button_size) {
221 if (_scroller_click_timeout <= 1) {
222 _scroller_click_timeout = 3;
226 }
else if (pos >= ma - button_size) {
230 if (_scroller_click_timeout <= 1) {
231 _scroller_click_timeout = 3;
240 }
else if (pos > end) {
243 _scrollbar_start_pos = start - mi - button_size;
244 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
246 _cursorpos_drag_start = _cursor.
pos;
280 assert(scrollbar !=
nullptr);
312 assert(colour < COLOUR_END);
320 Rect outer = {left, top, right, bottom};
324 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
325 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
326 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
327 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
330 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
331 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
332 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
333 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
334 interior = medium_dark;
337 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
347 d.height -= offset.
y;
367 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
368 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
398 DrawString(r_text.left, r_text.right, p.
y, text, text_colour, align,
false, fs);
412 if (str.empty())
return;
416 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
429 if (str.empty())
return;
433 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
461static inline void DrawMatrix(
const Rect &r, Colours colour,
bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
466 if (num_columns == 0) {
467 column_width = resize_x;
468 num_columns = r.
Width() / column_width;
470 column_width = r.
Width() / num_columns;
475 row_height = resize_y;
476 num_rows = r.
Height() / row_height;
478 row_height = r.
Height() / num_rows;
484 for (
int ctr = num_columns; ctr > 1; ctr--) {
490 for (
int ctr = num_rows; ctr > 1; ctr--) {
498 for (
int ctr = num_columns; ctr > 1; ctr--) {
504 for (
int ctr = num_rows; ctr > 1; ctr--) {
521 int height = NWidgetScrollbar::GetVerticalDimension().height;
536 int left = r.left + r.
Width() * 3 / 11;
537 int right = r.left + r.
Width() * 8 / 11;
562 int width = NWidgetScrollbar::GetHorizontalDimension().width;
576 int top = r.top + r.
Height() * 3 / 11;
577 int bottom = r.top + r.
Height() * 8 / 11;
636 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
637 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
640 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
641 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
644 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
645 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
700static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
704 }
else if (clicked) {
721 d.height -= offset.y;
738 bool company_owned = owner < MAX_COMPANIES;
748 if (str.empty())
return;
796 if (!widget->IsHighlighted())
continue;
798 Rect outer = widget->GetCurrentRect();
803 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
804 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
805 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
806 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
821 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
824 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
835 return NWidgetScrollbar::GetVerticalDimension().width + 1;
838bool _draw_widget_outlines;
935 if (this->
index >= 0) widget_lookup[this->
index] =
this;
971 return (this->
type == tp) ? this :
nullptr;
974void NWidgetBase::ApplyAspectRatio()
988void NWidgetBase::AdjustPaddingForZoom()
1024 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1027void NWidgetResizeBase::AdjustPaddingForZoom()
1037 NWidgetBase::AdjustPaddingForZoom();
1068 this->min_x = std::max(this->min_x,
min_x);
1069 this->min_y = std::max(this->min_y,
min_y);
1120 d.height *= max_lines;
1136 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1137 this->min_x =
min_x;
1138 this->min_y =
min_y;
1150 if (
min_y == this->min_y)
return false;
1151 this->min_y =
min_y;
1272 this->align =
align;
1300 if (this->
type == tp)
return this;
1301 for (
const auto &child_wid : this->
children) {
1303 if (nwid !=
nullptr)
return nwid;
1308void NWidgetContainer::AdjustPaddingForZoom()
1310 for (
const auto &child_wid : this->
children) {
1311 child_wid->AdjustPaddingForZoom();
1313 NWidgetBase::AdjustPaddingForZoom();
1322 assert(wid !=
nullptr);
1324 this->
children.push_back(std::move(wid));
1330 for (
const auto &child_wid : this->
children) {
1331 child_wid->FillWidgetLookup(widget_lookup);
1337 for (
const auto &child_wid : this->
children) {
1341 DrawOutline(w,
this);
1348 for (
const auto &child_wid : this->
children) {
1350 if (nwid !=
nullptr)
return nwid;
1368 this->
fill_x = fill.width;
1369 this->
fill_y = fill.height;
1372 this->ApplyAspectRatio();
1383 for (
const auto &child_wid : this->
children) {
1384 child_wid->SetupSmallestSize(w);
1386 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1387 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1388 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1389 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1392 this->ApplyAspectRatio();
1403 for (
const auto &child_wid : this->
children) {
1404 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1405 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1406 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1408 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1409 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1410 uint child_pos_y = child_wid->padding.top;
1412 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1432 DrawOutline(w,
this);
1478 for (
const auto &child_wid : this->
children) {
1479 child_wid->SetupSmallestSize(w);
1481 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1482 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1483 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1484 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1487 this->ApplyAspectRatio();
1496 for (
const auto &child_wid : this->
children) {
1497 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1498 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1499 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1501 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1502 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1503 uint child_pos_y = child_wid->padding.top;
1505 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1512 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1516 DrawOutline(w,
this);
1519void NWidgetPIPContainer::AdjustPaddingForZoom()
1524 NWidgetContainer::AdjustPaddingForZoom();
1575 uint max_vert_fill = 0;
1576 for (
const auto &child_wid : this->
children) {
1577 child_wid->SetupSmallestSize(w);
1578 longest = std::max(longest, child_wid->smallest_x);
1579 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1580 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1581 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1585 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1588 for (
const auto &child_wid : this->
children) {
1589 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1590 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1591 if (step_size > 1 && child_height < cur_height) {
1592 uint remainder = (cur_height - child_height) % step_size;
1593 if (remainder > 0) {
1594 cur_height += step_size - remainder;
1595 assert(cur_height < max_smallest);
1604 for (
const auto &child_wid : this->
children) {
1605 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1606 child_wid->ApplyAspectRatio();
1607 longest = std::max(longest, child_wid->smallest_x);
1610 for (
const auto &child_wid : this->
children) {
1611 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1615 for (
const auto &child_wid : this->
children) {
1616 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1617 if (child_wid->fill_x > 0) {
1618 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1620 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1622 if (child_wid->resize_x > 0) {
1638 for (
const auto &child_wid : this->
children) {
1639 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1658 int num_changing_childs = 0;
1659 uint biggest_stepsize = 0;
1660 for (
const auto &child_wid : this->
children) {
1661 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1664 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1666 child_wid->current_x = child_wid->smallest_x;
1669 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1670 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
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++;
1684 while (biggest_stepsize > 0) {
1685 uint next_biggest_stepsize = 0;
1686 for (
const auto &child_wid : this->
children) {
1687 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1688 if (hor_step > biggest_stepsize)
continue;
1689 if (hor_step == biggest_stepsize) {
1690 uint increment = additional_length / num_changing_childs;
1691 num_changing_childs--;
1692 if (hor_step > 1) increment -= increment % hor_step;
1693 child_wid->current_x = child_wid->smallest_x + increment;
1694 additional_length -= increment;
1697 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1699 biggest_stepsize = next_biggest_stepsize;
1703 for (
const auto &child_wid : this->
children) {
1704 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1705 if (hor_step == biggest_stepsize) {
1706 num_changing_childs++;
1711 assert(num_changing_childs == 0);
1716 if (additional_length > 0) {
1727 uint position = rtl ? this->
current_x - pre : pre;
1728 for (
const auto &child_wid : this->
children) {
1729 uint child_width = child_wid->current_x;
1730 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1731 uint child_y = y + child_wid->padding.top;
1733 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1734 if (child_wid->current_x != 0) {
1735 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1736 position = rtl ? position - padded_child_width : position + padded_child_width;
1758 uint max_hor_fill = 0;
1759 for (
const auto &child_wid : this->
children) {
1760 child_wid->SetupSmallestSize(w);
1761 highest = std::max(highest, child_wid->smallest_y);
1762 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1763 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1764 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1768 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1771 for (
const auto &child_wid : this->
children) {
1772 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1773 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1774 if (step_size > 1 && child_width < cur_width) {
1775 uint remainder = (cur_width - child_width) % step_size;
1776 if (remainder > 0) {
1777 cur_width += step_size - remainder;
1778 assert(cur_width < max_smallest);
1787 for (
const auto &child_wid : this->
children) {
1788 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1789 child_wid->ApplyAspectRatio();
1790 highest = std::max(highest, child_wid->smallest_y);
1793 for (
const auto &child_wid : this->
children) {
1794 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1798 for (
const auto &child_wid : this->
children) {
1799 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1800 if (child_wid->fill_y > 0) {
1801 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1803 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1805 if (child_wid->resize_y > 0) {
1821 for (
const auto &child_wid : this->
children) {
1822 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1832 int num_changing_childs = 0;
1833 uint biggest_stepsize = 0;
1834 for (
const auto &child_wid : this->
children) {
1835 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1836 if (vert_step > 0) {
1838 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1840 child_wid->current_y = child_wid->smallest_y;
1843 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1844 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
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++;
1858 while (biggest_stepsize > 0) {
1859 uint next_biggest_stepsize = 0;
1860 for (
const auto &child_wid : this->
children) {
1861 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1862 if (vert_step > biggest_stepsize)
continue;
1863 if (vert_step == biggest_stepsize) {
1864 uint increment = additional_length / num_changing_childs;
1865 num_changing_childs--;
1866 if (vert_step > 1) increment -= increment % vert_step;
1867 child_wid->current_y = child_wid->smallest_y + increment;
1868 additional_length -= increment;
1871 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1873 biggest_stepsize = next_biggest_stepsize;
1877 for (
const auto &child_wid : this->
children) {
1878 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1879 if (vert_step == biggest_stepsize) {
1880 num_changing_childs++;
1885 assert(num_changing_childs == 0);
1890 if (additional_length > 0) {
1901 uint position = pre;
1902 for (
const auto &child_wid : this->
children) {
1903 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1904 uint child_height = child_wid->current_y;
1906 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1907 if (child_wid->current_y != 0) {
1908 position += child_height + child_wid->padding.Vertical() + inter;
1928 this->ApplyAspectRatio();
1939 DrawOutline(w,
this);
1960 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1976 this->count =
count;
1978 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
2014 assert(this->
children.size() == 1);
2016 this->
children.front()->SetupSmallestSize(w);
2019 Dimension size = {this->
children.front()->smallest_x + padding.width, this->
children.front()->smallest_y + padding.height};
2027 this->
fill_x = fill.width;
2028 this->
fill_y = fill.height;
2031 this->ApplyAspectRatio();
2063 int start_x, start_y, base_offs_x, base_offs_y;
2068 int widget_col = (rtl ?
2079 assert(child !=
nullptr);
2103 assert(child !=
nullptr);
2104 int start_x, start_y, base_offs_x, base_offs_y;
2107 int offs_y = base_offs_y;
2108 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2110 if (offs_y + child->
smallest_y <= 0)
continue;
2111 if (offs_y >= (
int)this->
current_y)
break;
2116 int offs_x = base_offs_x;
2119 if (offs_x + child->
smallest_x <= 0)
continue;
2120 if (offs_x >= (
int)this->
current_x)
continue;
2133 DrawOutline(w,
this);
2149 if (this->
sb !=
nullptr) {
2157 base_offs_x += sub_x;
2159 base_offs_x -= sub_x;
2177 this->child = std::move(child);
2178 if (this->child !=
nullptr) this->child->parent =
this;
2191 if (this->
child ==
nullptr) {
2192 this->
child = std::make_unique<NWidgetVertical>();
2195 this->
child->Add(std::move(nwid));
2210 if (this->
child ==
nullptr) {
2211 this->
child = std::make_unique<NWidgetVertical>();
2213 this->
child->parent =
this;
2214 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2229 if (this->
child ==
nullptr) {
2230 this->
child = std::make_unique<NWidgetVertical>();
2232 this->
child->parent =
this;
2233 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2236void NWidgetBackground::AdjustPaddingForZoom()
2238 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2239 NWidgetCore::AdjustPaddingForZoom();
2244 if (this->
child !=
nullptr) {
2245 this->
child->SetupSmallestSize(w);
2255 if (w ==
nullptr)
return;
2258 std::string text = GetStringForWidget(w,
this);
2271 this->
child->padding = WidgetDimensions::scaled.
bevel;
2276 this->ApplyAspectRatio();
2283 std::string text = GetStringForWidget(w,
this);
2284 if (!text.empty()) {
2287 d =
maxdim(d, background);
2290 if (this->
index >= 0) {
2292 switch (this->
type) {
2293 default: NOT_REACHED();
2303 this->
fill_x = fill.width;
2304 this->
fill_y = fill.height;
2307 this->ApplyAspectRatio();
2315 if (this->
child !=
nullptr) {
2316 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2317 uint width = given_width - this->
child->padding.Horizontal();
2318 uint height = given_height - this->
child->padding.Vertical();
2319 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2326 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2333 Rect r = this->GetCurrentRect();
2336 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2338 switch (this->
type) {
2356 if (this->
child !=
nullptr) this->
child->Draw(w);
2362 DrawOutline(w,
this);
2370 if (nwid ==
nullptr) nwid =
this;
2379 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2391 this->ApplyAspectRatio();
2408 if (this->
disp_flags.
Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2412 DrawOutline(w,
this);
2432 if (w->
viewport ==
nullptr)
return;
2457 return (pos < 0 || pos >= this->
GetCount()) ? Scrollbar::npos :
pos;
2476 int new_pos = list_position;
2546 const int count = sb.
GetCount() * resize_step;
2547 const int position = sb.
GetPosition() * resize_step;
2551 r.bottom = r.top + count;
2555 r.right += position;
2556 r.left = r.right - count;
2559 r.right = r.left + count;
2576 switch (this->
type) {
2580 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2586 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2589 default: NOT_REACHED();
2598 switch (this->
type) {
2600 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2604 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2607 default: NOT_REACHED();
2618 Rect r = this->GetCurrentRect();
2621 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2637 DrawOutline(w,
this);
2640 void NWidgetScrollbar::InvalidateDimensionCache()
2646 Dimension NWidgetScrollbar::GetVerticalDimension()
2656 Dimension NWidgetScrollbar::GetHorizontalDimension()
2697NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp, colour, index, 1, 1, data, tip)
2706 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2710 if (
colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2716 if (colour != INVALID_COLOUR) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2731 case NWID_PUSHBUTTON_DROPDOWN:
2738 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2750 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2812 switch (this->
type) {
2881 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2914 padding.height + std::max(di.height, dt.height)
2965 case NWID_PUSHBUTTON_DROPDOWN: {
2986 this->
fill_x = fill.width;
2987 this->
fill_y = fill.height;
2990 this->ApplyAspectRatio();
3002 new_dpi.left += this->
pos_x;
3003 new_dpi.top += this->
pos_y;
3007 Rect r = this->GetCurrentRect();
3010 switch (this->
type) {
3013 if (this->
index == -1 && _draw_widget_outlines) {
3024 Colours button_colour = this->
widget_data.alternate_colour;
3025 if (button_colour == INVALID_COLOUR) button_colour = this->
colour;
3026 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3054 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
3055 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
3056 default: NOT_REACHED();
3071 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3076 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3113 case NWID_PUSHBUTTON_DROPDOWN:
3127 DrawOutline(w,
this);
3140 int button_width = this->
pos_x + this->
current_x - NWidgetLeaf::dropdown_dimension.width;
3141 return pt.
x < button_width;
3143 int button_left = this->
pos_x + NWidgetLeaf::dropdown_dimension.width;
3144 return pt.
x >= button_left;
3168 switch (nwid.
type) {
3171 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3172 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3179 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3180 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3187 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3195 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TOOLBARSIZE requires NWidgetResizeBase");
3196 assert(nwid.u.
xy.
x >= 0);
3203 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3210 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3217 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3224 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3231 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3242 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3253 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3259 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3265 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3287 switch (nwid.
type) {
3288 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3326static 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)
3330 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3335 if (dest ==
nullptr)
return nwid_begin;
3366static 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)
3372 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3374 while (nwid_begin != nwid_end) {
3375 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3376 bool fill_sub =
false;
3377 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3380 if (sub_widget ==
nullptr)
break;
3388 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3389 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3390 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3391 parent = std::move(sub_widget);
3396 if (nwid_begin == nwid_end)
return nwid_begin;
3398 assert(nwid_begin < nwid_end);
3400 return std::next(nwid_begin);
3410std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3412 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3413 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3415 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3417 return std::move(container);
3431 auto nwid_begin = std::begin(nwid_parts);
3432 auto nwid_end = std::end(nwid_parts);
3434 *shade_select =
nullptr;
3437 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3439 assert(nwid !=
nullptr);
3443 auto root = std::make_unique<NWidgetVertical>();
3444 root->Add(std::move(nwid));
3445 if (nwid_begin == nwid_end)
return root;
3449 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3450 *shade_select = shade_stack.get();
3452 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3453 root->Add(std::move(shade_stack));
3458 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3473 assert(max_length >= 1);
3474 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3475 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3482 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3484 if (hor_length == max_length) {
3485 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3486 vert->Add(std::move(hor));
3490 if (hor ==
nullptr) {
3491 hor = std::make_unique<NWidgetHorizontal>();
3495 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3496 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3497 panel->SetFill(1, 1);
3498 if (resizable) panel->SetResize(1, 0);
3499 panel->SetToolTip(button_tooltip);
3500 hor->Add(std::move(panel));
3503 if (vert ==
nullptr)
return hor;
3505 if (hor_length > 0 && hor_length < max_length) {
3507 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3509 if (resizable) spc->SetResize(1, 0);
3510 hor->Add(std::move(spc));
3512 if (hor !=
nullptr) vert->Add(std::move(hor));
3523 assert(parent_window !=
nullptr);
3525 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.
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 GetSquareScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI, as a square.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
FontSize
Available font sizes.
@ FS_NORMAL
Index of the normal font in the font tables.
StringAlignment
How to align the to-be drawn text.
@ SA_TOP
Top align the text.
@ SA_LEFT
Left align the text.
@ SA_HOR_MASK
Mask for horizontal alignment.
@ SA_RIGHT
Right align the text (must be a single bit).
@ SA_HOR_CENTER
Horizontally center the text.
@ SA_VERT_MASK
Mask for vertical alignment.
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
@ SA_BOTTOM
Bottom align the text.
@ SA_CENTER
Center both horizontally and vertically.
@ SA_VERT_CENTER
Vertically center the text.
uint32_t PaletteID
The number of the palette.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
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.
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.