diff --git a/src/tests/servers/app/newerClipping/Desktop.cpp b/src/tests/servers/app/newerClipping/Desktop.cpp index a132faeb3d..8ca41a6d5d 100644 --- a/src/tests/servers/app/newerClipping/Desktop.cpp +++ b/src/tests/servers/app/newerClipping/Desktop.cpp @@ -28,7 +28,10 @@ Desktop::Desktop(DrawView* drawView) fDrawView(drawView), fDrawingEngine(fDrawView->GetDrawingEngine()), - fWindows(64) + fWindows(64), + + fFocusFollowsMouse(true), + fFocusWindow(NULL) { fDrawView->SetDesktop(this); @@ -134,6 +137,10 @@ Desktop::MouseUp(BPoint where) void Desktop::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage) { + WindowLayer* window; + if (!fTracking && fFocusFollowsMouse && (window = WindowAt(where))) { + SetFocusWindow(window); + } if (fTracking) { int32 dx = (int32)(where.x - fLastMousePos.x); int32 dy = (int32)(where.y - fLastMousePos.y); @@ -239,6 +246,8 @@ Desktop::AddWindow(WindowLayer* window) UnlockClipping(); } + SetFocusWindow(window); + success = true; } return success; @@ -436,6 +445,9 @@ Desktop::BringToFront(WindowLayer* window) UnlockClipping(); } + + if (!fFocusFollowsMouse) + SetFocusWindow(TopWindow()); } // SendToBack @@ -467,8 +479,31 @@ Desktop::SendToBack(WindowLayer* window) UnlockClipping(); } + + if (!fFocusFollowsMouse) + SetFocusWindow(TopWindow()); } +// SetFocusWindow +void +Desktop::SetFocusWindow(WindowLayer* window) +{ + // TODO: find bug, this invalidates too many regions + + if (fFocusWindow == window) + return; + + if (fFocusWindow) + fFocusWindow->SetFocus(false); + + fFocusWindow = window; + + if (fFocusWindow) + fFocusWindow->SetFocus(true); +} + + + #pragma mark - // MarkDirty @@ -554,6 +589,11 @@ Desktop::_TriggerWindowRedrawing(BRegion* newDirtyRegion) void Desktop::_SetBackground(BRegion* background) { + // NOTE: the drawing operation is caried out + // in the clipping region rebuild, but it is + // ok actually, because it also avoids trails on + // moving windows + // remember the region not covered by any windows // and redraw the dirty background BRegion dirtyBackground(*background); diff --git a/src/tests/servers/app/newerClipping/Desktop.h b/src/tests/servers/app/newerClipping/Desktop.h index 4a5f72f86f..dd35765512 100644 --- a/src/tests/servers/app/newerClipping/Desktop.h +++ b/src/tests/servers/app/newerClipping/Desktop.h @@ -54,6 +54,8 @@ class Desktop : public BLooper { void BringToFront(WindowLayer* window); void SendToBack(WindowLayer* window); + void SetFocusWindow(WindowLayer* window); + #if MULTI_LOCKER # if 0 bool ReadLockClipping() { return fClippingLock.ReadLock(); } @@ -105,6 +107,9 @@ private: DrawingEngine* fDrawingEngine; BList fWindows; + + bool fFocusFollowsMouse; + WindowLayer* fFocusWindow; }; #endif // DESKTOP_H diff --git a/src/tests/servers/app/newerClipping/ViewLayer.cpp b/src/tests/servers/app/newerClipping/ViewLayer.cpp index 3732d874de..68e469c88c 100644 --- a/src/tests/servers/app/newerClipping/ViewLayer.cpp +++ b/src/tests/servers/app/newerClipping/ViewLayer.cpp @@ -5,6 +5,7 @@ #include "Desktop.h" #include "DrawingEngine.h" +#include "WindowLayer.h" #include "ViewLayer.h" @@ -39,6 +40,10 @@ ViewLayer::ViewLayer(BRect frame, const char* name, fScreenClipping(), fScreenClippingValid(false) { + fFrame.left = float((int32)fFrame.left); + fFrame.top = float((int32)fFrame.top); + fFrame.right = float((int32)fFrame.right); + fFrame.bottom = float((int32)fFrame.bottom); } // destructor @@ -103,8 +108,11 @@ ViewLayer::AddChild(ViewLayer* layer) } fLastChild = layer; - if (fParent) { - RebuildClipping(false); + RebuildClipping(false); + + if (fWindow) { + layer->AttachedToWindow(fWindow); + fWindow->MarkDirty(&layer->ScreenClipping()); } } @@ -138,8 +146,12 @@ ViewLayer::RemoveChild(ViewLayer* layer) layer->fPreviousSibling = NULL; layer->fNextSibling = NULL; - // TODO: track regions - RebuildClipping(false); + if (fParent) { + RebuildClipping(false); + } + if (fWindow) { + layer->DetachedFromWindow(); + } return true; } @@ -201,13 +213,38 @@ ViewLayer::CountChildren() const return count; } -// ConvertToTop +// ConvertToParent void -ViewLayer::ConvertToTop(BPoint* point) const +ViewLayer::ConvertToParent(BPoint* point) const { // remove scrolling offset and convert to parent coordinate space point->x += fFrame.left - fScrollingOffset.x; point->y += fFrame.top - fScrollingOffset.y; +} + +// ConvertToParent +void +ViewLayer::ConvertToParent(BRect* rect) const +{ + // remove scrolling offset and convert to parent coordinate space + rect->OffsetBy(fFrame.left - fScrollingOffset.x, + fFrame.top - fScrollingOffset.y); +} + +// ConvertToParent +void +ViewLayer::ConvertToParent(BRegion* region) const +{ + // remove scrolling offset and convert to parent coordinate space + region->OffsetBy(fFrame.left - fScrollingOffset.x, + fFrame.top - fScrollingOffset.y); +} + +// ConvertToTop +void +ViewLayer::ConvertToTop(BPoint* point) const +{ + ConvertToParent(point); if (fParent) fParent->ConvertToTop(point); @@ -217,9 +254,7 @@ ViewLayer::ConvertToTop(BPoint* point) const void ViewLayer::ConvertToTop(BRect* rect) const { - // remove scrolling offset and convert to parent coordinate space - rect->OffsetBy(fFrame.left - fScrollingOffset.x, - fFrame.top - fScrollingOffset.y); + ConvertToParent(rect); if (fParent) fParent->ConvertToTop(rect); @@ -229,9 +264,7 @@ ViewLayer::ConvertToTop(BRect* rect) const void ViewLayer::ConvertToTop(BRegion* region) const { - // remove scrolling offset and convert to parent coordinate space - region->OffsetBy(fFrame.left - fScrollingOffset.x, - fFrame.top - fScrollingOffset.y); + ConvertToParent(region); if (fParent) fParent->ConvertToTop(region); @@ -263,20 +296,22 @@ ViewLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) fFrame.right += x; fFrame.bottom += y; - // TODO: broken when getting smaller - // ... either here or in WindowLayer::ResizeBy() + // layout the children + for (ViewLayer* child = FirstChild(); child; child = NextChild()) + child->ParentResized(x, y, dirtyRegion); + // TODO: the dirty region must not include children! BRegion dirty(Bounds()); - dirty.Exclude(oldBounds); + if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) + dirty.Exclude(oldBounds); + if (dirty.CountRects() > 0) { ConvertToTop(&dirty); dirtyRegion->Include(&dirty); } RebuildClipping(false); - _InvalidateScreenClipping(false); - // TODO: layout children - // TODO: ... + _InvalidateScreenClipping(true); } // ScrollBy @@ -287,26 +322,91 @@ ViewLayer::ScrollBy(int32 x, int32 y) fScrollingOffset.y += y; // TODO: CopyRegion... // TODO: ... + + _InvalidateScreenClipping(true); +} + +// ParentResized +void +ViewLayer::ParentResized(int32 x, int32 y, BRegion* dirtyRegion) +{ + uint16 rm = fResizeMode & 0x0000FFFF; + BRect newFrame = fFrame; + + // follow with left side + if ((rm & 0x0F00U) == _VIEW_RIGHT_ << 8) + newFrame.left += x; + else if ((rm & 0x0F00U) == _VIEW_CENTER_ << 8) + newFrame.left += x / 2; + + // follow with right side + if ((rm & 0x000FU) == _VIEW_RIGHT_) + newFrame.right += x; + else if ((rm & 0x000FU) == _VIEW_CENTER_) + newFrame.right += x / 2; + + // follow with top side + if ((rm & 0xF000U) == _VIEW_BOTTOM_ << 12) + newFrame.top += y; + else if ((rm & 0xF000U) == _VIEW_CENTER_ << 12) + newFrame.top += y / 2; + + // follow with bottom side + if ((rm & 0x00F0U) == _VIEW_BOTTOM_ << 4) + newFrame.bottom += y; + else if ((rm & 0x00F0U) == _VIEW_CENTER_ << 4) + newFrame.bottom += y / 2; + + if (newFrame != fFrame) { + // MoveBy will change fFrame, so cache it + BRect oldFrame = fFrame; + MoveBy(newFrame.left - oldFrame.left, + newFrame.top - oldFrame.top); + + ResizeBy(newFrame.Width() - oldFrame.Width(), + newFrame.Height() - oldFrame.Height(), dirtyRegion); + } } // Draw void ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, bool deep) { - if (drawingEngine->Lock()) { - // TODO intersect with local dirtyRegion - // fill visible region with view color - drawingEngine->SetHighColor(fViewColor); - drawingEngine->FillRegion(effectiveClipping); + // we can only draw within our own area + BRegion redraw(ScreenClipping()); + // add the current clipping + redraw.IntersectWith(effectiveClipping); - drawingEngine->MarkDirty(effectiveClipping); + if (drawingEngine->Lock()) { + drawingEngine->ConstrainClippingRegion(&redraw); + + // fill visible region with white + drawingEngine->SetHighColor(255, 255, 255); + BRect b(Bounds()); + ConvertToTop(&b); + drawingEngine->FillRect(b); + + // draw a frame with the view color + b.OffsetTo(0.0, 0.0); + ConvertToTop(&b); + drawingEngine->SetHighColor(fViewColor); + drawingEngine->StrokeRect(b); + drawingEngine->StrokeLine(b.LeftTop(), b.RightBottom()); + + drawingEngine->ConstrainClippingRegion(NULL); + + drawingEngine->MarkDirty(&redraw); drawingEngine->Unlock(); - - // let children draw - if (deep) { - for (ViewLayer* child = FirstChild(); child; child = NextChild()) { - child->Draw(drawingEngine, effectiveClipping, deep); - } + } + + // let children draw + if (deep) { + // before passing the clipping on to children, exclude our + // own region from the available clipping + effectiveClipping->Exclude(&ScreenClipping()); + + for (ViewLayer* child = FirstChild(); child; child = NextChild()) { + child->Draw(drawingEngine, effectiveClipping, deep); } } } diff --git a/src/tests/servers/app/newerClipping/ViewLayer.h b/src/tests/servers/app/newerClipping/ViewLayer.h index 80ef09b797..e19eab1bf5 100644 --- a/src/tests/servers/app/newerClipping/ViewLayer.h +++ b/src/tests/servers/app/newerClipping/ViewLayer.h @@ -46,6 +46,10 @@ class ViewLayer { uint32 CountChildren() const; // coordinate conversion + void ConvertToParent(BPoint* point) const; + void ConvertToParent(BRect* rect) const; + void ConvertToParent(BRegion* region) const; + void ConvertToTop(BPoint* point) const; void ConvertToTop(BRect* rect) const; void ConvertToTop(BRegion* region) const; @@ -59,6 +63,9 @@ class ViewLayer { void ResizeBy(int32 dx, int32 dy, BRegion* dirtyRegion); void ScrollBy(int32 dx, int32 dy); + void ParentResized(int32 dx, int32 dy, + BRegion* dirtyRegion); + void Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, bool deep = false); diff --git a/src/tests/servers/app/newerClipping/WindowLayer.cpp b/src/tests/servers/app/newerClipping/WindowLayer.cpp index 48ef223dbb..1251dc46e5 100644 --- a/src/tests/servers/app/newerClipping/WindowLayer.cpp +++ b/src/tests/servers/app/newerClipping/WindowLayer.cpp @@ -19,7 +19,12 @@ WindowLayer::WindowLayer(BRect frame, const char* name, fFrame(frame), fVisibleRegion(), - fBorderColor((rgb_color){ 255, 203, 0, 255 }), + fBorderRegion(), + fBorderRegionValid(false), + fContentRegion(), + fContentRegionValid(false), + + fFocus(false), fTopLayer(NULL), @@ -95,6 +100,10 @@ WindowLayer::GetFullRegion(BRegion* region) const void WindowLayer::GetBorderRegion(BRegion* region) const { + if (fBorderRegionValid) { + *region = fBorderRegion; + } + // TODO: speed up by avoiding "Exclude()" // start from the frame, extend to include decorator border region->Set(BRect(fFrame.left - 4, fFrame.top - 4, @@ -112,6 +121,45 @@ WindowLayer::GetBorderRegion(BRegion* region) const fFrame.right, fFrame.bottom)); } +// GetContentRegion +void +WindowLayer::GetContentRegion(BRegion* region) const +{ + if (fContentRegionValid) { + *region = fContentRegion; + } + + // TODO: speed up by avoiding "Exclude()" + // start from the frame, extend to include decorator border + region->Set(fFrame); + + // resize handle + // if (B_DOCUMENT_WINDOW_LOOK) + region->Exclude(BRect(fFrame.right - 10, fFrame.bottom - 10, + fFrame.right, fFrame.bottom)); +} + +// SetFocus +void +WindowLayer::SetFocus(bool focus) +{ + if (Lock()) { + // executed from Desktop thread, so it's fine + // to use the clipping without locking + if (fDesktop->ReadLockClipping()) { + BRegion dirty(fBorderRegion); + dirty.IntersectWith(&fVisibleRegion); + MarkDirty(&dirty); + + fDesktop->ReadUnlockClipping(); + } + + fFocus = focus; + + Unlock(); + } +} + // MoveBy void WindowLayer::MoveBy(int32 x, int32 y) @@ -121,6 +169,11 @@ WindowLayer::MoveBy(int32 x, int32 y) fFrame.OffsetBy(x, y); + if (fBorderRegionValid) + fBorderRegion.OffsetBy(x, y); + if (fContentRegionValid) + fContentRegion.OffsetBy(x, y); + fTopLayer->MoveBy(x, y); // TODO: move a local dirty region! @@ -133,15 +186,21 @@ WindowLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) if (x == 0 && y == 0) return; -// BRegion previousBorder(); -// fFrame.right += x; fFrame.bottom += y; + fBorderRegionValid = false; + fContentRegionValid = false; + // the border is dirty, put it into // dirtyRegion for a start GetBorderRegion(dirtyRegion); + // put the previous border region into the dirty region as well + // to handle the part that was overlapping a layer +// if B_DOCUMENT_WINDOW_LOOK + dirtyRegion->Include(&fBorderRegion); + fTopLayer->ResizeBy(x, y, dirtyRegion); } @@ -158,7 +217,8 @@ WindowLayer::AddChild(ViewLayer* layer) void WindowLayer::MarkDirty(BRegion* regionOnScreen) { - fDesktop->MarkDirty(regionOnScreen); + if (fDesktop) + fDesktop->MarkDirty(regionOnScreen); } # pragma mark - @@ -175,12 +235,24 @@ WindowLayer::_DrawContents(ViewLayer* layer) if (!layer) layer = fTopLayer; - BRegion effectiveLayerClipping(layer->ScreenClipping()); - effectiveLayerClipping.IntersectWith(&fVisibleRegion); - effectiveLayerClipping.IntersectWith(fDesktop->DirtyRegion()); - if (effectiveLayerClipping.Frame().IsValid()) { - layer->Draw(fDrawingEngine, &effectiveLayerClipping, true); - fDesktop->MarkClean(&effectiveLayerClipping); + if (!fContentRegionValid) { + GetContentRegion(&fContentRegion); + fContentRegionValid = true; + } + + BRegion effectiveWindowClipping(fContentRegion); + // TODO: simplify + // ideally, there would only be a local fDirtyRegion, + // that we need to intersect with. fDirtyRegion would + // alread only include fVisibleRegion + effectiveWindowClipping.IntersectWith(&fVisibleRegion); + effectiveWindowClipping.IntersectWith(fDesktop->DirtyRegion()); + if (effectiveWindowClipping.Frame().IsValid()) { + layer->Draw(fDrawingEngine, &effectiveWindowClipping, true); + + fDesktop->MarkClean(&fContentRegion); + + // send UPDATE message to the client here } //else { //printf(" nothing to do\n"); @@ -197,20 +269,29 @@ WindowLayer::_DrawBorder() snooze(10000); #endif - // construct the region containing just the border - BRegion borderRegion(fVisibleRegion); - borderRegion.Exclude(fFrame); - // intersect with the Desktop's dirty region - borderRegion.IntersectWith(fDesktop->DirtyRegion()); + if (!fBorderRegionValid) { + GetBorderRegion(&fBorderRegion); + fBorderRegionValid = true; + } - if (borderRegion.Frame().IsValid()) { + // construct the region of the border that needs redrawing + BRegion dirtyBorderRegion(fBorderRegion); + // intersect with our visible region + dirtyBorderRegion.IntersectWith(&fVisibleRegion); + // intersect with the Desktop's dirty region + dirtyBorderRegion.IntersectWith(fDesktop->DirtyRegion()); + + if (dirtyBorderRegion.Frame().IsValid()) { if (fDrawingEngine->Lock()) { - fDrawingEngine->SetHighColor(fBorderColor); - fDrawingEngine->FillRegion(&borderRegion); - fDrawingEngine->MarkDirty(&borderRegion); + if (fFocus) + fDrawingEngine->SetHighColor(255, 203, 0, 255); + else + fDrawingEngine->SetHighColor(216, 216, 216, 0); + fDrawingEngine->FillRegion(&dirtyBorderRegion); + fDrawingEngine->MarkDirty(&dirtyBorderRegion); fDrawingEngine->Unlock(); } - fDesktop->MarkClean(&borderRegion); + fDesktop->MarkClean(&dirtyBorderRegion); } //else { //printf(" nothing to do\n"); diff --git a/src/tests/servers/app/newerClipping/WindowLayer.h b/src/tests/servers/app/newerClipping/WindowLayer.h index 9d9414902f..28dbae24c2 100644 --- a/src/tests/servers/app/newerClipping/WindowLayer.h +++ b/src/tests/servers/app/newerClipping/WindowLayer.h @@ -31,6 +31,9 @@ class WindowLayer : public BLooper { { return fVisibleRegion; } void GetFullRegion(BRegion* region) const; void GetBorderRegion(BRegion* region) const; + void GetContentRegion(BRegion* region) const; + + void SetFocus(bool focus); void MoveBy(int32 x, int32 y); void ResizeBy(int32 x, int32 y, BRegion* dirtyRegion); @@ -50,7 +53,13 @@ class WindowLayer : public BLooper { // has to be called BRegion fVisibleRegion; - rgb_color fBorderColor; + // caching local regions + BRegion fBorderRegion; + bool fBorderRegionValid; + BRegion fContentRegion; + bool fContentRegionValid; + + bool fFocus; ViewLayer* fTopLayer; diff --git a/src/tests/servers/app/newerClipping/main.cpp b/src/tests/servers/app/newerClipping/main.cpp index 685cc55e92..588790541f 100644 --- a/src/tests/servers/app/newerClipping/main.cpp +++ b/src/tests/servers/app/newerClipping/main.cpp @@ -14,7 +14,6 @@ class App : public BApplication { public: App(); - ~App(); virtual void ReadyToRun(); }; @@ -22,7 +21,7 @@ class App : public BApplication { class Window : public BWindow { public: Window(const char* title); - ~Window(); + virtual ~Window(); void AddWindow(BRect frame, const char* name); void Test(); @@ -31,16 +30,14 @@ class Window : public BWindow { Desktop* fDesktop; }; +// constructor App::App() : BApplication("application/x-vnd.stippi.ClippingTest") { srand(real_time_clock_usecs()); } -App::~App() -{ -} - +// ReadyToRun void App::ReadyToRun() { @@ -50,6 +47,7 @@ App::ReadyToRun() win->Test(); } +// constructor Window::Window(const char* title) : BWindow(BRect(50, 50, 800, 650), title, B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, @@ -62,6 +60,7 @@ Window::Window(const char* title) fView->MakeFocus(true); } +// destructor Window::~Window() { fDesktop->Lock(); @@ -75,6 +74,41 @@ Window::AddWindow(BRect frame, const char* name) WindowLayer* window = new WindowLayer(frame, name, fDesktop->GetDrawingEngine(), fDesktop); + + // add a coupld children + frame.OffsetTo(B_ORIGIN); + frame.InsetBy(5.0, 5.0); + if (frame.IsValid()) { + ViewLayer* layer1 = new ViewLayer(frame, "View 1", + B_FOLLOW_ALL, + B_FULL_UPDATE_ON_RESIZE, + (rgb_color){ 180, 180, 180, 255 }); + + frame.OffsetTo(B_ORIGIN); + frame.InsetBy(15.0, 15.0); + if (frame.IsValid()) { + frame.bottom = (frame.top + frame.bottom) / 2 - 5; + + ViewLayer* layer2 = new ViewLayer(frame, "View 2", + B_FOLLOW_ALL, + 0, + (rgb_color){ 120, 120, 120, 255 }); + + frame.OffsetBy(0.0, frame.Height() + 10); + + ViewLayer* layer3 = new ViewLayer(frame, "View 3", + B_FOLLOW_BOTTOM, + 0, + (rgb_color){ 120, 120, 120, 255 }); + + + layer1->AddChild(layer2); + layer1->AddChild(layer3); + } + + window->AddChild(layer1); + } + window->Run(); BMessage message(MSG_ADD_WINDOW);