Last active
December 30, 2021 12:35
-
-
Save studentutu/714d78ad6cebfe147bfa852fb7046a90 to your computer and use it in GitHub Desktop.
Unity Custom caret, set caret position from world position, from raycast. A script for Unity3D to customize the caret of a Text Mesh Pro Input Field game object.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using TMPro; | |
using UnityEngine; | |
using UnityEngine.UI; | |
using UnityEngine.EventSystems; | |
using UnityEngine.Scripting; | |
/// Should be Used in combination with the Second Transparent text ( substring of the inputfield text based on inputfield caret position) | |
/// This transparent text should use ContentSizeFitter + Horizontal Layout Group. Add Text Caret as child to the transparent text. | |
/// This way you will always have the same alighment as the original text. | |
/// Be sure to use Update function to set custom transparent text acording to new InputField caret Position (update function) | |
/// Add Separate Event Trigger with some UI Image Element On Top Of the Input Field. | |
/// Make sure anchors are the same for it as well as InputField Text. | |
/// Invoke OnClickMovePointerByEvenData from OnClick event | |
public class CustomUICaret : MonoBehaviour | |
{ | |
[SerializeField] private TMP_InputField _inputField; | |
public static Vector2 SetX(Vector2 vector, float value) | |
{ | |
return new Vector2(value, vector.y); | |
} | |
public static Vector2 SetY(Vector2 vector, float value) | |
{ | |
return new Vector2(vector.x, value); | |
} | |
public static Vector2 GetSize(RectTransform trans) | |
{ | |
return trans.rect.size; | |
} | |
public static Vector2 GetPixelFromWorldPoint(RectTransform rectTransform, Vector3 worldPoint) | |
{ | |
Vector3[] v = new Vector3[4]; | |
rectTransform.GetWorldCorners(v); | |
var loverLeft = v[0]; | |
var upperLeft = v[1]; | |
var upperRight = v[2]; | |
var worldDistanceWidth = (upperLeft - upperRight).magnitude; | |
var worldDistanceHeight = (upperLeft - loverLeft).magnitude; | |
Vector2 imagePos = Vector2.one * -1; | |
var size = GetSize(rectTransform); | |
// Assume lover left is (0,0) pixel | |
// Project on worldHeight | |
var vectorToWorldPoint = loverLeft - worldPoint; | |
var upperLeftPosition = upperLeft; | |
var projectOnHeight = Vector3.Project( | |
vectorToWorldPoint, | |
loverLeft - upperLeftPosition | |
); | |
// Project on worldWidth | |
var projectOnWidth = Vector3.Project( | |
vectorToWorldPoint, | |
upperLeftPosition - upperRight | |
); | |
// if using sqrt - we will not know if it is inside | |
var percentWidth = projectOnWidth.magnitude / worldDistanceWidth; | |
var percentHeight = projectOnHeight.magnitude / worldDistanceHeight; | |
if (percentWidth >= 0 && percentWidth <= 1) | |
{ | |
imagePos = SetX(imagePos,percentWidth * size.x); | |
} | |
if (percentHeight >= 0 && percentHeight <= 1) | |
{ | |
imagePos = SetY(imagePos,percentHeight * size.y); | |
} | |
return imagePos; | |
} | |
/// Invoke this method from OnClick on the event trigger | |
[Preserve] | |
public void OnClickMovePointerByEvenData(BaseEventData eventData) | |
{ | |
// Get World Point | |
var asPointerEventData = eventData as PointerEventData; | |
if (asPointerEventData == null) | |
{ | |
return; | |
} | |
// Transform Into Local Anchored Position | |
var positionWorld = asPointerEventData.pointerCurrentRaycast.worldPosition; | |
var fromExtension = GetPixelFromWorldPoint(_inputField.textViewport, positionWorld); | |
// find nearest | |
float currentX = 0; | |
int maxNumber = _inputField.text.Length; | |
var TextObject = _inputField.textComponent; | |
float constantValue = 0; | |
for (int i = 0; i < maxNumber; i++) | |
{ | |
if (i == 1) | |
{ | |
constantValue = TextObject.textInfo.characterInfo[i].xAdvance; | |
} | |
if (currentX >= fromExtension.x) | |
{ | |
_inputField.caretPosition = Mathf.Clamp(i - 1, 0, maxNumber); | |
return; | |
} | |
if (TextObject.textInfo.characterInfo.Length > i) | |
{ | |
currentX = TextObject.textInfo.characterInfo[i].xAdvance; | |
} | |
else | |
{ | |
currentX += constantValue; | |
} | |
} | |
_inputField.caretPosition = maxNumber; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this will set your caret into target world position ( raycast position)