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;