private static string TryGetFullXamarinFormsLineReplacement(InstanceSave instance, ElementSave container, VariableSave variable, StateSave state) { var rootName = variable.GetRootName(); if (rootName == "IsXamarinFormsControl" || rootName == "Name" || rootName == "X Origin" || rootName == "XOrigin" || rootName == "Y Origin" || rootName == "YOrigin") { return(" "); // Don't do anything with these variables:: } else if (rootName == "Parent") { var parentName = variable.Value as string; var parentInstance = container.GetInstance(parentName); var hasContent = parentInstance?.BaseType.EndsWith("/ScrollView") == true || parentInstance?.BaseType.EndsWith("/StickyScrollView") == true; if (hasContent) { return($"{parentName}.Content = {instance.Name};"); } else { return($"{parentName}.Children.Add({instance.Name});"); } } return(null); }
private string GetEffectiveParentNameFor(InstanceSave instance, ElementSave owner) { var variableName = instance.Name + ".Parent"; var state = owner.DefaultState; var parentVariableValue = state.Variables.Find(item => item.Name == variableName)?.Value; var parentName = (string)parentVariableValue; // even though an instance may have a parent variable, we don't consider // it an actual parent if there is no instance with that name (it could be // a left-over variable). if (!string.IsNullOrEmpty(parentName)) { var matchingInstance = owner.GetInstance(parentName); if (matchingInstance == null) { parentName = null; } } return(parentName); }
private void GenerateExposedVariableProperty(ElementSave elementSave, ICodeBlock currentBlock, VariableSave variable) { string variableType = variable.Type; ModifyVariableTypeForProperty(ref variableType, variable, elementSave); string propertyName = variable.ExposedAsName.Replace(" ", "_"); ICodeBlock property = currentBlock.Property("public " + variableType, propertyName); string whatToGetOrSet = variable.Name; // If this is an exposed property on a standard element, then we just need to kill all spaces and replace // them with nothing var instance = elementSave.GetInstance(variable.SourceObject); if (instance != null) { var baseElement = Gum.Managers.ObjectFinder.Self.GetElementSave(instance.BaseType); if (baseElement != null && baseElement is StandardElementSave) { whatToGetOrSet = whatToGetOrSet.Replace(" ", ""); } var rootName = variable.GetRootName(); if (rootName.EndsWith("State")) { var withoutState = rootName.Substring(0, rootName.Length - "State".Length); if (rootName == "State") { whatToGetOrSet = variable.SourceObject + "." + "CurrentVariableState"; } else if (baseElement != null && baseElement.Categories.Any(item => item.Name == withoutState)) { whatToGetOrSet = variable.SourceObject + ".Current" + withoutState + "State"; } } } property.Get() .Line("return " + whatToGetOrSet + ";"); var setter = property.Set(); if (EventCodeGenerator.Self.GetIfShouldGenerateEventOnVariableSet(elementSave, variable)) { string eventName = EventCodeGenerator.Self.GetEventName(variable, elementSave); setter.If($"{whatToGetOrSet} != value") .Line(whatToGetOrSet + " = value;") .Line($"{eventName}?.Invoke(this, null);"); } else { setter.Line(whatToGetOrSet + " = value;"); } }
private bool GetIfShouldApplyValue(ElementSave gumElement, GumInstance gumInstance, string variableName) { var shouldApply = true; if (variableName == "Parent" && gumInstance != null) { var currentState = SelectedState.Self.SelectedStateSave; var newParentName = currentState.GetValueOrDefault <string>($"{gumInstance.Name}.Parent"); GumInstance newGumParent = null; var glueElement = GluePluginObjectFinder.Self.GetGlueElementFrom(gumElement); var glueInstance = glueElement?.AllNamedObjects.FirstOrDefault(item => item.InstanceName == gumInstance.Name); FlatRedBall.Glue.SaveClasses.NamedObjectSave newGlueInstanceParent = null; if (!string.IsNullOrEmpty(newParentName)) { newGumParent = gumElement.GetInstance(newParentName); newGlueInstanceParent = glueElement?.AllNamedObjects.FirstOrDefault(item => item.FieldName == newParentName); } if (newGumParent != null) { if (newGlueInstanceParent?.SourceType == FlatRedBall.Glue.SaveClasses.SourceType.FlatRedBallType && newGlueInstanceParent.SourceClassType == "ShapeCollection") { var canBeParent = glueInstance?.SourceType == FlatRedBall.Glue.SaveClasses.SourceType.FlatRedBallType && ( glueInstance.SourceClassType == "Circle" || glueInstance.SourceClassType == "FlatRedBall.Math.Geometry.Circle" || glueInstance.SourceClassType == "AxisAlignedRectangle" || glueInstance.SourceClassType == "FlatRedBall.Math.Geometry.AxisAlignedRectangle" || glueInstance.SourceClassType == "Polygon" || glueInstance.SourceClassType == "FlatRedBall.Math.Geometry.Polygon" ); shouldApply = canBeParent; } else { var canNewParentBeParent = newGumParent.BaseType == "Container"; shouldApply = canNewParentBeParent; } } } return(shouldApply); }
private InstanceSave FindParentInstance(InstanceSave instance) { ElementSave element = instance.ParentContainer; string name = instance.Name + ".Parent"; VariableSave variable = element.DefaultState.Variables.FirstOrDefault(v => v.Name == name); if (variable != null && variable.SetsValue && variable.Value != null) { string parentName = (string)variable.Value; return(element.GetInstance(parentName)); } return(null); }
private float GetAnimationLength(ElementSave element, AnimationSave animation) { float max = 0; if (animation.States.Count != 0) { max = animation.States.Max(item => item.Time); } if (animation.Events.Count != 0) { max = System.Math.Max( max, animation.Events.Max(item => item.Time)); } foreach (var item in animation.Animations) { AnimationSave subAnimation = null; ElementSave subElement = null; if (!string.IsNullOrEmpty(item.SourceObject)) { var instance = element.GetInstance(item.SourceObject); // This may refer to an instance that was deleted at some point: if (instance != null) { subElement = Gum.Managers.ObjectFinder.Self.GetElementSave(instance); } if (subElement != null) { subAnimation = GetAnimationsFor(subElement).Animations.FirstOrDefault(candidate => candidate.Name == item.RootName); } } else { subElement = element; subAnimation = GetAnimationsFor(element).Animations.FirstOrDefault(candidate => candidate.Name == item.RootName); } if (subElement != null && subAnimation != null) { max = Math.Max(max, item.Time + GetAnimationLength(subElement, subAnimation)); } } return(max); }
private void HandleVariableSet(ElementSave container, InstanceSave instance, string variableName, object oldValue) { if (variableName == "Parent" && instance != null) { var currentState = SelectedState.Self.SelectedStateSave; var newParentName = currentState.GetValueOrDefault <string>($"{instance.Name}.Parent"); InstanceSave newParent = null; if (!string.IsNullOrEmpty(newParentName)) { newParent = container.GetInstance(newParentName); } if (newParent != null) { var typeRestriction = currentState.GetValueOrDefault <string>($"{newParent.Name}.Contained Type"); if (!string.IsNullOrEmpty(typeRestriction)) { // this is allowed only if the child inherits from this type var doTypesMatchExactly = typeRestriction == instance.BaseType; var doTypesMatchConsideringInheritance = false; if (!doTypesMatchExactly) { var element = ObjectFinder.Self.GetElementSave(typeRestriction); if (element != null) { var typesInheritingFromRestriction = ObjectFinder.Self.GetElementsInheritingFrom(element); doTypesMatchConsideringInheritance = typesInheritingFromRestriction.Any(item => item.Name == typeRestriction); } } var shouldRevert = doTypesMatchExactly == false && doTypesMatchConsideringInheritance == false; if (shouldRevert) { // This container can't support this value currentState.SetValue($"{instance.Name}.Parent", oldValue, "string"); GumCommands.Self.GuiCommands.PrintOutput( $"The instance {newParent.Name} has a type restriction of {typeRestriction} so {instance.Name} cannot be added as a child."); } } } } }
private static void FillWithChildrenOf(InstanceSave instance, List <InstanceSave> listToFill, ElementSave container) { var defaultState = container.DefaultState; foreach (var variable in defaultState.Variables) { if (variable.GetRootName() == "Parent") { var value = variable.Value as string; if (!string.IsNullOrEmpty(value) && value == instance.Name) { var foundObject = container.GetInstance(variable.SourceObject); if (foundObject != null && listToFill.Any(item => item.Name == foundObject.Name) == false) { listToFill.Add(foundObject.Clone()); FillWithChildrenOf(foundObject, listToFill, container); } } } } }
private static ElementSave GetBaseElementFromVariable(string variableName, ElementSave parent) { // this thing is the default state // But it's null, so we have to look // to the parent ElementSave baseElement = null; if (StringFunctions.ContainsNoAlloc(variableName, '.')) { string instanceToSearchFor = variableName.Substring(0, variableName.IndexOf('.')); InstanceSave instanceSave = parent.GetInstance(instanceToSearchFor); if (instanceSave != null) { baseElement = ObjectFinder.Self.GetElementSave(instanceSave.BaseType); } } else { baseElement = ObjectFinder.Self.GetElementSave(parent.BaseType); } return(baseElement); }
/// <summary> /// Attempts to get the value for the argument variableName, or null if not found. /// </summary> /// <param name="variableName">The qualified variable name</param> /// <returns>The value found, or null</returns> public object GetValue(string variableName) { ////////////////////Early Out//////////////// if (ParentContainer == null) { return(null); } //////////////////End Early Out////////////// // Check for reserved stuff if (variableName == "Name") { return(ParentContainer.Name); } else if (variableName == "Base Type") { if (string.IsNullOrEmpty(ParentContainer.BaseType)) { return(null); } else { string baseType = ParentContainer.BaseType; StandardElementTypes returnValue; if (Enum.TryParse <StandardElementTypes>(baseType, out returnValue)) { return(returnValue); } else { return(baseType); } } } if (ToolsUtilities.StringFunctions.ContainsNoAlloc(variableName, '.')) { string instanceName = variableName.Substring(0, variableName.IndexOf('.')); ElementSave elementSave = ParentContainer; InstanceSave instanceSave = null; if (elementSave != null) { instanceSave = elementSave.GetInstance(instanceName); } if (instanceSave != null) { // This is a variable on an instance if (variableName.EndsWith(".Name")) { return(instanceSave.Name); } else if (variableName.EndsWith(".Base Type")) { return(instanceSave.BaseType); } else if (variableName.EndsWith(".Locked")) { return(instanceSave.Locked); } } } VariableSave variableState = GetVariableSave(variableName); // If the user hasn't set this variable on this state, it'll be null. So let's just display null // for now. Eventually we'll display a variable plus some kind of indication that it's an unset variable. if (variableState == null || variableState.SetsValue == false) { VariableListSave variableListSave = GetVariableListSave(variableName); if (variableListSave != null) { return(variableListSave.ValueAsIList); } else { return(null); } } else { return(variableState.Value); } }
private bool GetIfShouldGenerateStateVariable(Gum.DataTypes.Variables.VariableSave variable, ElementSave container) { bool toReturn = true; string variableName = variable.GetRootName(); if (variable.Value == null || !variable.SetsValue) { toReturn = false; } // states can't set states on this if (variable.IsState(container) && string.IsNullOrEmpty(variable.SourceObject)) { toReturn = false; } if (toReturn && mVariableNamesToSkipForStates.Contains(variableName)) { toReturn = false; } bool hasSourceObject = !string.IsNullOrEmpty(variable.SourceObject); if (toReturn && hasSourceObject) { InstanceSave instanceSave = container.GetInstance(variable.SourceObject); if (instanceSave == null) { toReturn = false; } else { var baseElement = Gum.Managers.ObjectFinder.Self.GetElementSave(instanceSave.BaseType); if (baseElement == null) { toReturn = false; } if (toReturn) { // Gum (just like Glue) keeps variables that aren't needed around. This allows users to rename things and not lose // important information accidentally. But because of that we have to make sure that the variable we're working with is // valid for the type of object we're dealing with. var defaultState = baseElement.DefaultState; // October 26, 2018 // Bernardo reported // a crash caused by the // RecursiveVariableFinder // being given a state without // a ParentContainer. This is a // sign that the element hasn't // been initialized yet. Elements // should be initialized, but if they're // not, we could just catch it here and initialize // it on the spot. Not sure if I like this solution // or not. It allows code to behave a little unpredictably, // but at the same time, we could simply solve the problem by // initializing here, so I'm going to do that: if (defaultState.ParentContainer == null) { baseElement.Initialize(null); } RecursiveVariableFinder rvf = new RecursiveVariableFinder(defaultState); var foundVariable = rvf.GetVariable(variable.GetRootName()); if (foundVariable == null) { // This doesn't exist anywhere in the inheritance chain, so we don't want to generate it: toReturn = false; } } } } if (toReturn && !hasSourceObject) { // If a variable is part of a component, it better be defined in the base type or else we won't generate it. // For example, consider a component that used to inherit from Text. It will have variables for fonts. If that // component switches to inheriting from Sprite, those variables will still exist in the XML for that component, // but we shouldn't generate any state variables for those variables. So we'll go to the base type and see if those // variables exist bool isComponent = container is ComponentSave; var rootStandardElementSave = Gum.Managers.ObjectFinder.Self.GetRootStandardElementSave(container); // If the Container is a Screen, then rootComponent will be null, so we don't need to do anything if (rootStandardElementSave == null) { toReturn = false; } else { IEnumerable <VariableSave> variablesToCheck; // This code used to get the default state from the rootStandardElementSave, // but the standard element save can have variables missing from the Gum XML, // but it should still support them based on the definition in the StandardElementsManager, // especially if new variables have been added in the future. Therefore, use the StandardElementsManager // rather than the DefaultState: //var rootStandardElementVariables = rootStandardElementSave.DefaultState.Variables; var rootStandardElementVariables = StandardElementsManager.Self .DefaultStates[rootStandardElementSave.Name].Variables; if (isComponent) { var component = Gum.Managers.ObjectFinder.Self.GetStandardElement("Component"); variablesToCheck = rootStandardElementVariables.Concat(component.DefaultState.Variables).ToList(); } else { variablesToCheck = rootStandardElementVariables.ToList(); } bool wasMatchFound = variablesToCheck.Any(item => item.Name == variable.GetRootName()); toReturn = wasMatchFound; } } return(toReturn); }
public void DisplayReferencesTo(ElementSave element) { var elementName = element.Name; List <object> references = new List <object>(); foreach (var screen in ProjectState.Self.GumProjectSave.Screens) { foreach (var instanceInScreen in screen.Instances) { if (instanceInScreen.BaseType == elementName) { references.Add(instanceInScreen); } } foreach (var variable in screen.DefaultState.Variables.Where(item => item.GetRootName() == "Contained Type")) { if (variable.Value as string == elementName) { references.Add(variable); } } } foreach (var component in ProjectState.Self.GumProjectSave.Components) { if (component.BaseType == elementName) { references.Add(component); } foreach (var instanceInScreen in component.Instances) { if (instanceInScreen.BaseType == elementName) { references.Add(instanceInScreen); } } foreach (var variable in component.DefaultState.Variables.Where(item => item.GetRootName() == "Contained Type")) { if (variable.Value as string == elementName) { references.Add(variable); } } } if (references.Count > 0) { //var stringBuilder = new StringBuilder(); //stringBuilder.AppendLine($"The following objects reference {element}"); //foreach(var reference in references) //{ // stringBuilder.AppendLine(reference.ToString()); //} //GumCommands.Self.GuiCommands.ShowMessage(stringBuilder.ToString()); ListBoxMessageBox lbmb = new ListBoxMessageBox(); lbmb.RequiresSelection = true; lbmb.Message = $"The following objects reference {element}"; lbmb.ItemSelected += (not, used) => { var selectedItem = lbmb.SelectedItem; if (selectedItem is InstanceSave instance) { SelectedState.Self.SelectedInstance = instance; } else if (selectedItem is ElementSave selectedElement) { SelectedState.Self.SelectedElement = selectedElement; } else if (selectedItem is VariableSave variable) { ElementSave foundElement = ObjectFinder.Self.GumProjectSave.Screens .FirstOrDefault(item => item.DefaultState.Variables.Contains(variable)); if (foundElement == null) { foundElement = ObjectFinder.Self.GumProjectSave.Components .FirstOrDefault(item => item.DefaultState.Variables.Contains(variable)); } if (foundElement != null) { // what's the instance? var instanceWithVariable = foundElement.GetInstance(variable.SourceObject); if (instanceWithVariable != null) { SelectedState.Self.SelectedInstance = instanceWithVariable; } } } }; foreach (var reference in references) { lbmb.Items.Add(reference); } lbmb.HideCancelNoDialog(); lbmb.Show(); } else { GumCommands.Self.GuiCommands.ShowMessage($"{element} is not referenced by any other Screen/Component"); } }
private static void FillWithVariablesInState(ElementSave container, StateSave stateSave, StringBuilder stringBuilder, int tabCount) { VariableSave[] variablesToConsider = stateSave.Variables // make "Parent" first .Where(item => item.GetRootName() != "Parent") .ToArray(); var variableGroups = variablesToConsider.GroupBy(item => item.SourceObject); foreach (var group in variableGroups) { InstanceSave instance = null; var instanceName = group.Key; if (instanceName != null) { instance = container.GetInstance(instanceName); } #region Determine visual API (Gum or Forms) VisualApi visualApi = VisualApi.Gum; var defaultState = container.DefaultState; bool?isXamForms = false; if (instance == null) { isXamForms = defaultState.GetValueRecursive($"IsXamarinFormsControl") as bool?; } else { isXamForms = defaultState.GetValueRecursive($"{instance.Name}.IsXamarinFormsControl") as bool?; } if (isXamForms == true) { visualApi = VisualApi.XamarinForms; } #endregion ElementSave baseElement = null; if (instance == null) { baseElement = Gum.Managers.ObjectFinder.Self.GetElementSave(container.BaseType) ?? container; } else { baseElement = Gum.Managers.ObjectFinder.Self.GetElementSave(instance?.BaseType); } var baseDefaultState = baseElement?.DefaultState; RecursiveVariableFinder baseRecursiveVariableFinder = new RecursiveVariableFinder(baseDefaultState); List <VariableSave> variablesForThisInstance = group .Where(item => GetIfVariableShouldBeIncludedForInstance(instance, item, baseRecursiveVariableFinder)) .ToList(); ProcessVariableGroups(variablesForThisInstance, stateSave, instance, container, visualApi, stringBuilder, tabCount); // Now that they've been processed, we can process the remainder regularly foreach (var variable in variablesForThisInstance) { var codeLine = GetCodeLine(instance, variable, container, visualApi, stateSave); stringBuilder.AppendLine(ToTabs(tabCount) + codeLine); var suffixCodeLine = GetSuffixCodeLine(instance, variable, visualApi); if (!string.IsNullOrEmpty(suffixCodeLine)) { stringBuilder.AppendLine(ToTabs(tabCount) + suffixCodeLine); } } } }
private bool AdjustInstance(ElementSave baseElement, ElementSave derivedElement, string instanceName) { var instanceInBase = baseElement.GetInstance(instanceName); var instanceInDerived = derivedElement.GetInstance(instanceName); var indexInBase = baseElement.Instances.IndexOf(instanceInBase); string nameOfObjectBefore = null; if (indexInBase > 0) { nameOfObjectBefore = baseElement.Instances[indexInBase - 1].Name; } string nameOfObjectAfter = null; if (indexInBase < baseElement.Instances.Count - 1) { nameOfObjectAfter = baseElement.Instances[indexInBase + 1].Name; } int exclusiveLowerIndexInDerived = -1; if (nameOfObjectBefore != null) { var instanceBefore = derivedElement.GetInstance(nameOfObjectBefore); exclusiveLowerIndexInDerived = derivedElement.Instances.IndexOf(instanceBefore); } int exclusiveUpperIndexInDerived = derivedElement.Instances.Count; if (nameOfObjectAfter != null) { var instanceAfter = derivedElement.GetInstance(nameOfObjectAfter); exclusiveUpperIndexInDerived = derivedElement.Instances.IndexOf(instanceAfter); } int currentDerivedIndex = derivedElement.Instances.IndexOf(instanceInDerived); var desiredIndex = System.Math.Min(currentDerivedIndex, exclusiveUpperIndexInDerived - 1); desiredIndex = System.Math.Max(desiredIndex, exclusiveLowerIndexInDerived + 1); bool didAdjust = false; if (desiredIndex != currentDerivedIndex) { didAdjust = true; derivedElement.Instances.Remove(instanceInDerived); if (currentDerivedIndex < desiredIndex) { // we'll remove it from instances, which shifts everything above it up by one, so we have to -1 it derivedElement.Instances.Insert(desiredIndex - 1, instanceInDerived); } else { derivedElement.Instances.Insert(desiredIndex, instanceInDerived); } } return(didAdjust); }
private bool GetIfShouldGenerateStateVariable(Gum.DataTypes.Variables.VariableSave variable, ElementSave container) { bool toReturn = true; string variableName = variable.GetRootName(); if (variable.Value == null || !variable.SetsValue) { toReturn = false; } // states can't set states on this if (variable.IsState(container) && string.IsNullOrEmpty(variable.SourceObject)) { toReturn = false; } if (toReturn && mVariableNamesToSkipForStates.Contains(variableName)) { toReturn = false; } bool hasSourceObject = !string.IsNullOrEmpty(variable.SourceObject); if (toReturn && hasSourceObject) { InstanceSave instanceSave = container.GetInstance(variable.SourceObject); if (instanceSave == null) { toReturn = false; } else { var baseElement = Gum.Managers.ObjectFinder.Self.GetElementSave(instanceSave.BaseType); if (baseElement == null) { toReturn = false; } if (toReturn) { // Gum (just like Glue) keeps variables that aren't needed around. This allows users to rename things and not lose // important information accidentally. But because of that we have to make sure that the variable we're working with is // valid for the type of object we're dealing with. var defaultState = baseElement.DefaultState; RecursiveVariableFinder rvf = new RecursiveVariableFinder(defaultState); var foundVariable = rvf.GetVariable(variable.GetRootName()); if (foundVariable == null) { // This doesn't exist anywhere in the inheritance chain, so we don't want to generate it: toReturn = false; } } } } if (toReturn && !hasSourceObject) { // If a variable is part of a component, it better be defined in the base type or else we won't generate it. // For example, consider a component that used to inherit from Text. It will have variables for fonts. If that // component switches to inheriting from Sprite, those variables will still exist in the XML for that component, // but we shouldn't generate any state variables for those variables. So we'll go to the base type and see if those // variables exist bool isComponent = container is ComponentSave; var rootComponent = Gum.Managers.ObjectFinder.Self.GetRootStandardElementSave(container); // If the Container is a Screen, then rootComponent will be null, so we don't need to do anything if (rootComponent == null) { toReturn = false; } else { IEnumerable <VariableSave> variablesToCheck; if (isComponent) { var component = Gum.Managers.ObjectFinder.Self.GetStandardElement("Component"); variablesToCheck = rootComponent.DefaultState.Variables.Concat(component.DefaultState.Variables); } else { var defaultState = rootComponent.DefaultState; variablesToCheck = defaultState.Variables; } bool wasMatchFound = variablesToCheck.Any(item => item.Name == variable.GetRootName()); toReturn = wasMatchFound; } } return(toReturn); }