예제 #1
0
		public void Relayout()
		{
			var styles = new AssembledStyles();
			var box = new MockBox(styles);
			var fixMap = new Dictionary<Box, Rectangle>();
			var layoutInfo = ParaBuilderTests.MakeLayoutInfo(100, m_gm.VwGraphics);
			var site = new MockSite();
			var root = new RootBox(styles);
			root.Site = site;

			using (var lcb = new LayoutCallbacks(root))
			{
				Assert.IsFalse(box.Relayout(layoutInfo, fixMap, lcb),
					"Relayout of box never laid out should return false (can't have old loc)");
			}
			Assert.AreEqual(layoutInfo, box.LastLayoutTransform, "Relayout of box never laid out should call Layout() with same transform");
			Assert.AreEqual(0, site.RectsInvalidated.Count, "Relayout of box never laid out should not invalidate anything");

			box.LastLayoutTransform = null;
			using (var lcb = new LayoutCallbacks(root))
				Assert.IsFalse(box.Relayout(layoutInfo, fixMap, lcb), "Relayout of box not in map should return false");
			Assert.IsNull(box.LastLayoutTransform, "Relayout of box not in map should not call Layout()");
			Assert.AreEqual(0, site.RectsInvalidated.Count, "Relayout of box not in map should not invalidate anything");

			fixMap[box] = new Rectangle(2,3,4,7);
			using (var lcb = new LayoutCallbacks(root))
				Assert.IsTrue(box.Relayout(layoutInfo, fixMap, lcb), "Relayout of box in map should return true");
			Assert.AreEqual(layoutInfo, box.LastLayoutTransform, "Relayout of box in map should call Layout() with same transform");
			Assert.AreEqual(1, site.RectsInvalidatedInRoot.Count, "Relayout of box in map should invalidate rect from map");
			Assert.AreEqual(new Rectangle(2, 3, 4, 7), site.RectsInvalidatedInRoot[0], "Relayout of box in map should invalidate proper rect");
		}
예제 #2
0
        public void Relayout()
        {
            var styles     = new AssembledStyles();
            var box        = new MockBox(styles);
            var fixMap     = new Dictionary <Box, Rectangle>();
            var layoutInfo = ParaBuilderTests.MakeLayoutInfo(100, m_gm.VwGraphics);
            var site       = new MockSite();
            var root       = new RootBox(styles);

            root.Site = site;

            using (var lcb = new LayoutCallbacks(root))
            {
                Assert.IsFalse(box.Relayout(layoutInfo, fixMap, lcb),
                               "Relayout of box never laid out should return false (can't have old loc)");
            }
            Assert.AreEqual(layoutInfo, box.LastLayoutTransform, "Relayout of box never laid out should call Layout() with same transform");
            Assert.AreEqual(0, site.RectsInvalidated.Count, "Relayout of box never laid out should not invalidate anything");

            box.LastLayoutTransform = null;
            using (var lcb = new LayoutCallbacks(root))
                Assert.IsFalse(box.Relayout(layoutInfo, fixMap, lcb), "Relayout of box not in map should return false");
            Assert.IsNull(box.LastLayoutTransform, "Relayout of box not in map should not call Layout()");
            Assert.AreEqual(0, site.RectsInvalidated.Count, "Relayout of box not in map should not invalidate anything");

            fixMap[box] = new Rectangle(2, 3, 4, 7);
            using (var lcb = new LayoutCallbacks(root))
                Assert.IsTrue(box.Relayout(layoutInfo, fixMap, lcb), "Relayout of box in map should return true");
            Assert.AreEqual(layoutInfo, box.LastLayoutTransform, "Relayout of box in map should call Layout() with same transform");
            Assert.AreEqual(1, site.RectsInvalidatedInRoot.Count, "Relayout of box in map should invalidate rect from map");
            Assert.AreEqual(new Rectangle(2, 3, 4, 7), site.RectsInvalidatedInRoot[0], "Relayout of box in map should invalidate proper rect");
        }
예제 #3
0
		public void LazyBoxExpanded()
		{
			var root = new RootBox(new AssembledStyles());
			root.RaiseLazyExpanded(new RootBox.LazyExpandedEventArgs()); // make sure harmless with no subscribers
			root.LazyExpanded += root_LazyExpanded;
			using (var lc = new LayoutCallbacks(root))
			{
				lc.RaiseLazyExpanded(10, 17, 5);
				lc.RaiseLazyExpanded(5, 6, -2);
				Assert.That(m_expandArgs, Is.Empty);
			}
			VerifyExpandArgs(0, 10, 17, 5);
			VerifyExpandArgs(1, 5, 6, -2);
		}
예제 #4
0
		public void InvalidateInRoot()
		{
			var site = new MockSite();
			var root = new RootBox(new AssembledStyles());
			root.Site = site;
			using (var lc = new LayoutCallbacks(root))
			{
				lc.InvalidateInRoot(new Rectangle(10, 13, 17, 19));
				lc.InvalidateInRoot(new Rectangle(9, 8, 5, 4));
				Assert.That(site.RectsInvalidatedInRoot, Is.Empty);
			}
			Assert.That(site.RectsInvalidatedInRoot, Has.Member(new Rectangle(10, 13, 17, 19)));
			Assert.That(site.RectsInvalidatedInRoot, Has.Member(new Rectangle(9, 8, 5, 4)));
		}
예제 #5
0
        public void LazyBoxExpanded()
        {
            var root = new RootBox(new AssembledStyles());

            root.RaiseLazyExpanded(new RootBox.LazyExpandedEventArgs());             // make sure harmless with no subscribers
            root.LazyExpanded += root_LazyExpanded;
            using (var lc = new LayoutCallbacks(root))
            {
                lc.RaiseLazyExpanded(10, 17, 5);
                lc.RaiseLazyExpanded(5, 6, -2);
                Assert.That(m_expandArgs, Is.Empty);
            }
            VerifyExpandArgs(0, 10, 17, 5);
            VerifyExpandArgs(1, 5, 6, -2);
        }
예제 #6
0
        public void InvalidateInRoot()
        {
            var site = new MockSite();
            var root = new RootBox(new AssembledStyles());

            root.Site = site;
            using (var lc = new LayoutCallbacks(root))
            {
                lc.InvalidateInRoot(new Rectangle(10, 13, 17, 19));
                lc.InvalidateInRoot(new Rectangle(9, 8, 5, 4));
                Assert.That(site.RectsInvalidatedInRoot, Is.Empty);
            }
            Assert.That(site.RectsInvalidatedInRoot, Has.Member(new Rectangle(10, 13, 17, 19)));
            Assert.That(site.RectsInvalidatedInRoot, Has.Member(new Rectangle(9, 8, 5, 4)));
        }
예제 #7
0
        /// <summary>
        /// Redo the layout of the paragraph given the Source changes indicated in the given details.
        /// </summary>
        /// <param name="details"></param>
        private void Relayout(SourceChangeDetails details)
        {
            // We would prefer to keep both sources intact, but we can't just do Source = details.NewSource,
            // because there is currently no way to change the Source of existing segments we may want to reuse.
            Source.Copyfrom(details.NewSource);
            var oldHeight = Height;
            var oldWidth  = Width;

            using (var gh = Root.Site.DrawingInfo)
            {
                // Enhance JohnT: margins: need to adjust MaxWidth for margins and padding of containing boxes.
                var info = new LayoutInfo(ChildTransformFromRootTransform(gh.Transform), Root.LastLayoutInfo.MaxWidth,
                                          gh.VwGraphics, Root.LastLayoutInfo.RendererFactory);
                var builder = new ParaBuilder(this, info);
                using (var lcb = new LayoutCallbacks(Root))
                {
                    builder.Relayout(details, lcb);
                    if (Height != oldHeight || Width != oldWidth)
                    {
                        RelayoutParents(gh);
                    }
                }
            }
        }
