Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2025-05-14 18:41:13 +05:00
994 changed files with 38759 additions and 34518 deletions

View File

@@ -29,7 +29,7 @@ WidgetDimensions WidgetDimensions::scaled = {};
/**
* Scale a RectPadding to GUI zoom level.
* @param r RectPadding at ZOOM_LVL_BASE (traditional "normal" interface size).
* @param r RectPadding at ZOOM_BASE (traditional "normal" interface size).
* @return RectPadding at #ZOOM_LVL_GUI (current interface size).
*/
static inline RectPadding ScaleGUITrad(const RectPadding &r)
@@ -39,7 +39,7 @@ static inline RectPadding ScaleGUITrad(const RectPadding &r)
/**
* Scale a Dimension to GUI zoom level.
* @param d Dimension at ZOOM_LVL_BASE (traditional "normal" interface size).
* @param d Dimension at ZOOM_BASE (traditional "normal" interface size).
* @return Dimension at #ZOOM_LVL_GUI (current interface size).
*/
static inline Dimension ScaleGUITrad(const Dimension &dim)
@@ -54,7 +54,7 @@ static inline Dimension ScaleGUITrad(const Dimension &dim)
Dimension GetScaledSpriteSize(SpriteID sprid)
{
Point offset;
Dimension d = GetSpriteSize(sprid, &offset, ZOOM_LVL_OUT_4X);
Dimension d = GetSpriteSize(sprid, &offset, ZOOM_LVL_NORMAL);
d.width -= offset.x;
d.height -= offset.y;
return ScaleGUITrad(d);
@@ -145,15 +145,17 @@ static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom, bo
top += button_size; // top points to just below the up-button
bottom -= button_size; // bottom points to top of the down-button
int height = (bottom - top);
int pos = sb->GetPosition();
int count = sb->GetCount();
int cap = sb->GetCapacity();
if (count != 0) top += height * pos / count;
if (count > cap) {
int height = (bottom - top);
int slider_height = std::max(button_size, cap * height / count);
height -= slider_height;
if (cap > count) cap = count;
if (count != 0) bottom -= (count - pos - cap) * height / count;
top += height * sb->GetPosition() / (count - cap);
bottom = top + slider_height;
}
Point pt;
if (horizontal && _current_text_dir == TD_RTL) {
@@ -216,7 +218,7 @@ static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, in
changed = sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::SS_BIG);
} else {
_scrollbar_start_pos = pt.x - mi - button_size;
_scrollbar_size = ma - mi - button_size * 2;
_scrollbar_size = ma - mi - button_size * 2 - (pt.y - pt.x);
w->mouse_capture_widget = sb->index;
_cursorpos_drag_start = _cursor.pos;
}
@@ -275,21 +277,20 @@ WidgetID GetWidgetFromPos(const Window *w, int x, int y)
* @param top Top edge of the frame
* @param right Right edge of the frame
* @param bottom Bottom edge of the frame
* @param colour Colour table to use. @see _colour_gradient
* @param colour Colour table to use. @see Colours
* @param flags Flags controlling how to draw the frame. @see FrameFlags
*/
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
{
assert(colour < COLOUR_END);
uint dark = _colour_gradient[colour][3];
uint medium_dark = _colour_gradient[colour][5];
uint medium_light = _colour_gradient[colour][6];
uint light = _colour_gradient[colour][7];
if (flags & FR_TRANSPARENT) {
GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
} else {
assert(colour < COLOUR_END);
const uint dark = GetColourGradient(colour, SHADE_DARK);
const uint medium_dark = GetColourGradient(colour, SHADE_LIGHT);
const uint medium_light = GetColourGradient(colour, SHADE_LIGHTER);
const uint light = GetColourGradient(colour, SHADE_LIGHTEST);
uint interior;
Rect outer = {left, top, right, bottom}; // Outside rectangle
@@ -424,7 +425,7 @@ static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint1
row_height = r.Height() / num_rows;
}
int col = _colour_gradient[colour & 0xF][6];
int col = GetColourGradient(colour, SHADE_LIGHTER);
int x = r.left;
for (int ctr = num_columns; ctr > 1; ctr--) {
@@ -438,7 +439,7 @@ static inline void DrawMatrix(const Rect &r, Colours colour, bool clicked, uint1
GfxFillRect(r.left + WidgetDimensions::scaled.bevel.left, x, r.right - WidgetDimensions::scaled.bevel.right, x + WidgetDimensions::scaled.bevel.top - 1, col);
}
col = _colour_gradient[colour & 0xF][4];
col = GetColourGradient(colour, SHADE_NORMAL);
x = r.left - 1;
for (int ctr = num_columns; ctr > 1; ctr--) {
@@ -470,8 +471,8 @@ static inline void DrawVerticalScrollbar(const Rect &r, Colours colour, bool up_
DrawImageButtons(r.WithHeight(height, false), NWID_VSCROLLBAR, colour, up_clicked, SPR_ARROW_UP, SA_CENTER);
DrawImageButtons(r.WithHeight(height, true), NWID_VSCROLLBAR, colour, down_clicked, SPR_ARROW_DOWN, SA_CENTER);
int c1 = _colour_gradient[colour & 0xF][3];
int c2 = _colour_gradient[colour & 0xF][7];
int c1 = GetColourGradient(colour, SHADE_DARK);
int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
/* draw "shaded" background */
GfxFillRect(r.left, r.top + height, r.right, r.bottom - height, c2);
@@ -509,8 +510,8 @@ static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool l
DrawImageButtons(r.WithWidth(width, false), NWID_HSCROLLBAR, colour, left_clicked, SPR_ARROW_LEFT, SA_CENTER);
DrawImageButtons(r.WithWidth(width, true), NWID_HSCROLLBAR, colour, right_clicked, SPR_ARROW_RIGHT, SA_CENTER);
int c1 = _colour_gradient[colour & 0xF][3];
int c2 = _colour_gradient[colour & 0xF][7];
int c1 = GetColourGradient(colour, SHADE_DARK);
int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
/* draw "shaded" background */
GfxFillRect(r.left + width, r.top, r.right - width, r.bottom, c2);
@@ -548,8 +549,8 @@ static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colo
if (str != STR_NULL) x2 = DrawString(r.left + WidgetDimensions::scaled.frametext.left, r.right - WidgetDimensions::scaled.frametext.right, r.top, str, text_colour, align, false, fs);
int c1 = _colour_gradient[colour][3];
int c2 = _colour_gradient[colour][7];
int c1 = GetColourGradient(colour, SHADE_DARK);
int c2 = GetColourGradient(colour, SHADE_LIGHTEST);
/* If the frame has text, adjust the top bar to fit half-way through */
Rect inner = r.Shrink(ScaleGUITrad(1));
@@ -646,7 +647,7 @@ static inline void DrawResizeBox(const Rect &r, Colours colour, bool at_left, bo
if (bevel) {
DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE);
} else if (clicked) {
GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), _colour_gradient[colour][6]);
GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(colour, SHADE_LIGHTER));
}
DrawSpriteIgnorePadding(at_left ? SPR_WINDOW_RESIZE_LEFT : SPR_WINDOW_RESIZE_RIGHT, PAL_NONE, r.Shrink(ScaleGUITrad(2)), at_left ? (SA_LEFT | SA_BOTTOM | SA_FORCE) : (SA_RIGHT | SA_BOTTOM | SA_FORCE));
}
@@ -686,7 +687,7 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_col
DrawFrameRect(ir, colour, company_owned ? FR_LOWERED | FR_DARKENED | FR_BORDERONLY : FR_LOWERED | FR_DARKENED);
if (company_owned) {
GfxFillRect(ir.Shrink(WidgetDimensions::scaled.bevel), _colour_gradient[_company_colours[owner]][4]);
GfxFillRect(ir.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(_company_colours[owner], SHADE_NORMAL));
}
if (str != STR_NULL) {
@@ -925,6 +926,20 @@ NWidgetBase *NWidgetBase::GetWidgetOfType(WidgetType tp)
return (this->type == tp) ? this : nullptr;
}
void NWidgetBase::ApplyAspectRatio()
{
if (this->aspect_ratio == 0) return;
if (this->smallest_x == 0 || this->smallest_y == 0) return;
uint x = this->smallest_x;
uint y = this->smallest_y;
if (HasFlag(this->aspect_flags, AspectFlags::ResizeX)) x = std::max(this->smallest_x, static_cast<uint>(this->smallest_y * std::abs(this->aspect_ratio)));
if (HasFlag(this->aspect_flags, AspectFlags::ResizeY)) y = std::max(this->smallest_y, static_cast<uint>(this->smallest_x / std::abs(this->aspect_ratio)));
this->smallest_x = x;
this->smallest_y = y;
}
void NWidgetBase::AdjustPaddingForZoom()
{
this->padding = ScaleGUITrad(this->uz_padding);
@@ -942,6 +957,28 @@ NWidgetResizeBase::NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y) :
this->fill_y = fill_y;
}
/**
* Set desired aspect ratio of this widget.
* @param ratio Desired aspect ratio, or 0 for none.
* @param flags Dimensions which should be resized.
*/
void NWidgetResizeBase::SetAspect(float ratio, AspectFlags flags)
{
this->aspect_ratio = ratio;
this->aspect_flags = flags;
}
/**
* Set desired aspect ratio of this widget, in terms of horizontal and vertical dimensions.
* @param x_ratio Desired horizontal component of aspect ratio.
* @param y_ratio Desired vertical component of aspect ratio.
* @param flags Dimensions which should be resized.
*/
void NWidgetResizeBase::SetAspect(int x_ratio, int y_ratio, AspectFlags flags)
{
this->SetAspect(static_cast<float>(x_ratio) / static_cast<float>(y_ratio), flags);
}
void NWidgetResizeBase::AdjustPaddingForZoom()
{
if (!this->absolute) {
@@ -1220,14 +1257,6 @@ NWidgetStacked::NWidgetStacked(WidgetID index) : NWidgetContainer(NWID_SELECTION
{
}
void NWidgetStacked::AdjustPaddingForZoom()
{
for (const auto &child_wid : this->children) {
child_wid->AdjustPaddingForZoom();
}
NWidgetContainer::AdjustPaddingForZoom();
}
void NWidgetStacked::SetupSmallestSize(Window *w)
{
/* Zero size plane selected */
@@ -1237,7 +1266,7 @@ void NWidgetStacked::SetupSmallestSize(Window *w)
Dimension fill = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
Dimension resize = {(this->shown_plane == SZSP_HORIZONTAL), (this->shown_plane == SZSP_VERTICAL)};
/* Here we're primarily interested in the value of resize */
if (this->index >= 0) w->UpdateWidgetSize(this->index, &size, padding, &fill, &resize);
if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
this->smallest_x = size.width;
this->smallest_y = size.height;
@@ -1245,6 +1274,7 @@ void NWidgetStacked::SetupSmallestSize(Window *w)
this->fill_y = fill.height;
this->resize_x = resize.width;
this->resize_y = resize.height;
this->ApplyAspectRatio();
return;
}
@@ -1264,6 +1294,7 @@ void NWidgetStacked::SetupSmallestSize(Window *w)
this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
this->ApplyAspectRatio();
}
}
@@ -1349,6 +1380,70 @@ bool NWidgetStacked::SetDisplayedPlane(int plane)
return true;
}
class NWidgetLayer : public NWidgetContainer {
public:
NWidgetLayer(WidgetID index);
void SetupSmallestSize(Window *w) override;
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override;
void Draw(const Window *w) override;
const WidgetID index; ///< If non-negative, index in the #Window::widget_lookup.
};
NWidgetLayer::NWidgetLayer(WidgetID index) : NWidgetContainer(NWID_LAYER), index(index) {}
void NWidgetLayer::SetupSmallestSize(Window *w)
{
/* First sweep, recurse down and compute minimal size and filling. */
this->smallest_x = 0;
this->smallest_y = 0;
this->fill_x = this->IsEmpty() ? 0 : 1;
this->fill_y = this->IsEmpty() ? 0 : 1;
this->resize_x = this->IsEmpty() ? 0 : 1;
this->resize_y = this->IsEmpty() ? 0 : 1;
for (const auto &child_wid : this->children) {
child_wid->SetupSmallestSize(w);
this->smallest_x = std::max(this->smallest_x, child_wid->smallest_x + child_wid->padding.Horizontal());
this->smallest_y = std::max(this->smallest_y, child_wid->smallest_y + child_wid->padding.Vertical());
this->fill_x = std::lcm(this->fill_x, child_wid->fill_x);
this->fill_y = std::lcm(this->fill_y, child_wid->fill_y);
this->resize_x = std::lcm(this->resize_x, child_wid->resize_x);
this->resize_y = std::lcm(this->resize_y, child_wid->resize_y);
this->ApplyAspectRatio();
}
}
void NWidgetLayer::AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl)
{
assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
this->StoreSizePosition(sizing, x, y, given_width, given_height);
for (const auto &child_wid : this->children) {
uint hor_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetHorizontalStepSize(sizing);
uint child_width = ComputeMaxSize(child_wid->smallest_x, given_width - child_wid->padding.Horizontal(), hor_step);
uint child_pos_x = (rtl ? child_wid->padding.right : child_wid->padding.left);
uint vert_step = (sizing == ST_SMALLEST) ? 1 : child_wid->GetVerticalStepSize(sizing);
uint child_height = ComputeMaxSize(child_wid->smallest_y, given_height - child_wid->padding.Vertical(), vert_step);
uint child_pos_y = child_wid->padding.top;
child_wid->AssignSizePosition(sizing, x + child_pos_x, y + child_pos_y, child_width, child_height, rtl);
}
}
void NWidgetLayer::Draw(const Window *w)
{
/* Draw in reverse order, as layers are arranged top-down. */
for (auto it = std::rbegin(this->children); it != std::rend(this->children); ++it) {
(*it)->Draw(w);
}
DrawOutline(w, this);
}
NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags) : NWidgetContainer(tp)
{
this->flags = flags;
@@ -1455,6 +1550,11 @@ void NWidgetHorizontal::SetupSmallestSize(Window *w)
this->smallest_y = cur_height; // Smallest height got changed, try again.
}
/* 2. For containers that must maintain equal width, extend child minimal size. */
for (const auto &child_wid : this->children) {
child_wid->smallest_y = this->smallest_y - child_wid->padding.Vertical();
child_wid->ApplyAspectRatio();
longest = std::max(longest, child_wid->smallest_x);
}
if (this->flags & NC_EQUALSIZE) {
for (const auto &child_wid : this->children) {
if (child_wid->fill_x == 1) child_wid->smallest_x = longest;
@@ -1644,6 +1744,11 @@ void NWidgetVertical::SetupSmallestSize(Window *w)
this->smallest_x = cur_width; // Smallest width got changed, try again.
}
/* 2. For containers that must maintain equal width, extend children minimal size. */
for (const auto &child_wid : this->children) {
child_wid->smallest_x = this->smallest_x - child_wid->padding.Horizontal();
child_wid->ApplyAspectRatio();
highest = std::max(highest, child_wid->smallest_y);
}
if (this->flags & NC_EQUALSIZE) {
for (const auto &child_wid : this->children) {
if (child_wid->fill_y == 1) child_wid->smallest_y = highest;
@@ -1780,6 +1885,7 @@ void NWidgetSpacer::SetupSmallestSize(Window *)
{
this->smallest_x = this->min_x;
this->smallest_y = this->min_y;
this->ApplyAspectRatio();
}
void NWidgetSpacer::FillWidgetLookup(WidgetLookup &)
@@ -1890,7 +1996,7 @@ void NWidgetMatrix::SetupSmallestSize(Window *w)
Dimension fill = {0, 0};
Dimension resize = {this->pip_inter + this->children.front()->smallest_x, this->pip_inter + this->children.front()->smallest_y};
if (this->index >= 0) w->UpdateWidgetSize(this->index, &size, padding, &fill, &resize);
if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
this->smallest_x = size.width;
this->smallest_y = size.height;
@@ -1898,6 +2004,7 @@ void NWidgetMatrix::SetupSmallestSize(Window *w)
this->fill_y = fill.height;
this->resize_x = resize.width;
this->resize_y = resize.height;
this->ApplyAspectRatio();
}
void NWidgetMatrix::AssignSizePosition(SizingType, int x, int y, uint given_width, uint given_height, bool)
@@ -1972,7 +2079,7 @@ void NWidgetMatrix::FillDirtyWidgets(std::vector<NWidgetBase *> &dirty_widgets)
if (this->IsOutsideDrawArea()) return;
this->base_flags &= ~WBF_DIRTY;
/* Fill the background. */
GfxFillRect(this->GetCurrentRect(), _colour_gradient[this->colour & 0xF][5]);
GfxFillRect(this->GetCurrentRect(), GetColourGradient(this->colour, SHADE_LIGHT));
/* Set up a clipping area for the previews. */
bool rtl = _current_text_dir == TD_RTL;
@@ -2155,6 +2262,7 @@ void NWidgetBackground::SetupSmallestSize(Window *w)
this->smallest_x += this->child->padding.Horizontal();
this->smallest_y += this->child->padding.Vertical();
}
this->ApplyAspectRatio();
} else {
Dimension d = {this->min_x, this->min_y};
Dimension fill = {this->fill_x, this->fill_y};
@@ -2174,7 +2282,7 @@ void NWidgetBackground::SetupSmallestSize(Window *w)
case WWT_FRAME: padding = {WidgetDimensions::scaled.frametext.Horizontal(), WidgetDimensions::scaled.frametext.Vertical()}; break;
case WWT_INSET: padding = {WidgetDimensions::scaled.inset.Horizontal(), WidgetDimensions::scaled.inset.Vertical()}; break;
}
w->UpdateWidgetSize(this->index, &d, padding, &fill, &resize);
w->UpdateWidgetSize(this->index, d, padding, fill, resize);
}
}
this->smallest_x = d.width;
@@ -2183,6 +2291,7 @@ void NWidgetBackground::SetupSmallestSize(Window *w)
this->fill_y = fill.height;
this->resize_x = resize.width;
this->resize_y = resize.height;
this->ApplyAspectRatio();
}
}
@@ -2239,7 +2348,7 @@ void NWidgetBackground::Draw(const Window *w)
if (this->child != nullptr) this->child->Draw(w);
if (this->IsDisabled()) {
GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), _colour_gradient[this->colour & 0xF][2], FILLRECT_CHECKER);
GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_DARKER), FILLRECT_CHECKER);
}
DrawOutline(w, this);
@@ -2280,6 +2389,7 @@ void NWidgetViewport::SetupSmallestSize(Window *)
{
this->smallest_x = this->min_x;
this->smallest_y = this->min_y;
this->ApplyAspectRatio();
}
void NWidgetViewport::Draw(const Window *w)
@@ -2343,13 +2453,13 @@ void NWidgetViewport::UpdateViewportCoordinates(Window *w)
* @param widget Widget number of the widget clicked in.
* @param padding Amount of empty space between the widget edge and the top of the first row. Default value is \c 0.
* @param line_height Height of a single row. A negative value means using the vertical resize step of the widget.
* @return Row number clicked at. If clicked at a wrong position, #INT_MAX is returned.
* @return Row number clicked at. If clicked at a wrong position, #Scrollbar::npos is returned.
*/
int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, WidgetID widget, int padding, int line_height) const
Scrollbar::size_type Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, WidgetID widget, int padding, int line_height) const
{
uint pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
int pos = w->GetRowFromWidget(clickpos, widget, padding, line_height);
if (pos != INT_MAX) pos += this->GetPosition();
return (pos >= this->GetCount()) ? INT_MAX : pos;
return (pos < 0 || pos >= this->GetCount()) ? Scrollbar::npos : pos;
}
/**
@@ -2429,6 +2539,35 @@ void Scrollbar::SetCapacityFromWidget(Window *w, WidgetID widget, int padding)
}
}
/**
* Apply 'scroll' to a rect to be drawn in.
* @param r Rect to be 'scrolled'.
* @param sb The scrollbar affecting the scroll.
* @param resize_step Resize step of the widget/scrollbar (1 if the scrollbar is pixel-based.)
* @returns Scrolled rect.
*/
Rect ScrollRect(Rect r, const Scrollbar &sb, int resize_step)
{
const int count = sb.GetCount() * resize_step;
const int position = sb.GetPosition() * resize_step;
if (sb.IsVertical()) {
r.top -= position;
r.bottom = r.top + count;
} else {
bool rtl = _current_text_dir == TD_RTL;
if (rtl) {
r.right += position;
r.left = r.right - count;
} else {
r.left -= position;
r.right = r.left + count;
}
}
return r;
}
/**
* Scrollbar widget.
* @param tp Scrollbar type. (horizontal/vertical)
@@ -2499,7 +2638,7 @@ void NWidgetScrollbar::Draw(const Window *w)
}
if (this->IsDisabled()) {
GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), _colour_gradient[this->colour & 0xF][2], FILLRECT_CHECKER);
GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_DARKER), FILLRECT_CHECKER);
}
DrawOutline(w, this);
@@ -2589,9 +2728,13 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, uint32_t
case WWT_MATRIX:
case NWID_BUTTON_DROPDOWN:
case NWID_PUSHBUTTON_DROPDOWN:
this->SetFill(0, 0);
break;
case WWT_ARROWBTN:
case WWT_PUSHARROWBTN:
this->SetFill(0, 0);
this->SetAspect(WidgetDimensions::ASPECT_LEFT_RIGHT_BUTTON);
break;
case WWT_EDITBOX:
@@ -2610,24 +2753,28 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, uint32_t
this->SetFill(0, 0);
this->SetMinimalSize(WidgetDimensions::WD_STICKYBOX_WIDTH, WidgetDimensions::WD_CAPTION_HEIGHT);
this->SetDataTip(STR_NULL, STR_TOOLTIP_STICKY);
this->SetAspect(this->min_x, this->min_y);
break;
case WWT_SHADEBOX:
this->SetFill(0, 0);
this->SetMinimalSize(WidgetDimensions::WD_SHADEBOX_WIDTH, WidgetDimensions::WD_CAPTION_HEIGHT);
this->SetDataTip(STR_NULL, STR_TOOLTIP_SHADE);
this->SetAspect(this->min_x, this->min_y);
break;
case WWT_DEBUGBOX:
this->SetFill(0, 0);
this->SetMinimalSize(WidgetDimensions::WD_DEBUGBOX_WIDTH, WidgetDimensions::WD_CAPTION_HEIGHT);
this->SetDataTip(STR_NULL, STR_TOOLTIP_DEBUG);
this->SetAspect(this->min_x, this->min_y);
break;
case WWT_DEFSIZEBOX:
this->SetFill(0, 0);
this->SetMinimalSize(WidgetDimensions::WD_DEFSIZEBOX_WIDTH, WidgetDimensions::WD_CAPTION_HEIGHT);
this->SetDataTip(STR_NULL, STR_TOOLTIP_DEFSIZE);
this->SetAspect(this->min_x, this->min_y);
break;
case WWT_RESIZEBOX:
@@ -2640,6 +2787,7 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, WidgetID index, uint32_t
this->SetFill(0, 0);
this->SetMinimalSize(WidgetDimensions::WD_CLOSEBOX_WIDTH, WidgetDimensions::WD_CAPTION_HEIGHT);
this->SetDataTip(STR_NULL, STR_TOOLTIP_CLOSE_WINDOW);
this->SetAspect(this->min_x, this->min_y);
break;
case WWT_DROPDOWN:
@@ -2813,7 +2961,7 @@ void NWidgetLeaf::SetupSmallestSize(Window *w)
NOT_REACHED();
}
if (this->index >= 0) w->UpdateWidgetSize(this->index, &size, padding, &fill, &resize);
if (this->index >= 0) w->UpdateWidgetSize(this->index, size, padding, fill, resize);
this->smallest_x = size.width;
this->smallest_y = size.height;
@@ -2821,6 +2969,7 @@ void NWidgetLeaf::SetupSmallestSize(Window *w)
this->fill_y = fill.height;
this->resize_x = resize.width;
this->resize_y = resize.height;
this->ApplyAspectRatio();
}
void NWidgetLeaf::Draw(const Window *w)
@@ -2952,7 +3101,7 @@ void NWidgetLeaf::Draw(const Window *w)
if (this->index >= 0) w->DrawWidget(r, this->index);
if (this->IsDisabled()) {
GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), _colour_gradient[this->colour & 0xF][2], FILLRECT_CHECKER);
GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(this->colour, SHADE_DARKER), FILLRECT_CHECKER);
}
DrawOutline(w, this);
@@ -2978,186 +3127,191 @@ bool NWidgetLeaf::ButtonHit(const Point &pt)
/* == Conversion code from NWidgetPart array to NWidgetBase* tree == */
/**
* Test if (an NWidgetPart) WidgetType is an attribute widget part type.
* @param tp WidgetType to test.
* @return True iff WidgetType is an attribute widget.
*/
static bool IsAttributeWidgetPartType(WidgetType tp)
{
return tp > WPT_ATTRIBUTE_BEGIN && tp < WPT_ATTRIBUTE_END;
}
/**
* Apply an attribute NWidgetPart to an NWidget.
* @param nwid Attribute NWidgetPart
* @param dest NWidget to apply attribute to.
* @pre NWidgetPart must be an attribute NWidgetPart.
*/
static void ApplyNWidgetPartAttribute(const NWidgetPart &nwid, NWidgetBase *dest)
{
switch (nwid.type) {
case WPT_RESIZE: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_RESIZE requires NWidgetResizeBase");
assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
nwrb->SetResize(nwid.u.xy.x, nwid.u.xy.y);
break;
}
case WPT_MINSIZE: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINSIZE requires NWidgetResizeBase");
assert(nwid.u.xy.x >= 0 && nwid.u.xy.y >= 0);
nwrb->SetMinimalSize(nwid.u.xy.x, nwid.u.xy.y);
break;
}
case WPT_MINTEXTLINES: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINTEXTLINES requires NWidgetResizeBase");
assert(nwid.u.text_lines.size >= FS_BEGIN && nwid.u.text_lines.size < FS_END);
nwrb->SetMinimalTextLines(nwid.u.text_lines.lines, nwid.u.text_lines.spacing, nwid.u.text_lines.size);
break;
}
case WPT_TEXTSTYLE: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_TEXTSTYLE requires NWidgetCore");
nwc->SetTextStyle(nwid.u.text_style.colour, nwid.u.text_style.size);
break;
}
case WPT_ALIGNMENT: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_ALIGNMENT requires NWidgetCore");
nwc->SetAlignment(nwid.u.align.align);
break;
}
case WPT_FILL: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest);
if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_FILL requires NWidgetResizeBase");
nwrb->SetFill(nwid.u.xy.x, nwid.u.xy.y);
break;
}
case WPT_DATATIP: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_DATATIP requires NWidgetCore");
nwc->widget_data = nwid.u.data_tip.data;
nwc->tool_tip = nwid.u.data_tip.tooltip;
break;
}
case WPT_PADDING:
if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_PADDING requires NWidgetBase");
dest->SetPadding(nwid.u.padding);
break;
case WPT_PIPSPACE: {
NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
if (nwc != nullptr) nwc->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
if (nwb != nullptr) nwb->SetPIP(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
break;
}
case WPT_PIPRATIO: {
NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest);
if (nwc != nullptr) nwc->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest);
if (nwb != nullptr) nwb->SetPIPRatio(nwid.u.pip.pre, nwid.u.pip.inter, nwid.u.pip.post);
if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
break;
}
case WPT_SCROLLBAR: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest);
if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore");
nwc->scrollbar_index = nwid.u.widget.index;
break;
}
case WPT_ASPECT: {
if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_ASPECT requires NWidgetBase");
dest->aspect_ratio = nwid.u.aspect.ratio;
dest->aspect_flags = nwid.u.aspect.flags;
break;
}
default:
NOT_REACHED();
}
}
/**
* Make NWidget from an NWidgetPart.
* @param nwid NWidgetPart.
* @pre NWidgetPart must not be an attribute NWidgetPart nor WPT_ENDCONTAINER.
* @return Pointer to created NWidget.
*/
static std::unique_ptr<NWidgetBase> MakeNWidget(const NWidgetPart &nwid)
{
assert(!IsAttributeWidgetPartType(nwid.type));
assert(nwid.type != WPT_ENDCONTAINER);
switch (nwid.type) {
case NWID_SPACER: return std::make_unique<NWidgetSpacer>(0, 0);
case WWT_PANEL: [[fallthrough]];
case WWT_INSET: [[fallthrough]];
case WWT_FRAME: return std::make_unique<NWidgetBackground>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
case NWID_HORIZONTAL: return std::make_unique<NWidgetHorizontal>(nwid.u.cont_flags);
case NWID_HORIZONTAL_LTR: return std::make_unique<NWidgetHorizontalLTR>(nwid.u.cont_flags);
case NWID_VERTICAL: return std::make_unique<NWidgetVertical>(nwid.u.cont_flags);
case NWID_SELECTION: return std::make_unique<NWidgetStacked>(nwid.u.widget.index);
case NWID_MATRIX: return std::make_unique<NWidgetMatrix>(nwid.u.widget.colour, nwid.u.widget.index);
case NWID_VIEWPORT: return std::make_unique<NWidgetViewport>(nwid.u.widget.index);
case NWID_LAYER: return std::make_unique<NWidgetLayer>(nwid.u.widget.index);
case NWID_HSCROLLBAR: [[fallthrough]];
case NWID_VSCROLLBAR: return std::make_unique<NWidgetScrollbar>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index);
case WPT_FUNCTION: return nwid.u.func_ptr();
default:
assert((nwid.type & WWT_MASK) < WWT_LAST || (nwid.type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
return std::make_unique<NWidgetLeaf>(nwid.type, nwid.u.widget.colour, nwid.u.widget.index, 0x0, STR_NULL);
}
}
/**
* Construct a single nested widget in \a *dest from its parts.
*
* Construct a NWidgetBase object from a #NWidget function, and apply all
* settings that follow it, until encountering a #EndContainer, another
* attributes that follow it, until encountering a #EndContainer, another
* #NWidget, or the end of the parts array.
*
* @param nwid_begin Pointer to beginning of nested widget parts.
* @param nwid_end Pointer to ending of nested widget parts.
* @param dest Address of pointer to use for returning the composed widget.
* @param fill_dest Fill the composed widget with child widgets.
* @return Pointer to remaining nested widget parts.
* @param nwid_begin Iterator to beginning of nested widget parts.
* @param nwid_end Iterator to ending of nested widget parts.
* @param[out] dest Address of pointer to use for returning the composed widget.
* @param[out] fill_dest Fill the composed widget with child widgets.
* @return Iterator to remaining nested widget parts.
*/
static const NWidgetPart *MakeNWidget(const NWidgetPart *nwid_begin, const NWidgetPart *nwid_end, std::unique_ptr<NWidgetBase> &dest, bool *fill_dest)
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)
{
dest = nullptr;
*fill_dest = false;
while (nwid_begin < nwid_end) {
switch (nwid_begin->type) {
case NWID_SPACER:
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetSpacer>(0, 0);
break;
if (IsAttributeWidgetPartType(nwid_begin->type)) [[unlikely]] throw std::runtime_error("Expected non-attribute NWidgetPart type");
if (nwid_begin->type == WPT_ENDCONTAINER) return nwid_begin;
case NWID_HORIZONTAL:
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetHorizontal>(nwid_begin->u.cont_flags);
*fill_dest = true;
break;
fill_dest = IsContainerWidgetType(nwid_begin->type);
dest = MakeNWidget(*nwid_begin);
if (dest == nullptr) return nwid_begin;
case NWID_HORIZONTAL_LTR:
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetHorizontalLTR>(nwid_begin->u.cont_flags);
*fill_dest = true;
break;
++nwid_begin;
case WWT_PANEL:
case WWT_INSET:
case WWT_FRAME:
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetBackground>(nwid_begin->type, nwid_begin->u.widget.colour, nwid_begin->u.widget.index);
*fill_dest = true;
break;
case NWID_VERTICAL:
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetVertical>(nwid_begin->u.cont_flags);
*fill_dest = true;
break;
case NWID_MATRIX: {
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetMatrix>(nwid_begin->u.widget.colour, nwid_begin->u.widget.index);
*fill_dest = true;
break;
}
case WPT_FUNCTION: {
if (dest != nullptr) return nwid_begin;
dest = nwid_begin->u.func_ptr();
*fill_dest = false;
break;
}
case WPT_RESIZE: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest.get());
if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_RESIZE requires NWidgetResizeBase");
assert(nwid_begin->u.xy.x >= 0 && nwid_begin->u.xy.y >= 0);
nwrb->SetResize(nwid_begin->u.xy.x, nwid_begin->u.xy.y);
break;
}
case WPT_MINSIZE: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest.get());
if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINSIZE requires NWidgetResizeBase");
assert(nwid_begin->u.xy.x >= 0 && nwid_begin->u.xy.y >= 0);
nwrb->SetMinimalSize(nwid_begin->u.xy.x, nwid_begin->u.xy.y);
break;
}
case WPT_MINTEXTLINES: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest.get());
if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_MINTEXTLINES requires NWidgetResizeBase");
assert(nwid_begin->u.text_lines.size >= FS_BEGIN && nwid_begin->u.text_lines.size < FS_END);
nwrb->SetMinimalTextLines(nwid_begin->u.text_lines.lines, nwid_begin->u.text_lines.spacing, nwid_begin->u.text_lines.size);
break;
}
case WPT_TEXTSTYLE: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest.get());
if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_TEXTSTYLE requires NWidgetCore");
nwc->SetTextStyle(nwid_begin->u.text_style.colour, nwid_begin->u.text_style.size);
break;
}
case WPT_ALIGNMENT: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest.get());
if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_ALIGNMENT requires NWidgetCore");
nwc->SetAlignment(nwid_begin->u.align.align);
break;
}
case WPT_FILL: {
NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(dest.get());
if (nwrb == nullptr) [[unlikely]] throw std::runtime_error("WPT_FILL requires NWidgetResizeBase");
nwrb->SetFill(nwid_begin->u.xy.x, nwid_begin->u.xy.y);
break;
}
case WPT_DATATIP: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest.get());
if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_DATATIP requires NWidgetCore");
nwc->widget_data = nwid_begin->u.data_tip.data;
nwc->tool_tip = nwid_begin->u.data_tip.tooltip;
break;
}
case WPT_PADDING:
if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_PADDING requires NWidgetBase");
dest->SetPadding(nwid_begin->u.padding);
break;
case WPT_PIPSPACE: {
NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest.get());
if (nwc != nullptr) nwc->SetPIP(nwid_begin->u.pip.pre, nwid_begin->u.pip.inter, nwid_begin->u.pip.post);
NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest.get());
if (nwb != nullptr) nwb->SetPIP(nwid_begin->u.pip.pre, nwid_begin->u.pip.inter, nwid_begin->u.pip.post);
if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPSPACE requires NWidgetPIPContainer or NWidgetBackground");
break;
}
case WPT_PIPRATIO: {
NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(dest.get());
if (nwc != nullptr) nwc->SetPIPRatio(nwid_begin->u.pip.pre, nwid_begin->u.pip.inter, nwid_begin->u.pip.post);
NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(dest.get());
if (nwb != nullptr) nwb->SetPIPRatio(nwid_begin->u.pip.pre, nwid_begin->u.pip.inter, nwid_begin->u.pip.post);
if (nwc == nullptr && nwb == nullptr) [[unlikely]] throw std::runtime_error("WPT_PIPRATIO requires NWidgetPIPContainer or NWidgetBackground");
break;
}
case WPT_SCROLLBAR: {
NWidgetCore *nwc = dynamic_cast<NWidgetCore *>(dest.get());
if (nwc == nullptr) [[unlikely]] throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore");
nwc->scrollbar_index = nwid_begin->u.widget.index;
break;
}
case WPT_ENDCONTAINER:
return nwid_begin;
case NWID_VIEWPORT:
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetViewport>(nwid_begin->u.widget.index);
break;
case NWID_HSCROLLBAR:
case NWID_VSCROLLBAR:
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetScrollbar>(nwid_begin->type, nwid_begin->u.widget.colour, nwid_begin->u.widget.index);
break;
case NWID_SELECTION: {
if (dest != nullptr) return nwid_begin;
dest = std::make_unique<NWidgetStacked>(nwid_begin->u.widget.index);
*fill_dest = true;
break;
}
default:
if (dest != nullptr) return nwid_begin;
assert((nwid_begin->type & WWT_MASK) < WWT_LAST || (nwid_begin->type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
dest = std::make_unique<NWidgetLeaf>(nwid_begin->type, nwid_begin->u.widget.colour, nwid_begin->u.widget.index, 0x0, STR_NULL);
break;
}
nwid_begin++;
/* Once a widget is created, we're now looking for attributes. */
while (nwid_begin != nwid_end && IsAttributeWidgetPartType(nwid_begin->type)) {
ApplyNWidgetPartAttribute(*nwid_begin, dest.get());
++nwid_begin;
}
return nwid_begin;
@@ -3171,17 +3325,17 @@ static const NWidgetPart *MakeNWidget(const NWidgetPart *nwid_begin, const NWidg
bool IsContainerWidgetType(WidgetType tp)
{
return tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == NWID_MATRIX
|| tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION;
|| tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION || tp == NWID_LAYER;
}
/**
* Build a nested widget tree by recursively filling containers with nested widgets read from their parts.
* @param nwid_begin Pointer to beginning of nested widget parts.
* @param nwid_end Pointer to ending of nested widget parts.
* @param nwid_begin Iterator to beginning of nested widget parts.
* @param nwid_end Iterator to ending of nested widget parts.
* @param parent Pointer or container to use for storing the child widgets (*parent == nullptr or *parent == container or background widget).
* @return Pointer to remaining nested widget parts.
* @return Iterator to remaining nested widget parts.
*/
static const NWidgetPart *MakeWidgetTree(const NWidgetPart *nwid_begin, const NWidgetPart *nwid_end, std::unique_ptr<NWidgetBase> &parent)
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)
{
/* If *parent == nullptr, only the first widget is read and returned. Otherwise, *parent must point to either
* a #NWidgetContainer or a #NWidgetBackground object, and parts are added as much as possible. */
@@ -3189,10 +3343,10 @@ static const NWidgetPart *MakeWidgetTree(const NWidgetPart *nwid_begin, const NW
NWidgetBackground *nwid_parent = dynamic_cast<NWidgetBackground *>(parent.get());
assert(parent == nullptr || (nwid_cont != nullptr && nwid_parent == nullptr) || (nwid_cont == nullptr && nwid_parent != nullptr));
for (;;) {
while (nwid_begin != nwid_end) {
std::unique_ptr<NWidgetBase> sub_widget = nullptr;
bool fill_sub = false;
nwid_begin = MakeNWidget(nwid_begin, nwid_end, sub_widget, &fill_sub);
nwid_begin = MakeNWidget(nwid_begin, nwid_end, sub_widget, fill_sub);
/* Break out of loop when end reached */
if (sub_widget == nullptr) break;
@@ -3215,23 +3369,22 @@ static const NWidgetPart *MakeWidgetTree(const NWidgetPart *nwid_begin, const NW
assert(nwid_begin < nwid_end);
assert(nwid_begin->type == WPT_ENDCONTAINER);
return nwid_begin + 1; // *nwid_begin is also 'used'
return std::next(nwid_begin); // *nwid_begin is also 'used'
}
/**
* Construct a nested widget tree from an array of parts.
* @param nwid_begin Pointer to beginning of nested widget parts.
* @param nwid_end Pointer to ending of nested widget parts.
* @param nwid_parts Span of nested widget parts.
* @param container Container to add the nested widgets to. In case it is nullptr a vertical container is used.
* @return Root of the nested widget tree, a vertical container containing the entire GUI.
* @ingroup NestedWidgetParts
*/
std::unique_ptr<NWidgetBase> MakeNWidgets(const NWidgetPart *nwid_begin, const NWidgetPart *nwid_end, std::unique_ptr<NWidgetBase> &&container)
std::unique_ptr<NWidgetBase> MakeNWidgets(std::span<const NWidgetPart> nwid_parts, std::unique_ptr<NWidgetBase> &&container)
{
if (container == nullptr) container = std::make_unique<NWidgetVertical>();
[[maybe_unused]] const NWidgetPart *nwid_part = MakeWidgetTree(nwid_begin, nwid_end, container);
[[maybe_unused]] auto nwid_part = MakeWidgetTree(std::begin(nwid_parts), std::end(nwid_parts), container);
#ifdef WITH_ASSERT
if (nwid_part != nwid_end) [[unlikely]] throw std::runtime_error("Did not consume all NWidgetParts");
if (nwid_part != std::end(nwid_parts)) [[unlikely]] throw std::runtime_error("Did not consume all NWidgetParts");
#endif
return std::move(container);
}
@@ -3240,14 +3393,16 @@ std::unique_ptr<NWidgetBase> MakeNWidgets(const NWidgetPart *nwid_begin, const N
* Make a nested widget tree for a window from a parts array. Besides loading, it inserts a shading selection widget
* between the title bar and the window body if the first widget in the parts array looks like a title bar (it is a horizontal
* container with a caption widget) and has a shade box widget.
* @param nwid_begin Pointer to beginning of nested widget parts.
* @param nwid_end Pointer to ending of nested widget parts.
* @param nwid_parts Span of nested widget parts.
* @param[out] shade_select Pointer to the inserted shade selection widget (\c nullptr if not unserted).
* @return Root of the nested widget tree, a vertical container containing the entire GUI.
* @ingroup NestedWidgetParts
*/
std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(const NWidgetPart *nwid_begin, const NWidgetPart *nwid_end, NWidgetStacked **shade_select)
std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(std::span<const NWidgetPart> nwid_parts, NWidgetStacked **shade_select)
{
auto nwid_begin = std::begin(nwid_parts);
auto nwid_end = std::end(nwid_parts);
*shade_select = nullptr;
/* Read the first widget recursively from the array. */
@@ -3266,13 +3421,13 @@ std::unique_ptr<NWidgetBase> MakeWindowNWidgetTree(const NWidgetPart *nwid_begin
auto shade_stack = std::make_unique<NWidgetStacked>(-1);
*shade_select = shade_stack.get();
/* Load the remaining parts into the shade stack. */
shade_stack->Add(MakeNWidgets(nwid_begin, nwid_end, std::make_unique<NWidgetVertical>()));
shade_stack->Add(MakeNWidgets({nwid_begin, nwid_end}, std::make_unique<NWidgetVertical>()));
root->Add(std::move(shade_stack));
return root;
}
/* Load the remaining parts into 'root'. */
return MakeNWidgets(nwid_begin, nwid_end, std::move(root));
return MakeNWidgets({nwid_begin, nwid_end}, std::move(root));
}
/**
@@ -3292,7 +3447,7 @@ std::unique_ptr<NWidgetBase> MakeCompanyButtonRows(WidgetID widget_first, Widget
std::unique_ptr<NWidgetHorizontal> hor = nullptr; // Storage for buttons in one row.
int hor_length = 0;
Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_OUT_4X);
Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_NORMAL);
sprite_size.width += WidgetDimensions::unscaled.matrix.Horizontal();
sprite_size.height += WidgetDimensions::unscaled.matrix.Vertical();