1 /* 2 * Copyright 2001-2015 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Jérôme Duval (korli@users.berlios.de) 8 * Stephan Aßmus <superstippi@gmx.de> 9 * Artur Wyszynski 10 * Rene Gollent (rene@gollent.com) 11 */ 12 13 14 #include <TabView.h> 15 #include <TabViewPrivate.h> 16 17 #include <new> 18 19 #include <math.h> 20 #include <string.h> 21 22 #include <CardLayout.h> 23 #include <ControlLook.h> 24 #include <GroupLayout.h> 25 #include <LayoutUtils.h> 26 #include <List.h> 27 #include <Message.h> 28 #include <PropertyInfo.h> 29 #include <Rect.h> 30 #include <Region.h> 31 #include <String.h> 32 33 #include <binary_compatibility/Support.h> 34 35 36 static property_info sPropertyList[] = { 37 { 38 "Selection", 39 { B_GET_PROPERTY, B_SET_PROPERTY }, 40 { B_DIRECT_SPECIFIER }, 41 NULL, 0, 42 { B_INT32_TYPE } 43 }, 44 45 { 0 } 46 }; 47 48 49 BTab::BTab(BView* contentsView) 50 : 51 fEnabled(true), 52 fSelected(false), 53 fFocus(false), 54 fView(contentsView), 55 fTabView(NULL) 56 { 57 } 58 59 60 BTab::BTab(BMessage* archive) 61 : 62 BArchivable(archive), 63 fSelected(false), 64 fFocus(false), 65 fView(NULL), 66 fTabView(NULL) 67 { 68 bool disable; 69 70 if (archive->FindBool("_disable", &disable) != B_OK) 71 SetEnabled(true); 72 else 73 SetEnabled(!disable); 74 } 75 76 77 BTab::~BTab() 78 { 79 if (fView == NULL) 80 return; 81 82 if (fSelected) 83 fView->RemoveSelf(); 84 85 delete fView; 86 } 87 88 89 BArchivable* 90 BTab::Instantiate(BMessage* archive) 91 { 92 if (validate_instantiation(archive, "BTab")) 93 return new BTab(archive); 94 95 return NULL; 96 } 97 98 99 status_t 100 BTab::Archive(BMessage* data, bool deep) const 101 { 102 status_t result = BArchivable::Archive(data, deep); 103 if (result != B_OK) 104 return result; 105 106 if (!fEnabled) 107 result = data->AddBool("_disable", false); 108 109 return result; 110 } 111 112 113 status_t 114 BTab::Perform(uint32 d, void* arg) 115 { 116 return BArchivable::Perform(d, arg); 117 } 118 119 120 const char* 121 BTab::Label() const 122 { 123 if (fView != NULL) 124 return fView->Name(); 125 else 126 return NULL; 127 } 128 129 130 void 131 BTab::SetLabel(const char* label) 132 { 133 if (label == NULL || fView == NULL) 134 return; 135 136 fView->SetName(label); 137 138 if (fTabView != NULL) 139 fTabView->Invalidate(); 140 } 141 142 143 bool 144 BTab::IsSelected() const 145 { 146 return fSelected; 147 } 148 149 150 void 151 BTab::Select(BView* owner) 152 { 153 fSelected = true; 154 155 if (owner == NULL || fView == NULL) 156 return; 157 158 // NOTE: Views are not added/removed, if there is layout, 159 // they are made visible/invisible in that case. 160 if (owner->GetLayout() == NULL && fView->Parent() == NULL) 161 owner->AddChild(fView); 162 } 163 164 165 void 166 BTab::Deselect() 167 { 168 if (fView != NULL) { 169 // NOTE: Views are not added/removed, if there is layout, 170 // they are made visible/invisible in that case. 171 bool removeView = false; 172 BView* container = fView->Parent(); 173 if (container != NULL) 174 removeView = 175 dynamic_cast<BCardLayout*>(container->GetLayout()) == NULL; 176 if (removeView) 177 fView->RemoveSelf(); 178 } 179 180 fSelected = false; 181 } 182 183 184 void 185 BTab::SetEnabled(bool enable) 186 { 187 fEnabled = enable; 188 } 189 190 191 bool 192 BTab::IsEnabled() const 193 { 194 return fEnabled; 195 } 196 197 198 void 199 BTab::MakeFocus(bool focus) 200 { 201 fFocus = focus; 202 } 203 204 205 bool 206 BTab::IsFocus() const 207 { 208 return fFocus; 209 } 210 211 212 void 213 BTab::SetView(BView* view) 214 { 215 if (view == NULL || fView == view) 216 return; 217 218 if (fView != NULL) { 219 fView->RemoveSelf(); 220 delete fView; 221 } 222 fView = view; 223 224 if (fTabView != NULL && fSelected) { 225 Select(fTabView->ContainerView()); 226 fTabView->Invalidate(); 227 } 228 } 229 230 231 BView* 232 BTab::View() const 233 { 234 return fView; 235 } 236 237 238 void 239 BTab::DrawFocusMark(BView* owner, BRect frame) 240 { 241 float width = owner->StringWidth(Label()); 242 243 owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 244 245 float offset = IsSelected() ? 3 : 2; 246 switch (fTabView->TabSide()) { 247 case BTabView::kTopSide: 248 owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0, 249 frame.bottom - offset), 250 BPoint((frame.left + frame.right + width) / 2.0, 251 frame.bottom - offset)); 252 break; 253 case BTabView::kBottomSide: 254 owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0, 255 frame.top + offset), 256 BPoint((frame.left + frame.right + width) / 2.0, 257 frame.top + offset)); 258 break; 259 case BTabView::kLeftSide: 260 owner->StrokeLine(BPoint(frame.right - offset, 261 (frame.top + frame.bottom - width) / 2.0), 262 BPoint(frame.right - offset, 263 (frame.top + frame.bottom + width) / 2.0)); 264 break; 265 case BTabView::kRightSide: 266 owner->StrokeLine(BPoint(frame.left + offset, 267 (frame.top + frame.bottom - width) / 2.0), 268 BPoint(frame.left + offset, 269 (frame.top + frame.bottom + width) / 2.0)); 270 break; 271 } 272 } 273 274 275 void 276 BTab::DrawLabel(BView* owner, BRect frame) 277 { 278 float rotation = 0.0f; 279 BPoint center(frame.left + frame.Width() / 2, 280 frame.top + frame.Height() / 2); 281 switch (fTabView->TabSide()) { 282 case BTabView::kTopSide: 283 case BTabView::kBottomSide: 284 rotation = 0.0f; 285 break; 286 case BTabView::kLeftSide: 287 rotation = 270.0f; 288 break; 289 case BTabView::kRightSide: 290 rotation = 90.0f; 291 break; 292 } 293 294 if (rotation != 0.0f) { 295 // DrawLabel doesn't allow rendering rotated text 296 // rotate frame first and BAffineTransform will handle the rotation 297 // we can't give "unrotated" frame because it comes from 298 // BTabView::TabFrame and it is also used to handle mouse clicks 299 BRect originalFrame(frame); 300 frame.top = center.y - originalFrame.Width() / 2; 301 frame.bottom = center.y + originalFrame.Width() / 2; 302 frame.left = center.x - originalFrame.Height() / 2; 303 frame.right = center.x + originalFrame.Height() / 2; 304 } 305 306 BAffineTransform transform; 307 transform.RotateBy(center, rotation * M_PI / 180.0f); 308 owner->SetTransform(transform); 309 be_control_look->DrawLabel(owner, Label(), frame, frame, 310 ui_color(B_PANEL_BACKGROUND_COLOR), 311 IsEnabled() ? 0 : BControlLook::B_DISABLED, 312 BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); 313 owner->SetTransform(BAffineTransform()); 314 } 315 316 317 void 318 BTab::DrawTab(BView* owner, BRect frame, tab_position position, bool full) 319 { 320 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 321 uint32 borders = 0; 322 if (fTabView->TabSide() == BTabView::kTopSide 323 || fTabView->TabSide() == BTabView::kBottomSide) { 324 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER; 325 326 if (frame.left == owner->Bounds().left) 327 borders |= BControlLook::B_LEFT_BORDER; 328 329 if (frame.right == owner->Bounds().right) 330 borders |= BControlLook::B_RIGHT_BORDER; 331 } else if (fTabView->TabSide() == BTabView::kLeftSide 332 || fTabView->TabSide() == BTabView::kRightSide) { 333 borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER; 334 335 if (frame.top == owner->Bounds().top) 336 borders |= BControlLook::B_TOP_BORDER; 337 338 if (frame.bottom == owner->Bounds().bottom) 339 borders |= BControlLook::B_BOTTOM_BORDER; 340 } 341 342 if (position == B_TAB_FRONT) { 343 be_control_look->DrawActiveTab(owner, frame, frame, no_tint, 0, 344 borders, fTabView->TabSide()); 345 } else { 346 be_control_look->DrawInactiveTab(owner, frame, frame, no_tint, 0, 347 borders, fTabView->TabSide()); 348 } 349 350 DrawLabel(owner, frame); 351 } 352 353 354 // #pragma mark - FBC padding and private methods 355 356 357 void BTab::_ReservedTab1() {} 358 void BTab::_ReservedTab2() {} 359 void BTab::_ReservedTab3() {} 360 void BTab::_ReservedTab4() {} 361 void BTab::_ReservedTab5() {} 362 void BTab::_ReservedTab6() {} 363 void BTab::_ReservedTab7() {} 364 void BTab::_ReservedTab8() {} 365 void BTab::_ReservedTab9() {} 366 void BTab::_ReservedTab10() {} 367 void BTab::_ReservedTab11() {} 368 void BTab::_ReservedTab12() {} 369 370 BTab &BTab::operator=(const BTab &) 371 { 372 // this is private and not functional, but exported 373 return *this; 374 } 375 376 377 // #pragma mark - BTabView 378 379 380 BTabView::BTabView(const char* name, button_width width, uint32 flags) 381 : 382 BView(name, flags) 383 { 384 _InitObject(true, width); 385 } 386 387 388 BTabView::BTabView(BRect frame, const char* name, button_width width, 389 uint32 resizeMask, uint32 flags) 390 : 391 BView(frame, name, resizeMask, flags) 392 { 393 _InitObject(false, width); 394 } 395 396 397 BTabView::~BTabView() 398 { 399 for (int32 i = 0; i < CountTabs(); i++) 400 delete TabAt(i); 401 402 delete fTabList; 403 } 404 405 406 BTabView::BTabView(BMessage* archive) 407 : 408 BView(BUnarchiver::PrepareArchive(archive)), 409 fTabList(new BList), 410 fContainerView(NULL), 411 fFocus(-1) 412 { 413 BUnarchiver unarchiver(archive); 414 415 int16 width; 416 if (archive->FindInt16("_but_width", &width) == B_OK) 417 fTabWidthSetting = (button_width)width; 418 else 419 fTabWidthSetting = B_WIDTH_AS_USUAL; 420 421 if (archive->FindFloat("_high", &fTabHeight) != B_OK) { 422 font_height fh; 423 GetFontHeight(&fh); 424 fTabHeight = ceilf(fh.ascent + fh.descent + fh.leading + 8.0f); 425 } 426 427 if (archive->FindInt32("_sel", &fSelection) != B_OK) 428 fSelection = -1; 429 430 if (archive->FindInt32("_border_style", (int32*)&fBorderStyle) != B_OK) 431 fBorderStyle = B_FANCY_BORDER; 432 433 if (archive->FindInt32("_TabSide", (int32*)&fTabSide) != B_OK) 434 fTabSide = kTopSide; 435 436 int32 i = 0; 437 BMessage tabMsg; 438 439 if (BUnarchiver::IsArchiveManaged(archive)) { 440 int32 tabCount; 441 archive->GetInfo("_l_items", NULL, &tabCount); 442 for (int32 i = 0; i < tabCount; i++) { 443 unarchiver.EnsureUnarchived("_l_items", i); 444 unarchiver.EnsureUnarchived("_view_list", i); 445 } 446 return; 447 } 448 449 fContainerView = ChildAt(0); 450 _InitContainerView(Flags() & B_SUPPORTS_LAYOUT); 451 452 while (archive->FindMessage("_l_items", i, &tabMsg) == B_OK) { 453 BArchivable* archivedTab = instantiate_object(&tabMsg); 454 455 if (archivedTab) { 456 BTab* tab = dynamic_cast<BTab*>(archivedTab); 457 458 BMessage viewMsg; 459 if (archive->FindMessage("_view_list", i, &viewMsg) == B_OK) { 460 BArchivable* archivedView = instantiate_object(&viewMsg); 461 if (archivedView) 462 AddTab(dynamic_cast<BView*>(archivedView), tab); 463 } 464 } 465 466 tabMsg.MakeEmpty(); 467 i++; 468 } 469 } 470 471 472 BArchivable* 473 BTabView::Instantiate(BMessage* archive) 474 { 475 if ( validate_instantiation(archive, "BTabView")) 476 return new BTabView(archive); 477 478 return NULL; 479 } 480 481 482 status_t 483 BTabView::Archive(BMessage* archive, bool deep) const 484 { 485 BArchiver archiver(archive); 486 487 status_t result = BView::Archive(archive, deep); 488 489 if (result == B_OK) 490 result = archive->AddInt16("_but_width", fTabWidthSetting); 491 if (result == B_OK) 492 result = archive->AddFloat("_high", fTabHeight); 493 if (result == B_OK) 494 result = archive->AddInt32("_sel", fSelection); 495 if (result == B_OK && fBorderStyle != B_FANCY_BORDER) 496 result = archive->AddInt32("_border_style", fBorderStyle); 497 if (result == B_OK && fTabSide != kTopSide) 498 result = archive->AddInt32("_TabSide", fTabSide); 499 500 if (result == B_OK && deep) { 501 for (int32 i = 0; i < CountTabs(); i++) { 502 BTab* tab = TabAt(i); 503 504 if ((result = archiver.AddArchivable("_l_items", tab, deep)) 505 != B_OK) { 506 break; 507 } 508 result = archiver.AddArchivable("_view_list", tab->View(), deep); 509 } 510 } 511 512 return archiver.Finish(result); 513 } 514 515 516 status_t 517 BTabView::AllUnarchived(const BMessage* archive) 518 { 519 status_t err = BView::AllUnarchived(archive); 520 if (err != B_OK) 521 return err; 522 523 fContainerView = ChildAt(0); 524 _InitContainerView(Flags() & B_SUPPORTS_LAYOUT); 525 526 BUnarchiver unarchiver(archive); 527 528 int32 tabCount; 529 archive->GetInfo("_l_items", NULL, &tabCount); 530 for (int32 i = 0; i < tabCount && err == B_OK; i++) { 531 BTab* tab; 532 err = unarchiver.FindObject("_l_items", i, tab); 533 if (err == B_OK && tab) { 534 BView* view; 535 if ((err = unarchiver.FindObject("_view_list", i, 536 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, view)) != B_OK) 537 break; 538 539 tab->SetView(view); 540 fTabList->AddItem(tab); 541 } 542 } 543 544 if (err == B_OK) 545 Select(fSelection); 546 547 return err; 548 } 549 550 551 status_t 552 BTabView::Perform(perform_code code, void* _data) 553 { 554 switch (code) { 555 case PERFORM_CODE_ALL_UNARCHIVED: 556 { 557 perform_data_all_unarchived* data 558 = (perform_data_all_unarchived*)_data; 559 560 data->return_value = BTabView::AllUnarchived(data->archive); 561 return B_OK; 562 } 563 } 564 565 return BView::Perform(code, _data); 566 } 567 568 569 void 570 BTabView::AttachedToWindow() 571 { 572 BView::AttachedToWindow(); 573 574 if (fSelection < 0 && CountTabs() > 0) 575 Select(0); 576 } 577 578 579 void 580 BTabView::DetachedFromWindow() 581 { 582 BView::DetachedFromWindow(); 583 } 584 585 586 void 587 BTabView::AllAttached() 588 { 589 BView::AllAttached(); 590 } 591 592 593 void 594 BTabView::AllDetached() 595 { 596 BView::AllDetached(); 597 } 598 599 600 // #pragma mark - 601 602 603 void 604 BTabView::MessageReceived(BMessage* message) 605 { 606 switch (message->what) { 607 case B_GET_PROPERTY: 608 case B_SET_PROPERTY: 609 { 610 BMessage reply(B_REPLY); 611 bool handled = false; 612 613 BMessage specifier; 614 int32 index; 615 int32 form; 616 const char* property; 617 if (message->GetCurrentSpecifier(&index, &specifier, &form, 618 &property) == B_OK) { 619 if (strcmp(property, "Selection") == 0) { 620 if (message->what == B_GET_PROPERTY) { 621 reply.AddInt32("result", fSelection); 622 handled = true; 623 } else { 624 // B_GET_PROPERTY 625 int32 selection; 626 if (message->FindInt32("data", &selection) == B_OK) { 627 Select(selection); 628 reply.AddInt32("error", B_OK); 629 handled = true; 630 } 631 } 632 } 633 } 634 635 if (handled) 636 message->SendReply(&reply); 637 else 638 BView::MessageReceived(message); 639 break; 640 } 641 642 #if 0 643 case B_MOUSE_WHEEL_CHANGED: 644 { 645 float deltaX = 0.0f; 646 float deltaY = 0.0f; 647 message->FindFloat("be:wheel_delta_x", &deltaX); 648 message->FindFloat("be:wheel_delta_y", &deltaY); 649 650 if (deltaX == 0.0f && deltaY == 0.0f) 651 return; 652 653 if (deltaY == 0.0f) 654 deltaY = deltaX; 655 656 int32 selection = Selection(); 657 int32 numTabs = CountTabs(); 658 if (deltaY > 0 && selection < numTabs - 1) { 659 // move to the right tab. 660 Select(Selection() + 1); 661 } else if (deltaY < 0 && selection > 0 && numTabs > 1) { 662 // move to the left tab. 663 Select(selection - 1); 664 } 665 break; 666 } 667 #endif 668 669 default: 670 BView::MessageReceived(message); 671 break; 672 } 673 } 674 675 676 void 677 BTabView::KeyDown(const char* bytes, int32 numBytes) 678 { 679 if (IsHidden()) 680 return; 681 682 switch (bytes[0]) { 683 case B_DOWN_ARROW: 684 case B_LEFT_ARROW: { 685 int32 focus = fFocus - 1; 686 if (focus < 0) 687 focus = CountTabs() - 1; 688 SetFocusTab(focus, true); 689 break; 690 } 691 692 case B_UP_ARROW: 693 case B_RIGHT_ARROW: { 694 int32 focus = fFocus + 1; 695 if (focus >= CountTabs()) 696 focus = 0; 697 SetFocusTab(focus, true); 698 break; 699 } 700 701 case B_RETURN: 702 case B_SPACE: 703 Select(FocusTab()); 704 break; 705 706 default: 707 BView::KeyDown(bytes, numBytes); 708 } 709 } 710 711 712 void 713 BTabView::MouseDown(BPoint where) 714 { 715 for (int32 i = 0; i < CountTabs(); i++) { 716 if (TabFrame(i).Contains(where) 717 && i != Selection()) { 718 Select(i); 719 return; 720 } 721 } 722 723 BView::MouseDown(where); 724 } 725 726 727 void 728 BTabView::MouseUp(BPoint where) 729 { 730 BView::MouseUp(where); 731 } 732 733 734 void 735 BTabView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) 736 { 737 BView::MouseMoved(where, transit, dragMessage); 738 } 739 740 741 void 742 BTabView::Pulse() 743 { 744 BView::Pulse(); 745 } 746 747 748 void 749 BTabView::Select(int32 index) 750 { 751 if (index == Selection()) 752 return; 753 754 if (index < 0 || index >= CountTabs()) 755 index = Selection(); 756 757 BTab* tab = TabAt(Selection()); 758 759 if (tab) 760 tab->Deselect(); 761 762 tab = TabAt(index); 763 if (tab != NULL && fContainerView != NULL) { 764 if (index == 0) 765 fTabOffset = 0.0f; 766 767 tab->Select(fContainerView); 768 fSelection = index; 769 770 // make the view visible through the layout if there is one 771 BCardLayout* layout 772 = dynamic_cast<BCardLayout*>(fContainerView->GetLayout()); 773 if (layout != NULL) 774 layout->SetVisibleItem(index); 775 } 776 777 Invalidate(); 778 779 if (index != 0 && !Bounds().Contains(TabFrame(index))){ 780 if (!Bounds().Contains(TabFrame(index).LeftTop())) 781 fTabOffset += TabFrame(index).left - Bounds().left - 20.0f; 782 else 783 fTabOffset += TabFrame(index).right - Bounds().right + 20.0f; 784 785 Invalidate(); 786 } 787 788 SetFocusTab(index, true); 789 } 790 791 792 int32 793 BTabView::Selection() const 794 { 795 return fSelection; 796 } 797 798 799 void 800 BTabView::WindowActivated(bool active) 801 { 802 BView::WindowActivated(active); 803 804 if (IsFocus()) 805 Invalidate(); 806 } 807 808 809 void 810 BTabView::MakeFocus(bool focus) 811 { 812 BView::MakeFocus(focus); 813 814 SetFocusTab(Selection(), focus); 815 } 816 817 818 void 819 BTabView::SetFocusTab(int32 tab, bool focus) 820 { 821 if (tab >= CountTabs()) 822 tab = 0; 823 824 if (tab < 0) 825 tab = CountTabs() - 1; 826 827 if (focus) { 828 if (tab == fFocus) 829 return; 830 831 if (fFocus != -1){ 832 if (TabAt (fFocus) != NULL) 833 TabAt(fFocus)->MakeFocus(false); 834 Invalidate(TabFrame(fFocus)); 835 } 836 if (TabAt(tab) != NULL){ 837 TabAt(tab)->MakeFocus(true); 838 Invalidate(TabFrame(tab)); 839 fFocus = tab; 840 } 841 } else if (fFocus != -1) { 842 TabAt(fFocus)->MakeFocus(false); 843 Invalidate(TabFrame(fFocus)); 844 fFocus = -1; 845 } 846 } 847 848 849 int32 850 BTabView::FocusTab() const 851 { 852 return fFocus; 853 } 854 855 856 void 857 BTabView::Draw(BRect updateRect) 858 { 859 DrawTabs(); 860 DrawBox(TabFrame(fSelection)); 861 862 if (IsFocus() && fFocus != -1) 863 TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus)); 864 } 865 866 867 BRect 868 BTabView::DrawTabs() 869 { 870 BRect bounds(Bounds()); 871 BRect tabsBounds; 872 uint32 borders = 0; 873 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 874 if (fTabSide == kTopSide || fTabSide == kBottomSide) { 875 if (fTabSide == kTopSide) 876 bounds.bottom = fTabHeight; 877 else 878 bounds.top = bounds.bottom - fTabHeight; 879 tabsBounds = bounds; 880 // make a copy for later 881 882 // draw an inactive tab frame behind all tabs 883 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER; 884 if (fBorderStyle == B_NO_BORDER) { 885 // removes left border that is an artifact of DrawInactiveTab() 886 bounds.left -= 1; 887 } else { 888 borders |= BControlLook::B_LEFT_BORDER 889 | BControlLook::B_RIGHT_BORDER; 890 } 891 892 // DrawInactiveTab draws 2px border 893 // draw a little wider tab frame to align B_PLAIN_BORDER with it 894 if (fBorderStyle == B_PLAIN_BORDER) { 895 bounds.left -= 1; 896 bounds.right += 1; 897 } 898 } else if (fTabSide == kLeftSide || fTabSide == kRightSide) { 899 if (fTabSide == kLeftSide) 900 bounds.right = fTabHeight; 901 else 902 bounds.left = bounds.right - fTabHeight; 903 tabsBounds = bounds; 904 // make a copy for later 905 906 // draw an inactive tab frame behind all tabs 907 borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER; 908 if (fBorderStyle == B_NO_BORDER) { 909 // removes top border that is an artifact of DrawInactiveTab() 910 bounds.top -= 1; 911 } else { 912 borders |= BControlLook::B_TOP_BORDER 913 | BControlLook::B_BOTTOM_BORDER; 914 } 915 916 // DrawInactiveTab draws 2px border 917 // draw a little wider tab frame to align B_PLAIN_BORDER with it 918 if (fBorderStyle == B_PLAIN_BORDER) { 919 bounds.top -= 1; 920 bounds.bottom += 1; 921 } 922 } 923 924 be_control_look->DrawInactiveTab(this, bounds, bounds, base, 0, 925 borders, fTabSide); 926 927 // draw the tabs on top of the inactive tab bounds 928 BRect activeTabFrame; 929 int32 tabCount = CountTabs(); 930 for (int32 i = 0; i < tabCount; i++) { 931 BRect tabFrame = TabFrame(i); 932 if (i == fSelection) 933 activeTabFrame = tabFrame; 934 935 TabAt(i)->DrawTab(this, tabFrame, 936 i == fSelection ? B_TAB_FRONT : 937 (i == 0) ? B_TAB_FIRST : B_TAB_ANY, 938 i + 1 != fSelection); 939 } 940 941 float last = 0.0f; 942 float lastTab = 0.0f; 943 if (fTabSide == kTopSide || fTabSide == kBottomSide) { 944 lastTab = TabFrame(tabCount - 1).right; 945 last = bounds.right; 946 tabsBounds.left = tabsBounds.right = lastTab; 947 borders = BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER; 948 } else if (fTabSide == kLeftSide || fTabSide == kRightSide) { 949 lastTab = TabFrame(tabCount - 1).bottom; 950 last = bounds.bottom; 951 tabsBounds.top = tabsBounds.bottom = lastTab; 952 borders = BControlLook::B_LEFT_BORDER | BControlLook::B_RIGHT_BORDER; 953 } 954 955 if (lastTab < last) { 956 // draw a 1px right border on the last tab 957 be_control_look->DrawInactiveTab(this, tabsBounds, tabsBounds, base, 0, 958 borders, fTabSide); 959 } 960 961 return fSelection < CountTabs() ? TabFrame(fSelection) : BRect(); 962 } 963 964 965 void 966 BTabView::DrawBox(BRect selectedTabRect) 967 { 968 BRect rect(Bounds()); 969 uint32 bordersToDraw = BControlLook::B_ALL_BORDERS; 970 switch (fTabSide) { 971 case kTopSide: 972 bordersToDraw &= ~BControlLook::B_TOP_BORDER; 973 rect.top = fTabHeight; 974 break; 975 case kBottomSide: 976 bordersToDraw &= ~BControlLook::B_BOTTOM_BORDER; 977 rect.bottom -= fTabHeight; 978 break; 979 case kLeftSide: 980 bordersToDraw &= ~BControlLook::B_LEFT_BORDER; 981 rect.left = fTabHeight; 982 break; 983 case kRightSide: 984 bordersToDraw &= ~BControlLook::B_RIGHT_BORDER; 985 rect.right -= fTabHeight; 986 break; 987 } 988 989 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 990 if (fBorderStyle == B_FANCY_BORDER) 991 be_control_look->DrawGroupFrame(this, rect, rect, base, bordersToDraw); 992 else if (fBorderStyle == B_PLAIN_BORDER) { 993 be_control_look->DrawBorder(this, rect, rect, base, B_PLAIN_BORDER, 994 0, bordersToDraw); 995 } else 996 ; // B_NO_BORDER draws no box 997 } 998 999 1000 BRect 1001 BTabView::TabFrame(int32 index) const 1002 { 1003 if (index >= CountTabs() || index < 0) 1004 return BRect(); 1005 1006 float width = 100.0f; 1007 float height = fTabHeight; 1008 float offset = BControlLook::ComposeSpacing(B_USE_WINDOW_SPACING); 1009 BRect bounds(Bounds()); 1010 1011 switch (fTabWidthSetting) { 1012 case B_WIDTH_FROM_LABEL: 1013 { 1014 float x = 0.0f; 1015 for (int32 i = 0; i < index; i++){ 1016 x += StringWidth(TabAt(i)->Label()) + 20.0f; 1017 } 1018 1019 switch (fTabSide) { 1020 case kTopSide: 1021 return BRect(offset + x, 0.0f, 1022 offset + x + StringWidth(TabAt(index)->Label()) + 20.0f, 1023 height); 1024 case kBottomSide: 1025 return BRect(offset + x, bounds.bottom - height, 1026 offset + x + StringWidth(TabAt(index)->Label()) + 20.0f, 1027 bounds.bottom); 1028 case kLeftSide: 1029 return BRect(0.0f, offset + x, height, offset + x 1030 + StringWidth(TabAt(index)->Label()) + 20.0f); 1031 case kRightSide: 1032 return BRect(bounds.right - height, offset + x, 1033 bounds.right, offset + x 1034 + StringWidth(TabAt(index)->Label()) + 20.0f); 1035 default: 1036 return BRect(); 1037 } 1038 } 1039 1040 case B_WIDTH_FROM_WIDEST: 1041 width = 0.0; 1042 for (int32 i = 0; i < CountTabs(); i++) { 1043 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f; 1044 if (tabWidth > width) 1045 width = tabWidth; 1046 } 1047 // fall through 1048 1049 case B_WIDTH_AS_USUAL: 1050 default: 1051 switch (fTabSide) { 1052 case kTopSide: 1053 return BRect(offset + index * width, 0.0f, 1054 offset + index * width + width, height); 1055 case kBottomSide: 1056 return BRect(offset + index * width, bounds.bottom - height, 1057 offset + index * width + width, bounds.bottom); 1058 case kLeftSide: 1059 return BRect(0.0f, offset + index * width, height, 1060 offset + index * width + width); 1061 case kRightSide: 1062 return BRect(bounds.right - height, offset + index * width, 1063 bounds.right, offset + index * width + width); 1064 default: 1065 return BRect(); 1066 } 1067 } 1068 } 1069 1070 1071 void 1072 BTabView::SetFlags(uint32 flags) 1073 { 1074 BView::SetFlags(flags); 1075 } 1076 1077 1078 void 1079 BTabView::SetResizingMode(uint32 mode) 1080 { 1081 BView::SetResizingMode(mode); 1082 } 1083 1084 1085 // #pragma mark - 1086 1087 1088 void 1089 BTabView::ResizeToPreferred() 1090 { 1091 BView::ResizeToPreferred(); 1092 } 1093 1094 1095 void 1096 BTabView::GetPreferredSize(float* _width, float* _height) 1097 { 1098 BView::GetPreferredSize(_width, _height); 1099 } 1100 1101 1102 BSize 1103 BTabView::MinSize() 1104 { 1105 BSize size; 1106 if (GetLayout()) 1107 size = GetLayout()->MinSize(); 1108 else { 1109 size = _TabsMinSize(); 1110 BSize containerSize = fContainerView->MinSize(); 1111 containerSize.width += 2 * _BorderWidth(); 1112 containerSize.height += 2 * _BorderWidth(); 1113 if (containerSize.width > size.width) 1114 size.width = containerSize.width; 1115 size.height += containerSize.height; 1116 } 1117 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 1118 } 1119 1120 1121 BSize 1122 BTabView::MaxSize() 1123 { 1124 BSize size; 1125 if (GetLayout()) 1126 size = GetLayout()->MaxSize(); 1127 else { 1128 size = _TabsMinSize(); 1129 BSize containerSize = fContainerView->MaxSize(); 1130 containerSize.width += 2 * _BorderWidth(); 1131 containerSize.height += 2 * _BorderWidth(); 1132 if (containerSize.width > size.width) 1133 size.width = containerSize.width; 1134 size.height += containerSize.height; 1135 } 1136 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 1137 } 1138 1139 1140 BSize 1141 BTabView::PreferredSize() 1142 { 1143 BSize size; 1144 if (GetLayout() != NULL) 1145 size = GetLayout()->PreferredSize(); 1146 else { 1147 size = _TabsMinSize(); 1148 BSize containerSize = fContainerView->PreferredSize(); 1149 containerSize.width += 2 * _BorderWidth(); 1150 containerSize.height += 2 * _BorderWidth(); 1151 if (containerSize.width > size.width) 1152 size.width = containerSize.width; 1153 size.height += containerSize.height; 1154 } 1155 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); 1156 } 1157 1158 1159 void 1160 BTabView::FrameMoved(BPoint newPosition) 1161 { 1162 BView::FrameMoved(newPosition); 1163 } 1164 1165 1166 void 1167 BTabView::FrameResized(float newWidth, float newHeight) 1168 { 1169 BView::FrameResized(newWidth, newHeight); 1170 } 1171 1172 1173 // #pragma mark - 1174 1175 1176 BHandler* 1177 BTabView::ResolveSpecifier(BMessage* message, int32 index, 1178 BMessage* specifier, int32 what, const char* property) 1179 { 1180 BPropertyInfo propInfo(sPropertyList); 1181 1182 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK) 1183 return this; 1184 1185 return BView::ResolveSpecifier(message, index, specifier, what, property); 1186 } 1187 1188 1189 status_t 1190 BTabView::GetSupportedSuites(BMessage* message) 1191 { 1192 message->AddString("suites", "suite/vnd.Be-tab-view"); 1193 1194 BPropertyInfo propInfo(sPropertyList); 1195 message->AddFlat("messages", &propInfo); 1196 1197 return BView::GetSupportedSuites(message); 1198 } 1199 1200 1201 // #pragma mark - 1202 1203 1204 void 1205 BTabView::AddTab(BView* target, BTab* tab) 1206 { 1207 if (tab == NULL) 1208 tab = new BTab(target); 1209 else 1210 tab->SetView(target); 1211 1212 if (fContainerView->GetLayout()) 1213 fContainerView->GetLayout()->AddView(CountTabs(), target); 1214 1215 fTabList->AddItem(tab); 1216 BTab::Private(tab).SetTabView(this); 1217 1218 // When we haven't had a any tabs before, but are already attached to the 1219 // window, select this one. 1220 if (CountTabs() == 1 && Window() != NULL) 1221 Select(0); 1222 } 1223 1224 1225 BTab* 1226 BTabView::RemoveTab(int32 index) 1227 { 1228 if (index < 0 || index >= CountTabs()) 1229 return NULL; 1230 1231 BTab* tab = (BTab*)fTabList->RemoveItem(index); 1232 if (tab == NULL) 1233 return NULL; 1234 1235 tab->Deselect(); 1236 BTab::Private(tab).SetTabView(NULL); 1237 1238 if (fContainerView->GetLayout()) 1239 fContainerView->GetLayout()->RemoveItem(index); 1240 1241 if (CountTabs() == 0) 1242 fFocus = -1; 1243 else if (index <= fSelection) 1244 Select(fSelection - 1); 1245 1246 if (fFocus >= 0) { 1247 if (fFocus == CountTabs() - 1 || CountTabs() == 0) 1248 SetFocusTab(fFocus, false); 1249 else 1250 SetFocusTab(fFocus, true); 1251 } 1252 1253 return tab; 1254 } 1255 1256 1257 BTab* 1258 BTabView::TabAt(int32 index) const 1259 { 1260 return (BTab*)fTabList->ItemAt(index); 1261 } 1262 1263 1264 void 1265 BTabView::SetTabWidth(button_width width) 1266 { 1267 fTabWidthSetting = width; 1268 1269 Invalidate(); 1270 } 1271 1272 1273 button_width 1274 BTabView::TabWidth() const 1275 { 1276 return fTabWidthSetting; 1277 } 1278 1279 1280 void 1281 BTabView::SetTabHeight(float height) 1282 { 1283 if (fTabHeight == height) 1284 return; 1285 1286 fTabHeight = height; 1287 _LayoutContainerView(GetLayout() != NULL); 1288 1289 Invalidate(); 1290 } 1291 1292 1293 float 1294 BTabView::TabHeight() const 1295 { 1296 return fTabHeight; 1297 } 1298 1299 1300 void 1301 BTabView::SetBorder(border_style borderStyle) 1302 { 1303 if (fBorderStyle == borderStyle) 1304 return; 1305 1306 fBorderStyle = borderStyle; 1307 1308 _LayoutContainerView((Flags() & B_SUPPORTS_LAYOUT) != 0); 1309 } 1310 1311 1312 border_style 1313 BTabView::Border() const 1314 { 1315 return fBorderStyle; 1316 } 1317 1318 1319 void 1320 BTabView::SetTabSide(tab_side tabSide) 1321 { 1322 if (fTabSide == tabSide) 1323 return; 1324 1325 fTabSide = tabSide; 1326 _LayoutContainerView(Flags() & B_SUPPORTS_LAYOUT); 1327 } 1328 1329 1330 BTabView::tab_side 1331 BTabView::TabSide() const 1332 { 1333 return fTabSide; 1334 } 1335 1336 1337 BView* 1338 BTabView::ContainerView() const 1339 { 1340 return fContainerView; 1341 } 1342 1343 1344 int32 1345 BTabView::CountTabs() const 1346 { 1347 return fTabList->CountItems(); 1348 } 1349 1350 1351 BView* 1352 BTabView::ViewForTab(int32 tabIndex) const 1353 { 1354 BTab* tab = TabAt(tabIndex); 1355 if (tab != NULL) 1356 return tab->View(); 1357 1358 return NULL; 1359 } 1360 1361 1362 void 1363 BTabView::_InitObject(bool layouted, button_width width) 1364 { 1365 fTabList = new BList; 1366 1367 fTabWidthSetting = width; 1368 fSelection = -1; 1369 fFocus = -1; 1370 fTabOffset = 0.0f; 1371 fBorderStyle = B_FANCY_BORDER; 1372 fTabSide = kTopSide; 1373 1374 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1375 SetLowUIColor(B_PANEL_BACKGROUND_COLOR); 1376 1377 font_height fh; 1378 GetFontHeight(&fh); 1379 fTabHeight = ceilf(fh.ascent + fh.descent + fh.leading + 8.0f); 1380 1381 fContainerView = NULL; 1382 _InitContainerView(layouted); 1383 } 1384 1385 1386 void 1387 BTabView::_InitContainerView(bool layouted) 1388 { 1389 bool needsLayout = false; 1390 bool createdContainer = false; 1391 if (layouted) { 1392 if (GetLayout() == NULL) { 1393 SetLayout(new(std::nothrow) BGroupLayout(B_HORIZONTAL)); 1394 needsLayout = true; 1395 } 1396 1397 if (fContainerView == NULL) { 1398 fContainerView = new BView("view container", B_WILL_DRAW); 1399 fContainerView->SetLayout(new(std::nothrow) BCardLayout()); 1400 createdContainer = true; 1401 } 1402 } else if (fContainerView == NULL) { 1403 fContainerView = new BView(Bounds(), "view container", B_FOLLOW_ALL, 1404 B_WILL_DRAW); 1405 createdContainer = true; 1406 } 1407 1408 if (needsLayout || createdContainer) 1409 _LayoutContainerView(layouted); 1410 1411 if (createdContainer) { 1412 fContainerView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1413 fContainerView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR); 1414 AddChild(fContainerView); 1415 } 1416 } 1417 1418 1419 BSize 1420 BTabView::_TabsMinSize() const 1421 { 1422 BSize size(0.0f, TabHeight()); 1423 int32 count = min_c(2, CountTabs()); 1424 for (int32 i = 0; i < count; i++) { 1425 BRect frame = TabFrame(i); 1426 size.width += frame.Width(); 1427 } 1428 1429 if (count < CountTabs()) { 1430 // TODO: Add size for yet to be implemented buttons that allow 1431 // "scrolling" the displayed tabs left/right. 1432 } 1433 1434 return size; 1435 } 1436 1437 1438 float 1439 BTabView::_BorderWidth() const 1440 { 1441 switch (fBorderStyle) { 1442 default: 1443 case B_FANCY_BORDER: 1444 return 3.0f; 1445 1446 case B_PLAIN_BORDER: 1447 return 1.0f; 1448 1449 case B_NO_BORDER: 1450 return 0.0f; 1451 } 1452 } 1453 1454 1455 void 1456 BTabView::_LayoutContainerView(bool layouted) 1457 { 1458 float borderWidth = _BorderWidth(); 1459 if (layouted) { 1460 float topBorderOffset; 1461 switch (fBorderStyle) { 1462 default: 1463 case B_FANCY_BORDER: 1464 topBorderOffset = 1.0f; 1465 break; 1466 1467 case B_PLAIN_BORDER: 1468 topBorderOffset = 0.0f; 1469 break; 1470 1471 case B_NO_BORDER: 1472 topBorderOffset = -1.0f; 1473 break; 1474 } 1475 BGroupLayout* layout = dynamic_cast<BGroupLayout*>(GetLayout()); 1476 if (layout != NULL) { 1477 float inset = borderWidth + TabHeight() - topBorderOffset; 1478 switch (fTabSide) { 1479 case kTopSide: 1480 layout->SetInsets(borderWidth, inset, borderWidth, 1481 borderWidth); 1482 break; 1483 case kBottomSide: 1484 layout->SetInsets(borderWidth, borderWidth, borderWidth, 1485 inset); 1486 break; 1487 case kLeftSide: 1488 layout->SetInsets(inset, borderWidth, borderWidth, 1489 borderWidth); 1490 break; 1491 case kRightSide: 1492 layout->SetInsets(borderWidth, borderWidth, inset, 1493 borderWidth); 1494 break; 1495 } 1496 } 1497 } else { 1498 BRect bounds = Bounds(); 1499 switch (fTabSide) { 1500 case kTopSide: 1501 bounds.top += TabHeight(); 1502 break; 1503 case kBottomSide: 1504 bounds.bottom -= TabHeight(); 1505 break; 1506 case kLeftSide: 1507 bounds.left += TabHeight(); 1508 break; 1509 case kRightSide: 1510 bounds.right -= TabHeight(); 1511 break; 1512 } 1513 bounds.InsetBy(borderWidth, borderWidth); 1514 1515 fContainerView->MoveTo(bounds.left, bounds.top); 1516 fContainerView->ResizeTo(bounds.Width(), bounds.Height()); 1517 } 1518 } 1519 1520 1521 // #pragma mark - FBC and forbidden 1522 1523 1524 void BTabView::_ReservedTabView3() {} 1525 void BTabView::_ReservedTabView4() {} 1526 void BTabView::_ReservedTabView5() {} 1527 void BTabView::_ReservedTabView6() {} 1528 void BTabView::_ReservedTabView7() {} 1529 void BTabView::_ReservedTabView8() {} 1530 void BTabView::_ReservedTabView9() {} 1531 void BTabView::_ReservedTabView10() {} 1532 void BTabView::_ReservedTabView11() {} 1533 void BTabView::_ReservedTabView12() {} 1534 1535 1536 BTabView::BTabView(const BTabView& tabView) 1537 : BView(tabView) 1538 { 1539 // this is private and not functional, but exported 1540 } 1541 1542 1543 BTabView& 1544 BTabView::operator=(const BTabView&) 1545 { 1546 // this is private and not functional, but exported 1547 return *this; 1548 } 1549 1550 // #pragma mark - binary compatibility 1551 1552 1553 extern "C" void 1554 B_IF_GCC_2(_ReservedTabView1__8BTabView, _ZN8BTabView17_ReservedTabView1Ev)( 1555 BTabView* tabView, border_style borderStyle) 1556 { 1557 tabView->BTabView::SetBorder(borderStyle); 1558 } 1559 1560 extern "C" void 1561 B_IF_GCC_2(_ReservedTabView2__8BTabView, _ZN8BTabView17_ReservedTabView2Ev)( 1562 BTabView* tabView, BTabView::tab_side tabSide) 1563 { 1564 tabView->BTabView::SetTabSide(tabSide); 1565 } 1566