/// <summary> /// Expand part of the specified lazy box. /// Todo: implement firstItemToExpand, limItemToExpand. /// </summary> internal void ExpandItems(LazyBox <T> source, int firstItemToExpand, int limItemToExpand, int topOfFirstItemToExpand, int bottomOfLastItemToExpand) { var root = ContainingBox.Root; int oldRootHeight = root.Height; var builder = ContainingBox.Builder; builder.CurrentHookup = this; var objects = new T[limItemToExpand - firstItemToExpand]; source.Items.CopyTo(firstItemToExpand, objects, 0, limItemToExpand - firstItemToExpand); int insertAt = Children.IndexOf(source); // default, insert in place of source (or before it). if (firstItemToExpand > 0) { // we will keep source to represent the unexpanded items at the start. insertAt += 1; // insert right after source. if (limItemToExpand < source.Items.Count) { // We will have to make a new lazy box to insert after the expanded items var newLazyBox = new LazyBox <T>(source.Style, this, source.Items.Skip(limItemToExpand)); Children.Insert(insertAt, newLazyBox); ContainingBox.InsertBox(newLazyBox, source); // We will still insert the expanded items after source, before newLazyBox. } // Remove the items no longer considered part of source. source.RemoveItems(firstItemToExpand, source.Items.Count - firstItemToExpand); } else if (limItemToExpand < source.Items.Count) { // We will keep source to represent the unexpanded items at the end. source.RemoveItems(0, limItemToExpand); } else { // Everything is being expanded: get rid of source altogether. ContainingBox.RemoveBoxes(source, source); Children.RemoveAt(insertAt); // get rid of the lazy box from hookup collection } // Generate the new items. for (int i = 0; i < objects.Length; i++) { BuildAnItemDisplay(builder, objects[i], insertAt + i); } using (var gh = ContainingBox.Root.Site.DrawingInfo) { ContainingBox.RelayoutWithParents(gh, true); } // Notify root of changed overall size and possibly scroll position. root.RaiseLazyExpanded(new RootBox.LazyExpandedEventArgs() { EstimatedTop = topOfFirstItemToExpand, EstimatedBottom = bottomOfLastItemToExpand, DeltaHeight = root.Height - oldRootHeight }); }
///// <summary> ///// Update the display of the objects from min to lim (whether or not the sequence of objects has changed). ///// </summary> //public void UpdateDisplayOfSubSequence(int min, int lim) //{ // UpdateDisplayOfSubSequence(Fetcher().ToList(), min, lim, lim); //} /// <summary> /// Given the complete list of Ts that is the current property to be displayed, /// we want to replace the old display of objects from firstDiff to limCurrent /// with a newly created display of the propContent from firstDiff to limNew. /// </summary> private void UpdateDisplayOfSubSequence(List <T> propContent, int firstDiff, int limNew, int limCurrent) { var builder = ContainingBox.Builder; builder.CurrentHookup = this; // Items 0 to firstDiff are the same. // Items limCurrent to end of current equal items limNew to end of new. // Items firstDiff to limCurrent need to be deleted. if (limCurrent > firstDiff) { if (ContainingBox is ParaBox) { // remove runs. Enhance: allow for possibly empty items. Allow boxes nested in para. var firstDelChild = (LiteralStringParaHookup)((ItemHookup)Children[firstDiff]).Children.First(); var lastDelChild = (LiteralStringParaHookup)((ItemHookup)Children[limCurrent - 1]).Children.Last(); ((ParaBox)ContainingBox).RemoveRuns(firstDelChild.ClientRunIndex, lastDelChild.ClientRunIndex - firstDelChild.ClientRunIndex + 1); } else { // remove child boxes. Enhance: allow for possibly empty items. var firstGoner = ((ItemHookup)Children[firstDiff]).FirstBox; var lastGoner = ((ItemHookup)Children[limCurrent - 1]).LastBox; ContainingBox.RemoveBoxes(firstGoner, lastGoner); } for (int i = firstDiff; i < limCurrent; i++) { var disposeChild = Children[i] as IDisposable; if (disposeChild != null) { disposeChild.Dispose(); } } Children.RemoveRange(firstDiff, limCurrent - firstDiff); } // Items firstDiff to limNew are new and must be inserted. for (int i = firstDiff; i < limNew; i++) { BuildAnItemDisplay(builder, propContent[i], i); } using (var gh = ContainingBox.Root.Site.DrawingInfo) { ContainingBox.RelayoutWithParents(gh); } }
/// <summary> /// Sent when the contents of the property we are monitoring changes. /// </summary> public override void PropChanged(object sender, EventArgs args) { var builder = ContainingBox.Builder; builder.CurrentHookup = this; var newT = Fetcher(); var currentT = (T)Children[0].Target; if (ContainingBox is ParaBox) { // remove runs. Enhance: allow for possibly empty items. Allow boxes nested in para. var firstDelChild = (LiteralStringParaHookup)((ItemHookup)Children[0]).Children.First(); ((ParaBox)ContainingBox).RemoveRuns(firstDelChild.ClientRunIndex, 1); } else { // remove child boxes. Enhance: allow for possibly empty items. var firstGoner = ((ItemHookup)Children[0]).FirstBox; var lastGoner = ((ItemHookup)Children[0]).LastBox; if (firstGoner != null || lastGoner != null) { ContainingBox.RemoveBoxes(firstGoner, lastGoner); } } var disposeChild = Children[0] as IDisposable; if (disposeChild != null) { disposeChild.Dispose(); } Children.RemoveRange(0, 1); // Items firstDiff to limNew are new and must be inserted. BuildAnItemDisplay(builder, newT, 0); using (var gh = ContainingBox.Root.Site.DrawingInfo) { ContainingBox.RelayoutWithParents(gh); } }
public TestCaseCachedBufferedContainer() : base(5, 2) { string[] labels = { "uncached", "cached", "uncached with rotation", "cached with rotation", "uncached with movement", "cached with movement", "uncached with parent scale", "cached with parent scale", "uncached with parent scale&fade", "cached with parent scale&fade", }; var boxes = new List <ContainingBox>(); for (int i = 0; i < Rows * Cols; ++i) { ContainingBox box; Cell(i).AddRange(new Drawable[] { new SpriteText { Text = labels[i], Font = new FontUsage(size: 20), }, box = new ContainingBox(i >= 6, i >= 8) { Child = new CountingBox(i == 2 || i == 3, i == 4 || i == 5) { CacheDrawnFrameBuffer = i % 2 == 1, }, } }); boxes.Add(box); } AddWaitStep("wait for boxes", 5); // ensure uncached is always updating children. AddAssert("box 0 count > 0", () => boxes[0].Count > 0); AddAssert("even box counts equal", () => boxes[0].Count == boxes[2].Count && boxes[2].Count == boxes[4].Count && boxes[4].Count == boxes[6].Count); // ensure cached is never updating children. AddAssert("box 1 count is 1", () => boxes[1].Count == 1); // ensure rotation changes are invalidating cache (for now). AddAssert("box 2 count > 0", () => boxes[2].Count > 0); AddAssert("box 2 count equals box 3 count", () => boxes[2].Count == boxes[3].Count); // ensure cached with only translation is never updating children. AddAssert("box 5 count is 1", () => boxes[1].Count == 1); // ensure a parent scaling is invalidating cache. AddAssert("box 5 count equals box 6 count", () => boxes[5].Count == boxes[6].Count); // ensure we don't break on colour invalidations (due to blanket invalidation logic in Drawable.Invalidate). AddAssert("box 7 count equals box 8 count", () => boxes[7].Count == boxes[8].Count); }
public override void PropChanged(object sender, EventArgs args) { var builder = ContainingBox.Builder; builder.CurrentHookup = this; var newItems = Fetcher().ToArray(); // See how much we can keep at the start of the sequence; int firstChildToReplace = 0; int firstItemToReplace = 0; while (firstChildToReplace < Children.Count) { var group = ((IItemsHookup)Children[firstChildToReplace]).ItemGroup; if (!DoesGroupMatch(group, newItems, firstItemToReplace)) { break; } firstChildToReplace++; firstItemToReplace += group.Length; } // See how much we can keep at the end of the sequence; int limChildToReplace = Children.Count; int limItemToReplace = newItems.Length; while (limChildToReplace > firstChildToReplace) { var group = ((IItemsHookup)Children[limChildToReplace - 1]).ItemGroup; if (!DoesGroupMatch(group, newItems, limItemToReplace - group.Length)) { break; } limChildToReplace--; limItemToReplace -= group.Length; } Box firstBoxToRemove = null; for (int i = firstChildToReplace; i < limChildToReplace && firstBoxToRemove == null; i++) { firstBoxToRemove = ((IItemsHookup)Children[i]).FirstBox; } Box lastBoxToRemove = GetLastBoxInRange(firstChildToReplace, limChildToReplace); Children.RemoveRange(firstChildToReplace, limChildToReplace - firstChildToReplace); if (firstBoxToRemove != null) { ContainingBox.RemoveBoxes(firstBoxToRemove, lastBoxToRemove); } if (limItemToReplace > firstItemToReplace) { var lazyBox = new LazyBox <T>(builder.NestedBoxStyles, this, newItems.Skip(firstItemToReplace).Take(limItemToReplace - firstItemToReplace)); ContainingBox.InsertBox(lazyBox, GetLastBoxInRange(0, firstChildToReplace)); Children.Insert(firstChildToReplace, lazyBox); using (var gh = ContainingBox.Root.Site.DrawingInfo) { lazyBox.RelayoutWithParents(gh); } } else if (limChildToReplace > firstChildToReplace) { // pure deletion. using (var gh = ContainingBox.Root.Site.DrawingInfo) { ContainingBox.RelayoutWithParents(gh); } } }