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));
        }
Пример #2
0
        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);
        }
Пример #3
0
        private static UxNode StringProperty(string name)
        {
            var property = new UxNode("string");

            property.Attributes["ux:Property"] = new UxString(name);
            return(property);
        }
Пример #4
0
        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?");
        }
Пример #6
0
        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);
        }
Пример #8
0
        private UxNode BuildSymbolInstance(SketchSymbolInstance symbolInstance)
        {
            var className = _symbolClassNameBuilder.GetClassName(symbolInstance);
            var node      = new UxNode {
                ClassName       = className,
                SketchLayerName = symbolInstance.Name
            };

            return(BuildLayout(symbolInstance, node));
        }
Пример #9
0
        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));
            }
        }
Пример #10
0
        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);
        }
Пример #11
0
        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));
        }
Пример #12
0
        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);
        }
Пример #13
0
        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));
        }
Пример #14
0
        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."));
        }
Пример #16
0
        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));
        }
Пример #17
0
        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));
        }
Пример #18
0
        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!");
        }
Пример #21
0
        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");
                }
            }
        }
Пример #22
0
        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));
        }
Пример #23
0
 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>());
 }
Пример #25
0
        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);
        }
Пример #26
0
 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"));
 }
Пример #27
0
 static UxElement ParseElement(string initial)
 {
     return((UxElement)UxNode.FromSyntax(SyntaxParser.ParseNode(initial)));
 }
Пример #28
0
        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);
        }
Пример #29
0
 UxNode BuildLayout(SketchLayer layer, UxNode targetNode)
 {
     return(new LayoutBuilder(layer).Build(targetNode));
 }