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