Change: Ability to set aspect ratio of a widget.

This allows setting the shape of a widget without dealing with absolute pixel sizes.
This commit is contained in:
Peter Nelson 2024-03-27 09:02:40 +00:00 committed by Peter Nelson
parent f267b37a33
commit d43ff8dc49
4 changed files with 95 additions and 0 deletions

View File

@ -127,6 +127,7 @@ public:
}
this->smallest_x = this->children.front()->smallest_x + this->children.back()->smallest_x; // First and last are always shown, rest not
this->ApplyAspectRatio();
}
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override

View File

@ -1869,6 +1869,7 @@ public:
this->fill_y = (display->fill_y == 0 && bar->fill_y == 0) ? 0 : std::min(display->fill_y, bar->fill_y);
this->resize_x = std::max(display->resize_x, bar->resize_x);
this->resize_y = std::min(display->resize_y, bar->resize_y);
this->ApplyAspectRatio();
}
void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) override

View File

@ -925,6 +925,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 ((this->aspect_flags & AspectFlags::ResizeX) == AspectFlags::ResizeX) x = std::max(this->smallest_x, static_cast<uint>(this->smallest_y * std::abs(this->aspect_ratio)));
if ((this->aspect_flags & AspectFlags::ResizeY) == 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 +956,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) {
@ -1224,6 +1260,7 @@ void NWidgetStacked::SetupSmallestSize(Window *w)
this->fill_y = fill.height;
this->resize_x = resize.width;
this->resize_y = resize.height;
this->ApplyAspectRatio();
return;
}
@ -1243,6 +1280,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();
}
}
@ -1405,6 +1443,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;
@ -1594,6 +1637,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;
@ -1730,6 +1778,7 @@ void NWidgetSpacer::SetupSmallestSize(Window *)
{
this->smallest_x = this->min_x;
this->smallest_y = this->min_y;
this->ApplyAspectRatio();
}
void NWidgetSpacer::FillWidgetLookup(WidgetLookup &)
@ -1841,6 +1890,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)
@ -2089,6 +2139,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};
@ -2117,6 +2168,7 @@ void NWidgetBackground::SetupSmallestSize(Window *w)
this->fill_y = fill.height;
this->resize_x = resize.width;
this->resize_y = resize.height;
this->ApplyAspectRatio();
}
}
@ -2203,6 +2255,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)
@ -2738,6 +2791,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)
@ -3045,6 +3099,13 @@ static const NWidgetPart *MakeNWidget(const NWidgetPart *nwid_begin, const NWidg
break;
}
case WPT_ASPECT: {
if (dest == nullptr) [[unlikely]] throw std::runtime_error("WPT_ASPECT requires NWidgetBase");
dest->aspect_ratio = nwid_begin->u.aspect.ratio;
dest->aspect_flags = nwid_begin->u.aspect.flags;
break;
}
case WPT_ENDCONTAINER:
return nwid_begin;

View File

