figured out and solved several problems:

* views are now correctly clipped when they are
  (partly) hidden under their parent(s)
* removed fIsTopView, the top view in a window
  simply has no parent
* introduced WindowLayer::CopyContents() which
  blits part of the contents to another location,
  while moving that part in the dirty regions. Since
  this is currently used from the Desktop thread,
  messing with the update session dirty regions
  requires now to lock the clipping
* that feature is now used for blitting a view to its
  new location in ViewLayer::MoveBy(), which
  works for right and/or bottom aligned views just fine
* I left the global dirty region in Desktop for now,
  moving it into each WindowLayer gave quite a slowdown
  and caused all kinds of other problems.
* a view is now cleared to the background color right
  before the first drawing command from the client
  is executed for that view, this reduces flickering
  a lot because the content is drawn much more shortly
  after the background is cleared.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15201 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2005-11-28 19:28:38 +00:00
parent 94dc3ed625
commit 748533bbf9
6 changed files with 273 additions and 154 deletions

View File

@ -159,13 +159,13 @@ Desktop::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
if (dx != 0 || dy != 0) {
if (fClickedWindow) {
bigtime_t now = system_time();
//bigtime_t now = system_time();
if (fResizing) {
ResizeWindowBy(fClickedWindow, dx, dy);
printf("resizing: %lld\n", system_time() - now);
//printf("resizing: %lld\n", system_time() - now);
} else {
MoveWindowBy(fClickedWindow, dx, dy);
printf("moving: %lld\n", system_time() - now);
//printf("moving: %lld\n", system_time() - now);
}
}
}
@ -358,6 +358,9 @@ Desktop::BottomWindow() const
void
Desktop::MoveWindowBy(WindowLayer* window, int32 x, int32 y)
{
if (!Lock())
return;
if (LockClipping()) {
// the dirty region starts with the visible area of the window being moved
BRegion newDirtyRegion(window->VisibleRegion());
@ -403,12 +406,17 @@ Desktop::MoveWindowBy(WindowLayer* window, int32 x, int32 y)
UnlockClipping();
}
Unlock();
}
// ResizeWindowBy
void
Desktop::ResizeWindowBy(WindowLayer* window, int32 x, int32 y)
{
if (!Lock())
return;
if (LockClipping()) {
BRegion newDirtyRegion;
BRegion previouslyOccupiedRegion(window->VisibleRegion());
@ -428,6 +436,8 @@ Desktop::ResizeWindowBy(WindowLayer* window, int32 x, int32 y)
UnlockClipping();
}
Unlock();
}
// BringToFront

View File

