|
using System;
using System.Collections.Generic;
#if IOS || MACCATALYST
using PlatformView = UIKit.UIView;
#elif ANDROID
using PlatformView = Android.Views.View;
#elif WINDOWS
using PlatformView = Microsoft.UI.Xaml.FrameworkElement;
#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)
using PlatformView = System.Object;
#endif
namespace Microsoft.Maui
{
public abstract class PropertyMapper : IPropertyMapper
{
protected readonly Dictionary<string, Action<IElementHandler, IElement>> _mapper = new(StringComparer.Ordinal);
IPropertyMapper[]? _chained;
// Keep a distinct list of the keys so we don't run any duplicate (overridden) updates more than once
// when we call UpdateProperties
HashSet<string>? _updateKeys;
public PropertyMapper()
{
}
public PropertyMapper(params IPropertyMapper[]? chained)
{
Chained = chained;
}
protected virtual void SetPropertyCore(string key, Action<IElementHandler, IElement> action)
{
_mapper[key] = action;
ClearKeyCache();
}
protected virtual void UpdatePropertyCore(string key, IElementHandler viewHandler, IElement virtualView)
{
if (!viewHandler.CanInvokeMappers())
return;
var action = GetProperty(key);
action?.Invoke(viewHandler, virtualView);
}
public virtual Action<IElementHandler, IElement>? GetProperty(string key)
{
if (_mapper.TryGetValue(key, out var action))
return action;
else if (Chained is not null)
{
foreach (var ch in Chained)
{
var returnValue = ch.GetProperty(key);
if (returnValue != null)
return returnValue;
}
}
return null;
}
public void UpdateProperty(IElementHandler viewHandler, IElement? virtualView, string property)
{
if (virtualView == null)
return;
UpdatePropertyCore(property, viewHandler, virtualView);
}
public void UpdateProperties(IElementHandler viewHandler, IElement? virtualView)
{
if (virtualView == null)
return;
foreach (var key in UpdateKeys)
{
UpdatePropertyCore(key, viewHandler, virtualView);
}
}
public IPropertyMapper[]? Chained
{
get => _chained;
set
{
_chained = value;
ClearKeyCache();
}
}
private HashSet<string> PopulateKeys()
{
var keys = new HashSet<string>(StringComparer.Ordinal);
foreach (var key in GetKeys())
{
keys.Add(key);
}
return keys;
}
protected virtual void ClearKeyCache()
{
_updateKeys = null;
}
public virtual IReadOnlyCollection<string> UpdateKeys => _updateKeys ??= PopulateKeys();
public virtual IEnumerable<string> GetKeys()
{
foreach (var key in _mapper.Keys)
yield return key;
if (Chained is not null)
{
foreach (var chain in Chained)
foreach (var key in chain.GetKeys())
yield return key;
}
}
}
public interface IPropertyMapper
{
Action<IElementHandler, IElement>? GetProperty(string key);
IEnumerable<string> GetKeys();
void UpdateProperties(IElementHandler elementHandler, IElement virtualView);
void UpdateProperty(IElementHandler elementHandler, IElement virtualView, string property);
}
public interface IPropertyMapper<out TVirtualView, out TViewHandler> : IPropertyMapper
where TVirtualView : IElement
where TViewHandler : IElementHandler
{
void Add(string key, Action<TViewHandler, TVirtualView> action);
}
public class PropertyMapper<TVirtualView, TViewHandler> : PropertyMapper, IPropertyMapper<TVirtualView, TViewHandler>
where TVirtualView : IElement
where TViewHandler : IElementHandler
{
public PropertyMapper()
{
}
public PropertyMapper(params IPropertyMapper[] chained)
: base(chained)
{
}
public Action<TViewHandler, TVirtualView> this[string key]
{
get
{
var action = GetProperty(key) ?? throw new IndexOutOfRangeException($"Unable to find mapping for '{nameof(key)}'.");
return new Action<TViewHandler, TVirtualView>((h, v) => action.Invoke(h, v));
}
set => Add(key, value);
}
public void Add(string key, Action<TViewHandler, TVirtualView> action) =>
SetPropertyCore(key, (h, v) =>
{
if (v is TVirtualView vv)
action?.Invoke((TViewHandler)h, vv);
else if (Chained != null)
{
foreach (var chain in Chained)
{
if (chain.GetProperty(key) != null)
{
chain.UpdateProperty(h, v, key);
break;
}
}
}
});
}
public class PropertyMapper<TVirtualView> : PropertyMapper<TVirtualView, IElementHandler>
where TVirtualView : IElement
{
public PropertyMapper()
{
}
public PropertyMapper(params PropertyMapper[] chained)
: base(chained)
{
}
}
} |