diff options
-rw-r--r-- | src/widget.cpp | 102 | ||||
-rw-r--r-- | src/widget_type.h | 44 |
2 files changed, 131 insertions, 15 deletions
diff --git a/src/widget.cpp b/src/widget.cpp index e47e942c7..ab8011825 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -669,6 +669,7 @@ void Window::DrawSortButtonState(int widget, SortButtonState state) const * never swap order. * - #NWidgetVertical for organizing child widgets underneath each other. * - #NWidgetBackground for adding a background behind its child widget. + * - #NWidgetStacked for stacking child widgets on top of each other. * * @see NestedWidgetParts */ @@ -896,6 +897,86 @@ void NWidgetContainer::Add(NWidgetBase *wid) } /** + * Widgets stacked on top of each other. + * @param tp Kind of stacking, must be either #NWID_SELECTION or #NWID_LAYERED. + */ +NWidgetStacked::NWidgetStacked(WidgetType tp) : NWidgetContainer(tp) +{ +} + +int NWidgetStacked::ComputeMinimalSize() +{ + /* First sweep, recurse down and compute minimal size and filling. */ + int biggest_index = -1; + this->min_x = 0; + this->min_y = 0; + this->fill_x = (this->head != NULL); + this->fill_y = (this->head != NULL); + this->resize_x = (this->head != NULL) ? 1 : 0; + this->resize_y = (this->head != NULL) ? 1 : 0; + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + int idx = child_wid->ComputeMinimalSize(); + biggest_index = max(biggest_index, idx); + + this->min_x = max(this->min_x, child_wid->min_x + child_wid->padding_left + child_wid->padding_right); + this->min_y = max(this->min_y, child_wid->min_y + child_wid->padding_top + child_wid->padding_bottom); + this->fill_x &= child_wid->fill_x; + this->fill_y &= child_wid->fill_y; + this->resize_x = LeastCommonMultiple(this->resize_x, child_wid->resize_x); + this->resize_y = LeastCommonMultiple(this->resize_y, child_wid->resize_y); + } + return biggest_index; +} + +void NWidgetStacked::AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl) +{ + assert(given_width >= this->min_x && given_height >= this->min_y); + + this->pos_x = x; + this->pos_y = y; + this->min_x = given_width; + this->min_y = given_height; + if (!allow_resize_x) this->resize_x = 0; + if (!allow_resize_y) this->resize_y = 0; + + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + /* Decide about horizontal position and filling of the child. */ + uint child_width; + int child_pos_x; + if (child_wid->fill_x) { + child_width = given_width - child_wid->padding_left - child_wid->padding_right; + child_pos_x = (rtl ? child_wid->padding_right : child_wid->padding_left); + } else { + child_width = child_wid->min_x; + child_pos_x = (given_width - child_wid->padding_left - child_wid->padding_right - child_width) / 2 + (rtl ? child_wid->padding_right : child_wid->padding_left); + } + + /* Decide about vertical position and filling of the child. */ + uint child_height; + int child_pos_y; + if (child_wid->fill_y) { + child_height = given_height - child_wid->padding_top - child_wid->padding_bottom; + child_pos_y = 0; + } else { + child_height = child_wid->min_y; + child_pos_y = (given_height - child_wid->padding_top - child_wid->padding_bottom - child_height) / 2; + } + child_wid->AssignMinimalPosition(x + child_pos_x, y + child_pos_y, child_width, child_height, (this->resize_x > 0), (this->resize_y > 0), rtl); + } +} + +void NWidgetStacked::StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) +{ + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + child_wid->StoreWidgets(widgets, length, left_moving, top_moving, rtl); + } +} + +NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp) : NWidgetContainer(tp) +{ +} + +/** * Set additional pre/inter/post space for the container. * * @param pip_pre Additional space in front of the first child widget (above @@ -904,7 +985,7 @@ void NWidgetContainer::Add(NWidgetBase *wid) * @param pip_post Additional space after the last child widget (below for the * vertical container, at the right for the horizontal container). */ -void NWidgetContainer::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post) +void NWidgetPIPContainer::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post) { this->pip_pre = pip_pre; this->pip_inter = pip_inter; @@ -912,7 +993,7 @@ void NWidgetContainer::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post) } /** Horizontal container widget. */ -NWidgetHorizontal::NWidgetHorizontal() : NWidgetContainer(NWID_HORIZONTAL) +NWidgetHorizontal::NWidgetHorizontal() : NWidgetPIPContainer(NWID_HORIZONTAL) { } @@ -1037,7 +1118,7 @@ void NWidgetHorizontalLTR::StoreWidgets(Widget *widgets, int length, bool left_m } /** Vertical container widget. */ -NWidgetVertical::NWidgetVertical() : NWidgetContainer(NWID_VERTICAL) +NWidgetVertical::NWidgetVertical() : NWidgetPIPContainer(NWID_VERTICAL) { } @@ -1169,7 +1250,7 @@ void NWidgetSpacer::StoreWidgets(Widget *widgets, int length, bool left_moving, * vertical container will be inserted while adding the first * child widget. */ -NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, int index, NWidgetContainer *child) : NWidgetCore(tp, colour, true, true, 0x0, STR_NULL) +NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, int index, NWidgetPIPContainer *child) : NWidgetCore(tp, colour, true, true, 0x0, STR_NULL) { this->SetIndex(index); assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME); @@ -1487,6 +1568,14 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, *fill_dest = false; break; + case NWID_SELECTION: + case NWID_LAYERED: + if (*dest != NULL) return num_used; + *dest = new NWidgetStacked(parts->type); + *fill_dest = true; + break; + + case WPT_RESIZE: { NWidgetResizeBase *nwrb = dynamic_cast<NWidgetResizeBase *>(*dest); if (nwrb != NULL) { @@ -1559,7 +1648,7 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, } case WPT_PIPSPACE: { - NWidgetContainer *nwc = dynamic_cast<NWidgetContainer *>(*dest); + NWidgetPIPContainer *nwc = dynamic_cast<NWidgetPIPContainer *>(*dest); if (nwc != NULL) nwc->SetPIP(parts->u.pip.pre, parts->u.pip.inter, parts->u.pip.post); NWidgetBackground *nwb = dynamic_cast<NWidgetBackground *>(*dest); @@ -1614,7 +1703,8 @@ static int MakeWidgetTree(const NWidgetPart *parts, int count, NWidgetBase *pare /* If sub-widget is a container, recursively fill that container. */ WidgetType tp = sub_widget->type; - if (fill_sub && (tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET)) { + if (fill_sub && (tp == NWID_HORIZONTAL || tp == NWID_HORIZONTAL_LTR || tp == NWID_VERTICAL + || tp == WWT_PANEL || tp == WWT_FRAME || tp == WWT_INSET || tp == NWID_SELECTION || tp == NWID_LAYERED)) { int num_used = MakeWidgetTree(parts, count - total_used, sub_widget); parts += num_used; total_used += num_used; diff --git a/src/widget_type.h b/src/widget_type.h index 0a7772163..0128d510d 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -100,6 +100,8 @@ enum WidgetType { NWID_HORIZONTAL_LTR, ///< Horizontal container that doesn't change the order of the widgets for RTL languages. NWID_VERTICAL, ///< Vertical container. NWID_SPACER, ///< Invisible widget that takes some space. + NWID_SELECTION, ///< Stacked widgets, only one visible at a time (eg in a panel with tabs). + NWID_LAYERED, ///< Widgets layered on top of each other, all visible at the same time. /* Nested widget part types. */ WPT_RESIZE, ///< Widget part for specifying resizing. @@ -214,23 +216,43 @@ public: ~NWidgetContainer(); void Add(NWidgetBase *wid); - void SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post); /** Return whether the container is empty. */ inline bool IsEmpty() { return head == NULL; }; protected: + NWidgetBase *head; ///< Pointer to first widget in container. + NWidgetBase *tail; ///< Pointer to last widget in container. +}; + +/** Stacked widgets, widgets all occupying the same space in the window. + * @note the semantics difference between #NWID_SELECTION and #NWID_LAYERED is currently not used. + */ +class NWidgetStacked : public NWidgetContainer { +public: + NWidgetStacked(WidgetType tp); + + int ComputeMinimalSize(); + void AssignMinimalPosition(uint x, uint y, uint given_width, uint given_height, bool allow_resize_x, bool allow_resize_y, bool rtl); + void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); +}; + +/** Container with pre/inter/post child space. */ +class NWidgetPIPContainer : public NWidgetContainer { +public: + NWidgetPIPContainer(WidgetType tp); + + void SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post); + +protected: uint8 pip_pre; ///< Amount of space before first widget. uint8 pip_inter; ///< Amount of space between widgets. uint8 pip_post; ///< Amount of space after last widget. - - NWidgetBase *head; ///< Pointer to first widget in container. - NWidgetBase *tail; ///< Pointer to last widget in container. }; /** Horizontal container. * @ingroup NestedWidgets */ -class NWidgetHorizontal : public NWidgetContainer { +class NWidgetHorizontal : public NWidgetPIPContainer { public: NWidgetHorizontal(); @@ -253,7 +275,7 @@ public: /** Vertical container. * @ingroup NestedWidgets */ -class NWidgetVertical : public NWidgetContainer { +class NWidgetVertical : public NWidgetPIPContainer { public: NWidgetVertical(); @@ -278,7 +300,7 @@ public: * @ingroup NestedWidgets */ class NWidgetBackground : public NWidgetCore { public: - NWidgetBackground(WidgetType tp, Colours colour, int index, NWidgetContainer *child = NULL); + NWidgetBackground(WidgetType tp, Colours colour, int index, NWidgetPIPContainer *child = NULL); ~NWidgetBackground(); void Add(NWidgetBase *nwid); @@ -289,7 +311,7 @@ public: void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); private: - NWidgetContainer *child; ///< Child widget. + NWidgetPIPContainer *child; ///< Child widget. }; /** Leaf widget. @@ -333,6 +355,10 @@ bool CompareWidgetArrays(const Widget *orig, const Widget *gen, bool report = tr * the child widgets (it has no meaning for the compiler but it makes the widget parts easier to read). * Below the last child widget, use an #EndContainer part. This part should be aligned with the #NWidget part that started the container. * + * - Stacked widgets #NWidgetStacked map each of their childs onto the same space. It behaves like a container, except there is no pre/inter/post space, + * so the widget does not support #SetPIP. #SetPadding is allowed though. + * Like the other container widgets, below the last child widgets, a #EndContainer part should be used to denote the end of the stacked widget. + * * - Background widgets #NWidgetBackground start with a #NWidget(WidgetType tp, Colours col, int16 idx) part. * What follows depends on how the widget is used. * - If the widget is used as a leaf widget, that is, to create some space in the window to display a viewport or some text, use the properties of the @@ -589,7 +615,7 @@ static inline NWidgetPart NWidget(WidgetType tp, Colours col, int16 idx) /** * Widget part function for starting a new horizontal container, vertical container, or spacer widget. - * @param tp Type of the new nested widget, #NWID_HORIZONTAL(_LTR), #NWID_VERTICAL, or #NWID_SPACER + * @param tp Type of the new nested widget, #NWID_HORIZONTAL(_LTR), #NWID_VERTICAL, #NWID_SPACER, #NWID_SELECTION, or #NWID_LAYERED. * @ingroup NestedWidgetParts */ static inline NWidgetPart NWidget(WidgetType tp) |