@ -31,7 +31,6 @@ ViewLayer::ViewLayer(BRect frame, const char* name,
fWindow(NULL),
fParent(NULL),
fIsTopLayer(false),
fFirstChild(NULL),
fPreviousSibling(NULL),
@ -72,12 +71,23 @@ ViewLayer::Bounds() const
return bounds;
}
// ConvertToVisibleInTopView
void
ViewLayer::ConvertToVisibleInTopView(BRect* bounds) const
{
*bounds = *bounds & Bounds();
// NOTE: this step is necessary even if we don't have a parent!
ConvertToParent(bounds);
if (fParent)
fParent->ConvertToVisibleInTopView(bounds);
}
// AttachedToWindow
void
ViewLayer::AttachedToWindow(WindowLayer* window, bool topLayer)
ViewLayer::AttachedToWindow(WindowLayer* window)
{
fWindow = window;
fIsTopLayer = topLayer;
for (ViewLayer* child = FirstChild(); child; child = NextChild())
child->AttachedToWindow(window);
@ -211,13 +221,13 @@ ViewLayer::LastChild() const
ViewLayer*
ViewLayer::TopLayer()
{
if (fIsTopLayer)
return this;
// returns the top level view of the hirarchy,
// it doesn't have to be the top level of a window
if (fParent)
return fParent->TopLayer();
else
return NULL;
return this;
}
// CountChildren
@ -328,6 +338,36 @@ ViewLayer::ConvertToTop(BRegion* region) const
fParent->ConvertToTop(region);
}
// ConvertFromTop
void
ViewLayer::ConvertFromTop(BPoint* point) const
{
ConvertFromParent(point);
if (fParent)
fParent->ConvertFromTop(point);
}
// ConvertFromTop
void
ViewLayer::ConvertFromTop(BRect* rect) const
{
ConvertFromParent(rect);
if (fParent)
fParent->ConvertFromTop(rect);
}
// ConvertFromTop
void
ViewLayer::ConvertFromTop(BRegion* region) const
{
ConvertFromParent(region);
if (fParent)
fParent->ConvertFromTop(region);
}
// SetName
void
ViewLayer::SetName(const char* string)
@ -335,72 +375,7 @@ ViewLayer::SetName(const char* string)
fName.SetTo(string);
}
#if 0
// MoveBy
void
ViewLayer::MoveBy(int32 x, int32 y, BRegion* dirtyRegion)
{
if (x == 0 && y == 0)
return;
if (!fIsTopLayer && fWindow) {
// blit to new location (children as well)
BRect screenRect;
if (fParent) {
screenRect = fFrame & fParent->Bounds();
fParent->ConvertToTop(&screenRect);
} else {
screenRect = Bounds();
ConvertToTop(&screenRect);
}
BRegion effectiveClippingRegion;
fWindow->GetContentRegion(&effectiveClippingRegion);
effectiveClippingRegion.IntersectWith(&fWindow->VisibleRegion());
BRegion copyRegion(screenRect);
copyRegion.IntersectWith(&effectiveClippingRegion);
copyRegion.OffsetBy(x, y);
copyRegion.IntersectWith(&effectiveClippingRegion);
BRegion alreadyDirty(screenRect);
{
BRegion windowDirty(fWindow->DirtyRegion());
alreadyDirty.IntersectWith(&windowDirty);
}
fFrame.OffsetBy(x, y);
_MoveScreenClipping(x, y, true);
if (fParent) {
screenRect = fFrame & fParent->Bounds();
fParent->ConvertToTop(&screenRect);
} else {
screenRect = Bounds();
ConvertToTop(&screenRect);
}
BRegion dirtyRegion(screenRect);
dirtyRegion.Exclude(&copyRegion);
alreadyDirty.OffsetBy(x, y);
dirtyRegion.Include(&alreadyDirty);
fWindow->MarkDirty(&dirtyRegion);
copyRegion.OffsetBy(-x, -y);
if (fWindow->GetDrawingEngine()->Lock()) {
fWindow->GetDrawingEngine()->CopyRegion(&copyRegion, x, y);
fWindow->GetDrawingEngine()->Unlock();
}
} else {
fFrame.OffsetBy(x, y);
_MoveScreenClipping(x, y, true);
}
fFrame.OffsetBy(x, y);
}
#else // 0
#pragma mark -
// MoveBy
void
@ -410,28 +385,64 @@ ViewLayer::MoveBy(int32 x, int32 y, BRegion* dirtyRegion)
return;
fFrame.OffsetBy(x, y);
InvalidateScreenClipping(true);
// to move on screen, we must not be hidden and we must have a parent
if (!IsHidden() && fParent && !fIsTopLayer && dirtyRegion) {
// clip to parent
BRect oldScreenRect(Bounds());
oldScreenRect.OffsetByCopy(-x, -y);
ConvertToParent(&oldScreenRect);
if (!IsHidden() && fParent && dirtyRegion) {
#if 0
// based on redraw on new location
// the place were we are now visible
BRect newVisibleBounds = Bounds();
// we can use the frame of the old
// local clipping to see which parts need invalidation
BRect oldVisibleBounds(Bounds());
oldVisibleBounds.OffsetBy(-x, -y);
ConvertToTop(&oldVisibleBounds);
BRect screenRect(Bounds());
ConvertToParent(&screenRect);
ConvertToVisibleInTopView(&newVisibleBounds);
// TODO: see AddChild
// TODO: shouldn't we use regions?
BRect dirty = oldScreenRect | screenRect;
dirty = dirty & fParent->Bounds();
fParent->ConvertToTop(&dirty);
dirtyRegion->Include(dirty);
dirtyRegion->Include(oldVisibleBounds);
// newVisibleBounds already is in screen coords
dirtyRegion->Include(newVisibleBounds);
#else
// blitting version, invalidates
// old contents
BRect oldVisibleBounds(Bounds());
oldVisibleBounds.OffsetBy(-x, -y);
ConvertToTop(&oldVisibleBounds);
BRect newVisibleBounds(Bounds());
// NOTE: using ConvertToVisibleInTopView()
// instead of ConvertToTop()! see below
ConvertToVisibleInTopView(&newVisibleBounds);
newVisibleBounds.OffsetBy(-x, -y);
// clipping oldVisibleBounds to newVisibleBounds
// makes sure we don't copy parts hidden under
// parent views
BRegion copyRegion(oldVisibleBounds & newVisibleBounds);
fWindow->CopyContents(&copyRegion, x, y);
BRegion dirty(oldVisibleBounds);
newVisibleBounds.OffsetBy(x, y);
dirty.Exclude(newVisibleBounds);
dirtyRegion->Include(&dirty);
#endif
}
if (!fParent) {
// the top view's screen clipping does not change,
// because no parts are clipped away from parent
// views
_MoveScreenClipping(x, y, true);
} else {
// parts might have been revealed from underneath
// the parent, or might now be hidden underneath
// the parent, this is taken care of when building
// the screen clipping
InvalidateScreenClipping(true);
}
}
#endif // 0
// ResizeBy
void
@ -472,19 +483,17 @@ ViewLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
ConvertToTop(&dirty);
dirtyRegion->Include(&dirty);
}
// layout the children
for (ViewLayer* child = FirstChild(); child; child = NextChild())
child->ParentResized(x, y, dirtyRegion);
// at this point, children are at their new locations,
// so we can rebuild the clipping
RebuildClipping(false);
} else {
// just layout the children
for (ViewLayer* child = FirstChild(); child; child = NextChild())
child->ParentResized(x, y, NULL);
}
// layout the children
for (ViewLayer* child = FirstChild(); child; child = NextChild())
child->ParentResized(x, y, dirtyRegion);
// at this point, children are at their new locations,
// so we can rebuild the clipping
// TODO: when the implementation of Hide() and Show() is
// complete, see if this should be avoided
RebuildClipping(false);
}
// ParentResized
@ -542,6 +551,8 @@ ViewLayer::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion)
InvalidateScreenClipping(true);
}
#pragma mark -
// Draw
void
ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping,
@ -606,20 +617,17 @@ ViewLayer::ClientDraw(DrawingEngine* drawingEngine, BRegion* effectiveClipping)
}
}
#pragma mark -
// IsHidden
bool
ViewLayer::IsHidden() const
{
// if we're explicitely hidden...
// if we're explicitely hidden, then we're hidden...
if (fHidden)
return true;
// if we don't have a window, or our window is hidden,
// then yes, we're hidden too
if (fWindow == NULL || (fWindow && fWindow->IsHidden()))
return true;
// if we're not hidden yet, we might still be hidden if our parent is
// ...but if we're not hidden, we might still be hidden if our parent is
if (fParent)
return fParent->IsHidden();
@ -631,18 +639,30 @@ ViewLayer::IsHidden() const
void
ViewLayer::Hide()
{
fHidden = true;
if (!fHidden) {
fHidden = true;
// TODO: track regions
if (fParent && !fParent->IsHidden()) {
// TODO: track regions
// RebuildClipping()
// ...
}
}
}
// Show
void
ViewLayer::Show()
{
fHidden = false;
if (fHidden) {
fHidden = false;
// TODO: track regions
if (fParent && !fParent->IsHidden()) {
// TODO: track regions
// RebuildClipping()
// ...
}
}
}
// PrintToStream
@ -655,9 +675,6 @@ ViewLayer::PrintToStream() const
void
ViewLayer::RebuildClipping(bool deep)
{
// remember current local clipping in dirty region
BRegion oldLocalClipping(fLocalClipping);
// the clipping spans over the bounds area
fLocalClipping.Set(Bounds());
@ -678,13 +695,18 @@ ViewLayer::ScreenClipping(BRegion* windowContentClipping, bool force) const
{
if (!fScreenClippingValid || force) {
fScreenClipping = fLocalClipping;
if (fParent) {
BRect parentWindow = fParent->Bounds();
ConvertFromParent(&parentWindow);
BRegion visibleInParent(parentWindow);
fScreenClipping.IntersectWith(&visibleInParent);
}
ConvertToTop(&fScreenClipping);
// see if we parts of our bounds are hidden underneath
// the parent, the local clipping does not account for this
BRect clippedBounds = Bounds();
ConvertToVisibleInTopView(&clippedBounds);
if (clippedBounds.Width() < fScreenClipping.Frame().Width() ||
clippedBounds.Height() < fScreenClipping.Frame().Height()) {
BRegion temp(clippedBounds);
fScreenClipping.IntersectWith(&temp);
}
fScreenClipping.IntersectWith(windowContentClipping);
fScreenClippingValid = true;
}

View File

@ -23,12 +23,14 @@ class ViewLayer {
inline BRect Frame() const
{ return fFrame; }
BRect Bounds() const;
// converts the given frame up the view hirarchy and
// clips to each views bounds
void ConvertToVisibleInTopView(BRect* bounds) const;
inline rgb_color ViewColor() const
{ return fViewColor; }
void AttachedToWindow(WindowLayer* window,
bool topLayer = false);
void AttachedToWindow(WindowLayer* window);
void DetachedFromWindow();
// tree stuff
@ -61,6 +63,10 @@ class ViewLayer {
void ConvertToTop(BRect* rect) const;
void ConvertToTop(BRegion* region) const;
void ConvertFromTop(BPoint* point) const;
void ConvertFromTop(BRect* rect) const;
void ConvertFromTop(BRegion* region) const;
// settings
void SetName(const char* string);
inline const char* Name() const
@ -120,7 +126,6 @@ private:
WindowLayer* fWindow;
ViewLayer* fParent;
bool fIsTopLayer;
ViewLayer* fFirstChild;
ViewLayer* fPreviousSibling;

View File

@ -56,7 +56,7 @@ WindowLayer::WindowLayer(BRect frame, const char* name,
// fFrame as if it had
fTopLayer = new(nothrow) ViewLayer(fFrame, "top view", B_FOLLOW_ALL, 0,
(rgb_color){ 255, 255, 255, 255 });
fTopLayer->AttachedToWindow(this, true);
fTopLayer->AttachedToWindow(this);
fClient->Run();
}
@ -82,7 +82,7 @@ WindowLayer::MessageReceived(BMessage* message)
}
}
_DrawBorder();
_DrawContents(fTopLayer);
_TriggerContentRedraw();
fDesktop->ReadUnlockClipping();
} else {
@ -307,6 +307,7 @@ WindowLayer::Show()
void
WindowLayer::MarkDirty(BRegion* regionOnScreen)
{
// for triggering MSG_REDRAW
if (fDesktop)
fDesktop->MarkDirty(regionOnScreen);
}
@ -315,6 +316,7 @@ WindowLayer::MarkDirty(BRegion* regionOnScreen)
void
WindowLayer::MarkContentDirty(BRegion* regionOnScreen)
{
// for triggering MSG_REDRAW
if (fDesktop && fDesktop->LockClipping()) {
regionOnScreen->IntersectWith(&fVisibleContentRegion);
@ -323,29 +325,65 @@ WindowLayer::MarkContentDirty(BRegion* regionOnScreen)
fDesktop->UnlockClipping();
}
}
/*
// DirtyRegion
BRegion
WindowLayer::DirtyRegion()
{
BRegion dirty;
if (fDesktop->ReadLockClipping()) {
dirty = *fDesktop->DirtyRegion();
fDesktop->ReadUnlockClipping();
}
return dirty;
}
*/
# pragma mark -
// _DrawContents
// CopyContents
void
WindowLayer::_DrawContents(ViewLayer* layer)
WindowLayer::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
{
// this function takes care of invalidating parts that could not be copied
if (fDesktop->LockClipping()) {
BRegion newDirty(*region);
region->IntersectWith(&fVisibleContentRegion);
region->OffsetBy(xOffset, yOffset);
region->IntersectWith(&fVisibleContentRegion);
region->OffsetBy(-xOffset, -yOffset);
newDirty.Exclude(region);
_ShiftPartOfRegion(fDesktop->DirtyRegion(), region, xOffset, yOffset);
if (fDrawingEngine->Lock()) {
fDrawingEngine->CopyRegion(region, xOffset, yOffset);
fDrawingEngine->Unlock();
}
if (fCurrentUpdateSession)
_ShiftPartOfRegion(&fCurrentUpdateSession->DirtyRegion(), region, xOffset, yOffset);
if (fPendingUpdateSession)
_ShiftPartOfRegion(&fPendingUpdateSession->DirtyRegion(), region, xOffset, yOffset);
newDirty.OffsetBy(xOffset, yOffset);
newDirty.IntersectWith(&fVisibleContentRegion);
fDesktop->MarkDirty(&newDirty);
fDesktop->UnlockClipping();
}
}
# pragma mark -
// _ShiftPartOfRegion
void
WindowLayer::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
int32 xOffset, int32 yOffset)
{
BRegion common(*region);
common.IntersectWith(regionToShift);
region->Exclude(&common);
common.OffsetBy(xOffset, yOffset);
region->Include(&common);
}
// _TriggerContentRedraw
void
WindowLayer::_TriggerContentRedraw()
{
//printf("%s - DrawContents()\n", Name());
if (!layer)
layer = fTopLayer;
if (fDesktop->ReadLockClipping()) {
BRegion dirtyContentRegion(fVisibleContentRegion);
@ -367,8 +405,8 @@ if (fDrawingEngine->Lock()) {
fDesktop->MarkClean(&dirtyContentRegion);
layer->Draw(fDrawingEngine, &dirtyContentRegion,
&fVisibleContentRegion, true);
fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion,
&fVisibleContentRegion, false);
}
fDesktop->ReadUnlockClipping();
@ -398,8 +436,14 @@ WindowLayer::_DrawClient(int32 token)
BRegion effectiveClipping(fEffectiveDrawingRegion);
effectiveClipping.IntersectWith(&layer->ScreenClipping(&fVisibleContentRegion));
if (effectiveClipping.CountRects() > 0) {
// clear the back ground
// TODO: only if this is the first drawing command for
// this layer of course!
layer->Draw(fDrawingEngine, &effectiveClipping,
&fVisibleContentRegion, false);
layer->ClientDraw(fDrawingEngine, &effectiveClipping);
}
@ -456,6 +500,8 @@ WindowLayer::_DrawBorder()
void
WindowLayer::_MarkContentDirty(BRegion* contentDirtyRegion)
{
if (fDesktop->ReadLockClipping()) {
if (contentDirtyRegion->CountRects() <= 0)
return;
@ -480,12 +526,17 @@ WindowLayer::_MarkContentDirty(BRegion* contentDirtyRegion)
fClient->PostMessage(MSG_UPDATE);
fUpdateRequested = true;
}
fDesktop->ReadUnlockClipping();
}
}
// _BeginUpdate
void
WindowLayer::_BeginUpdate()
{
if (fDesktop->ReadLockClipping()) {
if (fUpdateRequested && !fCurrentUpdateSession) {
fCurrentUpdateSession = fPendingUpdateSession;
fPendingUpdateSession = NULL;
@ -498,12 +549,17 @@ WindowLayer::_BeginUpdate()
}
fEffectiveDrawingRegionValid = false;
}
fDesktop->ReadUnlockClipping();
}
}
// _EndUpdate
void
WindowLayer::_EndUpdate()
{
if (fDesktop->ReadLockClipping()) {
if (fInUpdate) {
delete fCurrentUpdateSession;
fCurrentUpdateSession = NULL;
@ -518,6 +574,9 @@ WindowLayer::_EndUpdate()
} else {
fUpdateRequested = false;
}
fDesktop->ReadUnlockClipping();
}
}
#pragma mark -

View File

@ -74,13 +74,19 @@ class WindowLayer : public BLooper {
DrawingEngine* GetDrawingEngine() const
{ return fDrawingEngine; }
// BRegion DirtyRegion();
void CopyContents(BRegion* region,
int32 xOffset, int32 yOffset);
private:
void _DrawContents(ViewLayer* layer = NULL);
void _ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
int32 xOffset, int32 yOffset);
// different types of drawing
void _TriggerContentRedraw();
void _DrawClient(int32 token);
void _DrawBorder();
// handling update sessions
void _MarkContentDirty(BRegion* contentDirtyRegion);
void _BeginUpdate();
void _EndUpdate();
@ -110,11 +116,26 @@ class WindowLayer : public BLooper {
DrawingEngine* fDrawingEngine;
Desktop* fDesktop;
// for mapping drawing requests from the client
// to the local view pointers
BList fTokenViewMap;
// the client looper, which will do asynchronous
// drawing (the window will clear the content
// and the client is supposed to draw onto
// the cleared regions)
ClientLooper* fClient;
// The synchronization, which client drawing commands
// belong to the redraw of which region is handled
// through an UpdateSession. When the client has
// been informed that it should redraw stuff, then
// this is the current update session. All new
// redraw requests from the root layer will go
// into the pending update session.
UpdateSession* fCurrentUpdateSession;
UpdateSession* fPendingUpdateSession;
// these two flags are supposed to ensure a sane
// and consistent update session
bool fUpdateRequested;
bool fInUpdate;
};

View File

@ -144,13 +144,15 @@ Window::AddWindow(BRect frame, const char* name)
f.OffsetBy(f.Width() + 6, 0);
layer = new ViewLayer(f, "View", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM,
// layer = new ViewLayer(f, "View", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM,
layer = new ViewLayer(f, "View", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM,
B_FULL_UPDATE_ON_RESIZE, (rgb_color){ 120, 120, 120, 255 });
layer1->AddChild(layer);
f.OffsetBy(f.Width() + 6, 0);
layer = new ViewLayer(f, "View", B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM,
// layer = new ViewLayer(f, "View", B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM,
layer = new ViewLayer(f, "View", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM,
B_FULL_UPDATE_ON_RESIZE, (rgb_color){ 120, 120, 120, 255 });
layer1->AddChild(layer);
}