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;
148 int height = (bottom - top);
153 if (count != 0) top += height * pos / count;
155 if (cap > count) cap = count;
156 if (count != 0) bottom -= (count - pos - cap) * height / count;
160 pt.x = rev_base - bottom;
161 pt.y = rev_base - top;
183 bool changed =
false;
188 button_size = NWidgetScrollbar::GetHorizontalDimension().width;
191 button_size = NWidgetScrollbar::GetVerticalDimension().height;
193 if (pos < mi + button_size) {
196 if (_scroller_click_timeout <= 1) {
197 _scroller_click_timeout = 3;
201 }
else if (pos >= ma - button_size) {
205 if (_scroller_click_timeout <= 1) {
206 _scroller_click_timeout = 3;
215 }
else if (pos > pt.y) {
218 _scrollbar_start_pos = pt.x - mi - button_size;
219 _scrollbar_size = ma - mi - button_size * 2;
221 _cursorpos_drag_start = _cursor.
pos;
254 assert(scrollbar !=
nullptr);
269 return (nw !=
nullptr) ? nw->
index : -1;
283 assert(colour < COLOUR_END);
295 Rect outer = {left, top, right, bottom};
299 GfxFillRect(outer.left, outer.top, inner.left - 1, outer.bottom, dark);
300 GfxFillRect(inner.left, outer.top, outer.right, inner.top - 1, dark);
301 GfxFillRect(inner.right + 1, inner.top, outer.right, inner.bottom, light);
302 GfxFillRect(inner.left, inner.bottom + 1, outer.right, outer.bottom, light);
303 interior = (flags &
FR_DARKENED ? medium_dark : medium_light);
305 GfxFillRect(outer.left, outer.top, inner.left - 1, inner.bottom, light);
306 GfxFillRect(inner.left, outer.top, inner.right, inner.top - 1, light);
307 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, dark);
308 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, dark);
309 interior = medium_dark;
312 GfxFillRect(inner.left, inner.top, inner.right, inner.bottom, interior);
322 d.height -= offset.y;
325 DrawSprite(img, pal, p.x - offset.x, p.y - offset.y);
342 if ((type & WWT_MASK) ==
WWT_IMGBTN_2 && clicked) img++;
343 DrawSpriteIgnorePadding(img, PAL_NONE, r, align);
358 if (str == STR_NULL)
return;
362 DrawString(r.left, r.right, p.y, str, colour, align,
false, fs);
377 if (str != STR_NULL)
DrawString(r.left, r.right, p.y, str, colour, align,
false, fs);
404 static inline void DrawMatrix(
const Rect &r, Colours colour,
bool clicked, uint16_t data, uint resize_x, uint resize_y)
410 if (num_columns == 0) {
411 column_width = resize_x;
412 num_columns = r.
Width() / column_width;
414 column_width = r.
Width() / num_columns;
420 row_height = resize_y;
421 num_rows = r.
Height() / row_height;
423 row_height = r.
Height() / num_rows;
429 for (
int ctr = num_columns; ctr > 1; ctr--) {
435 for (
int ctr = num_rows; ctr > 1; ctr--) {
443 for (
int ctr = num_columns; ctr > 1; ctr--) {
449 for (
int ctr = num_rows; ctr > 1; ctr--) {
466 int height = NWidgetScrollbar::GetVerticalDimension().height;
476 GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
480 int left = r.left + r.
Width() * 3 / 11;
481 int right = r.left + r.
Width() * 8 / 11;
486 GfxFillRect(left - bl, r.top + height, left - 1, r.bottom - height, c1);
487 GfxFillRect(left, r.top + height, left + br - 1, r.bottom - height, c2);
488 GfxFillRect(right - bl, r.top + height, right - 1, r.bottom - height, c1);
489 GfxFillRect(right, r.top + height, right + br - 1, r.bottom - height, c2);
506 int width = NWidgetScrollbar::GetHorizontalDimension().width;
515 GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
519 int top = r.top + r.
Height() * 3 / 11;
520 int bottom = r.top + r.
Height() * 8 / 11;
525 GfxFillRect(r.left + width, top - bt, r.right - width, top - 1, c1);
526 GfxFillRect(r.left + width, top, r.right - width, top + bb - 1, c2);
527 GfxFillRect(r.left + width, bottom - bt, r.right - width, bottom - 1, c1);
528 GfxFillRect(r.left + width, bottom, r.right - width, bottom + bb - 1, c2);
579 GfxFillRect(outer.left, inner.top, inner.left - 1, inner.bottom, c1);
580 GfxFillRect(inner.left, inside.top, inside.left - 1, inside.bottom, c2);
583 GfxFillRect(inside.right + 1, inner.top, inner.right, inside.bottom, c1);
584 GfxFillRect(inner.right + 1, outer.top, outer.right, inner.bottom, c2);
587 GfxFillRect(inner.left, inside.bottom + 1, inner.right, inner.bottom, c1);
588 GfxFillRect(outer.left, inner.bottom + 1, outer.right, outer.bottom, c2);
643 static inline void DrawResizeBox(
const Rect &r, Colours colour,
bool at_left,
bool clicked,
bool bevel)
647 }
else if (clicked) {
660 if (colour != COLOUR_WHITE)
DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_NONE);
664 d.height -= offset.y;
691 if (str != STR_NULL) {
716 if (str != STR_NULL) {
722 if (str != STR_NULL) {
743 if (!widget->IsHighlighted())
continue;
745 Rect outer = widget->GetCurrentRect();
750 GfxFillRect(outer.left, outer.top, inner.left, inner.bottom, colour);
751 GfxFillRect(inner.left + 1, outer.top, inner.right - 1, inner.top, colour);
752 GfxFillRect(inner.right, outer.top, outer.right, inner.bottom, colour);
753 GfxFillRect(outer.left + 1, inner.bottom, outer.right - 1, outer.bottom, colour);
768 Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect();
771 Dimension dim = NWidgetScrollbar::GetVerticalDimension();
782 return NWidgetScrollbar::GetVerticalDimension().width + 1;
785 bool _draw_widget_outlines;
925 return (this->
type == tp) ? this :
nullptr;
928 void NWidgetBase::ApplyAspectRatio()
942 void NWidgetBase::AdjustPaddingForZoom()
978 this->
SetAspect(
static_cast<float>(x_ratio) /
static_cast<float>(y_ratio), flags);
981 void NWidgetResizeBase::AdjustPaddingForZoom()
987 NWidgetBase::AdjustPaddingForZoom();
1011 this->min_x = std::max(this->min_x,
min_x);
1012 this->min_y = std::max(this->min_y,
min_y);
1064 d.height *= max_lines;
1080 if (
min_x == this->min_x &&
min_y == this->min_y)
return false;
1081 this->min_x =
min_x;
1082 this->min_y =
min_y;
1094 if (
min_y == this->min_y)
return false;
1095 this->min_y =
min_y;
1162 this->align =
align;
1167 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1177 if (this->
type == tp)
return this;
1178 for (
const auto &child_wid : this->
children) {
1180 if (nwid !=
nullptr)
return nwid;
1185 void NWidgetContainer::AdjustPaddingForZoom()
1187 for (
const auto &child_wid : this->
children) {
1188 child_wid->AdjustPaddingForZoom();
1190 NWidgetBase::AdjustPaddingForZoom();
1199 assert(wid !=
nullptr);
1201 this->children.push_back(std::move(wid));
1206 for (
const auto &child_wid : this->children) {
1207 child_wid->FillWidgetLookup(widget_lookup);
1213 for (
const auto &child_wid : this->children) {
1217 DrawOutline(w,
this);
1224 for (
const auto &child_wid : this->children) {
1226 if (nwid !=
nullptr)
return nwid;
1238 void NWidgetStacked::AdjustPaddingForZoom()
1240 for (
const auto &child_wid : this->children) {
1241 child_wid->AdjustPaddingForZoom();
1243 NWidgetContainer::AdjustPaddingForZoom();
1259 this->
fill_x = fill.width;
1260 this->
fill_y = fill.height;
1263 this->ApplyAspectRatio();
1274 for (
const auto &child_wid : this->children) {
1275 child_wid->SetupSmallestSize(w);
1277 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1278 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1279 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1280 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1283 this->ApplyAspectRatio();
1294 for (
const auto &child_wid : this->children) {
1295 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1296 uint child_width =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1297 uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
1299 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1300 uint child_height =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1301 uint child_pos_y = child_wid->padding.top;
1303 child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
1312 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1322 assert(
static_cast<size_t>(this->
shown_plane) < this->children.size());
1324 DrawOutline(w,
this);
1333 if (
static_cast<size_t>(this->
shown_plane) >= this->children.size())
return nullptr;
1353 this->flags = flags;
1356 void NWidgetPIPContainer::AdjustPaddingForZoom()
1361 NWidgetContainer::AdjustPaddingForZoom();
1417 uint max_vert_fill = 0;
1418 for (
const auto &child_wid : this->children) {
1419 child_wid->SetupSmallestSize(w);
1420 longest = std::max(longest, child_wid->smallest_x);
1421 max_vert_fill = std::max(max_vert_fill, child_wid->GetVerticalStepSize(
ST_SMALLEST));
1422 this->
smallest_y = std::max(this->
smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
1423 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) this->
gaps++;
1427 [[maybe_unused]] uint max_smallest = this->
smallest_y + 3 * max_vert_fill;
1430 for (
const auto &child_wid : this->children) {
1431 uint step_size = child_wid->GetVerticalStepSize(
ST_SMALLEST);
1432 uint child_height = child_wid->smallest_y + child_wid->padding.Vertical();
1433 if (step_size > 1 && child_height < cur_height) {
1434 uint remainder = (cur_height - child_height) % step_size;
1435 if (remainder > 0) {
1436 cur_height += step_size - remainder;
1437 assert(cur_height < max_smallest);
1446 for (
const auto &child_wid : this->children) {
1447 child_wid->smallest_y = this->
smallest_y - child_wid->padding.Vertical();
1448 child_wid->ApplyAspectRatio();
1449 longest = std::max(longest, child_wid->smallest_x);
1452 for (
const auto &child_wid : this->children) {
1453 if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
1457 for (
const auto &child_wid : this->children) {
1458 this->
smallest_x += child_wid->smallest_x + child_wid->padding.Horizontal();
1459 if (child_wid->fill_x > 0) {
1460 if (this->
fill_x == 0 || this->
fill_x > child_wid->fill_x) this->
fill_x = child_wid->fill_x;
1462 this->
fill_y = std::lcm(this->
fill_y, child_wid->fill_y);
1464 if (child_wid->resize_x > 0) {
1480 for (
const auto &child_wid : this->children) {
1481 if (child_wid->smallest_x != 0 || child_wid->fill_x != 0) additional_length -= child_wid->smallest_x + child_wid->padding.Horizontal();
1500 int num_changing_childs = 0;
1501 uint biggest_stepsize = 0;
1502 for (
const auto &child_wid : this->children) {
1503 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1506 biggest_stepsize = std::max(biggest_stepsize, hor_step);
1508 child_wid->current_x = child_wid->smallest_x;
1511 uint vert_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
1512 child_wid->current_y =
ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
1517 for (
const auto &child_wid : this->children) {
1518 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1519 if (hor_step == biggest_stepsize) {
1520 num_changing_childs++;
1526 while (biggest_stepsize > 0) {
1527 uint next_biggest_stepsize = 0;
1528 for (
const auto &child_wid : this->children) {
1529 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1530 if (hor_step > biggest_stepsize)
continue;
1531 if (hor_step == biggest_stepsize) {
1532 uint increment = additional_length / num_changing_childs;
1533 num_changing_childs--;
1534 if (hor_step > 1) increment -= increment % hor_step;
1535 child_wid->current_x = child_wid->smallest_x + increment;
1536 additional_length -= increment;
1539 next_biggest_stepsize = std::max(next_biggest_stepsize, hor_step);
1541 biggest_stepsize = next_biggest_stepsize;
1543 if (num_changing_childs == 0 && (
flags &
NC_BIGFIRST) && biggest_stepsize > 0) {
1545 for (
const auto &child_wid : this->children) {
1546 uint hor_step = child_wid->GetHorizontalStepSize(sizing);
1547 if (hor_step == biggest_stepsize) {
1548 num_changing_childs++;
1553 assert(num_changing_childs == 0);
1558 if (additional_length > 0) {
1569 uint position = rtl ? this->
current_x - pre : pre;
1570 for (
const auto &child_wid : this->children) {
1571 uint child_width = child_wid->current_x;
1572 uint child_x = x + (rtl ? position - child_width - child_wid->padding.left : position + child_wid->padding.left);
1573 uint child_y = y + child_wid->padding.top;
1575 child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl);
1576 if (child_wid->current_x != 0) {
1577 uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter;
1578 position = rtl ? position - padded_child_width : position + padded_child_width;
1611 uint max_hor_fill = 0;
1612 for (
const auto &child_wid : this->children) {
1613 child_wid->SetupSmallestSize(w);
1614 highest = std::max(highest, child_wid->smallest_y);
1615 max_hor_fill = std::max(max_hor_fill, child_wid->GetHorizontalStepSize(
ST_SMALLEST));
1616 this->
smallest_x = std::max(this->
smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
1617 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) this->
gaps++;
1621 [[maybe_unused]] uint max_smallest = this->
smallest_x + 3 * max_hor_fill;
1624 for (
const auto &child_wid : this->children) {
1625 uint step_size = child_wid->GetHorizontalStepSize(
ST_SMALLEST);
1626 uint child_width = child_wid->smallest_x + child_wid->padding.Horizontal();
1627 if (step_size > 1 && child_width < cur_width) {
1628 uint remainder = (cur_width - child_width) % step_size;
1629 if (remainder > 0) {
1630 cur_width += step_size - remainder;
1631 assert(cur_width < max_smallest);
1640 for (
const auto &child_wid : this->children) {
1641 child_wid->smallest_x = this->
smallest_x - child_wid->padding.Horizontal();
1642 child_wid->ApplyAspectRatio();
1643 highest = std::max(highest, child_wid->smallest_y);
1646 for (
const auto &child_wid : this->children) {
1647 if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
1651 for (
const auto &child_wid : this->children) {
1652 this->
smallest_y += child_wid->smallest_y + child_wid->padding.Vertical();
1653 if (child_wid->fill_y > 0) {
1654 if (this->
fill_y == 0 || this->
fill_y > child_wid->fill_y) this->
fill_y = child_wid->fill_y;
1656 this->
fill_x = std::lcm(this->
fill_x, child_wid->fill_x);
1658 if (child_wid->resize_y > 0) {
1674 for (
const auto &child_wid : this->children) {
1675 if (child_wid->smallest_y != 0 || child_wid->fill_y != 0) additional_length -= child_wid->smallest_y + child_wid->padding.Vertical();
1685 int num_changing_childs = 0;
1686 uint biggest_stepsize = 0;
1687 for (
const auto &child_wid : this->children) {
1688 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1689 if (vert_step > 0) {
1691 biggest_stepsize = std::max(biggest_stepsize, vert_step);
1693 child_wid->current_y = child_wid->smallest_y;
1696 uint hor_step = (sizing ==
ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
1697 child_wid->current_x =
ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
1702 for (
const auto &child_wid : this->children) {
1703 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1704 if (vert_step == biggest_stepsize) {
1705 num_changing_childs++;
1711 while (biggest_stepsize > 0) {
1712 uint next_biggest_stepsize = 0;
1713 for (
const auto &child_wid : this->children) {
1714 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1715 if (vert_step > biggest_stepsize)
continue;
1716 if (vert_step == biggest_stepsize) {
1717 uint increment = additional_length / num_changing_childs;
1718 num_changing_childs--;
1719 if (vert_step > 1) increment -= increment % vert_step;
1720 child_wid->current_y = child_wid->smallest_y + increment;
1721 additional_length -= increment;
1724 next_biggest_stepsize = std::max(next_biggest_stepsize, vert_step);
1726 biggest_stepsize = next_biggest_stepsize;
1728 if (num_changing_childs == 0 && (
flags &
NC_BIGFIRST) && biggest_stepsize > 0) {
1730 for (
const auto &child_wid : this->children) {
1731 uint vert_step = child_wid->GetVerticalStepSize(sizing);
1732 if (vert_step == biggest_stepsize) {
1733 num_changing_childs++;
1738 assert(num_changing_childs == 0);
1743 if (additional_length > 0) {
1754 uint position = pre;
1755 for (
const auto &child_wid : this->children) {
1756 uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left);
1757 uint child_height = child_wid->current_y;
1759 child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl);
1760 if (child_wid->current_y != 0) {
1761 position += child_height + child_wid->padding.Vertical() + inter;
1781 this->ApplyAspectRatio();
1796 DrawOutline(w,
this);
1812 this->colour = colour;
1822 if (this->clicked >= 0 && this->
sb !=
nullptr && this->
widgets_x != 0) {
1838 this->count =
count;
1840 if (this->
sb ==
nullptr || this->
widgets_x == 0)
return;
1848 count *= (this->
sb->
IsVertical() ? this->children.front()->smallest_y : this->children.front()->smallest_x) + this->
pip_inter;
1876 assert(this->children.size() == 1);
1878 this->children.front()->SetupSmallestSize(w);
1881 Dimension size = {this->children.front()->smallest_x +
padding.width, this->children.front()->smallest_y +
padding.height};
1889 this->
fill_x = fill.width;
1890 this->
fill_y = fill.height;
1893 this->ApplyAspectRatio();
1922 if (this->
index >= 0) widget_lookup[this->
index] =
this;
1931 int start_x, start_y, base_offs_x, base_offs_y;
1936 int widget_col = (rtl ?
1947 assert(child !=
nullptr);
1971 assert(child !=
nullptr);
1972 int start_x, start_y, base_offs_x, base_offs_y;
1975 int offs_y = base_offs_y;
1976 for (
int y = start_y; y < start_y + this->
widgets_y + 1; y++, offs_y += this->
widget_h) {
1978 if (offs_y + child->
smallest_y <= 0)
continue;
1979 if (offs_y >= (
int)this->
current_y)
break;
1984 int offs_x = base_offs_x;
1987 if (offs_x + child->
smallest_x <= 0)
continue;
1988 if (offs_x >= (
int)this->
current_x)
continue;
2001 DrawOutline(w,
this);
2017 if (this->
sb !=
nullptr) {
2025 base_offs_x += sub_x;
2027 base_offs_x -= sub_x;
2046 if (this->
child !=
nullptr) this->
child->parent =
this;
2059 if (this->
child ==
nullptr) {
2060 this->
child = std::make_unique<NWidgetVertical>();
2062 nwid->parent = this->
child.get();
2063 this->
child->Add(std::move(nwid));
2078 if (this->
child ==
nullptr) {
2079 this->
child = std::make_unique<NWidgetVertical>();
2081 this->
child->parent =
this;
2082 this->
child->SetPIP(pip_pre, pip_inter, pip_post);
2097 if (this->
child ==
nullptr) {
2098 this->
child = std::make_unique<NWidgetVertical>();
2100 this->
child->parent =
this;
2101 this->
child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post);
2104 void NWidgetBackground::AdjustPaddingForZoom()
2106 if (
child !=
nullptr)
child->AdjustPaddingForZoom();
2107 NWidgetCore::AdjustPaddingForZoom();
2112 if (this->
child !=
nullptr) {
2113 this->
child->SetupSmallestSize(w);
2123 if (w ==
nullptr)
return;
2137 this->
child->padding = WidgetDimensions::scaled.
bevel;
2142 this->ApplyAspectRatio();
2152 d =
maxdim(d, background);
2154 if (this->
index >= 0) {
2156 switch (this->
type) {
2157 default: NOT_REACHED();
2167 this->
fill_x = fill.width;
2168 this->
fill_y = fill.height;
2171 this->ApplyAspectRatio();
2179 if (this->
child !=
nullptr) {
2180 uint x_offset = (rtl ? this->
child->padding.right : this->
child->padding.left);
2181 uint width = given_width - this->
child->padding.Horizontal();
2182 uint height = given_height - this->
child->padding.Vertical();
2183 this->
child->AssignSizePosition(sizing, x + x_offset, y + this->
child->padding.top, width, height, rtl);
2189 if (this->
index >= 0) widget_lookup[this->
index] =
this;
2190 if (this->
child !=
nullptr) this->
child->FillWidgetLookup(widget_lookup);
2197 Rect r = this->GetCurrentRect();
2200 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2202 switch (this->
type) {
2223 if (this->
child !=
nullptr) this->
child->Draw(w);
2229 DrawOutline(w,
this);
2236 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetFromPos(x, y);
2237 if (nwid ==
nullptr) nwid =
this;
2245 if (this->
child !=
nullptr) nwid = this->
child->GetWidgetOfType(tp);
2246 if (nwid ==
nullptr && this->
type == tp) nwid =
this;
2258 this->ApplyAspectRatio();
2279 DrawOutline(w,
this);
2300 if (vp !=
nullptr) {
2324 return (pos < 0 || pos >= this->
GetCount()) ? Scrollbar::npos :
pos;
2343 int new_pos = list_position;
2413 const int count = sb.
GetCount() * resize_step;
2414 const int position = sb.
GetPosition() * resize_step;
2418 r.bottom = r.top + count;
2422 r.right += position;
2423 r.left = r.right - count;
2426 r.right = r.left + count;
2443 switch (this->
type) {
2447 this->
SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST);
2453 this->
SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST);
2456 default: NOT_REACHED();
2465 switch (this->
type) {
2467 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height);
2471 this->
SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3);
2474 default: NOT_REACHED();
2485 Rect r = this->GetCurrentRect();
2488 if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top)
return;
2504 DrawOutline(w,
this);
2507 void NWidgetScrollbar::InvalidateDimensionCache()
2513 Dimension NWidgetScrollbar::GetVerticalDimension()
2523 Dimension NWidgetScrollbar::GetHorizontalDimension()
2564 NWidgetLeaf::NWidgetLeaf(
WidgetType tp, Colours colour,
WidgetID index, uint32_t data,
StringID tip) :
NWidgetCore(tp, colour, index, 1, 1, data, tip)
2590 case NWID_PUSHBUTTON_DROPDOWN:
2597 this->
SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
2609 this->
SetDataTip(data, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
2615 this->
SetDataTip(STR_NULL, STR_TOOLTIP_STICKY);
2622 this->
SetDataTip(STR_NULL, STR_TOOLTIP_SHADE);
2629 this->
SetDataTip(STR_NULL, STR_TOOLTIP_DEBUG);
2636 this->
SetDataTip(STR_NULL, STR_TOOLTIP_DEFSIZE);
2649 this->
SetDataTip(STR_NULL, STR_TOOLTIP_CLOSE_WINDOW);
2670 switch (this->
type) {
2739 size.width = std::max(size.width,
ScaleGUITrad(30) + sprite_size.width);
2806 case NWID_PUSHBUTTON_DROPDOWN: {
2828 this->
fill_x = fill.width;
2829 this->
fill_y = fill.height;
2832 this->ApplyAspectRatio();
2844 new_dpi.left += this->
pos_x;
2845 new_dpi.top += this->
pos_y;
2849 Rect r = this->GetCurrentRect();
2852 switch (this->
type) {
2855 if (this->
index == -1 && _draw_widget_outlines) {
2885 case AWV_LEFT: sprite = SPR_ARROW_LEFT;
break;
2886 case AWV_RIGHT: sprite = SPR_ARROW_RIGHT;
break;
2887 default: NOT_REACHED();
2909 if (query !=
nullptr) query->DrawEditBox(w, this->
index);
2915 DrawCaption(r, this->
colour, w->
owner, this->text_colour, this->widget_data, this->align, this->text_size);
2951 case NWID_PUSHBUTTON_DROPDOWN:
2965 DrawOutline(w,
this);
2978 int button_width = this->
pos_x + this->
current_x - NWidgetLeaf::dropdown_dimension.width;
2979 return pt.x < button_width;
2981 int button_left = this->
pos_x + NWidgetLeaf::dropdown_dimension.width;
2982 return pt.x >= button_left;
3006 switch (nwid.
type) {
3009 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_RESIZE requires NWidgetResizeBase");
3010 assert(nwid.u.
xy.x >= 0 && nwid.u.
xy.y >= 0);
3017 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINSIZE requires NWidgetResizeBase");
3018 assert(nwid.u.
xy.x >= 0 && nwid.u.
xy.y >= 0);
3025 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_MINTEXTLINES requires NWidgetResizeBase");
3033 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_TEXTSTYLE requires NWidgetCore");
3040 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ALIGNMENT requires NWidgetCore");
3047 if (nwrb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_FILL requires NWidgetResizeBase");
3054 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_DATATIP requires NWidgetCore");
3061 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PADDING requires NWidgetBase");
3072 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
3083 if (nwc ==
nullptr && nwb ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
3089 if (nwc ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_SCROLLBAR requires NWidgetCore");
3095 if (dest ==
nullptr) [[unlikely]]
throw std::runtime_error(
"WPT_ASPECT requires NWidgetBase");
3117 switch (nwid.
type) {
3118 case NWID_SPACER:
return std::make_unique<NWidgetSpacer>(0, 0);
3155 static 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)
3159 if (
IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]]
throw std::runtime_error(
"Expected non-attribute NWidgetPart type");
3164 if (dest ==
nullptr)
return nwid_begin;
3195 static 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)
3201 assert(parent ==
nullptr || (nwid_cont !=
nullptr && nwid_parent ==
nullptr) || (nwid_cont ==
nullptr && nwid_parent !=
nullptr));
3203 while (nwid_begin != nwid_end) {
3204 std::unique_ptr<NWidgetBase> sub_widget =
nullptr;
3205 bool fill_sub =
false;
3206 nwid_begin =
MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
3209 if (sub_widget ==
nullptr)
break;
3217 if (nwid_cont !=
nullptr) nwid_cont->
Add(std::move(sub_widget));
3218 if (nwid_parent !=
nullptr) nwid_parent->
Add(std::move(sub_widget));
3219 if (nwid_cont ==
nullptr && nwid_parent ==
nullptr) {
3220 parent = std::move(sub_widget);
3225 if (nwid_begin == nwid_end)
return nwid_begin;
3227 assert(nwid_begin < nwid_end);
3229 return std::next(nwid_begin);
3239 std::unique_ptr<NWidgetBase>
MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
3241 if (container ==
nullptr) container = std::make_unique<NWidgetVertical>();
3242 [[maybe_unused]]
auto nwid_part =
MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
3244 if (nwid_part != std::end(nwid_parts)) [[unlikely]]
throw std::runtime_error(
"Did not consume all NWidgetParts");
3246 return std::move(container);
3260 auto nwid_begin = std::begin(nwid_parts);
3261 auto nwid_end = std::end(nwid_parts);
3263 *shade_select =
nullptr;
3266 std::unique_ptr<NWidgetBase> nwid =
nullptr;
3268 assert(nwid !=
nullptr);
3272 auto root = std::make_unique<NWidgetVertical>();
3273 root->Add(std::move(nwid));
3274 if (nwid_begin == nwid_end)
return root;
3278 auto shade_stack = std::make_unique<NWidgetStacked>(-1);
3279 *shade_select = shade_stack.get();
3281 shade_stack->
Add(
MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
3282 root->Add(std::move(shade_stack));
3287 return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
3302 assert(max_length >= 1);
3303 std::unique_ptr<NWidgetVertical> vert =
nullptr;
3304 std::unique_ptr<NWidgetHorizontal> hor =
nullptr;
3311 for (
WidgetID widnum = widget_first; widnum <= widget_last; widnum++) {
3313 if (hor_length == max_length) {
3314 if (vert ==
nullptr) vert = std::make_unique<NWidgetVertical>();
3315 vert->Add(std::move(hor));
3319 if (hor ==
nullptr) {
3320 hor = std::make_unique<NWidgetHorizontal>();
3324 auto panel = std::make_unique<NWidgetBackground>(
WWT_PANEL, button_colour, widnum);
3325 panel->SetMinimalSize(sprite_size.width, sprite_size.height);
3326 panel->SetFill(1, 1);
3327 if (resizable) panel->SetResize(1, 0);
3328 panel->SetDataTip(0x0, button_tooltip);
3329 hor->Add(std::move(panel));
3332 if (vert ==
nullptr)
return hor;
3334 if (hor_length > 0 && hor_length < max_length) {
3336 auto spc = std::make_unique<NWidgetSpacer>(sprite_size.width, sprite_size.height);
3338 if (resizable) spc->SetResize(1, 0);
3339 hor->Add(std::move(spc));
3341 if (hor !=
nullptr) vert->Add(std::move(hor));
Class for backupping variables and making sure they are restored later.
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr static debug_inline 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.
constexpr debug_inline 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.
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 T abs(const T a)
Returns the absolute value of (scalar) variable.
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.
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
void DrawWidgets() const
Paint all widgets of a window.
virtual void UpdateWidgetSize([[maybe_unused]] WidgetID widget, [[maybe_unused]] Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize)
Update size and resize step of a widget in the window.
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
virtual void SetStringParameters([[maybe_unused]] WidgetID widget) const
Initialize string parameters for a widget.
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.
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.
virtual void DrawWidget([[maybe_unused]] const Rect &r, [[maybe_unused]] WidgetID widget) const
Draw the contents of a nested widget.
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.
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.