Created
January 21, 2020 16:52
-
-
Save polerin/d93feca1f69534e1e64374324bbaf3cc to your computer and use it in GitHub Desktop.
Example UI Elements form structure -- simple "start game" menu
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 System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using Unity.UIElements.Runtime; | |
using UnityEngine.UIElements; | |
using Zenject; | |
using SMG.Common.Exceptions; | |
/// <summary> | |
/// Base utility class for user interface forms. | |
/// | |
/// This class is not intended for use in an ongoing | |
/// interface such as a HUD, but should instead be used | |
/// for enabling menus, settings forms, and other submission | |
/// based user interactions. | |
/// </summary> | |
namespace SMG.Common.UserInterface | |
{ | |
using Action = FormButtonEvent.ButtonActions; | |
public class Form : MonoBehaviour | |
{ | |
// is the current form displaying? | |
public bool isActive { | |
get { | |
return _container.activeSelf; | |
} | |
} | |
[SerializeField, Tooltip("The container that shows/hides the from. Will use Gameobject if not set")] | |
protected GameObject container = null; | |
protected Settings settings; | |
[SerializeField, Tooltip("The UXML file to use a template, may be injected via settings if desired")] | |
protected VisualTreeAsset formTemplate; | |
protected PanelRenderer formPanel; | |
protected VisualElement formRoot; | |
protected List<FormButton> formButtons; | |
protected List<TextField> formTextItems; | |
protected FormButton SubmitButton; | |
protected FormButton CancelButton; | |
protected FormButton ApplyButton; | |
protected Dictionary<string, FormButton> OtherButtons; | |
private GameObject _container { | |
get { | |
if (container == null) { | |
return gameObject; | |
} | |
return container; | |
} | |
} | |
[Inject] | |
public void InitBase( | |
PanelRenderer formPanel, | |
Settings settings) | |
{ | |
this.formPanel = formPanel; | |
this.settings = settings; | |
if (settings.FormTemplate != null) { | |
this.formTemplate = settings.FormTemplate; | |
} | |
if (this.formTemplate == null) { | |
throw new ConfigurationException("Unable to configure form (" + this.name + " ): No template configured"); | |
} | |
} | |
public void Awake() | |
{ | |
// Any time the root UXML is reloaded, we will need to rehoook. | |
// This will also enable us to hook on initial load. | |
formPanel.postUxmlReload += RehookForm; | |
} | |
public void ToggleFormActivation() | |
{ | |
if (isActive) { | |
DeactivateForm(); | |
return; | |
} | |
ActivateForm(); | |
} | |
public virtual void DeactivateForm() | |
{ | |
_container.SetActive(false); | |
} | |
public virtual void ActivateForm() | |
{ | |
if (formRoot == null) { | |
SetFormRoot(settings.RootSelector); | |
} | |
formTemplate.CloneTree(formRoot); | |
_container.SetActive(true); | |
ActivateButtons(); | |
} | |
public virtual void ActivateButtons() | |
{ | |
UQueryBuilder<FormButton> buttons = formRoot.Query<FormButton>(); | |
buttons.ForEach(ActivateButton); | |
} | |
public virtual void ActivateButton(FormButton button) | |
{ | |
switch (button.action) { | |
case Action.Submit: | |
SubmitButton = button; | |
button.clickable.clicked += SubmitForm; | |
break; | |
case Action.Apply: | |
ApplyButton = button; | |
button.clickable.clicked += ApplyForm; | |
break; | |
case Action.Cancel: | |
CancelButton = button; | |
button.clickable.clicked += CancelForm; | |
break; | |
default: | |
OtherButtons.Add(button.name, button); | |
button.clickable.clicked += OtherFormAction; | |
break; | |
} | |
} | |
public void DeactivateButtons() | |
{ | |
UQueryBuilder<FormButton> buttons = formRoot.Query<FormButton>(); | |
buttons.ForEach(DeactivateButton); | |
} | |
public void DeactivateButton(FormButton button) | |
{ | |
switch (button.action) { | |
case Action.Submit: | |
SubmitButton = button; | |
button.clickable.clicked -= SubmitForm; | |
break; | |
case Action.Apply: | |
ApplyButton = button; | |
button.clickable.clicked -= ApplyForm; | |
break; | |
case Action.Cancel: | |
CancelButton = button; | |
button.clickable.clicked -= CancelForm; | |
break; | |
default: | |
if (OtherButtons.ContainsKey(button.name)) { | |
OtherButtons.Remove(button.name); | |
} | |
button.clickable.clicked -= OtherFormAction; | |
break; | |
} | |
} | |
// @todo for follow-up, under what circumstances would we return non-null? | |
// Looks like it is for data binding? https://github.com/Unity-Technologies/UIElementsUniteCPH2019RuntimeDemo/blob/124f568c3eb325e19f83d005ddcbb86270ae2b8a/Assets/Tanks/Scripts/Managers/GameManager.cs#L180 | |
protected IEnumerable<UnityEngine.Object> RehookForm() | |
{ | |
SetFormRoot(settings.RootSelector); | |
if (!isActive) { | |
// If we aren't actually active, don't proceed. | |
return null; | |
} | |
// if we should be active, go ahead and fully activate | |
ActivateForm(); | |
return null; | |
} | |
protected void SetFormRoot(string selector) | |
{ | |
// @todo come back to this and create a custom element for form root | |
formRoot = formPanel.visualTree.Q<Box>(selector); | |
if (formRoot == null) { | |
throw new ConfigurationException("Unable to find the form root in supplied formPanel! Is there a build error?"); | |
} | |
} | |
protected virtual void SubmitForm() | |
{ | |
} | |
protected virtual void ApplyForm() | |
{ | |
} | |
protected virtual void CancelForm() | |
{ | |
} | |
protected virtual void OtherFormAction() | |
{ | |
} | |
protected Dictionary<string, string> BuildFormData() | |
{ | |
Dictionary<string, string> formData = new Dictionary<string, string>(); | |
UQueryBuilder<TextField> fields = formRoot.Query<TextField>(); | |
fields.ForEach((field) => formData.Add(field.name, field.value)); | |
return formData; | |
} | |
[Serializable] | |
public class Settings : IFormSettings | |
{ | |
// @todo ensure that the form's stylesheet is loaded in the panel? Need to figure out how | |
// StyleSheet formStylesheet; | |
[SerializeField, Tooltip("The individual form template")] | |
private VisualTreeAsset _formTemplate; | |
public VisualTreeAsset FormTemplate { get { return _formTemplate; } } | |
[SerializeField, Tooltip("The selector which references where to place the form in the panel.")] | |
private string _rootSelector = "form-root"; | |
public string RootSelector { get { return _rootSelector; } } | |
} | |
} | |
} |
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 UnityEngine; | |
using SMG.Common.UserInterface; | |
namespace SMG.Proximity.SceneObjects.UserInterface | |
{ | |
public class MainMenu : Form | |
{ | |
protected override void SubmitForm() | |
{ | |
eventBus.Publish<ConnectToNetworkFormEvent>(new ConnectToNetworkFormEvent(BuildFormData(), GameModes.Race)); | |
} | |
protected override void CancelForm() | |
{ | |
eventBus.Publish<DisconnectFromNetworkFormEvent>(new DisconnectFromNetworkFormEvent(BuildFormData())); | |
} | |
} | |
} |
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
<?xml version="1.0" encoding="utf-8"?> | |
<UXML | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns="UnityEngine.UIElements" | |
xmlns:smg="SMG.Common.UserInterface" | |
xsi:noNamespaceSchemaLocation="../UIElementsSchema/UIElements.xsd" | |
xsi:schemaLocation="UnityEngine.UIElements ../UIElementsSchema/UnityEngine.UIElements.xsd" | |
> | |
<Box class="container container--user-info"> | |
<Label text="User Name"/> <TextField name="UserName"/> | |
</Box> | |
<Box class="container container--buttons"> | |
<smg:FormButton action="Submit" name="submit" text="Ready" /> | |
<smg:FormButton action="Cancel" name="cancel" text="Quit Game"/> | |
</Box> | |
</UXML> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment