Search Unity

Input field - different color for words (code highlighting)

Discussion in 'UGUI & TextMesh Pro' started by denkun, Apr 9, 2017.

  1. denkun

    denkun

    Joined:
    Apr 9, 2017
    Posts:
    5
    I need to highlight user input code. I tried using rich text, but when you try to modify the text you modify color tags too. So is there a way to change the color of word without tags, or a way to ignore them?
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    You'd have to write your own parser. TextMeshPro is a free asset, and will soon be integrated into Unity's engine. For now, you can download the asset and add it to your project.
    You could try that and / or give a little more detail on what you're looking for/situation.
     
  3. denkun

    denkun

    Joined:
    Apr 9, 2017
    Posts:
    5
    Ok, thanks, TextMeshPro is realy good, but it seems to do strange thing to caret position: after inserting color tags it put caret at the begining of a word, and after that you cant put any character until you move caret by arrow keys. I tried to store caret position before inserting and restore after, it fixed unability to put characters, but the caret still is before the word, any idea why?

    Code (CSharp):
    1. public void BeautifyText()
    2.     {
    3.         string toB = text.GetComponent<TMP_InputField>().text;
    4.         int pos = text.GetComponent<TMP_InputField>().caretPosition;
    5.  
    6.         toB = System.Text.RegularExpressions.Regex.Replace(toB, "<[^>]*>", "", RegexOptions.Singleline);
    7.         toB = System.Text.RegularExpressions.Regex.Replace(toB, "for", "<color=#0000ffff>for</color>", RegexOptions.Singleline);
    8.  
    9.  
    10.         text.GetComponent<TMP_InputField>().text = toB;
    11.         text.GetComponent<TMP_InputField>().caretPosition = pos;
    12.     }
     

    Attached Files:

  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I'm not sure if that is the most efficient means, if you are checking the entire text after every entry/character, .. but that is sort of another story.
    As for the caret position, I guess it's because of how you change it in there that its position isn't updated.
    Right now, you're storing the caret position before the update, but you do not increment it if the text is found/altered. Right?
     
  5. denkun

    denkun

    Joined:
    Apr 9, 2017
    Posts:
    5
    This just for test i will alter it later.
    Yeah, but i checked the caret positions after word with and without insertion of tags - its the same. And i already tried increment the position and it does basicly doesnt work cause even if i add number of characters in tag + in word (actualy no matter how many i add) caret still appear at the beggining of the word.
     
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    This is what I did to get it working:
    This is in the function you posted (or part of it, not replacing it)
    Code (CSharp):
    1.    string s = "<color=#0000ffff>for</color>";
    2.         int len = s.Length;
    3.  
    4.         GetComponent<TMP_InputField>().text = toB;
    5.         StartCoroutine(moveCaret(pos + len));
    6.     }
    7.     IEnumerator moveCaret(int pos)
    8.     {
    9.         yield return new WaitForEndOfFrame();
    10.         GetComponent<TMP_InputField>().caretPosition = pos;
    11.     }
    It's snipped to the relevant bits :)
     
  7. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    lol then there is this even easier method :)
    just add : GetComponent<TMP_InputField>().MoveTextEnd(false);
    after you set the .text portion.
    Remove the coroutine ;)
     
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    And one last follow up.: my 2 above examples work in their own right, but when I tried to uncheck "allow rich text editing" (so users wouldn't see the color codes when backspacing, for example), then it wouldn't advance all the way to the end.
    I tried a few things, and what worked was firing the coroutine (which didn't need an int parameter anymore), yielding until the end of the frame, and then calling the method " MoveTextEnd(false)" and that worked every time in my tests.
     
  9. denkun

    denkun

    Joined:
    Apr 9, 2017
    Posts:
    5
    Yeah, it works, but there is slight problem - you cant modify the code after you write it cause it always jumps at the end of string.
     
  10. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Ya, I can imagine that.. um.. from where (/when) are you calling your method to adjust the text? After each character is inputted? Is there in-place editing (ie: can they skip backwords by clicking?) Gets more complicated, depending on the situation...
     
  11. denkun

    denkun

    Joined:
    Apr 9, 2017
    Posts:
    5
    I call it every time enter or space pressed (actually i dont think it matter)
    Sorry english isn't my native language, i did not understand the question.

    Actually highlighting with tags is pretty complicated, is there another methods, something like shaders or something?
     
  12. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Yes, sorry , it is a bit complicated. I do not know of any other means. Maybe someone else does.
    TextMeshPro does have word counting, though I've only looked at it briefly. If you could utilize that, you might be able to adjust your function to look at only the most recent word, and if it matches a word you want to highlight, then you change it , otherwise you don't. This way you are only working on 1 word at a time, instead of parsing the entire entry. It would also mean that you would have finer control over the caret's position at any given time/update.
     
  13. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Just chiming in...

    The Input Field has both caretPosition and stringPosition as well as selectionAnchorPosition and selectionStringAnchorPosition. The stringPosition takes into consideration the hidden rich text tags while the caretPosition only the visible text.

    If you are inserting new text (tags) the caret position should not change while the stringPosition would most certainly need to be set correctly.

    I have not tried changing / inserting new text in the Input Field manually but this should work. Hopefully with this additional information you'll be able to make this work.

    As suggested, you can also get information from textInfo.wordInfo[] which once the text has been processed / rendered will contain the updated information for each word. When inserting text via scripting, you might need to use .ForceMeshUpdate() to force the textInfo and text data to be updated.
     
  14. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Oh, nice. That's a nice bit of added information, that I will hopefully remember in the future :)
     
  15. Alex_Sour

    Alex_Sour

    Joined:
    Feb 2, 2018
    Posts:
    5
    Can anyone share the finished code?
     
  16. Parker_May

    Parker_May

    Joined:
    Dec 4, 2023
    Posts:
    1
    If anyone else finds this, I found a solution that might work (The loop is not needed but my project required looping through a list of words)
    Also the numbers being added on lines 18 and 22 come from the number of characters in the strings used to highlight the text, which includes the color ("<color=red>" and "</color>")
    Code (CSharp):
    1. //Get Caret Position Before Replacement
    2. int CurrentCaretPos = CurrentAnswerField.stringPosition;
    3. //Get The Number of Previously Replaced Items
    4. int ColoredIndexes = Regex.Matches(CurrentAnswerField.text, "<color=red>").Count;
    5.  
    6. //Reset Red Words
    7. CurrentAnswerField.text = CurrentAnswerField.text.Replace("<color=red>", "");
    8. CurrentAnswerField.text = CurrentAnswerField.text.Replace("</color>", "");
    9.  
    10. //Replace Words
    11. foreach (ThesaurusWords Current in ThesaurusWords_List)
    12. {
    13.     CurrentAnswerField.text = Regex.Replace(CurrentAnswerField.text, Current.Word, "<color=red>" + Current.Word + "</color>", RegexOptions.IgnoreCase);
    14.  
    15.     int HighlightDiff = (Regex.Matches(CurrentAnswerField.text, "<color=red>").Count - ColoredIndexes);
    16.     if (HighlightDiff > 0)
    17.     {
    18.         CurrentCaretPos += (19 * HighlightDiff);
    19.     }
    20.     else
    21.     {
    22.         CurrentCaretPos += (11 * HighlightDiff);
    23.     }
    24. }
    25. //Reset Cursor Position
    26. CurrentAnswerField.caretPosition = CurrentCaretPos;
    27. CurrentAnswerField.stringPosition = CurrentCaretPos;
     
    Last edited: Dec 4, 2023