Class that gathers all the info needed by a box Layout operation that typically needs to be passed down (sometimes with minor alterations) to child boxes.
Inheritance: LayoutTransform
Beispiel #1
0
		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;
		}
Beispiel #3
0
		// 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;
		}
Beispiel #4
0
		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);
		}
Beispiel #5
0
		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;
		}
Beispiel #6
0
		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);
			}
		}
Beispiel #7
0
		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;
		}
Beispiel #8
0
		/// <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);
Beispiel #9
0
		public override void Layout(LayoutInfo transform)
		{
			Height = transform.MpToPixelsY(MpHeight) + SurroundHeight(transform);
			Width = transform.MpToPixelsX(MpWidth) + SurroundWidth(transform);
		}
Beispiel #10
0
		/// <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;
		}
Beispiel #11
0
		public override void Layout(LayoutInfo transform)
		{
			LastLayoutInfo = transform;
			int oldHeight = Height;
			base.Layout(transform);
			if (Height != oldHeight)
				RaiseSizeChanged();
		}
Beispiel #12
0
		/// <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);
		}
Beispiel #13
0
		public int[] ColumnWidths(int ncols, LayoutInfo layoutInfo)
		{
			return m_ColumnWidths;
		}
Beispiel #14
0
 public override void Layout(LayoutInfo transform)
 {
     throw new NotImplementedException("We should never try to lay one of these out");
 }
Beispiel #15
0
 /// <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));
 }
Beispiel #16
0
		/// <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;
		}
Beispiel #17
0
		/// <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;
		}
Beispiel #18
0
		public override void Layout(LayoutInfo transform)
		{
			throw new NotImplementedException("We should never try to lay one of these out");
		}
Beispiel #19
0
		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);
		}
Beispiel #20
0
		/// <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;
		}
Beispiel #21
0
		/// <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;
		}
Beispiel #22
0
        /// <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);
        }