23#include "table/strings.h"
112 case SA_LEFT: p.x = r.left;
break;
114 case SA_RIGHT: p.x = r.right + 1 - d.width;
break;
115 default: NOT_REACHED();
118 case SA_TOP: p.y = r.top;
break;
120 case SA_BOTTOM: p.y = r.bottom + 1 - d.height;
break;
121 default: NOT_REACHED();
138 int rev_base = top + bottom;
141 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
143 button_size = NWidgetScrollbar::GetVerticalDimension().height;
146 bottom -= button_size;
152 int height = (bottom - top);
153 int slider_height = std::max(button_size, cap * height / count);
154 height -= slider_height;
157 bottom = top + slider_height;
162 pt.x = rev_base - bottom;
163 pt.y = rev_base - top;
185 bool changed =
false;
190 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
193 button_size = NWidgetScrollbar::GetVerticalDimension().height;
195 if (pos < mi + button_size) {
198 if (_scroller_click_timeout <= 1) {
199 _scroller_click_timeout = 3;
203 }
else if (pos >= ma - button_size) {
207 if (_scroller_click_timeout <= 1) {
208 _scroller_click_timeout = 3;
217 }
else if (pos > pt.y) {
220 _scrollbar_start_pos = pt.x - mi - button_size;
221 _scrollbar_size = ma - mi - button_size * 2 - (pt.y - pt.x);
223 _cursorpos_drag_start = _cursor.
pos;
256 assert(scrollbar !=
nullptr);
271 return (nw !=
nullptr) ? nw->
index : -1;
288 assert(colour < COLOUR_END);
296 Rect outer = {left, top, right, bottom};
300 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
301 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
302 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
303 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
304 interior = (flags &
FR_DARKENED ? medium_dark : medium_light);
306 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
307 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
308 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
309 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
310 interior = medium_dark;
313 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
323 d.height -= offset.y;
326 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
343 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
344 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
359 if (str == STR_NULL)
return;
363 DrawString(r.left, r.right, p.y, str, colour, align,
false, fs);
378 if (str != STR_NULL)
DrawString(r.left, r.right, p.y, str, colour, align,
false, fs);
405static inline void DrawMatrix(
const Rect &r, Colours colour,
bool clicked, uint16_t data, uint resize_x, uint resize_y)
411 if (num_columns == 0) {
412 column_width = resize_x;
413 num_columns = r.
Width() / column_width;
415 column_width = r.
Width() / num_columns;
421 row_height = resize_y;
422 num_rows = r.
Height() / row_height;
424 row_height = r.
Height() / num_rows;
430 for (
int ctr = num_columns; ctr > 1; ctr--) {
436 for (
int ctr = num_rows; ctr > 1; ctr--) {
444 for (
int ctr = num_columns; ctr > 1; ctr--) {
450 for (
int ctr = num_rows; ctr > 1; ctr--) {
467 int height = NWidgetScrollbar::GetVerticalDimension().height;
477 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
481 int left = r.left + r.
Width() * 3 / 11;
482 int right = r.left + r.
Width() * 8 / 11;
487 GfxFillRect(left - bl, r.top + height, left - 1, r.bottom - height, c1);
488 GfxFillRect(left, r.top + height, left + br - 1, r.bottom - height, c2);
489 GfxFillRect(right - bl, r.top + height, right - 1, r.bottom - height, c1);
490 GfxFillRect(right, r.top + height, right + br - 1, r.bottom - height, c2);
507 int width = NWidgetScrollbar::GetHorizontalDimension().width;
516 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
520 int top = r.top + r.
Height() * 3 / 11;
521 int bottom = r.top + r.
Height() * 8 / 11;
526 GfxFillRect(r.left + width, top - bt, r.right - width, top - 1, c1);
527 GfxFillRect(r.left + width, top, r.right - width, top + bb - 1, c2);
528 GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom - 1, c1);
529 GfxFillRect(r.left + width, bottom, r.right - width, bottom + bb - 1, c2);
580 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
581 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
584 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
585 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
588 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
589 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
644static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
648 }
else if (clicked) {
661 if (colour != COLOUR_WHITE)
DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_NONE);
665 d.height -= offset.y;
692 if (str != STR_NULL) {
717 if (str != STR_NULL) {
723 if (str != STR_NULL) {
744 if (!widget->IsHighlighted())
continue;
772 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
783 return NWidgetScrollbar::GetVerticalDimension().width + 1;
786bool _draw_widget_outlines;
926 return (this->
type == tp) ? this :
nullptr;
929void NWidgetBase::ApplyAspectRatio()
943void NWidgetBase::AdjustPaddingForZoom()
979 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
982void NWidgetResizeBase::AdjustPaddingForZoom()
988 NWidgetBase::AdjustPaddingForZoom();
1012 this->min_x = std::max(this->min_x,
min_x);
1013 this->min_y = std::max(this->min_y,
min_y);
1065 d.height *= max_lines;
1081 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1082 this->min_x =
min_x;
1083 this->min_y =
min_y;
1095 if (
min_y == this->min_y)
return false;
1096 this->min_y =
min_y;
1163 this->align =
align;
1168 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1178 if (this->
type == tp)
return this;
1179 for (
const auto &child_wid : this->
children) {
1181 if (nwid !=
nullptr)
return nwid;
1186void NWidgetContainer::AdjustPaddingForZoom()
1188 for (
const auto &child_wid : this->
children) {
1189 child_wid->AdjustPaddingForZoom();
1191 NWidgetBase::AdjustPaddingForZoom();
1200 assert(wid !=
nullptr);
1202 this->
children.push_back(std::move(wid));
1207 for (
const auto &child_wid : this->
children) {
1214 for (
const auto &child_wid : this->
children) {
1218 DrawOutline(w,
this);
1225 for (
const auto &child_wid : this->
children) {
1227 if (nwid !=
nullptr)
return nwid;
1252 this->
fill_x = fill.width;
1253 this->
fill_y = fill.height;
1256 this->ApplyAspectRatio();
1267 for (
const auto &child_wid : this->
children) {
1268 child_wid->SetupSmallestSize(w);
1270 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1271 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1272 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1273 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1276 this->ApplyAspectRatio();
1287 for (
const auto &child_wid : this->
children) {
1288 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1289 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1290 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1292 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1293 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1294 uint child_pos_y = child_wid->padding.top;
1296 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1305 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1317 DrawOutline(w,
this);
1367 for (
const auto &child_wid : this->
children) {
1370 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1371 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1372 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1373 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1376 this->ApplyAspectRatio();
1385 for (
const auto &child_wid : this->
children) {
1386 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1387 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1388 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1390 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1391 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1392 uint child_pos_y = child_wid->padding.top;
1394 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1401 for (
auto it = std::rbegin(this->
children); it != std::rend(this->
children); ++it) {
1405 DrawOutline(w,
this);
1410 this->flags = flags;
1413void NWidgetPIPContainer::AdjustPaddingForZoom()
1418 NWidgetContainer::AdjustPaddingForZoom();
1474 uint max_vert_fill = 0;
1475 for (
const auto &child_wid : this->
children) {
1477 longest = std::max(longest, child_wid->smallest_x);
1478 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1479 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1480 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1484 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1487 for (
const auto &child_wid : this->
children) {
1488 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1489 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1490 if (step_size > 1 && child_height < cur_height) {
1491 uint remainder = (cur_height - child_height) % step_size;
1492 if (remainder > 0) {
1493 cur_height += step_size - remainder;
1494 assert(cur_height < max_smallest);
1503 for (
const auto &child_wid : this->
children) {
1504 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1505 child_wid->ApplyAspectRatio();
1506 longest = std::max(longest, child_wid->smallest_x);
1509 for (
const auto &child_wid : this->
children) {
1510 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1514 for (
const auto &child_wid : this->
children) {
1515 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1516 if (child_wid->fill_x > 0) {
1517 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1519 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1521 if (child_wid->resize_x > 0) {
1537 for (
const auto &child_wid : this->
children) {
1538 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1557 int num_changing_childs = 0;
1558 uint biggest_stepsize = 0;
1559 for (
const auto &child_wid : this->
children) {
1560 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1563 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1565 child_wid->current_x = child_wid->smallest_x;
1568 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1569 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1574 for (
const auto &child_wid : this->
children) {
1575 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1576 if (hor_step == biggest_stepsize) {
1577 num_changing_childs++;
1583 while (biggest_stepsize > 0) {
1584 uint next_biggest_stepsize = 0;
1585 for (
const auto &child_wid : this->
children) {
1586 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1587 if (hor_step > biggest_stepsize)
continue;
1588 if (hor_step == biggest_stepsize) {
1589 uint increment = additional_length / num_changing_childs;
1590 num_changing_childs--;
1591 if (hor_step > 1) increment -= increment % hor_step;
1592 child_wid->current_x = child_wid->smallest_x + increment;
1593 additional_length -= increment;
1596 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1598 biggest_stepsize = next_biggest_stepsize;
1600 if (num_changing_childs == 0 && (
flags &
NC_BIGFIRST) && biggest_stepsize > 0) {
1602 for (
const auto &child_wid : this->
children) {
1603 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1604 if (hor_step == biggest_stepsize) {
1605 num_changing_childs++;
1610 assert(num_changing_childs == 0);
1615 if (additional_length > 0) {
1626 uint position = rtl ? this->
current_x - pre : pre;
1627 for (
const auto &child_wid : this->
children) {
1628 uint child_width = child_wid->current_x;
1629 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1630 uint child_y = y + child_wid->padding.top;
1632 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1633 if (child_wid->current_x != 0) {
1634 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1635 position = rtl ? position - padded_child_width : position + padded_child_width;
1668 uint max_hor_fill = 0;
1669 for (
const auto &child_wid : this->
children) {
1671 highest = std::max(highest, child_wid->smallest_y);
1672 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1673 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1674 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1678 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1681 for (
const auto &child_wid : this->
children) {
1682 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1683 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1684 if (step_size > 1 && child_width < cur_width) {
1685 uint remainder = (cur_width - child_width) % step_size;
1686 if (remainder > 0) {
1687 cur_width += step_size - remainder;
1688 assert(cur_width < max_smallest);
1697 for (
const auto &child_wid : this->
children) {
1698 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1699 child_wid->ApplyAspectRatio();
1700 highest = std::max(highest, child_wid->smallest_y);
1703 for (
const auto &child_wid : this->
children) {
1704 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1708 for (
const auto &child_wid : this->
children) {
1709 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1710 if (child_wid->fill_y > 0) {
1711 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1713 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1715 if (child_wid->resize_y > 0) {
1731 for (
const auto &child_wid : this->
children) {
1732 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1742 int num_changing_childs = 0;
1743 uint biggest_stepsize = 0;
1744 for (
const auto &child_wid : this->
children) {
1745 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1746 if (vert_step > 0) {
1748 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1750 child_wid->current_y = child_wid->smallest_y;
1753 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1754 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1759 for (
const auto &child_wid : this->
children) {
1760 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1761 if (vert_step == biggest_stepsize) {
1762 num_changing_childs++;
1768 while (biggest_stepsize > 0) {
1769 uint next_biggest_stepsize = 0;
1770 for (
const auto &child_wid : this->
children) {
1771 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1772 if (vert_step > biggest_stepsize)
continue;
1773 if (vert_step == biggest_stepsize) {
1774 uint increment = additional_length / num_changing_childs;
1775 num_changing_childs--;
1776 if (vert_step > 1) increment -= increment % vert_step;
1777 child_wid->current_y = child_wid->smallest_y + increment;
1778 additional_length -= increment;
1781 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1783 biggest_stepsize = next_biggest_stepsize;
1785 if (num_changing_childs == 0 && (
flags &
NC_BIGFIRST) && biggest_stepsize > 0) {
1787 for (
const auto &child_wid : this->
children) {
1788 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1789 if (vert_step == biggest_stepsize) {
1790 num_changing_childs++;
1795 assert(num_changing_childs == 0);
1800 if (additional_length > 0) {
1811 uint position = pre;
1812 for (
const auto &child_wid : this->
children) {
1813 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1814 uint child_height = child_wid->current_y;
1816 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1817 if (child_wid->current_y != 0) {
1818 position += child_height + child_wid->padding.Vertical() + inter;
1838 this->ApplyAspectRatio();
1853 DrawOutline(w,
this);
1869 this->colour = colour;
1879 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1895 this->count =
count;
1897 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1933 assert(this->
children.size() == 1);
1935 this->
children.front()->SetupSmallestSize(w);
1938 Dimension size = {this->
children.front()->smallest_x + padding.width, this->
children.front()->smallest_y + padding.height};
1946 this->
fill_x = fill.width;
1947 this->
fill_y = fill.height;
1950 this->ApplyAspectRatio();
1979 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1988 int start_x, start_y, base_offs_x, base_offs_y;
1993 int widget_col = (rtl ?
2004 assert(child !=
nullptr);
2028 assert(child !=
nullptr);
2029 int start_x, start_y, base_offs_x, base_offs_y;
2032 int offs_y = base_offs_y;
2033 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
2035 if (offs_y + child->
smallest_y <= 0)
continue;
2036 if (offs_y >= (
int)this->
current_y)
break;
2041 int offs_x = base_offs_x;
2044 if (offs_x + child->
smallest_x <= 0)
continue;
2045 if (offs_x >= (
int)this->
current_x)
continue;
2058 DrawOutline(w,
this);
2074 if (this->
sb !=
nullptr) {
2082 base_offs_x += sub_x;
2084 base_offs_x -= sub_x;
2103 if (this->
child !=
nullptr) this->
child->parent =
this;
2116 if (this->
child ==
nullptr) {
2117 this->
child = std::make_unique<NWidgetVertical>();
2119 nwid->parent = this->
child.get();
2120 this->
child->Add(std::move(nwid));
2135 if (this->
child ==
nullptr) {
2136 this->
child = std::make_unique<NWidgetVertical>();
2138 this->
child->parent =
this;
2139 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2154 if (this->
child ==
nullptr) {
2155 this->
child = std::make_unique<NWidgetVertical>();
2157 this->
child->parent =
this;
2158 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2161void NWidgetBackground::AdjustPaddingForZoom()
2163 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2164 NWidgetCore::AdjustPaddingForZoom();
2169 if (this->
child !=
nullptr) {
2170 this->
child->SetupSmallestSize(w);
2180 if (w ==
nullptr)
return;
2194 this->
child->padding = WidgetDimensions::scaled.
bevel;
2199 this->ApplyAspectRatio();
2209 d =
maxdim(d, background);
2211 if (this->
index >= 0) {
2213 switch (this->
type) {
2214 default: NOT_REACHED();
2224 this->
fill_x = fill.width;
2225 this->
fill_y = fill.height;
2228 this->ApplyAspectRatio();
2236 if (this->
child !=
nullptr) {
2237 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2238 uint width = given_width - this->
child->padding.Horizontal();
2239 uint height = given_height - this->
child->padding.Vertical();
2240 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2246 if (this->
index >= 0) widget_lookup[this->
index] =
this;
2247 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2254 Rect r = this->GetCurrentRect();
2257 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2259 switch (this->
type) {
2280 if (this->
child !=
nullptr) this->
child->Draw(w);
2286 DrawOutline(w,
this);
2294 if (nwid ==
nullptr) nwid =
this;
2303 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2315 this->ApplyAspectRatio();
2336 DrawOutline(w,
this);
2357 if (vp !=
nullptr) {
2381 return (pos < 0 || pos >= this->
GetCount()) ? Scrollbar::npos :
pos;
2400 int new_pos = list_position;
2470 const int count = sb.
GetCount() * resize_step;
2471 const int position = sb.
GetPosition() * resize_step;
2475 r.bottom = r.top + count;
2479 r.right += position;
2480 r.left = r.right - count;
2483 r.right = r.left + count;
2500 switch (this->
type) {
2504 this->
SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2510 this->
SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2513 default: NOT_REACHED();
2522 switch (this->
type) {
2524 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2528 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2531 default: NOT_REACHED();
2542 Rect r = this->GetCurrentRect();
2545 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2561 DrawOutline(w,
this);
2564 void NWidgetScrollbar::InvalidateDimensionCache()
2570 Dimension NWidgetScrollbar::GetVerticalDimension()
2580 Dimension NWidgetScrollbar::GetHorizontalDimension()
2621NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours colour,
WidgetID index, uint32_t data,
StringID tip) :
NWidgetCore(tp, colour, index, 1, 1, data, tip)
2647 case NWID_PUSHBUTTON_DROPDOWN:
2654 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2666 this->
SetDataTip(data, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2672 this->
SetDataTip(STR_NULL, STR_TOOLTIP_STICKY);
2679 this->
SetDataTip(STR_NULL, STR_TOOLTIP_SHADE);
2686 this->
SetDataTip(STR_NULL, STR_TOOLTIP_DEBUG);
2693 this->
SetDataTip(STR_NULL, STR_TOOLTIP_DEFSIZE);
2706 this->
SetDataTip(STR_NULL, STR_TOOLTIP_CLOSE_WINDOW);
2727 switch (this->
type) {
2796 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2863 case NWID_PUSHBUTTON_DROPDOWN: {
2885 this->
fill_x = fill.width;
2886 this->
fill_y = fill.height;
2889 this->ApplyAspectRatio();
2901 new_dpi.left += this->
pos_x;
2902 new_dpi.top += this->
pos_y;
2906 Rect r = this->GetCurrentRect();
2909 switch (this->
type) {
2912 if (this->
index == -1 && _draw_widget_outlines) {
2942 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
2943 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
2944 default: NOT_REACHED();
2966 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
2972 DrawCaption(r, this->
colour, w->
owner, this->text_colour, this->widget_data, this->align, this->text_size);
3008 case NWID_PUSHBUTTON_DROPDOWN:
3022 DrawOutline(w,
this);
3035 int button_width = this->
pos_x + this->
current_x - NWidgetLeaf::dropdown_dimension.width;
3036 return pt.x < button_width;
3038 int button_left = this->
pos_x + NWidgetLeaf::dropdown_dimension.width;
3039 return pt.x >= button_left;
3063 switch (nwid.
type) {
3066 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3067 assert(nwid.u.
xy.x >= 0 && nwid.u.
xy.y >= 0);
3074 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3075 assert(nwid.u.
xy.x >= 0 && nwid.u.
xy.y >= 0);
3082 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3090 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3097 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3104 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3111 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3118 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3129 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3140 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3146 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3152 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3174 switch (nwid.
type) {
3175 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3213static 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)
3217 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3222 if (dest ==
nullptr)
return nwid_begin;
3253static 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)
3259 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3261 while (nwid_begin != nwid_end) {
3262 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3263 bool fill_sub =
false;
3264 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3267 if (sub_widget ==
nullptr)
break;
3275 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3276 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3277 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3278 parent = std::move(sub_widget);
3283 if (nwid_begin == nwid_end)
return nwid_begin;
3285 assert(nwid_begin < nwid_end);
3287 return std::next(nwid_begin);
3297std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3299 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3300 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3302 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3304 return std::move(container);
3318 auto nwid_begin = std::begin(nwid_parts);
3319 auto nwid_end = std::end(nwid_parts);
3321 *shade_select =
nullptr;
3324 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3326 assert(nwid !=
nullptr);
3330 auto root = std::make_unique<NWidgetVertical>();
3331 root->Add(std::move(nwid));
3332 if (nwid_begin == nwid_end)
return root;
3336 auto shade_stack = std::make_unique<NWidgetStacked>(-1);
3337 *shade_select = shade_stack.get();
3339 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3340 root->Add(std::move(shade_stack));
3345 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3360 assert(max_length >= 1);
3361 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3362 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3369 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3371 if (hor_length == max_length) {
3372 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3373 vert->Add(std::move(hor));
3377 if (hor ==
nullptr) {
3378 hor = std::make_unique<NWidgetHorizontal>();
3382 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3383 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3384 panel->SetFill(1, 1);
3385 if (resizable) panel->SetResize(1, 0);
3386 panel->SetDataTip(0x0, button_tooltip);
3387 hor->Add(std::move(panel));
3390 if (vert ==
nullptr)
return hor;
3392 if (hor_length > 0 && hor_length < max_length) {
3394 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3396 if (resizable) spc->SetResize(1, 0);
3397 hor->Add(std::move(spc));
3399 if (hor !=
nullptr) vert->Add(std::move(hor));
Class for backupping variables and making sure they are restored later.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Base class that provides memory initialization on dynamically created objects.
Colours _company_colours[MAX_COMPANIES]
NOSAVE: can be determined from company structs.
Functions related to companies.
Owner
Enum for all companies/owners.
@ MAX_COMPANIES
Maximum number of companies.
debug_inline constexpr bool HasFlag(const T x, const T y)
Checks if a value in a bitset enum is set.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
void DrawRectOutline(const Rect &r, int colour, int width, int dash)
Draw the outline of a Rect.
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Dimension GetScaledSpriteSize(SpriteID sprid)
Scale sprite size for GUI.
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
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.
FontSize
Available font sizes.
@ FS_NORMAL
Index of the normal font in the font tables.
uint32_t PaletteID
The number of the palette.
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
static const uint8_t PC_WHITE
White palette colour.
static const uint8_t PC_BLACK
Black palette colour.
Base for the GUIs that have an edit box in them.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Types related to global configuration settings.
This file contains all sprite-related enums and defines.
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
static const PaletteID PALETTE_NEWSPAPER
Recolour sprite for newspaper-greying.
Definition of base types and functions in a cross-platform compatible way.
The colour translation of GRF's strings.
static const uint8_t _string_colourmap[17]
Colour mapping for TextColour.
TextDirection _current_text_dir
Text direction of the currently selected language.
Functions related to OTTD's strings.
@ TD_LTR
Text is written left-to-right by default.
@ TD_RTL
Text is written right-to-left by default.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
GUISettings gui
settings related to the GUI
Point pos
logical mouse position
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
bool scale_bevels
bevels are scaled with GUI scale.
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
uint8_t lines
Number of text lines.
uint8_t spacing
Extra spacing around lines.
FontSize size
Font size of text lines.
TextColour colour
TextColour for DrawString.
FontSize size
Font size of text.
Coordinates of a point in 2D.
Data stored about a string that can be modified in the GUI.
Padding dimensions to apply to each side of a Rect.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
int Width() const
Get width of Rect.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
int Height() const
Get height of Rect.
Rect Expand(int s) const
Copy and expand Rect by s pixels.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_width
width << zoom
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
int virtual_height
height << zoom
Data structure for an opened window.
virtual void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize)
Update size and resize step of a widget in the window.
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
void DrawWidgets() const
Paint all widgets of a window.
virtual void SetStringParameters(WidgetID widget) const
Initialize string parameters for a widget.
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
virtual bool IsNewGRFInspectable() const
Is the data related to this window NewGRF inspectable?
ViewportData * viewport
Pointer to viewport data, if present.
void DrawViewport() const
Draw the viewport of this window.
virtual void DrawWidget(const Rect &r, WidgetID widget) const
Draw the contents of a nested widget.
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
int left
x position of left edge of the window
bool IsShaded() const
Is window shaded currently?
int top
y position of top edge of the window
const QueryString * GetQueryString(WidgetID widnum) const
Return the querystring associated to a editbox.
WidgetLookup widget_lookup
Indexed access to the nested widget tree. Do not access directly, use Window::GetWidget() instead.
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
WindowFlags flags
Window flags.
std::unique_ptr< NWidgetBase > nested_root
Root of the nested tree.
int height
Height of the window (number of pixels down in y direction)
int width
width of the window (number of pixels to the right in x direction)
Functions related to transparency.
TransparencyOptionBits _transparency_opt
The bits that should be transparent.
@ TO_TEXT
loading and cost/income text
uint TransparencyOptionBits
transparency option bits
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.
SortButtonState
State of a sort direction button.
@ SBS_DOWN
Sort ascending.
@ SBS_OFF
Do not sort (with this button).
@ WF_WHITE_BORDER
Window white border counter bit mask.
@ WF_SIZING
Window is being resized.
@ WF_HIGHLIGHTED
Window has a widget that has a highlight.
@ WF_STICKY
Window is made sticky by user.
FrameFlags
Flags to describe the look of the frame.
@ FR_DARKENED
If set the background is darker, allows for lowered frames with normal background colour when used wi...
@ FR_BORDERONLY
Draw border only, no background.
@ FR_LOWERED
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
@ FR_TRANSPARENT
Makes the background transparent if set.
EventState
State of handling an event.
@ ES_HANDLED
The passed event is handled.
@ ES_NOT_HANDLED
The passed event is not handled.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_MIN) When shifting right,...
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
ZoomLevel
All zoom levels we know.
@ ZOOM_LVL_NORMAL
The normal zoom level.