diff --git a/src/widget.cpp b/src/widget.cpp --- a/src/widget.cpp +++ b/src/widget.cpp @@ -504,6 +504,11 @@ static inline void DrawDropdown(const Re */ void Window::DrawWidgets() const { + if (this->nested_root != NULL) { + this->nested_root->Draw(this); + return; + } + const DrawPixelInfo *dpi = _cur_dpi; for (uint i = 0; i < this->widget_count; i++) { @@ -741,8 +746,16 @@ void Window::DrawSortButtonState(int wid if (state == SBS_OFF) return; int offset = this->IsWidgetLowered(widget) ? 1 : 0; - int base = offset + (_dynlang.text_dir == TD_LTR ? this->widget[widget].right - 11 : this->widget[widget].left); - DrawString(base, base + 11, this->widget[widget].top + 1 + offset, state == SBS_DOWN ? DOWNARROW : UPARROW, TC_BLACK, SA_CENTER); + int base, top; + if (this->widget != NULL) { + base = offset + (_dynlang.text_dir == TD_LTR ? this->widget[widget].right - 11 : this->widget[widget].left); + top = this->widget[widget].top; + } else { + assert(this->nested_array != NULL); + base = offset + this->nested_array[widget]->pos_x + (_dynlang.text_dir == TD_LTR ? this->nested_array[widget]->current_x - 11 : 0); + top = this->nested_array[widget]->pos_y; + } + DrawString(base, base + 11, top + 1 + offset, state == SBS_DOWN ? DOWNARROW : UPARROW, TC_BLACK, SA_CENTER); } @@ -883,6 +896,23 @@ inline void NWidgetBase::StoreSizePositi */ /** + * @fn void Draw(const Window *w) + * Draw the widgets of the tree. + * @param w Window that owns the tree. + */ + +/** + * Mark the widget as 'dirty' (in need of repaint). + * @param w Window owning the widget. + */ +void NWidgetBase::Invalidate(const Window *w) const +{ + int abs_left = w->left + this->pos_x; + int abs_top = w->top + this->pos_y; + SetDirtyBlocks(abs_left, abs_top, abs_left + this->current_x, abs_top + this->current_y); +} + +/** * Constructor for resizable nested widgets. * @param tp Nested widget type. * @param fill_x Allow horizontal filling from initial size. @@ -1151,6 +1181,15 @@ void NWidgetStacked::StoreWidgets(Widget } } +void NWidgetStacked::Draw(const Window *w) +{ + assert(this->type == NWID_LAYERED); // Currently, NWID_SELECTION is not supported. + /* Render from back to front. */ + for (NWidgetBase *child_wid = this->tail; child_wid != NULL; child_wid = child_wid->prev) { + child_wid->Draw(w); + } +} + NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp) : NWidgetContainer(tp) { } @@ -1171,6 +1210,13 @@ void NWidgetPIPContainer::SetPIP(uint8 p this->pip_post = pip_post; } +void NWidgetPIPContainer::Draw(const Window *w) +{ + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + child_wid->Draw(w); + } +} + /** Horizontal container widget. */ NWidgetHorizontal::NWidgetHorizontal() : NWidgetPIPContainer(NWID_HORIZONTAL) { @@ -1447,6 +1493,16 @@ void NWidgetSpacer::StoreWidgets(Widget /* Spacer widgets are never stored in the widget array. */ } +void NWidgetSpacer::Draw(const Window *w) +{ + /* Spacer widget is never visible. */ +} + +void NWidgetSpacer::Invalidate(const Window *w) const +{ + /* Spacer widget never need repainting. */ +} + /** * Constructor parent nested widgets. * @param tp Type of parent widget. @@ -1547,6 +1603,44 @@ void NWidgetBackground::FillNestedArray( if (this->child != NULL) this->child->FillNestedArray(array, length); } +void NWidgetBackground::Draw(const Window *w) +{ + if (this->current_x == 0 || this->current_y == 0) return; + + Rect r; + r.left = this->pos_x; + r.right = this->pos_x + this->current_x - 1; + r.top = this->pos_y; + r.bottom = this->pos_y + this->current_y - 1; + + const DrawPixelInfo *dpi = _cur_dpi; + if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return; + + switch (this->type) { + case WWT_PANEL: + assert(this->widget_data == 0); + DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, this->IsLowered() ? FR_LOWERED : FR_NONE); + break; + + case WWT_FRAME: + DrawFrame(r, this->colour, this->widget_data); + break; + + case WWT_INSET: + DrawInset(r, this->colour, this->widget_data); + break; + + default: + NOT_REACHED(); + } + + if (this->child != NULL) this->child->Draw(w); + + if (this->IsDisabled()) { + GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[this->colour & 0xF][2], FILLRECT_CHECKER); + } +} + /** * Nested leaf widget. * @param tp Type of leaf widget. @@ -1635,6 +1729,116 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, } } +void NWidgetLeaf::Draw(const Window *w) +{ + if (this->current_x == 0 || this->current_y == 0) return; + + Rect r; + r.left = this->pos_x; + r.right = this->pos_x + this->current_x - 1; + r.top = this->pos_y; + r.bottom = this->pos_y + this->current_y - 1; + + const DrawPixelInfo *dpi = _cur_dpi; + if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return; + + bool clicked = this->IsLowered(); + switch (this->type) { + case WWT_EMPTY: + break; + + case WWT_PUSHBTN: + assert(this->widget_data == 0); + DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE); + break; + + case WWT_IMGBTN: + case WWT_PUSHIMGBTN: + case WWT_IMGBTN_2: + DrawImageButtons(r, this->type,this->colour, clicked, this->widget_data); + break; + + case WWT_TEXTBTN: + case WWT_PUSHTXTBTN: + case WWT_TEXTBTN_2: + DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE); + DrawLabel(r, this->type, clicked, this->widget_data); + break; + + case WWT_LABEL: + DrawLabel(r, this->type, clicked, this->widget_data); + break; + + case WWT_TEXT: + DrawText(r, (TextColour)this->colour, this->widget_data); + break; + + case WWT_MATRIX: + DrawMatrix(r, this->colour, clicked, this->widget_data); + break; + + case WWT_EDITBOX: + DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, FR_LOWERED | FR_DARKENED); + break; + + case WWT_SCROLLBAR: + assert(this->widget_data == 0); + DrawVerticalScrollbar(r, this->colour, (w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_UP, + (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_MIDDLE, + (w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == WF_SCROLL_DOWN, &w->vscroll); + break; + + case WWT_SCROLL2BAR: + assert(this->widget_data == 0); + DrawVerticalScrollbar(r, this->colour, (w->flags4 & (WF_SCROLL_UP | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_UP | WF_SCROLL2), + (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_MIDDLE | WF_SCROLL2), + (w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL | WF_SCROLL2)) == (WF_SCROLL_DOWN | WF_SCROLL2), &w->vscroll2); + break; + + case WWT_CAPTION: + DrawCaption(r, this->colour, w->owner, this->widget_data); + break; + + case WWT_HSCROLLBAR: + assert(this->widget_data == 0); + DrawHorizontalScrollbar(r, this->colour, (w->flags4 & (WF_SCROLL_UP | WF_HSCROLL)) == (WF_SCROLL_UP | WF_HSCROLL), + (w->flags4 & (WF_SCROLL_MIDDLE | WF_HSCROLL)) == (WF_SCROLL_MIDDLE | WF_HSCROLL), + (w->flags4 & (WF_SCROLL_DOWN | WF_HSCROLL)) == (WF_SCROLL_DOWN | WF_HSCROLL), &w->hscroll); + break; + + case WWT_STICKYBOX: + assert(this->widget_data == 0); + DrawStickyBox(r, this->colour, !!(w->flags4 & WF_STICKY)); + break; + + case WWT_RESIZEBOX: + assert(this->widget_data == 0); + DrawResizeBox(r, this->colour, this->pos_x < (uint)(w->width / 2), !!(w->flags4 & WF_SIZING)); + break; + + case WWT_CLOSEBOX: + DrawCloseBox(r, this->colour, this->widget_data); + break; + + case WWT_DROPDOWN: + DrawDropdown(r, this->colour, clicked, this->widget_data); + break; + + default: + NOT_REACHED(); + } + + if (this->IsDisabled()) { + GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[this->colour & 0xF][2], FILLRECT_CHECKER); + } +} + +void NWidgetLeaf::Invalidate(const Window *w) const +{ + if (this->type == WWT_EMPTY) return; // Don't repaint dummy widgets. + NWidgetBase::Invalidate(w); +} + /** * Intialize nested widget tree and convert to widget array. * @param nwid Nested widget tree. diff --git a/src/widget_type.h b/src/widget_type.h --- a/src/widget_type.h +++ b/src/widget_type.h @@ -188,6 +188,9 @@ public: inline uint GetHorizontalStepSize(SizingType sizing) const; inline uint GetVerticalStepSize(SizingType sizing) const; + virtual void Draw(const Window *w) = 0; + virtual void Invalidate(const Window *w) const; + WidgetType type; ///< Type of the widget / nested widget. bool fill_x; ///< Allow horizontal filling from initial size. bool fill_y; ///< Allow vertical filling from initial size. @@ -253,6 +256,16 @@ public: uint min_y; ///< Minimal vertical size of only this widget. }; +/** Nested widget flags that affect display and interaction withe 'real' widgets. */ +enum NWidgetDisplay { + NDB_LOWERED = 0, ///< Widget is lowered (pressed down) bit. + NDB_DISABLED = 1, ///< Widget is disabled (greyed out) bit. + + ND_LOWERED = 1 << NDB_LOWERED, ///< Bit value of the lowered flag. + ND_DISABLED = 1 << NDB_DISABLED, ///< Bit value of the disabled flag. +}; +DECLARE_ENUM_AS_BIT_SET(NWidgetDisplay); + /** Base class for a 'real' widget. * @ingroup NestedWidgets */ class NWidgetCore : public NWidgetResizeBase { @@ -262,16 +275,53 @@ public: void SetIndex(int index); void SetDataTip(uint16 widget_data, StringID tool_tip); + inline void SetLowered(bool lowered); + inline bool IsLowered(); + inline void SetDisabled(bool disabled); + inline bool IsDisabled(); + int SetupSmallestSize(); void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); /* virtual */ void FillNestedArray(NWidgetCore **array, uint length); - Colours colour; ///< Colour of this widget. - int index; ///< Index of the nested widget in the widget array of the window (\c -1 means 'not used'). - uint16 widget_data; ///< Data of the widget. @see Widget::data - StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips + NWidgetDisplay disp_flags; ///< Flags that affect display and interaction with the widget. + Colours colour; ///< Colour of this widget. + int index; ///< Index of the nested widget in the widget array of the window (\c -1 means 'not used'). + uint16 widget_data; ///< Data of the widget. @see Widget::data + StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips }; +/** + * Lower or raise the widget. + * @param lowered Widget must be lowered (drawn pressed down). + */ +inline void NWidgetCore::SetLowered(bool lowered) +{ + this->disp_flags = lowered ? SETBITS(this->disp_flags, ND_LOWERED) : CLRBITS(this->disp_flags, ND_LOWERED); +} + +/** Return whether the widget is lowered. */ +inline bool NWidgetCore::IsLowered() +{ + return HasBit(this->disp_flags, NDB_LOWERED); +} + +/** + * Disable (grey-out) or enable the widget. + * @param disabled Widget must be disabled. + */ +inline void NWidgetCore::SetDisabled(bool disabled) +{ + this->disp_flags = disabled ? SETBITS(this->disp_flags, ND_DISABLED) : CLRBITS(this->disp_flags, ND_DISABLED); +} + +/** Return whether the widget is disabled. */ +inline bool NWidgetCore::IsDisabled() +{ + return HasBit(this->disp_flags, NDB_DISABLED); +} + + /** Baseclass for container widgets. * @ingroup NestedWidgets */ class NWidgetContainer : public NWidgetBase { @@ -300,6 +350,8 @@ public: int SetupSmallestSize(); void AssignSizePosition(SizingType sizing, 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); + + /* virtual */ void Draw(const Window *w); }; /** Container with pre/inter/post child space. */ @@ -309,6 +361,7 @@ public: void SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post); + /* virtual */ void Draw(const Window *w); protected: uint8 pip_pre; ///< Amount of space before first widget. uint8 pip_inter; ///< Amount of space between widgets. @@ -360,6 +413,9 @@ public: int SetupSmallestSize(); void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); /* virtual */ void FillNestedArray(NWidgetCore **array, uint length); + + /* virtual */ void Draw(const Window *w); + /* virtual */ void Invalidate(const Window *w) const; }; /** Nested widget with a child. @@ -378,6 +434,8 @@ public: void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); /* virtual */ void FillNestedArray(NWidgetCore **array, uint length); + /* virtual */ void Draw(const Window *w); + private: NWidgetPIPContainer *child; ///< Child widget. }; @@ -387,6 +445,9 @@ private: class NWidgetLeaf : public NWidgetCore { public: NWidgetLeaf(WidgetType tp, Colours colour, int index, uint16 data, StringID tip); + + /* virtual */ void Draw(const Window *w); + /* virtual */ void Invalidate(const Window *w) const; }; Widget *InitializeNWidgets(NWidgetBase *nwid, bool rtl = false); diff --git a/src/window.cpp b/src/window.cpp --- a/src/window.cpp +++ b/src/window.cpp @@ -191,10 +191,20 @@ void CDECL Window::SetWidgetsLoweredStat */ void Window::RaiseButtons() { - for (uint i = 0; i < this->widget_count; i++) { - if (this->IsWidgetLowered(i)) { - this->RaiseWidget(i); - this->InvalidateWidget(i); + if (this->widget != NULL) { + for (uint i = 0; i < this->widget_count; i++) { + if (this->IsWidgetLowered(i)) { + this->RaiseWidget(i); + this->InvalidateWidget(i); + } + } + } + if (this->nested_array != NULL) { + for (uint i = 0; i < this->nested_array_size; i++) { + if (this->IsWidgetLowered(i)) { + this->RaiseWidget(i); + this->InvalidateWidget(i); + } } } } @@ -205,12 +215,15 @@ void Window::RaiseButtons() */ void Window::InvalidateWidget(byte widget_index) const { - const Widget *wi = &this->widget[widget_index]; + if (this->widget != NULL) { + const Widget *wi = &this->widget[widget_index]; - /* Don't redraw the window if the widget is invisible or of no-type */ - if (wi->type == WWT_EMPTY || IsWidgetHidden(widget_index)) return; + /* Don't redraw the window if the widget is invisible or of no-type */ + if (wi->type == WWT_EMPTY || IsWidgetHidden(widget_index)) return; - SetDirtyBlocks(this->left + wi->left, this->top + wi->top, this->left + wi->right + 1, this->top + wi->bottom + 1); + SetDirtyBlocks(this->left + wi->left, this->top + wi->top, this->left + wi->right + 1, this->top + wi->bottom + 1); + } + if (this->nested_array != NULL) this->nested_array[widget_index]->Invalidate(this); } /** diff --git a/src/window_gui.h b/src/window_gui.h --- a/src/window_gui.h +++ b/src/window_gui.h @@ -191,8 +191,14 @@ public: */ inline void SetWidgetDisabledState(byte widget_index, bool disab_stat) { - assert(widget_index < this->widget_count); - SB(this->widget[widget_index].display_flags, WIDG_DISABLED, 1, !!disab_stat); + if (this->widget != NULL) { + assert(widget_index < this->widget_count); + SB(this->widget[widget_index].display_flags, WIDG_DISABLED, 1, !!disab_stat); + } + if (this->nested_array != NULL) { + assert(widget_index < this->nested_array_size); + this->nested_array[widget_index]->SetDisabled(disab_stat); + } } /** @@ -220,6 +226,10 @@ public: */ inline bool IsWidgetDisabled(byte widget_index) const { + if (this->nested_array != NULL) { + assert(widget_index < this->nested_array_size); + return this->nested_array[widget_index]->IsDisabled(); + } assert(widget_index < this->widget_count); return HasBit(this->widget[widget_index].display_flags, WIDG_DISABLED); } @@ -313,8 +323,14 @@ public: */ inline void SetWidgetLoweredState(byte widget_index, bool lowered_stat) { - assert(widget_index < this->widget_count); - SB(this->widget[widget_index].display_flags, WIDG_LOWERED, 1, !!lowered_stat); + if (this->widget != NULL) { + assert(widget_index < this->widget_count); + SB(this->widget[widget_index].display_flags, WIDG_LOWERED, 1, !!lowered_stat); + } + if (this->nested_array != NULL) { + assert(widget_index < this->nested_array_size); + this->nested_array[widget_index]->SetLowered(lowered_stat); + } } /** @@ -323,8 +339,15 @@ public: */ inline void ToggleWidgetLoweredState(byte widget_index) { - assert(widget_index < this->widget_count); - ToggleBit(this->widget[widget_index].display_flags, WIDG_LOWERED); + if (this->widget != NULL) { + assert(widget_index < this->widget_count); + ToggleBit(this->widget[widget_index].display_flags, WIDG_LOWERED); + } + if (this->nested_array != NULL) { + assert(widget_index < this->nested_array_size); + bool lowered_state = this->nested_array[widget_index]->IsLowered(); + this->nested_array[widget_index]->SetLowered(!lowered_state); + } } /** @@ -352,6 +375,10 @@ public: */ inline bool IsWidgetLowered(byte widget_index) const { + if (this->nested_array != NULL) { + assert(widget_index < this->nested_array_size); + return this->nested_array[widget_index]->IsLowered(); + } assert(widget_index < this->widget_count); return HasBit(this->widget[widget_index].display_flags, WIDG_LOWERED); }