00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../stdafx.h"
00013 #include "../window_gui.h"
00014 #include "../strings_func.h"
00015 #include "../window_func.h"
00016 #include "dropdown_type.h"
00017
00018
00019 void DropDownListItem::Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
00020 {
00021 int c1 = _colour_gradient[bg_colour][3];
00022 int c2 = _colour_gradient[bg_colour][7];
00023
00024 int mid = top + this->Height(0) / 2;
00025 GfxFillRect(left + 1, mid - 2, right - 1, mid - 2, c1);
00026 GfxFillRect(left + 1, mid - 1, right - 1, mid - 1, c2);
00027 }
00028
00029 uint DropDownListStringItem::Width() const
00030 {
00031 char buffer[512];
00032 GetString(buffer, this->String(), lastof(buffer));
00033 return GetStringBoundingBox(buffer).width;
00034 }
00035
00036 void DropDownListStringItem::Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
00037 {
00038 DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, this->String(), sel ? TC_WHITE : TC_BLACK);
00039 }
00040
00041 StringID DropDownListParamStringItem::String() const
00042 {
00043 for (uint i = 0; i < lengthof(this->decode_params); i++) SetDParam(i, this->decode_params[i]);
00044 return this->string;
00045 }
00046
00047 uint DropDownListCharStringItem::Width() const
00048 {
00049 return GetStringBoundingBox(this->string).width;
00050 }
00051
00052 void DropDownListCharStringItem::Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
00053 {
00054 DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, this->string, sel ? TC_WHITE : TC_BLACK);
00055 }
00056
00061 static void DeleteDropDownList(DropDownList *list)
00062 {
00063 for (DropDownList::iterator it = list->begin(); it != list->end(); ++it) {
00064 DropDownListItem *item = *it;
00065 delete item;
00066 }
00067 delete list;
00068 }
00069
00071 enum DropdownMenuWidgets {
00072 DDM_ITEMS,
00073 DDM_SCROLL,
00074 };
00075
00076 static const NWidgetPart _nested_dropdown_menu_widgets[] = {
00077 NWidget(NWID_HORIZONTAL),
00078 NWidget(WWT_PANEL, COLOUR_END, DDM_ITEMS), SetMinimalSize(1, 1), EndContainer(),
00079 NWidget(WWT_SCROLLBAR, COLOUR_END, DDM_SCROLL),
00080 EndContainer(),
00081 };
00082
00083 const WindowDesc _dropdown_desc(
00084 WDP_MANUAL, 0, 0,
00085 WC_DROPDOWN_MENU, WC_NONE,
00086 0,
00087 _nested_dropdown_menu_widgets, lengthof(_nested_dropdown_menu_widgets)
00088 );
00089
00091 struct DropdownWindow : Window {
00092 WindowClass parent_wnd_class;
00093 WindowNumber parent_wnd_num;
00094 byte parent_button;
00095 DropDownList *list;
00096 int selected_index;
00097 byte click_delay;
00098 bool drag_mode;
00099 bool instant_close;
00100 int scrolling;
00101 Point position;
00102
00115 DropdownWindow(Window *parent, DropDownList *list, int selected, int button, bool instant_close, const Point &position, const Dimension &size, Colours wi_colour, bool scroll) : Window()
00116 {
00117 this->position = position;
00118
00119 this->CreateNestedTree(&_dropdown_desc);
00120
00121 uint items_width = size.width - (scroll ? WD_VSCROLLBAR_WIDTH : 0);
00122 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(DDM_ITEMS);
00123 nwi->SetMinimalSize(items_width, size.height + 4);
00124 nwi->colour = wi_colour;
00125
00126 nwi = this->GetWidget<NWidgetCore>(DDM_SCROLL);
00127 if (scroll) {
00128 nwi->colour = wi_colour;
00129 } else {
00130 nwi->min_x = 0;
00131 }
00132
00133 this->FinishInitNested(&_dropdown_desc, 0);
00134 this->flags4 &= ~WF_WHITE_BORDER_MASK;
00135
00136
00137 int list_height = 0;
00138 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00139 DropDownListItem *item = *it;
00140 list_height += item->Height(items_width);
00141 }
00142
00143
00144 this->vscroll.SetCapacity(size.height * (uint16)list->size() / list_height);
00145 this->vscroll.SetCount((uint16)list->size());
00146
00147 this->parent_wnd_class = parent->window_class;
00148 this->parent_wnd_num = parent->window_number;
00149 this->parent_button = button;
00150 this->list = list;
00151 this->selected_index = selected;
00152 this->click_delay = 0;
00153 this->drag_mode = true;
00154 this->instant_close = instant_close;
00155 }
00156
00157 ~DropdownWindow()
00158 {
00159 Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
00160 if (w2 != NULL) {
00161 if (w2->nested_array != NULL) {
00162 NWidgetCore *nwi2 = w2->GetWidget<NWidgetCore>(this->parent_button);
00163 if (nwi2->type == NWID_BUTTON_DROPDOWN) {
00164 nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
00165 } else {
00166 w2->RaiseWidget(this->parent_button);
00167 }
00168 } else {
00169 w2->RaiseWidget(this->parent_button);
00170 }
00171 w2->SetWidgetDirty(this->parent_button);
00172 }
00173
00174 DeleteDropDownList(this->list);
00175 }
00176
00177 virtual Point OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
00178 {
00179 return this->position;
00180 }
00181
00186 bool GetDropDownItem(int &value)
00187 {
00188 if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return false;
00189
00190 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(DDM_ITEMS);
00191 int y = _cursor.pos.y - this->top - nwi->pos_y - 2;
00192 int width = nwi->current_x - 4;
00193 int pos = this->vscroll.GetPosition();
00194
00195 const DropDownList *list = this->list;
00196
00197 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00198
00199 if (--pos >= 0) continue;
00200
00201 const DropDownListItem *item = *it;
00202 int item_height = item->Height(width);
00203
00204 if (y < item_height) {
00205 if (item->masked || !item->Selectable()) return false;
00206 value = item->result;
00207 return true;
00208 }
00209
00210 y -= item_height;
00211 }
00212
00213 return false;
00214 }
00215
00216 virtual void OnPaint()
00217 {
00218 this->DrawWidgets();
00219 }
00220
00221 virtual void DrawWidget(const Rect &r, int widget) const
00222 {
00223 if (widget != DDM_ITEMS) return;
00224
00225 TextColour colour = (TextColour)this->GetWidget<NWidgetCore>(widget)->colour;
00226
00227 int y = r.top + 2;
00228 int pos = this->vscroll.GetPosition();
00229 for (DropDownList::const_iterator it = this->list->begin(); it != this->list->end(); ++it) {
00230 const DropDownListItem *item = *it;
00231 int item_height = item->Height(r.right - r.left + 1);
00232
00233
00234 if (--pos >= 0) continue;
00235
00236 if (y + item_height < r.bottom) {
00237 bool selected = (this->selected_index == item->result);
00238 if (selected) GfxFillRect(r.left + 2, y, r.right - 1, y + item_height - 1, 0);
00239
00240 item->Draw(r.left, r.right, y, r.bottom, selected, colour);
00241
00242 if (item->masked) {
00243 GfxFillRect(r.left + 1, y, r.right - 1, y + item_height - 1, _colour_gradient[colour][5], FILLRECT_CHECKER);
00244 }
00245 }
00246 y += item_height;
00247 }
00248 }
00249
00250 virtual void OnClick(Point pt, int widget, int click_count)
00251 {
00252 if (widget != DDM_ITEMS) return;
00253 int item;
00254 if (this->GetDropDownItem(item)) {
00255 this->click_delay = 4;
00256 this->selected_index = item;
00257 this->SetDirty();
00258 }
00259 }
00260
00261 virtual void OnTick()
00262 {
00263 if (this->scrolling != 0) {
00264 int pos = this->vscroll.GetPosition();
00265
00266 this->vscroll.UpdatePosition(this->scrolling);
00267 this->scrolling = 0;
00268
00269 if (pos != this->vscroll.GetPosition()) {
00270 this->SetDirty();
00271 }
00272 }
00273 }
00274
00275 virtual void OnMouseLoop()
00276 {
00277 Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
00278 if (w2 == NULL) {
00279 delete this;
00280 return;
00281 }
00282
00283 if (this->click_delay != 0 && --this->click_delay == 0) {
00284 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00285 delete this;
00286 return;
00287 }
00288
00289 if (this->drag_mode) {
00290 int item;
00291
00292 if (!_left_button_clicked) {
00293 this->drag_mode = false;
00294 if (!this->GetDropDownItem(item)) {
00295 if (this->instant_close) {
00296 if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == this->parent_button) {
00297
00298
00299 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00300 }
00301 delete this;
00302 }
00303 return;
00304 }
00305 this->click_delay = 2;
00306 } else {
00307 if (_cursor.pos.y <= this->top + 2) {
00308
00309 this->scrolling = -1;
00310 return;
00311 } else if (_cursor.pos.y >= this->top + this->height - 2) {
00312
00313 this->scrolling = 1;
00314 return;
00315 }
00316
00317 if (!this->GetDropDownItem(item)) return;
00318 }
00319
00320 if (this->selected_index != item) {
00321 this->selected_index = item;
00322 this->SetDirty();
00323 }
00324 }
00325 }
00326 };
00327
00328 void ShowDropDownList(Window *w, DropDownList *list, int selected, int button, uint width, bool auto_width, bool instant_close)
00329 {
00330 DeleteWindowById(WC_DROPDOWN_MENU, 0);
00331
00332
00333
00334 Rect wi_rect;
00335 Colours wi_colour;
00336 NWidgetCore *nwi = w->GetWidget<NWidgetCore>(button);
00337 wi_rect.left = nwi->pos_x;
00338 wi_rect.right = nwi->pos_x + nwi->current_x - 1;
00339 wi_rect.top = nwi->pos_y;
00340 wi_rect.bottom = nwi->pos_y + nwi->current_y - 1;
00341 wi_colour = nwi->colour;
00342
00343 if (nwi->type == NWID_BUTTON_DROPDOWN) {
00344 nwi->disp_flags |= ND_DROPDOWN_ACTIVE;
00345 } else {
00346 w->LowerWidget(button);
00347 }
00348 w->SetWidgetDirty(button);
00349
00350
00351 int top = w->top + wi_rect.bottom + 1;
00352
00353 if (width == 0) width = wi_rect.right - wi_rect.left + 1;
00354
00355 uint max_item_width = 0;
00356
00357 if (auto_width) {
00358
00359 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00360 const DropDownListItem *item = *it;
00361 max_item_width = max(max_item_width, item->Width() + 5);
00362 }
00363 }
00364
00365
00366 int list_height = 0;
00367
00368 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00369 DropDownListItem *item = *it;
00370 list_height += item->Height(width);
00371 }
00372
00373
00374 int height = list_height;
00375
00376
00377 int screen_bottom = GetMainViewBottom();
00378 bool scroll = false;
00379
00380
00381 if (top + height + 4 >= screen_bottom) {
00382
00383 if (w->top + wi_rect.top - height > GetMainViewTop()) {
00384 top = w->top + wi_rect.top - height - 4;
00385 } else {
00386
00387
00388 int avg_height = list_height / (int)list->size();
00389 int rows = (screen_bottom - 4 - top) / avg_height;
00390 height = rows * avg_height;
00391 scroll = true;
00392
00393
00394 max_item_width += WD_VSCROLLBAR_WIDTH;
00395 }
00396 }
00397
00398 if (auto_width) width = max(width, max_item_width);
00399
00400 Point dw_pos = { w->left + (_dynlang.text_dir == TD_RTL ? wi_rect.right + 1 - width : wi_rect.left), top};
00401 Dimension dw_size = {width, height};
00402 new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll);
00403 }
00404
00415 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width)
00416 {
00417 DropDownList *list = new DropDownList();
00418
00419 for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) {
00420 if (!HasBit(hidden_mask, i)) {
00421 list->push_back(new DropDownListStringItem(strings[i], i, HasBit(disabled_mask, i)));
00422 }
00423 }
00424
00425
00426 if (list->size() == 0) {
00427 DeleteDropDownList(list);
00428 return;
00429 }
00430
00431 ShowDropDownList(w, list, selected, button, width);
00432 }
00433
00439 int HideDropDownMenu(Window *pw)
00440 {
00441 Window *w;
00442 FOR_ALL_WINDOWS_FROM_BACK(w) {
00443 if (w->window_class != WC_DROPDOWN_MENU) continue;
00444
00445 DropdownWindow *dw = dynamic_cast<DropdownWindow*>(w);
00446 if (pw->window_class == dw->parent_wnd_class &&
00447 pw->window_number == dw->parent_wnd_num) {
00448 int parent_button = dw->parent_button;
00449 delete dw;
00450 return parent_button;
00451 }
00452 }
00453
00454 return -1;
00455 }
00456