예제 #8
0
		public void DrawingBordersandBackground()
		{
			var root = new RootBoxFdo(new AssembledStyles());
			SetupFakeRootSite(root);
			var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55);
			root.RendererFactory = layoutInfo.RendererFactory;
			var mock1 = new MockData1() {SimpleThree = "This is the first paragraph."};
			// The length of the second paragraph is found by experiment to be enough so that
			// despite its lacking borders it also breaks into 2 lines in the second step.
			var mock2 = new MockData1() {SimpleThree = "Here is another paragraph. It needs to be a bit longer."};
			root.Builder.Show(
				Paragraph.Containing(Display.Of(() => mock1.SimpleThree)).BackColor(Color.Red)
					.Margins(1.Points(), 2.Points(), 3.Points(), 4.Points())
					.Borders(5.Points(), 6.Points(), 7.Points(), 8.Points(), Color.Blue)
					.Pads(9.Points(), 10.Points(), 11.Points(), 12.Points()),
				Paragraph.Containing(Display.Of(() => mock2.SimpleThree)).BackColor(Color.Yellow)
					.Margins(1.Points(), 2.Points(), 3.Points(), 4.Points()));
			root.Layout(layoutInfo);
			// We want to keep track of the sequence of paint operations in all three segments.
			var drawActions = new List<object>();
			var vg = new MockGraphics();
			vg.DrawActions = drawActions;
			var para1 = (ParaBox)root.FirstBox;
			var stringbox1 = (StringBox)para1.FirstBox;
			var seg1 = (FakeSegment)stringbox1.Segment;
			seg1.DrawActions = drawActions;
			var para2 = (ParaBox)para1.Next;
			var stringbox2 = (StringBox)para2.FirstBox;
			var seg2 = (FakeSegment)stringbox2.Segment;
			seg2.DrawActions = drawActions;

			var site = (MockSite)root.Site;
			root.Paint(vg, site.m_transform);
			var paintTrans = site.m_transform;
			int position = 0;

			int red = (int)ColorUtil.ConvertColorToBGR(Color.Red);
			int margLeft = layoutInfo.MpToPixelsX(1000);
			Assert.That(margLeft, Is.EqualTo(1));
			int bordLeft = layoutInfo.MpToPixelsX(5000);
			Assert.That(bordLeft, Is.EqualTo(7));
			int xOffset = 2 - 100; // how far it is pushed over by the offsets of the layoutInfo
			int margTop = layoutInfo.MpToPixelsY(2000);
			Assert.That(margTop, Is.EqualTo(3));
			int bordTop = layoutInfo.MpToPixelsY(6000);
			Assert.That(bordTop, Is.EqualTo(8));
			int yOffset = 2 - 200; // how far it is pushed down by the offsets of the layoutInfo
			int padLeft = layoutInfo.MpToPixelsX(9000);
			Assert.That(padLeft, Is.EqualTo(12));
			int padRight = layoutInfo.MpToPixelsX(11000);
			Assert.That(padRight, Is.EqualTo(15));
			int padTop = layoutInfo.MpToPixelsY(10000);
			Assert.That(padTop, Is.EqualTo(13));
			int padBottom = layoutInfo.MpToPixelsY(12000);
			Assert.That(padBottom, Is.EqualTo(16));
			// First it should draw a background rectangle for the first paragraph.
			// It is indented by the left margin and the left border, and down by the top margin and border.
			// The other side is determined by the size of the embedded box and the two pads.
			VerifyRect(drawActions, ref position, margLeft + bordLeft + xOffset, margTop + bordTop + yOffset,
				margLeft + bordLeft + xOffset + stringbox1.Width + padLeft + padRight,
				margTop + bordTop + yOffset + stringbox1.Height + padTop + padBottom,
				red);
			int bordBottom = layoutInfo.MpToPixelsY(8000);
			Assert.That(bordBottom, Is.EqualTo(11));
			int blue = (int)ColorUtil.ConvertColorToBGR(Color.Blue);
			// It's arbitrary what order we draw the borders, and I wish the test didn't specify it,
			// but in fact the current implementation draws the left border first.
			VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset,
				margLeft + bordLeft + xOffset,
				margTop + bordTop + yOffset + padTop + stringbox1.Height + padBottom + bordBottom,
				blue);
			int bordRight = layoutInfo.MpToPixelsX(7000);
			Assert.That(bordRight, Is.EqualTo(9));
			// Then the top border
			VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset,
				margLeft + bordLeft + xOffset + padLeft + stringbox1.Width + padRight + bordRight,
				margTop + bordTop + yOffset,
				blue);
			// Then the right border
			VerifyRect(drawActions, ref position,
				margLeft + bordLeft + xOffset + padLeft + stringbox1.Width + padRight,
				margTop + yOffset,
				margLeft + bordLeft + xOffset + padLeft + stringbox1.Width + padRight + bordRight,
				margTop + bordTop + yOffset + padTop + stringbox1.Height + padBottom + bordBottom,
				blue);
			// Then the bottom border
			VerifyRect(drawActions, ref position,
				margLeft + xOffset,
				margTop + bordTop + yOffset + padTop + stringbox1.Height + padBottom,
				margLeft + bordLeft + xOffset + padLeft + stringbox1.Width + padRight + bordRight,
				margTop + bordTop + yOffset + padTop + stringbox1.Height + padBottom + bordBottom,
				blue);
			// Figure an adjusted y offset for the second paragraph. Everything is down by the height
			// of the first paragraph, except that the top and bottom margins overlap by the
			// height of the smaller.
			int yOffset2 = yOffset + para1.Height - margTop;
			int yellow = (int)ColorUtil.ConvertColorToBGR(Color.Yellow);
			// Next a background block for the second paragraph.
			// (Background color should be reset for the embedded string boxes, so they should not draw their own
			// background.)
			VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset2,
				margLeft + xOffset + stringbox2.Width,
				margTop + yOffset2 + stringbox2.Height,
				yellow);
			// Verify the position where the text is drawn
			VerifyDraw(drawActions, ref position, seg1, margLeft + bordLeft + padLeft + 2, margTop + bordTop + padTop + 2);
			VerifyDraw(drawActions, ref position, seg2, margLeft + 2, para1.Height + 2); //margTop cancels out
			// And that should be all!
			Assert.That(position, Is.EqualTo(drawActions.Count));

			// Verify that multi-lines in a paragraph are appropriately laid out with margin etc.
			int maxWidth = para1.Width - FakeRenderEngine.SimulatedWidth("paragraph");
			// This maxWidth should force each paragraph to make two segments.
			layoutInfo = HookupTests.MakeLayoutInfo(maxWidth, m_gm.VwGraphics, 55);
			root.Layout(layoutInfo);
			drawActions.Clear();
			position = 0;
			var stringbox1a = (StringBox)para1.FirstBox;
			var seg1a = (FakeSegment)stringbox1a.Segment;
			seg1a.DrawActions = drawActions;
			var stringbox1b = (StringBox)stringbox1a.Next;
			var seg1b = (FakeSegment)stringbox1b.Segment;
			seg1b.DrawActions = drawActions;
			var stringbox2a = (StringBox)para2.FirstBox;
			var seg2a = (FakeSegment)stringbox2a.Segment;
			seg2a.DrawActions = drawActions;
			var stringbox2b = (StringBox)stringbox2a.Next;
			var seg2b = (FakeSegment)stringbox2b.Segment;
			seg2b.DrawActions = drawActions;

			root.Paint(vg, site.m_transform);
			int margRight = layoutInfo.MpToPixelsX(3000);
			Assert.That(margRight, Is.EqualTo(4));
			// First it should draw a background rectangle for the first paragraph.
			// It is indented by the left margin and the left border, and down by the top margin and border.
			// The other side is determined by maxWidth minus the right margin and border.
			int contentHeight1 = stringbox1a.Height + stringbox1b.Height;
			VerifyRect(drawActions, ref position, margLeft + bordLeft + xOffset, margTop + bordTop + yOffset,
				maxWidth - margRight - bordRight + xOffset,
				margTop + bordTop + yOffset + contentHeight1 + padTop + padBottom,
				red);
			// It's arbitrary what order we draw the borders, and I wish the test didn't specify it,
			// but in fact the current implementation draws the left border first.
			VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset,
				margLeft + bordLeft + xOffset,
				margTop + bordTop + yOffset + padTop + contentHeight1 + padBottom + bordBottom,
				blue);
			// Then the top border
			VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset,
				maxWidth - margRight + xOffset,
				margTop + bordTop + yOffset,
				blue);
			// Then the right border
			VerifyRect(drawActions, ref position,
				maxWidth - margRight - bordRight + xOffset,
				margTop + yOffset,
				maxWidth - margRight + xOffset,
				margTop + bordTop + yOffset + padTop + contentHeight1 + padBottom + bordBottom,
				blue);
			// Then the bottom border
			VerifyRect(drawActions, ref position,
				margLeft + xOffset,
				margTop + bordTop + yOffset + padTop + contentHeight1 + padBottom,
				maxWidth - margRight + xOffset,
				margTop + bordTop + yOffset + padTop + contentHeight1 + padBottom + bordBottom,
				blue);
			// Figure an adjusted y offset for the second paragraph. Everything is down by the height
			// of the first paragraph, except that the top and bottom margins overlap by the
			// height of the smaller.
			yOffset2 = yOffset + para1.Height - margTop;
			// Next a background block for the second paragraph.
			// (Background color should be reset for the embedded string boxes, so they should not draw their own
			// background.)
			VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset2,
				maxWidth - margRight + xOffset,
				margTop + yOffset2 + stringbox2a.Height + stringbox2b.Height,
				yellow);
			// Verify the position where the text is drawn
			VerifyDraw(drawActions, ref position, seg1a, margLeft + bordLeft + padLeft + 2, margTop + bordTop + padTop + 2);
			VerifyDraw(drawActions, ref position, seg1b, margLeft + bordLeft + padLeft + 2,
				margTop + bordTop + padTop + 2 + stringbox1a.Height);
			VerifyDraw(drawActions, ref position, seg2a, margLeft + 2, para1.Height + 2); //margTop cancels out
			VerifyDraw(drawActions, ref position, seg2b, margLeft + 2, para1.Height + 2 + stringbox2a.Height); //margTop cancels out
			// And that should be all!
			Assert.That(position, Is.EqualTo(drawActions.Count));

			// A quick check that Relayout puts things in the same places.
			drawActions.Clear();
			position = 0;
			var fixupMap = new Dictionary<Box, Rectangle>();
			fixupMap[para1] = new Rectangle(0, 0, 10, 10);
			var oldstring1aLeft = stringbox1a.Left;
			var oldstring1bTop = stringbox1b.Top;
			using (var lcb = new LayoutCallbacks(root))
				root.Relayout(layoutInfo, fixupMap, lcb);
			Assert.That(drawActions.Count, Is.EqualTo(0));
			Assert.That(para1.FirstBox.Left, Is.EqualTo(oldstring1aLeft));
			Assert.That(para1.FirstBox.Next.Top, Is.EqualTo(oldstring1bTop));
		}
예제 #9
0
		public void PileAndBlock()
		{
			var root = new RootBoxFdo(new AssembledStyles());
			SetupFakeRootSite(root);
			var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55);
			root.RendererFactory = layoutInfo.RendererFactory;
			var mock1 = new MockData1() { SimpleThree = "This is the first paragraph." };
			// The length of the second paragraph is found by experiment to be enough so that
			// despite its lacking borders it also breaks into 2 lines in the second step.
			var mock2 = new MockData1() { SimpleThree = "Here is another paragraph. It needs to be a bit longer." };
			root.Builder.Show(
				Div.Containing(
					Display.Block(Color.Red, 25000, 18000).BackColor(Color.Purple)
						.Margins(3000, 3000, 3000, 3000)
						.Border(5000, Color.Blue)
						.Pads(4000, 4000, 4000, 4000),
					Display.Block(Color.Green, 25000, 18000)
					).BackColor(Color.Pink) // these apply to div.
						.Margins(1000, 1000, 1000, 1000)
						.Border(2000, Color.Gold)
						.Pads(6000, 6000, 6000, 6000));
			root.Layout(layoutInfo);
			// We want to keep track of the sequence of paint operations in all three segments.
			var drawActions = new List<object>();
			var vg = new MockGraphics();
			vg.DrawActions = drawActions;

			var site = (MockSite)root.Site;
			root.Paint(vg, site.m_transform);
			var paintTrans = site.m_transform;
			int position = 0;
			int xOffset = 2 - 100; // how far it is pushed over by the offsets of the layoutInfo
			int yOffset = 2 - 200; // how far it is pushed down by the offsets of the layoutInfo

			int red = (int)ColorUtil.ConvertColorToBGR(Color.Red);
			int pink = (int)ColorUtil.ConvertColorToBGR(Color.Pink);
			int purple = (int)ColorUtil.ConvertColorToBGR(Color.Purple);
			int blue = (int)ColorUtil.ConvertColorToBGR(Color.Blue);
			int green = (int)ColorUtil.ConvertColorToBGR(Color.Green);
			int gold = (int)ColorUtil.ConvertColorToBGR(Color.Gold);
			// Technically we could do different conversions in the two directions, but for this test both dpi are the same.
			int margPile = layoutInfo.MpToPixelsX(1000);
			int bordPile = layoutInfo.MpToPixelsX(2000);
			int padPile = layoutInfo.MpToPixelsX(6000);
			int blockWidth = layoutInfo.MpToPixelsX(25000);
			int blockHeight = layoutInfo.MpToPixelsX(18000);
			int margBlock = layoutInfo.MpToPixelsX(3000);
			int bordBlock = layoutInfo.MpToPixelsX(5000);
			int padBlock = layoutInfo.MpToPixelsX(4000);

			// First a background rectangle for the whole pile.
			var leftPilePad = margPile + bordPile + xOffset;
			var topPilePad = margPile + bordPile + yOffset;
			var rightPilePad = margPile + bordPile + 2 * padPile + blockWidth + 2 * margBlock + 2 * bordBlock + 2 * padBlock + xOffset;
			var bottomPilePad = margPile + bordPile + 2 * padPile + 2 * blockHeight + 2 * margBlock + 2 * bordBlock + 2 * padBlock + yOffset;
			VerifyRect(drawActions, ref position, leftPilePad, topPilePad, rightPilePad, bottomPilePad, pink);
			// Left border, whole pile
			VerifyRect(drawActions, ref position, leftPilePad - bordPile, topPilePad - bordPile,
				leftPilePad, bottomPilePad + bordPile, gold);
			// top border, whole pile
			VerifyRect(drawActions, ref position, leftPilePad - bordPile, topPilePad - bordPile,
				rightPilePad + bordPile, topPilePad, gold);
			// right border, whole pile
			VerifyRect(drawActions, ref position, rightPilePad, topPilePad - bordPile,
				rightPilePad + bordPile, bottomPilePad + bordPile, gold);
			// bottom border, whole pile
			VerifyRect(drawActions, ref position, leftPilePad - bordPile, bottomPilePad,
				rightPilePad + bordPile, bottomPilePad + bordPile, gold);

			// background and border for first block.
			var leftBlockPad = margPile + bordPile + padPile + margBlock + bordBlock + xOffset;
			var topBlockPad = margPile + bordPile + padPile + margBlock + bordBlock + yOffset;
			var rightBlockPad = margPile + bordPile + padPile + margBlock + bordBlock + 2 * padBlock + blockWidth + xOffset;
			var bottomBlockPad = margPile + bordPile + padPile + margBlock + bordBlock + 2 * padBlock + blockHeight + yOffset;
			VerifyRect(drawActions, ref position, leftBlockPad, topBlockPad, rightBlockPad, bottomBlockPad, purple);
			// Left border, whole pile
			VerifyRect(drawActions, ref position, leftBlockPad - bordBlock, topBlockPad - bordBlock,
				leftBlockPad, bottomBlockPad + bordBlock, blue);
			// top border, whole pile
			VerifyRect(drawActions, ref position, leftBlockPad - bordBlock, topBlockPad - bordBlock,
				rightBlockPad + bordBlock, topBlockPad, blue);
			// right border, whole pile
			VerifyRect(drawActions, ref position, rightBlockPad, topBlockPad - bordBlock,
				rightBlockPad + bordBlock, bottomBlockPad + bordBlock, blue);
			// bottom border, whole pile
			VerifyRect(drawActions, ref position, leftBlockPad - bordBlock, bottomBlockPad,
				rightBlockPad + bordBlock, bottomBlockPad + bordBlock, blue);
			// The first block itself.
			VerifyRect(drawActions, ref position, leftBlockPad + padBlock, topBlockPad + padBlock,
				leftBlockPad + padBlock + blockWidth, topBlockPad + padBlock + blockHeight, red);
			// The second block itself.
			var topBlock2 = bottomBlockPad + bordBlock + margBlock;
			VerifyRect(drawActions, ref position, leftPilePad + padPile, topBlock2,
				leftPilePad + padPile + blockWidth, topBlock2 + blockHeight, green);
			// And that should be all!
			Assert.That(position, Is.EqualTo(drawActions.Count));

			// A quick check that Relayout puts things in the same places.
			drawActions.Clear();
			var fixupMap = new Dictionary<Box, Rectangle>();
			var div1 = (DivBox) root.FirstBox;
			var block1 = div1.FirstBox;
			fixupMap[div1] = new Rectangle(0, 0, 10, 10);
			fixupMap[block1] = new Rectangle(0, 0, 10, 10);
			var oldblock1Left = block1.Left;
			var oldblock1bTop = block1.Top;
			using (var lcb = new LayoutCallbacks(root))
				root.Relayout(layoutInfo, fixupMap, lcb);
			Assert.That(drawActions.Count, Is.EqualTo(0));
			Assert.That(div1.FirstBox.Left, Is.EqualTo(oldblock1Left));
			Assert.That(div1.FirstBox.Top, Is.EqualTo(oldblock1bTop));
		}
