diff --git a/src/PAModel/EditorState/ControlEditorState.cs b/src/PAModel/EditorState/ControlState.cs similarity index 97% rename from src/PAModel/EditorState/ControlEditorState.cs rename to src/PAModel/EditorState/ControlState.cs index d9c7a116..3ca0c416 100644 --- a/src/PAModel/EditorState/ControlEditorState.cs +++ b/src/PAModel/EditorState/ControlState.cs @@ -9,7 +9,7 @@ namespace Microsoft.PowerPlatform.Formulas.Tools.EditorState; /// Per control, this is the studio state content that doesn't wind up in the IR /// Similar to without the info encoded by .pa /// -public class ControlEditorState +public class ControlState { public string Name { get; set; } @@ -54,7 +54,7 @@ public class ControlEditorState // Used in GroupControlTransform.cs, and not written to .editorstate.json internal List GroupedControlsKey; - public ControlEditorState Clone() + public ControlState Clone() { var newState = Utilities.JsonClone(this); newState.TopParentName = TopParentName; diff --git a/src/PAModel/EditorState/ControlTreeState.cs b/src/PAModel/EditorState/ControlTreeState.cs index 63666ea9..d4812cfe 100644 --- a/src/PAModel/EditorState/ControlTreeState.cs +++ b/src/PAModel/EditorState/ControlTreeState.cs @@ -16,8 +16,8 @@ internal class ControlTreeState public string TopParentName { get; set; } /// - /// Collection of objects for + /// Collection of objects for /// this editor state. /// - public Dictionary ControlStates { get; set; } + public Dictionary ControlStates { get; set; } } diff --git a/src/PAModel/EditorState/EditorStateStore.cs b/src/PAModel/EditorState/EditorStateStore.cs index d31db2e2..e433b113 100644 --- a/src/PAModel/EditorState/EditorStateStore.cs +++ b/src/PAModel/EditorState/EditorStateStore.cs @@ -8,11 +8,11 @@ namespace Microsoft.PowerPlatform.Formulas.Tools.EditorState; internal class EditorStateStore { // Key is control name, case-sensitive - private readonly Dictionary _controls; + private readonly Dictionary _controls; public EditorStateStore() { - _controls = new Dictionary(StringComparer.Ordinal); + _controls = new Dictionary(StringComparer.Ordinal); } public EditorStateStore(EditorStateStore other) @@ -25,7 +25,7 @@ public bool ContainsControl(string name) return _controls.ContainsKey(name); } - public bool TryAddControl(ControlEditorState control) + public bool TryAddControl(ControlState control) { if (_controls.ContainsKey(control.Name)) return false; @@ -34,7 +34,7 @@ public bool TryAddControl(ControlEditorState control) return true; } - public bool TryGetControlState(string controlName, out ControlEditorState state) + public bool TryGetControlState(string controlName, out ControlState state) { return _controls.TryGetValue(controlName, out state); } @@ -44,10 +44,10 @@ public void Remove(string controlName) _controls.Remove(controlName); } - public IEnumerable GetControlsWithTopParent(string topParent) + public IEnumerable GetControlsWithTopParent(string topParent) { return _controls.Values.Where(ctrl => ctrl.TopParentName == topParent); } - public IEnumerable Contents => _controls.Values; + public IEnumerable Contents => _controls.Values; } diff --git a/src/PAModel/Entropy.cs b/src/PAModel/Entropy.cs index 70c29fbe..1ee09790 100644 --- a/src/PAModel/Entropy.cs +++ b/src/PAModel/Entropy.cs @@ -250,7 +250,7 @@ public void GetProperties(DocumentPropertiesJson documentProperties) } } - public void AddGroupControl(ControlEditorState groupControl) + public void AddGroupControl(ControlState groupControl) { var name = groupControl.Name; var groupOrder = new Dictionary(StringComparer.Ordinal); diff --git a/src/PAModel/Exceptions/SerializationException.cs b/src/PAModel/Exceptions/SerializationException.cs new file mode 100644 index 00000000..91209645 --- /dev/null +++ b/src/PAModel/Exceptions/SerializationException.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.PowerPlatform.Formulas.Tools; + +internal class SerializationException : Exception +{ + public string FileName { get; init; } + + public SerializationException(string message) + : base(message) + { + } + + public SerializationException(string message, Exception innerException) + : base(message, innerException) + { + } + + public SerializationException(string message, string fileName, Exception innerException) + { + FileName = fileName; + } +} diff --git a/src/PAModel/IR/IRStateHelpers.cs b/src/PAModel/IR/IRStateHelpers.cs index 334502b6..bfee1bf8 100644 --- a/src/PAModel/IR/IRStateHelpers.cs +++ b/src/PAModel/IR/IRStateHelpers.cs @@ -252,7 +252,7 @@ private static void SplitIRAndState(Item control, string topParentName, int inde { entropy.ControlUniqueGuids.Add(control.Name, Guid.Parse(control.ControlUniqueId)); } - var controlState = new ControlEditorState() + var controlState = new ControlState() { Name = control.Name, TopParentName = topParentName, @@ -545,7 +545,7 @@ private static void RepopulateTemplateCustomProperties(FunctionNode func, Combin } } - private static RuleEntry CombinePropertyIRAndState(PropertyNode node, ErrorContainer errors, ControlEditorState state = null) + private static RuleEntry CombinePropertyIRAndState(PropertyNode node, ErrorContainer errors, ControlState state = null) { var propName = node.Identifier; var expression = node.Expression.Expression; @@ -567,7 +567,7 @@ private static RuleEntry CombinePropertyIRAndState(PropertyNode node, ErrorConta } } - private static DynamicPropertyJson CombineDynamicPropertyIRAndState(PropertyNode node, ControlEditorState state = null) + private static DynamicPropertyJson CombineDynamicPropertyIRAndState(PropertyNode node, ControlState state = null) { var propName = node.Identifier; var expression = node.Expression.Expression; @@ -589,7 +589,7 @@ private static DynamicPropertyJson CombineDynamicPropertyIRAndState(PropertyNode } } - private static RuleEntry GetPropertyEntry(ControlEditorState state, ErrorContainer errors, string propName, string expression) + private static RuleEntry GetPropertyEntry(ControlState state, ErrorContainer errors, string propName, string expression) { var property = new RuleEntry { @@ -617,7 +617,7 @@ private static RuleEntry GetPropertyEntry(ControlEditorState state, ErrorContain return property; } - private static DynamicPropertyJson GetDynamicPropertyEntry(ControlEditorState state, string propName, string expression) + private static DynamicPropertyJson GetDynamicPropertyEntry(ControlState state, string propName, string expression) { var property = new DynamicPropertyJson { diff --git a/src/PAModel/MergeTool/ControlDiffVisitor.cs b/src/PAModel/MergeTool/ControlDiffVisitor.cs index 91916b97..23712532 100644 --- a/src/PAModel/MergeTool/ControlDiffVisitor.cs +++ b/src/PAModel/MergeTool/ControlDiffVisitor.cs @@ -32,19 +32,19 @@ private ControlDiffVisitor(EditorStateStore childStateStore, TemplateStore paren _childTemplateStore = childTemplateStore; } - private Dictionary GetSubtreeStates(BlockNode node) + private Dictionary GetSubtreeStates(BlockNode node) { return GetSubtreeStatesImpl(node).ToDictionary(state => state.Name); } - private IEnumerable GetSubtreeStatesImpl(BlockNode node) + private IEnumerable GetSubtreeStatesImpl(BlockNode node) { - var childstates = node.Children?.SelectMany(GetSubtreeStatesImpl) ?? Enumerable.Empty(); + var childstates = node.Children?.SelectMany(GetSubtreeStatesImpl) ?? Enumerable.Empty(); if (!_childStateStore.TryGetControlState(node.Name.Identifier, out var state)) return childstates; - return childstates.Concat(new List() { state }); + return childstates.Concat(new List() { state }); } public override void Visit(BlockNode node, ControlDiffContext context) diff --git a/src/PAModel/MergeTool/Deltas/AddControl.cs b/src/PAModel/MergeTool/Deltas/AddControl.cs index bc3dcc29..333b8d59 100644 --- a/src/PAModel/MergeTool/Deltas/AddControl.cs +++ b/src/PAModel/MergeTool/Deltas/AddControl.cs @@ -12,11 +12,11 @@ internal class AddControl : IDelta private readonly bool _isInComponent; private readonly ControlPath _parentControlPath; private readonly BlockNode _control; - private readonly Dictionary _controlStates; + private readonly Dictionary _controlStates; public string ControlName => _control.Name.Identifier; - public AddControl(ControlPath parentControlPath, BlockNode control, Dictionary controlStates, bool isInComponent) + public AddControl(ControlPath parentControlPath, BlockNode control, Dictionary controlStates, bool isInComponent) { _isInComponent = isInComponent; _parentControlPath = parentControlPath; @@ -79,7 +79,7 @@ public void Apply(CanvasDocument document) control.Children.Add(repairedControl); } - private static BlockNode MakeControlTreeCollisionFree(BlockNode root, Dictionary states, EditorStateStore stateStore) + private static BlockNode MakeControlTreeCollisionFree(BlockNode root, Dictionary states, EditorStateStore stateStore) { var name = root.Name.Identifier; if (stateStore.ContainsControl(name)) @@ -100,7 +100,7 @@ private static BlockNode MakeControlTreeCollisionFree(BlockNode root, Dictionary return root; } - private static void RemoveStates(BlockNode root, Dictionary states) + private static void RemoveStates(BlockNode root, Dictionary states) { var name = root.Name.Identifier; states.Remove(name); diff --git a/src/PAModel/Model/Control.cs b/src/PAModel/Model/Control.cs new file mode 100644 index 00000000..ae8f78f4 --- /dev/null +++ b/src/PAModel/Model/Control.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics; +using YamlDotNet.Serialization; + +namespace Microsoft.PowerPlatform.Formulas.Tools.Model; + +[DebuggerDisplay("{Name}")] +public record Control +{ + public Control() + { + + } + + public Control(ControlEditorState editorState) + { + EditorState = editorState; + Name = editorState.Name; + + if (editorState.Children != null) + { + var childControls = new List(); + foreach (var child in editorState.Children) + { + childControls.Add(new Control(child)); + } + Controls = childControls; + editorState.Children = null; + } + } + + [YamlIgnore] + public ControlEditorState EditorState { get; set; } + + public string Name { get; init; } + + public IList Controls { get; init; } + + public IDictionary Properties { get; init; } +} diff --git a/src/PAModel/Model/ControlEditorState.cs b/src/PAModel/Model/ControlEditorState.cs new file mode 100644 index 00000000..7daf8e14 --- /dev/null +++ b/src/PAModel/Model/ControlEditorState.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.PowerPlatform.Formulas.Tools.Model; + +public record ControlEditorState +{ + public string Name { get; init; } + + public ControlEditorState[] Children { get; set; } + + public IList Rules { get; init; } +} diff --git a/src/PAModel/Model/RuleEditorState.cs b/src/PAModel/Model/RuleEditorState.cs new file mode 100644 index 00000000..69c42ce1 --- /dev/null +++ b/src/PAModel/Model/RuleEditorState.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics; + +namespace Microsoft.PowerPlatform.Formulas.Tools.Model; + +[DebuggerDisplay("{Category} / {Property} / {InvariantScript}")] +public record RuleEditorState +{ + public string Category { get; init; } + public string Property { get; init; } + public string NameMap { get; init; } + public string InvariantScript { get; init; } + public string RuleProviderType { get; init; } + public IList TaggedRuleArray { get; init; } +} diff --git a/src/PAModel/Model/TaggedRuleEditorState.cs b/src/PAModel/Model/TaggedRuleEditorState.cs new file mode 100644 index 00000000..46ecee27 --- /dev/null +++ b/src/PAModel/Model/TaggedRuleEditorState.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.PowerPlatform.Formulas.Tools.Model; + +public record TaggedRuleEditorState : RuleEditorState +{ + public string Tag { get; init; } +} diff --git a/src/PAModel/MsApp/MsappArchive.cs b/src/PAModel/MsApp/MsappArchive.cs index 3a0442b0..b20cb861 100644 --- a/src/PAModel/MsApp/MsappArchive.cs +++ b/src/PAModel/MsApp/MsappArchive.cs @@ -5,7 +5,11 @@ using System.IO.Compression; using System.Linq; using System.Text; +using System.Text.Json; using Microsoft.Extensions.Logging; +using Microsoft.PowerPlatform.Formulas.Tools.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; namespace Microsoft.PowerPlatform.Formulas.Tools.MsApp; @@ -14,22 +18,44 @@ namespace Microsoft.PowerPlatform.Formulas.Tools.MsApp; /// public class MsappArchive : IMsappArchive, IDisposable { + #region Constants + + public const string SrcDirectory = "Src"; + public const string ControlsDirectory = "Controls"; + public const string ComponentsDirectory = "Components"; + public const string AppTestDirectory = "AppTests"; + public const string ReferencesDirectory = "References"; + public const string ResourcesDirectory = "Resources"; + + public const string YamlFileExtension = ".yaml"; + public const string YamlFxFileExtension = ".fx.yaml"; + public const string JsonFileExtension = ".json"; + + #endregion + #region Fields private Lazy> _canonicalEntries; + private Lazy> _topLevelControls; private bool _isDisposed; private readonly ILogger _logger; private FileStream _fileStream; + private static readonly IDeserializer YamlDeserializer = new DeserializerBuilder() + .IgnoreUnmatchedProperties() + .WithNamingConvention(PascalCaseNamingConvention.Instance) + .Build(); #endregion - #region Constants + #region Internal classes - public const string ControlsDirectory = "Controls"; - public const string ComponentsDirectory = "Components"; - public const string AppTestDirectory = "AppTests"; - public const string ReferencesDirectory = "References"; - public const string ResourcesDirectory = "Resources"; + /// + /// Helper class for deserializing the top level control editor state. + /// + private class TopParentJson + { + public ControlEditorState TopParent { get; set; } + } #endregion @@ -91,6 +117,7 @@ private void Initialize(Stream stream, ZipArchiveMode mode, bool leaveOpen, Enco return canonicalEntries; }); + _topLevelControls = new Lazy>(LoadTopLevelControls); } #endregion @@ -115,6 +142,8 @@ private void Initialize(Stream stream, ZipArchiveMode mode, bool leaveOpen, Enco /// public long CompressedSize => ZipArchive.Entries.Sum(zipArchiveEntry => zipArchiveEntry.CompressedLength); + public IReadOnlyList TopLevelControls => _topLevelControls.Value.AsReadOnly(); + #endregion #region Methods @@ -123,30 +152,23 @@ private void Initialize(Stream stream, ZipArchiveMode mode, bool leaveOpen, Enco /// Returns all entries in the archive that are in the given directory. /// /// + /// /// - public IEnumerable GetDirectoryEntries(string directoryName) + public IEnumerable GetDirectoryEntries(string directoryName, string extension = null) { _ = directoryName ?? throw new ArgumentNullException(nameof(directoryName)); directoryName = NormalizePath(directoryName); -#if FEATUREGATE_DOCUMENTPREVIEWFLAGS_CANVASYAMLPERSISTENCE - var yamlDirectoryName = FileUtils.NormalizePath(Path.Combine("src", directoryName)); -#endif - foreach (var entry in CanonicalEntries) { - if (entry.Key.StartsWith(directoryName + '/')) - { - yield return entry.Value; - } + if (!entry.Key.StartsWith(directoryName + '/')) + continue; -#if FEATUREGATE_DOCUMENTPREVIEWFLAGS_CANVASYAMLPERSISTENCE - if (entry.Key.StartsWith(yamlDirectoryName + '/')) - { - yield return entry.Value; - } -#endif + if (extension != null && !entry.Key.EndsWith(extension, StringComparison.OrdinalIgnoreCase)) + continue; + + yield return entry.Value; } } @@ -201,6 +223,81 @@ public static string NormalizePath(string path) #endregion + #region Private Methods + + private List LoadTopLevelControls() + { + _logger?.LogInformation("Loading top level controls from Yaml."); + + var controls = new Dictionary(); + foreach (var yamlEntry in GetDirectoryEntries(Path.Combine(SrcDirectory, ControlsDirectory), YamlFileExtension)) + { + using var textReader = new StreamReader(yamlEntry.Open()); + try + { + var control = YamlDeserializer.Deserialize(textReader); + controls.Add(control.Name, control); + } + catch (Exception ex) + { + throw new SerializationException("Failed to deserialize control yaml file.", yamlEntry.FullName, ex); + } + } + + _logger?.LogInformation("Loading top level controls editor state."); + var controlEditorStates = new Dictionary(); + foreach (var editorStateEntry in GetDirectoryEntries(Path.Combine(ControlsDirectory), JsonFileExtension)) + { + try + { + var topParentJson = JsonSerializer.Deserialize(editorStateEntry.Open()); + controlEditorStates.Add(topParentJson.TopParent.Name, topParentJson.TopParent); + } + catch (Exception ex) + { + throw new SerializationException("Failed to deserialize control editor state file.", editorStateEntry.FullName, ex); + } + } + + // Merge the editor state into the controls + foreach (var control in controls.Values) + { + if (controlEditorStates.TryGetValue(control.Name, out var editorState)) + { + MergeControlEditorState(control, editorState); + controlEditorStates.Remove(control.Name); + } + } + + // For backwards compatibility, add any editor states that don't have a matching control + foreach (var editorState in controlEditorStates.Values) + { + controls.Add(editorState.Name, new Control(editorState)); + } + + return controls.Values.ToList(); + } + + private static void MergeControlEditorState(Control control, ControlEditorState controlEditorState) + { + control.EditorState = controlEditorState; + if (control.Controls == null) + return; + + foreach (var child in control.Controls) + { + // Find the editor state for the child by name + var childEditorState = controlEditorState.Children.Where(c => c.Name == child.Name).FirstOrDefault(); + if (childEditorState == null) + continue; + + MergeControlEditorState(child, childEditorState); + } + controlEditorState.Children = null; + } + + #endregion + #region IDisposable protected virtual void Dispose(bool disposing) diff --git a/src/PAModel/Serializers/SourceSerializer.cs b/src/PAModel/Serializers/SourceSerializer.cs index 7476c559..606d791a 100644 --- a/src/PAModel/Serializers/SourceSerializer.cs +++ b/src/PAModel/Serializers/SourceSerializer.cs @@ -451,7 +451,7 @@ private static void LoadSourceFiles(CanvasDocument app, DirectoryReader director // When SourceSerializer is updated past v24, this could be removed entirely. private static void ApplyV24BackCompat(ControlTreeState editorState, DirectoryReader.Entry file) { - editorState.ControlStates = file.ToObject>(); + editorState.ControlStates = file.ToObject>(); editorState.TopParentName = Utilities.UnEscapeFilename(file._relativeName.Replace(".editorstate.json", "")); } @@ -1023,7 +1023,7 @@ private static void WriteTopParent( { var editorStateFilename = $"{newControlName}.editorstate.json"; - var controlStates = new Dictionary(); + var controlStates = new Dictionary(); foreach (var item in app._editorStateStore.GetControlsWithTopParent(controlName)) { controlStates.Add(item.Name, item); diff --git a/src/PAModel/SourceTransforms/GroupControlTransform.cs b/src/PAModel/SourceTransforms/GroupControlTransform.cs index 656d4df9..3796ee97 100644 --- a/src/PAModel/SourceTransforms/GroupControlTransform.cs +++ b/src/PAModel/SourceTransforms/GroupControlTransform.cs @@ -83,7 +83,7 @@ public void BeforeWrite(BlockNode control) if (!_editorStateStore.TryGetControlState(groupControlName, out var groupControlState)) { // There may not be editorstate present for this. Create a fake state to use - groupControlState = new ControlEditorState() + groupControlState = new ControlState() { Name = groupControlName, StyleName = "", diff --git a/src/PAModelTests/Apps/WithYaml/HelloWorld.msapp b/src/PAModelTests/Apps/WithYaml/HelloWorld.msapp new file mode 100644 index 00000000..2eab3da5 Binary files /dev/null and b/src/PAModelTests/Apps/WithYaml/HelloWorld.msapp differ diff --git a/src/PAModelTests/DefaultValuesTransformTests.cs b/src/PAModelTests/DefaultValuesTransformTests.cs index c2a30bbf..6fd72424 100644 --- a/src/PAModelTests/DefaultValuesTransformTests.cs +++ b/src/PAModelTests/DefaultValuesTransformTests.cs @@ -63,7 +63,7 @@ private static EditorStateStore getEditorStateStore() var editorStateStore = new EditorStateStore(); var someProperty = new PropertyState() { PropertyName = "SomeProperty" }; - editorStateStore.TryAddControl(new ControlEditorState() + editorStateStore.TryAddControl(new ControlState() { Name = "Canvas1", TopParentName = "Screen1", diff --git a/src/PAModelTests/MsApp/MsappArchiveTests.cs b/src/PAModelTests/MsApp/MsappArchiveTests.cs index 57bd024b..20a919fa 100644 --- a/src/PAModelTests/MsApp/MsappArchiveTests.cs +++ b/src/PAModelTests/MsApp/MsappArchiveTests.cs @@ -72,4 +72,21 @@ public void AddEntryTests(string[] entries) var action = () => msappArchive.GetRequiredEntry("not-exist"); action.Invoking(a => a()).Should().Throw(); } + + [TestMethod] + [DataRow(@"Apps/WithYaml/HelloWorld.msapp", 14, 2, "HelloScreen", 8)] + [DataRow(@"Apps/AppWithLabel.msapp", 11, 2, "Screen1", 8)] + public void GetTopLevelControlsTests(string testFile, int allEntriesCount, int controlsCount, string topLevelControlName, int topLevelRulesCount) + { + // Arrange: Create new ZipArchive in memory + using var msappArchive = new MsappArchive(testFile); + + // Assert + msappArchive.CanonicalEntries.Count.Should().Be(allEntriesCount); + msappArchive.TopLevelControls.Count.Should().Be(controlsCount); + msappArchive.TopLevelControls.Should().ContainSingle(c => c.Name == "App"); + + var topLevelControl = msappArchive.TopLevelControls.Single(c => c.Name == topLevelControlName); + topLevelControl.EditorState.Rules.Count.Should().Be(topLevelRulesCount); + } } diff --git a/src/PAModelTests/PAModelTests.csproj b/src/PAModelTests/PAModelTests.csproj index 2ab78d0f..d174ad28 100644 --- a/src/PAModelTests/PAModelTests.csproj +++ b/src/PAModelTests/PAModelTests.csproj @@ -23,7 +23,7 @@ - + PreserveNewest diff --git a/src/PAModelTests/SmartMergeTests.cs b/src/PAModelTests/SmartMergeTests.cs index b49eba48..992ec2a8 100644 --- a/src/PAModelTests/SmartMergeTests.cs +++ b/src/PAModelTests/SmartMergeTests.cs @@ -350,8 +350,8 @@ public void ScreenAddWithChildCollisionTestInLocal() }); branchADoc._screens.Add("Screen32", newScreen); - branchADoc._editorStateStore.TryAddControl(new ControlEditorState() { Name = "Screen32", TopParentName = "Screen32" }); - branchADoc._editorStateStore.TryAddControl(new ControlEditorState() { Name = "Foo", TopParentName = "Screen32" }); + branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Screen32", TopParentName = "Screen32" }); + branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen32" }); }, (branchBDoc) => { @@ -365,7 +365,7 @@ public void ScreenAddWithChildCollisionTestInLocal() }, Properties = new List() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromB" } } } }); - branchBDoc._editorStateStore.TryAddControl(new ControlEditorState() { Name = "Foo", TopParentName = "Screen1" }); + branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen1" }); }, (resultDoc) => { @@ -397,7 +397,7 @@ public void ScreenAddWithChildCollisionTestInRemote() }, Properties = new List() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromB" } } } }); - branchADoc._editorStateStore.TryAddControl(new ControlEditorState() { Name = "Foo", TopParentName = "Screen1" }); + branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen1" }); }, (branchBDoc) => { @@ -422,8 +422,8 @@ public void ScreenAddWithChildCollisionTestInRemote() }); branchBDoc._screens.Add("Screen32", newScreen); - branchBDoc._editorStateStore.TryAddControl(new ControlEditorState() { Name = "Screen32", TopParentName = "Screen32" }); - branchBDoc._editorStateStore.TryAddControl(new ControlEditorState() { Name = "Foo", TopParentName = "Screen32" }); + branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Screen32", TopParentName = "Screen32" }); + branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen32" }); }, (resultDoc) => {