private Box ChildSetup(Box box, LayoutInfo transform, int maxBoxWidth, int left, int top) { var childTransform = transform.WithMaxWidthOffsetBy(maxBoxWidth, Left, Top); // todo: test box.Layout(childTransform); box.Left = left; box.Top = top; return box; }
public int[] ColumnWidths(int ncols, LayoutInfo layoutInfo) { int[] columnWidths = new int[ncols]; for (int i = 0; i < ncols; i++) { columnWidths[i] = (int)(m_ColumnWidths[i] * (layoutInfo.MaxWidth) / 100.0); } return columnWidths; }
// Figure out how much to adjust the top of box, which would be the input value of top // except for overlapping margins, if in fact margin overlaps the previous box. internal int AdjustTopForMargins(int top, LayoutInfo transform, Box prevBox, Box box) { if (prevBox != null) { top -= Math.Min(transform.MpToPixelsY(prevBox.Style.Margins.BottomMp), transform.MpToPixelsY(box.Style.Margins.TopMp)); } return top; }
const int HIMETRIC_INCH = 2540; // HiMetric units per inch. public override void Layout(LayoutInfo transform) { int hmHeight = Picture.Height; // "HiMetric" height and width int hmWidth = Picture.Width; Width = ((transform.DpiX * hmWidth + HIMETRIC_INCH / 2) / HIMETRIC_INCH) + GapLeading(transform) + GapTrailing(transform); Height = ((transform.DpiY * hmHeight + HIMETRIC_INCH / 2) / HIMETRIC_INCH) + GapTop(transform) + GapBottom(transform); }
public override void Layout(LayoutInfo transform) { var left = GapLeading(transform); var top = GapTop(transform); var rightGap = GapTrailing(transform); var maxHeight = 0; var maxWidth = transform.MaxWidth; Box prevBox = null; int[] columnWidths = m_ColumnWidths.ColumnWidths(NumBoxes, new LayoutInfo(transform.XOffset, transform.YOffset, transform.DpiX, transform.DpiY, transform.MaxWidth - left - rightGap, transform.VwGraphics, transform.RendererFactory)); int i = 0; if (m_WrapRow) { var leftGap = GapLeading(transform); for (var box = FirstBox; box != null; box = box.Next) { box = ChildSetup(box, transform, columnWidths[i], leftGap, top); if (leftGap + Math.Min(box.Width, columnWidths[i]) >= maxWidth && prevBox != null) { top = maxHeight + GapTop(transform); leftGap = GapLeading(transform); box = ChildSetup(box, transform, columnWidths[i], leftGap, top); maxHeight += box.Height; } else { box = ChildSetup(box, transform, columnWidths[i], leftGap, top); maxHeight = Math.Max(maxHeight, box.Height + box.Top - GapTop(transform)); } leftGap += box.Width; left = Math.Max(left, leftGap); prevBox = box; i++; } } else { for (var box = FirstBox; box != null; box = box.Next) { var childTransform = transform.WithMaxWidthOffsetBy(columnWidths[i], Left, Top); // todo: test i++; box.Layout(childTransform); box.Left = left; box.Top = top; left += box.Width; maxHeight = Math.Max(maxHeight, box.Height); } } Height = maxHeight + GapTop(transform) + GapBottom(transform); Width = left + rightGap; }
internal void UpdateSize(int mpWidth, int mpHeight) { if (MpWidth == mpWidth && mpHeight == MpHeight) return; MpWidth = mpWidth; MpHeight = mpHeight; using (var gh = Root.Site.DrawingInfo) { // Enhance JohnT: margins: need to adjust MaxWidth for margins and padding of containing boxes. LayoutInfo transform = new LayoutInfo(Container.ChildTransformFromRootTransform(gh.Transform), Root.LastLayoutInfo.MaxWidth, gh.VwGraphics, Root.LastLayoutInfo.RendererFactory); RelayoutWithParents(gh); } }
public override void Layout(LayoutInfo transform) { var left = GapLeading(transform); var top = GapTop(transform); var rightGap = GapTrailing(transform); var maxWidth = transform.MaxWidth; Box prevBox = null; var childTransform = transform.WithMaxWidthOffsetBy(maxWidth - left - rightGap, Left, Top); for (var box = FirstBox; box != null; box = box.Next) { box.Layout(childTransform); box.Left = left; top = AdjustTopForMargins(top, transform, prevBox, box); box.Top = top; top += box.Height; prevBox = box; } Height = top + GapBottom(transform); Width = maxWidth; }
/// <summary> /// Determine the height and width of the box for the circumstances indicated by the transform. /// Also positions (and lays out) any contained boxes. /// </summary> public abstract void Layout(LayoutInfo transform);
public override void Layout(LayoutInfo transform) { Height = transform.MpToPixelsY(MpHeight) + SurroundHeight(transform); Width = transform.MpToPixelsX(MpWidth) + SurroundWidth(transform); }
/// <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; }
public override void Layout(LayoutInfo transform) { LastLayoutInfo = transform; int oldHeight = Height; base.Layout(transform); if (Height != oldHeight) RaiseSizeChanged(); }
/// <summary> /// This variant of RelayoutParents is used if the caller also needs the lcb, and therefore must /// be the thing to create it. /// </summary> internal void RelayoutParents(IGraphicsHolder gh, LayoutCallbacks lcb) { var root = Root; LayoutInfo transform = new LayoutInfo(gh.Transform, root.LastLayoutInfo.MaxWidth, gh.VwGraphics, Root.LastLayoutInfo.RendererFactory); var fixupMap = new Dictionary<Box, Rectangle>(); foreach (var gb in AllContainers) fixupMap[gb] = gb.InvalidateRect; root.Relayout(transform, fixupMap, lcb); }
public int[] ColumnWidths(int ncols, LayoutInfo layoutInfo) { return m_ColumnWidths; }
public override void Layout(LayoutInfo transform) { throw new NotImplementedException("We should never try to lay one of these out"); }
/// <summary> /// Return the estimated height of the indicated item. Index is relative to m_items, not necessarily /// to the total collection of items in the list managed by the hookup. /// Enhance JohnT: eventually we will allow the client to supply a non-trivial height estimator. /// </summary> int EstimatedItemHeight(LayoutInfo transform, int index) { return(transform.MpToPixelsY(DefaultItemHeight * 1000)); }
/// <summary> /// This routine and its overrides are used to make sure that the part of the root box between /// yTop and yBottom (measured from the top of the root) can be painted successfully. To facilitate /// the process of replacing a lazy box with real boxes, it is passed the previous box in its /// container (or null if there is none) and returns the next box which the container should /// check is prepared to paint. By default this is simply the next box, but if a lazy box expands /// (part of) itself, it should answer the replacement box. /// Most box classes are always prepared to paint and just answer Next. Lazy boxes convert all or part of /// themselves to real boxes if they intersect the specified vertical range (relative to the root as /// a whole). Boxes (currently DivBoxes only) which might contain lazy boxes pass the message on /// to their children, making the appropriate adjustment to the layout transform, just as when /// asking their children to Layout. /// </summary> internal virtual Box PrepareToPaint(LayoutInfo transform, Box prevBox, int yTop, int yBottom) { return Next; }
/// <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; }
internal void RelayoutWithParents(IGraphicsHolder gh, bool skipInvalidate) { var root = Root; LayoutInfo transform = new LayoutInfo(gh.Transform, root.LastLayoutInfo.MaxWidth, gh.VwGraphics, Root.LastLayoutInfo.RendererFactory); using (var lcb = skipInvalidate ? new NoInvalidateLayoutCallbacks(root): new LayoutCallbacks(root)) root.Relayout(transform, PrepareFixupMap(), lcb); }
/// <summary> /// This routine and its overrides are used to make sure that the part of the root box between /// yTop and yBottom (measured from the top of the root) can be painted successfully. To facilitate /// the process of replacing a lazy box with real boxes, it is passed the previous box in its /// container (or null if there is none) and returns the next box which the container should /// check is prepared to paint. By default this is simply the next box, but if a lazy box expands /// (part of) itself, it should answer the replacement box. /// Most box classes are always prepared to paint and do nothing. Lazy boxes convert all or part of /// themselves to real boxes if they intersect the specified vertical range (relative to the root as /// a whole). Boxes (currently DivBoxes only) which might contain lazy boxes pass the message on /// to their children, making the appropriate adjustment to the layout transform, just as when /// asking their children to Layout. /// </summary> internal override Box PrepareToPaint(LayoutInfo transform, Box myPrevBox, int dysTop, int dysBottom) { var childMaxWidth = transform.MaxWidth - GapLeading(transform) - GapTrailing(transform); var childTransform = transform.WithMaxWidthOffsetBy(childMaxWidth, Left, Top); Box prevBox = null; for (Box box = FirstBox; box != null; ) { if (transform.YOffset + box.Top > dysBottom) { // nothing further down in this can be visible. Possibly we should return null, // since presumably nothing further down in the container is visible, either. // However, it feels more robust to answer in the usual way and let the container decide // for itself. return Next; } if (transform.YOffset + box.Bottom < dysTop) box = box.Next; // this box is not visible, but a later one might be else box = box.PrepareToPaint(childTransform, prevBox, dysTop, dysBottom); } return Next; }
/// <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> /// 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); }