# HG changeset patch # User alberth # Date 2009-06-04 12:46:37 # Node ID 980c096f27ac26250c76f037d99ba67591c35c08 # Parent 6bd5701b8f853cbd57a0f655af3816845346150f (svn r16515) -Codechange: Added scrollbar handling for nested widgets, and finding widgets by type or position in the tree. diff --git a/src/widget.cpp b/src/widget.cpp --- a/src/widget.cpp +++ b/src/widget.cpp @@ -47,25 +47,25 @@ static Point HandleScrollbarHittest(cons return pt; } -/** Special handling for the scrollbar widget type. - * Handles the special scrolling buttons and other - * scrolling. - * @param w Window on which a scroll was performed. - * @param wi Pointer to the scrollbar widget. - * @param x The X coordinate of the mouse click. - * @param y The Y coordinate of the mouse click. */ -void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y) +/** + * Compute new position of the scrollbar after a click and updates the window flags. + * @param w Window on which a scroll was performed. + * @param wtp Scrollbar widget type. + * @param mi Minimum coordinate of the scroll bar. + * @param ma Maximum coordinate of the scroll bar. + * @param x The X coordinate of the mouse click. + * @param y The Y coordinate of the mouse click. + */ +static void ScrollbarClickPositioning(Window *w, WidgetType wtp, int x, int y, int mi, int ma) { - int mi, ma, pos; + int pos; Scrollbar *sb; - switch (wi->type) { + switch (wtp) { case WWT_SCROLLBAR: /* vertical scroller */ w->flags4 &= ~WF_HSCROLL; w->flags4 &= ~WF_SCROLL2; - mi = wi->top; - ma = wi->bottom; pos = y; sb = &w->vscroll; break; @@ -74,8 +74,6 @@ void ScrollbarClickHandler(Window *w, co /* 2nd vertical scroller */ w->flags4 &= ~WF_HSCROLL; w->flags4 |= WF_SCROLL2; - mi = wi->top; - ma = wi->bottom; pos = y; sb = &w->vscroll2; break; @@ -84,8 +82,6 @@ void ScrollbarClickHandler(Window *w, co /* horizontal scroller */ w->flags4 &= ~WF_SCROLL2; w->flags4 |= WF_HSCROLL; - mi = wi->left; - ma = wi->right; pos = x; sb = &w->hscroll; break; @@ -106,8 +102,7 @@ void ScrollbarClickHandler(Window *w, co if (_scroller_click_timeout == 0) { _scroller_click_timeout = 6; - if ((byte)(sb->pos + sb->cap) < sb->count) - sb->pos++; + if (sb->pos + sb->cap < sb->count) sb->pos++; } _left_button_clicked = false; } else { @@ -116,10 +111,7 @@ void ScrollbarClickHandler(Window *w, co if (pos < pt.x) { sb->pos = max(sb->pos - sb->cap, 0); } else if (pos > pt.y) { - sb->pos = min( - sb->pos + sb->cap, - max(sb->count - sb->cap, 0) - ); + sb->pos = min(sb->pos + sb->cap, max(sb->count - sb->cap, 0)); } else { _scrollbar_start_pos = pt.x - mi - 9; _scrollbar_size = ma - mi - 23; @@ -132,6 +124,76 @@ void ScrollbarClickHandler(Window *w, co w->SetDirty(); } +/** Special handling for the scrollbar widget type. + * Handles the special scrolling buttons and other scrolling. + * @param w Window on which a scroll was performed. + * @param wi Pointer to the scrollbar widget. + * @param x The X coordinate of the mouse click. + * @param y The Y coordinate of the mouse click. + */ +void ScrollbarClickHandler(Window *w, const Widget *wi, int x, int y) +{ + int mi, ma; + + switch (wi->type) { + case WWT_SCROLLBAR: + /* vertical scroller */ + mi = wi->top; + ma = wi->bottom; + break; + + case WWT_SCROLL2BAR: + /* 2nd vertical scroller */ + mi = wi->top; + ma = wi->bottom; + break; + + case WWT_HSCROLLBAR: + /* horizontal scroller */ + mi = wi->left; + ma = wi->right; + break; + + default: NOT_REACHED(); + } + ScrollbarClickPositioning(w, wi->type, x, y, mi, ma); +} + +/** Special handling for the scrollbar widget type. + * Handles the special scrolling buttons and other scrolling. + * @param w Window on which a scroll was performed. + * @param nw Pointer to the scrollbar widget. + * @param x The X coordinate of the mouse click. + * @param y The Y coordinate of the mouse click. + */ +void ScrollbarClickHandler(Window *w, const NWidgetCore *nw, int x, int y) +{ + int mi, ma; + + switch (nw->type) { + case WWT_SCROLLBAR: + /* vertical scroller */ + mi = nw->pos_y; + ma = nw->pos_y + nw->current_y; + break; + + case WWT_SCROLL2BAR: + /* 2nd vertical scroller */ + mi = nw->pos_y; + ma = nw->pos_y + nw->current_y; + break; + + case WWT_HSCROLLBAR: + /* horizontal scroller */ + mi = nw->pos_x; + ma = nw->pos_x + nw->current_x; + break; + + default: NOT_REACHED(); + } + ScrollbarClickPositioning(w, nw->type, x, y, mi, ma); +} + /** Returns the index for the widget located at the given position * relative to the window. It includes all widget-corner pixels as well. * @param *w Window to look inside @@ -141,8 +203,12 @@ void ScrollbarClickHandler(Window *w, co */ int GetWidgetFromPos(const Window *w, int x, int y) { + if (w->nested_root != NULL) { + NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y); + return (nw != NULL) ? nw->index : -1; + } + int found_index = -1; - /* Go through the widgets and check if we find the widget that the coordinate is inside. */ for (uint index = 0; index < w->widget_count; index++) { const Widget *wi = &w->widget[index]; @@ -913,6 +979,21 @@ void NWidgetBase::Invalidate(const Windo } /** + * @fn NWidgetCore *GetWidgetFromPos(int x, int y) + * Retrieve a widget by its position. + * @param x Horizontal position relative to the left edge of the window. + * @param y Vertical position relative to the top edge of the window. + * @return Returns the deepest nested widget that covers the given position, or \c NULL if no widget can be found. + */ + +/** + * @fn NWidgetBase *GetWidgetOfType(WidgetType tp) + * Retrieve a widget by its type. + * @param tp Widget type to search for. + * @return Returns the first widget of the specified type, or \c NULL if no widget can be found. + */ + +/** * Constructor for resizable nested widgets. * @param tp Nested widget type. * @param fill_x Allow horizontal filling from initial size. @@ -1048,6 +1129,18 @@ void NWidgetCore::StoreWidgets(Widget *w } /** + * @fn Scrollbar *NWidgetCore::FindScrollbar(Window *w, bool allow_next = true) + * Find the scrollbar of the widget through the Window::nested_array. + * @param w Window containing the widgets and the scrollbar, + * @param allow_next Search may be extended to the next widget. + * + * @todo This implementation uses the constraint that a scrollbar must be the next item in the #Window::nested_array, and the scrollbar + * data is stored in the #Window structure (#Window::vscroll, #Window::vscroll2, and #Window::hscroll). + * Alternative light-weight implementations may be considered, eg by sub-classing a canvas-like widget, and/or by having + * an explicit link between the scrollbar and the widget being scrolled. + */ + +/** * Constructor container baseclass. * @param tp Type of the container. */ @@ -1067,6 +1160,16 @@ NWidgetContainer::~NWidgetContainer() this->tail = NULL; } +NWidgetBase *NWidgetContainer::GetWidgetOfType(WidgetType tp) +{ + if (this->type == tp) return this; + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + NWidgetBase *nwid = child_wid->GetWidgetOfType(tp); + if (nwid != NULL) return nwid; + } + return NULL; +} + /** * Append widget \a wid to container. * @param wid Widget to append. @@ -1190,6 +1293,16 @@ void NWidgetStacked::Draw(const Window * } } +NWidgetCore *NWidgetStacked::GetWidgetFromPos(int x, int y) +{ + if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL; + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y); + if (nwid != NULL) return nwid; + } + return NULL; +} + NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp) : NWidgetContainer(tp) { } @@ -1217,6 +1330,17 @@ void NWidgetPIPContainer::Draw(const Win } } +NWidgetCore *NWidgetPIPContainer::GetWidgetFromPos(int x, int y) +{ + if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL; + + for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) { + NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y); + if (nwid != NULL) return nwid; + } + return NULL; +} + /** Horizontal container widget. */ NWidgetHorizontal::NWidgetHorizontal() : NWidgetPIPContainer(NWID_HORIZONTAL) { @@ -1503,6 +1627,16 @@ void NWidgetSpacer::Invalidate(const Win /* Spacer widget never need repainting. */ } +NWidgetCore *NWidgetSpacer::GetWidgetFromPos(int x, int y) +{ + return NULL; +} + +NWidgetBase *NWidgetSpacer::GetWidgetOfType(WidgetType tp) +{ + return (this->type == tp) ? this : NULL; +} + /** * Constructor parent nested widgets. * @param tp Type of parent widget. @@ -1641,6 +1775,33 @@ void NWidgetBackground::Draw(const Windo } } +NWidgetCore *NWidgetBackground::GetWidgetFromPos(int x, int y) +{ + NWidgetCore *nwid = NULL; + if (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) { + if (this->child != NULL) nwid = this->child->GetWidgetFromPos(x, y); + if (nwid == NULL) nwid = this; + } + return nwid; +} + +Scrollbar *NWidgetBackground::FindScrollbar(Window *w, bool allow_next) +{ + if (this->index > 0 && allow_next && this->child == NULL && (uint)(this->index) + 1 < w->nested_array_size) { + NWidgetCore *next_wid = w->nested_array[this->index + 1]; + if (next_wid != NULL) return next_wid->FindScrollbar(w, false); + } + return NULL; +} + +NWidgetBase *NWidgetBackground::GetWidgetOfType(WidgetType tp) +{ + NWidgetBase *nwid = NULL; + if (this->child != NULL) nwid = this->child->GetWidgetOfType(tp); + if (nwid == NULL && this->type == tp) nwid = this; + return nwid; +} + /** * Nested leaf widget. * @param tp Type of leaf widget. @@ -1839,6 +2000,28 @@ void NWidgetLeaf::Invalidate(const Windo NWidgetBase::Invalidate(w); } +NWidgetCore *NWidgetLeaf::GetWidgetFromPos(int x, int y) +{ + return (IsInsideBS(x, this->pos_x, this->current_x) && IsInsideBS(y, this->pos_y, this->current_y)) ? this : NULL; +} + +Scrollbar *NWidgetLeaf::FindScrollbar(Window *w, bool allow_next) +{ + if (this->type == WWT_SCROLLBAR) return &w->vscroll; + if (this->type == WWT_SCROLL2BAR) return &w->vscroll2; + + if (this->index > 0 && allow_next && (uint)(this->index) + 1 < w->nested_array_size) { + NWidgetCore *next_wid = w->nested_array[this->index + 1]; + if (next_wid != NULL) return next_wid->FindScrollbar(w, false); + } + return NULL; +} + +NWidgetBase *NWidgetLeaf::GetWidgetOfType(WidgetType tp) +{ + return (this->type == tp) ? this : NULL; +} + /** * 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 @@ -153,6 +153,7 @@ enum SizingType { /* Forward declarations. */ class NWidgetCore; +struct Scrollbar; /** * Baseclass for nested widgets. @@ -170,6 +171,9 @@ public: virtual void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl) = 0; virtual void FillNestedArray(NWidgetCore **array, uint length) = 0; + virtual NWidgetCore *GetWidgetFromPos(int x, int y) = 0; + virtual NWidgetBase *GetWidgetOfType(WidgetType tp) = 0; + /** * Set additional space (padding) around the widget. * @param top Amount of additional space above the widget. @@ -284,6 +288,8 @@ public: void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); /* virtual */ void FillNestedArray(NWidgetCore **array, uint length); + virtual Scrollbar *FindScrollbar(Window *w, bool allow_next = true) = 0; + 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'). @@ -335,6 +341,8 @@ public: /** Return whether the container is empty. */ inline bool IsEmpty() { return head == NULL; }; + /* virtual */ NWidgetBase *GetWidgetOfType(WidgetType tp); + protected: NWidgetBase *head; ///< Pointer to first widget in container. NWidgetBase *tail; ///< Pointer to last widget in container. @@ -352,6 +360,7 @@ public: void StoreWidgets(Widget *widgets, int length, bool left_moving, bool top_moving, bool rtl); /* virtual */ void Draw(const Window *w); + /* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y); }; /** Container with pre/inter/post child space. */ @@ -362,6 +371,8 @@ public: void SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post); /* virtual */ void Draw(const Window *w); + /* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y); + protected: uint8 pip_pre; ///< Amount of space before first widget. uint8 pip_inter; ///< Amount of space between widgets. @@ -416,6 +427,8 @@ public: /* virtual */ void Draw(const Window *w); /* virtual */ void Invalidate(const Window *w) const; + /* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y); + /* virtual */ NWidgetBase *GetWidgetOfType(WidgetType tp); }; /** Nested widget with a child. @@ -435,6 +448,9 @@ public: /* virtual */ void FillNestedArray(NWidgetCore **array, uint length); /* virtual */ void Draw(const Window *w); + /* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y); + /* virtual */ NWidgetBase *GetWidgetOfType(WidgetType tp); + /* virtual */ Scrollbar *FindScrollbar(Window *w, bool allow_next = true); private: NWidgetPIPContainer *child; ///< Child widget. @@ -448,6 +464,9 @@ public: /* virtual */ void Draw(const Window *w); /* virtual */ void Invalidate(const Window *w) const; + /* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y); + /* virtual */ NWidgetBase *GetWidgetOfType(WidgetType tp); + /* virtual */ Scrollbar *FindScrollbar(Window *w, bool allow_next = true); }; 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 @@ -399,23 +399,24 @@ static void DispatchMouseWheelEvent(Wind { if (widget < 0) return; - const Widget *wi1 = &w->widget[widget]; - const Widget *wi2 = &w->widget[widget + 1]; + Scrollbar *sb = NULL; + if (w->widget != NULL) { + const Widget *wi1 = &w->widget[widget]; + const Widget *wi2 = &w->widget[widget + 1]; + if (wi1->type == WWT_SCROLLBAR || wi2->type == WWT_SCROLLBAR) { + sb = &w->vscroll; + } else if (wi1->type == WWT_SCROLL2BAR || wi2->type == WWT_SCROLL2BAR) { + sb = &w->vscroll2; + } + } - /* The listbox can only scroll if scrolling was done on the scrollbar itself, - * or on the listbox (and the next item is (must be) the scrollbar) - * XXX - should be rewritten as a widget-dependent scroller but that's - * not happening until someone rewrites the whole widget-code */ - Scrollbar *sb; - if ((sb = &w->vscroll, wi1->type == WWT_SCROLLBAR) || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR) || - (sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) { + if (w->nested_array != NULL && (uint)widget < w->nested_array_size) sb = w->nested_array[widget]->FindScrollbar(w); - if (sb->count > sb->cap) { - int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap); - if (pos != sb->pos) { - sb->pos = pos; - w->SetDirty(); - } + if (sb != NULL && sb->count > sb->cap) { + int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap); + if (pos != sb->pos) { + sb->pos = pos; + w->SetDirty(); } } }