/** * Show a drop down list. * @param w Parent window for the list. * @param list Prepopulated DropDownList. Will be deleted when the list is * closed. * @param selected The initially selected list item. * @param button The widget which is passed to Window::OnDropdownSelect and OnDropdownClose. * Unless you override those functions, this should be then widget index of the dropdown button. * @param wi_rect Coord of the parent drop down button, used to position the dropdown menu. * @param auto_width The width is determined by the widest item in the list, * in this case only one of \a left or \a right is used (depending on text direction). * @param instant_close Set to true if releasing mouse button should close the * list regardless of where the cursor is. */ void ShowDropDownListAt(Window *w, DropDownList *list, int selected, int button, Rect wi_rect, Colours wi_colour, bool auto_width, bool instant_close) { DeleteWindowById(WC_DROPDOWN_MENU, 0); /* The preferred position is just below the dropdown calling widget */ int top = w->top + wi_rect.bottom + 1; /* The preferred width equals the calling widget */ uint width = wi_rect.right - wi_rect.left + 1; uint max_item_width = 0; if (auto_width) { /* Find the longest item in the list */ for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) { const DropDownListItem *item = *it; max_item_width = max(max_item_width, item->Width() + 5); } } /* Total length of list */ int list_height = 0; for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) { DropDownListItem *item = *it; list_height += item->Height(width); } /* Height of window visible */ int height = list_height; /* Check if the status bar is visible, as we don't want to draw over it */ int screen_bottom = GetMainViewBottom(); bool scroll = false; /* Check if the dropdown will fully fit below the widget */ if (top + height + 4 >= screen_bottom) { /* If not, check if it will fit above the widget */ if (w->top + wi_rect.top - height > GetMainViewTop()) { top = w->top + wi_rect.top - height - 4; } else { /* ... and lastly if it won't, enable the scroll bar and fit the * list in below the widget */ int avg_height = list_height / (int)list->size(); int rows = (screen_bottom - 4 - top) / avg_height; height = rows * avg_height; scroll = true; /* Add space for the scroll bar if we automatically determined * the width of the list. */ max_item_width += NWidgetScrollbar::GetVerticalDimension().width; } } if (auto_width) width = max(width, max_item_width); Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - width : wi_rect.left), top}; Dimension dw_size = {width, height}; new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll); }
} } virtual Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) { /* Position (0, 0) given, center the window. */ if (this->position.x == 0 && this->position.y == 0) { Point pt = {(_screen.width - sm_width) >> 1, (_screen.height - sm_height) >> 1}; return pt; } /* Find the free screen space between the main toolbar at the top, and the statusbar at the bottom. * Add a fixed distance 20 to make it less cluttered. */ int scr_top = GetMainViewTop() + 20; int scr_bot = GetMainViewBottom() - 20; Point pt = RemapCoords2(this->position.x, this->position.y); const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport; if (this->face == INVALID_COMPANY) { /* move x pos to opposite corner */ pt.x = UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left; pt.x = (pt.x < (_screen.width >> 1)) ? _screen.width - sm_width - 20 : 20; // Stay 20 pixels away from the edge of the screen. /* move y pos to opposite corner */ pt.y = UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top; pt.y = (pt.y < (_screen.height >> 1)) ? scr_bot - sm_height : scr_top; } else { pt.x = Clamp(UnScaleByZoom(pt.x - vp->virtual_left, vp->zoom) + vp->left - (sm_width / 2), 0, _screen.width - sm_width); pt.y = Clamp(UnScaleByZoom(pt.y - vp->virtual_top, vp->zoom) + vp->top - (sm_height / 2), scr_top, scr_bot - sm_height); }
void ShowDropDownList(Window *w, DropDownList *list, int selected, int button, uint width, bool auto_width, bool instant_close) { DeleteWindowById(WC_DROPDOWN_MENU, 0); /* Our parent's button widget is used to determine where to place the drop * down list window. */ Rect wi_rect; Colours wi_colour; NWidgetCore *nwi = w->GetWidget<NWidgetCore>(button); wi_rect.left = nwi->pos_x; wi_rect.right = nwi->pos_x + nwi->current_x - 1; wi_rect.top = nwi->pos_y; wi_rect.bottom = nwi->pos_y + nwi->current_y - 1; wi_colour = nwi->colour; if (nwi->type == NWID_BUTTON_DROPDOWN) { nwi->disp_flags |= ND_DROPDOWN_ACTIVE; } else { w->LowerWidget(button); } w->SetWidgetDirty(button); /* The preferred position is just below the dropdown calling widget */ int top = w->top + wi_rect.bottom + 1; if (width == 0) width = wi_rect.right - wi_rect.left + 1; uint max_item_width = 0; if (auto_width) { /* Find the longest item in the list */ for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) { const DropDownListItem *item = *it; max_item_width = max(max_item_width, item->Width() + 5); } } /* Total length of list */ int list_height = 0; for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) { DropDownListItem *item = *it; list_height += item->Height(width); } /* Height of window visible */ int height = list_height; /* Check if the status bar is visible, as we don't want to draw over it */ int screen_bottom = GetMainViewBottom(); bool scroll = false; /* Check if the dropdown will fully fit below the widget */ if (top + height + 4 >= screen_bottom) { /* If not, check if it will fit above the widget */ if (w->top + wi_rect.top - height > GetMainViewTop()) { top = w->top + wi_rect.top - height - 4; } else { /* ... and lastly if it won't, enable the scroll bar and fit the * list in below the widget */ int avg_height = list_height / (int)list->size(); int rows = (screen_bottom - 4 - top) / avg_height; height = rows * avg_height; scroll = true; /* Add space for the scroll bar if we automatically determined * the width of the list. */ max_item_width += WD_VSCROLLBAR_WIDTH; } } if (auto_width) width = max(width, max_item_width); Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - width : wi_rect.left), top}; Dimension dw_size = {width, height}; new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll); }
/** * Show a drop down list. * @param w Parent window for the list. * @param list Prepopulated DropDownList. Will be deleted when the list is * closed. * @param selected The initially selected list item. * @param button The widget which is passed to Window::OnDropdownSelect and OnDropdownClose. * Unless you override those functions, this should be then widget index of the dropdown button. * @param wi_rect Coord of the parent drop down button, used to position the dropdown menu. * @param auto_width The width is determined by the widest item in the list, * in this case only one of \a left or \a right is used (depending on text direction). * @param instant_close Set to true if releasing mouse button should close the * list regardless of where the cursor is. */ void ShowDropDownListAt(Window *w, const DropDownList *list, int selected, int button, Rect wi_rect, Colours wi_colour, bool auto_width, bool instant_close) { DeleteWindowById(WC_DROPDOWN_MENU, 0); /* The preferred position is just below the dropdown calling widget */ int top = w->top + wi_rect.bottom + 1; /* The preferred width equals the calling widget */ uint width = wi_rect.right - wi_rect.left + 1; /* Longest item in the list, if auto_width is enabled */ uint max_item_width = 0; /* Total length of list */ int height = 0; for (const DropDownListItem * const *it = list->Begin(); it != list->End(); ++it) { const DropDownListItem *item = *it; height += item->Height(width); if (auto_width) max_item_width = max(max_item_width, item->Width() + 5); } /* Check if the status bar is visible, as we don't want to draw over it */ int screen_bottom = GetMainViewBottom(); bool scroll = false; enum { DISPLAY_BORDER = 20, TOP_BORDER = 4 }; /* Check if the dropdown will fully fit below the widget */ if (top + height + DISPLAY_BORDER >= screen_bottom) { /* If not, check if it will fit above the widget */ int screen_top = GetMainViewTop(); if (w->top + wi_rect.top - TOP_BORDER > screen_top + height) { top = w->top + wi_rect.top - height - TOP_BORDER; } else { /* If it doesn't fit above the widget, we need to enable a scrollbar... */ int avg_height = height / (int)list->Length(); scroll = true; /* ... and choose whether to put the list above or below the widget. */ bool put_above = false; int available_height = screen_bottom - w->top - wi_rect.bottom; if (w->top + wi_rect.top - screen_top > available_height) { // Put it above. available_height = w->top + wi_rect.top - screen_top - DISPLAY_BORDER - TOP_BORDER; put_above = true; } /* Check at least there is space for one item. */ assert(available_height >= avg_height); /* ... and lastly if it won't, enable the scroll bar and fit the * list in below the widget */ int rows = available_height / avg_height; height = rows * avg_height; scroll = true; /* Add space for the scroll bar if we automatically determined * the width of the list. */ max_item_width += NWidgetScrollbar::GetVerticalDimension().width; /* ... and set the top position if needed. */ if (put_above) { top = w->top + wi_rect.top - height - TOP_BORDER; } } } if (auto_width) width = max(width, max_item_width); Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - (int)width : wi_rect.left), top}; Dimension dw_size = {width, (uint)height}; new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll); }
/** * Show a drop down list. * @param w Parent window for the list. * @param list Prepopulated DropDownList. Will be deleted when the list is * closed. * @param selected The initially selected list item. * @param button The widget which is passed to Window::OnDropdownSelect and OnDropdownClose. * Unless you override those functions, this should be then widget index of the dropdown button. * @param wi_rect Coord of the parent drop down button, used to position the dropdown menu. * @param auto_width The width is determined by the widest item in the list, * in this case only one of \a left or \a right is used (depending on text direction). * @param instant_close Set to true if releasing mouse button should close the * list regardless of where the cursor is. */ void ShowDropDownListAt(Window *w, const DropDownList *list, int selected, int button, Rect wi_rect, Colours wi_colour, bool auto_width, bool instant_close) { DeleteWindowById(WC_DROPDOWN_MENU, 0); /* The preferred position is just below the dropdown calling widget */ int top = w->top + wi_rect.bottom + 1; /* The preferred width equals the calling widget */ uint width = wi_rect.right - wi_rect.left + 1; /* Longest item in the list, if auto_width is enabled */ uint max_item_width = 0; /* Total height of list */ uint height = 0; for (const DropDownListItem * const *it = list->Begin(); it != list->End(); ++it) { const DropDownListItem *item = *it; height += item->Height(width); if (auto_width) max_item_width = max(max_item_width, item->Width() + 5); } /* Scrollbar needed? */ bool scroll = false; /* Is it better to place the dropdown above the widget? */ bool above = false; /* Available height below (or above, if the dropdown is placed above the widget). */ uint available_height = (uint)max(GetMainViewBottom() - top - 4, 0); /* If the dropdown doesn't fully fit below the widget... */ if (height > available_height) { uint available_height_above = (uint)max(w->top + wi_rect.top - GetMainViewTop() - 4, 0); /* Put the dropdown above if there is more available space. */ if (available_height_above > available_height) { above = true; available_height = available_height_above; } /* If the dropdown doesn't fully fit, we need a dropdown. */ if (height > available_height) { scroll = true; uint avg_height = height / list->Length(); /* Check at least there is space for one item. */ assert(available_height >= avg_height); /* Fit the list. */ uint rows = available_height / avg_height; height = rows * avg_height; /* Add space for the scrollbar. */ max_item_width += NWidgetScrollbar::GetVerticalDimension().width; } /* Set the top position if needed. */ if (above) { top = w->top + wi_rect.top - height - 4; } } if (auto_width) width = max(width, max_item_width); Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - (int)width : wi_rect.left), top}; Dimension dw_size = {width, height}; DropdownWindow *dropdown = new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll); /* The dropdown starts scrolling downwards when opening it towards * the top and holding down the mouse button. It can be fooled by * opening the dropdown scrolled to the very bottom. */ if (above && scroll) dropdown->vscroll->UpdatePosition(INT_MAX); }