diff --git a/src/widget.cpp b/src/widget.cpp index f53389cd58..be40716e46 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -1498,6 +1498,22 @@ void NWidgetPIPContainer::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip this->pip_post = ScaleGUITrad(this->uz_pip_post); } +/** + * Set additional pre/inter/post space for the container. + * + * @param pip_ratio_pre Ratio of additional space in front of the first child widget (above + * for the vertical container, at the left for the horizontal container). + * @param pip_ratio_inter Ratio of additional space between two child widgets. + * @param pip_ratio_post Ratio of additional space after the last child widget (below for the + * vertical container, at the right for the horizontal container). + */ +void NWidgetPIPContainer::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post) +{ + this->pip_ratio_pre = pip_ratio_pre; + this->pip_ratio_inter = pip_ratio_inter; + this->pip_ratio_post = pip_ratio_post; +} + /** Horizontal container widget. */ NWidgetHorizontal::NWidgetHorizontal(NWidContainerFlags flags) : NWidgetPIPContainer(NWID_HORIZONTAL, flags) { @@ -1562,6 +1578,7 @@ void NWidgetHorizontal::SetupSmallestSize(Window *w, bool init_array) } this->resize_y = LeastCommonMultiple(this->resize_y, child_wid->resize_y); } + if (this->fill_x == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_x = 1; /* 4. Increase by required PIP space. */ this->smallest_x += this->pip_pre + this->gaps * this->pip_inter + this->pip_post; } @@ -1572,7 +1589,7 @@ void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint /* Compute additional width given to us. */ uint additional_length = given_width; - if (sizing == ST_SMALLEST && (this->flags & NC_EQUALSIZE)) { + if (this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post != 0 || (sizing == ST_SMALLEST && (this->flags & NC_EQUALSIZE))) { /* For EQUALSIZE containers this does not sum to smallest_x during initialisation */ additional_length -= this->pip_pre + this->gaps * this->pip_inter + this->pip_post; for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) { @@ -1653,8 +1670,21 @@ void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint } assert(num_changing_childs == 0); + uint pre = this->pip_pre; + uint inter = this->pip_inter; + + if (additional_length > 0) { + /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post + * which is never explicitly needed. */ + int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post; + if (r > 0) { + pre += this->pip_ratio_pre * additional_length / r; + if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r; + } + } + /* Third loop: Compute position and call the child. */ - uint position = rtl ? this->current_x - this->pip_pre : this->pip_pre; // Place to put next child relative to origin of the container. + uint position = rtl ? this->current_x - pre : pre; // Place to put next child relative to origin of the container. NWidgetBase *child_wid = this->head; while (child_wid != nullptr) { uint child_width = child_wid->current_x; @@ -1662,7 +1692,7 @@ void NWidgetHorizontal::AssignSizePosition(SizingType sizing, int x, int y, uint uint child_y = y + child_wid->padding.top; child_wid->AssignSizePosition(sizing, child_x, child_y, child_width, child_wid->current_y, rtl); - uint padded_child_width = child_width + child_wid->padding.Horizontal() + this->pip_inter; + uint padded_child_width = child_width + child_wid->padding.Horizontal() + inter; position = rtl ? position - padded_child_width : position + padded_child_width; child_wid = child_wid->next; @@ -1744,6 +1774,7 @@ void NWidgetVertical::SetupSmallestSize(Window *w, bool init_array) } this->resize_x = LeastCommonMultiple(this->resize_x, child_wid->resize_x); } + if (this->fill_y == 0 && this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post > 0) this->fill_y = 1; /* 4. Increase by required PIP space. */ this->smallest_y += this->pip_pre + this->gaps * this->pip_inter + this->pip_post; } @@ -1754,7 +1785,7 @@ void NWidgetVertical::AssignSizePosition(SizingType sizing, int x, int y, uint g /* Compute additional height given to us. */ uint additional_length = given_height; - if (sizing == ST_SMALLEST && (this->flags & NC_EQUALSIZE)) { + if (this->pip_ratio_pre + this->pip_ratio_inter + this->pip_ratio_post != 0 || (sizing == ST_SMALLEST && (this->flags & NC_EQUALSIZE))) { /* For EQUALSIZE containers this does not sum to smallest_y during initialisation */ additional_length -= this->pip_pre + this->gaps * this->pip_inter + this->pip_post; for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) { @@ -1826,14 +1857,27 @@ void NWidgetVertical::AssignSizePosition(SizingType sizing, int x, int y, uint g } assert(num_changing_childs == 0); + uint pre = this->pip_pre; + uint inter = this->pip_inter; + + if (additional_length > 0) { + /* Allocate remaining space by pip ratios. If this doesn't round exactly, the unused space will fall into pip_post + * which is never explicitly needed. */ + int r = this->pip_ratio_pre + this->gaps * this->pip_ratio_inter + this->pip_ratio_post; + if (r > 0) { + pre += this->pip_ratio_pre * additional_length / r; + if (this->gaps > 0) inter += this->pip_ratio_inter * additional_length / r; + } + } + /* Third loop: Compute position and call the child. */ - uint position = this->pip_pre; // Place to put next child relative to origin of the container. + uint position = pre; // Place to put next child relative to origin of the container. for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) { uint child_x = x + (rtl ? child_wid->padding.right : child_wid->padding.left); uint child_height = child_wid->current_y; child_wid->AssignSizePosition(sizing, child_x, y + position + child_wid->padding.top, child_wid->current_x, child_height, rtl); - position += child_height + child_wid->padding.Vertical() + this->pip_inter; + position += child_height + child_wid->padding.Vertical() + inter; } } @@ -2170,6 +2214,24 @@ void NWidgetBackground::SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_p this->child->SetPIP(pip_pre, pip_inter, pip_post); } +/** + * Set additional pre/inter/post space ratios for the background widget. + * + * @param pip_ratio_pre Ratio of additional space in front of the first child widget (above + * for the vertical container, at the left for the horizontal container). + * @param pip_ratio_inter Ratio of additional space between two child widgets. + * @param pip_ratio_post Ratio of additional space after the last child widget (below for the + * vertical container, at the right for the horizontal container). + * @note Using this function implies that the widget has (or will have) child widgets. + */ +void NWidgetBackground::SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post) +{ + if (this->child == nullptr) { + this->child = new NWidgetVertical(); + } + this->child->SetPIPRatio(pip_ratio_pre, pip_ratio_inter, pip_ratio_post); +} + void NWidgetBackground::AdjustPaddingForZoom() { if (child != nullptr) child->AdjustPaddingForZoom(); @@ -3178,6 +3240,15 @@ static const NWidgetPart *MakeNWidget(const NWidgetPart *nwid_begin, const NWidg break; } + case WPT_PIPRATIO: { + NWidgetPIPContainer *nwc = dynamic_cast(*dest); + if (nwc != nullptr) nwc->SetPIPRatio(nwid_begin->u.pip.pre, nwid_begin->u.pip.inter, nwid_begin->u.pip.post); + + NWidgetBackground *nwb = dynamic_cast(*dest); + if (nwb != nullptr) nwb->SetPIPRatio(nwid_begin->u.pip.pre, nwid_begin->u.pip.inter, nwid_begin->u.pip.post); + break; + } + case WPT_SCROLLBAR: { NWidgetCore *nwc = dynamic_cast(*dest); if (unlikely(nwc == nullptr)) throw std::runtime_error("WPT_SCROLLBAR requires NWidgetCore"); diff --git a/src/widget_type.h b/src/widget_type.h index d6caaa7fee..9898520611 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -87,6 +87,7 @@ enum WidgetType { WPT_DATATIP, ///< Widget part for specifying data and tooltip. WPT_PADDING, ///< Widget part for specifying a padding. WPT_PIPSPACE, ///< Widget part for specifying pre/inter/post space for containers. + WPT_PIPRATIO, ///< Widget part for specifying pre/inter/post ratio for containers. WPT_TEXTSTYLE, ///< Widget part for specifying text colour. WPT_ALIGNMENT, ///< Widget part for specifying text/image alignment. WPT_ENDCONTAINER, ///< Widget part to denote end of a container. @@ -482,12 +483,16 @@ public: void AdjustPaddingForZoom() override; void SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post); + void SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_rato_post); protected: NWidContainerFlags flags; ///< Flags of the container. uint8_t pip_pre; ///< Amount of space before first widget. uint8_t pip_inter; ///< Amount of space between widgets. uint8_t pip_post; ///< Amount of space after last widget. + uint8_t pip_ratio_pre; ///< Ratio of remaining space before first widget. + uint8_t pip_ratio_inter; ///< Ratio of remaining space between widgets. + uint8_t pip_ratio_post; ///< Ratio of remaining space after last widget. uint8_t uz_pip_pre; ///< Unscaled space before first widget. uint8_t uz_pip_inter; ///< Unscaled space between widgets. @@ -598,6 +603,7 @@ public: void Add(NWidgetBase *nwid); void SetPIP(uint8_t pip_pre, uint8_t pip_inter, uint8_t pip_post); + void SetPIPRatio(uint8_t pip_ratio_pre, uint8_t pip_ratio_inter, uint8_t pip_ratio_post); void AdjustPaddingForZoom() override; void SetupSmallestSize(Window *w, bool init_array) override; @@ -1228,6 +1234,25 @@ static inline NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post) return part; } +/** + * Widget part function for setting a pre/inter/post ratio. + * @param pre The ratio of space before the first widget. + * @param inter The ratio of space between widgets. + * @param post The ratio of space after the last widget. + * @ingroup NestedWidgetParts + */ +static inline NWidgetPart SetPIPRatio(uint8_t ratio_pre, uint8_t ratio_inter, uint8_t ratio_post) +{ + NWidgetPart part; + + part.type = WPT_PIPRATIO; + part.u.pip.pre = ratio_pre; + part.u.pip.inter = ratio_inter; + part.u.pip.post = ratio_post; + + return part; +} + /** * Attach a scrollbar to a widget. * The scrollbar is controlled when using the mousewheel on the widget.