private static void AssertStyleNodesEqual(StyleNodeTestDef expected, StyleASTNode actual) { Assert.AreEqual(expected.type, actual.GetType()); if (expected.identifier != null) { if (actual is StyleNodeContainer n) { Assert.AreEqual(expected.identifier, n.identifier); } else if (actual is StyleIdentifierNode id) { Assert.AreEqual(expected.identifier, id.name); } else { Assert.IsTrue(false, $"Expected node to have an identifier {expected.identifier} but {actual} did not"); } } if (expected.nodeType != 0) { Assert.AreEqual(expected.nodeType, actual.type); } if (expected.type == typeof(MeasurementNode)) { AssertMeasurementNode(expected, actual as MeasurementNode); } if (expected.rawValue != null) { FieldInfo fieldInfo = null; if (ReflectionUtil.IsField(actual.GetType(), "rawValue", out fieldInfo) || ReflectionUtil.IsField(actual.GetType(), "value", out fieldInfo)) { Assert.AreEqual(expected.rawValue, fieldInfo.GetValue(actual)); } else { Assert.IsTrue(false, $"Expected {actual} to have a value or rawValue field, it did not"); } } if (expected.children != null) { if (actual is StyleNodeContainer c) { Assert.AreEqual(expected.children.Length, c.children.Count); for (int i = 0; i < expected.children.Length; i++) { AssertStyleNodesEqual(expected.children[i], c.children[i]); } } else { Assert.IsTrue(false, $"Expected node to have children but {actual} is not a StyleContainer"); } } }
private UISoundData CompileSoundProperties(SoundRootNode soundRootNode) { UISoundData soundData = new UISoundData(); // set defaults soundData.duration = new UITimeMeasurement(1, UITimeMeasurementUnit.Percentage); soundData.pitch = 1; soundData.iterations = 1; soundData.tempo = 1; for (int i = 0; i < soundRootNode.children.Count; i++) { StyleASTNode property = soundRootNode.children[i]; if (property is SoundPropertyNode soundPropertyNode) { StyleASTNode value = soundPropertyNode.value; switch (soundPropertyNode.name.ToLower()) { case "asset": soundData.asset = StylePropertyMappers.MapString(value, context); break; case "duration": soundData.duration = StylePropertyMappers.MapUITimeMeasurement(value, context); break; case "iterations": soundData.iterations = (int)StylePropertyMappers.MapNumberOrInfinite(value, context); break; case "pitch": soundData.pitch = StylePropertyMappers.MapNumber(value, context); break; case "pitchrange": soundData.pitchRange = StylePropertyMappers.MapFloatRange(value, context); break; case "tempo": soundData.tempo = StylePropertyMappers.MapNumber(value, context); break; case "volume": soundData.volume = StylePropertyMappers.MapNumber(value, context); break; case "mixergroup": soundData.mixerGroup = StylePropertyMappers.MapString(value, context); break; } } else { throw new CompileException(property, "Expected a sound property."); } } return(soundData); }
private StyleASTNode ParseRgb() { AssertTokenTypeAndAdvance(StyleTokenType.Rgb); AssertTokenTypeAndAdvance(StyleTokenType.ParenOpen); StyleASTNode red = ParseLiteralOrReference(StyleTokenType.Number); AssertTokenTypeAndAdvance(StyleTokenType.Comma); StyleASTNode green = ParseLiteralOrReference(StyleTokenType.Number); AssertTokenTypeAndAdvance(StyleTokenType.Comma); StyleASTNode blue = ParseLiteralOrReference(StyleTokenType.Number); AssertTokenTypeAndAdvance(StyleTokenType.ParenClose); return(StyleASTNodeFactory.RgbNode(red, green, blue)); }
/// <summary> /// Returns a referenced StyleASTNode if the passed in node is a ReferenceNode. /// Only called once all references have been resolved in the constants list. /// </summary> /// <param name="node">A node that can be a ReferenceNode or something else.</param> /// <returns>The referenced node or the node itself if it's a regular one.</returns> /// <exception cref="CompileException">thrown in case a reference cannot be resolved.</exception> public StyleASTNode GetValueForReference(StyleASTNode node) { if (node is ConstReferenceNode referenceNode) { for (int index = 0; index < constants.Count; index++) { StyleConstant c = constants[index]; if (c.name == referenceNode.identifier) { return(c.value); } } if (referenceNode.children.Count > 0) { if (importedStyleConstants.ContainsKey(referenceNode.identifier)) { DotAccessNode importedConstant = (DotAccessNode)referenceNode.children[0]; StyleConstant importedStyleConstant = importedStyleConstants[referenceNode.identifier] .Find(importedConstant.propertyName, s_FindStyleConstant); if (importedStyleConstant.name == null) { throw new CompileException(importedConstant, $"Could not find referenced property '{importedConstant.propertyName}' in imported scope '{referenceNode.identifier}'."); } return(importedStyleConstant.value); } throw new CompileException(referenceNode, "Constants cannot reference members of other constants."); } throw new CompileException(referenceNode, $"Couldn't resolve reference {referenceNode}. Known references are: {PrintConstants()}"); } return(node); }
private StyleASTNode ParsePropertyValue() { StyleToken propertyToken = tokenStream.Current; StyleASTNode propertyValue; switch (tokenStream.Current.styleTokenType) { case StyleTokenType.Number: StyleLiteralNode value = StyleASTNodeFactory.NumericLiteralNode(tokenStream.Current.value).WithLocation(propertyToken) as StyleLiteralNode; tokenStream.Advance(); if (!IsAnyTokenType(StyleTokenType.EndStatement, StyleTokenType.Number, StyleTokenType.Comma, StyleTokenType.ParenClose)) { UnitNode unit = ParseUnit().WithLocation(tokenStream.Previous) as UnitNode; propertyValue = StyleASTNodeFactory.MeasurementNode(value, unit); } else { propertyValue = value; } break; case StyleTokenType.String: propertyValue = StyleASTNodeFactory.StringLiteralNode(tokenStream.Current.value); tokenStream.Advance(); if (tokenStream.Current == StyleTokenType.BracesOpen) { TextUtil.StringBuilder.Clear(); StyleLiteralNode n = (StyleLiteralNode)propertyValue; TextUtil.StringBuilder.Append("\""); TextUtil.StringBuilder.Append(n.rawValue); TextUtil.StringBuilder.Append("\" "); while (tokenStream.HasMoreTokens && tokenStream.Current != StyleTokenType.BracesClose) { TextUtil.StringBuilder.Append(tokenStream.Current.value); tokenStream.Advance(); } TextUtil.StringBuilder.Append(tokenStream.Current.value); tokenStream.Advance(); n.rawValue = TextUtil.StringBuilder.ToString(); TextUtil.StringBuilder.Clear(); } break; case StyleTokenType.Identifier: string identifier = tokenStream.Current.value; tokenStream.Advance(); if (tokenStream.Current.styleTokenType == StyleTokenType.ParenOpen) { tokenStream.Advance(); propertyValue = ParsePropertyFunction(identifier); } else { propertyValue = StyleASTNodeFactory.IdentifierNode(identifier); } break; case StyleTokenType.Rgba: propertyValue = ParseRgba(); break; case StyleTokenType.Rgb: propertyValue = ParseRgb(); break; case StyleTokenType.HashColor: propertyValue = StyleASTNodeFactory.ColorNode(tokenStream.Current.value); tokenStream.Advance(); break; case StyleTokenType.Url: tokenStream.Advance(); AssertTokenTypeAndAdvance(StyleTokenType.ParenOpen); StyleASTNode url; StyleASTNode spriteName = null; if (tokenStream.Current.styleTokenType == StyleTokenType.String) { url = ParseLiteralOrReference(StyleTokenType.String); } else { url = ParseLiteralOrReference(StyleTokenType.Identifier); } if (url is StyleIdentifierNode urlIdentifier) { // todo -- this doesn't handle spaces! while (tokenStream.HasMoreTokens && !AdvanceIfTokenType(StyleTokenType.ParenClose)) { // advancing tokens no matter the type. We want to concatenate all identifiers and slashes of a path again. urlIdentifier.name += tokenStream.Current.value; spriteName = ParseSpriteName(); tokenStream.Advance(); } } else if (url is StyleLiteralNode || url is ConstReferenceNode) { spriteName = ParseSpriteName(); AssertTokenTypeAndAdvance(StyleTokenType.ParenClose); } else { throw new CompileException(url, "URL could not be parsed."); } propertyValue = StyleASTNodeFactory.UrlNode(url, spriteName); break; case StyleTokenType.At: propertyValue = ParseConstReference(); break; case StyleTokenType.Dollar: propertyValue = ParseVariableReference(); break; default: throw new ParseException(propertyToken, "Expected a property value but found no valid token."); } return(propertyValue.WithLocation(propertyToken)); }
public CompileException(string fileName, StyleASTNode node, string message = null) : base($"Compile error for style token at line {node.line}, column {node.column}, node type '{node.type}'\n{message}") { this.fileName = fileName; }
private void CompileStyleGroups(StyleNodeContainer root, StyleType styleType, LightList <UIStyleGroup> groups, UIStyleGroup targetGroup, AnimationData[] styleSheetAnimations, UISoundData[] uiSoundData) { for (int index = 0; index < root.children.Count; index++) { StyleASTNode node = root.children[index]; switch (node) { case SelectNode selectNode: break; case PropertyNode propertyNode: // add to normal ui style set StylePropertyMappers.MapProperty(targetGroup.normal.style, propertyNode, context); break; case AttributeNodeContainer attribute: if (root is AttributeNodeContainer) { throw new CompileException(attribute, "You cannot nest attribute group definitions."); } UIStyleGroup attributeGroup = new UIStyleGroup(); attributeGroup.normal = UIStyleRunCommand.CreateInstance(); attributeGroup.name = root.identifier; attributeGroup.rule = MapAttributeContainerToRule(attribute); attributeGroup.styleType = styleType; groups.Add(attributeGroup); CompileStyleGroups(attribute, styleType, groups, attributeGroup, styleSheetAnimations, uiSoundData); break; case RunNode runNode: UIStyleRunCommand cmd = new UIStyleRunCommand() { style = targetGroup.normal.style, runCommands = targetGroup.normal.runCommands ?? new LightList <IRunCommand>(4) }; if (runNode.command is AnimationCommandNode animationCommandNode) { MapAnimationCommand(styleSheetAnimations, cmd, animationCommandNode); } else if (runNode.command is SoundCommandNode soundCommandNode) { MapSoundCommand(uiSoundData, cmd, soundCommandNode); } targetGroup.normal = cmd; break; case StyleStateContainer styleContainer: if (styleContainer.identifier == "hover") { UIStyleRunCommand uiStyleRunCommand = targetGroup.hover; uiStyleRunCommand.style = uiStyleRunCommand.style ?? new UIStyle(); MapProperties(styleSheetAnimations, uiSoundData, ref uiStyleRunCommand, styleContainer.children); targetGroup.hover = uiStyleRunCommand; } else if (styleContainer.identifier == "focus") { UIStyleRunCommand uiStyleRunCommand = targetGroup.focused; uiStyleRunCommand.style = uiStyleRunCommand.style ?? new UIStyle(); MapProperties(styleSheetAnimations, uiSoundData, ref uiStyleRunCommand, styleContainer.children); targetGroup.focused = uiStyleRunCommand; } else if (styleContainer.identifier == "active") { UIStyleRunCommand uiStyleRunCommand = targetGroup.active; uiStyleRunCommand.style = uiStyleRunCommand.style ?? new UIStyle(); MapProperties(styleSheetAnimations, uiSoundData, ref uiStyleRunCommand, styleContainer.children); targetGroup.active = uiStyleRunCommand; } else { throw new CompileException(styleContainer, $"Unknown style state '{styleContainer.identifier}'. Please use [hover], [focus] or [active] instead."); } break; default: throw new CompileException(node, $"You cannot have a {node} at this level."); } } }
private AnimationOptions CompileSpriteSheetOptions(SpriteSheetNode node) { AnimationOptions options = new AnimationOptions(); LightList <StyleASTNode> spriteSheetProperties = node.children; if (spriteSheetProperties == null) { return(options); } for (int i = 0; i < spriteSheetProperties.Count; i++) { if (spriteSheetProperties[i] is PropertyNode property) { string optionName = property.identifier.ToLower(); StyleASTNode value = property.children[0]; switch (optionName) { case "iterations": options.iterations = (int)StylePropertyMappers.MapNumberOrInfinite(value, context); break; case "delay": options.delay = StylePropertyMappers.MapUITimeMeasurement(value, context); break; case "duration": options.duration = StylePropertyMappers.MapUITimeMeasurement(value, context); break; case "looptype": options.loopType = StylePropertyMappers.MapEnum <AnimationLoopType>(value, context); break; case "direction": options.direction = StylePropertyMappers.MapEnum <AnimationDirection>(value, context); break; case "forwardstartdelay": options.forwardStartDelay = (int)StylePropertyMappers.MapNumber(value, context); break; case "reversestartdelay": options.reverseStartDelay = (int)StylePropertyMappers.MapNumber(value, context); break; case "fps": options.fps = (int)StylePropertyMappers.MapNumber(value, context); break; case "startframe": options.startFrame = (int)StylePropertyMappers.MapNumber(value, context); break; case "endframe": options.endFrame = (int)StylePropertyMappers.MapNumber(value, context); break; case "pathprefix": options.pathPrefix = StylePropertyMappers.MapString(value, context); break; default: throw new CompileException(property, "Invalid option argument for animation"); } } else { throw new CompileException(spriteSheetProperties[i], "Invalid option argument for animation"); } } return(options); }
private AnimationOptions CompileAnimationOptions(AnimationRootNode animNode) { AnimationOptions options = new AnimationOptions(); if (animNode.optionNodes == null) { return(options); } LightList <AnimationOptionNode> optionNodes = animNode.optionNodes; if (optionNodes == null) { return(options); } for (int i = 0; i < optionNodes.Count; i++) { string optionName = optionNodes[i].optionName.ToLower(); StyleASTNode value = optionNodes[i].value; switch (optionName) { case "duration": options.duration = StylePropertyMappers.MapUITimeMeasurement(value, context); break; case "iterations": options.iterations = (int)StylePropertyMappers.MapNumberOrInfinite(value, context); break; case "looptime": options.loopTime = StylePropertyMappers.MapNumber(value, context); break; case "delay": options.delay = StylePropertyMappers.MapUITimeMeasurement(value, context); break; case "direction": options.direction = StylePropertyMappers.MapEnum <AnimationDirection>(value, context); break; case "looptype": options.loopType = StylePropertyMappers.MapEnum <AnimationLoopType>(value, context); break; case "forwardstartdelay": options.forwardStartDelay = (int)StylePropertyMappers.MapNumber(value, context); break; case "reversestartdelay": options.reverseStartDelay = (int)StylePropertyMappers.MapNumber(value, context); break; case "timingfunction": options.timingFunction = StylePropertyMappers.MapEnum <EasingFunction>(value, context); break; default: throw new CompileException(optionNodes[i], "Invalid option argument for animation"); } } return(options); }
private unsafe StyleAnimationKeyFrame[] CompileKeyFrames(AnimationRootNode animNode, AnimationData[] styleSheetAnimations, UISoundData[] uiSoundData) { if (animNode.keyframeNodes == null) { // todo throw error or log warning? return(new StyleAnimationKeyFrame[0]); } int keyframeCount = 0; for (int i = 0; i < animNode.keyframeNodes.Count; i++) { keyframeCount += animNode.keyframeNodes[i].keyframes.Count; } StyleAnimationKeyFrame[] frames = new StyleAnimationKeyFrame[keyframeCount]; int nextKeyframeIndex = 0; for (int i = 0; i < animNode.keyframeNodes.Count; i++) { KeyFrameNode keyFrameNode = animNode.keyframeNodes[i]; // todo -- this is madness and not working, fix it!!!!!!! for (int j = 0; j < keyFrameNode.children.Count; j++) { StyleASTNode keyFrameProperty = keyFrameNode.children[j]; if (keyFrameProperty is PropertyNode propertyNode) { StylePropertyMappers.MapProperty(s_ScratchStyle, propertyNode, context); } else if (keyFrameProperty is MaterialPropertyNode materialPropertyNode) { string materialName = materialPropertyNode.materialName; if (context.materialDatabase.TryGetBaseMaterialId(materialName, out MaterialId materialId)) { if (context.materialDatabase.TryGetMaterialProperty(materialId, materialPropertyNode.identifier, out MaterialPropertyInfo propertyInfo)) { fixed(char *charptr = materialPropertyNode.value) { CharStream stream = new CharStream(charptr, 0, (uint)materialPropertyNode.value.Length); MaterialKeyFrameValue kfv = default; switch (propertyInfo.propertyType) { case MaterialPropertyType.Color: if (stream.TryParseColorProperty(out Color32 color)) { kfv = new MaterialKeyFrameValue(materialId, propertyInfo.propertyId, new MaterialPropertyValue2() { colorValue = color }); } break; case MaterialPropertyType.Float: if (stream.TryParseFloat(out float floatVal)) { kfv = new MaterialKeyFrameValue(materialId, propertyInfo.propertyId, new MaterialPropertyValue2() { floatValue = floatVal }); } break; case MaterialPropertyType.Vector: break; case MaterialPropertyType.Range: break; case MaterialPropertyType.Texture: break; default: throw new ArgumentOutOfRangeException(); } } } } } } StructList <StyleKeyFrameValue> styleKeyValues = new StructList <StyleKeyFrameValue>(s_ScratchStyle.PropertyCount); for (int j = 0; j < s_ScratchStyle.PropertyCount; j++) { styleKeyValues[j] = new StyleKeyFrameValue(s_ScratchStyle[j]); } styleKeyValues.size = s_ScratchStyle.PropertyCount; for (int keyframeIndex = 0; keyframeIndex < keyFrameNode.keyframes.Count; keyframeIndex++) { float time = keyFrameNode.keyframes[keyframeIndex] / 100f; frames[nextKeyframeIndex] = new StyleAnimationKeyFrame(time) { properties = styleKeyValues }; nextKeyframeIndex++; } s_ScratchStyle.PropertyCount = 0; } return(frames); }