Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,15 @@ static SKPath MakePath(GeometryGroup geometryGroup)
var path = new SKPath();
path.FillType = geometryGroup.FillRule == FillRule.Nonzero ? SKPathFillType.Winding : SKPathFillType.EvenOdd;

foreach (Geometry child in geometryGroup.Children)
var children = geometryGroup.Children;
if (children is null)
return path;

foreach (Geometry child in children)
{
if (child is null)
continue;

#pragma warning disable IL2026
SKPath childPath = MakePath((dynamic)child);
#pragma warning disable IL2026
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,15 @@ public static PathData ToCGPath(this Geometry geometry, Transform renderTransfor

pathData.IsNonzeroFillRule = geometryGroup.FillRule == FillRule.Nonzero;

foreach (Geometry child in geometryGroup.Children)
var children = geometryGroup.Children;
if (children is null)
return pathData;

foreach (Geometry child in children)
{
if (child is null)
continue;

PathData pathChild = child.ToCGPath(renderTransform);
pathData.Data.AddPath(pathChild.Data);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,15 @@ public static APath ToAPath(this Geometry geometry, Context context)

path.SetFillType(geometryGroup.FillRule == FillRule.Nonzero ? APath.FillType.Winding : APath.FillType.EvenOdd);

foreach (Geometry child in geometryGroup.Children)
var children = geometryGroup.Children;
if (children is null)
return path;

foreach (Geometry child in children)
{
if (child is null)
continue;

APath childPath = child.ToAPath(context);
path.AddPath(childPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,16 @@ public static WMedia.Geometry ToPlatform(this Geometry geometry)
FillRule = ConvertFillRule(geometryGroup.FillRule)
};

foreach (Geometry children in geometryGroup.Children)
var children = geometryGroup.Children;
if (children is null)
return wGeometry;

foreach (Geometry child in children)
{
WMedia.Geometry winChild = children.ToPlatform();
if (child is null)
continue;

WMedia.Geometry winChild = child.ToPlatform();
(wGeometry as WMedia.GeometryGroup).Children.Add(winChild);
}
}
Expand Down
184 changes: 77 additions & 107 deletions src/Controls/src/Core/Shapes/GeometryGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ namespace Microsoft.Maui.Controls.Shapes
[ContentProperty("Children")]
public class GeometryGroup : Geometry
{
readonly Dictionary<Geometry, int> _subscriptionRefCounts = new();

/// <summary>Bindable property for <see cref="Children"/>.</summary>
public static readonly BindableProperty ChildrenProperty =
BindableProperty.Create(nameof(Children), typeof(GeometryCollection), typeof(GeometryGroup), null,
Expand Down Expand Up @@ -56,148 +54,116 @@ public FillRule FillRule

public event EventHandler InvalidateGeometryRequested;

void UpdateChildren(GeometryCollection oldCollection, GeometryCollection newCollection)
{
DetachCollection(oldCollection);
AttachCollection(newCollection);
ChildrenSubscriptions _childrenSubscriptions;
NotifyCollectionChangedEventHandler _childrenCollectionChanged;
PropertyChangedEventHandler _childrenPropertyChanged;

Invalidate();
}

void AttachCollection(GeometryCollection collection)
void UpdateChildren(GeometryCollection oldCollection, GeometryCollection newCollection)
{
if (collection == null)
return;

collection.CollectionChanged += OnChildrenCollectionChanged;

foreach (var geometry in collection)
if (oldCollection != null)
{
SubscribeToGeometry(geometry);
_childrenSubscriptions?.UnsubscribeAll();
}
}

void DetachCollection(GeometryCollection collection)
{
if (collection == null)
if (newCollection == null)
return;

collection.CollectionChanged -= OnChildrenCollectionChanged;
_childrenCollectionChanged ??= OnChildrenCollectionChanged;
_childrenPropertyChanged ??= OnChildrenPropertyChanged;

var subscriptions = _childrenSubscriptions ??= new ChildrenSubscriptions();
subscriptions.Subscribe(newCollection, _childrenCollectionChanged);

foreach (var geometry in collection)
foreach (var newChildren in newCollection)
{
UnsubscribeFromGeometry(geometry);
if (newChildren is not null)
Comment thread
kubaflo marked this conversation as resolved.
Comment thread
kubaflo marked this conversation as resolved.
{
subscriptions.Add(newChildren, _childrenPropertyChanged);
}
}
}

void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
if (e.Action == NotifyCollectionChangedAction.Reset)
{
case NotifyCollectionChangedAction.Add:
if (e.NewItems != null)
{
foreach (Geometry geometry in e.NewItems)
{
SubscribeToGeometry(geometry);
}
}
break;

case NotifyCollectionChangedAction.Remove:
if (e.OldItems != null)
{
foreach (Geometry geometry in e.OldItems)
{
UnsubscribeFromGeometry(geometry);
}
}
break;

case NotifyCollectionChangedAction.Replace:
if (e.OldItems != null)
{
foreach (Geometry geometry in e.OldItems)
{
UnsubscribeFromGeometry(geometry);
}
}
// GeometryCollection is sealed, so Reset follows Clear after the collection is empty.
_childrenSubscriptions?.ResetChildren();
}
else if (e.OldItems != null)
{
foreach (var oldItem in e.OldItems)
{
if (!(oldItem is Geometry oldGeometry))
continue;

if (e.NewItems != null)
{
foreach (Geometry geometry in e.NewItems)
{
SubscribeToGeometry(geometry);
}
}
break;
_childrenSubscriptions?.Remove(oldGeometry);
}
}

case NotifyCollectionChangedAction.Move:
// No subscription changes required.
break;
if (e.NewItems != null)
{
foreach (var newItem in e.NewItems)
{
if (!(newItem is Geometry newGeometry))
continue;

case NotifyCollectionChangedAction.Reset:
ResubscribeCollection(sender as GeometryCollection);
break;
_childrenSubscriptions?.Add(newGeometry, _childrenPropertyChanged);
}
}

Invalidate();
}

void ResubscribeCollection(GeometryCollection collection)
sealed class ChildrenSubscriptions
{
UnsubscribeFromAllChildren();
readonly WeakNotifyCollectionChangedProxy _collectionProxy = new();
readonly List<WeakNotifyPropertyChangedProxy> _childProxies = new();

if (collection == null)
return;
~ChildrenSubscriptions() => UnsubscribeAll();

foreach (var geometry in collection)
public void Subscribe(GeometryCollection source, NotifyCollectionChangedEventHandler handler)
{
SubscribeToGeometry(geometry);
_collectionProxy.Subscribe(source, handler);
}
}

void SubscribeToGeometry(Geometry geometry)
{
if (geometry == null)
return;

if (_subscriptionRefCounts.TryGetValue(geometry, out var count))
public void Add(Geometry source, PropertyChangedEventHandler handler)
{
_subscriptionRefCounts[geometry] = count + 1;
return;
_childProxies.Add(new WeakNotifyPropertyChangedProxy(source, handler));
}

_subscriptionRefCounts[geometry] = 1;
geometry.PropertyChanged += OnChildrenPropertyChanged;
}

void UnsubscribeFromGeometry(Geometry geometry)
{
if (geometry == null)
return;

if (!_subscriptionRefCounts.TryGetValue(geometry, out var count))
return;

if (count > 1)
public void Remove(Geometry source)
{
_subscriptionRefCounts[geometry] = count - 1;
return;
for (int i = _childProxies.Count - 1; i >= 0; i--)
{
var proxy = _childProxies[i];
if (proxy.TryGetSource(out var proxySource) && ReferenceEquals(proxySource, source))
{
proxy.Unsubscribe();
_childProxies.RemoveAt(i);
break;
}
}
}

_subscriptionRefCounts.Remove(geometry);
geometry.PropertyChanged -= OnChildrenPropertyChanged;
}
public void ResetChildren()
{
UnsubscribeChildren();
}

void UnsubscribeFromAllChildren()
{
foreach (var geometry in _subscriptionRefCounts.Keys)
public void UnsubscribeAll()
{
geometry.PropertyChanged -= OnChildrenPropertyChanged;
_collectionProxy.Unsubscribe();
UnsubscribeChildren();
}

_subscriptionRefCounts.Clear();
void UnsubscribeChildren()
{
for (int i = 0; i < _childProxies.Count; i++)
_childProxies[i].Unsubscribe();

_childProxies.Clear();
}
}

void OnChildrenPropertyChanged(object sender, PropertyChangedEventArgs e)
Expand All @@ -212,9 +178,13 @@ void Invalidate()

public override void AppendPath(Graphics.PathF path)
{
foreach (var c in Children)
var children = Children;
Comment thread
kubaflo marked this conversation as resolved.
if (children is null)
return;

foreach (var c in children)
{
c.AppendPath(path);
c?.AppendPath(path);
Comment thread
kubaflo marked this conversation as resolved.
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions src/Controls/src/Core/Shapes/GeometryHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ public static void FlattenGeometry(PathGeometry pathGeoDst, Geometry geoSrc, dou

Matrix matx = matxPrevious;

if (geoSrc is GeometryGroup)
if (geoSrc is GeometryGroup geometryGroup)
{
foreach (Geometry geoChild in (geoSrc as GeometryGroup).Children)
var children = geometryGroup.Children;
if (children is null)
return;

foreach (Geometry geoChild in children)
{
FlattenGeometry(pathGeoDst, geoChild, tolerance, matx);
if (geoChild is not null)
FlattenGeometry(pathGeoDst, geoChild, tolerance, matx);
}
}
else if (geoSrc is LineGeometry)
Expand Down
Loading
Loading