- Home /
Putting a multiline InputField in a Scroll Rect?
I'm working on making an in-game script editor for my game, and I've been trying to add a scroll bar to my multiline TextInput so that the line numbers and text input can scroll together. It works pretty good, but for some reason the Scroll Rect causes the InputField's caret and selection highlighting to get messed up when I add any more lines. Here's a .gif of the problem: http://i.imgur.com/h3DD72w.gifv
How can I accomplish this? What am I doing wrong?
Here's an album that shows my setup: http://imgur.com/a/mkuAO
EDIT: I'm still having this problem, any help would be appreciated.
kinda hard to tell without seeing your script, but it looks like while you hit enter (ending the line), it doesn't generate a new textfield for you to place your cursor in and remains in the original one opened.
I'm only using one InputField (the one from the recent UI stuff) set to multiline. I'm not using TextFields, or OnGUI() at all.
Hi! you found a solution for this problem ? I'm trying to do something like this ( script editor ), but also I have the same problem (Scroll in inputfield).
Yes I did! Here's an image of my hierarchy view:
You can see the scroll rect's Content is set to the Script Editor. The script editor has a script on it that extends InputField, and part of that script automatically resizes the InputField to fit the height of the text. I'm not sure if it's necessary to extend InputField though, I did that to solve some other problems I was having.
Here's the script: ScriptEditor.cs
public class ScriptEditor : InputField {
private RectTransform rectTransform;
protected override void Start() {
rectTransform = GetComponent<RectTransform>();
onValueChange.AddListener(new UnityEngine.Events.UnityAction<string>(ResizeInput));
}
// Resize input field as new lines get added
private void ResizeInput(string iText) {
string fullText = text;
Vector2 extents = textComponent.rectTransform.rect.size;
var settings = textComponent.GetGenerationSettings(extents);
settings.generateOutOfBounds = false;
var prefHeight = new TextGenerator().GetPreferredHeight(fullText, settings) + 16;
if(prefHeight > textComponent.rectTransform.rect.height - 16 || prefHeight < textComponent.rectTransform.rect.height + 16) {
rectTransform.sizeDelta = new Vector2(rectTransform.sizeDelta.x, prefHeight);
}
}
}
I've had a similar problem, and I used a similar solution with the preferred height of the input field's text component. But another problem I am having now is that when I highlight text, the highlight effect is not masked when the text is off screen. Have you encountered this problem, and if so, how have you fixed it?
Hi Shoffing,
I am very interesting by what you did!
Did you find any solution for color parsing? And did you do anything to make the code auto-indent itself?
Thanks,
@Shoffing, worked like a charm thanks, man! and indeed there was no need to make it an extension.
@tbll, I found solutions for both:
To auto-indent and line break I just grabbed the text from the input field and removed all \t
and \n
characters. And went trough the text placing line breaks and tabs where needed. The tricky thing here is to see if, and how much you have to move your caret. I also made a pass that handles horizontal wrapping in a way that preserves the indents when wrapping.
For color parsing: - Duplicated the Text Object from the input field and name one InputText (this should still be connected to the InputField) and the other OutputText. - $$anonymous$$ake the text color of InputText transparent - Enable rich text on the OutputText - Change the caret color on the InputField component
Now you can write a script that goes through your code and looks for parts of the text to highlight (using <color=#0000ff></color>
). After that just place the colored text in the OutputText.
Hope it helps
Ooahh !
Looks impressive and clever. I have to admit that I didn't get everything. Would you be willing to share your code and/or project ?
Thanks a lot in advance,
Thibault
Answer by illa3d · Feb 09, 2017 at 02:04 PM
I've had a simmilar issue and compiled a workaround until Unity fixes the InputField functionality. All the combinations of layous/content fitters didn't yield proper display of the last line. The only downside is that you can't fix the height of the input field without a ScrollRect component, also in the example.
Also, this example doesn't cover the ScrollRect following the caret nor the selection in the InputField, if someone does that please post it here! :)
HACKY SOLUTION:
InputFieldMod.cs - http://pastebin.com/AHggHUMX
Example - http://illa3d.com/unity/inputfield_mod_v1.0.unitypackage
Thank you so much for posting your solutions. I've been banging my head against the wall over a similar issue for a while now. You solution works well, scales easily, all while being simple to implement. If I had a cookie, I'd give it to you!
Answer by TruthVoyager · May 25, 2017 at 04:25 PM
I was having a similar problem. The solution I found was simply create a game-object, obj1, to assign as a parent to the game-object with the input-field component, obj2. Then have the scroll-rect move the parent, obj1, which contains the child with the input-field component, obj2.
--->ScrollRect
-------->Obj1 <---- ScrollRect moves this one
-------------->Obj2 <--- This one has input field component
Answer by SweatyChair · Nov 05, 2018 at 06:38 AM
This is my modified version of InputFieldMod, removing the highlight and take into account for canvas scale, etc:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using System.Collections;
namespace SweatyChair.UI
{
[ExecuteInEditMode]
[RequireComponent(typeof(InputField))]
public class InputFieldScroller : UIBehaviour
{
[Tooltip("The default row count in InputField, this will be ignored if a ScrollRect is assigned")]
[Range(1, 50)] [SerializeField] private int _minRowCount = 1;
[Tooltip("Scroll rect parent")]
[SerializeField] private ScrollRect _scrollRect = null;
private InputField _inputField;
private RectTransform _rectTransform;
// Layout
private LayoutElement _layoutElement;
private HorizontalOrVerticalLayoutGroup _parentLayoutGroup;
private Vector2 _scrollRectSize;
private float _canvasScaleFactor = 1;
protected override void Awake()
{
_inputField = GetComponent<InputField>();
_inputField.onValueChanged.AddListener(new UnityAction<string>(ResizeInput));
_rectTransform = GetComponent<RectTransform>();
CanvasScaler canvasScaler = GetComponentInParent<CanvasScaler>();
if (canvasScaler)
_canvasScaleFactor = canvasScaler.scaleFactor;
_layoutElement = GetComponent<LayoutElement>();
_parentLayoutGroup = transform.parent.GetComponent<HorizontalOrVerticalLayoutGroup>();
}
// Resize input field recttransform
private void ResizeInput()
{
ResizeInput(_inputField.text);
}
private void ResizeInput(string text)
{
// Current text settings
TextGenerationSettings settings = _inputField.textComponent.GetGenerationSettings(_inputField.textComponent.rectTransform.rect.size);
settings.generateOutOfBounds = false;
settings.scaleFactor = _canvasScaleFactor; // HACK: scale factor of settings not following the global scale factor... make sure it do
// Get text padding (min max vertical offset for size calculation)
float vecticalOffset = _inputField.placeholder.rectTransform.offsetMin.y - _inputField.placeholder.rectTransform.offsetMax.y;
// Preferred text rect height
float preferredHeight = (new TextGenerator().GetPreferredHeight(text, settings) / _canvasScaleFactor) + vecticalOffset + 10;
float minHeight;
// Default text rect height (fit to scroll parent or expand to fit text)
if (_scrollRect)
minHeight = _scrollRect.GetComponent<RectTransform>().rect.size.y;
else
minHeight = ((new TextGenerator().GetPreferredHeight("", settings) * _minRowCount) / _canvasScaleFactor) + vecticalOffset;
// Current text rect height
float currentHeight = _inputField.textComponent.rectTransform.rect.height;
// Force resize
if (Mathf.Abs(currentHeight - preferredHeight) > Mathf.Epsilon) {
float newHeight = Mathf.Max(preferredHeight, minHeight); // At least min height
if (_parentLayoutGroup && _layoutElement)
_layoutElement.preferredHeight = newHeight;
else
_rectTransform.sizeDelta = new Vector2(_rectTransform.rect.width, newHeight);
}
// Scroll to bottom if just added new line
if (gameObject.activeInHierarchy && _inputField.caretPosition == _inputField.text.Length && _inputField.text.Length > 0 && _inputField.text[_inputField.text.Length - 1] == '\n')
StartCoroutine(ScrollToBottomCoroutine());
}
// Update scroll rect position (after Layout was rebuilt)
private IEnumerator ScrollToBottomCoroutine()
{
yield return new WaitForEndOfFrame();
if (_scrollRect != null)
_scrollRect.verticalNormalizedPosition = 0;
}
}
}
Your answer
Follow this Question
Related Questions
Add scrollbar to multiline input field??? 3 Answers
Hover Over Input Field Before Inputting? 2 Answers
Drag on Scroll Rect Non Proportional 0 Answers
Masking GameObject in Canvas 1 Answer