1/* 2 * Copyright 2001-2011, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Stephan Aßmus <superstippi@gmx.de> 8 * Philippe Saint-Pierre, stpere@gmail.com 9 * Ryan Leavengood <leavengood@gmail.com> 10 * Clemens Zeidler <haiku@clemens-zeidler.de> 11 * Ingo Weinhold <ingo_weinhold@gmx.de> 12 */ 13 14 15/*! Default and fallback decorator for the app_server - the yellow tabs */ 16 17 18#include "DefaultDecorator.h" 19 20#include <algorithm> 21#include <cmath> 22#include <new> 23#include <stdio.h> 24 25#include <Autolock.h> 26#include <Debug.h> 27#include <GradientLinear.h> 28#include <Rect.h> 29#include <View.h> 30 31#include <WindowPrivate.h> 32 33#include "BitmapDrawingEngine.h" 34#include "DesktopSettings.h" 35#include "DrawingEngine.h" 36#include "DrawState.h" 37#include "FontManager.h" 38#include "PatternHandler.h" 39#include "ServerBitmap.h" 40 41 42//#define DEBUG_DECORATOR 43#ifdef DEBUG_DECORATOR 44# define STRACE(x) printf x 45#else 46# define STRACE(x) ; 47#endif 48 49 50DefaultDecorator::Tab::Tab() 51 : 52 tabOffset(0), 53 tabLocation(0.0), 54 isHighlighted(false) 55{ 56 closeBitmaps[0] = closeBitmaps[1] = closeBitmaps[2] = closeBitmaps[3] 57 = zoomBitmaps[0] = zoomBitmaps[1] = zoomBitmaps[2] = zoomBitmaps[3] 58 = NULL; 59} 60 61 62static const float kBorderResizeLength = 22.0; 63static const float kResizeKnobSize = 18.0; 64 65 66static inline uint8 67blend_color_value(uint8 a, uint8 b, float position) 68{ 69 int16 delta = (int16)b - a; 70 int32 value = a + (int32)(position * delta); 71 if (value > 255) 72 return 255; 73 if (value < 0) 74 return 0; 75 76 return (uint8)value; 77} 78 79 80// #pragma mark - 81 82 83const rgb_color DefaultDecorator::kFrameColors[4] = { 84 { 152, 152, 152, 255 }, 85 { 240, 240, 240, 255 }, 86 { 152, 152, 152, 255 }, 87 { 108, 108, 108, 255 } 88}; 89 90 91// TODO: get rid of DesktopSettings here, and introduce private accessor 92// methods to the Decorator base class 93DefaultDecorator::DefaultDecorator(DesktopSettings& settings, BRect rect) 94 : 95 Decorator(settings, rect), 96 // focus color constants 97 kFocusFrameColor(settings.UIColor(B_WINDOW_BORDER_COLOR)), 98 kFocusTabColor(settings.UIColor(B_WINDOW_TAB_COLOR)), 99 kFocusTabColorLight(tint_color(kFocusTabColor, 100 (B_LIGHTEN_MAX_TINT + B_LIGHTEN_2_TINT) / 2)), 101 kFocusTabColorBevel(tint_color(kFocusTabColor, B_LIGHTEN_2_TINT)), 102 kFocusTabColorShadow(tint_color(kFocusTabColor, 103 (B_DARKEN_1_TINT + B_NO_TINT) / 2)), 104 kFocusTextColor(settings.UIColor(B_WINDOW_TEXT_COLOR)), 105 // non-focus color constants 106 kNonFocusFrameColor(settings.UIColor(B_WINDOW_INACTIVE_BORDER_COLOR)), 107 kNonFocusTabColor(settings.UIColor(B_WINDOW_INACTIVE_TAB_COLOR)), 108 kNonFocusTabColorLight(tint_color(kNonFocusTabColor, 109 (B_LIGHTEN_MAX_TINT + B_LIGHTEN_2_TINT) / 2)), 110 kNonFocusTabColorBevel(tint_color(kNonFocusTabColor, B_LIGHTEN_2_TINT)), 111 kNonFocusTabColorShadow(tint_color(kNonFocusTabColor, 112 (B_DARKEN_1_TINT + B_NO_TINT) / 2)), 113 kNonFocusTextColor(settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR)), 114 115 fOldMovingTab(0, 0, -1, -1) 116{ 117 // TODO: If the decorator was created with a frame too small, it should 118 // resize itself! 119 120 STRACE(("DefaultDecorator:\n")); 121 STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n", 122 rect.left, rect.top, rect.right, rect.bottom)); 123} 124 125 126DefaultDecorator::~DefaultDecorator() 127{ 128 STRACE(("DefaultDecorator: ~DefaultDecorator()\n")); 129} 130 131 132float 133DefaultDecorator::TabLocation(int32 tab) const 134{ 135 DefaultDecorator::Tab* decoratorTab = _TabAt(tab); 136 if (decoratorTab == NULL) 137 return 0.0f; 138 return (float)decoratorTab->tabOffset; 139} 140 141 142bool 143DefaultDecorator::GetSettings(BMessage* settings) const 144{ 145 if (!fTitleBarRect.IsValid()) 146 return false; 147 148 if (settings->AddRect("tab frame", fTitleBarRect) != B_OK) 149 return false; 150 151 if (settings->AddFloat("border width", fBorderWidth) != B_OK) 152 return false; 153 154 // TODO only add the location of the tab of the window who requested the 155 // settings 156 for (int32 i = 0; i < fTabList.CountItems(); i++) { 157 DefaultDecorator::Tab* tab = _TabAt(i); 158 if (settings->AddFloat("tab location", (float)tab->tabOffset) != B_OK) 159 return false; 160 } 161 return true; 162} 163 164 165// #pragma mark - 166 167 168void 169DefaultDecorator::Draw(BRect update) 170{ 171 STRACE(("DefaultDecorator: Draw(%.1f,%.1f,%.1f,%.1f)\n", 172 update.left, update.top, update.right, update.bottom)); 173 174 // We need to draw a few things: the tab, the resize knob, the borders, 175 // and the buttons 176 fDrawingEngine->SetDrawState(&fDrawState); 177 178 _DrawFrame(update); 179 _DrawTabs(update); 180} 181 182 183void 184DefaultDecorator::Draw() 185{ 186 // Easy way to draw everything - no worries about drawing only certain 187 // things 188 fDrawingEngine->SetDrawState(&fDrawState); 189 190 _DrawFrame(BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom())); 191 _DrawTabs(fTitleBarRect); 192} 193 194 195void 196DefaultDecorator::GetSizeLimits(int32* minWidth, int32* minHeight, 197 int32* maxWidth, int32* maxHeight) const 198{ 199 float minTabSize = 0; 200 if (CountTabs() > 0) 201 minTabSize = _TabAt(0)->minTabSize; 202 if (fTitleBarRect.IsValid()) { 203 *minWidth = (int32)roundf(max_c(*minWidth, 204 minTabSize - 2 * fBorderWidth)); 205 } 206 if (fResizeRect.IsValid()) { 207 *minHeight = (int32)roundf(max_c(*minHeight, 208 fResizeRect.Height() - fBorderWidth)); 209 } 210} 211 212 213Decorator::Region 214DefaultDecorator::RegionAt(BPoint where, int32& tab) const 215{ 216 // Let the base class version identify hits of the buttons and the tab. 217 Region region = Decorator::RegionAt(where, tab); 218 if (region != REGION_NONE) 219 return region; 220 221 // check the resize corner 222 if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK && fResizeRect.Contains(where)) 223 return REGION_RIGHT_BOTTOM_CORNER; 224 225 // hit-test the borders 226 if (fLeftBorder.Contains(where)) 227 return REGION_LEFT_BORDER; 228 if (fTopBorder.Contains(where)) 229 return REGION_TOP_BORDER; 230 231 // Part of the bottom and right borders may be a resize-region, so we have 232 // to check explicitly, if it has been it. 233 if (fRightBorder.Contains(where)) 234 region = REGION_RIGHT_BORDER; 235 else if (fBottomBorder.Contains(where)) 236 region = REGION_BOTTOM_BORDER; 237 else 238 return REGION_NONE; 239 240 // check resize area 241 if ((fTopTab->flags & B_NOT_RESIZABLE) == 0 242 && (fTopTab->look == B_TITLED_WINDOW_LOOK 243 || fTopTab->look == B_FLOATING_WINDOW_LOOK 244 || fTopTab->look == B_MODAL_WINDOW_LOOK 245 || fTopTab->look == kLeftTitledWindowLook)) { 246 BRect resizeRect(BPoint(fBottomBorder.right - kBorderResizeLength, 247 fBottomBorder.bottom - kBorderResizeLength), 248 fBottomBorder.RightBottom()); 249 if (resizeRect.Contains(where)) 250 return REGION_RIGHT_BOTTOM_CORNER; 251 } 252 253 return region; 254} 255 256 257bool 258DefaultDecorator::SetRegionHighlight(Region region, uint8 highlight, 259 BRegion* dirty, int32 tabIndex) 260{ 261 DefaultDecorator::Tab* tab = _TabAt(tabIndex); 262 if (tab != NULL) { 263 tab->isHighlighted = highlight != 0; 264 // Invalidate the bitmap caches for the close/zoom button, when the 265 // highlight changes. 266 switch (region) { 267 case REGION_CLOSE_BUTTON: 268 if (highlight != RegionHighlight(region)) 269 memset(&tab->closeBitmaps, 0, sizeof(tab->closeBitmaps)); 270 break; 271 case REGION_ZOOM_BUTTON: 272 if (highlight != RegionHighlight(region)) 273 memset(&tab->zoomBitmaps, 0, sizeof(tab->zoomBitmaps)); 274 break; 275 default: 276 break; 277 } 278 } 279 280 return Decorator::SetRegionHighlight(region, highlight, dirty, tabIndex); 281} 282 283 284void 285DefaultDecorator::ExtendDirtyRegion(Region region, BRegion& dirty) 286{ 287 switch (region) { 288 case REGION_TAB: 289 dirty.Include(fTitleBarRect); 290 break; 291 292 case REGION_CLOSE_BUTTON: 293 if ((fTopTab->flags & B_NOT_CLOSABLE) == 0) 294 for (int32 i = 0; i < fTabList.CountItems(); i++) 295 dirty.Include(fTabList.ItemAt(i)->closeRect); 296 break; 297 298 case REGION_ZOOM_BUTTON: 299 if ((fTopTab->flags & B_NOT_ZOOMABLE) == 0) 300 for (int32 i = 0; i < fTabList.CountItems(); i++) 301 dirty.Include(fTabList.ItemAt(i)->zoomRect); 302 break; 303 304 case REGION_LEFT_BORDER: 305 if (fLeftBorder.IsValid()) { 306 // fLeftBorder doesn't include the corners, so we have to add 307 // them manually. 308 BRect rect(fLeftBorder); 309 rect.top = fTopBorder.top; 310 rect.bottom = fBottomBorder.bottom; 311 dirty.Include(rect); 312 } 313 break; 314 315 case REGION_RIGHT_BORDER: 316 if (fRightBorder.IsValid()) { 317 // fRightBorder doesn't include the corners, so we have to add 318 // them manually. 319 BRect rect(fRightBorder); 320 rect.top = fTopBorder.top; 321 rect.bottom = fBottomBorder.bottom; 322 dirty.Include(rect); 323 } 324 break; 325 326 case REGION_TOP_BORDER: 327 dirty.Include(fTopBorder); 328 break; 329 330 case REGION_BOTTOM_BORDER: 331 dirty.Include(fBottomBorder); 332 break; 333 334 case REGION_RIGHT_BOTTOM_CORNER: 335 if ((fTopTab->flags & B_NOT_RESIZABLE) == 0) 336 dirty.Include(fResizeRect); 337 break; 338 339 default: 340 break; 341 } 342} 343 344 345float 346DefaultDecorator::BorderWidth() 347{ 348 return fBorderWidth; 349} 350 351 352float 353DefaultDecorator::TabHeight() 354{ 355 if (fTitleBarRect.IsValid()) 356 return fTitleBarRect.Height(); 357 return BorderWidth(); 358} 359 360 361void 362DefaultDecorator::_DoLayout() 363{ 364 STRACE(("DefaultDecorator: Do Layout\n")); 365 // Here we determine the size of every rectangle that we use 366 // internally when we are given the size of the client rectangle. 367 368 bool hasTab = false; 369 370 switch ((int)fTopTab->look) { 371 case B_MODAL_WINDOW_LOOK: 372 fBorderWidth = 5; 373 break; 374 375 case B_TITLED_WINDOW_LOOK: 376 case B_DOCUMENT_WINDOW_LOOK: 377 hasTab = true; 378 fBorderWidth = 5; 379 break; 380 case B_FLOATING_WINDOW_LOOK: 381 case kLeftTitledWindowLook: 382 hasTab = true; 383 fBorderWidth = 3; 384 break; 385 386 case B_BORDERED_WINDOW_LOOK: 387 fBorderWidth = 1; 388 break; 389 390 default: 391 fBorderWidth = 0; 392 } 393 394 // calculate left/top/right/bottom borders 395 if (fBorderWidth > 0) { 396 // NOTE: no overlapping, the left and right border rects 397 // don't include the corners! 398 fLeftBorder.Set(fFrame.left - fBorderWidth, fFrame.top, 399 fFrame.left - 1, fFrame.bottom); 400 401 fRightBorder.Set(fFrame.right + 1, fFrame.top , 402 fFrame.right + fBorderWidth, fFrame.bottom); 403 404 fTopBorder.Set(fFrame.left - fBorderWidth, fFrame.top - fBorderWidth, 405 fFrame.right + fBorderWidth, fFrame.top - 1); 406 407 fBottomBorder.Set(fFrame.left - fBorderWidth, fFrame.bottom + 1, 408 fFrame.right + fBorderWidth, fFrame.bottom + fBorderWidth); 409 } else { 410 // no border 411 fLeftBorder.Set(0.0, 0.0, -1.0, -1.0); 412 fRightBorder.Set(0.0, 0.0, -1.0, -1.0); 413 fTopBorder.Set(0.0, 0.0, -1.0, -1.0); 414 fBottomBorder.Set(0.0, 0.0, -1.0, -1.0); 415 } 416 417 // calculate resize rect 418 if (fBorderWidth > 1) { 419 fResizeRect.Set(fBottomBorder.right - kResizeKnobSize, 420 fBottomBorder.bottom - kResizeKnobSize, fBottomBorder.right, 421 fBottomBorder.bottom); 422 } else { 423 // no border or one pixel border (menus and such) 424 fResizeRect.Set(0, 0, -1, -1); 425 } 426 427 if (hasTab) { 428 _DoTabLayout(); 429 return; 430 } else { 431 // no tab 432 for (int32 i = 0; i < fTabList.CountItems(); i++) { 433 Decorator::Tab* tab = fTabList.ItemAt(i); 434 tab->tabRect.Set(0.0, 0.0, -1.0, -1.0); 435 } 436 fTabsRegion.MakeEmpty(); 437 fTitleBarRect.Set(0.0, 0.0, -1.0, -1.0); 438 } 439} 440 441 442void 443DefaultDecorator::_DoTabLayout() 444{ 445 float tabOffset = 0; 446 if (fTabList.CountItems() == 1) { 447 float tabSize; 448 tabOffset = _SingleTabOffsetAndSize(tabSize); 449 } 450 451 float sumTabWidth = 0; 452 // calculate our tab rect 453 for (int32 i = 0; i < fTabList.CountItems(); i++) { 454 DefaultDecorator::Tab* tab = _TabAt(i); 455 456 BRect& tabRect = tab->tabRect; 457 // distance from one item of the tab bar to another. 458 // In this case the text and close/zoom rects 459 tab->textOffset = _DefaultTextOffset(); 460 461 font_height fontHeight; 462 fDrawState.Font().GetHeight(fontHeight); 463 464 if (tab->look != kLeftTitledWindowLook) { 465 tabRect.Set(fFrame.left - fBorderWidth, 466 fFrame.top - fBorderWidth 467 - ceilf(fontHeight.ascent + fontHeight.descent + 7.0), 468 ((fFrame.right - fFrame.left) < 35.0 ? 469 fFrame.left + 35.0 : fFrame.right) + fBorderWidth, 470 fFrame.top - fBorderWidth); 471 } else { 472 tabRect.Set(fFrame.left - fBorderWidth 473 - ceilf(fontHeight.ascent + fontHeight.descent + 5.0), 474 fFrame.top - fBorderWidth, fFrame.left - fBorderWidth, 475 fFrame.bottom + fBorderWidth); 476 } 477 478 // format tab rect for a floating window - make the rect smaller 479 if (tab->look == B_FLOATING_WINDOW_LOOK) { 480 tabRect.InsetBy(0, 2); 481 tabRect.OffsetBy(0, 2); 482 } 483 484 float offset; 485 float size; 486 float inset; 487 _GetButtonSizeAndOffset(tabRect, &offset, &size, &inset); 488 489 // tab->minTabSize contains just the room for the buttons 490 tab->minTabSize = inset * 2 + tab->textOffset; 491 if ((tab->flags & B_NOT_CLOSABLE) == 0) 492 tab->minTabSize += offset + size; 493 if ((tab->flags & B_NOT_ZOOMABLE) == 0) 494 tab->minTabSize += offset + size; 495 496 // tab->maxTabSize contains tab->minTabSize + the width required for the 497 // title 498 tab->maxTabSize = fDrawingEngine 499 ? ceilf(fDrawingEngine->StringWidth(Title(tab), strlen(Title(tab)), 500 fDrawState.Font())) : 0.0; 501 if (tab->maxTabSize > 0.0) 502 tab->maxTabSize += tab->textOffset; 503 tab->maxTabSize += tab->minTabSize; 504 505 float tabSize = (tab->look != kLeftTitledWindowLook 506 ? fFrame.Width() : fFrame.Height()) + fBorderWidth * 2; 507 if (tabSize < tab->minTabSize) 508 tabSize = tab->minTabSize; 509 if (tabSize > tab->maxTabSize) 510 tabSize = tab->maxTabSize; 511 512 // layout buttons and truncate text 513 if (tab->look != kLeftTitledWindowLook) 514 tabRect.right = tabRect.left + tabSize; 515 else 516 tabRect.bottom = tabRect.top + tabSize; 517 518 // make sure fTabOffset is within limits and apply it to 519 // the tabRect 520 tab->tabOffset = (uint32)tabOffset; 521 if (tab->tabLocation != 0.0 && fTabList.CountItems() == 1 522 && tab->tabOffset > (fRightBorder.right - fLeftBorder.left 523 - tabRect.Width())) { 524 tab->tabOffset = uint32(fRightBorder.right - fLeftBorder.left 525 - tabRect.Width()); 526 } 527 tabRect.OffsetBy(tab->tabOffset, 0); 528 tabOffset += tabRect.Width(); 529 530 sumTabWidth += tabRect.Width(); 531 } 532 533 float windowWidth = fFrame.Width() + 2 * fBorderWidth; 534 if (CountTabs() > 1 && sumTabWidth > windowWidth) 535 _DistributeTabSize(sumTabWidth - windowWidth); 536 537 // finally, layout the buttons and text within the tab rect 538 for (int32 i = 0; i < fTabList.CountItems(); i++) { 539 Decorator::Tab* tab = fTabList.ItemAt(i); 540 541 if (i == 0) 542 fTitleBarRect = tab->tabRect; 543 else 544 fTitleBarRect = fTitleBarRect | tab->tabRect; 545 546 _LayoutTabItems(tab, tab->tabRect); 547 } 548 fTabsRegion = fTitleBarRect; 549} 550 551 552static bool 553int_equal(float x, float y) 554{ 555 return abs(x - y) <= 1; 556} 557 558 559void 560DefaultDecorator::_DistributeTabSize(float delta) 561{ 562 ASSERT(CountTabs() > 1); 563 564 float maxTabSize = 0; 565 float secMaxTabSize = 0; 566 int32 nTabsWithMaxSize = 0; 567 for (int32 i = 0; i < fTabList.CountItems(); i++) { 568 Decorator::Tab* tab = fTabList.ItemAt(i); 569 float tabWidth = tab->tabRect.Width(); 570 if (int_equal(maxTabSize, tabWidth)) { 571 nTabsWithMaxSize++; 572 continue; 573 } 574 if (maxTabSize < tabWidth) { 575 secMaxTabSize = maxTabSize; 576 maxTabSize = tabWidth; 577 nTabsWithMaxSize = 1; 578 } else if (secMaxTabSize <= tabWidth) 579 secMaxTabSize = tabWidth; 580 } 581 582 float minus = ceil(std::min(maxTabSize - secMaxTabSize, delta)); 583 delta -= minus; 584 minus /= nTabsWithMaxSize; 585 586 Decorator::Tab* prevTab = NULL; 587 for (int32 i = 0; i < fTabList.CountItems(); i++) { 588 Decorator::Tab* tab = fTabList.ItemAt(i); 589 if (int_equal(maxTabSize, tab->tabRect.Width())) 590 tab->tabRect.right -= minus; 591 592 if (prevTab) { 593 tab->tabRect.OffsetBy(prevTab->tabRect.right - tab->tabRect.left, 594 0); 595 } 596 597 prevTab = tab; 598 } 599 600 if (delta > 0) { 601 _DistributeTabSize(delta); 602 return; 603 } 604 605 // done 606 prevTab->tabRect.right = floor(fFrame.right + fBorderWidth); 607 608 for (int32 i = 0; i < fTabList.CountItems(); i++) { 609 DefaultDecorator::Tab* tab = _TabAt(i); 610 tab->tabOffset = uint32(tab->tabRect.left - fLeftBorder.left); 611 } 612} 613 614 615Decorator::Tab* 616DefaultDecorator::_AllocateNewTab() 617{ 618 Decorator::Tab* tab = new(std::nothrow) DefaultDecorator::Tab; 619 if (tab == NULL) 620 return NULL; 621 // Set appropriate colors based on the current focus value. In this case, 622 // each decorator defaults to not having the focus. 623 _SetFocus(tab); 624 return tab; 625} 626 627 628DefaultDecorator::Tab* 629DefaultDecorator::_TabAt(int32 index) const 630{ 631 return static_cast<DefaultDecorator::Tab*>(fTabList.ItemAt(index)); 632} 633 634 635void 636DefaultDecorator::_DrawFrame(BRect invalid) 637{ 638 STRACE(("_DrawFrame(%f,%f,%f,%f)\n", invalid.left, invalid.top, 639 invalid.right, invalid.bottom)); 640 641 // NOTE: the DrawingEngine needs to be locked for the entire 642 // time for the clipping to stay valid for this decorator 643 644 if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK) 645 return; 646 647 if (fBorderWidth <= 0) 648 return; 649 650 // Draw the border frame 651 BRect r = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom()); 652 switch ((int)fTopTab->look) { 653 case B_TITLED_WINDOW_LOOK: 654 case B_DOCUMENT_WINDOW_LOOK: 655 case B_MODAL_WINDOW_LOOK: 656 { 657 // top 658 if (invalid.Intersects(fTopBorder)) { 659 ComponentColors colors; 660 _GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab); 661 662 for (int8 i = 0; i < 5; i++) { 663 fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i), 664 BPoint(r.right - i, r.top + i), colors[i]); 665 } 666 if (fTitleBarRect.IsValid()) { 667 // grey along the bottom of the tab 668 // (overwrites "white" from frame) 669 fDrawingEngine->StrokeLine( 670 BPoint(fTitleBarRect.left + 2, 671 fTitleBarRect.bottom + 1), 672 BPoint(fTitleBarRect.right - 2, 673 fTitleBarRect.bottom + 1), 674 colors[2]); 675 } 676 } 677 // left 678 if (invalid.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) { 679 ComponentColors colors; 680 _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); 681 682 for (int8 i = 0; i < 5; i++) { 683 fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i), 684 BPoint(r.left + i, r.bottom - i), colors[i]); 685 } 686 } 687 // bottom 688 if (invalid.Intersects(fBottomBorder)) { 689 ComponentColors colors; 690 _GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab); 691 692 for (int8 i = 0; i < 5; i++) { 693 fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i), 694 BPoint(r.right - i, r.bottom - i), 695 colors[(4 - i) == 4 ? 5 : (4 - i)]); 696 } 697 } 698 // right 699 if (invalid.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) { 700 ComponentColors colors; 701 _GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab); 702 703 for (int8 i = 0; i < 5; i++) { 704 fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i), 705 BPoint(r.right - i, r.bottom - i), 706 colors[(4 - i) == 4 ? 5 : (4 - i)]); 707 } 708 } 709 break; 710 } 711 712 case B_FLOATING_WINDOW_LOOK: 713 case kLeftTitledWindowLook: 714 { 715 // top 716 if (invalid.Intersects(fTopBorder)) { 717 ComponentColors colors; 718 _GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab); 719 720 for (int8 i = 0; i < 3; i++) { 721 fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i), 722 BPoint(r.right - i, r.top + i), colors[i * 2]); 723 } 724 if (fTitleBarRect.IsValid() 725 && fTopTab->look != kLeftTitledWindowLook) { 726 // grey along the bottom of the tab 727 // (overwrites "white" from frame) 728 fDrawingEngine->StrokeLine( 729 BPoint(fTitleBarRect.left + 2, 730 fTitleBarRect.bottom + 1), 731 BPoint(fTitleBarRect.right - 2, 732 fTitleBarRect.bottom + 1), colors[2]); 733 } 734 } 735 // left 736 if (invalid.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) { 737 ComponentColors colors; 738 _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); 739 740 for (int8 i = 0; i < 3; i++) { 741 fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i), 742 BPoint(r.left + i, r.bottom - i), colors[i * 2]); 743 } 744 if (fTopTab->look == kLeftTitledWindowLook 745 && fTitleBarRect.IsValid()) { 746 // grey along the right side of the tab 747 // (overwrites "white" from frame) 748 fDrawingEngine->StrokeLine( 749 BPoint(fTitleBarRect.right + 1, 750 fTitleBarRect.top + 2), 751 BPoint(fTitleBarRect.right + 1, 752 fTitleBarRect.bottom - 2), colors[2]); 753 } 754 } 755 // bottom 756 if (invalid.Intersects(fBottomBorder)) { 757 ComponentColors colors; 758 _GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab); 759 760 for (int8 i = 0; i < 3; i++) { 761 fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i), 762 BPoint(r.right - i, r.bottom - i), 763 colors[(2 - i) == 2 ? 5 : (2 - i) * 2]); 764 } 765 } 766 // right 767 if (invalid.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) { 768 ComponentColors colors; 769 _GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab); 770 771 for (int8 i = 0; i < 3; i++) { 772 fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i), 773 BPoint(r.right - i, r.bottom - i), 774 colors[(2 - i) == 2 ? 5 : (2 - i) * 2]); 775 } 776 } 777 break; 778 } 779 780 case B_BORDERED_WINDOW_LOOK: 781 { 782 // TODO: Draw the borders individually! 783 ComponentColors colors; 784 _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); 785 786 fDrawingEngine->StrokeRect(r, colors[5]); 787 break; 788 } 789 790 default: 791 // don't draw a border frame 792 break; 793 } 794 795 // Draw the resize knob if we're supposed to 796 if (!(fTopTab->flags & B_NOT_RESIZABLE)) { 797 r = fResizeRect; 798 799 ComponentColors colors; 800 _GetComponentColors(COMPONENT_RESIZE_CORNER, colors, fTopTab); 801 802 switch ((int)fTopTab->look) { 803 case B_DOCUMENT_WINDOW_LOOK: 804 { 805 if (!invalid.Intersects(r)) 806 break; 807 808 float x = r.right - 3; 809 float y = r.bottom - 3; 810 811 BRect bg(x - 13, y - 13, x, y); 812 813 BGradientLinear gradient; 814 gradient.SetStart(bg.LeftTop()); 815 gradient.SetEnd(bg.RightBottom()); 816 gradient.AddColor(colors[1], 0); 817 gradient.AddColor(colors[2], 255); 818 819 fDrawingEngine->FillRect(bg, gradient); 820 821 fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15), 822 BPoint(x - 15, y - 2), colors[0]); 823 fDrawingEngine->StrokeLine(BPoint(x - 14, y - 14), 824 BPoint(x - 14, y - 1), colors[1]); 825 fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15), 826 BPoint(x - 2, y - 15), colors[0]); 827 fDrawingEngine->StrokeLine(BPoint(x - 14, y - 14), 828 BPoint(x - 1, y - 14), colors[1]); 829 830 if (fTopTab && !IsFocus(fTopTab)) 831 break; 832 833 static const rgb_color kWhite 834 = (rgb_color){ 255, 255, 255, 255 }; 835 for (int8 i = 1; i <= 4; i++) { 836 for (int8 j = 1; j <= i; j++) { 837 BPoint pt1(x - (3 * j) + 1, y - (3 * (5 - i)) + 1); 838 BPoint pt2(x - (3 * j) + 2, y - (3 * (5 - i)) + 2); 839 fDrawingEngine->StrokePoint(pt1, colors[0]); 840 fDrawingEngine->StrokePoint(pt2, kWhite); 841 } 842 } 843 break; 844 } 845 846 case B_TITLED_WINDOW_LOOK: 847 case B_FLOATING_WINDOW_LOOK: 848 case B_MODAL_WINDOW_LOOK: 849 case kLeftTitledWindowLook: 850 { 851 if (!invalid.Intersects(BRect(fRightBorder.right - kBorderResizeLength, 852 fBottomBorder.bottom - kBorderResizeLength, fRightBorder.right - 1, 853 fBottomBorder.bottom - 1))) 854 break; 855 856 fDrawingEngine->StrokeLine( 857 BPoint(fRightBorder.left, fBottomBorder.bottom - kBorderResizeLength), 858 BPoint(fRightBorder.right - 1, fBottomBorder.bottom - kBorderResizeLength), 859 colors[0]); 860 fDrawingEngine->StrokeLine( 861 BPoint(fRightBorder.right - kBorderResizeLength, fBottomBorder.top), 862 BPoint(fRightBorder.right - kBorderResizeLength, fBottomBorder.bottom - 1), 863 colors[0]); 864 break; 865 } 866 867 default: 868 // don't draw resize corner 869 break; 870 } 871 } 872} 873 874 875void 876DefaultDecorator::_DrawTab(Decorator::Tab* tab, BRect invalid) 877{ 878 STRACE(("_DrawTab(%.1f,%.1f,%.1f,%.1f)\n", 879 invalid.left, invalid.top, invalid.right, invalid.bottom)); 880 const BRect& tabRect = tab->tabRect; 881 // If a window has a tab, this will draw it and any buttons which are 882 // in it. 883 if (!tabRect.IsValid() || !invalid.Intersects(tabRect)) 884 return; 885 886 ComponentColors colors; 887 _GetComponentColors(COMPONENT_TAB, colors, tab); 888 889 // outer frame 890 fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(), 891 colors[COLOR_TAB_FRAME_LIGHT]); 892 fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(), 893 colors[COLOR_TAB_FRAME_LIGHT]); 894 if (tab->look != kLeftTitledWindowLook) { 895 fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(), 896 colors[COLOR_TAB_FRAME_DARK]); 897 } else { 898 fDrawingEngine->StrokeLine(tabRect.LeftBottom(), 899 tabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]); 900 } 901 902 float tabBotton = tabRect.bottom; 903 if (fTopTab != tab) 904 tabBotton -= 1; 905 906 // bevel 907 fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1), 908 BPoint(tabRect.left + 1, 909 tabBotton - (tab->look == kLeftTitledWindowLook ? 1 : 0)), 910 colors[COLOR_TAB_BEVEL]); 911 fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1), 912 BPoint(tabRect.right - (tab->look == kLeftTitledWindowLook ? 0 : 1), 913 tabRect.top + 1), 914 colors[COLOR_TAB_BEVEL]); 915 916 if (tab->look != kLeftTitledWindowLook) { 917 fDrawingEngine->StrokeLine(BPoint(tabRect.right - 1, tabRect.top + 2), 918 BPoint(tabRect.right - 1, tabBotton), 919 colors[COLOR_TAB_SHADOW]); 920 } else { 921 fDrawingEngine->StrokeLine( 922 BPoint(tabRect.left + 2, tabRect.bottom - 1), 923 BPoint(tabRect.right, tabRect.bottom - 1), 924 colors[COLOR_TAB_SHADOW]); 925 } 926 927 // fill 928 BGradientLinear gradient; 929 gradient.SetStart(tabRect.LeftTop()); 930 gradient.AddColor(colors[COLOR_TAB_LIGHT], 0); 931 gradient.AddColor(colors[COLOR_TAB], 255); 932 933 if (tab->look != kLeftTitledWindowLook) { 934 gradient.SetEnd(tabRect.LeftBottom()); 935 fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2, 936 tabRect.right - 2, tabBotton), gradient); 937 } else { 938 gradient.SetEnd(tabRect.RightTop()); 939 fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2, 940 tabRect.right, tabRect.bottom - 2), gradient); 941 } 942 943 _DrawTitle(tab, tabRect); 944 945 DrawButtons(tab, invalid); 946} 947 948 949void 950DefaultDecorator::_DrawClose(Decorator::Tab* _tab, bool direct, BRect rect) 951{ 952 STRACE(("_DrawClose(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, 953 rect.bottom)); 954 955 DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab); 956 957 int32 index = (tab->buttonFocus ? 0 : 1) + (tab->closePressed ? 0 : 2); 958 ServerBitmap* bitmap = tab->closeBitmaps[index]; 959 if (bitmap == NULL) { 960 bitmap = _GetBitmapForButton(tab, COMPONENT_CLOSE_BUTTON, 961 tab->closePressed, rect.IntegerWidth(), rect.IntegerHeight()); 962 tab->closeBitmaps[index] = bitmap; 963 } 964 965 _DrawButtonBitmap(bitmap, direct, rect); 966} 967 968 969void 970DefaultDecorator::_DrawTitle(Decorator::Tab* _tab, BRect r) 971{ 972 DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab); 973 974 const BRect& tabRect = tab->tabRect; 975 const BRect& closeRect = tab->closeRect; 976 const BRect& zoomRect = tab->zoomRect; 977 978 STRACE(("_DrawTitle(%f,%f,%f,%f)\n", r.left, r.top, r.right, r.bottom)); 979 980 ComponentColors colors; 981 _GetComponentColors(COMPONENT_TAB, colors, tab); 982 983 fDrawingEngine->SetDrawingMode(B_OP_OVER); 984 fDrawingEngine->SetHighColor(colors[COLOR_TAB_TEXT]); 985 fDrawingEngine->SetFont(fDrawState.Font()); 986 987 // figure out position of text 988 font_height fontHeight; 989 fDrawState.Font().GetHeight(fontHeight); 990 991 BPoint titlePos; 992 if (tab->look != kLeftTitledWindowLook) { 993 titlePos.x = closeRect.IsValid() ? closeRect.right + tab->textOffset 994 : tabRect.left + tab->textOffset; 995 titlePos.y = floorf(((tabRect.top + 2.0) + tabRect.bottom 996 + fontHeight.ascent + fontHeight.descent) / 2.0 997 - fontHeight.descent + 0.5); 998 } else { 999 titlePos.x = floorf(((tabRect.left + 2.0) + tabRect.right 1000 + fontHeight.ascent + fontHeight.descent) / 2.0 1001 - fontHeight.descent + 0.5); 1002 titlePos.y = zoomRect.IsValid() ? zoomRect.top - tab->textOffset 1003 : tabRect.bottom - tab->textOffset; 1004 } 1005 1006 fDrawingEngine->DrawString(tab->truncatedTitle, tab->truncatedTitleLength, 1007 titlePos); 1008 1009 fDrawingEngine->SetDrawingMode(B_OP_COPY); 1010} 1011 1012 1013void 1014DefaultDecorator::_DrawZoom(Decorator::Tab* _tab, bool direct, BRect rect) 1015{ 1016 STRACE(("_DrawZoom(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, 1017 rect.bottom)); 1018 if (rect.IntegerWidth() < 1) 1019 return; 1020 DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab); 1021 1022 int32 index = (tab->buttonFocus ? 0 : 1) + (tab->zoomPressed ? 0 : 2); 1023 ServerBitmap* bitmap = tab->zoomBitmaps[index]; 1024 if (bitmap == NULL) { 1025 bitmap = _GetBitmapForButton(tab, COMPONENT_ZOOM_BUTTON, 1026 tab->zoomPressed, rect.IntegerWidth(), rect.IntegerHeight()); 1027 tab->zoomBitmaps[index] = bitmap; 1028 } 1029 1030 _DrawButtonBitmap(bitmap, direct, rect); 1031} 1032 1033 1034void 1035DefaultDecorator::_SetTitle(Decorator::Tab* tab, const char* string, 1036 BRegion* updateRegion) 1037{ 1038 // TODO: we could be much smarter about the update region 1039 1040 BRect rect = TabRect(tab); 1041 1042 _DoLayout(); 1043 1044 if (updateRegion == NULL) 1045 return; 1046 1047 rect = rect | TabRect(tab); 1048 1049 rect.bottom++; 1050 // the border will look differently when the title is adjacent 1051 1052 updateRegion->Include(rect); 1053} 1054 1055 1056void 1057DefaultDecorator::_FontsChanged(DesktopSettings& settings, 1058 BRegion* updateRegion) 1059{ 1060 // get previous extent 1061 if (updateRegion != NULL) 1062 updateRegion->Include(&GetFootprint()); 1063 1064 _UpdateFont(settings); 1065 _InvalidateBitmaps(); 1066 _DoLayout(); 1067 1068 _InvalidateFootprint(); 1069 if (updateRegion != NULL) 1070 updateRegion->Include(&GetFootprint()); 1071} 1072 1073 1074void 1075DefaultDecorator::_SetLook(Decorator::Tab* tab, DesktopSettings& settings, 1076 window_look look, BRegion* updateRegion) 1077{ 1078 // TODO: we could be much smarter about the update region 1079 1080 // get previous extent 1081 if (updateRegion != NULL) 1082 updateRegion->Include(&GetFootprint()); 1083 1084 tab->look = look; 1085 1086 _UpdateFont(settings); 1087 _InvalidateBitmaps(); 1088 _DoLayout(); 1089 1090 _InvalidateFootprint(); 1091 if (updateRegion != NULL) 1092 updateRegion->Include(&GetFootprint()); 1093} 1094 1095 1096void 1097DefaultDecorator::_SetFlags(Decorator::Tab* tab, uint32 flags, 1098 BRegion* updateRegion) 1099{ 1100 // TODO: we could be much smarter about the update region 1101 1102 // get previous extent 1103 if (updateRegion != NULL) 1104 updateRegion->Include(&GetFootprint()); 1105 1106 tab->flags = flags; 1107 _DoLayout(); 1108 1109 _InvalidateFootprint(); 1110 if (updateRegion != NULL) 1111 updateRegion->Include(&GetFootprint()); 1112} 1113 1114 1115void 1116DefaultDecorator::_SetFocus(Decorator::Tab* _tab) 1117{ 1118 DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab); 1119 tab->buttonFocus = IsFocus(tab) 1120 || ((tab->look == B_FLOATING_WINDOW_LOOK 1121 || tab->look == kLeftTitledWindowLook) 1122 && (tab->flags & B_AVOID_FOCUS) != 0); 1123 if (CountTabs() > 1) 1124 _LayoutTabItems(tab, tab->tabRect); 1125} 1126 1127 1128void 1129DefaultDecorator::_MoveBy(BPoint offset) 1130{ 1131 STRACE(("DefaultDecorator: Move By (%.1f, %.1f)\n", offset.x, offset.y)); 1132 // Move all internal rectangles the appropriate amount 1133 for (int32 i = 0; i < fTabList.CountItems(); i++) { 1134 Decorator::Tab* tab = fTabList.ItemAt(i); 1135 1136 tab->zoomRect.OffsetBy(offset); 1137 tab->closeRect.OffsetBy(offset); 1138 tab->tabRect.OffsetBy(offset); 1139 } 1140 fFrame.OffsetBy(offset); 1141 fTitleBarRect.OffsetBy(offset); 1142 fTabsRegion.OffsetBy(offset); 1143 fResizeRect.OffsetBy(offset); 1144 fBorderRect.OffsetBy(offset); 1145 1146 fLeftBorder.OffsetBy(offset); 1147 fRightBorder.OffsetBy(offset); 1148 fTopBorder.OffsetBy(offset); 1149 fBottomBorder.OffsetBy(offset); 1150} 1151 1152 1153void 1154DefaultDecorator::_ResizeBy(BPoint offset, BRegion* dirty) 1155{ 1156 STRACE(("DefaultDecorator: Resize By (%.1f, %.1f)\n", offset.x, offset.y)); 1157 // Move all internal rectangles the appropriate amount 1158 fFrame.right += offset.x; 1159 fFrame.bottom += offset.y; 1160 1161 // Handle invalidation of resize rect 1162 if (dirty && !(fTopTab->flags & B_NOT_RESIZABLE)) { 1163 BRect realResizeRect; 1164 switch ((int)fTopTab->look) { 1165 case B_DOCUMENT_WINDOW_LOOK: 1166 realResizeRect = fResizeRect; 1167 // Resize rect at old location 1168 dirty->Include(realResizeRect); 1169 realResizeRect.OffsetBy(offset); 1170 // Resize rect at new location 1171 dirty->Include(realResizeRect); 1172 break; 1173 case B_TITLED_WINDOW_LOOK: 1174 case B_FLOATING_WINDOW_LOOK: 1175 case B_MODAL_WINDOW_LOOK: 1176 case kLeftTitledWindowLook: 1177 // The bottom border resize line 1178 realResizeRect.Set(fRightBorder.right - kBorderResizeLength, fBottomBorder.top, 1179 fRightBorder.right - kBorderResizeLength, fBottomBorder.bottom - 1); 1180 // Old location 1181 dirty->Include(realResizeRect); 1182 realResizeRect.OffsetBy(offset); 1183 // New location 1184 dirty->Include(realResizeRect); 1185 1186 // The right border resize line 1187 realResizeRect.Set(fRightBorder.left, fBottomBorder.bottom - kBorderResizeLength, 1188 fRightBorder.right - 1, fBottomBorder.bottom - kBorderResizeLength); 1189 // Old location 1190 dirty->Include(realResizeRect); 1191 realResizeRect.OffsetBy(offset); 1192 // New location 1193 dirty->Include(realResizeRect); 1194 break; 1195 default: 1196 break; 1197 } 1198 } 1199 1200 fResizeRect.OffsetBy(offset); 1201 1202 fBorderRect.right += offset.x; 1203 fBorderRect.bottom += offset.y; 1204 1205 fLeftBorder.bottom += offset.y; 1206 fTopBorder.right += offset.x; 1207 1208 fRightBorder.OffsetBy(offset.x, 0.0); 1209 fRightBorder.bottom += offset.y; 1210 1211 fBottomBorder.OffsetBy(0.0, offset.y); 1212 fBottomBorder.right += offset.x; 1213 1214 if (dirty) { 1215 if (offset.x > 0.0) { 1216 BRect t(fRightBorder.left - offset.x, fTopBorder.top, 1217 fRightBorder.right, fTopBorder.bottom); 1218 dirty->Include(t); 1219 t.Set(fRightBorder.left - offset.x, fBottomBorder.top, 1220 fRightBorder.right, fBottomBorder.bottom); 1221 dirty->Include(t); 1222 dirty->Include(fRightBorder); 1223 } else if (offset.x < 0.0) { 1224 dirty->Include(BRect(fRightBorder.left, fTopBorder.top, 1225 fRightBorder.right, fBottomBorder.bottom)); 1226 } 1227 if (offset.y > 0.0) { 1228 BRect t(fLeftBorder.left, fLeftBorder.bottom - offset.y, 1229 fLeftBorder.right, fLeftBorder.bottom); 1230 dirty->Include(t); 1231 t.Set(fRightBorder.left, fRightBorder.bottom - offset.y, 1232 fRightBorder.right, fRightBorder.bottom); 1233 dirty->Include(t); 1234 dirty->Include(fBottomBorder); 1235 } else if (offset.y < 0.0) { 1236 dirty->Include(fBottomBorder); 1237 } 1238 } 1239 1240 // resize tab and layout tab items 1241 if (fTitleBarRect.IsValid()) { 1242 if (fTabList.CountItems() > 1) { 1243 _DoTabLayout(); 1244 if (dirty != NULL) 1245 dirty->Include(fTitleBarRect); 1246 return; 1247 } 1248 1249 DefaultDecorator::Tab* tab = _TabAt(0); 1250 BRect& tabRect = tab->tabRect; 1251 BRect oldTabRect(tabRect); 1252 1253 float tabSize; 1254 float tabOffset = _SingleTabOffsetAndSize(tabSize); 1255 1256 float delta = tabOffset - tab->tabOffset; 1257 tab->tabOffset = (uint32)tabOffset; 1258 if (fTopTab->look != kLeftTitledWindowLook) 1259 tabRect.OffsetBy(delta, 0.0); 1260 else 1261 tabRect.OffsetBy(0.0, delta); 1262 1263 if (tabSize < tab->minTabSize) 1264 tabSize = tab->minTabSize; 1265 if (tabSize > tab->maxTabSize) 1266 tabSize = tab->maxTabSize; 1267 1268 if (fTopTab->look != kLeftTitledWindowLook 1269 && tabSize != tabRect.Width()) { 1270 tabRect.right = tabRect.left + tabSize; 1271 } else if (fTopTab->look == kLeftTitledWindowLook 1272 && tabSize != tabRect.Height()) { 1273 tabRect.bottom = tabRect.top + tabSize; 1274 } 1275 1276 if (oldTabRect != tabRect) { 1277 _LayoutTabItems(tab, tabRect); 1278 1279 if (dirty) { 1280 // NOTE: the tab rect becoming smaller only would 1281 // handled be the Desktop anyways, so it is sufficient 1282 // to include it into the dirty region in it's 1283 // final state 1284 BRect redraw(tabRect); 1285 if (delta != 0.0) { 1286 redraw = redraw | oldTabRect; 1287 if (fTopTab->look != kLeftTitledWindowLook) 1288 redraw.bottom++; 1289 else 1290 redraw.right++; 1291 } 1292 dirty->Include(redraw); 1293 } 1294 } 1295 fTitleBarRect = tabRect; 1296 fTabsRegion = fTitleBarRect; 1297 } 1298} 1299 1300 1301bool 1302DefaultDecorator::_SetTabLocation(Decorator::Tab* _tab, float location, 1303 bool isShifting, BRegion* updateRegion) 1304{ 1305 STRACE(("DefaultDecorator: Set Tab Location(%.1f)\n", location)); 1306 if (CountTabs() > 1) { 1307 if (isShifting == false) { 1308 _DoTabLayout(); 1309 if (updateRegion != NULL) 1310 updateRegion->Include(fTitleBarRect); 1311 fOldMovingTab = BRect(0, 0, -1, -1); 1312 return true; 1313 } else { 1314 if (fOldMovingTab.IsValid() == false) 1315 fOldMovingTab = _tab->tabRect; 1316 } 1317 } 1318 1319 DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab); 1320 BRect& tabRect = tab->tabRect; 1321 if (tabRect.IsValid() == false) 1322 return false; 1323 1324 if (location < 0) 1325 location = 0; 1326 1327 float maxLocation 1328 = fRightBorder.right - fLeftBorder.left - tabRect.Width(); 1329 if (CountTabs() > 1) 1330 maxLocation = fTitleBarRect.right - fLeftBorder.left - tabRect.Width(); 1331 1332 if (location > maxLocation) 1333 location = maxLocation; 1334 1335 float delta = floor(location - tab->tabOffset); 1336 if (delta == 0.0) 1337 return false; 1338 1339 // redraw old rect (1 pixel on the border must also be updated) 1340 BRect rect(tabRect); 1341 rect.bottom++; 1342 if (updateRegion != NULL) 1343 updateRegion->Include(rect); 1344 1345 tabRect.OffsetBy(delta, 0); 1346 tab->tabOffset = (int32)location; 1347 _LayoutTabItems(_tab, tabRect); 1348 tab->tabLocation = maxLocation > 0.0 ? tab->tabOffset / maxLocation : 0.0; 1349 1350 if (fTabList.CountItems() == 1) 1351 fTitleBarRect = tabRect; 1352 1353 _CalculateTabsRegion(); 1354 1355 // redraw new rect as well 1356 rect = tabRect; 1357 rect.bottom++; 1358 if (updateRegion != NULL) 1359 updateRegion->Include(rect); 1360 1361 return true; 1362} 1363 1364 1365bool 1366DefaultDecorator::_SetSettings(const BMessage& settings, BRegion* updateRegion) 1367{ 1368 float tabLocation; 1369 bool modified = false; 1370 for (int32 i = 0; i < fTabList.CountItems(); i++) { 1371 if (settings.FindFloat("tab location", i, &tabLocation) != B_OK) 1372 return false; 1373 modified |= SetTabLocation(i, tabLocation, updateRegion); 1374 } 1375 return modified; 1376} 1377 1378 1379bool 1380DefaultDecorator::_AddTab(DesktopSettings& settings, int32 index, 1381 BRegion* updateRegion) 1382{ 1383 _UpdateFont(settings); 1384 1385 _DoLayout(); 1386 if (updateRegion != NULL) 1387 updateRegion->Include(fTitleBarRect); 1388 return true; 1389} 1390 1391 1392bool 1393DefaultDecorator::_RemoveTab(int32 index, BRegion* updateRegion) 1394{ 1395 BRect oldTitle = fTitleBarRect; 1396 _DoLayout(); 1397 if (updateRegion != NULL) { 1398 updateRegion->Include(oldTitle); 1399 updateRegion->Include(fTitleBarRect); 1400 } 1401 return true; 1402} 1403 1404 1405bool 1406DefaultDecorator::_MoveTab(int32 from, int32 to, bool isMoving, 1407 BRegion* updateRegion) 1408{ 1409 DefaultDecorator::Tab* toTab = _TabAt(to); 1410 if (toTab == NULL) 1411 return false; 1412 1413 if (from < to) { 1414 fOldMovingTab.OffsetBy(toTab->tabRect.Width(), 0); 1415 toTab->tabRect.OffsetBy(-fOldMovingTab.Width(), 0); 1416 } else { 1417 fOldMovingTab.OffsetBy(-toTab->tabRect.Width(), 0); 1418 toTab->tabRect.OffsetBy(fOldMovingTab.Width(), 0); 1419 } 1420 1421 toTab->tabOffset = uint32(toTab->tabRect.left - fLeftBorder.left); 1422 _LayoutTabItems(toTab, toTab->tabRect); 1423 1424 _CalculateTabsRegion(); 1425 1426 if (updateRegion != NULL) 1427 updateRegion->Include(fTitleBarRect); 1428 return true; 1429} 1430 1431 1432void 1433DefaultDecorator::_GetFootprint(BRegion *region) 1434{ 1435 STRACE(("DefaultDecorator: Get Footprint\n")); 1436 // This function calculates the decorator's footprint in coordinates 1437 // relative to the view. This is most often used to set a Window 1438 // object's visible region. 1439 if (!region) 1440 return; 1441 1442 region->MakeEmpty(); 1443 1444 if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK) 1445 return; 1446 1447 region->Include(fTopBorder); 1448 region->Include(fLeftBorder); 1449 region->Include(fRightBorder); 1450 region->Include(fBottomBorder); 1451 1452 if (fTopTab->look == B_BORDERED_WINDOW_LOOK) 1453 return; 1454 1455 region->Include(&fTabsRegion); 1456 1457 if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK) { 1458 // include the rectangular resize knob on the bottom right 1459 float knobSize = kResizeKnobSize - fBorderWidth; 1460 region->Include(BRect(fFrame.right - knobSize, fFrame.bottom - knobSize, 1461 fFrame.right, fFrame.bottom)); 1462 } 1463} 1464 1465 1466void 1467DefaultDecorator::DrawButtons(Decorator::Tab* tab, const BRect& invalid) 1468{ 1469 // Draw the buttons if we're supposed to 1470 if (!(tab->flags & B_NOT_CLOSABLE) && invalid.Intersects(tab->closeRect)) 1471 _DrawClose(tab, false, tab->closeRect); 1472 if (!(tab->flags & B_NOT_ZOOMABLE) && invalid.Intersects(tab->zoomRect)) 1473 _DrawZoom(tab, false, tab->zoomRect); 1474} 1475 1476 1477/*! Returns the frame colors for the specified decorator component. 1478 1479 The meaning of the color array elements depends on the specified component. 1480 For some components some array elements are unused. 1481 1482 \param component The component for which to return the frame colors. 1483 \param highlight The highlight set for the component. 1484 \param colors An array of colors to be initialized by the function. 1485*/ 1486void 1487DefaultDecorator::GetComponentColors(Component component, uint8 highlight, 1488 ComponentColors _colors, Decorator::Tab* _tab) 1489{ 1490 DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab); 1491 switch (component) { 1492 case COMPONENT_TAB: 1493 if (tab && tab->buttonFocus) { 1494 _colors[COLOR_TAB_FRAME_LIGHT] 1495 = tint_color(kFocusFrameColor, B_DARKEN_2_TINT); 1496 _colors[COLOR_TAB_FRAME_DARK] 1497 = tint_color(kFocusFrameColor, B_DARKEN_3_TINT); 1498 _colors[COLOR_TAB] = kFocusTabColor; 1499 _colors[COLOR_TAB_LIGHT] = kFocusTabColorLight; 1500 _colors[COLOR_TAB_BEVEL] = kFocusTabColorBevel; 1501 _colors[COLOR_TAB_SHADOW] = kFocusTabColorShadow; 1502 _colors[COLOR_TAB_TEXT] = kFocusTextColor; 1503 } else { 1504 _colors[COLOR_TAB_FRAME_LIGHT] 1505 = tint_color(kNonFocusFrameColor, B_DARKEN_2_TINT); 1506 _colors[COLOR_TAB_FRAME_DARK] 1507 = tint_color(kNonFocusFrameColor, B_DARKEN_3_TINT); 1508 _colors[COLOR_TAB] = kNonFocusTabColor; 1509 _colors[COLOR_TAB_LIGHT] = kNonFocusTabColorLight; 1510 _colors[COLOR_TAB_BEVEL] = kNonFocusTabColorBevel; 1511 _colors[COLOR_TAB_SHADOW] = kNonFocusTabColorShadow; 1512 _colors[COLOR_TAB_TEXT] = kNonFocusTextColor; 1513 } 1514 break; 1515 1516 case COMPONENT_CLOSE_BUTTON: 1517 case COMPONENT_ZOOM_BUTTON: 1518 if (tab && tab->buttonFocus) { 1519 _colors[COLOR_BUTTON] = kFocusTabColor; 1520 _colors[COLOR_BUTTON_LIGHT] = kFocusTabColorLight; 1521 } else { 1522 _colors[COLOR_BUTTON] = kNonFocusTabColor; 1523 _colors[COLOR_BUTTON_LIGHT] = kNonFocusTabColorLight; 1524 } 1525 break; 1526 1527 case COMPONENT_LEFT_BORDER: 1528 case COMPONENT_RIGHT_BORDER: 1529 case COMPONENT_TOP_BORDER: 1530 case COMPONENT_BOTTOM_BORDER: 1531 case COMPONENT_RESIZE_CORNER: 1532 default: 1533 if (tab && tab->buttonFocus) { 1534 _colors[0] = tint_color(kFocusFrameColor, B_DARKEN_2_TINT); 1535 _colors[1] = tint_color(kFocusFrameColor, B_LIGHTEN_2_TINT); 1536 _colors[2] = kFocusFrameColor; 1537 _colors[3] = tint_color(kFocusFrameColor, 1538 (B_DARKEN_1_TINT + B_NO_TINT) / 2); 1539 _colors[4] = tint_color(kFocusFrameColor, B_DARKEN_2_TINT); 1540 _colors[5] = tint_color(kFocusFrameColor, B_DARKEN_3_TINT); 1541 } else { 1542 _colors[0] = tint_color(kNonFocusFrameColor, B_DARKEN_2_TINT); 1543 _colors[1] = tint_color(kNonFocusFrameColor, B_LIGHTEN_2_TINT); 1544 _colors[2] = kNonFocusFrameColor; 1545 _colors[3] = tint_color(kNonFocusFrameColor, 1546 (B_DARKEN_1_TINT + B_NO_TINT) / 2); 1547 _colors[4] = tint_color(kNonFocusFrameColor, B_DARKEN_2_TINT); 1548 _colors[5] = tint_color(kNonFocusFrameColor, B_DARKEN_3_TINT); 1549 } 1550 1551 // for the resize-border highlight dye everything bluish. 1552 if (highlight == HIGHLIGHT_RESIZE_BORDER) { 1553 for (int32 i = 0; i < 6; i++) { 1554 _colors[i].red = std::max((int)_colors[i].red - 80, 0); 1555 _colors[i].green = std::max((int)_colors[i].green - 80, 0); 1556 _colors[i].blue = 255; 1557 } 1558 } 1559 break; 1560 } 1561} 1562 1563 1564void 1565DefaultDecorator::_UpdateFont(DesktopSettings& settings) 1566{ 1567 ServerFont font; 1568 if (fTopTab->look == B_FLOATING_WINDOW_LOOK 1569 || fTopTab->look == kLeftTitledWindowLook) { 1570 settings.GetDefaultPlainFont(font); 1571 if (fTopTab->look == kLeftTitledWindowLook) 1572 font.SetRotation(90.0f); 1573 } else 1574 settings.GetDefaultBoldFont(font); 1575 1576 font.SetFlags(B_FORCE_ANTIALIASING); 1577 font.SetSpacing(B_STRING_SPACING); 1578 fDrawState.SetFont(font); 1579} 1580 1581 1582void 1583DefaultDecorator::_DrawButtonBitmap(ServerBitmap* bitmap, bool direct, 1584 BRect rect) 1585{ 1586 if (bitmap == NULL) 1587 return; 1588 1589 bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled(); 1590 fDrawingEngine->SetCopyToFrontEnabled(direct); 1591 drawing_mode oldMode; 1592 fDrawingEngine->SetDrawingMode(B_OP_OVER, oldMode); 1593 fDrawingEngine->DrawBitmap(bitmap, rect.OffsetToCopy(0, 0), rect); 1594 fDrawingEngine->SetDrawingMode(oldMode); 1595 fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled); 1596} 1597 1598 1599/*! \brief Draws a framed rectangle with a gradient. 1600 \param down The rectangle should be drawn recessed or not. 1601 \param colors A button color array with the colors to be used. 1602*/ 1603void 1604DefaultDecorator::_DrawBlendedRect(DrawingEngine* engine, BRect rect, 1605 bool down, const ComponentColors& colors) 1606{ 1607 // figure out which colors to use 1608 rgb_color startColor, endColor; 1609 if (down) { 1610 startColor = tint_color(colors[COLOR_BUTTON], B_DARKEN_1_TINT); 1611 endColor = colors[COLOR_BUTTON_LIGHT]; 1612 } else { 1613 startColor = tint_color(colors[COLOR_BUTTON], B_LIGHTEN_MAX_TINT); 1614 endColor = colors[COLOR_BUTTON]; 1615 } 1616 1617 // fill 1618 rect.InsetBy(1, 1); 1619 BGradientLinear gradient; 1620 gradient.SetStart(rect.LeftTop()); 1621 gradient.SetEnd(rect.RightBottom()); 1622 gradient.AddColor(startColor, 0); 1623 gradient.AddColor(endColor, 255); 1624 1625 engine->FillRect(rect, gradient); 1626 1627 // outline 1628 rect.InsetBy(-1, -1); 1629 engine->StrokeRect(rect, tint_color(colors[COLOR_BUTTON], B_DARKEN_2_TINT)); 1630} 1631 1632 1633void 1634DefaultDecorator::_GetButtonSizeAndOffset(const BRect& tabRect, float* _offset, 1635 float* _size, float* _inset) const 1636{ 1637 float tabSize = fTopTab->look == kLeftTitledWindowLook ? 1638 tabRect.Width() : tabRect.Height(); 1639 1640 bool smallTab = fTopTab->look == B_FLOATING_WINDOW_LOOK 1641 || fTopTab->look == kLeftTitledWindowLook; 1642 1643 *_offset = smallTab ? floorf(fDrawState.Font().Size() / 2.6) 1644 : floorf(fDrawState.Font().Size() / 2.3); 1645 *_inset = smallTab ? floorf(fDrawState.Font().Size() / 5.0) 1646 : floorf(fDrawState.Font().Size() / 6.0); 1647 1648 // "+ 2" so that the rects are centered within the solid area 1649 // (without the 2 pixels for the top border) 1650 *_size = tabSize - 2 * *_offset + *_inset; 1651} 1652 1653 1654void 1655DefaultDecorator::_LayoutTabItems(Decorator::Tab* _tab, const BRect& tabRect) 1656{ 1657 DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab); 1658 1659 float offset; 1660 float size; 1661 float inset; 1662 _GetButtonSizeAndOffset(tabRect, &offset, &size, &inset); 1663 1664 // default textOffset 1665 tab->textOffset = _DefaultTextOffset(); 1666 1667 BRect& closeRect = tab->closeRect; 1668 BRect& zoomRect = tab->zoomRect; 1669 1670 // calulate close rect based on the tab rectangle 1671 if (tab->look != kLeftTitledWindowLook) { 1672 closeRect.Set(tabRect.left + offset, tabRect.top + offset, 1673 tabRect.left + offset + size, tabRect.top + offset + size); 1674 1675 zoomRect.Set(tabRect.right - offset - size, tabRect.top + offset, 1676 tabRect.right - offset, tabRect.top + offset + size); 1677 1678 // hidden buttons have no width 1679 if ((tab->flags & B_NOT_CLOSABLE) != 0) 1680 closeRect.right = closeRect.left - offset; 1681 if ((tab->flags & B_NOT_ZOOMABLE) != 0) 1682 zoomRect.left = zoomRect.right + offset; 1683 } else { 1684 closeRect.Set(tabRect.left + offset, tabRect.top + offset, 1685 tabRect.left + offset + size, tabRect.top + offset + size); 1686 1687 zoomRect.Set(tabRect.left + offset, tabRect.bottom - offset - size, 1688 tabRect.left + size + offset, tabRect.bottom - offset); 1689 1690 // hidden buttons have no height 1691 if ((tab->flags & B_NOT_CLOSABLE) != 0) 1692 closeRect.bottom = closeRect.top - offset; 1693 if ((tab->flags & B_NOT_ZOOMABLE) != 0) 1694 zoomRect.top = zoomRect.bottom + offset; 1695 } 1696 1697 // calculate room for title 1698 // TODO: the +2 is there because the title often appeared 1699 // truncated for no apparent reason - OTOH the title does 1700 // also not appear perfectly in the middle 1701 if (tab->look != kLeftTitledWindowLook) 1702 size = (zoomRect.left - closeRect.right) - tab->textOffset * 2 + inset; 1703 else 1704 size = (zoomRect.top - closeRect.bottom) - tab->textOffset * 2 + inset; 1705 1706 bool stackMode = fTabList.CountItems() > 1; 1707 if (stackMode && IsFocus(tab) == false) { 1708 zoomRect.Set(0, 0, 0, 0); 1709 size = (tab->tabRect.right - closeRect.right) - tab->textOffset * 2 1710 + inset; 1711 } 1712 uint8 truncateMode = B_TRUNCATE_MIDDLE; 1713 if (stackMode) { 1714 if (tab->tabRect.Width() < 100) 1715 truncateMode = B_TRUNCATE_END; 1716 float titleWidth = fDrawState.Font().StringWidth(Title(tab), 1717 BString(Title(tab)).Length()); 1718 if (size < titleWidth) { 1719 float oldTextOffset = tab->textOffset; 1720 tab->textOffset -= (titleWidth - size) / 2; 1721 const float kMinTextOffset = 5.; 1722 if (tab->textOffset < kMinTextOffset) 1723 tab->textOffset = kMinTextOffset; 1724 size += oldTextOffset * 2; 1725 size -= tab->textOffset * 2; 1726 } 1727 } 1728 tab->truncatedTitle = Title(tab); 1729 fDrawState.Font().TruncateString(&tab->truncatedTitle, truncateMode, size); 1730 tab->truncatedTitleLength = tab->truncatedTitle.Length(); 1731} 1732 1733 1734void 1735DefaultDecorator::_InvalidateBitmaps() 1736{ 1737 for (int32 i = 0; i < fTabList.CountItems(); i++) { 1738 DefaultDecorator::Tab* tab = _TabAt(i); 1739 for (int32 index = 0; index < 4; index++) { 1740 tab->closeBitmaps[index] = NULL; 1741 tab->zoomBitmaps[index] = NULL; 1742 } 1743 } 1744} 1745 1746 1747ServerBitmap* 1748DefaultDecorator::_GetBitmapForButton(Decorator::Tab* tab, Component item, 1749 bool down, int32 width, int32 height) 1750{ 1751 // TODO: the list of shared bitmaps is never freed 1752 struct decorator_bitmap { 1753 Component item; 1754 bool down; 1755 int32 width; 1756 int32 height; 1757 rgb_color baseColor; 1758 rgb_color lightColor; 1759 UtilityBitmap* bitmap; 1760 decorator_bitmap* next; 1761 }; 1762 1763 static BLocker sBitmapListLock("decorator lock", true); 1764 static decorator_bitmap* sBitmapList = NULL; 1765 1766 ComponentColors colors; 1767 _GetComponentColors(item, colors, tab); 1768 1769 BAutolock locker(sBitmapListLock); 1770 1771 // search our list for a matching bitmap 1772 // TODO: use a hash map instead? 1773 decorator_bitmap* current = sBitmapList; 1774 while (current) { 1775 if (current->item == item && current->down == down 1776 && current->width == width && current->height == height 1777 && current->baseColor == colors[COLOR_BUTTON] 1778 && current->lightColor == colors[COLOR_BUTTON_LIGHT]) { 1779 return current->bitmap; 1780 } 1781 1782 current = current->next; 1783 } 1784 1785 static BitmapDrawingEngine* sBitmapDrawingEngine = NULL; 1786 1787 // didn't find any bitmap, create a new one 1788 if (sBitmapDrawingEngine == NULL) 1789 sBitmapDrawingEngine = new(std::nothrow) BitmapDrawingEngine(); 1790 if (sBitmapDrawingEngine == NULL 1791 || sBitmapDrawingEngine->SetSize(width, height) != B_OK) 1792 return NULL; 1793 1794 BRect rect(0, 0, width - 1, height - 1); 1795 1796 STRACE(("DefaultDecorator creating bitmap for %s %s at size %ldx%ld\n", 1797 item == COMPONENT_CLOSE_BUTTON ? "close" : "zoom", 1798 down ? "down" : "up", width, height)); 1799 switch (item) { 1800 case COMPONENT_CLOSE_BUTTON: 1801 _DrawBlendedRect(sBitmapDrawingEngine, rect, down, colors); 1802 break; 1803 1804 case COMPONENT_ZOOM_BUTTON: 1805 { 1806 // init the background 1807 sBitmapDrawingEngine->FillRect(rect, B_TRANSPARENT_COLOR); 1808 1809 float inset = floorf(width / 4.0); 1810 BRect zoomRect(rect); 1811 zoomRect.left += inset; 1812 zoomRect.top += inset; 1813 _DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors); 1814 1815 inset = floorf(width / 2.1); 1816 zoomRect = rect; 1817 zoomRect.right -= inset; 1818 zoomRect.bottom -= inset; 1819 _DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors); 1820 break; 1821 } 1822 1823 default: 1824 break; 1825 } 1826 1827 UtilityBitmap* bitmap = sBitmapDrawingEngine->ExportToBitmap(width, height, 1828 B_RGB32); 1829 if (bitmap == NULL) 1830 return NULL; 1831 1832 // bitmap ready, put it into the list 1833 decorator_bitmap* entry = new(std::nothrow) decorator_bitmap; 1834 if (entry == NULL) { 1835 delete bitmap; 1836 return NULL; 1837 } 1838 1839 entry->item = item; 1840 entry->down = down; 1841 entry->width = width; 1842 entry->height = height; 1843 entry->bitmap = bitmap; 1844 entry->baseColor = colors[COLOR_BUTTON]; 1845 entry->lightColor = colors[COLOR_BUTTON_LIGHT]; 1846 entry->next = sBitmapList; 1847 sBitmapList = entry; 1848 return bitmap; 1849} 1850 1851 1852void 1853DefaultDecorator::_GetComponentColors(Component component, 1854 ComponentColors _colors, Decorator::Tab* tab) 1855{ 1856 // get the highlight for our component 1857 Region region = REGION_NONE; 1858 switch (component) { 1859 case COMPONENT_TAB: 1860 region = REGION_TAB; 1861 break; 1862 case COMPONENT_CLOSE_BUTTON: 1863 region = REGION_CLOSE_BUTTON; 1864 break; 1865 case COMPONENT_ZOOM_BUTTON: 1866 region = REGION_ZOOM_BUTTON; 1867 break; 1868 case COMPONENT_LEFT_BORDER: 1869 region = REGION_LEFT_BORDER; 1870 break; 1871 case COMPONENT_RIGHT_BORDER: 1872 region = REGION_RIGHT_BORDER; 1873 break; 1874 case COMPONENT_TOP_BORDER: 1875 region = REGION_TOP_BORDER; 1876 break; 1877 case COMPONENT_BOTTOM_BORDER: 1878 region = REGION_BOTTOM_BORDER; 1879 break; 1880 case COMPONENT_RESIZE_CORNER: 1881 region = REGION_RIGHT_BOTTOM_CORNER; 1882 break; 1883 } 1884 1885 return GetComponentColors(component, RegionHighlight(region), _colors, tab); 1886} 1887 1888 1889float 1890DefaultDecorator::_DefaultTextOffset() const 1891{ 1892 return (fTopTab->look == B_FLOATING_WINDOW_LOOK 1893 || fTopTab->look == kLeftTitledWindowLook) ? 10 : 18; 1894} 1895 1896 1897float 1898DefaultDecorator::_SingleTabOffsetAndSize(float& tabSize) 1899{ 1900 float maxLocation; 1901 if (fTopTab->look != kLeftTitledWindowLook) { 1902 tabSize = fRightBorder.right - fLeftBorder.left; 1903 } else { 1904 tabSize = fBottomBorder.bottom - fTopBorder.top; 1905 } 1906 DefaultDecorator::Tab* tab = _TabAt(0); 1907 maxLocation = tabSize - tab->maxTabSize; 1908 if (maxLocation < 0) 1909 maxLocation = 0; 1910 1911 return floorf(tab->tabLocation * maxLocation); 1912} 1913 1914 1915void 1916DefaultDecorator::_CalculateTabsRegion() 1917{ 1918 fTabsRegion.MakeEmpty(); 1919 for (int32 i = 0; i < fTabList.CountItems(); i++) 1920 fTabsRegion.Include(fTabList.ItemAt(i)->tabRect); 1921} 1922