diff --git a/CommonFormsAndControls/CommonFormsAndControls.csproj b/CommonFormsAndControls/CommonFormsAndControls.csproj index 008118e0d..c9a7c9035 100644 --- a/CommonFormsAndControls/CommonFormsAndControls.csproj +++ b/CommonFormsAndControls/CommonFormsAndControls.csproj @@ -55,6 +55,12 @@ + + Form + + + TextInputWithCheckboxWindow.cs + Form @@ -82,6 +88,9 @@ TextInputWindow.cs Designer + + TextInputWithCheckboxWindow.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Gum/Commands/EditCommands.cs b/Gum/Commands/EditCommands.cs index aebc729cc..615eb3814 100644 --- a/Gum/Commands/EditCommands.cs +++ b/Gum/Commands/EditCommands.cs @@ -10,6 +10,7 @@ using StateAnimationPlugin.Views; using System.Collections.Generic; using System.Linq; +using System.Windows.Controls; using System.Windows.Forms; using ToolsUtilities; @@ -465,6 +466,82 @@ public void DuplicateSelectedElement() } + public void ShowCreateComponentFromInstancesDialog() + { + var element = SelectedState.Self.SelectedElement; + var instances = SelectedState.Self.SelectedInstances.ToList(); + if (instances == null || instances.Count == 0 || element == null) + { + MessageBox.Show("You must first save the project before adding a new component"); + } + else if (instances is List) + { + TextInputWindow tiwcw = new TextInputWindow(); + tiwcw.Message = "Enter new Component name:"; + + FilePath filePath = element.Name; + var nameWithoutPath = filePath.FileNameNoPath; + + tiwcw.Result = $"{nameWithoutPath}Component"; + //tiwcw.Option = $"Replace {nameWithoutPath} and all children with an instance of the new component"; + + if (tiwcw.ShowDialog() == DialogResult.OK) + { + string name = tiwcw.Result; + //bool replace = tiwcw.Checked + + string whyNotValid; + NameVerifier.Self.IsComponentNameValid(tiwcw.Result, "", null, out whyNotValid); + + if (string.IsNullOrEmpty(whyNotValid)) + { + ComponentSave componentSave = new ComponentSave(); + componentSave.BaseType = "Container"; + string folder = null; + if (!string.IsNullOrEmpty(folder)) + { + folder += "/"; + } + componentSave.Name = folder + name; + + StateSave defaultState; + + // Clone instances + foreach (var instance in instances) + { + var instanceSave = instance.Clone(); + instanceSave.BaseType = instance.BaseType; + instanceSave.ParentContainer = componentSave; + + componentSave.Instances.Add(instanceSave); + } + + // Clone states + foreach (var state in element.States) + { + if (element.DefaultState == state) + { + defaultState = state.Clone(); + + componentSave.Initialize(defaultState); + continue; + } + componentSave.States.Add(state.Clone()); + } + + StandardElementsManagerGumTool.Self.FixCustomTypeConverters(componentSave); + ProjectCommands.Self.AddComponent(componentSave); + + } + else + { + MessageBox.Show($"Invalid name for new component: {whyNotValid}"); + ShowCreateComponentFromInstancesDialog(); + } + } + } + } + public void DisplayReferencesTo(ElementSave element) { diff --git a/Gum/Plugins/InternalPlugins/TreeView/ElementTreeViewManager.RightClick.cs b/Gum/Plugins/InternalPlugins/TreeView/ElementTreeViewManager.RightClick.cs index 839577280..f94dd6d55 100644 --- a/Gum/Plugins/InternalPlugins/TreeView/ElementTreeViewManager.RightClick.cs +++ b/Gum/Plugins/InternalPlugins/TreeView/ElementTreeViewManager.RightClick.cs @@ -32,6 +32,7 @@ public partial class ElementTreeViewManager ToolStripMenuItem mAddParentInstance; ToolStripMenuItem mSaveObject; ToolStripMenuItem mGoToDefinition; + ToolStripMenuItem mCreateComponent; ToolStripMenuItem mDeleteObject; ToolStripMenuItem mAddFolder; @@ -78,6 +79,10 @@ private void InitializeMenuItems() mGoToDefinition.Text = "Go to definition"; mGoToDefinition.Click += OnGoToDefinitionClick; + mCreateComponent = new ToolStripMenuItem(); + mCreateComponent.Text = "Create Component"; + mCreateComponent.Click += CreateComponentClick; + mAddFolder = new ToolStripMenuItem(); mAddFolder.Text = "Add Folder"; mAddFolder.Click += AddFolderClick; @@ -117,6 +122,15 @@ void OnGoToDefinitionClick(object sender, EventArgs e) } } + void CreateComponentClick(object sender, EventArgs e) + { + if (SelectedState.Self.SelectedScreen != null || + SelectedState.Self.SelectedComponent != null) + { + GumCommands.Self.Edit.ShowCreateComponentFromInstancesDialog(); + } + } + void ForceSaveObjectClick(object sender, EventArgs e) { GumCommands.Self.FileCommands.ForceSaveElement(SelectedState.Self.SelectedElement); @@ -280,6 +294,10 @@ public void PopulateMenuStrip() mMenuStrip.Items.Add("-"); + mMenuStrip.Items.Add(mCreateComponent); + + mMenuStrip.Items.Add("-"); + var deleteText = SelectedState.Self.SelectedInstances.Count() > 1 ? $"Delete {SelectedState.Self.SelectedInstances.Count()} instances" : $"Delete {SelectedState.Self.SelectedInstance.Name}"; diff --git a/Gum/ToolCommands/ElementCommands.cs b/Gum/ToolCommands/ElementCommands.cs index d681b49b8..db40d3345 100644 --- a/Gum/ToolCommands/ElementCommands.cs +++ b/Gum/ToolCommands/ElementCommands.cs @@ -39,25 +39,31 @@ public static ElementCommands Self public InstanceSave AddInstance(ElementSave elementToAddTo, string name, string type = null, string parentName = null) { - if (elementToAddTo == null) - { - throw new Exception("Could not add instance named " + name + " because no element is selected"); - } - - InstanceSave instanceSave = new InstanceSave(); instanceSave.Name = name; instanceSave.ParentContainer = elementToAddTo; instanceSave.BaseType = type ?? StandardElementsManager.Self.DefaultType; elementToAddTo.Instances.Add(instanceSave); + return AddInstance(elementToAddTo, instanceSave, parentName); + } + + public InstanceSave AddInstance(ElementSave elementToAddTo, InstanceSave instanceSave, string parentName = null) + { + if (elementToAddTo == null) + { + throw new Exception("Could not add instance named " + instanceSave.Name + " because no element is selected"); + } + + elementToAddTo.Instances.Add(instanceSave); + GumCommands.Self.GuiCommands.RefreshElementTreeView(elementToAddTo); Wireframe.WireframeObjectManager.Self.RefreshAll(true); //SelectedState.Self.SelectedInstance = instanceSave; // Set the parent before adding the instance in case plugins want to reject the creation of the object... - if(!string.IsNullOrEmpty(parentName)) + if (!string.IsNullOrEmpty(parentName)) { elementToAddTo.DefaultState.SetValue($"{instanceSave.Name}.Parent", parentName, "string"); } @@ -66,7 +72,7 @@ public InstanceSave AddInstance(ElementSave elementToAddTo, string name, string PluginManager.Self.InstanceAdd(elementToAddTo, instanceSave); // a plugin may have removed this instance. If so, we need to refresh the tree node again: - if(elementToAddTo.Instances.Contains(instanceSave) == false) + if (elementToAddTo.Instances.Contains(instanceSave) == false) { GumCommands.Self.GuiCommands.RefreshElementTreeView(elementToAddTo); Wireframe.WireframeObjectManager.Self.RefreshAll(true); diff --git a/Gum/ToolCommands/ProjectCommands.cs b/Gum/ToolCommands/ProjectCommands.cs index b2f3cedf9..eea18135c 100644 --- a/Gum/ToolCommands/ProjectCommands.cs +++ b/Gum/ToolCommands/ProjectCommands.cs @@ -6,6 +6,9 @@ using ToolsUtilities; using CommonFormsAndControls; using System.Windows.Forms; +using System.Windows.Controls; +using Gum.DataTypes.Variables; +using Gum.Plugins; namespace Gum.ToolCommands { diff --git a/Gum/Wireframe/WireframeObjectManager.RepresentationCreation.cs b/Gum/Wireframe/WireframeObjectManager.RepresentationCreation.cs index 0cca32ce1..5a7a673c0 100644 --- a/Gum/Wireframe/WireframeObjectManager.RepresentationCreation.cs +++ b/Gum/Wireframe/WireframeObjectManager.RepresentationCreation.cs @@ -50,203 +50,6 @@ public partial class WireframeObjectManager #endregion - private GraphicalUiElement CreateIpsoForElement(ElementSave elementSave) - { - GraphicalUiElement rootGue = null; - bool isScreen = elementSave is ScreenSave; - rootGue = new GraphicalUiElement(); - - - // If we don't turn off layouts, layouts can be called tens of thousands of times for - // complex elements. We'll turn off layouts, then turn them back on at the end and manually - // layout: - GraphicalUiElement.IsAllLayoutSuspended = true; - { - - rootGue.Tag = elementSave; - AllIpsos.Add(rootGue); - - rootGue.ElementSave = elementSave; - if (isScreen == false) - { - // We used to not add the IPSO for the root element to the list of graphical elements - // and this prevented selection. I'm not sure if this was intentionally left out or not - // but I think it should be here - // July 18, 2022 - // This is a duplicate - // add. It's also added - // above. Which should it - // be? - //AllIpsos.Add(rootIpso); - - rootGue.CreateGraphicalComponent(elementSave, null); - - // can be null if the element save references a bad file - if(rootGue.Component != null) - { - rootGue.Name = elementSave.Name; - rootGue.Component.Tag = elementSave; - - } - - RecursiveVariableFinder rvf = new DataTypes.RecursiveVariableFinder(SelectedState.Self.SelectedStateSaveOrDefault); - - string guide = rvf.GetValue("Guide"); - SetGuideParent(null, rootGue, guide); - } - - var exposedVariables = elementSave - .DefaultState - .Variables - .Where(item => - !string.IsNullOrEmpty(item.ExposedAsName) - // October 18, 2022 - // Why do we only consider variables that - // have no SourceObject? This should consider - // all exposed variables: - //&& string.IsNullOrEmpty(item.SourceObject) - ) - .ToArray(); - - foreach (var exposedVariable in exposedVariables) - { - rootGue.AddExposedVariable(exposedVariable.ExposedAsName, exposedVariable.Name); - } - - List newlyAdded = new List(); - List elementStack = new List(); - - ElementWithState elementWithState = new ElementWithState(elementSave); - if (elementSave == SelectedState.Self.SelectedElement) - { - if (SelectedState.Self.SelectedStateSave != null) - { - elementWithState.StateName = SelectedState.Self.SelectedStateSave.Name; - } - else - { - elementWithState.StateName = "Default"; - } - } - - var baseElements = ObjectFinder.Self.GetBaseElements(elementSave); - baseElements.Reverse(); - foreach (var baseElement in baseElements) - { - elementStack.Add(baseElement); - } - elementStack.Add(elementWithState); - - // parallel screws up the ordering of objects, so we'll do it on the primary thread for now - // and parallelize it later: - //Parallel.ForEach(elementSave.Instances, instance => - foreach (var instance in elementSave.Instances) - { - GraphicalUiElement child = CreateRepresentationForInstance(instance, null, elementStack, rootGue); - - if (child == null) - { - // This can occur - // if an instance references - // a component that doesn't exist. - // I don't think we need to do anything - // here. - } - else - { - newlyAdded.Add(child); - AllIpsos.Add(child); - } - } - - SetUpParentRelationship(newlyAdded, elementStack); - - elementStack.Remove(elementStack.FirstOrDefault(item => item.Element == elementSave)); - - rootGue.SetStatesAndCategoriesRecursively(elementSave); - - // First we need to the default state (and do so recursively) - try - { - rootGue.SetVariablesRecursively(elementSave, elementSave.DefaultState); - } - catch(Exception e) - { - // this barfed, but we don't want to crash the tool - GumCommands.Self.GuiCommands.PrintOutput($"Error loading {elementSave}:\n{e.ToString()}"); - } - // then we override it with the current state if one is set: - var appliedRecursively = false; - var state = SelectedState.Self.SelectedStateSave; - if (SelectedState.Self.SelectedStateSave != elementSave.DefaultState && SelectedState.Self.SelectedStateSave != null) - { - bool isRecursive = GetIfSelectedStateIsSetRecursively(); - if (isRecursive) - { - var category = SelectedState.Self.SelectedStateCategorySave; - rootGue.ApplyStateRecursive(category.Name, SelectedState.Self.SelectedStateSave.Name); - } - if(!appliedRecursively) - { - rootGue.ApplyState(state); - } - } - - var rootElementSave = ObjectFinder.Self.GetRootStandardElementSave(elementSave); - if(rootElementSave?.Name == "Text" && SelectedState.Self.SelectedStateSave != null) - { - // We created a new Text object, so let's try generating fonts for it: - FontManager.Self.ReactToFontValueSet(null); - - } - - // I think this has to be *after* we set varaibles because that's where clipping gets set - if (rootGue != null) - { - gueManager.Add(rootGue); - - rootGue.AddToManagers(SystemManagers.Default, null); - - } - } - GraphicalUiElement.IsAllLayoutSuspended = false; - HashSet hashSet = new HashSet(); - var tempSorted = AllIpsos.OrderBy(item => - { - hashSet.Clear(); - return GetDepth(item, hashSet); - }).ToArray(); - - foreach(var item in AllIpsos) - { - hashSet.Clear(); - var isRecursive = IsRecursive(item, hashSet); - - if(isRecursive) - { - var message = $"Recursion found in {elementSave}:"; - - foreach(var recursiveItem in hashSet) - { - message += $"\n {recursiveItem} child of..."; - } - - message += $"\n back to {item}"; - - - GumCommands.Self.GuiCommands.ShowMessage(message); - } - } - - AllIpsos.Clear(); - AllIpsos.AddRange(tempSorted); - - rootGue.UpdateFontRecursive(); - rootGue.UpdateLayout(); - - return rootGue; - } - private static bool GetIfSelectedStateIsSetRecursively() { var category = SelectedState.Self.SelectedStateCategorySave; diff --git a/Gum/Wireframe/WireframeObjectManager.cs b/Gum/Wireframe/WireframeObjectManager.cs index 4fdcb280a..38d723c76 100644 --- a/Gum/Wireframe/WireframeObjectManager.cs +++ b/Gum/Wireframe/WireframeObjectManager.cs @@ -244,6 +244,12 @@ private void RefreshAll(bool forceLayout, bool forceReloadTextures, ElementSave ClearAll(); RootGue = null; } + else if(elementSave is ComponentSave && string.IsNullOrEmpty(elementSave.BaseType)) + { + ClearAll(); + RootGue = null; + GumCommands.Self.GuiCommands.PrintOutput($"Error - cannot create representation for Component {elementSave.Name} because its BaseType is not set."); + } else if (forceLayout || forceReloadTextures) { ObjectFinder.Self.EnableCache(); @@ -258,50 +264,43 @@ private void RefreshAll(bool forceLayout, bool forceReloadTextures, ElementSave LoaderManager.Self.CacheTextures = true; - var useNew = true; - if(useNew) - { - GraphicalUiElement.IsAllLayoutSuspended = true; - RootGue = elementSave.ToGraphicalUiElement(SystemManagers.Default, addToManagers: true); - // Always set default first, then if the selected state is not the default, then apply that after: - RootGue.SetVariablesRecursively(elementSave, elementSave.DefaultState); - var selectedState = GumState.Self.SelectedState.SelectedStateSave; - if(selectedState != null && selectedState != elementSave.DefaultState) - { - RootGue.ApplyState(selectedState); - } + GraphicalUiElement.IsAllLayoutSuspended = true; + + RootGue = elementSave.ToGraphicalUiElement(SystemManagers.Default, addToManagers: true); + // Always set default first, then if the selected state is not the default, then apply that after: + RootGue.SetVariablesRecursively(elementSave, elementSave.DefaultState); + var selectedState = GumState.Self.SelectedState.SelectedStateSave; + if(selectedState != null && selectedState != elementSave.DefaultState) + { + RootGue.ApplyState(selectedState); + } - AddAllIpsos(RootGue); - HashSet hashSet = new HashSet(); - var tempSorted = AllIpsos.OrderBy(item => - { - hashSet.Clear(); - return GetDepth(item, hashSet); - }).ToArray(); + AddAllIpsos(RootGue); + HashSet hashSet = new HashSet(); + var tempSorted = AllIpsos.OrderBy(item => + { + hashSet.Clear(); + return GetDepth(item, hashSet); + }).ToArray(); - AllIpsos.Clear(); - AllIpsos.AddRange(tempSorted); + AllIpsos.Clear(); + AllIpsos.AddRange(tempSorted); - UpdateTextOutlines(RootGue); + UpdateTextOutlines(RootGue); - GraphicalUiElement.IsAllLayoutSuspended = false; + GraphicalUiElement.IsAllLayoutSuspended = false; - RootGue.UpdateFontRecursive(); - RootGue.UpdateLayout(); + RootGue.UpdateFontRecursive(); + RootGue.UpdateLayout(); - gueManager.Add(RootGue); - // what about fonts? - // We recreate missing fonts on startup, so do we need to bother here? - // I'm not sure, but if we do we would call: - //FontManager.Self.CreateAllMissingFontFiles(ObjectFinder.Self.GumProjectSave); - } - else - { - RootGue = CreateIpsoForElement(elementSave); + gueManager.Add(RootGue); + // what about fonts? + // We recreate missing fonts on startup, so do we need to bother here? + // I'm not sure, but if we do we would call: + //FontManager.Self.CreateAllMissingFontFiles(ObjectFinder.Self.GumProjectSave); - } if(LocalizationManager.HasDatabase) { diff --git a/GumRuntime/GraphicalUiElement.cs b/GumRuntime/GraphicalUiElement.cs index 3333c5eda..932e94b9c 100644 --- a/GumRuntime/GraphicalUiElement.cs +++ b/GumRuntime/GraphicalUiElement.cs @@ -2084,9 +2084,9 @@ bool GetIfShouldCallUpdateOnParent() } } } - else if (this.Parent is GraphicalUiElement) + else if (this.Parent is GraphicalUiElement parentGue && parentGue.Children != null) { - var siblingsAsIpsos = ((GraphicalUiElement)Parent).Children; + var siblingsAsIpsos = parentGue.Children; for (int i = 0; i < siblingsAsIpsos.Count; i++) { var siblingAsGraphicalUiElement = siblingsAsIpsos[i] as GraphicalUiElement;