Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Text field with popup text field

Discussion in 'UI Toolkit' started by maxxa05, Feb 18, 2020.

  1. maxxa05

    maxxa05

    Joined:
    Nov 17, 2012
    Posts:
    186
    Anybody knows if there's a simple way to have a text field with a search popup in UIElements? I'm looking for something like UnityEditorJunkie SearchableEnum, but for text fields in UI Elements. I checked at ToolbarPopupSearchField, but it's not the same behaviour I'm looking for.

    I may end up trying to build one, but it seems like something that could already exist somewhere, so I thought I'd ask here first.
     
  2. V0odo0

    V0odo0

    Joined:
    Jan 8, 2012
    Posts:
    328
    There is no such built-in control as I know. Here is my solution with button but you can add the text field in front of the button to allow user either input the text or select the value from list.
    Code (UXML):
    1. <engine:VisualElement class="unity-base-field">
    2.   <engine:Label text="Culture" class="unity-base-field__label"/>
    3.   <engine:Button name="LanguageCultureButton" text="Pick" class="unity-enum-field__input flexGrow1 margin0">
    4.     <engine:VisualElement class="unity-enum-field__arrow enumArrow"/>
    5.   </engine:Button>
    6. </engine:VisualElement>
    Code (USS):
    1. .margin0{
    2.     margin: 0;
    3. }
    4.  
    5. .flexGrow1{
    6.     flex-grow: 1;
    7. }
    8.  
    9. .enumArrow{
    10.     position: absolute;
    11.     right: 1px;
    12. }
    Make your own PopupWindowContent and Show it with provided Visual Element's worldBound rect. Inside OnGUI I've used IMGUI SearchField and custom TreeView controls. You can end up with UIElements SearchField and ListView instead.
     
    Threeyes and Xarbrough like this.
  3. unity_zIYWJyNr2AiCPA

    unity_zIYWJyNr2AiCPA

    Joined:
    Jan 5, 2020
    Posts:
    9
    An impl for this might look like:

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. using UnityEditor;
    7. using UnityEngine;
    8. using UnityEngine.UIElements;
    9. using XRTK.UIElements.Attributes;
    10. using XRTK.UIElements.Interfaces;
    11. using PopupWindow = UnityEditor.PopupWindow;
    12.  
    13. namespace XRTK.UIElements.Components
    14. {
    15.     public abstract class PopupElement : VisualElement, IPopup
    16.     {
    17.         /// <summary>
    18.         /// Initial Rect bounds.
    19.         /// </summary>
    20.         /// <remarks>(0, 0, 0, 0) for (x, y, width, height)</remarks>
    21.         /// <remarks>(250, 250) for (250, 250, 200 (default), 200 (default))</remarks>
    22.         public Rect initialRect { get; protected set; }
    23.  
    24.         public new class UxmlTraits : VisualElement.UxmlTraits
    25.         {
    26.             public UxmlRectAttributeDescription m_initialRect = new UxmlRectAttributeDescription();
    27.  
    28.             /// <summary>
    29.             /// Support adding any kind of element for an inline popup option.
    30.             /// </summary>
    31.             public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
    32.             {
    33.                 get { yield return new UxmlChildElementDescription(typeof(VisualElement)); }
    34.             }
    35.  
    36.             public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
    37.             {
    38.                 base.Init(ve, bag, cc);
    39.  
    40.                 var popup = (PopupElement) ve;
    41.                 popup.initialRect = m_initialRect.GetValueFromBag(bag, cc);
    42.             }
    43.         }
    44.  
    45.         public virtual void OnOpen(EditorWindow window)
    46.         {
    47.             window.rootVisualElement.Add(this);
    48.         }
    49.  
    50.         public virtual void OnClose() { }
    51.  
    52.         public virtual void Show(Rect rect, Vector2 windowSize)
    53.         {
    54.             PopupWindow.Show(rect, new PopupWindowWrapper(this, windowSize));
    55.         }
    56.     }
    57.  
    58.     internal sealed class PopupWindowWrapper : PopupWindowContent
    59.     {
    60.         private PopupElement container;
    61.         private Vector2 windowSize;
    62.         public override void OnGUI(Rect rect) { }
    63.         internal PopupWindowWrapper(PopupElement ve, Vector2 ws)
    64.         { container = ve; windowSize = ws; }
    65.         public override void OnOpen()
    66.         { container.OnOpen(editorWindow); }
    67.         public override void OnClose()
    68.         { container.OnClose(); }
    69.         public override Vector2 GetWindowSize() => windowSize;
    70.     }
    71. }
     
  4. cdr9042

    cdr9042

    Joined:
    Apr 22, 2018
    Posts:
    175
    what's class IPopup and UxmlRectAttributeDescription?
     
  5. cdr9042

    cdr9042

    Joined:
    Apr 22, 2018
    Posts:
    175
    I made a script from this

    https://gist.github.com/cdr9042/a8b89bb9c7b5ca309b3b6ad770f8a03e

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using System.Linq;
    3. using UnityEditor;
    4. using UnityEditor.IMGUI.Controls;
    5. using UnityEngine;
    6.  
    7. public class SearchablePopupContent : PopupWindowContent
    8. {
    9.     private SerializedProperty property;
    10.     private SearchField searchField;
    11.     private MyTreeView treeView;
    12.     private List<string> data;
    13.     private bool focused;
    14.  
    15.     public SearchablePopupContent(SerializedProperty property, string[] data)
    16.     {
    17.         this.property = property;
    18.         this.data = data.ToList();
    19.         searchField = new SearchField();
    20.         treeView = new MyTreeView(new TreeViewState(), this.data, property, this);
    21.         treeView.Reload();
    22.         searchField.SetFocus();
    23.         searchField.downOrUpArrowKeyPressed += OnArrowKeyPressed;
    24.     }
    25.  
    26.     private void OnArrowKeyPressed()
    27.     {
    28.         treeView.SetFocusAndEnsureSelectedItem();
    29.     }
    30.  
    31.     public override void OnGUI(Rect rect)
    32.     {
    33.         Rect searchRect = new Rect(rect.x, rect.y, rect.width, 25);
    34.         treeView.searchString = searchField.OnGUI(searchRect, treeView.searchString);
    35.  
    36.         Rect treeViewRect = new Rect(rect.x, rect.y + 25, rect.width, rect.height - 25);
    37.         treeView.OnGUI(treeViewRect);
    38.        
    39.         if (Event.current.type == EventType.KeyUp && Event.current.keyCode == KeyCode.Return)
    40.         {
    41.             treeView.SelectItem(0);
    42.         }
    43.     }
    44.  
    45.     private class MyTreeView : TreeView
    46.     {
    47.         private List<string> data;
    48.         private SerializedProperty property;
    49.         private PopupWindowContent parentWindow;
    50.         private IList<TreeViewItem> filteredItems;
    51.  
    52.         public MyTreeView(TreeViewState state, List<string> data, SerializedProperty property, PopupWindowContent parentWindow) :
    53.             base(state)
    54.         {
    55.             this.data = data;
    56.             this.property = property;
    57.             this.parentWindow = parentWindow;
    58.         }
    59.  
    60.         protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
    61.         {
    62.             filteredItems = new List<TreeViewItem>();
    63.  
    64.             for (int i = 0; i < data.Count; i++)
    65.             {
    66.                 if (string.IsNullOrEmpty(searchString) || data[i].Contains(searchString))
    67.                 {
    68.                     filteredItems.Add(new TreeViewItem { id = i, depth = 1, displayName = data[i] });
    69.                 }
    70.             }
    71.  
    72.             SetupParentsAndChildrenFromDepths(root, filteredItems);
    73.  
    74.             return filteredItems;
    75.         }
    76.  
    77.         protected override void SingleClickedItem(int id)
    78.         {
    79.             base.SingleClickedItem(id);
    80.             SelectItem(id);
    81.             // var item = FindItem(id, rootItem);
    82.             // OnSelectItem(item.displayName);
    83.         }
    84.  
    85.         protected override TreeViewItem BuildRoot()
    86.         {
    87.             var root = new TreeViewItem { id = 0, depth = -1, displayName = data[0] };
    88.             var allItems = new List<TreeViewItem>();
    89.  
    90.             for (int i = 0; i < data.Count; i++)
    91.             {
    92.                 allItems.Add(new TreeViewItem { id = i + 1, depth = 0, displayName = data[i] });
    93.             }
    94.  
    95.             SetupParentsAndChildrenFromDepths(root, allItems);
    96.  
    97.             return root;
    98.         }
    99.  
    100.         public override void OnGUI(Rect rect)
    101.         {
    102.             base.OnGUI(rect);
    103.             if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
    104.             {
    105.                 // Set the value of the property when the Return key is pressed
    106.                 var selecteds = (IEnumerable<int>)GetSelection();
    107.                 if (selecteds.Count() > 0)
    108.                 {
    109.                     int id = selecteds.First();
    110.                     SelectItem(id);
    111.                 }
    112.             }
    113.         }
    114.  
    115.         public void SelectItem(int id)
    116.         {
    117.             // Debug.Log($"selecting:{id}");
    118.             var item = FindItem(id, rootItem);
    119.             // var item = filteredItems[id];
    120.             // Debug.Log($"selected:{item.displayName}");
    121.             OnSelectItem(item.displayName);
    122.         }
    123.  
    124.         void OnSelectItem(string value)
    125.         {
    126.             property.stringValue = value;
    127.             property.serializedObject.ApplyModifiedProperties();
    128.             parentWindow.editorWindow.Close();
    129.         }
    130.     }
    131. }