Ooui-tws-port/Ooui.Forms/RendererPool.cs

150 lines
5.7 KiB
C#

using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace Ooui.Forms
{
public sealed class RendererPool
{
readonly Dictionary<Type, Stack<IVisualElementRenderer>> _freeRenderers =
new Dictionary<Type, Stack<IVisualElementRenderer>> ();
readonly VisualElement _oldElement;
readonly IVisualElementRenderer _parent;
public RendererPool (IVisualElementRenderer renderer, VisualElement oldElement)
{
if (renderer == null)
throw new ArgumentNullException (nameof (renderer));
if (oldElement == null)
throw new ArgumentNullException (nameof (oldElement));
_oldElement = oldElement;
_parent = renderer;
}
public IVisualElementRenderer GetFreeRenderer (VisualElement view)
{
if (view == null)
throw new ArgumentNullException (nameof (view));
var rendererType = Xamarin.Forms.Internals.Registrar.Registered.GetHandlerType (view.GetType()) ?? typeof (Renderers.ViewRenderer);
Stack<IVisualElementRenderer> renderers;
if (!_freeRenderers.TryGetValue (rendererType, out renderers) || renderers.Count == 0)
return null;
var renderer = renderers.Pop ();
renderer.SetElement (view);
return renderer;
}
public void UpdateNewElement (VisualElement newElement)
{
if (newElement == null)
throw new ArgumentNullException ("newElement");
var sameChildrenTypes = true;
var oldChildren = ((IElementController)_oldElement).LogicalChildren;
var newChildren = ((IElementController)newElement).LogicalChildren;
if (oldChildren.Count == newChildren.Count) {
for (var i = 0; i < oldChildren.Count; i++) {
if (oldChildren[i].GetType () != newChildren[i].GetType ()) {
sameChildrenTypes = false;
break;
}
}
}
else
sameChildrenTypes = false;
if (!sameChildrenTypes) {
ClearRenderers (_parent);
FillChildrenWithRenderers (newElement);
}
else
UpdateRenderers (newElement);
}
void ClearRenderers (IVisualElementRenderer renderer)
{
if (renderer == null)
return;
var subviews = renderer.NativeView.Children;
for (var i = 0; i < subviews.Count; i++) {
var childRenderer = subviews[i] as IVisualElementRenderer;
if (childRenderer != null) {
PushRenderer (childRenderer);
// The ListView CalculateHeightForCell method can create renderers and dispose its child renderers before this is called.
// Thus, it is possible that this work is already completed.
if (childRenderer.Element != null && ReferenceEquals (childRenderer, Platform.GetRenderer (childRenderer.Element)))
childRenderer.Element.ClearValue (Platform.RendererProperty);
}
renderer.NativeView.RemoveChild (subviews[i]);
}
}
void FillChildrenWithRenderers (VisualElement element)
{
foreach (var logicalChild in ((IElementController)element).LogicalChildren) {
var child = logicalChild as VisualElement;
if (child != null) {
//if (CompressedLayout.GetIsHeadless (child)) {
// child.IsPlatformEnabled = true;
// FillChildrenWithRenderers (child);
//}
//else {
var renderer = GetFreeRenderer (child) ?? Platform.CreateRenderer (child);
Platform.SetRenderer (child, renderer);
_parent.NativeView.AppendChild (renderer.NativeView);
//}
}
}
}
void PushRenderer (IVisualElementRenderer renderer)
{
var reflectableType = renderer as System.Reflection.IReflectableType;
var rendererType = reflectableType != null ? reflectableType.GetTypeInfo ().AsType () : renderer.GetType ();
Stack<IVisualElementRenderer> renderers;
if (!_freeRenderers.TryGetValue (rendererType, out renderers))
_freeRenderers[rendererType] = renderers = new Stack<IVisualElementRenderer> ();
renderers.Push (renderer);
}
void UpdateRenderers (Xamarin.Forms.Element newElement)
{
var newElementController = (IElementController)newElement;
if (newElementController.LogicalChildren.Count == 0)
return;
var subviews = _parent.NativeView.Children;
for (var i = 0; i < subviews.Count; i++) {
var childRenderer = subviews[i] as IVisualElementRenderer;
if (childRenderer == null)
continue;
var x = (int)childRenderer.NativeView.Style.ZIndex / 1000;
var element = newElementController.LogicalChildren[x] as VisualElement;
if (element == null)
continue;
if (childRenderer.Element != null && ReferenceEquals (childRenderer, Platform.GetRenderer (childRenderer.Element)))
childRenderer.Element.ClearValue (Platform.RendererProperty);
childRenderer.SetElement (element);
Platform.SetRenderer (element, childRenderer);
}
}
}
}