예제 #10
0
		/// <summary>
		/// Redo layout. Should produce the same segments as FullLayout, but assume that segments for text up to
		/// details.StartChange may be reused (if not affected by changing line breaks), and segments after
		/// details.StartChange+details.DeleteCount may be re-used if a line break works out (and after adjusting
		/// their begin offset).
		/// </summary>
		/// <param name="details"></param>
		internal void Relayout(SourceChangeDetails details, LayoutCallbacks lcb)
		{
			m_reuseableLines = m_para.Lines;
			m_lines = new List<ParaLine>();
			m_renderRunIndex = 0;
			m_ichRendered = 0;
			IRenderRun last = m_renderRuns[m_renderRuns.Count - 1];
			m_ichLim = last.RenderStart + last.RenderLength;
			m_lastRenderRunIndex = m_renderRuns.Count;
			Rectangle invalidateRect = m_para.InvalidateRect;
			int delta = details.InsertCount - details.DeleteCount;
			int oldHeight = m_para.Height;
			int oldWidth = m_para.Width;
			// Make use of details.StartChange to reuse some lines at start.
			if (m_reuseableLines.Count > 0)
			{
				// As long as we have two complete lines before the change, we can certainly reuse the first of them.
				while (m_reuseableLines.Count > 2 && details.StartChange > m_reuseableLines[2].IchMin)
				{
					m_lines.Add(m_reuseableLines[0]);
					m_reuseableLines.RemoveAt(0);
				}
				// If we still have one complete line before the change, we can reuse it provided there is white
				// space after the end of the line and before the change.
				if (m_reuseableLines.Count > 1)
				{
					int startNextLine = m_reuseableLines[1].IchMin;
					if (details.StartChange > startNextLine)
					{
						bool fGotWhite = false;
						string line1Text = m_reuseableLines[1].CheckedText;
						int lim = details.StartChange - startNextLine;
						var cpe = LgIcuCharPropEngineClass.Create();
						for (int ich = 0; ich < lim; ich++)
						{
							// Enhance JohnT: possibly we need to consider surrogates here?
							// Worst case is we don't reuse a line we could have, since a surrogate won't
							// be considered white.
							if (cpe.get_IsSeparator(Convert.ToInt32(line1Text[ich])))
							{
								fGotWhite = true;
								break;
							}
						}
						if (fGotWhite)
						{
							m_lines.Add(m_reuseableLines[0]);
							m_reuseableLines.RemoveAt(0);
						}
					}
				}
				m_ichRendered = m_reuseableLines[0].IchMin;
				int topOfFirstDiscardedLine = m_reuseableLines[0].Top;
				// We don't need to invalidate the lines we're keeping.
				invalidateRect = new Rectangle(invalidateRect.Left, invalidateRect.Top + topOfFirstDiscardedLine,
											   invalidateRect.Width, invalidateRect.Height - topOfFirstDiscardedLine);
			}

			// Figure out which run we need to continue from, to correspond to the start of the first line
			// we need to rebuild.
			while (m_renderRunIndex < m_renderRuns.Count && m_renderRuns[m_renderRunIndex].RenderLim <= m_ichRendered)
				m_renderRunIndex++;

			while (!Finished)
			{
				// Todo: I think we need to adjust available width if this is the first line.
				BuildALine();
				// Drop any initial reusable lines we now determine to be unuseable after all.
				// If we've used characters beyond the start of this potentially reusable line, we can't reuse it.
				// Also, we don't reuse empty lines. Typically an empty line is left over from a previously empty
				// paragraph, and we no longer need the empty segment, even though it doesn't have any of the same
				// characters (since it has none) as the segment that has replaced it.
				while (m_reuseableLines.Count > 0 && (m_ichRendered > m_reuseableLines[0].IchMin + delta || m_reuseableLines[0].Length == 0))
				{
					m_reuseableLines.RemoveAt(0);
				}
				if (m_reuseableLines.Count > 0)
				{
					// See if we can resync.
					var nextLine = m_reuseableLines[0];
					if (m_ichRendered == nextLine.IchMin + delta)
					{
						// reuse it.
						int top = m_gapTop;
						if (m_lines.Count > 0)
						{
							ParaLine previous = m_lines.Last();
							previous.LastBox.Next = nextLine.FirstBox;
							top = TopOfNextLine(previous, nextLine.Ascent);
						}

						m_lines.AddRange(m_reuseableLines);
						if (top != nextLine.Top)
						{
							ParaLine previous = null;
							foreach (var line in m_reuseableLines)
							{
								if (previous != null) // first time top has already been computed
									top = TopOfNextLine(previous, line.Ascent);
								line.Top = top; // BEFORE ArrangeBoxes, since it gets copied to the individual boxes
								m_currentLine.ArrangeBoxes(m_para.Style.ParaAlignment, m_gapLeft, m_gapRight, 0, m_layoutInfo.MaxWidth, TopDepth);
								previous = line;
							}
						}
						else
						{
							// reusable lines have not moved, we don't need to invalidate them.
							invalidateRect.Height -= (m_reuseableLines.Last().Bottom - top);
						}
						for (Box box = nextLine.FirstBox; box != null; box = box.Next)
						{
							if (box is StringBox)
								(box as StringBox).IchMin += delta;
						}

						break;
					}
				}

			}
			SetParaInfo();
			// if the paragraph got larger, we need to invalidate the extra area.
			// (But, don't reduce it if it got smaller; we want to invalidate all the old stuff as well as all the new.)
			if (m_para.Height > oldHeight)
				invalidateRect.Height += m_para.Height - oldHeight;
			if (m_para.Width > oldWidth)
				invalidateRect.Width += m_para.Width - oldWidth;
			lcb.InvalidateInRoot(invalidateRect);
		}
