1 2 #include <stdio.h> 3 4 #include <List.h> 5 #include <View.h> // for resize modes 6 7 #include "Desktop.h" 8 #include "DrawingEngine.h" 9 #include "WindowLayer.h" 10 11 #include "ViewLayer.h" 12 13 extern BWindow* wind; 14 15 // constructor 16 ViewLayer::ViewLayer(BRect frame, const char* name, 17 uint32 resizeMode, uint32 flags, 18 rgb_color viewColor) 19 : fName(name), 20 21 fFrame(frame), 22 fScrollingOffset(0.0, 0.0), 23 24 fViewColor(viewColor), 25 26 fResizeMode(resizeMode), 27 fFlags(flags), 28 29 // ViewLayers start visible by default 30 fHidden(false), 31 fVisible(true), 32 33 fWindow(NULL), 34 fParent(NULL), 35 36 fFirstChild(NULL), 37 fPreviousSibling(NULL), 38 fNextSibling(NULL), 39 fLastChild(NULL), 40 41 fCurrentChild(NULL), 42 43 fLocalClipping(Bounds()), 44 fScreenClipping(), 45 fScreenClippingValid(false) 46 { 47 fFrame.left = float((int32)fFrame.left); 48 fFrame.top = float((int32)fFrame.top); 49 fFrame.right = float((int32)fFrame.right); 50 fFrame.bottom = float((int32)fFrame.bottom); 51 } 52 53 // destructor 54 ViewLayer::~ViewLayer() 55 { 56 // iterate over children and delete each one 57 ViewLayer* layer = fFirstChild; 58 while (layer) { 59 ViewLayer* toast = layer; 60 layer = layer->fNextSibling; 61 delete toast; 62 } 63 } 64 65 // Bounds 66 BRect 67 ViewLayer::Bounds() const 68 { 69 BRect bounds(fScrollingOffset.x, fScrollingOffset.y, 70 fScrollingOffset.x + fFrame.Width(), 71 fScrollingOffset.y + fFrame.Height()); 72 return bounds; 73 } 74 75 // ConvertToVisibleInTopView 76 void 77 ViewLayer::ConvertToVisibleInTopView(BRect* bounds) const 78 { 79 *bounds = *bounds & Bounds(); 80 // NOTE: this step is necessary even if we don't have a parent! 81 ConvertToParent(bounds); 82 83 if (fParent) 84 fParent->ConvertToVisibleInTopView(bounds); 85 } 86 87 // AttachedToWindow 88 void 89 ViewLayer::AttachedToWindow(WindowLayer* window) 90 { 91 fWindow = window; 92 93 for (ViewLayer* child = FirstChild(); child; child = NextChild()) 94 child->AttachedToWindow(window); 95 } 96 97 98 // DetachedFromWindow 99 void 100 ViewLayer::DetachedFromWindow() 101 { 102 fWindow = NULL; 103 for (ViewLayer* child = FirstChild(); child; child = NextChild()) 104 child->DetachedFromWindow(); 105 } 106 107 // AddChild 108 void 109 ViewLayer::AddChild(ViewLayer* layer) 110 { 111 if (layer->fParent) { 112 printf("ViewLayer::AddChild() - ViewLayer already has a parent\n"); 113 return; 114 } 115 116 layer->fParent = this; 117 118 if (!fLastChild) { 119 // no children yet 120 fFirstChild = layer; 121 } else { 122 // append layer to formerly last child 123 fLastChild->fNextSibling = layer; 124 layer->fPreviousSibling = fLastChild; 125 } 126 fLastChild = layer; 127 128 if (layer->IsVisible()) 129 RebuildClipping(false); 130 131 if (fWindow) { 132 layer->AttachedToWindow(fWindow); 133 134 if (fVisible && layer->IsVisible()) { 135 // trigger redraw 136 BRect clippedFrame = layer->Frame(); 137 ConvertToVisibleInTopView(&clippedFrame); 138 BRegion dirty(clippedFrame); 139 fWindow->MarkContentDirty(&dirty); 140 } 141 } 142 } 143 144 // RemoveChild 145 bool 146 ViewLayer::RemoveChild(ViewLayer* layer) 147 { 148 if (layer->fParent != this) { 149 printf("ViewLayer::RemoveChild(%p - %s) - ViewLayer is not child of this (%p) layer!\n", layer, layer ? layer->Name() : NULL, this); 150 return false; 151 } 152 153 layer->fParent = NULL; 154 155 if (fLastChild == layer) 156 fLastChild = layer->fPreviousSibling; 157 // layer->fNextSibling would be NULL 158 159 if (fFirstChild == layer ) 160 fFirstChild = layer->fNextSibling; 161 // layer->fPreviousSibling would be NULL 162 163 // connect child before and after layer 164 if (layer->fPreviousSibling) 165 layer->fPreviousSibling->fNextSibling = layer->fNextSibling; 166 167 if (layer->fNextSibling) 168 layer->fNextSibling->fPreviousSibling = layer->fPreviousSibling; 169 170 // layer has no siblings anymore 171 layer->fPreviousSibling = NULL; 172 layer->fNextSibling = NULL; 173 174 if (layer->IsVisible()) 175 RebuildClipping(false); 176 177 if (fWindow) { 178 layer->DetachedFromWindow(); 179 180 if (fVisible && layer->IsVisible()) { 181 // trigger redraw 182 BRect clippedFrame = layer->Frame(); 183 ConvertToVisibleInTopView(&clippedFrame); 184 BRegion dirty(clippedFrame); 185 fWindow->MarkContentDirty(&dirty); 186 } 187 } 188 189 return true; 190 } 191 192 // FirstChild 193 ViewLayer* 194 ViewLayer::FirstChild() const 195 { 196 fCurrentChild = fFirstChild; 197 return fCurrentChild; 198 } 199 200 // PreviousChild 201 ViewLayer* 202 ViewLayer::PreviousChild() const 203 { 204 fCurrentChild = fCurrentChild->fPreviousSibling; 205 return fCurrentChild; 206 } 207 208 // NextChild 209 ViewLayer* 210 ViewLayer::NextChild() const 211 { 212 fCurrentChild = fCurrentChild->fNextSibling; 213 return fCurrentChild; 214 } 215 216 // LastChild 217 ViewLayer* 218 ViewLayer::LastChild() const 219 { 220 fCurrentChild = fLastChild; 221 return fCurrentChild; 222 } 223 224 // TopLayer 225 ViewLayer* 226 ViewLayer::TopLayer() 227 { 228 // returns the top level view of the hirarchy, 229 // it doesn't have to be the top level of a window 230 231 if (fParent) 232 return fParent->TopLayer(); 233 234 return this; 235 } 236 237 // CountChildren 238 uint32 239 ViewLayer::CountChildren(bool deep) const 240 { 241 uint32 count = 0; 242 for (ViewLayer* child = FirstChild(); child; child = NextChild()) { 243 count++; 244 if (deep) { 245 count += child->CountChildren(deep); 246 } 247 } 248 return count; 249 } 250 251 // CollectTokensForChildren 252 void 253 ViewLayer::CollectTokensForChildren(BList* tokenMap) const 254 { 255 for (ViewLayer* child = FirstChild(); child; child = NextChild()) { 256 tokenMap->AddItem((void*)child); 257 child->CollectTokensForChildren(tokenMap); 258 } 259 } 260 261 // ViewAt 262 ViewLayer* 263 ViewLayer::ViewAt(const BPoint& where, BRegion* windowContentClipping) 264 { 265 if (!fVisible) 266 return NULL; 267 268 if (ScreenClipping(windowContentClipping).Contains(where)) 269 return this; 270 271 for (ViewLayer* child = FirstChild(); child; child = NextChild()) { 272 ViewLayer* layer = child->ViewAt(where, windowContentClipping); 273 if (layer) 274 return layer; 275 } 276 return NULL; 277 } 278 279 // ConvertToParent 280 void 281 ViewLayer::ConvertToParent(BPoint* point) const 282 { 283 // remove scrolling offset and convert to parent coordinate space 284 point->x += fFrame.left - fScrollingOffset.x; 285 point->y += fFrame.top - fScrollingOffset.y; 286 } 287 288 // ConvertToParent 289 void 290 ViewLayer::ConvertToParent(BRect* rect) const 291 { 292 // remove scrolling offset and convert to parent coordinate space 293 rect->OffsetBy(fFrame.left - fScrollingOffset.x, 294 fFrame.top - fScrollingOffset.y); 295 } 296 297 // ConvertToParent 298 void 299 ViewLayer::ConvertToParent(BRegion* region) const 300 { 301 // remove scrolling offset and convert to parent coordinate space 302 region->OffsetBy(fFrame.left - fScrollingOffset.x, 303 fFrame.top - fScrollingOffset.y); 304 } 305 306 // ConvertFromParent 307 void 308 ViewLayer::ConvertFromParent(BPoint* point) const 309 { 310 // remove scrolling offset and convert to parent coordinate space 311 point->x += fScrollingOffset.x - fFrame.left; 312 point->y += fScrollingOffset.y - fFrame.top; 313 } 314 315 // ConvertFromParent 316 void 317 ViewLayer::ConvertFromParent(BRect* rect) const 318 { 319 // remove scrolling offset and convert to parent coordinate space 320 rect->OffsetBy(fScrollingOffset.x - fFrame.left, 321 fScrollingOffset.y - fFrame.top); 322 } 323 324 // ConvertFromParent 325 void 326 ViewLayer::ConvertFromParent(BRegion* region) const 327 { 328 // remove scrolling offset and convert to parent coordinate space 329 region->OffsetBy(fScrollingOffset.x - fFrame.left, 330 fScrollingOffset.y - fFrame.top); 331 } 332 333 // ConvertToTop 334 void 335 ViewLayer::ConvertToTop(BPoint* point) const 336 { 337 ConvertToParent(point); 338 339 if (fParent) 340 fParent->ConvertToTop(point); 341 } 342 343 // ConvertToTop 344 void 345 ViewLayer::ConvertToTop(BRect* rect) const 346 { 347 ConvertToParent(rect); 348 349 if (fParent) 350 fParent->ConvertToTop(rect); 351 } 352 353 // ConvertToTop 354 void 355 ViewLayer::ConvertToTop(BRegion* region) const 356 { 357 ConvertToParent(region); 358 359 if (fParent) 360 fParent->ConvertToTop(region); 361 } 362 363 // ConvertFromTop 364 void 365 ViewLayer::ConvertFromTop(BPoint* point) const 366 { 367 ConvertFromParent(point); 368 369 if (fParent) 370 fParent->ConvertFromTop(point); 371 } 372 373 // ConvertFromTop 374 void 375 ViewLayer::ConvertFromTop(BRect* rect) const 376 { 377 ConvertFromParent(rect); 378 379 if (fParent) 380 fParent->ConvertFromTop(rect); 381 } 382 383 // ConvertFromTop 384 void 385 ViewLayer::ConvertFromTop(BRegion* region) const 386 { 387 ConvertFromParent(region); 388 389 if (fParent) 390 fParent->ConvertFromTop(region); 391 } 392 393 // SetName 394 void 395 ViewLayer::SetName(const char* string) 396 { 397 fName.SetTo(string); 398 } 399 400 // #pragma mark - 401 402 // MoveBy 403 void 404 ViewLayer::MoveBy(int32 x, int32 y, BRegion* dirtyRegion) 405 { 406 if (x == 0 && y == 0) 407 return; 408 409 fFrame.OffsetBy(x, y); 410 411 // to move on screen, we must not be hidden and we must have a parent 412 if (fVisible && fParent && dirtyRegion) { 413 #if 0 414 // based on redraw on new location 415 // the place were we are now visible 416 BRect newVisibleBounds = Bounds(); 417 // we can use the frame of the old 418 // local clipping to see which parts need invalidation 419 BRect oldVisibleBounds(Bounds()); 420 oldVisibleBounds.OffsetBy(-x, -y); 421 ConvertToTop(&oldVisibleBounds); 422 423 ConvertToVisibleInTopView(&newVisibleBounds); 424 425 dirtyRegion->Include(oldVisibleBounds); 426 // newVisibleBounds already is in screen coords 427 dirtyRegion->Include(newVisibleBounds); 428 #else 429 // blitting version, invalidates 430 // old contents 431 BRect oldVisibleBounds(Bounds()); 432 oldVisibleBounds.OffsetBy(-x, -y); 433 ConvertToTop(&oldVisibleBounds); 434 435 BRect newVisibleBounds(Bounds()); 436 // NOTE: using ConvertToVisibleInTopView() 437 // instead of ConvertToTop()! see below 438 ConvertToVisibleInTopView(&newVisibleBounds); 439 440 newVisibleBounds.OffsetBy(-x, -y); 441 442 // clipping oldVisibleBounds to newVisibleBounds 443 // makes sure we don't copy parts hidden under 444 // parent views 445 BRegion copyRegion(oldVisibleBounds & newVisibleBounds); 446 fWindow->CopyContents(©Region, x, y); 447 448 BRegion dirty(oldVisibleBounds); 449 newVisibleBounds.OffsetBy(x, y); 450 dirty.Exclude(newVisibleBounds); 451 dirtyRegion->Include(&dirty); 452 #endif 453 } 454 455 if (!fParent) { 456 // the top view's screen clipping does not change, 457 // because no parts are clipped away from parent 458 // views 459 _MoveScreenClipping(x, y, true); 460 } else { 461 // parts might have been revealed from underneath 462 // the parent, or might now be hidden underneath 463 // the parent, this is taken care of when building 464 // the screen clipping 465 InvalidateScreenClipping(true); 466 } 467 } 468 469 // ResizeBy 470 void 471 ViewLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) 472 { 473 if (x == 0 && y == 0) 474 return; 475 476 fFrame.right += x; 477 fFrame.bottom += y; 478 479 if (fVisible && dirtyRegion) { 480 BRect oldBounds(Bounds()); 481 oldBounds.right -= x; 482 oldBounds.bottom -= y; 483 484 BRegion dirty(Bounds()); 485 dirty.Include(oldBounds); 486 487 if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) { 488 // the dirty region is just the difference of 489 // old and new bounds 490 dirty.Exclude(oldBounds & Bounds()); 491 } 492 493 InvalidateScreenClipping(true); 494 495 if (dirty.CountRects() > 0) { 496 // exclude children, they are expected to 497 // include their own dirty regions in ParentResized() 498 for (ViewLayer* child = FirstChild(); child; child = NextChild()) { 499 if (child->IsVisible()) { 500 BRect previousChildVisible(child->Frame() & oldBounds & Bounds()); 501 if (dirty.Frame().Intersects(previousChildVisible)) { 502 dirty.Exclude(previousChildVisible); 503 } 504 } 505 } 506 507 ConvertToTop(&dirty); 508 dirtyRegion->Include(&dirty); 509 } 510 } 511 512 // layout the children 513 for (ViewLayer* child = FirstChild(); child; child = NextChild()) 514 child->ParentResized(x, y, dirtyRegion); 515 516 // at this point, children are at their new locations, 517 // so we can rebuild the clipping 518 // TODO: when the implementation of Hide() and Show() is 519 // complete, see if this should be avoided 520 RebuildClipping(false); 521 } 522 523 // ParentResized 524 void 525 ViewLayer::ParentResized(int32 x, int32 y, BRegion* dirtyRegion) 526 { 527 uint16 rm = fResizeMode & 0x0000FFFF; 528 BRect newFrame = fFrame; 529 530 // follow with left side 531 if ((rm & 0x0F00U) == _VIEW_RIGHT_ << 8) 532 newFrame.left += x; 533 else if ((rm & 0x0F00U) == _VIEW_CENTER_ << 8) 534 newFrame.left += x / 2; 535 536 // follow with right side 537 if ((rm & 0x000FU) == _VIEW_RIGHT_) 538 newFrame.right += x; 539 else if ((rm & 0x000FU) == _VIEW_CENTER_) 540 newFrame.right += x / 2; 541 542 // follow with top side 543 if ((rm & 0xF000U) == _VIEW_BOTTOM_ << 12) 544 newFrame.top += y; 545 else if ((rm & 0xF000U) == _VIEW_CENTER_ << 12) 546 newFrame.top += y / 2; 547 548 // follow with bottom side 549 if ((rm & 0x00F0U) == _VIEW_BOTTOM_ << 4) 550 newFrame.bottom += y; 551 else if ((rm & 0x00F0U) == _VIEW_CENTER_ << 4) 552 newFrame.bottom += y / 2; 553 554 if (newFrame != fFrame) { 555 // careful, MoveBy will change fFrame 556 int32 widthDiff = (int32)(newFrame.Width() - fFrame.Width()); 557 int32 heightDiff = (int32)(newFrame.Height() - fFrame.Height()); 558 559 MoveBy(newFrame.left - fFrame.left, 560 newFrame.top - fFrame.top, dirtyRegion); 561 562 ResizeBy(widthDiff, heightDiff, dirtyRegion); 563 } 564 } 565 566 // ScrollBy 567 void 568 ViewLayer::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion) 569 { 570 // blitting version, invalidates 571 // old contents 572 573 // remember old bounds for tracking dirty region 574 BRect oldBounds(Bounds()); 575 // find the area of the view that can be scrolled, 576 // contents are shifted in the opposite direction from scrolling 577 BRect stillVisibleBounds(oldBounds); 578 stillVisibleBounds.OffsetBy(x, y); 579 580 // NOTE: using ConvertToVisibleInTopView() 581 // instead of ConvertToTop(), this makes 582 // sure we don't try to move or invalidate an 583 // area hidden underneath the parent view 584 ConvertToVisibleInTopView(&oldBounds); 585 ConvertToVisibleInTopView(&stillVisibleBounds); 586 587 fScrollingOffset.x += x; 588 fScrollingOffset.y += y; 589 590 // do the blit, this will make sure 591 // that other more complex dirty regions 592 // are taken care of 593 BRegion copyRegion(stillVisibleBounds); 594 fWindow->CopyContents(©Region, -x, -y); 595 596 // find the dirty region as far as we are 597 // concerned 598 BRegion dirty(oldBounds); 599 stillVisibleBounds.OffsetBy(-x, -y); 600 dirty.Exclude(stillVisibleBounds); 601 dirtyRegion->Include(&dirty); 602 603 // the screen clipping of this view and it's 604 // childs is no longer valid 605 InvalidateScreenClipping(true); 606 RebuildClipping(false); 607 } 608 609 // #pragma mark - 610 611 // Draw 612 void 613 ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, 614 BRegion* windowContentClipping, bool deep) 615 { 616 // we can only draw within our own area 617 BRegion redraw(ScreenClipping(windowContentClipping)); 618 // add the current clipping 619 redraw.IntersectWith(effectiveClipping); 620 621 // fill visible region with white 622 drawingEngine->FillRegion(&redraw, (rgb_color){ 255, 255, 255, 255 }); 623 624 // let children draw 625 if (deep) { 626 // before passing the clipping on to children, exclude our 627 // own region from the available clipping 628 effectiveClipping->Exclude(&ScreenClipping(windowContentClipping)); 629 630 for (ViewLayer* child = FirstChild(); child; child = NextChild()) { 631 child->Draw(drawingEngine, effectiveClipping, 632 windowContentClipping, deep); 633 } 634 } 635 } 636 637 // ClientDraw 638 void 639 ViewLayer::ClientDraw(DrawingEngine* drawingEngine, BRegion* effectiveClipping) 640 { 641 BRect b(Bounds()); 642 b.OffsetTo(0.0, 0.0); 643 ConvertToTop(&b); 644 645 if (drawingEngine->Lock()) { 646 drawingEngine->ConstrainClipping(effectiveClipping); 647 648 // draw a frame with the view color 649 drawingEngine->StrokeRect(b, fViewColor); 650 b.InsetBy(1, 1); 651 drawingEngine->StrokeRect(b, fViewColor); 652 b.InsetBy(1, 1); 653 drawingEngine->StrokeRect(b, fViewColor); 654 b.InsetBy(1, 1); 655 drawingEngine->StrokeLine(b.LeftTop(), b.RightBottom(), fViewColor); 656 657 drawingEngine->Unlock(); 658 } 659 } 660 661 // #pragma mark - 662 663 // SetHidden 664 void 665 ViewLayer::SetHidden(bool hidden) 666 { 667 // TODO: test... 668 669 if (fHidden != hidden) { 670 fHidden = hidden; 671 672 // recurse into children and update their visible flag 673 if (fParent) { 674 bool olfVisible = fVisible; 675 UpdateVisibleDeep(fParent->IsVisible()); 676 if (olfVisible != fVisible) { 677 // include or exclude us from the parent area 678 fParent->RebuildClipping(false); 679 if (fWindow) { 680 // trigger a redraw 681 BRect clippedBounds = Bounds(); 682 ConvertToVisibleInTopView(&clippedBounds); 683 BRegion dirty(clippedBounds); 684 fWindow->MarkContentDirty(&dirty); 685 } 686 } 687 } else { 688 UpdateVisibleDeep(true); 689 } 690 } 691 } 692 693 // IsHidden 694 bool 695 ViewLayer::IsHidden() const 696 { 697 return fHidden; 698 } 699 700 // UpdateVisibleDeep 701 void 702 ViewLayer::UpdateVisibleDeep(bool parentVisible) 703 { 704 fVisible = parentVisible && !fHidden; 705 for (ViewLayer* child = FirstChild(); child; child = NextChild()) 706 child->UpdateVisibleDeep(fVisible); 707 } 708 709 // PrintToStream 710 void 711 ViewLayer::PrintToStream() const 712 { 713 } 714 715 // RebuildClipping 716 void 717 ViewLayer::RebuildClipping(bool deep) 718 { 719 // the clipping spans over the bounds area 720 fLocalClipping.Set(Bounds()); 721 722 // exclude all childs from the clipping 723 for (ViewLayer* child = FirstChild(); child; child = NextChild()) { 724 if (child->IsVisible()) 725 fLocalClipping.Exclude(child->Frame()); 726 727 if (deep) 728 child->RebuildClipping(deep); 729 } 730 731 fScreenClippingValid = false; 732 } 733 734 // ScreenClipping 735 BRegion& 736 ViewLayer::ScreenClipping(BRegion* windowContentClipping, bool force) const 737 { 738 if (!fScreenClippingValid || force) { 739 fScreenClipping = fLocalClipping; 740 ConvertToTop(&fScreenClipping); 741 742 // see if we parts of our bounds are hidden underneath 743 // the parent, the local clipping does not account for this 744 BRect clippedBounds = Bounds(); 745 ConvertToVisibleInTopView(&clippedBounds); 746 if (clippedBounds.Width() < fScreenClipping.Frame().Width() || 747 clippedBounds.Height() < fScreenClipping.Frame().Height()) { 748 BRegion temp(clippedBounds); 749 fScreenClipping.IntersectWith(&temp); 750 } 751 752 fScreenClipping.IntersectWith(windowContentClipping); 753 fScreenClippingValid = true; 754 } 755 return fScreenClipping; 756 } 757 758 // InvalidateScreenClipping 759 void 760 ViewLayer::InvalidateScreenClipping(bool deep) 761 { 762 fScreenClippingValid = false; 763 if (deep) { 764 // invalidate the childrens screen clipping as well 765 for (ViewLayer* child = FirstChild(); child; child = NextChild()) { 766 child->InvalidateScreenClipping(deep); 767 } 768 } 769 } 770 771 // _MoveScreenClipping 772 void 773 ViewLayer::_MoveScreenClipping(int32 x, int32 y, bool deep) 774 { 775 if (fScreenClippingValid) 776 fScreenClipping.OffsetBy(x, y); 777 778 if (deep) { 779 // move the childrens screen clipping as well 780 for (ViewLayer* child = FirstChild(); child; child = NextChild()) { 781 child->_MoveScreenClipping(x, y, deep); 782 } 783 } 784 } 785 786