So, thanks to @Kevin I have approached to custom solution, which I think is the best for me now. I have created FormBehaviour script, which I am linking to Form Container Game Object, where I have the array of inputs, and object for submit and cancel button (all assagnable from editor). And of course submit event, assignable.
Also the Tab navigation is less easier solved, on tab select I find in EventSystem, if is there selected game object and found next or previous selectable.
Here's my Behaviour code.
EDIT: the TabSelectable should be seperated to some custom script, for the scene. Especially when I will have more forms in one screen.
[Serializable]
public class SubmitForm : UnityEvent<GameObject, IDictionary<string, string>>
{
}
public class FormBehaviour : MonoBehaviour
{
public SubmitForm OnSubmit;
public GameObject[] Inputs;
public GameObject SubmitButton;
public GameObject CancelButton;
private readonly IDictionary<string, string> _values = new Dictionary<string, string>();
private EventSystem _eventSystem;
public void Start()
{
if (SubmitButton == null) throw new InvalidOperationException("The SubmitButton is not set.");
_eventSystem = EventSystem.current;
SubmitButton.GetComponent<Button>()
.onClick
.AddListener(Submit);
if (CancelButton != null)
{
// Add cancel listener
}
BindInputEvents();
}
public void Update()
{
if (Input.GetButtonUp("Submit") && gameObject.activeSelf)
{
Submit();
}
TabSelectable();
}
public void Submit()
{
OnSubmit.Invoke(gameObject, _values);
}
private void BindInputEvents()
{
foreach (var input in Inputs)
{
var inputField = input.GetComponent<TMP_InputField>();
if (inputField == null) continue;
_values.Add(input.name, "");
inputField.onValueChanged.AddListener(value =>
{
_values[input.name] = value;
});
}
}
private void TabSelectable()
{
if (!Input.GetKeyUp(KeyCode.Tab)) return;
Selectable element;
// Tab to previous element.
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
element = GetCurrentSelectable().FindSelectableOnUp();
}
// Tab to next element.
else
{
element = GetCurrentSelectable().FindSelectableOnDown();
}
if(element != null) element.Select();
}
private Selectable GetCurrentSelectable()
{
return _eventSystem.currentSelectedGameObject.GetComponent<Selectable>();
}
}