예제 #11
0
		public void ParaSequenceTest()
		{
			var owner = new MockData1(55, 77);
			var styles = new AssembledStyles();
			var root = new RootBoxFdo(styles);
			var layoutInfo = MakeLayoutInfo(int.MaxValue/2, m_gm.VwGraphics, 55);
			SetupFakeRootSite(root);
			var engine = layoutInfo.RendererFactory.GetRenderer(55, m_gm.VwGraphics) as FakeRenderEngine;
			MockSite site = new MockSite();
			int topLazy = 0; // top of the part of the root box that is occupied by the lazy stuff, relative to the top of the root box itself.
			root.Site = site;
			PaintTransform ptrans = new PaintTransform(2, 4, 96, 96, 0, 10, 96, 96);
			site.m_transform = ptrans;
			site.m_vwGraphics = m_gm.VwGraphics;
			root.Builder.Show(LazyDisplay.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55)));
			var heightOfOneItem = layoutInfo.MpToPixelsY(LazyBox<MockData1>.DefaultItemHeight * 1000);
			// This does two things: it makes sure the boxes produced by expanding a lazy box item won't be the SAME size,
			// so we'll get nontrivial changes in root box size; and it makes sure we don't have to expand MORE items than
			// expected based on the height estimate, which could throw off our predictions of what gets expanded.
			// Todo: we need a test where we DO have to expand more items after the initial estimate.
			engine.SegmentHeight = heightOfOneItem + 2;
			root.Layout(layoutInfo);
			VerifyParagraphs(root, new string[0]);

			var child1 = new MockData1(55, 77);
			var child1String = "Hello world, this is a wide string";
			child1.SimpleThree = child1String;
			owner.InsertIntoObjSeq1(0, child1);
			Assert.That(root.FirstBox, Is.TypeOf(typeof(LazyBox<MockData1>)));
			var lazyBox = (LazyBox<MockData1>)root.FirstBox;
			Assert.That(lazyBox.Width, Is.EqualTo(int.MaxValue / 2)); // no margins, should equal avail width.
			Assert.That(lazyBox.Height, Is.EqualTo(heightOfOneItem));
			var lazyTop = lazyBox.Top;
			var lazyBottom = lazyBox.Bottom;
			var oldRootHeight = root.Height;
			int invalidateWidth = lazyBox.Width + 2 * RootBox.InvalidateMargin;
			var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin,
				invalidateWidth,
				lazyBox.Height + 2 * RootBox.InvalidateMargin);
			Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1));
			site.RectsInvalidatedInRoot.Clear();
			var lazyHookup = ((IHookup) lazyBox).ParentHookup as LazyHookup<MockData1>;
			Assert.That(lazyHookup, Is.Not.Null);
			Assert.That(lazyHookup.Children[0], Is.EqualTo(lazyBox));
			Assert.That(lazyHookup.Children, Has.Count.EqualTo(1));
			root.LazyExpanded += root_LazyExpanded;
			using (var lc = new LayoutCallbacks(root))
			{
				root.PrepareToPaint(layoutInfo, null, 0, 200);
			}
			VerifyParagraphs(root, new [] { child1String });
			Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
			Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
			VerifyExpandArgs(0, lazyTop + 2, lazyBottom + 2, root.Height - oldRootHeight);
			Assert.That(lazyHookup.Children, Has.Count.EqualTo(1));
			Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "the lazy box standing for item hookups should have been replaced");

			// Now replace that one object with a list of several. I want to be able to expand two at the start, one at the end,
			// and one in the middle, and leave two lazy boxes behind. Then expand the rest and make them go away. So I need six.
			var values = new MockData1[10];
			for (int i = 0; i < 10; i++)
			{
				values[i] = new MockData1(55, 77);
				values[i].SimpleThree = i.ToString();
			}
			var newValues = values.Take(6).ToArray();
			site.RectsInvalidatedInRoot.Clear();
			int phase2RootHeight = root.Height;
			int phase2RootWidth = root.Width;
			owner.ReplaceObjSeq1(newValues);
			var expectedInvalidate2 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin,
				phase2RootWidth + 2 * RootBox.InvalidateMargin,
				phase2RootHeight + 2 * RootBox.InvalidateMargin);
			Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2), "should invalidate the old replaced paragraph.");
			lazyBox = (LazyBox<MockData1>)root.FirstBox;
			Assert.That(lazyBox.Width, Is.EqualTo(int.MaxValue / 2)); // no margins, should equal avail width.
			Assert.That(root.LastBox, Is.EqualTo(lazyBox), "old paragraph should have been replaced");
			Assert.That(lazyHookup.Children[0], Is.EqualTo(lazyBox), "after second replace we just have the lazy box");
			Assert.That(lazyHookup.Children, Has.Count.EqualTo(1), "should not have anything but lazy box after second replace");
			Assert.That(lazyBox.Height, Is.EqualTo(6*heightOfOneItem));

			// Make it expand the first two items.
			site.RectsInvalidatedInRoot.Clear();
			m_expandArgs.Clear();
			oldRootHeight = root.Height;
			using (var lc = new LayoutCallbacks(root))
			{
				root.PrepareToPaint(layoutInfo, null, 0, heightOfOneItem * 2 - 2);
			}
			VerifyParagraphs(root, new[] { "0", "1", null }); // Should have two paras then lazy box
			Assert.That(newValues[0].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
			Assert.That(newValues[1].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
			Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
			Assert.That(root.Height, Is.Not.EqualTo(oldRootHeight));
			VerifyExpandArgs(0, 2, heightOfOneItem * 2 + 2, root.Height - oldRootHeight); // +2's from root layout offset
			Assert.That(lazyHookup.Children, Has.Count.EqualTo(3));
			Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the first expanded item should be inserted");
			Assert.That(lazyHookup.Children[1], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 2nd expanded item should be inserted");
			Assert.That(lazyHookup.Children[2], Is.TypeOf(typeof(LazyBox<MockData1>)), "the lazy box standing for item hookups should still be there");
			lazyBox = root.FirstBox.Next.Next as LazyBox<MockData1>;
			Assert.That(lazyBox, Is.Not.Null);
			Assert.That(lazyBox.Height, Is.EqualTo(heightOfOneItem * 4));

			int topOfLastItem = lazyBox.Bottom - heightOfOneItem + 2;
			// Make it expand the last item.
			site.RectsInvalidatedInRoot.Clear();
			m_expandArgs.Clear();
			oldRootHeight = root.Height;
			using (var lc = new LayoutCallbacks(root))
			{
				root.PrepareToPaint(layoutInfo, null, topOfLastItem + 2, topOfLastItem + 10);
			}
			VerifyParagraphs(root, new[] { "0", "1", null, "5" }); // Should have two paras then lazy box then last para
			Assert.That(newValues[5].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
			Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
			Assert.That(root.Height, Is.Not.EqualTo(oldRootHeight));
			VerifyExpandArgs(0, topOfLastItem, topOfLastItem + heightOfOneItem, root.Height - oldRootHeight);
			Assert.That(lazyHookup.Children, Has.Count.EqualTo(4));
			Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the first expanded item should be inserted");
			Assert.That(lazyHookup.Children[1], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 2nd expanded item should be inserted");
			Assert.That(lazyHookup.Children[2], Is.TypeOf(typeof(LazyBox<MockData1>)), "the lazy box standing for item hookups should still be there");
			Assert.That(lazyHookup.Children[3], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the last expanded item should be inserted");
			lazyBox = root.FirstBox.Next.Next as LazyBox<MockData1>;
			Assert.That(lazyBox, Is.Not.Null);
			Assert.That(lazyBox.Height, Is.EqualTo(heightOfOneItem * 3));

			// Expand middle item in lazy box, leaving two lazy boxes.
			int topOfMiddleItem = lazyBox.Top + heightOfOneItem + 2;
			site.RectsInvalidatedInRoot.Clear();
			m_expandArgs.Clear();
			oldRootHeight = root.Height;
			using (var lc = new LayoutCallbacks(root))
			{
				root.PrepareToPaint(layoutInfo, null, topOfMiddleItem + 2, topOfMiddleItem + 10);
			}
			VerifyParagraphs(root, new[] { "0", "1", null, "3", null, "5" }); // Should have two paras then lazy box then middle para then another lazy then last para
			Assert.That(newValues[3].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
			Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
			Assert.That(root.Height, Is.Not.EqualTo(oldRootHeight));
			VerifyExpandArgs(0, topOfMiddleItem, topOfMiddleItem + heightOfOneItem, root.Height - oldRootHeight);
			Assert.That(lazyHookup.Children, Has.Count.EqualTo(6));
			Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the first expanded item should be inserted");
			Assert.That(lazyHookup.Children[1], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 2nd expanded item should be inserted");
			Assert.That(lazyHookup.Children[2], Is.TypeOf(typeof(LazyBox<MockData1>)), "the lazy box standing for item hookups should still be there");
			Assert.That(lazyHookup.Children[3], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the last expanded item should be inserted");
			Assert.That(lazyHookup.Children[4], Is.TypeOf(typeof(LazyBox<MockData1>)), "the lazy box standing for item hookups should still be there");
			Assert.That(lazyHookup.Children[5], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the last expanded item should be inserted");
			lazyBox = root.FirstBox.Next.Next as LazyBox<MockData1>;
			Assert.That(lazyBox, Is.Not.Null);
			Assert.That(lazyBox.Height, Is.EqualTo(heightOfOneItem));
			var lazyBox2 = lazyBox.Next.Next as LazyBox<MockData1>;
			Assert.That(lazyBox2, Is.Not.Null);
			Assert.That(lazyBox2.Height, Is.EqualTo(heightOfOneItem));
			// Expand lazy box when it is between two other items. (Also verify expanding two lazy boxes in one PrepareToPaint.)
			int topOfFirstLazy = lazyBox.Top + 2;
			int topOfLastLazy = lazyBox2.Top + 2;
			site.RectsInvalidatedInRoot.Clear();
			m_expandArgs.Clear();
			oldRootHeight = root.Height;
			using (var lc = new LayoutCallbacks(root))
			{
				root.PrepareToPaint(layoutInfo, null, topOfFirstLazy + 2, topOfLastLazy + 2);
			}
			VerifyParagraphs(root, new[] { "0", "1", "2", "3", "4", "5" }); // Should have all the real paragraphs now.
			Assert.That(newValues[2].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
			Assert.That(newValues[4].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
			Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
			Assert.That(root.Height, Is.Not.EqualTo(oldRootHeight));
			var delta = engine.SegmentHeight - heightOfOneItem;
			VerifyExpandArgs(0, topOfFirstLazy, topOfFirstLazy + heightOfOneItem, delta);
			VerifyExpandArgs(1, topOfLastLazy + delta, topOfLastLazy + delta + heightOfOneItem, delta);
			Assert.That(lazyHookup.Children, Has.Count.EqualTo(6));
			Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the first expanded item should be inserted");
			Assert.That(lazyHookup.Children[1], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 2nd expanded item should be inserted");
			Assert.That(lazyHookup.Children[2], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 3rd expanded item should be inserted");
			Assert.That(lazyHookup.Children[3], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 4th expanded item should be inserted");
			Assert.That(lazyHookup.Children[4], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 5th expanded item should be inserted");
			Assert.That(lazyHookup.Children[5], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the last expanded item should be inserted");

			// Now try removing the first two items.
			site.RectsInvalidatedInRoot.Clear();
			int heightOfFirst2Paras = root.FirstBox.Next.Bottom - root.FirstBox.Top;
			int phase3RootWidth = root.Width;
			int phase3RootHeight = root.Height;
			var phase3Values = newValues.Skip(2).ToArray();
			owner.ReplaceObjSeq1(phase3Values);
			var expectedInvalidate3 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin,
				phase3RootWidth + 2 * RootBox.InvalidateMargin,
				phase3RootHeight + 2 * RootBox.InvalidateMargin);
			Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3), "should invalidate the whole old root box; everything changes or moves.");
			VerifyParagraphs(root, new[] {"2", "3", "4", "5" }); // Should have last 4 paragraphs now (not made lazy).

			// Now try removing the last item.
			site.RectsInvalidatedInRoot.Clear();
			int phase4RootWidth = root.Width;
			var phase4Values = phase3Values.Take(3).ToArray();
			var topOfPara4 = root.FirstBox.Next.Next.Bottom + topLazy;
			owner.ReplaceObjSeq1(phase4Values);
			var expectedInvalidate4 = new Rectangle(-RootBox.InvalidateMargin, topOfPara4 - RootBox.InvalidateMargin,
				phase4RootWidth + 2 * RootBox.InvalidateMargin,
				root.FirstBox.Height + 2 * RootBox.InvalidateMargin);
			Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4), "should invalidate the old replaced paragraphs.");
			VerifyParagraphs(root, new[] { "2", "3", "4" }); // Should have last 3 paragraphs now (not made lazy).

			// Now try removing a middle item.
			site.RectsInvalidatedInRoot.Clear();
			int phase5RootWidth = root.Width;
			var phase5Values = new [] {newValues[2], newValues[4]};
			var topOfPara3 = root.FirstBox.Bottom + topLazy;
			var phase4RootHeight = root.Height;
			owner.ReplaceObjSeq1(phase5Values);
			var expectedInvalidate5 = new Rectangle(-RootBox.InvalidateMargin, topOfPara3 - RootBox.InvalidateMargin,
				phase5RootWidth + 2 * RootBox.InvalidateMargin,
				phase4RootHeight - topOfPara3 + 2 * RootBox.InvalidateMargin);
			Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate5), "should invalidate the old replaced paragraphs.");
			VerifyParagraphs(root, new[] { "2", "4" }); // Should have remaining 2 paragraphs now (not made lazy).

			// Insert three items at start: 0, 1, 3, 2, 4.
			site.RectsInvalidatedInRoot.Clear();
			int phase6RootWidth = root.Width;
			var phase6Values = new[] {newValues[0], newValues[1], newValues[3], newValues[2], newValues[4] };
			owner.ReplaceObjSeq1(phase6Values);
			int lazyWidth = root.LastLayoutInfo.MaxWidth; // current standard width for lazy boxes.
			var expectedInvalidate6 = new Rectangle(-RootBox.InvalidateMargin, topLazy - RootBox.InvalidateMargin,
				lazyWidth + 2 * RootBox.InvalidateMargin,
				root.Height + 2 * RootBox.InvalidateMargin);
			Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate6), "should invalidate everything...all moved or added.");
			VerifyParagraphs(root, new[] {null, "2", "4" }); // Should have added lazy box at start.
			VerifyLazyContents(root.FirstBox, new[] {newValues[0], newValues[1], newValues[3]});

			// Insert at end: 0, 1, 3, 2, 4, 9. I think we've tested the invalidate rects enough.
			var phase7Values = new[] { values[0], values[1], values[3], values[2], values[4], values[9] };
			owner.ReplaceObjSeq1(phase7Values);
			VerifyParagraphs(root, new[] { null, "2", "4", null }); // Should have added lazy box at end.
			VerifyLazyContents(root.LastBox, new[] { values[9] });
			// Insert between two non-lazy items: 0, 1, 3, 2, 5, 6, 4, 9.
			var phase8Values = new[] { values[0], values[1], values[3], values[2], values[5], values[6], values[4], values[9] };
			owner.ReplaceObjSeq1(phase8Values);
			VerifyParagraphs(root, new[] { null, "2", null, "4", null }); // Should have added lazy box in middle.
			VerifyLazyContents(root.FirstBox.Next.Next, new[] { values[5], values[6] });
			// Try a more complex overwrite. We'll replace the last item in the first lazy box and the first one in the second
			var phase9Values = new[] { values[0], values[1], values[7], values[2], values[8], values[6], values[4], values[9] };
			owner.ReplaceObjSeq1(phase9Values);
			VerifyParagraphs(root, new[] { null, "4", null }); // Should replace first 3 items with new lazy box.
			VerifyLazyContents(root.FirstBox, new[] { values[0], values[1], values[7], values[2], values[8], values[6] });
		}
예제 #12
0
        public void ParaSequenceTest()
        {
            var owner      = new MockData1(55, 77);
            var styles     = new AssembledStyles();
            var root       = new RootBoxFdo(styles);
            var layoutInfo = MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55);

            SetupFakeRootSite(root);
            var      engine  = layoutInfo.RendererFactory.GetRenderer(55, m_gm.VwGraphics) as FakeRenderEngine;
            MockSite site    = new MockSite();
            int      topLazy = 0;        // top of the part of the root box that is occupied by the lazy stuff, relative to the top of the root box itself.

            root.Site = site;
            PaintTransform ptrans = new PaintTransform(2, 4, 96, 96, 0, 10, 96, 96);

            site.m_transform  = ptrans;
            site.m_vwGraphics = m_gm.VwGraphics;
            root.Builder.Show(LazyDisplay.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55)));
            var heightOfOneItem = layoutInfo.MpToPixelsY(LazyBox <MockData1> .DefaultItemHeight * 1000);

            // This does two things: it makes sure the boxes produced by expanding a lazy box item won't be the SAME size,
            // so we'll get nontrivial changes in root box size; and it makes sure we don't have to expand MORE items than
            // expected based on the height estimate, which could throw off our predictions of what gets expanded.
            // Todo: we need a test where we DO have to expand more items after the initial estimate.
            engine.SegmentHeight = heightOfOneItem + 2;
            root.Layout(layoutInfo);
            VerifyParagraphs(root, new string[0]);

            var child1       = new MockData1(55, 77);
            var child1String = "Hello world, this is a wide string";

            child1.SimpleThree = child1String;
            owner.InsertIntoObjSeq1(0, child1);
            Assert.That(root.FirstBox, Is.TypeOf(typeof(LazyBox <MockData1>)));
            var lazyBox = (LazyBox <MockData1>)root.FirstBox;

            Assert.That(lazyBox.Width, Is.EqualTo(int.MaxValue / 2));             // no margins, should equal avail width.
            Assert.That(lazyBox.Height, Is.EqualTo(heightOfOneItem));
            var lazyTop             = lazyBox.Top;
            var lazyBottom          = lazyBox.Bottom;
            var oldRootHeight       = root.Height;
            int invalidateWidth     = lazyBox.Width + 2 * RootBox.InvalidateMargin;
            var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin,
                                                    invalidateWidth,
                                                    lazyBox.Height + 2 * RootBox.InvalidateMargin);

            Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1));
            site.RectsInvalidatedInRoot.Clear();
            var lazyHookup = ((IHookup)lazyBox).ParentHookup as LazyHookup <MockData1>;

            Assert.That(lazyHookup, Is.Not.Null);
            Assert.That(lazyHookup.Children[0], Is.EqualTo(lazyBox));
            Assert.That(lazyHookup.Children, Has.Count.EqualTo(1));
            root.LazyExpanded += root_LazyExpanded;
            using (var lc = new LayoutCallbacks(root))
            {
                root.PrepareToPaint(layoutInfo, null, 0, 200);
            }
            VerifyParagraphs(root, new [] { child1String });
            Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
            Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
            VerifyExpandArgs(0, lazyTop + 2, lazyBottom + 2, root.Height - oldRootHeight);
            Assert.That(lazyHookup.Children, Has.Count.EqualTo(1));
            Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "the lazy box standing for item hookups should have been replaced");

            // Now replace that one object with a list of several. I want to be able to expand two at the start, one at the end,
            // and one in the middle, and leave two lazy boxes behind. Then expand the rest and make them go away. So I need six.
            var values = new MockData1[10];

            for (int i = 0; i < 10; i++)
            {
                values[i]             = new MockData1(55, 77);
                values[i].SimpleThree = i.ToString();
            }
            var newValues = values.Take(6).ToArray();

            site.RectsInvalidatedInRoot.Clear();
            int phase2RootHeight = root.Height;
            int phase2RootWidth  = root.Width;

            owner.ReplaceObjSeq1(newValues);
            var expectedInvalidate2 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin,
                                                    phase2RootWidth + 2 * RootBox.InvalidateMargin,
                                                    phase2RootHeight + 2 * RootBox.InvalidateMargin);

            Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2), "should invalidate the old replaced paragraph.");
            lazyBox = (LazyBox <MockData1>)root.FirstBox;
            Assert.That(lazyBox.Width, Is.EqualTo(int.MaxValue / 2));             // no margins, should equal avail width.
            Assert.That(root.LastBox, Is.EqualTo(lazyBox), "old paragraph should have been replaced");
            Assert.That(lazyHookup.Children[0], Is.EqualTo(lazyBox), "after second replace we just have the lazy box");
            Assert.That(lazyHookup.Children, Has.Count.EqualTo(1), "should not have anything but lazy box after second replace");
            Assert.That(lazyBox.Height, Is.EqualTo(6 * heightOfOneItem));

            // Make it expand the first two items.
            site.RectsInvalidatedInRoot.Clear();
            m_expandArgs.Clear();
            oldRootHeight = root.Height;
            using (var lc = new LayoutCallbacks(root))
            {
                root.PrepareToPaint(layoutInfo, null, 0, heightOfOneItem * 2 - 2);
            }
            VerifyParagraphs(root, new[] { "0", "1", null });             // Should have two paras then lazy box
            Assert.That(newValues[0].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
            Assert.That(newValues[1].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
            Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
            Assert.That(root.Height, Is.Not.EqualTo(oldRootHeight));
            VerifyExpandArgs(0, 2, heightOfOneItem * 2 + 2, root.Height - oldRootHeight);             // +2's from root layout offset
            Assert.That(lazyHookup.Children, Has.Count.EqualTo(3));
            Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the first expanded item should be inserted");
            Assert.That(lazyHookup.Children[1], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 2nd expanded item should be inserted");
            Assert.That(lazyHookup.Children[2], Is.TypeOf(typeof(LazyBox <MockData1>)), "the lazy box standing for item hookups should still be there");
            lazyBox = root.FirstBox.Next.Next as LazyBox <MockData1>;
            Assert.That(lazyBox, Is.Not.Null);
            Assert.That(lazyBox.Height, Is.EqualTo(heightOfOneItem * 4));

            int topOfLastItem = lazyBox.Bottom - heightOfOneItem + 2;

            // Make it expand the last item.
            site.RectsInvalidatedInRoot.Clear();
            m_expandArgs.Clear();
            oldRootHeight = root.Height;
            using (var lc = new LayoutCallbacks(root))
            {
                root.PrepareToPaint(layoutInfo, null, topOfLastItem + 2, topOfLastItem + 10);
            }
            VerifyParagraphs(root, new[] { "0", "1", null, "5" });             // Should have two paras then lazy box then last para
            Assert.That(newValues[5].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
            Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
            Assert.That(root.Height, Is.Not.EqualTo(oldRootHeight));
            VerifyExpandArgs(0, topOfLastItem, topOfLastItem + heightOfOneItem, root.Height - oldRootHeight);
            Assert.That(lazyHookup.Children, Has.Count.EqualTo(4));
            Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the first expanded item should be inserted");
            Assert.That(lazyHookup.Children[1], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 2nd expanded item should be inserted");
            Assert.That(lazyHookup.Children[2], Is.TypeOf(typeof(LazyBox <MockData1>)), "the lazy box standing for item hookups should still be there");
            Assert.That(lazyHookup.Children[3], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the last expanded item should be inserted");
            lazyBox = root.FirstBox.Next.Next as LazyBox <MockData1>;
            Assert.That(lazyBox, Is.Not.Null);
            Assert.That(lazyBox.Height, Is.EqualTo(heightOfOneItem * 3));

            // Expand middle item in lazy box, leaving two lazy boxes.
            int topOfMiddleItem = lazyBox.Top + heightOfOneItem + 2;

            site.RectsInvalidatedInRoot.Clear();
            m_expandArgs.Clear();
            oldRootHeight = root.Height;
            using (var lc = new LayoutCallbacks(root))
            {
                root.PrepareToPaint(layoutInfo, null, topOfMiddleItem + 2, topOfMiddleItem + 10);
            }
            VerifyParagraphs(root, new[] { "0", "1", null, "3", null, "5" });             // Should have two paras then lazy box then middle para then another lazy then last para
            Assert.That(newValues[3].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
            Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
            Assert.That(root.Height, Is.Not.EqualTo(oldRootHeight));
            VerifyExpandArgs(0, topOfMiddleItem, topOfMiddleItem + heightOfOneItem, root.Height - oldRootHeight);
            Assert.That(lazyHookup.Children, Has.Count.EqualTo(6));
            Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the first expanded item should be inserted");
            Assert.That(lazyHookup.Children[1], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 2nd expanded item should be inserted");
            Assert.That(lazyHookup.Children[2], Is.TypeOf(typeof(LazyBox <MockData1>)), "the lazy box standing for item hookups should still be there");
            Assert.That(lazyHookup.Children[3], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the last expanded item should be inserted");
            Assert.That(lazyHookup.Children[4], Is.TypeOf(typeof(LazyBox <MockData1>)), "the lazy box standing for item hookups should still be there");
            Assert.That(lazyHookup.Children[5], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the last expanded item should be inserted");
            lazyBox = root.FirstBox.Next.Next as LazyBox <MockData1>;
            Assert.That(lazyBox, Is.Not.Null);
            Assert.That(lazyBox.Height, Is.EqualTo(heightOfOneItem));
            var lazyBox2 = lazyBox.Next.Next as LazyBox <MockData1>;

            Assert.That(lazyBox2, Is.Not.Null);
            Assert.That(lazyBox2.Height, Is.EqualTo(heightOfOneItem));
            // Expand lazy box when it is between two other items. (Also verify expanding two lazy boxes in one PrepareToPaint.)
            int topOfFirstLazy = lazyBox.Top + 2;
            int topOfLastLazy  = lazyBox2.Top + 2;

            site.RectsInvalidatedInRoot.Clear();
            m_expandArgs.Clear();
            oldRootHeight = root.Height;
            using (var lc = new LayoutCallbacks(root))
            {
                root.PrepareToPaint(layoutInfo, null, topOfFirstLazy + 2, topOfLastLazy + 2);
            }
            VerifyParagraphs(root, new[] { "0", "1", "2", "3", "4", "5" });             // Should have all the real paragraphs now.
            Assert.That(newValues[2].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
            Assert.That(newValues[4].SimpleThreeHookupCount, Is.EqualTo(1), "expanding lazy box should set up a hookup for the string");
            Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "we don't need to invalidate expanding something that's never been painted");
            Assert.That(root.Height, Is.Not.EqualTo(oldRootHeight));
            var delta = engine.SegmentHeight - heightOfOneItem;

            VerifyExpandArgs(0, topOfFirstLazy, topOfFirstLazy + heightOfOneItem, delta);
            VerifyExpandArgs(1, topOfLastLazy + delta, topOfLastLazy + delta + heightOfOneItem, delta);
            Assert.That(lazyHookup.Children, Has.Count.EqualTo(6));
            Assert.That(lazyHookup.Children[0], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the first expanded item should be inserted");
            Assert.That(lazyHookup.Children[1], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 2nd expanded item should be inserted");
            Assert.That(lazyHookup.Children[2], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 3rd expanded item should be inserted");
            Assert.That(lazyHookup.Children[3], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 4th expanded item should be inserted");
            Assert.That(lazyHookup.Children[4], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the 5th expanded item should be inserted");
            Assert.That(lazyHookup.Children[5], Is.TypeOf(typeof(ItemHookup)), "a regular item hookup for the last expanded item should be inserted");

            // Now try removing the first two items.
            site.RectsInvalidatedInRoot.Clear();
            int heightOfFirst2Paras = root.FirstBox.Next.Bottom - root.FirstBox.Top;
            int phase3RootWidth     = root.Width;
            int phase3RootHeight    = root.Height;
            var phase3Values        = newValues.Skip(2).ToArray();

            owner.ReplaceObjSeq1(phase3Values);
            var expectedInvalidate3 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin,
                                                    phase3RootWidth + 2 * RootBox.InvalidateMargin,
                                                    phase3RootHeight + 2 * RootBox.InvalidateMargin);

            Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3), "should invalidate the whole old root box; everything changes or moves.");
            VerifyParagraphs(root, new[] { "2", "3", "4", "5" });            // Should have last 4 paragraphs now (not made lazy).

            // Now try removing the last item.
            site.RectsInvalidatedInRoot.Clear();
            int phase4RootWidth = root.Width;
            var phase4Values    = phase3Values.Take(3).ToArray();
            var topOfPara4      = root.FirstBox.Next.Next.Bottom + topLazy;

            owner.ReplaceObjSeq1(phase4Values);
            var expectedInvalidate4 = new Rectangle(-RootBox.InvalidateMargin, topOfPara4 - RootBox.InvalidateMargin,
                                                    phase4RootWidth + 2 * RootBox.InvalidateMargin,
                                                    root.FirstBox.Height + 2 * RootBox.InvalidateMargin);

            Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4), "should invalidate the old replaced paragraphs.");
            VerifyParagraphs(root, new[] { "2", "3", "4" });             // Should have last 3 paragraphs now (not made lazy).

            // Now try removing a middle item.
            site.RectsInvalidatedInRoot.Clear();
            int phase5RootWidth  = root.Width;
            var phase5Values     = new [] { newValues[2], newValues[4] };
            var topOfPara3       = root.FirstBox.Bottom + topLazy;
            var phase4RootHeight = root.Height;

            owner.ReplaceObjSeq1(phase5Values);
            var expectedInvalidate5 = new Rectangle(-RootBox.InvalidateMargin, topOfPara3 - RootBox.InvalidateMargin,
                                                    phase5RootWidth + 2 * RootBox.InvalidateMargin,
                                                    phase4RootHeight - topOfPara3 + 2 * RootBox.InvalidateMargin);

            Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate5), "should invalidate the old replaced paragraphs.");
            VerifyParagraphs(root, new[] { "2", "4" });             // Should have remaining 2 paragraphs now (not made lazy).

            // Insert three items at start: 0, 1, 3, 2, 4.
            site.RectsInvalidatedInRoot.Clear();
            int phase6RootWidth = root.Width;
            var phase6Values    = new[] { newValues[0], newValues[1], newValues[3], newValues[2], newValues[4] };

            owner.ReplaceObjSeq1(phase6Values);
            int lazyWidth           = root.LastLayoutInfo.MaxWidth;   // current standard width for lazy boxes.
            var expectedInvalidate6 = new Rectangle(-RootBox.InvalidateMargin, topLazy - RootBox.InvalidateMargin,
                                                    lazyWidth + 2 * RootBox.InvalidateMargin,
                                                    root.Height + 2 * RootBox.InvalidateMargin);

            Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate6), "should invalidate everything...all moved or added.");
            VerifyParagraphs(root, new[] { null, "2", "4" });            // Should have added lazy box at start.
            VerifyLazyContents(root.FirstBox, new[] { newValues[0], newValues[1], newValues[3] });

            // Insert at end: 0, 1, 3, 2, 4, 9. I think we've tested the invalidate rects enough.
            var phase7Values = new[] { values[0], values[1], values[3], values[2], values[4], values[9] };

            owner.ReplaceObjSeq1(phase7Values);
            VerifyParagraphs(root, new[] { null, "2", "4", null });             // Should have added lazy box at end.
            VerifyLazyContents(root.LastBox, new[] { values[9] });
            // Insert between two non-lazy items: 0, 1, 3, 2, 5, 6, 4, 9.
            var phase8Values = new[] { values[0], values[1], values[3], values[2], values[5], values[6], values[4], values[9] };

            owner.ReplaceObjSeq1(phase8Values);
            VerifyParagraphs(root, new[] { null, "2", null, "4", null });             // Should have added lazy box in middle.
            VerifyLazyContents(root.FirstBox.Next.Next, new[] { values[5], values[6] });
            // Try a more complex overwrite. We'll replace the last item in the first lazy box and the first one in the second
            var phase9Values = new[] { values[0], values[1], values[7], values[2], values[8], values[6], values[4], values[9] };

            owner.ReplaceObjSeq1(phase9Values);
            VerifyParagraphs(root, new[] { null, "4", null });             // Should replace first 3 items with new lazy box.
            VerifyLazyContents(root.FirstBox, new[] { values[0], values[1], values[7], values[2], values[8], values[6] });
        }
예제 #13
0
		/// <summary>
		/// Redo the layout of the paragraph given the Source changes indicated in the given details.
		/// </summary>
		/// <param name="details"></param>
		private void Relayout(SourceChangeDetails details)
		{
			// We would prefer to keep both sources intact, but we can't just do Source = details.NewSource,
			// because there is currently no way to change the Source of existing segments we may want to reuse.
			Source.Copyfrom(details.NewSource);
			var oldHeight = Height;
			var oldWidth = Width;
			using (var gh = Root.Site.DrawingInfo)
			{
				// Enhance JohnT: margins: need to adjust MaxWidth for margins and padding of containing boxes.
				var info = new LayoutInfo(ChildTransformFromRootTransform(gh.Transform), Root.LastLayoutInfo.MaxWidth,
					gh.VwGraphics, Root.LastLayoutInfo.RendererFactory);
				var builder = new ParaBuilder(this, info);
				using (var lcb = new LayoutCallbacks(Root))
				{
					builder.Relayout(details, lcb);
					if (Height != oldHeight || Width != oldWidth)
						RelayoutParents(gh);
				}
			}
		}
예제 #14
0
        /// <summary>
        /// Redo layout. Should produce the same segments as FullLayout, but assume that segments for text up to
        /// details.StartChange may be reused (if not affected by changing line breaks), and segments after
        /// details.StartChange+details.DeleteCount may be re-used if a line break works out (and after adjusting
        /// their begin offset).
        /// </summary>
        /// <param name="details"></param>
        internal void Relayout(SourceChangeDetails details, LayoutCallbacks lcb)
        {
            m_reuseableLines = m_para.Lines;
            m_lines          = new List <ParaLine>();
            m_renderRunIndex = 0;
            m_ichRendered    = 0;
            IRenderRun last = m_renderRuns[m_renderRuns.Count - 1];

            m_ichLim             = last.RenderStart + last.RenderLength;
            m_lastRenderRunIndex = m_renderRuns.Count;
            Rectangle invalidateRect = m_para.InvalidateRect;
            int       delta          = details.InsertCount - details.DeleteCount;
            int       oldHeight      = m_para.Height;
            int       oldWidth       = m_para.Width;

            // Make use of details.StartChange to reuse some lines at start.
            if (m_reuseableLines.Count > 0)
            {
                // As long as we have two complete lines before the change, we can certainly reuse the first of them.
                while (m_reuseableLines.Count > 2 && details.StartChange > m_reuseableLines[2].IchMin)
                {
                    m_lines.Add(m_reuseableLines[0]);
                    m_reuseableLines.RemoveAt(0);
                }
                // If we still have one complete line before the change, we can reuse it provided there is white
                // space after the end of the line and before the change.
                if (m_reuseableLines.Count > 1)
                {
                    int startNextLine = m_reuseableLines[1].IchMin;
                    if (details.StartChange > startNextLine)
                    {
                        bool   fGotWhite = false;
                        string line1Text = m_reuseableLines[1].CheckedText;
                        int    lim       = details.StartChange - startNextLine;
                        var    cpe       = LgIcuCharPropEngineClass.Create();
                        for (int ich = 0; ich < lim; ich++)
                        {
                            // Enhance JohnT: possibly we need to consider surrogates here?
                            // Worst case is we don't reuse a line we could have, since a surrogate won't
                            // be considered white.
                            if (cpe.get_IsSeparator(Convert.ToInt32(line1Text[ich])))
                            {
                                fGotWhite = true;
                                break;
                            }
                        }
                        if (fGotWhite)
                        {
                            m_lines.Add(m_reuseableLines[0]);
                            m_reuseableLines.RemoveAt(0);
                        }
                    }
                }
                m_ichRendered = m_reuseableLines[0].IchMin;
                int topOfFirstDiscardedLine = m_reuseableLines[0].Top;
                // We don't need to invalidate the lines we're keeping.
                invalidateRect = new Rectangle(invalidateRect.Left, invalidateRect.Top + topOfFirstDiscardedLine,
                                               invalidateRect.Width, invalidateRect.Height - topOfFirstDiscardedLine);
            }

            // Figure out which run we need to continue from, to correspond to the start of the first line
            // we need to rebuild.
            while (m_renderRunIndex < m_renderRuns.Count && m_renderRuns[m_renderRunIndex].RenderLim <= m_ichRendered)
            {
                m_renderRunIndex++;
            }

            while (!Finished)
            {
                // Todo: I think we need to adjust available width if this is the first line.
                BuildALine();
                // Drop any initial reusable lines we now determine to be unuseable after all.
                // If we've used characters beyond the start of this potentially reusable line, we can't reuse it.
                // Also, we don't reuse empty lines. Typically an empty line is left over from a previously empty
                // paragraph, and we no longer need the empty segment, even though it doesn't have any of the same
                // characters (since it has none) as the segment that has replaced it.
                while (m_reuseableLines.Count > 0 && (m_ichRendered > m_reuseableLines[0].IchMin + delta || m_reuseableLines[0].Length == 0))
                {
                    m_reuseableLines.RemoveAt(0);
                }
                if (m_reuseableLines.Count > 0)
                {
                    // See if we can resync.
                    var nextLine = m_reuseableLines[0];
                    if (m_ichRendered == nextLine.IchMin + delta)
                    {
                        // reuse it.
                        int top = m_gapTop;
                        if (m_lines.Count > 0)
                        {
                            ParaLine previous = m_lines.Last();
                            previous.LastBox.Next = nextLine.FirstBox;
                            top = TopOfNextLine(previous, nextLine.Ascent);
                        }

                        m_lines.AddRange(m_reuseableLines);
                        if (top != nextLine.Top)
                        {
                            ParaLine previous = null;
                            foreach (var line in m_reuseableLines)
                            {
                                if (previous != null)                                 // first time top has already been computed
                                {
                                    top = TopOfNextLine(previous, line.Ascent);
                                }
                                line.Top = top;                                 // BEFORE ArrangeBoxes, since it gets copied to the individual boxes
                                m_currentLine.ArrangeBoxes(m_para.Style.ParaAlignment, m_gapLeft, m_gapRight, 0, m_layoutInfo.MaxWidth, TopDepth);
                                previous = line;
                            }
                        }
                        else
                        {
                            // reusable lines have not moved, we don't need to invalidate them.
                            invalidateRect.Height -= (m_reuseableLines.Last().Bottom - top);
                        }
                        for (Box box = nextLine.FirstBox; box != null; box = box.Next)
                        {
                            if (box is StringBox)
                            {
                                (box as StringBox).IchMin += delta;
                            }
                        }

                        break;
                    }
                }
            }
            SetParaInfo();
            // if the paragraph got larger, we need to invalidate the extra area.
            // (But, don't reduce it if it got smaller; we want to invalidate all the old stuff as well as all the new.)
            if (m_para.Height > oldHeight)
            {
                invalidateRect.Height += m_para.Height - oldHeight;
            }
            if (m_para.Width > oldWidth)
            {
                invalidateRect.Width += m_para.Width - oldWidth;
            }
            lcb.InvalidateInRoot(invalidateRect);
        }
예제 #15
0
        public void DrawingBordersandBackground()
        {
            var root = new RootBoxFdo(new AssembledStyles());

            SetupFakeRootSite(root);
            var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55);

            root.RendererFactory = layoutInfo.RendererFactory;
            var mock1 = new MockData1()
            {
                SimpleThree = "This is the first paragraph."
            };
            // The length of the second paragraph is found by experiment to be enough so that
            // despite its lacking borders it also breaks into 2 lines in the second step.
            var mock2 = new MockData1()
            {
                SimpleThree = "Here is another paragraph. It needs to be a bit longer."
            };

            root.Builder.Show(
                Paragraph.Containing(Display.Of(() => mock1.SimpleThree)).BackColor(Color.Red)
                .Margins(1.Points(), 2.Points(), 3.Points(), 4.Points())
                .Borders(5.Points(), 6.Points(), 7.Points(), 8.Points(), Color.Blue)
                .Pads(9.Points(), 10.Points(), 11.Points(), 12.Points()),
                Paragraph.Containing(Display.Of(() => mock2.SimpleThree)).BackColor(Color.Yellow)
                .Margins(1.Points(), 2.Points(), 3.Points(), 4.Points()));
            root.Layout(layoutInfo);
            // We want to keep track of the sequence of paint operations in all three segments.
            var drawActions = new List <object>();
            var vg          = new MockGraphics();

            vg.DrawActions = drawActions;
            var para1      = (ParaBox)root.FirstBox;
            var stringbox1 = (StringBox)para1.FirstBox;
            var seg1       = (FakeSegment)stringbox1.Segment;

            seg1.DrawActions = drawActions;
            var para2      = (ParaBox)para1.Next;
            var stringbox2 = (StringBox)para2.FirstBox;
            var seg2       = (FakeSegment)stringbox2.Segment;

            seg2.DrawActions = drawActions;

            var site = (MockSite)root.Site;

            root.Paint(vg, site.m_transform);
            var paintTrans = site.m_transform;
            int position   = 0;

            int red      = (int)ColorUtil.ConvertColorToBGR(Color.Red);
            int margLeft = layoutInfo.MpToPixelsX(1000);

            Assert.That(margLeft, Is.EqualTo(1));
            int bordLeft = layoutInfo.MpToPixelsX(5000);

            Assert.That(bordLeft, Is.EqualTo(7));
            int xOffset = 2 - 100;             // how far it is pushed over by the offsets of the layoutInfo
            int margTop = layoutInfo.MpToPixelsY(2000);

            Assert.That(margTop, Is.EqualTo(3));
            int bordTop = layoutInfo.MpToPixelsY(6000);

            Assert.That(bordTop, Is.EqualTo(8));
            int yOffset = 2 - 200;             // how far it is pushed down by the offsets of the layoutInfo
            int padLeft = layoutInfo.MpToPixelsX(9000);

            Assert.That(padLeft, Is.EqualTo(12));
            int padRight = layoutInfo.MpToPixelsX(11000);

            Assert.That(padRight, Is.EqualTo(15));
            int padTop = layoutInfo.MpToPixelsY(10000);

            Assert.That(padTop, Is.EqualTo(13));
            int padBottom = layoutInfo.MpToPixelsY(12000);

            Assert.That(padBottom, Is.EqualTo(16));
            // First it should draw a background rectangle for the first paragraph.
            // It is indented by the left margin and the left border, and down by the top margin and border.
            // The other side is determined by the size of the embedded box and the two pads.
            VerifyRect(drawActions, ref position, margLeft + bordLeft + xOffset, margTop + bordTop + yOffset,
                       margLeft + bordLeft + xOffset + stringbox1.Width + padLeft + padRight,
                       margTop + bordTop + yOffset + stringbox1.Height + padTop + padBottom,
                       red);
            int bordBottom = layoutInfo.MpToPixelsY(8000);

            Assert.That(bordBottom, Is.EqualTo(11));
            int blue = (int)ColorUtil.ConvertColorToBGR(Color.Blue);

            // It's arbitrary what order we draw the borders, and I wish the test didn't specify it,
            // but in fact the current implementation draws the left border first.
            VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset,
                       margLeft + bordLeft + xOffset,
                       margTop + bordTop + yOffset + padTop + stringbox1.Height + padBottom + bordBottom,
                       blue);
            int bordRight = layoutInfo.MpToPixelsX(7000);

            Assert.That(bordRight, Is.EqualTo(9));
            // Then the top border
            VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset,
                       margLeft + bordLeft + xOffset + padLeft + stringbox1.Width + padRight + bordRight,
                       margTop + bordTop + yOffset,
                       blue);
            // Then the right border
            VerifyRect(drawActions, ref position,
                       margLeft + bordLeft + xOffset + padLeft + stringbox1.Width + padRight,
                       margTop + yOffset,
                       margLeft + bordLeft + xOffset + padLeft + stringbox1.Width + padRight + bordRight,
                       margTop + bordTop + yOffset + padTop + stringbox1.Height + padBottom + bordBottom,
                       blue);
            // Then the bottom border
            VerifyRect(drawActions, ref position,
                       margLeft + xOffset,
                       margTop + bordTop + yOffset + padTop + stringbox1.Height + padBottom,
                       margLeft + bordLeft + xOffset + padLeft + stringbox1.Width + padRight + bordRight,
                       margTop + bordTop + yOffset + padTop + stringbox1.Height + padBottom + bordBottom,
                       blue);
            // Figure an adjusted y offset for the second paragraph. Everything is down by the height
            // of the first paragraph, except that the top and bottom margins overlap by the
            // height of the smaller.
            int yOffset2 = yOffset + para1.Height - margTop;
            int yellow   = (int)ColorUtil.ConvertColorToBGR(Color.Yellow);

            // Next a background block for the second paragraph.
            // (Background color should be reset for the embedded string boxes, so they should not draw their own
            // background.)
            VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset2,
                       margLeft + xOffset + stringbox2.Width,
                       margTop + yOffset2 + stringbox2.Height,
                       yellow);
            // Verify the position where the text is drawn
            VerifyDraw(drawActions, ref position, seg1, margLeft + bordLeft + padLeft + 2, margTop + bordTop + padTop + 2);
            VerifyDraw(drawActions, ref position, seg2, margLeft + 2, para1.Height + 2);             //margTop cancels out
            // And that should be all!
            Assert.That(position, Is.EqualTo(drawActions.Count));

            // Verify that multi-lines in a paragraph are appropriately laid out with margin etc.
            int maxWidth = para1.Width - FakeRenderEngine.SimulatedWidth("paragraph");

            // This maxWidth should force each paragraph to make two segments.
            layoutInfo = HookupTests.MakeLayoutInfo(maxWidth, m_gm.VwGraphics, 55);
            root.Layout(layoutInfo);
            drawActions.Clear();
            position = 0;
            var stringbox1a = (StringBox)para1.FirstBox;
            var seg1a       = (FakeSegment)stringbox1a.Segment;

            seg1a.DrawActions = drawActions;
            var stringbox1b = (StringBox)stringbox1a.Next;
            var seg1b       = (FakeSegment)stringbox1b.Segment;

            seg1b.DrawActions = drawActions;
            var stringbox2a = (StringBox)para2.FirstBox;
            var seg2a       = (FakeSegment)stringbox2a.Segment;

            seg2a.DrawActions = drawActions;
            var stringbox2b = (StringBox)stringbox2a.Next;
            var seg2b       = (FakeSegment)stringbox2b.Segment;

            seg2b.DrawActions = drawActions;

            root.Paint(vg, site.m_transform);
            int margRight = layoutInfo.MpToPixelsX(3000);

            Assert.That(margRight, Is.EqualTo(4));
            // First it should draw a background rectangle for the first paragraph.
            // It is indented by the left margin and the left border, and down by the top margin and border.
            // The other side is determined by maxWidth minus the right margin and border.
            int contentHeight1 = stringbox1a.Height + stringbox1b.Height;

            VerifyRect(drawActions, ref position, margLeft + bordLeft + xOffset, margTop + bordTop + yOffset,
                       maxWidth - margRight - bordRight + xOffset,
                       margTop + bordTop + yOffset + contentHeight1 + padTop + padBottom,
                       red);
            // It's arbitrary what order we draw the borders, and I wish the test didn't specify it,
            // but in fact the current implementation draws the left border first.
            VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset,
                       margLeft + bordLeft + xOffset,
                       margTop + bordTop + yOffset + padTop + contentHeight1 + padBottom + bordBottom,
                       blue);
            // Then the top border
            VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset,
                       maxWidth - margRight + xOffset,
                       margTop + bordTop + yOffset,
                       blue);
            // Then the right border
            VerifyRect(drawActions, ref position,
                       maxWidth - margRight - bordRight + xOffset,
                       margTop + yOffset,
                       maxWidth - margRight + xOffset,
                       margTop + bordTop + yOffset + padTop + contentHeight1 + padBottom + bordBottom,
                       blue);
            // Then the bottom border
            VerifyRect(drawActions, ref position,
                       margLeft + xOffset,
                       margTop + bordTop + yOffset + padTop + contentHeight1 + padBottom,
                       maxWidth - margRight + xOffset,
                       margTop + bordTop + yOffset + padTop + contentHeight1 + padBottom + bordBottom,
                       blue);
            // Figure an adjusted y offset for the second paragraph. Everything is down by the height
            // of the first paragraph, except that the top and bottom margins overlap by the
            // height of the smaller.
            yOffset2 = yOffset + para1.Height - margTop;
            // Next a background block for the second paragraph.
            // (Background color should be reset for the embedded string boxes, so they should not draw their own
            // background.)
            VerifyRect(drawActions, ref position, margLeft + xOffset, margTop + yOffset2,
                       maxWidth - margRight + xOffset,
                       margTop + yOffset2 + stringbox2a.Height + stringbox2b.Height,
                       yellow);
            // Verify the position where the text is drawn
            VerifyDraw(drawActions, ref position, seg1a, margLeft + bordLeft + padLeft + 2, margTop + bordTop + padTop + 2);
            VerifyDraw(drawActions, ref position, seg1b, margLeft + bordLeft + padLeft + 2,
                       margTop + bordTop + padTop + 2 + stringbox1a.Height);
            VerifyDraw(drawActions, ref position, seg2a, margLeft + 2, para1.Height + 2);                      //margTop cancels out
            VerifyDraw(drawActions, ref position, seg2b, margLeft + 2, para1.Height + 2 + stringbox2a.Height); //margTop cancels out
            // And that should be all!
            Assert.That(position, Is.EqualTo(drawActions.Count));

            // A quick check that Relayout puts things in the same places.
            drawActions.Clear();
            position = 0;
            var fixupMap = new Dictionary <Box, Rectangle>();

            fixupMap[para1] = new Rectangle(0, 0, 10, 10);
            var oldstring1aLeft = stringbox1a.Left;
            var oldstring1bTop  = stringbox1b.Top;

            using (var lcb = new LayoutCallbacks(root))
                root.Relayout(layoutInfo, fixupMap, lcb);
            Assert.That(drawActions.Count, Is.EqualTo(0));
            Assert.That(para1.FirstBox.Left, Is.EqualTo(oldstring1aLeft));
            Assert.That(para1.FirstBox.Next.Top, Is.EqualTo(oldstring1bTop));
        }
