public void Select() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "new old contents"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Display.Of(() => mock1.SimpleThree, 23)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; int x = FakeRenderEngine.SimulatedWidth("new ") + 2; var location = new Point(x, 8); EventArgs e = new EventArgs(); MouseEventArgs m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo("old ")); mock1.SimpleThree = "new old:contents"; x = FakeRenderEngine.SimulatedWidth("new o") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo("old")); mock1.SimpleThree = "new(old contents"; x = FakeRenderEngine.SimulatedWidth("new ol") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo("old ")); mock1.SimpleThree = "newo1dcontents"; x = FakeRenderEngine.SimulatedWidth("new o1d") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo("newo1dcontents")); }
public void DragStartsOnMoveInSelection() { string contents = "This is the day."; var engine = new FakeRenderEngine() {Ws = 34, SegmentHeight = 13}; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); var styles = new AssembledStyles().WithWs(34); var clientRuns = new List<IClientRun>(); var run = new StringClientRun(contents, styles); clientRuns.Add(run); var source = new TextSource(clientRuns, null); var para = new ParaBox(styles, source); var extraBox = new BlockBox(styles, Color.Red, 50, 72000); // tall, narrow spacer at top var root = new RootBoxFdo(styles); root.AddBox(extraBox); root.AddBox(para); var layoutArgs = MakeLayoutInfo(Int32.MaxValue/2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); Assert.That(root.Height, Is.EqualTo(96 + 13)); Assert.That(root.Width, Is.EqualTo(FakeRenderEngine.SimulatedWidth(contents))); var ip1 = run.SelectAt(para, 5, false); var ip2 = run.SelectAt(para, 7, true); var range = new RangeSelection(ip1, ip2); range.Install(); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); var sbox = para.FirstBox as StringBox; MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; int indent = FakeRenderEngine.SimulatedWidth("This "); root.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, indent + 5, 100, 0), Keys.None, m_gm.VwGraphics, ptrans); root.OnMouseMove(new MouseEventArgs(MouseButtons.Left, 1, indent + 5, 100, 0), Keys.None, m_gm.VwGraphics, ptrans); Assert.That(GetStringDropData(site), Is.EqualTo("is")); Assert.That(site.LastDoDragDropArgs.AllowedEffects, Is.EqualTo(DragDropEffects.Copy), "editing not possible in this paragraph, we can only copy"); Assert.That(root.Selection, Is.EqualTo(range), "selection should not be changed by drag drop"); site.LastDoDragDropArgs = null; root.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, 3, 100, 0), Keys.None, m_gm.VwGraphics, ptrans); Assert.That(site.LastDoDragDropArgs, Is.Null, "click outside selection should not initiate drag"); // Tack on an extra check that a read-only view does not handle drop. var dataObj = new DataObject(DataFormats.StringFormat, "new "); var dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.ControlKey, 10, 8, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, new Point(14, 8), m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.None)); Assert.That(root.DragState, Is.EqualTo(WindowDragState.DraggingHere)); }
public void SendingInsertLineBreak() { var owner = new MockData1(55, 77); var child1 = new MockData1(55, 77); var child2 = new MockData1(55, 77); owner.InsertIntoObjSeq1(0, child1); owner.InsertIntoObjSeq1(1, child2); child1.SimpleThree = "Hello World. "; child2.SimpleThree = "This is a test."; var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); SetupFakeRootSite(root); var po = new MockSendParagraphOperations(); root.Builder.Show(Display.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55)) .EditParagraphsUsing(po)); Assert.That(po.Hookup, Is.Not.Null); root.Layout(layoutInfo); var para = (ParaBox)root.FirstBox; var ip = para.SelectAtEnd(); ip.InsertLineBreak(); Assert.That(po.InsertFollowingParagraphIp, Is.EqualTo(ip), "should have called InsertFollowingParagraph with right IP"); Assert.That(po.InsertFollowingParagraphActionPerformed, Is.False, "make selection action performed too soon"); ((MockSite)root.Site).DoPendingAfterNotificationTasks(); Assert.That(po.InsertFollowingParagraphActionPerformed, Is.True, "make selection action not performed or not saved"); po.InsertFollowingParagraphIp = null; ip = para.SelectAtStart(); ip.InsertLineBreak(); Assert.That(po.InsertFollowingParagraphIp, Is.Null); Assert.That(po.InsertPrecedingParagraphIp, Is.EqualTo(ip), "should have called InsertPrecedingParagraph with right IP"); Assert.That(po.InsertPrecedingParagraphActionPerformed, Is.False, "make selection action performed too soon"); ((MockSite)root.Site).DoPendingAfterNotificationTasks(); Assert.That(po.InsertPrecedingParagraphActionPerformed, Is.True, "make selection action not performed or not saved"); // Todo: test sending InsertLineBreak when selection mid-paragraph, also empty paragraph. }
public void DrawIPMultiRunPara() { var styles = new AssembledStyles(); RootBox root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing( Display.Of("first"), Display.Of("second"), Display.Of("third"))); SetupFakeRootSite(root); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); root.Layout(layoutInfo); InsertionPoint ip = root.SelectAtEnd(); ip.Install(); var para = (ParaBox)root.FirstBox; Assert.AreEqual(ip, root.Selection); Assert.AreEqual(para, ip.Para, "IP should know about the paragraph it is in"); Assert.AreEqual("third".Length, ip.StringPosition, "IP position is relative to run"); Assert.AreEqual(true, ip.AssociatePrevious, "selection at end should always associate previous in non-empty para"); StringBox sbox = para.FirstBox as StringBox; Assert.That(sbox, Is.EqualTo(para.LastBox), "uniform text in infinite width should make one string box"); var seg = sbox.Segment as FakeSegment; Assert.IsNotNull(seg); PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, -10, 120, 128); ip.Draw(m_gm.VwGraphics, ptrans); VerifySegmentDrawing(para, sbox, seg.LastDrawIpCall, -4); seg.NextPosIpResult = new MockSegment.PositionsOfIpResults(); seg.NextPosIpResult.RectPrimary = new Rect(5, 6, 7, 9); seg.NextPosIpResult.PrimaryHere = true; Rectangle selRect = ip.GetSelectionLocation(m_gm.VwGraphics, ptrans); VerifySelLocation(para, sbox, seg.LastPosIpCall, -4); Assert.AreEqual(new Rectangle(5, 6, 2, 3), selRect); }
public void ParaSequenceWithInitialContent() { var owner = new MockData1(55, 77); var child1 = new MockData1(55, 77); var child2 = new MockData1(55, 77); owner.InsertIntoObjSeq1(0, child1); owner.InsertIntoObjSeq1(1, child2); child1.SimpleThree = "Hello World. "; child2.SimpleThree = "This is a test."; var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var layoutInfo = MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); SetupFakeRootSite(root); root.Builder.Show(Display.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55))); root.Layout(layoutInfo); // Two children produces two paragraphs. Assert.That(root.FirstBox, Is.AssignableTo(typeof(ParaBox))); Assert.That(root.FirstBox.Next, Is.AssignableTo(typeof(ParaBox))); Assert.That(root.FirstBox.Next.Next, Is.Null); }
public void BidiBacktracking() { var styles = new AssembledStyles(); var layoutInfo = MakeLayoutInfo(); var engine = new FakeRenderEngine() {Ws = 34, SegmentHeight = 13}; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); var engine2 = new FakeRenderEngine() {Ws = 35, SegmentHeight = 13}; factory.SetRenderer(35, engine2); engine2.RightToLeft = true; // This first test doesn't strictly require backtracking; it is an example of a similar // case where all the second client run fits, nothing of the following one fits, but // there is a satisfactory break at the end of the last thing that fit. // But, the space at the 'end' of "some mixed" will need to be moved to the end so it can 'disappear', // so it should become a separate segment. // That is, we should get something like // this is dexim emos_ // text // Where the underline stands for the space after "some mixed" which is moved to the end of the line. var root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing(Display.Of("this is ", 34), Display.Of("some mixed ", 35), Display.Of("text", 34))); int maxWidth = FakeRenderEngine.SimulatedWidth("this is some mixed t"); var layoutArgs = MakeLayoutInfo(maxWidth, m_gm.VwGraphics, factory); root.Layout(layoutArgs); var para = (ParaBox) root.FirstBox; var secondChild = (StringBox) para.FirstBox.Next; Assert.That(secondChild.Text, Is.EqualTo("some mixed")); var thirdChild = (StringBox)secondChild.Next; Assert.That(thirdChild.Text, Is.EqualTo(" ")); Assert.That(thirdChild.Top, Is.EqualTo(para.FirstBox.Top)); // True backtracking: the second client run fits entirely, but nothing of the following text. // We must go back and find the break in the middle of that second run. root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing(Display.Of("this is ", 34), Display.Of("some mixed", 35), Display.Of("text", 34))); maxWidth = FakeRenderEngine.SimulatedWidth("this is some mixedte"); layoutArgs = MakeLayoutInfo(maxWidth, m_gm.VwGraphics, factory); root.Layout(layoutArgs); para = (ParaBox)root.FirstBox; secondChild = (StringBox)para.FirstBox.Next; Assert.That(secondChild.Text, Is.EqualTo("some")); thirdChild = (StringBox)secondChild.Next; Assert.That(thirdChild.Text, Is.EqualTo(" ")); Assert.That(thirdChild.Top, Is.EqualTo(para.FirstBox.Top)); var fourthChild = (StringBox)thirdChild.Next; Assert.That(fourthChild.Text, Is.EqualTo("mixed")); Assert.That(fourthChild.Top, Is.GreaterThan(thirdChild.Top)); var fifthChild = (StringBox)fourthChild.Next; Assert.That(fifthChild.Text, Is.EqualTo("text")); Assert.That(fifthChild.Top, Is.EqualTo(fourthChild.Top)); }
public void RtlPara() { var styles = new AssembledStyles(); var layoutInfo = MakeLayoutInfo(); var root = new RootBoxFdo(styles); var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); engine.RightToLeft = true; factory.SetRenderer(34, engine); var engine2 = new FakeRenderEngine() { Ws = 35, SegmentHeight = 13 }; engine2.RightToLeft = true; factory.SetRenderer(35, engine); root.Builder.Show(Paragraph.Containing(Display.Of("this is ", 34), Display.Of("mixed ", 35), Display.Of("text", 34)).RightToLeft(true)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); var para = (ParaBox) root.FirstBox; int left1 = para.FirstBox.Left; int left2 = para.FirstBox.Next.Left; int left3 = para.FirstBox.Next.Next.Left; Assert.That(left3, Is.LessThan(left2)); Assert.That(left2, Is.LessThan(left1)); }
public void DragCopyRtf() { var stylesheet = new MockStylesheet(); var styleFirst = stylesheet.AddStyle("first", false); var styleSecond = stylesheet.AddStyle("second", false); var propsTrue = new MockStyleProp <bool>() { Value = true, ValueIsSet = true }; var charInfo = new MockCharStyleInfo(); styleFirst.DefaultCharacterStyleInfo = charInfo; charInfo.Bold = propsTrue; // Todo: make styleSecond have pretty much everything else. var charInfo2 = new MockCharStyleInfo(); styleSecond.DefaultCharacterStyleInfo = charInfo2; charInfo2.FontColor = MakeColorProp(Color.Red); charInfo2.BackColor = MakeColorProp(Color.Yellow); charInfo2.UnderlineColor = MakeColorProp(Color.Green); charInfo2.Italic = propsTrue; charInfo2.FontName = new MockStyleProp <string>() { Value = "Arial", ValueIsSet = true }; var styles = new AssembledStyles(stylesheet); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "This is"; var mock2 = new MockData1(23, 23); mock2.SimpleThree = " the day"; var mock3 = new MockData1(23, 23); mock3.SimpleThree = " that the"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); var wsf = new MockWsf(); engine.WritingSystemFactory = wsf; var wsEngine = wsf.MakeMockEngine(23, "en", engine); factory.SetRenderer(23, engine); root.Builder.Show( Paragraph.Containing( Display.Of(() => mock1.SimpleThree, 23).Style("first"), Display.Of(() => mock2.SimpleThree, 23).Style("second"), Display.Of(() => mock3.SimpleThree, 23).Style("first") )); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; SelectionBuilder.In(root).Offset("This ".Length).To.Offset("This is the day that".Length).Install(); int indent = FakeRenderEngine.SimulatedWidth("This "); root.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, indent + 5, 4, 0), Keys.None, m_gm.VwGraphics, ptrans); root.OnMouseMove(new MouseEventArgs(MouseButtons.Left, 1, indent + 5, 4, 0), Keys.None, m_gm.VwGraphics, ptrans); Assert.That(GetStringDropData(site), Is.EqualTo("is the day that")); // The order of the font and colors in the color table is arbitrary. This happens to be what the code does now. For some reason // Color.Green has green only 128. // The order of items in the definition of a style is arbitrary. // We're not doing anything yet for background color. \highlightN can specify background color for a character run, // but it can't be part of a style definition. Assert.That(GetRtfDropData(site), Is.EqualTo( RangeSelection.RtfPrefix + @"{\fonttbl{\f0 MockFont;}{\f1 Arial;}}" + @"{\colortbl ;\red0\green0\blue0;\red255\green255\blue255;\red255\green0\blue0;\red255\green255\blue0;\red0\green128\blue0;}" + @"{\stylesheet{\*\cs1\b\additive first;\*\cs2\i\f1\cf3\ulc5\additive second;}}" + RangeSelection.RtfDataPrefix + @"{\*\cs1\b is}{\*\cs2\i\f1\cf3\ulc5\highlight4 the day}{\*\cs1\b that\par}" + @"}")); // Todo: handle styles that depend on WS // Todo: handle more than two runs // Todo: handle runs where actual formatting differs from style-specified formatting // Todo: handle multiple paragraphs // Todo: handle paragraph styles }
public void BasicDragMove() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "This is the day"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Display.Of(() => mock1.SimpleThree, 23)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; SelectionBuilder.In(root).Offset("This ".Length).To.Offset("This is ".Length).Install(); var dataObj = new DataObject(DataFormats.StringFormat, "is "); int x = FakeRenderEngine.SimulatedWidth("This is the ") + 2; var location = new Point(x, 8); // A drag to where we can drop, allowing both copy and move, no keys held var dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.None, 200, 300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.Move)); Assert.That(root.DragState, Is.EqualTo(WindowDragState.DraggingHere)); var qcdArgs = new QueryContinueDragEventArgs((int)DragDropKeyStates.None, false, DragAction.Drop); root.OnQueryContinueDrag(qcdArgs); Assert.That(root.DragState, Is.EqualTo(WindowDragState.InternalMove)); root.OnDragLeave(); Assert.That(root.DragState, Is.EqualTo(WindowDragState.InternalMove), "DragLeave should not clear InternalMove"); root.OnDragDrop(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(mock1.SimpleThree, Is.EqualTo("This the is day")); Assert.That(root.DragState, Is.EqualTo(WindowDragState.None)); // Now let's drag the 'is' out to another window. SelectionBuilder.In(root).Offset("This the ".Length).To.Offset("This the is ".Length).Install(); qcdArgs = new QueryContinueDragEventArgs((int)DragDropKeyStates.None, false, DragAction.Drop); root.OnQueryContinueDrag(qcdArgs); Assert.That(root.DragState, Is.EqualTo(WindowDragState.None), "We should only set InternalMove if this window is the destination"); Assert.That(mock1.SimpleThree, Is.EqualTo("This the day")); // Check that we can't drag inside our own selection. SelectionBuilder.In(root).Offset("This ".Length).To.Offset("This the".Length).Install(); x = FakeRenderEngine.SimulatedWidth("This t") + 2; location = new Point(x, 8); dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.None, 200, 300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.DragState = WindowDragState.InternalMove; root.OnDragDrop(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.None)); Assert.That(mock1.SimpleThree, Is.EqualTo("This the day")); }
public void SubParaSequenceTest() { 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(); root.Site = site; PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Builder.Show(Paragraph.Containing(Display.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55)))); root.Layout(layoutInfo); VerifyParagraphs(root, new [] { "" }); //// Insert the first item into owner.ObjSeq1 and check all the right connections appear var child1 = new MockData1(55, 77); var child1String = "Hello world, this is a wide string"; child1.SimpleThree = child1String; owner.InsertIntoObjSeq1(0, child1); VerifyParagraphs(root, new string[] { child1String }); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); int invalidateWidth = FakeRenderEngine.SimulatedWidth(child1String) + 2 * RootBox.InvalidateMargin; var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); // Insert a second item and check again. var child2 = new MockData1(55, 77); child2.SimpleThree = "Another world"; invalidateWidth += FakeRenderEngine.SimulatedWidth("Another world"); site.RectsInvalidated.Clear(); owner.InsertIntoObjSeq1(1, child2); VerifyParagraphs(root, new string[] { child1String + "Another world" }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); var expectedInvalidate2 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); // Insert a third item between the first two. var child3 = new MockData1(55, 77); child3.SimpleThree = "Inserted world"; invalidateWidth += FakeRenderEngine.SimulatedWidth("Inserted world"); site.RectsInvalidated.Clear(); owner.InsertIntoObjSeq1(1, child3); VerifyParagraphs(root, new string[] { child1String + "Inserted world" + "Another world" }); var expectedInvalidate3 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); // Insert a fourth item at the start. var child4 = new MockData1(55, 77); child4.SimpleThree = "Beginning of world"; invalidateWidth += FakeRenderEngine.SimulatedWidth("Beginning of world"); site.RectsInvalidated.Clear(); owner.InsertIntoObjSeq1(0, child4); VerifyParagraphs(root, new string[] { "Beginning of world" + child1String + "Inserted world" + "Another world" }); var expectedInvalidate4 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4)); // Delete the first item. site.RectsInvalidated.Clear(); owner.RemoveAtObjSeq1(0); VerifyParagraphs(root, new string[] { child1String + "Inserted world" + "Another world" }); Assert.That(child4.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4)); // Delete a middle item. site.RectsInvalidated.Clear(); owner.RemoveAtObjSeq1(1); VerifyParagraphs(root, new string[] { child1String + "Another world" }); Assert.That(child3.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); // Delete the last item. site.RectsInvalidated.Clear(); owner.RemoveAtObjSeq1(1); VerifyParagraphs(root, new string[] { child1String }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); // Delete the only remaining item. site.RectsInvalidated.Clear(); owner.RemoveAtObjSeq1(0); VerifyParagraphs(root, new string[] { "" }); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); // Eventually add some operations that allow inserting and deleting multiple items. // We also need to be able to insert two or more object sequences into the same containing box. // That's probably another test, perhaps a view of the root where for each data item, // we insert its subitems, then for those insert paragraphs. // Let's assume there's always one top-level hookup for the root. }
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)); }
public void DragCopyRtf() { var stylesheet = new MockStylesheet(); var styleFirst = stylesheet.AddStyle("first", false); var styleSecond = stylesheet.AddStyle("second", false); var propsTrue = new MockStyleProp<bool>() {Value = true, ValueIsSet = true}; var charInfo = new MockCharStyleInfo(); styleFirst.DefaultCharacterStyleInfo = charInfo; charInfo.Bold = propsTrue; // Todo: make styleSecond have pretty much everything else. var charInfo2 = new MockCharStyleInfo(); styleSecond.DefaultCharacterStyleInfo = charInfo2; charInfo2.FontColor = MakeColorProp(Color.Red); charInfo2.BackColor = MakeColorProp(Color.Yellow); charInfo2.UnderlineColor = MakeColorProp(Color.Green); charInfo2.Italic = propsTrue; charInfo2.FontName = new MockStyleProp<string>() {Value = "Arial", ValueIsSet = true}; var styles = new AssembledStyles(stylesheet); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "This is"; var mock2 = new MockData1(23, 23); mock2.SimpleThree = " the day"; var mock3 = new MockData1(23, 23); mock3.SimpleThree = " that the"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); var wsf = new MockWsf(); engine.WritingSystemFactory = wsf; var wsEngine = wsf.MakeMockEngine(23, "en", engine); factory.SetRenderer(23, engine); root.Builder.Show( Paragraph.Containing( Display.Of(() => mock1.SimpleThree, 23).Style("first"), Display.Of(() => mock2.SimpleThree, 23).Style("second"), Display.Of(() => mock3.SimpleThree, 23).Style("first") )); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; SelectionBuilder.In(root).Offset("This ".Length).To.Offset("This is the day that".Length).Install(); int indent = FakeRenderEngine.SimulatedWidth("This "); root.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, indent + 5, 4, 0), Keys.None, m_gm.VwGraphics, ptrans); root.OnMouseMove(new MouseEventArgs(MouseButtons.Left, 1, indent + 5, 4, 0), Keys.None, m_gm.VwGraphics, ptrans); Assert.That(GetStringDropData(site), Is.EqualTo("is the day that")); // The order of the font and colors in the color table is arbitrary. This happens to be what the code does now. For some reason // Color.Green has green only 128. // The order of items in the definition of a style is arbitrary. // We're not doing anything yet for background color. \highlightN can specify background color for a character run, // but it can't be part of a style definition. Assert.That(GetRtfDropData(site), Is.EqualTo( RangeSelection.RtfPrefix + @"{\fonttbl{\f0 MockFont;}{\f1 Arial;}}" + @"{\colortbl ;\red0\green0\blue0;\red255\green255\blue255;\red255\green0\blue0;\red255\green255\blue0;\red0\green128\blue0;}" + @"{\stylesheet{\*\cs1\b\additive first;\*\cs2\i\f1\cf3\ulc5\additive second;}}" + RangeSelection.RtfDataPrefix + @"{\*\cs1\b is}{\*\cs2\i\f1\cf3\ulc5\highlight4 the day}{\*\cs1\b that\par}" + @"}")); // Todo: handle styles that depend on WS // Todo: handle more than two runs // Todo: handle runs where actual formatting differs from style-specified formatting // Todo: handle multiple paragraphs // Todo: handle paragraph styles }
public void ArrowKeys() { var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); var styles = new AssembledStyles().WithWs(34); var root = new RootBoxFdo(styles); var para1 = AddPara("This i~^s the~ day", styles, root); var para2 = AddPara("", styles, root); var para3 = AddPara(new string[] { "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lord", "has made" }, styles, root); var para4 = AddPara(new string[] { "we will", "", "rejoice" }, styles, root); int widthThisIsThe = FakeRenderEngine.SimulatedWidth("This is the"); var layoutArgs = MakeLayoutInfo(widthThisIsThe + 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); Assert.That(root.Height, Is.EqualTo(13 * 8), "A two-line and a one-line and a three-line and a two-line paragraph"); PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; // Simple left movement. var ipThisIsTheDay = para1.SelectAtEnd(); var ipThisIsTheDa = ipThisIsTheDay.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThisIsTheDa, para1, "This i~^s the~ da", false, "left from end"); var ipThisIsTheD = ipThisIsTheDa.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThisIsTheD, para1, "This i~^s the~ d", false, "left from no special plae"); // Left from one run into an adjacent non-empty run var ipThatTheLord2 = ((TextClientRun)para3.Source.ClientRuns[1]).SelectAt(para3, 0, false); var ipThatTheLor = ipThatTheLord2.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThatTheLor, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lor", false, "left from start run2"); // Left from one run into an adjacent empty run. Is this right or should we skip over it into // another run so we actually move a character? var ipStartOfRejoice = ((TextClientRun)para4.Source.ClientRuns[2]).SelectAt(para4, 0, false); var ipEmptyPara4 = ipStartOfRejoice.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipEmptyPara4, para4, "we will", false, "left from start run into empty"); // Out of the empty run into the previous one. var ipWeWil = ipEmptyPara4.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipWeWil, para4, "we wil", false, "left from empty run"); // back from one para into another. var ipPara2 = para2.SelectAtStart(); var ipEndPara1 = ipPara2.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipEndPara1, para1, "This i~^s the~ day", true, "left from one para to another"); // back at the very start. var ipStart = para1.SelectAtStart(); Assert.That(ipStart.MoveByKey(new KeyEventArgs(Keys.Left)), Is.Null); // back after a surrogate pair should not stop in the middle. var ipThatSurrogate = ((TextClientRun)para3.Source.ClientRuns[0]).SelectAt(para3, ("that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE).Length, false); var ipThat = ipThatSurrogate.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThat, para3, "that", false, "left over surrogate pair"); // Back to a place between diacritic and base should not stop. var ipThisI_Diacritics = ((TextClientRun)para1.Source.ClientRuns[0]).SelectAt(para1, "This i~^".Length, false); var ipThisSpace = ipThisI_Diacritics.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThisSpace, para1, "This ", false, "left over diacritics"); // We can use many of the same ones to check right movement. var ipThisIsTheDa_r = ipThisIsTheD.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipThisIsTheDa_r, para1, "This i~^s the~ da", true, "simple right"); // Move right into an empty paragraph. // Review JohnT: should this IP in an empty para associate forward or back? var ipStartP2 = ipThisIsTheDay.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipStartP2, para2, "", false, "right into empty para"); // Should definitely associate with the character following, not the nonexistent preceding one. var ipStartP3 = ipStartP2.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipStartP3, para3, "", false, "right to start of non-empty para"); var ipP3_t = ipStartP3.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipP3_t, para3, "t", true, "simple right"); var ipThatSurrogate2 = ipThat.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipThatSurrogate2, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE, true, "right over surrogate pair"); var ipThatTheLord_left = ((TextClientRun)para3.Source.ClientRuns[0]).SelectAt(para3, ("that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lord").Length, true); var ipThatTheLord_space = ipThatTheLord_left.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipThatTheLord_space, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lord ", true, "right from end run1"); var ipEnd = para4.SelectAtEnd(); Assert.That(ipEnd.MoveByKey(new KeyEventArgs(Keys.Right)), Is.Null); // Also can't make range by moving right from end. Assert.That(ipEnd.MoveByKey(new KeyEventArgs(Keys.Right | Keys.Shift)), Is.Null); var rangeThatSurrogate2 = ipThat.MoveByKey(new KeyEventArgs(Keys.Right | Keys.Shift)); VerifyRange(rangeThatSurrogate2, para3, "that", para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE, "shift-right over surrogate pair"); var rangeThatTha = ipThat.MoveByKey(new KeyEventArgs(Keys.Left | Keys.Shift)); VerifyRange(rangeThatTha, para3, "that", para3, "tha", "shift-left end before anchor"); // left from a range puts us at the start of the range var ipThat2 = rangeThatSurrogate2.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThat2, para3, "that", false, "left from range to IP"); // right from a range puts us at the end of the range var ipThatSurrrogate2 = rangeThatSurrogate2.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipThatSurrrogate2, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE, true, "right from range"); // shift-left from a 1-char range collapses it to an IP var ipThat3 = rangeThatSurrogate2.MoveByKey(new KeyEventArgs(Keys.Left | Keys.Shift)); VerifyIp(ipThat3, para3, "that", false, "left over surrogate pair"); // shift-right from a range makes one with the same anchor but an extended end var rangeThatSurrogateSpace = rangeThatSurrogate2.MoveByKey(new KeyEventArgs(Keys.Right | Keys.Shift)); VerifyRange(rangeThatSurrogateSpace, para3, "that", para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " ", "shift-right from range"); // shift-right from a range that can't grow returns null. var ipWeWillRejoic = (InsertionPoint)ipEnd.MoveByKey(new KeyEventArgs(Keys.Left)); var range1AtEnd = new RangeSelection(ipWeWillRejoic, ipEnd); Assert.That(range1AtEnd.MoveByKey(new KeyEventArgs(Keys.Right | Keys.Shift)), Is.Null); // Home key. var ipStartP2_2 = ipThat.MoveByKey(new KeyEventArgs(Keys.Home)); VerifyIp(ipStartP2_2, para3, "", false, "home in Para 3"); var ipStart_2 = ipThat.MoveByKey(new KeyEventArgs(Keys.Home | Keys.Control)); VerifyIp(ipStart_2, para1, "", false, "ctrl-home in Para 3"); // End key. var ipEndP2 = ipThat.MoveByKey(new KeyEventArgs(Keys.End)); VerifyIp(ipEndP2, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lord" + "has made", true, "end in Para 3"); var ipEnd_2 = ipThat.MoveByKey(new KeyEventArgs(Keys.End | Keys.Control)); VerifyIp(ipEnd_2, para4, "we will" + "rejoice", true, "ctrl-end in Para 3"); // Down key var ipThisIsThe_R = ipStart.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipThisIsThe_R, para1, "This i~^s the~ ", false, "down from start line 1"); var ipTh = para1.SelectAt(2, true); var ipThisIsTheDa2 = ipTh.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipThisIsTheDa2, para1, "This i~^s the~ da", false, "down from 2 chars into line 1"); var ipThisIdTh = para1.SelectAt("This i~^s th".Length, false); var ipThisIsTheDay2 = ipThisIdTh.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipThisIsTheDay2, para1, "This i~^s the~ day", true, "down from near end line 1"); // Empty para: arbitrary which way it associates. var ipPara2Down = ipThisIsTheDay2.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipPara2Down, para2, "", true, "down twice from near end line 1"); // Going on down, we should remember the starting X position and end up about that // far into the next full-length line. The 'i' characters in the first line make it // a bit iffy; might be closer to the start of the 'e' at the end of 'the'. // The other complication is that our fake render engine is not smart about surrogate pairs, // and treats the musical semibrevis as two ordinary characters. // Omitting the diacritics in the first paragraph, our selection starts 10 characters in. // The result string here is 9 characters, since with no narrow letters on this para, // we end up closer to the left of the 'e'. var ipThatTheSpaceDown = ipPara2Down.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipThatTheSpaceDown, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " th", false, "down 3x from near end line 1"); Assert.That(ipEnd.MoveByKey(new KeyEventArgs(Keys.Down)), Is.Null); var ipPara2Up = ipThatTheSpaceDown.MoveByKey(new KeyEventArgs(Keys.Up)); VerifyIp(ipPara2Up, para2, "", true, "back up aligned with near end line 1"); var ipThisIsTheDayUp = ipPara2Up.MoveByKey(new KeyEventArgs(Keys.Up)); VerifyIp(ipThisIsTheDayUp, para1, "This i~^s the~ day", true, "up from para2 aligned near end line 1"); // It's going to be looking for a position right at the boundary...either assocPrev would be // reasonable. var ipThisIdTh2Up = ipThisIsTheDayUp.MoveByKey(new KeyEventArgs(Keys.Up)); VerifyIp(ipThisIdTh2Up, para1, "This i~^s th", false, "up from end para 1 aligned near end line 1"); //var ipPara2_2 = ipThisIsTheDay.MoveByKey(new KeyEventArgs(Keys.Down)); //VerifyIp(ipPara2_2, para2, "", true, "down from end para 1"); // Todo: // HandleSpecialKey is called from OnKeyDown and should handle at least these: //case Keys.PageUp: //case Keys.PageDown: //case Keys.End: //case Keys.Home: //case Keys.Left: // done //case Keys.Up: //case Keys.Right: // done //case Keys.Down: //case Keys.F7: // the only two function keys currently known to the Views code, //case Keys.F8: // used for left and right arrow by string character amounts. // Test Left: (done) // - char to char in same line // - skipping diacritics // - skipping surrogate pairs // - to another line in same paragraph // - to previous (empty?) paragraph // - at very start (nothing happens) // - range collapses to start // - anything special to test if there are multiple runs? e.g., at boundary // - skip over embedded pictures // - eventually drop into embedded boxes that contain text? // Similarly right (done) // Down: // - same para, there is text below // - same para, no text immediately below on same line (goes to end of previous line) // - eventually: what should happen if logical and physical end of next line don't coincide? // - down again to a longer line: should stay aligned with start position (what resets this??) // etc for others. }
private void BodyofUserPromptTest(MockData1 data1, Flow promptField, Func <string> reader) { var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); factory.SetRenderer(35, engine); factory.SetRenderer(0, engine); // for literals var styles = new AssembledStyles().WithWs(34); var root = new RootBoxFdo(styles); root.Builder.Show( Paragraph.Containing( Display.Of("lead in ", 34), promptField, Display.Of("trailing", 34) ) ); var para = (ParaBox)root.FirstBox; Assert.That(para.Source.RenderText, Is.EqualTo("lead in type here trailing")); int width = FakeRenderEngine.SimulatedWidth("lead in type her"); // should make it take 2 lines and split prompt. var layoutArgs = MakeLayoutInfo(width, m_gm.VwGraphics, factory); root.Layout(layoutArgs); MockSite site = new MockSite(); PaintTransform ptrans = new PaintTransform(2, 4, 96, 96, 0, 10, 96, 96); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; // Click on "type here" produces an IP in the empty string. int leadWidth = FakeRenderEngine.SimulatedWidth("lead in "); var mouseArgs = new MouseEventArgs(MouseButtons.Left, 1, 2 + leadWidth + 3, 0, 0); root.OnMouseDown(mouseArgs, Keys.None, m_gm.VwGraphics, ptrans); var ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in ".Length)); Assert.That(ip.StringPosition, Is.EqualTo(0)); // IP is drawn as range covering "type here" ip.Draw(m_gm.VwGraphics, ptrans); var first = (StringBox)para.FirstBox; VerifyRangeSegmentDrawing(para, first, (FakeSegment)first.Segment, "lead in ".Length, "lead in type here ".Length, -4, 4 - 10, 4 - 10 + 13); var second = (StringBox)first.Next; VerifyRangeSegmentDrawing(para, second, (FakeSegment)second.Segment, "lead in ".Length, "lead in type here ".Length, -4 - 13, 4 - 10 + 13, 4 - 10 + 13 * 2); // Check that we get a sensible answer for the selection's containing rectangle. ((FakeSegment)first.Segment).LeftPositionOfRangeResult = 17; ((FakeSegment)first.Segment).RightPositionOfRangeResult = 29; ((FakeSegment)second.Segment).LeftPositionOfRangeResult = 5; ((FakeSegment)second.Segment).RightPositionOfRangeResult = 13; var rect = ip.GetSelectionLocation(m_gm.VwGraphics, ptrans); Assert.That(rect.Top, Is.EqualTo(4 - 10)); Assert.That(rect.Bottom, Is.EqualTo(4 - 10 + 13 * 2)); Assert.That(rect.Left, Is.EqualTo(5)); Assert.That(rect.Right, Is.EqualTo(29)); VerifyRangeSegmentQuery(para, first, (FakeSegment)first.Segment, "lead in ".Length, "lead in type here ".Length, -4, 4 - 10, 4 - 10 + 13); VerifyRangeSegmentQuery(para, second, (FakeSegment)second.Segment, "lead in ".Length, "lead in type here ".Length, -4 - 13, 4 - 10 + 13, 4 - 10 + 13 * 2); Assert.That(second.IchMin, Is.EqualTo("lead in type ".Length)); // When the IP is drawn like this, it doesn't flash! site.RectsInvalidatedInRoot.Clear(); site.RectsInvalidated.Clear(); root.FlashInsertionPoint(); // Call twice just in case somehow only some invalidates worked. root.FlashInsertionPoint(); Assert.That(site.RectsInvalidated, Is.Empty); Assert.That(site.RectsInvalidatedInRoot, Is.Empty); // Typing something else makes "type here" go away and produces a normal IP after it. ip.InsertText("x"); Assert.That(reader(), Is.EqualTo("x")); Assert.That(para.Source.RenderText, Is.EqualTo("lead in xtrailing")); ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.ShouldFlash, Is.True); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in x".Length)); // Deleting back to empty string makes "type here" reappear. ip.Backspace(); Assert.That(reader(), Is.EqualTo("")); Assert.That(para.Source.RenderText, Is.EqualTo("lead in type here trailing")); ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.ShouldFlash, Is.False); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in ".Length)); Assert.That(ip.LastRenderParaPosition, Is.EqualTo("lead in type here ".Length)); second = (StringBox)para.FirstBox.Next; Assert.That(second.IchMin, Is.EqualTo("lead in type ".Length)); // Click after "type here" produces an IP at the right place in the following string. // We've arranged for the prompt to be split, so this is after the word 'here' on the second line. int hereTWidth = FakeRenderEngine.SimulatedWidth("here t"); mouseArgs = new MouseEventArgs(MouseButtons.Left, 1, 2 + hereTWidth - 1, 4 - 10 + 13 + 2, 0); root.OnMouseDown(mouseArgs, Keys.None, m_gm.VwGraphics, ptrans); ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in t".Length)); Assert.That(ip.AssociatePrevious, Is.True); Assert.That(ip.StringPosition, Is.EqualTo(1)); Assert.That(ip.RenderParaPosition, Is.EqualTo("lead in type here t".Length)); // Also try a click in the second-line part of the prompt. int herWidth = FakeRenderEngine.SimulatedWidth("her"); mouseArgs = new MouseEventArgs(MouseButtons.Left, 1, 2 + herWidth - 1, 4 - 10 + 13 + 2, 0); root.OnMouseDown(mouseArgs, Keys.None, m_gm.VwGraphics, ptrans); ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in ".Length)); Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.RenderParaPosition, Is.EqualTo("lead in ".Length)); Assert.That(ip.LastRenderParaPosition, Is.EqualTo("lead in type here ".Length)); }
public void InsertGrowsPara() { string contents = "This is the day."; var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); var styles = new AssembledStyles().WithWs(34); var clientRuns = new List <IClientRun>(); var run = new StringClientRun(contents, styles); clientRuns.Add(run); var data1 = new MockData1(34, 35); data1.SimpleThree = contents; var source = new TextSource(clientRuns, null); var para = new ParaBox(styles, source); var hookup = new StringHookup(this, () => data1.SimpleThree, hook => data1.SimpleThreeChanged += hook.StringPropChanged, hook => data1.SimpleThreeChanged -= hook.StringPropChanged, para); hookup.Writer = newVal => data1.SimpleThree = newVal; run.Hookup = hookup; var extraBox = new BlockBox(styles, Color.Red, 50, 72000); var root = new RootBoxFdo(styles); root.SizeChanged += root_SizeChanged; root.AddBox(para); root.AddBox(extraBox); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); Assert.IsTrue(m_sizeChangedCalled); Assert.That(root.Height, Is.EqualTo(13 + 96)); Assert.That(root.Width, Is.EqualTo(FakeRenderEngine.SimulatedWidth(contents))); int widthThisIsThe = FakeRenderEngine.SimulatedWidth("This is the"); layoutArgs = MakeLayoutInfo(widthThisIsThe + 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); Assert.That(root.Height, Is.EqualTo(26 + 96), "two line para is twice the height"); Assert.That(root.Width, Is.EqualTo(widthThisIsThe + 2), "two-line para occupies full available width"); Assert.That(extraBox.Top, Is.EqualTo(26)); PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; m_sizeChangedCalled = false; var ip = para.SelectAtEnd(); ip.InsertText(" We will be"); Assert.That(para.Height, Is.EqualTo(39), "inserted text makes para a line higher"); Assert.That(root.Height, Is.EqualTo(39 + 96), "root grows when para does"); Assert.That(root.Width, Is.EqualTo(widthThisIsThe + 2), "three-line para occupies full available width"); Assert.That(extraBox.Top, Is.EqualTo(39)); Assert.IsTrue(m_sizeChangedCalled); }
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)); }
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)); }
public void BackgroundAndUnderlinePainting() { var styles = new AssembledStyles(); RootBox root = new RootBoxFdo(styles); SetupFakeRootSite(root); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); root.RendererFactory = layoutInfo.RendererFactory; root.Builder.Show( Paragraph.Containing( Display.Of("plain"), Display.Of("underOnYellow").Underline(FwUnderlineType.kuntSingle).BackColor(Color.Yellow) ), Paragraph.Containing( Display.Of("doubleRedOnPink").Underline(FwUnderlineType.kuntDouble, Color.Red).BackColor(Color.Pink), Display.Of("dotted").Underline(FwUnderlineType.kuntDotted), Display.Of("dottedOnYellow").Underline(FwUnderlineType.kuntDotted).BackColor(Color.Yellow) ), Paragraph.Containing( Display.Of("dashed").Underline(FwUnderlineType.kuntDashed), Display.Of("dashedRed").Underline(FwUnderlineType.kuntDashed).ForeColor(Color.Red), Display.Of("squiggle").Underline(FwUnderlineType.kuntSquiggle) ) ); root.Layout(layoutInfo); var para1 = (ParaBox)root.FirstBox; var stringBox1 = (StringBox)para1.FirstBox; var seg1 = (FakeSegment)stringBox1.Segment; // A convenient place to test that literals get Chrp with default user ws. LgCharRenderProps chrp; int ichMin, ichLim; para1.Source.GetCharProps(0, out chrp, out ichMin, out ichLim); Assert.That(chrp.ws, Is.EqualTo(layoutInfo.RendererFactory.UserWs)); // This segment has just one chunk of underline, the second run. seg1.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() {Lefts = new[] {10}, Rights = new[] {20}, Tops = new[] {15}}); // Todo: add expectations for each run of underlining. var para2 = (ParaBox)para1.Next; var stringBox2 = (StringBox)para2.FirstBox; var seg2 = (FakeSegment)stringBox2.Segment; // For the double red underline, we'll pretend there are two line segments. seg2.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 5, 15 }, Rights = new[] { 10, 20 }, Tops = new[] { 15, 16 } }); seg2.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 12 }, Rights = new[] { 22 }, Tops = new[] { 13 } }); seg2.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 30 }, Rights = new[] { 41 }, Tops = new[] { 12 } }); var para3 = (ParaBox)para2.Next; var stringBox3 = (StringBox)para3.FirstBox; var seg3 = (FakeSegment)stringBox3.Segment; seg3.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 0 }, Rights = new[] { 10 }, Tops = new[] { 11 } }); seg3.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 10 }, Rights = new[] { 20 }, Tops = new[] { 11 } }); seg3.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 30 }, Rights = new[] { 40 }, Tops = new[] { 11 } }); // We want to keep track of the sequence of paint operations in all three segments. var drawActions = new List<object>(); seg1.DrawActions = seg2.DrawActions = seg3.DrawActions = drawActions; var vg = new MockGraphics(); vg.DrawActions = drawActions; var site = (MockSite)root.Site; root.Paint(vg, site.m_transform); var paintTrans = site.m_transform; // We should have asked about each run of distinct underlining. VerifyCharPlacementCall(seg1, 0, "plain".Length, "plain".Length + "underOnYellow".Length, paintTrans.SourceRect, paintTrans.DestRect, vg, 1); // para/seg 2 Rect srcRect = paintTrans.SourceRect; srcRect.top -= para1.Height; srcRect.bottom -= para1.Height; VerifyCharPlacementCall(seg2, 0, 0, "doubleRedOnPink".Length, srcRect, paintTrans.DestRect, vg, 2); VerifyCharPlacementCall(seg2, 0, "doubleRedOnPink".Length, "doubleRedOnPink".Length + "dotted".Length, srcRect, paintTrans.DestRect, vg, 1); VerifyCharPlacementCall(seg2, 0, "doubleRedOnPink".Length + "dotted".Length, "doubleRedOnPink".Length + "dotted".Length + "dottedOnYellow".Length, srcRect, paintTrans.DestRect, vg, 1); // para/seg 3 srcRect.top -= para2.Height; srcRect.bottom -= para2.Height; VerifyCharPlacementCall(seg3, 0, 0, "dashed".Length, srcRect, paintTrans.DestRect, vg, 1); VerifyCharPlacementCall(seg3, 0, "dashed".Length, "dashed".Length + "dashedRed".Length, srcRect, paintTrans.DestRect, vg, 1); VerifyCharPlacementCall(seg3, 0, "dashed".Length + "dashedRed".Length, "dashed".Length + "dashedRed".Length + "squiggle".Length, srcRect, paintTrans.DestRect, vg, 1); // Todo: eventually arrange a run where ichBase is non-zero, and check what happens. // We want to check a lot of things about the drawing. // - all the background color and underline drawing should happen before any of the text drawing. // - the right stuff should be drawn to construct the underlines // - in particular dotted, dashed, and squiggle underlines should have the gaps and alternations // aligned, even if in different segments. // Todo: check actual line segs drawn int position = 0; // in drawActions // Normal calls to Draw have the effect of painting the background. All three backgrounds // should be painted before the foreground. First we paint seg1 with background, since it has some. VerifyDraw(drawActions, ref position, seg1); // Next we draw its one horizontal line of underline. VerifyHorzLine(drawActions, ref position, 10, 20, 15, 1, new int[] {int.MaxValue}, 10); // Then segment 2's background VerifyDraw(drawActions, ref position, seg2); // And various lots of underline: double takes 2 per segment VerifyHorzLine(drawActions, ref position, 5, 10, 17, 1, new int[] { int.MaxValue }, 5); VerifyHorzLine(drawActions, ref position, 5, 10, 15, 1, new int[] { int.MaxValue }, 5); VerifyHorzLine(drawActions, ref position, 15, 20, 18, 1, new int[] { int.MaxValue }, 15); VerifyHorzLine(drawActions, ref position, 15, 20, 16, 1, new int[] { int.MaxValue }, 15); // dotted has non-trivial array of dx values (2 pix each) VerifyHorzLine(drawActions, ref position, 12, 22, 13, 1, new int[] { 2, 2 }, 12); // dotted has non-trivial array of dx values (2 pix each) VerifyHorzLine(drawActions, ref position, 30, 41, 12, 1, new int[] { 2, 2 }, 30); // No background in para 3, doesn't get a draw background call. //VerifyDraw(drawActions, ref position, seg3); // But underlines still drawn in the background phase // Dashed line VerifyHorzLine(drawActions, ref position, 0, 10, 11, 1, new int[] { 6, 3 }, 0); VerifyHorzLine(drawActions, ref position, 10, 20, 11, 1, new int[] { 6, 3 }, 10); // Todo: verify line segs drawn for squiggle. VerifyLine(drawActions, ref position, 30, 12, 32, 10, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyLine(drawActions, ref position, 32, 10, 34, 12, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyLine(drawActions, ref position, 34, 12, 36, 10, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyLine(drawActions, ref position, 36, 10, 38, 12, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyLine(drawActions, ref position, 38, 12, 40, 10, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyNbDraw(drawActions, ref position, seg1); VerifyNbDraw(drawActions, ref position, seg2); VerifyDraw(drawActions, ref position, seg3); }
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] }); }
public void MlsDelete() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles.WithWs(23)); var mock1 = new MockData1(23, 23); mock1.MlSimpleOne = new MultiAccessor(23, 23); mock1.MlSimpleOne.set_String(23, TsStrFactoryClass.Create().MakeString("This is it", 23)); var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Display.Of(() => mock1.MlSimpleOne, 23)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; SelectionBuilder.In(root).Offset("This ".Length).To.Offset("This is ".Length).Install(); var sel = root.Selection as RangeSelection; // This is currently the main test for SelectionBuilder.In(RootBox) and SelectionBuilder.To in TsStrings // This verifies that it makes roughly the right range selection. Assert.That(sel.Anchor.LogicalParaPosition, Is.EqualTo("This ".Length)); Assert.That(sel.DragEnd.LogicalParaPosition, Is.EqualTo("This is ".Length)); Assert.That(sel.CanDelete(), Is.True); root.OnDelete(); ITsString i = mock1.MlSimpleOne.get_String(23); Assert.That(mock1.MlSimpleOne.get_String(23).Text, Is.EqualTo("This it")); var ip = root.Selection as InsertionPoint; Assert.That(ip.LogicalParaPosition, Is.EqualTo("This ".Length)); // Enhance JohnT: if there is any reason to prefer associatePrevious to be true or false, // clamp that and make it so. // A fairly rudimentary check on invalidate, since we elsewhere check general string-edit ops. Assert.That(site.RectsInvalidatedInRoot, Is.Not.Empty); }
public void BasicDrop() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "old contents"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Display.Of(() => mock1.SimpleThree, 23)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; var dataObj = new DataObject(DataFormats.StringFormat, "new "); int x = FakeRenderEngine.SimulatedWidth("old ") + 2; var location = new Point(x, 8); // A drag to where we can drop, allowing both copy and move, no keys held var dragArgs = new DragEventArgs(dataObj, (int) DragDropKeyStates.None, 200,300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.Move)); Assert.That(root.DragState, Is.EqualTo(WindowDragState.DraggingHere)); root.OnDragLeave(); Assert.That(root.DragState, Is.EqualTo(WindowDragState.None)); // Though other factors would favor move, only copy is allowed here. dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.None, 200,300, DragDropEffects.Copy, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.Copy)); // Though otherwise we could copy, there is no text data in the data object. dragArgs = new DragEventArgs(new DataObject(), (int)DragDropKeyStates.None, 200,300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.None)); dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.ControlKey, 200,300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.Copy)); root.OnDragDrop(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(mock1.SimpleThree, Is.EqualTo("old new contents")); }
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(); root.Site = site; PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Builder.Show(Display.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55))); root.Layout(layoutInfo); VerifyParagraphs(root, new string[0]); //var seqHookup = new IndependentSequenceHookup<MockData1>(owner); // Tell seqHookup how to make a display of one MockData1: // - make a paragraph // - make it display the SimpleDataThree property of the item (editable) // - Make an item hookup that knows about the paragraph and the StringHookup for SimpleDataThree // - that hookup should be connected to the Item Hookup // Tell seqHookup how it relates to the root box // - Somehow the item hookup for each item gets inserted into the right place in seqHookup's children, // - and the paragraph for the item gets inserted into the right place in the rootbox. // Tell seqHookup to get its items from ObjSeq1 and to listen for ObjSeq1Changed. // (Eventually I'd like to be able to do all the above something like this: // root.Builder.AddObjSeq(()=>owner.ObjSeq1, (md, bldr)=>bldr.AddString(()=>md.SimpleThree); // Insert the first item into owner.ObjSeq1 and check all the right connections appear var child1 = new MockData1(55, 77); var child1String = "Hello world, this is a wide string"; child1.SimpleThree = child1String; owner.InsertIntoObjSeq1(0, child1); // The first string we insert is deliberately the widest. After that, the width of the pile // remains constant, allowing us to test smarter, smaller invalidate rectangles; when the width // changes we invalidate the whole pile. VerifyParagraphs(root, new string[] { child1String }); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); int invalidateWidth = FakeRenderEngine.SimulatedWidth(child1String) + 2 * RootBox.InvalidateMargin; var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); // Insert a second item and check again. var child2 = new MockData1(55, 77); child2.SimpleThree = "Another world"; site.RectsInvalidated.Clear(); owner.InsertIntoObjSeq1(1, child2); VerifyParagraphs(root, new string[] { child1String, "Another world" }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); var expectedInvalidate2 = new Rectangle(-RootBox.InvalidateMargin, engine.SegmentHeight - RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); // Insert a third item between the first two. var child3 = new MockData1(55, 77); child3.SimpleThree = "Inserted world"; owner.InsertIntoObjSeq1(1, child3); VerifyParagraphs(root, new string[] { child1String, "Inserted world", "Another world" }); var expectedInvalidate3 = new Rectangle(-RootBox.InvalidateMargin, engine.SegmentHeight - RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight * 2 + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); // Insert a fourth item at the start. var child4 = new MockData1(55, 77); child4.SimpleThree = "Beginning of world"; owner.InsertIntoObjSeq1(0, child4); VerifyParagraphs(root, new string[] { "Beginning of world", child1String, "Inserted world", "Another world" }); var expectedInvalidate4 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight * 4 + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4)); // Delete the first item. //var topHookup = root.RootHookup as IndependentSequenceHookup<MockData1>; //Assert.That(topHookup, Is.Not.Null); owner.RemoveAtObjSeq1(0); VerifyParagraphs(root, new string[] { child1String, "Inserted world", "Another world" }); Assert.That(child4.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4)); // Delete a middle item. owner.RemoveAtObjSeq1(1); VerifyParagraphs(root, new string[] { child1String, "Another world" }); Assert.That(child3.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); // Delete the last item. owner.RemoveAtObjSeq1(1); VerifyParagraphs(root, new string[] { child1String }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); // Delete the only remaining item. owner.RemoveAtObjSeq1(0); VerifyParagraphs(root, new string[0]); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); // Eventually add some operations that allow inserting and deleting multiple items. // We also need to be able to insert two or more object sequences into the same containing box. // That's probably another test, perhaps a view of the root where for each data item, // we insert its subitems, then for those insert paragraphs. // Let's assume there's always one top-level hookup for the root. }
public void ParaObjTest() { var owner = new MockData1(55, 77); var child1 = new MockData1(55, 77); var child1String = "Hello world, this is a wide string"; child1.SimpleThree = child1String; owner.SimpleFour = child1; 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(); root.Site = site; PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Builder.Show(Display.OfObj(() => owner.SimpleFour).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55))); root.Layout(layoutInfo); VerifyParagraphs(root, new[] { child1String }); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); int invalidateWidth = FakeRenderEngine.SimulatedWidth(child1String) + 2 * RootBox.InvalidateMargin; // Change item and check side effects. var child2 = new MockData1(55, 77); child2.SimpleThree = "Another world"; site.RectsInvalidated.Clear(); owner.SimpleFour = child2; VerifyParagraphs(root, new string[] { "Another world" }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); }
public void DiffWS() { var tsf = TsStrFactoryClass.Create(); var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleTwo = tsf.MakeString("newoldcontents", 23); var bldr = mock1.SimpleTwo.GetBldr(); bldr.SetIntPropValues(3, 6, (int)FwTextPropType.ktptWs, (int)FwTextPropVar.ktpvDefault, 24); bldr.SetIntPropValues(6, 14, (int)FwTextPropType.ktptWs, (int)FwTextPropVar.ktpvDefault, 25); mock1.SimpleTwo = bldr.GetString(); var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Display.Of(() => mock1.SimpleTwo)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; int x = FakeRenderEngine.SimulatedWidth("ne") + 2; var location = new Point(x, 8); EventArgs e = new EventArgs(); MouseEventArgs m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo("new")); x = FakeRenderEngine.SimulatedWidth("new") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo("old")); x = FakeRenderEngine.SimulatedWidth("newold") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo("contents")); x = FakeRenderEngine.SimulatedWidth("newold"); location = new Point(x, 8); // at the right edge of the d at the end of newold m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo("old")); }
public void CombinedParaOperations() { // Set up a simple multi-para layout using the ObjSeq2 and SimpleThree properties of MockData1 AssembledStyles styles = new AssembledStyles(); RootBox root = new RootBoxFdo(styles); var owner = new MockData1(); var p1 = new MockData1(10, 11); var p2 = new MockData1(10, 11); p1.SimpleThree = "Hello World"; p2.SimpleThree = "This is a test"; owner.ObjSeq2.Add(p1); owner.ObjSeq2.Add(p2); var po = new MockReceiveParagraphOperations(); root.Builder.Show(Display.Of(() => owner.ObjSeq2) .Using((builder, para) => builder.AddString(() => para.SimpleThree, 10)) .EditParagraphsUsing(po)); SetupFakeRootSite(root); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); root.Layout(layoutInfo); var ip = root.FirstBox.SelectAtEnd(); // Insert at end of paragraph. ip.InsertLineBreak(); Assert.That(owner.ObjSeq2.Count, Is.EqualTo(3)); Assert.That(owner.ObjSeq2[0], Is.EqualTo(p1)); Assert.That(owner.ObjSeq2[2], Is.EqualTo(p2)); var p3 = owner.ObjSeq2[1]; // empty inserted paragraph. ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(root.FirstBox.Next.Next, Is.Not.Null); Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next)); // Insert at start of paragraph. To make it unambiguously the start, give p3 some data, // and since that might eventually destroy the selection, reset it. var rootHookup = ip.Hookup.ParentHookup.ParentHookup; // StringHookup, ItemHookup, Sequence p3.SimpleThree = "First insert"; ip = (InsertionPoint)SelectionBuilder.In(rootHookup)[1].Offset(0).Install(); ip.InsertLineBreak(); Assert.That(owner.ObjSeq2.Count, Is.EqualTo(4)); Assert.That(owner.ObjSeq2[0], Is.EqualTo(p1)); Assert.That(owner.ObjSeq2[2], Is.EqualTo(p3)); Assert.That(owner.ObjSeq2[3], Is.EqualTo(p2)); var p4 = owner.ObjSeq2[1]; // empty inserted paragraph (before p3). Assert.That(root.FirstBox.Next.Next.Next, Is.Not.Null); ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next)); // Split a paragraph. var oldObjects = owner.ObjSeq2.ToArray(); ip = (InsertionPoint)SelectionBuilder.In(rootHookup)[2].Offset("First ".Length).Install(); ip.InsertLineBreak(); Assert.That(owner.ObjSeq2.Count, Is.EqualTo(5)); Assert.That(owner.ObjSeq2[1], Is.EqualTo(oldObjects[1])); Assert.That(owner.ObjSeq2[2], Is.EqualTo(oldObjects[2])); Assert.That(owner.ObjSeq2[4], Is.EqualTo(oldObjects[3])); // insert between 2 and 3 var p5 = owner.ObjSeq2[3]; // inserted paragraph. ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next.Next)); Assert.That(owner.ObjSeq2[2].SimpleThree, Is.EqualTo("First ")); Assert.That(owner.ObjSeq2[3].SimpleThree, Is.EqualTo("insert")); // Combine two paragraphs by backspace at start of line. oldObjects = owner.ObjSeq2.ToArray(); ip.Backspace(); Assert.That(owner.ObjSeq2.Count, Is.EqualTo(4)); Assert.That(owner.ObjSeq2[1], Is.EqualTo(oldObjects[1])); Assert.That(owner.ObjSeq2[2], Is.EqualTo(oldObjects[2])); Assert.That(owner.ObjSeq2[3], Is.EqualTo(oldObjects[4])); // delete item 3 ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo("First ".Length)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next)); Assert.That(owner.ObjSeq2[2].SimpleThree, Is.EqualTo("First insert")); }
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(); root.Site = site; PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Builder.Show(Display.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55))); root.Layout(layoutInfo); VerifyParagraphs(root, new string[0]); //var seqHookup = new IndependentSequenceHookup<MockData1>(owner); // Tell seqHookup how to make a display of one MockData1: // - make a paragraph // - make it display the SimpleDataThree property of the item (editable) // - Make an item hookup that knows about the paragraph and the StringHookup for SimpleDataThree // - that hookup should be connected to the Item Hookup // Tell seqHookup how it relates to the root box // - Somehow the item hookup for each item gets inserted into the right place in seqHookup's children, // - and the paragraph for the item gets inserted into the right place in the rootbox. // Tell seqHookup to get its items from ObjSeq1 and to listen for ObjSeq1Changed. // (Eventually I'd like to be able to do all the above something like this: // root.Builder.AddObjSeq(()=>owner.ObjSeq1, (md, bldr)=>bldr.AddString(()=>md.SimpleThree); // Insert the first item into owner.ObjSeq1 and check all the right connections appear var child1 = new MockData1(55, 77); var child1String = "Hello world, this is a wide string"; child1.SimpleThree = child1String; owner.InsertIntoObjSeq1(0, child1); // The first string we insert is deliberately the widest. After that, the width of the pile // remains constant, allowing us to test smarter, smaller invalidate rectangles; when the width // changes we invalidate the whole pile. VerifyParagraphs(root, new string[] { child1String }); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); int invalidateWidth = FakeRenderEngine.SimulatedWidth(child1String) + 2 * RootBox.InvalidateMargin; var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); // Insert a second item and check again. var child2 = new MockData1(55, 77); child2.SimpleThree = "Another world"; site.RectsInvalidated.Clear(); owner.InsertIntoObjSeq1(1, child2); VerifyParagraphs(root, new string[] { child1String, "Another world" }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); var expectedInvalidate2 = new Rectangle(-RootBox.InvalidateMargin, engine.SegmentHeight - RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); // Insert a third item between the first two. var child3 = new MockData1(55, 77); child3.SimpleThree = "Inserted world"; owner.InsertIntoObjSeq1(1, child3); VerifyParagraphs(root, new string[] { child1String, "Inserted world", "Another world" }); var expectedInvalidate3 = new Rectangle(-RootBox.InvalidateMargin, engine.SegmentHeight - RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight * 2 + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); // Insert a fourth item at the start. var child4 = new MockData1(55, 77); child4.SimpleThree = "Beginning of world"; owner.InsertIntoObjSeq1(0, child4); VerifyParagraphs(root, new string[] { "Beginning of world", child1String, "Inserted world", "Another world" }); var expectedInvalidate4 = new Rectangle(-RootBox.InvalidateMargin, - RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight * 4 + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4)); // Delete the first item. //var topHookup = root.RootHookup as IndependentSequenceHookup<MockData1>; //Assert.That(topHookup, Is.Not.Null); owner.RemoveAtObjSeq1(0); VerifyParagraphs(root, new string[] { child1String, "Inserted world", "Another world" }); Assert.That(child4.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4)); // Delete a middle item. owner.RemoveAtObjSeq1(1); VerifyParagraphs(root, new string[] { child1String, "Another world" }); Assert.That(child3.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); // Delete the last item. owner.RemoveAtObjSeq1(1); VerifyParagraphs(root, new string[] { child1String }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); // Delete the only remaining item. owner.RemoveAtObjSeq1(0); VerifyParagraphs(root, new string[0]); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); // Eventually add some operations that allow inserting and deleting multiple items. // We also need to be able to insert two or more object sequences into the same containing box. // That's probably another test, perhaps a view of the root where for each data item, // we insert its subitems, then for those insert paragraphs. // Let's assume there's always one top-level hookup for the root. }
public void NumericSurrogates() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "new o" + NumericSurrogatePair + "ld contents"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Paragraph.Containing(Display.Of(() => mock1.SimpleThree, 23))); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; int x = FakeRenderEngine.SimulatedWidth("new ol") + 2; var location = new Point(x, 8); EventArgs e = new EventArgs(); MouseEventArgs m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); string test = "o" + NumericSurrogatePair + "ld "; root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo(test)); x = FakeRenderEngine.SimulatedWidth("new ") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo(test)); x = FakeRenderEngine.SimulatedWidth("new old") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo(test)); mock1.SimpleThree = "new " + NumericSurrogatePair + " contents"; x = FakeRenderEngine.SimulatedWidth("new o") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); test = NumericSurrogatePair + " "; root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo(test)); x = FakeRenderEngine.SimulatedWidth("new ") + 2; location = new Point(x, 8); m = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnMouseClick(m, Keys.None, site.m_vwGraphics, site.m_transform); root.OnDoubleClick(e); Assert.That(!root.Selection.IsInsertionPoint, "Should be ranged selection"); Assert.That((root.Selection as RangeSelection).SelectedText(), Is.EqualTo(test)); }
public void DragStartsOnMoveInSelection() { string contents = "This is the day."; var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); var styles = new AssembledStyles().WithWs(34); var clientRuns = new List <IClientRun>(); var run = new StringClientRun(contents, styles); clientRuns.Add(run); var source = new TextSource(clientRuns, null); var para = new ParaBox(styles, source); var extraBox = new BlockBox(styles, Color.Red, 50, 72000); // tall, narrow spacer at top var root = new RootBoxFdo(styles); root.AddBox(extraBox); root.AddBox(para); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); Assert.That(root.Height, Is.EqualTo(96 + 13)); Assert.That(root.Width, Is.EqualTo(FakeRenderEngine.SimulatedWidth(contents))); var ip1 = run.SelectAt(para, 5, false); var ip2 = run.SelectAt(para, 7, true); var range = new RangeSelection(ip1, ip2); range.Install(); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); var sbox = para.FirstBox as StringBox; MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; int indent = FakeRenderEngine.SimulatedWidth("This "); root.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, indent + 5, 100, 0), Keys.None, m_gm.VwGraphics, ptrans); root.OnMouseMove(new MouseEventArgs(MouseButtons.Left, 1, indent + 5, 100, 0), Keys.None, m_gm.VwGraphics, ptrans); Assert.That(GetStringDropData(site), Is.EqualTo("is")); Assert.That(site.LastDoDragDropArgs.AllowedEffects, Is.EqualTo(DragDropEffects.Copy), "editing not possible in this paragraph, we can only copy"); Assert.That(root.Selection, Is.EqualTo(range), "selection should not be changed by drag drop"); site.LastDoDragDropArgs = null; root.OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, 3, 100, 0), Keys.None, m_gm.VwGraphics, ptrans); Assert.That(site.LastDoDragDropArgs, Is.Null, "click outside selection should not initiate drag"); // Tack on an extra check that a read-only view does not handle drop. var dataObj = new DataObject(DataFormats.StringFormat, "new "); var dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.ControlKey, 10, 8, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, new Point(14, 8), m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.None)); Assert.That(root.DragState, Is.EqualTo(WindowDragState.DraggingHere)); }
public void SubParaSequenceTest() { 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(); root.Site = site; PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Builder.Show(Paragraph.Containing(Display.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55)))); root.Layout(layoutInfo); VerifyParagraphs(root, new [] {""}); //// Insert the first item into owner.ObjSeq1 and check all the right connections appear var child1 = new MockData1(55, 77); var child1String = "Hello world, this is a wide string"; child1.SimpleThree = child1String; owner.InsertIntoObjSeq1(0, child1); VerifyParagraphs(root, new string[] { child1String }); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); int invalidateWidth = FakeRenderEngine.SimulatedWidth(child1String) + 2 * RootBox.InvalidateMargin; var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); // Insert a second item and check again. var child2 = new MockData1(55, 77); child2.SimpleThree = "Another world"; invalidateWidth += FakeRenderEngine.SimulatedWidth("Another world"); site.RectsInvalidated.Clear(); owner.InsertIntoObjSeq1(1, child2); VerifyParagraphs(root, new string[] { child1String + "Another world" }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(1), "Builder.AddString should set up a hookup for the string"); var expectedInvalidate2 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); // Insert a third item between the first two. var child3 = new MockData1(55, 77); child3.SimpleThree = "Inserted world"; invalidateWidth += FakeRenderEngine.SimulatedWidth("Inserted world"); site.RectsInvalidated.Clear(); owner.InsertIntoObjSeq1(1, child3); VerifyParagraphs(root, new string[] { child1String + "Inserted world" + "Another world" }); var expectedInvalidate3 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); // Insert a fourth item at the start. var child4 = new MockData1(55, 77); child4.SimpleThree = "Beginning of world"; invalidateWidth += FakeRenderEngine.SimulatedWidth("Beginning of world"); site.RectsInvalidated.Clear(); owner.InsertIntoObjSeq1(0, child4); VerifyParagraphs(root, new string[] { "Beginning of world" + child1String + "Inserted world" +"Another world" }); var expectedInvalidate4 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4)); // Delete the first item. site.RectsInvalidated.Clear(); owner.RemoveAtObjSeq1(0); VerifyParagraphs(root, new string[] { child1String + "Inserted world" + "Another world" }); Assert.That(child4.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate4)); // Delete a middle item. site.RectsInvalidated.Clear(); owner.RemoveAtObjSeq1(1); VerifyParagraphs(root, new string[] { child1String + "Another world" }); Assert.That(child3.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); // Delete the last item. site.RectsInvalidated.Clear(); owner.RemoveAtObjSeq1(1); VerifyParagraphs(root, new string[] { child1String }); Assert.That(child2.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); // Delete the only remaining item. site.RectsInvalidated.Clear(); owner.RemoveAtObjSeq1(0); VerifyParagraphs(root, new string[] {""}); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for a deleted object should be disposed."); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate1)); // Eventually add some operations that allow inserting and deleting multiple items. // We also need to be able to insert two or more object sequences into the same containing box. // That's probably another test, perhaps a view of the root where for each data item, // we insert its subitems, then for those insert paragraphs. // Let's assume there's always one top-level hookup for the root. }
public void BasicDrop() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "old contents"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Display.Of(() => mock1.SimpleThree, 23)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; var dataObj = new DataObject(DataFormats.StringFormat, "new "); int x = FakeRenderEngine.SimulatedWidth("old ") + 2; var location = new Point(x, 8); // A drag to where we can drop, allowing both copy and move, no keys held var dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.None, 200, 300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.Move)); Assert.That(root.DragState, Is.EqualTo(WindowDragState.DraggingHere)); root.OnDragLeave(); Assert.That(root.DragState, Is.EqualTo(WindowDragState.None)); // Though other factors would favor move, only copy is allowed here. dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.None, 200, 300, DragDropEffects.Copy, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.Copy)); // Though otherwise we could copy, there is no text data in the data object. dragArgs = new DragEventArgs(new DataObject(), (int)DragDropKeyStates.None, 200, 300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.None)); dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.ControlKey, 200, 300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.Copy)); root.OnDragDrop(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(mock1.SimpleThree, Is.EqualTo("old new contents")); }
public void NullObjChecker() { var owner = new MockData1(55, 77); var child1 = new MockData1(55, 77); child1.SimpleThree = "This is a Test"; owner.SimpleFour = null; 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(); root.Site = site; PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Builder.Show(Display.OfObj(() => owner.SimpleFour).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55))); root.Layout(layoutInfo); int invalidateWidth1 = FakeRenderEngine.SimulatedWidth(child1.SimpleThree) + 2*RootBox.InvalidateMargin; var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth1, engine.SegmentHeight + 2*RootBox.InvalidateMargin); VerifyParagraphs(root, new string[0]); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for this unconnected object should not exist"); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "The hookup for this null object should exist"); site.RectsInvalidatedInRoot.Clear(); owner.SimpleFour = child1; int invalidateWidth2 = FakeRenderEngine.SimulatedWidth(child1.SimpleThree) + 2*RootBox.InvalidateMargin; var expectedInvalidate2 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth2, engine.SegmentHeight + 2*RootBox.InvalidateMargin); VerifyParagraphs(root, new[] {child1.SimpleThree}); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "The hookup for this object should exist"); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "The hookup for this object should exist"); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); site.RectsInvalidatedInRoot.Clear(); owner.SimpleFour = null; int invalidateWidth3 = FakeRenderEngine.SimulatedWidth(child1.SimpleThree) + 2*RootBox.InvalidateMargin; var expectedInvalidate3 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth3, engine.SegmentHeight + 2*RootBox.InvalidateMargin); VerifyParagraphs(root, new string[0]); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for this unconnected object should not exist"); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "The hookup for this null object should exist"); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); site.RectsInvalidatedInRoot.Clear(); owner.SimpleFour = null; int invalidateWidth4 = FakeRenderEngine.SimulatedWidth(child1.SimpleThree) + 2 * RootBox.InvalidateMargin; var expectedInvalidate4 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth3, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); VerifyParagraphs(root, new string[0]); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for this unconnected object should not exist"); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "The hookup for this null object should exist"); Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "Nothing changed, so nothing should be invalidated"); }
public void Backtracking() { var styles = new AssembledStyles(); var layoutInfo = MakeLayoutInfo(); var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); var engine2 = new FakeRenderEngine() { Ws = 35, SegmentHeight = 13 }; factory.SetRenderer(35, engine2); // This first test doesn't strictly require backtracking; it is an example of a similar // case where all the second client run fits, nothing of the following one fits, but // there is a satisfactory break at the end of the last thing that fit. var root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing(Display.Of("this is ", 34), Display.Of("some mixed ", 35), Display.Of("text", 34))); int maxWidth = FakeRenderEngine.SimulatedWidth("this is some mixed t"); var layoutArgs = MakeLayoutInfo(maxWidth, m_gm.VwGraphics, factory); root.Layout(layoutArgs); var para = (ParaBox)root.FirstBox; var secondChild = (StringBox) para.FirstBox.Next; Assert.That(secondChild.Text, Is.EqualTo("some mixed ")); // True backtracking: the second client run fits entirely, but nothing of the following text. // We must go back and find the break in the middle of that second run. root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing(Display.Of("this is ", 34), Display.Of("some mixed", 35), Display.Of("text", 34))); maxWidth = FakeRenderEngine.SimulatedWidth("this is some mixedte"); layoutArgs = MakeLayoutInfo(maxWidth, m_gm.VwGraphics, factory); root.Layout(layoutArgs); para = (ParaBox)root.FirstBox; secondChild = (StringBox)para.FirstBox.Next; Assert.That(secondChild.Text, Is.EqualTo("some ")); // Now a harder problem: the second client run fits entirely, but nothing of the following text, // and there is no break point in the second client run at all. // We must go back and find the break in the first client run. root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing(Display.Of("this is", 34), Display.Of("some", 35), Display.Of("text", 34))); maxWidth = FakeRenderEngine.SimulatedWidth("this issomete"); layoutArgs = MakeLayoutInfo(maxWidth, m_gm.VwGraphics, factory); root.Layout(layoutArgs); para = (ParaBox)root.FirstBox; var firstChild = (StringBox)para.FirstBox; Assert.That(firstChild.Text, Is.EqualTo("this ")); secondChild = (StringBox)para.FirstBox.Next; Assert.That(secondChild.Text, Is.EqualTo("is")); var thirdChild = (StringBox)secondChild.Next; Assert.That(thirdChild.Text, Is.EqualTo("some")); var fourthChild = (StringBox)thirdChild.Next; Assert.That(fourthChild.Text, Is.EqualTo("text")); Assert.That(secondChild.Top > firstChild.Top); Assert.That(fourthChild.Top, Is.EqualTo(secondChild.Top)); // This time the third client run fits entirely, but nothing of the following text, // and there is no break point in the second client run at all; but right // before that third client run there is a non-text box. // We must break following the non-text box. root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing(Display.Of("this is", 34), Display.Block(Color.Red, 6000, 2000), Display.Of("some", 35), Display.Of("text", 34))); maxWidth = FakeRenderEngine.SimulatedWidth("this issomete") + layoutArgs.MpToPixelsX(6000); layoutArgs = MakeLayoutInfo(maxWidth, m_gm.VwGraphics, factory); root.Layout(layoutArgs); para = (ParaBox)root.FirstBox; firstChild = (StringBox)para.FirstBox; Assert.That(firstChild.Text, Is.EqualTo("this is")); Assert.That(firstChild.Next, Is.InstanceOf(typeof(BlockBox))); thirdChild = (StringBox)firstChild.Next.Next; Assert.That(thirdChild.Text, Is.EqualTo("some")); fourthChild = (StringBox)thirdChild.Next; Assert.That(fourthChild.Text, Is.EqualTo("text")); Assert.That(firstChild.Next.Top, Is.EqualTo(firstChild.Top), "The blockbox should be left on the first line"); Assert.That(thirdChild.Top, Is.GreaterThan(firstChild.Top), "The 'some' run should be on the second line"); Assert.That(fourthChild.Top, Is.EqualTo(thirdChild.Top), "all the rest of the text should fit on one more line"); // This time the thing that does not fit IS the block at the end. // It should move to the next line, without taking any text with it. root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing(Display.Of("this is ", 34), Display.Of("some text", 35), Display.Block(Color.Red, 6000, 2000))); maxWidth = FakeRenderEngine.SimulatedWidth("this is some text") + layoutArgs.MpToPixelsX(2000); layoutArgs = MakeLayoutInfo(maxWidth, m_gm.VwGraphics, factory); root.Layout(layoutArgs); para = (ParaBox)root.FirstBox; firstChild = (StringBox)para.FirstBox; Assert.That(firstChild.Text, Is.EqualTo("this is ")); secondChild = (StringBox)para.FirstBox.Next; Assert.That(secondChild.Text, Is.EqualTo("some text")); Assert.That(secondChild.Next, Is.InstanceOf(typeof(BlockBox))); Assert.That(secondChild.Top, Is.EqualTo(firstChild.Top), "The two string boxes should fit on the first line"); Assert.That(secondChild.Next.Top, Is.GreaterThan(secondChild.Top), "The block should be on the second line"); // Todo JohnT: should also test that a break can occur after a non-string box. // And many other cases (see Backtrack method). }
public void InsertGrowsPara() { string contents = "This is the day."; var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); var styles = new AssembledStyles().WithWs(34); var clientRuns = new List<IClientRun>(); var run = new StringClientRun(contents, styles); clientRuns.Add(run); var data1 = new MockData1(34, 35); data1.SimpleThree = contents; var source = new TextSource(clientRuns, null); var para = new ParaBox(styles, source); var hookup = new StringHookup(this, () => data1.SimpleThree, hook => data1.SimpleThreeChanged += hook.StringPropChanged, hook => data1.SimpleThreeChanged -= hook.StringPropChanged, para); hookup.Writer = newVal => data1.SimpleThree = newVal; run.Hookup = hookup; var extraBox = new BlockBox(styles, Color.Red, 50, 72000); var root = new RootBoxFdo(styles); root.SizeChanged += root_SizeChanged; root.AddBox(para); root.AddBox(extraBox); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); Assert.IsTrue(m_sizeChangedCalled); Assert.That(root.Height, Is.EqualTo(13 + 96)); Assert.That(root.Width, Is.EqualTo(FakeRenderEngine.SimulatedWidth(contents))); int widthThisIsThe = FakeRenderEngine.SimulatedWidth("This is the"); layoutArgs = MakeLayoutInfo(widthThisIsThe + 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); Assert.That(root.Height, Is.EqualTo(26 + 96), "two line para is twice the height"); Assert.That(root.Width, Is.EqualTo(widthThisIsThe + 2), "two-line para occupies full available width"); Assert.That(extraBox.Top, Is.EqualTo(26)); PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; m_sizeChangedCalled = false; var ip = para.SelectAtEnd(); ip.InsertText(" We will be"); Assert.That(para.Height, Is.EqualTo(39), "inserted text makes para a line higher"); Assert.That(root.Height, Is.EqualTo(39 + 96), "root grows when para does"); Assert.That(root.Width, Is.EqualTo(widthThisIsThe + 2), "three-line para occupies full available width"); Assert.That(extraBox.Top, Is.EqualTo(39)); Assert.IsTrue(m_sizeChangedCalled); }
public void BidiLayout() { string content1 = "This is the "; string contentRtl = "day "; string content3 = "that "; // Two writing systems int wsLtr = 5; int wsRtl = 6; // Two corresponding renderers var factory = new FakeRendererFactory(); var engineLtr = new FakeRenderEngine() { Ws = wsLtr, SegmentHeight = 13 }; factory.SetRenderer(wsLtr, engineLtr); var engineRtl = new FakeRenderEngine() {Ws = wsRtl, SegmentHeight = 13 }; engineRtl.RightToLeft = true; factory.SetRenderer(wsRtl, engineRtl); // Two corresponding styles (and a vanilla one) var styles = new AssembledStyles(); var stylesLtr = new AssembledStyles().WithWs(wsLtr); var stylesRtl = new AssembledStyles().WithWs(wsRtl); var clientRuns = new List<IClientRun>(); var run1 = new StringClientRun(content1, stylesLtr); clientRuns.Add(run1); var runRtl = new StringClientRun(contentRtl, stylesRtl); clientRuns.Add(runRtl); var run3 = new StringClientRun(content3, stylesLtr); clientRuns.Add(run3); var root = new RootBoxFdo(styles); var source = new TextSource(clientRuns, null); var para = new ParaBox(styles, source); root.AddBox(para); var stylesParaRtl = styles.WithRightToLeft(true); var sourceRtl = new TextSource(clientRuns, null); var paraRtl = new ParaBox(stylesParaRtl, sourceRtl); root.AddBox(paraRtl); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); // "day " being upstream should make two distinct boxes. // We should get something like // "This is the yad that ", where the space between "yad" and "that" is the one that // follows the 'y' in "day". var box1 = para.FirstBox as StringBox; var box2 = box1.Next as StringBox; var box3 = box2.Next as StringBox; var box4 = box3.Next as StringBox; Assert.That(box4, Is.Not.Null); Assert.That(box1.Segment.get_Lim(box1.IchMin) == content1.Length); Assert.That(box2.Segment.get_Lim(box2.IchMin) == contentRtl.Length - 1); Assert.That(box3.Segment.get_Lim(box3.IchMin) == 1); Assert.That(box4.Segment.get_Lim(box4.IchMin) == content3.Length); Assert.That(box1.Left, Is.LessThan(box2.Left)); Assert.That(box2.Left, Is.LessThan(box3.Left)); Assert.That(box3.Left, Is.LessThan(box4.Left)); // In the second paragraph, the two LRT runs are upstream. We should get boxes // "This is the", " ", "day ", "that" and " " (but the final space will have zero width at end of line) // The effect should be something like // that yad This is the", where the space between "yad" and "This" is the one following "the", // and the one between "that" and "yad" is the one following "day", and the space following "that" // is invisible at the end of the line to the left of 'that'. var boxR1 = paraRtl.FirstBox as StringBox; var boxR2 = boxR1.Next as StringBox; var boxR3 = boxR2.Next as StringBox; var boxR4 = boxR3.Next as StringBox; var boxR5 = boxR4.Next as StringBox; Assert.That(boxR5, Is.Not.Null); Assert.That(boxR1.Segment.get_Lim(boxR1.IchMin) == content1.Length - 1); Assert.That(boxR2.Segment.get_Lim(boxR2.IchMin) == 1); Assert.That(boxR3.Segment.get_Lim(boxR3.IchMin) == contentRtl.Length); Assert.That(boxR4.Segment.get_Lim(boxR4.IchMin) == content3.Length - 1); Assert.That(boxR5.Segment.get_Lim(boxR5.IchMin) == 1); }
private void BodyofUserPromptTest(MockData1 data1, Flow promptField, Func<string> reader) { var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); factory.SetRenderer(35, engine); factory.SetRenderer(0, engine); // for literals var styles = new AssembledStyles().WithWs(34); var root = new RootBoxFdo(styles); root.Builder.Show( Paragraph.Containing( Display.Of("lead in ", 34), promptField, Display.Of("trailing", 34) ) ); var para = (ParaBox)root.FirstBox; Assert.That(para.Source.RenderText, Is.EqualTo("lead in type here trailing")); int width = FakeRenderEngine.SimulatedWidth("lead in type her"); // should make it take 2 lines and split prompt. var layoutArgs = MakeLayoutInfo(width, m_gm.VwGraphics, factory); root.Layout(layoutArgs); MockSite site = new MockSite(); PaintTransform ptrans = new PaintTransform(2, 4, 96, 96, 0, 10, 96, 96); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; // Click on "type here" produces an IP in the empty string. int leadWidth = FakeRenderEngine.SimulatedWidth("lead in "); var mouseArgs = new MouseEventArgs(MouseButtons.Left, 1, 2 + leadWidth + 3, 0, 0); root.OnMouseDown(mouseArgs, Keys.None, m_gm.VwGraphics, ptrans); var ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in ".Length)); Assert.That(ip.StringPosition, Is.EqualTo(0)); // IP is drawn as range covering "type here" ip.Draw(m_gm.VwGraphics, ptrans); var first = (StringBox)para.FirstBox; VerifyRangeSegmentDrawing(para, first, (FakeSegment)first.Segment, "lead in ".Length, "lead in type here ".Length, -4, 4 - 10, 4 - 10 + 13); var second = (StringBox)first.Next; VerifyRangeSegmentDrawing(para, second, (FakeSegment)second.Segment, "lead in ".Length, "lead in type here ".Length, -4 - 13, 4 - 10 + 13, 4 - 10 + 13 * 2); // Check that we get a sensible answer for the selection's containing rectangle. ((FakeSegment) first.Segment).LeftPositionOfRangeResult = 17; ((FakeSegment)first.Segment).RightPositionOfRangeResult = 29; ((FakeSegment)second.Segment).LeftPositionOfRangeResult = 5; ((FakeSegment)second.Segment).RightPositionOfRangeResult = 13; var rect = ip.GetSelectionLocation(m_gm.VwGraphics, ptrans); Assert.That(rect.Top, Is.EqualTo(4 - 10)); Assert.That(rect.Bottom, Is.EqualTo(4 - 10 + 13*2)); Assert.That(rect.Left, Is.EqualTo(5)); Assert.That(rect.Right, Is.EqualTo(29)); VerifyRangeSegmentQuery(para, first, (FakeSegment)first.Segment, "lead in ".Length, "lead in type here ".Length, -4, 4 - 10, 4 - 10 + 13); VerifyRangeSegmentQuery(para, second, (FakeSegment)second.Segment, "lead in ".Length, "lead in type here ".Length, -4 - 13, 4 - 10 + 13, 4 - 10 + 13 * 2); Assert.That(second.IchMin, Is.EqualTo("lead in type ".Length)); // When the IP is drawn like this, it doesn't flash! site.RectsInvalidatedInRoot.Clear(); site.RectsInvalidated.Clear(); root.FlashInsertionPoint(); // Call twice just in case somehow only some invalidates worked. root.FlashInsertionPoint(); Assert.That(site.RectsInvalidated, Is.Empty); Assert.That(site.RectsInvalidatedInRoot, Is.Empty); // Typing something else makes "type here" go away and produces a normal IP after it. ip.InsertText("x"); Assert.That(reader(), Is.EqualTo("x")); Assert.That(para.Source.RenderText, Is.EqualTo("lead in xtrailing")); ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.ShouldFlash, Is.True); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in x".Length)); // Deleting back to empty string makes "type here" reappear. ip.Backspace(); Assert.That(reader(), Is.EqualTo("")); Assert.That(para.Source.RenderText, Is.EqualTo("lead in type here trailing")); ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.ShouldFlash, Is.False); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in ".Length)); Assert.That(ip.LastRenderParaPosition, Is.EqualTo("lead in type here ".Length)); second = (StringBox)para.FirstBox.Next; Assert.That(second.IchMin, Is.EqualTo("lead in type ".Length)); // Click after "type here" produces an IP at the right place in the following string. // We've arranged for the prompt to be split, so this is after the word 'here' on the second line. int hereTWidth = FakeRenderEngine.SimulatedWidth("here t"); mouseArgs = new MouseEventArgs(MouseButtons.Left, 1, 2 + hereTWidth - 1, 4 - 10 + 13 + 2, 0); root.OnMouseDown(mouseArgs, Keys.None, m_gm.VwGraphics, ptrans); ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in t".Length)); Assert.That(ip.AssociatePrevious, Is.True); Assert.That(ip.StringPosition, Is.EqualTo(1)); Assert.That(ip.RenderParaPosition, Is.EqualTo("lead in type here t".Length)); // Also try a click in the second-line part of the prompt. int herWidth = FakeRenderEngine.SimulatedWidth("her"); mouseArgs = new MouseEventArgs(MouseButtons.Left, 1, 2 + herWidth - 1, 4 - 10 + 13 + 2, 0); root.OnMouseDown(mouseArgs, Keys.None, m_gm.VwGraphics, ptrans); ip = root.Selection as InsertionPoint; Assert.That(ip, Is.Not.Null); Assert.That(ip.LogicalParaPosition, Is.EqualTo("lead in ".Length)); Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.RenderParaPosition, Is.EqualTo("lead in ".Length)); Assert.That(ip.LastRenderParaPosition, Is.EqualTo("lead in type here ".Length)); }
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] }); }
public void ArrowKeys() { var engine = new FakeRenderEngine() { Ws = 34, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(34, engine); var styles = new AssembledStyles().WithWs(34); var root = new RootBoxFdo(styles); var para1 = AddPara("This i~^s the~ day", styles, root); var para2 = AddPara("", styles, root); var para3 = AddPara(new string[] {"that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lord", "has made"}, styles, root); var para4 = AddPara(new string[] { "we will", "", "rejoice" }, styles, root); int widthThisIsThe = FakeRenderEngine.SimulatedWidth("This is the"); var layoutArgs = MakeLayoutInfo(widthThisIsThe + 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); Assert.That(root.Height, Is.EqualTo(13 * 8), "A two-line and a one-line and a three-line and a two-line paragraph"); PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; // Simple left movement. var ipThisIsTheDay = para1.SelectAtEnd(); var ipThisIsTheDa = ipThisIsTheDay.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThisIsTheDa, para1, "This i~^s the~ da", false, "left from end"); var ipThisIsTheD = ipThisIsTheDa.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThisIsTheD, para1, "This i~^s the~ d", false, "left from no special plae"); // Left from one run into an adjacent non-empty run var ipThatTheLord2 = ((TextClientRun) para3.Source.ClientRuns[1]).SelectAt(para3, 0, false); var ipThatTheLor = ipThatTheLord2.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThatTheLor, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lor", false, "left from start run2"); // Left from one run into an adjacent empty run. Is this right or should we skip over it into // another run so we actually move a character? var ipStartOfRejoice = ((TextClientRun)para4.Source.ClientRuns[2]).SelectAt(para4, 0, false); var ipEmptyPara4 = ipStartOfRejoice.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipEmptyPara4, para4, "we will", false, "left from start run into empty"); // Out of the empty run into the previous one. var ipWeWil = ipEmptyPara4.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipWeWil, para4, "we wil", false, "left from empty run"); // back from one para into another. var ipPara2 = para2.SelectAtStart(); var ipEndPara1 = ipPara2.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipEndPara1, para1, "This i~^s the~ day", true, "left from one para to another"); // back at the very start. var ipStart = para1.SelectAtStart(); Assert.That(ipStart.MoveByKey(new KeyEventArgs(Keys.Left)), Is.Null); // back after a surrogate pair should not stop in the middle. var ipThatSurrogate = ((TextClientRun)para3.Source.ClientRuns[0]).SelectAt(para3, ("that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE).Length, false); var ipThat = ipThatSurrogate.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThat, para3, "that", false, "left over surrogate pair"); // Back to a place between diacritic and base should not stop. var ipThisI_Diacritics = ((TextClientRun)para1.Source.ClientRuns[0]).SelectAt(para1, "This i~^".Length, false); var ipThisSpace = ipThisI_Diacritics.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThisSpace, para1, "This ", false, "left over diacritics"); // We can use many of the same ones to check right movement. var ipThisIsTheDa_r = ipThisIsTheD.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipThisIsTheDa_r, para1, "This i~^s the~ da", true, "simple right"); // Move right into an empty paragraph. // Review JohnT: should this IP in an empty para associate forward or back? var ipStartP2 = ipThisIsTheDay.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipStartP2, para2, "", false, "right into empty para"); // Should definitely associate with the character following, not the nonexistent preceding one. var ipStartP3 = ipStartP2.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipStartP3, para3, "", false, "right to start of non-empty para"); var ipP3_t = ipStartP3.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipP3_t, para3, "t", true, "simple right"); var ipThatSurrogate2 = ipThat.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipThatSurrogate2, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE, true, "right over surrogate pair"); var ipThatTheLord_left = ((TextClientRun)para3.Source.ClientRuns[0]).SelectAt(para3, ("that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lord").Length, true); var ipThatTheLord_space = ipThatTheLord_left.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipThatTheLord_space, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lord ", true, "right from end run1"); var ipEnd = para4.SelectAtEnd(); Assert.That(ipEnd.MoveByKey(new KeyEventArgs(Keys.Right)), Is.Null); // Also can't make range by moving right from end. Assert.That(ipEnd.MoveByKey(new KeyEventArgs(Keys.Right | Keys.Shift)), Is.Null); var rangeThatSurrogate2 = ipThat.MoveByKey(new KeyEventArgs(Keys.Right | Keys.Shift)); VerifyRange(rangeThatSurrogate2, para3, "that", para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE, "shift-right over surrogate pair"); var rangeThatTha = ipThat.MoveByKey(new KeyEventArgs(Keys.Left | Keys.Shift)); VerifyRange(rangeThatTha, para3, "that", para3, "tha", "shift-left end before anchor"); // left from a range puts us at the start of the range var ipThat2 = rangeThatSurrogate2.MoveByKey(new KeyEventArgs(Keys.Left)); VerifyIp(ipThat2, para3, "that", false, "left from range to IP"); // right from a range puts us at the end of the range var ipThatSurrrogate2 = rangeThatSurrogate2.MoveByKey(new KeyEventArgs(Keys.Right)); VerifyIp(ipThatSurrrogate2, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE, true, "right from range"); // shift-left from a 1-char range collapses it to an IP var ipThat3 = rangeThatSurrogate2.MoveByKey(new KeyEventArgs(Keys.Left | Keys.Shift)); VerifyIp(ipThat3, para3, "that", false, "left over surrogate pair"); // shift-right from a range makes one with the same anchor but an extended end var rangeThatSurrogateSpace = rangeThatSurrogate2.MoveByKey(new KeyEventArgs(Keys.Right | Keys.Shift)); VerifyRange(rangeThatSurrogateSpace, para3, "that", para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " ", "shift-right from range"); // shift-right from a range that can't grow returns null. var ipWeWillRejoic = (InsertionPoint) ipEnd.MoveByKey(new KeyEventArgs(Keys.Left)); var range1AtEnd = new RangeSelection(ipWeWillRejoic, ipEnd); Assert.That(range1AtEnd.MoveByKey(new KeyEventArgs(Keys.Right | Keys.Shift)), Is.Null); // Home key. var ipStartP2_2 = ipThat.MoveByKey(new KeyEventArgs(Keys.Home)); VerifyIp(ipStartP2_2, para3, "", false, "home in Para 3"); var ipStart_2 = ipThat.MoveByKey(new KeyEventArgs(Keys.Home | Keys.Control)); VerifyIp(ipStart_2, para1, "", false, "ctrl-home in Para 3"); // End key. var ipEndP2 = ipThat.MoveByKey(new KeyEventArgs(Keys.End)); VerifyIp(ipEndP2, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " the Lord" + "has made", true, "end in Para 3"); var ipEnd_2 = ipThat.MoveByKey(new KeyEventArgs(Keys.End | Keys.Control)); VerifyIp(ipEnd_2, para4, "we will" + "rejoice", true, "ctrl-end in Para 3"); // Down key var ipThisIsThe_R = ipStart.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipThisIsThe_R, para1, "This i~^s the~ ", false, "down from start line 1"); var ipTh = para1.SelectAt(2, true); var ipThisIsTheDa2 = ipTh.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipThisIsTheDa2, para1, "This i~^s the~ da", false, "down from 2 chars into line 1"); var ipThisIdTh = para1.SelectAt("This i~^s th".Length, false); var ipThisIsTheDay2 = ipThisIdTh.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipThisIsTheDay2, para1, "This i~^s the~ day", true, "down from near end line 1"); // Empty para: arbitrary which way it associates. var ipPara2Down = ipThisIsTheDay2.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipPara2Down, para2, "", true, "down twice from near end line 1"); // Going on down, we should remember the starting X position and end up about that // far into the next full-length line. The 'i' characters in the first line make it // a bit iffy; might be closer to the start of the 'e' at the end of 'the'. // The other complication is that our fake render engine is not smart about surrogate pairs, // and treats the musical semibrevis as two ordinary characters. // Omitting the diacritics in the first paragraph, our selection starts 10 characters in. // The result string here is 9 characters, since with no narrow letters on this para, // we end up closer to the left of the 'e'. var ipThatTheSpaceDown = ipPara2Down.MoveByKey(new KeyEventArgs(Keys.Down)); VerifyIp(ipThatTheSpaceDown, para3, "that" + MUSICAL_SYMBOL_SEMIBREVIS_WHITE + " th", false, "down 3x from near end line 1"); Assert.That(ipEnd.MoveByKey(new KeyEventArgs(Keys.Down)), Is.Null); var ipPara2Up = ipThatTheSpaceDown.MoveByKey(new KeyEventArgs(Keys.Up)); VerifyIp(ipPara2Up, para2, "", true, "back up aligned with near end line 1"); var ipThisIsTheDayUp = ipPara2Up.MoveByKey(new KeyEventArgs(Keys.Up)); VerifyIp(ipThisIsTheDayUp, para1, "This i~^s the~ day", true, "up from para2 aligned near end line 1"); // It's going to be looking for a position right at the boundary...either assocPrev would be // reasonable. var ipThisIdTh2Up = ipThisIsTheDayUp.MoveByKey(new KeyEventArgs(Keys.Up)); VerifyIp(ipThisIdTh2Up, para1, "This i~^s th", false, "up from end para 1 aligned near end line 1"); //var ipPara2_2 = ipThisIsTheDay.MoveByKey(new KeyEventArgs(Keys.Down)); //VerifyIp(ipPara2_2, para2, "", true, "down from end para 1"); // Todo: // HandleSpecialKey is called from OnKeyDown and should handle at least these: //case Keys.PageUp: //case Keys.PageDown: //case Keys.End: //case Keys.Home: //case Keys.Left: // done //case Keys.Up: //case Keys.Right: // done //case Keys.Down: //case Keys.F7: // the only two function keys currently known to the Views code, //case Keys.F8: // used for left and right arrow by string character amounts. // Test Left: (done) // - char to char in same line // - skipping diacritics // - skipping surrogate pairs // - to another line in same paragraph // - to previous (empty?) paragraph // - at very start (nothing happens) // - range collapses to start // - anything special to test if there are multiple runs? e.g., at boundary // - skip over embedded pictures // - eventually drop into embedded boxes that contain text? // Similarly right (done) // Down: // - same para, there is text below // - same para, no text immediately below on same line (goes to end of previous line) // - eventually: what should happen if logical and physical end of next line don't coincide? // - down again to a longer line: should stay aligned with start position (what resets this??) // etc for others. }
public void BasicDragMove() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "This is the day"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Display.Of(() => mock1.SimpleThree, 23)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; SelectionBuilder.In(root).Offset("This ".Length).To.Offset("This is ".Length).Install(); var dataObj = new DataObject(DataFormats.StringFormat, "is "); int x = FakeRenderEngine.SimulatedWidth("This is the ") + 2; var location = new Point(x, 8); // A drag to where we can drop, allowing both copy and move, no keys held var dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.None, 200, 300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.OnDragEnter(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.Move)); Assert.That(root.DragState, Is.EqualTo(WindowDragState.DraggingHere)); var qcdArgs = new QueryContinueDragEventArgs((int) DragDropKeyStates.None, false, DragAction.Drop); root.OnQueryContinueDrag(qcdArgs); Assert.That(root.DragState, Is.EqualTo(WindowDragState.InternalMove)); root.OnDragLeave(); Assert.That(root.DragState, Is.EqualTo(WindowDragState.InternalMove), "DragLeave should not clear InternalMove"); root.OnDragDrop(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(mock1.SimpleThree, Is.EqualTo("This the is day")); Assert.That(root.DragState, Is.EqualTo(WindowDragState.None)); // Now let's drag the 'is' out to another window. SelectionBuilder.In(root).Offset("This the ".Length).To.Offset("This the is ".Length).Install(); qcdArgs = new QueryContinueDragEventArgs((int)DragDropKeyStates.None, false, DragAction.Drop); root.OnQueryContinueDrag(qcdArgs); Assert.That(root.DragState, Is.EqualTo(WindowDragState.None), "We should only set InternalMove if this window is the destination"); Assert.That(mock1.SimpleThree, Is.EqualTo("This the day")); // Check that we can't drag inside our own selection. SelectionBuilder.In(root).Offset("This ".Length).To.Offset("This the".Length).Install(); x = FakeRenderEngine.SimulatedWidth("This t") + 2; location = new Point(x, 8); dragArgs = new DragEventArgs(dataObj, (int)DragDropKeyStates.None, 200, 300, DragDropEffects.Copy | DragDropEffects.Move, DragDropEffects.None); root.DragState = WindowDragState.InternalMove; root.OnDragDrop(dragArgs, location, m_gm.VwGraphics, ptrans); Assert.That(dragArgs.Effect, Is.EqualTo(DragDropEffects.None)); Assert.That(mock1.SimpleThree, Is.EqualTo("This the day")); }
public void DrawIPMultiRunPara() { var styles = new AssembledStyles(); RootBox root = new RootBoxFdo(styles); root.Builder.Show(Paragraph.Containing( Display.Of("first"), Display.Of("second"), Display.Of("third"))); SetupFakeRootSite(root); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); root.Layout(layoutInfo); InsertionPoint ip = root.SelectAtEnd(); ip.Install(); var para = (ParaBox) root.FirstBox; Assert.AreEqual(ip, root.Selection); Assert.AreEqual(para, ip.Para, "IP should know about the paragraph it is in"); Assert.AreEqual("third".Length, ip.StringPosition, "IP position is relative to run"); Assert.AreEqual(true, ip.AssociatePrevious, "selection at end should always associate previous in non-empty para"); StringBox sbox = para.FirstBox as StringBox; Assert.That(sbox, Is.EqualTo(para.LastBox), "uniform text in infinite width should make one string box"); var seg = sbox.Segment as FakeSegment; Assert.IsNotNull(seg); PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, -10, 120, 128); ip.Draw(m_gm.VwGraphics, ptrans); VerifySegmentDrawing(para, sbox, seg.LastDrawIpCall, -4); seg.NextPosIpResult = new MockSegment.PositionsOfIpResults(); seg.NextPosIpResult.RectPrimary = new Rect(5, 6, 7, 9); seg.NextPosIpResult.PrimaryHere = true; Rectangle selRect = ip.GetSelectionLocation(m_gm.VwGraphics, ptrans); VerifySelLocation(para, sbox, seg.LastPosIpCall, -4); Assert.AreEqual(new Rectangle(5, 6, 2, 3), selRect); }
public void CombinedParaOperationsFdo() { // Set up a simple multi-para layout using the ObjSeq2 and SimpleThree properties of MockData1 AssembledStyles styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var text = Cache.ServiceLocator.GetInstance<ITextFactory>().Create(); //Cache.LangProject.TextsOC.Add(text); var owner = Cache.ServiceLocator.GetInstance<IStTextFactory>().Create(); text.ContentsOA = owner; IStTxtPara p1 = MakePara(owner, "Hello World"); var p2 = MakePara(owner,"This is a test"); m_actionHandler.EndUndoTask(); // Finish settin up data, we want to control from now on. var po = new StTextParagraphOperations(owner); root.Builder.Show(Display.Of(() => owner.ParagraphsOS).Using((builder, para) => builder.AddString(() => ((IStTxtPara)para).Contents)).EditParagraphsUsing(po)); SetupFakeRootSite(root); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); root.Layout(layoutInfo); var ip = root.FirstBox.SelectAtEnd(); // Insert at end of paragraph. m_actionHandler.BeginUndoTask("undo insert line break", "redo insert"); ip.InsertLineBreak(); m_actionHandler.EndUndoTask(); Assert.That(owner.ParagraphsOS.Count, Is.EqualTo(3)); Assert.That(owner.ParagraphsOS[0], Is.EqualTo(p1)); Assert.That(owner.ParagraphsOS[2], Is.EqualTo(p2)); var p3 = (IStTxtPara)owner.ParagraphsOS[1]; // empty inserted paragraph. Assert.That(p3.Contents.Length, Is.EqualTo(0)); Assert.That(p3.Contents.get_WritingSystem(0), Is.EqualTo(Cache.DefaultVernWs)); ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(root.FirstBox.Next.Next, Is.Not.Null); Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next)); // Insert at start of paragraph. To make it unambiguously the start, give p3 some data, // and since that might eventually destroy the selection, reset it. var rootHookup = ip.Hookup.ParentHookup.ParentHookup; // StringHookup, ItemHookup, Sequence UndoableUnitOfWorkHelper.Do("adjust contents", "redo", m_actionHandler, () => p3.Contents = Cache.TsStrFactory.MakeString("First insert", Cache.DefaultVernWs)); ip = (InsertionPoint)SelectionBuilder.In(rootHookup)[1].Offset(0).Install(); m_actionHandler.BeginUndoTask("undo insert line break", "redo insert"); ip.InsertLineBreak(); m_actionHandler.EndUndoTask(); Assert.That(owner.ParagraphsOS.Count, Is.EqualTo(4)); Assert.That(owner.ParagraphsOS[0], Is.EqualTo(p1)); Assert.That(owner.ParagraphsOS[2], Is.EqualTo(p3)); Assert.That(owner.ParagraphsOS[3], Is.EqualTo(p2)); var p4 = (IStTxtPara)owner.ParagraphsOS[1]; // empty inserted paragraph (before p3). Assert.That(root.FirstBox.Next.Next.Next, Is.Not.Null); Assert.That(p4.Contents.get_WritingSystem(0), Is.EqualTo(Cache.DefaultVernWs)); ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next)); // Split a paragraph. var oldObjects = owner.ParagraphsOS.ToArray(); ip = (InsertionPoint)SelectionBuilder.In(rootHookup)[2].Offset("First ".Length).Install(); m_actionHandler.BeginUndoTask("undo insert line break", "redo insert"); ip.InsertLineBreak(); m_actionHandler.EndUndoTask(); Assert.That(owner.ParagraphsOS.Count, Is.EqualTo(5)); Assert.That(owner.ParagraphsOS[1], Is.EqualTo(oldObjects[1])); Assert.That(owner.ParagraphsOS[2], Is.EqualTo(oldObjects[2])); Assert.That(owner.ParagraphsOS[4], Is.EqualTo(oldObjects[3])); // insert between 2 and 3 var p5 = (IStTxtPara)owner.ParagraphsOS[3]; // inserted paragraph. ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next.Next)); Assert.That(((IStTxtPara)owner.ParagraphsOS[2]).Contents.Text, Is.EqualTo("First ")); Assert.That(((IStTxtPara)owner.ParagraphsOS[3]).Contents.Text, Is.EqualTo("insert")); // Combine two paragraphs by backspace at start of line. oldObjects = owner.ParagraphsOS.ToArray(); m_actionHandler.BeginUndoTask("undo backspace", "redo backspace"); ip.Backspace(); m_actionHandler.EndUndoTask(); Assert.That(owner.ParagraphsOS.Count, Is.EqualTo(4)); Assert.That(owner.ParagraphsOS[1], Is.EqualTo(oldObjects[1])); Assert.That(owner.ParagraphsOS[2], Is.EqualTo(oldObjects[2])); Assert.That(owner.ParagraphsOS[3], Is.EqualTo(oldObjects[4])); // delete item 3 ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo("First ".Length)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next)); Assert.That(((IStTxtPara)owner.ParagraphsOS[2]).Contents.Text, Is.EqualTo("First insert")); }
public void BasicPaste() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var mock1 = new MockData1(23, 23); mock1.SimpleThree = "old contents"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); root.Builder.Show(Display.Of(() => mock1.SimpleThree, 23)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; var dataObj = new DataObject(DataFormats.StringFormat, "new "); int x = FakeRenderEngine.SimulatedWidth("new old ") + 2; var location = new Point(x, 8); Clipboard.SetDataObject(""); Assert.That(root.CanPaste(), Is.EqualTo(false), "Should not be able to Paste"); root.OnEditPaste(); Assert.That(mock1.SimpleThree, Is.EqualTo("old contents"), "Nothing should have changed"); MouseEventArgs e = new MouseEventArgs(MouseButtons.Left, 1, 2, location.Y, 0); root.OnMouseDown(e, Keys.None, site.m_vwGraphics, site.m_transform); Clipboard.SetDataObject(dataObj); Assert.That(root.CanPaste(), Is.EqualTo(true), "Should be able to Paste"); root.OnEditPaste(); Assert.That(mock1.SimpleThree, Is.EqualTo("new old contents"), "Selected String should be \"new old contents\""); Assert.That(Clipboard.GetDataObject().GetData(DataFormats.StringFormat), Is.Not.EqualTo(null), "Selected String should not be null"); Assert.That(Clipboard.GetDataObject().GetData(DataFormats.StringFormat), Is.EqualTo("new "), "Selected String should be \"new \""); dataObj = new DataObject(DataFormats.StringFormat, "new "); x = FakeRenderEngine.SimulatedWidth("") + 2; location = new Point(x, 8); e = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); Clipboard.SetDataObject(dataObj); root.OnMouseDown(e, Keys.None, site.m_vwGraphics, site.m_transform); x = FakeRenderEngine.SimulatedWidth("new old ") + 2; location = new Point(x, 8); e = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseMove(e, Keys.None, site.m_vwGraphics, site.m_transform); Assert.That(root.Selection, Is.Not.EqualTo(null), "Selection should now be assigned"); Assert.That(root.Selection.DragDropData, Is.Not.EqualTo(null), "Selection should now be assigned"); root.OnEditPaste(); Assert.That(mock1.SimpleThree, Is.EqualTo("new contents"), "Selected String should be \"new contents\""); Assert.That(Clipboard.GetDataObject().GetData(DataFormats.StringFormat), Is.EqualTo("new "), "Selected String should be \"new \""); }
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)); }
public void MultiParaPaste() { var styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var owner = new MockData1(23, 23); var mock1 = new MockData1(23, 23); owner.InsertIntoObjSeq1(0, mock1); mock1.SimpleThree = "This has made"; var engine = new FakeRenderEngine() { Ws = 23, SegmentHeight = 13 }; var factory = new FakeRendererFactory(); factory.SetRenderer(23, engine); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 23); PaintTransform ptrans = new PaintTransform(2, 2, 96, 96, 0, 0, 96, 96); MockSite site = new MockSite(); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Site = site; var po = new MockReceiveParagraphOperations(); root.Builder.Show(Display.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 23)) .EditParagraphsUsing(po)); var layoutArgs = MakeLayoutInfo(Int32.MaxValue / 2, m_gm.VwGraphics, factory); root.Layout(layoutArgs); int x = FakeRenderEngine.SimulatedWidth("This ") + 2; var location = new Point(x, 8); Clipboard.SetDataObject("is the\r\nday that the\r\nLord "); MouseEventArgs e = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(e, Keys.None, site.m_vwGraphics, site.m_transform); Assert.That(root.CanPaste(), Is.EqualTo(true), "Should be able to paste"); root.OnEditPaste(); Assert.That(Clipboard.GetDataObject().GetData(DataFormats.StringFormat), Is.EqualTo("is the\r\nday that the\r\nLord "), "Selected String should be \"is the\nday that the\nLord \""); Assert.That(owner.ObjSeq1[0].SimpleThree + owner.ObjSeq1[1].SimpleThree + owner.ObjSeq1[2].SimpleThree, Is.EqualTo("This is the" + "day that the" + "Lord has made")); x = FakeRenderEngine.SimulatedWidth("") + 2; location = new Point(x, 8); e = new MouseEventArgs(MouseButtons.Left, 1, location.X, location.Y, 0); root.OnMouseDown(e, Keys.None, site.m_vwGraphics, site.m_transform); mock1.SimpleThree = ""; owner = new MockData1(); owner.InsertIntoObjSeq1(0, mock1); root.Builder.Show( Display.Of(() => owner.ObjSeq1).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 23)).EditParagraphsUsing(po)); root.Layout(layoutArgs); Clipboard.SetDataObject( "\r\nThis is the\r\n\r\n\r\nDay that the\r\nLord\r\n\r\nHas\r\n\r\n\r\nMade\r\nW\r\n"); Assert.That(root.CanPaste(), Is.EqualTo(true), "Should be able to paste"); root.OnEditPaste(); string testString = ""; foreach (MockData1 obj in owner.ObjSeq1) { string nextString = obj.SimpleThree; testString += "\r\n"; testString += nextString; } Assert.That(testString, Is.EqualTo("\r\nThis is the\r\n\r\n\r\nDay that the\r\nLord\r\n\r\nHas\r\n\r\n\r\nMade\r\nW\r\n\r\n")); }
public void CombinedParaOperationsFdo() { // Set up a simple multi-para layout using the ObjSeq2 and SimpleThree properties of MockData1 AssembledStyles styles = new AssembledStyles(); var root = new RootBoxFdo(styles); var text = Cache.ServiceLocator.GetInstance <ITextFactory>().Create(); //Cache.LangProject.TextsOC.Add(text); var owner = Cache.ServiceLocator.GetInstance <IStTextFactory>().Create(); text.ContentsOA = owner; IStTxtPara p1 = MakePara(owner, "Hello World"); var p2 = MakePara(owner, "This is a test"); m_actionHandler.EndUndoTask(); // Finish settin up data, we want to control from now on. var po = new StTextParagraphOperations(owner); root.Builder.Show(Display.Of(() => owner.ParagraphsOS).Using((builder, para) => builder.AddString(() => ((IStTxtPara)para).Contents)).EditParagraphsUsing(po)); SetupFakeRootSite(root); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); root.Layout(layoutInfo); var ip = root.FirstBox.SelectAtEnd(); // Insert at end of paragraph. m_actionHandler.BeginUndoTask("undo insert line break", "redo insert"); ip.InsertLineBreak(); m_actionHandler.EndUndoTask(); Assert.That(owner.ParagraphsOS.Count, Is.EqualTo(3)); Assert.That(owner.ParagraphsOS[0], Is.EqualTo(p1)); Assert.That(owner.ParagraphsOS[2], Is.EqualTo(p2)); var p3 = (IStTxtPara)owner.ParagraphsOS[1]; // empty inserted paragraph. Assert.That(p3.Contents.Length, Is.EqualTo(0)); Assert.That(p3.Contents.get_WritingSystem(0), Is.EqualTo(Cache.DefaultVernWs)); ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(root.FirstBox.Next.Next, Is.Not.Null); Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next)); // Insert at start of paragraph. To make it unambiguously the start, give p3 some data, // and since that might eventually destroy the selection, reset it. var rootHookup = ip.Hookup.ParentHookup.ParentHookup; // StringHookup, ItemHookup, Sequence UndoableUnitOfWorkHelper.Do("adjust contents", "redo", m_actionHandler, () => p3.Contents = Cache.TsStrFactory.MakeString("First insert", Cache.DefaultVernWs)); ip = (InsertionPoint)SelectionBuilder.In(rootHookup)[1].Offset(0).Install(); m_actionHandler.BeginUndoTask("undo insert line break", "redo insert"); ip.InsertLineBreak(); m_actionHandler.EndUndoTask(); Assert.That(owner.ParagraphsOS.Count, Is.EqualTo(4)); Assert.That(owner.ParagraphsOS[0], Is.EqualTo(p1)); Assert.That(owner.ParagraphsOS[2], Is.EqualTo(p3)); Assert.That(owner.ParagraphsOS[3], Is.EqualTo(p2)); var p4 = (IStTxtPara)owner.ParagraphsOS[1]; // empty inserted paragraph (before p3). Assert.That(root.FirstBox.Next.Next.Next, Is.Not.Null); Assert.That(p4.Contents.get_WritingSystem(0), Is.EqualTo(Cache.DefaultVernWs)); ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next)); // Split a paragraph. var oldObjects = owner.ParagraphsOS.ToArray(); ip = (InsertionPoint)SelectionBuilder.In(rootHookup)[2].Offset("First ".Length).Install(); m_actionHandler.BeginUndoTask("undo insert line break", "redo insert"); ip.InsertLineBreak(); m_actionHandler.EndUndoTask(); Assert.That(owner.ParagraphsOS.Count, Is.EqualTo(5)); Assert.That(owner.ParagraphsOS[1], Is.EqualTo(oldObjects[1])); Assert.That(owner.ParagraphsOS[2], Is.EqualTo(oldObjects[2])); Assert.That(owner.ParagraphsOS[4], Is.EqualTo(oldObjects[3])); // insert between 2 and 3 var p5 = (IStTxtPara)owner.ParagraphsOS[3]; // inserted paragraph. ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo(0)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next.Next)); Assert.That(((IStTxtPara)owner.ParagraphsOS[2]).Contents.Text, Is.EqualTo("First ")); Assert.That(((IStTxtPara)owner.ParagraphsOS[3]).Contents.Text, Is.EqualTo("insert")); // Combine two paragraphs by backspace at start of line. oldObjects = owner.ParagraphsOS.ToArray(); m_actionHandler.BeginUndoTask("undo backspace", "redo backspace"); ip.Backspace(); m_actionHandler.EndUndoTask(); Assert.That(owner.ParagraphsOS.Count, Is.EqualTo(4)); Assert.That(owner.ParagraphsOS[1], Is.EqualTo(oldObjects[1])); Assert.That(owner.ParagraphsOS[2], Is.EqualTo(oldObjects[2])); Assert.That(owner.ParagraphsOS[3], Is.EqualTo(oldObjects[4])); // delete item 3 ((MockSite)root.Site).DoPendingAfterNotificationTasks(); ip = (InsertionPoint)root.Selection; Assert.That(ip.StringPosition, Is.EqualTo("First ".Length)); Assert.That(ip.Para, Is.EqualTo(root.FirstBox.Next.Next)); Assert.That(((IStTxtPara)owner.ParagraphsOS[2]).Contents.Text, Is.EqualTo("First insert")); }
public void BackgroundAndUnderlinePainting() { var styles = new AssembledStyles(); RootBox root = new RootBoxFdo(styles); SetupFakeRootSite(root); var layoutInfo = HookupTests.MakeLayoutInfo(int.MaxValue / 2, m_gm.VwGraphics, 55); root.RendererFactory = layoutInfo.RendererFactory; root.Builder.Show( Paragraph.Containing( Display.Of("plain"), Display.Of("underOnYellow").Underline(FwUnderlineType.kuntSingle).BackColor(Color.Yellow) ), Paragraph.Containing( Display.Of("doubleRedOnPink").Underline(FwUnderlineType.kuntDouble, Color.Red).BackColor(Color.Pink), Display.Of("dotted").Underline(FwUnderlineType.kuntDotted), Display.Of("dottedOnYellow").Underline(FwUnderlineType.kuntDotted).BackColor(Color.Yellow) ), Paragraph.Containing( Display.Of("dashed").Underline(FwUnderlineType.kuntDashed), Display.Of("dashedRed").Underline(FwUnderlineType.kuntDashed).ForeColor(Color.Red), Display.Of("squiggle").Underline(FwUnderlineType.kuntSquiggle) ) ); root.Layout(layoutInfo); var para1 = (ParaBox)root.FirstBox; var stringBox1 = (StringBox)para1.FirstBox; var seg1 = (FakeSegment)stringBox1.Segment; // A convenient place to test that literals get Chrp with default user ws. LgCharRenderProps chrp; int ichMin, ichLim; para1.Source.GetCharProps(0, out chrp, out ichMin, out ichLim); Assert.That(chrp.ws, Is.EqualTo(layoutInfo.RendererFactory.UserWs)); // This segment has just one chunk of underline, the second run. seg1.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 10 }, Rights = new[] { 20 }, Tops = new[] { 15 } }); // Todo: add expectations for each run of underlining. var para2 = (ParaBox)para1.Next; var stringBox2 = (StringBox)para2.FirstBox; var seg2 = (FakeSegment)stringBox2.Segment; // For the double red underline, we'll pretend there are two line segments. seg2.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 5, 15 }, Rights = new[] { 10, 20 }, Tops = new[] { 15, 16 } }); seg2.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 12 }, Rights = new[] { 22 }, Tops = new[] { 13 } }); seg2.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 30 }, Rights = new[] { 41 }, Tops = new[] { 12 } }); var para3 = (ParaBox)para2.Next; var stringBox3 = (StringBox)para3.FirstBox; var seg3 = (FakeSegment)stringBox3.Segment; seg3.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 0 }, Rights = new[] { 10 }, Tops = new[] { 11 } }); seg3.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 10 }, Rights = new[] { 20 }, Tops = new[] { 11 } }); seg3.NextCharPlacementResults.Add(new FakeSegment.CharPlacementResults() { Lefts = new[] { 30 }, Rights = new[] { 40 }, Tops = new[] { 11 } }); // We want to keep track of the sequence of paint operations in all three segments. var drawActions = new List <object>(); seg1.DrawActions = seg2.DrawActions = seg3.DrawActions = drawActions; var vg = new MockGraphics(); vg.DrawActions = drawActions; var site = (MockSite)root.Site; root.Paint(vg, site.m_transform); var paintTrans = site.m_transform; // We should have asked about each run of distinct underlining. VerifyCharPlacementCall(seg1, 0, "plain".Length, "plain".Length + "underOnYellow".Length, paintTrans.SourceRect, paintTrans.DestRect, vg, 1); // para/seg 2 Rect srcRect = paintTrans.SourceRect; srcRect.top -= para1.Height; srcRect.bottom -= para1.Height; VerifyCharPlacementCall(seg2, 0, 0, "doubleRedOnPink".Length, srcRect, paintTrans.DestRect, vg, 2); VerifyCharPlacementCall(seg2, 0, "doubleRedOnPink".Length, "doubleRedOnPink".Length + "dotted".Length, srcRect, paintTrans.DestRect, vg, 1); VerifyCharPlacementCall(seg2, 0, "doubleRedOnPink".Length + "dotted".Length, "doubleRedOnPink".Length + "dotted".Length + "dottedOnYellow".Length, srcRect, paintTrans.DestRect, vg, 1); // para/seg 3 srcRect.top -= para2.Height; srcRect.bottom -= para2.Height; VerifyCharPlacementCall(seg3, 0, 0, "dashed".Length, srcRect, paintTrans.DestRect, vg, 1); VerifyCharPlacementCall(seg3, 0, "dashed".Length, "dashed".Length + "dashedRed".Length, srcRect, paintTrans.DestRect, vg, 1); VerifyCharPlacementCall(seg3, 0, "dashed".Length + "dashedRed".Length, "dashed".Length + "dashedRed".Length + "squiggle".Length, srcRect, paintTrans.DestRect, vg, 1); // Todo: eventually arrange a run where ichBase is non-zero, and check what happens. // We want to check a lot of things about the drawing. // - all the background color and underline drawing should happen before any of the text drawing. // - the right stuff should be drawn to construct the underlines // - in particular dotted, dashed, and squiggle underlines should have the gaps and alternations // aligned, even if in different segments. // Todo: check actual line segs drawn int position = 0; // in drawActions // Normal calls to Draw have the effect of painting the background. All three backgrounds // should be painted before the foreground. First we paint seg1 with background, since it has some. VerifyDraw(drawActions, ref position, seg1); // Next we draw its one horizontal line of underline. VerifyHorzLine(drawActions, ref position, 10, 20, 15, 1, new int[] { int.MaxValue }, 10); // Then segment 2's background VerifyDraw(drawActions, ref position, seg2); // And various lots of underline: double takes 2 per segment VerifyHorzLine(drawActions, ref position, 5, 10, 17, 1, new int[] { int.MaxValue }, 5); VerifyHorzLine(drawActions, ref position, 5, 10, 15, 1, new int[] { int.MaxValue }, 5); VerifyHorzLine(drawActions, ref position, 15, 20, 18, 1, new int[] { int.MaxValue }, 15); VerifyHorzLine(drawActions, ref position, 15, 20, 16, 1, new int[] { int.MaxValue }, 15); // dotted has non-trivial array of dx values (2 pix each) VerifyHorzLine(drawActions, ref position, 12, 22, 13, 1, new int[] { 2, 2 }, 12); // dotted has non-trivial array of dx values (2 pix each) VerifyHorzLine(drawActions, ref position, 30, 41, 12, 1, new int[] { 2, 2 }, 30); // No background in para 3, doesn't get a draw background call. //VerifyDraw(drawActions, ref position, seg3); // But underlines still drawn in the background phase // Dashed line VerifyHorzLine(drawActions, ref position, 0, 10, 11, 1, new int[] { 6, 3 }, 0); VerifyHorzLine(drawActions, ref position, 10, 20, 11, 1, new int[] { 6, 3 }, 10); // Todo: verify line segs drawn for squiggle. VerifyLine(drawActions, ref position, 30, 12, 32, 10, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyLine(drawActions, ref position, 32, 10, 34, 12, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyLine(drawActions, ref position, 34, 12, 36, 10, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyLine(drawActions, ref position, 36, 10, 38, 12, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyLine(drawActions, ref position, 38, 12, 40, 10, ColorUtil.ConvertColorToBGR(Color.Black)); VerifyNbDraw(drawActions, ref position, seg1); VerifyNbDraw(drawActions, ref position, seg2); VerifyDraw(drawActions, ref position, seg3); }
public void NullObjChecker() { var owner = new MockData1(55, 77); var child1 = new MockData1(55, 77); child1.SimpleThree = "This is a Test"; owner.SimpleFour = null; 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(); root.Site = site; PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); site.m_transform = ptrans; site.m_vwGraphics = m_gm.VwGraphics; root.Builder.Show(Display.OfObj(() => owner.SimpleFour).Using((bldr, md) => bldr.AddString(() => md.SimpleThree, 55))); root.Layout(layoutInfo); int invalidateWidth1 = FakeRenderEngine.SimulatedWidth(child1.SimpleThree) + 2 * RootBox.InvalidateMargin; var expectedInvalidate1 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth1, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); VerifyParagraphs(root, new string[0]); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for this unconnected object should not exist"); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "The hookup for this null object should exist"); site.RectsInvalidatedInRoot.Clear(); owner.SimpleFour = child1; int invalidateWidth2 = FakeRenderEngine.SimulatedWidth(child1.SimpleThree) + 2 * RootBox.InvalidateMargin; var expectedInvalidate2 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth2, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); VerifyParagraphs(root, new[] { child1.SimpleThree }); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(1), "The hookup for this object should exist"); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "The hookup for this object should exist"); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate2)); site.RectsInvalidatedInRoot.Clear(); owner.SimpleFour = null; int invalidateWidth3 = FakeRenderEngine.SimulatedWidth(child1.SimpleThree) + 2 * RootBox.InvalidateMargin; var expectedInvalidate3 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth3, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); VerifyParagraphs(root, new string[0]); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for this unconnected object should not exist"); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "The hookup for this null object should exist"); Assert.That(site.RectsInvalidatedInRoot, Has.Member(expectedInvalidate3)); site.RectsInvalidatedInRoot.Clear(); owner.SimpleFour = null; int invalidateWidth4 = FakeRenderEngine.SimulatedWidth(child1.SimpleThree) + 2 * RootBox.InvalidateMargin; var expectedInvalidate4 = new Rectangle(-RootBox.InvalidateMargin, -RootBox.InvalidateMargin, invalidateWidth3, engine.SegmentHeight + 2 * RootBox.InvalidateMargin); VerifyParagraphs(root, new string[0]); Assert.That(child1.SimpleThreeHookupCount, Is.EqualTo(0), "The hookup for this unconnected object should not exist"); Assert.That(owner.SimpleFourHookupCount, Is.EqualTo(1), "The hookup for this null object should exist"); Assert.That(site.RectsInvalidatedInRoot, Is.Empty, "Nothing changed, so nothing should be invalidated"); }