1/* 2 * Copyright 2001-2020 Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan A��mus, superstippi@gmx.de 7 * DarkWyrm, bpmagic@columbus.rr.com 8 * Ryan Leavengood, leavengood@gmail.com 9 * Philippe Saint-Pierre, stpere@gmail.com 10 * John Scipione, jscipione@gmail.com 11 * Ingo Weinhold, ingo_weinhold@gmx.de 12 * Clemens Zeidler, haiku@clemens-zeidler.de 13 * Joseph Groover, looncraz@looncraz.net 14 * Tri-Edge AI 15 * Jacob Secunda, secundja@gmail.com 16 */ 17 18 19/*! Default and fallback decorator for the app_server - the yellow tabs */ 20 21 22#include "DefaultDecorator.h" 23 24#include <algorithm> 25#include <cmath> 26#include <new> 27#include <stdio.h> 28 29#include <Autolock.h> 30#include <Debug.h> 31#include <GradientLinear.h> 32#include <Rect.h> 33#include <Region.h> 34#include <View.h> 35 36#include <WindowPrivate.h> 37 38#include "BitmapDrawingEngine.h" 39#include "DesktopSettings.h" 40#include "DrawingEngine.h" 41#include "DrawState.h" 42#include "FontManager.h" 43#include "PatternHandler.h" 44#include "ServerBitmap.h" 45 46 47//#define DEBUG_DECORATOR 48#ifdef DEBUG_DECORATOR 49# define STRACE(x) printf x 50#else 51# define STRACE(x) ; 52#endif 53 54 55static inline uint8 56blend_color_value(uint8 a, uint8 b, float position) 57{ 58 int16 delta = (int16)b - a; 59 int32 value = a + (int32)(position * delta); 60 if (value > 255) 61 return 255; 62 if (value < 0) 63 return 0; 64 65 return (uint8)value; 66} 67 68 69// #pragma mark - 70 71 72// TODO: get rid of DesktopSettings here, and introduce private accessor 73// methods to the Decorator base class 74DefaultDecorator::DefaultDecorator(DesktopSettings& settings, BRect rect, 75 Desktop* desktop) 76 : 77 TabDecorator(settings, rect, desktop) 78{ 79 // TODO: If the decorator was created with a frame too small, it should 80 // resize itself! 81 82 STRACE(("DefaultDecorator:\n")); 83 STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n", 84 rect.left, rect.top, rect.right, rect.bottom)); 85} 86 87 88DefaultDecorator::~DefaultDecorator() 89{ 90 STRACE(("DefaultDecorator: ~DefaultDecorator()\n")); 91} 92 93 94// #pragma mark - Public methods 95 96 97/*! Returns the frame colors for the specified decorator component. 98 99 The meaning of the color array elements depends on the specified component. 100 For some components some array elements are unused. 101 102 \param component The component for which to return the frame colors. 103 \param highlight The highlight set for the component. 104 \param colors An array of colors to be initialized by the function. 105*/ 106void 107DefaultDecorator::GetComponentColors(Component component, uint8 highlight, 108 ComponentColors _colors, Decorator::Tab* _tab) 109{ 110 Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab); 111 switch (component) { 112 case COMPONENT_TAB: 113 if (tab && tab->buttonFocus) { 114 _colors[COLOR_TAB_FRAME_LIGHT] 115 = tint_color(fFocusFrameColor, B_DARKEN_2_TINT); 116 _colors[COLOR_TAB_FRAME_DARK] 117 = tint_color(fFocusFrameColor, B_DARKEN_3_TINT); 118 _colors[COLOR_TAB] = fFocusTabColor; 119 _colors[COLOR_TAB_LIGHT] = fFocusTabColorLight; 120 _colors[COLOR_TAB_BEVEL] = fFocusTabColorBevel; 121 _colors[COLOR_TAB_SHADOW] = fFocusTabColorShadow; 122 _colors[COLOR_TAB_TEXT] = fFocusTextColor; 123 } else { 124 _colors[COLOR_TAB_FRAME_LIGHT] 125 = tint_color(fNonFocusFrameColor, B_DARKEN_2_TINT); 126 _colors[COLOR_TAB_FRAME_DARK] 127 = tint_color(fNonFocusFrameColor, B_DARKEN_3_TINT); 128 _colors[COLOR_TAB] = fNonFocusTabColor; 129 _colors[COLOR_TAB_LIGHT] = fNonFocusTabColorLight; 130 _colors[COLOR_TAB_BEVEL] = fNonFocusTabColorBevel; 131 _colors[COLOR_TAB_SHADOW] = fNonFocusTabColorShadow; 132 _colors[COLOR_TAB_TEXT] = fNonFocusTextColor; 133 } 134 break; 135 136 case COMPONENT_CLOSE_BUTTON: 137 case COMPONENT_ZOOM_BUTTON: 138 if (tab && tab->buttonFocus) { 139 _colors[COLOR_BUTTON] = fFocusTabColor; 140 _colors[COLOR_BUTTON_LIGHT] = fFocusTabColorLight; 141 } else { 142 _colors[COLOR_BUTTON] = fNonFocusTabColor; 143 _colors[COLOR_BUTTON_LIGHT] = fNonFocusTabColorLight; 144 } 145 break; 146 147 case COMPONENT_LEFT_BORDER: 148 case COMPONENT_RIGHT_BORDER: 149 case COMPONENT_TOP_BORDER: 150 case COMPONENT_BOTTOM_BORDER: 151 case COMPONENT_RESIZE_CORNER: 152 default: 153 if (tab && tab->buttonFocus) { 154 _colors[0] = tint_color(fFocusFrameColor, B_DARKEN_2_TINT); 155 _colors[1] = tint_color(fFocusFrameColor, B_LIGHTEN_2_TINT); 156 _colors[2] = fFocusFrameColor; 157 _colors[3] = tint_color(fFocusFrameColor, 158 (B_DARKEN_1_TINT + B_NO_TINT) / 2); 159 _colors[4] = tint_color(fFocusFrameColor, B_DARKEN_2_TINT); 160 _colors[5] = tint_color(fFocusFrameColor, B_DARKEN_3_TINT); 161 } else { 162 _colors[0] = tint_color(fNonFocusFrameColor, B_DARKEN_2_TINT); 163 _colors[1] = tint_color(fNonFocusFrameColor, B_LIGHTEN_2_TINT); 164 _colors[2] = fNonFocusFrameColor; 165 _colors[3] = tint_color(fNonFocusFrameColor, 166 (B_DARKEN_1_TINT + B_NO_TINT) / 2); 167 _colors[4] = tint_color(fNonFocusFrameColor, B_DARKEN_2_TINT); 168 _colors[5] = tint_color(fNonFocusFrameColor, B_DARKEN_3_TINT); 169 } 170 171 // for the resize-border highlight dye everything bluish. 172 if (highlight == HIGHLIGHT_RESIZE_BORDER) { 173 for (int32 i = 0; i < 6; i++) { 174 _colors[i].red = std::max((int)_colors[i].red - 80, 0); 175 _colors[i].green = std::max((int)_colors[i].green - 80, 0); 176 _colors[i].blue = 255; 177 } 178 } 179 break; 180 } 181} 182 183 184void 185DefaultDecorator::UpdateColors(DesktopSettings& settings) 186{ 187 TabDecorator::UpdateColors(settings); 188} 189 190 191// #pragma mark - Protected methods 192 193 194void 195DefaultDecorator::_DrawFrame(BRect rect) 196{ 197 STRACE(("_DrawFrame(%f,%f,%f,%f)\n", rect.left, rect.top, 198 rect.right, rect.bottom)); 199 200 // NOTE: the DrawingEngine needs to be locked for the entire 201 // time for the clipping to stay valid for this decorator 202 203 if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK) 204 return; 205 206 if (fBorderWidth <= 0) 207 return; 208 209 // TODO: While this works, it does not look so crisp at higher resolutions. 210#define COLORS_INDEX(i, borderWidth, nominalLimit) int32((float(i) / float(borderWidth)) * nominalLimit) 211 212 // Draw the border frame 213 BRect border = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom()); 214 switch ((int)fTopTab->look) { 215 case B_TITLED_WINDOW_LOOK: 216 case B_DOCUMENT_WINDOW_LOOK: 217 case B_MODAL_WINDOW_LOOK: 218 { 219 // top 220 if (rect.Intersects(fTopBorder)) { 221 ComponentColors colors; 222 _GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab); 223 224 for (int8 i = 0; i < fBorderWidth; i++) { 225 const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 5); 226 fDrawingEngine->StrokeLine( 227 BPoint(border.left + i, border.top + i), 228 BPoint(border.right - i, border.top + i), 229 colors[colorsIndex]); 230 } 231 if (fTitleBarRect.IsValid()) { 232 // grey along the bottom of the tab 233 // (overwrites "white" from frame) 234 const int overdraw = (int)ceilf(fBorderWidth / 5.0f); 235 for (int i = 1; i <= overdraw; i++) { 236 fDrawingEngine->StrokeLine( 237 BPoint(fTitleBarRect.left + 2, fTitleBarRect.bottom + i), 238 BPoint(fTitleBarRect.right - 2, fTitleBarRect.bottom + i), 239 colors[2]); 240 } 241 } 242 } 243 // left 244 if (rect.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) { 245 ComponentColors colors; 246 _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); 247 248 for (int8 i = 0; i < fBorderWidth; i++) { 249 const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 5); 250 fDrawingEngine->StrokeLine( 251 BPoint(border.left + i, border.top + i), 252 BPoint(border.left + i, border.bottom - i), 253 colors[colorsIndex]); 254 } 255 } 256 // bottom 257 if (rect.Intersects(fBottomBorder)) { 258 ComponentColors colors; 259 _GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab); 260 261 for (int8 i = 0; i < fBorderWidth; i++) { 262 const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 5); 263 fDrawingEngine->StrokeLine( 264 BPoint(border.left + i, border.bottom - i), 265 BPoint(border.right - i, border.bottom - i), 266 colors[(4 - colorsIndex) == 4 ? 5 : (4 - colorsIndex)]); 267 } 268 } 269 // right 270 if (rect.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) { 271 ComponentColors colors; 272 _GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab); 273 274 for (int8 i = 0; i < fBorderWidth; i++) { 275 const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 5); 276 fDrawingEngine->StrokeLine( 277 BPoint(border.right - i, border.top + i), 278 BPoint(border.right - i, border.bottom - i), 279 colors[(4 - colorsIndex) == 4 ? 5 : (4 - colorsIndex)]); 280 } 281 } 282 break; 283 } 284 285 case B_FLOATING_WINDOW_LOOK: 286 case kLeftTitledWindowLook: 287 { 288 // top 289 if (rect.Intersects(fTopBorder)) { 290 ComponentColors colors; 291 _GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab); 292 293 for (int8 i = 0; i < fBorderWidth; i++) { 294 const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 3); 295 fDrawingEngine->StrokeLine( 296 BPoint(border.left + i, border.top + i), 297 BPoint(border.right - i, border.top + i), 298 colors[colorsIndex * 2]); 299 } 300 if (fTitleBarRect.IsValid() && fTopTab->look != kLeftTitledWindowLook) { 301 // grey along the bottom of the tab 302 // (overwrites "white" from frame) 303 const int overdraw = (int)ceilf(fBorderWidth / 5.0f); 304 for (int i = 1; i <= overdraw; i++) { 305 fDrawingEngine->StrokeLine( 306 BPoint(fTitleBarRect.left + 2, fTitleBarRect.bottom + i), 307 BPoint(fTitleBarRect.right - 2, fTitleBarRect.bottom + i), 308 colors[2]); 309 } 310 } 311 } 312 // left 313 if (rect.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) { 314 ComponentColors colors; 315 _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); 316 317 for (int8 i = 0; i < fBorderWidth; i++) { 318 const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 3); 319 fDrawingEngine->StrokeLine( 320 BPoint(border.left + i, border.top + i), 321 BPoint(border.left + i, border.bottom - i), 322 colors[colorsIndex * 2]); 323 } 324 if (fTopTab->look == kLeftTitledWindowLook 325 && fTitleBarRect.IsValid()) { 326 // grey along the right side of the tab 327 // (overwrites "white" from frame) 328 fDrawingEngine->StrokeLine( 329 BPoint(fTitleBarRect.right + 1, 330 fTitleBarRect.top + 2), 331 BPoint(fTitleBarRect.right + 1, 332 fTitleBarRect.bottom - 2), colors[2]); 333 } 334 } 335 // bottom 336 if (rect.Intersects(fBottomBorder)) { 337 ComponentColors colors; 338 _GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab); 339 340 for (int8 i = 0; i < fBorderWidth; i++) { 341 const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 3); 342 fDrawingEngine->StrokeLine( 343 BPoint(border.left + i, border.bottom - i), 344 BPoint(border.right - i, border.bottom - i), 345 colors[(2 - colorsIndex) == 2 ? 5 : (2 - colorsIndex) * 2]); 346 } 347 } 348 // right 349 if (rect.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) { 350 ComponentColors colors; 351 _GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab); 352 353 for (int8 i = 0; i < fBorderWidth; i++) { 354 const int8 colorsIndex = COLORS_INDEX(i, fBorderWidth, 3); 355 fDrawingEngine->StrokeLine( 356 BPoint(border.right - i, border.top + i), 357 BPoint(border.right - i, border.bottom - i), 358 colors[(2 - colorsIndex) == 2 ? 5 : (2 - colorsIndex) * 2]); 359 } 360 } 361 break; 362 } 363 364 case B_BORDERED_WINDOW_LOOK: 365 { 366 // TODO: Draw the borders individually! 367 ComponentColors colors; 368 _GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab); 369 370 fDrawingEngine->StrokeRect(border, colors[5]); 371 break; 372 } 373 374 default: 375 // don't draw a border frame 376 break; 377 } 378 379 // Draw the resize knob if we're supposed to 380 if (!(fTopTab->flags & B_NOT_RESIZABLE)) { 381 ComponentColors colors; 382 _GetComponentColors(COMPONENT_RESIZE_CORNER, colors, fTopTab); 383 384 switch ((int)fTopTab->look) { 385 case B_DOCUMENT_WINDOW_LOOK: 386 { 387 if (fOutlinesDelta.x != 0 || fOutlinesDelta.y != 0) { 388 border.Set(fFrame.right - 13, fFrame.bottom - 13, 389 fFrame.right + 3, fFrame.bottom + 3); 390 391 if (rect.Intersects(border)) 392 _DrawResizeKnob(border, false, colors); 393 } 394 395 if (rect.Intersects(fResizeRect)) { 396 _DrawResizeKnob(fResizeRect, fTopTab && IsFocus(fTopTab), 397 colors); 398 } 399 400 break; 401 } 402 403 case B_TITLED_WINDOW_LOOK: 404 case B_FLOATING_WINDOW_LOOK: 405 case B_MODAL_WINDOW_LOOK: 406 case kLeftTitledWindowLook: 407 { 408 if (!rect.Intersects(BRect( 409 fRightBorder.right - fBorderResizeLength, 410 fBottomBorder.bottom - fBorderResizeLength, 411 fRightBorder.right - 1, 412 fBottomBorder.bottom - 1))) 413 break; 414 415 fDrawingEngine->StrokeLine( 416 BPoint(fRightBorder.left, 417 fBottomBorder.bottom - fBorderResizeLength), 418 BPoint(fRightBorder.right - 1, 419 fBottomBorder.bottom - fBorderResizeLength), 420 colors[0]); 421 fDrawingEngine->StrokeLine( 422 BPoint(fRightBorder.right - fBorderResizeLength, 423 fBottomBorder.top), 424 BPoint(fRightBorder.right - fBorderResizeLength, 425 fBottomBorder.bottom - 1), 426 colors[0]); 427 break; 428 } 429 430 default: 431 // don't draw resize corner 432 break; 433 } 434 } 435} 436 437 438void 439DefaultDecorator::_DrawResizeKnob(BRect rect, bool full, 440 const ComponentColors& colors) 441{ 442 float x = rect.right -= 3; 443 float y = rect.bottom -= 3; 444 445 BGradientLinear gradient; 446 gradient.SetStart(rect.LeftTop()); 447 gradient.SetEnd(rect.RightBottom()); 448 gradient.AddColor(colors[1], 0); 449 gradient.AddColor(colors[2], 255); 450 451 fDrawingEngine->FillRect(rect, gradient); 452 453 BPoint offset1(rect.Width(), rect.Height()), 454 offset2(rect.Width() - 1, rect.Height() - 1); 455 fDrawingEngine->StrokeLine(BPoint(x, y) - offset1, 456 BPoint(x - offset1.x, y - 2), colors[0]); 457 fDrawingEngine->StrokeLine(BPoint(x, y) - offset2, 458 BPoint(x - offset2.x, y - 1), colors[1]); 459 fDrawingEngine->StrokeLine(BPoint(x, y) - offset1, 460 BPoint(x - 2, y - offset1.y), colors[0]); 461 fDrawingEngine->StrokeLine(BPoint(x, y) - offset2, 462 BPoint(x - 1, y - offset2.y), colors[1]); 463 464 if (!full) 465 return; 466 467 static const rgb_color kWhite 468 = (rgb_color){ 255, 255, 255, 255 }; 469 for (int8 i = 1; i <= 4; i++) { 470 for (int8 j = 1; j <= i; j++) { 471 BPoint pt1(x - (3 * j) + 1, y - (3 * (5 - i)) + 1); 472 BPoint pt2(x - (3 * j) + 2, y - (3 * (5 - i)) + 2); 473 fDrawingEngine->StrokePoint(pt1, colors[0]); 474 fDrawingEngine->StrokePoint(pt2, kWhite); 475 } 476 } 477} 478 479 480/*! \brief Actually draws the tab 481 482 This function is called when the tab itself needs drawn. Other items, 483 like the window title or buttons, should not be drawn here. 484 485 \param tab The \a tab to update. 486 \param rect The area of the \a tab to update. 487*/ 488void 489DefaultDecorator::_DrawTab(Decorator::Tab* tab, BRect invalid) 490{ 491 STRACE(("_DrawTab(%.1f,%.1f,%.1f,%.1f)\n", 492 invalid.left, invalid.top, invalid.right, invalid.bottom)); 493 const BRect& tabRect = tab->tabRect; 494 // If a window has a tab, this will draw it and any buttons which are 495 // in it. 496 if (!tabRect.IsValid() || !invalid.Intersects(tabRect)) 497 return; 498 499 ComponentColors colors; 500 _GetComponentColors(COMPONENT_TAB, colors, tab); 501 502 // outer frame 503 fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(), 504 colors[COLOR_TAB_FRAME_LIGHT]); 505 fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(), 506 colors[COLOR_TAB_FRAME_LIGHT]); 507 if (tab->look != kLeftTitledWindowLook) { 508 fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(), 509 colors[COLOR_TAB_FRAME_DARK]); 510 } else { 511 fDrawingEngine->StrokeLine(tabRect.LeftBottom(), 512 tabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]); 513 } 514 515 float tabBotton = tabRect.bottom; 516 if (fTopTab != tab) 517 tabBotton -= 1; 518 519 // bevel 520 fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1), 521 BPoint(tabRect.left + 1, 522 tabBotton - (tab->look == kLeftTitledWindowLook ? 1 : 0)), 523 colors[COLOR_TAB_BEVEL]); 524 fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1), 525 BPoint(tabRect.right - (tab->look == kLeftTitledWindowLook ? 0 : 1), 526 tabRect.top + 1), 527 colors[COLOR_TAB_BEVEL]); 528 529 if (tab->look != kLeftTitledWindowLook) { 530 fDrawingEngine->StrokeLine(BPoint(tabRect.right - 1, tabRect.top + 2), 531 BPoint(tabRect.right - 1, tabBotton), 532 colors[COLOR_TAB_SHADOW]); 533 } else { 534 fDrawingEngine->StrokeLine( 535 BPoint(tabRect.left + 2, tabRect.bottom - 1), 536 BPoint(tabRect.right, tabRect.bottom - 1), 537 colors[COLOR_TAB_SHADOW]); 538 } 539 540 // fill 541 BGradientLinear gradient; 542 gradient.SetStart(tabRect.LeftTop()); 543 gradient.AddColor(colors[COLOR_TAB_LIGHT], 0); 544 gradient.AddColor(colors[COLOR_TAB], 255); 545 546 if (tab->look != kLeftTitledWindowLook) { 547 gradient.SetEnd(tabRect.LeftBottom()); 548 fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2, 549 tabRect.right - 2, tabBotton), gradient); 550 } else { 551 gradient.SetEnd(tabRect.RightTop()); 552 fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2, 553 tabRect.right, tabRect.bottom - 2), gradient); 554 } 555 556 _DrawTitle(tab, tabRect); 557 558 _DrawButtons(tab, invalid); 559} 560 561 562/*! \brief Actually draws the title 563 564 The main tasks for this function are to ensure that the decorator draws 565 the title only in its own area and drawing the title itself. 566 Using B_OP_COPY for drawing the title is recommended because of the marked 567 performance hit of the other drawing modes, but it is not a requirement. 568 569 \param tab The \a tab to update. 570 \param rect area of the title to update. 571*/ 572void 573DefaultDecorator::_DrawTitle(Decorator::Tab* _tab, BRect rect) 574{ 575 STRACE(("_DrawTitle(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, 576 rect.bottom)); 577 578 Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab); 579 580 const BRect& tabRect = tab->tabRect; 581 const BRect& closeRect = tab->closeRect; 582 const BRect& zoomRect = tab->zoomRect; 583 584 ComponentColors colors; 585 _GetComponentColors(COMPONENT_TAB, colors, tab); 586 587 fDrawingEngine->SetDrawingMode(B_OP_OVER); 588 fDrawingEngine->SetHighColor(colors[COLOR_TAB_TEXT]); 589 fDrawingEngine->SetFont(fDrawState.Font()); 590 591 // figure out position of text 592 font_height fontHeight; 593 fDrawState.Font().GetHeight(fontHeight); 594 595 BPoint titlePos; 596 if (tab->look != kLeftTitledWindowLook) { 597 titlePos.x = closeRect.IsValid() ? closeRect.right + tab->textOffset 598 : tabRect.left + tab->textOffset; 599 titlePos.y = floorf(((tabRect.top + 2.0) + tabRect.bottom 600 + fontHeight.ascent + fontHeight.descent) / 2.0 601 - fontHeight.descent + 0.5); 602 } else { 603 titlePos.x = floorf(((tabRect.left + 2.0) + tabRect.right 604 + fontHeight.ascent + fontHeight.descent) / 2.0 605 - fontHeight.descent + 0.5); 606 titlePos.y = zoomRect.IsValid() ? zoomRect.top - tab->textOffset 607 : tabRect.bottom - tab->textOffset; 608 } 609 610 fDrawingEngine->DrawString(tab->truncatedTitle, tab->truncatedTitleLength, 611 titlePos); 612 613 fDrawingEngine->SetDrawingMode(B_OP_COPY); 614} 615 616 617/*! \brief Actually draws the close button 618 619 Unless a subclass has a particularly large button, it is probably 620 unnecessary to check the update rectangle. 621 622 \param _tab The \a tab to update. 623 \param direct Draw without double buffering. 624 \param rect The area of the button to update. 625*/ 626void 627DefaultDecorator::_DrawClose(Decorator::Tab* _tab, bool direct, BRect rect) 628{ 629 STRACE(("_DrawClose(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, 630 rect.bottom)); 631 632 Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab); 633 634 int32 index = (tab->buttonFocus ? 0 : 1) + (tab->closePressed ? 0 : 2); 635 ServerBitmap* bitmap = tab->closeBitmaps[index]; 636 if (bitmap == NULL) { 637 bitmap = _GetBitmapForButton(tab, COMPONENT_CLOSE_BUTTON, 638 tab->closePressed, rect.IntegerWidth(), rect.IntegerHeight()); 639 tab->closeBitmaps[index] = bitmap; 640 } 641 642 _DrawButtonBitmap(bitmap, direct, rect); 643} 644 645 646/*! \brief Actually draws the zoom button 647 648 Unless a subclass has a particularly large button, it is probably 649 unnecessary to check the update rectangle. 650 651 \param _tab The \a tab to update. 652 \param direct Draw without double buffering. 653 \param rect The area of the button to update. 654*/ 655void 656DefaultDecorator::_DrawZoom(Decorator::Tab* _tab, bool direct, BRect rect) 657{ 658 STRACE(("_DrawZoom(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right, 659 rect.bottom)); 660 661 if (rect.IntegerWidth() < 1) 662 return; 663 664 Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab); 665 int32 index = (tab->buttonFocus ? 0 : 1) + (tab->zoomPressed ? 0 : 2); 666 ServerBitmap* bitmap = tab->zoomBitmaps[index]; 667 if (bitmap == NULL) { 668 bitmap = _GetBitmapForButton(tab, COMPONENT_ZOOM_BUTTON, 669 tab->zoomPressed, rect.IntegerWidth(), rect.IntegerHeight()); 670 tab->zoomBitmaps[index] = bitmap; 671 } 672 673 _DrawButtonBitmap(bitmap, direct, rect); 674} 675 676 677void 678DefaultDecorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect) 679{ 680 // This decorator doesn't have this button 681} 682 683 684// #pragma mark - Private methods 685 686 687void 688DefaultDecorator::_DrawButtonBitmap(ServerBitmap* bitmap, bool direct, 689 BRect rect) 690{ 691 if (bitmap == NULL) 692 return; 693 694 bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled(); 695 fDrawingEngine->SetCopyToFrontEnabled(direct); 696 drawing_mode oldMode; 697 fDrawingEngine->SetDrawingMode(B_OP_OVER, oldMode); 698 fDrawingEngine->DrawBitmap(bitmap, rect.OffsetToCopy(0, 0), rect); 699 fDrawingEngine->SetDrawingMode(oldMode); 700 fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled); 701} 702 703 704/*! \brief Draws a framed rectangle with a gradient. 705 \param engine The drawing engine to use. 706 \param rect The rectangular area to draw in. 707 \param down The rectangle should be drawn recessed or not. 708 \param colors A button color array of the colors to be used. 709*/ 710void 711DefaultDecorator::_DrawBlendedRect(DrawingEngine* engine, const BRect rect, 712 bool down, const ComponentColors& colors) 713{ 714 // figure out which colors to use 715 rgb_color startColor, endColor; 716 if (down) { 717 startColor = tint_color(colors[COLOR_BUTTON], B_DARKEN_1_TINT); 718 endColor = colors[COLOR_BUTTON_LIGHT]; 719 } else { 720 startColor = tint_color(colors[COLOR_BUTTON], B_LIGHTEN_MAX_TINT); 721 endColor = colors[COLOR_BUTTON]; 722 } 723 724 // fill 725 BRect fillRect(rect.InsetByCopy(1.0f, 1.0f)); 726 727 BGradientLinear gradient; 728 gradient.SetStart(fillRect.LeftTop()); 729 gradient.SetEnd(fillRect.RightBottom()); 730 gradient.AddColor(startColor, 0); 731 gradient.AddColor(endColor, 255); 732 733 engine->FillRect(fillRect, gradient); 734 735 // outline 736 engine->StrokeRect(rect, tint_color(colors[COLOR_BUTTON], B_DARKEN_2_TINT)); 737} 738 739 740ServerBitmap* 741DefaultDecorator::_GetBitmapForButton(Decorator::Tab* tab, Component item, 742 bool down, int32 width, int32 height) 743{ 744 // TODO: the list of shared bitmaps is never freed 745 struct decorator_bitmap { 746 Component item; 747 bool down; 748 int32 width; 749 int32 height; 750 rgb_color baseColor; 751 rgb_color lightColor; 752 UtilityBitmap* bitmap; 753 decorator_bitmap* next; 754 }; 755 756 static BLocker sBitmapListLock("decorator lock", true); 757 static decorator_bitmap* sBitmapList = NULL; 758 759 ComponentColors colors; 760 _GetComponentColors(item, colors, tab); 761 762 BAutolock locker(sBitmapListLock); 763 764 // search our list for a matching bitmap 765 // TODO: use a hash map instead? 766 decorator_bitmap* current = sBitmapList; 767 while (current) { 768 if (current->item == item && current->down == down 769 && current->width == width && current->height == height 770 && current->baseColor == colors[COLOR_BUTTON] 771 && current->lightColor == colors[COLOR_BUTTON_LIGHT]) { 772 return current->bitmap; 773 } 774 775 current = current->next; 776 } 777 778 static BitmapDrawingEngine* sBitmapDrawingEngine = NULL; 779 780 // didn't find any bitmap, create a new one 781 if (sBitmapDrawingEngine == NULL) 782 sBitmapDrawingEngine = new(std::nothrow) BitmapDrawingEngine(); 783 if (sBitmapDrawingEngine == NULL 784 || sBitmapDrawingEngine->SetSize(width, height) != B_OK) 785 return NULL; 786 787 BRect rect(0, 0, width - 1, height - 1); 788 789 STRACE(("DefaultDecorator creating bitmap for %s %s at size %ldx%ld\n", 790 item == COMPONENT_CLOSE_BUTTON ? "close" : "zoom", 791 down ? "down" : "up", width, height)); 792 switch (item) { 793 case COMPONENT_CLOSE_BUTTON: 794 _DrawBlendedRect(sBitmapDrawingEngine, rect, down, colors); 795 break; 796 797 case COMPONENT_ZOOM_BUTTON: 798 { 799 sBitmapDrawingEngine->FillRect(rect, B_TRANSPARENT_COLOR); 800 // init the background 801 802 float inset = floorf(width / 4.0); 803 BRect zoomRect(rect); 804 zoomRect.left += inset; 805 zoomRect.top += inset; 806 _DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors); 807 808 inset = floorf(width / 2.1); 809 zoomRect = rect; 810 zoomRect.right -= inset; 811 zoomRect.bottom -= inset; 812 _DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors); 813 break; 814 } 815 816 default: 817 break; 818 } 819 820 UtilityBitmap* bitmap = sBitmapDrawingEngine->ExportToBitmap(width, height, 821 B_RGB32); 822 if (bitmap == NULL) 823 return NULL; 824 825 // bitmap ready, put it into the list 826 decorator_bitmap* entry = new(std::nothrow) decorator_bitmap; 827 if (entry == NULL) { 828 delete bitmap; 829 return NULL; 830 } 831 832 entry->item = item; 833 entry->down = down; 834 entry->width = width; 835 entry->height = height; 836 entry->bitmap = bitmap; 837 entry->baseColor = colors[COLOR_BUTTON]; 838 entry->lightColor = colors[COLOR_BUTTON_LIGHT]; 839 entry->next = sBitmapList; 840 sBitmapList = entry; 841 842 return bitmap; 843} 844 845 846void 847DefaultDecorator::_GetComponentColors(Component component, 848 ComponentColors _colors, Decorator::Tab* tab) 849{ 850 // get the highlight for our component 851 Region region = REGION_NONE; 852 switch (component) { 853 case COMPONENT_TAB: 854 region = REGION_TAB; 855 break; 856 case COMPONENT_CLOSE_BUTTON: 857 region = REGION_CLOSE_BUTTON; 858 break; 859 case COMPONENT_ZOOM_BUTTON: 860 region = REGION_ZOOM_BUTTON; 861 break; 862 case COMPONENT_LEFT_BORDER: 863 region = REGION_LEFT_BORDER; 864 break; 865 case COMPONENT_RIGHT_BORDER: 866 region = REGION_RIGHT_BORDER; 867 break; 868 case COMPONENT_TOP_BORDER: 869 region = REGION_TOP_BORDER; 870 break; 871 case COMPONENT_BOTTOM_BORDER: 872 region = REGION_BOTTOM_BORDER; 873 break; 874 case COMPONENT_RESIZE_CORNER: 875 region = REGION_RIGHT_BOTTOM_CORNER; 876 break; 877 } 878 879 return GetComponentColors(component, RegionHighlight(region), _colors, tab); 880} 881