예제 #16
0
        public void PileAndBlock()
        {
            var root = new RootBoxFdo(new AssembledStyles());

            SetupFakeRootSite(root);
            var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55);

            root.RendererFactory = layoutInfo.RendererFactory;
            var mock1 = new MockData1()
            {
                SimpleThree = "This is the first paragraph."
            };
            // The length of the second paragraph is found by experiment to be enough so that
            // despite its lacking borders it also breaks into 2 lines in the second step.
            var mock2 = new MockData1()
            {
                SimpleThree = "Here is another paragraph. It needs to be a bit longer."
            };

            root.Builder.Show(
                Div.Containing(
                    Display.Block(Color.Red, 25000, 18000).BackColor(Color.Purple)
                    .Margins(3000, 3000, 3000, 3000)
                    .Border(5000, Color.Blue)
                    .Pads(4000, 4000, 4000, 4000),
                    Display.Block(Color.Green, 25000, 18000)
                    ).BackColor(Color.Pink)                     // these apply to div.
                .Margins(1000, 1000, 1000, 1000)
                .Border(2000, Color.Gold)
                .Pads(6000, 6000, 6000, 6000));
            root.Layout(layoutInfo);
            // We want to keep track of the sequence of paint operations in all three segments.
            var drawActions = new List <object>();
            var vg          = new MockGraphics();

            vg.DrawActions = drawActions;

            var site = (MockSite)root.Site;

            root.Paint(vg, site.m_transform);
            var paintTrans = site.m_transform;
            int position   = 0;
            int xOffset    = 2 - 100;          // how far it is pushed over by the offsets of the layoutInfo
            int yOffset    = 2 - 200;          // how far it is pushed down by the offsets of the layoutInfo

            int red    = (int)ColorUtil.ConvertColorToBGR(Color.Red);
            int pink   = (int)ColorUtil.ConvertColorToBGR(Color.Pink);
            int purple = (int)ColorUtil.ConvertColorToBGR(Color.Purple);
            int blue   = (int)ColorUtil.ConvertColorToBGR(Color.Blue);
            int green  = (int)ColorUtil.ConvertColorToBGR(Color.Green);
            int gold   = (int)ColorUtil.ConvertColorToBGR(Color.Gold);
            // Technically we could do different conversions in the two directions, but for this test both dpi are the same.
            int margPile    = layoutInfo.MpToPixelsX(1000);
            int bordPile    = layoutInfo.MpToPixelsX(2000);
            int padPile     = layoutInfo.MpToPixelsX(6000);
            int blockWidth  = layoutInfo.MpToPixelsX(25000);
            int blockHeight = layoutInfo.MpToPixelsX(18000);
            int margBlock   = layoutInfo.MpToPixelsX(3000);
            int bordBlock   = layoutInfo.MpToPixelsX(5000);
            int padBlock    = layoutInfo.MpToPixelsX(4000);

            // First a background rectangle for the whole pile.
            var leftPilePad   = margPile + bordPile + xOffset;
            var topPilePad    = margPile + bordPile + yOffset;
            var rightPilePad  = margPile + bordPile + 2 * padPile + blockWidth + 2 * margBlock + 2 * bordBlock + 2 * padBlock + xOffset;
            var bottomPilePad = margPile + bordPile + 2 * padPile + 2 * blockHeight + 2 * margBlock + 2 * bordBlock + 2 * padBlock + yOffset;

            VerifyRect(drawActions, ref position, leftPilePad, topPilePad, rightPilePad, bottomPilePad, pink);
            // Left border, whole pile
            VerifyRect(drawActions, ref position, leftPilePad - bordPile, topPilePad - bordPile,
                       leftPilePad, bottomPilePad + bordPile, gold);
            // top border, whole pile
            VerifyRect(drawActions, ref position, leftPilePad - bordPile, topPilePad - bordPile,
                       rightPilePad + bordPile, topPilePad, gold);
            // right border, whole pile
            VerifyRect(drawActions, ref position, rightPilePad, topPilePad - bordPile,
                       rightPilePad + bordPile, bottomPilePad + bordPile, gold);
            // bottom border, whole pile
            VerifyRect(drawActions, ref position, leftPilePad - bordPile, bottomPilePad,
                       rightPilePad + bordPile, bottomPilePad + bordPile, gold);

            // background and border for first block.
            var leftBlockPad   = margPile + bordPile + padPile + margBlock + bordBlock + xOffset;
            var topBlockPad    = margPile + bordPile + padPile + margBlock + bordBlock + yOffset;
            var rightBlockPad  = margPile + bordPile + padPile + margBlock + bordBlock + 2 * padBlock + blockWidth + xOffset;
            var bottomBlockPad = margPile + bordPile + padPile + margBlock + bordBlock + 2 * padBlock + blockHeight + yOffset;

            VerifyRect(drawActions, ref position, leftBlockPad, topBlockPad, rightBlockPad, bottomBlockPad, purple);
            // Left border, whole pile
            VerifyRect(drawActions, ref position, leftBlockPad - bordBlock, topBlockPad - bordBlock,
                       leftBlockPad, bottomBlockPad + bordBlock, blue);
            // top border, whole pile
            VerifyRect(drawActions, ref position, leftBlockPad - bordBlock, topBlockPad - bordBlock,
                       rightBlockPad + bordBlock, topBlockPad, blue);
            // right border, whole pile
            VerifyRect(drawActions, ref position, rightBlockPad, topBlockPad - bordBlock,
                       rightBlockPad + bordBlock, bottomBlockPad + bordBlock, blue);
            // bottom border, whole pile
            VerifyRect(drawActions, ref position, leftBlockPad - bordBlock, bottomBlockPad,
                       rightBlockPad + bordBlock, bottomBlockPad + bordBlock, blue);
            // The first block itself.
            VerifyRect(drawActions, ref position, leftBlockPad + padBlock, topBlockPad + padBlock,
                       leftBlockPad + padBlock + blockWidth, topBlockPad + padBlock + blockHeight, red);
            // The second block itself.
            var topBlock2 = bottomBlockPad + bordBlock + margBlock;

            VerifyRect(drawActions, ref position, leftPilePad + padPile, topBlock2,
                       leftPilePad + padPile + blockWidth, topBlock2 + blockHeight, green);
            // And that should be all!
            Assert.That(position, Is.EqualTo(drawActions.Count));

            // A quick check that Relayout puts things in the same places.
            drawActions.Clear();
            var fixupMap = new Dictionary <Box, Rectangle>();
            var div1     = (DivBox)root.FirstBox;
            var block1   = div1.FirstBox;

            fixupMap[div1]   = new Rectangle(0, 0, 10, 10);
            fixupMap[block1] = new Rectangle(0, 0, 10, 10);
            var oldblock1Left = block1.Left;
            var oldblock1bTop = block1.Top;

            using (var lcb = new LayoutCallbacks(root))
                root.Relayout(layoutInfo, fixupMap, lcb);
            Assert.That(drawActions.Count, Is.EqualTo(0));
            Assert.That(div1.FirstBox.Left, Is.EqualTo(oldblock1Left));
            Assert.That(div1.FirstBox.Top, Is.EqualTo(oldblock1bTop));
        }