/// <summary> /// Overriden since, if our old Height is zero, the base method will assume the box is new and does /// not need invalidate, since it's container will figure its new area. But the root has no container, /// so we handle this special case here. /// </summary> internal override bool Relayout(LayoutInfo transform, Dictionary <Box, Rectangle> fixupMap, LayoutCallbacks lcb) { var oldHeight = Height; var result = base.Relayout(transform, fixupMap, lcb); if (oldHeight == 0 && Height != 0) { lcb.InvalidateInRoot(InvalidateRect); } if (Height != oldHeight) { RaiseSizeChanged(); } return(result); }
/// <summary> ///When the contents of a box changes, and its size consequently might change, something ///needs to be done about the layout of that box and all its containers. ///The process begins by constructing a FixupMap, which contains the changed box and all ///its parents, each recording (against the box as a key) the invalidate rectangle appropriate ///to the old box layout. ///We then pass this to the Relayout method of the root box. ///By default, any box which finds itself in the map, or which has never been laid out ///(its height is zero), does a full normal layout, and invalidates its old (if any) ///rectangle. It can't invalidate its new rectangle, because at the point where relayout ///is called, the parent box may not have finalized the new position of the child... ///so return true if the parent needs to invalidate the new position. ///Relayout should not be used in cases where the available width may have changed, as this ///could affect the layout of boxes that are not in the map. ///Note that, if the box moves, invalidating its new size at its old position, or vice versa, ///may not do much good. If it moves, the containing box must do appropriate extra ///invalidating. ///Some boxes, notably VwDivBox, may not need to relayout all their children, or even to ///invalidate all their own contents. This can be an important optimization, but must be ///done with care to ensure that what is actually drawn is always correct. /// </summary> internal virtual bool Relayout(LayoutInfo transform, Dictionary <Box, Rectangle> fixupMap, LayoutCallbacks lcb) { if (Height == 0) { // Never been laid out. Can't need to invalidate, before or after. Layout(transform); return(false); } Rectangle invalidRect; if (fixupMap.TryGetValue(this, out invalidRect)) { lcb.InvalidateInRoot(invalidRect); Layout(transform); return(true); } // Previously laid-out box unaffected by current events. Do nothing, caller need not invalidate unless moved. return(false); }
/// <summary> /// Keep in sync with PileBox.Layout. /// It's important to optimize relayout of DivBox, because they can be very large, and often the height /// of a child does not actually change. Also, this is currently the implementation inherited by root box, /// and the boolean returned from Relayout on the root is ignored; this routine must accomplish all needed /// layout itself. /// We override here rather than on PileBox because non-div piles are typically small and laying out the whole /// thing is not too expensive, while it is harder to predict how changes (e.g., in width) might affect parents. /// The parents of Divs can only be divs. /// </summary> internal override bool Relayout(LayoutInfo transform, Dictionary <Box, Rectangle> fixupMap, LayoutCallbacks lcb) { if (Height == 0) { // brand new box, needs full layout but not invalidate, since it never has been visible. Layout(transform); return(false); } Rectangle oldLocation; if (!fixupMap.TryGetValue(this, out oldLocation)) { return(false); // unchanged, this box does not need to be re-laid out or invalidated. } var left = GapLeading(transform); var top = GapTop(transform); var rightGap = GapTrailing(transform); var maxWidth = 0; int oldHeight = Height; int oldWidth = Width; var topInvalidate = int.MaxValue; int prevBoxOldBottom = 0; var childTransform = transform.WithMaxWidthOffsetBy(transform.MaxWidth - left - rightGap, Left, Top); Box prevBox = null; for (var box = FirstBox; box != null; box = box.Next) { int oldBottom = box.Bottom; // before it is moved or resized (but may be spuriously 0 for new box) bool needsInvalidate = box.Relayout(childTransform, fixupMap, lcb); top = AdjustTopForMargins(top, transform, prevBox, box); // Now figure whether we need to start invalidating based on moved boxes. // If this box moved, we need to invalidate from its top down...or even from its old // top, since the preceding box may have shrunk, without this one changing internally. if (box.Top != top) // test this before we add height to top. { topInvalidate = Math.Min(topInvalidate, Math.Min(top, prevBoxOldBottom)); box.Top = top; } box.Left = left; top += box.Height; maxWidth = Math.Max(maxWidth, box.Width); // assumes our top will not move; if it does, everything gets invalidated, so this is only wasted. if (needsInvalidate) { lcb.InvalidateInRoot(box.InvalidateRect); } // The Math.Max prevents us adjusting it downwards for a new box, which originally had zero top // and height. prevBoxOldBottom = Math.Max(prevBoxOldBottom, oldBottom); prevBox = box; } Height = top + GapBottom(transform); Width = maxWidth + left + rightGap; if (oldWidth != Width) { // The new invalidate rect may not be strictly right, but in the unlikley event that this // box moves, its parent will invalidate its new location again. lcb.InvalidateInRoot(oldLocation); lcb.InvalidateInRoot(InvalidateRect); return(false); } // Even if none of the tops moved, if our height changed, we need to invalidate the difference area. if (Height != oldHeight) { topInvalidate = Math.Min(topInvalidate, Math.Min(Height, oldHeight)); } if (topInvalidate != int.MaxValue) { var bottomInvalidate = Math.Max(Height, oldHeight); // if our top moves, the whole box will get invalidated. Assuming it doesn't, // oldLocation needs it, adjusted for the bit that didn't move at the top, and possibly // to account for getting bigger at the bottom. var invalidate = new Rectangle(oldLocation.Left, oldLocation.Top + topInvalidate, oldLocation.Width, bottomInvalidate - topInvalidate + Box.InvalidateMargin * 2); lcb.InvalidateInRoot(invalidate); } return(false); }
/// <summary> /// Overriden since, if our old Height is zero, the base method will assume the box is new and does /// not need invalidate, since it's container will figure its new area. But the root has no container, /// so we handle this special case here. /// </summary> internal override bool Relayout(LayoutInfo transform, Dictionary<Box, Rectangle> fixupMap, LayoutCallbacks lcb) { var oldHeight = Height; var result = base.Relayout(transform, fixupMap, lcb); if (oldHeight == 0 && Height != 0) lcb.InvalidateInRoot(InvalidateRect); if (Height != oldHeight) RaiseSizeChanged(); return result; }
/// <summary> ///When the contents of a box changes, and its size consequently might change, something ///needs to be done about the layout of that box and all its containers. ///The process begins by constructing a FixupMap, which contains the changed box and all ///its parents, each recording (against the box as a key) the invalidate rectangle appropriate ///to the old box layout. ///We then pass this to the Relayout method of the root box. ///By default, any box which finds itself in the map, or which has never been laid out ///(its height is zero), does a full normal layout, and invalidates its old (if any) ///rectangle. It can't invalidate its new rectangle, because at the point where relayout ///is called, the parent box may not have finalized the new position of the child... ///so return true if the parent needs to invalidate the new position. ///Relayout should not be used in cases where the available width may have changed, as this ///could affect the layout of boxes that are not in the map. ///Note that, if the box moves, invalidating its new size at its old position, or vice versa, ///may not do much good. If it moves, the containing box must do appropriate extra ///invalidating. ///Some boxes, notably VwDivBox, may not need to relayout all their children, or even to ///invalidate all their own contents. This can be an important optimization, but must be ///done with care to ensure that what is actually drawn is always correct. /// </summary> internal virtual bool Relayout(LayoutInfo transform, Dictionary<Box, Rectangle> fixupMap, LayoutCallbacks lcb) { if (Height == 0) { // Never been laid out. Can't need to invalidate, before or after. Layout(transform); return false; } Rectangle invalidRect; if (fixupMap.TryGetValue(this, out invalidRect)) { lcb.InvalidateInRoot(invalidRect); Layout(transform); return true; } // Previously laid-out box unaffected by current events. Do nothing, caller need not invalidate unless moved. return false; }
/// <summary> /// Keep in sync with PileBox.Layout. /// It's important to optimize relayout of DivBox, because they can be very large, and often the height /// of a child does not actually change. Also, this is currently the implementation inherited by root box, /// and the boolean returned from Relayout on the root is ignored; this routine must accomplish all needed /// layout itself. /// We override here rather than on PileBox because non-div piles are typically small and laying out the whole /// thing is not too expensive, while it is harder to predict how changes (e.g., in width) might affect parents. /// The parents of Divs can only be divs. /// </summary> internal override bool Relayout(LayoutInfo transform, Dictionary<Box, Rectangle> fixupMap, LayoutCallbacks lcb) { if (Height == 0) { // brand new box, needs full layout but not invalidate, since it never has been visible. Layout(transform); return false; } Rectangle oldLocation; if (!fixupMap.TryGetValue(this, out oldLocation)) return false; // unchanged, this box does not need to be re-laid out or invalidated. var left = GapLeading(transform); var top = GapTop(transform); var rightGap = GapTrailing(transform); var maxWidth = 0; int oldHeight = Height; int oldWidth = Width; var topInvalidate = int.MaxValue; int prevBoxOldBottom = 0; var childTransform = transform.WithMaxWidthOffsetBy(transform.MaxWidth - left - rightGap, Left, Top); Box prevBox = null; for (var box = FirstBox; box != null; box = box.Next) { int oldBottom = box.Bottom; // before it is moved or resized (but may be spuriously 0 for new box) bool needsInvalidate = box.Relayout(childTransform, fixupMap, lcb); top = AdjustTopForMargins(top, transform, prevBox, box); // Now figure whether we need to start invalidating based on moved boxes. // If this box moved, we need to invalidate from its top down...or even from its old // top, since the preceding box may have shrunk, without this one changing internally. if (box.Top != top) // test this before we add height to top. { topInvalidate = Math.Min(topInvalidate, Math.Min(top, prevBoxOldBottom)); box.Top = top; } box.Left = left; top += box.Height; maxWidth = Math.Max(maxWidth, box.Width); // assumes our top will not move; if it does, everything gets invalidated, so this is only wasted. if (needsInvalidate) lcb.InvalidateInRoot(box.InvalidateRect); // The Math.Max prevents us adjusting it downwards for a new box, which originally had zero top // and height. prevBoxOldBottom = Math.Max(prevBoxOldBottom, oldBottom); prevBox = box; } Height = top + GapBottom(transform); Width = maxWidth + left + rightGap; if (oldWidth != Width) { // The new invalidate rect may not be strictly right, but in the unlikley event that this // box moves, its parent will invalidate its new location again. lcb.InvalidateInRoot(oldLocation); lcb.InvalidateInRoot(InvalidateRect); return false; } // Even if none of the tops moved, if our height changed, we need to invalidate the difference area. if (Height != oldHeight) topInvalidate = Math.Min(topInvalidate, Math.Min(Height, oldHeight)); if (topInvalidate != int.MaxValue) { var bottomInvalidate = Math.Max(Height, oldHeight); // if our top moves, the whole box will get invalidated. Assuming it doesn't, // oldLocation needs it, adjusted for the bit that didn't move at the top, and possibly // to account for getting bigger at the bottom. var invalidate = new Rectangle(oldLocation.Left, oldLocation.Top + topInvalidate, oldLocation.Width, bottomInvalidate - topInvalidate + Box.InvalidateMargin*2); lcb.InvalidateInRoot(invalidate); } return false; }