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