* a bit more sophisticated

* now with actual view layers inside the windows
* implemented the resize modes (from Adis code)
* windows have resize handles and more correctly
  clip the views inside
* bringing windows to front or sending them behind all others
* one active window, the others are inactive
* with and without focus follows mouse mode

* bugs:
  - the region marked dirty when views are
  resized is not correct yet

* todo:
  - move the dirty region from being managed by the
  desktop to being managed in each window (and being
  local too)
  - scrolling
  - hiding/showing of windows and views

I plan to extend this to fully simulate asynchronous
drawing from clients, to see any problems before
using this in the real server one day.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15132 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2005-11-24 20:49:30 +00:00
parent 1863b5b6a4
commit 0b78f37e8e
7 changed files with 334 additions and 58 deletions
@@ -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);
@@ -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
+130 -30
View File
@@ -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);
}
}
}
@@ -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);
@@ -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");
@@ -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;
+40 -6
View File
@@ -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);