More support for property bindings. (#200)

* More support for property bindings.

Add support for Color values in CompositionPropertySet.
Add support for TryGet* methods in CompositionPropertySet.
Refactor the CompositionPropertySet implementation so that it's easier to add new types.
Adds a switch to the translator to determine whether or not it recognizes property bindings. Property bindings are only useful for codegen so this switch is off for LottieVisualSource and on for LottieGen.
Translator will now insert a property for a solid color fill that has a property binding for "Color".

* CR feedback.
This commit is contained in:
Simeon 2019-12-09 11:33:48 -08:00 коммит произвёл GitHub
Родитель 7dc82de085
Коммит ac2b575140
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 231 добавлений и 23 удалений

Просмотреть файл

@ -633,7 +633,8 @@ sealed class LottieFileProcessor
targetUapVersion: _options.TargetUapVersion ?? uint.MaxValue,
minimumUapVersion: _minimumUapVersion,
strictTranslation: false,
addCodegenDescriptions: true);
addCodegenDescriptions: true,
translatePropertyBindings: true);
_translationResults = translationResult.TranslationResults;
_translationIssues = translationResult.Issues;

Просмотреть файл

@ -484,6 +484,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie
result = CacheAndInitializeCompositionObject(obj, result);
foreach (var prop in obj.ColorProperties)
{
result.InsertColor(prop.Key, Color(prop.Value));
}
foreach (var prop in obj.ScalarProperties)
{
result.InsertScalar(prop.Key, prop.Value);

Просмотреть файл

@ -410,7 +410,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie
lottieComposition: lottieComposition,
targetUapVersion: GetCurrentUapVersion(),
strictTranslation: false,
addCodegenDescriptions: true);
addCodegenDescriptions: false,
translatePropertyBindings: false);
wincompDataRootVisual = translationResult.RootVisual;
sourceMetadata = translationResult.SourceMetadata;

Просмотреть файл

