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; |