#include #include #include #include #include #include #include "Layer.h" #include "MyView.h" extern BWindow* wind; Layer::Layer(BRect frame, const char* name, uint32 rm, uint32 flags, rgb_color c) { fFrame = frame; fOrigin.Set(0.0f, 0.0f); fResizeMode = rm; fFlags = flags; fColor = c; fBottom = NULL; fUpper = NULL; fLower = NULL; fTop = NULL; fParent = NULL; fView = NULL; fCurrent = NULL; fHidden = false; strcpy(fName, name); } Layer::~Layer() { Layer *c = fBottom; Layer *toast; while (c) { toast = c; c = c->fUpper; delete toast; } } void Layer::ConvertToScreen2(BRect* rect) const { if (GetRootLayer()) if (fParent) { rect->OffsetBy(-fOrigin.x, -fOrigin.y); rect->OffsetBy(fFrame.left, fFrame.top); fParent->ConvertToScreen2(rect); } } void Layer::ConvertToScreen2(BRegion* reg) const { if (GetRootLayer()) if (fParent) { reg->OffsetBy(-fOrigin.x, -fOrigin.y); reg->OffsetBy(fFrame.left, fFrame.top); fParent->ConvertToScreen2(reg); } } MyView* Layer::GetRootLayer() const // we already have { if (fView) return fView; else if (fParent) return fParent->GetRootLayer(); else return NULL; } Layer* Layer::BottomChild() const // we already have { fCurrent = fBottom; return fCurrent; } Layer* Layer::TopChild() const// we already have { fCurrent = fTop; return fCurrent; } Layer* Layer::UpperSibling() const// we already have { fCurrent = fCurrent->fUpper; return fCurrent; } Layer* Layer::LowerSibling() const// we already have { fCurrent = fCurrent->fLower; return fCurrent; } void Layer::AddLayer(Layer* layer)// we already have { if( layer->fParent != NULL ) { printf("ERROR: Layer already has a parent\n"); return; } layer->fParent = this; if (!fBottom) { fBottom = layer; fTop = layer; return; } fBottom->fLower = layer; layer->fUpper = fBottom; fBottom = layer; } bool Layer::RemLayer(Layer* layer)// we already have { if(!layer->fParent || layer->fParent != this) { printf("ERROR: Rem: Layer doesn't have a fParent or !=this\n"); return false; } layer->fParent = NULL; if(fTop == layer) fTop = layer->fLower; if(fBottom == layer ) fBottom = layer->fUpper; if(layer->fUpper != NULL) layer->fUpper->fLower = layer->fLower; if(layer->fLower != NULL) layer->fLower->fUpper = layer->fUpper; layer->fUpper = NULL; layer->fLower = NULL; layer->clear_visible_regions(); // TAKE return true; } bool Layer::IsHidden() const { if (fHidden) return true; // TODO: remove the following 2 lines when for real. if (fView) return false; if (fParent) return fParent->IsHidden(); return fHidden; } void Layer::Hide() { fHidden = true; if (fParent && !fParent->IsHidden() && GetRootLayer()) { // save fullVisible so we know what to invalidate BRegion invalid(fFullVisible); clear_visible_regions(); if (invalid.Frame().IsValid()) fParent->Invalidate(invalid, this); } } void Layer::Show() { fHidden = false; if (fParent && !fParent->IsHidden() && GetRootLayer()) { BRegion invalid; get_user_regions(invalid); if (invalid.CountRects() > 0) fParent->Invalidate(invalid, this); } } void Layer::Invalidate(const BRegion &invalid, const Layer *startFrom) { BRegion localVisible(fFullVisible); localVisible.IntersectWith(&invalid); rebuild_visible_regions(invalid, localVisible, startFrom? startFrom: BottomChild()); // add localVisible to our RootLayer's redraw region. GetRootLayer()->fRedrawReg.Include(&localVisible); GetRootLayer()->RequestRedraw(); // TODO: what if we pass (fParent, startFromTHIS, &redrawReg)? } inline void Layer::resize_layer_frame_by(float x, float y) { uint16 rm = fResizeMode & 0x0000FFFF; BRect newFrame = fFrame; if ((rm & 0x0F00U) == _VIEW_LEFT_ << 8) newFrame.left += 0.0f; else if ((rm & 0x0F00U) == _VIEW_RIGHT_ << 8) newFrame.left += x; else if ((rm & 0x0F00U) == _VIEW_CENTER_ << 8) newFrame.left += x/2; if ((rm & 0x000FU) == _VIEW_LEFT_) newFrame.right += 0.0f; else if ((rm & 0x000FU) == _VIEW_RIGHT_) newFrame.right += x; else if ((rm & 0x000FU) == _VIEW_CENTER_) newFrame.right += x/2; if ((rm & 0xF000U) == _VIEW_TOP_ << 12) newFrame.top += 0.0f; else if ((rm & 0xF000U) == _VIEW_BOTTOM_ << 12) newFrame.top += y; else if ((rm & 0xF000U) == _VIEW_CENTER_ << 12) newFrame.top += y/2; if ((rm & 0x00F0U) == _VIEW_TOP_ << 4) newFrame.bottom += 0.0f; else if ((rm & 0x00F0U) == _VIEW_BOTTOM_ << 4) newFrame.bottom += y; else if ((rm & 0x00F0U) == _VIEW_CENTER_ << 4) newFrame.bottom += y/2; if (newFrame != fFrame) { float dx, dy; dx = newFrame.Width() - fFrame.Width(); dy = newFrame.Height() - fFrame.Height(); fFrame = newFrame; if (dx != 0.0f || dy != 0.0f) { // call hook function ResizedByHook(dx, dy, true); // automatic for (Layer *lay = BottomChild(); lay; lay = UpperSibling()) lay->resize_layer_frame_by(dx, dy); } else MovedByHook(dx, dy); } } inline void Layer::rezize_layer_redraw_more(BRegion ®, float dx, float dy) { if (dx == 0 && dy == 0) return; for (Layer *lay = BottomChild(); lay; lay = UpperSibling()) { uint16 rm = lay->fResizeMode & 0x0000FFFF; if ((rm & 0x0F0F) == (uint16)B_FOLLOW_LEFT_RIGHT || (rm & 0xF0F0) == (uint16)B_FOLLOW_TOP_BOTTOM) { // NOTE: this is not exactly corect, but it works :-) // Normaly we shoud've used the lay's old, required region - the one returned // from get_user_region() with the old frame, and the current one. lay->Bounds() // works for the moment so we leave it like this. // calculate the old bounds. BRect oldBounds(lay->Bounds()); if ((rm & 0x0F0F) == (uint16)B_FOLLOW_LEFT_RIGHT) oldBounds.right -=dx; if ((rm & 0xF0F0) == (uint16)B_FOLLOW_TOP_BOTTOM) oldBounds.bottom -=dy; // compute the region that became visible because we got bigger OR smaller. BRegion regZ(lay->Bounds()); regZ.Include(oldBounds); regZ.Exclude(oldBounds&lay->Bounds()); lay->ConvertToScreen2(®Z); // intersect that with this'(not lay's) fullVisible region regZ.IntersectWith(&fFullVisible); reg.Include(®Z); lay->rezize_layer_redraw_more(reg, (rm & 0x0F0F) == (uint16)B_FOLLOW_LEFT_RIGHT? dx: 0, (rm & 0xF0F0) == (uint16)B_FOLLOW_TOP_BOTTOM? dy: 0); // above, OR this: // reg.Include(&lay->fFullVisible); } else if (((rm & 0x0F0F) == (uint16)B_FOLLOW_RIGHT && dx != 0) || ((rm & 0x0F0F) == (uint16)B_FOLLOW_H_CENTER && dx != 0) || ((rm & 0xF0F0) == (uint16)B_FOLLOW_BOTTOM && dy != 0)|| ((rm & 0xF0F0) == (uint16)B_FOLLOW_V_CENTER && dy != 0)) { reg.Include(&lay->fFullVisible); } } } inline void Layer::resize_layer_full_update_on_resize(BRegion ®, float dx, float dy) { if (dx == 0 && dy == 0) return; for (Layer *lay = BottomChild(); lay; lay = UpperSibling()) { uint16 rm = lay->fResizeMode & 0x0000FFFF; if ((rm & 0x0F0F) == (uint16)B_FOLLOW_LEFT_RIGHT || (rm & 0xF0F0) == (uint16)B_FOLLOW_TOP_BOTTOM) { if (lay->fFlags & B_FULL_UPDATE_ON_RESIZE && lay->fVisible.CountRects() > 0) reg.Include(&lay->fVisible); lay->resize_layer_full_update_on_resize(reg, (rm & 0x0F0F) == (uint16)B_FOLLOW_LEFT_RIGHT? dx: 0, (rm & 0xF0F0) == (uint16)B_FOLLOW_TOP_BOTTOM? dy: 0); } } } void Layer::ResizeBy(float dx, float dy) { fFrame.Set(fFrame.left, fFrame.top, fFrame.right+dx, fFrame.bottom+dy); // resize children using their resize_mask. for (Layer *lay = BottomChild(); lay; lay = UpperSibling()) lay->resize_layer_frame_by(dx, dy); // call hook function if (dx != 0.0f || dy != 0.0f) ResizedByHook(dx, dy, false); // manual if (!IsHidden() && GetRootLayer()) { BRegion oldFullVisible(fFullVisible); // this is required to invalidate the old border BRegion oldVisible(fVisible); // in case they moved, bottom, right and center aligned layers must be redrawn BRegion redrawMore; rezize_layer_redraw_more(redrawMore, dx, dy); // we'll invalidate the old area and the new, maxmial one. BRegion invalid; get_user_regions(invalid); invalid.Include(&fFullVisible); clear_visible_regions(); fParent->RebuildVisibleRegions(invalid, this); // done rebuilding regions, now redraw regions that became visible // what's invalid, are the differences between to old and the new fullVisible region // 1) in case we grow. BRegion redrawReg(fFullVisible); redrawReg.Exclude(&oldFullVisible); // 2) in case we shrink BRegion redrawReg2(oldFullVisible); redrawReg2.Exclude(&fFullVisible); // 3) combine. redrawReg.Include(&redrawReg2); // for center, right and bottom alligned layers, redraw their old positions redrawReg.Include(&redrawMore); // layers that had their frame modified must be entirely redrawn. rezize_layer_redraw_more(redrawReg, dx, dy); // add redrawReg to our RootLayer's redraw region. GetRootLayer()->fRedrawReg.Include(&redrawReg); // include layer's visible region in case we want a full update on resize if (fFlags & B_FULL_UPDATE_ON_RESIZE && fVisible.Frame().IsValid()) { resize_layer_full_update_on_resize(GetRootLayer()->fRedrawReg, dx, dy); GetRootLayer()->fRedrawReg.Include(&fVisible); GetRootLayer()->fRedrawReg.Include(&oldVisible); } // clear canvas and set invalid regions for affected WinBorders GetRootLayer()->RequestRedraw(); // TODO: what if we pass (fParent, startFromTHIS, &redrawReg)? } } void Layer::MoveBy(float dx, float dy) { if (dx == 0.0f && dy == 0.0f) return; // fFrame.Set(fFrame.left+dx, fFrame.top+dy, fFrame.right+dx, fFrame.bottom+dy); fFrame.OffsetBy(dx, dy); // call hook function MovedByHook(dx, dy); if (!IsHidden() && GetRootLayer()) { BRegion oldFullVisible(fFullVisible); // we'll invalidate the old position and the new, maxmial one. BRegion invalid; get_user_regions(invalid); invalid.Include(&fFullVisible); clear_visible_regions(); fParent->RebuildVisibleRegions(invalid, this); // done rebuilding regions, now copy common parts and redraw regions that became visible // include the actual and the old fullVisible regions. later, we'll exclude the common parts. BRegion redrawReg(fFullVisible); redrawReg.Include(&oldFullVisible); // offset to layer's new location so that we can calculate the common region. oldFullVisible.OffsetBy(dx, dy); // finally we have the region that needs to be redrawn. redrawReg.Exclude(&oldFullVisible); // by intersecting the old fullVisible offseted to layer's new location, with the current // fullVisible, we'll have the common region which can be copied using HW acceleration. oldFullVisible.IntersectWith(&fFullVisible); // offset back and instruct the HW to do the actual copying. oldFullVisible.OffsetBy(-dx, -dy); GetRootLayer()->CopyRegion(&oldFullVisible, dx, dy); // add redrawReg to our RootLayer's redraw region. GetRootLayer()->fRedrawReg.Include(&redrawReg); GetRootLayer()->RequestRedraw(); // TODO: what if we pass (fParent, startFromTHIS, &redrawReg)? } } void Layer::ScrollBy(float dx, float dy) { fOrigin.Set(fOrigin.x + dx, fOrigin.y + dy); if (!IsHidden() && GetRootLayer()) { // set the region to be invalidated. BRegion invalid(fFullVisible); clear_visible_regions(); rebuild_visible_regions(invalid, invalid, BottomChild()); // for the moment we say that the whole surface needs to be redraw. BRegion redrawReg(fFullVisible); // offset old region so that we can start comparing. invalid.OffsetBy(dx, dy); // compute the common region. we'll use HW acc to copy this to the new location. invalid.IntersectWith(&fFullVisible); GetRootLayer()->CopyRegion(&invalid, -dx, -dy); // common region goes back to its original location. then, by excluding // it from curent fullVisible we'll obtain the region that needs to be redrawn. invalid.OffsetBy(-dx, -dy); redrawReg.Exclude(&invalid); GetRootLayer()->fRedrawReg.Include(&redrawReg); GetRootLayer()->RequestRedraw(); // TODO: what if we pass (fParent, startFromTHIS, &redrawReg)? } if (dx != 0.0f || dy != 0.0f) ScrolledByHook(dx, dy); } void Layer::GetWantedRegion(BRegion ®) // TAKE? { get_user_regions(reg); } void Layer::get_user_regions(BRegion ®) { // 1) set to frame in screen coords BRect screenFrame(Bounds()); ConvertToScreen2(&screenFrame); reg.Set(screenFrame); // 2) intersect with screen region // TODO: remove locking when for real wind->Lock(); BRegion screenReg(GetRootLayer()->Bounds()); wind->Unlock(); reg.IntersectWith(&screenReg); // TODO: you MUST at some point uncomment this block! /* // 3) impose user constrained regions LayerData *stackData = fLayerData; while (stackData) { // transform in screen coords BRegion screenReg(stackData->ClippingRegion()); ConvertToScreen2(&screenReg); reg.IntersectWith(&screenReg); stackData = stackData->prevState; } */ } void Layer::RebuildVisibleRegions(const BRegion &invalid, const Layer *startFrom) { BRegion localVisible(fFullVisible); localVisible.IntersectWith(&invalid); rebuild_visible_regions(invalid, localVisible, startFrom); } void Layer::rebuild_visible_regions(const BRegion &invalid, const BRegion &parentLocalVisible, const Layer *startFrom) { // no point in continuing if this layer is hidden. starting from here, all // descendants have (and will have) invalid visible regions. if (fHidden) return; // no need to go deeper if the parent doesn't have a visible region anymore // and our fullVisible region is also empty. if (!parentLocalVisible.Frame().IsValid() && !(fFullVisible.CountRects() > 0)) return; bool fullRebuild = false; // intersect maximum wanted region with the invalid region BRegion common; get_user_regions(common); common.IntersectWith(&invalid); // if the resulted region is not valid, this layer is not in the catchment area // of the region being invalidated if (!common.CountRects() > 0) return; // now intersect with parent's visible part of the region that was/is invalidated common.IntersectWith(&parentLocalVisible); // exclude the invalid region fFullVisible.Exclude(&invalid); fVisible.Exclude(&invalid); // put in what's really visible fFullVisible.Include(&common); // this is to allow a layer to hide some parts of itself so children // won't take them. BRegion unalteredVisible(common); bool altered = alter_visible_for_children(common); for (Layer *lay = BottomChild(); lay; lay = UpperSibling()) { if (lay == startFrom) fullRebuild = true; if (fullRebuild) lay->rebuild_visible_regions(invalid, common, lay->BottomChild()); // to let children know much they can take from parent's visible region common.Exclude(&lay->fFullVisible); // we've hidden some parts of our visible region from our children, // and we must be in sysnc with this region too... if (altered) unalteredVisible.Exclude(&lay->fFullVisible); } // the visible region of this layer is what left after all its children took // what they could. if (altered) fVisible.Include(&unalteredVisible); else fVisible.Include(&common); } bool Layer::alter_visible_for_children(BRegion ®) { // Empty Hook function return false; } void Layer::clear_visible_regions() { // OPT: maybe we should uncomment these lines for performance //if (fFullVisible.CountRects() <= 0) // return; fVisible.MakeEmpty(); fFullVisible.MakeEmpty(); for (Layer *child = BottomChild(); child; child = UpperSibling()) child->clear_visible_regions(); } void Layer::PrintToStream() const { printf("-> %s\n", fName); fVisible.PrintToStream(); fFullVisible.PrintToStream(); for (Layer *child = BottomChild(); child; child = UpperSibling()) child->PrintToStream(); }