src/widget.cpp
changeset 8780 6f3f3ec6c69a
parent 8760 ce0891c412ce
child 8816 0917ba1990f2
equal deleted inserted replaced
8779:efb41d093a39 8780:6f3f3ec6c69a
     7 #include "core/math_func.hpp"
     7 #include "core/math_func.hpp"
     8 #include "player_func.h"
     8 #include "player_func.h"
     9 #include "gfx_func.h"
     9 #include "gfx_func.h"
    10 #include "window_gui.h"
    10 #include "window_gui.h"
    11 #include "window_func.h"
    11 #include "window_func.h"
       
    12 #include "widgets/dropdown_func.h"
    12 
    13 
    13 #include "table/sprites.h"
    14 #include "table/sprites.h"
    14 #include "table/strings.h"
    15 #include "table/strings.h"
    15 
    16 
    16 static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom)
    17 static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom)
   482 		DrawFrameRect(0, 0, w->width - 1, w->height - 1, 0xF, FR_BORDERONLY);
   483 		DrawFrameRect(0, 0, w->width - 1, w->height - 1, 0xF, FR_BORDERONLY);
   483 	}
   484 	}
   484 
   485 
   485 }
   486 }
   486 
   487 
   487 static const Widget _dropdown_menu_widgets[] = {
       
   488 {      WWT_PANEL,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_NULL},
       
   489 {  WWT_SCROLLBAR,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   490 {   WIDGETS_END},
       
   491 };
       
   492 
       
   493 static int GetDropdownItem(const Window *w)
       
   494 {
       
   495 	byte item, counter;
       
   496 	int y;
       
   497 
       
   498 	if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0)
       
   499 		return -1;
       
   500 
       
   501 	y = _cursor.pos.y - w->top - 2 + w->vscroll.pos * 10;
       
   502 
       
   503 	if (y < 0)
       
   504 		return - 1;
       
   505 
       
   506 	item = y / 10;
       
   507 	if (item >= WP(w, dropdown_d).num_items || (HasBit(WP(w,dropdown_d).disabled_state, item) && !HasBit(WP(w,dropdown_d).hidden_state, item)) || WP(w,dropdown_d).items[item] == 0)
       
   508 		return - 1;
       
   509 
       
   510 	/* Skip hidden items -- +1 for each hidden item before the clicked item. */
       
   511 	for (counter = 0; item >= counter; ++counter)
       
   512 		if (HasBit(WP(w, dropdown_d).hidden_state, counter)) item++;
       
   513 
       
   514 	return item;
       
   515 }
       
   516 
       
   517 static void DropdownMenuWndProc(Window *w, WindowEvent *e)
       
   518 {
       
   519 	int item;
       
   520 
       
   521 	switch (e->event) {
       
   522 		case WE_PAINT: {
       
   523 			int x,y,i,sel;
       
   524 			int width, height;
       
   525 
       
   526 			DrawWindowWidgets(w);
       
   527 
       
   528 			x = 1;
       
   529 			y = 2 - w->vscroll.pos * 10;
       
   530 
       
   531 			sel    = WP(w, dropdown_d).selected_index;
       
   532 			width  = w->widget[0].right - 3;
       
   533 			height = w->widget[0].bottom - 3;
       
   534 
       
   535 			for (i = 0; WP(w, dropdown_d).items[i] != INVALID_STRING_ID; i++, sel--) {
       
   536 				if (HasBit(WP(w, dropdown_d).hidden_state, i)) continue;
       
   537 
       
   538 				if (y >= 0 && y <= height) {
       
   539 					if (WP(w, dropdown_d).items[i] != STR_NULL) {
       
   540 						if (sel == 0) GfxFillRect(x + 1, y, x + width, y + 9, 0);
       
   541 						DrawStringTruncated(x + 2, y, WP(w, dropdown_d).items[i], sel == 0 ? TC_WHITE : TC_BLACK, x + width);
       
   542 
       
   543 						if (HasBit(WP(w, dropdown_d).disabled_state, i)) {
       
   544 							GfxFillRect(x, y, x + width, y + 9,
       
   545 								(1 << PALETTE_MODIFIER_GREYOUT) | _colour_gradient[_dropdown_menu_widgets[0].color][5]
       
   546 							);
       
   547 						}
       
   548 					} else {
       
   549 						int c1 = _colour_gradient[_dropdown_menu_widgets[0].color][3];
       
   550 						int c2 = _colour_gradient[_dropdown_menu_widgets[0].color][7];
       
   551 
       
   552 						GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1);
       
   553 						GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2);
       
   554 					}
       
   555 				}
       
   556 				y += 10;
       
   557 			}
       
   558 		} break;
       
   559 
       
   560 		case WE_CLICK: {
       
   561 			if (e->we.click.widget != 0) break;
       
   562 			item = GetDropdownItem(w);
       
   563 			if (item >= 0) {
       
   564 				WP(w, dropdown_d).click_delay = 4;
       
   565 				WP(w, dropdown_d).selected_index = item;
       
   566 				SetWindowDirty(w);
       
   567 			}
       
   568 		} break;
       
   569 
       
   570 		case WE_MOUSELOOP: {
       
   571 			Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
       
   572 			if (w2 == NULL) {
       
   573 				DeleteWindow(w);
       
   574 				return;
       
   575 			}
       
   576 
       
   577 			if (WP(w, dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) {
       
   578 				WindowEvent e;
       
   579 				e.event = WE_DROPDOWN_SELECT;
       
   580 				e.we.dropdown.button = WP(w, dropdown_d).parent_button;
       
   581 				e.we.dropdown.index  = WP(w, dropdown_d).selected_index;
       
   582 				w2->wndproc(w2, &e);
       
   583 				DeleteWindow(w);
       
   584 				return;
       
   585 			}
       
   586 
       
   587 			if (WP(w, dropdown_d).drag_mode) {
       
   588 				item = GetDropdownItem(w);
       
   589 
       
   590 				if (!_left_button_clicked) {
       
   591 					WP(w, dropdown_d).drag_mode = false;
       
   592 					if (item < 0) return;
       
   593 					WP(w, dropdown_d).click_delay = 2;
       
   594 				} else {
       
   595 					if (item < 0) return;
       
   596 				}
       
   597 
       
   598 				WP(w, dropdown_d).selected_index = item;
       
   599 				SetWindowDirty(w);
       
   600 			}
       
   601 		} break;
       
   602 
       
   603 		case WE_DESTROY: {
       
   604 			Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
       
   605 			if (w2 != NULL) {
       
   606 				w2->RaiseWidget(WP(w, dropdown_d).parent_button);
       
   607 				w2->InvalidateWidget(WP(w, dropdown_d).parent_button);
       
   608 			}
       
   609 		} break;
       
   610 	}
       
   611 }
       
   612 
       
   613 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask)
       
   614 {
       
   615 	int i;
       
   616 	const Widget *wi;
       
   617 	Window *w2;
       
   618 	const Window *w3;
       
   619 	bool is_dropdown_menu_shown = w->IsWidgetLowered(button);
       
   620 	int top, height;
       
   621 	int screen_top, screen_bottom;
       
   622 	bool scroll = false;
       
   623 
       
   624 	DeleteWindowById(WC_DROPDOWN_MENU, 0);
       
   625 
       
   626 	if (is_dropdown_menu_shown) return;
       
   627 
       
   628 	w->LowerWidget(button);
       
   629 
       
   630 	w->InvalidateWidget(button);
       
   631 
       
   632 	for (i = 0; strings[i] != INVALID_STRING_ID; i++) {}
       
   633 	if (i == 0) return;
       
   634 
       
   635 	wi = &w->widget[button];
       
   636 
       
   637 	if (hidden_mask != 0) {
       
   638 		uint j;
       
   639 
       
   640 		for (j = 0; strings[j] != INVALID_STRING_ID; j++) {
       
   641 			if (HasBit(hidden_mask, j)) i--;
       
   642 		}
       
   643 	}
       
   644 
       
   645 	/* The preferred position is just below the dropdown calling widget */
       
   646 	top = w->top + wi->bottom + 2;
       
   647 	height = i * 10 + 4;
       
   648 
       
   649 	w3 = FindWindowById(WC_STATUS_BAR, 0);
       
   650 	screen_bottom = w3 == NULL ? _screen.height : w3->top;
       
   651 
       
   652 	/* Check if the dropdown will fully fit below the widget */
       
   653 	if (top + height >= screen_bottom) {
       
   654 		w3 = FindWindowById(WC_MAIN_TOOLBAR, 0);
       
   655 		screen_top = w3 == NULL ? 0 : w3->top + w3->height;
       
   656 
       
   657 		/* If not, check if it will fit above the widget */
       
   658 		if (w->top + wi->top - height - 1 > screen_top) {
       
   659 			top = w->top + wi->top - height - 1;
       
   660 		} else {
       
   661 			/* ... and lastly if it won't, enable the scroll bar and fit the
       
   662 			 * list in below the widget */
       
   663 			int rows = (screen_bottom - 4 - top) / 10;
       
   664 			height = rows * 10 + 4;
       
   665 			scroll = true;
       
   666 		}
       
   667 	}
       
   668 
       
   669 	w2 = AllocateWindow(
       
   670 		w->left + wi[-1].left + 1,
       
   671 		top,
       
   672 		wi->right - wi[-1].left + 1,
       
   673 		height,
       
   674 		DropdownMenuWndProc,
       
   675 		WC_DROPDOWN_MENU,
       
   676 		_dropdown_menu_widgets);
       
   677 
       
   678 	w2->widget[0].color = wi->color;
       
   679 	w2->widget[0].right = wi->right - wi[-1].left;
       
   680 	w2->widget[0].bottom = height - 1;
       
   681 
       
   682 	w2->SetWidgetHiddenState(1, !scroll);
       
   683 
       
   684 	if (scroll) {
       
   685 		/* We're scrolling, so enable the scroll bar and shrink the list by
       
   686 		 * the scrollbar's width */
       
   687 		w2->widget[1].color  = wi->color;
       
   688 		w2->widget[1].right  = w2->widget[0].right;
       
   689 		w2->widget[1].left   = w2->widget[1].right - 11;
       
   690 		w2->widget[1].bottom = height - 1;
       
   691 		w2->widget[0].right -= 12;
       
   692 
       
   693 		w2->vscroll.cap   = (height - 4) / 10;
       
   694 		w2->vscroll.count = i;
       
   695 	}
       
   696 
       
   697 	w2->desc_flags = WDF_DEF_WIDGET;
       
   698 	w2->flags4 &= ~WF_WHITE_BORDER_MASK;
       
   699 
       
   700 	WP(w2, dropdown_d).disabled_state = disabled_mask;
       
   701 	WP(w2, dropdown_d).hidden_state = hidden_mask;
       
   702 
       
   703 	WP(w2, dropdown_d).parent_wnd_class = w->window_class;
       
   704 	WP(w2, dropdown_d).parent_wnd_num = w->window_number;
       
   705 	WP(w2, dropdown_d).parent_button = button;
       
   706 
       
   707 	WP(w2, dropdown_d).num_items = i;
       
   708 	WP(w2, dropdown_d).selected_index = selected;
       
   709 	WP(w2, dropdown_d).items = strings;
       
   710 
       
   711 	WP(w2, dropdown_d).click_delay = 0;
       
   712 	WP(w2, dropdown_d).drag_mode = true;
       
   713 }
       
   714 
       
   715 
       
   716 static void ResizeWidgets(Window *w, byte a, byte b)
   488 static void ResizeWidgets(Window *w, byte a, byte b)
   717 {
   489 {
   718 	int16 offset = w->widget[a].left;
   490 	int16 offset = w->widget[a].left;
   719 	int16 length = w->widget[b].right - offset;
   491 	int16 length = w->widget[b].right - offset;
   720 
   492