@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using Microsoft.Toolkit.Uwp.UI.Lottie.LottieData;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
@ -29,13 +30,16 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
/// Must be >= 7 and &lt;= targetUapVersion.</param>
/// <param name="strictTranslation">If true, throw an exception if translation issues are found.</param>
/// <param name="addCodegenDescriptions">Add descriptions to objects for comments on generated code.</param>
/// <param name="translatePropertyBindings">Translate the special property binding language in Lottie object
/// names and create bindings to <see cref="WinCompData.CompositionPropertySet"/> values.</param>
/// <returns>The results of the translation and the issues.</returns>
public static MultiVersionTranslationResult TryTranslateLottieComposition(
LottieComposition lottieComposition,
uint targetUapVersion,
uint minimumUapVersion,
bool strictTranslation,
bool addCodegenDescriptions = true)
bool addCodegenDescriptions,
bool translatePropertyBindings)
{
if (targetUapVersion < LowestValidUapVersion)
{
@ -52,7 +56,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
targetUapVersion: targetUapVersion,
minimumUapVersion: minimumUapVersion,
strictTranslation: strictTranslation,
addCodegenDescriptions: addCodegenDescriptions).ToArray();
addCodegenDescriptions: addCodegenDescriptions,
translatePropertyBindings: translatePropertyBindings).ToArray();
// Combine the issues that are the same in multiple versions into issues with a version range.
var dict = new Dictionary<TranslationIssue, UapVersionRange>();
@ -97,15 +102,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
uint targetUapVersion,
uint minimumUapVersion,
bool strictTranslation,
bool addCodegenDescriptions)
bool addCodegenDescriptions,
bool translatePropertyBindings)
{
// First, generate code for the target version.
var translationResult =
LottieToWinCompTranslator.TryTranslateLottieComposition(
lottieComposition,
targetUapVersion,
targetUapVersion: targetUapVersion,
strictTranslation,
addCodegenDescriptions);
addCodegenDescriptions,
translatePropertyBindings);
yield return (
translationResult,
@ -126,9 +133,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
translationResult =
LottieToWinCompTranslator.TryTranslateLottieComposition(
lottieComposition,
nextLowerTarget,
strictTranslation,
addCodegenDescriptions);
targetUapVersion: nextLowerTarget,
strictTranslation: strictTranslation,
addCodegenDescriptions: addCodegenDescriptions,
translatePropertyBindings: translatePropertyBindings);
yield return (
translationResult,

Просмотреть файл

@ -63,6 +63,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
readonly LottieComposition _lc;
readonly TranslationIssues _issues;
readonly bool _addDescriptions;
readonly bool _translatePropertyBindings;
readonly CompositionObjectFactory _c;
readonly ContainerVisual _rootVisual;
readonly Dictionary<ScaleAndOffset, ExpressionAnimation> _progressBindingAnimations = new Dictionary<ScaleAndOffset, ExpressionAnimation>();
@ -98,6 +99,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
Compositor compositor,
bool strictTranslation,
bool addDescriptions,
bool translatePropertyBindings,
uint targetUapVersion)
{
_lc = lottieComposition;
@ -105,6 +107,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
_c = new CompositionObjectFactory(compositor, targetUapVersion);
_issues = new TranslationIssues(strictTranslation);
_addDescriptions = addDescriptions;
_translatePropertyBindings = translatePropertyBindings;
// Create the root.
_rootVisual = _c.CreateContainerVisual();
@ -124,19 +127,23 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
/// <param name="targetUapVersion">The version of UAP that the translator will ensure compatibility with. Must be >= 7.</param>
/// <param name="strictTranslation">If true, throw an exception if translation issues are found.</param>
/// <param name="addCodegenDescriptions">Add descriptions to objects for comments on generated code.</param>
/// <param name="translatePropertyBindings">Translate the special property binding language in Lottie object
/// names and create bindings to <see cref="CompositionPropertySet"/> values.</param>
/// <returns>The result of the translation.</returns>
public static TranslationResult TryTranslateLottieComposition(
LottieComposition lottieComposition,
uint targetUapVersion,
bool strictTranslation,
bool addCodegenDescriptions = true)
bool addCodegenDescriptions,
bool translatePropertyBindings)
{
// Set up the translator.
using (var translator = new LottieToWinCompTranslator(
lottieComposition,
new Compositor(),
strictTranslation,
addCodegenDescriptions,
strictTranslation: strictTranslation,
addDescriptions: addCodegenDescriptions,
translatePropertyBindings: translatePropertyBindings,
targetUapVersion))
{
// Translate the Lottie content to a Composition graph.
@ -2834,8 +2841,32 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.LottieToWinComp
CompositionColorBrush TranslateSolidColorFill(TranslationContext context, SolidColorFill shapeFill, TrimmedAnimatable<double> opacityPercent)
{
// Read property bindings embedded into the name of the fill.
// TODO: Currently the bindings are ignored and the parsing is being done just to exercise the parser.
var propertyBindings = PropertyBindingsParser.ParseBindings(shapeFill.Name);
if (_translatePropertyBindings)
{
var propertyBindings = PropertyBindingsParser.ParseBindings(shapeFill.Name);
foreach (var binding in propertyBindings)
{
if (binding.propertyName == "Color")
{
var bindingName = binding.bindingName;
if (_rootVisual.Properties.TryGetColor(bindingName, out _) == CompositionGetValueStatus.Succeeded)
{
// A property has already been inserted for this binding name.
}
else
{
// Insert a color with the given binding name. The client is expected to set
// this property name with a theme color, so the actual color placed here
// doesn't really matter, but giving it an unusual color might help developers
// see that they have an unbound color property.
_rootVisual.Properties.InsertColor(bindingName, WinCompData.Wui.Colors.YellowGreen);
}
break;
}
}
}
return TranslateSolidColor(context, shapeFill.Color, shapeFill.OpacityPercent, opacityPercent);
}

Просмотреть файл

@ -493,6 +493,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.UIData.Tools
result = CacheAndInitializeCompositionObject(obj, result);
foreach (var prop in obj.ColorProperties)
{
result.InsertColor(prop.Key, prop.Value);
}
foreach (var prop in obj.ScalarProperties)
{
result.InsertScalar(prop.Key, prop.Value);

Просмотреть файл

@ -1407,6 +1407,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.UIData.CodeGen
if (!propertySet.IsEmpty)
{
builder.WriteLine($"{Var} propertySet = {localName}{Deref}Properties;");
foreach (var prop in propertySet.ColorProperties)
{
builder.WriteLine($"propertySet{Deref}InsertColor({String(prop.Key)}, {Color(prop.Value)});");
}
foreach (var prop in propertySet.ScalarProperties)
{
builder.WriteLine($"propertySet{Deref}InsertScalar({String(prop.Key)}, {Float(prop.Value)});");

Просмотреть файл

@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData
{
[MetaData.UapVersion(2)]
#if PUBLIC_WinCompData
public
#endif
enum CompositionGetValueStatus
{
/// <summary>
/// The value was successfully retrieved.
/// </summary>
Succeeded = 0,
/// <summary>
/// The value type of the key-value pair is different from the value type requested.
/// </summary>
TypeMismatch = 1,
/// <summary>
/// The key-value pair does not exist.
/// </summary>
NotFound = 2,
}
}

Просмотреть файл

@ -2,20 +2,26 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.Wui;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData
{
// CompositionPropertySet was introduced in version 2. Boolean types
// were added in version 3.
[MetaData.UapVersion(2)]
#if PUBLIC_WinCompData
public
#endif
sealed class CompositionPropertySet : CompositionObject
{
readonly Dictionary<string, float> _scalarProperties = new Dictionary<string, float>();
readonly Dictionary<string, Vector2> _vector2Properties = new Dictionary<string, Vector2>();
HashSet<string> _names;
PropertyBag<Color> _colorProperties;
PropertyBag<float> _scalarProperties;
PropertyBag<Vector2> _vector2Properties;
internal CompositionPropertySet(CompositionObject owner)
{
@ -24,19 +30,105 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData
public CompositionObject Owner { get; }
public void InsertScalar(string name, float value) => _scalarProperties.Add(name, value);
public void InsertColor(string propertyName, Color value) => Insert(propertyName, in value, ref _colorProperties);
public void InsertVector2(string name, Vector2 value) => _vector2Properties.Add(name, value);
public void InsertScalar(string propertyName, float value) => Insert(propertyName, in value, ref _scalarProperties);
public IEnumerable<KeyValuePair<string, float>> ScalarProperties => _scalarProperties;
public void InsertVector2(string propertyName, Vector2 value) => Insert(propertyName, in value, ref _vector2Properties);
public IEnumerable<KeyValuePair<string, Vector2>> Vector2Properties => _vector2Properties;
public CompositionGetValueStatus TryGetColor(string propertyName, out Color value) => TryGet(propertyName, ref _colorProperties, out value);
public IEnumerable<string> PropertyNames => _scalarProperties.Keys.Concat(_vector2Properties.Keys);
public CompositionGetValueStatus TryGetScalar(string propertyName, out float value) => TryGet(propertyName, ref _scalarProperties, out value);
public bool IsEmpty => _scalarProperties.Count + _vector2Properties.Count == 0;
public CompositionGetValueStatus TryGetVector2(string propertyName, out Vector2 value) => TryGet(propertyName, ref _vector2Properties, out value);
public IEnumerable<KeyValuePair<string, Color>> ColorProperties => _colorProperties.Entries;
public IEnumerable<KeyValuePair<string, float>> ScalarProperties => _scalarProperties.Entries;
public IEnumerable<KeyValuePair<string, Vector2>> Vector2Properties => _vector2Properties.Entries;
public IEnumerable<string> PropertyNames => _names ?? Enumerable.Empty<string>();
public bool IsEmpty => !(_names?.Count > 0);
/// <inheritdoc/>
public override CompositionObjectType Type => CompositionObjectType.CompositionPropertySet;
void Insert<T>(string propertyName, in T value, ref PropertyBag<T> bag)
{
// Ensure the names set exists.
if (_names is null)
{
// CompositionPropertySet ignores the case of property names.
_names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
}
// Try to add the name to the set of names.
if (!_names.Add(propertyName))
{
// The name already existed. That's ok as long the name is associated
// with the correct type.
if (!bag.ContainsKey(propertyName))
{
throw new ArgumentException();
}
}
// Set the value.
bag.SetValue(propertyName, in value);
}
CompositionGetValueStatus TryGet<T>(string propertyName, ref PropertyBag<T> bag, out T value)
{
// There's nothing in this property set.
if (_names is null)
{
value = default(T);
return CompositionGetValueStatus.NotFound;
}
// The name is in the property set - does it refer to the right type?
return bag.TryGetValue(propertyName, out value)
? CompositionGetValueStatus.Succeeded
: CompositionGetValueStatus.TypeMismatch;
}
struct PropertyBag<T>
{
Dictionary<string, T> _dictionary;
internal bool IsEmpty => (_dictionary?.Count ?? 0) == 0;
internal void SetValue(string propertyName, in T value)
{
if (_dictionary == null)
{
// CompositionPropertySet ignores the case of property names.
_dictionary = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
}
// Set or replace the value in the dictionary.
_dictionary[propertyName] = value;
}
internal bool ContainsKey(string propertyName) => _dictionary?.ContainsKey(propertyName) ?? false;
internal bool TryGetValue(string propertyName, out T value)
{
if (_dictionary is null)
{
value = default(T);
return false;
}
return _dictionary.TryGetValue(propertyName, out value);
}
internal IEnumerable<string> Names => _dictionary?.Keys ?? Enumerable.Empty<string>();
internal IEnumerable<KeyValuePair<string, T>> Entries
=> _dictionary ?? Enumerable.Empty<KeyValuePair<string, T>>();
}
}
}

Просмотреть файл

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Xml.Linq;
using Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.Wui;
namespace Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.Tools
{
@ -692,6 +693,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.Tools
return new XElement("PropertySet", GetContents());
IEnumerable<XObject> GetContents()
{
foreach (var prop in obj.ColorProperties)
{
foreach (var item in FromAnimatableColor(prop.Key, animators, prop.Value))
{
yield return item;
}
}
foreach (var prop in obj.ScalarProperties)
{
foreach (var item in FromAnimatableScalar(prop.Key, animators, prop.Value))
@ -798,6 +807,23 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.Tools
}
}
IEnumerable<XObject> FromAnimatableColor(string name, IEnumerable<CompositionObject.Animator> animators, Color? initialValue)
{
var animation = animators.Where(a => a.AnimatedProperty == name).FirstOrDefault()?.Animation;
if (animation != null)
{
yield return FromAnimation(name, animation, initialValue);
}
else
{
if (initialValue.HasValue)
{
yield return FromColor(name, initialValue.Value);
}
}
}
IEnumerable<XObject> FromAnimatableScalar(string name, IEnumerable<CompositionObject.Animator> animators, float? initialValue)
{
var animation = animators.Where(a => a.AnimatedProperty == name).FirstOrDefault()?.Animation;
@ -849,6 +875,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData.Tools
}
}
XElement FromColor(string name, Color value)
{
return new XElement(name, new XAttribute("ColorValue", value));
}
XElement FromScalar(string name, float value)
{
return new XElement(name, new XAttribute("ScalarValue", value));

Просмотреть файл

@ -23,6 +23,7 @@
<Compile Include="$(MSBuildThisFileDirectory)CompositionEllipseGeometry.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CompositionGeometricClip.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CompositionGeometry.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CompositionGetValueStatus.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CompositionGradientBrush.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CompositionGradientExtendMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CompositionLinearGradientBrush.cs" />