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_SHOW_SCROLL,
00074 DDM_SCROLL,
00075 };
00076
00077 static const NWidgetPart _nested_dropdown_menu_widgets[] = {
00078 NWidget(NWID_HORIZONTAL),
00079 NWidget(WWT_PANEL, COLOUR_END, DDM_ITEMS), SetMinimalSize(1, 1), SetScrollbar(DDM_SCROLL), EndContainer(),
00080 NWidget(NWID_SELECTION, INVALID_COLOUR, DDM_SHOW_SCROLL),
00081 NWidget(NWID_VSCROLLBAR, COLOUR_END, DDM_SCROLL),
00082 EndContainer(),
00083 EndContainer(),
00084 };
00085
00086 const WindowDesc _dropdown_desc(
00087 WDP_MANUAL, 0, 0,
00088 WC_DROPDOWN_MENU, WC_NONE,
00089 0,
00090 _nested_dropdown_menu_widgets, lengthof(_nested_dropdown_menu_widgets)
00091 );
00092
00094 struct DropdownWindow : Window {
00095 WindowClass parent_wnd_class;
00096 WindowNumber parent_wnd_num;
00097 byte parent_button;
00098 DropDownList *list;
00099 int selected_index;
00100 byte click_delay;
00101 bool drag_mode;
00102 bool instant_close;
00103 int scrolling;
00104 Point position;
00105 Scrollbar *vscroll;
00106
00120 DropdownWindow(Window *parent, DropDownList *list, int selected, int button, bool instant_close, const Point &position, const Dimension &size, Colours wi_colour, bool scroll) : Window()
00121 {
00122 this->position = position;
00123
00124 this->CreateNestedTree(&_dropdown_desc);
00125
00126 this->vscroll = this->GetScrollbar(DDM_SCROLL);
00127
00128 uint items_width = size.width - (scroll ? WD_VSCROLLBAR_WIDTH : 0);
00129 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(DDM_ITEMS);
00130 nwi->SetMinimalSize(items_width, size.height + 4);
00131 nwi->colour = wi_colour;
00132
00133 nwi = this->GetWidget<NWidgetCore>(DDM_SCROLL);
00134 nwi->colour = wi_colour;
00135
00136 this->GetWidget<NWidgetStacked>(DDM_SHOW_SCROLL)->SetDisplayedPlane(scroll ? 0 : SZSP_NONE);
00137
00138 this->FinishInitNested(&_dropdown_desc, 0);
00139 this->flags4 &= ~WF_WHITE_BORDER_MASK;
00140
00141
00142 int list_height = 0;
00143 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00144 DropDownListItem *item = *it;
00145 list_height += item->Height(items_width);
00146 }
00147
00148
00149 this->vscroll->SetCapacity(size.height * (uint16)list->size() / list_height);
00150 this->vscroll->SetCount((uint16)list->size());
00151
00152 this->parent_wnd_class = parent->window_class;
00153 this->parent_wnd_num = parent->window_number;
00154 this->parent_button = button;
00155 this->list = list;
00156 this->selected_index = selected;
00157 this->click_delay = 0;
00158 this->drag_mode = true;
00159 this->instant_close = instant_close;
00160 }
00161
00162 ~DropdownWindow()
00163 {
00164 Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
00165 if (w2 != NULL) {
00166 if (w2->nested_array != NULL) {
00167 NWidgetCore *nwi2 = w2->GetWidget<NWidgetCore>(this->parent_button);
00168 if (nwi2->type == NWID_BUTTON_DROPDOWN) {
00169 nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
00170 } else {
00171 w2->RaiseWidget(this->parent_button);
00172 }
00173 } else {
00174 w2->RaiseWidget(this->parent_button);
00175 }
00176 w2->SetWidgetDirty(this->parent_button);
00177 }
00178
00179 DeleteDropDownList(this->list);
00180 }
00181
00182 virtual Point OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
00183 {
00184 return this->position;
00185 }
00186
00192 bool GetDropDownItem(int &value)
00193 {
00194 if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return false;
00195
00196 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(DDM_ITEMS);
00197 int y = _cursor.pos.y - this->top - nwi->pos_y - 2;
00198 int width = nwi->current_x - 4;
00199 int pos = this->vscroll->GetPosition();
00200
00201 const DropDownList *list = this->list;
00202
00203 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00204
00205 if (--pos >= 0) continue;
00206
00207 const DropDownListItem *item = *it;
00208 int item_height = item->Height(width);
00209
00210 if (y < item_height) {
00211 if (item->masked || !item->Selectable()) return false;
00212 value = item->result;
00213 return true;
00214 }
00215
00216 y -= item_height;
00217 }
00218
00219 return false;
00220 }
00221
00222 virtual void DrawWidget(const Rect &r, int widget) const
00223 {
00224 if (widget != DDM_ITEMS) return;
00225
00226 TextColour colour = (TextColour)this->GetWidget<NWidgetCore>(widget)->colour;
00227
00228 int y = r.top + 2;
00229 int pos = this->vscroll->GetPosition();
00230 for (DropDownList::const_iterator it = this->list->begin(); it != this->list->end(); ++it) {
00231 const DropDownListItem *item = *it;
00232 int item_height = item->Height(r.right - r.left + 1);
00233
00234
00235 if (--pos >= 0) continue;
00236
00237 if (y + item_height < r.bottom) {
00238 bool selected = (this->selected_index == item->result);
00239 if (selected) GfxFillRect(r.left + 2, y, r.right - 1, y + item_height - 1, 0);
00240
00241 item->Draw(r.left, r.right, y, r.bottom, selected, colour);
00242
00243 if (item->masked) {
00244 GfxFillRect(r.left + 1, y, r.right - 1, y + item_height - 1, _colour_gradient[colour][5], FILLRECT_CHECKER);
00245 }
00246 }
00247 y += item_height;
00248 }
00249 }
00250
00251 virtual void OnClick(Point pt, int widget, int click_count)
00252 {
00253 if (widget != DDM_ITEMS) return;
00254 int item;
00255 if (this->GetDropDownItem(item)) {
00256 this->click_delay = 4;
00257 this->selected_index = item;
00258 this->SetDirty();
00259 }
00260 }
00261
00262 virtual void OnTick()
00263 {
00264 if (this->scrolling != 0) {
00265 int pos = this->vscroll->GetPosition();
00266
00267 this->vscroll->UpdatePosition(this->scrolling);
00268 this->scrolling = 0;
00269
00270 if (pos != this->vscroll->GetPosition()) {
00271 this->SetDirty();
00272 }
00273 }
00274 }
00275
00276 virtual void OnMouseLoop()
00277 {
00278 Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
00279 if (w2 == NULL) {
00280 delete this;
00281 return;
00282 }
00283
00284 if (this->click_delay != 0 && --this->click_delay == 0) {
00285
00286
00287 this->window_class = WC_INVALID;
00288 this->SetDirty();
00289
00290 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00291 delete this;
00292 return;
00293 }
00294
00295 if (this->drag_mode) {
00296 int item;
00297
00298 if (!_left_button_clicked) {
00299 this->drag_mode = false;
00300 if (!this->GetDropDownItem(item)) {
00301 if (this->instant_close) {
00302
00303
00304 this->window_class = WC_INVALID;
00305 this->SetDirty();
00306
00307 if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == this->parent_button) {
00308
00309
00310 w2->OnDropdownSelect(this->parent_button, this->selected_index);
00311 }
00312 delete this;
00313 }
00314 return;
00315 }
00316 this->click_delay = 2;
00317 } else {
00318 if (_cursor.pos.y <= this->top + 2) {
00319
00320 this->scrolling = -1;
00321 return;
00322 } else if (_cursor.pos.y >= this->top + this->height - 2) {
00323
00324 this->scrolling = 1;
00325 return;
00326 }
00327
00328 if (!this->GetDropDownItem(item)) return;
00329 }
00330
00331 if (this->selected_index != item) {
00332 this->selected_index = item;
00333 this->SetDirty();
00334 }
00335 }
00336 }
00337 };
00338
00339 void ShowDropDownList(Window *w, DropDownList *list, int selected, int button, uint width, bool auto_width, bool instant_close)
00340 {
00341 DeleteWindowById(WC_DROPDOWN_MENU, 0);
00342
00343
00344
00345 Rect wi_rect;
00346 Colours wi_colour;
00347 NWidgetCore *nwi = w->GetWidget<NWidgetCore>(button);
00348 wi_rect.left = nwi->pos_x;
00349 wi_rect.right = nwi->pos_x + nwi->current_x - 1;
00350 wi_rect.top = nwi->pos_y;
00351 wi_rect.bottom = nwi->pos_y + nwi->current_y - 1;
00352 wi_colour = nwi->colour;
00353
00354 if (nwi->type == NWID_BUTTON_DROPDOWN) {
00355 nwi->disp_flags |= ND_DROPDOWN_ACTIVE;
00356 } else {
00357 w->LowerWidget(button);
00358 }
00359 w->SetWidgetDirty(button);
00360
00361
00362 int top = w->top + wi_rect.bottom + 1;
00363
00364 if (width == 0) width = wi_rect.right - wi_rect.left + 1;
00365
00366 uint max_item_width = 0;
00367
00368 if (auto_width) {
00369
00370 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00371 const DropDownListItem *item = *it;
00372 max_item_width = max(max_item_width, item->Width() + 5);
00373 }
00374 }
00375
00376
00377 int list_height = 0;
00378
00379 for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
00380 DropDownListItem *item = *it;
00381 list_height += item->Height(width);
00382 }
00383
00384
00385 int height = list_height;
00386
00387
00388 int screen_bottom = GetMainViewBottom();
00389 bool scroll = false;
00390
00391
00392 if (top + height + 4 >= screen_bottom) {
00393
00394 if (w->top + wi_rect.top - height > GetMainViewTop()) {
00395 top = w->top + wi_rect.top - height - 4;
00396 } else {
00397
00398
00399 int avg_height = list_height / (int)list->size();
00400 int rows = (screen_bottom - 4 - top) / avg_height;
00401 height = rows * avg_height;
00402 scroll = true;
00403
00404
00405 max_item_width += WD_VSCROLLBAR_WIDTH;
00406 }
00407 }
00408
00409 if (auto_width) width = max(width, max_item_width);
00410
00411 Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - width : wi_rect.left), top};
00412 Dimension dw_size = {width, height};
00413 new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll);
00414 }
00415
00427 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width)
00428 {
00429 DropDownList *list = new DropDownList();
00430
00431 for (uint i = 0; strings[i] != INVALID_STRING_ID; i++) {
00432 if (!HasBit(hidden_mask, i)) {
00433 list->push_back(new DropDownListStringItem(strings[i], i, HasBit(disabled_mask, i)));
00434 }
00435 }
00436
00437
00438 if (list->size() == 0) {
00439 DeleteDropDownList(list);
00440 return;
00441 }
00442
00443 ShowDropDownList(w, list, selected, button, width);
00444 }
00445
00451 int HideDropDownMenu(Window *pw)
00452 {
00453 Window *w;
00454 FOR_ALL_WINDOWS_FROM_BACK(w) {
00455 if (w->window_class != WC_DROPDOWN_MENU) continue;
00456
00457 DropdownWindow *dw = dynamic_cast<DropdownWindow*>(w);
00458 if (pw->window_class == dw->parent_wnd_class &&
00459 pw->window_number == dw->parent_wnd_num) {
00460 int parent_button = dw->parent_button;
00461 delete dw;
00462 return parent_button;
00463 }
00464 }
00465
00466 return -1;
00467 }
00468