private void AssertHasAttribute(UxNode node, string name, string value) { var attributes = node.Attributes; Assert.That(attributes.Keys, Does.Contain(name)); Assert.That(((UxString)attributes[name]).Value, Is.EqualTo(value)); }
void ApplyShapeStyle(SketchShapeGroup layer, UxNode targetNode) { var style = layer.Style; if (style.Blur.HasValue) { _log.Warning($"Skipping {style.Blur.Value.BlurType} blur on {layer.Name}. Not supported in UX"); } targetNode.Children.AddRange(BuildShadows(layer.Name, layer.Style)); var fillNodes = style.Fills .Where(fill => fill.IsEnabled) .Select(x => BuildBrush(x.Brush)); targetNode.Children.AddRange(fillNodes); var strokeNodes = style.Borders .Where(border => border.IsEnabled) .Select(border => new UxNode { ClassName = "Stroke", Attributes = new Dictionary <string, IUxSerializeable> { { "Width", new UxFloat((float)border.Thickness) }, { "Alignment", new UxString(border.Position.ToString()) } }, Children = new List <IUxSerializeable> { BuildBrush(border.Brush) } }); targetNode.Children.AddRange(strokeNodes); }
private static UxNode StringProperty(string name) { var property = new UxNode("string"); property.Attributes["ux:Property"] = new UxString(name); return(property); }
UxNode BuildShadow(SketchShadow shadow, bool perPixel) { var dx = shadow.Offset.X; var dy = shadow.Offset.Y; // atan2 will give us the angle in the correct quadrant with cartesian coordinates. // Sketch uses screen coordinates which resulted in the shadow being flipped in the X-direction // We therefore call atan2 with -dx. This gives us the right result. 2018-02-05 anette var angle = (180 / Math.PI) * Math.Atan2(dy, -dx); var distance = Math.Sqrt(dx * dx + dy * dy); var node = new UxNode { ClassName = "Shadow", Attributes = new Dictionary <string, IUxSerializeable> { { "Angle", new UxFloat((float)angle) }, { "Distance", new UxFloat((float)distance) }, { "Color", SketchColorToFloat4(shadow.Color) }, { "Size", new UxFloat((float)shadow.BlurRadius) } } }; if (perPixel) { node.Attributes["Mode"] = new UxString("PerPixel"); } return(node); }
public void CreatesPropertiesForSeveralStringValues() { var log = new MessageListLogger(); var transform = new TextPropertyTransform(log); var node = new UxNode("Panel"); node.Children.Add(CreateTextNode("FirstLayer", "Guten Tag!")); node.Children.Add(CreateTextNode("OtherLayer", "Wie geht's?")); transform.Apply(node); var firstPropertyShouldBeFirstElement = (UxNode)node.Children.ToList()[0]; AssertHasAttribute(firstPropertyShouldBeFirstElement, "ux:Property", "FirstLayer"); var secondPropertyShouldBeSecondElement = (UxNode)node.Children.ToList()[1]; AssertHasAttribute(secondPropertyShouldBeSecondElement, "ux:Property", "OtherLayer"); var texts = node.ChildrenOfClass("Text").ToList(); Assert.That(texts.Count(), Is.EqualTo(2)); AssertHasAttribute(texts[0], "Value", "{Property FirstLayer}"); AssertHasAttribute(texts[1], "Value", "{Property OtherLayer}"); AssertHasAttribute(node, "FirstLayer", "Guten Tag!"); AssertHasAttribute(node, "OtherLayer", "Wie geht's?"); }
private void ApplyTransforms(UxNode uxClass) { var transforms = new ITransform[] { new TextPropertyTransform(_log) }; foreach (var transform in transforms) { transform.Apply(uxClass); } }
private UxNode CreateTextNode(string layerName, string value) { var text = new UxNode("Text") { SketchLayerName = layerName }; text.Attributes["Value"] = new UxString(value); return(text); }
private UxNode BuildSymbolInstance(SketchSymbolInstance symbolInstance) { var className = _symbolClassNameBuilder.GetClassName(symbolInstance); var node = new UxNode { ClassName = className, SketchLayerName = symbolInstance.Name }; return(BuildLayout(symbolInstance, node)); }
public void Apply(UxNode uxClass) { var propertyValues = new List <PropertyValue>(); Apply(uxClass, propertyValues); for (var i = propertyValues.Count - 1; i >= 0; i--) { uxClass.Children.Insert(0, StringProperty(propertyValues[i].Name)); uxClass.Attributes.Add(propertyValues[i].Name, new UxString(propertyValues[i].DefaultValue)); } }
public UxNode BuildPage(SketchLayer parent) { var pageNode = new UxNode { ClassName = "Panel", SketchLayerName = parent.Name, Attributes = new Dictionary <string, IUxSerializeable> { { "ClipToBounds", new UxString("true") } } }; AddChildrenLayers(pageNode, parent); return(pageNode); }
UxNode BuildGroup(SketchGroup group) { if (group.Layers.Count == 0) { return(new NullNode(new UxComment("Skipped empty group '" + group.Name + "'"))); } var node = new UxNode { ClassName = "Panel", SketchLayerName = group.Name }; return(BuildLayout(group, node)); }
public UxNode BuildSymbolClass(SketchSymbolMaster symbol) { var symbolClassNode = new UxNode { ClassName = "Panel", SketchLayerName = symbol.Name, Attributes = new Dictionary <string, IUxSerializeable> { { "ux:Class", new UxString(_symbolClassNameBuilder.GetClassName(symbol)) }, { "Width", UxSize.Points((float)symbol.Frame.Width) }, { "Height", UxSize.Points((float)symbol.Frame.Height) } } }; AddChildrenLayers(symbolClassNode, symbol); return(symbolClassNode); }
private UxNode BuildShapePath(SketchShapePath shapePath) { var svgString = SketchCurvePointsToSvg.ToSvgString(shapePath.Path); var node = new UxNode { ClassName = "Path", SketchLayerName = shapePath.Name, Attributes = { ["Data"] = new UxString(svgString), ["StretchMode"] = new UxString("Fill") } }; return(BuildLayout(shapePath, node)); }
private void AddChildrenLayers(UxNode node, SketchLayer parent) { var hasMaskedChildLayer = HasMaskedChild(parent); if (hasMaskedChildLayer) { _log.Warning("Masked shapes are not supported " + parent.Name); node.Children.Add(new UxComment("Masked shape group is not supported in UX")); } else { node.Children = parent.Layers .AsEnumerable() .Reverse() .Select(BuildLayer) .Cast <IUxSerializeable>() .ToList(); } }
public void GivenDuplicateNamesCreatesPropertyForOneAndLogsErrorForTheOther() { var log = new MessageListLogger(); var transform = new TextPropertyTransform(log); var node = new UxNode("Panel"); node.Children.Add(CreateTextNode("Strata", "Grata")); node.Children.Add(CreateTextNode("Strata", "NonGrata")); transform.Apply(node); AssertHasAttribute((UxNode)node.Children.ToList()[0], "ux:Property", "Strata"); AssertHasAttribute(node.ChildrenOfClass("Text").ToList()[0], "Value", "{Property Strata}"); AssertHasAttribute(node, "Strata", "Grata"); Assert.That(log.Warnings().Count(), Is.EqualTo(1)); Assert.That(log.Warnings().First(), Is.EqualTo("Could not create a text property for the layer 'Strata', as a text property for another layer with the same name has already been created. Please use unique names for text layers within the same symbol.")); }
UxNode BuildShapeGroup(SketchShapeGroup shapeGroupLayer) { var groupNode = new UxNode { ClassName = "Panel", SketchLayerName = shapeGroupLayer.Name }; if (shapeGroupLayer.Layers.Count == 0) { _log.Warning($"Shape group {shapeGroupLayer.Name} has no layers"); return(groupNode); } // Warn if we have any combined shapes, as it is currently not supported var combinedShapes = shapeGroupLayer .Layers.Cast <SketchShapePath>() .Where(shape => shape.BooleanOperation != SketchBooleanOperation.NoOperation); if (combinedShapes.Any()) { groupNode.Children.Add(new UxComment("Combined shapes are not supported in UX")); _log.Warning("Combined shapes are not supported " + shapeGroupLayer.Name + " with " + combinedShapes.Count() + " number of combined layers"); return(groupNode); } var shapeNodes = shapeGroupLayer .Layers .Cast <SketchShapePath>() .Where(shape => shape.BooleanOperation == SketchBooleanOperation.NoOperation) .Select(BuildLayer); foreach (var shapeNode in shapeNodes) { ApplyShapeStyle(shapeGroupLayer, shapeNode); groupNode.Children.Add(shapeNode); } return(BuildLayout(shapeGroupLayer, groupNode)); }
UxNode BuildBitmap(SketchBitmap bitmap) { var imagePath = bitmap.Image.Path; ExportImage(bitmap.Image); var imageNode = new UxNode { ClassName = "Image", SketchLayerName = bitmap.Name, Attributes = new Dictionary <string, IUxSerializeable> { { "File", new UxString(imagePath) } } }; imageNode.Children.AddRange(BuildShadows(bitmap.Name, bitmap.Style)); return(BuildLayout(bitmap, imageNode)); }
public void Build(SketchDocument document, string outputDirectory) { var symbols = document .Pages .AsEnumerable() .SelectMany(page => page.Layers.OfType <SketchSymbolMaster>()); var symbolClassNameBuilder = new SymbolClassNameBuilder(); symbolClassNameBuilder.Init(symbols); var appNode = new UxNode { ClassName = "App" }; var pageControl = new UxNode { ClassName = "PageControl" }; appNode.Children.Add(pageControl); var artboards = document.Pages .AsEnumerable() .Reverse() .SelectMany(page => page.Layers .OfType <SketchArtboard>() .Reverse()); var builder = new UxBuilder(symbolClassNameBuilder, new AssetEmitter(outputDirectory, _log), _log); var pages = artboards.Select(builder.BuildPage); pageControl.Children.AddRange(pages); var serializerContext = new UxSerializerContext(); var ux = appNode.SerializeUx(serializerContext); var outputFilePath = Path.Combine(outputDirectory, "MainView.ux"); File.WriteAllText(outputFilePath, ux); }
public void SkipsAndLogsInvalidNames() { var log = new MessageListLogger(); var transform = new TextPropertyTransform(log); var node = new UxNode("Panel"); node.Children.Add(CreateTextNode("I have a space", "Ohayou Gozaimasu!")); node.Children.Add(CreateTextNode("var", "Konbanwa")); transform.Apply(node); var stringProperties = node.Children.Where(c => c is UxNode).Cast <UxNode>().Where(c => c.ClassName == "string"); Assert.That(stringProperties.Count(), Is.EqualTo(0)); Assert.That(node.Attributes.Keys, Does.Not.Contain("var")); Assert.That(node.Attributes.Keys, Does.Not.Contain("I have a space")); Assert.That(log.Warnings(), Does.Contain("Could not create a text property for the layer 'var', as 'var' is a reserved word. Please choose another name.")); Assert.That(log.Warnings(), Does.Contain("Could not create a text property for the layer 'I have a space', as it contains an invalid character. Please only use the letters a-z, numbers, or underscores, and don't start the name with a number.")); }
public void CreatesPropertyForStringValue() { var log = new MessageListLogger(); var transform = new TextPropertyTransform(log); var node = new UxNode("Panel"); node.Children.Add(CreateTextNode("TextLayer", "Guten Tag!")); transform.Apply(node); var propertyShouldBeFirstElement = (UxNode)node.Children.ToList()[0]; AssertHasAttribute(propertyShouldBeFirstElement, "ux:Property", "TextLayer"); var texts = node.ChildrenOfClass("Text").ToList(); Assert.That(texts.Count(), Is.EqualTo(1)); AssertHasAttribute(texts[0], "Value", "{Property TextLayer}"); AssertHasAttribute(node, "TextLayer", "Guten Tag!"); }
private void Apply(UxNode uxClass, List <PropertyValue> propertyValues) { foreach (var child in uxClass.Children.Where(c => c is UxNode).Cast <UxNode>()) { Apply(child, propertyValues); } if (uxClass.ClassName == "Text") { var layerName = uxClass.SketchLayerName; switch (NameValidator.NameIsValid(layerName)) { case NameValidity.InvalidCharacter: _log.Warning($"Could not create a text property for the layer '{layerName}', as it contains an invalid character. Please only use the letters a-z, numbers, or underscores, and don't start the name with a number."); break; case NameValidity.InvalidKeyword: _log.Warning($"Could not create a text property for the layer '{layerName}', as '{layerName}' is a reserved word. Please choose another name."); break; case NameValidity.Valid: if (propertyValues.Any(p => p.Name.Equals(layerName))) { _log.Warning($"Could not create a text property for the layer '{layerName}', as a text property for another layer with the same name has already been created. Please use unique names for text layers within the same symbol."); } else { var defaultValue = (UxString)uxClass.Attributes["Value"]; propertyValues.Add(new PropertyValue(layerName, defaultValue.Value)); uxClass.Attributes["Value"] = new UxString("{Property " + layerName + "}"); } break; default: throw new NotImplementedException("Internal error"); } } }
UxNode BuildText(SketchText text) { var uxNode = new UxNode { ClassName = "Text", SketchLayerName = text.Name }; if (text.AttributedString.Attributes.Count > 1) { _log.Warning($"UX Builder: Multiple text styles on the same text element not supported in UX. Found {text.AttributedString.Attributes.Count} text styles on {text.Name}, using just one of them."); } var stringAttributes = text.AttributedString.Attributes.First(); uxNode.Attributes["Color"] = SketchColorToFloat4(stringAttributes.Color); uxNode.Attributes["FontSize"] = new UxFloat((float)stringAttributes.FontSize); uxNode.Attributes["Value"] = new UxString(text.AttributedString.Contents); uxNode.Attributes["TextTruncation"] = new UxString("None"); uxNode.Attributes["TextWrapping"] = new UxString("Wrap"); if (stringAttributes.Alignment != SketchTextAlignment.Left) { uxNode.Attributes["TextAlignment"] = new UxString(BuildTextAlignment(stringAttributes.Alignment)); } uxNode.Children.AddRange(BuildShadows(text.Name, text.Style)); if (text.Style.Borders.Count > 0) { _log.Warning($"UX Builder: Borders on Sketch texts not supported in UX (Sketch object: {text.Name})."); } if (text.Style.Fills.Count > 0) { _log.Warning($"UX Builder: Fills on Sketch texts not supported in UX (Sketch object: {text.Name}). Using text style color instead."); } return(BuildLayout(text, uxNode)); }
UxNode BuildRectangle(SketchRectangle rectangle) { // Is axis aligned rectangle if (rectangle.Path == null) { throw new SketchParserException("Rectangle has no path"); } if (rectangle.Path.Points == null) { throw new SketchParserException("Rectangle.Path has no points"); } if (Geometry.IsAxisAlignedRectangle(rectangle.Path)) { // Create a Ux Rectangle if we have an axis aligned quadrilateral var node = new UxNode { ClassName = "Rectangle", SketchLayerName = rectangle.Name }; if (rectangle.Path.Points.Any(p => p.CornerRadius > 0.0)) { var cr = rectangle.Path.Points.Select(p => p.CornerRadius).ToList(); node.Attributes["CornerRadius"] = new UxFloat4( (float)cr[0], (float)cr[1], (float)cr[2], (float)cr[3] ); } return(BuildLayout(rectangle, node)); } // transformed rectangle, not axis aligned anymore or have less than or // more than four corners return(BuildShapePath(rectangle)); }
public static IEnumerable <UxNode> ChildrenOfClass(this UxNode node, string className) { return(node.Children .Where(c => c is UxNode && ((UxNode)c).ClassName == className) .Cast <UxNode>()); }
public UxNode Build(UxNode node) { var alignment = _layer.Alignment; var frame = _layer.Frame; var parentFrame = _layer.Parent.Frame; var attributes = node.Attributes; var horizontal = BuildAxisLayout(alignment.Horizontal, (float)frame.X, (float)frame.Width, (float?)parentFrame?.Width ?? 0); var vertical = BuildAxisLayout(alignment.Vertical, (float)frame.Y, (float)frame.Height, (float?)parentFrame?.Height ?? 0); if (horizontal.Size != null) { attributes["Width"] = horizontal.Size; } if (vertical.Size != null) { attributes["Height"] = vertical.Size; } var alignmentString = BuildUxAlignmentString(horizontal.Alignment, vertical.Alignment); if (alignmentString == "Default") { // If alignment is Default, we don't need to specify Width|Height="100%" if (horizontal.Size == UxSize.Percent(100)) { attributes.Remove("Width"); } if (vertical.Size == UxSize.Percent(100)) { attributes.Remove("Height"); } } else { attributes["Alignment"] = new UxString(alignmentString); } if (horizontal.Offset != null || vertical.Offset != null) { var horizontalOffset = horizontal.Offset ?? UxSize.Points(0); var verticalOffset = vertical.Offset ?? UxSize.Points(0); if (horizontalOffset.Value != 0 || verticalOffset.Value != 0) { attributes["Offset"] = new UxVector(horizontalOffset, verticalOffset); } } var hMargin = horizontal.Margin; var vMargin = vertical.Margin; attributes["Margin"] = new UxFloat4( hMargin.X, vMargin.X, hMargin.Y, vMargin.Y); return(node); }
private void AddUnsupportedMaskingWarning(UxNode node, string layerName) { _log.Warning("Masked shapes are not supported " + layerName); node.Children.Add(new UxComment("Masked shape group is not supported in UX")); }
static UxElement ParseElement(string initial) { return((UxElement)UxNode.FromSyntax(SyntaxParser.ParseNode(initial))); }
UxNode BuildLayerInternal(SketchLayer layer) { var group = layer as SketchGroup; if (group != null) { return(BuildGroup(group)); } var rectangle = layer as SketchRectangle; if (rectangle != null) { return(BuildRectangle(rectangle)); } var shapeGroup = layer as SketchShapeGroup; if (shapeGroup != null) { return(BuildShapeGroup(shapeGroup)); } var bitmap = layer as SketchBitmap; if (bitmap != null) { return(BuildBitmap(bitmap)); } var text = layer as SketchText; if (text != null) { return(BuildText(text)); } var symbolInstance = layer as SketchSymbolInstance; if (symbolInstance != null) { return(BuildSymbolInstance(symbolInstance)); } var shapePath = layer as SketchShapePath; if (shapePath != null) { return(BuildShapePath(shapePath)); } var warning = $"Unimplemented layer type: {layer.GetType().Name}"; _log.Warning(warning); var groupNode = new UxNode { ClassName = "Panel", SketchLayerName = layer.Name }; groupNode.Children.Add(new UxComment(warning)); return(groupNode); }
UxNode BuildLayout(SketchLayer layer, UxNode targetNode) { return(new LayoutBuilder(layer).Build(targetNode)); }