public void Init(Rect position, SerializedProperty property, GUIContent label) { if (property == null) return; if (this.inited == true) return; this.window = (property.serializedObject.targetObject as LayoutWindowType); this.items = new List<SerializedProperty>(); this.components = new List<UnityEngine.UI.Windows.Types.Layout.Component>(); for (int i = 0; i < property.arraySize; ++i) { var element = property.GetArrayElementAtIndex(i); if (this.window.layout.layout == null) continue; var rootElement = this.window.layout.layout.GetRootByTag(this.window.layout.components[i].tag); if (rootElement != null && rootElement.showInComponentsList == true) { this.components.Add(this.window.layout.components[i]); this.items.Add(element); } } this.onItemDraw = (rect, item) => { var h = 0f; return LayoutSettingsEditor.OnItemDraw(this.window, this.items, this.components, true, true, rect, item, out h); }; System.Func<int, float> getHeight = (index) => { var h = 56f; LayoutSettingsEditor.OnItemDraw(this.window, this.items, this.components, false, true, new Rect(), this.items[index], out h); return h; }; this.elements = new ReorderableListControl(ReorderableListFlags.HideAddButton | ReorderableListFlags.HideRemoveButtons | ReorderableListFlags.DisableAutoScroll | ReorderableListFlags.DisableReordering); this.adapter = new ComponentsListAdaptor<SerializedProperty>(this.items, this.onItemDraw, getHeight); this.inited = true; }
/// <summary> /// Draw layout version of list control. /// </summary> /// <param name="controlID">Unique ID of list control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="drawEmpty">Delegate for drawing empty list.</param> private void Draw(int controlID, IReorderableListAdaptor adaptor, DrawEmpty drawEmpty) { FixStyles(); PrepareState(controlID, adaptor); Rect position; if (adaptor.Count > 0) position = DrawLayoutListField(controlID, adaptor); else position = DrawLayoutEmptyList(drawEmpty); DrawFooterControls(position, controlID, adaptor); }
protected override void AddItemToCollection(TKey item, ref IDictionary <TKey, TValue> collection, IReorderableListAdaptor adaptor) { try { if (!collection.ContainsKey(item)) { collection.Add(item, default(TValue)); } } catch (Exception) { if (ReferenceEquals(item, null)) { Debug.LogError("Unable to add null keys to dictionaries; please select an instance first."); return; } throw; } }
/// <summary> /// Accept reordering. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> private void AcceptReorderDrag(IReorderableListAdaptor adaptor) { try { // Reorder list as needed! s_TargetIndex = Mathf.Clamp(s_TargetIndex, 0, adaptor.Count + 1); if (s_TargetIndex != s_AnchorIndex && s_TargetIndex != s_AnchorIndex + 1) MoveItem(adaptor, s_AnchorIndex, s_TargetIndex); } finally { StopTrackingReorderDrag(); } }
/// <summary> /// Initializes a new instance of <see cref="ItemMovedEventArgs"/>. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="buttonPosition">Position of the add menu button.</param> public AddMenuClickedEventArgs(IReorderableListAdaptor adaptor, Rect buttonPosition) { this.Adaptor = adaptor; this.ButtonPosition = buttonPosition; }
/// <summary> /// Duplicate specified item and raises the event <see cref="ItemInserted"/>. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="itemIndex">Zero-based index of item.</param> protected void DuplicateItem(IReorderableListAdaptor adaptor, int itemIndex) { adaptor.Duplicate(itemIndex); AutoFocusItem(s_ContextControlID, itemIndex + 1); GUI.changed = true; ReorderableListGUI.IndexOfChangedItem = -1; var args = new ItemInsertedEventArgs(adaptor, itemIndex + 1, true); OnItemInserted(args); }
/// <summary> /// Calculate height of list control in pixels. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <returns> /// Required list height in pixels. /// </returns> public float CalculateListHeight(IReorderableListAdaptor adaptor) { FixStyles(); float totalHeight = ContainerStyle.padding.vertical - 1; // Take list items into consideration. int count = adaptor.Count; for (int i = 0; i < count; ++i) totalHeight += adaptor.GetItemHeight(i); // Add spacing between list items. totalHeight += 4 * count; // Add height of footer buttons. if (HasFooterButtons) totalHeight += FooterButtonStyle.fixedHeight; return totalHeight; }
/// <summary> /// Invoked to generate context menu for list item. /// </summary> /// <param name="menu">Menu which can be populated.</param> /// <param name="itemIndex">Zero-based index of item which was right-clicked.</param> /// <param name="adaptor">Reorderable list adaptor.</param> protected virtual void AddItemsToMenu(GenericMenu menu, int itemIndex, IReorderableListAdaptor adaptor) { if ((Flags & ReorderableListFlags.DisableReordering) == 0) { if (itemIndex > 0) menu.AddItem(CommandMoveToTop, false, DefaultContextHandler, CommandMoveToTop); else menu.AddDisabledItem(CommandMoveToTop); if (itemIndex + 1 < adaptor.Count) menu.AddItem(CommandMoveToBottom, false, DefaultContextHandler, CommandMoveToBottom); else menu.AddDisabledItem(CommandMoveToBottom); if (HasAddButton) { menu.AddSeparator(""); menu.AddItem(CommandInsertAbove, false, DefaultContextHandler, CommandInsertAbove); menu.AddItem(CommandInsertBelow, false, DefaultContextHandler, CommandInsertBelow); if ((Flags & ReorderableListFlags.DisableDuplicateCommand) == 0) menu.AddItem(CommandDuplicate, false, DefaultContextHandler, CommandDuplicate); } } if (HasRemoveButtons) { if (menu.GetItemCount() > 0) menu.AddSeparator(""); menu.AddItem(CommandRemove, false, DefaultContextHandler, CommandRemove); menu.AddSeparator(""); menu.AddItem(CommandClearAll, false, DefaultContextHandler, CommandClearAll); } }
protected override void AddItemsToMenu(GenericMenu menu, int itemIndex, IReorderableListAdaptor adaptor) { menu.AddItem(menuItem1, false, DefaultContextHandler, menuItem1); }
/// <summary> /// Draw list field control for adapted collection. /// </summary> /// <param name="position">Position of control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param> /// <param name="flags">Optional flags to pass into list field.</param> private static void DoListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmptyAbsolute drawEmpty, [System.ComponentModel.DefaultValue(0)] ReorderableListFlags flags) { ReorderableListControl.DrawControlFromState(position, adaptor, drawEmpty, flags); }
/// <summary> /// Draw list field control for adapted collection. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param> /// <param name="flags">Optional flags to pass into list field.</param> private static void DoListField(IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmpty drawEmpty, [System.ComponentModel.DefaultValue(0)] ReorderableListFlags flags) { ReorderableListControl.DrawControlFromState(adaptor, drawEmpty, flags); }
protected override void AddItemToCollection(TKey item, ref IDictionary <TKey, TValue> collection, IReorderableListAdaptor adaptor0) { try { if (!collection.ContainsKey(item)) { var adaptor = adaptor0 as CollectionAdaptor <KeyValuePair <TKey, TValue> >; if (adaptor == null && adaptor0 is PageAdaptor) { PageAdaptor pageAdaptor = (PageAdaptor)adaptor0; adaptor = (CollectionAdaptor <KeyValuePair <TKey, TValue> >)pageAdaptor.BackingAdaptor; } adaptor.Add(new KeyValuePair <TKey, TValue>(item, default(TValue))); } } catch (Exception) { if (ReferenceEquals(item, null)) { Debug.LogError("Unable to add null keys to dictionaries; please select an instance first."); return; } throw; } }
protected override void OnPostEdit(ref T[] collection, IReorderableListAdaptor adaptor) { collection = ((ArrayAdaptor <T>)adaptor).StoredArray; }
public PageAdaptor(IReorderableListAdaptor backingAdaptor, int startIndex, int endIndex) { BackingAdaptor = backingAdaptor; _startIndex = startIndex; _endIndex = endIndex; }
///Load entry content into Editor block, does not thing to do with (int) selected. void LoadInfoGroup(InfoLogGroup group) { infoGroupCache = group; _listAdaptor = new GenericListAdaptor <InfoLogEntry>(infoGroupCache.entries, DrawListEntry, 16f); DrawSideBar_Entry = DrawSideBar_Entries_Normal; }
/// <summary> /// Draw list control with absolute positioning. /// </summary> /// <param name="position">Position of list control in GUI.</param> /// <param name="controlID">Unique ID of list control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="drawEmpty">Delegate for drawing empty list.</param> private void Draw(Rect position, int controlID, IReorderableListAdaptor adaptor, DrawEmptyAbsolute drawEmpty) { FixStyles(); PrepareState(controlID, adaptor); // Allow for footer area. if (HasFooterButtons) position.height -= FooterButtonStyle.fixedHeight; if (adaptor.Count > 0) { DrawListContainerAndItems(position, controlID, adaptor); CheckForAutoFocusControl(controlID); } else { DrawEmptyListControl(position, drawEmpty); } DrawFooterControls(position, controlID, adaptor); }
/// <inheritdoc cref="Draw(Rect, IReorderableListAdaptor, DrawEmptyAbsolute)"/> public void Draw(Rect position, IReorderableListAdaptor adaptor) { int controlID = GUIUtility.GetControlID(FocusType.Passive); Draw(position, controlID, adaptor, null); }
//Nothing in here is used ATM, the context menu is disabled protected override void AddItemsToMenu(GenericMenu menu, int itemIndex, IReorderableListAdaptor adaptor) { menu.AddItem(commandCreate, false, defaultContextHandler, commandCreate); menu.AddItem(commandImport, false, defaultContextHandler, commandImport); }
/// <summary> /// Call to manually perform command. /// </summary> /// <remarks> /// <para>Warning message is logged to console if attempted to execute unknown command.</para> /// </remarks> /// <param name="commandName">Name of command. This is the text shown in the context menu.</param> /// <param name="itemIndex">Zero-based index of item which was right-clicked.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <returns> /// A value of <c>true</c> if command was known; otherwise <c>false</c>. /// </returns> public bool DoCommand(string commandName, int itemIndex, IReorderableListAdaptor adaptor) { if (!HandleCommand(s_ContextCommandName, itemIndex, adaptor)) { Debug.LogWarning("Unknown context command."); return false; } return true; }
/// <summary> /// Initializes a new instance of <see cref="ItemRemovingEventArgs"/>. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="itemIndex">Zero-based index of item.</param> public ItemRemovingEventArgs(IReorderableListAdaptor adaptor, int itemIndex) { this.Adaptor = adaptor; this.ItemIndex = itemIndex; }
/// <summary> /// Move item from source index to destination index. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="sourceIndex">Zero-based index of source item.</param> /// <param name="destIndex">Zero-based index of destination index.</param> protected void MoveItem(IReorderableListAdaptor adaptor, int sourceIndex, int destIndex) { // Raise event before moving item so that the operation can be cancelled. var movingEventArgs = new ItemMovingEventArgs(adaptor, sourceIndex, destIndex); OnItemMoving(movingEventArgs); if (!movingEventArgs.Cancel) { adaptor.Move(sourceIndex, destIndex); // Item was actually moved! int newIndex = destIndex; if (newIndex > sourceIndex) --newIndex; OnItemMoved(new ItemMovedEventArgs(adaptor, sourceIndex, newIndex)); GUI.changed = true; } ReorderableListGUI.IndexOfChangedItem = -1; }
/// <summary> /// Initializes a new instance of <see cref="ItemMovingEventArgs"/>. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="itemIndex">Zero-based index of item.</param> /// <param name="destinationItemIndex">Xero-based index of item destination.</param> public ItemMovingEventArgs(IReorderableListAdaptor adaptor, int itemIndex, int destinationItemIndex) { this.Adaptor = adaptor; this.ItemIndex = itemIndex; this.DestinationItemIndex = destinationItemIndex; }
/// <summary> /// Remove all items from list. /// </summary> /// <remarks> /// <para>The event <see cref="ItemRemoving"/> is raised for each item prior to /// clearing array and allows entire operation to be cancelled.</para> /// </remarks> /// <param name="adaptor">Reorderable list adaptor.</param> /// <returns> /// Returns a value of <c>false</c> if operation was cancelled. /// </returns> protected bool ClearAll(IReorderableListAdaptor adaptor) { if (adaptor.Count == 0) return true; var args = new ItemRemovingEventArgs(adaptor, 0); int count = adaptor.Count; for (int i = 0; i < count; ++i) { args.ItemIndex = i; OnItemRemoving(args); if (args.Cancel) return false; } adaptor.Clear(); GUI.changed = true; ReorderableListGUI.IndexOfChangedItem = -1; return true; }
/// <summary> /// Prepare initial state for list control. /// </summary> /// <param name="controlID">Unique ID of list control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> private void PrepareState(int controlID, IReorderableListAdaptor adaptor) { _controlID = controlID; _visibleRect = GUIHelper.VisibleRect(); if ((Flags & ReorderableListFlags.ShowIndices) != 0) { int digitCount = Mathf.Max(2, Mathf.CeilToInt(Mathf.Log10((float)adaptor.Count))); _indexLabelWidth = digitCount * 8 + 8; } else { _indexLabelWidth = 0; } _tracking = IsTrackingControl(controlID); _allowReordering = (Flags & ReorderableListFlags.DisableReordering) == 0; }
/// <summary> /// Initializes a new instance of <see cref="ItemMovedEventArgs"/>. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="oldItemIndex">Old zero-based index of item.</param> /// <param name="newItemIndex">New zero-based index of item.</param> public ItemMovedEventArgs(IReorderableListAdaptor adaptor, int oldItemIndex, int newItemIndex) { this.Adaptor = adaptor; this.OldItemIndex = oldItemIndex; this.NewItemIndex = newItemIndex; }
private void DrawFloatingListItem(EventType eventType, IReorderableListAdaptor adaptor, float targetSlotPosition) { if (eventType == EventType.Repaint) { Color restoreColor = GUI.color; // Fill background of target area. Rect targetPosition = s_DragItemPosition; targetPosition.y = targetSlotPosition - 1; targetPosition.height = 1; GUIHelper.DrawTexture(targetPosition, ReorderableListResources.texItemSplitter); --targetPosition.x; ++targetPosition.y; targetPosition.width += 2; targetPosition.height = s_DragItemPosition.height - 1; GUI.color = TargetBackgroundColor; GUIHelper.DrawTexture(targetPosition, EditorGUIUtility.whiteTexture); // Fill background of item which is being dragged. --s_DragItemPosition.x; s_DragItemPosition.width += 2; --s_DragItemPosition.height; GUI.color = AnchorBackgroundColor; GUIHelper.DrawTexture(s_DragItemPosition, EditorGUIUtility.whiteTexture); ++s_DragItemPosition.x; s_DragItemPosition.width -= 2; ++s_DragItemPosition.height; // Draw horizontal splitter above and below. GUI.color = new Color(0f, 0f, 0f, 0.6f); targetPosition.y = s_DragItemPosition.y - 1; targetPosition.height = 1; GUIHelper.DrawTexture(targetPosition, EditorGUIUtility.whiteTexture); targetPosition.y += s_DragItemPosition.height; GUIHelper.DrawTexture(targetPosition, EditorGUIUtility.whiteTexture); GUI.color = restoreColor; } DrawListItem(eventType, s_DragItemPosition, adaptor, s_AnchorIndex); }
/// <summary> /// Initializes a new instance of <see cref="ItemInsertedEventArgs"/>. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="itemIndex">Zero-based index of item.</param> /// <param name="wasDuplicated">Indicates if inserted item was duplicated from another item.</param> public ItemInsertedEventArgs(IReorderableListAdaptor adaptor, int itemIndex, bool wasDuplicated) { this.Adaptor = adaptor; this.ItemIndex = itemIndex; this.WasDuplicated = wasDuplicated; }
/// <summary> /// Do layout version of list field. /// </summary> /// <param name="controlID">Unique ID of list control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <returns> /// Position of list container area in GUI (excludes footer area). /// </returns> private Rect DrawLayoutListField(int controlID, IReorderableListAdaptor adaptor) { float totalHeight; // Calculate position of list field using layout engine. if (Event.current.type == EventType.Layout) { totalHeight = CalculateListHeight(adaptor); s_ContainerHeightCache[controlID] = totalHeight; } else { totalHeight = s_ContainerHeightCache.ContainsKey(controlID) ? s_ContainerHeightCache[controlID] : 0; } Rect position = GUILayoutUtility.GetRect(GUIContent.none, ContainerStyle, GUILayout.Height(totalHeight)); // Make room for footer buttons? if (HasFooterButtons) position.height -= FooterButtonStyle.fixedHeight; // Draw list as normal. DrawListContainerAndItems(position, controlID, adaptor); CheckForAutoFocusControl(controlID); return position; }
/// <summary> /// Draw list field control for adapted collection. /// </summary> /// <param name="position">Position of control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param> /// <param name="flags">Optional flags to pass into list field.</param> private static void DoListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmptyAbsolute drawEmpty, ReorderableListFlags flags) { ReorderableListControl.DrawControlFromState(position, adaptor, drawEmpty, flags); }
/// <inheritdoc cref="Draw(int, IReorderableListAdaptor, DrawEmpty)"/> public void Draw(IReorderableListAdaptor adaptor, DrawEmpty drawEmpty) { int controlID = GUIUtility.GetControlID(FocusType.Passive); Draw(controlID, adaptor, drawEmpty); }
/// <inheritdoc cref="DoListField(IReorderableListAdaptor, ReorderableListControl.DrawEmpty, ReorderableListFlags)"/> public static void ListField(IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmpty drawEmpty) { DoListField(adaptor, drawEmpty, 0); }
/// <summary> /// Draw list control with absolute positioning. /// </summary> /// <param name="position">Position of list control in GUI.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="drawEmpty">Delegate for drawing empty list.</param> public void Draw(Rect position, IReorderableListAdaptor adaptor, DrawEmptyAbsolute drawEmpty) { int controlID = GUIUtility.GetControlID(FocusType.Passive); Draw(position, controlID, adaptor, drawEmpty); }
/// <inheritdoc cref="DoListFieldAbsolute(Rect, IReorderableListAdaptor, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)"/> public static void ListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmptyAbsolute drawEmpty) { DoListFieldAbsolute(position, adaptor, drawEmpty, 0); }
private void ShowContextMenu(int controlID, int itemIndex, IReorderableListAdaptor adaptor) { GenericMenu menu = new GenericMenu(); s_ContextControlID = controlID; s_ContextItemIndex = itemIndex; AddItemsToMenu(menu, itemIndex, adaptor); if (menu.GetItemCount() > 0) menu.ShowAsContext(); }
/// <inheritdoc cref="DoListField(IReorderableListAdaptor, ReorderableListControl.DrawEmpty, ReorderableListFlags)"/> public static void ListField(IReorderableListAdaptor adaptor, ReorderableListFlags flags) { DoListField(adaptor, null, flags); }
/// <summary> /// Invoked to handle context command. /// </summary> /// <remarks> /// <para>It is important to set the value of <c>GUI.changed</c> to <c>true</c> if any /// changes are made by command handler.</para> /// <para>Default command handling functionality can be inherited:</para> /// <code language="csharp"><![CDATA[ /// protected override bool HandleCommand(string commandName, int itemIndex, IReorderableListAdaptor adaptor) { /// if (base.HandleCommand(itemIndex, adaptor)) /// return true; /// /// // Place custom command handling code here... /// switch (commandName) { /// case "Your Command": /// return true; /// } /// /// return false; /// } /// ]]></code> /// <code language="unityscript"><![CDATA[ /// function HandleCommand(commandName:String, itemIndex:int, adaptor:IReorderableListAdaptor):boolean { /// if (base.HandleCommand(itemIndex, adaptor)) /// return true; /// /// // Place custom command handling code here... /// switch (commandName) { /// case 'Your Command': /// return true; /// } /// /// return false; /// } /// ]]></code> /// </remarks> /// <param name="commandName">Name of command. This is the text shown in the context menu.</param> /// <param name="itemIndex">Zero-based index of item which was right-clicked.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <returns> /// A value of <c>true</c> if command was known; otherwise <c>false</c>. /// </returns> protected virtual bool HandleCommand(string commandName, int itemIndex, IReorderableListAdaptor adaptor) { switch (commandName) { case "Move to Top": MoveItem(adaptor, itemIndex, 0); return true; case "Move to Bottom": MoveItem(adaptor, itemIndex, adaptor.Count); return true; case "Insert Above": InsertItem(adaptor, itemIndex); return true; case "Insert Below": InsertItem(adaptor, itemIndex + 1); return true; case "Duplicate": DuplicateItem(adaptor, itemIndex); return true; case "Remove": RemoveItem(adaptor, itemIndex); return true; case "Clear All": ClearAll(adaptor); return true; default: return false; } }
/// <inheritdoc cref="DoListField(IReorderableListAdaptor, ReorderableListControl.DrawEmpty, ReorderableListFlags)"/> public static void ListField(IReorderableListAdaptor adaptor) { DoListField(adaptor, null, 0); }
/// <summary> /// Call to manually perform command. /// </summary> /// <remarks> /// <para>Warning message is logged to console if attempted to execute unknown command.</para> /// </remarks> /// <param name="command">Content representing command.</param> /// <param name="itemIndex">Zero-based index of item which was right-clicked.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <returns> /// A value of <c>true</c> if command was known; otherwise <c>false</c>. /// </returns> public bool DoCommand(GUIContent command, int itemIndex, IReorderableListAdaptor adaptor) { return DoCommand(command.text, itemIndex, adaptor); }
/// <inheritdoc cref="DoListFieldAbsolute(Rect, IReorderableListAdaptor, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)"/> public static void ListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor) { DoListFieldAbsolute(position, adaptor, null, 0); }
/// <inheritdoc cref="CalculateListFieldHeight(IReorderableListAdaptor, ReorderableListFlags)"/> public static float CalculateListFieldHeight(IReorderableListAdaptor adaptor) { return(CalculateListFieldHeight(adaptor, 0)); }
/// <summary> /// Add item at end of list and raises the event <see cref="ItemInserted"/>. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> protected void AddItem(IReorderableListAdaptor adaptor) { adaptor.Add(); AutoFocusItem(s_ContextControlID, adaptor.Count - 1); GUI.changed = true; ReorderableListGUI.IndexOfChangedItem = -1; var args = new ItemInsertedEventArgs(adaptor, adaptor.Count - 1, false); OnItemInserted(args); }
public void DoEdit(Rect initialRegion, Rect region, GUIContent label, ref TCollection collection, fiGraphMetadata metadata, IReorderableListAdaptor adaptor) { Rect bodyRect = new Rect(region); bodyRect.height -= GetAddRegionHeightWithMargin(metadata); Rect addItemButtonRect, addItemItemRect; { Rect baseAddItemRect = new Rect(region); baseAddItemRect.y += bodyRect.height + AddRegionMargin; baseAddItemRect.height = GetAddRegionHeight(metadata); fiRectUtility.SplitLeftHorizontalExact(baseAddItemRect, /*leftWidth:*/ ReorderableListGUI.defaultAddButtonStyle.fixedWidth, /*margin:*/ 1, out addItemButtonRect, out addItemItemRect); // move the margin up addItemItemRect.x += AddRegionBorder; addItemItemRect.width -= AddRegionBorder * 2; addItemButtonRect.y -= AddRegionBorder * 2; addItemItemRect.y -= AddRegionBorder; if (DisplayAddItemPreview && Event.current.type == EventType.repaint) { Rect container = addItemItemRect; container.x -= AddRegionBorder; container.y -= AddRegionBorder; container.width += AddRegionBorder * 2; container.height += AddRegionBorder * 2; ReorderableListGUI.defaultContainerStyle.Draw(container, false, false, false, false); } addItemButtonRect.height = ReorderableListGUI.defaultAddButtonStyle.fixedHeight; } // draw the collection elements ReorderableListGUI.ListFieldAbsolute(bodyRect, adaptor, DrawEmpty, _listFlags); // draw the next key metadata.Enter("NextValue").Metadata.GetPersistentMetadata <fiDropdownMetadata>().ForceDisable(); if (DisplayAddItemPreview) { var addButtonStyle = ReorderableListGUI.defaultAddButtonStyleFlipped; if (adaptor.Count == 0) { addButtonStyle = ReorderableListGUI.defaultAddButtonStyleIndependent; } if (GUI.Button(addItemButtonRect, "", addButtonStyle)) { AddItemToCollection(_addItem, ref collection, adaptor); GUI.FocusControl(null); _addItem = default(TAddItem); } EnsureValidAddItem(ref _addItem); fiEditorGUI.PushHierarchyMode(false); _addItem = _addItemEditor.FirstEditor.Edit(addItemItemRect, GUIContent.none, _addItem, metadata.Enter("NextValue")); fiEditorGUI.PopHierarchyMode(); } AcceptDragAndDrop(initialRegion, ref collection, adaptor); }
/// <summary> /// Remove specified item. /// </summary> /// <remarks> /// <para>The event <see cref="ItemRemoving"/> is raised prior to removing item /// and allows removal to be cancelled.</para> /// </remarks> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="itemIndex">Zero-based index of item.</param> /// <returns> /// Returns a value of <c>false</c> if operation was cancelled. /// </returns> protected bool RemoveItem(IReorderableListAdaptor adaptor, int itemIndex) { var args = new ItemRemovingEventArgs(adaptor, itemIndex); OnItemRemoving(args); if (args.Cancel) return false; adaptor.Remove(itemIndex); GUI.changed = true; ReorderableListGUI.IndexOfChangedItem = -1; return true; }
/// <summary> /// Called after an edit cycle is done if the collection needs to be updated from the adaptor. /// </summary> protected virtual void OnPostEdit(ref TCollection collection, IReorderableListAdaptor adaptor) { }
/// <summary> /// An item has been added to the collection. /// </summary> protected virtual void AddItemToCollection(TAddItem item, ref TCollection collection, IReorderableListAdaptor adaptor) { if (typeof(TItem).IsAssignableFrom(typeof(TAddItem)) == false) { Debug.LogError("Please override AddItemToCollection; " + typeof(TAddItem).CSharpName() + " cannot be converted to " + typeof(TItem).CSharpName()); return; } if (collection.GetType().IsArray) { // TODO: This is a hack. We should remove it. We update the array reference in OnPostEdit, so we have to // add the item to the actual array we will later update. var a = (ArrayAdaptor <TItem>)adaptor; adaptor.Add(); a.StoredArray[a.Count - 1] = (TItem)(object)item; } else { collection.Add((TItem)(object)item); } }
/// <summary> /// Generate and draw control from state object. /// </summary> /// <param name="position">Position of control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="drawEmpty">Delegate for drawing empty list.</param> /// <param name="flags">Optional flags to pass into list field.</param> public static void DrawControlFromState(Rect position, IReorderableListAdaptor adaptor, DrawEmptyAbsolute drawEmpty, ReorderableListFlags flags) { int controlID = GUIUtility.GetControlID(FocusType.Passive); var control = GUIUtility.GetStateObject(typeof(ReorderableListControl), controlID) as ReorderableListControl; control.Flags = flags; control.Draw(position, controlID, adaptor, drawEmpty); }
/// <summary> /// Draw list field control for adapted collection. /// </summary> /// <param name="adaptor">Reorderable list adaptor.</param> /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param> /// <param name="flags">Optional flags to pass into list field.</param> private static void DoListField(IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmpty drawEmpty, ReorderableListFlags flags = 0) { ReorderableListControl.DrawControlFromState(adaptor, drawEmpty, flags); }
private void DrawListItem(EventType eventType, Rect position, IReorderableListAdaptor adaptor, int itemIndex) { bool visible = (position.y < _visibleRect.yMax && position.yMax > _visibleRect.y); bool draggable = _allowReordering && adaptor.CanDrag(itemIndex); Rect itemContentPosition = position; itemContentPosition.x = position.x + 2; itemContentPosition.y += 1; itemContentPosition.width = position.width - 4; itemContentPosition.height = position.height - 4; // Make space for grab handle? if (draggable) { itemContentPosition.x += 20; itemContentPosition.width -= 20; } // Make space for element index. if (_indexLabelWidth != 0) { itemContentPosition.width -= _indexLabelWidth; if (eventType == EventType.Repaint && visible) s_RightAlignedLabelStyle.Draw(new Rect(itemContentPosition.x, position.y, _indexLabelWidth, position.height - 4), itemIndex + ":", false, false, false, false); itemContentPosition.x += _indexLabelWidth; } // Make space for remove button? if (HasRemoveButtons) itemContentPosition.width -= 27; try { s_CurrentItemIndex.Push(itemIndex); EditorGUI.BeginChangeCheck(); if (eventType == EventType.Repaint && visible) { // Draw background of list item. var backgroundPosition = new Rect(position.x, position.y, position.width, position.height - 1); adaptor.DrawItemBackground(backgroundPosition, itemIndex); // Draw grab handle? if (draggable) { var texturePosition = new Rect(position.x + 6, position.y + position.height / 2f - 3, 9, 5); GUIHelper.DrawTexture(texturePosition, ReorderableListResources.GetTexture(ReorderableListTexture.GrabHandle)); } // Draw splitter between list items. if (itemIndex != 0 && (!_tracking || itemIndex != s_AnchorIndex)) { var texturePosition = new Rect(position.x, position.y - 1, position.width, 1); GUIHelper.DrawTexture(texturePosition, ReorderableListResources.texItemSplitter); } } // Allow control to be automatically focused. if (s_AutoFocusIndex == itemIndex) GUI.SetNextControlName("AutoFocus_" + _controlID + "_" + itemIndex); // Present actual control. adaptor.DrawItem(itemContentPosition, itemIndex); if (EditorGUI.EndChangeCheck()) ReorderableListGUI.IndexOfChangedItem = itemIndex; // Draw remove button? if (HasRemoveButtons && adaptor.CanRemove(itemIndex)) { s_RemoveButtonPosition = position; s_RemoveButtonPosition.width = 27; s_RemoveButtonPosition.x = itemContentPosition.xMax + 2; s_RemoveButtonPosition.height -= 2; if (DoRemoveButton(s_RemoveButtonPosition, visible)) RemoveItem(adaptor, itemIndex); } // Check for context click? if (eventType == EventType.ContextClick && position.Contains(Event.current.mousePosition) && (Flags & ReorderableListFlags.DisableContextMenu) == 0) { ShowContextMenu(_controlID, itemIndex, adaptor); Event.current.Use(); } } finally { s_CurrentItemIndex.Pop(); } }
/// <summary> /// Draw additional controls below list control and highlight drop target. /// </summary> /// <param name="position">Position of list control in GUI.</param> /// <param name="controlID">Unique ID of list control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> private void DrawFooterControls(Rect position, int controlID, IReorderableListAdaptor adaptor) { if (HasFooterButtons) { Rect buttonPosition = new Rect(position.xMax - 30, position.yMax - 1, 30, FooterButtonStyle.fixedHeight); Rect menuButtonPosition = buttonPosition; var menuIconNormal = ReorderableListResources.GetTexture(ReorderableListTexture.Icon_AddMenu_Normal); var menuIconActive = ReorderableListResources.GetTexture(ReorderableListTexture.Icon_AddMenu_Active); if (HasAddButton) { // Draw add menu drop-down button. if (HasAddMenuButton) { menuButtonPosition.x = buttonPosition.xMax - 14; menuButtonPosition.xMax = buttonPosition.xMax; menuIconNormal = ReorderableListResources.GetTexture(ReorderableListTexture.Icon_Menu_Normal); menuIconActive = ReorderableListResources.GetTexture(ReorderableListTexture.Icon_Menu_Active); buttonPosition.width -= 5; buttonPosition.x = menuButtonPosition.x - buttonPosition.width + 1; } // Draw add item button. var iconNormal = ReorderableListResources.GetTexture(ReorderableListTexture.Icon_Add_Normal); var iconActive = ReorderableListResources.GetTexture(ReorderableListTexture.Icon_Add_Active); if (GUIHelper.IconButton(buttonPosition, true, iconNormal, iconActive, FooterButtonStyle)) { // Append item to list. GUIUtility.keyboardControl = 0; AddItem(adaptor); } } if (HasAddMenuButton) { // Draw add menu drop-down button. if (GUIHelper.IconButton(menuButtonPosition, true, menuIconNormal, menuIconActive, FooterButtonStyle)) { GUIUtility.keyboardControl = 0; Rect totalAddButtonPosition = buttonPosition; totalAddButtonPosition.xMax = position.xMax; OnAddMenuClicked(new AddMenuClickedEventArgs(adaptor, totalAddButtonPosition)); // This will be helpful in many circumstances; including by default! GUIUtility.ExitGUI(); } } } }
/// <summary> /// Draw list container and items. /// </summary> /// <param name="position">Position of list control in GUI.</param> /// <param name="controlID">Unique ID of list control.</param> /// <param name="adaptor">Reorderable list adaptor.</param> private void DrawListContainerAndItems(Rect position, int controlID, IReorderableListAdaptor adaptor) { // Get local copy of event information for efficiency. EventType eventType = Event.current.GetTypeForControl(controlID); Vector2 mousePosition = Event.current.mousePosition; //if (Event.current.isMouse) // s_MousePosition = GUIUtility.GUIToScreenPoint(mousePosition); int newTargetIndex = s_TargetIndex; // Position of first item in list. float firstItemY = position.y + ContainerStyle.padding.top; switch (eventType) { case EventType.MouseDown: if (_tracking) { // Cancel drag when other mouse button is pressed. s_TrackingCancelBlockContext = true; Event.current.Use(); } break; case EventType.MouseDrag: if (_tracking) { // Reset target index and adjust when looping through list items. if (mousePosition.y < firstItemY) newTargetIndex = 0; else if (mousePosition.y >= position.yMax) newTargetIndex = adaptor.Count; s_DragItemPosition.y = Mathf.Clamp(mousePosition.y + s_AnchorMouseOffset, firstItemY, position.yMax - s_DragItemPosition.height - 1); } break; case EventType.MouseUp: if (controlID == GUIUtility.hotControl) { // Allow user code to change control over reordering during drag. if (!s_TrackingCancelBlockContext && _allowReordering) AcceptReorderDrag(adaptor); else StopTrackingReorderDrag(); Event.current.Use(); } break; case EventType.KeyDown: if (_tracking && Event.current.keyCode == KeyCode.Escape) { StopTrackingReorderDrag(); Event.current.Use(); } break; case EventType.ExecuteCommand: if (s_ContextControlID == controlID) { int itemIndex = s_ContextItemIndex; try { DoCommand(s_ContextCommandName, itemIndex, adaptor); Event.current.Use(); } finally { s_ContextControlID = 0; s_ContextItemIndex = 0; } } break; case EventType.Repaint: // Draw caption area of list. ContainerStyle.Draw(position, GUIContent.none, false, false, false, false); break; } ReorderableListGUI.IndexOfChangedItem = -1; // Draw list items! Rect itemPosition = new Rect(position.x + 2, firstItemY, position.width - 4, 0); float targetSlotPosition = position.yMax - s_DragItemPosition.height - 1; float lastMidPoint = 0f; float lastHeight = 0f; int count = adaptor.Count; for (int i = 0; i < count; ++i) { itemPosition.y = itemPosition.yMax; itemPosition.height = 0; if (_tracking) { // Does this represent the target index? if (i == s_TargetIndex) { targetSlotPosition = itemPosition.y; itemPosition.y += s_DragItemPosition.height; } // Do not draw item if it is currently being dragged. // Draw later so that it is shown in front of other controls. if (i == s_AnchorIndex) continue; lastMidPoint = itemPosition.y - lastHeight / 2f; } // Update position for current item. itemPosition.height = adaptor.GetItemHeight(i) + 4; lastHeight = itemPosition.height; if (_tracking && eventType == EventType.MouseDrag) { float midpoint = itemPosition.y + itemPosition.height / 2f; if (s_TargetIndex < i) { if (s_DragItemPosition.yMax > lastMidPoint && s_DragItemPosition.yMax < midpoint) newTargetIndex = i; } else if (s_TargetIndex > i) { if (s_DragItemPosition.y > lastMidPoint && s_DragItemPosition.y < midpoint) newTargetIndex = i; } /*if (s_DragItemPosition.y > itemPosition.y && s_DragItemPosition.y <= midpoint) newTargetIndex = i; else if (s_DragItemPosition.yMax > midpoint && s_DragItemPosition.yMax <= itemPosition.yMax) newTargetIndex = i + 1;*/ } // The following may break use of tab key to navigate through controls :/ if ((Flags & ReorderableListFlags.DisableClipping) == 0) { // Clip list item? Performance boost! if (itemPosition.yMax < _visibleRect.y - itemPosition.height) { // Let's try and trick Unity into maintaining tab key support... GUIUtility.GetControlID(FocusType.Keyboard, itemPosition); continue; } if (itemPosition.y > _visibleRect.yMax + itemPosition.height) break; } // Draw list item. DrawListItem(eventType, itemPosition, adaptor, i); // Did list count change (i.e. item removed)? if (adaptor.Count < count) { // We assume that it was this item which was removed, so --i allows us // to process the next item as usual. count = adaptor.Count; --i; continue; } // Event has already been used, skip to next item. if (Event.current.type != EventType.Used) { switch (eventType) { case EventType.MouseDown: if (GUI.enabled && itemPosition.Contains(mousePosition)) { // Remove input focus from control before attempting a context click or drag. GUIUtility.keyboardControl = 0; if (_allowReordering && adaptor.CanDrag(i) && Event.current.button == 0) { s_DragItemPosition = itemPosition; BeginTrackingReorderDrag(controlID, i); s_AnchorMouseOffset = itemPosition.y - mousePosition.y; s_TargetIndex = i; Event.current.Use(); } } break; /* DEBUG case EventType.Repaint: GUI.color = Color.red; GUI.DrawTexture(new Rect(0, lastMidPoint, 10, 1), EditorGUIUtility.whiteTexture); GUI.color = Color.yellow; GUI.DrawTexture(new Rect(5, itemPosition.y + itemPosition.height / 2f, 10, 1), EditorGUIUtility.whiteTexture); GUI.color = Color.white; break; //*/ } } } // Item which is being dragged should be shown on top of other controls! if (IsTrackingControl(controlID)) { lastMidPoint = position.yMax - lastHeight / 2f; if (eventType == EventType.MouseDrag) { if (s_DragItemPosition.yMax >= lastMidPoint) newTargetIndex = count; // Force repaint to occur so that dragging rectangle is visible. s_TargetIndex = newTargetIndex; Event.current.Use(); } DrawFloatingListItem(eventType, adaptor, targetSlotPosition); /* DEBUG if (eventType == EventType.Repaint) { GUI.color = Color.blue; GUI.DrawTexture(new Rect(100, lastMidPoint, 20, 1), EditorGUIUtility.whiteTexture); GUI.color = Color.white; } //*/ } // Fake control to catch input focus if auto focus was not possible. GUIUtility.GetControlID(FocusType.Keyboard); }
protected override void AddItemToCollection(TKey item, ref IDictionary <TKey, TValue> collection, IReorderableListAdaptor adaptor) { if (!collection.ContainsKey(item)) { collection.Add(item, default(TValue)); } }