private void SetCustomItem(VisualElement ve, DropdownMenuItem item, string[] path, int level) { ve.Q <Label>(name: "text").text = path[level]; //don't style sub menu items if (path.Length - 1 == level && item is DropdownMenuAction actionItem && actionItem.userData is CustomDropdownItemData data) { ve.Q(name: "icon").style.backgroundImage = data.icon; ve.Q <Label>(name: "hotkey-text").text = data.hotkey; }
public void AddItem(DropdownMenuItem item, string[] path, int level) { //leaf item if (level == path.Length - 1 || path.Length == 0) { if (item is DropdownMenuAction action) { var m = dropdown.actionItemPool.GetPooled(); m.Set(action.Execute, this, action.status != DropdownMenuAction.Status.Disabled); dropdown.SetItem(m.target, item, path, level); dropdown.SetItemStatus(m.target, action.status); actionItems.Add(m); menuContainer.Add(m.target); } else { var seperator = dropdown.seperatorPool.GetPooled(); seperators.Add(seperator); menuContainer.Add(seperator); } } //create submenu item else { if (!subMenus.TryGetValue(path[level], out Menu subMenu)) { //create new submenu if it has not been created yet var m = dropdown.subItemPool.GetPooled(); subMenu = dropdown.subMenuPool.GetPooled(); subMenu.Set(this, m.target); m.Set(this, subMenu, true); dropdown.SetItem(m.target, item, path, level); menuContainer.Add(m.target); subMenus.Add(path[level], subMenu); subItems.Add(m); } //add menu intially so its style is resolved Add(subMenu); subMenu.AddItem(item, path, level + 1); } }
// Add a new drop-down list item with the specified values. private DropdownMenuItem AddItem(OptionData data, DropdownMenuItem itemTemplate, List <DropdownMenuItem> items) { // Add a new item to the dropdown. DropdownMenuItem item = CreateItem(itemTemplate); item.rectTransform.SetParent(itemTemplate.rectTransform.parent, false); item.gameObject.SetActive(true); item.gameObject.name = "Item " + items.Count + (data.text != null ? ": " + data.text : ""); // Set the item's data if (item.text) { item.text.text = data.text; } if (item.image) { item.image.sprite = data.image; item.image.enabled = (item.image.sprite != null); } items.Add(item); return(item); }
public void ShowStructureSettingsUI(Structure focus) { settingsUI.SetActive(true); currentFocus = focus; settingsHeadderLabel.text = focus.displayName; foreach (Paramater p in focus.paramaters) { if (p is DropdownParamater) { DropdownMenuItem item = Instantiate(dropdownParamaterUIPrefab, settingsView, false).GetComponent <DropdownMenuItem>(); item.Setup(p as DropdownParamater); currentSettings.Add(item); } if (p is DecimalParamater) { DecimalMenuItem item = Instantiate(decimalParamaterUIPrefab, settingsView, false).GetComponent <DecimalMenuItem>(); item.Setup(p as DecimalParamater); currentSettings.Add(item); } } }
/// <summary> /// Convenience method to explicitly destroy the previously generated Items. /// </summary> /// <remarks> /// Override this method to implement a different way to dispose of an option item. /// Likely no action needed since destroying the dropdown list destroys all contained items as well. /// </remarks> /// <param name="item">The Item to destroy.</param> protected virtual void DestroyItem(DropdownMenuItem item) { }
/// <summary> /// Create a dropdown item based upon the item template. /// </summary> /// <remarks> /// Override this method to implement a different way to obtain an option item. /// The option item should correspond to the provided template DropdownItem and its GameObject, equivalent to instantiating a copy of it. /// </remarks> /// <param name="itemTemplate">e template to create the option item from.</param> /// <returns>The created dropdown item component</returns> protected virtual DropdownMenuItem CreateItem(DropdownMenuItem itemTemplate) { return(Instantiate(itemTemplate)); }
private void SetupTemplate() { validTemplate = false; if (!_template) { Debug.LogError("The dropdown template is not assigned. The template needs to be assigned and must have a child GameObject with a Toggle component serving as the item.", this); return; } GameObject templateGo = _template.gameObject; templateGo.SetActive(true); Button itemButton = _template.GetComponentInChildren <Button>(); validTemplate = true; if (!itemButton || itemButton.transform == template) { validTemplate = false; Debug.LogError("The dropdown template is not valid. The template must have a child GameObject with a Toggle component serving as the item.", template); } else if (!(itemButton.transform.parent is RectTransform)) { validTemplate = false; Debug.LogError("The dropdown template is not valid. The child GameObject with a Toggle component (the item) must have a RectTransform on its parent.", template); } else if (itemText != null && !itemText.transform.IsChildOf(itemButton.transform)) { validTemplate = false; Debug.LogError("The dropdown template is not valid. The Item Text must be on the item GameObject or children of it.", template); } else if (itemImage != null && !itemImage.transform.IsChildOf(itemButton.transform)) { validTemplate = false; Debug.LogError("The dropdown template is not valid. The Item Image must be on the item GameObject or children of it.", template); } if (!validTemplate) { templateGo.SetActive(false); return; } DropdownMenuItem item = itemButton.gameObject.AddComponent <DropdownMenuItem>(); item.text = _itemText; item.image = _itemImage; item.button = itemButton; item.rectTransform = (RectTransform)itemButton.transform; // Find the Canvas that this dropdown is a part of Canvas parentCanvas = null; Transform parentTransform = _template.parent; while (parentTransform != null) { parentCanvas = parentTransform.GetComponent <Canvas>(); if (parentCanvas != null) { break; } parentTransform = parentTransform.parent; } Canvas popupCanvas = GetOrAddComponent <Canvas>(templateGo); popupCanvas.overrideSorting = true; popupCanvas.sortingOrder = 30000; // If we have a parent canvas, apply the same raycasters as the parent for consistency. if (parentCanvas != null) { Component[] components = parentCanvas.GetComponents <BaseRaycaster>(); for (int i = 0; i < components.Length; i++) { Type raycasterType = components[i].GetType(); if (templateGo.GetComponent(raycasterType) == null) { templateGo.AddComponent(raycasterType); } } } else { GetOrAddComponent <GraphicRaycaster>(templateGo); } GetOrAddComponent <CanvasGroup>(templateGo); templateGo.SetActive(false); validTemplate = true; }
/// <summary> /// Show the dropdown. /// /// Plan for dropdown scrolling to ensure dropdown is contained within screen. /// /// We assume the Canvas is the screen that the dropdown must be kept inside. /// This is always valid for screen space canvas modes. /// For world space canvases we don't know how it's used, but it could be e.g. for an in-game monitor. /// We consider it a fair constraint that the canvas must be big enough to contain dropdowns. /// </summary> public void Show(bool forceUpdate = false) { if (!forceUpdate && (!IsActive() || !IsInteractable() || _dropdown != null)) { return; } // Get root Canvas. var list = ListPool <Canvas> .Get(); gameObject.GetComponentsInParent(false, list); if (list.Count == 0) { return; } // case 1064466 rootCanvas should be last element returned by GetComponentsInParent() Canvas rootCanvas = list[list.Count - 1]; for (int i = 0; i < list.Count; i++) { if (list[i].isRootCanvas) { rootCanvas = list[i]; break; } } ListPool <Canvas> .Release(list); if (!validTemplate) { SetupTemplate(); if (!validTemplate) { return; } } _template.gameObject.SetActive(true); // popupCanvas used to assume the root canvas had the default sorting Layer, next line fixes (case 958281 - [UI] Dropdown list does not copy the parent canvas layer when the panel is opened) _template.GetComponent <Canvas>().sortingLayerID = rootCanvas.sortingLayerID; // Instantiate the drop-down template _dropdown = CreateDropdownList(_template.gameObject); _dropdown.name = "Dropdown List"; _dropdown.SetActive(true); // Make drop-down RectTransform have same values as original. RectTransform dropdownRectTransform = _dropdown.transform as RectTransform; dropdownRectTransform.SetParent(_template.transform.parent, false); // Instantiate the drop-down list items // Find the dropdown item and disable it. DropdownMenuItem itemTemplate = _dropdown.GetComponentInChildren <DropdownMenuItem>(); GameObject content = itemTemplate.rectTransform.parent.gameObject; RectTransform contentRectTransform = content.transform as RectTransform; itemTemplate.rectTransform.gameObject.SetActive(true); // Get the rects of the dropdown and item Rect dropdownContentRect = contentRectTransform.rect; Rect itemTemplateRect = itemTemplate.rectTransform.rect; // Calculate the visual offset between the item's edges and the background's edges Vector2 offsetMin = itemTemplateRect.min - dropdownContentRect.min + (Vector2)itemTemplate.rectTransform.localPosition; Vector2 offsetMax = itemTemplateRect.max - dropdownContentRect.max + (Vector2)itemTemplate.rectTransform.localPosition; Vector2 itemSize = itemTemplateRect.size; _itemList.Clear(); Button prev = null; for (int i = 0; i < options.Count; ++i) { OptionData data = options[i]; DropdownMenuItem item = AddItem(data, itemTemplate, _itemList); if (item == null) { continue; } // Automatically set up a toggle state change listener item.button.onClick.AddListener(() => OnSelectItem(item.button)); // Automatically set up explicit navigation if (prev != null) { Navigation prevNav = prev.navigation; Navigation toggleNav = item.button.navigation; prevNav.mode = Navigation.Mode.Explicit; toggleNav.mode = Navigation.Mode.Explicit; prevNav.selectOnDown = item.button; prevNav.selectOnRight = item.button; toggleNav.selectOnLeft = prev; toggleNav.selectOnUp = prev; prev.navigation = prevNav; item.button.navigation = toggleNav; } prev = item.button; } // Reposition all items now that all of them have been added Vector2 sizeDelta = contentRectTransform.sizeDelta; sizeDelta.y = itemSize.y * _itemList.Count + offsetMin.y - offsetMax.y; contentRectTransform.sizeDelta = sizeDelta; float extraSpace = dropdownRectTransform.rect.height - contentRectTransform.rect.height; if (extraSpace > 0) { dropdownRectTransform.sizeDelta = new Vector2(dropdownRectTransform.sizeDelta.x, dropdownRectTransform.sizeDelta.y - extraSpace); } // Invert anchoring and position if dropdown is partially or fully outside of canvas rect. // Typically this will have the effect of placing the dropdown above the button instead of below, // but it works as inversion regardless of initial setup. Vector3[] corners = new Vector3[4]; dropdownRectTransform.GetWorldCorners(corners); RectTransform rootCanvasRectTransform = rootCanvas.transform as RectTransform; Rect rootCanvasRect = rootCanvasRectTransform.rect; for (int axis = 0; axis < 2; axis++) { bool outside = false; for (int i = 0; i < 4; i++) { Vector3 corner = rootCanvasRectTransform.InverseTransformPoint(corners[i]); if ((corner[axis] < rootCanvasRect.min[axis] && !Mathf.Approximately(corner[axis], rootCanvasRect.min[axis])) || (corner[axis] > rootCanvasRect.max[axis] && !Mathf.Approximately(corner[axis], rootCanvasRect.max[axis]))) { outside = true; break; } } if (outside) { RectTransformUtility.FlipLayoutOnAxis(dropdownRectTransform, axis, false, false); } } for (int i = 0; i < _itemList.Count; i++) { RectTransform itemRect = _itemList[i].rectTransform; itemRect.anchorMin = new Vector2(itemRect.anchorMin.x, 0); itemRect.anchorMax = new Vector2(itemRect.anchorMax.x, 0); itemRect.anchoredPosition = new Vector2(itemRect.anchoredPosition.x, offsetMin.y + itemSize.y * (_itemList.Count - 1 - i) + itemSize.y * itemRect.pivot.y); itemRect.sizeDelta = new Vector2(itemRect.sizeDelta.x, itemSize.y); } // Fade in the popup AlphaFadeList(_alphaFadeSpeed, 0f, 1f); // Make drop-down template and item template inactive _template.gameObject.SetActive(false); itemTemplate.gameObject.SetActive(false); _blocker = CreateBlocker(rootCanvas); }
private void SetDefaultItem(VisualElement ve, DropdownMenuItem item, string[] path, int level) { ve.Q <Label>(name: "text").text = path[level]; }