1 /* 2 * Copyright 2001-2009, 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 */ 11 12 13 #include <TabView.h> 14 15 #include <new> 16 #include <string.h> 17 18 #include <CardLayout.h> 19 #include <ControlLook.h> 20 #include <GroupLayout.h> 21 #include <LayoutUtils.h> 22 #include <List.h> 23 #include <Message.h> 24 #include <PropertyInfo.h> 25 #include <Rect.h> 26 #include <Region.h> 27 #include <String.h> 28 29 30 static property_info sPropertyList[] = { 31 { 32 "Selection", 33 { B_GET_PROPERTY, B_SET_PROPERTY }, 34 { B_DIRECT_SPECIFIER }, 35 NULL, 0, 36 { B_INT32_TYPE } 37 }, 38 {} 39 }; 40 41 42 43 BTab::BTab(BView *tabView) 44 : 45 fEnabled(true), 46 fSelected(false), 47 fFocus(false), 48 fView(tabView) 49 { 50 } 51 52 53 BTab::BTab(BMessage *archive) 54 : 55 fSelected(false), 56 fFocus(false), 57 fView(NULL) 58 { 59 bool disable; 60 61 if (archive->FindBool("_disable", &disable) != B_OK) 62 SetEnabled(true); 63 else 64 SetEnabled(!disable); 65 } 66 67 68 BTab::~BTab() 69 { 70 if (!fView) 71 return; 72 73 if (fSelected) 74 fView->RemoveSelf(); 75 76 delete fView; 77 } 78 79 80 BArchivable * 81 BTab::Instantiate(BMessage *archive) 82 { 83 if (validate_instantiation(archive, "BTab")) 84 return new BTab(archive); 85 86 return NULL; 87 } 88 89 90 status_t 91 BTab::Archive(BMessage *archive, bool deep) const 92 { 93 status_t err = BArchivable::Archive(archive, deep); 94 if (err != B_OK) 95 return err; 96 97 if (!fEnabled) 98 err = archive->AddBool("_disable", false); 99 100 return err; 101 } 102 103 104 status_t 105 BTab::Perform(uint32 d, void *arg) 106 { 107 return BArchivable::Perform(d, arg); 108 } 109 110 111 const char * 112 BTab::Label() const 113 { 114 if (fView) 115 return fView->Name(); 116 else 117 return NULL; 118 } 119 120 121 void 122 BTab::SetLabel(const char *label) 123 { 124 if (!label || !fView) 125 return; 126 127 fView->SetName(label); 128 } 129 130 131 bool 132 BTab::IsSelected() const 133 { 134 return fSelected; 135 } 136 137 138 void 139 BTab::Select(BView *owner) 140 { 141 // TODO: Shouldn't we still maintain fSelected like in Deselect()? 142 if (!owner || !View() || !owner->Window()) 143 return; 144 145 // NOTE: Views are not added/removed, if there is layout, 146 // they are made visible/invisible in that case. 147 if (!owner->GetLayout()) 148 owner->AddChild(fView); 149 150 fSelected = true; 151 } 152 153 154 void 155 BTab::Deselect() 156 { 157 if (View()) { 158 // NOTE: Views are not added/removed, if there is layout, 159 // they are made visible/invisible in that case. 160 bool removeView = false; 161 BView* container = View()->Parent(); 162 if (container) 163 removeView = 164 dynamic_cast<BCardLayout*>(container->GetLayout()) == NULL; 165 if (removeView) 166 View()->RemoveSelf(); 167 } 168 169 fSelected = false; 170 } 171 172 173 void 174 BTab::SetEnabled(bool enabled) 175 { 176 fEnabled = enabled; 177 } 178 179 180 bool 181 BTab::IsEnabled() const 182 { 183 return fEnabled; 184 } 185 186 187 void 188 BTab::MakeFocus(bool inFocus) 189 { 190 fFocus = inFocus; 191 } 192 193 194 bool 195 BTab::IsFocus() const 196 { 197 return fFocus; 198 } 199 200 201 void 202 BTab::SetView(BView *view) 203 { 204 if (!view || fView == view) 205 return; 206 207 if (fView != NULL) { 208 fView->RemoveSelf(); 209 delete fView; 210 } 211 fView = view; 212 } 213 214 215 BView * 216 BTab::View() const 217 { 218 return fView; 219 } 220 221 222 void 223 BTab::DrawFocusMark(BView *owner, BRect frame) 224 { 225 float width = owner->StringWidth(Label()); 226 227 owner->SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 228 229 float offset = IsSelected() ? 3 : 2; 230 owner->StrokeLine(BPoint((frame.left + frame.right - width) / 2.0, 231 frame.bottom - offset), 232 BPoint((frame.left + frame.right + width) / 2.0, 233 frame.bottom - offset)); 234 } 235 236 237 void 238 BTab::DrawLabel(BView *owner, BRect frame) 239 { 240 if (Label() == NULL) 241 return; 242 243 BString label = Label(); 244 float frameWidth = frame.Width(); 245 float width = owner->StringWidth(label.String()); 246 font_height fh; 247 248 if (width > frameWidth) { 249 BFont font; 250 owner->GetFont(&font); 251 font.TruncateString(&label, B_TRUNCATE_END, frameWidth); 252 width = frameWidth; 253 font.GetHeight(&fh); 254 } else { 255 owner->GetFontHeight(&fh); 256 } 257 258 owner->SetDrawingMode(B_OP_OVER); 259 owner->SetHighColor(ui_color(B_CONTROL_TEXT_COLOR)); 260 owner->DrawString(label.String(), 261 BPoint((frame.left + frame.right - width) / 2.0, 262 (frame.top + frame.bottom - fh.ascent - fh.descent) / 2.0 263 + fh.ascent)); 264 } 265 266 267 void 268 BTab::DrawTab(BView *owner, BRect frame, tab_position position, bool full) 269 { 270 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR); 271 272 if (be_control_look != NULL) { 273 // uint32 borders = BControlLook::B_RIGHT_BORDER 274 // | BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER; 275 // if (frame.left == owner->Bounds().left) 276 // borders |= BControlLook::B_LEFT_BORDER; 277 // be_control_look->DrawButtonFrame(owner, frame, frame, 278 // no_tint, 0, borders); 279 // if (position == B_TAB_FRONT) 280 // no_tint = tint_color(no_tint, B_DARKEN_2_TINT); 281 // be_control_look->DrawButtonBackground(owner, frame, frame, no_tint); 282 283 uint32 borders = BControlLook::B_TOP_BORDER 284 | BControlLook::B_BOTTOM_BORDER; 285 if (frame.left == owner->Bounds().left) 286 borders |= BControlLook::B_LEFT_BORDER; 287 if (frame.right == owner->Bounds().right) 288 borders |= BControlLook::B_RIGHT_BORDER; 289 290 if (position == B_TAB_FRONT) { 291 frame.bottom += 1; 292 be_control_look->DrawActiveTab(owner, frame, frame, no_tint, 0, 293 borders); 294 } else { 295 be_control_look->DrawInactiveTab(owner, frame, frame, no_tint, 0, 296 borders); 297 } 298 299 DrawLabel(owner, frame); 300 return; 301 } 302 303 rgb_color lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT); 304 rgb_color darken2 = tint_color(no_tint, B_DARKEN_2_TINT); 305 rgb_color darken3 = tint_color(no_tint, B_DARKEN_3_TINT); 306 rgb_color darken4 = tint_color(no_tint, B_DARKEN_4_TINT); 307 rgb_color darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 308 309 owner->SetHighColor(darkenmax); 310 owner->SetLowColor(no_tint); 311 // NOTE: "frame" goes from the beginning of the left slope to the beginning 312 // of the right slope - "lableFrame" is the frame between both slopes 313 BRect lableFrame = frame; 314 lableFrame.left = lableFrame.left + frame.Height() / 2.0; 315 DrawLabel(owner, lableFrame); 316 317 owner->SetDrawingMode(B_OP_OVER); 318 319 owner->BeginLineArray(12); 320 321 int32 slopeWidth = (int32)ceilf(frame.Height() / 2.0); 322 323 if (position != B_TAB_ANY) { 324 // full height left side 325 owner->AddLine(BPoint(frame.left, frame.bottom), 326 BPoint(frame.left + slopeWidth, frame.top), darken3); 327 owner->AddLine(BPoint(frame.left, frame.bottom + 1), 328 BPoint(frame.left + slopeWidth, frame.top + 1), lightenmax); 329 } else { 330 // upper half of left side 331 owner->AddLine(BPoint(frame.left + slopeWidth / 2, 332 frame.bottom - slopeWidth), 333 BPoint(frame.left + slopeWidth, frame.top), darken3); 334 owner->AddLine(BPoint(frame.left + slopeWidth / 2 + 2, 335 frame.bottom - slopeWidth - 1), 336 BPoint(frame.left + slopeWidth, frame.top + 1), lightenmax); 337 } 338 339 // lines along the top 340 owner->AddLine(BPoint(frame.left + slopeWidth, frame.top), 341 BPoint(frame.right, frame.top), darken3); 342 owner->AddLine(BPoint(frame.left + slopeWidth, frame.top + 1), 343 BPoint(frame.right, frame.top + 1), lightenmax); 344 345 if (full) { 346 // full height right side 347 owner->AddLine(BPoint(frame.right, frame.top), 348 BPoint(frame.right + slopeWidth + 2, frame.bottom), darken2); 349 owner->AddLine(BPoint(frame.right, frame.top + 1), 350 BPoint(frame.right + slopeWidth + 1, frame.bottom), darken4); 351 } else { 352 // upper half of right side 353 owner->AddLine(BPoint(frame.right, frame.top), 354 BPoint(frame.right + slopeWidth / 2 + 1, 355 frame.bottom - slopeWidth), darken2); 356 owner->AddLine(BPoint(frame.right, frame.top + 1), 357 BPoint(frame.right + slopeWidth / 2, 358 frame.bottom - slopeWidth), darken4); 359 } 360 361 owner->EndLineArray(); 362 } 363 364 365 void BTab::_ReservedTab1() {} 366 void BTab::_ReservedTab2() {} 367 void BTab::_ReservedTab3() {} 368 void BTab::_ReservedTab4() {} 369 void BTab::_ReservedTab5() {} 370 void BTab::_ReservedTab6() {} 371 void BTab::_ReservedTab7() {} 372 void BTab::_ReservedTab8() {} 373 void BTab::_ReservedTab9() {} 374 void BTab::_ReservedTab10() {} 375 void BTab::_ReservedTab11() {} 376 void BTab::_ReservedTab12() {} 377 378 BTab &BTab::operator=(const BTab &) 379 { 380 // this is private and not functional, but exported 381 return *this; 382 } 383 384 385 // #pragma mark - BTabView 386 387 388 BTabView::BTabView(const char *name, button_width width, uint32 flags) 389 : BView(name, flags) 390 { 391 _InitObject(true, width); 392 } 393 394 395 BTabView::BTabView(BRect frame, const char *name, button_width width, 396 uint32 resizingMode, uint32 flags) 397 : BView(frame, name, resizingMode, flags) 398 { 399 _InitObject(false, width); 400 } 401 402 403 BTabView::~BTabView() 404 { 405 for (int32 i = 0; i < CountTabs(); i++) { 406 delete TabAt(i); 407 } 408 409 delete fTabList; 410 } 411 412 413 BTabView::BTabView(BMessage *archive) 414 : BView(archive), 415 fFocus(-1) 416 { 417 fContainerView = NULL; 418 fTabList = new BList; 419 420 int16 width; 421 422 if (archive->FindInt16("_but_width", &width) == B_OK) 423 fTabWidthSetting = (button_width)width; 424 else 425 fTabWidthSetting = B_WIDTH_AS_USUAL; 426 427 if (archive->FindFloat("_high", &fTabHeight) != B_OK) { 428 font_height fh; 429 GetFontHeight(&fh); 430 fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f; 431 } 432 433 fFocus = -1; 434 435 if (archive->FindInt32("_sel", &fSelection) != B_OK) 436 fSelection = 0; 437 438 if (fContainerView == NULL) 439 fContainerView = ChildAt(0); 440 441 int32 i = 0; 442 BMessage tabMsg; 443 444 while (archive->FindMessage("_l_items", i, &tabMsg) == B_OK) { 445 BArchivable *archivedTab = instantiate_object(&tabMsg); 446 447 if (archivedTab) { 448 BTab *tab = dynamic_cast<BTab *>(archivedTab); 449 450 BMessage viewMsg; 451 if (archive->FindMessage("_view_list", i, &viewMsg) == B_OK) { 452 BArchivable *archivedView = instantiate_object(&viewMsg); 453 if (archivedView) 454 AddTab(dynamic_cast<BView*>(archivedView), tab); 455 } 456 } 457 458 tabMsg.MakeEmpty(); 459 i++; 460 } 461 } 462 463 464 BArchivable * 465 BTabView::Instantiate(BMessage *archive) 466 { 467 if ( validate_instantiation(archive, "BTabView")) 468 return new BTabView(archive); 469 470 return NULL; 471 } 472 473 474 status_t 475 BTabView::Archive(BMessage *archive, bool deep) const 476 { 477 if (CountTabs() > 0) 478 TabAt(Selection())->View()->RemoveSelf(); 479 480 status_t ret = BView::Archive(archive, deep); 481 482 if (ret == B_OK) 483 ret = archive->AddInt16("_but_width", fTabWidthSetting); 484 if (ret == B_OK) 485 ret = archive->AddFloat("_high", fTabHeight); 486 if (ret == B_OK) 487 ret = archive->AddInt32("_sel", fSelection); 488 489 if (ret == B_OK && deep) { 490 for (int32 i = 0; i < CountTabs(); i++) { 491 BMessage tabArchive; 492 BTab *tab = TabAt(i); 493 494 if (!tab) 495 continue; 496 ret = tab->Archive(&tabArchive, true); 497 if (ret == B_OK) 498 ret = archive->AddMessage("_l_items", &tabArchive); 499 500 if (!tab->View()) 501 continue; 502 503 BMessage viewArchive; 504 ret = tab->View()->Archive(&viewArchive, true); 505 if (ret == B_OK) 506 ret = archive->AddMessage("_view_list", &viewArchive); 507 } 508 } 509 510 if (CountTabs() > 0) { 511 if (TabAt(Selection())->View() && ContainerView()) 512 TabAt(Selection())->Select(ContainerView()); 513 } 514 515 return ret; 516 } 517 518 519 status_t 520 BTabView::Perform(perform_code d, void *arg) 521 { 522 return BView::Perform(d, arg); 523 } 524 525 526 void 527 BTabView::AttachedToWindow() 528 { 529 BView::AttachedToWindow(); 530 531 Select(fSelection); 532 } 533 534 535 void 536 BTabView::DetachedFromWindow() 537 { 538 BView::DetachedFromWindow(); 539 } 540 541 542 void 543 BTabView::AllAttached() 544 { 545 BView::AllAttached(); 546 } 547 548 549 void 550 BTabView::AllDetached() 551 { 552 BView::AllDetached(); 553 } 554 555 556 // #pragma mark - 557 558 559 void 560 BTabView::MessageReceived(BMessage *message) 561 { 562 switch (message->what) { 563 case B_GET_PROPERTY: 564 case B_SET_PROPERTY: 565 { 566 BMessage reply(B_REPLY); 567 bool handled = false; 568 569 BMessage specifier; 570 int32 index; 571 int32 form; 572 const char *property; 573 if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) { 574 if (strcmp(property, "Selection") == 0) { 575 if (message->what == B_GET_PROPERTY) { 576 reply.AddInt32("result", fSelection); 577 handled = true; 578 } else { 579 // B_GET_PROPERTY 580 int32 selection; 581 if (message->FindInt32("data", &selection) == B_OK) { 582 Select(selection); 583 reply.AddInt32("error", B_OK); 584 handled = true; 585 } 586 } 587 } 588 } 589 590 if (handled) 591 message->SendReply(&reply); 592 else 593 BView::MessageReceived(message); 594 break; 595 } 596 597 #if 0 598 case B_MOUSE_WHEEL_CHANGED: 599 { 600 float deltaX = 0.0f; 601 float deltaY = 0.0f; 602 message->FindFloat("be:wheel_delta_x", &deltaX); 603 message->FindFloat("be:wheel_delta_y", &deltaY); 604 605 if (deltaX == 0.0f && deltaY == 0.0f) 606 return; 607 608 if (deltaY == 0.0f) 609 deltaY = deltaX; 610 611 int32 selection = Selection(); 612 int32 numTabs = CountTabs(); 613 if (deltaY > 0 && selection < numTabs - 1) { 614 // move to the right tab. 615 Select(Selection() + 1); 616 } else if (deltaY < 0 && selection > 0 && numTabs > 1) { 617 // move to the left tab. 618 Select(selection - 1); 619 } 620 break; 621 } 622 #endif 623 624 default: 625 BView::MessageReceived(message); 626 break; 627 } 628 } 629 630 631 void 632 BTabView::KeyDown(const char *bytes, int32 numBytes) 633 { 634 if (IsHidden()) 635 return; 636 637 switch (bytes[0]) { 638 case B_DOWN_ARROW: 639 case B_LEFT_ARROW: { 640 int32 focus = fFocus - 1; 641 if (focus < 0) 642 focus = CountTabs() - 1; 643 SetFocusTab(focus, true); 644 break; 645 } 646 647 case B_UP_ARROW: 648 case B_RIGHT_ARROW: { 649 int32 focus = fFocus + 1; 650 if (focus >= CountTabs()) 651 focus = 0; 652 SetFocusTab(focus, true); 653 break; 654 } 655 656 case B_RETURN: 657 case B_SPACE: 658 Select(FocusTab()); 659 break; 660 661 default: 662 BView::KeyDown(bytes, numBytes); 663 } 664 } 665 666 667 void 668 BTabView::MouseDown(BPoint point) 669 { 670 if (point.y > fTabHeight) 671 return; 672 673 for (int32 i = 0; i < CountTabs(); i++) { 674 if (TabFrame(i).Contains(point) 675 && i != Selection()) { 676 Select(i); 677 return; 678 } 679 } 680 681 BView::MouseDown(point); 682 } 683 684 685 void 686 BTabView::MouseUp(BPoint point) 687 { 688 BView::MouseUp(point); 689 } 690 691 692 void 693 BTabView::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 694 { 695 BView::MouseMoved(point, transit, message); 696 } 697 698 699 void 700 BTabView::Pulse() 701 { 702 BView::Pulse(); 703 } 704 705 706 void 707 BTabView::Select(int32 index) 708 { 709 if (index < 0 || index >= CountTabs()) 710 index = Selection(); 711 712 BTab *tab = TabAt(Selection()); 713 714 if (tab) 715 tab->Deselect(); 716 717 tab = TabAt(index); 718 if (tab && ContainerView()) { 719 if (index == 0) 720 fTabOffset = 0.0f; 721 tab->Select(ContainerView()); 722 fSelection = index; 723 724 // make the view visible through the layout if there is one 725 BCardLayout* layout 726 = dynamic_cast<BCardLayout*>(fContainerView->GetLayout()); 727 if (layout) 728 layout->SetVisibleItem(index); 729 } 730 731 Invalidate(); 732 733 if (index != 0 && !Bounds().Contains(TabFrame(index))){ 734 if (!Bounds().Contains(TabFrame(index).LeftTop())) 735 fTabOffset += TabFrame(index).left - Bounds().left - 20.0f; 736 else 737 fTabOffset += TabFrame(index).right - Bounds().right + 20.0f; 738 739 Invalidate(); 740 } 741 742 /*MakeFocus(); 743 SetFocusTab(index, true); 744 FocusTab();*/ 745 } 746 747 748 int32 749 BTabView::Selection() const 750 { 751 return fSelection; 752 } 753 754 755 void 756 BTabView::WindowActivated(bool active) 757 { 758 BView::WindowActivated(active); 759 760 if (IsFocus()) 761 Invalidate(); 762 } 763 764 765 void 766 BTabView::MakeFocus(bool focused) 767 { 768 BView::MakeFocus(focused); 769 770 SetFocusTab(Selection(), focused); 771 } 772 773 774 void 775 BTabView::SetFocusTab(int32 tab, bool focused) 776 { 777 if (tab >= CountTabs()) 778 tab = 0; 779 780 if (tab < 0) 781 tab = CountTabs() - 1; 782 783 if (focused) { 784 if (tab == fFocus) 785 return; 786 787 if (fFocus != -1){ 788 if (TabAt (fFocus) != NULL) 789 TabAt(fFocus)->MakeFocus(false); 790 Invalidate(TabFrame(fFocus)); 791 } 792 if (TabAt(tab) != NULL){ 793 TabAt(tab)->MakeFocus(true); 794 Invalidate(TabFrame(tab)); 795 fFocus = tab; 796 } 797 } else if (fFocus != -1) { 798 TabAt(fFocus)->MakeFocus(false); 799 Invalidate(TabFrame(fFocus)); 800 fFocus = -1; 801 } 802 } 803 804 805 int32 806 BTabView::FocusTab() const 807 { 808 return fFocus; 809 } 810 811 812 void 813 BTabView::Draw(BRect updateRect) 814 { 815 if (be_control_look != NULL) { 816 DrawBox(TabFrame(fSelection)); 817 DrawTabs(); 818 } else 819 DrawBox(DrawTabs()); 820 821 if (IsFocus() && fFocus != -1) 822 TabAt(fFocus)->DrawFocusMark(this, TabFrame(fFocus)); 823 } 824 825 826 BRect 827 BTabView::DrawTabs() 828 { 829 if (be_control_look != NULL) { 830 // BRect rect(Bounds()); 831 // rect.bottom = rect.top + fTabHeight; 832 // rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 833 // be_control_look->DrawButtonFrame(this, rect, rect, base); 834 // be_control_look->DrawButtonBackground(this, rect, rect, base); 835 } 836 837 float left = 0; 838 839 for (int32 i = 0; i < CountTabs(); i++) { 840 BRect tabFrame = TabFrame(i); 841 TabAt(i)->DrawTab(this, tabFrame, 842 i == fSelection ? B_TAB_FRONT : (i == 0) ? B_TAB_FIRST : B_TAB_ANY, 843 i + 1 != fSelection); 844 left = tabFrame.right; 845 } 846 847 if (be_control_look != NULL) { 848 BRect frame(Bounds()); 849 if (left < frame.right) { 850 frame.left = left; 851 frame.bottom = fTabHeight; 852 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 853 uint32 borders = BControlLook::B_TOP_BORDER 854 | BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER; 855 if (left == 0) 856 borders |= BControlLook::B_LEFT_BORDER; 857 be_control_look->DrawInactiveTab(this, frame, frame, base, 0, 858 borders); 859 } 860 } 861 862 if (fSelection < CountTabs()) 863 return TabFrame(fSelection); 864 865 return BRect(); 866 } 867 868 869 void 870 BTabView::DrawBox(BRect selTabRect) 871 { 872 if (be_control_look != NULL) { 873 BRect rect(Bounds()); 874 rect.top = selTabRect.bottom; 875 876 // BRegion clipping(Bounds()); 877 // selTabRect.left += 2; 878 // selTabRect.right -= 2; 879 // clipping.Exclude(selTabRect); 880 // ConstrainClippingRegion(&clipping); 881 882 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 883 be_control_look->DrawGroupFrame(this, rect, rect, base); 884 885 // ConstrainClippingRegion(NULL); 886 887 return; 888 } 889 890 BRect rect = Bounds(); 891 BRect lastTabRect = TabFrame(CountTabs() - 1); 892 893 rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR); 894 rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT); 895 rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT); 896 rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT); 897 rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT); 898 899 BeginLineArray(12); 900 901 int32 offset = (int32)ceilf(selTabRect.Height() / 2.0); 902 903 // outer lines 904 AddLine(BPoint(rect.left, rect.bottom - 1), 905 BPoint(rect.left, selTabRect.bottom), darken2); 906 if (selTabRect.left >= rect.left + 1) 907 AddLine(BPoint(rect.left + 1, selTabRect.bottom), 908 BPoint(selTabRect.left, selTabRect.bottom), darken2); 909 if (lastTabRect.right + offset + 1 <= rect.right - 1) 910 AddLine(BPoint(lastTabRect.right + offset + 1, selTabRect.bottom), 911 BPoint(rect.right - 1, selTabRect.bottom), darken2); 912 AddLine(BPoint(rect.right, selTabRect.bottom + 2), 913 BPoint(rect.right, rect.bottom), darken2); 914 AddLine(BPoint(rect.right - 1, rect.bottom), 915 BPoint(rect.left + 2, rect.bottom), darken2); 916 917 // inner lines 918 rect.InsetBy(1, 1); 919 selTabRect.bottom += 1; 920 921 AddLine(BPoint(rect.left, rect.bottom - 2), 922 BPoint(rect.left, selTabRect.bottom), lightenMax); 923 if (selTabRect.left >= rect.left + 1) 924 AddLine(BPoint(rect.left + 1, selTabRect.bottom), 925 BPoint(selTabRect.left, selTabRect.bottom), lightenMax); 926 if (selTabRect.right + offset + 1 <= rect.right - 2) 927 AddLine(BPoint(selTabRect.right + offset + 1, selTabRect.bottom), 928 BPoint(rect.right - 2, selTabRect.bottom), lightenMax); 929 AddLine(BPoint(rect.right, selTabRect.bottom), 930 BPoint(rect.right, rect.bottom), darken4); 931 AddLine(BPoint(rect.right - 1, rect.bottom), 932 BPoint(rect.left, rect.bottom), darken4); 933 934 // soft inner bevel at right/bottom 935 rect.right--; 936 rect.bottom--; 937 938 AddLine(BPoint(rect.right, selTabRect.bottom + 1), 939 BPoint(rect.right, rect.bottom), darken1); 940 AddLine(BPoint(rect.right - 1, rect.bottom), 941 BPoint(rect.left + 1, rect.bottom), darken1); 942 943 EndLineArray(); 944 } 945 946 #define X_OFFSET 0.0f 947 948 BRect 949 BTabView::TabFrame(int32 tab_index) const 950 { 951 if (be_control_look != NULL) { 952 float width = 100.0; 953 float height = fTabHeight;; 954 switch (fTabWidthSetting) { 955 case B_WIDTH_FROM_LABEL: 956 { 957 float x = 0.0; 958 for (int32 i = 0; i < tab_index; i++){ 959 x += StringWidth(TabAt(i)->Label()) + 20.0; 960 } 961 962 return BRect(x, 0.0, 963 x + StringWidth(TabAt(tab_index)->Label()) + 20.0, 964 height); 965 } 966 967 case B_WIDTH_FROM_WIDEST: 968 width = 0.0; 969 for (int32 i = 0; i < CountTabs(); i++) { 970 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0; 971 if (tabWidth > width) 972 width = tabWidth; 973 } 974 // fall through 975 976 case B_WIDTH_AS_USUAL: 977 default: 978 return BRect(tab_index * width, 0.0, 979 tab_index * width + width, height); 980 } 981 } 982 983 // TODO: fix to remove "offset" in DrawTab and DrawLabel ... 984 switch (fTabWidthSetting) { 985 case B_WIDTH_FROM_LABEL: 986 { 987 float x = 6.0f; 988 for (int32 i = 0; i < tab_index; i++){ 989 x += StringWidth(TabAt(i)->Label()) + 20.0f; 990 } 991 992 return BRect(x - fTabOffset, 0.0f, 993 x - fTabOffset + StringWidth(TabAt(tab_index)->Label()) + 20.0f , fTabHeight); 994 995 996 /*float x = X_OFFSET; 997 for (int32 i = 0; i < tab_index; i++) 998 x += StringWidth(TabAt(i)->Label()) + 20.0f; 999 1000 return BRect(x, 0.0f, 1001 x + StringWidth(TabAt(tab_index)->Label()) + 20.0f, fTabHeight);*/ 1002 } 1003 1004 case B_WIDTH_FROM_WIDEST: 1005 { 1006 float width = 0.0f; 1007 1008 for (int32 i = 0; i < CountTabs(); i++) { 1009 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f; 1010 if (tabWidth > width) 1011 width = tabWidth; 1012 } 1013 return BRect((6.0f + tab_index * width) - fTabOffset, 0.0f, 1014 (6.0f + tab_index * width + width) - fTabOffset, fTabHeight); 1015 /*float width = 0.0f; 1016 1017 for (int32 i = 0; i < CountTabs(); i++) { 1018 float tabWidth = StringWidth(TabAt(i)->Label()) + 20.0f; 1019 1020 if (tabWidth > width) 1021 width = tabWidth; 1022 } 1023 1024 return BRect(X_OFFSET + tab_index * width, 0.0f, 1025 X_OFFSET + tab_index * width + width, fTabHeight);*/ 1026 } 1027 1028 case B_WIDTH_AS_USUAL: 1029 default: 1030 return BRect((6.0f + tab_index * 100.0f) - fTabOffset, 0.0f, 1031 (6.0f + tab_index * 100.0f + 100.0f) - fTabOffset, fTabHeight); 1032 /*return BRect(X_OFFSET + tab_index * 100.0f, 0.0f, 1033 X_OFFSET + tab_index * 100.0f + 100.0f, fTabHeight);*/ 1034 } 1035 } 1036 1037 1038 void 1039 BTabView::SetFlags(uint32 flags) 1040 { 1041 BView::SetFlags(flags); 1042 } 1043 1044 1045 void 1046 BTabView::SetResizingMode(uint32 mode) 1047 { 1048 BView::SetResizingMode(mode); 1049 } 1050 1051 1052 // #pragma mark - 1053 1054 1055 void 1056 BTabView::ResizeToPreferred() 1057 { 1058 BView::ResizeToPreferred(); 1059 } 1060 1061 1062 void 1063 BTabView::GetPreferredSize(float *width, float *height) 1064 { 1065 BView::GetPreferredSize(width, height); 1066 } 1067 1068 1069 BSize 1070 BTabView::MinSize() 1071 { 1072 BSize size(_TabsMinSize()); 1073 BSize containerSize = fContainerView->MinSize(); 1074 if (containerSize.width > size.width) 1075 size.width = containerSize.width; 1076 size.height += containerSize.height; 1077 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 1078 } 1079 1080 1081 BSize 1082 BTabView::MaxSize() 1083 { 1084 BSize size(_TabsMinSize()); 1085 BSize containerSize = fContainerView->MaxSize(); 1086 if (containerSize.width > size.width) 1087 size.width = containerSize.width; 1088 size.height += containerSize.height; 1089 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 1090 } 1091 1092 1093 BSize 1094 BTabView::PreferredSize() 1095 { 1096 BSize size(_TabsMinSize()); 1097 BSize containerSize = fContainerView->PreferredSize(); 1098 if (containerSize.width > size.width) 1099 size.width = containerSize.width; 1100 size.height += containerSize.height; 1101 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); 1102 } 1103 1104 1105 void 1106 BTabView::FrameMoved(BPoint newLocation) 1107 { 1108 BView::FrameMoved(newLocation); 1109 } 1110 1111 1112 void 1113 BTabView::FrameResized(float width,float height) 1114 { 1115 BView::FrameResized(width, height); 1116 } 1117 1118 1119 // #pragma mark - 1120 1121 1122 BHandler * 1123 BTabView::ResolveSpecifier(BMessage *message, int32 index, 1124 BMessage *specifier, int32 what, const char *property) 1125 { 1126 BPropertyInfo propInfo(sPropertyList); 1127 1128 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK) 1129 return this; 1130 1131 return BView::ResolveSpecifier(message, index, specifier, what, 1132 property); 1133 } 1134 1135 1136 status_t 1137 BTabView::GetSupportedSuites(BMessage *message) 1138 { 1139 message->AddString("suites", "suite/vnd.Be-tab-view"); 1140 1141 BPropertyInfo propInfo(sPropertyList); 1142 message->AddFlat("messages", &propInfo); 1143 1144 return BView::GetSupportedSuites(message); 1145 } 1146 1147 1148 // #pragma mark - 1149 1150 1151 void 1152 BTabView::AddTab(BView *target, BTab *tab) 1153 { 1154 if (tab == NULL) 1155 tab = new BTab(target); 1156 else 1157 tab->SetView(target); 1158 1159 if (fContainerView->GetLayout()) 1160 fContainerView->GetLayout()->AddView(CountTabs(), target); 1161 1162 fTabList->AddItem(tab); 1163 1164 // When we haven't had a any tabs before, but are already attached to the 1165 // window, select this one. 1166 if (CountTabs() == 1 && Window() != NULL) 1167 Select(0); 1168 } 1169 1170 1171 BTab * 1172 BTabView::RemoveTab(int32 index) 1173 { 1174 if (index < 0 || index >= CountTabs()) 1175 return NULL; 1176 1177 BTab *tab = (BTab *)fTabList->RemoveItem(index); 1178 if (tab == NULL) 1179 return NULL; 1180 1181 tab->Deselect(); 1182 1183 if (index <= fSelection && fSelection != 0) 1184 fSelection--; 1185 1186 if (CountTabs() == 0) 1187 fFocus = -1; 1188 else 1189 Select(fSelection); 1190 1191 if (fFocus == CountTabs() - 1 || CountTabs() == 0) 1192 SetFocusTab(fFocus, false); 1193 else 1194 SetFocusTab(fFocus, true); 1195 1196 if (fContainerView->GetLayout()) 1197 fContainerView->GetLayout()->RemoveItem(index); 1198 1199 return tab; 1200 } 1201 1202 1203 BTab * 1204 BTabView::TabAt(int32 index) const 1205 { 1206 return (BTab *)fTabList->ItemAt(index); 1207 } 1208 1209 1210 void 1211 BTabView::SetTabWidth(button_width width) 1212 { 1213 fTabWidthSetting = width; 1214 1215 Invalidate(); 1216 } 1217 1218 1219 button_width 1220 BTabView::TabWidth() const 1221 { 1222 return fTabWidthSetting; 1223 } 1224 1225 1226 void 1227 BTabView::SetTabHeight(float height) 1228 { 1229 if (fTabHeight == height) 1230 return; 1231 1232 fContainerView->MoveBy(0.0f, height - fTabHeight); 1233 fContainerView->ResizeBy(0.0f, height - fTabHeight); 1234 1235 fTabHeight = height; 1236 1237 Invalidate(); 1238 } 1239 1240 1241 float 1242 BTabView::TabHeight() const 1243 { 1244 return fTabHeight; 1245 } 1246 1247 1248 BView * 1249 BTabView::ContainerView() const 1250 { 1251 return fContainerView; 1252 } 1253 1254 1255 int32 1256 BTabView::CountTabs() const 1257 { 1258 return fTabList->CountItems(); 1259 } 1260 1261 1262 BView * 1263 BTabView::ViewForTab(int32 tabIndex) const 1264 { 1265 BTab *tab = TabAt(tabIndex); 1266 if (tab) 1267 return tab->View(); 1268 1269 return NULL; 1270 } 1271 1272 1273 void 1274 BTabView::_InitObject(bool layouted, button_width width) 1275 { 1276 if (!be_control_look) 1277 SetFont(be_bold_font); 1278 1279 fTabList = new BList; 1280 1281 fTabWidthSetting = width; 1282 fSelection = 0; 1283 fFocus = -1; 1284 fTabOffset = 0.0f; 1285 1286 rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR); 1287 1288 SetViewColor(color); 1289 SetLowColor(color); 1290 1291 font_height fh; 1292 GetFontHeight(&fh); 1293 fTabHeight = fh.ascent + fh.descent + fh.leading + 8.0f; 1294 1295 if (layouted) { 1296 BGroupLayout* layout = new(std::nothrow) BGroupLayout(B_HORIZONTAL); 1297 if (layout) { 1298 layout->SetInsets(3.0, 3.0 + TabHeight() - 1, 3.0, 3.0); 1299 SetLayout(layout); 1300 } 1301 1302 fContainerView = new BView("view container", B_WILL_DRAW); 1303 fContainerView->SetLayout(new(std::nothrow) BCardLayout()); 1304 } else { 1305 BRect bounds = Bounds(); 1306 1307 bounds.top += TabHeight(); 1308 bounds.InsetBy(3.0f, 3.0f); 1309 1310 fContainerView = new BView(bounds, "view container", B_FOLLOW_ALL, 1311 B_WILL_DRAW); 1312 } 1313 1314 fContainerView->SetViewColor(color); 1315 fContainerView->SetLowColor(color); 1316 1317 AddChild(fContainerView); 1318 } 1319 1320 1321 BSize 1322 BTabView::_TabsMinSize() const 1323 { 1324 BSize size(0.0f, TabHeight() + 6.0f); 1325 int32 count = min_c(2, CountTabs()); 1326 for (int32 i = 0; i < count; i++) { 1327 BRect frame = TabFrame(i); 1328 size.width += frame.Width(); 1329 } 1330 1331 if (count < CountTabs()) { 1332 // TODO: Add size for yet to be implemented buttons that allow 1333 // "scrolling" the displayed tabs left/right. 1334 } 1335 1336 return size; 1337 } 1338 1339 1340 // #pragma mark - FBC and forbidden 1341 1342 1343 void BTabView::_ReservedTabView1() {} 1344 void BTabView::_ReservedTabView2() {} 1345 void BTabView::_ReservedTabView3() {} 1346 void BTabView::_ReservedTabView4() {} 1347 void BTabView::_ReservedTabView5() {} 1348 void BTabView::_ReservedTabView6() {} 1349 void BTabView::_ReservedTabView7() {} 1350 void BTabView::_ReservedTabView8() {} 1351 void BTabView::_ReservedTabView9() {} 1352 void BTabView::_ReservedTabView10() {} 1353 void BTabView::_ReservedTabView11() {} 1354 void BTabView::_ReservedTabView12() {} 1355 1356 1357 BTabView::BTabView(const BTabView& tabView) 1358 : BView(tabView) 1359 { 1360 // this is private and not functional, but exported 1361 } 1362 1363 1364 BTabView& 1365 BTabView::operator=(const BTabView&) 1366 { 1367 // this is private and not functional, but exported 1368 return *this; 1369 } 1370