Search Unity

TextField cannt input chinese

Discussion in 'UI Toolkit' started by wang37921, Jan 25, 2019.

  1. wang37921

    wang37921

    Joined:
    Aug 1, 2014
    Posts:
    102
    Use Google's Pinyin ime.
     
  2. jeanfrancois_unity

    jeanfrancois_unity

    Unity Technologies

    Joined:
    Oct 24, 2018
    Posts:
    4
    Can you confirm the Unity version you are using and that this behaviour is with UIElements TextField ?
     
  3. wang37921

    wang37921

    Joined:
    Aug 1, 2014
    Posts:
    102
    Unity 2018.3.0f2
     
  4. Seto

    Seto

    Joined:
    Oct 10, 2010
    Posts:
    243
    The same here.
    2019.1.0b6
     
  5. Seto

    Seto

    Joined:
    Oct 10, 2010
    Posts:
    243
    @jeanfrancois_unity When will it be resolved? It's the main issue keep us away from it. Not only Chinese, but also Japanese can't be inputted.
     
  6. benoitd_unity

    benoitd_unity

    Unity Technologies

    Joined:
    Jan 2, 2018
    Posts:
    331
    It's in the backlog but sadly we have no ETA.
     
  7. Seto

    Seto

    Joined:
    Oct 10, 2010
    Posts:
    243
    It's just in the backlog or it's being fixed? If it's not fixed. I can't even use it for real projects.
     
  8. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    We're looking into it as a bug and planning to fix it but the issue seems to affect the Editor at large and require coordination with some other teams.
     
  9. Seto

    Seto

    Joined:
    Oct 10, 2010
    Posts:
    243
    I finally find out a workaround for it. Could you please apply TextField by default?
    Code (CSharp):
    1.             m_TextField = new TextField();
    2.             m_TextField.RegisterCallback<FocusInEvent>(evt => { Input.imeCompositionMode = IMECompositionMode.On; });
    3.             m_TextField.RegisterCallback<FocusOutEvent>(evt => { Input.imeCompositionMode = IMECompositionMode.Auto; });
     
    wang37921 and YuGu_Zhang like this.
  10. wang37921

    wang37921

    Joined:
    Aug 1, 2014
    Posts:
    102
    Seto found the problem, could fix it?

    blow code is modify base on UnityReferenceSource branch 2019.3
    style base on exist code.
    insert line 309
    insert line 707~715

    Code (CSharp):
    1. // Unity C# reference source
    2. // Copyright (c) Unity Technologies. For terms of use, see
    3. // https://unity3d.com/legal/licenses/Unity_Reference_Only_License
    4.  
    5. using System;
    6.  
    7. namespace UnityEngine.UIElements
    8. {
    9.     internal interface ITextInputField : IEventHandler, ITextElement
    10.     {
    11.         bool hasFocus { get; }
    12.  
    13.         bool doubleClickSelectsWord { get; }
    14.         bool tripleClickSelectsLine { get; }
    15.  
    16.         bool isReadOnly { get; }
    17.  
    18.         bool isDelayed { get; }
    19.  
    20.         bool isPasswordField { get; }
    21.  
    22.         TextEditorEngine editorEngine { get; }
    23.  
    24.         void SyncTextEngine();
    25.         bool AcceptCharacter(char c);
    26.         string CullString(string s);
    27.         void UpdateText(string value);
    28.         void UpdateValueFromText();
    29.     }
    30.  
    31.     public abstract class TextInputBaseField<TValueType> : BaseField<TValueType>
    32.     {
    33.         static CustomStyleProperty<Color> s_SelectionColorProperty = new CustomStyleProperty<Color>("--unity-selection-color");
    34.         static CustomStyleProperty<Color> s_CursorColorProperty = new CustomStyleProperty<Color>("--unity-cursor-color");
    35.  
    36.         public new class UxmlTraits : BaseFieldTraits<string, UxmlStringAttributeDescription>
    37.         {
    38.             UxmlIntAttributeDescription m_MaxLength = new UxmlIntAttributeDescription { name = "max-length", obsoleteNames = new[] { "maxLength" }, defaultValue = kMaxLengthNone };
    39.             UxmlBoolAttributeDescription m_Password = new UxmlBoolAttributeDescription { name = "password" };
    40.             UxmlStringAttributeDescription m_MaskCharacter = new UxmlStringAttributeDescription { name = "mask-character", obsoleteNames = new[] { "maskCharacter" }, defaultValue = kMaskCharDefault.ToString()};
    41.             UxmlStringAttributeDescription m_Text = new UxmlStringAttributeDescription { name = "text" };
    42.             UxmlBoolAttributeDescription m_IsReadOnly = new UxmlBoolAttributeDescription { name = "readonly" };
    43.  
    44.             public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
    45.             {
    46.                 base.Init(ve, bag, cc);
    47.  
    48.                 var field = ((TextInputBaseField<TValueType>)ve);
    49.                 field.maxLength = m_MaxLength.GetValueFromBag(bag, cc);
    50.                 field.isPasswordField = m_Password.GetValueFromBag(bag, cc);
    51.                 field.isReadOnly = m_IsReadOnly.GetValueFromBag(bag, cc);
    52.                 string maskCharacter = m_MaskCharacter.GetValueFromBag(bag, cc);
    53.                 if (maskCharacter != null && maskCharacter.Length > 0)
    54.                 {
    55.                     field.maskChar = maskCharacter[0];
    56.                 }
    57.                 field.text = m_Text.GetValueFromBag(bag, cc);
    58.             }
    59.         }
    60.  
    61.         TextInputBase m_TextInputBase;
    62.         protected TextInputBase textInputBase => m_TextInputBase;
    63.  
    64.         internal const int kMaxLengthNone = -1;
    65.         internal const char kMaskCharDefault = '*';
    66.  
    67.         internal TextHandle textHandle { get; private set; } = TextHandle.New();
    68.  
    69.         public new static readonly string ussClassName = "unity-base-text-field";
    70.         public new static readonly string labelUssClassName = ussClassName + "__label";
    71.         public new static readonly string inputUssClassName = ussClassName + "__input";
    72.  
    73.         public static readonly string textInputUssName = "unity-text-input";
    74.  
    75.  
    76.         public string text
    77.         {
    78.             get { return m_TextInputBase.text; }
    79.             protected set
    80.             {
    81.                 m_TextInputBase.text = value;
    82.             }
    83.         }
    84.  
    85.         public bool isReadOnly
    86.         {
    87.             get { return m_TextInputBase.isReadOnly; }
    88.             set { m_TextInputBase.isReadOnly = value; }
    89.         }
    90.  
    91.         // Password field (indirectly lossy behaviour when activated via multiline)
    92.         public bool isPasswordField
    93.         {
    94.             get { return m_TextInputBase.isPasswordField; }
    95.             set { m_TextInputBase.isPasswordField = value; }
    96.         }
    97.  
    98.         public Color selectionColor => m_TextInputBase.selectionColor;
    99.         public Color cursorColor => m_TextInputBase.cursorColor;
    100.  
    101.  
    102.         public int cursorIndex => m_TextInputBase.cursorIndex;
    103.         public int selectIndex => m_TextInputBase.selectIndex;
    104.         public int maxLength
    105.         {
    106.             get { return m_TextInputBase.maxLength; }
    107.             set { m_TextInputBase.maxLength = value; }
    108.         }
    109.  
    110.         public bool doubleClickSelectsWord
    111.         {
    112.             get { return m_TextInputBase.doubleClickSelectsWord; }
    113.             set { m_TextInputBase.doubleClickSelectsWord = value; }
    114.         }
    115.         public bool tripleClickSelectsLine
    116.         {
    117.             get { return m_TextInputBase.tripleClickSelectsLine; }
    118.             set { m_TextInputBase.tripleClickSelectsLine = value; }
    119.         }
    120.  
    121.         public bool isDelayed
    122.         {
    123.             get { return m_TextInputBase.isDelayed; }
    124.             set { m_TextInputBase.isDelayed = value; }
    125.         }
    126.  
    127.         public char maskChar
    128.         {
    129.             get { return m_TextInputBase.maskChar; }
    130.             set { m_TextInputBase.maskChar = value; }
    131.         }
    132.  
    133.         /* internal for VisualTree tests */
    134.         internal TextEditorEventHandler editorEventHandler => m_TextInputBase.editorEventHandler;
    135.  
    136.         /* internal for VisualTree tests */
    137.         internal TextEditorEngine editorEngine  => m_TextInputBase.editorEngine;
    138.  
    139.         internal bool hasFocus => m_TextInputBase.hasFocus;
    140.  
    141.         public void SelectAll()
    142.         {
    143.             m_TextInputBase.SelectAll();
    144.         }
    145.  
    146.         internal void SyncTextEngine()
    147.         {
    148.             m_TextInputBase.SyncTextEngine();
    149.         }
    150.  
    151.         internal void DrawWithTextSelectionAndCursor(MeshGenerationContext mgc, string newText)
    152.         {
    153.             m_TextInputBase.DrawWithTextSelectionAndCursor(mgc, newText, scaledPixelsPerPoint);
    154.         }
    155.  
    156.         protected TextInputBaseField(int maxLength, char maskChar, TextInputBase textInputBase)
    157.             : this(null, maxLength, maskChar, textInputBase) {}
    158.  
    159.         protected TextInputBaseField(string label, int maxLength, char maskChar, TextInputBase textInputBase)
    160.             : base(label, textInputBase)
    161.         {
    162.             tabIndex = 0;
    163.             delegatesFocus = false;
    164.  
    165.             AddToClassList(ussClassName);
    166.             labelElement.AddToClassList(labelUssClassName);
    167.             visualInput.AddToClassList(inputUssClassName);
    168.  
    169.             m_TextInputBase = textInputBase;
    170.             m_TextInputBase.maxLength = maxLength;
    171.             m_TextInputBase.maskChar = maskChar;
    172.  
    173.             RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
    174.         }
    175.  
    176.         private void OnAttachToPanel(AttachToPanelEvent e)
    177.         {
    178.             var h = textHandle;
    179.             h.useLegacy = e.destinationPanel.contextType == ContextType.Editor;
    180.             textHandle = h;
    181.         }
    182.  
    183.         protected override void ExecuteDefaultActionAtTarget(EventBase evt)
    184.         {
    185.             base.ExecuteDefaultActionAtTarget(evt);
    186.  
    187.             if (evt == null)
    188.             {
    189.                 return;
    190.             }
    191.  
    192.             if (evt.eventTypeId == KeyDownEvent.TypeId())
    193.             {
    194.                 KeyDownEvent keyDownEvt = evt as KeyDownEvent;
    195.  
    196.                 // We must handle the ETX (char 3) or the \n instead of the KeypadEnter or Return because the focus will
    197.                 //     have the drawback of having the second event to be handled by the focused field.
    198.                 if ((keyDownEvt?.character == 3) ||     // KeyCode.KeypadEnter
    199.                     (keyDownEvt?.character == '\n'))    // KeyCode.Return
    200.                 {
    201.                     visualInput?.Focus();
    202.                 }
    203.             }
    204.         }
    205.  
    206.         protected abstract class TextInputBase : VisualElement, ITextInputField
    207.         {
    208.             string m_OriginalText;
    209.  
    210.             void SaveValueAndText()
    211.             {
    212.                 // When getting the FocusIn, we must keep the value in case of Escape...
    213.                 m_OriginalText = text;
    214.             }
    215.  
    216.             void RestoreValueAndText()
    217.             {
    218.                 text = m_OriginalText;
    219.             }
    220.  
    221.             public void SelectAll()
    222.             {
    223.                 editorEngine?.SelectAll();
    224.             }
    225.  
    226.             internal void SelectNone()
    227.             {
    228.                 editorEngine?.SelectNone();
    229.             }
    230.  
    231.             private void UpdateText(string value)
    232.             {
    233.                 if (text != value)
    234.                 {
    235.                     // Setting the VisualElement text here cause a repaint since it dirty the layout flag.
    236.                     using (InputEvent evt = InputEvent.GetPooled(text, value))
    237.                     {
    238.                         evt.target = parent;
    239.                         text = value;
    240.                         parent?.SendEvent(evt);
    241.                     }
    242.                 }
    243.             }
    244.  
    245.             protected virtual TValueType StringToValue(string str)
    246.             {
    247.                 throw new NotSupportedException();
    248.             }
    249.  
    250.             internal void UpdateValueFromText()
    251.             {
    252.                 var newValue = StringToValue(text);
    253.                 TextInputBaseField<TValueType> parentTextField = (TextInputBaseField<TValueType>)parent;
    254.                 parentTextField.value = newValue;
    255.             }
    256.  
    257.             public int cursorIndex
    258.             {
    259.                 get { return editorEngine.cursorIndex; }
    260.             }
    261.  
    262.             public int selectIndex
    263.             {
    264.                 get { return editorEngine.selectIndex; }
    265.             }
    266.  
    267.             bool ITextInputField.isReadOnly => isReadOnly;
    268.  
    269.             public bool isReadOnly { get; set; }
    270.             public int maxLength { get; set; }
    271.             public char maskChar { get; set; }
    272.  
    273.             public virtual bool isPasswordField { get; set; }
    274.  
    275.             public bool doubleClickSelectsWord { get; set; }
    276.             public bool tripleClickSelectsLine { get; set; }
    277.             internal bool isDelayed { get; set; }
    278.  
    279.             internal bool isDragging { get; set; }
    280.  
    281.             bool touchScreenTextField
    282.             {
    283.                 get { return TouchScreenKeyboard.isSupported && !TouchScreenKeyboard.isInPlaceEditingAllowed; }
    284.             }
    285.  
    286.  
    287.             Color m_SelectionColor = Color.clear;
    288.             Color m_CursorColor = Color.grey;
    289.  
    290.             public Color selectionColor => m_SelectionColor;
    291.             public Color cursorColor => m_CursorColor;
    292.  
    293.  
    294.             internal bool hasFocus
    295.             {
    296.                 get { return elementPanel != null && elementPanel.focusController.GetLeafFocusedElement() == this; }
    297.             }
    298.  
    299.             /* internal for VisualTree tests */
    300.             internal TextEditorEventHandler editorEventHandler { get; private set; }
    301.  
    302.             /* internal for VisualTree tests */
    303.             internal TextEditorEngine editorEngine { get; private set; }
    304.  
    305.             private TextHandle m_TextHandle = TextHandle.New();
    306.  
    307.             private string m_Text;
    308.  
    309.             private IMECompositionMode _savedInputImeCompositionMode;
    310.  
    311.             public string text
    312.             {
    313.                 get { return m_Text; }
    314.                 set
    315.                 {
    316.                     if (m_Text == value)
    317.                         return;
    318.  
    319.                     m_Text = value;
    320.                     editorEngine.text = value;
    321.                     IncrementVersion(VersionChangeType.Layout | VersionChangeType.Repaint);
    322.                 }
    323.             }
    324.  
    325.             internal TextInputBase()
    326.             {
    327.                 isReadOnly = false;
    328.                 focusable = true;
    329.  
    330.                 AddToClassList(inputUssClassName);
    331.                 m_Text = string.Empty;
    332.                 name = TextField.textInputUssName;
    333.  
    334.                 requireMeasureFunction = true;
    335.  
    336.                 editorEngine = new TextEditorEngine(OnDetectFocusChange, OnCursorIndexChange);
    337.  
    338.                 if (touchScreenTextField)
    339.                 {
    340.                     editorEventHandler = new TouchScreenTextEditorEventHandler(editorEngine, this);
    341.                 }
    342.                 else
    343.                 {
    344.                     // TODO: Default values should come from GUI.skin.settings
    345.                     doubleClickSelectsWord = true;
    346.                     tripleClickSelectsLine = true;
    347.  
    348.                     editorEventHandler = new KeyboardTextEditorEventHandler(editorEngine, this);
    349.                 }
    350.  
    351.                 // Make the editor style unique across all textfields
    352.                 editorEngine.style = new GUIStyle(editorEngine.style);
    353.  
    354.                 RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
    355.                 RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
    356.                 this.generateVisualContent += OnGenerateVisualContent;
    357.             }
    358.  
    359.             DropdownMenuAction.Status CutCopyActionStatus(DropdownMenuAction a)
    360.             {
    361.                 return (editorEngine.hasSelection && !isPasswordField) ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled;
    362.             }
    363.  
    364.             DropdownMenuAction.Status PasteActionStatus(DropdownMenuAction a)
    365.             {
    366.                 return (editorEngine.CanPaste() ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);
    367.             }
    368.  
    369.             void ProcessMenuCommand(string command)
    370.             {
    371.                 using (ExecuteCommandEvent evt = ExecuteCommandEvent.GetPooled(command))
    372.                 {
    373.                     evt.target = this;
    374.                     SendEvent(evt);
    375.                 }
    376.             }
    377.  
    378.             void Cut(DropdownMenuAction a)
    379.             {
    380.                 ProcessMenuCommand(EventCommandNames.Cut);
    381.             }
    382.  
    383.             void Copy(DropdownMenuAction a)
    384.             {
    385.                 ProcessMenuCommand(EventCommandNames.Copy);
    386.             }
    387.  
    388.             void Paste(DropdownMenuAction a)
    389.             {
    390.                 ProcessMenuCommand(EventCommandNames.Paste);
    391.             }
    392.  
    393.             private void OnCustomStyleResolved(CustomStyleResolvedEvent e)
    394.             {
    395.                 Color selectionValue = Color.clear;
    396.                 Color cursorValue = Color.clear;
    397.  
    398.                 ICustomStyle customStyle = e.customStyle;
    399.                 if (customStyle.TryGetValue(s_SelectionColorProperty, out selectionValue))
    400.                     m_SelectionColor = selectionValue;
    401.  
    402.                 if (customStyle.TryGetValue(s_CursorColorProperty, out cursorValue))
    403.                     m_CursorColor = cursorValue;
    404.  
    405.                 SyncGUIStyle(this, editorEngine.style);
    406.             }
    407.  
    408.             private void OnAttachToPanel(AttachToPanelEvent e)
    409.             {
    410.                 m_TextHandle.useLegacy = e.destinationPanel.contextType == ContextType.Editor;
    411.             }
    412.  
    413.             internal virtual void SyncTextEngine()
    414.             {
    415.                 editorEngine.text = CullString(text);
    416.  
    417.                 editorEngine.SaveBackup();
    418.  
    419.                 editorEngine.position = layout;
    420.  
    421.                 editorEngine.DetectFocusChange();
    422.             }
    423.  
    424.             internal string CullString(string s)
    425.             {
    426.                 if (maxLength >= 0 && s != null && s.Length > maxLength)
    427.                     return s.Substring(0, maxLength);
    428.                 return s;
    429.             }
    430.  
    431.             internal void OnGenerateVisualContent(MeshGenerationContext mgc)
    432.             {
    433.                 string drawText = text;
    434.                 if (isPasswordField)
    435.                 {
    436.                     drawText = "".PadRight(text.Length, maskChar);
    437.                 }
    438.  
    439.                 if (touchScreenTextField)
    440.                 {
    441.                     var touchScreenEditor = editorEventHandler as TouchScreenTextEditorEventHandler;
    442.                     if (touchScreenEditor != null)
    443.                     {
    444.                         mgc.Text(MeshGenerationContextUtils.TextParams.MakeStyleBased(this, drawText), m_TextHandle, scaledPixelsPerPoint);
    445.                     }
    446.                 }
    447.                 else
    448.                 {
    449.                     if (!hasFocus)
    450.                     {
    451.                         mgc.Text(MeshGenerationContextUtils.TextParams.MakeStyleBased(this, drawText), m_TextHandle, scaledPixelsPerPoint);
    452.                     }
    453.                     else
    454.                     {
    455.                         DrawWithTextSelectionAndCursor(mgc, drawText, scaledPixelsPerPoint);
    456.                     }
    457.                 }
    458.             }
    459.  
    460.             internal void DrawWithTextSelectionAndCursor(MeshGenerationContext mgc, string newText, float pixelsPerPoint)
    461.             {
    462.                 var playmodeTintColor = panel.contextType == ContextType.Editor
    463.                     ? UIElementsUtility.editorPlayModeTintColor
    464.                     : Color.white;
    465.  
    466.                 var keyboardTextEditor = editorEventHandler as KeyboardTextEditorEventHandler;
    467.                 if (keyboardTextEditor == null)
    468.                     return;
    469.  
    470.                 keyboardTextEditor.PreDrawCursor(newText);
    471.  
    472.                 int cursorIndex = editorEngine.cursorIndex;
    473.                 int selectIndex = editorEngine.selectIndex;
    474.                 Rect localPosition = editorEngine.localPosition;
    475.                 var scrollOffset = editorEngine.scrollOffset;
    476.  
    477.                 float textScaling = TextHandle.ComputeTextScaling(worldTransform, pixelsPerPoint);
    478.  
    479.                 var textParams = MeshGenerationContextUtils.TextParams.MakeStyleBased(this, text);
    480.                 textParams.text = " ";
    481.                 textParams.wordWrapWidth = 0.0f;
    482.                 textParams.wordWrap = false;
    483.  
    484.                 float lineHeight = m_TextHandle.ComputeTextHeight(textParams, textScaling);
    485.  
    486.                 float wordWrapWidth = 0.0f;
    487.  
    488.                 // Make sure to take into account the word wrap style...
    489.                 if (editorEngine.multiline && (resolvedStyle.whiteSpace == WhiteSpace.Normal))
    490.                 {
    491.                     wordWrapWidth = contentRect.width;
    492.  
    493.                     // Since the wrapping is enabled, there is no need to offset the text... It will always fit the space on screen !
    494.                     scrollOffset = Vector2.zero;
    495.                 }
    496.  
    497.                 GUIUtility.compositionCursorPos = editorEngine.graphicalCursorPos - scrollOffset +
    498.                     new Vector2(localPosition.x, localPosition.y + lineHeight);
    499.  
    500.                 Color drawCursorColor = cursorColor;
    501.  
    502.                 int selectionEndIndex = string.IsNullOrEmpty(GUIUtility.compositionString)
    503.                     ? selectIndex
    504.                     : cursorIndex + GUIUtility.compositionString.Length;
    505.  
    506.                 CursorPositionStylePainterParameters cursorParams;
    507.  
    508.                 // Draw highlighted section, if any
    509.                 if ((cursorIndex != selectionEndIndex) && !isDragging)
    510.                 {
    511.                     int min = cursorIndex < selectionEndIndex ? cursorIndex : selectionEndIndex;
    512.                     int max = cursorIndex > selectionEndIndex ? cursorIndex : selectionEndIndex;
    513.  
    514.                     cursorParams = CursorPositionStylePainterParameters.GetDefault(this, text);
    515.                     cursorParams.text = editorEngine.text;
    516.                     cursorParams.wordWrapWidth = wordWrapWidth;
    517.                     cursorParams.cursorIndex = min;
    518.  
    519.                     Vector2 minPos = m_TextHandle.GetCursorPosition(cursorParams, textScaling);
    520.  
    521.                     cursorParams.cursorIndex = max;
    522.                     Vector2 maxPos = m_TextHandle.GetCursorPosition(cursorParams, textScaling);
    523.  
    524.                     minPos -= scrollOffset;
    525.                     maxPos -= scrollOffset;
    526.  
    527.                     if (Mathf.Approximately(minPos.y, maxPos.y))
    528.                     {
    529.                         mgc.Rectangle(new MeshGenerationContextUtils.RectangleParams()
    530.                         {
    531.                             rect = new Rect(minPos.x, minPos.y, maxPos.x - minPos.x, lineHeight),
    532.                             color = selectionColor,
    533.                             playmodeTintColor = playmodeTintColor
    534.                         });
    535.                     }
    536.                     else
    537.                     {
    538.                         // Draw first line
    539.                         mgc.Rectangle(new MeshGenerationContextUtils.RectangleParams()
    540.                         {
    541.                             rect = new Rect(minPos.x, minPos.y, contentRect.xMax - minPos.x, lineHeight),
    542.                             color = selectionColor,
    543.                             playmodeTintColor = playmodeTintColor
    544.                         });
    545.  
    546.                         var inbetweenHeight = (maxPos.y - minPos.y) - lineHeight;
    547.                         if (inbetweenHeight > 0f)
    548.                         {
    549.                             // Draw all lines in-between
    550.                             mgc.Rectangle(new MeshGenerationContextUtils.RectangleParams()
    551.                             {
    552.                                 rect = new Rect(contentRect.xMin, minPos.y + lineHeight, contentRect.width, inbetweenHeight),
    553.                                 color = selectionColor,
    554.                                 playmodeTintColor = playmodeTintColor
    555.                             });
    556.                         }
    557.  
    558.                         // Draw last line if not empty
    559.                         if (maxPos.x != contentRect.x)
    560.                         {
    561.                             mgc.Rectangle(new MeshGenerationContextUtils.RectangleParams()
    562.                             {
    563.                                 rect = new Rect(contentRect.xMin, maxPos.y, maxPos.x, lineHeight),
    564.                                 color = selectionColor,
    565.                                 playmodeTintColor = playmodeTintColor
    566.                             });
    567.                         }
    568.                     }
    569.                 }
    570.  
    571.                 // Draw the text with the scroll offset
    572.                 if (!string.IsNullOrEmpty(editorEngine.text) && contentRect.width > 0.0f && contentRect.height > 0.0f)
    573.                 {
    574.                     textParams = MeshGenerationContextUtils.TextParams.MakeStyleBased(this, text);
    575.                     textParams.rect = new Rect(contentRect.x - scrollOffset.x, contentRect.y - scrollOffset.y, contentRect.width + scrollOffset.x, contentRect.height + scrollOffset.y);
    576.                     textParams.text = editorEngine.text;
    577.  
    578.                     mgc.Text(textParams, m_TextHandle, scaledPixelsPerPoint);
    579.                 }
    580.  
    581.                 // Draw the cursor
    582.                 if (!isReadOnly && !isDragging)
    583.                 {
    584.                     if (cursorIndex == selectionEndIndex && computedStyle.unityFont.value != null)
    585.                     {
    586.                         cursorParams = CursorPositionStylePainterParameters.GetDefault(this, text);
    587.                         cursorParams.text = editorEngine.text;
    588.                         cursorParams.wordWrapWidth = wordWrapWidth;
    589.                         cursorParams.cursorIndex = cursorIndex;
    590.  
    591.                         Vector2 cursorPosition = m_TextHandle.GetCursorPosition(cursorParams, textScaling);
    592.                         cursorPosition -= scrollOffset;
    593.                         mgc.Rectangle(new MeshGenerationContextUtils.RectangleParams
    594.                         {
    595.                             rect = new Rect(cursorPosition.x, cursorPosition.y, 1f, lineHeight),
    596.                             color = drawCursorColor,
    597.                             playmodeTintColor = playmodeTintColor
    598.                         });
    599.                     }
    600.  
    601.                     // Draw alternate cursor, if any
    602.                     if (editorEngine.altCursorPosition != -1)
    603.                     {
    604.                         cursorParams = CursorPositionStylePainterParameters.GetDefault(this, text);
    605.                         cursorParams.text = editorEngine.text.Substring(0, editorEngine.altCursorPosition);
    606.                         cursorParams.wordWrapWidth = wordWrapWidth;
    607.                         cursorParams.cursorIndex = editorEngine.altCursorPosition;
    608.  
    609.                         Vector2 altCursorPosition = m_TextHandle.GetCursorPosition(cursorParams, textScaling);
    610.                         altCursorPosition -= scrollOffset;
    611.                         mgc.Rectangle(new MeshGenerationContextUtils.RectangleParams
    612.                         {
    613.                             rect = new Rect(altCursorPosition.x, altCursorPosition.y, 1f, lineHeight),
    614.                             color = drawCursorColor,
    615.                             playmodeTintColor = playmodeTintColor
    616.                         });
    617.                     }
    618.                 }
    619.  
    620.                 keyboardTextEditor.PostDrawCursor();
    621.             }
    622.  
    623.             internal virtual bool AcceptCharacter(char c)
    624.             {
    625.                 // when readonly, we do not accept any character
    626.                 return !isReadOnly;
    627.             }
    628.  
    629.             protected virtual void BuildContextualMenu(ContextualMenuPopulateEvent evt)
    630.             {
    631.                 if (evt?.target is TextInputBase)
    632.                 {
    633.                     if (!isReadOnly)
    634.                     {
    635.                         evt.menu.AppendAction("Cut", Cut, CutCopyActionStatus);
    636.                     }
    637.                     evt.menu.AppendAction("Copy", Copy, CutCopyActionStatus);
    638.                     if (!isReadOnly)
    639.                     {
    640.                         evt.menu.AppendAction("Paste", Paste, PasteActionStatus);
    641.                     }
    642.                 }
    643.             }
    644.  
    645.             private void OnDetectFocusChange()
    646.             {
    647.                 if (editorEngine.m_HasFocus && !hasFocus)
    648.                 {
    649.                     editorEngine.OnFocus();
    650.                 }
    651.  
    652.                 if (!editorEngine.m_HasFocus && hasFocus)
    653.                     editorEngine.OnLostFocus();
    654.             }
    655.  
    656.             private void OnCursorIndexChange()
    657.             {
    658.                 IncrementVersion(VersionChangeType.Repaint);
    659.             }
    660.  
    661.             protected internal override Vector2 DoMeasure(float desiredWidth, MeasureMode widthMode, float desiredHeight, MeasureMode heightMode)
    662.             {
    663.                 // If the text is empty, we should make sure it returns at least the height/width of 1 character...
    664.                 var textToUse = m_Text;
    665.                 if (string.IsNullOrEmpty(textToUse))
    666.                 {
    667.                     textToUse = " ";
    668.                 }
    669.  
    670.                 return TextElement.MeasureVisualElementTextSize(this, textToUse, desiredWidth, widthMode, desiredHeight, heightMode, m_TextHandle);
    671.             }
    672.  
    673.             protected override void ExecuteDefaultActionAtTarget(EventBase evt)
    674.             {
    675.                 base.ExecuteDefaultActionAtTarget(evt);
    676.  
    677.                 if (elementPanel != null && elementPanel.contextualMenuManager != null)
    678.                 {
    679.                     elementPanel.contextualMenuManager.DisplayMenuIfEventMatches(evt, this);
    680.                 }
    681.  
    682.                 if (evt?.eventTypeId == ContextualMenuPopulateEvent.TypeId())
    683.                 {
    684.                     ContextualMenuPopulateEvent e = evt as ContextualMenuPopulateEvent;
    685.                     int count = e.menu.MenuItems().Count;
    686.                     BuildContextualMenu(e);
    687.  
    688.                     if (count > 0 && e.menu.MenuItems().Count > count)
    689.                     {
    690.                         e.menu.InsertSeparator(null, count);
    691.                     }
    692.                 }
    693.                 else if (evt.eventTypeId == FocusInEvent.TypeId())
    694.                 {
    695.                     SaveValueAndText();
    696.                 }
    697.                 else if (evt.eventTypeId == KeyDownEvent.TypeId())
    698.                 {
    699.                     KeyDownEvent keyDownEvt = evt as KeyDownEvent;
    700.  
    701.                     if (keyDownEvt?.keyCode == KeyCode.Escape)
    702.                     {
    703.                         RestoreValueAndText();
    704.                         parent.Focus();
    705.                     }
    706.                 }
    707.                 else if (evt.eventTypeId == FocusInEvent.TypeId())
    708.                 {
    709.                     _savedInputImeCompositionMode = Input.imeCompositionMode;
    710.                     Input.imeCompositionMode = IMECompositionMode.On;
    711.                 }
    712.                 else if (evt.eventTypeId == FocusOutEvent.TypeId())
    713.                 {
    714.                     Input.imeCompositionMode = _savedInputImeCompositionMode;
    715.                 }
    716.  
    717.                 editorEventHandler.ExecuteDefaultActionAtTarget(evt);
    718.             }
    719.  
    720.             protected override void ExecuteDefaultAction(EventBase evt)
    721.             {
    722.                 base.ExecuteDefaultAction(evt);
    723.  
    724.                 editorEventHandler.ExecuteDefaultAction(evt);
    725.             }
    726.  
    727.             bool ITextInputField.hasFocus => hasFocus;
    728.  
    729.             void ITextInputField.SyncTextEngine()
    730.             {
    731.                 SyncTextEngine();
    732.             }
    733.  
    734.             bool ITextInputField.AcceptCharacter(char c)
    735.             {
    736.                 return AcceptCharacter(c);
    737.             }
    738.  
    739.             string ITextInputField.CullString(string s)
    740.             {
    741.                 return CullString(s);
    742.             }
    743.  
    744.             void ITextInputField.UpdateText(string value)
    745.             {
    746.                 UpdateText(value);
    747.             }
    748.  
    749.             TextEditorEngine ITextInputField.editorEngine => editorEngine;
    750.  
    751.             bool ITextInputField.isDelayed => isDelayed;
    752.  
    753.             void ITextInputField.UpdateValueFromText()
    754.             {
    755.                 UpdateValueFromText();
    756.             }
    757.  
    758.             private void DeferGUIStyleRectSync()
    759.             {
    760.                 RegisterCallback<GeometryChangedEvent>(OnPercentResolved);
    761.             }
    762.  
    763.             private void OnPercentResolved(GeometryChangedEvent evt)
    764.             {
    765.                 UnregisterCallback<GeometryChangedEvent>(OnPercentResolved);
    766.  
    767.                 var guiStyle = editorEngine.style;
    768.                 int left = (int)resolvedStyle.marginLeft;
    769.                 int top = (int)resolvedStyle.marginTop;
    770.                 int right = (int)resolvedStyle.marginRight;
    771.                 int bottom = (int)resolvedStyle.marginBottom;
    772.                 AssignRect(guiStyle.margin, left, top, right, bottom);
    773.  
    774.                 left = (int)resolvedStyle.paddingLeft;
    775.                 top = (int)resolvedStyle.paddingTop;
    776.                 right = (int)resolvedStyle.paddingRight;
    777.                 bottom = (int)resolvedStyle.paddingBottom;
    778.                 AssignRect(guiStyle.padding, left, top, right, bottom);
    779.             }
    780.  
    781.             private static void SyncGUIStyle(TextInputBase textInput, GUIStyle style)
    782.             {
    783.                 var computedStyle = textInput.computedStyle;
    784.                 style.alignment = computedStyle.unityTextAlign.GetSpecifiedValueOrDefault(style.alignment);
    785.                 style.wordWrap = computedStyle.whiteSpace.specificity != StyleValueExtensions.UndefinedSpecificity
    786.                     ? computedStyle.whiteSpace.value == WhiteSpace.Normal
    787.                     : style.wordWrap;
    788.                 bool overflowVisible = computedStyle.overflow.specificity != StyleValueExtensions.UndefinedSpecificity
    789.                     ? computedStyle.overflow.value == Overflow.Visible
    790.                     : style.clipping == TextClipping.Overflow;
    791.                 style.clipping = overflowVisible ? TextClipping.Overflow : TextClipping.Clip;
    792.                 if (computedStyle.unityFont.value != null)
    793.                 {
    794.                     style.font = computedStyle.unityFont.value;
    795.                 }
    796.  
    797.                 style.fontSize = (int)computedStyle.fontSize.GetSpecifiedValueOrDefault((float)style.fontSize);
    798.                 style.fontStyle = computedStyle.unityFontStyleAndWeight.GetSpecifiedValueOrDefault(style.fontStyle);
    799.  
    800.                 int left = computedStyle.unitySliceLeft.value;
    801.                 int top = computedStyle.unitySliceTop.value;
    802.                 int right = computedStyle.unitySliceRight.value;
    803.                 int bottom = computedStyle.unitySliceBottom.value;
    804.                 AssignRect(style.border, left, top, right, bottom);
    805.  
    806.                 if (IsLayoutUsingPercent(textInput))
    807.                 {
    808.                     textInput.DeferGUIStyleRectSync();
    809.                 }
    810.                 else
    811.                 {
    812.                     left = (int)computedStyle.marginLeft.value.value;
    813.                     top = (int)computedStyle.marginTop.value.value;
    814.                     right = (int)computedStyle.marginRight.value.value;
    815.                     bottom = (int)computedStyle.marginBottom.value.value;
    816.                     AssignRect(style.margin, left, top, right, bottom);
    817.  
    818.                     left = (int)computedStyle.paddingLeft.value.value;
    819.                     top = (int)computedStyle.paddingTop.value.value;
    820.                     right = (int)computedStyle.paddingRight.value.value;
    821.                     bottom = (int)computedStyle.paddingBottom.value.value;
    822.                     AssignRect(style.padding, left, top, right, bottom);
    823.                 }
    824.             }
    825.  
    826.             private static bool IsLayoutUsingPercent(VisualElement ve)
    827.             {
    828.                 var computedStyle = ve.computedStyle;
    829.  
    830.                 // Margin
    831.                 if (computedStyle.marginLeft.value.unit == LengthUnit.Percent ||
    832.                     computedStyle.marginTop.value.unit == LengthUnit.Percent ||
    833.                     computedStyle.marginRight.value.unit == LengthUnit.Percent ||
    834.                     computedStyle.marginBottom.value.unit == LengthUnit.Percent)
    835.                     return true;
    836.  
    837.                 // Padding
    838.                 if (computedStyle.paddingLeft.value.unit == LengthUnit.Percent ||
    839.                     computedStyle.paddingTop.value.unit == LengthUnit.Percent ||
    840.                     computedStyle.paddingRight.value.unit == LengthUnit.Percent ||
    841.                     computedStyle.paddingBottom.value.unit == LengthUnit.Percent)
    842.                     return true;
    843.  
    844.                 return false;
    845.             }
    846.  
    847.             private static void AssignRect(RectOffset rect, int left, int top, int right, int bottom)
    848.             {
    849.                 rect.left = left;
    850.                 rect.top = top;
    851.                 rect.right = right;
    852.                 rect.bottom = bottom;
    853.             }
    854.         }
    855.     }
    856. }
    857.  
     
    Last edited: May 20, 2020
  11. Sangemdoko

    Sangemdoko

    Joined:
    Dec 15, 2013
    Posts:
    222
    Hi

    I'm working on an asset on the asset store and some of our clients have complained that they couldn't write in Chinese or Japanese in our text fields.

    I tried the both work arounds, neither worked for me. This is the class I created to replace the base TextField:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UIElements;
    3. /// <summary>
    4. /// A custom textfield that fixes a bug which hides 2-byte characters.
    5. /// </summary>
    6. public class UnicodeTextField : TextField
    7. {
    8.     private IMECompositionMode m_SavedInputImeCompositionMode;
    9.     public UnicodeTextField() : base()
    10.     {
    11.         RegisterCallback<FocusInEvent>(evt => { Input.imeCompositionMode = IMECompositionMode.On; });
    12.         RegisterCallback<FocusOutEvent>(evt => { Input.imeCompositionMode = IMECompositionMode.Auto; });
    13.     }
    14.     public UnicodeTextField(string name) : base(name)
    15.     {
    16.         RegisterCallback<FocusInEvent>(evt => { Input.imeCompositionMode = IMECompositionMode.On; });
    17.         RegisterCallback<FocusOutEvent>(evt => { Input.imeCompositionMode = IMECompositionMode.Auto; });
    18.     }
    19.  
    20.     /*protected override void ExecuteDefaultActionAtTarget(EventBase evt)
    21.     {
    22.         base.ExecuteDefaultActionAtTarget(evt);
    23.         if (evt?.eventTypeId == ContextualMenuPopulateEvent.TypeId())
    24.         { }
    25.         else if (evt.eventTypeId == FocusInEvent.TypeId())
    26.         { }
    27.         else if (evt.eventTypeId == KeyDownEvent.TypeId())
    28.         { }
    29.         else if (evt.eventTypeId == FocusInEvent.TypeId())
    30.         {
    31.             m_SavedInputImeCompositionMode = Input.imeCompositionMode;
    32.             Input.imeCompositionMode = IMECompositionMode.On;
    33.         }
    34.         else if (evt.eventTypeId == FocusOutEvent.TypeId())
    35.         {
    36.             Input.imeCompositionMode = m_SavedInputImeCompositionMode;
    37.         }
    38.     }*/
    39. }
    I'm using the default IME pad that comes with Windows 10 when you add Japanese as a language.
    Any news on a potential fix for this? Or another workaround? Or maybe the issue is the IME pad I'm using?

    Any help would be greatly appriciated

    EDIT: It works now, It seems closing and reopening the IME Pad and the custom Unity Editor refreshed something and now characters appear as they should
     
    Last edited: Jan 21, 2021
  12. Amane224

    Amane224

    Joined:
    May 4, 2016
    Posts:
    6
    Two years have passed, but no progress has been made. Are they going to let this bug continue?
     
  13. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,129
    Hi @antoine-unity. Any new progress update?
     
  14. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    We're actively working on the text field right now and there should be new stuff coming out at some point in the 2022 release cycle :cool:
     
  15. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,129
    @JuliaP_Unity Awesome. Btw will it backport to 2021 release cycle?
     
  16. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    It will not be backported as far as I know, it's a full new feature for 2022.
     
    Amane224 likes this.