Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity: Dynamically update Dropdown list when opened while not losing focus on InputField

I'm creating a Dropdown with a searchbar on top. Basically, I put an Inputfield on top of the Dropdown.

When you click the Inputfield, the Dropdown opens and when you type something into it, the Dropdown should update its list dynamically.

Here's the problem I'm having, the Dropdown option changes in the inspector, however, it doesn't update the list in the scene.

When I do Dropdown.Hide() and then Dropdown.Show(), it updates but then I lose focus on the Inputfield. I can always just do Inputfield.Select() but then the whole Inputfield gets highlighted and you have to click the position you were at to be able to edit from where you were.

Any suggestions?

Edit Here is the code below. I took out all the unimportant things to make it as slim as possible to show here

public class SearchbarInput : InputField
    {
        private GameObject[] m_Ordnance;
        private Dropdown m_Dropdown;
        private SearchbarInput m_InputField;

        // Needed for basic inputfield functionality
        private List<string> m_OrdnanceNames = new List<string>(); // Used to display our data

        protected override void Start()
        {
            m_Ordnance = GetComponent<OrdnanceSelector>().m_Ordnance;

            // Get necessary components
            m_Dropdown = GetComponentInParent<Dropdown>();
            m_InputField = GetComponent<SearchbarInput>();
            m_InputField.gameObject.GetComponentInChildren<Text>().text = "Click here to select Ordnance";

            // Set InputField onValueChange listener
            m_InputField.onValueChanged.AddListener(OnInputValueChanged);

            // Add each ordnance name to our string list
            foreach (GameObject ordnance in m_Ordnance)
            {
                if (ordnance != null) m_OrdnanceNames.Add(ordnance.name);
            }
            if (m_OrdnanceNames.Count == 0) DisplayError("Ordnance were not added");
            else
            {
                ChangeDropdownOptions(m_OrdnanceNames);
                m_Dropdown.onValueChanged.AddListener((index) => OnDropdownItemClicked(index));
            }

            base.Start();
        }

        // When the InputField is selected
        public override void OnSelect(BaseEventData eventData)
        {
            base.OnSelect(eventData);
            Debug.Log("SearchbarInput selected");

            Dropdown parentDropdown = GetComponentInParent<Dropdown>();
            parentDropdown.Show();
        }

        // When the InputField is deselected
        public override void OnDeselect(BaseEventData eventData)
        {
            base.OnDeselect(eventData);
            Debug.Log("SearchbarInput deselected");
        }

        /// Displays items in list that are similar to what the user typed in the Input Field
        private void OnInputValueChanged(string typedText)
        {
            List<string> results = GetResults(typedText);
            ChangeDropdownOptions(results);
        }

        /// Get list of items that contains characters similar to input
        private List<string> GetResults(string input)
        {
            return m_OrdnanceNames.FindAll((str) => str.IndexOf(input) >= 0);
        }

        ///============================== Dropdown Methods===================================
        /// Called when the dropdown menu is clicked. Is set inside of scripts Start function
        public void OnDropdownItemClicked(int index)
        {
            // Get selected ordnance name
            string ordnanceName = m_Dropdown.options[index].text;
            m_InputField.text = ordnanceName;

            // Change AndyGenerator Prefab
            int indexOfOrdnance = m_OrdnanceNames.IndexOf(ordnanceName);
            //m_AndyScript.AndyPrefab = m_Ordnance[indexOfOrdnance]; <- Took out for StackOverflow to make as short as possible
        }

        /// Clears the dropdown options and add's options set inside of the list
        private void ChangeDropdownOptions(List<string> options)
        {
            m_Dropdown.ClearOptions();
            m_Dropdown.AddOptions(options);
        }

        ///============================== Error Method===================================
        /// Displays error inside of our InputField.
        private void DisplayError(string errorText)
        {
            Debug.Log("Searchbar.cs: " + errorText);
            // Decided to make it more obvious since this is absolutely needed and
            // it saves the headache of looking for an error
            m_InputField.text = "Error: " + errorText;
        }
    }
like image 900
Rayaarito Avatar asked Sep 18 '25 01:09

Rayaarito


2 Answers

To make sure the dropdown options list UI is up to date, you need to disable and enable back the component.

Unfortunately, this makes the dropdown "flicker". Look at the end for a "no-flicker" solution.

Here, I'm using a TextMeshPro dropdown.

Dropdown.ClearOptions();
Dropdown.AddOptions(options);

Dropdown.RefreshOptions();
InputField.Input.ActivateInputField();

With the following extension method:

/// <summary>
/// Call this after modifying options while the dropdown is displayed
/// to make sure the visual is up to date.
/// </summary>
public static void RefreshOptions(this TMPro.TMP_Dropdown dropdown)
{
    dropdown.enabled = false;
    dropdown.enabled = true;
    dropdown.Show();
}

enter image description here

EDIT :

I found a way to achieve this without any flickering. This involves getting rid of the Dropdown script and writing your own.

Basically, I took the same UI as for the dropdown, but I placed a VerticalLayoutGroup in the content of the ScrollRect.

I then wrote a custom script that:

  • Show the ScrollRect and fill the Layout with all the options when InputField.onSelect is triggered
  • Filter the options by hiding them when InputField.onValueChange is triggered (with throttle)
  • Close the ScrollRect when InputField.onEndEdit is triggered

And there is some important changes that also need to be made:

  • The ScrollRect should have a Canvas script with a dedicated Sort Order to be on top of the UI
  • The ScrollRect should have a GraphicsRaycaster component to be able to select the options in the Layout
  • Closing the ScrollRect on onEndEdit should be done with a delay. Otherwise the click event won't be evaluated by the buttons in your Layout.

enter image description here

like image 181
oktomus Avatar answered Sep 19 '25 13:09

oktomus


I dont know that answer, but I have a similar problem... I got this search bar working, with a dropdown showing dynamic values, but this dropdown only updates on the odd chars (on the first, third... not on the second, fourth...)

Here:

//call whenever the input field changes, even OR odd, its working
public void newSearchFieldValueChanged()
{
    //read the input field, ok...   
    searchText = newSearchField.text;

    //return when empty...
    if (string.IsNullOrEmpty(searchText)) return;

    //I need to hide the dropdown   
    dropdown.Hide();

    //clear its old options
    dropdown.ClearOptions();

    //this is a dictionary to fill the dropdown options, clear it
    dicTemp.Clear();

    //add a first empty value
    dicTemp.Add("", "0");

    //so I run for another dic, that dont change its original values 
    for (int i = 0; i < dic.Keys.Count; i++)
    {
        //if it contains in its keys the word typed in the search bar...
        if (dic.Keys.ElementAt(i).ToLower().Contains(searchText.ToLower()))
        {
            //I add it to the cleared dicTemp that will fill the dropdown options   
            dicTemp.Add(dic.Keys.ElementAt(i), dic.Values.ElementAt(i));
        }
    }

    //fill the dropdown options with the new dicTemp, each time something changes
    dropdown.AddOptions(dicTemp.Keys.ToList());

    //duh
    dropdown.Show();

    //keep the focus on input field to continue type (dropdown selected by mouse)
    newSearchField.ActivateInputField();
}

Again, it works on first letter, and third... but not on second, and fourth, the dropdown DOES NOT SHOW UP (besides the function IS CALLED everytime)...

like image 36
Cleber Tavares Jr. Avatar answered Sep 19 '25 13:09

Cleber Tavares Jr.