diff --git a/src/Engine/Draw/Layout/SkiaLayout.ColumnRow.cs b/src/Engine/Draw/Layout/SkiaLayout.ColumnRow.cs index 666e1a45..2c6e4b23 100644 --- a/src/Engine/Draw/Layout/SkiaLayout.ColumnRow.cs +++ b/src/Engine/Draw/Layout/SkiaLayout.ColumnRow.cs @@ -49,6 +49,7 @@ void LayoutCell(ScaledSize measured, ControlInStack cell, SkiaControl child, flo { if (!measured.IsEmpty) { + child.Arrange(cell.Area, measured.Units.Width, measured.Units.Height, scale); var maybeArranged = child.Destination; @@ -73,7 +74,13 @@ void LayoutCell(ScaledSize measured, ControlInStack cell, SkiaControl child, flo } } - + /// + /// Cell.Area contains the area for layout + /// + /// + /// + /// + public record SecondPassArrange(ControlInStack Cell, SkiaControl Child, float Scale); [MethodImpl(MethodImplOptions.AggressiveInlining)] protected float GetSpacingForIndex(int forIndex, float scale) @@ -291,6 +298,10 @@ public virtual ScaledSize MeasureStack(SKRect rectForChildrenPixels, float scale template = ChildrenFactory.GetTemplateInstance(); } + var maybeSecondPass = Type == LayoutType.Row && + (float.IsInfinity(rectForChild.Bottom) || float.IsInfinity(rectForChild.Top)); + List listSecondPass = new(); + //measure //left to right, top to bottom for (var row = 0; row < layoutStructure.MaxRows; row++) @@ -380,6 +391,18 @@ public virtual ScaledSize MeasureStack(SKRect rectForChildrenPixels, float scale else { measured = MeasureAndArrangeCell(rectFitChild, cell, child, scale); + if (maybeSecondPass) //has infinity in destination + { + if (Type == LayoutType.Column && child.HorizontalOptions != LayoutOptions.Start) + { + listSecondPass.Add(new(cell, child, scale)); + } + else + if (Type == LayoutType.Row && child.VerticalOptions != LayoutOptions.Start) + { + listSecondPass.Add(new(cell, child, scale)); + } + } } if (!measured.IsEmpty) @@ -412,6 +435,33 @@ public virtual ScaledSize MeasureStack(SKRect rectForChildrenPixels, float scale }//end of iterate rows + //second layout pass for if we had infinity + foreach (var secondPass in listSecondPass) + { + if (float.IsInfinity(secondPass.Cell.Area.Bottom)) + { + secondPass.Cell.Area = new(secondPass.Cell.Area.Left, secondPass.Cell.Area.Top, secondPass.Cell.Area.Right, secondPass.Cell.Area.Top + stackHeight); + } + else + if (float.IsInfinity(secondPass.Cell.Area.Top)) + { + secondPass.Cell.Area = new(secondPass.Cell.Area.Left, secondPass.Cell.Area.Bottom - stackHeight, secondPass.Cell.Area.Right, secondPass.Cell.Area.Bottom); + } + + if (float.IsInfinity(secondPass.Cell.Area.Right)) + { + secondPass.Cell.Area = new(secondPass.Cell.Area.Left, secondPass.Cell.Area.Top, secondPass.Cell.Area.Left + stackWidth, secondPass.Cell.Area.Bottom); + } + else + if (float.IsInfinity(secondPass.Cell.Area.Left)) + { + secondPass.Cell.Area = new(secondPass.Cell.Area.Right - stackWidth, secondPass.Cell.Area.Top, secondPass.Cell.Area.Right, secondPass.Cell.Area.Bottom); + } + + LayoutCell(secondPass.Child.MeasuredSize, secondPass.Cell, secondPass.Child, secondPass.Scale); + } + + if (IsTemplated && useOneTemplate) { ChildrenFactory.ReleaseView(template); @@ -529,325 +579,6 @@ bool ProcessStructure(int i, SkiaControl control) return structure; } - //2 passes for FILL LAYOUT OPTIONS - //not using this as fps drops - /* - case LayoutType.Column: - case LayoutType.Row: - - if (ViewsMaster.GetChildrenCount() > 0) - { - float AddSpacing(int pos) - { - var spacing = 0.0f; - if (pos > 0) - { - spacing = (float)(Spacing * scale); - } - return spacing; - } - - SKRect rectForChild = rectForChildrenPixels.Clone(); - - var column = 0; - var row = 0; - var rows = new List>(); - var columns = new List(); - int maxColumns = MaxColumns; // New MaxColumns property - int maxRows = MaxRows; // New MaxRows property - - for (int index = 0; index < ViewsMaster.GetChildrenCount(); index++) - { - // vertical stack or if maxColumns is exceeded - if (Type == LayoutType.Column && maxColumns < 1 || (maxColumns > 0 && column >= maxColumns) || LineBreaks.Contains(index)) - { - if (index > 0) - { - //insert a vbreak between all children - rows.Add(columns); - columns = new(); - column = 0; - row++; - } - } - - // If maxRows is reached and exceeded, break the loop - if (maxRows > 0 && row >= maxRows) - { - break; - } - - columns.Add(new ControlInStack { ControlIndex = index }); - column++; - } - - rows.Add(columns); - StackStructure = rows; - - SkiaControl template = null; - if (IsTemplated) - { - template = ViewsMaster.GetTemplateInstance(); - } - - var stackHeight = 0.0f; - var stackWidth = 0.0f; - var maxHeight = 0.0f; - var maxWidth = 0.0f; - bool hasFills = false; - - var takenHeight = 0f; - var takenWidth = 0f; - - //PASS 1 - for (row = 0; row < rows.Count; row++) - { - maxHeight = 0.0f; //max row height - rectForChild.Top += AddSpacing(row); - - stackWidth = 0.0f; - var columnsCount = rows[row].Count; - - if (!DynamicColumns && columnsCount < maxColumns) - { - columnsCount = Math.Min(1, MaxColumns); - } - - var widthPerColumn = (float)(columnsCount > 1 ? - (rectForChildrenPixels.Width - (columnsCount - 1) * Spacing * scale) / columnsCount : - rectForChildrenPixels.Width); - - for (column = 0; column < columnsCount; column++) - { - rectForChild.Left += AddSpacing(column); - - var rectFitChild = new SKRect(rectForChild.Left, rectForChild.Top, rectForChild.Left + widthPerColumn, rectForChild.Bottom); - - var cell = rows[row][column]; - - var child = ViewsMaster.GetChildAt(cell.ControlIndex, template); - - //Trace.WriteLine($"[PASS 1] LAYOUT - {child.Tag}"); - - if (child == null) - { - ContentSize = ScaledSize.FromPixels(rectForChildrenPixels.Width, rectForChildrenPixels.Height, scale); - widthConstraint = AdaptWidthContraintToContentRequest(widthConstraint, ContentSize, constraintLeft + constraintRight); - heightConstraint = AdaptHeightContraintToContentRequest(heightConstraint, ContentSize, constraintTop + constraintBottom); - return SetMeasured(widthConstraint, heightConstraint, scale); - } - else - { - //reset calculated stuff as this might be a template being reused - if (child is SkiaControl control) - { - control.InvalidateInternal(); - } - } - - if ( - (Type == LayoutType.Row && child.HorizontalOptions.Alignment == LayoutAlignment.Fill && child.WidthRequest < 0) - || - (Type == LayoutType.Column && child.VerticalOptions.Alignment == LayoutAlignment.Fill && child.HeightRequest < 0) - ) - { - hasFills = true; - cell.Tmp = rectFitChild; - cell.Expands = true; - continue; - } - - cell.Expands = false; - - var measured = MeasureChild(child, - rectFitChild.Width, rectFitChild.Height, - scale); - - cell.Measured = ScaledSize.FromPixels(measured, scale); - - if (measured != SKSize.Empty) - { - child.Arrange(rectFitChild, measured.Width, measured.Height, scale); - - var maybeArranged = child.Destination; - - var arranged = new SKRect(rectFitChild.Left, rectFitChild.Top, - rectFitChild.Left + cell.Measured.Pixels.Width, - rectFitChild.Top + cell.Measured.Pixels.Height); - - if (float.IsNormal(maybeArranged.Height)) - { - arranged.Top = maybeArranged.Top; - arranged.Bottom = maybeArranged.Bottom; - } - if (float.IsNormal(maybeArranged.Width)) - { - arranged.Left = maybeArranged.Left; - arranged.Right = maybeArranged.Right; - } - - cell.Destination = arranged; - - var width = measured.Width; - var height = measured.Height; - - stackWidth += width + AddSpacing(column); - - if (measured.Height > maxHeight) - maxHeight = height; - - //offset --> - rectForChild.Left += (float)(width); - } - - }//end of iterate columns - - if (stackWidth > maxWidth) - maxWidth = stackWidth; - - stackHeight += maxHeight + AddSpacing(row); - rectForChild.Top += (float)(maxHeight); - - rectForChild.Left = 0; //reset to start - - }//end of iterate rows - - //PASS 2 - if (hasFills) - { - rectForChild = rectForChildrenPixels.Clone(); - - var offsetMoveY = 0f; - - for (row = 0; row < rows.Count; row++) - { - var offsetMoveX = 0f; - - rectForChild.Top += AddSpacing(row); - stackWidth = 0.0f; - maxHeight = 0.0f; - - var columnsCount = rows[row].Count; - - if (!DynamicColumns && columnsCount < maxColumns) - { - columnsCount = Math.Min(1, MaxColumns); - } - - var widthPerColumn = (float)(columnsCount > 1 ? - (rectForChildrenPixels.Width - (columnsCount - 1) * Spacing * scale) / columnsCount : - rectForChildrenPixels.Width); - - for (column = 0; column < columnsCount; column++) - { - - var cell = rows[row][column]; - - var child = ViewsMaster.GetChildAt(cell.ControlIndex, template); - - if (!cell.Expands) - { - if (offsetMoveY > 0 || offsetMoveX > 0) - { - //newly filled made us move - var itBecaime = new SKRect(cell.Destination.Left + offsetMoveX, cell.Destination.Top + offsetMoveY, - cell.Destination.Right + offsetMoveX, cell.Destination.Bottom + offsetMoveY); - cell.Destination = itBecaime; - } - rectForChild.Left += cell.Measured.Pixels.Width + AddSpacing(column); - - //usual end of row - stackWidth += cell.Measured.Pixels.Width + AddSpacing(column); - maxHeight = cell.Measured.Pixels.Height; - continue; - } - - var availableWidth = rectForChildrenPixels.Width - - (rectForChild.Left - rectForChildrenPixels.Left) - - CalculateTakenWidthRight(row, column, (float)(Spacing * scale)); - - var availableHeight = rectForChildrenPixels.Height - - (rectForChild.Top - rectForChildrenPixels.Top) - - CalculateTakenHeightBelow(row, (float)(Spacing * scale)); - - //Trace.WriteLine($"[PASS 2] LAYOUT - {child.Tag}"); - - var measured = MeasureChild(child, - availableWidth, availableHeight, scale); - - cell.Measured = ScaledSize.FromPixels(measured, scale); - - if (measured != SKSize.Empty) - { - //child.InvalidateChildren(); - child.Arrange(new SKRect(rectForChild.Left, rectForChild.Top, rectForChild.Left + availableWidth, rectForChild.Top + availableHeight), - measured.Width, measured.Height, scale); - - //child.InvalidateChildren(); - - var maybeArranged = child.Destination; - - var arranged = new SKRect(cell.Tmp.Left, cell.Tmp.Top, - cell.Tmp.Left + cell.Measured.Pixels.Width, - cell.Tmp.Top + cell.Measured.Pixels.Height); - - if (float.IsNormal(maybeArranged.Height)) - { - arranged.Top = maybeArranged.Top; - arranged.Bottom = maybeArranged.Bottom; - } - if (float.IsNormal(maybeArranged.Width)) - { - arranged.Left = maybeArranged.Left; - arranged.Right = maybeArranged.Right; - } - - cell.Destination = arranged; - - var width = measured.Width; - var height = measured.Height; - - offsetMoveY += height; - offsetMoveX += width; - - stackWidth += width + AddSpacing(column); - - if (measured.Height > maxHeight) - maxHeight = height; - - //offset --> - rectForChild.Left += (float)(width); - } - }//end of iterate columns - - if (stackWidth > maxWidth) - maxWidth = stackWidth; - - stackHeight += maxHeight + AddSpacing(row); - rectForChild.Top += (float)(maxHeight); - rectForChild.Left = 0; //reset to start - - }//end of iterate rows - - } - - if (IsTemplated) - { - ViewsMaster.ReleaseView(template); - } - - ContentSize = ScaledSize.FromPixels(maxWidth, stackHeight, scale); - - widthConstraint = AdaptWidthContraintToContentRequest(widthConstraint, ContentSize, constraintLeft + constraintRight); - heightConstraint = AdaptHeightContraintToContentRequest(heightConstraint, ContentSize, constraintBottom + constraintTop); - - childrenmeasured = true; - } - - break; - */ - - #endregion } diff --git a/src/Engine/Draw/SkiaControl.cs b/src/Engine/Draw/SkiaControl.cs index 2cd60e0e..71c86cf9 100644 --- a/src/Engine/Draw/SkiaControl.cs +++ b/src/Engine/Draw/SkiaControl.cs @@ -4731,7 +4731,8 @@ public CachedObject RenderObjectPrevious { var kill = _renderObjectPrevious; _renderObjectPrevious = value; - kill?.Dispose(); + if (kill != null && UsingCacheType != SkiaCacheType.Image && UsingCacheType == SkiaCacheType.ImageComposite) + DisposeObject(kill); } } } @@ -5009,9 +5010,17 @@ protected virtual bool UseRenderingObject(SkiaDrawingContext context, SKRect rec if (RenderObjectPrevious != null && RenderObjectPreviousNeedsUpdate) { - DisposeObject(RenderObjectPrevious); + var kill = RenderObjectPrevious; RenderObjectPrevious = null; RenderObjectPreviousNeedsUpdate = false; + if (kill != null) + { + + Tasks.StartDelayed(TimeSpan.FromSeconds(3.5), () => + { + kill.Dispose(); + }); + } } if (cache != null) @@ -5555,7 +5564,10 @@ protected void CreateRenderingObjectAndPaint( } if (usingCacheType != SkiaCacheType.ImageDoubleBuffered && usingCacheType != SkiaCacheType.ImageComposite) { - oldObject.Dispose(); + Tasks.StartDelayed(TimeSpan.FromSeconds(3.5), () => + { + oldObject.Dispose(); + }); } } diff --git a/src/Engine/Draw/Text/SkiaLabel.cs b/src/Engine/Draw/Text/SkiaLabel.cs index 10d73f11..14d714a5 100644 --- a/src/Engine/Draw/Text/SkiaLabel.cs +++ b/src/Engine/Draw/Text/SkiaLabel.cs @@ -454,34 +454,34 @@ public override ScaledSize Measure(float widthConstraint, float heightConstraint if (IsDisposed || IsDisposing) return ScaledSize.Default; - ReplaceFont(); - //background measuring or invisible or self measure from draw because layout will never pass -1 if (IsMeasuring || !CanDraw || (widthConstraint < 0 || heightConstraint < 0)) { return MeasuredSize; } - var request = CreateMeasureRequest(widthConstraint, heightConstraint, scale); - if (request.IsSame) + IsMeasuring = true; + + try { - return MeasuredSize; - } + var request = CreateMeasureRequest(widthConstraint, heightConstraint, scale); + if (request.IsSame) + { + return MeasuredSize; + } - SetupDefaultPaint(scale); + ReplaceFont(); - if (PaintDefault.Typeface == null) - { - UpdateFont(); - return MeasuredSize; - } + SetupDefaultPaint(scale); - var constraints = GetMeasuringConstraints(request); + if (PaintDefault.Typeface == null) + { + UpdateFont(); + return MeasuredSize; + } - IsMeasuring = true; + var constraints = GetMeasuringConstraints(request); - try - { var textWidthPixels = 0f; var textHeightPixels = 0f; @@ -745,13 +745,13 @@ public override void OnDisposing() _spans.CollectionChanged -= OnCollectionChanged; - PaintDefault.Typeface = null; //preserve cached font from disposing + PaintDefault.Typeface = SKTypeface.Default; //preserve cached font from disposing PaintDefault.Dispose(); - PaintStroke.Typeface = null; //preserve cached font from disposing + PaintStroke.Typeface = SKTypeface.Default; //preserve cached font from disposing PaintStroke.Dispose(); - PaintShadow.Typeface = null; //preserve cached font from disposing + PaintShadow.Typeface = SKTypeface.Default; //preserve cached font from disposing PaintShadow.Dispose(); PaintDeco.Dispose(); @@ -1458,9 +1458,10 @@ protected virtual async void UpdateFont() protected void ReplaceFont() { - if (_replaceFont != null) + var newFont = _replaceFont; + if (newFont != null) { - TypeFace = _replaceFont; + TypeFace = newFont; _replaceFont = null; OnFontUpdated(); } @@ -1811,12 +1812,14 @@ public static SKShaper.Result GetShapedText(SKShaper shaper, string text, float if (paint == null) throw new ArgumentNullException(nameof(paint)); - using var font = paint.ToFont(); + var font = paint.ToFont(); + if (font != null && shaper.Typeface != null) { font.Typeface = shaper.Typeface; // shape the text var result = shaper.Shape(text, x, y, paint); + return result; } @@ -1883,7 +1886,7 @@ public static bool IsGlyphAlwaysAvailable(string glyphText) if (needsShaping) { - using var shaper = new SKShaper(paint.Typeface); + var shaper = new SKShaper(paint.Typeface); var result = GetShapedText(shaper, text, 0, 0, paint); if (result == null) { @@ -2747,7 +2750,7 @@ public int FontWeight nameof(TypeFace), typeof(SKTypeface), typeof(SkiaLabel), - defaultValue: null, + defaultValue: SKTypeface.Default, propertyChanged: NeedUpdateFont); public SKTypeface TypeFace diff --git a/src/Engine/Draw/Text/TextSpan.cs b/src/Engine/Draw/Text/TextSpan.cs index 7041d385..df823ec5 100644 --- a/src/Engine/Draw/Text/TextSpan.cs +++ b/src/Engine/Draw/Text/TextSpan.cs @@ -109,11 +109,21 @@ public SKPaint SetupPaint(double scale, SKPaint defaultPaint) if (HasSetFont || AutoFindFont || defaultPaint == null) { - Paint.Typeface = TypeFace; + if (TypeFace != null) + { + Paint.Typeface = TypeFace; + } + else + { + Paint.Typeface = SKTypeface.Default; + } } else { - Paint.Typeface = defaultPaint.Typeface; + if (defaultPaint.Typeface != null) + Paint.Typeface = defaultPaint.Typeface; + else + Paint.Typeface = SKTypeface.Default; } if (defaultPaint != null) @@ -326,7 +336,7 @@ public virtual void Dispose() if (Paint != null) { - Paint.Typeface = null; //do not sipose typeface that can be cached and reused + Paint.Typeface = SKTypeface.Default; //do not sipose typeface that can be cached and reused Paint.Dispose(); } @@ -466,7 +476,7 @@ void Invalidate() private Color _backgroundColor = Colors.Transparent; private Color _paragraphColor = Colors.Transparent; private bool _autoFindFont; - private SKTypeface _typeFace; + private SKTypeface _typeFace = SKTypeface.Default; private bool _needShape; public string FontFamily diff --git a/src/Engine/Features/Fonts/SkiaFontManager.cs b/src/Engine/Features/Fonts/SkiaFontManager.cs index ebadb8d6..a6e0c443 100644 --- a/src/Engine/Features/Fonts/SkiaFontManager.cs +++ b/src/Engine/Features/Fonts/SkiaFontManager.cs @@ -314,7 +314,7 @@ public SKTypeface GetEmbeededFont(string filename, Assembly assembly, string ali { if (stream == null) - return null; + return SKTypeface.Default; font = SKTypeface.FromStream(stream); if (font != null) @@ -326,6 +326,9 @@ public SKTypeface GetEmbeededFont(string filename, Assembly assembly, string ali } + if (font == null) + font = SKTypeface.Default; + return font; }