src/widget.cpp
branchnoai
changeset 9724 b39bc69bb2f2
parent 9723 eee46cb39750
child 9826 9707ad4c9b60
equal deleted inserted replaced
9723:eee46cb39750 9724:b39bc69bb2f2
     2 
     2 
     3 /** @file widget.cpp */
     3 /** @file widget.cpp */
     4 
     4 
     5 #include "stdafx.h"
     5 #include "stdafx.h"
     6 #include "openttd.h"
     6 #include "openttd.h"
     7 #include "player.h"
     7 #include "core/math_func.hpp"
     8 #include "table/sprites.h"
     8 #include "player_func.h"
     9 #include "table/strings.h"
       
    10 #include "gfx_func.h"
     9 #include "gfx_func.h"
    11 #include "window_gui.h"
    10 #include "window_gui.h"
    12 #include "window_func.h"
    11 #include "window_func.h"
    13 
    12 #include "widgets/dropdown_func.h"
       
    13 
       
    14 #include "table/sprites.h"
       
    15 #include "table/strings.h"
       
    16 
       
    17 static const char *UPARROW   = "\xEE\x8A\xA0";
       
    18 static const char *DOWNARROW = "\xEE\x8A\xAA";
    14 
    19 
    15 static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom)
    20 static Point HandleScrollbarHittest(const Scrollbar *sb, int top, int bottom)
    16 {
    21 {
    17 	Point pt;
    22 	Point pt;
    18 	int height, count, pos, cap;
    23 	int height, count, pos, cap;
   206 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE);
   211 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE);
   207 
   212 
   208 			/* show different image when clicked for WWT_IMGBTN_2 */
   213 			/* show different image when clicked for WWT_IMGBTN_2 */
   209 			if ((wi->type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++;
   214 			if ((wi->type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++;
   210 			DrawSprite(img, PAL_NONE, r.left + 1 + clicked, r.top + 1 + clicked);
   215 			DrawSprite(img, PAL_NONE, r.left + 1 + clicked, r.top + 1 + clicked);
   211 			goto draw_default;
   216 			break;
   212 		}
   217 		}
   213 
   218 
   214 		case WWT_PANEL: {
   219 		case WWT_PANEL: {
   215 			assert(wi->data == 0);
   220 			assert(wi->data == 0);
   216 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE);
   221 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE);
   217 			goto draw_default;
   222 			break;
   218 		}
   223 		}
   219 
   224 
   220 		case WWT_TEXTBTN:
   225 		case WWT_TEXTBTN:
   221 		case WWT_TEXTBTN_2: {
   226 		case WWT_TEXTBTN_2: {
   222 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE);
   227 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, (clicked) ? FR_LOWERED : FR_NONE);
   227 			StringID str = wi->data;
   232 			StringID str = wi->data;
   228 
   233 
   229 			if ((wi->type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++;
   234 			if ((wi->type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++;
   230 
   235 
   231 			DrawStringCentered(((r.left + r.right + 1) >> 1) + clicked, ((r.top + r.bottom + 1) >> 1) - 5 + clicked, str, TC_FROMSTRING);
   236 			DrawStringCentered(((r.left + r.right + 1) >> 1) + clicked, ((r.top + r.bottom + 1) >> 1) - 5 + clicked, str, TC_FROMSTRING);
   232 			goto draw_default;
   237 			break;
   233 		}
   238 		}
   234 
   239 
   235 		case WWT_TEXT: {
   240 		case WWT_TEXT: {
   236 			const StringID str = wi->data;
   241 			const StringID str = wi->data;
   237 
   242 
   242 		case WWT_INSET: {
   247 		case WWT_INSET: {
   243 			const StringID str = wi->data;
   248 			const StringID str = wi->data;
   244 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED);
   249 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED);
   245 
   250 
   246 			if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 1, str, TC_FROMSTRING, r.right - r.left - 10);
   251 			if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 1, str, TC_FROMSTRING, r.right - r.left - 10);
   247 			goto draw_default;
   252 			break;
   248 		}
   253 		}
   249 
   254 
   250 		case WWT_MATRIX: {
   255 		case WWT_MATRIX: {
   251 			int c, d, ctr;
   256 			int c, d, ctr;
   252 			int x, amt1, amt2;
   257 			int x, amt1, amt2;
   286 			for (ctr = d; ctr > 1; ctr--) {
   291 			for (ctr = d; ctr > 1; ctr--) {
   287 				x += amt2;
   292 				x += amt2;
   288 				GfxFillRect(r.left + 1, x, r.right - 1, x, color);
   293 				GfxFillRect(r.left + 1, x, r.right - 1, x, color);
   289 			}
   294 			}
   290 
   295 
   291 			goto draw_default;
   296 			break;
   292 		}
   297 		}
   293 
   298 
   294 		/* vertical scrollbar */
   299 		/* vertical scrollbar */
   295 		case WWT_SCROLLBAR: {
   300 		case WWT_SCROLLBAR: {
   296 			Point pt;
   301 			Point pt;
   422 			GfxFillRect(r.right, r.top + 4, r.right, r.bottom - 1, c2);
   427 			GfxFillRect(r.right, r.top + 4, r.right, r.bottom - 1, c2);
   423 
   428 
   424 			GfxFillRect(r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1, c1);
   429 			GfxFillRect(r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1, c1);
   425 			GfxFillRect(r.left, r.bottom, r.right, r.bottom, c2);
   430 			GfxFillRect(r.left, r.bottom, r.right, r.bottom, c2);
   426 
   431 
   427 			goto draw_default;
   432 			break;
   428 		}
   433 		}
   429 
   434 
   430 		case WWT_STICKYBOX: {
   435 		case WWT_STICKYBOX: {
   431 			assert(wi->data == 0);
   436 			assert(wi->data == 0);
   432 			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
   437 			assert(r.right - r.left == 11); // XXX - to ensure the same sizes are used everywhere!
   466 			if (w->caption_color != 0xFF) {
   471 			if (w->caption_color != 0xFF) {
   467 				GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, _colour_gradient[_player_colors[w->caption_color]][4]);
   472 				GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, _colour_gradient[_player_colors[w->caption_color]][4]);
   468 			}
   473 			}
   469 
   474 
   470 			DrawStringCenteredTruncated(r.left + 2, r.right - 2, r.top + 2, wi->data, 0x84);
   475 			DrawStringCenteredTruncated(r.left + 2, r.right - 2, r.top + 2, wi->data, 0x84);
   471 draw_default:;
   476 			break;
   472 			if (w->IsWidgetDisabled(i)) {
   477 		}
   473 				GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[wi->color & 0xF][2] | (1 << PALETTE_MODIFIER_GREYOUT));
   478 
   474 			}
   479 		case WWT_DROPDOWN: {
   475 		}
   480 			assert(r.bottom - r.top == 11); // ensure consistent size
       
   481 
       
   482 			StringID str = wi->data;
       
   483 			DrawFrameRect(r.left, r.top, r.right - 12, r.bottom, wi->color, FR_NONE);
       
   484 			DrawFrameRect(r.right - 11, r.top, r.right, r.bottom, wi->color, clicked ? FR_LOWERED : FR_NONE);
       
   485 			DrawString(r.right - (clicked ? 8 : 9), r.top + (clicked ? 2 : 1), STR_0225, TC_BLACK);
       
   486 			if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 1, str, TC_BLACK, r.right - r.left - 12);
       
   487 			break;
       
   488 		}
       
   489 
       
   490 		case WWT_DROPDOWNIN: {
       
   491 			assert(r.bottom - r.top == 11); // ensure consistent size
       
   492 
       
   493 			StringID str = wi->data;
       
   494 			DrawFrameRect(r.left, r.top, r.right, r.bottom, wi->color, FR_LOWERED | FR_DARKENED);
       
   495 			DrawFrameRect(r.right - 11, r.top + 1, r.right - 1, r.bottom - 1, wi->color, clicked ? FR_LOWERED : FR_NONE);
       
   496 			DrawString(r.right - (clicked ? 8 : 9), r.top + (clicked ? 2 : 1), STR_0225, TC_BLACK);
       
   497 			if (str != STR_NULL) DrawStringTruncated(r.left + 2, r.top + 2, str, TC_BLACK, r.right - r.left - 12);
       
   498 			break;
       
   499 		}
       
   500 		}
       
   501 
       
   502 		if (w->IsWidgetDisabled(i)) {
       
   503 			GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, _colour_gradient[wi->color & 0xF][2] | (1 << PALETTE_MODIFIER_GREYOUT));
   476 		}
   504 		}
   477 	}
   505 	}
   478 
   506 
   479 
   507 
   480 	if (w->flags4 & WF_WHITE_BORDER_MASK) {
   508 	if (w->flags4 & WF_WHITE_BORDER_MASK) {
   481 		DrawFrameRect(0, 0, w->width - 1, w->height - 1, 0xF, FR_BORDERONLY);
   509 		DrawFrameRect(0, 0, w->width - 1, w->height - 1, 0xF, FR_BORDERONLY);
   482 	}
   510 	}
   483 
   511 
   484 }
   512 }
   485 
       
   486 static const Widget _dropdown_menu_widgets[] = {
       
   487 {      WWT_PANEL,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_NULL},
       
   488 {  WWT_SCROLLBAR,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   489 {   WIDGETS_END},
       
   490 };
       
   491 
       
   492 static int GetDropdownItem(const Window *w)
       
   493 {
       
   494 	byte item, counter;
       
   495 	int y;
       
   496 
       
   497 	if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0)
       
   498 		return -1;
       
   499 
       
   500 	y = _cursor.pos.y - w->top - 2 + w->vscroll.pos * 10;
       
   501 
       
   502 	if (y < 0)
       
   503 		return - 1;
       
   504 
       
   505 	item = y / 10;
       
   506 	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)
       
   507 		return - 1;
       
   508 
       
   509 	/* Skip hidden items -- +1 for each hidden item before the clicked item. */
       
   510 	for (counter = 0; item >= counter; ++counter)
       
   511 		if (HasBit(WP(w, dropdown_d).hidden_state, counter)) item++;
       
   512 
       
   513 	return item;
       
   514 }
       
   515 
       
   516 static void DropdownMenuWndProc(Window *w, WindowEvent *e)
       
   517 {
       
   518 	int item;
       
   519 
       
   520 	switch (e->event) {
       
   521 		case WE_PAINT: {
       
   522 			int x,y,i,sel;
       
   523 			int width, height;
       
   524 
       
   525 			DrawWindowWidgets(w);
       
   526 
       
   527 			x = 1;
       
   528 			y = 2 - w->vscroll.pos * 10;
       
   529 
       
   530 			sel    = WP(w, dropdown_d).selected_index;
       
   531 			width  = w->widget[0].right - 3;
       
   532 			height = w->widget[0].bottom - 3;
       
   533 
       
   534 			for (i = 0; WP(w, dropdown_d).items[i] != INVALID_STRING_ID; i++, sel--) {
       
   535 				if (HasBit(WP(w, dropdown_d).hidden_state, i)) continue;
       
   536 
       
   537 				if (y >= 0 && y <= height) {
       
   538 					if (WP(w, dropdown_d).items[i] != STR_NULL) {
       
   539 						if (sel == 0) GfxFillRect(x + 1, y, x + width, y + 9, 0);
       
   540 						DrawStringTruncated(x + 2, y, WP(w, dropdown_d).items[i], sel == 0 ? TC_WHITE : TC_BLACK, x + width);
       
   541 
       
   542 						if (HasBit(WP(w, dropdown_d).disabled_state, i)) {
       
   543 							GfxFillRect(x, y, x + width, y + 9,
       
   544 								(1 << PALETTE_MODIFIER_GREYOUT) | _colour_gradient[_dropdown_menu_widgets[0].color][5]
       
   545 							);
       
   546 						}
       
   547 					} else {
       
   548 						int c1 = _colour_gradient[_dropdown_menu_widgets[0].color][3];
       
   549 						int c2 = _colour_gradient[_dropdown_menu_widgets[0].color][7];
       
   550 
       
   551 						GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1);
       
   552 						GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2);
       
   553 					}
       
   554 				}
       
   555 				y += 10;
       
   556 			}
       
   557 		} break;
       
   558 
       
   559 		case WE_CLICK: {
       
   560 			if (e->we.click.widget != 0) break;
       
   561 			item = GetDropdownItem(w);
       
   562 			if (item >= 0) {
       
   563 				WP(w, dropdown_d).click_delay = 4;
       
   564 				WP(w, dropdown_d).selected_index = item;
       
   565 				SetWindowDirty(w);
       
   566 			}
       
   567 		} break;
       
   568 
       
   569 		case WE_MOUSELOOP: {
       
   570 			Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
       
   571 			if (w2 == NULL) {
       
   572 				DeleteWindow(w);
       
   573 				return;
       
   574 			}
       
   575 
       
   576 			if (WP(w, dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) {
       
   577 				WindowEvent e;
       
   578 				e.event = WE_DROPDOWN_SELECT;
       
   579 				e.we.dropdown.button = WP(w, dropdown_d).parent_button;
       
   580 				e.we.dropdown.index  = WP(w, dropdown_d).selected_index;
       
   581 				w2->wndproc(w2, &e);
       
   582 				DeleteWindow(w);
       
   583 				return;
       
   584 			}
       
   585 
       
   586 			if (WP(w, dropdown_d).drag_mode) {
       
   587 				item = GetDropdownItem(w);
       
   588 
       
   589 				if (!_left_button_clicked) {
       
   590 					WP(w, dropdown_d).drag_mode = false;
       
   591 					if (item < 0) return;
       
   592 					WP(w, dropdown_d).click_delay = 2;
       
   593 				} else {
       
   594 					if (item < 0) return;
       
   595 				}
       
   596 
       
   597 				WP(w, dropdown_d).selected_index = item;
       
   598 				SetWindowDirty(w);
       
   599 			}
       
   600 		} break;
       
   601 
       
   602 		case WE_DESTROY: {
       
   603 			Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
       
   604 			if (w2 != NULL) {
       
   605 				w2->RaiseWidget(WP(w, dropdown_d).parent_button);
       
   606 				w2->InvalidateWidget(WP(w, dropdown_d).parent_button);
       
   607 			}
       
   608 		} break;
       
   609 	}
       
   610 }
       
   611 
       
   612 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask)
       
   613 {
       
   614 	int i;
       
   615 	const Widget *wi;
       
   616 	Window *w2;
       
   617 	const Window *w3;
       
   618 	bool is_dropdown_menu_shown = w->IsWidgetLowered(button);
       
   619 	int top, height;
       
   620 	int screen_top, screen_bottom;
       
   621 	bool scroll = false;
       
   622 
       
   623 	DeleteWindowById(WC_DROPDOWN_MENU, 0);
       
   624 
       
   625 	if (is_dropdown_menu_shown) return;
       
   626 
       
   627 	w->LowerWidget(button);
       
   628 
       
   629 	w->InvalidateWidget(button);
       
   630 
       
   631 	for (i = 0; strings[i] != INVALID_STRING_ID; i++) {}
       
   632 	if (i == 0) return;
       
   633 
       
   634 	wi = &w->widget[button];
       
   635 
       
   636 	if (hidden_mask != 0) {
       
   637 		uint j;
       
   638 
       
   639 		for (j = 0; strings[j] != INVALID_STRING_ID; j++) {
       
   640 			if (HasBit(hidden_mask, j)) i--;
       
   641 		}
       
   642 	}
       
   643 
       
   644 	/* The preferred position is just below the dropdown calling widget */
       
   645 	top = w->top + wi->bottom + 2;
       
   646 	height = i * 10 + 4;
       
   647 
       
   648 	w3 = FindWindowById(WC_STATUS_BAR, 0);
       
   649 	screen_bottom = w3 == NULL ? _screen.height : w3->top;
       
   650 
       
   651 	/* Check if the dropdown will fully fit below the widget */
       
   652 	if (top + height >= screen_bottom) {
       
   653 		w3 = FindWindowById(WC_MAIN_TOOLBAR, 0);
       
   654 		screen_top = w3 == NULL ? 0 : w3->top + w3->height;
       
   655 
       
   656 		/* If not, check if it will fit above the widget */
       
   657 		if (w->top + wi->top - height - 1 > screen_top) {
       
   658 			top = w->top + wi->top - height - 1;
       
   659 		} else {
       
   660 			/* ... and lastly if it won't, enable the scroll bar and fit the
       
   661 			 * list in below the widget */
       
   662 			int rows = (screen_bottom - 4 - top) / 10;
       
   663 			height = rows * 10 + 4;
       
   664 			scroll = true;
       
   665 		}
       
   666 	}
       
   667 
       
   668 	w2 = AllocateWindow(
       
   669 		w->left + wi[-1].left + 1,
       
   670 		top,
       
   671 		wi->right - wi[-1].left + 1,
       
   672 		height,
       
   673 		DropdownMenuWndProc,
       
   674 		WC_DROPDOWN_MENU,
       
   675 		_dropdown_menu_widgets);
       
   676 
       
   677 	w2->widget[0].color = wi->color;
       
   678 	w2->widget[0].right = wi->right - wi[-1].left;
       
   679 	w2->widget[0].bottom = height - 1;
       
   680 
       
   681 	w2->SetWidgetHiddenState(1, !scroll);
       
   682 
       
   683 	if (scroll) {
       
   684 		/* We're scrolling, so enable the scroll bar and shrink the list by
       
   685 		 * the scrollbar's width */
       
   686 		w2->widget[1].color  = wi->color;
       
   687 		w2->widget[1].right  = w2->widget[0].right;
       
   688 		w2->widget[1].left   = w2->widget[1].right - 11;
       
   689 		w2->widget[1].bottom = height - 1;
       
   690 		w2->widget[0].right -= 12;
       
   691 
       
   692 		w2->vscroll.cap   = (height - 4) / 10;
       
   693 		w2->vscroll.count = i;
       
   694 	}
       
   695 
       
   696 	w2->desc_flags = WDF_DEF_WIDGET;
       
   697 	w2->flags4 &= ~WF_WHITE_BORDER_MASK;
       
   698 
       
   699 	WP(w2, dropdown_d).disabled_state = disabled_mask;
       
   700 	WP(w2, dropdown_d).hidden_state = hidden_mask;
       
   701 
       
   702 	WP(w2, dropdown_d).parent_wnd_class = w->window_class;
       
   703 	WP(w2, dropdown_d).parent_wnd_num = w->window_number;
       
   704 	WP(w2, dropdown_d).parent_button = button;
       
   705 
       
   706 	WP(w2, dropdown_d).num_items = i;
       
   707 	WP(w2, dropdown_d).selected_index = selected;
       
   708 	WP(w2, dropdown_d).items = strings;
       
   709 
       
   710 	WP(w2, dropdown_d).click_delay = 0;
       
   711 	WP(w2, dropdown_d).drag_mode = true;
       
   712 }
       
   713 
       
   714 
   513 
   715 static void ResizeWidgets(Window *w, byte a, byte b)
   514 static void ResizeWidgets(Window *w, byte a, byte b)
   716 {
   515 {
   717 	int16 offset = w->widget[a].left;
   516 	int16 offset = w->widget[a].left;
   718 	int16 length = w->widget[b].right - offset;
   517 	int16 length = w->widget[b].right - offset;
   781 			ResizeButtons(w, left, widget);
   580 			ResizeButtons(w, left, widget);
   782 			ResizeButtons(w, widget + 1, right);
   581 			ResizeButtons(w, widget + 1, right);
   783 		}
   582 		}
   784 	}
   583 	}
   785 }
   584 }
       
   585 
       
   586 void ResizeWindowForWidget(Window *w, int widget, int delta_x, int delta_y)
       
   587 {
       
   588 	int right  = w->widget[widget].right;
       
   589 	int bottom = w->widget[widget].bottom;
       
   590 
       
   591 	for (uint i = 0; i < w->widget_count; i++) {
       
   592 		if (w->widget[i].left >= right) w->widget[i].left += delta_x;
       
   593 		if (w->widget[i].right >= right) w->widget[i].right += delta_x;
       
   594 		if (w->widget[i].top >= bottom) w->widget[i].top += delta_y;
       
   595 		if (w->widget[i].bottom >= bottom) w->widget[i].bottom += delta_y;
       
   596 	}
       
   597 
       
   598 	w->width  += delta_x;
       
   599 	w->height += delta_y;
       
   600 	w->resize.width  += delta_x;
       
   601 	w->resize.height += delta_y;
       
   602 }
       
   603 
       
   604 /** Draw a sort button's up or down arrow symbol.
       
   605  * @param w Window of widget
       
   606  * @param widget Sort button widget
       
   607  * @param state State of sort button
       
   608  */
       
   609 void DrawSortButtonState(const Window *w, int widget, SortButtonState state)
       
   610 {
       
   611 	if (state == SBS_OFF) return;
       
   612 
       
   613 	int offset = w->IsWidgetLowered(widget) ? 1 : 0;
       
   614 	DoDrawString(state == SBS_DOWN ? DOWNARROW : UPARROW, w->widget[widget].right - 11 + offset, w->widget[widget].top + 1 + offset, TC_BLACK);
       
   615 }