Browse Source

Dynamic element prototype

Marcin Ziąbek 4 years ago
parent
commit
d3e8386418

+ 115 - 0
QuestPDF.Examples/DynamicExamples.cs

@@ -0,0 +1,115 @@
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using QuestPDF.Elements;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples
+{
+    public class TableWithSubtotals : IDynamic
+    {
+        private ICollection<int> Values { get; }
+        private Queue<int> ValuesQueue { get; set; }
+
+        public TableWithSubtotals(ICollection<int> values)
+        {
+            Values = values;
+        }
+        
+        public void Reset()
+        {
+            ValuesQueue = new Queue<int>(Values);
+        }
+
+        public bool Compose(DynamicContext context, IContainer container)
+        {
+            var internalQueue = new Queue<int>(ValuesQueue);
+            
+            container.Box().Border(2).Background(Colors.Grey.Lighten3).Stack(stack =>
+            {
+                var summaryHeight = 40f;
+                
+                var totalHeight = summaryHeight;
+                var total = 0;
+                
+                while (internalQueue.Any())
+                {
+                    var value = internalQueue.Peek();
+
+                    var structure = context.Content(content =>
+                    {
+                        content
+                            .Padding(10)
+                            .Text(value);
+                    });
+
+                    var structureHeight = structure.Measure().Height;
+
+                    if (totalHeight + structureHeight > context.AvailableSize.Height)
+                        break;
+
+                    totalHeight += structureHeight;
+                    total += value;
+
+                    stack.Item().Border(1).Element(structure);
+                    internalQueue.Dequeue();
+                }
+                
+                stack
+                    .Item()
+                    .ShowEntire()
+                    .Border(2)
+                    .Background(Colors.Grey.Lighten1)
+                    .Padding(10)
+                    .Text($"Total: {total}", TextStyle.Default.SemiBold());
+            });
+
+            if (context.IsDrawStep)
+                ValuesQueue = internalQueue;
+            
+            return internalQueue.Any();
+        }
+    }
+    
+    public static class DynamicExamples
+    {
+        [Test]
+        public static void Dynamic()
+        {
+            RenderingTest
+                .Create()
+                .PageSize(300, 500)
+                .FileName()
+                .ShowResults()
+                .Render(container =>
+                {
+                    var values = Enumerable.Range(0, 15).ToList();
+                    
+                    container
+                        .Background(Colors.White)
+                        .Padding(25)
+                        .Decoration(decoration =>
+                        {
+                            decoration
+                                .Header()
+                                .PaddingBottom(5)
+                                .Text(text =>
+                                {
+                                    text.DefaultTextStyle(TextStyle.Default.SemiBold().Color(Colors.Blue.Darken2).Size(16));
+                                    text.Span("Page ");
+                                    text.CurrentPageNumber();
+                                    text.Span(" of ");
+                                    text.TotalPages();
+                                });
+                            
+                            decoration
+                                .Content()
+                                .Dynamic(new TableWithSubtotals(values));
+                        });
+                });
+        }
+    }
+}

+ 1 - 1
QuestPDF/Drawing/DocumentGenerator.cs

@@ -125,7 +125,7 @@ namespace QuestPDF.Drawing
 
             content.HandleVisitor(x =>
             {
-                x.CreateProxy(y => new DebuggingProxy(debuggingState, x));
+                x.CreateProxy(y => new DebuggingProxy(debuggingState, y));
             });
 
             return debuggingState;

+ 97 - 0
QuestPDF/Elements/Dynamic.cs

@@ -0,0 +1,97 @@
+using System;
+using QuestPDF.Drawing;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Elements
+{
+    internal class DynamicHost : Element, IStateResettable
+    {
+        private IDynamic Child { get; }
+
+        public DynamicHost(IDynamic child)
+        {
+            Child = child;
+        }
+
+        public void ResetState()
+        {
+            Child.Reset();
+        }
+        
+        internal override SpacePlan Measure(Size availableSpace)
+        {
+            var content = GetContent(availableSpace, false);
+            var measurement = content.element.Measure(availableSpace);
+
+            if (measurement.Type == SpacePlanType.FullRender)
+                return content.hasMore ? SpacePlan.PartialRender(measurement) : measurement;
+
+            return measurement;
+        }
+
+        internal override void Draw(Size availableSpace)
+        {
+            GetContent(availableSpace, true).element.Draw(availableSpace);
+        }
+
+        (Element element, bool hasMore) GetContent(Size availableSize, bool isDrawState)
+        {
+            var context = new DynamicContext
+            {
+                PageContext = PageContext,
+                Canvas = Canvas,
+                
+                AvailableSize = availableSize,
+                IsDrawStep = isDrawState
+            };
+            
+            var container = new Container();
+            var hasMore = Child.Compose(context, container);
+            
+            container.HandleVisitor(x => x?.Initialize(PageContext, Canvas));
+            container.HandleVisitor(x => (x as IStateResettable)?.ResetState());
+            
+            return (container, hasMore);
+        }
+    }
+    
+    public class DynamicContext
+    {
+        internal IPageContext PageContext { get; set; }
+        internal ICanvas Canvas { get; set; }
+        
+        public Size AvailableSize { get; internal set; }
+        public bool IsDrawStep { get; internal set; }
+        
+        public IDynamicElement Content(Action<IContainer> content)
+        {
+            var container = new DynamicElement(() => AvailableSize);
+            content(container);
+            
+            container.HandleVisitor(x => x?.Initialize(PageContext, Canvas));
+            container.HandleVisitor(x => (x as IStateResettable)?.ResetState());
+            
+            return container;
+        }
+    }
+
+    public interface IDynamicElement : IElement
+    {
+        Size Measure();
+    }
+
+    internal class DynamicElement : ContainerElement, IDynamicElement
+    {
+        private Func<Size> AvailableSizeSource { get; }
+
+        public DynamicElement(Func<Size> availableSizeSource)
+        {
+            AvailableSizeSource = availableSizeSource;
+        }
+        
+        Size IDynamicElement.Measure()
+        {
+            return Measure(AvailableSizeSource());
+        }
+    }
+}

+ 10 - 0
QuestPDF/Fluent/ElementExtensions.cs

@@ -41,6 +41,16 @@ namespace QuestPDF.Fluent
             return handler(parent.Container()).Container();
         }
         
+        public static void Dynamic<TDynamic>(this IContainer element) where TDynamic : IDynamic, new()
+        {
+            element.Dynamic(new TDynamic());
+        }
+        
+        public static void Dynamic(this IContainer element, IDynamic dynamicElement)
+        {
+            element.Element(new DynamicHost(dynamicElement));
+        }
+        
         public static IContainer AspectRatio(this IContainer element, float ratio, AspectRatioOption option = AspectRatioOption.FitWidth)
         {
             return element.Element(new AspectRatio

+ 0 - 5
QuestPDF/Infrastructure/Element.cs

@@ -28,10 +28,5 @@ namespace QuestPDF.Infrastructure
         
         internal abstract SpacePlan Measure(Size availableSpace);
         internal abstract void Draw(Size availableSpace);
-
-        protected virtual IEnumerable<string> GetDebugInformation()
-        {
-            yield break;
-        }
     }
 }

+ 10 - 0
QuestPDF/Infrastructure/IDynamic.cs

@@ -0,0 +1,10 @@
+using QuestPDF.Elements;
+
+namespace QuestPDF.Infrastructure
+{
+    public interface IDynamic
+    {
+        void Reset();
+        bool Compose(DynamicContext context, IContainer container);
+    }
+}