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));
88 uint x = std::max(d.width, d.height);
148 default: NOT_REACHED();
154 default: NOT_REACHED();
171 int rev_base = mi + ma;
172 int button_size = horizontal ? NWidgetScrollbar::GetHorizontalDimension().width : NWidgetScrollbar::GetVerticalDimension().height;
181 int height = ma + 1 - mi;
182 int slider_height = std::max(button_size, cap * height / count);
183 height -= slider_height;
186 ma = mi + slider_height - 1;
209 bool changed =
false;
214 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
217 button_size = NWidgetScrollbar::GetVerticalDimension().height;
219 if (pos < mi + button_size) {
222 if (_scroller_click_timeout <= 1) {
223 _scroller_click_timeout = 3;
227 }
else if (pos >= ma - button_size) {
231 if (_scroller_click_timeout <= 1) {
232 _scroller_click_timeout = 3;
241 }
else if (pos > end) {
244 _scrollbar_start_pos = start - mi - button_size;
245 _scrollbar_size = ma - mi - button_size * 2 - (end - start);
247 _cursorpos_drag_start = _cursor.pos;
281 assert(scrollbar !=
nullptr);
321 Rect outer = {left, top, right, bottom};
325 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
326 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
327 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
328 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
331 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
332 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
333 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
334 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
335 interior = medium_dark;
338 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
355 d.height -= offset.
y;
375 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
406 DrawString(r_text.left, r_text.right, p.
y, text, text_colour, align,
false, fs);
420 if (str.empty())
return;
424 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
437 if (str.empty())
return;
441 DrawString(r.left, r.right, p.
y, str, colour, align,
false, fs);
469static inline void DrawMatrix(
const Rect &r,
Colours colour,
bool clicked, uint32_t num_columns, uint32_t num_rows, uint resize_x, uint resize_y)
474 if (num_columns == 0) {
475 column_width = resize_x;
476 num_columns = r.
Width() / column_width;
478 column_width = r.
Width() / num_columns;
483 row_height = resize_y;
484 num_rows = r.
Height() / row_height;
486 row_height = r.
Height() / num_rows;
492 for (
int ctr = num_columns; ctr > 1; ctr--) {
498 for (
int ctr = num_rows; ctr > 1; ctr--) {
506 for (
int ctr = num_columns; ctr > 1; ctr--) {
512 for (
int ctr = num_rows; ctr > 1; ctr--) {
529 int height = NWidgetScrollbar::GetVerticalDimension().height;
544 int left = r.left + r.
Width() * 3 / 11;
545 int right = r.left + r.
Width() * 8 / 11;
570 int width = NWidgetScrollbar::GetHorizontalDimension().width;
584 int top = r.top + r.
Height() * 3 / 11;
585 int bottom = r.top + r.
Height() * 8 / 11;
644 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
645 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
648 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
649 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
652 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
653 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
712 }
else if (clicked) {
729 d.height -= offset.y;
746 bool company_owned = owner < MAX_COMPANIES;
756 if (str.empty())
return;
806 Rect outer = widget->GetCurrentRect();
811 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
812 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
813 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
814 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
830 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
841 return NWidgetScrollbar::GetVerticalDimension().width + 1;
844bool _draw_widget_outlines;
941 if (this->
index >= 0) widget_lookup[this->
index] =
this;
977 return (this->
type == tp) ? this :
nullptr;
980void NWidgetBase::ApplyAspectRatio()
1031 assert(x_ratio > 0 && y_ratio > 0);
1032 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
1076 this->min_x = std::max(this->min_x,
min_x);
1077 this->min_y = std::max(this->min_y,
min_y);
1128 d.height *= max_lines;
1144 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1145 this->min_x =
min_x;
1146 this->min_y =
min_y;
1158 if (
min_y == this->min_y)
return false;
1159 this->min_y =
min_y;
1280 this->align =
align;
1308 if (this->
type == tp)
return this;
1309 for (
const auto &child_wid : this->
children) {
1311 if (nwid !=
nullptr)
return nwid;
1318 for (
const auto &child_wid : this->
children) {
1319 child_wid->AdjustPaddingForZoom();
1330 assert(wid !=
nullptr);
1332 this->
children.push_back(std::move(wid));
1338 for (
const auto &child_wid : this->
children) {
1339 child_wid->FillWidgetLookup(widget_lookup);
1345 for (
const auto &child_wid : this->
children) {
1349 DrawOutline(w,
this);
1356 for (
const auto &child_wid : this->
children) {
1358 if (nwid !=
nullptr)
return nwid;
1376 this->
fill_x = fill.width;
1377 this->
fill_y = fill.height;
1380 this->ApplyAspectRatio();
1391 for (
const auto &child_wid : this->
children) {
1392 child_wid->SetupSmallestSize(w);
1394 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1395 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1396 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1397 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1400 this->ApplyAspectRatio();
1411 for (
const auto &child_wid : this->
children) {
1412 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1413 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1414 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1416 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1417 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1418 uint child_pos_y = child_wid->padding.top;
1420 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1440 DrawOutline(w,
this);
1467class NWidgetLayer :
public NWidgetContainer {
1486 for (
const auto &child_wid : this->
children) {
1487 child_wid->SetupSmallestSize(w);
1489 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1490 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1491 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1492 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1495 this->ApplyAspectRatio();
1504 for (
const auto &child_wid : this->
children) {
1505 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1506 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1507 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1509 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1510 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1511 uint child_pos_y = child_wid->padding.top;
1513 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1520 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1524 DrawOutline(w,
this);
1583 uint max_vert_fill = 0;
1584 for (
const auto &child_wid : this->
children) {
1585 child_wid->SetupSmallestSize(w);
1586 longest = std::max(longest, child_wid->smallest_x);
1588 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1589 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1593 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1596 for (
const auto &child_wid : this->children) {
1598 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1599 if (step_size > 1 && child_height < cur_height) {
1600 uint remainder = (cur_height - child_height) % step_size;
1601 if (remainder > 0) {
1602 cur_height += step_size - remainder;
1603 assert(cur_height < max_smallest);
1612 for (
const auto &child_wid : this->children) {
1613 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1614 child_wid->ApplyAspectRatio();
1615 longest = std::max(longest, child_wid->smallest_x);
1618 for (
const auto &child_wid : this->children) {
1619 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1623 for (
const auto &child_wid : this->children) {
1624 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1625 if (child_wid->fill_x > 0) {
1626 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1628 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1630 if (child_wid->resize_x > 0) {
1646 for (
const auto &child_wid : this->
children) {
1647 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1666 int num_changing_childs = 0;
1667 uint biggest_stepsize = 0;
1668 for (
const auto &child_wid : this->children) {
1669 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1672 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1674 child_wid->current_x = child_wid->smallest_x;
1677 uint vert_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetVerticalStepSize(sizing);
1678 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1683 for (
const auto &child_wid : this->children) {
1684 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1685 if (hor_step == biggest_stepsize) {
1686 num_changing_childs++;
1692 while (biggest_stepsize > 0) {
1693 uint next_biggest_stepsize = 0;
1694 for (
const auto &child_wid : this->children) {
1695 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1696 if (hor_step > biggest_stepsize)
continue;
1697 if (hor_step == biggest_stepsize) {
1698 uint increment = additional_length / num_changing_childs;
1699 num_changing_childs--;
1700 if (hor_step > 1) increment -= increment % hor_step;
1701 child_wid->current_x = child_wid->smallest_x + increment;
1702 additional_length -= increment;
1705 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1707 biggest_stepsize = next_biggest_stepsize;
1711 for (
const auto &child_wid : this->children) {
1712 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1713 if (hor_step == biggest_stepsize) {
1714 num_changing_childs++;
1719 assert(num_changing_childs == 0);
1724 if (additional_length > 0) {
1735 uint position = rtl ? this->
current_x - pre : pre;
1736 for (
const auto &child_wid : this->children) {
1737 uint child_width = child_wid->current_x;
1738 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1739 uint child_y = y + child_wid->padding.top;
1741 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1742 if (child_wid->current_x != 0) {
1743 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1744 position = rtl ? position - padded_child_width : position + padded_child_width;
1766 uint max_hor_fill = 0;
1767 for (
const auto &child_wid : this->
children) {
1768 child_wid->SetupSmallestSize(w);
1769 highest = std::max(highest, child_wid->smallest_y);
1771 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1772 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1776 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1779 for (
const auto &child_wid : this->children) {
1781 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1782 if (step_size > 1 && child_width < cur_width) {
1783 uint remainder = (cur_width - child_width) % step_size;
1784 if (remainder > 0) {
1785 cur_width += step_size - remainder;
1786 assert(cur_width < max_smallest);
1795 for (
const auto &child_wid : this->children) {
1796 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1797 child_wid->ApplyAspectRatio();
1798 highest = std::max(highest, child_wid->smallest_y);
1801 for (
const auto &child_wid : this->children) {
1802 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1806 for (
const auto &child_wid : this->children) {
1807 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1808 if (child_wid->fill_y > 0) {
1809 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1811 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1813 if (child_wid->resize_y > 0) {
1829 for (
const auto &child_wid : this->
children) {
1830 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1840 int num_changing_childs = 0;
1841 uint biggest_stepsize = 0;
1842 for (
const auto &child_wid : this->children) {
1843 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1844 if (vert_step > 0) {
1846 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1848 child_wid->current_y = child_wid->smallest_y;
1851 uint hor_step = (sizing ==
SizingType::Smallest) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1852 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1857 for (
const auto &child_wid : this->children) {
1858 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1859 if (vert_step == biggest_stepsize) {
1860 num_changing_childs++;
1866 while (biggest_stepsize > 0) {
1867 uint next_biggest_stepsize = 0;
1868 for (
const auto &child_wid : this->children) {
1869 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1870 if (vert_step > biggest_stepsize)
continue;
1871 if (vert_step == biggest_stepsize) {
1872 uint increment = additional_length / num_changing_childs;
1873 num_changing_childs--;
1874 if (vert_step > 1) increment -= increment % vert_step;
1875 child_wid->current_y = child_wid->smallest_y + increment;
1876 additional_length -= increment;
1879 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1881 biggest_stepsize = next_biggest_stepsize;
1885 for (
const auto &child_wid : this->children) {
1886 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1887 if (vert_step == biggest_stepsize) {
1888 num_changing_childs++;
1893 assert(num_changing_childs == 0);
1898 if (additional_length > 0) {
1910 for (
const auto &child_wid : this->children) {
1911 uint child_height = child_wid->current_y;
1912 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1913 uint child_y = y + (this->
bottom_up ? position - child_height - child_wid->padding.top : position + child_wid->padding.top);
1915 child_wid->AssignSizePosition(sizing, child_x, child_y, child_wid->current_x, child_height, rtl);
1916 if (child_wid->current_y != 0) {
1917 uint padded_child_height = child_height + child_wid->padding.Vertical() + inter;
1918 position = this->
bottom_up ? position - padded_child_height : position + padded_child_height;
1938 this->ApplyAspectRatio();
1949 DrawOutline(w,
this);
1970 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1975 this->
sb->ScrollTowards(vpos);
1986 this->count =
count;
1988 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1996 count *= (this->
sb->IsVertical() ? this->
children.front()->smallest_y : this->children.front()->smallest_x) + this->
pip_inter;
2000 this->
sb->SetCapacity(this->
sb->IsVertical() ? this->current_y : this->current_x);
2001 this->
sb->SetStepSize(this->
sb->IsVertical() ? this->widget_h : this->widget_w);
2024 assert(this->
children.size() == 1);
2026 this->
children.front()->SetupSmallestSize(w);
2037 this->
fill_x = fill.width;
2038 this->
fill_y = fill.height;
2041 this->ApplyAspectRatio();
2073 int start_x, start_y, base_offs_x, base_offs_y;
2078 int widget_col = (rtl ?
2089 assert(child !=
nullptr);
2113 assert(child !=
nullptr);
2114 int start_x, start_y, base_offs_x, base_offs_y;
2117 int offs_y = base_offs_y;
2118 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2120 if (offs_y + child->
smallest_y <= 0)
continue;
2121 if (offs_y >= (
int)this->
current_y)
break;
2126 int offs_x = base_offs_x;
2129 if (offs_x + child->
smallest_x <= 0)
continue;
2130 if (offs_x >= (
int)this->
current_x)
continue;
2143 DrawOutline(w,
this);
2159 if (this->
sb !=
nullptr) {
2160 if (this->
sb->IsVertical()) {
2161 start_y = this->
sb->GetPosition() / this->
widget_h;
2162 base_offs_y += -this->
sb->GetPosition() + start_y * this->
widget_h;
2164 start_x = this->
sb->GetPosition() / this->widget_w;
2165 int sub_x = this->
sb->GetPosition() - start_x * this->widget_w;
2167 base_offs_x += sub_x;
2169 base_offs_x -= sub_x;
2187 this->child = std::move(child);
2188 if (this->child !=
nullptr) this->child->parent =
this;
2201 if (this->
child ==
nullptr) {
2202 this->
child = std::make_unique<NWidgetVertical>();
2205 this->
child->Add(std::move(nwid));
2220 if (this->
child ==
nullptr) {
2221 this->
child = std::make_unique<NWidgetVertical>();
2223 this->
child->parent =
this;
2224 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2239 if (this->
child ==
nullptr) {
2240 this->
child = std::make_unique<NWidgetVertical>();
2242 this->
child->parent =
this;
2243 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2248 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2254 if (this->
child !=
nullptr) {
2255 this->
child->SetupSmallestSize(w);
2265 if (w ==
nullptr)
return;
2268 std::string text = GetStringForWidget(w,
this);
2286 this->ApplyAspectRatio();
2293 std::string text = GetStringForWidget(w,
this);
2294 if (!text.empty()) {
2297 d =
maxdim(d, background);
2300 if (this->
index >= 0) {
2302 switch (this->
type) {
2303 default: NOT_REACHED();
2313 this->
fill_x = fill.width;
2314 this->
fill_y = fill.height;
2317 this->ApplyAspectRatio();
2325 if (this->
child !=
nullptr) {
2326 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2327 uint width = given_width - this->
child->padding.Horizontal();
2328 uint height = given_height - this->
child->padding.Vertical();
2329 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2336 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2343 Rect r = this->GetCurrentRect();
2346 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2348 switch (this->
type) {
2366 if (this->
child !=
nullptr) this->
child->Draw(w);
2372 DrawOutline(w,
this);
2379 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetFromPos(x, y);
2380 if (nwid ==
nullptr) nwid =
this;
2388 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetOfType(tp);
2389 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2401 this->ApplyAspectRatio();
2417 if (this->
disp_flags.Any({NWidgetDisplayFlag::ShadeGrey, NWidgetDisplayFlag::ShadeDimmed})) {
2421 DrawOutline(w,
this);
2441 if (w->
viewport ==
nullptr)
return;
2485 int new_pos = list_position;
2555 const int count = sb.
GetCount() * resize_step;
2556 const int position = sb.
GetPosition() * resize_step;
2560 r.bottom = r.top + count;
2564 r.right += position;
2565 r.left = r.right - count;
2568 r.right = r.left + count;
2585 switch (this->type) {
2589 this->
SetToolTip(STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2595 this->
SetToolTip(STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2598 default: NOT_REACHED();
2607 switch (this->
type) {
2609 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2613 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2616 default: NOT_REACHED();
2627 Rect r = this->GetCurrentRect();
2630 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2646 DrawOutline(w,
this);
2649 void NWidgetScrollbar::InvalidateDimensionCache()
2655 Dimension NWidgetScrollbar::GetVerticalDimension()
2665 Dimension NWidgetScrollbar::GetHorizontalDimension()
2706NWidgetLeaf::NWidgetLeaf(
WidgetType tp,
Colours colour,
WidgetID index,
const WidgetData &data,
StringID tip) :
NWidgetCore(tp,
colour,
index, 1, 1, data, tip)
2715 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_EMPTY should not have a colour");
2719 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_TEXT should not have a colour");
2725 if (
colour !=
Colours::Invalid) [[unlikely]]
throw std::runtime_error(
"WWT_LABEL should not have a colour");
2740 case NWID_PUSHBUTTON_DROPDOWN:
2747 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2759 this->
SetToolTip(STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2821 switch (this->
type) {
2890 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2923 padding.height + std::max(di.height, dt.height)
2974 case NWID_PUSHBUTTON_DROPDOWN: {
2995 this->
fill_x = fill.width;
2996 this->
fill_y = fill.height;
2999 this->ApplyAspectRatio();
3011 new_dpi.left += this->
pos_x;
3012 new_dpi.top += this->
pos_y;
3016 Rect r = this->GetCurrentRect();
3019 switch (this->
type) {
3022 if (this->
index == -1 && _draw_widget_outlines) {
3035 DrawBoolButton(pt.
x, pt.
y, button_colour, this->colour, clicked, !this->IsDisabled());
3065 default: NOT_REACHED();
3080 DrawMatrix(r, this->
colour, clicked, this->
widget_data.matrix.width, this->widget_data.matrix.height, this->resize_x, this->resize_y);
3085 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
3122 case NWID_PUSHBUTTON_DROPDOWN:
3136 DrawOutline(w,
this);
3150 return pt.
x < button_width;
3153 return pt.
x >= button_left;
3177 switch (nwid.
type) {
3180 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3181 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3188 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3189 assert(nwid.u.
xy.
x >= 0 && nwid.u.
xy.
y >= 0);
3196 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3204 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TOOLBARSIZE requires NWidgetResizeBase");
3205 assert(nwid.u.
xy.
x >= 0);
3212 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3219 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3226 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3233 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3240 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3251 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3262 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3268 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3274 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3296 switch (nwid.
type) {
3297 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3335static 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)
3339 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3344 if (dest ==
nullptr)
return nwid_begin;
3375static 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)
3381 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3383 while (nwid_begin != nwid_end) {
3384 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3385 bool fill_sub =
false;
3386 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3389 if (sub_widget ==
nullptr)
break;
3397 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3398 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3399 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3400 parent = std::move(sub_widget);
3405 if (nwid_begin == nwid_end)
return nwid_begin;
3407 assert(nwid_begin < nwid_end);
3409 return std::next(nwid_begin);
3419std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3421 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3422 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3424 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3426 return std::move(container);
3440 auto nwid_begin = std::begin(nwid_parts);
3441 auto nwid_end = std::end(nwid_parts);
3443 *shade_select =
nullptr;
3446 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3448 assert(nwid !=
nullptr);
3452 auto root = std::make_unique<NWidgetVertical>();
3453 root->Add(std::move(nwid));
3454 if (nwid_begin == nwid_end)
return root;
3458 auto shade_stack = std::make_unique<NWidgetStacked>(
INVALID_WIDGET);
3459 *shade_select = shade_stack.get();
3461 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3462 root->Add(std::move(shade_stack));
3467 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3482 assert(max_length >= 1);
3483 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3484 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3491 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3493 if (hor_length == max_length) {
3494 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3495 vert->Add(std::move(hor));
3499 if (hor ==
nullptr) {
3500 hor = std::make_unique<NWidgetHorizontal>();
3504 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3505 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3506 panel->SetFill(1, 1);
3507 if (resizable) panel->SetResize(1, 0);
3508 panel->SetToolTip(button_tooltip);
3509 hor->Add(std::move(panel));
3512 if (vert ==
nullptr)
return hor;
3514 if (hor_length > 0 && hor_length < max_length) {
3516 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3518 if (resizable) spc->SetResize(1, 0);
3519 hor->Add(std::move(spc));
3521 if (hor !=
nullptr) vert->Add(std::move(hor));
3532 assert(parent_window !=
nullptr);
3534 for (
auto &widget : this->
children) {
Class for backupping variables and making sure they are restored later.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Set()
Set all bits.
TypedIndexContainer< std::array< Colours, MAX_COMPANIES >, CompanyID > _company_colours
NOSAVE: can be determined from company structs.
Functions related to companies.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
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.
@ ForceRight
Force align to the right.
@ Centre
Align to the centre.
@ Start
Align to the start, LTR/RTL aware.
@ ForceLeft
Force align to the left.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
@ Bottom
Align to the bottom.
@ Middle
Align to the middle.
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, 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.
int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, Alignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
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.
void DrawSpriteIgnorePadding(SpriteID img, PaletteID pal, const Rect &r, Alignment align)
Draw a sprite within a Rect, ignoring the sprite's padding.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
FontSize
Available font sizes.
@ Begin
Marker for the first font in the enumeration.
@ End
Marker for the end of the enumerations.
@ Normal
Index of the normal font in the font tables.
uint32_t PaletteID
The number of the palette.
Colours
One of 16 base colours used for companies and windows/widgets.
@ End
End-of-array marker.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
@ Recolour
Apply a recolour sprite to the screen content.
@ Checker
Draw only every second pixel, used for greying-out.
void SetDirty() const
Mark entire window as dirty (in need of re-paint).
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
PixelColour GetColourGradient(Colours colour, Shade shade)
Get colour gradient palette index.
@ Darker
Darker colour shade.
@ Lightest
Lightest colour shade.
@ Lighter
Lighter colour shade.
@ Normal
Normal colour shade.
@ Light
Light colour shade.
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.
@ Invalid
broken savegame (used internally)
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 const SpriteID SPR_ARROW_RIGHT
static const SpriteID SPR_WINDOW_SHADE
Shade the window icon.
static const SpriteID SPR_WINDOW_DEBUG
NewGRF debug window icon.
static const SpriteID SPR_PIN_UP
Pin icon.
static const SpriteID SPR_WINDOW_UNSHADE
Unshade the window icon.
static const SpriteID SPR_COMPANY_ICON
Icon showing company colour.
static const SpriteID SPR_ARROW_LEFT
static const SpriteID SPR_WINDOW_RESIZE_RIGHT
Resize icon to the right.
static const SpriteID SPR_WINDOW_RESIZE_LEFT
Resize icon to the left.
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
static const SpriteID SPR_ARROW_DOWN
static const SpriteID SPR_ARROW_UP
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.
Horizontal and vertical alignment.
AlignmentH ResolveRTL() const
Resolve horizontal alignment for the current text direction.
AlignmentV v
Vertical alignment.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
uint8_t lines
Number of text lines.
uint8_t spacing
Extra spacing around lines.
FontSize size
Font size of text lines.
TextColour colour
TextColour for DrawString.
FontSize size
Font size of text.
Colour for pixel/line drawing.
Data stored about a string that can be modified in the GUI.
Padding dimensions to apply to each side of a Rect.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Rect CentreToHeight(int height) const
Centre a vertical dimension within this Rect.
int Height() const
Get height of Rect.
Rect WithY(int new_top, int new_bottom) const
Create a new Rect, replacing the top and bottom coordinates.
Rect WithX(int new_left, int new_right) const
Create a new Rect, replacing the left and right coordinates.
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 UnfocusFocusedWidget()
Makes no widget on this window have focus.
void DrawSortButton(WidgetID widget, bool descending) 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?
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.
EnumBitSet< TransparencyOption, uint32_t > TransparencyOptions
Bitset of TransparencyOption elements.
TransparencyOptions _transparency_opt
The bits that should be transparent.
@ 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.
EnumBitSet< FrameFlag, uint8_t > FrameFlags
Bitset of FrameFlag elements.
@ 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.
EventState
State of handling an event.
@ Handled
The passed event is handled.
@ NotHandled
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.