Dynamic: improve state management

This commit is contained in:
MarcinZiabek
2022-04-27 06:56:13 +02:00
parent 152304b467
commit e1101d870d
4 changed files with 74 additions and 51 deletions
+29 -22
View File
@@ -15,25 +15,29 @@ namespace QuestPDF.Examples
public int Price { get; set; } = Placeholders.Random.Next(1, 11) * 10;
public int Count { get; set; } = Placeholders.Random.Next(1, 11);
}
public struct OrdersTableState
{
public int ShownItemsCount { get; set; }
}
public class OrdersTable : IDynamicComponent
public class OrdersTable : IDynamicComponent<OrdersTableState>
{
private ICollection<OrderItem> Items { get; }
private ICollection<OrderItem> ItemsLeft { get; set; }
public OrdersTableState State { get; set; }
public OrdersTable(ICollection<OrderItem> items)
{
Items = items;
State = new OrdersTableState
{
ShownItemsCount = 0
};
}
public void Compose(DynamicContext context, IDynamicContainer container)
{
if (context.Operation == DynamicLayoutOperation.Reset)
{
ItemsLeft = new List<OrderItem>(Items);
return;
}
var header = ComposeHeader(context);
var sampleFooter = ComposeFooter(context, Enumerable.Empty<OrderItem>());
var decorationHeight = header.Size.Height + sampleFooter.Size.Height;
@@ -41,12 +45,9 @@ namespace QuestPDF.Examples
var rows = GetItemsForPage(context, decorationHeight).ToList();
var footer = ComposeFooter(context, rows.Select(x => x.Item));
if (ItemsLeft.Count > rows.Count)
if (State.ShownItemsCount + rows.Count < Items.Count)
container.HasMoreContent();
if (context.Operation == DynamicLayoutOperation.Draw)
ItemsLeft = ItemsLeft.Skip(rows.Count).ToList();
container.MinimalBox().Decoration(decoration =>
{
decoration.Header().Element(header);
@@ -59,6 +60,11 @@ namespace QuestPDF.Examples
decoration.Footer().Element(footer);
});
State = new OrdersTableState
{
ShownItemsCount = State.ShownItemsCount + rows.Count
};
}
private IDynamicElement ComposeHeader(DynamicContext context)
@@ -98,10 +104,11 @@ namespace QuestPDF.Examples
private IEnumerable<(OrderItem Item, IDynamicElement Element)> GetItemsForPage(DynamicContext context, float decorationHeight)
{
var totalHeight = decorationHeight;
var counter = Items.Count - ItemsLeft.Count + 1;
foreach (var orderItem in ItemsLeft)
foreach (var index in Enumerable.Range(State.ShownItemsCount, Items.Count - State.ShownItemsCount))
{
var item = Items.ElementAt(index);
var element = context.CreateElement(content =>
{
content
@@ -110,11 +117,11 @@ namespace QuestPDF.Examples
.Padding(5)
.Row(row =>
{
row.ConstantItem(30).Text(counter++);
row.RelativeItem().Text(orderItem.ItemName);
row.ConstantItem(50).AlignRight().Text(orderItem.Count);
row.ConstantItem(50).AlignRight().Text($"{orderItem.Price}$");
row.ConstantItem(50).AlignRight().Text($"{orderItem.Count*orderItem.Price}$");
row.ConstantItem(30).Text(index + 1);
row.RelativeItem().Text(item.ItemName);
row.ConstantItem(50).AlignRight().Text(item.Count);
row.ConstantItem(50).AlignRight().Text($"{item.Price}$");
row.ConstantItem(50).AlignRight().Text($"{item.Count*item.Price}$");
});
});
@@ -124,7 +131,7 @@ namespace QuestPDF.Examples
break;
totalHeight += elementHeight;
yield return (orderItem, element);
yield return (item, element);
}
}
}
+19 -19
View File
@@ -8,22 +8,26 @@ namespace QuestPDF.Elements
{
internal class DynamicHost : Element, IStateResettable
{
private IDynamicComponent Child { get; }
private DynamicComponentProxy Child { get; }
private object InitialComponentState { get; set; }
internal TextStyle TextStyle { get; } = new();
public DynamicHost(IDynamicComponent child)
public DynamicHost(DynamicComponentProxy child)
{
Child = child;
InitialComponentState = Child.GetState();
}
public void ResetState()
{
GetContent(Size.Zero, DynamicLayoutOperation.Reset);
Child.SetState(InitialComponentState);
}
internal override SpacePlan Measure(Size availableSpace)
{
var content = GetContent(availableSpace, DynamicLayoutOperation.Measure);
var content = GetContent(availableSpace, acceptNewState: false);
var measurement = content.Measure(availableSpace);
if (measurement.Type != SpacePlanType.FullRender)
@@ -36,11 +40,13 @@ namespace QuestPDF.Elements
internal override void Draw(Size availableSpace)
{
GetContent(availableSpace, DynamicLayoutOperation.Draw).Draw(availableSpace);
GetContent(availableSpace, acceptNewState: true).Draw(availableSpace);
}
DynamicContainer GetContent(Size availableSize, DynamicLayoutOperation operation)
private DynamicContainer GetContent(Size availableSize, bool acceptNewState)
{
var componentState = Child.GetState();
var context = new DynamicContext
{
PageNumber = PageContext.CurrentPage,
@@ -48,13 +54,15 @@ namespace QuestPDF.Elements
Canvas = Canvas,
TextStyle = TextStyle,
AvailableSize = availableSize,
Operation = operation
AvailableSize = availableSize
};
var container = new DynamicContainer();
Child.Compose(context, container);
if (!acceptNewState)
Child.SetState(componentState);
container.VisitChildren(x => x?.Initialize(PageContext, Canvas));
container.VisitChildren(x => (x as IStateResettable)?.ResetState());
@@ -62,23 +70,15 @@ namespace QuestPDF.Elements
}
}
public enum DynamicLayoutOperation
{
Reset,
Measure,
Draw
}
public class DynamicContext
{
internal IPageContext PageContext { get; set; }
internal ICanvas Canvas { get; set; }
internal TextStyle TextStyle { get; set; }
public int PageNumber { get; internal set; }
public Size AvailableSize { get; internal set; }
public DynamicLayoutOperation Operation { get; internal set; }
public IDynamicElement CreateElement(Action<IContainer> content)
{
var container = new DynamicElement();
@@ -5,14 +5,10 @@ namespace QuestPDF.Fluent
{
public static class DynamicComponentExtensions
{
public static void Dynamic<TDynamic>(this IContainer element) where TDynamic : IDynamicComponent, new()
public static void Dynamic<TState>(this IContainer element, IDynamicComponent<TState> dynamicElement) where TState : struct
{
element.Dynamic(new TDynamic());
}
public static void Dynamic(this IContainer element, IDynamicComponent dynamicElement)
{
element.Element(new DynamicHost(dynamicElement));
var componentProxy = DynamicComponentProxy.CreateFrom(dynamicElement);
element.Element(new DynamicHost(componentProxy));
}
}
}
+23 -3
View File
@@ -1,9 +1,29 @@
using QuestPDF.Elements;
using System;
using QuestPDF.Elements;
using QuestPDF.Helpers;
namespace QuestPDF.Infrastructure
{
public interface IDynamicComponent
internal class DynamicComponentProxy
{
void Compose(DynamicContext context, IDynamicContainer container);
internal Action<object> SetState { get; private set; }
internal Func<object> GetState { get; private set; }
internal Action<DynamicContext, IDynamicContainer> Compose { get; private set; }
internal static DynamicComponentProxy CreateFrom<TState>(IDynamicComponent<TState> component) where TState : struct
{
return new DynamicComponentProxy
{
GetState = () => component.State,
SetState = x => component.State = (TState)x,
Compose = component.Compose
};
}
}
public interface IDynamicComponent<TState> where TState : struct
{
TState State { get; set; }
void Compose(DynamicContext context, IDynamicContainer dynamicContainer);
}
}