1 /* 2 * Copyright 2001-2006, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stephan Aßmus <superstippi@gmx.de> 8 * Ingo Weinhold <bonefish@cs.tu-berlin.de> 9 */ 10 11 #include <stdlib.h> 12 #include <string.h> 13 14 #include <AbstractLayoutItem.h> 15 #include <LayoutUtils.h> 16 #include <MenuBar.h> 17 #include <MenuField.h> 18 #include <Message.h> 19 #include <BMCPrivate.h> 20 #include <Window.h> 21 22 23 class BMenuField::LabelLayoutItem : public BAbstractLayoutItem { 24 public: 25 LabelLayoutItem(BMenuField* parent); 26 27 virtual bool IsVisible(); 28 virtual void SetVisible(bool visible); 29 30 virtual BRect Frame(); 31 virtual void SetFrame(BRect frame); 32 33 virtual BView* View(); 34 35 virtual BSize BaseMinSize(); 36 virtual BSize BaseMaxSize(); 37 virtual BSize BasePreferredSize(); 38 virtual BAlignment BaseAlignment(); 39 40 private: 41 BMenuField* fParent; 42 BRect fFrame; 43 }; 44 45 46 class BMenuField::MenuBarLayoutItem : public BAbstractLayoutItem { 47 public: 48 MenuBarLayoutItem(BMenuField* parent); 49 50 virtual bool IsVisible(); 51 virtual void SetVisible(bool visible); 52 53 virtual BRect Frame(); 54 virtual void SetFrame(BRect frame); 55 56 virtual BView* View(); 57 58 virtual BSize BaseMinSize(); 59 virtual BSize BaseMaxSize(); 60 virtual BSize BasePreferredSize(); 61 virtual BAlignment BaseAlignment(); 62 63 private: 64 BMenuField* fParent; 65 BRect fFrame; 66 }; 67 68 69 struct BMenuField::LayoutData { 70 LayoutData() 71 : label_layout_item(NULL), 72 menu_bar_layout_item(NULL), 73 previous_height(-1), 74 valid(false) 75 { 76 } 77 78 LabelLayoutItem* label_layout_item; 79 MenuBarLayoutItem* menu_bar_layout_item; 80 float previous_height; // used in FrameResized() for 81 // invalidation 82 font_height font_info; 83 float label_width; 84 float label_height; 85 BSize min; 86 BSize menu_bar_min; 87 bool valid; 88 }; 89 90 91 // #pragma mark - 92 93 94 static float kVMargin = 2.0f; 95 96 97 BMenuField::BMenuField(BRect frame, const char *name, const char *label, 98 BMenu *menu, uint32 resize, uint32 flags) 99 : BView(frame, name, resize, flags) 100 { 101 InitObject(label); 102 103 frame.OffsetTo(B_ORIGIN); 104 _InitMenuBar(menu, frame, false); 105 106 InitObject2(); 107 } 108 109 110 BMenuField::BMenuField(BRect frame, const char *name, const char *label, 111 BMenu *menu, bool fixedSize, uint32 resize, uint32 flags) 112 : BView(frame, name, resize, flags) 113 { 114 InitObject(label); 115 116 fFixedSizeMB = fixedSize; 117 118 frame.OffsetTo(B_ORIGIN); 119 _InitMenuBar(menu, frame, fixedSize); 120 121 InitObject2(); 122 } 123 124 125 BMenuField::BMenuField(const char* name, const char* label, BMenu* menu, 126 BMessage* message, uint32 flags) 127 : BView(BRect(0, 0, -1, -1), name, B_FOLLOW_NONE, 128 flags | B_FRAME_EVENTS | B_SUPPORTS_LAYOUT) 129 { 130 InitObject(label); 131 132 _InitMenuBar(menu, BRect(0, 0, 100, 15), false); 133 134 InitObject2(); 135 } 136 137 138 BMenuField::BMenuField(const char* label, 139 BMenu* menu, BMessage* message) 140 : BView(BRect(0, 0, -1, -1), NULL, B_FOLLOW_NONE, 141 B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS | B_SUPPORTS_LAYOUT) 142 { 143 InitObject(label); 144 145 _InitMenuBar(menu, BRect(0, 0, 100, 15), false); 146 147 InitObject2(); 148 } 149 150 151 BMenuField::BMenuField(BMessage *data) 152 : BView(data) 153 { 154 const char *label = NULL; 155 data->FindString("_label", &label); 156 157 InitObject(label); 158 159 fMenuBar = (BMenuBar*)FindView("_mc_mb_"); 160 fMenu = fMenuBar->SubmenuAt(0); 161 162 InitObject2(); 163 164 bool disable; 165 if (data->FindBool("_disable", &disable) == B_OK) 166 SetEnabled(!disable); 167 168 int32 align; 169 data->FindInt32("_align", &align); 170 SetAlignment((alignment)align); 171 172 data->FindFloat("_divide", &fDivider); 173 174 bool fixed; 175 if (data->FindBool("be:fixeds", &fixed) == B_OK) 176 fFixedSizeMB = fixed; 177 178 bool dmark = false; 179 data->FindBool("be:dmark", &dmark); 180 if (_BMCMenuBar_ *menuBar = dynamic_cast<_BMCMenuBar_ *>(fMenuBar)) { 181 menuBar->TogglePopUpMarker(dmark); 182 } 183 } 184 185 186 BMenuField::~BMenuField() 187 { 188 free(fLabel); 189 190 status_t dummy; 191 if (fMenuTaskID >= 0) 192 wait_for_thread(fMenuTaskID, &dummy); 193 194 delete fLayoutData; 195 } 196 197 198 BArchivable * 199 BMenuField::Instantiate(BMessage *data) 200 { 201 if (validate_instantiation(data, "BMenuField")) 202 return new BMenuField(data); 203 204 return NULL; 205 } 206 207 208 status_t 209 BMenuField::Archive(BMessage *data, bool deep) const 210 { 211 status_t ret = BView::Archive(data, deep); 212 213 if (ret == B_OK && Label()) 214 ret = data->AddString("_label", Label()); 215 216 if (ret == B_OK && !IsEnabled()) 217 ret = data->AddBool("_disable", true); 218 219 if (ret == B_OK) 220 ret = data->AddInt32("_align", Alignment()); 221 if (ret == B_OK) 222 ret = data->AddFloat("_divide", Divider()); 223 224 if (ret == B_OK && fFixedSizeMB) 225 ret = data->AddBool("be:fixeds", true); 226 227 bool dmark = false; 228 if (_BMCMenuBar_ *menuBar = dynamic_cast<_BMCMenuBar_ *>(fMenuBar)) { 229 dmark = menuBar->IsPopUpMarkerShown(); 230 } 231 data->AddBool("be:dmark", dmark); 232 233 return ret; 234 } 235 236 237 void 238 BMenuField::Draw(BRect update) 239 { 240 BRect bounds(Bounds()); 241 bool active = false; 242 243 if (IsFocus()) 244 active = Window()->IsActive(); 245 246 DrawLabel(bounds, update); 247 248 BRect frame(fMenuBar->Frame()); 249 250 if (frame.InsetByCopy(-kVMargin, -kVMargin).Intersects(update)) { 251 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_2_TINT)); 252 StrokeLine(BPoint(frame.left - 1.0f, frame.top - 1.0f), 253 BPoint(frame.left - 1.0f, frame.bottom - 1.0f)); 254 StrokeLine(BPoint(frame.left - 1.0f, frame.top - 1.0f), 255 BPoint(frame.right - 1.0f, frame.top - 1.0f)); 256 257 StrokeLine(BPoint(frame.left + 1.0f, frame.bottom + 1.0f), 258 BPoint(frame.right + 1.0f, frame.bottom + 1.0f)); 259 StrokeLine(BPoint(frame.right + 1.0f, frame.top + 1.0f)); 260 261 SetHighColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_DARKEN_4_TINT)); 262 StrokeLine(BPoint(frame.left - 1.0f, frame.bottom), 263 BPoint(frame.left - 1.0f, frame.bottom)); 264 StrokeLine(BPoint(frame.right, frame.top - 1.0f), 265 BPoint(frame.right, frame.top - 1.0f)); 266 } 267 268 if (active || fTransition) { 269 SetHighColor(active ? ui_color(B_KEYBOARD_NAVIGATION_COLOR) : 270 ViewColor()); 271 StrokeRect(frame.InsetByCopy(-kVMargin, -kVMargin)); 272 273 fTransition = false; 274 } 275 } 276 277 278 void 279 BMenuField::AttachedToWindow() 280 { 281 if (Parent()) { 282 SetViewColor(Parent()->ViewColor()); 283 SetLowColor(Parent()->ViewColor()); 284 } 285 } 286 287 288 void 289 BMenuField::AllAttached() 290 { 291 ResizeTo(Bounds().Width(), 292 fMenuBar->Bounds().Height() + kVMargin + kVMargin); 293 } 294 295 296 void 297 BMenuField::MouseDown(BPoint where) 298 { 299 if (!fMenuBar->Frame().Contains(where)) 300 return; 301 302 BRect bounds = fMenuBar->ConvertFromParent(Bounds()); 303 304 fMenuBar->StartMenuBar(0, false, true, &bounds); 305 306 fMenuTaskID = spawn_thread((thread_func)MenuTask, "_m_task_", B_NORMAL_PRIORITY, this); 307 if (fMenuTaskID) 308 resume_thread(fMenuTaskID); 309 } 310 311 312 void 313 BMenuField::KeyDown(const char *bytes, int32 numBytes) 314 { 315 switch (bytes[0]) { 316 case B_SPACE: 317 case B_RIGHT_ARROW: 318 case B_DOWN_ARROW: 319 { 320 if (!IsEnabled()) 321 break; 322 323 BRect bounds = fMenuBar->ConvertFromParent(Bounds()); 324 325 fMenuBar->StartMenuBar(0, true, true, &bounds); 326 327 fSelected = true; 328 fTransition = true; 329 330 bounds = Bounds(); 331 bounds.right = fDivider; 332 333 Invalidate(bounds); 334 } 335 336 default: 337 BView::KeyDown(bytes, numBytes); 338 } 339 } 340 341 342 void 343 BMenuField::MakeFocus(bool state) 344 { 345 if (IsFocus() == state) 346 return; 347 348 BView::MakeFocus(state); 349 350 if (Window()) 351 Invalidate(); // TODO: use fLayoutData->label_width 352 } 353 354 355 void 356 BMenuField::MessageReceived(BMessage *msg) 357 { 358 BView::MessageReceived(msg); 359 } 360 361 362 void 363 BMenuField::WindowActivated(bool state) 364 { 365 BView::WindowActivated(state); 366 367 if (IsFocus()) 368 Invalidate(); 369 } 370 371 372 void 373 BMenuField::MouseUp(BPoint point) 374 { 375 BView::MouseUp(point); 376 } 377 378 379 void 380 BMenuField::MouseMoved(BPoint point, uint32 code, const BMessage *message) 381 { 382 BView::MouseMoved(point, code, message); 383 } 384 385 386 void 387 BMenuField::DetachedFromWindow() 388 { 389 BView::DetachedFromWindow(); 390 } 391 392 393 void 394 BMenuField::AllDetached() 395 { 396 BView::AllDetached(); 397 } 398 399 400 void 401 BMenuField::FrameMoved(BPoint newPosition) 402 { 403 BView::FrameMoved(newPosition); 404 } 405 406 407 void 408 BMenuField::FrameResized(float newWidth, float newHeight) 409 { 410 BView::FrameResized(newWidth, newHeight); 411 412 if (newHeight != fLayoutData->previous_height && Label()) { 413 // The height changed, which means the label has to move and we 414 // probably also invalidate a part of the borders around the menu bar. 415 // So don't be shy and invalidate the whole thing. 416 Invalidate(); 417 } 418 419 fLayoutData->previous_height = newHeight; 420 } 421 422 423 BMenu * 424 BMenuField::Menu() const 425 { 426 return fMenu; 427 } 428 429 430 BMenuBar * 431 BMenuField::MenuBar() const 432 { 433 return fMenuBar; 434 } 435 436 437 BMenuItem * 438 BMenuField::MenuItem() const 439 { 440 return fMenuBar->ItemAt(0); 441 } 442 443 444 void 445 BMenuField::SetLabel(const char *label) 446 { 447 if (fLabel) { 448 if (label && strcmp(fLabel, label) == 0) 449 return; 450 451 free(fLabel); 452 } 453 454 fLabel = strdup(label); 455 456 if (Window()) 457 Invalidate(); 458 459 InvalidateLayout(); 460 } 461 462 463 const char * 464 BMenuField::Label() const 465 { 466 return fLabel; 467 } 468 469 470 void 471 BMenuField::SetEnabled(bool on) 472 { 473 if (fEnabled == on) 474 return; 475 476 fEnabled = on; 477 fMenuBar->SetEnabled(on); 478 479 if (Window()) { 480 fMenuBar->Invalidate(fMenuBar->Bounds()); 481 Invalidate(Bounds()); 482 } 483 } 484 485 486 bool 487 BMenuField::IsEnabled() const 488 { 489 return fEnabled; 490 } 491 492 493 void 494 BMenuField::SetAlignment(alignment label) 495 { 496 fAlign = label; 497 } 498 499 500 alignment 501 BMenuField::Alignment() const 502 { 503 return fAlign; 504 } 505 506 507 void 508 BMenuField::SetDivider(float divider) 509 { 510 divider = floorf(divider + 0.5); 511 512 float dx = fDivider - divider; 513 514 if (dx == 0.0f) 515 return; 516 517 fDivider = divider; 518 519 if (Flags() & B_SUPPORTS_LAYOUT) { 520 // We should never get here, since layout support means, we also 521 // layout the divider, and don't use this method at all. 522 Relayout(); 523 } else { 524 BRect dirty(fMenuBar->Frame()); 525 526 fMenuBar->MoveTo(fDivider + 1, kVMargin); 527 528 if (fFixedSizeMB) { 529 fMenuBar->ResizeTo(Bounds().Width() - fDivider + 1 - 2, 530 dirty.Height()); 531 } 532 533 dirty = dirty | fMenuBar->Frame(); 534 dirty.InsetBy(-kVMargin, -kVMargin); 535 536 Invalidate(dirty); 537 } 538 } 539 540 541 float 542 BMenuField::Divider() const 543 { 544 return fDivider; 545 } 546 547 548 void 549 BMenuField::ShowPopUpMarker() 550 { 551 if (_BMCMenuBar_ *menuBar = dynamic_cast<_BMCMenuBar_ *>(fMenuBar)) { 552 menuBar->TogglePopUpMarker(true); 553 menuBar->Invalidate(); 554 } 555 } 556 557 558 void 559 BMenuField::HidePopUpMarker() 560 { 561 if (_BMCMenuBar_ *menuBar = dynamic_cast<_BMCMenuBar_ *>(fMenuBar)) { 562 menuBar->TogglePopUpMarker(false); 563 menuBar->Invalidate(); 564 } 565 } 566 567 568 BHandler * 569 BMenuField::ResolveSpecifier(BMessage *message, int32 index, 570 BMessage *specifier, int32 form, const char *property) 571 { 572 return BView::ResolveSpecifier(message, index, specifier, form, property); 573 } 574 575 576 status_t 577 BMenuField::GetSupportedSuites(BMessage *data) 578 { 579 return BView::GetSupportedSuites(data); 580 } 581 582 583 void 584 BMenuField::ResizeToPreferred() 585 { 586 fMenuBar->ResizeToPreferred(); 587 588 BView::ResizeToPreferred(); 589 } 590 591 592 void 593 BMenuField::GetPreferredSize(float *_width, float *_height) 594 { 595 _ValidateLayoutData(); 596 597 if (_width) 598 *_width = fLayoutData->min.width; 599 600 if (_height) 601 *_height = fLayoutData->min.height; 602 } 603 604 605 BSize 606 BMenuField::MinSize() 607 { 608 _ValidateLayoutData(); 609 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min); 610 } 611 612 613 BSize 614 BMenuField::MaxSize() 615 { 616 _ValidateLayoutData(); 617 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), fLayoutData->min); 618 } 619 620 621 BSize 622 BMenuField::PreferredSize() 623 { 624 _ValidateLayoutData(); 625 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData->min); 626 } 627 628 629 void 630 BMenuField::InvalidateLayout(bool descendants) 631 { 632 fLayoutData->valid = false; 633 634 BView::InvalidateLayout(descendants); 635 } 636 637 638 BLayoutItem* 639 BMenuField::CreateLabelLayoutItem() 640 { 641 if (!fLayoutData->label_layout_item) 642 fLayoutData->label_layout_item = new LabelLayoutItem(this); 643 return fLayoutData->label_layout_item; 644 } 645 646 647 BLayoutItem* 648 BMenuField::CreateMenuBarLayoutItem() 649 { 650 if (!fLayoutData->menu_bar_layout_item) 651 fLayoutData->menu_bar_layout_item = new MenuBarLayoutItem(this); 652 return fLayoutData->menu_bar_layout_item; 653 } 654 655 656 status_t 657 BMenuField::Perform(perform_code d, void *arg) 658 { 659 return BView::Perform(d, arg); 660 } 661 662 663 void 664 BMenuField::DoLayout() 665 { 666 // Bail out, if we shan't do layout. 667 if (!(Flags() & B_SUPPORTS_LAYOUT)) 668 return; 669 670 // If the user set a layout, we let the base class version call its 671 // hook. 672 if (GetLayout()) { 673 BView::DoLayout(); 674 return; 675 } 676 677 _ValidateLayoutData(); 678 679 // validate current size 680 BSize size(Bounds().Size()); 681 if (size.width < fLayoutData->min.width) 682 size.width = fLayoutData->min.width; 683 if (size.height < fLayoutData->min.height) 684 size.height = fLayoutData->min.height; 685 686 // divider 687 float divider = 0; 688 if (fLayoutData->label_layout_item && fLayoutData->menu_bar_layout_item) { 689 // We have layout items. They define the divider location. 690 divider = fLayoutData->menu_bar_layout_item->Frame().left 691 - fLayoutData->label_layout_item->Frame().left; 692 } else { 693 if (fLayoutData->label_width > 0) 694 divider = fLayoutData->label_width + 5; 695 } 696 697 // menu bar 698 BRect dirty(fMenuBar->Frame()); 699 BRect menuBarFrame(divider + 1, kVMargin, size.width - 2, 700 size.height - kVMargin); 701 702 // place the menu bar and set the divider 703 BLayoutUtils::AlignInFrame(fMenuBar, menuBarFrame); 704 705 fDivider = divider; 706 707 // invalidate dirty region 708 dirty = dirty | fMenuBar->Frame(); 709 dirty.InsetBy(-kVMargin, -kVMargin); 710 711 Invalidate(dirty); 712 } 713 714 715 void BMenuField::_ReservedMenuField1() {} 716 void BMenuField::_ReservedMenuField2() {} 717 void BMenuField::_ReservedMenuField3() {} 718 719 720 BMenuField & 721 BMenuField::operator=(const BMenuField &) 722 { 723 return *this; 724 } 725 726 727 void 728 BMenuField::InitObject(const char *label) 729 { 730 fLabel = NULL; 731 fMenu = NULL; 732 fMenuBar = NULL; 733 fAlign = B_ALIGN_LEFT; 734 fEnabled = true; 735 fSelected = false; 736 fTransition = false; 737 fFixedSizeMB = false; 738 fMenuTaskID = -1; 739 fLayoutData = new LayoutData; 740 741 SetLabel(label); 742 743 if (label) 744 fDivider = (float)floor(Frame().Width() / 2.0f); 745 else 746 fDivider = 0; 747 748 // default to unlimited maximum width 749 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 750 } 751 752 753 void 754 BMenuField::InitObject2() 755 { 756 font_height fontHeight; 757 GetFontHeight(&fontHeight); 758 759 // TODO: fix this calculation 760 float height = floorf(fontHeight.ascent + fontHeight.descent 761 + fontHeight.leading) + 7; 762 763 fMenuBar->ResizeTo(Bounds().Width() - fDivider, height); 764 fMenuBar->AddFilter(new _BMCFilter_(this, B_MOUSE_DOWN)); 765 } 766 767 768 void 769 BMenuField::DrawLabel(BRect bounds, BRect update) 770 { 771 _ValidateLayoutData(); 772 font_height& fh = fLayoutData->font_info; 773 774 if (Label()) { 775 SetLowColor(ViewColor()); 776 777 // horizontal alignment 778 float x; 779 switch (fAlign) { 780 case B_ALIGN_RIGHT: 781 x = fDivider - fLayoutData->label_width - 3.0f; 782 break; 783 784 case B_ALIGN_CENTER: 785 x = fDivider - fLayoutData->label_width / 2.0f; 786 break; 787 788 default: 789 x = 3.0f; 790 break; 791 } 792 793 // vertical alignment 794 float y = Bounds().top 795 + (Bounds().Height() + 1 - fh.ascent - fh.descent) / 2 796 + fh.ascent; 797 y = floor(y + 0.5); 798 799 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 800 IsEnabled() ? B_DARKEN_MAX_TINT : B_DISABLED_LABEL_TINT)); 801 DrawString(Label(), BPoint(x, y)); 802 } 803 } 804 805 806 void 807 BMenuField::InitMenu(BMenu *menu) 808 { 809 menu->SetFont(be_plain_font); 810 811 int32 index = 0; 812 BMenu *subMenu; 813 814 while ((subMenu = menu->SubmenuAt(index++)) != NULL) 815 InitMenu(subMenu); 816 } 817 818 819 long 820 BMenuField::MenuTask(void *arg) 821 { 822 BMenuField *menuField = static_cast<BMenuField *>(arg); 823 824 if (!menuField->LockLooper()) 825 return 0; 826 827 menuField->fSelected = true; 828 menuField->fTransition = true; 829 menuField->Invalidate(); 830 menuField->UnlockLooper(); 831 832 bool tracking; 833 do { 834 snooze(20000); 835 if (!menuField->LockLooper()) 836 return 0; 837 838 tracking = menuField->fMenuBar->fTracking; 839 840 menuField->UnlockLooper(); 841 } while (tracking); 842 843 if (menuField->LockLooper()) { 844 menuField->fSelected = false; 845 menuField->fTransition = true; 846 menuField->Invalidate(); 847 menuField->UnlockLooper(); 848 } 849 850 return 0; 851 } 852 853 854 void 855 BMenuField::_UpdateFrame() 856 { 857 if (fLayoutData->label_layout_item && fLayoutData->menu_bar_layout_item) { 858 BRect labelFrame = fLayoutData->label_layout_item->Frame(); 859 BRect menuFrame = fLayoutData->menu_bar_layout_item->Frame(); 860 861 // update divider 862 fDivider = menuFrame.left - labelFrame.left; 863 864 // update our frame 865 MoveTo(labelFrame.left, labelFrame.top); 866 BSize oldSize = Bounds().Size(); 867 ResizeTo(menuFrame.left + menuFrame.Width() - labelFrame.left, 868 menuFrame.top + menuFrame.Height() - labelFrame.top); 869 BSize newSize = Bounds().Size(); 870 871 // If the size changes, ResizeTo() will trigger a relayout, otherwise 872 // we need to do that explicitly. 873 if (newSize != oldSize) 874 Relayout(); 875 } 876 } 877 878 879 void 880 BMenuField::_InitMenuBar(BMenu* menu, BRect frame, bool fixedSize) 881 { 882 fMenu = menu; 883 InitMenu(menu); 884 885 fMenuBar = new _BMCMenuBar_(BRect(frame.left + fDivider + 1, 886 frame.top + kVMargin, frame.right, frame.bottom - kVMargin), 887 fixedSize, this); 888 889 // by default align the menu bar left in the available space 890 fMenuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 891 B_ALIGN_VERTICAL_UNSET)); 892 893 AddChild(fMenuBar); 894 fMenuBar->AddItem(menu); 895 896 fMenuBar->SetFont(be_plain_font); 897 } 898 899 900 void 901 BMenuField::_ValidateLayoutData() 902 { 903 if (fLayoutData->valid) 904 return; 905 906 // cache font height 907 font_height& fh = fLayoutData->font_info; 908 GetFontHeight(&fh); 909 910 fLayoutData->label_width = (Label() ? ceilf(StringWidth(Label())) : 0); 911 fLayoutData->label_height = ceilf(fh.ascent) + ceilf(fh.descent); 912 913 // compute the minimal divider 914 float divider = 0; 915 if (fLayoutData->label_width > 0) 916 divider = fLayoutData->label_width + 5; 917 918 // If we shan't do real layout, we let the current divider take influence. 919 if (!(Flags() & B_SUPPORTS_LAYOUT)) 920 divider = max_c(divider, fDivider); 921 922 // get the minimal (== preferred) menu bar size 923 fLayoutData->menu_bar_min = fMenuBar->MinSize(); 924 925 // compute our minimal (== preferred) size 926 // TODO: The layout is a bit broken. A one pixel wide border is draw around 927 // the menu bar to give it it's look. When the view has the focus, 928 // additionally a one pixel wide blue frame is drawn around it. In order 929 // to be able to easily visually align the menu bar with the text view of 930 // a text control, the divider must ignore the focus frame, though. Hence 931 // we add one less pixel to our width. 932 BSize min(fLayoutData->menu_bar_min); 933 min.width += 2 * kVMargin - 1; 934 min.height += 2 * kVMargin; 935 936 if (fLayoutData->label_width > 0) 937 min.width += fLayoutData->label_width + 5; 938 if (fLayoutData->label_height > min.height) 939 min.height = fLayoutData->label_height; 940 941 fLayoutData->min = min; 942 943 fLayoutData->valid = true; 944 } 945 946 947 // #pragma mark - 948 949 950 BMenuField::LabelLayoutItem::LabelLayoutItem(BMenuField* parent) 951 : fParent(parent), 952 fFrame() 953 { 954 } 955 956 957 bool 958 BMenuField::LabelLayoutItem::IsVisible() 959 { 960 return !fParent->IsHidden(fParent); 961 } 962 963 964 void 965 BMenuField::LabelLayoutItem::SetVisible(bool visible) 966 { 967 // not allowed 968 } 969 970 971 BRect 972 BMenuField::LabelLayoutItem::Frame() 973 { 974 return fFrame; 975 } 976 977 978 void 979 BMenuField::LabelLayoutItem::SetFrame(BRect frame) 980 { 981 fFrame = frame; 982 fParent->_UpdateFrame(); 983 } 984 985 986 BView* 987 BMenuField::LabelLayoutItem::View() 988 { 989 return fParent; 990 } 991 992 993 BSize 994 BMenuField::LabelLayoutItem::BaseMinSize() 995 { 996 fParent->_ValidateLayoutData(); 997 998 if (!fParent->Label()) 999 return BSize(-1, -1); 1000 1001 return BSize(fParent->fLayoutData->label_width + 5, 1002 fParent->fLayoutData->label_height); 1003 } 1004 1005 1006 BSize 1007 BMenuField::LabelLayoutItem::BaseMaxSize() 1008 { 1009 return BaseMinSize(); 1010 } 1011 1012 1013 BSize 1014 BMenuField::LabelLayoutItem::BasePreferredSize() 1015 { 1016 return BaseMinSize(); 1017 } 1018 1019 1020 BAlignment 1021 BMenuField::LabelLayoutItem::BaseAlignment() 1022 { 1023 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 1024 } 1025 1026 1027 // #pragma mark - 1028 1029 1030 BMenuField::MenuBarLayoutItem::MenuBarLayoutItem(BMenuField* parent) 1031 : fParent(parent), 1032 fFrame() 1033 { 1034 // by default the part left of the divider shall have an unlimited maximum 1035 // width 1036 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 1037 } 1038 1039 1040 bool 1041 BMenuField::MenuBarLayoutItem::IsVisible() 1042 { 1043 return !fParent->IsHidden(fParent); 1044 } 1045 1046 1047 void 1048 BMenuField::MenuBarLayoutItem::SetVisible(bool visible) 1049 { 1050 // not allowed 1051 } 1052 1053 1054 BRect 1055 BMenuField::MenuBarLayoutItem::Frame() 1056 { 1057 return fFrame; 1058 } 1059 1060 1061 void 1062 BMenuField::MenuBarLayoutItem::SetFrame(BRect frame) 1063 { 1064 fFrame = frame; 1065 fParent->_UpdateFrame(); 1066 } 1067 1068 1069 BView* 1070 BMenuField::MenuBarLayoutItem::View() 1071 { 1072 return fParent; 1073 } 1074 1075 1076 BSize 1077 BMenuField::MenuBarLayoutItem::BaseMinSize() 1078 { 1079 fParent->_ValidateLayoutData(); 1080 1081 // TODO: Cf. the TODO in _ValidateLayoutData(). 1082 BSize size = fParent->fLayoutData->menu_bar_min; 1083 size.width += 2 * kVMargin - 1; 1084 size.height += 2 * kVMargin; 1085 1086 return size; 1087 } 1088 1089 1090 BSize 1091 BMenuField::MenuBarLayoutItem::BaseMaxSize() 1092 { 1093 return BaseMinSize(); 1094 } 1095 1096 1097 BSize 1098 BMenuField::MenuBarLayoutItem::BasePreferredSize() 1099 { 1100 return BaseMinSize(); 1101 } 1102 1103 1104 BAlignment 1105 BMenuField::MenuBarLayoutItem::BaseAlignment() 1106 { 1107 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 1108 } 1109 1110