This class handles some callbacks which a view may need to make during layout and similar operations, including expanding a lazy box. It stands as a proxy for the root site, and forwards all the messages to it when disposed. During its lifetime, the root box cannot be painted; an attempt to do so will add to the rectangles to be invalidated when the LCB is disposed.
Inheritance: IDisposable
Beispiel #1
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 #2
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 #3
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 #4
0
 /// <summary>
 /// This routine typically fires off the whole relayout process. It builds the fixmap and calls
 /// Relayout. It also invalidates the new area of this, if it has moved.
 /// Todo: write a test for this moving if that ever becomes possible, and implement.
 /// </summary>
 internal void RelayoutParents(IGraphicsHolder gh)
 {
     using (var lcb = new LayoutCallbacks(Root))
         RelayoutParents(gh, lcb);
 }
Beispiel #5
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 #6
0
		internal void ResumePaint()
		{
			m_layoutCallbacks = null;
		}
Beispiel #7
0
		internal void SuspendPaint(LayoutCallbacks layoutCallbacks)
		{
			m_layoutCallbacks = layoutCallbacks;
		}
Beispiel #8
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 #9
0
		/// <summary>
		/// This routine typically fires off the whole relayout process. It builds the fixmap and calls
		/// Relayout. It also invalidates the new area of this, if it has moved.
		/// Todo: write a test for this moving if that ever becomes possible, and implement.
		/// </summary>
		internal void RelayoutParents(IGraphicsHolder gh)
		{
			using (var lcb = new LayoutCallbacks(Root))
				RelayoutParents(gh, lcb);
		}
Beispiel #10
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 #11
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 #12
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 #13
0
 internal void ResumePaint()
 {
     m_layoutCallbacks = null;
 }
Beispiel #14
0
 internal void SuspendPaint(LayoutCallbacks layoutCallbacks)
 {
     m_layoutCallbacks = layoutCallbacks;
 }