@ -100,6 +100,7 @@ enum WidgetType {
WPT_ENDCONTAINER, ///< Widget part to denote end of a container.
WPT_FUNCTION, ///< Widget part for calling a user function.
WPT_SCROLLBAR, ///< Widget part for attaching a scrollbar.
WPT_ASPECT, ///< Widget part for sepcifying aspect ratio.
/* Pushable window widget types. */
WWT_MASK = 0x7F,
@ -119,6 +120,13 @@ enum SizingType {
ST_RESIZE, ///< Resize the nested widget tree.
};
enum class AspectFlags : uint8_t {
ResizeX = 1U << 0,
ResizeY = 1U << 1,
ResizeXY = ResizeX | ResizeY,
};
DECLARE_ENUM_AS_BIT_SET(AspectFlags)
/* Forward declarations. */
class NWidgetCore;
class Scrollbar;
@ -136,6 +144,7 @@ class NWidgetBase : public ZeroedMemoryAllocator {
public:
NWidgetBase(WidgetType tp);
void ApplyAspectRatio();
virtual void AdjustPaddingForZoom();
virtual void SetupSmallestSize(Window *w) = 0;
virtual void AssignSizePosition(SizingType sizing, int x, int y, uint given_width, uint given_height, bool rtl) = 0;
@ -232,6 +241,8 @@ public:
/* Current widget size (that is, after resizing). */
uint current_x; ///< Current horizontal size (after resizing).
uint current_y; ///< Current vertical size (after resizing).
float aspect_ratio = 0; ///< Desired aspect ratio of widget.
AspectFlags aspect_flags = AspectFlags::ResizeX; ///< Which dimensions can be resized.
int pos_x; ///< Horizontal position of top-left corner of the widget in the window.
int pos_y; ///< Vertical position of top-left corner of the widget in the window.
@ -298,6 +309,8 @@ public:
void SetMinimalTextLines(uint8_t min_lines, uint8_t spacing, FontSize size);
void SetFill(uint fill_x, uint fill_y);
void SetResize(uint resize_x, uint resize_y);
void SetAspect(float ratio, AspectFlags flags = AspectFlags::ResizeX);
void SetAspect(int x_ratio, int y_ratio, AspectFlags flags = AspectFlags::ResizeX);
bool UpdateMultilineWidgetSize(const std::string &str, int max_lines);
bool UpdateSize(uint min_x, uint min_y);
@ -1045,6 +1058,11 @@ struct NWidgetPartAlignment {
StringAlignment align; ///< Alignment of text/image.
};
struct NWidgetPartAspect {
float ratio;
AspectFlags flags;
};
/**
* Pointer to function returning a nested widget.
* @return Nested widget (tree).
@ -1068,6 +1086,7 @@ struct NWidgetPart {
NWidgetPartAlignment align; ///< Part with internal alignment.
NWidgetFunctionType *func_ptr; ///< Part with a function call.
NWidContainerFlags cont_flags; ///< Part with container flags.
NWidgetPartAspect aspect; ///< Part to set aspect ratio.
/* Constructors for each NWidgetPartUnion data type. */
constexpr NWidgetPartUnion() : xy() {}
@ -1081,6 +1100,7 @@ struct NWidgetPart {
constexpr NWidgetPartUnion(NWidgetPartAlignment align) : align(align) {}
constexpr NWidgetPartUnion(NWidgetFunctionType *func_ptr) : func_ptr(func_ptr) {}
constexpr NWidgetPartUnion(NWidContainerFlags cont_flags) : cont_flags(cont_flags) {}
constexpr NWidgetPartUnion(NWidgetPartAspect aspect) : aspect(aspect) {}
} u;
/* Constructors for each NWidgetPart data type. */
@ -1095,6 +1115,7 @@ struct NWidgetPart {
constexpr NWidgetPart(WidgetType type, NWidgetPartAlignment align) : type(type), u(align) {}
constexpr NWidgetPart(WidgetType type, NWidgetFunctionType *func_ptr) : type(type), u(func_ptr) {}
constexpr NWidgetPart(WidgetType type, NWidContainerFlags cont_flags) : type(type), u(cont_flags) {}
constexpr NWidgetPart(WidgetType type, NWidgetPartAspect aspect) : type(type), u(aspect) {}
};
/**
@ -1266,6 +1287,17 @@ constexpr NWidgetPart SetScrollbar(WidgetID index)
return NWidgetPart{WPT_SCROLLBAR, NWidgetPartWidget{INVALID_COLOUR, index}};
}
/**
* Widget part function for setting the aspect ratio.
* @param ratio Desired aspect ratio, or 0 for none.
* @param flags Dimensions which should be resized.
* @ingroup NestedWidgetParts
*/
constexpr NWidgetPart SetAspect(float ratio, AspectFlags flags = AspectFlags::ResizeX)
{
return NWidgetPart{WPT_ASPECT, NWidgetPartAspect{ratio, flags}};
}
/**
* Widget part function for starting a new 'real' widget.
* @param tp Type of the new nested widget.