5 #include <View.h> // for resize modes
8 #include "DrawingEngine.h"
9 #include "WindowLayer.h"
11 #include "ViewLayer.h"
16 ViewLayer::ViewLayer(BRect frame
, const char* name
,
17 uint32 resizeMode
, uint32 flags
,
22 fScrollingOffset(0.0, 0.0),
24 fViewColor(viewColor
),
26 fResizeMode(resizeMode
),
29 // ViewLayers start visible by default
37 fPreviousSibling(NULL
),
43 fLocalClipping(Bounds()),
45 fScreenClippingValid(false)
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
);
54 ViewLayer::~ViewLayer()
56 // iterate over children and delete each one
57 ViewLayer
* layer
= fFirstChild
;
59 ViewLayer
* toast
= layer
;
60 layer
= layer
->fNextSibling
;
67 ViewLayer::Bounds() const
69 BRect
bounds(fScrollingOffset
.x
, fScrollingOffset
.y
,
70 fScrollingOffset
.x
+ fFrame
.Width(),
71 fScrollingOffset
.y
+ fFrame
.Height());
75 // ConvertToVisibleInTopView
77 ViewLayer::ConvertToVisibleInTopView(BRect
* bounds
) const
79 *bounds
= *bounds
& Bounds();
80 // NOTE: this step is necessary even if we don't have a parent!
81 ConvertToParent(bounds
);
84 fParent
->ConvertToVisibleInTopView(bounds
);
89 ViewLayer::AttachedToWindow(WindowLayer
* window
)
93 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild())
94 child
->AttachedToWindow(window
);
100 ViewLayer::DetachedFromWindow()
103 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild())
104 child
->DetachedFromWindow();
109 ViewLayer::AddChild(ViewLayer
* layer
)
111 if (layer
->fParent
) {
112 printf("ViewLayer::AddChild() - ViewLayer already has a parent\n");
116 layer
->fParent
= this;
122 // append layer to formerly last child
123 fLastChild
->fNextSibling
= layer
;
124 layer
->fPreviousSibling
= fLastChild
;
128 if (layer
->IsVisible())
129 RebuildClipping(false);
132 layer
->AttachedToWindow(fWindow
);
134 if (fVisible
&& layer
->IsVisible()) {
136 BRect clippedFrame
= layer
->Frame();
137 ConvertToVisibleInTopView(&clippedFrame
);
138 BRegion
dirty(clippedFrame
);
139 fWindow
->MarkContentDirty(&dirty
);
146 ViewLayer::RemoveChild(ViewLayer
* layer
)
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);
153 layer
->fParent
= NULL
;
155 if (fLastChild
== layer
)
156 fLastChild
= layer
->fPreviousSibling
;
157 // layer->fNextSibling would be NULL
159 if (fFirstChild
== layer
)
160 fFirstChild
= layer
->fNextSibling
;
161 // layer->fPreviousSibling would be NULL
163 // connect child before and after layer
164 if (layer
->fPreviousSibling
)
165 layer
->fPreviousSibling
->fNextSibling
= layer
->fNextSibling
;
167 if (layer
->fNextSibling
)
168 layer
->fNextSibling
->fPreviousSibling
= layer
->fPreviousSibling
;
170 // layer has no siblings anymore
171 layer
->fPreviousSibling
= NULL
;
172 layer
->fNextSibling
= NULL
;
174 if (layer
->IsVisible())
175 RebuildClipping(false);
178 layer
->DetachedFromWindow();
180 if (fVisible
&& layer
->IsVisible()) {
182 BRect clippedFrame
= layer
->Frame();
183 ConvertToVisibleInTopView(&clippedFrame
);
184 BRegion
dirty(clippedFrame
);
185 fWindow
->MarkContentDirty(&dirty
);
194 ViewLayer::FirstChild() const
196 fCurrentChild
= fFirstChild
;
197 return fCurrentChild
;
202 ViewLayer::PreviousChild() const
204 fCurrentChild
= fCurrentChild
->fPreviousSibling
;
205 return fCurrentChild
;
210 ViewLayer::NextChild() const
212 fCurrentChild
= fCurrentChild
->fNextSibling
;
213 return fCurrentChild
;
218 ViewLayer::LastChild() const
220 fCurrentChild
= fLastChild
;
221 return fCurrentChild
;
226 ViewLayer::TopLayer()
228 // returns the top level view of the hirarchy,
229 // it doesn't have to be the top level of a window
232 return fParent
->TopLayer();
239 ViewLayer::CountChildren(bool deep
) const
242 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild()) {
245 count
+= child
->CountChildren(deep
);
251 // CollectTokensForChildren
253 ViewLayer::CollectTokensForChildren(BList
* tokenMap
) const
255 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild()) {
256 tokenMap
->AddItem((void*)child
);
257 child
->CollectTokensForChildren(tokenMap
);
263 ViewLayer::ViewAt(const BPoint
& where
, BRegion
* windowContentClipping
)
268 if (ScreenClipping(windowContentClipping
).Contains(where
))
271 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild()) {
272 ViewLayer
* layer
= child
->ViewAt(where
, windowContentClipping
);
281 ViewLayer::ConvertToParent(BPoint
* point
) const
283 // remove scrolling offset and convert to parent coordinate space
284 point
->x
+= fFrame
.left
- fScrollingOffset
.x
;
285 point
->y
+= fFrame
.top
- fScrollingOffset
.y
;
290 ViewLayer::ConvertToParent(BRect
* rect
) const
292 // remove scrolling offset and convert to parent coordinate space
293 rect
->OffsetBy(fFrame
.left
- fScrollingOffset
.x
,
294 fFrame
.top
- fScrollingOffset
.y
);
299 ViewLayer::ConvertToParent(BRegion
* region
) const
301 // remove scrolling offset and convert to parent coordinate space
302 region
->OffsetBy(fFrame
.left
- fScrollingOffset
.x
,
303 fFrame
.top
- fScrollingOffset
.y
);
308 ViewLayer::ConvertFromParent(BPoint
* point
) const
310 // remove scrolling offset and convert to parent coordinate space
311 point
->x
+= fScrollingOffset
.x
- fFrame
.left
;
312 point
->y
+= fScrollingOffset
.y
- fFrame
.top
;
317 ViewLayer::ConvertFromParent(BRect
* rect
) const
319 // remove scrolling offset and convert to parent coordinate space
320 rect
->OffsetBy(fScrollingOffset
.x
- fFrame
.left
,
321 fScrollingOffset
.y
- fFrame
.top
);
326 ViewLayer::ConvertFromParent(BRegion
* region
) const
328 // remove scrolling offset and convert to parent coordinate space
329 region
->OffsetBy(fScrollingOffset
.x
- fFrame
.left
,
330 fScrollingOffset
.y
- fFrame
.top
);
335 ViewLayer::ConvertToTop(BPoint
* point
) const
337 ConvertToParent(point
);
340 fParent
->ConvertToTop(point
);
345 ViewLayer::ConvertToTop(BRect
* rect
) const
347 ConvertToParent(rect
);
350 fParent
->ConvertToTop(rect
);
355 ViewLayer::ConvertToTop(BRegion
* region
) const
357 ConvertToParent(region
);
360 fParent
->ConvertToTop(region
);
365 ViewLayer::ConvertFromTop(BPoint
* point
) const
367 ConvertFromParent(point
);
370 fParent
->ConvertFromTop(point
);
375 ViewLayer::ConvertFromTop(BRect
* rect
) const
377 ConvertFromParent(rect
);
380 fParent
->ConvertFromTop(rect
);
385 ViewLayer::ConvertFromTop(BRegion
* region
) const
387 ConvertFromParent(region
);
390 fParent
->ConvertFromTop(region
);
395 ViewLayer::SetName(const char* string
)
404 ViewLayer::MoveBy(int32 x
, int32 y
, BRegion
* dirtyRegion
)
406 if (x
== 0 && y
== 0)
409 fFrame
.OffsetBy(x
, y
);
411 // to move on screen, we must not be hidden and we must have a parent
412 if (fVisible
&& fParent
&& dirtyRegion
) {
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
);
423 ConvertToVisibleInTopView(&newVisibleBounds
);
425 dirtyRegion
->Include(oldVisibleBounds
);
426 // newVisibleBounds already is in screen coords
427 dirtyRegion
->Include(newVisibleBounds
);
429 // blitting version, invalidates
431 BRect
oldVisibleBounds(Bounds());
432 oldVisibleBounds
.OffsetBy(-x
, -y
);
433 ConvertToTop(&oldVisibleBounds
);
435 BRect
newVisibleBounds(Bounds());
436 // NOTE: using ConvertToVisibleInTopView()
437 // instead of ConvertToTop()! see below
438 ConvertToVisibleInTopView(&newVisibleBounds
);
440 newVisibleBounds
.OffsetBy(-x
, -y
);
442 // clipping oldVisibleBounds to newVisibleBounds
443 // makes sure we don't copy parts hidden under
445 BRegion
copyRegion(oldVisibleBounds
& newVisibleBounds
);
446 fWindow
->CopyContents(©Region
, x
, y
);
448 BRegion
dirty(oldVisibleBounds
);
449 newVisibleBounds
.OffsetBy(x
, y
);
450 dirty
.Exclude(newVisibleBounds
);
451 dirtyRegion
->Include(&dirty
);
456 // the top view's screen clipping does not change,
457 // because no parts are clipped away from parent
459 _MoveScreenClipping(x
, y
, true);
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);
471 ViewLayer::ResizeBy(int32 x
, int32 y
, BRegion
* dirtyRegion
)
473 if (x
== 0 && y
== 0)
479 if (fVisible
&& dirtyRegion
) {
480 BRect
oldBounds(Bounds());
481 oldBounds
.right
-= x
;
482 oldBounds
.bottom
-= y
;
484 BRegion
dirty(Bounds());
485 dirty
.Include(oldBounds
);
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());
493 InvalidateScreenClipping(true);
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
);
507 ConvertToTop(&dirty
);
508 dirtyRegion
->Include(&dirty
);
512 // layout the children
513 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild())
514 child
->ParentResized(x
, y
, dirtyRegion
);
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);
525 ViewLayer::ParentResized(int32 x
, int32 y
, BRegion
* dirtyRegion
)
527 uint16 rm
= fResizeMode
& 0x0000FFFF;
528 BRect newFrame
= fFrame
;
530 // follow with left side
531 if ((rm
& 0x0F00U
) == _VIEW_RIGHT_
<< 8)
533 else if ((rm
& 0x0F00U
) == _VIEW_CENTER_
<< 8)
534 newFrame
.left
+= x
/ 2;
536 // follow with right side
537 if ((rm
& 0x000FU
) == _VIEW_RIGHT_
)
539 else if ((rm
& 0x000FU
) == _VIEW_CENTER_
)
540 newFrame
.right
+= x
/ 2;
542 // follow with top side
543 if ((rm
& 0xF000U
) == _VIEW_BOTTOM_
<< 12)
545 else if ((rm
& 0xF000U
) == _VIEW_CENTER_
<< 12)
546 newFrame
.top
+= y
/ 2;
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;
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());
559 MoveBy(newFrame
.left
- fFrame
.left
,
560 newFrame
.top
- fFrame
.top
, dirtyRegion
);
562 ResizeBy(widthDiff
, heightDiff
, dirtyRegion
);
568 ViewLayer::ScrollBy(int32 x
, int32 y
, BRegion
* dirtyRegion
)
570 // blitting version, invalidates
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
);
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
);
587 fScrollingOffset
.x
+= x
;
588 fScrollingOffset
.y
+= y
;
590 // do the blit, this will make sure
591 // that other more complex dirty regions
593 BRegion
copyRegion(stillVisibleBounds
);
594 fWindow
->CopyContents(©Region
, -x
, -y
);
596 // find the dirty region as far as we are
598 BRegion
dirty(oldBounds
);
599 stillVisibleBounds
.OffsetBy(-x
, -y
);
600 dirty
.Exclude(stillVisibleBounds
);
601 dirtyRegion
->Include(&dirty
);
603 // the screen clipping of this view and it's
604 // childs is no longer valid
605 InvalidateScreenClipping(true);
606 RebuildClipping(false);
613 ViewLayer::Draw(DrawingEngine
* drawingEngine
, BRegion
* effectiveClipping
,
614 BRegion
* windowContentClipping
, bool deep
)
616 // we can only draw within our own area
617 BRegion
redraw(ScreenClipping(windowContentClipping
));
618 // add the current clipping
619 redraw
.IntersectWith(effectiveClipping
);
621 // fill visible region with white
622 drawingEngine
->FillRegion(&redraw
, (rgb_color
){ 255, 255, 255, 255 });
626 // before passing the clipping on to children, exclude our
627 // own region from the available clipping
628 effectiveClipping
->Exclude(&ScreenClipping(windowContentClipping
));
630 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild()) {
631 child
->Draw(drawingEngine
, effectiveClipping
,
632 windowContentClipping
, deep
);
639 ViewLayer::ClientDraw(DrawingEngine
* drawingEngine
, BRegion
* effectiveClipping
)
642 b
.OffsetTo(0.0, 0.0);
645 if (drawingEngine
->Lock()) {
646 drawingEngine
->ConstrainClipping(effectiveClipping
);
648 // draw a frame with the view color
649 drawingEngine
->StrokeRect(b
, fViewColor
);
651 drawingEngine
->StrokeRect(b
, fViewColor
);
653 drawingEngine
->StrokeRect(b
, fViewColor
);
655 drawingEngine
->StrokeLine(b
.LeftTop(), b
.RightBottom(), fViewColor
);
657 drawingEngine
->Unlock();
665 ViewLayer::SetHidden(bool hidden
)
669 if (fHidden
!= hidden
) {
672 // recurse into children and update their visible flag
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);
681 BRect clippedBounds
= Bounds();
682 ConvertToVisibleInTopView(&clippedBounds
);
683 BRegion
dirty(clippedBounds
);
684 fWindow
->MarkContentDirty(&dirty
);
688 UpdateVisibleDeep(true);
695 ViewLayer::IsHidden() const
702 ViewLayer::UpdateVisibleDeep(bool parentVisible
)
704 fVisible
= parentVisible
&& !fHidden
;
705 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild())
706 child
->UpdateVisibleDeep(fVisible
);
711 ViewLayer::PrintToStream() const
717 ViewLayer::RebuildClipping(bool deep
)
719 // the clipping spans over the bounds area
720 fLocalClipping
.Set(Bounds());
722 // exclude all childs from the clipping
723 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild()) {
724 if (child
->IsVisible())
725 fLocalClipping
.Exclude(child
->Frame());
728 child
->RebuildClipping(deep
);
731 fScreenClippingValid
= false;
736 ViewLayer::ScreenClipping(BRegion
* windowContentClipping
, bool force
) const
738 if (!fScreenClippingValid
|| force
) {
739 fScreenClipping
= fLocalClipping
;
740 ConvertToTop(&fScreenClipping
);
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
);
752 fScreenClipping
.IntersectWith(windowContentClipping
);
753 fScreenClippingValid
= true;
755 return fScreenClipping
;
758 // InvalidateScreenClipping
760 ViewLayer::InvalidateScreenClipping(bool deep
)
762 fScreenClippingValid
= false;
764 // invalidate the childrens screen clipping as well
765 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild()) {
766 child
->InvalidateScreenClipping(deep
);
771 // _MoveScreenClipping
773 ViewLayer::_MoveScreenClipping(int32 x
, int32 y
, bool deep
)
775 if (fScreenClippingValid
)
776 fScreenClipping
.OffsetBy(x
, y
);
779 // move the childrens screen clipping as well
780 for (ViewLayer
* child
= FirstChild(); child
; child
= NextChild()) {
781 child
->_MoveScreenClipping(x
, y
, deep
);