void DrawListKeyValuePair <K, V>(List <KeyValuePair <K, V> > pairs, ref bool state, string label, Action <K> DrawItem) { #if UNITY_EDITOR SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); { SirenixEditorGUI.BeginHorizontalToolbar(); { state = SirenixEditorGUI.Foldout(state, label); GUILayout.FlexibleSpace(); } SirenixEditorGUI.EndHorizontalToolbar(); if (state) { SirenixEditorGUI.BeginVerticalList(false); foreach (var item in pairs) { SirenixEditorGUI.BeginListItem(false); EditorGUILayout.BeginHorizontal(); DrawItem(item.Key); EditorGUILayout.EndHorizontal(); SirenixEditorGUI.EndListItem(); } SirenixEditorGUI.EndVerticalList(); } } SirenixEditorGUI.EndIndentedVertical(); #endif }
/// <summary> /// Draws the property layout. /// </summary> protected override void DrawPropertyLayout(GUIContent label) { if (this.drawAsList) { if (GUILayout.Button("Draw as table")) { this.drawAsList = false; } this.CallNextDrawer(label); return; } this.picker = ObjectPicker.GetObjectPicker(this, this.resolver.ElementType); this.paging.Update(this.resolver.MaxCollectionLength); this.currPage.Value = this.paging.CurrentPage; this.isPagingExpanded.Value = this.paging.IsExpanded; var rect = SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); { if (!this.Attribute.HideToolbar) { this.DrawToolbar(label); } if (this.Attribute.AlwaysExpanded) { this.DrawColumnHeaders(); this.DrawTable(); } else { if (SirenixEditorGUI.BeginFadeGroup(this, this.isVisible.Value) && this.Property.Children.Count > 0) { this.DrawColumnHeaders(); this.DrawTable(); } SirenixEditorGUI.EndFadeGroup(); } } SirenixEditorGUI.EndIndentedVertical(); if (Event.current.type == EventType.Repaint) { rect.yMin -= 1; rect.height -= 3; SirenixEditorGUI.DrawBorders(rect, 1); } this.DropZone(rect); this.HandleObjectPickerEvents(); if (Event.current.type == EventType.Repaint) { this.isFirstFrame = false; } }
/// <summary> /// Draws the property. /// </summary> protected override void DrawPropertyLayout(GUIContent label) { var property = this.Property; var attribute = this.Attribute; if (attribute.HideTabGroupIfTabGroupOnlyHasOneTab && this.tabs.Count <= 1) { for (int i = 0; i < this.tabs.Count; i++) { int pageCount = this.tabs[i].InspectorProperties.Count; for (int j = 0; j < pageCount; j++) { var child = this.tabs[i].InspectorProperties[j]; child.Update(); child.Draw(child.Label); } } return; } this.tabGroup.AnimationSpeed = 1 / SirenixEditorGUI.TabPageSlideAnimationDuration; this.tabGroup.FixedHeight = attribute.UseFixedHeight; if (this.currentPage.Value >= this.tabs.Count || this.currentPage.Value < 0) { this.currentPage.Value = 0; } SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); tabGroup.BeginGroup(true, attribute.Paddingless ? GUIStyle.none : null); for (int i = 0; i < this.tabs.Count; i++) { var page = tabGroup.RegisterTab(this.tabs[i].TabName); page.Title = this.tabs[i].Title.GetString(property); if (page.BeginPage()) { this.currentPage.Value = i; int pageCount = this.tabs[i].InspectorProperties.Count; for (int j = 0; j < pageCount; j++) { var child = this.tabs[i].InspectorProperties[j]; child.Update(); // Since the property is not fetched through the property system, ensure it's updated before drawing it. child.Draw(child.Label); } } page.EndPage(); } tabGroup.EndGroup(); SirenixEditorGUI.EndIndentedVertical(); }
/// <summary> /// Not yet documented. /// </summary> protected override void DrawPropertyLayout(GUIContent label) { var entry = this.ValueEntry; var attribute = this.Attribute; var config = entry.Property.Context.Get(this, "Test", (CurrentContext)null); if (config.Value == null) { config.Value = new CurrentContext(); config.Value.Attribute = attribute; config.Value.Tags = attribute.Tags != null?attribute.Tags.Trim().Split(',').Select(i => i.Trim()).ToArray() : null; config.Value.LayerNames = attribute.LayerNames != null?attribute.LayerNames.Trim().Split(',').Select(i => i.Trim()).ToArray() : null; config.Value.Property = entry.Property; if (attribute.Path != null) { var path = attribute.Path.Trim('/', ' '); path = "Assets/" + path + "/"; path = Application.dataPath + "/" + path; config.Value.AssetsFolderLocation = new DirectoryInfo(path); path = attribute.Path.TrimStart('/').TrimEnd('/'); config.Value.PrettyPath = "/" + path.TrimStart('/'); } if (attribute.CustomFilterMethod != null) { MethodInfo methodInfo; string error; if (MemberFinder.Start(entry.ParentType) .IsMethod() .IsNamed(attribute.CustomFilterMethod) .HasReturnType <bool>() .HasParameters <TElement>() .TryGetMember <MethodInfo>(out methodInfo, out error)) { if (methodInfo.IsStatic) { config.Value.StaticCustomIncludeMethod = (Func <TElement, bool>)Delegate.CreateDelegate(typeof(Func <TElement, bool>), methodInfo, true); } else { config.Value.InstanceCustomIncludeMethod = EmitUtilities.CreateWeakInstanceMethodCaller <bool, TElement>(methodInfo); } } config.Value.ErrorMessage = error; } if (config.Value.ErrorMessage != null) { // We can get away with lag on load. config.Value.MaxSearchDurationPrFrameInMS = 20; config.Value.EnsureListPopulation(); config.Value.MaxSearchDurationPrFrameInMS = 1; } } var currentValue = (UnityEngine.Object)entry.WeakSmartValue; if (config.Value.ErrorMessage != null) { SirenixEditorGUI.ErrorMessageBox(config.Value.ErrorMessage); } else { config.Value.EnsureListPopulation(); } SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); { SirenixEditorGUI.BeginHorizontalToolbar(); if (label != null) { GUILayout.Label(label); } GUILayout.FlexibleSpace(); if (config.Value.PrettyPath != null) { GUILayout.Label(config.Value.PrettyPath, SirenixGUIStyles.RightAlignedGreyMiniLabel); SirenixEditorGUI.VerticalLineSeparator(); } if (config.Value.IsPopulated) { GUILayout.Label(config.Value.AvailableAsset.Count + " items", SirenixGUIStyles.RightAlignedGreyMiniLabel); GUIHelper.PushGUIEnabled(GUI.enabled && (config.Value.AvailableAsset.Count > 0 && config.Value.ErrorMessage == null)); } else { GUILayout.Label("Scanning " + config.Value.CurrentSearchingIndex + " / " + config.Value.NumberOfResultsToSearch, SirenixGUIStyles.RightAlignedGreyMiniLabel); GUIHelper.PushGUIEnabled(false); } SirenixEditorGUI.VerticalLineSeparator(); bool drawConflict = entry.Property.ParentValues.Count > 1; if (drawConflict == false) { var index = config.Value.AvailableAsset.IndexOf(currentValue) + 1; if (index > 0) { GUILayout.Label(index.ToString(), SirenixGUIStyles.RightAlignedGreyMiniLabel); } else { drawConflict = true; } } if (drawConflict) { GUILayout.Label("-", SirenixGUIStyles.RightAlignedGreyMiniLabel); } if (SirenixEditorGUI.ToolbarButton(EditorIcons.TriangleLeft) && config.Value.IsPopulated) { var index = config.Value.AvailableAsset.IndexOf(currentValue) - 1; index = index < 0 ? config.Value.AvailableAsset.Count - 1 : index; entry.WeakSmartValue = config.Value.AvailableAsset[index]; } if (SirenixEditorGUI.ToolbarButton(EditorIcons.TriangleDown) && config.Value.IsPopulated) { GenericMenu m = new GenericMenu(); var selected = currentValue; int itemsPrPage = 40; bool showPages = config.Value.AvailableAsset.Count > 50; string page = ""; int selectedPage = (config.Value.AvailableAsset.IndexOf(entry.WeakSmartValue as UnityEngine.Object) / itemsPrPage); for (int i = 0; i < config.Value.AvailableAsset.Count; i++) { var obj = config.Value.AvailableAsset[i]; if (obj != null) { var path = AssetDatabase.GetAssetPath(obj); var name = string.IsNullOrEmpty(path) ? obj.name : path.Substring(7).Replace("/", "\\"); var localEntry = entry; if (showPages) { var p = (i / itemsPrPage); page = (p * itemsPrPage) + " - " + Mathf.Min(((p + 1) * itemsPrPage), config.Value.AvailableAsset.Count - 1); if (selectedPage == p) { page += " (contains selected)"; } page += "/"; } m.AddItem(new GUIContent(page + name), obj == selected, () => { localEntry.Property.Tree.DelayActionUntilRepaint(() => localEntry.WeakSmartValue = obj); }); } } m.ShowAsContext(); } if (SirenixEditorGUI.ToolbarButton(EditorIcons.TriangleRight) && config.Value.IsPopulated) { var index = config.Value.AvailableAsset.IndexOf(currentValue) + 1; entry.WeakSmartValue = config.Value.AvailableAsset[index % config.Value.AvailableAsset.Count]; } GUIHelper.PopGUIEnabled(); SirenixEditorGUI.EndHorizontalToolbar(); SirenixEditorGUI.BeginVerticalList(); SirenixEditorGUI.BeginListItem(false, padding); this.CallNextDrawer(null); SirenixEditorGUI.EndListItem(); SirenixEditorGUI.EndVerticalList(); } SirenixEditorGUI.EndIndentedVertical(); }
/// <summary> /// Draws the property. /// </summary> protected override void DrawPropertyLayout(GUIContent label) { var resolver = this.Property.ChildResolver as ICollectionResolver; bool isReadOnly = resolver.IsReadOnly; if (this.errorMessage != null) { SirenixEditorGUI.ErrorMessageBox(errorMessage); } if (info.Label == null || (label != null && label.text != info.Label.text)) { info.Label = new GUIContent(label == null || string.IsNullOrEmpty(label.text) ? Property.ValueEntry.TypeOfValue.GetNiceName() : label.text, label == null ? string.Empty : label.tooltip); } info.IsReadOnly = resolver.IsReadOnly; info.ListItemStyle.padding.left = info.Draggable ? 25 : 7; info.ListItemStyle.padding.right = info.IsReadOnly || info.HideRemoveButton ? 4 : 20; if (Event.current.type == EventType.Repaint) { info.DropZoneTopLeft = GUIUtility.GUIToScreenPoint(new Vector2(0, 0)); } info.CollectionResolver = Property.ChildResolver as ICollectionResolver; info.OrderedCollectionResolver = Property.ChildResolver as IOrderedCollectionResolver; info.Count = Property.Children.Count; info.IsEmpty = Property.Children.Count == 0; SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); this.BeginDropZone(); { this.DrawToolbar(); if (SirenixEditorGUI.BeginFadeGroup(UniqueDrawerKey.Create(Property, this), info.Toggled.Value)) { GUIHelper.PushLabelWidth(GUIHelper.BetterLabelWidth - info.ListItemStyle.padding.left); this.DrawItems(); GUIHelper.PopLabelWidth(); } SirenixEditorGUI.EndFadeGroup(); } this.EndDropZone(); SirenixEditorGUI.EndIndentedVertical(); if (info.OrderedCollectionResolver != null) { if (info.RemoveAt >= 0 && Event.current.type == EventType.Repaint) { try { if (info.CustomRemoveIndexFunction != null) { foreach (var parent in this.Property.ParentValues) { info.CustomRemoveIndexFunction( parent, info.RemoveAt); } } else if (info.CustomRemoveElementFunction != null) { for (int i = 0; i < this.Property.ParentValues.Count; i++) { info.CustomRemoveElementFunction( this.Property.ParentValues[i], this.Property.Children[info.RemoveAt].ValueEntry.WeakValues[i]); } } else { info.OrderedCollectionResolver.QueueRemoveAt(info.RemoveAt); } } finally { info.RemoveAt = -1; } GUIHelper.RequestRepaint(); } } else if (info.RemoveValues != null && Event.current.type == EventType.Repaint) { try { if (info.CustomRemoveElementFunction != null) { for (int i = 0; i < this.Property.ParentValues.Count; i++) { info.CustomRemoveElementFunction( this.Property.ParentValues[i], this.info.RemoveValues[i]); } } else { info.CollectionResolver.QueueRemove(info.RemoveValues); } } finally { info.RemoveValues = null; } GUIHelper.RequestRepaint(); } if (info.ObjectPicker != null && info.ObjectPicker.IsReadyToClaim && Event.current.type == EventType.Repaint) { var value = info.ObjectPicker.ClaimObject(); if (info.JumpToNextPageOnAdd) { info.StartIndex = int.MaxValue; } object[] values = new object[info.Property.Tree.WeakTargets.Count]; values[0] = value; for (int j = 1; j < values.Length; j++) { values[j] = SerializationUtility.CreateCopy(value); } info.CollectionResolver.QueueAdd(values); } }
/// <summary> /// Draws the property. /// </summary> protected override void DrawPropertyLayout(IPropertyValueEntry <TList> entry, GUIContent label) { var property = entry.Property; var infoContext = property.Context.Get(this, "Context", (ListDrawerConfigInfo)null); var info = infoContext.Value; bool isReadOnly = false; if (entry.TypeOfValue.IsArray == false) { for (int i = 0; i < entry.ValueCount; i++) { if ((entry.WeakValues[i] as IList <TElement>).IsReadOnly) { isReadOnly = true; break; } } } if (info == null) { var customListDrawerOptions = property.Info.GetAttribute <ListDrawerSettingsAttribute>() ?? new ListDrawerSettingsAttribute(); isReadOnly = entry.IsEditable == false || isReadOnly || customListDrawerOptions.IsReadOnlyHasValue && customListDrawerOptions.IsReadOnly; info = infoContext.Value = new ListDrawerConfigInfo() { StartIndex = 0, Toggled = entry.Context.GetPersistent <bool>(this, "ListDrawerToggled", customListDrawerOptions.ExpandedHasValue ? customListDrawerOptions.Expanded : GeneralDrawerConfig.Instance.OpenListsByDefault), RemoveAt = -1, label = new GUIContent(label == null || string.IsNullOrEmpty(label.text) ? property.ValueEntry.TypeOfValue.GetNiceName() : label.text, label == null ? string.Empty : label.tooltip), ShowAllWhilePageing = false, EndIndex = 0, CustomListDrawerOptions = customListDrawerOptions, IsReadOnly = isReadOnly, Draggable = !isReadOnly && (!customListDrawerOptions.IsReadOnlyHasValue) }; info.listConfig = GeneralDrawerConfig.Instance; info.property = property; if (customListDrawerOptions.DraggableHasValue && !customListDrawerOptions.DraggableItems) { info.Draggable = false; } if (info.CustomListDrawerOptions.OnBeginListElementGUI != null) { string errorMessage; MemberInfo memberInfo = property.ParentType .FindMember() .IsMethod() .IsNamed(info.CustomListDrawerOptions.OnBeginListElementGUI) .HasParameters <int>() .ReturnsVoid() .GetMember <MethodInfo>(out errorMessage); if (memberInfo == null || errorMessage != null) { Debug.LogError(errorMessage ?? "There should really be an error message here."); } else { info.OnBeginListElementGUI = EmitUtilities.CreateWeakInstanceMethodCaller <int>(memberInfo as MethodInfo); } } if (info.CustomListDrawerOptions.OnEndListElementGUI != null) { string errorMessage; MemberInfo memberInfo = property.ParentType .FindMember() .IsMethod() .IsNamed(info.CustomListDrawerOptions.OnEndListElementGUI) .HasParameters <int>() .ReturnsVoid() .GetMember <MethodInfo>(out errorMessage); if (memberInfo == null || errorMessage != null) { Debug.LogError(errorMessage ?? "There should really be an error message here."); } else { info.OnEndListElementGUI = EmitUtilities.CreateWeakInstanceMethodCaller <int>(memberInfo as MethodInfo); } } if (info.CustomListDrawerOptions.OnTitleBarGUI != null) { string errorMessage; MemberInfo memberInfo = property.ParentType .FindMember() .IsMethod() .IsNamed(info.CustomListDrawerOptions.OnTitleBarGUI) .HasNoParameters() .ReturnsVoid() .GetMember <MethodInfo>(out errorMessage); if (memberInfo == null || errorMessage != null) { Debug.LogError(errorMessage ?? "There should really be an error message here."); } else { info.OnTitleBarGUI = EmitUtilities.CreateWeakInstanceMethodCaller(memberInfo as MethodInfo); } } if (info.CustomListDrawerOptions.ListElementLabelName != null) { string errorMessage; MemberInfo memberInfo = typeof(TElement) .FindMember() .HasNoParameters() .IsNamed(info.CustomListDrawerOptions.ListElementLabelName) .HasReturnType <object>(true) .GetMember(out errorMessage); if (memberInfo == null || errorMessage != null) { Debug.LogError(errorMessage ?? "There should really be an error message here."); } else { string methodSuffix = memberInfo as MethodInfo == null ? "" : "()"; info.GetListElementLabelText = DeepReflection.CreateWeakInstanceValueGetter(typeof(TElement), typeof(object), info.CustomListDrawerOptions.ListElementLabelName + methodSuffix); } } } info.listConfig = GeneralDrawerConfig.Instance; info.property = property; info.ListItemStyle.padding.left = info.Draggable ? 25 : 7; info.ListItemStyle.padding.right = info.IsReadOnly ? 4 : 20; if (Event.current.type == EventType.Repaint) { info.DropZoneTopLeft = GUIUtility.GUIToScreenPoint(new Vector2(0, 0)); } info.ListValueChanger = property.ValueEntry.GetListValueEntryChanger(); info.Count = property.Children.Count; info.IsEmpty = property.Children.Count == 0; SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); this.BeginDropZone(info); { this.DrawToolbar(info); if (SirenixEditorGUI.BeginFadeGroup(UniqueDrawerKey.Create(property, this), info.Toggled.Value)) { GUIHelper.PushLabelWidth(EditorGUIUtility.labelWidth - info.ListItemStyle.padding.left); this.DrawItems(info); GUIHelper.PopLabelWidth(); } SirenixEditorGUI.EndFadeGroup(); } this.EndDropZone(info); SirenixEditorGUI.EndIndentedVertical(); if (info.RemoveAt >= 0 && Event.current.type == EventType.Repaint) { info.ListValueChanger.RemoveListElementAt(info.RemoveAt, CHANGE_ID); info.RemoveAt = -1; GUIHelper.RequestRepaint(); } if (info.ObjectPicker != null && info.ObjectPicker.IsReadyToClaim && Event.current.type == EventType.Repaint) { var value = info.ObjectPicker.ClaimObject(); if (info.JumpToNextPageOnAdd) { info.StartIndex = int.MaxValue; } object[] values = new object[info.ListValueChanger.ValueCount]; values[0] = value; for (int j = 1; j < values.Length; j++) { values[j] = SerializationUtility.CreateCopy(value); } info.ListValueChanger.AddListElement(values, CHANGE_ID); } }
/// <summary> /// Draws the property. /// </summary> protected override void DrawPropertyLayout(GUIContent label) { var property = this.Property; var attribute = this.Attribute; var tabGroup = SirenixEditorGUI.CreateAnimatedTabGroup(property); tabGroup.AnimationSpeed = 1 / SirenixEditorGUI.TabPageSlideAnimationDuration; tabGroup.FixedHeight = attribute.UseFixedHeight; LocalPersistentContext <int> persistentPageContext = property.Context.GetPersistent(this, "CurrentPage", 0); bool setPersistentPage = false; PropertyContext <List <Tab> > tabs; if (property.Context.Get(this, "Tabs", out tabs)) { setPersistentPage = true; tabs.Value = new List <Tab>(); var addLastTabs = new List <Tab>(); for (int j = 0; j < property.Children.Count; j++) { var child = property.Children[j]; bool added = false; if (child.Info.PropertyType == PropertyType.Group) { var attrType = child.GetAttribute <PropertyGroupAttribute>().GetType(); if (attrType.IsNested && attrType.DeclaringType == typeof(TabGroupAttribute)) { // This is a tab subgroup; add all its children to a tab for that subgroup Tab tab = new Tab(); tab.TabName = child.NiceName; tab.Title = new StringMemberHelper(property, child.Name.TrimStart('#')); for (int i = 0; i < child.Children.Count; i++) { tab.InspectorProperties.Add(child.Children[i]); } tabs.Value.Add(tab); added = true; } } if (!added) { // This is a group member of the tab group itself, so it gets its own tab Tab tab = new Tab(); tab.TabName = child.NiceName; tab.Title = new StringMemberHelper(property, child.Name.TrimStart('#')); tab.InspectorProperties.Add(child); addLastTabs.Add(tab); } } foreach (var tab in addLastTabs) { tabs.Value.Add(tab); } } for (int i = 0; i < tabs.Value.Count; i++) { tabGroup.RegisterTab(tabs.Value[i].TabName); } if (setPersistentPage) { if (persistentPageContext.Value >= tabs.Value.Count || persistentPageContext.Value < 0) { persistentPageContext.Value = 0; } tabGroup.SetCurrentPage(tabGroup.RegisterTab(tabs.Value[persistentPageContext.Value].TabName)); } SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); tabGroup.BeginGroup(true, attribute.Paddingless ? GUIStyle.none : null); for (int i = 0; i < tabs.Value.Count; i++) { var page = tabGroup.RegisterTab(tabs.Value[i].TabName); page.Title = tabs.Value[i].Title.GetString(property); if (page.BeginPage()) { persistentPageContext.Value = i; int pageCount = tabs.Value[i].InspectorProperties.Count; for (int j = 0; j < pageCount; j++) { var child = tabs.Value[i].InspectorProperties[j]; child.Update(); // Since the property is not fetched through the property system, ensure it's updated before drawing it. child.Draw(child.Label); } } page.EndPage(); } tabGroup.EndGroup(); SirenixEditorGUI.EndIndentedVertical(); }
/// <summary> /// Draws the property. /// </summary> protected override void DrawPropertyLayout(GUIContent label) { var entry = this.ValueEntry; this.dictionaryResolver = entry.Property.ChildResolver as StrongDictionaryPropertyResolver <TDictionary, TKey, TValue>; this.config = GeneralDrawerConfig.Instance; this.paging.NumberOfItemsPerPage = this.config.NumberOfItemsPrPage; listItemStyle.padding.right = !entry.IsEditable || this.attrSettings.IsReadOnly ? 4 : 20; SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); { this.paging.Update(elementCount: entry.Property.Children.Count); this.DrawToolbar(entry); this.paging.Update(elementCount: entry.Property.Children.Count); if (!this.disableAddKey && this.attrSettings.IsReadOnly == false) { this.DrawAddKey(entry); } float t; GUIHelper.BeginLayoutMeasuring(); if (SirenixEditorGUI.BeginFadeGroup(UniqueDrawerKey.Create(entry.Property, this), this.toggled.Value, out t)) { var rect = SirenixEditorGUI.BeginVerticalList(false); if (this.attrSettings.DisplayMode == DictionaryDisplayOptions.OneLine) { var maxWidth = rect.width - 90; rect.xMin = this.keyWidthOffset + 22; rect.xMax = rect.xMin + 10; this.keyWidthOffset = this.keyWidthOffset + SirenixEditorGUI.SlideRect(rect).x; if (Event.current.type == EventType.Repaint) { this.keyWidthOffset = Mathf.Clamp(this.keyWidthOffset, 90, maxWidth); } if (this.paging.ElementCount != 0) { var headerRect = SirenixEditorGUI.BeginListItem(false); { GUILayout.Space(14); if (Event.current.type == EventType.Repaint) { GUI.Label(headerRect.SetWidth(this.keyWidthOffset), this.keyLabel, SirenixGUIStyles.LabelCentered); GUI.Label(headerRect.AddXMin(this.keyWidthOffset), this.valueLabel, SirenixGUIStyles.LabelCentered); SirenixEditorGUI.DrawSolidRect(headerRect.AlignBottom(1), SirenixGUIStyles.BorderColor); } } SirenixEditorGUI.EndListItem(); } } GUIHelper.PushHierarchyMode(false); this.DrawElements(entry, label); GUIHelper.PopHierarchyMode(); SirenixEditorGUI.EndVerticalList(); } SirenixEditorGUI.EndFadeGroup(); // Draw borders var outerRect = GUIHelper.EndLayoutMeasuring(); if (t > 0.01f && Event.current.type == EventType.Repaint) { Color col = SirenixGUIStyles.BorderColor; outerRect.yMin -= 1; SirenixEditorGUI.DrawBorders(outerRect, 1, col); col.a *= t; if (this.attrSettings.DisplayMode == DictionaryDisplayOptions.OneLine) { // Draw Slide Rect Border outerRect.width = 1; outerRect.x += this.keyWidthOffset + 13; SirenixEditorGUI.DrawSolidRect(outerRect, col); } } } SirenixEditorGUI.EndIndentedVertical(); }
/// <summary> /// Draws the property. /// </summary> protected override void DrawPropertyLayout(IPropertyValueEntry <TDictionary> entry, GUIContent label) { var context = entry.Property.Context.Get(this, "context", (Context)null); if (context.Value == null) { context.Value = new Context(); context.Value.Toggled = entry.Context.GetPersistent(this, "Toggled", GeneralDrawerConfig.Instance.OpenListsByDefault); context.Value.KeyWidthOffset = 130; context.Value.Label = label ?? new GUIContent(typeof(TDictionary).GetNiceName()); context.Value.AttrSettings = entry.Property.Info.GetAttribute <DictionaryDrawerSettings>() ?? new DictionaryDrawerSettings(); context.Value.DisableAddKey = entry.Property.Tree.HasPrefabs && !entry.GetDictionaryHandler().SupportsPrefabModifications; if (!context.Value.DisableAddKey) { context.Value.TempKeyValue = new TempKeyValue(); var tree = PropertyTree.Create(context.Value.TempKeyValue); tree.UpdateTree(); context.Value.TempKeyEntry = (IPropertyValueEntry <TKey>)tree.GetPropertyAtPath("Key").ValueEntry; context.Value.TempValueEntry = (IPropertyValueEntry <TValue>)tree.GetPropertyAtPath("Value").ValueEntry; } } context.Value.DictionaryHandler = (DictionaryHandler <TDictionary, TKey, TValue>)entry.GetDictionaryHandler(); context.Value.Config = GeneralDrawerConfig.Instance; context.Value.Paging.NumberOfItemsPerPage = context.Value.Config.NumberOfItemsPrPage; context.Value.ListItemStyle.padding.right = !entry.IsEditable || context.Value.AttrSettings.IsReadOnly ? 4 : 20; //if (!IsSupportedKeyType) //{ // var message = entry.Property.Context.Get(this, "error_message", (string)null); // var detailedMessage = entry.Property.Context.Get(this, "error_message_detailed", (string)null); // var folded = entry.Property.Context.Get(this, "error_message_folded", true); // if (message.Value == null) // { // string str = ""; // if (label != null) // { // str += label.text + "\n\n"; // } // str += "The dictionary key type '" + typeof(TKey).GetNiceFullName() + "' is not supported in prefab instances. Expand this box to see which key types are supported."; // message.Value = str; // } // if (detailedMessage.Value == null) // { // var sb = new StringBuilder("The following key types are supported:"); // sb.AppendLine() // .AppendLine(); // foreach (var type in DictionaryKeyUtility.GetPersistentPathKeyTypes()) // { // sb.AppendLine(type.GetNiceName()); // } // sb.AppendLine("Enums of any type"); // detailedMessage.Value = sb.ToString(); // } // folded.Value = SirenixEditorGUI.DetailedMessageBox(message.Value, detailedMessage.Value, MessageType.Error, folded.Value); // return; //} SirenixEditorGUI.BeginIndentedVertical(SirenixGUIStyles.PropertyPadding); { context.Value.Paging.Update(elementCount: entry.Property.Children.Count); this.DrawToolbar(entry, context.Value); context.Value.Paging.Update(elementCount: entry.Property.Children.Count); if (!context.Value.DisableAddKey && context.Value.AttrSettings.IsReadOnly == false) { this.DrawAddKey(entry, context.Value); } float t; GUIHelper.BeginLayoutMeasuring(); if (SirenixEditorGUI.BeginFadeGroup(UniqueDrawerKey.Create(entry.Property, this), context.Value.Toggled.Value, out t)) { var rect = SirenixEditorGUI.BeginVerticalList(false); if (context.Value.AttrSettings.DisplayMode == DictionaryDisplayOptions.OneLine) { var maxWidth = rect.width - 90; rect.xMin = context.Value.KeyWidthOffset + 22; rect.xMax = rect.xMin + 10; context.Value.KeyWidthOffset = context.Value.KeyWidthOffset + SirenixEditorGUI.SlideRect(rect).x; if (Event.current.type == EventType.Repaint) { context.Value.KeyWidthOffset = Mathf.Clamp(context.Value.KeyWidthOffset, 90, maxWidth); } if (context.Value.Paging.ElementCount != 0) { var headerRect = SirenixEditorGUI.BeginListItem(false); { GUILayout.Space(14); if (Event.current.type == EventType.Repaint) { GUI.Label(headerRect.SetWidth(context.Value.KeyWidthOffset), context.Value.AttrSettings.KeyLabel, SirenixGUIStyles.LabelCentered); GUI.Label(headerRect.AddXMin(context.Value.KeyWidthOffset), context.Value.AttrSettings.ValueLabel, SirenixGUIStyles.LabelCentered); SirenixEditorGUI.DrawSolidRect(headerRect.AlignBottom(1), SirenixGUIStyles.BorderColor); } } SirenixEditorGUI.EndListItem(); } } this.DrawElements(entry, label, context.Value); SirenixEditorGUI.EndVerticalList(); } SirenixEditorGUI.EndFadeGroup(); // Draw borders var outerRect = GUIHelper.EndLayoutMeasuring(); if (t > 0.01f && Event.current.type == EventType.Repaint) { Color col = SirenixGUIStyles.BorderColor; outerRect.yMin -= 1; SirenixEditorGUI.DrawBorders(outerRect, 1, col); col.a *= t; if (context.Value.AttrSettings.DisplayMode == DictionaryDisplayOptions.OneLine) { // Draw Slide Rect Border outerRect.width = 1; outerRect.x += context.Value.KeyWidthOffset + 13; SirenixEditorGUI.DrawSolidRect(outerRect, col); } } } SirenixEditorGUI.EndIndentedVertical(); }
/// <summary> /// Draws the property. /// </summary> protected override void DrawPropertyImplementation(InspectorProperty property, GUIContent label) { if (label == null) { SirenixEditorGUI.BeginIndentedVertical(); for (int i = 0; i < property.Children.Count; i++) { InspectorUtilities.DrawProperty(property.Children[i]); } SirenixEditorGUI.EndIndentedVertical(); } else { Context context; if (property.Context.Get <Context>(this, "context", out context)) { context.IsVisisble = property.Context.GetPersistent(this, "IsVisible", SirenixEditorGUI.ExpandFoldoutByDefault); context.IsInlineProperty = property.ValueEntry.TypeOfValue.GetAttribute <InlinePropertyAttribute>() ?? property.Info.GetAttribute <InlinePropertyAttribute>(); } if (context.IsInlineProperty != null) { var outerRect = EditorGUILayout.BeginHorizontal(); { if (Event.current.type == EventType.Repaint) { outerRect.y += 1; EditorGUI.PrefixLabel(outerRect, label); } GUILayout.Space(EditorGUIUtility.labelWidth); GUILayout.BeginVertical(); { if (context.IsInlineProperty.LabelWidth > 0) { GUIHelper.PushLabelWidth(context.IsInlineProperty.LabelWidth); } GUIHelper.PushIndentLevel(0); for (int i = 0; i < property.Children.Count; i++) { InspectorUtilities.DrawProperty(property.Children[i]); } GUIHelper.PopIndentLevel(); if (context.IsInlineProperty.LabelWidth > 0) { GUIHelper.PopLabelWidth(); } } GUILayout.EndVertical(); } EditorGUILayout.EndHorizontal(); } else { context.IsVisisble.Value = SirenixEditorGUI.Foldout(context.IsVisisble.Value, label); if (SirenixEditorGUI.BeginFadeGroup(context, context.IsVisisble.Value)) { EditorGUI.indentLevel++; for (int i = 0; i < property.Children.Count; i++) { InspectorUtilities.DrawProperty(property.Children[i]); } EditorGUI.indentLevel--; } SirenixEditorGUI.EndFadeGroup(); } } }
private int DrawEntry(int index, ref bool collectionChanged) { SirenixEditorGUI.BeginIndentedHorizontal(new GUILayoutOption[] {}); SirenixEditorGUI.BeginBox(); SirenixEditorGUI.Title(this.Attribute.ListItemType.Name.Split('`')[0] + " " + index + "", "", TextAlignment.Center, true, false); SirenixEditorGUI.BeginIndentedVertical(); EditorGUI.BeginChangeCheck(); foreach (var child in this.Property.Children[index].Children) { child.Draw(); if (true /*is node port?*/) { NodeEditorGUILayout.PortField(/*port*/ null, new GUILayoutOption[] {}); /* * To draw a port field here: * - have a port to populate this field * - the port should represent the set of properties for this item in * the list * - I (hopefully) shouldn't have to add a port within the dynamic list * property's type * To get the ports: * - The list of ports is stored in the node at edit time. Somehow, * they need to end up here, and in the right order. * - The NodeEditor does something similar already using attributes, so * it can't be *totally* impossible. * Complications: * - This property drawer isn't specific to the Node Editor Window. * This means ports shouldn't get drawn in every context. * - There doesn't seem to be a way to get info about the node directly * into the property attribute as would be the simplest solution... * instead, online the suggestion is to use some kind of 3rd class to * look at class instances and pull information from point A to point * B. Sounds difficult to do elegantly in this scenario...but again, * the NodeEditor can accomplish drawing port handles over existing * property draws, so it must be possible. * Observations: * - NodeEditor passes target.DynamicPorts into * NodeEditorGUILayout.PortField. That's the call we care about. * - target is almost certainly the Node that the NodeEditor is * tasked with rendering. * - target is defined in NodeEditorBase, as generic type K (which * for us is XNode.Node...) * - target seems to be handed to NodeEditorBase via GetEditor, which * seems like some weird roundabout constructor... * - is a static method that creates an instance of type T (NodeEditor in our case, * which again directly inherits from NodeEditorBase) using * reflection * - populates target (and other props) * - hands back the created instance * - NodeEditorBase.GetEditor() used by: * - NodeEditor: to add context menu stuff (not useful) * - NodeEditorAction: to handle mouse behavior (not useful) * - NodeEditorGUI: to draw all the nodes (sounds about right) * - Soon after GetEditor(), NodeEditorGUI calls OnBodyGUI() on * the instance, which in turn draws the port fields, so yes, * this is correct spot... * - NodeEditorGUI gets the node in question from a reference to * the graph. * - graph in this case is a property belonging to * NodeEditorGUI's outer class...yes, NodeEditorGUI is * enclosed within a partial class of NodeEditorWindow...wow, * the code smell is funky down here in XNode land. * - this, in turn, looks to be set in NodeEditorWindow.Open() * - NodeEditorWindow: * - Open() called by OnOpen(), which pulls in the graph via * Unity API (EditorUtility.InstanceIDToObject()), which is fed * an instanceID given to them by (you guessed it) the Unity * Engine...crap. That's gonna be way too much work. * - Where do I already have access to the graph? * - well, the graph has access to itself (obviously) * - each node has a reference to the graph. * - I can create a custom node editor script, and that would have * access to the graph. * - Right now, my assumption is that NodeEditors are specific to a * given type of node. I could supply a NodeEditor that's * specific to nodes of type AutoNode...but, well, much of the * drawing logic would be identical. * - NodeEditor walks through all visible properties and draws * them using NodeEditorGUILayout.PropertyField. * - Wait, the call we care about may actually be PropertyField, * which supposedly automatically displays ports if relevant. How * the heck does that work? * - It looks like that method grabs the Node directly from the * SerializedProperty. You don't need to explicitly pass it in! * - But how do I know if that'd even be set in * AutoTableAttributeDrawer? Am I even working with * SerializedProperties in my case? I don't think I am... * - Nope, I'm working with OdinInspector's InspectorProperty * class. * - Could the serialized property be buried somewhere in that * class's properties? Hold on... * - ValueEntry? BaseValueEntry? Oooh, SerializationRoot sounds * promising...trouble is, that's also an * InspectorProperty...dang it. * - Maybe the OdinAttributeDrawer base class has some hints! * - It has ValueEntry...which at least will get us the values * that we're trying to draw. That's a good start! * - Can you get the object ValueEntry belongs to, perhaps? * That'd be an odd, but doable way of getting the Ports. * - ValueEntry.SmartValue is the actual list of stuff, but not * the ports. * - oh, hold on...SmartValue may have the elements, but they * may be separated from the Node's property at this point. * - Wait wait, we have access to the instance values of the * Attribute * - Can I get the property an attribute belongs to in that * case? * - ...I think we're allllll the way back to square one. I'd * be able to do this if I had .NET 4.5 to work with, but * that seems to be from THIS YEAR (2021), and the latest * stable unity version is from last year, so there's no * way their dist of .NET would be 4.5. * - ...2012. * - .NET 4.5 was released in 2012. * - That's...GREAT in this case! * - I just tried it. It works (like, we ARE using 4.5), but the calling function * didn't turn out to be helpful at all. * - ASDLJHDFLKJHSJSLDFH * - * - ... * - * - *sad 4:00 AM programmer noises.* * * * */ } } if (EditorGUI.EndChangeCheck()) { collectionChanged = true; } SirenixEditorGUI.EndIndentedVertical(); SirenixEditorGUI.EndBox(); if (!SirenixEditorGUI.IconButton(EditorIcons.Minus)) { index++; } else { this.ValueEntry.SmartValue.RemoveAt(index); } SirenixEditorGUI.EndIndentedHorizontal(); return(index); }