Source code 10: InputField
InputField is the input box that provides users with input text content. It is an important interactive means. For example, it is commonly used to enter user name, password and so on.
public class InputField : Selectable, IUpdateSelectedHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler, ISubmitHandler, ICanvasElement, ILayoutElement { ... }
The implementation of InputField class is not explained in the previous article.
protected override void OnEnable() { base.OnEnable(); if (m_Text == null) m_Text = string.Empty; m_DrawStart = 0; m_DrawEnd = m_Text.Length; // If we have a cached renderer then we had OnDisable called so just restore the material. if (m_CachedInputRenderer != null) m_CachedInputRenderer.SetMaterial(m_TextComponent.GetModifiedMaterial(Graphic.defaultGraphicMaterial), Texture2D.whiteTexture); if (m_TextComponent != null) { m_TextComponent.RegisterDirtyVerticesCallback(MarkGeometryAsDirty); m_TextComponent.RegisterDirtyVerticesCallback(UpdateLabel); m_TextComponent.RegisterDirtyMaterialCallback(UpdateCaretMaterial); UpdateLabel(); } }
OnEnable component just activated
1. Set the start and end positions of input text drawing
2. Add two RegisterDirtyVerticesCallback events, MarkGeometryAsDirty and UpdateLabel, to the text component
Registerdirtymaterialcallcallback was called and UpdateCaretMaterial callback was added
Finally, call UpdateLable.
private void MarkGeometryAsDirty() { #if UNITY_EDITOR if (!Application.isPlaying || UnityEditor.PrefabUtility.IsPartOfPrefabAsset(gameObject)) return; #endif CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); }
Mark geometry asdirty registers himself in the canvas update registry to the graphics reconstruction sequence. (analysis in later articles)
The UpdateLable method is mainly used to update the display when entering
/// </summary> protected void UpdateLabel() { if (m_TextComponent != null && m_TextComponent.font != null && !m_PreventFontCallback) { m_PreventFontCallback = true; //Get all displayed text strings //When there is IME combined string, the combined string needs to be accessed and calculated according to the cursor position //https://docs.unity3d.com/cn/2018.4/ScriptReference/Input-compositionString.html string fullText; if (EventSystem.current != null && gameObject == EventSystem.current.currentSelectedGameObject && compositionString.Length > 0) fullText = text.Substring(0, m_CaretPosition) + compositionString + text.Substring(m_CaretPosition); else fullText = text; //For password type input, use * instead of input string processed; if (inputType == InputType.Password) processed = new string(asteriskChar, fullText.Length); else processed = fullText; //Display Placeholder without input bool isEmpty = string.IsNullOrEmpty(fullText); if (m_Placeholder != null) m_Placeholder.enabled = isEmpty; // If not currently editing the text, set the visible range to the whole text. // The UpdateLabel method will then truncate it to the part that fits inside the Text area. // We can't do this when text is being edited since it would discard the current scroll, // which is defined by means of the m_DrawStart and m_DrawEnd indices. if (!m_AllowInput) { m_DrawStart = 0; m_DrawEnd = m_Text.Length; } if (!isEmpty) { //Get Text generation settings (Text component function) // Determine what will actually fit into the given line Vector2 extents = m_TextComponent.rectTransform.rect.size; var settings = m_TextComponent.GetGenerationSettings(extents); settings.generateOutOfBounds = true; //Generate vertex data and text string data of Mesh, cachedInputTextGenerator.PopulateWithErrors(processed, settings, gameObject); //Calculate m according to TextGenerator and caretPos (cursor position)_ Drawstart and m_DrawEnd, and take the substring of the string according to these two values, and set the cursor to be visible (using CO process flashing). SetDrawRangeToContainCaretPosition(caretSelectPositionInternal); //Gets the string that can actually be drawn and displayed processed = processed.Substring(m_DrawStart, Mathf.Min(m_DrawEnd, processed.Length) - m_DrawStart); //Set the cursor to be visible (using CO process flashing). SetCaretVisible(); } //The processed string is registered in the graph reconstruction list to the Text component m_TextComponent.text = processed; MarkGeometryAsDirty(); m_PreventFontCallback = false; } }
private void UpdateCaretMaterial() { if (m_TextComponent != null && m_CachedInputRenderer != null) m_CachedInputRenderer.SetMaterial(m_TextComponent.GetModifiedMaterial(Graphic.defaultGraphicMaterial), Texture2D.whiteTexture); }
- Call m_TextComponent.GetModifiedMaterial(Graphic.defaultGraphicMaterial), Texture2D.whiteTexture); Create a default material
- Call m_CachedInputRenderer.SetMaterial assigns the created material to the cursor and updates the material of the cursor
public void DeactivateInputField() { // Not activated do nothing. if (!m_AllowInput) return; m_HasDoneFocusTransition = false; m_AllowInput = false; if (m_Placeholder != null) m_Placeholder.enabled = string.IsNullOrEmpty(m_Text); if (m_TextComponent != null && IsInteractable()) { if (m_WasCanceled) text = m_OriginalText; SendOnSubmit(); if (m_Keyboard != null) { m_Keyboard.active = false; m_Keyboard = null; } m_CaretPosition = m_CaretSelectPosition = 0; if (input != null) input.imeCompositionMode = IMECompositionMode.Auto; } MarkGeometryAsDirty(); }
The disable Input event is usually called when Input ends or Focus disable is lost
- Set m_Keyboard.active is false,
- Set the cursor position to 0,
- If IsInteractable is true, call SendOnSubmit and send onEndEdit event, onEndEdit Invoke(m_Text),
- Finally, call MarkGeometryAsDirty to wait for reconstruction.
public void ActivateInputField() { if (m_TextComponent == null || m_TextComponent.font == null || !IsActive() || !IsInteractable()) return; if (isFocused) { if (m_Keyboard != null && !m_Keyboard.active) { m_Keyboard.active = true; m_Keyboard.text = m_Text; } } m_ShouldActivateNextUpdate = true; }
Activate input, usually activate the keyboard for input when clicking
public override void OnSelect(BaseEventData eventData) { base.OnSelect(eventData); if (shouldActivateOnSelect) ActivateInputField(); }
Override OnSelect in Selectable to activate keyboard input
public virtual void OnPointerClick(PointerEventData eventData) { if (eventData.button != PointerEventData.InputButton.Left) return; ActivateInputField(); }
As above, activate keyboard input when clicked
public override void OnDeselect(BaseEventData eventData) { DeactivateInputField(); base.OnDeselect(eventData); }
Uncheck disable input
public virtual void OnUpdateSelected(BaseEventData eventData) { if (!isFocused) return; bool consumedEvent = false; while (Event.PopEvent(m_ProcessingEvent)) { if (m_ProcessingEvent.rawType == EventType.KeyDown) { consumedEvent = true; var shouldContinue = KeyPressed(m_ProcessingEvent); if (shouldContinue == EditState.Finish) { DeactivateInputField(); break; } } switch (m_ProcessingEvent.type) { case EventType.ValidateCommand: case EventType.ExecuteCommand: switch (m_ProcessingEvent.commandName) { case "SelectAll": SelectAll(); consumedEvent = true; break; } break; } } if (consumedEvent) UpdateLabel(); eventData.Use(); }
The method of implementing IUpdateSelectedHandler is that the StandaloneInputModule detects whether there is a selected object to send events every frame
This is mainly used to handle a series of keyboard input operations, such as backspace key, copy and paste (Ctrl+ c Ctrl+v).
private void OnFillVBO(Mesh vbo) { using (var helper = new VertexHelper()) { if (!isFocused) { helper.FillMesh(vbo); return; } Vector2 roundingOffset = m_TextComponent.PixelAdjustPoint(Vector2.zero); if (!hasSelection) GenerateCaret(helper, roundingOffset); else GenerateHighlight(helper, roundingOffset); helper.FillMesh(vbo); } }
If no area is selected, call GenerateCaret to generate the Mesh of the cursor; otherwise, call GenerateHightlight to generate the Mesh of the selected area. Then call VertexHelper.. Fillmesh, fill Mesh.
summary
Inputfile source code involves a lot of things related to displaying keyboard operation, which really looks messy. However, it contains many features and functions of UGUI, which is worth pondering carefully
Reference article: https://zhuanlan.zhihu.com/p/340600190
Later, we will start to analyze and compare the important content, and the display core of Graphic UGUI