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
00285
00286 this->window_class = WC_INVALID;
00287 this->SetDirty();
00288
00289 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00290 delete this;
00291 return;
00292 }
00293
00294 if (this->drag_mode) {
00295 int item;
00296
00297 if (!_left_button_clicked) {
00298 this->drag_mode = false;
00299 if (!this->GetDropDownItem(item)) {
00300 if (this->instant_close) {
00301
00302
00303 this->window_class = WC_INVALID;
00304 this->SetDirty();
00305
00306 if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == this->parent_button) {
00307
00308
00309 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00310 }
00311 delete this;
00312 }
00313 return;
00314 }
00315 this->click_delay = 2;
00316 } else {
00317 if (_cursor.pos.y <= this->top + 2) {
00318
00319 this->scrolling = -1;
00320 return;
00321 } else if (_cursor.pos.y >= this->top + this->height - 2) {
00322
00323 this->scrolling = 1;
00324 return;
00325 }
00326
00327 if (!this->GetDropDownItem(item)) return;
00328 }
00329
00330 if (this->selected_index != item) {
00331 this->selected_index = item;
00332 this->SetDirty();
00333 }
00334 }
00335 }
00336 };
00337
00338 void ShowDropDownList(Window *w, DropDownList *list, int selected, int button, uint width, bool auto_width, bool instant_close)
00339 {
00340 DeleteWindowById(WC_DROPDOWN_MENU, 0);
00341
00342
00343
00344 Rect wi_rect;
00345 Colours wi_colour;
00346 NWidgetCore *nwi = w->GetWidget<NWidgetCore>(button);
00347 wi_rect.left = nwi->pos_x;
00348 wi_rect.right = nwi->pos_x + nwi->current_x - 1;
00349 wi_rect.top = nwi->pos_y;
00350 wi_rect.bottom = nwi->pos_y + nwi->current_y - 1;
00351 wi_colour = nwi->colour;
00352
00353 if (nwi->type == NWID_BUTTON_DROPDOWN) {
00354 nwi->disp_flags |= ND_DROPDOWN_ACTIVE;
00355 } else {
00356 w->LowerWidget(button);
00357 }
00358 w->SetWidgetDirty(button);
00359
00360
00361 int top = w->top + wi_rect.bottom + 1;
00362
00363 if (width == 0) width = wi_rect.right - wi_rect.left + 1;
00364
00365 uint max_item_width = 0;
00366
00367 if (auto_width) {
00368
00369 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00370 const DropDownListItem *item = *it;
00371 max_item_width = max(max_item_width, item->Width() + 5);
00372 }
00373 }
00374
00375
00376 int list_height = 0;
00377
00378 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00379 DropDownListItem *item = *it;
00380 list_height += item->Height(width);
00381 }
00382
00383
00384 int height = list_height;
00385
00386
00387 int screen_bottom = GetMainViewBottom();
00388 bool scroll = false;
00389
00390
00391 if (top + height + 4 >= screen_bottom) {
00392
00393 if (w->top + wi_rect.top - height > GetMainViewTop()) {
00394 top = w->top + wi_rect.top - height - 4;
00395 } else {
00396
00397
00398 int avg_height = list_height / (int)list->size();
00399 int rows = (screen_bottom - 4 - top) / avg_height;
00400 height = rows * avg_height;
00401 scroll = true;
00402
00403
00404 max_item_width += WD_VSCROLLBAR_WIDTH;
00405 }
00406 }
00407
00408 if (auto_width) width = max(width, max_item_width);
00409
00410 Point dw_pos = { w->left + (_dynlang.text_dir == TD_RTL ? wi_rect.right + 1 - width : wi_rect.left), top};
00411 Dimension dw_size = {width, height};
00412 new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll);
00413 }
00414
00425 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width)
00426 {
00427 DropDownList *list = new DropDownList();
00428
00429 for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) {
00430 if (!HasBit(hidden_mask, i)) {
00431 list->push_back(new DropDownListStringItem(strings[i], i, HasBit(disabled_mask, i)));
00432 }
00433 }
00434
00435
00436 if (list->size() == 0) {
00437 DeleteDropDownList(list);
00438 return;
00439 }
00440
00441 ShowDropDownList(w, list, selected, button, width);
00442 }
00443
00449 int HideDropDownMenu(Window *pw)
00450 {
00451 Window *w;
00452 FOR_ALL_WINDOWS_FROM_BACK(w) {
00453 if (w->window_class != WC_DROPDOWN_MENU) continue;
00454
00455 DropdownWindow *dw = dynamic_cast<DropdownWindow*>(w);
00456 if (pw->window_class == dw->parent_wnd_class &&
00457 pw->window_number == dw->parent_wnd_num) {
00458 int parent_button = dw->parent_button;
00459 delete dw;
00460 return parent_button;
00461 }
00462 }
00463
00464 return -1;
00465 }
00466