#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 { MyView *view = GetRootLayer(); if (view) if (fParent) { rect->OffsetBy(-fOrigin.x, -fOrigin.y); rect->OffsetBy(fFrame.left, fFrame.top); fParent->ConvertToScreen2(rect); } } void Layer::ConvertToScreen2(BRegion* reg) const { MyView *view = GetRootLayer(); if (view) if (fParent) { reg->OffsetBy(-fOrigin.x, -fOrigin.y); reg->OffsetBy(fFrame.left, fFrame.top); fParent->ConvertToScreen2(reg); } } MyView* Layer::GetRootLayer() const { if (fView) return fView; else if (fParent) return fParent->GetRootLayer(); else return NULL; } Layer* Layer::VirtualBottomChild() const { fCurrent = fBottom; return fCurrent; } Layer* Layer::VirtualTopChild() const { fCurrent = fTop; return fCurrent; } Layer* Layer::VirtualUpperSibling() const { fCurrent = fCurrent->fUpper; return fCurrent; } Layer* Layer::VirtualLowerSibling() const { fCurrent = fCurrent->fLower; return fCurrent; } void Layer::AddLayer(Layer* layer) { 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) { 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(); return true; } bool Layer::IsVisuallyHidden() const { if (fHidden) return true; // TODO: remove the following 2 lines when for real. if (fView) return false; if (fParent) return fParent->IsVisuallyHidden(); return fHidden; } void Layer::Hide() { fHidden = true; if (fParent && !fParent->IsVisuallyHidden() && 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->IsVisuallyHidden() && GetRootLayer()) { BRegion invalid; set_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: VirtualBottomChild()); // add localVisible to our RootLayer's redraw region. GetRootLayer()->fRedrawReg.Include(&localVisible); GetRootLayer()->RequestRedraw(); // TODO: what if we pass (fParent, startFromTHIS, &redrawReg)? } 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; fFrame = newFrame; dx = newFrame.Width() - fFrame.Width(); dy = newFrame.Height() - fFrame.Height(); if (dx != 0.0f || dy != 0.0f) { // call hook function ResizedByHook(dx, dy, true); // automatic for (Layer *lay = VirtualBottomChild(); lay ; lay = VirtualUpperSibling()) { lay->resize_layer_frame_by(dx, dy); } } else MovedByHook(dx, dy); } } void Layer::resize_redraw_more_regions(BRegion &redraw) { uint16 rm = fResizeMode & 0x0000FFFF; if (rm & B_FOLLOW_RIGHT || rm & B_FOLLOW_H_CENTER || rm & B_FOLLOW_BOTTOM || rm & B_FOLLOW_V_CENTER // TODO: these 2 don't need to be redrawn entirely. but ATM we don't have a choice || rm & B_FOLLOW_LEFT_RIGHT || rm & B_FOLLOW_TOP_BOTTOM) { redraw.Include(&fFullVisible); } else { for (Layer *lay = VirtualBottomChild(); lay; lay = VirtualUpperSibling()) lay->resize_redraw_more_regions(redraw); } } 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 = VirtualBottomChild(); lay; lay = VirtualUpperSibling()) lay->resize_layer_frame_by(dx, dy); // call hook function if (dx != 0.0f || dy != 0.0f) ResizedByHook(dx, dy, false); // manual if (!IsVisuallyHidden() && GetRootLayer()) { BRegion oldFullVisible(fFullVisible); BRegion oldVisible(fVisible); // we'll invalidate the old area and the new, maxmial one. BRegion invalid; set_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); // layers that had their frame modified must be entirely redrawn. for (Layer *lay = VirtualBottomChild(); lay; lay = VirtualUpperSibling()) lay->resize_redraw_more_regions(redrawReg); // 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()) { 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 (!IsVisuallyHidden() && GetRootLayer()) { BRegion oldFullVisible(fFullVisible); // we'll invalidate the old position and the new, maxmial one. BRegion invalid; set_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 (!IsVisuallyHidden() && GetRootLayer()) { // set the region to be invalidated. BRegion invalid(fFullVisible); clear_visible_regions(); rebuild_visible_regions(invalid, invalid, VirtualBottomChild()); // 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 ®) { set_user_regions(reg); } void Layer::set_user_regions(BRegion ®) { // OPT: maybe we should have all these cached in a 'fFull' member // 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.Frame().IsValid()) return; bool fullRebuild = false; // intersect maximum wanted region with the invalid region BRegion common; set_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.Frame().IsValid()) return; // now intersect with parent's visible part of the region that was/is invalidated common.IntersectWith(&parentLocalVisible); if (common.Frame().IsValid()) { // we have something to include to our fullVisible. It may already be in // there, but we'll never know. // TODO: further analyze the next 2 lines. fFullVisible.Exclude(&invalid); fVisible.Exclude(&invalid); fFullVisible.Include(&common); } else { // this layer is in the catchment area of the region being invalidated, // yet it will have no new visible area attached to it. It means // this layer was overshaddowed by those above it and any visible area // that it may have common with the region being invalidated, must be // excluded from it's fFullVisible and fVisible regions. fFullVisible.Exclude(&invalid); fVisible.Exclude(&invalid); // we don't return here becase we want the same thing to happen to all // our children. // Don't worry about the last line from this method, it will do nothing - // common is invalid. Same goes for the last two in the 'for' statement below. } // 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 = VirtualBottomChild(); lay ; lay = VirtualUpperSibling()) { if (lay == startFrom) fullRebuild = true; if (fullRebuild) lay->rebuild_visible_regions(invalid, common, lay->VirtualBottomChild()); // to let children know much they can take from parent's visible region common.Exclude(&lay->fFullVisible); // all that a child took must be excluded from our visible region fVisible.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 = VirtualBottomChild(); child; child = VirtualUpperSibling()) child->clear_visible_regions(); } void Layer::PrintToStream() const { printf("-> %s\n", fName); fVisible.PrintToStream(); fFullVisible.PrintToStream(); for (Layer *child = VirtualBottomChild(); child; child = VirtualUpperSibling()) child->PrintToStream(); }