1 /* 2 * Copyright (c) 2001-2015, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Adi Oanca <adioanca@gmail.com> 8 * Axel Dörfler, axeld@pinc-software.de 9 * Stephan Aßmus <superstippi@gmx.de> 10 * Marcus Overhagen <marcus@overhagen.de> 11 * Adrien Destugues <pulkomandy@pulkomandy.tk 12 * Julian Harnath <julian.harnath@rwth-aachen.de> 13 * Joseph Groover <looncraz@looncraz.net> 14 */ 15 #include "View.h" 16 17 #include <new> 18 #include <stdio.h> 19 20 #include "AlphaMask.h" 21 #include "Desktop.h" 22 #include "DrawingEngine.h" 23 #include "DrawState.h" 24 #include "Layer.h" 25 #include "Overlay.h" 26 #include "ServerApp.h" 27 #include "ServerBitmap.h" 28 #include "ServerCursor.h" 29 #include "ServerPicture.h" 30 #include "ServerWindow.h" 31 #include "Window.h" 32 33 #include "BitmapHWInterface.h" 34 #include "drawing_support.h" 35 36 #include <List.h> 37 #include <Message.h> 38 #include <PortLink.h> 39 #include <View.h> // for resize modes 40 #include <WindowPrivate.h> 41 42 #include <GradientLinear.h> 43 #include <GradientRadial.h> 44 #include <GradientRadialFocus.h> 45 #include <GradientDiamond.h> 46 #include <GradientConic.h> 47 48 49 using std::nothrow; 50 51 52 void 53 resize_frame(IntRect& frame, uint32 resizingMode, int32 x, int32 y) 54 { 55 // follow with left side 56 if ((resizingMode & 0x0F00U) == _VIEW_RIGHT_ << 8) 57 frame.left += x; 58 else if ((resizingMode & 0x0F00U) == _VIEW_CENTER_ << 8) 59 frame.left += x / 2; 60 61 // follow with right side 62 if ((resizingMode & 0x000FU) == _VIEW_RIGHT_) 63 frame.right += x; 64 else if ((resizingMode & 0x000FU) == _VIEW_CENTER_) 65 frame.right += x / 2; 66 67 // follow with top side 68 if ((resizingMode & 0xF000U) == _VIEW_BOTTOM_ << 12) 69 frame.top += y; 70 else if ((resizingMode & 0xF000U) == _VIEW_CENTER_ << 12) 71 frame.top += y / 2; 72 73 // follow with bottom side 74 if ((resizingMode & 0x00F0U) == _VIEW_BOTTOM_ << 4) 75 frame.bottom += y; 76 else if ((resizingMode & 0x00F0U) == _VIEW_CENTER_ << 4) 77 frame.bottom += y / 2; 78 } 79 80 81 // #pragma mark - 82 83 84 View::View(IntRect frame, IntPoint scrollingOffset, const char* name, 85 int32 token, uint32 resizeMode, uint32 flags) 86 : 87 fName(name), 88 fToken(token), 89 90 fFrame(frame), 91 fScrollingOffset(scrollingOffset), 92 93 fViewColor((rgb_color){ 255, 255, 255, 255 }), 94 fWhichViewColor(B_NO_COLOR), 95 fWhichViewColorTint(B_NO_TINT), 96 fViewBitmap(NULL), 97 fBitmapResizingMode(0), 98 fBitmapOptions(0), 99 100 fResizeMode(resizeMode), 101 fFlags(flags), 102 103 // Views start visible by default 104 fHidden(false), 105 fVisible(true), 106 fBackgroundDirty(true), 107 fIsDesktopBackground(false), 108 109 fEventMask(0), 110 fEventOptions(0), 111 112 fWindow(NULL), 113 fParent(NULL), 114 115 fFirstChild(NULL), 116 fPreviousSibling(NULL), 117 fNextSibling(NULL), 118 fLastChild(NULL), 119 120 fCursor(NULL), 121 fPicture(NULL), 122 123 fLocalClipping((BRect)Bounds()), 124 fScreenClipping(), 125 fScreenClippingValid(false), 126 fUserClipping(NULL), 127 fScreenAndUserClipping(NULL) 128 { 129 if (fDrawState) 130 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE); 131 } 132 133 134 View::~View() 135 { 136 if (fViewBitmap != NULL) 137 fViewBitmap->ReleaseReference(); 138 139 delete fScreenAndUserClipping; 140 delete fUserClipping; 141 delete fDrawState; 142 143 // if (fWindow && this == fWindow->TopView()) 144 // fWindow->SetTopView(NULL); 145 146 if (fCursor) 147 fCursor->ReleaseReference(); 148 149 // iterate over children and delete each one 150 View* view = fFirstChild; 151 while (view) { 152 View* toast = view; 153 view = view->fNextSibling; 154 delete toast; 155 } 156 } 157 158 159 IntRect 160 View::Bounds() const 161 { 162 IntRect bounds(fScrollingOffset.x, fScrollingOffset.y, 163 fScrollingOffset.x + fFrame.Width(), 164 fScrollingOffset.y + fFrame.Height()); 165 return bounds; 166 } 167 168 169 void 170 View::ConvertToVisibleInTopView(IntRect* bounds) const 171 { 172 *bounds = *bounds & Bounds(); 173 // NOTE: this step is necessary even if we don't have a parent! 174 bounds->OffsetBy(fFrame.left - fScrollingOffset.x, 175 fFrame.top - fScrollingOffset.y); 176 177 if (fParent) 178 fParent->ConvertToVisibleInTopView(bounds); 179 } 180 181 182 void 183 View::AttachedToWindow(::Window* window) 184 { 185 fWindow = window; 186 187 // an ugly hack to detect the desktop background 188 if (window->Feel() == kDesktopWindowFeel && Parent() == TopView()) 189 fIsDesktopBackground = true; 190 191 // insert view into local token space 192 if (fWindow != NULL) { 193 fWindow->ServerWindow()->App()->ViewTokens().SetToken(fToken, 194 B_HANDLER_TOKEN, this); 195 } 196 197 // attach child views as well 198 for (View* child = FirstChild(); child; child = child->NextSibling()) 199 child->AttachedToWindow(window); 200 } 201 202 203 void 204 View::DetachedFromWindow() 205 { 206 // remove view from local token space 207 if (fWindow != NULL && fWindow->ServerWindow()->App() != NULL) 208 fWindow->ServerWindow()->App()->ViewTokens().RemoveToken(fToken); 209 210 fWindow = NULL; 211 // detach child views as well 212 for (View* child = FirstChild(); child; child = child->NextSibling()) 213 child->DetachedFromWindow(); 214 } 215 216 217 // #pragma mark - 218 219 220 DrawingEngine* 221 View::GetDrawingEngine() const 222 { 223 return Window()->GetDrawingEngine(); 224 } 225 226 227 ServerPicture* 228 View::GetPicture(int32 token) const 229 { 230 return Window()->ServerWindow()->App()->GetPicture(token); 231 } 232 233 234 void 235 View::ResyncDrawState() 236 { 237 return Window()->ServerWindow()->ResyncDrawState(); 238 } 239 240 241 void 242 View::UpdateCurrentDrawingRegion() 243 { 244 return Window()->ServerWindow()->UpdateCurrentDrawingRegion(); 245 } 246 247 248 void 249 View::AddChild(View* view) 250 { 251 if (view->fParent) { 252 printf("View::AddChild() - View already has a parent\n"); 253 return; 254 } 255 256 view->fParent = this; 257 258 if (!fLastChild) { 259 // no children yet 260 fFirstChild = view; 261 } else { 262 // append view to formerly last child 263 fLastChild->fNextSibling = view; 264 view->fPreviousSibling = fLastChild; 265 } 266 fLastChild = view; 267 268 view->UpdateVisibleDeep(fVisible); 269 270 if (view->IsVisible()) 271 RebuildClipping(false); 272 273 if (fWindow) { 274 view->AttachedToWindow(fWindow); 275 276 if (view->IsVisible()) { 277 // trigger redraw 278 IntRect clippedFrame = view->Frame(); 279 ConvertToVisibleInTopView(&clippedFrame); 280 281 BRegion dirty; 282 dirty.Set((clipping_rect)clippedFrame); 283 fWindow->MarkContentDirtyAsync(dirty); 284 } 285 } 286 } 287 288 289 bool 290 View::RemoveChild(View* view) 291 { 292 if (view == NULL || view->fParent != this) { 293 printf("View::RemoveChild(%p - %s) - View is not child of " 294 "this (%p) view!\n", view, view ? view->Name() : NULL, this); 295 return false; 296 } 297 298 view->fParent = NULL; 299 300 if (fLastChild == view) 301 fLastChild = view->fPreviousSibling; 302 // view->fNextSibling would be NULL 303 304 if (fFirstChild == view ) 305 fFirstChild = view->fNextSibling; 306 // view->fPreviousSibling would be NULL 307 308 // connect child before and after view 309 if (view->fPreviousSibling) 310 view->fPreviousSibling->fNextSibling = view->fNextSibling; 311 312 if (view->fNextSibling) 313 view->fNextSibling->fPreviousSibling = view->fPreviousSibling; 314 315 // view has no siblings anymore 316 view->fPreviousSibling = NULL; 317 view->fNextSibling = NULL; 318 319 if (view->IsVisible()) { 320 Overlay* overlay = view->_Overlay(); 321 if (overlay != NULL) 322 overlay->Hide(); 323 324 RebuildClipping(false); 325 } 326 327 if (fWindow) { 328 view->DetachedFromWindow(); 329 330 if (fVisible && view->IsVisible()) { 331 // trigger redraw 332 IntRect clippedFrame = view->Frame(); 333 ConvertToVisibleInTopView(&clippedFrame); 334 335 BRegion dirty; 336 dirty.Set((clipping_rect)clippedFrame); 337 fWindow->MarkContentDirtyAsync(dirty); 338 } 339 } 340 341 return true; 342 } 343 344 345 View* 346 View::TopView() 347 { 348 // returns the top level view of the hirarchy, 349 // it doesn't have to be the top level of a window 350 351 if (fParent) 352 return fParent->TopView(); 353 354 return this; 355 } 356 357 358 uint32 359 View::CountChildren(bool deep) const 360 { 361 uint32 count = 0; 362 for (View* child = FirstChild(); child; child = child->NextSibling()) { 363 count++; 364 if (deep) { 365 count += child->CountChildren(deep); 366 } 367 } 368 return count; 369 } 370 371 372 void 373 View::CollectTokensForChildren(BList* tokenMap) const 374 { 375 for (View* child = FirstChild(); child; child = child->NextSibling()) { 376 tokenMap->AddItem((void*)child); 377 child->CollectTokensForChildren(tokenMap); 378 } 379 } 380 381 382 #if 0 383 bool 384 View::MarkAt(DrawingEngine* engine, const BPoint& where, int32 level) 385 { 386 BRect rect(fFrame.left, fFrame.top, fFrame.right, fFrame.bottom); 387 388 if (Parent() != NULL) { 389 Parent()->ConvertToScreen(&rect); 390 if (!rect.Contains(where)) 391 return false; 392 393 engine->StrokeRect(rect, (rgb_color){level * 30, level * 30, level * 30}); 394 } 395 396 397 bool found = false; 398 for (View* child = FirstChild(); child; child = child->NextSibling()) { 399 found |= child->MarkAt(engine, where, level + 1); 400 } 401 402 if (!found) { 403 rgb_color color = {0}; 404 switch (level % 2) { 405 case 0: color.green = rand() % 256; break; 406 case 1: color.blue = rand() % 256; break; 407 } 408 409 rect.InsetBy(1, 1); 410 //engine->FillRegion(fLocalClipping, (rgb_color){255, 255, 0, 10}); 411 engine->StrokeRect(rect, color); 412 rect.InsetBy(1, 1); 413 engine->StrokeRect(rect, color); 414 } 415 416 return true; 417 } 418 #endif 419 420 421 void 422 View::FindViews(uint32 flags, BObjectList<View>& list, int32& left) 423 { 424 if ((Flags() & flags) == flags) { 425 list.AddItem(this); 426 left--; 427 return; 428 } 429 430 for (View* child = FirstChild(); child; child = child->NextSibling()) { 431 child->FindViews(flags, list, left); 432 if (left == 0) 433 break; 434 } 435 } 436 437 438 bool 439 View::HasView(View* view) 440 { 441 if (view == this) 442 return true; 443 444 for (View* child = FirstChild(); child; child = child->NextSibling()) { 445 if (child->HasView(view)) 446 return true; 447 } 448 449 return false; 450 } 451 452 453 View* 454 View::ViewAt(const BPoint& where) 455 { 456 if (!fVisible) 457 return NULL; 458 459 IntRect frame = Frame(); 460 if (Parent() != NULL) 461 Parent()->LocalToScreenTransform().Apply(&frame); 462 463 if (!frame.Contains(where)) 464 return NULL; 465 466 for (View* child = FirstChild(); child; child = child->NextSibling()) { 467 View* view = child->ViewAt(where); 468 if (view != NULL) 469 return view; 470 } 471 472 return this; 473 } 474 475 476 // #pragma mark - 477 478 479 void 480 View::SetName(const char* string) 481 { 482 fName.SetTo(string); 483 } 484 485 486 void 487 View::SetFlags(uint32 flags) 488 { 489 uint32 oldFlags = fFlags; 490 fFlags = flags; 491 492 // Child view with B_TRANSPARENT_BACKGROUND flag change clipping of 493 // parent view. 494 if (fParent != NULL 495 && IsVisible() 496 && (((oldFlags & B_TRANSPARENT_BACKGROUND) != 0) 497 != ((fFlags & B_TRANSPARENT_BACKGROUND) != 0))) { 498 fParent->RebuildClipping(false); 499 } 500 501 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE); 502 } 503 504 505 void 506 View::SetViewBitmap(ServerBitmap* bitmap, IntRect sourceRect, 507 IntRect destRect, int32 resizingMode, int32 options) 508 { 509 if (fViewBitmap != NULL) { 510 Overlay* overlay = _Overlay(); 511 512 if (bitmap != NULL) { 513 // take over overlay token from current overlay (if it has any) 514 Overlay* newOverlay = bitmap->Overlay(); 515 516 if (overlay != NULL && newOverlay != NULL) 517 newOverlay->TakeOverToken(overlay); 518 } else if (overlay != NULL) 519 overlay->Hide(); 520 521 fViewBitmap->ReleaseReference(); 522 } 523 524 // the caller is allowed to delete the bitmap after setting the background 525 if (bitmap != NULL) 526 bitmap->AcquireReference(); 527 528 fViewBitmap = bitmap; 529 fBitmapSource = sourceRect; 530 fBitmapDestination = destRect; 531 fBitmapResizingMode = resizingMode; 532 fBitmapOptions = options; 533 534 _UpdateOverlayView(); 535 } 536 537 538 ::Overlay* 539 View::_Overlay() const 540 { 541 if (fViewBitmap == NULL) 542 return NULL; 543 544 return fViewBitmap->Overlay(); 545 } 546 547 548 void 549 View::_UpdateOverlayView() const 550 { 551 Overlay* overlay = _Overlay(); 552 if (overlay == NULL) 553 return; 554 555 IntRect destination = fBitmapDestination; 556 LocalToScreenTransform().Apply(&destination); 557 558 overlay->Configure(fBitmapSource, destination); 559 } 560 561 562 /*! 563 This method is called whenever the window is resized or moved - would 564 be nice to have a better solution for this, though. 565 */ 566 void 567 View::UpdateOverlay() 568 { 569 if (!IsVisible()) 570 return; 571 572 if (_Overlay() != NULL) { 573 _UpdateOverlayView(); 574 } else { 575 // recursively ask children of this view 576 577 for (View* child = FirstChild(); child; child = child->NextSibling()) { 578 child->UpdateOverlay(); 579 } 580 } 581 } 582 583 584 // #pragma mark - 585 586 587 void 588 View::_LocalToScreenTransform(SimpleTransform& transform) const 589 { 590 const View* view = this; 591 int32 offsetX = 0; 592 int32 offsetY = 0; 593 do { 594 offsetX += view->fFrame.left - view->fScrollingOffset.x; 595 offsetY += view->fFrame.top - view->fScrollingOffset.y; 596 view = view->fParent; 597 } while (view != NULL); 598 599 transform.AddOffset(offsetX, offsetY); 600 } 601 602 603 void 604 View::_ScreenToLocalTransform(SimpleTransform& transform) const 605 { 606 const View* view = this; 607 int32 offsetX = 0; 608 int32 offsetY = 0; 609 do { 610 offsetX += view->fScrollingOffset.x - view->fFrame.left; 611 offsetY += view->fScrollingOffset.y - view->fFrame.top; 612 view = view->fParent; 613 } while (view != NULL); 614 615 transform.AddOffset(offsetX, offsetY); 616 } 617 618 619 // #pragma mark - 620 621 622 void 623 View::MoveBy(int32 x, int32 y, BRegion* dirtyRegion) 624 { 625 if (x == 0 && y == 0) 626 return; 627 628 fFrame.OffsetBy(x, y); 629 630 // to move on screen, we must not be hidden and we must have a parent 631 if (fVisible && fParent && dirtyRegion) { 632 #if 1 633 // based on redraw on new location 634 // the place were we are now visible 635 IntRect newVisibleBounds(Bounds()); 636 // we can use the frame of the old 637 // local clipping to see which parts need invalidation 638 IntRect oldVisibleBounds(newVisibleBounds); 639 oldVisibleBounds.OffsetBy(-x, -y); 640 LocalToScreenTransform().Apply(&oldVisibleBounds); 641 642 ConvertToVisibleInTopView(&newVisibleBounds); 643 644 dirtyRegion->Include((clipping_rect)oldVisibleBounds); 645 // newVisibleBounds already is in screen coords 646 dirtyRegion->Include((clipping_rect)newVisibleBounds); 647 #else 648 // blitting version, invalidates 649 // old contents 650 IntRect oldVisibleBounds(Bounds()); 651 IntRect newVisibleBounds(oldVisibleBounds); 652 oldVisibleBounds.OffsetBy(-x, -y); 653 LocalToScreenTransform().Apply(&oldVisibleBounds); 654 655 // NOTE: using ConvertToVisibleInTopView() 656 // instead of ConvertToScreen()! see below 657 ConvertToVisibleInTopView(&newVisibleBounds); 658 659 newVisibleBounds.OffsetBy(-x, -y); 660 661 // clipping oldVisibleBounds to newVisibleBounds 662 // makes sure we don't copy parts hidden under 663 // parent views 664 BRegion* region = fWindow->GetRegion(); 665 if (region) { 666 region->Set(oldVisibleBounds & newVisibleBounds); 667 fWindow->CopyContents(region, x, y); 668 669 region->Set(oldVisibleBounds); 670 newVisibleBounds.OffsetBy(x, y); 671 region->Exclude((clipping_rect)newVisibleBounds); 672 dirtyRegion->Include(dirty); 673 674 fWindow->RecycleRegion(region); 675 } 676 677 #endif 678 } 679 680 if (!fParent) { 681 // the top view's screen clipping does not change, 682 // because no parts are clipped away from parent 683 // views 684 _MoveScreenClipping(x, y, true); 685 } else { 686 // parts might have been revealed from underneath 687 // the parent, or might now be hidden underneath 688 // the parent, this is taken care of when building 689 // the screen clipping 690 InvalidateScreenClipping(); 691 } 692 } 693 694 695 void 696 View::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) 697 { 698 if (x == 0 && y == 0) 699 return; 700 701 fFrame.right += x; 702 fFrame.bottom += y; 703 704 if (fVisible && dirtyRegion) { 705 IntRect oldBounds(Bounds()); 706 oldBounds.right -= x; 707 oldBounds.bottom -= y; 708 709 BRegion* dirty = fWindow->GetRegion(); 710 if (!dirty) 711 return; 712 713 dirty->Set((clipping_rect)Bounds()); 714 dirty->Include((clipping_rect)oldBounds); 715 716 if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) { 717 // the dirty region is just the difference of 718 // old and new bounds 719 dirty->Exclude((clipping_rect)(oldBounds & Bounds())); 720 } 721 722 InvalidateScreenClipping(); 723 724 if (dirty->CountRects() > 0) { 725 if ((fFlags & B_DRAW_ON_CHILDREN) == 0) { 726 // exclude children, they are expected to 727 // include their own dirty regions in ParentResized() 728 for (View* child = FirstChild(); child; 729 child = child->NextSibling()) { 730 if (!child->IsVisible() 731 || (child->fFlags & B_TRANSPARENT_BACKGROUND) != 0) { 732 continue; 733 } 734 IntRect previousChildVisible( 735 child->Frame() & oldBounds & Bounds()); 736 if (dirty->Frame().Intersects(previousChildVisible)) { 737 dirty->Exclude((clipping_rect)previousChildVisible); 738 } 739 } 740 } 741 742 LocalToScreenTransform().Apply(dirty); 743 dirtyRegion->Include(dirty); 744 } 745 fWindow->RecycleRegion(dirty); 746 } 747 748 // layout the children 749 for (View* child = FirstChild(); child; child = child->NextSibling()) 750 child->ParentResized(x, y, dirtyRegion); 751 752 // view bitmap 753 if (fViewBitmap != NULL) 754 resize_frame(fBitmapDestination, fBitmapResizingMode, x, y); 755 756 // at this point, children are at their new locations, 757 // so we can rebuild the clipping 758 // TODO: when the implementation of Hide() and Show() is 759 // complete, see if this should be avoided 760 RebuildClipping(false); 761 } 762 763 764 void 765 View::ParentResized(int32 x, int32 y, BRegion* dirtyRegion) 766 { 767 IntRect newFrame = fFrame; 768 resize_frame(newFrame, fResizeMode & 0x0000ffff, x, y); 769 770 if (newFrame != fFrame) { 771 // careful, MoveBy will change fFrame 772 int32 widthDiff = (int32)(newFrame.Width() - fFrame.Width()); 773 int32 heightDiff = (int32)(newFrame.Height() - fFrame.Height()); 774 775 MoveBy(newFrame.left - fFrame.left, 776 newFrame.top - fFrame.top, dirtyRegion); 777 778 ResizeBy(widthDiff, heightDiff, dirtyRegion); 779 } else { 780 // TODO: this covers the fact that our screen clipping might change 781 // when the parent changes its size, even though our frame stays 782 // the same - there might be a way to test for this, but axeld doesn't 783 // know, stippi should look into this when he's back :) 784 InvalidateScreenClipping(); 785 } 786 } 787 788 789 void 790 View::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion) 791 { 792 if (!fVisible || !fWindow) { 793 fScrollingOffset.x += x; 794 fScrollingOffset.y += y; 795 return; 796 } 797 798 // blitting version, invalidates 799 // old contents 800 801 // remember old bounds for tracking dirty region 802 IntRect oldBounds(Bounds()); 803 804 // NOTE: using ConvertToVisibleInTopView() 805 // instead of ConvertToScreen(), this makes 806 // sure we don't try to move or invalidate an 807 // area hidden underneath the parent view 808 ConvertToVisibleInTopView(&oldBounds); 809 810 // find the area of the view that can be scrolled, 811 // contents are shifted in the opposite direction from scrolling 812 IntRect stillVisibleBounds(oldBounds); 813 stillVisibleBounds.OffsetBy(x, y); 814 stillVisibleBounds = stillVisibleBounds & oldBounds; 815 816 fScrollingOffset.x += x; 817 fScrollingOffset.y += y; 818 819 // do the blit, this will make sure 820 // that other more complex dirty regions 821 // are taken care of 822 BRegion* copyRegion = fWindow->GetRegion(); 823 if (!copyRegion) 824 return; 825 copyRegion->Set((clipping_rect)stillVisibleBounds); 826 fWindow->CopyContents(copyRegion, -x, -y); 827 828 // find the dirty region as far as we are 829 // concerned 830 BRegion* dirty = copyRegion; 831 // reuse copyRegion and call it dirty 832 833 dirty->Set((clipping_rect)oldBounds); 834 stillVisibleBounds.OffsetBy(-x, -y); 835 dirty->Exclude((clipping_rect)stillVisibleBounds); 836 dirtyRegion->Include(dirty); 837 838 fWindow->RecycleRegion(dirty); 839 840 // the screen clipping of this view and it's 841 // childs is no longer valid 842 InvalidateScreenClipping(); 843 RebuildClipping(false); 844 } 845 846 847 void 848 View::CopyBits(IntRect src, IntRect dst, BRegion& windowContentClipping) 849 { 850 if (!fVisible || !fWindow) 851 return; 852 853 // TODO: figure out what to do when we have a transform which is not 854 // a dilation 855 BAffineTransform transform = CurrentState()->CombinedTransform(); 856 if (!transform.IsIdentity() && transform.IsDilation()) { 857 BPoint points[4] = { src.LeftTop(), src.RightBottom(), 858 dst.LeftTop(), dst.RightBottom() }; 859 transform.Apply(&points[0], 4); 860 src.Set(points[0].x, points[0].y, points[1].x, points[1].y); 861 dst.Set(points[2].x, points[2].y, points[3].x, points[3].y); 862 } 863 864 // TODO: confirm that in R5 this call is affected by origin and scale 865 866 // blitting version 867 868 int32 xOffset = dst.left - src.left; 869 int32 yOffset = dst.top - src.top; 870 871 // figure out which part can be blittet 872 IntRect visibleSrc(src); 873 ConvertToVisibleInTopView(&visibleSrc); 874 875 IntRect visibleSrcAtDest(src); 876 visibleSrcAtDest.OffsetBy(xOffset, yOffset); 877 ConvertToVisibleInTopView(&visibleSrcAtDest); 878 879 // clip src to visible at dest 880 visibleSrcAtDest.OffsetBy(-xOffset, -yOffset); 881 visibleSrc = visibleSrc & visibleSrcAtDest; 882 883 // do the blit, this will make sure 884 // that other more complex dirty regions 885 // are taken care of 886 BRegion* copyRegion = fWindow->GetRegion(); 887 if (!copyRegion) 888 return; 889 890 // move src rect to destination here for efficiency reasons 891 visibleSrc.OffsetBy(xOffset, yOffset); 892 893 // we need to interstect the copyRegion two times, onces 894 // at the source and once at the destination (here done 895 // the other way arround but it doesn't matter) 896 // the reason for this is that we are not supposed to visually 897 // copy children in the source rect and neither to copy onto 898 // children in the destination rect... 899 copyRegion->Set((clipping_rect)visibleSrc); 900 BRegion *screenAndUserClipping 901 = &ScreenAndUserClipping(&windowContentClipping); 902 copyRegion->IntersectWith(screenAndUserClipping); 903 copyRegion->OffsetBy(-xOffset, -yOffset); 904 copyRegion->IntersectWith(screenAndUserClipping); 905 906 // do the actual blit 907 fWindow->CopyContents(copyRegion, xOffset, yOffset); 908 909 // find the dirty region as far as we are concerned 910 IntRect dirtyDst(dst); 911 ConvertToVisibleInTopView(&dirtyDst); 912 913 BRegion* dirty = fWindow->GetRegion(); 914 if (!dirty) { 915 fWindow->RecycleRegion(copyRegion); 916 return; 917 } 918 919 // offset copyRegion to destination again 920 copyRegion->OffsetBy(xOffset, yOffset); 921 // start with destination given by user 922 dirty->Set((clipping_rect)dirtyDst); 923 // exclude the part that we could copy 924 dirty->Exclude(copyRegion); 925 926 dirty->IntersectWith(screenAndUserClipping); 927 fWindow->MarkContentDirty(*dirty); 928 929 fWindow->RecycleRegion(dirty); 930 fWindow->RecycleRegion(copyRegion); 931 } 932 933 934 // #pragma mark - 935 936 937 void 938 View::ColorUpdated(color_which which, rgb_color color) 939 { 940 float tint = B_NO_TINT; 941 942 if (fWhichViewColor == which) 943 SetViewColor(tint_color(color, fWhichViewColorTint)); 944 945 if (CurrentState()->HighUIColor(&tint) == which) 946 CurrentState()->SetHighColor(tint_color(color, tint)); 947 948 if (CurrentState()->LowUIColor(&tint) == which) 949 CurrentState()->SetLowColor(tint_color(color, tint)); 950 951 for (View* child = FirstChild(); child != NULL; 952 child = child->NextSibling()) { 953 954 child->ColorUpdated(which, color); 955 } 956 } 957 958 959 void 960 View::SetViewUIColor(color_which which, float tint) 961 { 962 if (which != B_NO_COLOR) { 963 DesktopSettings settings(fWindow->Desktop()); 964 SetViewColor(tint_color(settings.UIColor(which), tint)); 965 } 966 967 fWhichViewColor = which; 968 fWhichViewColorTint = tint; 969 } 970 971 972 color_which 973 View::ViewUIColor(float* tint) 974 { 975 if (tint != NULL) 976 *tint = fWhichViewColorTint; 977 978 return fWhichViewColor; 979 } 980 981 982 // #pragma mark - 983 984 985 void 986 View::PushState() 987 { 988 DrawState* newState = fDrawState->PushState(); 989 if (newState) { 990 fDrawState = newState; 991 // In BeAPI, B_SUBPIXEL_PRECISE is a view flag, and not affected by the 992 // view state. Our implementation moves it to the draw state, but let's 993 // be compatible with the API here and make it survive accross state 994 // changes. 995 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE); 996 } 997 } 998 999 1000 void 1001 View::PopState() 1002 { 1003 if (fDrawState->PreviousState() == NULL) { 1004 fprintf(stderr, "WARNING: User called BView(%s)::PopState(), " 1005 "but there is NO state on stack!\n", Name()); 1006 return; 1007 } 1008 1009 bool rebuildClipping = fDrawState->HasAdditionalClipping(); 1010 1011 fDrawState = fDrawState->PopState(); 1012 fDrawState->SetSubPixelPrecise(fFlags & B_SUBPIXEL_PRECISE); 1013 1014 // rebuild clipping 1015 // (the clipping from the popped state is not effective anymore) 1016 if (rebuildClipping) 1017 RebuildClipping(false); 1018 } 1019 1020 1021 // #pragma mark - 1022 1023 1024 void 1025 View::SetEventMask(uint32 eventMask, uint32 options) 1026 { 1027 fEventMask = eventMask; 1028 fEventOptions = options; 1029 } 1030 1031 1032 void 1033 View::SetCursor(ServerCursor* cursor) 1034 { 1035 if (cursor == fCursor) 1036 return; 1037 1038 if (fCursor) 1039 fCursor->ReleaseReference(); 1040 1041 fCursor = cursor; 1042 1043 if (fCursor) 1044 fCursor->AcquireReference(); 1045 } 1046 1047 1048 void 1049 View::SetPicture(ServerPicture* picture) 1050 { 1051 if (picture == fPicture) 1052 return; 1053 1054 if (fPicture != NULL) 1055 fPicture->ReleaseReference(); 1056 1057 fPicture = picture; 1058 1059 if (fPicture != NULL) 1060 fPicture->AcquireReference(); 1061 } 1062 1063 1064 void 1065 View::BlendAllLayers() 1066 { 1067 if (fPicture == NULL) 1068 return; 1069 Layer* layer = dynamic_cast<Layer*>(fPicture); 1070 if (layer == NULL) 1071 return; 1072 BlendLayer(layer); 1073 } 1074 1075 1076 void 1077 View::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, 1078 BRegion* windowContentClipping, bool deep) 1079 { 1080 if (!fVisible) { 1081 // child views cannot be visible either 1082 return; 1083 } 1084 1085 if (fViewBitmap != NULL || fViewColor != B_TRANSPARENT_COLOR) { 1086 // we can only draw within our own area 1087 BRegion* redraw; 1088 if ((fFlags & B_DRAW_ON_CHILDREN) != 0) { 1089 // The client may actually want to prevent the background to 1090 // be painted outside the user clipping. 1091 redraw = fWindow->GetRegion( 1092 ScreenAndUserClipping(windowContentClipping)); 1093 } else { 1094 // Ignore user clipping as in BeOS for painting the background. 1095 redraw = fWindow->GetRegion( 1096 _ScreenClipping(windowContentClipping)); 1097 } 1098 if (!redraw) 1099 return; 1100 // add the current clipping 1101 redraw->IntersectWith(effectiveClipping); 1102 1103 Overlay* overlayCookie = _Overlay(); 1104 1105 if (fViewBitmap != NULL && overlayCookie == NULL) { 1106 // draw view bitmap 1107 // TODO: support other options! 1108 BRect rect = fBitmapDestination; 1109 PenToScreenTransform().Apply(&rect); 1110 1111 align_rect_to_pixels(&rect); 1112 1113 if (fBitmapOptions & B_TILE_BITMAP_Y) { 1114 // move rect up as much as needed 1115 while (rect.top > redraw->Frame().top) 1116 rect.OffsetBy(0.0, -(rect.Height() + 1)); 1117 } 1118 if (fBitmapOptions & B_TILE_BITMAP_X) { 1119 // move rect left as much as needed 1120 while (rect.left > redraw->Frame().left) 1121 rect.OffsetBy(-(rect.Width() + 1), 0.0); 1122 } 1123 1124 // XXX: locking removed because the Window keeps the engine locked 1125 // because it keeps track of syncing right now 1126 1127 // lock the drawing engine for as long as we need the clipping 1128 // to be valid 1129 if (rect.IsValid()/* && drawingEngine->Lock()*/) { 1130 drawingEngine->ConstrainClippingRegion(redraw); 1131 1132 drawing_mode oldMode; 1133 drawingEngine->SetDrawingMode(B_OP_COPY, oldMode); 1134 1135 if (fBitmapOptions & B_TILE_BITMAP) { 1136 // tile across entire view 1137 1138 float start = rect.left; 1139 while (rect.top < redraw->Frame().bottom) { 1140 while (rect.left < redraw->Frame().right) { 1141 drawingEngine->DrawBitmap(fViewBitmap, 1142 fBitmapSource, rect, fBitmapOptions); 1143 rect.OffsetBy(rect.Width() + 1, 0.0); 1144 } 1145 rect.OffsetBy(start - rect.left, rect.Height() + 1); 1146 } 1147 // nothing left to be drawn 1148 redraw->MakeEmpty(); 1149 } else if (fBitmapOptions & B_TILE_BITMAP_X) { 1150 // tile in x direction 1151 1152 while (rect.left < redraw->Frame().right) { 1153 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1154 rect, fBitmapOptions); 1155 rect.OffsetBy(rect.Width() + 1, 0.0); 1156 } 1157 // remove horizontal stripe from clipping 1158 rect.left = redraw->Frame().left; 1159 rect.right = redraw->Frame().right; 1160 redraw->Exclude(rect); 1161 } else if (fBitmapOptions & B_TILE_BITMAP_Y) { 1162 // tile in y direction 1163 1164 while (rect.top < redraw->Frame().bottom) { 1165 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1166 rect, fBitmapOptions); 1167 rect.OffsetBy(0.0, rect.Height() + 1); 1168 } 1169 // remove vertical stripe from clipping 1170 rect.top = redraw->Frame().top; 1171 rect.bottom = redraw->Frame().bottom; 1172 redraw->Exclude(rect); 1173 } else { 1174 // no tiling at all 1175 1176 drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, 1177 rect, fBitmapOptions); 1178 redraw->Exclude(rect); 1179 } 1180 1181 drawingEngine->SetDrawingMode(oldMode); 1182 1183 // NOTE: It is ok not to reset the clipping, that 1184 // would only waste time 1185 // drawingEngine->Unlock(); 1186 } 1187 1188 } 1189 1190 if (fViewColor != B_TRANSPARENT_COLOR) { 1191 // fill visible region with view color, 1192 // this version of FillRegion ignores any 1193 // clipping, that's why "redraw" needs to 1194 // be correct 1195 // see #634 1196 // if (redraw->Frame().left < 0 || redraw->Frame().top < 0) { 1197 // char message[1024]; 1198 // BRect c = effectiveClipping->Frame(); 1199 // BRect w = windowContentClipping->Frame(); 1200 // BRect r = redraw->Frame(); 1201 // sprintf(message, "invalid background: current clipping: (%d, %d)->(%d, %d), " 1202 // "window content: (%d, %d)->(%d, %d), redraw: (%d, %d)->(%d, %d)", 1203 // (int)c.left, (int)c.top, (int)c.right, (int)c.bottom, 1204 // (int)w.left, (int)w.top, (int)w.right, (int)w.bottom, 1205 // (int)r.left, (int)r.top, (int)r.right, (int)r.bottom); 1206 // debugger(message); 1207 // } 1208 1209 drawingEngine->FillRegion(*redraw, overlayCookie != NULL 1210 ? overlayCookie->Color() : fViewColor); 1211 } 1212 1213 fWindow->RecycleRegion(redraw); 1214 } 1215 1216 fBackgroundDirty = false; 1217 1218 // let children draw 1219 if (deep) { 1220 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1221 child->Draw(drawingEngine, effectiveClipping, 1222 windowContentClipping, deep); 1223 } 1224 } 1225 } 1226 1227 1228 // #pragma mark - 1229 1230 1231 void 1232 View::MouseDown(BMessage* message, BPoint where) 1233 { 1234 // empty hook method 1235 } 1236 1237 1238 void 1239 View::MouseUp(BMessage* message, BPoint where) 1240 { 1241 // empty hook method 1242 } 1243 1244 1245 void 1246 View::MouseMoved(BMessage* message, BPoint where) 1247 { 1248 // empty hook method 1249 } 1250 1251 1252 // #pragma mark - 1253 1254 1255 void 1256 View::SetHidden(bool hidden) 1257 { 1258 if (fHidden != hidden) { 1259 fHidden = hidden; 1260 1261 // recurse into children and update their visible flag 1262 bool oldVisible = fVisible; 1263 UpdateVisibleDeep(fParent ? fParent->IsVisible() : !fHidden); 1264 if (oldVisible != fVisible) { 1265 // Include or exclude us from the parent area, and update the 1266 // children's clipping as well when the view will be visible 1267 if (fParent) 1268 fParent->RebuildClipping(fVisible); 1269 else 1270 RebuildClipping(fVisible); 1271 1272 if (fWindow) { 1273 // trigger a redraw 1274 IntRect clippedBounds = Bounds(); 1275 ConvertToVisibleInTopView(&clippedBounds); 1276 1277 BRegion dirty; 1278 dirty.Set((clipping_rect)clippedBounds); 1279 fWindow->MarkContentDirty(dirty); 1280 } 1281 } 1282 } 1283 } 1284 1285 1286 bool 1287 View::IsHidden() const 1288 { 1289 return fHidden; 1290 } 1291 1292 1293 void 1294 View::UpdateVisibleDeep(bool parentVisible) 1295 { 1296 bool wasVisible = fVisible; 1297 1298 fVisible = parentVisible && !fHidden; 1299 for (View* child = FirstChild(); child; child = child->NextSibling()) 1300 child->UpdateVisibleDeep(fVisible); 1301 1302 // overlay handling 1303 1304 Overlay* overlay = _Overlay(); 1305 if (overlay == NULL) 1306 return; 1307 1308 if (fVisible && !wasVisible) 1309 _UpdateOverlayView(); 1310 else if (!fVisible && wasVisible) 1311 overlay->Hide(); 1312 } 1313 1314 1315 // #pragma mark - 1316 1317 1318 void 1319 View::MarkBackgroundDirty() 1320 { 1321 if (fBackgroundDirty) 1322 return; 1323 fBackgroundDirty = true; 1324 for (View* child = FirstChild(); child; child = child->NextSibling()) 1325 child->MarkBackgroundDirty(); 1326 } 1327 1328 1329 void 1330 View::AddTokensForViewsInRegion(BPrivate::PortLink& link, BRegion& region, 1331 BRegion* windowContentClipping) 1332 { 1333 if (!fVisible) 1334 return; 1335 1336 { 1337 // NOTE: use scope in order to reduce stack space requirements 1338 1339 // This check will prevent descending the view hierarchy 1340 // any further than necessary 1341 IntRect screenBounds(Bounds()); 1342 LocalToScreenTransform().Apply(&screenBounds); 1343 if (!region.Intersects((clipping_rect)screenBounds)) 1344 return; 1345 1346 // Unfortunately, we intersecting another region, but otherwise 1347 // we couldn't provide the exact update rect to the client 1348 BRegion localDirty = _ScreenClipping(windowContentClipping); 1349 localDirty.IntersectWith(®ion); 1350 if (localDirty.CountRects() > 0) { 1351 link.Attach<int32>(fToken); 1352 link.Attach<BRect>(localDirty.Frame()); 1353 } 1354 } 1355 1356 for (View* child = FirstChild(); child; child = child->NextSibling()) 1357 child->AddTokensForViewsInRegion(link, region, windowContentClipping); 1358 } 1359 1360 1361 void 1362 View::PrintToStream() const 1363 { 1364 printf("View: %s\n", Name()); 1365 printf(" fToken: %" B_PRId32 "\n", fToken); 1366 printf(" fFrame: IntRect(%" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ", %" B_PRId32 ")\n", 1367 fFrame.left, fFrame.top, fFrame.right, fFrame.bottom); 1368 printf(" fScrollingOffset: IntPoint(%" B_PRId32 ", %" B_PRId32 ")\n", 1369 fScrollingOffset.x, fScrollingOffset.y); 1370 printf(" fHidden: %d\n", fHidden); 1371 printf(" fVisible: %d\n", fVisible); 1372 printf(" fWindow: %p\n", fWindow); 1373 printf(" fParent: %p\n", fParent); 1374 printf(" fLocalClipping:\n"); 1375 fLocalClipping.PrintToStream(); 1376 printf(" fScreenClipping:\n"); 1377 fScreenClipping.PrintToStream(); 1378 printf(" valid: %d\n", fScreenClippingValid); 1379 1380 printf(" fUserClipping:\n"); 1381 if (fUserClipping != NULL) 1382 fUserClipping->PrintToStream(); 1383 else 1384 printf(" none\n"); 1385 1386 printf(" fScreenAndUserClipping:\n"); 1387 if (fScreenAndUserClipping != NULL) 1388 fScreenAndUserClipping->PrintToStream(); 1389 else 1390 printf(" invalid\n"); 1391 1392 printf(" state:\n"); 1393 printf(" user clipping: %d\n", fDrawState->HasClipping()); 1394 BPoint origin = fDrawState->CombinedOrigin(); 1395 printf(" origin: BPoint(%.1f, %.1f)\n", origin.x, origin.y); 1396 printf(" scale: %.2f\n", fDrawState->CombinedScale()); 1397 printf("\n"); 1398 } 1399 1400 1401 void 1402 View::RebuildClipping(bool deep) 1403 { 1404 // the clipping spans over the bounds area 1405 fLocalClipping.Set((clipping_rect)Bounds()); 1406 1407 if (View* child = FirstChild()) { 1408 // if this view does not draw over children, 1409 // exclude all children from the clipping 1410 if ((fFlags & B_DRAW_ON_CHILDREN) == 0) { 1411 BRegion* childrenRegion = fWindow->GetRegion(); 1412 if (!childrenRegion) 1413 return; 1414 1415 for (; child; child = child->NextSibling()) { 1416 if (child->IsVisible() 1417 && (child->fFlags & B_TRANSPARENT_BACKGROUND) == 0) { 1418 childrenRegion->Include((clipping_rect)child->Frame()); 1419 } 1420 } 1421 1422 fLocalClipping.Exclude(childrenRegion); 1423 fWindow->RecycleRegion(childrenRegion); 1424 } 1425 // if the operation is "deep", make children rebuild their 1426 // clipping too 1427 if (deep) { 1428 for (child = FirstChild(); child; child = child->NextSibling()) 1429 child->RebuildClipping(true); 1430 } 1431 } 1432 1433 // add the user clipping in case there is one 1434 if (fDrawState->HasClipping()) { 1435 // NOTE: in case the user sets a user defined clipping region, 1436 // rebuilding the clipping is a bit more expensive because there 1437 // is no separate "drawing region"... on the other 1438 // hand, views for which this feature is actually used will 1439 // probably not have any children, so it is not that expensive 1440 // after all 1441 if (fUserClipping == NULL) { 1442 fUserClipping = new (nothrow) BRegion; 1443 if (fUserClipping == NULL) 1444 return; 1445 } 1446 1447 fDrawState->GetCombinedClippingRegion(fUserClipping); 1448 } else { 1449 delete fUserClipping; 1450 fUserClipping = NULL; 1451 } 1452 1453 delete fScreenAndUserClipping; 1454 fScreenAndUserClipping = NULL; 1455 fScreenClippingValid = false; 1456 } 1457 1458 1459 BRegion& 1460 View::ScreenAndUserClipping(BRegion* windowContentClipping, bool force) const 1461 { 1462 // no user clipping - return screen clipping directly 1463 if (fUserClipping == NULL) 1464 return _ScreenClipping(windowContentClipping, force); 1465 1466 // combined screen and user clipping already valid 1467 if (fScreenAndUserClipping != NULL) 1468 return *fScreenAndUserClipping; 1469 1470 // build a new combined user and screen clipping 1471 fScreenAndUserClipping = new (nothrow) BRegion(*fUserClipping); 1472 if (fScreenAndUserClipping == NULL) 1473 return fScreenClipping; 1474 1475 LocalToScreenTransform().Apply(fScreenAndUserClipping); 1476 fScreenAndUserClipping->IntersectWith( 1477 &_ScreenClipping(windowContentClipping, force)); 1478 return *fScreenAndUserClipping; 1479 } 1480 1481 1482 void 1483 View::InvalidateScreenClipping() 1484 { 1485 // TODO: appearantly, we are calling ScreenClipping() on 1486 // views who's parents don't have a valid screen clipping yet, 1487 // this messes up the logic that for any given view with 1488 // fScreenClippingValid == false, all children have 1489 // fScreenClippingValid == false too. If this could be made the 1490 // case, we could save some performance here with the commented 1491 // out check, since InvalidateScreenClipping() might be called 1492 // frequently. 1493 // TODO: investigate, if InvalidateScreenClipping() could be 1494 // called in "deep" and "non-deep" mode, ie. see if there are 1495 // any cases where the children would still have valid screen 1496 // clipping, even though the parent's screen clipping becomes 1497 // invalid. 1498 // if (!fScreenClippingValid) 1499 // return; 1500 1501 delete fScreenAndUserClipping; 1502 fScreenAndUserClipping = NULL; 1503 fScreenClippingValid = false; 1504 // invalidate the childrens screen clipping as well 1505 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1506 child->InvalidateScreenClipping(); 1507 } 1508 } 1509 1510 1511 BRegion& 1512 View::_ScreenClipping(BRegion* windowContentClipping, bool force) const 1513 { 1514 if (!fScreenClippingValid || force) { 1515 fScreenClipping = fLocalClipping; 1516 LocalToScreenTransform().Apply(&fScreenClipping); 1517 1518 // see if parts of our bounds are hidden underneath 1519 // the parent, the local clipping does not account for this 1520 IntRect clippedBounds = Bounds(); 1521 ConvertToVisibleInTopView(&clippedBounds); 1522 if (clippedBounds.Width() < fScreenClipping.Frame().Width() 1523 || clippedBounds.Height() < fScreenClipping.Frame().Height()) { 1524 BRegion temp; 1525 temp.Set((clipping_rect)clippedBounds); 1526 fScreenClipping.IntersectWith(&temp); 1527 } 1528 1529 fScreenClipping.IntersectWith(windowContentClipping); 1530 fScreenClippingValid = true; 1531 } 1532 1533 return fScreenClipping; 1534 } 1535 1536 1537 void 1538 View::_MoveScreenClipping(int32 x, int32 y, bool deep) 1539 { 1540 if (fScreenClippingValid) { 1541 fScreenClipping.OffsetBy(x, y); 1542 delete fScreenAndUserClipping; 1543 fScreenAndUserClipping = NULL; 1544 } 1545 1546 if (deep) { 1547 // move the childrens screen clipping as well 1548 for (View* child = FirstChild(); child; child = child->NextSibling()) { 1549 child->_MoveScreenClipping(x, y, deep); 1550 } 1551 } 1552 } 1553 1554