2 * Copyright (c) 2001-2015, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
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>
20 #include "AlphaMask.h"
22 #include "DrawingEngine.h"
23 #include "DrawState.h"
26 #include "ServerApp.h"
27 #include "ServerBitmap.h"
28 #include "ServerCursor.h"
29 #include "ServerPicture.h"
30 #include "ServerWindow.h"
33 #include "BitmapHWInterface.h"
34 #include "drawing_support.h"
39 #include <View.h> // for resize modes
40 #include <WindowPrivate.h>
42 #include <GradientLinear.h>
43 #include <GradientRadial.h>
44 #include <GradientRadialFocus.h>
45 #include <GradientDiamond.h>
46 #include <GradientConic.h>
53 resize_frame(IntRect
& frame
, uint32 resizingMode
, int32 x
, int32 y
)
55 // follow with left side
56 if ((resizingMode
& 0x0F00U
) == _VIEW_RIGHT_
<< 8)
58 else if ((resizingMode
& 0x0F00U
) == _VIEW_CENTER_
<< 8)
61 // follow with right side
62 if ((resizingMode
& 0x000FU
) == _VIEW_RIGHT_
)
64 else if ((resizingMode
& 0x000FU
) == _VIEW_CENTER_
)
67 // follow with top side
68 if ((resizingMode
& 0xF000U
) == _VIEW_BOTTOM_
<< 12)
70 else if ((resizingMode
& 0xF000U
) == _VIEW_CENTER_
<< 12)
73 // follow with bottom side
74 if ((resizingMode
& 0x00F0U
) == _VIEW_BOTTOM_
<< 4)
76 else if ((resizingMode
& 0x00F0U
) == _VIEW_CENTER_
<< 4)
77 frame
.bottom
+= y
/ 2;
84 View::View(IntRect frame
, IntPoint scrollingOffset
, const char* name
,
85 int32 token
, uint32 resizeMode
, uint32 flags
)
91 fScrollingOffset(scrollingOffset
),
93 fViewColor((rgb_color
){ 255, 255, 255, 255 }),
94 fWhichViewColor(B_NO_COLOR
),
95 fWhichViewColorTint(B_NO_TINT
),
97 fBitmapResizingMode(0),
100 fResizeMode(resizeMode
),
103 // Views start visible by default
106 fBackgroundDirty(true),
107 fIsDesktopBackground(false),
116 fPreviousSibling(NULL
),
123 fLocalClipping((BRect
)Bounds()),
125 fScreenClippingValid(false),
127 fScreenAndUserClipping(NULL
)
130 fDrawState
->SetSubPixelPrecise(fFlags
& B_SUBPIXEL_PRECISE
);
136 if (fViewBitmap
!= NULL
)
137 fViewBitmap
->ReleaseReference();
139 delete fScreenAndUserClipping
;
140 delete fUserClipping
;
143 // if (fWindow && this == fWindow->TopView())
144 // fWindow->SetTopView(NULL);
147 fCursor
->ReleaseReference();
149 // iterate over children and delete each one
150 View
* view
= fFirstChild
;
153 view
= view
->fNextSibling
;
162 IntRect
bounds(fScrollingOffset
.x
, fScrollingOffset
.y
,
163 fScrollingOffset
.x
+ fFrame
.Width(),
164 fScrollingOffset
.y
+ fFrame
.Height());
170 View::ConvertToVisibleInTopView(IntRect
* bounds
) const
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
);
178 fParent
->ConvertToVisibleInTopView(bounds
);
183 View::AttachedToWindow(::Window
* window
)
187 // an ugly hack to detect the desktop background
188 if (window
->Feel() == kDesktopWindowFeel
&& Parent() == TopView())
189 fIsDesktopBackground
= true;
191 // insert view into local token space
192 if (fWindow
!= NULL
) {
193 fWindow
->ServerWindow()->App()->ViewTokens().SetToken(fToken
,
194 B_HANDLER_TOKEN
, this);
197 // attach child views as well
198 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling())
199 child
->AttachedToWindow(window
);
204 View::DetachedFromWindow()
206 // remove view from local token space
207 if (fWindow
!= NULL
&& fWindow
->ServerWindow()->App() != NULL
)
208 fWindow
->ServerWindow()->App()->ViewTokens().RemoveToken(fToken
);
211 // detach child views as well
212 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling())
213 child
->DetachedFromWindow();
221 View::GetDrawingEngine() const
223 return Window()->GetDrawingEngine();
228 View::GetPicture(int32 token
) const
230 return Window()->ServerWindow()->App()->GetPicture(token
);
235 View::ResyncDrawState()
237 return Window()->ServerWindow()->ResyncDrawState();
242 View::UpdateCurrentDrawingRegion()
244 return Window()->ServerWindow()->UpdateCurrentDrawingRegion();
249 View::AddChild(View
* view
)
252 printf("View::AddChild() - View already has a parent\n");
256 view
->fParent
= this;
262 // append view to formerly last child
263 fLastChild
->fNextSibling
= view
;
264 view
->fPreviousSibling
= fLastChild
;
268 view
->UpdateVisibleDeep(fVisible
);
270 if (view
->IsVisible())
271 RebuildClipping(false);
274 view
->AttachedToWindow(fWindow
);
276 if (view
->IsVisible()) {
278 IntRect clippedFrame
= view
->Frame();
279 ConvertToVisibleInTopView(&clippedFrame
);
280 BRegion
* dirty
= fWindow
->GetRegion();
282 dirty
->Set((clipping_rect
)clippedFrame
);
283 fWindow
->MarkContentDirtyAsync(*dirty
);
284 fWindow
->RecycleRegion(dirty
);
292 View::RemoveChild(View
* view
)
294 if (view
== NULL
|| view
->fParent
!= this) {
295 printf("View::RemoveChild(%p - %s) - View is not child of "
296 "this (%p) view!\n", view
, view
? view
->Name() : NULL
, this);
300 view
->fParent
= NULL
;
302 if (fLastChild
== view
)
303 fLastChild
= view
->fPreviousSibling
;
304 // view->fNextSibling would be NULL
306 if (fFirstChild
== view
)
307 fFirstChild
= view
->fNextSibling
;
308 // view->fPreviousSibling would be NULL
310 // connect child before and after view
311 if (view
->fPreviousSibling
)
312 view
->fPreviousSibling
->fNextSibling
= view
->fNextSibling
;
314 if (view
->fNextSibling
)
315 view
->fNextSibling
->fPreviousSibling
= view
->fPreviousSibling
;
317 // view has no siblings anymore
318 view
->fPreviousSibling
= NULL
;
319 view
->fNextSibling
= NULL
;
321 if (view
->IsVisible()) {
322 Overlay
* overlay
= view
->_Overlay();
326 RebuildClipping(false);
330 view
->DetachedFromWindow();
332 if (fVisible
&& view
->IsVisible()) {
334 IntRect clippedFrame
= view
->Frame();
335 ConvertToVisibleInTopView(&clippedFrame
);
336 BRegion
* dirty
= fWindow
->GetRegion();
338 dirty
->Set((clipping_rect
)clippedFrame
);
339 fWindow
->MarkContentDirtyAsync(*dirty
);
340 fWindow
->RecycleRegion(dirty
);
352 // returns the top level view of the hirarchy,
353 // it doesn't have to be the top level of a window
356 return fParent
->TopView();
363 View::CountChildren(bool deep
) const
366 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
369 count
+= child
->CountChildren(deep
);
377 View::CollectTokensForChildren(BList
* tokenMap
) const
379 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
380 tokenMap
->AddItem((void*)child
);
381 child
->CollectTokensForChildren(tokenMap
);
388 View::MarkAt(DrawingEngine
* engine
, const BPoint
& where
, int32 level
)
390 BRect
rect(fFrame
.left
, fFrame
.top
, fFrame
.right
, fFrame
.bottom
);
392 if (Parent() != NULL
) {
393 Parent()->ConvertToScreen(&rect
);
394 if (!rect
.Contains(where
))
397 engine
->StrokeRect(rect
, (rgb_color
){level
* 30, level
* 30, level
* 30});
402 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
403 found
|= child
->MarkAt(engine
, where
, level
+ 1);
407 rgb_color color
= {0};
409 case 0: color
.green
= rand() % 256; break;
410 case 1: color
.blue
= rand() % 256; break;
414 //engine->FillRegion(fLocalClipping, (rgb_color){255, 255, 0, 10});
415 engine
->StrokeRect(rect
, color
);
417 engine
->StrokeRect(rect
, color
);
426 View::FindViews(uint32 flags
, BObjectList
<View
>& list
, int32
& left
)
428 if ((Flags() & flags
) == flags
) {
434 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
435 child
->FindViews(flags
, list
, left
);
443 View::HasView(View
* view
)
448 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
449 if (child
->HasView(view
))
458 View::ViewAt(const BPoint
& where
)
463 IntRect frame
= Frame();
464 if (Parent() != NULL
)
465 Parent()->LocalToScreenTransform().Apply(&frame
);
467 if (!frame
.Contains(where
))
470 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
471 View
* view
= child
->ViewAt(where
);
484 View::SetName(const char* string
)
491 View::SetFlags(uint32 flags
)
494 fDrawState
->SetSubPixelPrecise(fFlags
& B_SUBPIXEL_PRECISE
);
499 View::SetViewBitmap(ServerBitmap
* bitmap
, IntRect sourceRect
,
500 IntRect destRect
, int32 resizingMode
, int32 options
)
502 if (fViewBitmap
!= NULL
) {
503 Overlay
* overlay
= _Overlay();
505 if (bitmap
!= NULL
) {
506 // take over overlay token from current overlay (if it has any)
507 Overlay
* newOverlay
= bitmap
->Overlay();
509 if (overlay
!= NULL
&& newOverlay
!= NULL
)
510 newOverlay
->TakeOverToken(overlay
);
511 } else if (overlay
!= NULL
)
514 fViewBitmap
->ReleaseReference();
517 // the caller is allowed to delete the bitmap after setting the background
519 bitmap
->AcquireReference();
521 fViewBitmap
= bitmap
;
522 fBitmapSource
= sourceRect
;
523 fBitmapDestination
= destRect
;
524 fBitmapResizingMode
= resizingMode
;
525 fBitmapOptions
= options
;
527 _UpdateOverlayView();
532 View::_Overlay() const
534 if (fViewBitmap
== NULL
)
537 return fViewBitmap
->Overlay();
542 View::_UpdateOverlayView() const
544 Overlay
* overlay
= _Overlay();
548 IntRect destination
= fBitmapDestination
;
549 LocalToScreenTransform().Apply(&destination
);
551 overlay
->Configure(fBitmapSource
, destination
);
556 This method is called whenever the window is resized or moved - would
557 be nice to have a better solution for this, though.
560 View::UpdateOverlay()
565 if (_Overlay() != NULL
) {
566 _UpdateOverlayView();
568 // recursively ask children of this view
570 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
571 child
->UpdateOverlay();
581 View::_LocalToScreenTransform(SimpleTransform
& transform
) const
583 const View
* view
= this;
587 offsetX
+= view
->fFrame
.left
- view
->fScrollingOffset
.x
;
588 offsetY
+= view
->fFrame
.top
- view
->fScrollingOffset
.y
;
589 view
= view
->fParent
;
590 } while (view
!= NULL
);
592 transform
.AddOffset(offsetX
, offsetY
);
597 View::_ScreenToLocalTransform(SimpleTransform
& transform
) const
599 const View
* view
= this;
603 offsetX
+= view
->fScrollingOffset
.x
- view
->fFrame
.left
;
604 offsetY
+= view
->fScrollingOffset
.y
- view
->fFrame
.top
;
605 view
= view
->fParent
;
606 } while (view
!= NULL
);
608 transform
.AddOffset(offsetX
, offsetY
);
616 View::MoveBy(int32 x
, int32 y
, BRegion
* dirtyRegion
)
618 if (x
== 0 && y
== 0)
621 fFrame
.OffsetBy(x
, y
);
623 // to move on screen, we must not be hidden and we must have a parent
624 if (fVisible
&& fParent
&& dirtyRegion
) {
626 // based on redraw on new location
627 // the place were we are now visible
628 IntRect
newVisibleBounds(Bounds());
629 // we can use the frame of the old
630 // local clipping to see which parts need invalidation
631 IntRect
oldVisibleBounds(newVisibleBounds
);
632 oldVisibleBounds
.OffsetBy(-x
, -y
);
633 LocalToScreenTransform().Apply(&oldVisibleBounds
);
635 ConvertToVisibleInTopView(&newVisibleBounds
);
637 dirtyRegion
->Include((clipping_rect
)oldVisibleBounds
);
638 // newVisibleBounds already is in screen coords
639 dirtyRegion
->Include((clipping_rect
)newVisibleBounds
);
641 // blitting version, invalidates
643 IntRect
oldVisibleBounds(Bounds());
644 IntRect
newVisibleBounds(oldVisibleBounds
);
645 oldVisibleBounds
.OffsetBy(-x
, -y
);
646 LocalToScreenTransform().Apply(&oldVisibleBounds
);
648 // NOTE: using ConvertToVisibleInTopView()
649 // instead of ConvertToScreen()! see below
650 ConvertToVisibleInTopView(&newVisibleBounds
);
652 newVisibleBounds
.OffsetBy(-x
, -y
);
654 // clipping oldVisibleBounds to newVisibleBounds
655 // makes sure we don't copy parts hidden under
657 BRegion
* region
= fWindow
->GetRegion();
659 region
->Set(oldVisibleBounds
& newVisibleBounds
);
660 fWindow
->CopyContents(region
, x
, y
);
662 region
->Set(oldVisibleBounds
);
663 newVisibleBounds
.OffsetBy(x
, y
);
664 region
->Exclude((clipping_rect
)newVisibleBounds
);
665 dirtyRegion
->Include(dirty
);
667 fWindow
->RecycleRegion(region
);
674 // the top view's screen clipping does not change,
675 // because no parts are clipped away from parent
677 _MoveScreenClipping(x
, y
, true);
679 // parts might have been revealed from underneath
680 // the parent, or might now be hidden underneath
681 // the parent, this is taken care of when building
682 // the screen clipping
683 InvalidateScreenClipping();
689 View::ResizeBy(int32 x
, int32 y
, BRegion
* dirtyRegion
)
691 if (x
== 0 && y
== 0)
697 if (fVisible
&& dirtyRegion
) {
698 IntRect
oldBounds(Bounds());
699 oldBounds
.right
-= x
;
700 oldBounds
.bottom
-= y
;
702 BRegion
* dirty
= fWindow
->GetRegion();
706 dirty
->Set((clipping_rect
)Bounds());
707 dirty
->Include((clipping_rect
)oldBounds
);
709 if (!(fFlags
& B_FULL_UPDATE_ON_RESIZE
)) {
710 // the dirty region is just the difference of
711 // old and new bounds
712 dirty
->Exclude((clipping_rect
)(oldBounds
& Bounds()));
715 InvalidateScreenClipping();
717 if (dirty
->CountRects() > 0) {
718 if ((fFlags
& B_DRAW_ON_CHILDREN
) == 0) {
719 // exclude children, they are expected to
720 // include their own dirty regions in ParentResized()
721 for (View
* child
= FirstChild(); child
;
722 child
= child
->NextSibling()) {
723 if (!child
->IsVisible())
725 IntRect
previousChildVisible(
726 child
->Frame() & oldBounds
& Bounds());
727 if (dirty
->Frame().Intersects(previousChildVisible
)) {
728 dirty
->Exclude((clipping_rect
)previousChildVisible
);
733 LocalToScreenTransform().Apply(dirty
);
734 dirtyRegion
->Include(dirty
);
736 fWindow
->RecycleRegion(dirty
);
739 // layout the children
740 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling())
741 child
->ParentResized(x
, y
, dirtyRegion
);
744 if (fViewBitmap
!= NULL
)
745 resize_frame(fBitmapDestination
, fBitmapResizingMode
, x
, y
);
747 // at this point, children are at their new locations,
748 // so we can rebuild the clipping
749 // TODO: when the implementation of Hide() and Show() is
750 // complete, see if this should be avoided
751 RebuildClipping(false);
756 View::ParentResized(int32 x
, int32 y
, BRegion
* dirtyRegion
)
758 IntRect newFrame
= fFrame
;
759 resize_frame(newFrame
, fResizeMode
& 0x0000ffff, x
, y
);
761 if (newFrame
!= fFrame
) {
762 // careful, MoveBy will change fFrame
763 int32 widthDiff
= (int32
)(newFrame
.Width() - fFrame
.Width());
764 int32 heightDiff
= (int32
)(newFrame
.Height() - fFrame
.Height());
766 MoveBy(newFrame
.left
- fFrame
.left
,
767 newFrame
.top
- fFrame
.top
, dirtyRegion
);
769 ResizeBy(widthDiff
, heightDiff
, dirtyRegion
);
771 // TODO: this covers the fact that our screen clipping might change
772 // when the parent changes its size, even though our frame stays
773 // the same - there might be a way to test for this, but axeld doesn't
774 // know, stippi should look into this when he's back :)
775 InvalidateScreenClipping();
781 View::ScrollBy(int32 x
, int32 y
, BRegion
* dirtyRegion
)
783 if (!fVisible
|| !fWindow
) {
784 fScrollingOffset
.x
+= x
;
785 fScrollingOffset
.y
+= y
;
789 // blitting version, invalidates
792 // remember old bounds for tracking dirty region
793 IntRect
oldBounds(Bounds());
795 // NOTE: using ConvertToVisibleInTopView()
796 // instead of ConvertToScreen(), this makes
797 // sure we don't try to move or invalidate an
798 // area hidden underneath the parent view
799 ConvertToVisibleInTopView(&oldBounds
);
801 // find the area of the view that can be scrolled,
802 // contents are shifted in the opposite direction from scrolling
803 IntRect
stillVisibleBounds(oldBounds
);
804 stillVisibleBounds
.OffsetBy(x
, y
);
805 stillVisibleBounds
= stillVisibleBounds
& oldBounds
;
807 fScrollingOffset
.x
+= x
;
808 fScrollingOffset
.y
+= y
;
810 // do the blit, this will make sure
811 // that other more complex dirty regions
813 BRegion
* copyRegion
= fWindow
->GetRegion();
816 copyRegion
->Set((clipping_rect
)stillVisibleBounds
);
817 fWindow
->CopyContents(copyRegion
, -x
, -y
);
819 // find the dirty region as far as we are
821 BRegion
* dirty
= copyRegion
;
822 // reuse copyRegion and call it dirty
824 dirty
->Set((clipping_rect
)oldBounds
);
825 stillVisibleBounds
.OffsetBy(-x
, -y
);
826 dirty
->Exclude((clipping_rect
)stillVisibleBounds
);
827 dirtyRegion
->Include(dirty
);
829 fWindow
->RecycleRegion(dirty
);
831 // the screen clipping of this view and it's
832 // childs is no longer valid
833 InvalidateScreenClipping();
834 RebuildClipping(false);
839 View::CopyBits(IntRect src
, IntRect dst
, BRegion
& windowContentClipping
)
841 if (!fVisible
|| !fWindow
)
844 // TODO: figure out what to do when we have a transform which is not
846 BAffineTransform transform
= CurrentState()->CombinedTransform();
847 if (!transform
.IsIdentity() && transform
.IsDilation()) {
848 BPoint points
[4] = { src
.LeftTop(), src
.RightBottom(),
849 dst
.LeftTop(), dst
.RightBottom() };
850 transform
.Apply(&points
[0], 4);
851 src
.Set(points
[0].x
, points
[0].y
, points
[1].x
, points
[1].y
);
852 dst
.Set(points
[2].x
, points
[2].y
, points
[3].x
, points
[3].y
);
855 // TODO: confirm that in R5 this call is affected by origin and scale
859 int32 xOffset
= dst
.left
- src
.left
;
860 int32 yOffset
= dst
.top
- src
.top
;
862 // figure out which part can be blittet
863 IntRect
visibleSrc(src
);
864 ConvertToVisibleInTopView(&visibleSrc
);
866 IntRect
visibleSrcAtDest(src
);
867 visibleSrcAtDest
.OffsetBy(xOffset
, yOffset
);
868 ConvertToVisibleInTopView(&visibleSrcAtDest
);
870 // clip src to visible at dest
871 visibleSrcAtDest
.OffsetBy(-xOffset
, -yOffset
);
872 visibleSrc
= visibleSrc
& visibleSrcAtDest
;
874 // do the blit, this will make sure
875 // that other more complex dirty regions
877 BRegion
* copyRegion
= fWindow
->GetRegion();
881 // move src rect to destination here for efficiency reasons
882 visibleSrc
.OffsetBy(xOffset
, yOffset
);
884 // we need to interstect the copyRegion two times, onces
885 // at the source and once at the destination (here done
886 // the other way arround but it doesn't matter)
887 // the reason for this is that we are not supposed to visually
888 // copy children in the source rect and neither to copy onto
889 // children in the destination rect...
890 copyRegion
->Set((clipping_rect
)visibleSrc
);
891 BRegion
*screenAndUserClipping
892 = &ScreenAndUserClipping(&windowContentClipping
);
893 copyRegion
->IntersectWith(screenAndUserClipping
);
894 copyRegion
->OffsetBy(-xOffset
, -yOffset
);
895 copyRegion
->IntersectWith(screenAndUserClipping
);
897 // do the actual blit
898 fWindow
->CopyContents(copyRegion
, xOffset
, yOffset
);
900 // find the dirty region as far as we are concerned
901 IntRect
dirtyDst(dst
);
902 ConvertToVisibleInTopView(&dirtyDst
);
904 BRegion
* dirty
= fWindow
->GetRegion();
906 fWindow
->RecycleRegion(copyRegion
);
910 // offset copyRegion to destination again
911 copyRegion
->OffsetBy(xOffset
, yOffset
);
912 // start with destination given by user
913 dirty
->Set((clipping_rect
)dirtyDst
);
914 // exclude the part that we could copy
915 dirty
->Exclude(copyRegion
);
917 dirty
->IntersectWith(screenAndUserClipping
);
918 fWindow
->MarkContentDirty(*dirty
);
920 fWindow
->RecycleRegion(dirty
);
921 fWindow
->RecycleRegion(copyRegion
);
929 View::ColorUpdated(color_which which
, rgb_color color
)
931 float tint
= B_NO_TINT
;
933 if (fWhichViewColor
== which
)
934 SetViewColor(tint_color(color
, fWhichViewColorTint
));
936 if (CurrentState()->HighUIColor(&tint
) == which
)
937 CurrentState()->SetHighColor(tint_color(color
, tint
));
939 if (CurrentState()->LowUIColor(&tint
) == which
)
940 CurrentState()->SetLowColor(tint_color(color
, tint
));
942 for (View
* child
= FirstChild(); child
!= NULL
;
943 child
= child
->NextSibling()) {
945 child
->ColorUpdated(which
, color
);
951 View::SetViewUIColor(color_which which
, float tint
)
953 if (which
!= B_NO_COLOR
) {
954 DesktopSettings
settings(fWindow
->Desktop());
955 SetViewColor(tint_color(settings
.UIColor(which
), tint
));
958 fWhichViewColor
= which
;
959 fWhichViewColorTint
= tint
;
964 View::ViewUIColor(float* tint
)
967 *tint
= fWhichViewColorTint
;
969 return fWhichViewColor
;
979 DrawState
* newState
= fDrawState
->PushState();
981 fDrawState
= newState
;
982 // In BeAPI, B_SUBPIXEL_PRECISE is a view flag, and not affected by the
983 // view state. Our implementation moves it to the draw state, but let's
984 // be compatible with the API here and make it survive accross state
986 fDrawState
->SetSubPixelPrecise(fFlags
& B_SUBPIXEL_PRECISE
);
994 if (fDrawState
->PreviousState() == NULL
) {
995 fprintf(stderr
, "WARNING: User called BView(%s)::PopState(), "
996 "but there is NO state on stack!\n", Name());
1000 bool rebuildClipping
= fDrawState
->HasAdditionalClipping();
1002 fDrawState
= fDrawState
->PopState();
1003 fDrawState
->SetSubPixelPrecise(fFlags
& B_SUBPIXEL_PRECISE
);
1006 // (the clipping from the popped state is not effective anymore)
1007 if (rebuildClipping
)
1008 RebuildClipping(false);
1016 View::SetEventMask(uint32 eventMask
, uint32 options
)
1018 fEventMask
= eventMask
;
1019 fEventOptions
= options
;
1024 View::SetCursor(ServerCursor
* cursor
)
1026 if (cursor
== fCursor
)
1030 fCursor
->ReleaseReference();
1035 fCursor
->AcquireReference();
1040 View::SetPicture(ServerPicture
* picture
)
1042 if (picture
== fPicture
)
1045 if (fPicture
!= NULL
)
1046 fPicture
->ReleaseReference();
1050 if (fPicture
!= NULL
)
1051 fPicture
->AcquireReference();
1056 View::BlendAllLayers()
1058 if (fPicture
== NULL
)
1060 Layer
* layer
= dynamic_cast<Layer
*>(fPicture
);
1068 View::Draw(DrawingEngine
* drawingEngine
, BRegion
* effectiveClipping
,
1069 BRegion
* windowContentClipping
, bool deep
)
1072 // child views cannot be visible either
1076 if (fViewBitmap
!= NULL
|| fViewColor
!= B_TRANSPARENT_COLOR
) {
1077 // we can only draw within our own area
1079 if ((fFlags
& B_DRAW_ON_CHILDREN
) != 0) {
1080 // The client may actually want to prevent the background to
1081 // be painted outside the user clipping.
1082 redraw
= fWindow
->GetRegion(
1083 ScreenAndUserClipping(windowContentClipping
));
1085 // Ignore user clipping as in BeOS for painting the background.
1086 redraw
= fWindow
->GetRegion(
1087 _ScreenClipping(windowContentClipping
));
1091 // add the current clipping
1092 redraw
->IntersectWith(effectiveClipping
);
1094 Overlay
* overlayCookie
= _Overlay();
1096 if (fViewBitmap
!= NULL
&& overlayCookie
== NULL
) {
1098 // TODO: support other options!
1099 BRect rect
= fBitmapDestination
;
1100 PenToScreenTransform().Apply(&rect
);
1102 align_rect_to_pixels(&rect
);
1104 if (fBitmapOptions
& B_TILE_BITMAP_Y
) {
1105 // move rect up as much as needed
1106 while (rect
.top
> redraw
->Frame().top
)
1107 rect
.OffsetBy(0.0, -(rect
.Height() + 1));
1109 if (fBitmapOptions
& B_TILE_BITMAP_X
) {
1110 // move rect left as much as needed
1111 while (rect
.left
> redraw
->Frame().left
)
1112 rect
.OffsetBy(-(rect
.Width() + 1), 0.0);
1115 // XXX: locking removed because the Window keeps the engine locked
1116 // because it keeps track of syncing right now
1118 // lock the drawing engine for as long as we need the clipping
1120 if (rect
.IsValid()/* && drawingEngine->Lock()*/) {
1121 drawingEngine
->ConstrainClippingRegion(redraw
);
1123 drawing_mode oldMode
;
1124 drawingEngine
->SetDrawingMode(B_OP_COPY
, oldMode
);
1126 if (fBitmapOptions
& B_TILE_BITMAP
) {
1127 // tile across entire view
1129 float start
= rect
.left
;
1130 while (rect
.top
< redraw
->Frame().bottom
) {
1131 while (rect
.left
< redraw
->Frame().right
) {
1132 drawingEngine
->DrawBitmap(fViewBitmap
,
1133 fBitmapSource
, rect
, fBitmapOptions
);
1134 rect
.OffsetBy(rect
.Width() + 1, 0.0);
1136 rect
.OffsetBy(start
- rect
.left
, rect
.Height() + 1);
1138 // nothing left to be drawn
1139 redraw
->MakeEmpty();
1140 } else if (fBitmapOptions
& B_TILE_BITMAP_X
) {
1141 // tile in x direction
1143 while (rect
.left
< redraw
->Frame().right
) {
1144 drawingEngine
->DrawBitmap(fViewBitmap
, fBitmapSource
,
1145 rect
, fBitmapOptions
);
1146 rect
.OffsetBy(rect
.Width() + 1, 0.0);
1148 // remove horizontal stripe from clipping
1149 rect
.left
= redraw
->Frame().left
;
1150 rect
.right
= redraw
->Frame().right
;
1151 redraw
->Exclude(rect
);
1152 } else if (fBitmapOptions
& B_TILE_BITMAP_Y
) {
1153 // tile in y direction
1155 while (rect
.top
< redraw
->Frame().bottom
) {
1156 drawingEngine
->DrawBitmap(fViewBitmap
, fBitmapSource
,
1157 rect
, fBitmapOptions
);
1158 rect
.OffsetBy(0.0, rect
.Height() + 1);
1160 // remove vertical stripe from clipping
1161 rect
.top
= redraw
->Frame().top
;
1162 rect
.bottom
= redraw
->Frame().bottom
;
1163 redraw
->Exclude(rect
);
1167 drawingEngine
->DrawBitmap(fViewBitmap
, fBitmapSource
,
1168 rect
, fBitmapOptions
);
1169 redraw
->Exclude(rect
);
1172 drawingEngine
->SetDrawingMode(oldMode
);
1174 // NOTE: It is ok not to reset the clipping, that
1175 // would only waste time
1176 // drawingEngine->Unlock();
1181 if (fViewColor
!= B_TRANSPARENT_COLOR
) {
1182 // fill visible region with view color,
1183 // this version of FillRegion ignores any
1184 // clipping, that's why "redraw" needs to
1187 // if (redraw->Frame().left < 0 || redraw->Frame().top < 0) {
1188 // char message[1024];
1189 // BRect c = effectiveClipping->Frame();
1190 // BRect w = windowContentClipping->Frame();
1191 // BRect r = redraw->Frame();
1192 // sprintf(message, "invalid background: current clipping: (%d, %d)->(%d, %d), "
1193 // "window content: (%d, %d)->(%d, %d), redraw: (%d, %d)->(%d, %d)",
1194 // (int)c.left, (int)c.top, (int)c.right, (int)c.bottom,
1195 // (int)w.left, (int)w.top, (int)w.right, (int)w.bottom,
1196 // (int)r.left, (int)r.top, (int)r.right, (int)r.bottom);
1197 // debugger(message);
1200 drawingEngine
->FillRegion(*redraw
, overlayCookie
!= NULL
1201 ? overlayCookie
->Color() : fViewColor
);
1204 fWindow
->RecycleRegion(redraw
);
1207 fBackgroundDirty
= false;
1209 // let children draw
1211 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
1212 child
->Draw(drawingEngine
, effectiveClipping
,
1213 windowContentClipping
, deep
);
1223 View::MouseDown(BMessage
* message
, BPoint where
)
1225 // empty hook method
1230 View::MouseUp(BMessage
* message
, BPoint where
)
1232 // empty hook method
1237 View::MouseMoved(BMessage
* message
, BPoint where
)
1239 // empty hook method
1247 View::SetHidden(bool hidden
)
1249 if (fHidden
!= hidden
) {
1252 // recurse into children and update their visible flag
1253 bool oldVisible
= fVisible
;
1254 UpdateVisibleDeep(fParent
? fParent
->IsVisible() : !fHidden
);
1255 if (oldVisible
!= fVisible
) {
1256 // Include or exclude us from the parent area, and update the
1257 // children's clipping as well when the view will be visible
1259 fParent
->RebuildClipping(fVisible
);
1261 RebuildClipping(fVisible
);
1265 IntRect clippedBounds
= Bounds();
1266 ConvertToVisibleInTopView(&clippedBounds
);
1267 BRegion
* dirty
= fWindow
->GetRegion();
1270 dirty
->Set((clipping_rect
)clippedBounds
);
1271 fWindow
->MarkContentDirty(*dirty
);
1272 fWindow
->RecycleRegion(dirty
);
1280 View::IsHidden() const
1287 View::UpdateVisibleDeep(bool parentVisible
)
1289 bool wasVisible
= fVisible
;
1291 fVisible
= parentVisible
&& !fHidden
;
1292 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling())
1293 child
->UpdateVisibleDeep(fVisible
);
1297 Overlay
* overlay
= _Overlay();
1298 if (overlay
== NULL
)
1301 if (fVisible
&& !wasVisible
)
1302 _UpdateOverlayView();
1303 else if (!fVisible
&& wasVisible
)
1312 View::MarkBackgroundDirty()
1314 if (fBackgroundDirty
)
1316 fBackgroundDirty
= true;
1317 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling())
1318 child
->MarkBackgroundDirty();
1323 View::AddTokensForViewsInRegion(BPrivate::PortLink
& link
, BRegion
& region
,
1324 BRegion
* windowContentClipping
)
1330 // NOTE: use scope in order to reduce stack space requirements
1332 // This check will prevent descending the view hierarchy
1333 // any further than necessary
1334 IntRect
screenBounds(Bounds());
1335 LocalToScreenTransform().Apply(&screenBounds
);
1336 if (!region
.Intersects((clipping_rect
)screenBounds
))
1339 // Unfortunately, we intersecting another region, but otherwise
1340 // we couldn't provide the exact update rect to the client
1341 BRegion localDirty
= _ScreenClipping(windowContentClipping
);
1342 localDirty
.IntersectWith(®ion
);
1343 if (localDirty
.CountRects() > 0) {
1344 link
.Attach
<int32
>(fToken
);
1345 link
.Attach
<BRect
>(localDirty
.Frame());
1349 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling())
1350 child
->AddTokensForViewsInRegion(link
, region
, windowContentClipping
);
1355 View::PrintToStream() const
1357 printf("View: %s\n", Name());
1358 printf(" fToken: %" B_PRId32
"\n", fToken
);
1359 printf(" fFrame: IntRect(%" B_PRId32
", %" B_PRId32
", %" B_PRId32
", %" B_PRId32
")\n",
1360 fFrame
.left
, fFrame
.top
, fFrame
.right
, fFrame
.bottom
);
1361 printf(" fScrollingOffset: IntPoint(%" B_PRId32
", %" B_PRId32
")\n",
1362 fScrollingOffset
.x
, fScrollingOffset
.y
);
1363 printf(" fHidden: %d\n", fHidden
);
1364 printf(" fVisible: %d\n", fVisible
);
1365 printf(" fWindow: %p\n", fWindow
);
1366 printf(" fParent: %p\n", fParent
);
1367 printf(" fLocalClipping:\n");
1368 fLocalClipping
.PrintToStream();
1369 printf(" fScreenClipping:\n");
1370 fScreenClipping
.PrintToStream();
1371 printf(" valid: %d\n", fScreenClippingValid
);
1373 printf(" fUserClipping:\n");
1374 if (fUserClipping
!= NULL
)
1375 fUserClipping
->PrintToStream();
1379 printf(" fScreenAndUserClipping:\n");
1380 if (fScreenAndUserClipping
!= NULL
)
1381 fScreenAndUserClipping
->PrintToStream();
1383 printf(" invalid\n");
1385 printf(" state:\n");
1386 printf(" user clipping: %d\n", fDrawState
->HasClipping());
1387 BPoint origin
= fDrawState
->CombinedOrigin();
1388 printf(" origin: BPoint(%.1f, %.1f)\n", origin
.x
, origin
.y
);
1389 printf(" scale: %.2f\n", fDrawState
->CombinedScale());
1395 View::RebuildClipping(bool deep
)
1397 // the clipping spans over the bounds area
1398 fLocalClipping
.Set((clipping_rect
)Bounds());
1400 if (View
* child
= FirstChild()) {
1401 // if this view does not draw over children,
1402 // exclude all children from the clipping
1403 if ((fFlags
& B_DRAW_ON_CHILDREN
) == 0) {
1404 BRegion
* childrenRegion
= fWindow
->GetRegion();
1405 if (!childrenRegion
)
1408 for (; child
; child
= child
->NextSibling()) {
1409 if (child
->IsVisible())
1410 childrenRegion
->Include((clipping_rect
)child
->Frame());
1413 fLocalClipping
.Exclude(childrenRegion
);
1414 fWindow
->RecycleRegion(childrenRegion
);
1416 // if the operation is "deep", make children rebuild their
1419 for (child
= FirstChild(); child
; child
= child
->NextSibling())
1420 child
->RebuildClipping(true);
1424 // add the user clipping in case there is one
1425 if (fDrawState
->HasClipping()) {
1426 // NOTE: in case the user sets a user defined clipping region,
1427 // rebuilding the clipping is a bit more expensive because there
1428 // is no separate "drawing region"... on the other
1429 // hand, views for which this feature is actually used will
1430 // probably not have any children, so it is not that expensive
1432 if (fUserClipping
== NULL
) {
1433 fUserClipping
= new (nothrow
) BRegion
;
1434 if (fUserClipping
== NULL
)
1438 fDrawState
->GetCombinedClippingRegion(fUserClipping
);
1440 delete fUserClipping
;
1441 fUserClipping
= NULL
;
1444 delete fScreenAndUserClipping
;
1445 fScreenAndUserClipping
= NULL
;
1446 fScreenClippingValid
= false;
1451 View::ScreenAndUserClipping(BRegion
* windowContentClipping
, bool force
) const
1453 // no user clipping - return screen clipping directly
1454 if (fUserClipping
== NULL
)
1455 return _ScreenClipping(windowContentClipping
, force
);
1457 // combined screen and user clipping already valid
1458 if (fScreenAndUserClipping
!= NULL
)
1459 return *fScreenAndUserClipping
;
1461 // build a new combined user and screen clipping
1462 fScreenAndUserClipping
= new (nothrow
) BRegion(*fUserClipping
);
1463 if (fScreenAndUserClipping
== NULL
)
1464 return fScreenClipping
;
1466 LocalToScreenTransform().Apply(fScreenAndUserClipping
);
1467 fScreenAndUserClipping
->IntersectWith(
1468 &_ScreenClipping(windowContentClipping
, force
));
1469 return *fScreenAndUserClipping
;
1474 View::InvalidateScreenClipping()
1476 // TODO: appearantly, we are calling ScreenClipping() on
1477 // views who's parents don't have a valid screen clipping yet,
1478 // this messes up the logic that for any given view with
1479 // fScreenClippingValid == false, all children have
1480 // fScreenClippingValid == false too. If this could be made the
1481 // case, we could save some performance here with the commented
1482 // out check, since InvalidateScreenClipping() might be called
1484 // TODO: investigate, if InvalidateScreenClipping() could be
1485 // called in "deep" and "non-deep" mode, ie. see if there are
1486 // any cases where the children would still have valid screen
1487 // clipping, even though the parent's screen clipping becomes
1489 // if (!fScreenClippingValid)
1492 delete fScreenAndUserClipping
;
1493 fScreenAndUserClipping
= NULL
;
1494 fScreenClippingValid
= false;
1495 // invalidate the childrens screen clipping as well
1496 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
1497 child
->InvalidateScreenClipping();
1503 View::_ScreenClipping(BRegion
* windowContentClipping
, bool force
) const
1505 if (!fScreenClippingValid
|| force
) {
1506 fScreenClipping
= fLocalClipping
;
1507 LocalToScreenTransform().Apply(&fScreenClipping
);
1509 // see if parts of our bounds are hidden underneath
1510 // the parent, the local clipping does not account for this
1511 IntRect clippedBounds
= Bounds();
1512 ConvertToVisibleInTopView(&clippedBounds
);
1513 if (clippedBounds
.Width() < fScreenClipping
.Frame().Width()
1514 || clippedBounds
.Height() < fScreenClipping
.Frame().Height()) {
1515 BRegion
* temp
= fWindow
->GetRegion();
1517 temp
->Set((clipping_rect
)clippedBounds
);
1518 fScreenClipping
.IntersectWith(temp
);
1519 fWindow
->RecycleRegion(temp
);
1523 fScreenClipping
.IntersectWith(windowContentClipping
);
1524 fScreenClippingValid
= true;
1527 return fScreenClipping
;
1532 View::_MoveScreenClipping(int32 x
, int32 y
, bool deep
)
1534 if (fScreenClippingValid
) {
1535 fScreenClipping
.OffsetBy(x
, y
);
1536 delete fScreenAndUserClipping
;
1537 fScreenAndUserClipping
= NULL
;
1541 // move the childrens screen clipping as well
1542 for (View
* child
= FirstChild(); child
; child
= child
->NextSibling()) {
1543 child
->_MoveScreenClipping(x
, y
, deep
);