1/* 2 * Copyright 2009-2014 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * DarkWyrm, bpmagic@columbus.rr.com 7 * Adrien Destugues, pulkomandy@gmail.com 8 * John Scipione, jscipione@gmail.com 9 */ 10 11 12/*! Decorator resembling Windows 95 */ 13 14 15#include "WinDecorator.h" 16 17#include <new> 18#include <stdio.h> 19 20#include <Point.h> 21#include <Rect.h> 22#include <View.h> 23 24#include "DesktopSettings.h" 25#include "DrawingEngine.h" 26#include "PatternHandler.h" 27#include "RGBColor.h" 28 29 30//#define DEBUG_DECORATOR 31#ifdef DEBUG_DECORATOR 32# define STRACE(x) printf x 33#else 34# define STRACE(x) ; 35#endif 36 37 38WinDecorAddOn::WinDecorAddOn(image_id id, const char* name) 39 : 40 DecorAddOn(id, name) 41{ 42} 43 44 45Decorator* 46WinDecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect, 47 Desktop* desktop) 48{ 49 return new (std::nothrow)WinDecorator(settings, rect, desktop); 50} 51 52 53WinDecorator::WinDecorator(DesktopSettings& settings, BRect frame, 54 Desktop* desktop) 55 : 56 SATDecorator(settings, frame, desktop), 57 // common colors to both focus and non focus state 58 fFrameHighColor((rgb_color){ 255, 255, 255, 255 }), 59 fFrameMidColor((rgb_color){ 216, 216, 216, 255 }), 60 fFrameLowColor((rgb_color){ 110, 110, 110, 255 }), 61 fFrameLowerColor((rgb_color){ 0, 0, 0, 255 }), 62 63 // state based colors 64 fFocusTabColor(settings.UIColor(B_WINDOW_TAB_COLOR)), 65 fFocusTextColor(settings.UIColor(B_WINDOW_TEXT_COLOR)), 66 fNonFocusTabColor(settings.UIColor(B_WINDOW_INACTIVE_TAB_COLOR)), 67 fNonFocusTextColor(settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR)) 68{ 69 STRACE(("WinDecorator:\n")); 70 STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n", 71 frame.left, frame.top, frame.right, frame.bottom)); 72} 73 74 75WinDecorator::~WinDecorator() 76{ 77 STRACE(("~WinDecorator()\n")); 78} 79 80 81/*! \brief Updates the decorator in the rectangular area \a updateRect. 82 83 Updates all areas which intersect the frame and tab. 84 85 \param updateRect The rectangular area to update. 86*/ 87void 88WinDecorator::Draw(BRect updateRect) 89{ 90 STRACE(("WinDecorator::Draw(BRect updateRect): ")); 91 updateRect.PrintToStream(); 92 93 // We need to draw a few things: the tab, the resize knob, the borders, 94 // and the buttons 95 fDrawingEngine->SetDrawState(&fDrawState); 96 97 _DrawFrame(updateRect & fBorderRect); 98 _DrawTabs(updateRect & fTitleBarRect); 99} 100 101 102//! Forces a complete decorator update 103void 104WinDecorator::Draw() 105{ 106 STRACE(("WinDecorator: Draw()")); 107 108 // Easy way to draw everything - no worries about drawing only certain 109 // things 110 fDrawingEngine->SetDrawState(&fDrawState); 111 112 _DrawFrame(fBorderRect); 113 _DrawTabs(fTitleBarRect); 114} 115 116 117// TODO : add GetSizeLimits 118 119 120Decorator::Region 121WinDecorator::RegionAt(BPoint where, int32& tabIndex) const 122{ 123 tabIndex = -1; 124 125 for (int32 i = 0; i < fTabList.CountItems(); i++) { 126 Decorator::Tab* tab = fTabList.ItemAt(i); 127 if (tab->minimizeRect.Contains(where)) { 128 tabIndex = i; 129 return REGION_MINIMIZE_BUTTON; 130 } 131 } 132 // Let the base class version identify hits of the buttons and the tab. 133 Region region = Decorator::RegionAt(where, tabIndex); 134 if (region != REGION_NONE) 135 return region; 136 137 // check the resize corner 138 if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK && fResizeRect.Contains(where)) 139 return REGION_RIGHT_BOTTOM_CORNER; 140 141 // hit-test the borders 142 if (!(fTopTab->flags & B_NOT_RESIZABLE) 143 && (fTopTab->look == B_TITLED_WINDOW_LOOK 144 || fTopTab->look == B_FLOATING_WINDOW_LOOK 145 || fTopTab->look == B_MODAL_WINDOW_LOOK) 146 && fBorderRect.Contains(where) && !fFrame.Contains(where)) { 147 return REGION_BOTTOM_BORDER; 148 // TODO Determine the actual border! 149 } 150 151 return REGION_NONE; 152} 153 154 155bool 156WinDecorator::SetRegionHighlight(Region region, uint8 highlight, 157 BRegion* dirty, int32 tabIndex) 158{ 159 Decorator::Tab* tab 160 = static_cast<Decorator::Tab*>(_TabAt(tabIndex)); 161 if (tab != NULL) { 162 tab->isHighlighted = highlight != 0; 163 // Invalidate the bitmap caches for the close/minimize/zoom button 164 // when the highlight changes. 165 switch (region) { 166 case REGION_CLOSE_BUTTON: 167 if (highlight != RegionHighlight(region)) 168 memset(&tab->closeBitmaps, 0, sizeof(tab->closeBitmaps)); 169 break; 170 case REGION_MINIMIZE_BUTTON: 171 if (highlight != RegionHighlight(region)) { 172 memset(&tab->minimizeBitmaps, 0, 173 sizeof(tab->minimizeBitmaps)); 174 } 175 break; 176 case REGION_ZOOM_BUTTON: 177 if (highlight != RegionHighlight(region)) 178 memset(&tab->zoomBitmaps, 0, sizeof(tab->zoomBitmaps)); 179 break; 180 default: 181 break; 182 } 183 } 184 185 return Decorator::SetRegionHighlight(region, highlight, dirty, tabIndex); 186} 187 188 189void 190WinDecorator::_DoLayout() 191{ 192 STRACE(("WinDecorator()::_DoLayout()\n")); 193 194 bool hasTab = false; 195 196 fBorderRect = fFrame; 197 switch ((int)fTopTab->look) { 198 case B_MODAL_WINDOW_LOOK: 199 fBorderWidth = 4; 200 break; 201 202 case B_TITLED_WINDOW_LOOK: 203 case B_DOCUMENT_WINDOW_LOOK: 204 hasTab = true; 205 fBorderWidth = 4; 206 break; 207 case B_FLOATING_WINDOW_LOOK: 208 fBorderWidth = 0; 209 hasTab = true; 210 break; 211 212 case B_BORDERED_WINDOW_LOOK: 213 fBorderWidth = 1; 214 break; 215 216 default: 217 fBorderWidth = 0; 218 break; 219 } 220 221 fBorderRect.InsetBy(-fBorderWidth, -fBorderWidth); 222 223 if (hasTab) { 224 font_height fontHeight; 225 fDrawState.Font().GetHeight(fontHeight); 226 227 float tabSize = ceilf(fontHeight.ascent + fontHeight.descent + 4.0); 228 229 if (tabSize < 20) 230 tabSize = 20; 231 fBorderRect.top -= tabSize; 232 233 fTitleBarRect.Set(fFrame.left - 1, 234 fFrame.top - tabSize, 235 ((fFrame.right - fFrame.left) < 32.0 ? 236 fFrame.left + 32.0 : fFrame.right) + 1, 237 fFrame.top - 1); 238 239 for (int32 i = 0; i < fTabList.CountItems(); i++) { 240 Decorator::Tab* tab = fTabList.ItemAt(i); 241 242 tab->tabRect = fTitleBarRect; 243 244 const float buttonsInset = 3; 245 246 tab->zoomRect = tab->tabRect; 247 tab->zoomRect.top += buttonsInset; 248 tab->zoomRect.right -= buttonsInset; 249 tab->zoomRect.bottom -= buttonsInset; 250 tab->zoomRect.left = tab->zoomRect.right - tabSize + buttonsInset; 251 252 tab->closeRect = tab->zoomRect; 253 tab->zoomRect.OffsetBy(0 - tab->zoomRect.Width() - 3, 0); 254 255 tab->minimizeRect = tab->zoomRect; 256 tab->minimizeRect.OffsetBy(0 - tab->zoomRect.Width() - 1, 0); 257 } 258 } else { 259 for (int32 i = 0; i < fTabList.CountItems(); i++) { 260 Decorator::Tab* tab = fTabList.ItemAt(i); 261 262 tab->tabRect.Set(0.0, 0.0, -1.0, -1.0); 263 tab->closeRect.Set(0.0, 0.0, -1.0, -1.0); 264 tab->zoomRect.Set(0.0, 0.0, -1.0, -1.0); 265 tab->minimizeRect.Set(0.0, 0.0, -1.0, -1.0); 266 } 267 } 268} 269 270 271void 272WinDecorator::_DrawFrame(BRect rect) 273{ 274 if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK) 275 return; 276 277 if (fBorderRect == fFrame) 278 return; 279 280 BRect r = fBorderRect; 281 282 fDrawingEngine->SetHighColor(fFrameLowerColor); 283 fDrawingEngine->StrokeRect(r); 284 285 if (fTopTab->look == B_BORDERED_WINDOW_LOOK) 286 return; 287 288 BPoint pt; 289 290 pt = r.RightTop(); 291 pt.x--; 292 fDrawingEngine->StrokeLine(r.LeftTop(), pt, fFrameMidColor); 293 pt = r.LeftBottom(); 294 pt.y--; 295 fDrawingEngine->StrokeLine(r.LeftTop(), pt, fFrameMidColor); 296 297 fDrawingEngine->StrokeLine(r.RightTop(), r.RightBottom(), fFrameLowerColor); 298 fDrawingEngine->StrokeLine(r.LeftBottom(), r.RightBottom(), fFrameLowerColor); 299 300 r.InsetBy(1, 1); 301 pt = r.RightTop(); 302 pt.x--; 303 fDrawingEngine->StrokeLine(r.LeftTop(),pt,fFrameHighColor); 304 pt = r.LeftBottom(); 305 pt.y--; 306 fDrawingEngine->StrokeLine(r.LeftTop(),pt,fFrameHighColor); 307 308 fDrawingEngine->StrokeLine(r.RightTop(), r.RightBottom(), fFrameLowColor); 309 fDrawingEngine->StrokeLine(r.LeftBottom(), r.RightBottom(), fFrameLowColor); 310 311 r.InsetBy(1, 1); 312 fDrawingEngine->StrokeRect(r, fFrameMidColor); 313 r.InsetBy(1, 1); 314 fDrawingEngine->StrokeRect(r, fFrameMidColor); 315} 316 317 318/*! \brief Actually draws the tab 319 320 This function is called when the tab itself needs drawn. Other items, 321 like the window title or buttons, should not be drawn here. 322 323 \param tab The \a tab to update. 324 \param rect The area of the \a tab to update. 325*/ 326void 327WinDecorator::_DrawTab(Decorator::Tab* tab, BRect rect) 328{ 329 const BRect& tabRect = tab->tabRect; 330 331 // If a window has a tab, this will draw it and any buttons in it. 332 if (!tabRect.IsValid() || !rect.Intersects(tabRect) 333 || fTopTab->look == B_NO_BORDER_WINDOW_LOOK) { 334 return; 335 } 336 337 fDrawingEngine->FillRect(tabRect & rect, fTabColor); 338 339 _DrawTitle(tab, tabRect); 340 341 // Draw the buttons if we're supposed to 342 // TODO : we should still draw the buttons if they are disabled, but grey them out 343 _DrawButtons(tab, rect); 344} 345 346 347/*! \brief Actually draws the title 348 349 The main tasks for this function are to ensure that the decorator draws 350 the title only in its own area and drawing the title itself. 351 Using B_OP_COPY for drawing the title is recommended because of the marked 352 performance hit of the other drawing modes, but it is not a requirement. 353 354 \param tab The \a tab to update. 355 \param rect area of the title to update. 356*/ 357void 358WinDecorator::_DrawTitle(Decorator::Tab* tab, BRect rect) 359{ 360 const BRect& tabRect = tab->tabRect; 361 const BRect& minimizeRect = tab->minimizeRect; 362 const BRect& zoomRect = tab->zoomRect; 363 const BRect& closeRect = tab->closeRect; 364 365 fDrawingEngine->SetHighColor(fTextColor); 366 fDrawingEngine->SetLowColor(IsFocus(tab) 367 ? fFocusTabColor : fNonFocusTabColor); 368 369 tab->truncatedTitle = Title(tab); 370 fDrawState.Font().TruncateString(&tab->truncatedTitle, B_TRUNCATE_END, 371 ((minimizeRect.IsValid() ? minimizeRect.left : 372 zoomRect.IsValid() ? zoomRect.left : 373 closeRect.IsValid() ? closeRect.left : tabRect.right) - 5) 374 - (tabRect.left + 5)); 375 tab->truncatedTitleLength = tab->truncatedTitle.Length(); 376 fDrawingEngine->SetFont(fDrawState.Font()); 377 378 font_height fontHeight; 379 fDrawState.Font().GetHeight(fontHeight); 380 381 BPoint titlePos; 382 titlePos.x = tabRect.left + 5; 383 titlePos.y = floorf(((tabRect.top + 2.0) + tabRect.bottom 384 + fontHeight.ascent + fontHeight.descent) / 2.0 385 - fontHeight.descent + 0.5); 386 387 fDrawingEngine->DrawString(tab->truncatedTitle, tab->truncatedTitleLength, 388 titlePos); 389} 390 391 392void 393WinDecorator::_DrawButtons(Decorator::Tab* tab, const BRect& invalid) 394{ 395 if ((tab->flags & B_NOT_MINIMIZABLE) == 0 396 && invalid.Intersects(tab->minimizeRect)) { 397 _DrawMinimize(tab, false, tab->minimizeRect); 398 } 399 if ((tab->flags & B_NOT_ZOOMABLE) == 0 400 && invalid.Intersects(tab->zoomRect)) { 401 _DrawZoom(tab, false, tab->zoomRect); 402 } 403 if ((tab->flags & B_NOT_CLOSABLE) == 0 404 && invalid.Intersects(tab->closeRect)) { 405 _DrawClose(tab, false, tab->closeRect); 406 } 407} 408 409 410/*! 411 \brief Actually draws the minimize button 412 413 Unless a subclass has a particularly large button, it is probably 414 unnecessary to check the update rectangle. 415 416 \param tab The \a tab to update. 417 \param direct Draw without double buffering. 418 \param rect The area of the button to update. 419*/ 420void 421WinDecorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect) 422{ 423 // Just like DrawZoom, but for a Minimize button 424 _DrawBeveledRect(rect, tab->minimizePressed); 425 426 fDrawingEngine->SetHighColor(fTextColor); 427 BRect minimizeBox(rect.left + 5, rect.bottom - 4, rect.right - 5, 428 rect.bottom - 3); 429 if (true) 430 minimizeBox.OffsetBy(1, 1); 431 432 fDrawingEngine->SetHighColor(RGBColor(0, 0, 0)); 433 fDrawingEngine->StrokeRect(minimizeBox); 434} 435 436 437/*! \brief Actually draws the zoom button 438 439 Unless a subclass has a particularly large button, it is probably 440 unnecessary to check the update rectangle. 441 442 \param tab The \a tab to update. 443 \param direct Draw without double buffering. 444 \param rect The area of the button to update. 445*/ 446void 447WinDecorator::_DrawZoom(Decorator::Tab* tab, bool direct, BRect rect) 448{ 449 _DrawBeveledRect(rect, tab->zoomPressed); 450 451 // Draw the Zoom box 452 453 BRect zoomBox(rect); 454 zoomBox.InsetBy(2, 2); 455 zoomBox.InsetBy(1, 0); 456 zoomBox.bottom--; 457 zoomBox.right--; 458 459 if (true) 460 zoomBox.OffsetBy(1, 1); 461 462 fDrawingEngine->SetHighColor(RGBColor(0,0,0)); 463 fDrawingEngine->StrokeRect(zoomBox); 464 zoomBox.InsetBy(1, 1); 465 fDrawingEngine->StrokeLine(zoomBox.LeftTop(), zoomBox.RightTop()); 466} 467 468 469/*! \brief Actually draws the close button 470 471 Unless a subclass has a particularly large button, it is probably 472 unnecessary to check the update rectangle. 473 474 \param tab The \a tab to update. 475 \param direct Draw without double buffering. 476 \param rect The area of the button to update. 477*/ 478void 479WinDecorator::_DrawClose(Decorator::Tab* tab, bool direct, BRect rect) 480{ 481 STRACE(("_DrawClose(%f, %f, %f, %f)\n", rect.left, rect.top, rect.right, 482 rect.bottom)); 483 484 // Just like DrawZoom, but for a close button 485 _DrawBeveledRect(rect, tab->closePressed); 486 487 // Draw the X 488 489 BRect closeBox(rect); 490 closeBox.InsetBy(4, 4); 491 closeBox.right--; 492 closeBox.top--; 493 494 if (true) 495 closeBox.OffsetBy(1, 1); 496 497 fDrawingEngine->SetHighColor(RGBColor(0, 0, 0)); 498 fDrawingEngine->StrokeLine(closeBox.LeftTop(), closeBox.RightBottom()); 499 fDrawingEngine->StrokeLine(closeBox.RightTop(), closeBox.LeftBottom()); 500 closeBox.OffsetBy(1, 0); 501 fDrawingEngine->StrokeLine(closeBox.LeftTop(), closeBox.RightBottom()); 502 fDrawingEngine->StrokeLine(closeBox.RightTop(), closeBox.LeftBottom()); 503} 504 505 506void 507WinDecorator::_SetTitle(Decorator::Tab* tab, const char* string, 508 BRegion* updateRegion) 509{ 510 // TODO we could be much smarter about the update region 511 512 BRect rect = TabRect(tab); 513 514 if (updateRegion == NULL) 515 return; 516 517 BRect updatedRect = TabRect(tab); 518 if (rect.left > updatedRect.left) 519 rect.left = updatedRect.left; 520 if (rect.right < updatedRect.right) 521 rect.right = updatedRect.right; 522 523 updateRegion->Include(rect); 524} 525 526 527void 528WinDecorator::_SetFocus(Decorator::Tab* tab) 529{ 530 // TODO stub 531 532 // SetFocus() performs necessary duties for color swapping and 533 // other things when a window is deactivated or activated. 534 535 if (IsFocus(tab)) { 536 fTabColor = fFocusTabColor; 537 fTextColor = fFocusTextColor; 538 } else { 539 fTabColor = fNonFocusTabColor; 540 fTextColor = fNonFocusTextColor; 541 } 542} 543 544 545void 546WinDecorator::_MoveBy(BPoint offset) 547{ 548 // Move all tab rectangles over 549 for (int32 i = 0; i < fTabList.CountItems(); i++) { 550 Decorator::Tab* tab = fTabList.ItemAt(i); 551 552 tab->zoomRect.OffsetBy(offset); 553 tab->closeRect.OffsetBy(offset); 554 tab->minimizeRect.OffsetBy(offset); 555 tab->tabRect.OffsetBy(offset); 556 } 557 558 // Move all internal rectangles over 559 fFrame.OffsetBy(offset); 560 fTitleBarRect.OffsetBy(offset); 561 fResizeRect.OffsetBy(offset); 562 fBorderRect.OffsetBy(offset); 563} 564 565 566void 567WinDecorator::_ResizeBy(BPoint offset, BRegion* dirty) 568{ 569 // Move all internal rectangles the appropriate amount 570 fFrame.right += offset.x; 571 fFrame.bottom += offset.y; 572 fTitleBarRect.right += offset.x; 573 fTitleBarRect.bottom += offset.y; 574 fResizeRect.OffsetBy(offset); 575 fBorderRect.right += offset.x; 576 fBorderRect.bottom += offset.y; 577 578 for (int32 i = 0; i < fTabList.CountItems(); i++) { 579 Decorator::Tab* tab = fTabList.ItemAt(i); 580 581 tab->tabRect.right += offset.x; 582 if (dirty != NULL) 583 dirty->Include(tab->tabRect); 584 //tab->zoomRect.right += offset.x; 585 //tab->closeRect.right += offset.x; 586 //tab->minimizeRect.right += offset.x; 587 } 588 if (dirty != NULL) 589 dirty->Include(fBorderRect); 590 591 // TODO probably some other layouting stuff here 592 _DoLayout(); 593} 594 595 596// TODO : _SetSettings 597 598 599Decorator::Tab* 600WinDecorator::_AllocateNewTab() 601{ 602 Decorator::Tab* tab = new(std::nothrow) Decorator::Tab; 603 if (tab == NULL) 604 return NULL; 605 606 // Set appropriate colors based on the current focus value. In this case, 607 // each decorator defaults to not having the focus. 608 _SetFocus(tab); 609 return tab; 610} 611 612 613bool 614WinDecorator::_AddTab(DesktopSettings& settings, int32 index, 615 BRegion* updateRegion) 616{ 617 _UpdateFont(settings); 618 619 _DoLayout(); 620 if (updateRegion != NULL) 621 updateRegion->Include(fTitleBarRect); 622 623 return true; 624} 625 626 627bool 628WinDecorator::_RemoveTab(int32 index, BRegion* updateRegion) 629{ 630 BRect oldTitle = fTitleBarRect; 631 _DoLayout(); 632 if (updateRegion != NULL) { 633 updateRegion->Include(oldTitle); 634 updateRegion->Include(fTitleBarRect); 635 } 636 return true; 637} 638 639 640bool 641WinDecorator::_MoveTab(int32 from, int32 to, bool isMoving, 642 BRegion* updateRegion) 643{ 644 // TODO stub 645 return false; 646} 647 648 649void 650WinDecorator::_GetFootprint(BRegion* region) 651{ 652 // This function calculates the decorator's footprint in coordinates 653 // relative to the view. This is most often used to set a Window 654 // object's visible region. 655 if (region == NULL) 656 return; 657 658 region->MakeEmpty(); 659 660 if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK) 661 return; 662 663 region->Set(fBorderRect); 664 for (int32 i = 0; i < fTabList.CountItems(); i++) { 665 Decorator::Tab* tab = fTabList.ItemAt(i); 666 region->Include(tab->tabRect); 667 } 668 region->Exclude(fFrame); 669} 670 671 672void 673WinDecorator::_UpdateFont(DesktopSettings& settings) 674{ 675 ServerFont font; 676 if (fTopTab->look == B_FLOATING_WINDOW_LOOK) 677 settings.GetDefaultPlainFont(font); 678 else 679 settings.GetDefaultBoldFont(font); 680 681 font.SetFlags(B_FORCE_ANTIALIASING); 682 font.SetSpacing(B_STRING_SPACING); 683 fDrawState.SetFont(font); 684} 685 686 687void 688WinDecorator::_DrawBeveledRect(BRect r, bool down) 689{ 690 RGBColor higher; 691 RGBColor high; 692 RGBColor mid; 693 RGBColor low; 694 RGBColor lower; 695 696 if (down) { 697 lower.SetColor(255,255,255); 698 low.SetColor(216,216,216); 699 mid.SetColor(192,192,192); 700 high.SetColor(128,128,128); 701 higher.SetColor(0,0,0); 702 } else { 703 higher.SetColor(255,255,255); 704 high.SetColor(216,216,216); 705 mid.SetColor(192,192,192); 706 low.SetColor(128,128,128); 707 lower.SetColor(0,0,0); 708 } 709 710 BRect rect(r); 711 BPoint point; 712 713 // Top highlight 714 fDrawingEngine->SetHighColor(higher); 715 fDrawingEngine->StrokeLine(rect.LeftTop(), rect.RightTop()); 716 717 // Left highlight 718 fDrawingEngine->StrokeLine(rect.LeftTop(), rect.LeftBottom()); 719 720 // Right shading 721 point = rect.RightTop(); 722 point.y++; 723 fDrawingEngine->StrokeLine(point, rect.RightBottom(), lower); 724 725 // Bottom shading 726 point = rect.LeftBottom(); 727 point.x++; 728 fDrawingEngine->StrokeLine(point, rect.RightBottom(), lower); 729 730 rect.InsetBy(1,1); 731 fDrawingEngine->SetHighColor(high); 732 // Top inside highlight 733 fDrawingEngine->StrokeLine(rect.LeftTop(), rect.RightTop()); 734 735 // Left inside highlight 736 fDrawingEngine->StrokeLine(rect.LeftTop(), rect.LeftBottom()); 737 738 // Right inside shading 739 point = rect.RightTop(); 740 point.y++; 741 fDrawingEngine->StrokeLine(point, rect.RightBottom(), low); 742 743 // Bottom inside shading 744 point = rect.LeftBottom(); 745 point.x++; 746 fDrawingEngine->StrokeLine(point, rect.RightBottom(), low); 747 748 rect.InsetBy(1,1); 749 750 fDrawingEngine->FillRect(rect, mid); 751} 752 753 754// #pragma mark - DecorAddOn 755 756 757extern "C" DecorAddOn* 758instantiate_decor_addon(image_id id, const char* name) 759{ 760 return new (std::nothrow)WinDecorAddOn(id, name); 761} 762