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(-1, false, true, &bounds); 305 306 fMenuTaskID = spawn_thread((thread_func)_thread_entry, 307 "_m_task_", B_NORMAL_PRIORITY, this); 308 if (fMenuTaskID >= 0) 309 resume_thread(fMenuTaskID); 310 } 311 312 313 void 314 BMenuField::KeyDown(const char *bytes, int32 numBytes) 315 { 316 switch (bytes[0]) { 317 case B_SPACE: 318 case B_RIGHT_ARROW: 319 case B_DOWN_ARROW: 320 { 321 if (!IsEnabled()) 322 break; 323 324 BRect bounds = fMenuBar->ConvertFromParent(Bounds()); 325 326 fMenuBar->StartMenuBar(0, true, true, &bounds); 327 328 fSelected = true; 329 fTransition = true; 330 331 bounds = Bounds(); 332 bounds.right = fDivider; 333 334 Invalidate(bounds); 335 } 336 337 default: 338 BView::KeyDown(bytes, numBytes); 339 } 340 } 341 342 343 void 344 BMenuField::MakeFocus(bool state) 345 { 346 if (IsFocus() == state) 347 return; 348 349 BView::MakeFocus(state); 350 351 if (Window()) 352 Invalidate(); // TODO: use fLayoutData->label_width 353 } 354 355 356 void 357 BMenuField::MessageReceived(BMessage *msg) 358 { 359 BView::MessageReceived(msg); 360 } 361 362 363 void 364 BMenuField::WindowActivated(bool state) 365 { 366 BView::WindowActivated(state); 367 368 if (IsFocus()) 369 Invalidate(); 370 } 371 372 373 void 374 BMenuField::MouseUp(BPoint point) 375 { 376 BView::MouseUp(point); 377 } 378 379 380 void 381 BMenuField::MouseMoved(BPoint point, uint32 code, const BMessage *message) 382 { 383 BView::MouseMoved(point, code, message); 384 } 385 386 387 void 388 BMenuField::DetachedFromWindow() 389 { 390 BView::DetachedFromWindow(); 391 } 392 393 394 void 395 BMenuField::AllDetached() 396 { 397 BView::AllDetached(); 398 } 399 400 401 void 402 BMenuField::FrameMoved(BPoint newPosition) 403 { 404 BView::FrameMoved(newPosition); 405 } 406 407 408 void 409 BMenuField::FrameResized(float newWidth, float newHeight) 410 { 411 BView::FrameResized(newWidth, newHeight); 412 413 if (newHeight != fLayoutData->previous_height && Label()) { 414 // The height changed, which means the label has to move and we 415 // probably also invalidate a part of the borders around the menu bar. 416 // So don't be shy and invalidate the whole thing. 417 Invalidate(); 418 } 419 420 fLayoutData->previous_height = newHeight; 421 } 422 423 424 BMenu * 425 BMenuField::Menu() const 426 { 427 return fMenu; 428 } 429 430 431 BMenuBar * 432 BMenuField::MenuBar() const 433 { 434 return fMenuBar; 435 } 436 437 438 BMenuItem * 439 BMenuField::MenuItem() const 440 { 441 return fMenuBar->ItemAt(0); 442 } 443 444 445 void 446 BMenuField::SetLabel(const char *label) 447 { 448 if (fLabel) { 449 if (label && strcmp(fLabel, label) == 0) 450 return; 451 452 free(fLabel); 453 } 454 455 fLabel = strdup(label); 456 457 if (Window()) 458 Invalidate(); 459 460 InvalidateLayout(); 461 } 462 463 464 const char * 465 BMenuField::Label() const 466 { 467 return fLabel; 468 } 469 470 471 void 472 BMenuField::SetEnabled(bool on) 473 { 474 if (fEnabled == on) 475 return; 476 477 fEnabled = on; 478 fMenuBar->SetEnabled(on); 479 480 if (Window()) { 481 fMenuBar->Invalidate(fMenuBar->Bounds()); 482 Invalidate(Bounds()); 483 } 484 } 485 486 487 bool 488 BMenuField::IsEnabled() const 489 { 490 return fEnabled; 491 } 492 493 494 void 495 BMenuField::SetAlignment(alignment label) 496 { 497 fAlign = label; 498 } 499 500 501 alignment 502 BMenuField::Alignment() const 503 { 504 return fAlign; 505 } 506 507 508 void 509 BMenuField::SetDivider(float divider) 510 { 511 divider = floorf(divider + 0.5); 512 513 float dx = fDivider - divider; 514 515 if (dx == 0.0f) 516 return; 517 518 fDivider = divider; 519 520 if (Flags() & B_SUPPORTS_LAYOUT) { 521 // We should never get here, since layout support means, we also 522 // layout the divider, and don't use this method at all. 523 Relayout(); 524 } else { 525 BRect dirty(fMenuBar->Frame()); 526 527 fMenuBar->MoveTo(fDivider + 1, kVMargin); 528 529 if (fFixedSizeMB) { 530 fMenuBar->ResizeTo(Bounds().Width() - fDivider - 2, 531 dirty.Height()); 532 } 533 534 dirty = dirty | fMenuBar->Frame(); 535 dirty.InsetBy(-kVMargin, -kVMargin); 536 537 Invalidate(dirty); 538 } 539 } 540 541 542 float 543 BMenuField::Divider() const 544 { 545 return fDivider; 546 } 547 548 549 void 550 BMenuField::ShowPopUpMarker() 551 { 552 if (_BMCMenuBar_ *menuBar = dynamic_cast<_BMCMenuBar_ *>(fMenuBar)) { 553 menuBar->TogglePopUpMarker(true); 554 menuBar->Invalidate(); 555 } 556 } 557 558 559 void 560 BMenuField::HidePopUpMarker() 561 { 562 if (_BMCMenuBar_ *menuBar = dynamic_cast<_BMCMenuBar_ *>(fMenuBar)) { 563 menuBar->TogglePopUpMarker(false); 564 menuBar->Invalidate(); 565 } 566 } 567 568 569 BHandler * 570 BMenuField::ResolveSpecifier(BMessage *message, int32 index, 571 BMessage *specifier, int32 form, const char *property) 572 { 573 return BView::ResolveSpecifier(message, index, specifier, form, property); 574 } 575 576 577 status_t 578 BMenuField::GetSupportedSuites(BMessage *data) 579 { 580 return BView::GetSupportedSuites(data); 581 } 582 583 584 void 585 BMenuField::ResizeToPreferred() 586 { 587 fMenuBar->ResizeToPreferred(); 588 589 BView::ResizeToPreferred(); 590 591 if (fFixedSizeMB) { 592 // we have let the menubar resize itsself, but 593 // in fixed size mode, the menubar is supposed to 594 // be at the right end of the view always. Since 595 // the menu bar is in follow left/right mode then, 596 // resizing ourselfs might have caused the menubar 597 // to be outside now 598 fMenuBar->ResizeTo(Bounds().Width() - fDivider - 2, 599 fMenuBar->Frame().Height()); 600 } 601 } 602 603 604 void 605 BMenuField::GetPreferredSize(float *_width, float *_height) 606 { 607 _ValidateLayoutData(); 608 609 if (_width) 610 *_width = fLayoutData->min.width; 611 612 if (_height) 613 *_height = fLayoutData->min.height; 614 } 615 616 617 BSize 618 BMenuField::MinSize() 619 { 620 _ValidateLayoutData(); 621 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min); 622 } 623 624 625 BSize 626 BMenuField::MaxSize() 627 { 628 _ValidateLayoutData(); 629 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), fLayoutData->min); 630 } 631 632 633 BSize 634 BMenuField::PreferredSize() 635 { 636 _ValidateLayoutData(); 637 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), fLayoutData->min); 638 } 639 640 641 void 642 BMenuField::InvalidateLayout(bool descendants) 643 { 644 fLayoutData->valid = false; 645 646 BView::InvalidateLayout(descendants); 647 } 648 649 650 BLayoutItem* 651 BMenuField::CreateLabelLayoutItem() 652 { 653 if (!fLayoutData->label_layout_item) 654 fLayoutData->label_layout_item = new LabelLayoutItem(this); 655 return fLayoutData->label_layout_item; 656 } 657 658 659 BLayoutItem* 660 BMenuField::CreateMenuBarLayoutItem() 661 { 662 if (!fLayoutData->menu_bar_layout_item) 663 fLayoutData->menu_bar_layout_item = new MenuBarLayoutItem(this); 664 return fLayoutData->menu_bar_layout_item; 665 } 666 667 668 status_t 669 BMenuField::Perform(perform_code d, void *arg) 670 { 671 return BView::Perform(d, arg); 672 } 673 674 675 void 676 BMenuField::DoLayout() 677 { 678 // Bail out, if we shan't do layout. 679 if (!(Flags() & B_SUPPORTS_LAYOUT)) 680 return; 681 682 // If the user set a layout, we let the base class version call its 683 // hook. 684 if (GetLayout()) { 685 BView::DoLayout(); 686 return; 687 } 688 689 _ValidateLayoutData(); 690 691 // validate current size 692 BSize size(Bounds().Size()); 693 if (size.width < fLayoutData->min.width) 694 size.width = fLayoutData->min.width; 695 if (size.height < fLayoutData->min.height) 696 size.height = fLayoutData->min.height; 697 698 // divider 699 float divider = 0; 700 if (fLayoutData->label_layout_item && fLayoutData->menu_bar_layout_item) { 701 // We have layout items. They define the divider location. 702 divider = fLayoutData->menu_bar_layout_item->Frame().left 703 - fLayoutData->label_layout_item->Frame().left; 704 } else { 705 if (fLayoutData->label_width > 0) 706 divider = fLayoutData->label_width + 5; 707 } 708 709 // menu bar 710 BRect dirty(fMenuBar->Frame()); 711 BRect menuBarFrame(divider + 1, kVMargin, size.width - 2, 712 size.height - kVMargin); 713 714 // place the menu bar and set the divider 715 BLayoutUtils::AlignInFrame(fMenuBar, menuBarFrame); 716 717 fDivider = divider; 718 719 // invalidate dirty region 720 dirty = dirty | fMenuBar->Frame(); 721 dirty.InsetBy(-kVMargin, -kVMargin); 722 723 Invalidate(dirty); 724 } 725 726 727 void BMenuField::_ReservedMenuField1() {} 728 void BMenuField::_ReservedMenuField2() {} 729 void BMenuField::_ReservedMenuField3() {} 730 731 732 BMenuField & 733 BMenuField::operator=(const BMenuField &) 734 { 735 return *this; 736 } 737 738 739 void 740 BMenuField::InitObject(const char *label) 741 { 742 fLabel = NULL; 743 fMenu = NULL; 744 fMenuBar = NULL; 745 fAlign = B_ALIGN_LEFT; 746 fEnabled = true; 747 fSelected = false; 748 fTransition = false; 749 fFixedSizeMB = false; 750 fMenuTaskID = -1; 751 fLayoutData = new LayoutData; 752 753 SetLabel(label); 754 755 if (label) 756 fDivider = (float)floor(Frame().Width() / 2.0f); 757 else 758 fDivider = 0; 759 760 // default to unlimited maximum width 761 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 762 } 763 764 765 void 766 BMenuField::InitObject2() 767 { 768 font_height fontHeight; 769 GetFontHeight(&fontHeight); 770 771 // TODO: fix this calculation 772 float height = floorf(fontHeight.ascent + fontHeight.descent 773 + fontHeight.leading) + 7; 774 775 fMenuBar->ResizeTo(Bounds().Width() - fDivider - 2, height); 776 fMenuBar->AddFilter(new _BMCFilter_(this, B_MOUSE_DOWN)); 777 } 778 779 780 void 781 BMenuField::DrawLabel(BRect bounds, BRect update) 782 { 783 _ValidateLayoutData(); 784 font_height& fh = fLayoutData->font_info; 785 786 if (Label()) { 787 SetLowColor(ViewColor()); 788 789 // horizontal alignment 790 float x; 791 switch (fAlign) { 792 case B_ALIGN_RIGHT: 793 x = fDivider - fLayoutData->label_width - 3.0; 794 break; 795 796 case B_ALIGN_CENTER: 797 x = fDivider - fLayoutData->label_width / 2.0; 798 break; 799 800 default: 801 x = 0.0; 802 break; 803 } 804 805 // vertical alignment 806 float y = Bounds().top 807 + (Bounds().Height() + 1 - fh.ascent - fh.descent) / 2 808 + fh.ascent; 809 y = floor(y + 0.5); 810 811 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 812 IsEnabled() ? B_DARKEN_MAX_TINT : B_DISABLED_LABEL_TINT)); 813 DrawString(Label(), BPoint(x, y)); 814 } 815 } 816 817 818 void 819 BMenuField::InitMenu(BMenu *menu) 820 { 821 menu->SetFont(be_plain_font); 822 823 int32 index = 0; 824 BMenu *subMenu; 825 826 while ((subMenu = menu->SubmenuAt(index++)) != NULL) 827 InitMenu(subMenu); 828 } 829 830 831 /* static */ 832 int32 833 BMenuField::_thread_entry(void *arg) 834 { 835 return static_cast<BMenuField *>(arg)->_MenuTask(); 836 } 837 838 839 int32 840 BMenuField::_MenuTask() 841 { 842 if (!LockLooper()) 843 return 0; 844 845 fSelected = true; 846 fTransition = true; 847 Invalidate(); 848 UnlockLooper(); 849 850 bool tracking; 851 do { 852 snooze(20000); 853 if (!LockLooper()) 854 return 0; 855 856 tracking = fMenuBar->fTracking; 857 858 UnlockLooper(); 859 } while (tracking); 860 861 if (LockLooper()) { 862 fSelected = false; 863 fTransition = true; 864 Invalidate(); 865 UnlockLooper(); 866 } 867 868 return 0; 869 } 870 871 872 void 873 BMenuField::_UpdateFrame() 874 { 875 if (fLayoutData->label_layout_item && fLayoutData->menu_bar_layout_item) { 876 BRect labelFrame = fLayoutData->label_layout_item->Frame(); 877 BRect menuFrame = fLayoutData->menu_bar_layout_item->Frame(); 878 879 // update divider 880 fDivider = menuFrame.left - labelFrame.left; 881 882 // update our frame 883 MoveTo(labelFrame.left, labelFrame.top); 884 BSize oldSize = Bounds().Size(); 885 ResizeTo(menuFrame.left + menuFrame.Width() - labelFrame.left, 886 menuFrame.top + menuFrame.Height() - labelFrame.top); 887 BSize newSize = Bounds().Size(); 888 889 // If the size changes, ResizeTo() will trigger a relayout, otherwise 890 // we need to do that explicitly. 891 if (newSize != oldSize) 892 Relayout(); 893 } 894 } 895 896 897 void 898 BMenuField::_InitMenuBar(BMenu* menu, BRect frame, bool fixedSize) 899 { 900 fMenu = menu; 901 InitMenu(menu); 902 903 fMenuBar = new _BMCMenuBar_(BRect(frame.left + fDivider + 1, 904 frame.top + kVMargin, frame.right - 2, frame.bottom - kVMargin), 905 fixedSize, this); 906 907 // by default align the menu bar left in the available space 908 fMenuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 909 B_ALIGN_VERTICAL_UNSET)); 910 911 AddChild(fMenuBar); 912 fMenuBar->AddItem(menu); 913 914 fMenuBar->SetFont(be_plain_font); 915 } 916 917 918 void 919 BMenuField::_ValidateLayoutData() 920 { 921 if (fLayoutData->valid) 922 return; 923 924 // cache font height 925 font_height& fh = fLayoutData->font_info; 926 GetFontHeight(&fh); 927 928 fLayoutData->label_width = (Label() ? ceilf(StringWidth(Label())) : 0); 929 fLayoutData->label_height = ceilf(fh.ascent) + ceilf(fh.descent); 930 931 // compute the minimal divider 932 float divider = 0; 933 if (fLayoutData->label_width > 0) 934 divider = fLayoutData->label_width + 5; 935 936 // If we shan't do real layout, we let the current divider take influence. 937 if (!(Flags() & B_SUPPORTS_LAYOUT)) 938 divider = max_c(divider, fDivider); 939 940 // get the minimal (== preferred) menu bar size 941 fLayoutData->menu_bar_min = fMenuBar->MinSize(); 942 943 // compute our minimal (== preferred) size 944 // TODO: The layout is a bit broken. A one pixel wide border is draw around 945 // the menu bar to give it it's look. When the view has the focus, 946 // additionally a one pixel wide blue frame is drawn around it. In order 947 // to be able to easily visually align the menu bar with the text view of 948 // a text control, the divider must ignore the focus frame, though. Hence 949 // we add one less pixel to our width. 950 BSize min(fLayoutData->menu_bar_min); 951 min.width += 2 * kVMargin - 1; 952 min.height += 2 * kVMargin; 953 954 if (fLayoutData->label_width > 0) 955 min.width += fLayoutData->label_width + 5; 956 if (fLayoutData->label_height > min.height) 957 min.height = fLayoutData->label_height; 958 959 fLayoutData->min = min; 960 961 fLayoutData->valid = true; 962 } 963 964 965 // #pragma mark - 966 967 968 BMenuField::LabelLayoutItem::LabelLayoutItem(BMenuField* parent) 969 : fParent(parent), 970 fFrame() 971 { 972 } 973 974 975 bool 976 BMenuField::LabelLayoutItem::IsVisible() 977 { 978 return !fParent->IsHidden(fParent); 979 } 980 981 982 void 983 BMenuField::LabelLayoutItem::SetVisible(bool visible) 984 { 985 // not allowed 986 } 987 988 989 BRect 990 BMenuField::LabelLayoutItem::Frame() 991 { 992 return fFrame; 993 } 994 995 996 void 997 BMenuField::LabelLayoutItem::SetFrame(BRect frame) 998 { 999 fFrame = frame; 1000 fParent->_UpdateFrame(); 1001 } 1002 1003 1004 BView* 1005 BMenuField::LabelLayoutItem::View() 1006 { 1007 return fParent; 1008 } 1009 1010 1011 BSize 1012 BMenuField::LabelLayoutItem::BaseMinSize() 1013 { 1014 fParent->_ValidateLayoutData(); 1015 1016 if (!fParent->Label()) 1017 return BSize(-1, -1); 1018 1019 return BSize(fParent->fLayoutData->label_width + 5, 1020 fParent->fLayoutData->label_height); 1021 } 1022 1023 1024 BSize 1025 BMenuField::LabelLayoutItem::BaseMaxSize() 1026 { 1027 return BaseMinSize(); 1028 } 1029 1030 1031 BSize 1032 BMenuField::LabelLayoutItem::BasePreferredSize() 1033 { 1034 return BaseMinSize(); 1035 } 1036 1037 1038 BAlignment 1039 BMenuField::LabelLayoutItem::BaseAlignment() 1040 { 1041 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 1042 } 1043 1044 1045 // #pragma mark - 1046 1047 1048 BMenuField::MenuBarLayoutItem::MenuBarLayoutItem(BMenuField* parent) 1049 : fParent(parent), 1050 fFrame() 1051 { 1052 // by default the part left of the divider shall have an unlimited maximum 1053 // width 1054 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 1055 } 1056 1057 1058 bool 1059 BMenuField::MenuBarLayoutItem::IsVisible() 1060 { 1061 return !fParent->IsHidden(fParent); 1062 } 1063 1064 1065 void 1066 BMenuField::MenuBarLayoutItem::SetVisible(bool visible) 1067 { 1068 // not allowed 1069 } 1070 1071 1072 BRect 1073 BMenuField::MenuBarLayoutItem::Frame() 1074 { 1075 return fFrame; 1076 } 1077 1078 1079 void 1080 BMenuField::MenuBarLayoutItem::SetFrame(BRect frame) 1081 { 1082 fFrame = frame; 1083 fParent->_UpdateFrame(); 1084 } 1085 1086 1087 BView* 1088 BMenuField::MenuBarLayoutItem::View() 1089 { 1090 return fParent; 1091 } 1092 1093 1094 BSize 1095 BMenuField::MenuBarLayoutItem::BaseMinSize() 1096 { 1097 fParent->_ValidateLayoutData(); 1098 1099 // TODO: Cf. the TODO in _ValidateLayoutData(). 1100 BSize size = fParent->fLayoutData->menu_bar_min; 1101 size.width += 2 * kVMargin - 1; 1102 size.height += 2 * kVMargin; 1103 1104 return size; 1105 } 1106 1107 1108 BSize 1109 BMenuField::MenuBarLayoutItem::BaseMaxSize() 1110 { 1111 return BaseMinSize(); 1112 } 1113 1114 1115 BSize 1116 BMenuField::MenuBarLayoutItem::BasePreferredSize() 1117 { 1118 return BaseMinSize(); 1119 } 1120 1121 1122 BAlignment 1123 BMenuField::MenuBarLayoutItem::BaseAlignment() 1124 { 1125 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 1126 } 1127 1128