Dynamic: improve state management
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user