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 |