summaryrefslogtreecommitdiff
path: root/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls
diff options
context:
space:
mode:
Diffstat (limited to 'Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls')
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/AnimationTriggers.cs36
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/AnimationTriggers.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Button.cs83
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Button.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ColorBlock.cs91
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ColorBlock.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/DefaultControls.cs581
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/DefaultControls.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs649
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/InputField.cs2484
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/InputField.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Navigation.cs74
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Navigation.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ScrollRect.cs873
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ScrollRect.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Scrollbar.cs422
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Scrollbar.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Selectable.cs672
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Selectable.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Slider.cs450
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Slider.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Toggle.cs246
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Toggle.cs.meta11
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ToggleGroup.cs73
-rw-r--r--Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ToggleGroup.cs.meta11
26 files changed, 6877 insertions, 0 deletions
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/AnimationTriggers.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/AnimationTriggers.cs
new file mode 100644
index 0000000..e42871b
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/AnimationTriggers.cs
@@ -0,0 +1,36 @@
+using System;
+using UnityEngine.Serialization;
+
+namespace UnityEngine.UI
+{
+ [Serializable]
+ public class AnimationTriggers
+ {
+ private const string kDefaultNormalAnimName = "Normal";
+ private const string kDefaultSelectedAnimName = "Highlighted";
+ private const string kDefaultPressedAnimName = "Pressed";
+ private const string kDefaultDisabledAnimName = "Disabled";
+
+ [FormerlySerializedAs("normalTrigger")]
+ [SerializeField]
+ private string m_NormalTrigger = kDefaultNormalAnimName;
+
+ [FormerlySerializedAs("highlightedTrigger")]
+ [FormerlySerializedAs("m_SelectedTrigger")]
+ [SerializeField]
+ private string m_HighlightedTrigger = kDefaultSelectedAnimName;
+
+ [FormerlySerializedAs("pressedTrigger")]
+ [SerializeField]
+ private string m_PressedTrigger = kDefaultPressedAnimName;
+
+ [FormerlySerializedAs("disabledTrigger")]
+ [SerializeField]
+ private string m_DisabledTrigger = kDefaultDisabledAnimName;
+
+ public string normalTrigger { get { return m_NormalTrigger; } set { m_NormalTrigger = value; } }
+ public string highlightedTrigger { get { return m_HighlightedTrigger; } set { m_HighlightedTrigger = value; } }
+ public string pressedTrigger { get { return m_PressedTrigger; } set { m_PressedTrigger = value; } }
+ public string disabledTrigger { get { return m_DisabledTrigger; } set { m_DisabledTrigger = value; } }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/AnimationTriggers.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/AnimationTriggers.cs.meta
new file mode 100644
index 0000000..52f2ca0
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/AnimationTriggers.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fd02e9c578e561646b10984381250bf5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Button.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Button.cs
new file mode 100644
index 0000000..d0c9ae3
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Button.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using UnityEngine.Serialization;
+
+namespace UnityEngine.UI
+{
+ // Button that's meant to work with mouse or touch-based devices.
+ [AddComponentMenu("UI/Button", 30)]
+ public class Button
+ : Selectable
+ , IPointerClickHandler // 鼠标点击\触摸
+ , ISubmitHandler // Input>Submit触发,比如手柄、键盘某个按键按下
+ {
+ [Serializable]
+ public class ButtonClickedEvent : UnityEvent {}
+
+ // Event delegates triggered on click.
+ [FormerlySerializedAs("onClick")]
+ [SerializeField]
+ private ButtonClickedEvent m_OnClick = new ButtonClickedEvent();
+
+ protected Button()
+ {}
+
+ public ButtonClickedEvent onClick
+ {
+ get { return m_OnClick; }
+ set { m_OnClick = value; }
+ }
+
+ // 调回调
+ private void Press()
+ {
+ if (!IsActive() || !IsInteractable())
+ return;
+
+ UISystemProfilerApi.AddMarker("Button.onClick", this);
+ m_OnClick.Invoke();
+ }
+
+ // Trigger all registered callbacks.
+ public virtual void OnPointerClick(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ Press();
+ }
+
+ public virtual void OnSubmit(BaseEventData eventData)
+ {
+ LogHelper.Log("OnSubmit() " + gameObject.name);
+
+ Press();
+
+ // if we get set disabled during the press
+ // don't run the coroutine.
+ if (!IsActive() || !IsInteractable())
+ return;
+
+ DoStateTransition(SelectionState.Pressed, false);
+ StartCoroutine(OnFinishSubmit());
+ }
+
+ private IEnumerator OnFinishSubmit()
+ {
+ var fadeTime = colors.fadeDuration;
+ var elapsedTime = 0f;
+
+ while (elapsedTime < fadeTime)
+ {
+ elapsedTime += Time.unscaledDeltaTime;
+ yield return null;
+ }
+
+ DoStateTransition(currentSelectionState, false);
+ }
+
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Button.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Button.cs.meta
new file mode 100644
index 0000000..cbdb3a1
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Button.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a754c749030e1d848bed7a3cb2e5520f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ColorBlock.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ColorBlock.cs
new file mode 100644
index 0000000..c78d617
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ColorBlock.cs
@@ -0,0 +1,91 @@
+using System;
+using UnityEngine.Serialization;
+
+namespace UnityEngine.UI
+{
+ [Serializable]
+ public struct ColorBlock : IEquatable<ColorBlock>
+ {
+ [FormerlySerializedAs("normalColor")]
+ [SerializeField]
+ private Color m_NormalColor;
+
+ [FormerlySerializedAs("highlightedColor")]
+ [FormerlySerializedAs("m_SelectedColor")]
+ [SerializeField]
+ private Color m_HighlightedColor;
+
+ [FormerlySerializedAs("pressedColor")]
+ [SerializeField]
+ private Color m_PressedColor;
+
+ [FormerlySerializedAs("disabledColor")]
+ [SerializeField]
+ private Color m_DisabledColor;
+
+ [Range(1, 5)]
+ [SerializeField]
+ private float m_ColorMultiplier;
+
+ [FormerlySerializedAs("fadeDuration")]
+ [SerializeField]
+ private float m_FadeDuration;
+
+ public Color normalColor { get { return m_NormalColor; } set { m_NormalColor = value; } }
+ public Color highlightedColor { get { return m_HighlightedColor; } set { m_HighlightedColor = value; } }
+ public Color pressedColor { get { return m_PressedColor; } set { m_PressedColor = value; } }
+ public Color disabledColor { get { return m_DisabledColor; } set { m_DisabledColor = value; } }
+ public float colorMultiplier { get { return m_ColorMultiplier; } set { m_ColorMultiplier = value; } }
+ public float fadeDuration { get { return m_FadeDuration; } set { m_FadeDuration = value; } }
+
+ public static ColorBlock defaultColorBlock
+ {
+ get
+ {
+ var c = new ColorBlock
+ {
+ m_NormalColor = new Color32(255, 255, 255, 255),
+ m_HighlightedColor = new Color32(245, 245, 245, 255),
+ m_PressedColor = new Color32(200, 200, 200, 255),
+ m_DisabledColor = new Color32(200, 200, 200, 128),
+ colorMultiplier = 1.0f,
+ fadeDuration = 0.1f
+ };
+ return c;
+ }
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is ColorBlock))
+ return false;
+
+ return Equals((ColorBlock)obj);
+ }
+
+ public bool Equals(ColorBlock other)
+ {
+ return normalColor == other.normalColor &&
+ highlightedColor == other.highlightedColor &&
+ pressedColor == other.pressedColor &&
+ disabledColor == other.disabledColor &&
+ colorMultiplier == other.colorMultiplier &&
+ fadeDuration == other.fadeDuration;
+ }
+
+ public static bool operator==(ColorBlock point1, ColorBlock point2)
+ {
+ return point1.Equals(point2);
+ }
+
+ public static bool operator!=(ColorBlock point1, ColorBlock point2)
+ {
+ return !point1.Equals(point2);
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ColorBlock.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ColorBlock.cs.meta
new file mode 100644
index 0000000..8680539
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ColorBlock.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f7d25b8a109dd134fa40749f78bbd907
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/DefaultControls.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/DefaultControls.cs
new file mode 100644
index 0000000..a22ff9a
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/DefaultControls.cs
@@ -0,0 +1,581 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace UnityEngine.UI
+{
+ public static class DefaultControls
+ {
+ public struct Resources
+ {
+ public Sprite standard;
+ public Sprite background;
+ public Sprite inputField;
+ public Sprite knob;
+ public Sprite checkmark;
+ public Sprite dropdown;
+ public Sprite mask;
+ }
+
+ private const float kWidth = 160f;
+ private const float kThickHeight = 30f;
+ private const float kThinHeight = 20f;
+ private static Vector2 s_ThickElementSize = new Vector2(kWidth, kThickHeight);
+ private static Vector2 s_ThinElementSize = new Vector2(kWidth, kThinHeight);
+ private static Vector2 s_ImageElementSize = new Vector2(100f, 100f);
+ private static Color s_DefaultSelectableColor = new Color(1f, 1f, 1f, 1f);
+ private static Color s_PanelColor = new Color(1f, 1f, 1f, 0.392f);
+ private static Color s_TextColor = new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f);
+
+ // Helper methods at top
+
+ private static GameObject CreateUIElementRoot(string name, Vector2 size)
+ {
+ GameObject child = new GameObject(name);
+ RectTransform rectTransform = child.AddComponent<RectTransform>();
+ rectTransform.sizeDelta = size;
+ return child;
+ }
+
+ static GameObject CreateUIObject(string name, GameObject parent)
+ {
+ GameObject go = new GameObject(name);
+ go.AddComponent<RectTransform>();
+ SetParentAndAlign(go, parent);
+ return go;
+ }
+
+ private static void SetDefaultTextValues(Text lbl)
+ {
+ // Set text values we want across UI elements in default controls.
+ // Don't set values which are the same as the default values for the Text component,
+ // since there's no point in that, and it's good to keep them as consistent as possible.
+ lbl.color = s_TextColor;
+
+ // Reset() is not called when playing. We still want the default font to be assigned
+ lbl.AssignDefaultFont();
+ }
+
+ private static void SetDefaultColorTransitionValues(Selectable slider)
+ {
+ ColorBlock colors = slider.colors;
+ colors.highlightedColor = new Color(0.882f, 0.882f, 0.882f);
+ colors.pressedColor = new Color(0.698f, 0.698f, 0.698f);
+ colors.disabledColor = new Color(0.521f, 0.521f, 0.521f);
+ }
+
+ private static void SetParentAndAlign(GameObject child, GameObject parent)
+ {
+ if (parent == null)
+ return;
+
+ child.transform.SetParent(parent.transform, false);
+ SetLayerRecursively(child, parent.layer);
+ }
+
+ private static void SetLayerRecursively(GameObject go, int layer)
+ {
+ go.layer = layer;
+ Transform t = go.transform;
+ for (int i = 0; i < t.childCount; i++)
+ SetLayerRecursively(t.GetChild(i).gameObject, layer);
+ }
+
+ // Actual controls
+
+ public static GameObject CreatePanel(Resources resources)
+ {
+ GameObject panelRoot = CreateUIElementRoot("Panel", s_ThickElementSize);
+
+ // Set RectTransform to stretch
+ RectTransform rectTransform = panelRoot.GetComponent<RectTransform>();
+ rectTransform.anchorMin = Vector2.zero;
+ rectTransform.anchorMax = Vector2.one;
+ rectTransform.anchoredPosition = Vector2.zero;
+ rectTransform.sizeDelta = Vector2.zero;
+
+ Image image = panelRoot.AddComponent<Image>();
+ image.sprite = resources.background;
+ image.type = Image.Type.Sliced;
+ image.color = s_PanelColor;
+
+ return panelRoot;
+ }
+
+ public static GameObject CreateButton(Resources resources)
+ {
+ GameObject buttonRoot = CreateUIElementRoot("Button", s_ThickElementSize);
+
+ GameObject childText = new GameObject("Text");
+ childText.AddComponent<RectTransform>();
+ SetParentAndAlign(childText, buttonRoot);
+
+ Image image = buttonRoot.AddComponent<Image>();
+ image.sprite = resources.standard;
+ image.type = Image.Type.Sliced;
+ image.color = s_DefaultSelectableColor;
+
+ Button bt = buttonRoot.AddComponent<Button>();
+ SetDefaultColorTransitionValues(bt);
+
+ Text text = childText.AddComponent<Text>();
+ text.text = "Button";
+ text.alignment = TextAnchor.MiddleCenter;
+ SetDefaultTextValues(text);
+
+ RectTransform textRectTransform = childText.GetComponent<RectTransform>();
+ textRectTransform.anchorMin = Vector2.zero;
+ textRectTransform.anchorMax = Vector2.one;
+ textRectTransform.sizeDelta = Vector2.zero;
+
+ return buttonRoot;
+ }
+
+ public static GameObject CreateText(Resources resources)
+ {
+ GameObject go = CreateUIElementRoot("Text", s_ThickElementSize);
+
+ Text lbl = go.AddComponent<Text>();
+ lbl.text = "New Text";
+ SetDefaultTextValues(lbl);
+
+ return go;
+ }
+
+ public static GameObject CreateImage(Resources resources)
+ {
+ GameObject go = CreateUIElementRoot("Image", s_ImageElementSize);
+ go.AddComponent<Image>();
+ return go;
+ }
+
+ public static GameObject CreateRawImage(Resources resources)
+ {
+ GameObject go = CreateUIElementRoot("RawImage", s_ImageElementSize);
+ go.AddComponent<RawImage>();
+ return go;
+ }
+
+ public static GameObject CreateSlider(Resources resources)
+ {
+ // Create GOs Hierarchy
+ GameObject root = CreateUIElementRoot("Slider", s_ThinElementSize);
+
+ GameObject background = CreateUIObject("Background", root);
+ GameObject fillArea = CreateUIObject("Fill Area", root);
+ GameObject fill = CreateUIObject("Fill", fillArea);
+ GameObject handleArea = CreateUIObject("Handle Slide Area", root);
+ GameObject handle = CreateUIObject("Handle", handleArea);
+
+ // Background
+ Image backgroundImage = background.AddComponent<Image>();
+ backgroundImage.sprite = resources.background;
+ backgroundImage.type = Image.Type.Sliced;
+ backgroundImage.color = s_DefaultSelectableColor;
+ RectTransform backgroundRect = background.GetComponent<RectTransform>();
+ backgroundRect.anchorMin = new Vector2(0, 0.25f);
+ backgroundRect.anchorMax = new Vector2(1, 0.75f);
+ backgroundRect.sizeDelta = new Vector2(0, 0);
+
+ // Fill Area
+ RectTransform fillAreaRect = fillArea.GetComponent<RectTransform>();
+ fillAreaRect.anchorMin = new Vector2(0, 0.25f);
+ fillAreaRect.anchorMax = new Vector2(1, 0.75f);
+ fillAreaRect.anchoredPosition = new Vector2(-5, 0);
+ fillAreaRect.sizeDelta = new Vector2(-20, 0);
+
+ // Fill
+ Image fillImage = fill.AddComponent<Image>();
+ fillImage.sprite = resources.standard;
+ fillImage.type = Image.Type.Sliced;
+ fillImage.color = s_DefaultSelectableColor;
+
+ RectTransform fillRect = fill.GetComponent<RectTransform>();
+ fillRect.sizeDelta = new Vector2(10, 0);
+
+ // Handle Area
+ RectTransform handleAreaRect = handleArea.GetComponent<RectTransform>();
+ handleAreaRect.sizeDelta = new Vector2(-20, 0);
+ handleAreaRect.anchorMin = new Vector2(0, 0);
+ handleAreaRect.anchorMax = new Vector2(1, 1);
+
+ // Handle
+ Image handleImage = handle.AddComponent<Image>();
+ handleImage.sprite = resources.knob;
+ handleImage.color = s_DefaultSelectableColor;
+
+ RectTransform handleRect = handle.GetComponent<RectTransform>();
+ handleRect.sizeDelta = new Vector2(20, 0);
+
+ // Setup slider component
+ Slider slider = root.AddComponent<Slider>();
+ slider.fillRect = fill.GetComponent<RectTransform>();
+ slider.handleRect = handle.GetComponent<RectTransform>();
+ slider.targetGraphic = handleImage;
+ slider.direction = Slider.Direction.LeftToRight;
+ SetDefaultColorTransitionValues(slider);
+
+ return root;
+ }
+
+ public static GameObject CreateScrollbar(Resources resources)
+ {
+ // Create GOs Hierarchy
+ GameObject scrollbarRoot = CreateUIElementRoot("Scrollbar", s_ThinElementSize);
+
+ GameObject sliderArea = CreateUIObject("Sliding Area", scrollbarRoot);
+ GameObject handle = CreateUIObject("Handle", sliderArea);
+
+ Image bgImage = scrollbarRoot.AddComponent<Image>();
+ bgImage.sprite = resources.background;
+ bgImage.type = Image.Type.Sliced;
+ bgImage.color = s_DefaultSelectableColor;
+
+ Image handleImage = handle.AddComponent<Image>();
+ handleImage.sprite = resources.standard;
+ handleImage.type = Image.Type.Sliced;
+ handleImage.color = s_DefaultSelectableColor;
+
+ RectTransform sliderAreaRect = sliderArea.GetComponent<RectTransform>();
+ sliderAreaRect.sizeDelta = new Vector2(-20, -20);
+ sliderAreaRect.anchorMin = Vector2.zero;
+ sliderAreaRect.anchorMax = Vector2.one;
+
+ RectTransform handleRect = handle.GetComponent<RectTransform>();
+ handleRect.sizeDelta = new Vector2(20, 20);
+
+ Scrollbar scrollbar = scrollbarRoot.AddComponent<Scrollbar>();
+ scrollbar.handleRect = handleRect;
+ scrollbar.targetGraphic = handleImage;
+ SetDefaultColorTransitionValues(scrollbar);
+
+ return scrollbarRoot;
+ }
+
+ public static GameObject CreateToggle(Resources resources)
+ {
+ // Set up hierarchy
+ GameObject toggleRoot = CreateUIElementRoot("Toggle", s_ThinElementSize);
+
+ GameObject background = CreateUIObject("Background", toggleRoot);
+ GameObject checkmark = CreateUIObject("Checkmark", background);
+ GameObject childLabel = CreateUIObject("Label", toggleRoot);
+
+ // Set up components
+ Toggle toggle = toggleRoot.AddComponent<Toggle>();
+ toggle.isOn = true;
+
+ Image bgImage = background.AddComponent<Image>();
+ bgImage.sprite = resources.standard;
+ bgImage.type = Image.Type.Sliced;
+ bgImage.color = s_DefaultSelectableColor;
+
+ Image checkmarkImage = checkmark.AddComponent<Image>();
+ checkmarkImage.sprite = resources.checkmark;
+
+ Text label = childLabel.AddComponent<Text>();
+ label.text = "Toggle";
+ SetDefaultTextValues(label);
+
+ toggle.graphic = checkmarkImage;
+ toggle.targetGraphic = bgImage;
+ SetDefaultColorTransitionValues(toggle);
+
+ RectTransform bgRect = background.GetComponent<RectTransform>();
+ bgRect.anchorMin = new Vector2(0f, 1f);
+ bgRect.anchorMax = new Vector2(0f, 1f);
+ bgRect.anchoredPosition = new Vector2(10f, -10f);
+ bgRect.sizeDelta = new Vector2(kThinHeight, kThinHeight);
+
+ RectTransform checkmarkRect = checkmark.GetComponent<RectTransform>();
+ checkmarkRect.anchorMin = new Vector2(0.5f, 0.5f);
+ checkmarkRect.anchorMax = new Vector2(0.5f, 0.5f);
+ checkmarkRect.anchoredPosition = Vector2.zero;
+ checkmarkRect.sizeDelta = new Vector2(20f, 20f);
+
+ RectTransform labelRect = childLabel.GetComponent<RectTransform>();
+ labelRect.anchorMin = new Vector2(0f, 0f);
+ labelRect.anchorMax = new Vector2(1f, 1f);
+ labelRect.offsetMin = new Vector2(23f, 1f);
+ labelRect.offsetMax = new Vector2(-5f, -2f);
+
+ return toggleRoot;
+ }
+
+ public static GameObject CreateInputField(Resources resources)
+ {
+ GameObject root = CreateUIElementRoot("InputField", s_ThickElementSize);
+
+ GameObject childPlaceholder = CreateUIObject("Placeholder", root);
+ GameObject childText = CreateUIObject("Text", root);
+
+ Image image = root.AddComponent<Image>();
+ image.sprite = resources.inputField;
+ image.type = Image.Type.Sliced;
+ image.color = s_DefaultSelectableColor;
+
+ InputField inputField = root.AddComponent<InputField>();
+ SetDefaultColorTransitionValues(inputField);
+
+ Text text = childText.AddComponent<Text>();
+ text.text = "";
+ text.supportRichText = false;
+ SetDefaultTextValues(text);
+
+ Text placeholder = childPlaceholder.AddComponent<Text>();
+ placeholder.text = "Enter text...";
+ placeholder.fontStyle = FontStyle.Italic;
+ // Make placeholder color half as opaque as normal text color.
+ Color placeholderColor = text.color;
+ placeholderColor.a *= 0.5f;
+ placeholder.color = placeholderColor;
+
+ RectTransform textRectTransform = childText.GetComponent<RectTransform>();
+ textRectTransform.anchorMin = Vector2.zero;
+ textRectTransform.anchorMax = Vector2.one;
+ textRectTransform.sizeDelta = Vector2.zero;
+ textRectTransform.offsetMin = new Vector2(10, 6);
+ textRectTransform.offsetMax = new Vector2(-10, -7);
+
+ RectTransform placeholderRectTransform = childPlaceholder.GetComponent<RectTransform>();
+ placeholderRectTransform.anchorMin = Vector2.zero;
+ placeholderRectTransform.anchorMax = Vector2.one;
+ placeholderRectTransform.sizeDelta = Vector2.zero;
+ placeholderRectTransform.offsetMin = new Vector2(10, 6);
+ placeholderRectTransform.offsetMax = new Vector2(-10, -7);
+
+ inputField.textComponent = text;
+ inputField.placeholder = placeholder;
+
+ return root;
+ }
+
+ public static GameObject CreateDropdown(Resources resources)
+ {
+ GameObject root = CreateUIElementRoot("Dropdown", s_ThickElementSize);
+
+ GameObject label = CreateUIObject("Label", root);
+ GameObject arrow = CreateUIObject("Arrow", root);
+ GameObject template = CreateUIObject("Template", root);
+ GameObject viewport = CreateUIObject("Viewport", template);
+ GameObject content = CreateUIObject("Content", viewport);
+ GameObject item = CreateUIObject("Item", content);
+ GameObject itemBackground = CreateUIObject("Item Background", item);
+ GameObject itemCheckmark = CreateUIObject("Item Checkmark", item);
+ GameObject itemLabel = CreateUIObject("Item Label", item);
+
+ // Sub controls.
+
+ GameObject scrollbar = CreateScrollbar(resources);
+ scrollbar.name = "Scrollbar";
+ SetParentAndAlign(scrollbar, template);
+
+ Scrollbar scrollbarScrollbar = scrollbar.GetComponent<Scrollbar>();
+ scrollbarScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
+
+ RectTransform vScrollbarRT = scrollbar.GetComponent<RectTransform>();
+ vScrollbarRT.anchorMin = Vector2.right;
+ vScrollbarRT.anchorMax = Vector2.one;
+ vScrollbarRT.pivot = Vector2.one;
+ vScrollbarRT.sizeDelta = new Vector2(vScrollbarRT.sizeDelta.x, 0);
+
+ // Setup item UI components.
+
+ Text itemLabelText = itemLabel.AddComponent<Text>();
+ SetDefaultTextValues(itemLabelText);
+ itemLabelText.alignment = TextAnchor.MiddleLeft;
+
+ Image itemBackgroundImage = itemBackground.AddComponent<Image>();
+ itemBackgroundImage.color = new Color32(245, 245, 245, 255);
+
+ Image itemCheckmarkImage = itemCheckmark.AddComponent<Image>();
+ itemCheckmarkImage.sprite = resources.checkmark;
+
+ Toggle itemToggle = item.AddComponent<Toggle>();
+ itemToggle.targetGraphic = itemBackgroundImage;
+ itemToggle.graphic = itemCheckmarkImage;
+ itemToggle.isOn = true;
+
+ // Setup template UI components.
+
+ Image templateImage = template.AddComponent<Image>();
+ templateImage.sprite = resources.standard;
+ templateImage.type = Image.Type.Sliced;
+
+ ScrollRect templateScrollRect = template.AddComponent<ScrollRect>();
+ templateScrollRect.content = (RectTransform)content.transform;
+ templateScrollRect.viewport = (RectTransform)viewport.transform;
+ templateScrollRect.horizontal = false;
+ templateScrollRect.movementType = ScrollRect.MovementType.Clamped;
+ templateScrollRect.verticalScrollbar = scrollbarScrollbar;
+ templateScrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
+ templateScrollRect.verticalScrollbarSpacing = -3;
+
+ Mask scrollRectMask = viewport.AddComponent<Mask>();
+ scrollRectMask.showMaskGraphic = false;
+
+ Image viewportImage = viewport.AddComponent<Image>();
+ viewportImage.sprite = resources.mask;
+ viewportImage.type = Image.Type.Sliced;
+
+ // Setup dropdown UI components.
+
+ Text labelText = label.AddComponent<Text>();
+ SetDefaultTextValues(labelText);
+ labelText.alignment = TextAnchor.MiddleLeft;
+
+ Image arrowImage = arrow.AddComponent<Image>();
+ arrowImage.sprite = resources.dropdown;
+
+ Image backgroundImage = root.AddComponent<Image>();
+ backgroundImage.sprite = resources.standard;
+ backgroundImage.color = s_DefaultSelectableColor;
+ backgroundImage.type = Image.Type.Sliced;
+
+ Dropdown dropdown = root.AddComponent<Dropdown>();
+ dropdown.targetGraphic = backgroundImage;
+ SetDefaultColorTransitionValues(dropdown);
+ dropdown.template = template.GetComponent<RectTransform>();
+ dropdown.captionText = labelText;
+ dropdown.itemText = itemLabelText;
+
+ // Setting default Item list.
+ itemLabelText.text = "Option A";
+ dropdown.options.Add(new Dropdown.OptionData {text = "Option A"});
+ dropdown.options.Add(new Dropdown.OptionData {text = "Option B"});
+ dropdown.options.Add(new Dropdown.OptionData {text = "Option C"});
+ dropdown.RefreshShownValue();
+
+ // Set up RectTransforms.
+
+ RectTransform labelRT = label.GetComponent<RectTransform>();
+ labelRT.anchorMin = Vector2.zero;
+ labelRT.anchorMax = Vector2.one;
+ labelRT.offsetMin = new Vector2(10, 6);
+ labelRT.offsetMax = new Vector2(-25, -7);
+
+ RectTransform arrowRT = arrow.GetComponent<RectTransform>();
+ arrowRT.anchorMin = new Vector2(1, 0.5f);
+ arrowRT.anchorMax = new Vector2(1, 0.5f);
+ arrowRT.sizeDelta = new Vector2(20, 20);
+ arrowRT.anchoredPosition = new Vector2(-15, 0);
+
+ RectTransform templateRT = template.GetComponent<RectTransform>();
+ templateRT.anchorMin = new Vector2(0, 0);
+ templateRT.anchorMax = new Vector2(1, 0);
+ templateRT.pivot = new Vector2(0.5f, 1);
+ templateRT.anchoredPosition = new Vector2(0, 2);
+ templateRT.sizeDelta = new Vector2(0, 150);
+
+ RectTransform viewportRT = viewport.GetComponent<RectTransform>();
+ viewportRT.anchorMin = new Vector2(0, 0);
+ viewportRT.anchorMax = new Vector2(1, 1);
+ viewportRT.sizeDelta = new Vector2(-18, 0);
+ viewportRT.pivot = new Vector2(0, 1);
+
+ RectTransform contentRT = content.GetComponent<RectTransform>();
+ contentRT.anchorMin = new Vector2(0f, 1);
+ contentRT.anchorMax = new Vector2(1f, 1);
+ contentRT.pivot = new Vector2(0.5f, 1);
+ contentRT.anchoredPosition = new Vector2(0, 0);
+ contentRT.sizeDelta = new Vector2(0, 28);
+
+ RectTransform itemRT = item.GetComponent<RectTransform>();
+ itemRT.anchorMin = new Vector2(0, 0.5f);
+ itemRT.anchorMax = new Vector2(1, 0.5f);
+ itemRT.sizeDelta = new Vector2(0, 20);
+
+ RectTransform itemBackgroundRT = itemBackground.GetComponent<RectTransform>();
+ itemBackgroundRT.anchorMin = Vector2.zero;
+ itemBackgroundRT.anchorMax = Vector2.one;
+ itemBackgroundRT.sizeDelta = Vector2.zero;
+
+ RectTransform itemCheckmarkRT = itemCheckmark.GetComponent<RectTransform>();
+ itemCheckmarkRT.anchorMin = new Vector2(0, 0.5f);
+ itemCheckmarkRT.anchorMax = new Vector2(0, 0.5f);
+ itemCheckmarkRT.sizeDelta = new Vector2(20, 20);
+ itemCheckmarkRT.anchoredPosition = new Vector2(10, 0);
+
+ RectTransform itemLabelRT = itemLabel.GetComponent<RectTransform>();
+ itemLabelRT.anchorMin = Vector2.zero;
+ itemLabelRT.anchorMax = Vector2.one;
+ itemLabelRT.offsetMin = new Vector2(20, 1);
+ itemLabelRT.offsetMax = new Vector2(-10, -2);
+
+ template.SetActive(false);
+
+ return root;
+ }
+
+ public static GameObject CreateScrollView(Resources resources)
+ {
+ GameObject root = CreateUIElementRoot("Scroll View", new Vector2(200, 200));
+
+ GameObject viewport = CreateUIObject("Viewport", root);
+ GameObject content = CreateUIObject("Content", viewport);
+
+ // Sub controls.
+
+ GameObject hScrollbar = CreateScrollbar(resources);
+ hScrollbar.name = "Scrollbar Horizontal";
+ SetParentAndAlign(hScrollbar, root);
+ RectTransform hScrollbarRT = hScrollbar.GetComponent<RectTransform>();
+ hScrollbarRT.anchorMin = Vector2.zero;
+ hScrollbarRT.anchorMax = Vector2.right;
+ hScrollbarRT.pivot = Vector2.zero;
+ hScrollbarRT.sizeDelta = new Vector2(0, hScrollbarRT.sizeDelta.y);
+
+ GameObject vScrollbar = CreateScrollbar(resources);
+ vScrollbar.name = "Scrollbar Vertical";
+ SetParentAndAlign(vScrollbar, root);
+ vScrollbar.GetComponent<Scrollbar>().SetDirection(Scrollbar.Direction.BottomToTop, true);
+ RectTransform vScrollbarRT = vScrollbar.GetComponent<RectTransform>();
+ vScrollbarRT.anchorMin = Vector2.right;
+ vScrollbarRT.anchorMax = Vector2.one;
+ vScrollbarRT.pivot = Vector2.one;
+ vScrollbarRT.sizeDelta = new Vector2(vScrollbarRT.sizeDelta.x, 0);
+
+ // Setup RectTransforms.
+
+ // Make viewport fill entire scroll view.
+ RectTransform viewportRT = viewport.GetComponent<RectTransform>();
+ viewportRT.anchorMin = Vector2.zero;
+ viewportRT.anchorMax = Vector2.one;
+ viewportRT.sizeDelta = Vector2.zero;
+ viewportRT.pivot = Vector2.up;
+
+ // Make context match viewpoprt width and be somewhat taller.
+ // This will show the vertical scrollbar and not the horizontal one.
+ RectTransform contentRT = content.GetComponent<RectTransform>();
+ contentRT.anchorMin = Vector2.up;
+ contentRT.anchorMax = Vector2.one;
+ contentRT.sizeDelta = new Vector2(0, 300);
+ contentRT.pivot = Vector2.up;
+
+ // Setup UI components.
+
+ ScrollRect scrollRect = root.AddComponent<ScrollRect>();
+ scrollRect.content = contentRT;
+ scrollRect.viewport = viewportRT;
+ scrollRect.horizontalScrollbar = hScrollbar.GetComponent<Scrollbar>();
+ scrollRect.verticalScrollbar = vScrollbar.GetComponent<Scrollbar>();
+ scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
+ scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
+ scrollRect.horizontalScrollbarSpacing = -3;
+ scrollRect.verticalScrollbarSpacing = -3;
+
+ Image rootImage = root.AddComponent<Image>();
+ rootImage.sprite = resources.background;
+ rootImage.type = Image.Type.Sliced;
+ rootImage.color = s_PanelColor;
+
+ Mask viewportMask = viewport.AddComponent<Mask>();
+ viewportMask.showMaskGraphic = false;
+
+ Image viewportImage = viewport.AddComponent<Image>();
+ viewportImage.sprite = resources.mask;
+ viewportImage.type = Image.Type.Sliced;
+
+ return root;
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/DefaultControls.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/DefaultControls.cs.meta
new file mode 100644
index 0000000..4365d7a
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/DefaultControls.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ca969270ec2be6848bd9453f8c69a4b3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs
new file mode 100644
index 0000000..86bed37
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs
@@ -0,0 +1,649 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using UnityEngine.UI.CoroutineTween;
+
+namespace UnityEngine.UI
+{
+ [AddComponentMenu("UI/Dropdown", 35)]
+ [RequireComponent(typeof(RectTransform))]
+ public class Dropdown : Selectable, IPointerClickHandler, ISubmitHandler, ICancelHandler
+ {
+ protected internal class DropdownItem : MonoBehaviour, IPointerEnterHandler, ICancelHandler
+ {
+ [SerializeField]
+ private Text m_Text;
+ [SerializeField]
+ private Image m_Image;
+ [SerializeField]
+ private RectTransform m_RectTransform;
+ [SerializeField]
+ private Toggle m_Toggle;
+
+ public Text text { get { return m_Text; } set { m_Text = value; } }
+ public Image image { get { return m_Image; } set { m_Image = value; } }
+ public RectTransform rectTransform { get { return m_RectTransform; } set { m_RectTransform = value; } }
+ public Toggle toggle { get { return m_Toggle; } set { m_Toggle = value; } }
+
+ public virtual void OnPointerEnter(PointerEventData eventData)
+ {
+ EventSystem.current.SetSelectedGameObject(gameObject);
+ }
+
+ public virtual void OnCancel(BaseEventData eventData)
+ {
+ Dropdown dropdown = GetComponentInParent<Dropdown>();
+ if (dropdown)
+ dropdown.Hide();
+ }
+ }
+
+ [Serializable]
+ public class OptionData
+ {
+ [SerializeField]
+ private string m_Text;
+ [SerializeField]
+ private Sprite m_Image;
+
+ public string text { get { return m_Text; } set { m_Text = value; } }
+ public Sprite image { get { return m_Image; } set { m_Image = value; } }
+
+ public OptionData()
+ {
+ }
+
+ public OptionData(string text)
+ {
+ this.text = text;
+ }
+
+ public OptionData(Sprite image)
+ {
+ this.image = image;
+ }
+
+ public OptionData(string text, Sprite image)
+ {
+ this.text = text;
+ this.image = image;
+ }
+ }
+
+ [Serializable]
+ public class OptionDataList
+ {
+ [SerializeField]
+ private List<OptionData> m_Options;
+ public List<OptionData> options { get { return m_Options; } set { m_Options = value; } }
+
+
+ public OptionDataList()
+ {
+ options = new List<OptionData>();
+ }
+ }
+
+ [Serializable]
+ public class DropdownEvent : UnityEvent<int> {}
+
+ // Template used to create the dropdown.
+ [SerializeField]
+ private RectTransform m_Template;
+ public RectTransform template { get { return m_Template; } set { m_Template = value; RefreshShownValue(); } }
+
+ // Text to be used as a caption for the current value. It's not required, but it's kept here for convenience.
+ [SerializeField]
+ private Text m_CaptionText;
+ public Text captionText { get { return m_CaptionText; } set { m_CaptionText = value; RefreshShownValue(); } }
+
+ [SerializeField]
+ private Image m_CaptionImage;
+ public Image captionImage { get { return m_CaptionImage; } set { m_CaptionImage = value; RefreshShownValue(); } }
+
+ [Space]
+
+ [SerializeField]
+ private Text m_ItemText;
+ public Text itemText { get { return m_ItemText; } set { m_ItemText = value; RefreshShownValue(); } }
+
+ [SerializeField]
+ private Image m_ItemImage;
+ public Image itemImage { get { return m_ItemImage; } set { m_ItemImage = value; RefreshShownValue(); } }
+
+ [Space]
+
+ [SerializeField]
+ private int m_Value;
+
+ [Space]
+
+ // Items that will be visible when the dropdown is shown.
+ // We box this into its own class so we can use a Property Drawer for it.
+ [SerializeField]
+ private OptionDataList m_Options = new OptionDataList();
+ public List<OptionData> options
+ {
+ get { return m_Options.options; }
+ set { m_Options.options = value; RefreshShownValue(); }
+ }
+
+ [Space]
+
+ // Notification triggered when the dropdown changes.
+ [SerializeField]
+ private DropdownEvent m_OnValueChanged = new DropdownEvent();
+ public DropdownEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
+
+ private GameObject m_Dropdown;
+ private GameObject m_Blocker;
+ private List<DropdownItem> m_Items = new List<DropdownItem>();
+ private TweenRunner<FloatTween> m_AlphaTweenRunner;
+ private bool validTemplate = false;
+
+ private static OptionData s_NoOptionData = new OptionData();
+
+ // Current value.
+ public int value
+ {
+ get
+ {
+ return m_Value;
+ }
+ set
+ {
+ if (Application.isPlaying && (value == m_Value || options.Count == 0))
+ return;
+
+ m_Value = Mathf.Clamp(value, 0, options.Count - 1);
+ RefreshShownValue();
+
+ // Notify all listeners
+ UISystemProfilerApi.AddMarker("Dropdown.value", this);
+ m_OnValueChanged.Invoke(m_Value);
+ }
+ }
+
+ protected Dropdown()
+ {}
+
+ protected override void Awake()
+ {
+ #if UNITY_EDITOR
+ if (!Application.isPlaying)
+ return;
+ #endif
+
+ m_AlphaTweenRunner = new TweenRunner<FloatTween>();
+ m_AlphaTweenRunner.Init(this);
+
+ if (m_CaptionImage)
+ m_CaptionImage.enabled = (m_CaptionImage.sprite != null);
+
+ if (m_Template)
+ m_Template.gameObject.SetActive(false);
+ }
+
+ #if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+
+ if (!IsActive())
+ return;
+
+ RefreshShownValue();
+ }
+
+ #endif
+
+ public void RefreshShownValue()
+ {
+ OptionData data = s_NoOptionData;
+
+ if (options.Count > 0)
+ data = options[Mathf.Clamp(m_Value, 0, options.Count - 1)];
+
+ if (m_CaptionText)
+ {
+ if (data != null && data.text != null)
+ m_CaptionText.text = data.text;
+ else
+ m_CaptionText.text = "";
+ }
+
+ if (m_CaptionImage)
+ {
+ if (data != null)
+ m_CaptionImage.sprite = data.image;
+ else
+ m_CaptionImage.sprite = null;
+ m_CaptionImage.enabled = (m_CaptionImage.sprite != null);
+ }
+ }
+
+ public void AddOptions(List<OptionData> options)
+ {
+ this.options.AddRange(options);
+ RefreshShownValue();
+ }
+
+ public void AddOptions(List<string> options)
+ {
+ for (int i = 0; i < options.Count; i++)
+ this.options.Add(new OptionData(options[i]));
+ RefreshShownValue();
+ }
+
+ public void AddOptions(List<Sprite> options)
+ {
+ for (int i = 0; i < options.Count; i++)
+ this.options.Add(new OptionData(options[i]));
+ RefreshShownValue();
+ }
+
+ public void ClearOptions()
+ {
+ options.Clear();
+ RefreshShownValue();
+ }
+
+ private void SetupTemplate()
+ {
+ validTemplate = false;
+
+ if (!m_Template)
+ {
+ Debug.LogError("The dropdown template is not assigned. The template needs to be assigned and must have a child GameObject with a Toggle component serving as the item.", this);
+ return;
+ }
+
+ GameObject templateGo = m_Template.gameObject;
+ templateGo.SetActive(true);
+ Toggle itemToggle = m_Template.GetComponentInChildren<Toggle>();
+
+ validTemplate = true;
+ if (!itemToggle || itemToggle.transform == template)
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The template must have a child GameObject with a Toggle component serving as the item.", template);
+ }
+ else if (!(itemToggle.transform.parent is RectTransform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The child GameObject with a Toggle component (the item) must have a RectTransform on its parent.", template);
+ }
+ else if (itemText != null && !itemText.transform.IsChildOf(itemToggle.transform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The Item Text must be on the item GameObject or children of it.", template);
+ }
+ else if (itemImage != null && !itemImage.transform.IsChildOf(itemToggle.transform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The Item Image must be on the item GameObject or children of it.", template);
+ }
+
+ if (!validTemplate)
+ {
+ templateGo.SetActive(false);
+ return;
+ }
+
+ DropdownItem item = itemToggle.gameObject.AddComponent<DropdownItem>();
+ item.text = m_ItemText;
+ item.image = m_ItemImage;
+ item.toggle = itemToggle;
+ item.rectTransform = (RectTransform)itemToggle.transform;
+
+ Canvas popupCanvas = GetOrAddComponent<Canvas>(templateGo);
+ popupCanvas.overrideSorting = true;
+ popupCanvas.sortingOrder = 30000;
+
+ GetOrAddComponent<GraphicRaycaster>(templateGo);
+ GetOrAddComponent<CanvasGroup>(templateGo);
+ templateGo.SetActive(false);
+
+ validTemplate = true;
+ }
+
+ private static T GetOrAddComponent<T>(GameObject go) where T : Component
+ {
+ T comp = go.GetComponent<T>();
+ if (!comp)
+ comp = go.AddComponent<T>();
+ return comp;
+ }
+
+ public virtual void OnPointerClick(PointerEventData eventData)
+ {
+ Show();
+ }
+
+ public virtual void OnSubmit(BaseEventData eventData)
+ {
+ Show();
+ }
+
+ public virtual void OnCancel(BaseEventData eventData)
+ {
+ Hide();
+ }
+
+ // Show the dropdown.
+ //
+ // Plan for dropdown scrolling to ensure dropdown is contained within screen.
+ //
+ // We assume the Canvas is the screen that the dropdown must be kept inside.
+ // This is always valid for screen space canvas modes.
+ // For world space canvases we don't know how it's used, but it could be e.g. for an in-game monitor.
+ // We consider it a fair constraint that the canvas must be big enough to contains dropdowns.
+ public void Show()
+ {
+ if (!IsActive() || !IsInteractable() || m_Dropdown != null)
+ return;
+
+ if (!validTemplate)
+ {
+ SetupTemplate();
+ if (!validTemplate)
+ return;
+ }
+
+ // Get root Canvas.
+ var list = ListPool<Canvas>.Get();
+ gameObject.GetComponentsInParent(false, list);
+ if (list.Count == 0)
+ return;
+ Canvas rootCanvas = list[0];
+ ListPool<Canvas>.Release(list);
+
+ m_Template.gameObject.SetActive(true);
+
+ // Instantiate the drop-down template
+ m_Dropdown = CreateDropdownList(m_Template.gameObject);
+ m_Dropdown.name = "Dropdown List";
+ m_Dropdown.SetActive(true);
+
+ // Make drop-down RectTransform have same values as original.
+ RectTransform dropdownRectTransform = m_Dropdown.transform as RectTransform;
+ dropdownRectTransform.SetParent(m_Template.transform.parent, false);
+
+ // Instantiate the drop-down list items
+
+ // Find the dropdown item and disable it.
+ DropdownItem itemTemplate = m_Dropdown.GetComponentInChildren<DropdownItem>();
+
+ GameObject content = itemTemplate.rectTransform.parent.gameObject;
+ RectTransform contentRectTransform = content.transform as RectTransform;
+ itemTemplate.rectTransform.gameObject.SetActive(true);
+
+ // Get the rects of the dropdown and item
+ Rect dropdownContentRect = contentRectTransform.rect;
+ Rect itemTemplateRect = itemTemplate.rectTransform.rect;
+
+ // Calculate the visual offset between the item's edges and the background's edges
+ Vector2 offsetMin = itemTemplateRect.min - dropdownContentRect.min + (Vector2)itemTemplate.rectTransform.localPosition;
+ Vector2 offsetMax = itemTemplateRect.max - dropdownContentRect.max + (Vector2)itemTemplate.rectTransform.localPosition;
+ Vector2 itemSize = itemTemplateRect.size;
+
+ m_Items.Clear();
+
+ Toggle prev = null;
+ for (int i = 0; i < options.Count; ++i)
+ {
+ OptionData data = options[i];
+ DropdownItem item = AddItem(data, value == i, itemTemplate, m_Items);
+ if (item == null)
+ continue;
+
+ // Automatically set up a toggle state change listener
+ item.toggle.isOn = value == i;
+ item.toggle.onValueChanged.AddListener(x => OnSelectItem(item.toggle));
+
+ // Select current option
+ if (item.toggle.isOn)
+ item.toggle.Select();
+
+ // Automatically set up explicit navigation
+ if (prev != null)
+ {
+ Navigation prevNav = prev.navigation;
+ Navigation toggleNav = item.toggle.navigation;
+ prevNav.mode = Navigation.Mode.Explicit;
+ toggleNav.mode = Navigation.Mode.Explicit;
+
+ prevNav.selectOnDown = item.toggle;
+ prevNav.selectOnRight = item.toggle;
+ toggleNav.selectOnLeft = prev;
+ toggleNav.selectOnUp = prev;
+
+ prev.navigation = prevNav;
+ item.toggle.navigation = toggleNav;
+ }
+ prev = item.toggle;
+ }
+
+ // Reposition all items now that all of them have been added
+ Vector2 sizeDelta = contentRectTransform.sizeDelta;
+ sizeDelta.y = itemSize.y * m_Items.Count + offsetMin.y - offsetMax.y;
+ contentRectTransform.sizeDelta = sizeDelta;
+
+ float extraSpace = dropdownRectTransform.rect.height - contentRectTransform.rect.height;
+ if (extraSpace > 0)
+ dropdownRectTransform.sizeDelta = new Vector2(dropdownRectTransform.sizeDelta.x, dropdownRectTransform.sizeDelta.y - extraSpace);
+
+ // Invert anchoring and position if dropdown is partially or fully outside of canvas rect.
+ // Typically this will have the effect of placing the dropdown above the button instead of below,
+ // but it works as inversion regardless of initial setup.
+ Vector3[] corners = new Vector3[4];
+ dropdownRectTransform.GetWorldCorners(corners);
+
+ RectTransform rootCanvasRectTransform = rootCanvas.transform as RectTransform;
+ Rect rootCanvasRect = rootCanvasRectTransform.rect;
+ for (int axis = 0; axis < 2; axis++)
+ {
+ bool outside = false;
+ for (int i = 0; i < 4; i++)
+ {
+ Vector3 corner = rootCanvasRectTransform.InverseTransformPoint(corners[i]);
+ if (corner[axis] < rootCanvasRect.min[axis] || corner[axis] > rootCanvasRect.max[axis])
+ {
+ outside = true;
+ break;
+ }
+ }
+ if (outside)
+ RectTransformUtility.FlipLayoutOnAxis(dropdownRectTransform, axis, false, false);
+ }
+
+ for (int i = 0; i < m_Items.Count; i++)
+ {
+ RectTransform itemRect = m_Items[i].rectTransform;
+ itemRect.anchorMin = new Vector2(itemRect.anchorMin.x, 0);
+ itemRect.anchorMax = new Vector2(itemRect.anchorMax.x, 0);
+ itemRect.anchoredPosition = new Vector2(itemRect.anchoredPosition.x, offsetMin.y + itemSize.y * (m_Items.Count - 1 - i) + itemSize.y * itemRect.pivot.y);
+ itemRect.sizeDelta = new Vector2(itemRect.sizeDelta.x, itemSize.y);
+ }
+
+ // Fade in the popup
+ AlphaFadeList(0.15f, 0f, 1f);
+
+ // Make drop-down template and item template inactive
+ m_Template.gameObject.SetActive(false);
+ itemTemplate.gameObject.SetActive(false);
+
+ m_Blocker = CreateBlocker(rootCanvas);
+ }
+
+ protected virtual GameObject CreateBlocker(Canvas rootCanvas)
+ {
+ // Create blocker GameObject.
+ GameObject blocker = new GameObject("Blocker");
+
+ // Setup blocker RectTransform to cover entire root canvas area.
+ RectTransform blockerRect = blocker.AddComponent<RectTransform>();
+ blockerRect.SetParent(rootCanvas.transform, false);
+ blockerRect.anchorMin = Vector3.zero;
+ blockerRect.anchorMax = Vector3.one;
+ blockerRect.sizeDelta = Vector2.zero;
+
+ // Make blocker be in separate canvas in same layer as dropdown and in layer just below it.
+ Canvas blockerCanvas = blocker.AddComponent<Canvas>();
+ blockerCanvas.overrideSorting = true;
+ Canvas dropdownCanvas = m_Dropdown.GetComponent<Canvas>();
+ blockerCanvas.sortingLayerID = dropdownCanvas.sortingLayerID;
+ blockerCanvas.sortingOrder = dropdownCanvas.sortingOrder - 1;
+
+ // Add raycaster since it's needed to block.
+ blocker.AddComponent<GraphicRaycaster>();
+
+ // Add image since it's needed to block, but make it clear.
+ Image blockerImage = blocker.AddComponent<Image>();
+ blockerImage.color = Color.clear;
+
+ // Add button since it's needed to block, and to close the dropdown when blocking area is clicked.
+ Button blockerButton = blocker.AddComponent<Button>();
+ blockerButton.onClick.AddListener(Hide);
+
+ return blocker;
+ }
+
+ protected virtual void DestroyBlocker(GameObject blocker)
+ {
+ Destroy(blocker);
+ }
+
+ protected virtual GameObject CreateDropdownList(GameObject template)
+ {
+ return (GameObject)Instantiate(template);
+ }
+
+ protected virtual void DestroyDropdownList(GameObject dropdownList)
+ {
+ Destroy(dropdownList);
+ }
+
+ protected virtual DropdownItem CreateItem(DropdownItem itemTemplate)
+ {
+ return (DropdownItem)Instantiate(itemTemplate);
+ }
+
+ protected virtual void DestroyItem(DropdownItem item)
+ {
+ // No action needed since destroying the dropdown list destroys all contained items as well.
+ }
+
+ // Add a new drop-down list item with the specified values.
+ private DropdownItem AddItem(OptionData data, bool selected, DropdownItem itemTemplate, List<DropdownItem> items)
+ {
+ // Add a new item to the dropdown.
+ DropdownItem item = CreateItem(itemTemplate);
+ item.rectTransform.SetParent(itemTemplate.rectTransform.parent, false);
+
+ item.gameObject.SetActive(true);
+ item.gameObject.name = "Item " + items.Count + (data.text != null ? ": " + data.text : "");
+
+ if (item.toggle != null)
+ {
+ item.toggle.isOn = false;
+ }
+
+ // Set the item's data
+ if (item.text)
+ item.text.text = data.text;
+ if (item.image)
+ {
+ item.image.sprite = data.image;
+ item.image.enabled = (item.image.sprite != null);
+ }
+
+ items.Add(item);
+ return item;
+ }
+
+ private void AlphaFadeList(float duration, float alpha)
+ {
+ CanvasGroup group = m_Dropdown.GetComponent<CanvasGroup>();
+ AlphaFadeList(duration, group.alpha, alpha);
+ }
+
+ private void AlphaFadeList(float duration, float start, float end)
+ {
+ if (end.Equals(start))
+ return;
+
+ FloatTween tween = new FloatTween {duration = duration, startValue = start, targetValue = end};
+ tween.AddOnChangedCallback(SetAlpha);
+ tween.ignoreTimeScale = true;
+ m_AlphaTweenRunner.StartTween(tween);
+ }
+
+ private void SetAlpha(float alpha)
+ {
+ if (!m_Dropdown)
+ return;
+ CanvasGroup group = m_Dropdown.GetComponent<CanvasGroup>();
+ group.alpha = alpha;
+ }
+
+ // Hide the dropdown.
+ public void Hide()
+ {
+ if (m_Dropdown != null)
+ {
+ AlphaFadeList(0.15f, 0f);
+
+ // User could have disabled the dropdown during the OnValueChanged call.
+ if (IsActive())
+ StartCoroutine(DelayedDestroyDropdownList(0.15f));
+ }
+ if (m_Blocker != null)
+ DestroyBlocker(m_Blocker);
+ m_Blocker = null;
+ Select();
+ }
+
+ private IEnumerator DelayedDestroyDropdownList(float delay)
+ {
+ yield return new WaitForSecondsRealtime(delay);
+ for (int i = 0; i < m_Items.Count; i++)
+ {
+ if (m_Items[i] != null)
+ DestroyItem(m_Items[i]);
+ }
+ m_Items.Clear();
+ if (m_Dropdown != null)
+ DestroyDropdownList(m_Dropdown);
+ m_Dropdown = null;
+ }
+
+ // Change the value and hide the dropdown.
+ private void OnSelectItem(Toggle toggle)
+ {
+ if (!toggle.isOn)
+ toggle.isOn = true;
+
+ int selectedIndex = -1;
+ Transform tr = toggle.transform;
+ Transform parent = tr.parent;
+ for (int i = 0; i < parent.childCount; i++)
+ {
+ if (parent.GetChild(i) == tr)
+ {
+ // Subtract one to account for template child.
+ selectedIndex = i - 1;
+ break;
+ }
+ }
+
+ if (selectedIndex < 0)
+ return;
+
+ value = selectedIndex;
+ Hide();
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs.meta
new file mode 100644
index 0000000..a80360c
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Dropdown.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bfed003f40ad7ff4ba0ba7ccf49021aa
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/InputField.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/InputField.cs
new file mode 100644
index 0000000..1f3267a
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/InputField.cs
@@ -0,0 +1,2484 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using UnityEngine.Serialization;
+#if UNITY_EDITOR
+using UnityEditor;
+#endif
+
+namespace UnityEngine.UI
+{
+ /// <summary>
+ /// Editable text input field.
+ /// </summary>
+
+ [AddComponentMenu("UI/Input Field", 31)]
+ public class InputField
+ : Selectable,
+ IUpdateSelectedHandler,
+ IBeginDragHandler,
+ IDragHandler,
+ IEndDragHandler,
+ IPointerClickHandler,
+ ISubmitHandler,
+ ICanvasElement,
+ ILayoutElement
+ {
+ // Setting the content type acts as a shortcut for setting a combination of InputType, CharacterValidation, LineType, and TouchScreenKeyboardType
+ public enum ContentType
+ {
+ Standard,
+ Autocorrected,
+ IntegerNumber,
+ DecimalNumber,
+ Alphanumeric,
+ Name,
+ EmailAddress,
+ Password,
+ Pin,
+ Custom
+ }
+
+ public enum InputType
+ {
+ Standard,
+ AutoCorrect,
+ Password,
+ }
+
+ public enum CharacterValidation
+ {
+ None,
+ Integer,
+ Decimal,
+ Alphanumeric,
+ Name,
+ EmailAddress
+ }
+
+ public enum LineType
+ {
+ SingleLine,
+ MultiLineSubmit,
+ MultiLineNewline
+ }
+
+ public delegate char OnValidateInput(string text, int charIndex, char addedChar);
+
+ [Serializable]
+ public class SubmitEvent : UnityEvent<string> {}
+
+ [Serializable]
+ public class OnChangeEvent : UnityEvent<string> {}
+
+ protected TouchScreenKeyboard m_Keyboard;
+ static private readonly char[] kSeparators = { ' ', '.', ',', '\t', '\r', '\n' };
+
+ /// <summary>
+ /// Text Text used to display the input's value.
+ /// </summary>
+
+ [SerializeField]
+ [FormerlySerializedAs("text")]
+ protected Text m_TextComponent;
+
+ [SerializeField]
+ protected Graphic m_Placeholder;
+
+ [SerializeField]
+ private ContentType m_ContentType = ContentType.Standard;
+
+ /// <summary>
+ /// Type of data expected by the input field.
+ /// </summary>
+ [FormerlySerializedAs("inputType")]
+ [SerializeField]
+ private InputType m_InputType = InputType.Standard;
+
+ /// <summary>
+ /// The character used to hide text in password field.
+ /// </summary>
+ [FormerlySerializedAs("asteriskChar")]
+ [SerializeField]
+ private char m_AsteriskChar = '*';
+
+ /// <summary>
+ /// Keyboard type applies to mobile keyboards that get shown.
+ /// </summary>
+ [FormerlySerializedAs("keyboardType")]
+ [SerializeField]
+ private TouchScreenKeyboardType m_KeyboardType = TouchScreenKeyboardType.Default;
+
+ [SerializeField]
+ private LineType m_LineType = LineType.SingleLine;
+
+ /// <summary>
+ /// Should hide mobile input.
+ /// </summary>
+
+ [FormerlySerializedAs("hideMobileInput")]
+ [SerializeField]
+ private bool m_HideMobileInput = false;
+
+ /// <summary>
+ /// What kind of validation to use with the input field's data.
+ /// </summary>
+ [FormerlySerializedAs("validation")]
+ [SerializeField]
+ private CharacterValidation m_CharacterValidation = CharacterValidation.None;
+
+ /// <summary>
+ /// Maximum number of characters allowed before input no longer works.
+ /// </summary>
+ [FormerlySerializedAs("characterLimit")]
+ [SerializeField]
+ private int m_CharacterLimit = 0;
+
+ /// <summary>
+ /// Event delegates triggered when the input field submits its data.
+ /// </summary>
+ [FormerlySerializedAs("onSubmit")]
+ [FormerlySerializedAs("m_OnSubmit")]
+ [FormerlySerializedAs("m_EndEdit")]
+ [SerializeField]
+ private SubmitEvent m_OnEndEdit = new SubmitEvent();
+
+ /// <summary>
+ /// Event delegates triggered when the input field changes its data.
+ /// </summary>
+ [FormerlySerializedAs("onValueChange")]
+ [FormerlySerializedAs("m_OnValueChange")]
+ [SerializeField]
+ private OnChangeEvent m_OnValueChanged = new OnChangeEvent();
+
+ /// <summary>
+ /// Custom validation callback.
+ /// </summary>
+ [FormerlySerializedAs("onValidateInput")]
+ [SerializeField]
+ private OnValidateInput m_OnValidateInput;
+
+ [SerializeField]
+ private Color m_CaretColor = new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f);
+
+ [SerializeField]
+ private bool m_CustomCaretColor = false;
+
+ [FormerlySerializedAs("selectionColor")]
+ [SerializeField]
+ private Color m_SelectionColor = new Color(168f / 255f, 206f / 255f, 255f / 255f, 192f / 255f);
+
+ /// <summary>
+ /// Input field's value.
+ /// </summary>
+
+ [SerializeField]
+ [FormerlySerializedAs("mValue")]
+ protected string m_Text = string.Empty;
+
+ [SerializeField]
+ [Range(0f, 4f)]
+ private float m_CaretBlinkRate = 0.85f;
+
+ [SerializeField]
+ [Range(1, 5)]
+ private int m_CaretWidth = 1;
+
+ [SerializeField]
+ private bool m_ReadOnly = false;
+
+ protected int m_CaretPosition = 0;
+ protected int m_CaretSelectPosition = 0;
+ private RectTransform caretRectTrans = null;
+ protected UIVertex[] m_CursorVerts = null;
+ private TextGenerator m_InputTextCache;
+ private CanvasRenderer m_CachedInputRenderer;
+ private bool m_PreventFontCallback = false;
+ [NonSerialized] protected Mesh m_Mesh;
+ private bool m_AllowInput = false;
+ private bool m_ShouldActivateNextUpdate = false;
+ private bool m_UpdateDrag = false;
+ private bool m_DragPositionOutOfBounds = false;
+ private const float kHScrollSpeed = 0.05f;
+ private const float kVScrollSpeed = 0.10f;
+ protected bool m_CaretVisible;
+ private Coroutine m_BlinkCoroutine = null;
+ private float m_BlinkStartTime = 0.0f;
+ protected int m_DrawStart = 0;
+ protected int m_DrawEnd = 0;
+ private Coroutine m_DragCoroutine = null;
+ private string m_OriginalText = "";
+ private bool m_WasCanceled = false;
+ private bool m_HasDoneFocusTransition = false;
+
+ private BaseInput input
+ {
+ get
+ {
+ if (EventSystem.current && EventSystem.current.currentInputModule)
+ return EventSystem.current.currentInputModule.input;
+ return null;
+ }
+ }
+
+ private string compositionString
+ {
+ get { return input != null ? input.compositionString : Input.compositionString; }
+ }
+
+ // Doesn't include dot and @ on purpose! See usage for details.
+ const string kEmailSpecialCharacters = "!#$%&'*+-/=?^_`{|}~";
+
+ protected InputField()
+ {
+ EnforceTextHOverflow();
+ }
+
+ protected Mesh mesh
+ {
+ get
+ {
+ if (m_Mesh == null)
+ m_Mesh = new Mesh();
+ return m_Mesh;
+ }
+ }
+
+ protected TextGenerator cachedInputTextGenerator
+ {
+ get
+ {
+ if (m_InputTextCache == null)
+ m_InputTextCache = new TextGenerator();
+
+ return m_InputTextCache;
+ }
+ }
+
+ /// <summary>
+ /// Should the mobile keyboard input be hidden.
+ /// </summary>
+
+ public bool shouldHideMobileInput
+ {
+ set
+ {
+ SetPropertyUtility.SetStruct(ref m_HideMobileInput, value);
+ }
+ get
+ {
+ switch (Application.platform)
+ {
+ case RuntimePlatform.Android:
+ case RuntimePlatform.IPhonePlayer:
+ case RuntimePlatform.TizenPlayer:
+ case RuntimePlatform.tvOS:
+ return m_HideMobileInput;
+ }
+
+ return true;
+ }
+ }
+
+ bool shouldActivateOnSelect
+ {
+ get
+ {
+ return Application.platform != RuntimePlatform.tvOS;
+ }
+ }
+
+ /// <summary>
+ /// Input field's current text value.
+ /// </summary>
+
+ public string text
+ {
+ get
+ {
+ return m_Text;
+ }
+ set
+ {
+ if (this.text == value)
+ return;
+ if (value == null)
+ value = "";
+ value = value.Replace("\0", string.Empty); // remove embedded nulls
+ if (m_LineType == LineType.SingleLine)
+ value = value.Replace("\n", "").Replace("\t", "");
+
+ // If we have an input validator, validate the input and apply the character limit at the same time.
+ if (onValidateInput != null || characterValidation != CharacterValidation.None)
+ {
+ m_Text = "";
+ OnValidateInput validatorMethod = onValidateInput ?? Validate;
+ m_CaretPosition = m_CaretSelectPosition = value.Length;
+ int charactersToCheck = characterLimit > 0 ? Math.Min(characterLimit, value.Length) : value.Length;
+ for (int i = 0; i < charactersToCheck; ++i)
+ {
+ char c = validatorMethod(m_Text, m_Text.Length, value[i]);
+ if (c != 0)
+ m_Text += c;
+ }
+ }
+ else
+ {
+ m_Text = characterLimit > 0 && value.Length > characterLimit ? value.Substring(0, characterLimit) : value;
+ }
+
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ {
+ SendOnValueChangedAndUpdateLabel();
+ return;
+ }
+#endif
+
+ if (m_Keyboard != null)
+ m_Keyboard.text = m_Text;
+
+ if (m_CaretPosition > m_Text.Length)
+ m_CaretPosition = m_CaretSelectPosition = m_Text.Length;
+ else if (m_CaretSelectPosition > m_Text.Length)
+ m_CaretSelectPosition = m_Text.Length;
+ SendOnValueChangedAndUpdateLabel();
+ }
+ }
+
+ public bool isFocused
+ {
+ get { return m_AllowInput; }
+ }
+
+ public float caretBlinkRate
+ {
+ get { return m_CaretBlinkRate; }
+ set
+ {
+ if (SetPropertyUtility.SetStruct(ref m_CaretBlinkRate, value))
+ {
+ if (m_AllowInput)
+ SetCaretActive();
+ }
+ }
+ }
+
+ public int caretWidth { get { return m_CaretWidth; } set { if (SetPropertyUtility.SetStruct(ref m_CaretWidth, value)) MarkGeometryAsDirty(); } }
+
+ public Text textComponent
+ {
+ get { return m_TextComponent; }
+ set
+ {
+ if (m_TextComponent != null)
+ {
+ m_TextComponent.UnregisterDirtyVerticesCallback(MarkGeometryAsDirty);
+ m_TextComponent.UnregisterDirtyVerticesCallback(UpdateLabel);
+ m_TextComponent.UnregisterDirtyMaterialCallback(UpdateCaretMaterial);
+ }
+
+ if (SetPropertyUtility.SetClass(ref m_TextComponent, value))
+ {
+ EnforceTextHOverflow();
+ if (m_TextComponent != null)
+ {
+ m_TextComponent.RegisterDirtyVerticesCallback(MarkGeometryAsDirty);
+ m_TextComponent.RegisterDirtyVerticesCallback(UpdateLabel);
+ m_TextComponent.RegisterDirtyMaterialCallback(UpdateCaretMaterial);
+ }
+ }
+ }
+ }
+
+ public Graphic placeholder { get { return m_Placeholder; } set { SetPropertyUtility.SetClass(ref m_Placeholder, value); } }
+
+ public Color caretColor { get { return customCaretColor ? m_CaretColor : textComponent.color; } set { if (SetPropertyUtility.SetColor(ref m_CaretColor, value)) MarkGeometryAsDirty(); } }
+
+ public bool customCaretColor { get { return m_CustomCaretColor; } set { if (m_CustomCaretColor != value) { m_CustomCaretColor = value; MarkGeometryAsDirty(); } } }
+
+ public Color selectionColor { get { return m_SelectionColor; } set { if (SetPropertyUtility.SetColor(ref m_SelectionColor, value)) MarkGeometryAsDirty(); } }
+
+ public SubmitEvent onEndEdit { get { return m_OnEndEdit; } set { SetPropertyUtility.SetClass(ref m_OnEndEdit, value); } }
+
+ [Obsolete("onValueChange has been renamed to onValueChanged")]
+ public OnChangeEvent onValueChange { get { return onValueChanged; } set { onValueChanged = value; } }
+
+ public OnChangeEvent onValueChanged { get { return m_OnValueChanged; } set { SetPropertyUtility.SetClass(ref m_OnValueChanged, value); } }
+
+ public OnValidateInput onValidateInput { get { return m_OnValidateInput; } set { SetPropertyUtility.SetClass(ref m_OnValidateInput, value); } }
+
+ public int characterLimit { get { return m_CharacterLimit; } set { if (SetPropertyUtility.SetStruct(ref m_CharacterLimit, Math.Max(0, value))) UpdateLabel(); } }
+
+ // Content Type related
+
+ public ContentType contentType { get { return m_ContentType; } set { if (SetPropertyUtility.SetStruct(ref m_ContentType, value)) EnforceContentType(); } }
+
+ public LineType lineType
+ {
+ get { return m_LineType; }
+ set
+ {
+ if (SetPropertyUtility.SetStruct(ref m_LineType, value))
+ {
+ SetToCustomIfContentTypeIsNot(ContentType.Standard, ContentType.Autocorrected);
+ EnforceTextHOverflow();
+ }
+ }
+ }
+
+ public InputType inputType { get { return m_InputType; } set { if (SetPropertyUtility.SetStruct(ref m_InputType, value)) SetToCustom(); } }
+
+ public TouchScreenKeyboardType keyboardType
+ {
+ get { return m_KeyboardType; }
+ set
+ {
+#if UNITY_EDITOR
+ if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.WiiU)
+ {
+ if (value == TouchScreenKeyboardType.NintendoNetworkAccount)
+ Debug.LogWarning("Invalid InputField.keyboardType value set. TouchScreenKeyboardType.NintendoNetworkAccount only applies to the Wii U. InputField.keyboardType will default to TouchScreenKeyboardType.Default .");
+ }
+#elif !UNITY_WIIU
+ if (value == TouchScreenKeyboardType.NintendoNetworkAccount)
+ Debug.LogWarning("Invalid InputField.keyboardType value set. TouchScreenKeyboardType.NintendoNetworkAccount only applies to the Wii U. InputField.keyboardType will default to TouchScreenKeyboardType.Default .");
+#endif
+ if (SetPropertyUtility.SetStruct(ref m_KeyboardType, value))
+ SetToCustom();
+ }
+ }
+
+ public CharacterValidation characterValidation { get { return m_CharacterValidation; } set { if (SetPropertyUtility.SetStruct(ref m_CharacterValidation, value)) SetToCustom(); } }
+
+ public bool readOnly { get { return m_ReadOnly; } set { m_ReadOnly = value; } }
+
+ // Derived property
+ public bool multiLine { get { return m_LineType == LineType.MultiLineNewline || lineType == LineType.MultiLineSubmit; } }
+ // Not shown in Inspector.
+ public char asteriskChar { get { return m_AsteriskChar; } set { if (SetPropertyUtility.SetStruct(ref m_AsteriskChar, value)) UpdateLabel(); } }
+ public bool wasCanceled { get { return m_WasCanceled; } }
+
+ protected void ClampPos(ref int pos)
+ {
+ if (pos < 0) pos = 0;
+ else if (pos > text.Length) pos = text.Length;
+ }
+
+ /// <summary>
+ /// Current position of the cursor.
+ /// Getters are public Setters are protected
+ /// </summary>
+
+ protected int caretPositionInternal { get { return m_CaretPosition + compositionString.Length; } set { m_CaretPosition = value; ClampPos(ref m_CaretPosition); } }
+ protected int caretSelectPositionInternal { get { return m_CaretSelectPosition + compositionString.Length; } set { m_CaretSelectPosition = value; ClampPos(ref m_CaretSelectPosition); } }
+ private bool hasSelection { get { return caretPositionInternal != caretSelectPositionInternal; } }
+
+#if UNITY_EDITOR
+ [Obsolete("caretSelectPosition has been deprecated. Use selectionFocusPosition instead (UnityUpgradable) -> selectionFocusPosition", true)]
+ public int caretSelectPosition { get { return selectionFocusPosition; } protected set { selectionFocusPosition = value; } }
+#endif
+
+ /// <summary>
+ /// Get: Returns the focus position as thats the position that moves around even during selection.
+ /// Set: Set both the anchor and focus position such that a selection doesn't happen
+ /// </summary>
+
+ public int caretPosition
+ {
+ get { return m_CaretSelectPosition + compositionString.Length; }
+ set { selectionAnchorPosition = value; selectionFocusPosition = value; }
+ }
+
+ /// <summary>
+ /// Get: Returns the fixed position of selection
+ /// Set: If Input.compositionString is 0 set the fixed position
+ /// </summary>
+
+ public int selectionAnchorPosition
+ {
+ get { return m_CaretPosition + compositionString.Length; }
+ set
+ {
+ if (compositionString.Length != 0)
+ return;
+
+ m_CaretPosition = value;
+ ClampPos(ref m_CaretPosition);
+ }
+ }
+
+ /// <summary>
+ /// Get: Returns the variable position of selection
+ /// Set: If Input.compositionString is 0 set the variable position
+ /// </summary>
+
+ public int selectionFocusPosition
+ {
+ get { return m_CaretSelectPosition + compositionString.Length; }
+ set
+ {
+ if (compositionString.Length != 0)
+ return;
+
+ m_CaretSelectPosition = value;
+ ClampPos(ref m_CaretSelectPosition);
+ }
+ }
+
+ #if UNITY_EDITOR
+ // Remember: This is NOT related to text validation!
+ // This is Unity's own OnValidate method which is invoked when changing values in the Inspector.
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+ EnforceContentType();
+ EnforceTextHOverflow();
+
+ m_CharacterLimit = Math.Max(0, m_CharacterLimit);
+
+ //This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
+ if (!IsActive())
+ return;
+
+ UpdateLabel();
+ if (m_AllowInput)
+ SetCaretActive();
+ }
+
+ #endif // if UNITY_EDITOR
+
+ 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();
+ }
+ }
+
+ protected override void OnDisable()
+ {
+ // the coroutine will be terminated, so this will ensure it restarts when we are next activated
+ m_BlinkCoroutine = null;
+
+ DeactivateInputField();
+ if (m_TextComponent != null)
+ {
+ m_TextComponent.UnregisterDirtyVerticesCallback(MarkGeometryAsDirty);
+ m_TextComponent.UnregisterDirtyVerticesCallback(UpdateLabel);
+ m_TextComponent.UnregisterDirtyMaterialCallback(UpdateCaretMaterial);
+ }
+ CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
+
+ // Clear needs to be called otherwise sync never happens as the object is disabled.
+ if (m_CachedInputRenderer != null)
+ m_CachedInputRenderer.Clear();
+
+ if (m_Mesh != null)
+ DestroyImmediate(m_Mesh);
+ m_Mesh = null;
+
+ base.OnDisable();
+ }
+
+ IEnumerator CaretBlink()
+ {
+ // Always ensure caret is initially visible since it can otherwise be confusing for a moment.
+ m_CaretVisible = true;
+ yield return null;
+
+ while (isFocused && m_CaretBlinkRate > 0)
+ {
+ // the blink rate is expressed as a frequency
+ float blinkPeriod = 1f / m_CaretBlinkRate;
+
+ // the caret should be ON if we are in the first half of the blink period
+ bool blinkState = (Time.unscaledTime - m_BlinkStartTime) % blinkPeriod < blinkPeriod / 2;
+ if (m_CaretVisible != blinkState)
+ {
+ m_CaretVisible = blinkState;
+ if (!hasSelection)
+ MarkGeometryAsDirty();
+ }
+
+ // Then wait again.
+ yield return null;
+ }
+ m_BlinkCoroutine = null;
+ }
+
+ void SetCaretVisible()
+ {
+ if (!m_AllowInput)
+ return;
+
+ m_CaretVisible = true;
+ m_BlinkStartTime = Time.unscaledTime;
+ SetCaretActive();
+ }
+
+ // SetCaretActive will not set the caret immediately visible - it will wait for the next time to blink.
+ // However, it will handle things correctly if the blink speed changed from zero to non-zero or non-zero to zero.
+ void SetCaretActive()
+ {
+ if (!m_AllowInput)
+ return;
+
+ if (m_CaretBlinkRate > 0.0f)
+ {
+ if (m_BlinkCoroutine == null)
+ m_BlinkCoroutine = StartCoroutine(CaretBlink());
+ }
+ else
+ {
+ m_CaretVisible = true;
+ }
+ }
+
+ private void UpdateCaretMaterial()
+ {
+ if (m_TextComponent != null && m_CachedInputRenderer != null)
+ m_CachedInputRenderer.SetMaterial(m_TextComponent.GetModifiedMaterial(Graphic.defaultGraphicMaterial), Texture2D.whiteTexture);
+ }
+
+ protected void OnFocus()
+ {
+ SelectAll();
+ }
+
+ protected void SelectAll()
+ {
+ caretPositionInternal = text.Length;
+ caretSelectPositionInternal = 0;
+ }
+
+ public void MoveTextEnd(bool shift)
+ {
+ int position = text.Length;
+
+ if (shift)
+ {
+ caretSelectPositionInternal = position;
+ }
+ else
+ {
+ caretPositionInternal = position;
+ caretSelectPositionInternal = caretPositionInternal;
+ }
+ UpdateLabel();
+ }
+
+ public void MoveTextStart(bool shift)
+ {
+ int position = 0;
+
+ if (shift)
+ {
+ caretSelectPositionInternal = position;
+ }
+ else
+ {
+ caretPositionInternal = position;
+ caretSelectPositionInternal = caretPositionInternal;
+ }
+
+ UpdateLabel();
+ }
+
+ static string clipboard
+ {
+ get
+ {
+ return GUIUtility.systemCopyBuffer;
+ }
+ set
+ {
+ GUIUtility.systemCopyBuffer = value;
+ }
+ }
+
+ private bool InPlaceEditing()
+ {
+ return !TouchScreenKeyboard.isSupported;
+ }
+
+ void UpdateCaretFromKeyboard()
+ {
+ var selectionRange = m_Keyboard.selection;
+
+ var selectionStart = selectionRange.start;
+ var selectionEnd = selectionRange.end;
+
+ var caretChanged = false;
+
+ if (caretPositionInternal != selectionStart)
+ {
+ caretChanged = true;
+ caretPositionInternal = selectionStart;
+ }
+
+ if (caretSelectPositionInternal != selectionEnd)
+ {
+ caretSelectPositionInternal = selectionEnd;
+ caretChanged = true;
+ }
+
+ if (caretChanged)
+ {
+ m_BlinkStartTime = Time.unscaledTime;
+
+ UpdateLabel();
+ }
+ }
+
+ /// <summary>
+ /// Update the text based on input.
+ /// </summary>
+ // TODO: Make LateUpdate a coroutine instead. Allows us to control the update to only be when the field is active.
+ protected virtual void LateUpdate()
+ {
+ // Only activate if we are not already activated.
+ if (m_ShouldActivateNextUpdate)
+ {
+ if (!isFocused)
+ {
+ ActivateInputFieldInternal();
+ m_ShouldActivateNextUpdate = false;
+ return;
+ }
+
+ // Reset as we are already activated.
+ m_ShouldActivateNextUpdate = false;
+ }
+
+ if (InPlaceEditing() || !isFocused)
+ return;
+
+ AssignPositioningIfNeeded();
+
+ if (m_Keyboard == null || m_Keyboard.done)
+ {
+ if (m_Keyboard != null)
+ {
+ if (!m_ReadOnly)
+ text = m_Keyboard.text;
+
+ if (m_Keyboard.wasCanceled)
+ m_WasCanceled = true;
+ }
+
+ OnDeselect(null);
+ return;
+ }
+
+ string val = m_Keyboard.text;
+
+ if (m_Text != val)
+ {
+ if (m_ReadOnly)
+ {
+ m_Keyboard.text = m_Text;
+ }
+ else
+ {
+ m_Text = "";
+
+ for (int i = 0; i < val.Length; ++i)
+ {
+ char c = val[i];
+
+ if (c == '\r' || (int)c == 3)
+ c = '\n';
+
+ if (onValidateInput != null)
+ c = onValidateInput(m_Text, m_Text.Length, c);
+ else if (characterValidation != CharacterValidation.None)
+ c = Validate(m_Text, m_Text.Length, c);
+
+ if (lineType == LineType.MultiLineSubmit && c == '\n')
+ {
+ m_Keyboard.text = m_Text;
+
+ OnDeselect(null);
+ return;
+ }
+
+ if (c != 0)
+ m_Text += c;
+ }
+
+ if (characterLimit > 0 && m_Text.Length > characterLimit)
+ m_Text = m_Text.Substring(0, characterLimit);
+
+ if (m_Keyboard.canGetSelection)
+ {
+ UpdateCaretFromKeyboard();
+ }
+ else
+ {
+ caretPositionInternal = caretSelectPositionInternal = m_Text.Length;
+ }
+
+ // Set keyboard text before updating label, as we might have changed it with validation
+ // and update label will take the old value from keyboard if we don't change it here
+ if (m_Text != val)
+ m_Keyboard.text = m_Text;
+
+ SendOnValueChangedAndUpdateLabel();
+ }
+ }
+ else if (m_Keyboard.canGetSelection)
+ {
+ UpdateCaretFromKeyboard();
+ }
+
+
+ if (m_Keyboard.done)
+ {
+ if (m_Keyboard.wasCanceled)
+ m_WasCanceled = true;
+
+ OnDeselect(null);
+ }
+ }
+
+ [Obsolete("This function is no longer used. Please use RectTransformUtility.ScreenPointToLocalPointInRectangle() instead.")]
+ public Vector2 ScreenToLocal(Vector2 screen)
+ {
+ var theCanvas = m_TextComponent.canvas;
+ if (theCanvas == null)
+ return screen;
+
+ Vector3 pos = Vector3.zero;
+ if (theCanvas.renderMode == RenderMode.ScreenSpaceOverlay)
+ {
+ pos = m_TextComponent.transform.InverseTransformPoint(screen);
+ }
+ else if (theCanvas.worldCamera != null)
+ {
+ Ray mouseRay = theCanvas.worldCamera.ScreenPointToRay(screen);
+ float dist;
+ Plane plane = new Plane(m_TextComponent.transform.forward, m_TextComponent.transform.position);
+ plane.Raycast(mouseRay, out dist);
+ pos = m_TextComponent.transform.InverseTransformPoint(mouseRay.GetPoint(dist));
+ }
+ return new Vector2(pos.x, pos.y);
+ }
+
+ private int GetUnclampedCharacterLineFromPosition(Vector2 pos, TextGenerator generator)
+ {
+ if (!multiLine)
+ return 0;
+
+ // transform y to local scale
+ float y = pos.y * m_TextComponent.pixelsPerUnit;
+ float lastBottomY = 0.0f;
+
+ for (int i = 0; i < generator.lineCount; ++i)
+ {
+ float topY = generator.lines[i].topY;
+ float bottomY = topY - generator.lines[i].height;
+
+ // pos is somewhere in the leading above this line
+ if (y > topY)
+ {
+ // determine which line we're closer to
+ float leading = topY - lastBottomY;
+ if (y > topY - 0.5f * leading)
+ return i - 1;
+ else
+ return i;
+ }
+
+ if (y > bottomY)
+ return i;
+
+ lastBottomY = bottomY;
+ }
+
+ // Position is after last line.
+ return generator.lineCount;
+ }
+
+ /// <summary>
+ /// Given an input position in local space on the Text return the index for the selection cursor at this position.
+ /// </summary>
+
+ protected int GetCharacterIndexFromPosition(Vector2 pos)
+ {
+ TextGenerator gen = m_TextComponent.cachedTextGenerator;
+
+ if (gen.lineCount == 0)
+ return 0;
+
+ int line = GetUnclampedCharacterLineFromPosition(pos, gen);
+ if (line < 0)
+ return 0;
+ if (line >= gen.lineCount)
+ return gen.characterCountVisible;
+
+ int startCharIndex = gen.lines[line].startCharIdx;
+ int endCharIndex = GetLineEndPosition(gen, line);
+
+ for (int i = startCharIndex; i < endCharIndex; i++)
+ {
+ if (i >= gen.characterCountVisible)
+ break;
+
+ UICharInfo charInfo = gen.characters[i];
+ Vector2 charPos = charInfo.cursorPos / m_TextComponent.pixelsPerUnit;
+
+ float distToCharStart = pos.x - charPos.x;
+ float distToCharEnd = charPos.x + (charInfo.charWidth / m_TextComponent.pixelsPerUnit) - pos.x;
+ if (distToCharStart < distToCharEnd)
+ return i;
+ }
+
+ return endCharIndex;
+ }
+
+ private bool MayDrag(PointerEventData eventData)
+ {
+ return IsActive() &&
+ IsInteractable() &&
+ eventData.button == PointerEventData.InputButton.Left &&
+ m_TextComponent != null &&
+ m_Keyboard == null;
+ }
+
+ public virtual void OnBeginDrag(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ m_UpdateDrag = true;
+ }
+
+ public virtual void OnDrag(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ Vector2 localMousePos;
+ RectTransformUtility.ScreenPointToLocalPointInRectangle(textComponent.rectTransform, eventData.position, eventData.pressEventCamera, out localMousePos);
+ caretSelectPositionInternal = GetCharacterIndexFromPosition(localMousePos) + m_DrawStart;
+ MarkGeometryAsDirty();
+
+ m_DragPositionOutOfBounds = !RectTransformUtility.RectangleContainsScreenPoint(textComponent.rectTransform, eventData.position, eventData.pressEventCamera);
+ if (m_DragPositionOutOfBounds && m_DragCoroutine == null)
+ m_DragCoroutine = StartCoroutine(MouseDragOutsideRect(eventData));
+
+ eventData.Use();
+ }
+
+ IEnumerator MouseDragOutsideRect(PointerEventData eventData)
+ {
+ while (m_UpdateDrag && m_DragPositionOutOfBounds)
+ {
+ Vector2 localMousePos;
+ RectTransformUtility.ScreenPointToLocalPointInRectangle(textComponent.rectTransform, eventData.position, eventData.pressEventCamera, out localMousePos);
+
+ Rect rect = textComponent.rectTransform.rect;
+
+ if (multiLine)
+ {
+ if (localMousePos.y > rect.yMax)
+ MoveUp(true, true);
+ else if (localMousePos.y < rect.yMin)
+ MoveDown(true, true);
+ }
+ else
+ {
+ if (localMousePos.x < rect.xMin)
+ MoveLeft(true, false);
+ else if (localMousePos.x > rect.xMax)
+ MoveRight(true, false);
+ }
+ UpdateLabel();
+ float delay = multiLine ? kVScrollSpeed : kHScrollSpeed;
+ yield return new WaitForSecondsRealtime(delay);
+ }
+ m_DragCoroutine = null;
+ }
+
+ public virtual void OnEndDrag(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ m_UpdateDrag = false;
+ }
+
+ public override void OnPointerDown(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ EventSystem.current.SetSelectedGameObject(gameObject, eventData);
+
+ bool hadFocusBefore = m_AllowInput;
+ base.OnPointerDown(eventData);
+
+ if (!InPlaceEditing())
+ {
+ if (m_Keyboard == null || !m_Keyboard.active)
+ {
+ OnSelect(eventData);
+ return;
+ }
+ }
+
+ // Only set caret position if we didn't just get focus now.
+ // Otherwise it will overwrite the select all on focus.
+ if (hadFocusBefore)
+ {
+ Vector2 localMousePos;
+ RectTransformUtility.ScreenPointToLocalPointInRectangle(textComponent.rectTransform, eventData.position, eventData.pressEventCamera, out localMousePos);
+
+ caretSelectPositionInternal = caretPositionInternal = GetCharacterIndexFromPosition(localMousePos) + m_DrawStart;
+ }
+ UpdateLabel();
+ eventData.Use();
+ }
+
+ protected enum EditState
+ {
+ Continue,
+ Finish
+ }
+
+ protected EditState KeyPressed(Event evt)
+ {
+ var currentEventModifiers = evt.modifiers;
+ bool ctrl = SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX ? (currentEventModifiers & EventModifiers.Command) != 0 : (currentEventModifiers & EventModifiers.Control) != 0;
+ bool shift = (currentEventModifiers & EventModifiers.Shift) != 0;
+ bool alt = (currentEventModifiers & EventModifiers.Alt) != 0;
+ bool ctrlOnly = ctrl && !alt && !shift;
+
+ switch (evt.keyCode)
+ {
+ case KeyCode.Backspace:
+ {
+ Backspace();
+ return EditState.Continue;
+ }
+
+ case KeyCode.Delete:
+ {
+ ForwardSpace();
+ return EditState.Continue;
+ }
+
+ case KeyCode.Home:
+ {
+ MoveTextStart(shift);
+ return EditState.Continue;
+ }
+
+ case KeyCode.End:
+ {
+ MoveTextEnd(shift);
+ return EditState.Continue;
+ }
+
+ // Select All
+ case KeyCode.A:
+ {
+ if (ctrlOnly)
+ {
+ SelectAll();
+ return EditState.Continue;
+ }
+ break;
+ }
+
+ // Copy
+ case KeyCode.C:
+ {
+ if (ctrlOnly)
+ {
+ if (inputType != InputType.Password)
+ clipboard = GetSelectedString();
+ else
+ clipboard = "";
+ return EditState.Continue;
+ }
+ break;
+ }
+
+ // Paste
+ case KeyCode.V:
+ {
+ if (ctrlOnly)
+ {
+ Append(clipboard);
+ return EditState.Continue;
+ }
+ break;
+ }
+
+ // Cut
+ case KeyCode.X:
+ {
+ if (ctrlOnly)
+ {
+ if (inputType != InputType.Password)
+ clipboard = GetSelectedString();
+ else
+ clipboard = "";
+ Delete();
+ SendOnValueChangedAndUpdateLabel();
+ return EditState.Continue;
+ }
+ break;
+ }
+
+ case KeyCode.LeftArrow:
+ {
+ MoveLeft(shift, ctrl);
+ return EditState.Continue;
+ }
+
+ case KeyCode.RightArrow:
+ {
+ MoveRight(shift, ctrl);
+ return EditState.Continue;
+ }
+
+ case KeyCode.UpArrow:
+ {
+ MoveUp(shift);
+ return EditState.Continue;
+ }
+
+ case KeyCode.DownArrow:
+ {
+ MoveDown(shift);
+ return EditState.Continue;
+ }
+
+ // Submit
+ case KeyCode.Return:
+ case KeyCode.KeypadEnter:
+ {
+ if (lineType != LineType.MultiLineNewline)
+ {
+ return EditState.Finish;
+ }
+ break;
+ }
+
+ case KeyCode.Escape:
+ {
+ m_WasCanceled = true;
+ return EditState.Finish;
+ }
+ }
+
+ char c = evt.character;
+ // Don't allow return chars or tabulator key to be entered into single line fields.
+ if (!multiLine && (c == '\t' || c == '\r' || c == 10))
+ return EditState.Continue;
+
+ // Convert carriage return and end-of-text characters to newline.
+ if (c == '\r' || (int)c == 3)
+ c = '\n';
+
+ if (IsValidChar(c))
+ {
+ Append(c);
+ }
+
+ if (c == 0)
+ {
+ if (compositionString.Length > 0)
+ {
+ UpdateLabel();
+ }
+ }
+ return EditState.Continue;
+ }
+
+ private bool IsValidChar(char c)
+ {
+ // Delete key on mac
+ if ((int)c == 127)
+ return false;
+ // Accept newline and tab
+ if (c == '\t' || c == '\n')
+ return true;
+
+ return m_TextComponent.font.HasCharacter(c);
+ }
+
+ /// <summary>
+ /// Handle the specified event.
+ /// </summary>
+ private Event m_ProcessingEvent = new Event();
+
+ public void ProcessEvent(Event e)
+ {
+ KeyPressed(e);
+ }
+
+ //c IUpdateSelectedHandler的接口
+ 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();
+ }
+
+ private string GetSelectedString()
+ {
+ if (!hasSelection)
+ return "";
+
+ int startPos = caretPositionInternal;
+ int endPos = caretSelectPositionInternal;
+
+ // Ensure startPos is always less then endPos to make the code simpler
+ if (startPos > endPos)
+ {
+ int temp = startPos;
+ startPos = endPos;
+ endPos = temp;
+ }
+
+ return text.Substring(startPos, endPos - startPos);
+ }
+
+ private int FindtNextWordBegin()
+ {
+ if (caretSelectPositionInternal + 1 >= text.Length)
+ return text.Length;
+
+ int spaceLoc = text.IndexOfAny(kSeparators, caretSelectPositionInternal + 1);
+
+ if (spaceLoc == -1)
+ spaceLoc = text.Length;
+ else
+ spaceLoc++;
+
+ return spaceLoc;
+ }
+
+ private void MoveRight(bool shift, bool ctrl)
+ {
+ if (hasSelection && !shift)
+ {
+ // By convention, if we have a selection and move right without holding shift,
+ // we just place the cursor at the end.
+ caretPositionInternal = caretSelectPositionInternal = Mathf.Max(caretPositionInternal, caretSelectPositionInternal);
+ return;
+ }
+
+ int position;
+ if (ctrl)
+ position = FindtNextWordBegin();
+ else
+ position = caretSelectPositionInternal + 1;
+
+ if (shift)
+ caretSelectPositionInternal = position;
+ else
+ caretSelectPositionInternal = caretPositionInternal = position;
+ }
+
+ private int FindtPrevWordBegin()
+ {
+ if (caretSelectPositionInternal - 2 < 0)
+ return 0;
+
+ int spaceLoc = text.LastIndexOfAny(kSeparators, caretSelectPositionInternal - 2);
+
+ if (spaceLoc == -1)
+ spaceLoc = 0;
+ else
+ spaceLoc++;
+
+ return spaceLoc;
+ }
+
+ private void MoveLeft(bool shift, bool ctrl)
+ {
+ if (hasSelection && !shift)
+ {
+ // By convention, if we have a selection and move left without holding shift,
+ // we just place the cursor at the start.
+ caretPositionInternal = caretSelectPositionInternal = Mathf.Min(caretPositionInternal, caretSelectPositionInternal);
+ return;
+ }
+
+ int position;
+ if (ctrl)
+ position = FindtPrevWordBegin();
+ else
+ position = caretSelectPositionInternal - 1;
+
+ if (shift)
+ caretSelectPositionInternal = position;
+ else
+ caretSelectPositionInternal = caretPositionInternal = position;
+ }
+
+ private int DetermineCharacterLine(int charPos, TextGenerator generator)
+ {
+ for (int i = 0; i < generator.lineCount - 1; ++i)
+ {
+ if (generator.lines[i + 1].startCharIdx > charPos)
+ return i;
+ }
+
+ return generator.lineCount - 1;
+ }
+
+ /// <summary>
+ /// Use cachedInputTextGenerator as the y component for the UICharInfo is not required
+ /// </summary>
+
+ private int LineUpCharacterPosition(int originalPos, bool goToFirstChar)
+ {
+ if (originalPos >= cachedInputTextGenerator.characters.Count)
+ return 0;
+
+ UICharInfo originChar = cachedInputTextGenerator.characters[originalPos];
+ int originLine = DetermineCharacterLine(originalPos, cachedInputTextGenerator);
+
+ // We are on the first line return first character
+ if (originLine <= 0)
+ return goToFirstChar ? 0 : originalPos;
+
+ int endCharIdx = cachedInputTextGenerator.lines[originLine].startCharIdx - 1;
+
+ for (int i = cachedInputTextGenerator.lines[originLine - 1].startCharIdx; i < endCharIdx; ++i)
+ {
+ if (cachedInputTextGenerator.characters[i].cursorPos.x >= originChar.cursorPos.x)
+ return i;
+ }
+ return endCharIdx;
+ }
+
+ /// <summary>
+ /// Use cachedInputTextGenerator as the y component for the UICharInfo is not required
+ /// </summary>
+
+ private int LineDownCharacterPosition(int originalPos, bool goToLastChar)
+ {
+ if (originalPos >= cachedInputTextGenerator.characterCountVisible)
+ return text.Length;
+
+ UICharInfo originChar = cachedInputTextGenerator.characters[originalPos];
+ int originLine = DetermineCharacterLine(originalPos, cachedInputTextGenerator);
+
+ // We are on the last line return last character
+ if (originLine + 1 >= cachedInputTextGenerator.lineCount)
+ return goToLastChar ? text.Length : originalPos;
+
+ // Need to determine end line for next line.
+ int endCharIdx = GetLineEndPosition(cachedInputTextGenerator, originLine + 1);
+
+ for (int i = cachedInputTextGenerator.lines[originLine + 1].startCharIdx; i < endCharIdx; ++i)
+ {
+ if (cachedInputTextGenerator.characters[i].cursorPos.x >= originChar.cursorPos.x)
+ return i;
+ }
+ return endCharIdx;
+ }
+
+ private void MoveDown(bool shift)
+ {
+ MoveDown(shift, true);
+ }
+
+ private void MoveDown(bool shift, bool goToLastChar)
+ {
+ if (hasSelection && !shift)
+ {
+ // If we have a selection and press down without shift,
+ // set caret position to end of selection before we move it down.
+ caretPositionInternal = caretSelectPositionInternal = Mathf.Max(caretPositionInternal, caretSelectPositionInternal);
+ }
+
+ int position = multiLine ? LineDownCharacterPosition(caretSelectPositionInternal, goToLastChar) : text.Length;
+
+ if (shift)
+ caretSelectPositionInternal = position;
+ else
+ caretPositionInternal = caretSelectPositionInternal = position;
+ }
+
+ private void MoveUp(bool shift)
+ {
+ MoveUp(shift, true);
+ }
+
+ private void MoveUp(bool shift, bool goToFirstChar)
+ {
+ if (hasSelection && !shift)
+ {
+ // If we have a selection and press up without shift,
+ // set caret position to start of selection before we move it up.
+ caretPositionInternal = caretSelectPositionInternal = Mathf.Min(caretPositionInternal, caretSelectPositionInternal);
+ }
+
+ int position = multiLine ? LineUpCharacterPosition(caretSelectPositionInternal, goToFirstChar) : 0;
+
+ if (shift)
+ caretSelectPositionInternal = position;
+ else
+ caretSelectPositionInternal = caretPositionInternal = position;
+ }
+
+ private void Delete()
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (caretPositionInternal == caretSelectPositionInternal)
+ return;
+
+ if (caretPositionInternal < caretSelectPositionInternal)
+ {
+ m_Text = text.Substring(0, caretPositionInternal) + text.Substring(caretSelectPositionInternal, text.Length - caretSelectPositionInternal);
+ caretSelectPositionInternal = caretPositionInternal;
+ }
+ else
+ {
+ m_Text = text.Substring(0, caretSelectPositionInternal) + text.Substring(caretPositionInternal, text.Length - caretPositionInternal);
+ caretPositionInternal = caretSelectPositionInternal;
+ }
+ }
+
+ private void ForwardSpace()
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (hasSelection)
+ {
+ Delete();
+ SendOnValueChangedAndUpdateLabel();
+ }
+ else
+ {
+ if (caretPositionInternal < text.Length)
+ {
+ m_Text = text.Remove(caretPositionInternal, 1);
+ SendOnValueChangedAndUpdateLabel();
+ }
+ }
+ }
+
+ private void Backspace()
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (hasSelection)
+ {
+ Delete();
+ SendOnValueChangedAndUpdateLabel();
+ }
+ else
+ {
+ if (caretPositionInternal > 0)
+ {
+ m_Text = text.Remove(caretPositionInternal - 1, 1);
+ caretSelectPositionInternal = caretPositionInternal = caretPositionInternal - 1;
+ SendOnValueChangedAndUpdateLabel();
+ }
+ }
+ }
+
+ // Insert the character and update the label.
+ private void Insert(char c)
+ {
+ if (m_ReadOnly)
+ return;
+
+ string replaceString = c.ToString();
+ Delete();
+
+ // Can't go past the character limit
+ if (characterLimit > 0 && text.Length >= characterLimit)
+ return;
+
+ m_Text = text.Insert(m_CaretPosition, replaceString);
+ caretSelectPositionInternal = caretPositionInternal += replaceString.Length;
+
+ SendOnValueChanged();
+ }
+
+ private void SendOnValueChangedAndUpdateLabel()
+ {
+ SendOnValueChanged();
+ UpdateLabel();
+ }
+
+ private void SendOnValueChanged()
+ {
+ UISystemProfilerApi.AddMarker("InputField.value", this);
+ if (onValueChanged != null)
+ onValueChanged.Invoke(text);
+ }
+
+ /// <summary>
+ /// Submit the input field's text.
+ /// </summary>
+
+ protected void SendOnSubmit()
+ {
+ UISystemProfilerApi.AddMarker("InputField.onSubmit", this);
+ if (onEndEdit != null)
+ onEndEdit.Invoke(m_Text);
+ }
+
+ /// <summary>
+ /// Append the specified text to the end of the current.
+ /// </summary>
+
+ protected virtual void Append(string input)
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (!InPlaceEditing())
+ return;
+
+ for (int i = 0, imax = input.Length; i < imax; ++i)
+ {
+ char c = input[i];
+
+ if (c >= ' ' || c == '\t' || c == '\r' || c == 10 || c == '\n')
+ {
+ Append(c);
+ }
+ }
+ }
+
+ protected virtual void Append(char input)
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (!InPlaceEditing())
+ return;
+
+ // If we have an input validator, validate the input first
+ int insertionPoint = Math.Min(selectionFocusPosition, selectionAnchorPosition);
+ if (onValidateInput != null)
+ input = onValidateInput(text, insertionPoint, input);
+ else if (characterValidation != CharacterValidation.None)
+ input = Validate(text, insertionPoint, input);
+
+ // If the input is invalid, skip it
+ if (input == 0)
+ return;
+
+ // Append the character and update the label
+ Insert(input);
+ }
+
+ /// <summary>
+ /// Update the visual text Text.
+ /// </summary>
+
+ protected void UpdateLabel()
+ {
+ if (m_TextComponent != null && m_TextComponent.font != null && !m_PreventFontCallback)
+ {
+ // TextGenerator.Populate invokes a callback that's called for anything
+ // that needs to be updated when the data for that font has changed.
+ // This makes all Text components that use that font update their vertices.
+ // In turn, this makes the InputField that's associated with that Text component
+ // update its label by calling this UpdateLabel method.
+ // This is a recursive call we want to prevent, since it makes the InputField
+ // update based on font data that didn't yet finish executing, or alternatively
+ // hang on infinite recursion, depending on whether the cached value is cached
+ // before or after the calculation.
+ //
+ // This callback also occurs when assigning text to our Text component, i.e.,
+ // m_TextComponent.text = processed;
+
+ m_PreventFontCallback = true;
+
+ string fullText;
+ if (compositionString.Length > 0)
+ fullText = text.Substring(0, m_CaretPosition) + compositionString + text.Substring(m_CaretPosition);
+ else
+ fullText = text;
+
+ string processed;
+ if (inputType == InputType.Password)
+ processed = new string(asteriskChar, fullText.Length);
+ else
+ processed = fullText;
+
+ 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)
+ {
+ // 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;
+
+ cachedInputTextGenerator.PopulateWithErrors(processed, settings, gameObject);
+
+ SetDrawRangeToContainCaretPosition(caretSelectPositionInternal);
+
+ processed = processed.Substring(m_DrawStart, Mathf.Min(m_DrawEnd, processed.Length) - m_DrawStart);
+
+ SetCaretVisible();
+ }
+ m_TextComponent.text = processed;
+ MarkGeometryAsDirty();
+ m_PreventFontCallback = false;
+ }
+ }
+
+ private bool IsSelectionVisible()
+ {
+ if (m_DrawStart > caretPositionInternal || m_DrawStart > caretSelectPositionInternal)
+ return false;
+
+ if (m_DrawEnd < caretPositionInternal || m_DrawEnd < caretSelectPositionInternal)
+ return false;
+
+ return true;
+ }
+
+ private static int GetLineStartPosition(TextGenerator gen, int line)
+ {
+ line = Mathf.Clamp(line, 0, gen.lines.Count - 1);
+ return gen.lines[line].startCharIdx;
+ }
+
+ private static int GetLineEndPosition(TextGenerator gen, int line)
+ {
+ line = Mathf.Max(line, 0);
+ if (line + 1 < gen.lines.Count)
+ return gen.lines[line + 1].startCharIdx - 1;
+ return gen.characterCountVisible;
+ }
+
+ private void SetDrawRangeToContainCaretPosition(int caretPos)
+ {
+ // We don't have any generated lines generation is not valid.
+ if (cachedInputTextGenerator.lineCount <= 0)
+ return;
+
+ // the extents gets modified by the pixel density, so we need to use the generated extents since that will be in the same 'space' as
+ // the values returned by the TextGenerator.lines[x].height for instance.
+ Vector2 extents = cachedInputTextGenerator.rectExtents.size;
+
+ if (multiLine)
+ {
+ var lines = cachedInputTextGenerator.lines;
+ int caretLine = DetermineCharacterLine(caretPos, cachedInputTextGenerator);
+
+ if (caretPos > m_DrawEnd)
+ {
+ // Caret comes after drawEnd, so we need to move drawEnd to the end of the line with the caret
+ m_DrawEnd = GetLineEndPosition(cachedInputTextGenerator, caretLine);
+ float bottomY = lines[caretLine].topY - lines[caretLine].height;
+ if (caretLine == lines.Count - 1)
+ {
+ // Remove interline spacing on last line.
+ bottomY += lines[caretLine].leading;
+ }
+ int startLine = caretLine;
+ while (startLine > 0)
+ {
+ float topY = lines[startLine - 1].topY;
+ if (topY - bottomY > extents.y)
+ break;
+ startLine--;
+ }
+ m_DrawStart = GetLineStartPosition(cachedInputTextGenerator, startLine);
+ }
+ else
+ {
+ if (caretPos < m_DrawStart)
+ {
+ // Caret comes before drawStart, so we need to move drawStart to an earlier line start that comes before caret.
+ m_DrawStart = GetLineStartPosition(cachedInputTextGenerator, caretLine);
+ }
+
+ int startLine = DetermineCharacterLine(m_DrawStart, cachedInputTextGenerator);
+ int endLine = startLine;
+
+ float topY = lines[startLine].topY;
+ float bottomY = lines[endLine].topY - lines[endLine].height;
+
+ if (endLine == lines.Count - 1)
+ {
+ // Remove interline spacing on last line.
+ bottomY += lines[endLine].leading;
+ }
+
+ while (endLine < lines.Count - 1)
+ {
+ bottomY = lines[endLine + 1].topY - lines[endLine + 1].height;
+
+ if (endLine + 1 == lines.Count - 1)
+ {
+ // Remove interline spacing on last line.
+ bottomY += lines[endLine + 1].leading;
+ }
+
+ if (topY - bottomY > extents.y)
+ break;
+ ++endLine;
+ }
+
+ m_DrawEnd = GetLineEndPosition(cachedInputTextGenerator, endLine);
+
+ while (startLine > 0)
+ {
+ topY = lines[startLine - 1].topY;
+ if (topY - bottomY > extents.y)
+ break;
+ startLine--;
+ }
+ m_DrawStart = GetLineStartPosition(cachedInputTextGenerator, startLine);
+ }
+ }
+ else
+ {
+ var characters = cachedInputTextGenerator.characters;
+ if (m_DrawEnd > cachedInputTextGenerator.characterCountVisible)
+ m_DrawEnd = cachedInputTextGenerator.characterCountVisible;
+
+ float width = 0.0f;
+ if (caretPos > m_DrawEnd || (caretPos == m_DrawEnd && m_DrawStart > 0))
+ {
+ // fit characters from the caretPos leftward
+ m_DrawEnd = caretPos;
+ for (m_DrawStart = m_DrawEnd - 1; m_DrawStart >= 0; --m_DrawStart)
+ {
+ if (width + characters[m_DrawStart].charWidth > extents.x)
+ break;
+
+ width += characters[m_DrawStart].charWidth;
+ }
+ ++m_DrawStart; // move right one to the last character we could fit on the left
+ }
+ else
+ {
+ if (caretPos < m_DrawStart)
+ m_DrawStart = caretPos;
+
+ m_DrawEnd = m_DrawStart;
+ }
+
+ // fit characters rightward
+ for (; m_DrawEnd < cachedInputTextGenerator.characterCountVisible; ++m_DrawEnd)
+ {
+ width += characters[m_DrawEnd].charWidth;
+ if (width > extents.x)
+ break;
+ }
+ }
+ }
+
+ public void ForceLabelUpdate()
+ {
+ UpdateLabel();
+ }
+
+ private void MarkGeometryAsDirty()
+ {
+#if UNITY_EDITOR
+ if (!Application.isPlaying || UnityEditor.PrefabUtility.GetPrefabObject(gameObject) != null)
+ return;
+#endif
+
+ CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
+ }
+
+ public virtual void Rebuild(CanvasUpdate update)
+ {
+ switch (update)
+ {
+ case CanvasUpdate.LatePreRender:
+ UpdateGeometry();
+ break;
+ }
+ }
+
+ public virtual void LayoutComplete()
+ {}
+
+ public virtual void GraphicUpdateComplete()
+ {}
+
+ private void UpdateGeometry()
+ {
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ return;
+#endif
+ // No need to draw a cursor on mobile as its handled by the devices keyboard.
+ if (!shouldHideMobileInput)
+ return;
+
+ if (m_CachedInputRenderer == null && m_TextComponent != null)
+ {
+ GameObject go = new GameObject(transform.name + " Input Caret", typeof(RectTransform), typeof(CanvasRenderer));
+ go.hideFlags = HideFlags.DontSave;
+ go.transform.SetParent(m_TextComponent.transform.parent);
+ go.transform.SetAsFirstSibling();
+ go.layer = gameObject.layer;
+
+ caretRectTrans = go.GetComponent<RectTransform>();
+ m_CachedInputRenderer = go.GetComponent<CanvasRenderer>();
+ m_CachedInputRenderer.SetMaterial(m_TextComponent.GetModifiedMaterial(Graphic.defaultGraphicMaterial), Texture2D.whiteTexture);
+
+ // Needed as if any layout is present we want the caret to always be the same as the text area.
+ go.AddComponent<LayoutElement>().ignoreLayout = true;
+
+ AssignPositioningIfNeeded();
+ }
+
+ if (m_CachedInputRenderer == null)
+ return;
+
+ OnFillVBO(mesh);
+ m_CachedInputRenderer.SetMesh(mesh);
+ }
+
+ private void AssignPositioningIfNeeded()
+ {
+ if (m_TextComponent != null && caretRectTrans != null &&
+ (caretRectTrans.localPosition != m_TextComponent.rectTransform.localPosition ||
+ caretRectTrans.localRotation != m_TextComponent.rectTransform.localRotation ||
+ caretRectTrans.localScale != m_TextComponent.rectTransform.localScale ||
+ caretRectTrans.anchorMin != m_TextComponent.rectTransform.anchorMin ||
+ caretRectTrans.anchorMax != m_TextComponent.rectTransform.anchorMax ||
+ caretRectTrans.anchoredPosition != m_TextComponent.rectTransform.anchoredPosition ||
+ caretRectTrans.sizeDelta != m_TextComponent.rectTransform.sizeDelta ||
+ caretRectTrans.pivot != m_TextComponent.rectTransform.pivot))
+ {
+ caretRectTrans.localPosition = m_TextComponent.rectTransform.localPosition;
+ caretRectTrans.localRotation = m_TextComponent.rectTransform.localRotation;
+ caretRectTrans.localScale = m_TextComponent.rectTransform.localScale;
+ caretRectTrans.anchorMin = m_TextComponent.rectTransform.anchorMin;
+ caretRectTrans.anchorMax = m_TextComponent.rectTransform.anchorMax;
+ caretRectTrans.anchoredPosition = m_TextComponent.rectTransform.anchoredPosition;
+ caretRectTrans.sizeDelta = m_TextComponent.rectTransform.sizeDelta;
+ caretRectTrans.pivot = m_TextComponent.rectTransform.pivot;
+ }
+ }
+
+ 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
+ GenerateHightlight(helper, roundingOffset);
+
+ helper.FillMesh(vbo);
+ }
+ }
+
+ private void GenerateCaret(VertexHelper vbo, Vector2 roundingOffset)
+ {
+ if (!m_CaretVisible)
+ return;
+
+ if (m_CursorVerts == null)
+ {
+ CreateCursorVerts();
+ }
+
+ float width = m_CaretWidth;
+ int adjustedPos = Mathf.Max(0, caretPositionInternal - m_DrawStart);
+ TextGenerator gen = m_TextComponent.cachedTextGenerator;
+
+ if (gen == null)
+ return;
+
+ if (gen.lineCount == 0)
+ return;
+
+ Vector2 startPosition = Vector2.zero;
+
+ // Calculate startPosition
+ if (adjustedPos < gen.characters.Count)
+ {
+ UICharInfo cursorChar = gen.characters[adjustedPos];
+ startPosition.x = cursorChar.cursorPos.x;
+ }
+ startPosition.x /= m_TextComponent.pixelsPerUnit;
+
+ // TODO: Only clamp when Text uses horizontal word wrap.
+ if (startPosition.x > m_TextComponent.rectTransform.rect.xMax)
+ startPosition.x = m_TextComponent.rectTransform.rect.xMax;
+
+ int characterLine = DetermineCharacterLine(adjustedPos, gen);
+ startPosition.y = gen.lines[characterLine].topY / m_TextComponent.pixelsPerUnit;
+ float height = gen.lines[characterLine].height / m_TextComponent.pixelsPerUnit;
+
+ for (int i = 0; i < m_CursorVerts.Length; i++)
+ m_CursorVerts[i].color = caretColor;
+
+ m_CursorVerts[0].position = new Vector3(startPosition.x, startPosition.y - height, 0.0f);
+ m_CursorVerts[1].position = new Vector3(startPosition.x + width, startPosition.y - height, 0.0f);
+ m_CursorVerts[2].position = new Vector3(startPosition.x + width, startPosition.y, 0.0f);
+ m_CursorVerts[3].position = new Vector3(startPosition.x, startPosition.y, 0.0f);
+
+ if (roundingOffset != Vector2.zero)
+ {
+ for (int i = 0; i < m_CursorVerts.Length; i++)
+ {
+ UIVertex uiv = m_CursorVerts[i];
+ uiv.position.x += roundingOffset.x;
+ uiv.position.y += roundingOffset.y;
+ }
+ }
+
+ vbo.AddUIVertexQuad(m_CursorVerts);
+
+ int screenHeight = Screen.height;
+ // Multiple display support only when not the main display. For display 0 the reported
+ // resolution is always the desktops resolution since its part of the display API,
+ // so we use the standard none multiple display method. (case 741751)
+ int displayIndex = m_TextComponent.canvas.targetDisplay;
+ if (displayIndex > 0 && displayIndex < Display.displays.Length)
+ screenHeight = Display.displays[displayIndex].renderingHeight;
+
+ startPosition.y = screenHeight - startPosition.y;
+ input.compositionCursorPos = startPosition;
+ }
+
+ private void CreateCursorVerts()
+ {
+ m_CursorVerts = new UIVertex[4];
+
+ for (int i = 0; i < m_CursorVerts.Length; i++)
+ {
+ m_CursorVerts[i] = UIVertex.simpleVert;
+ m_CursorVerts[i].uv0 = Vector2.zero;
+ }
+ }
+
+ private void GenerateHightlight(VertexHelper vbo, Vector2 roundingOffset)
+ {
+ int startChar = Mathf.Max(0, caretPositionInternal - m_DrawStart);
+ int endChar = Mathf.Max(0, caretSelectPositionInternal - m_DrawStart);
+
+ // Ensure pos is always less then selPos to make the code simpler
+ if (startChar > endChar)
+ {
+ int temp = startChar;
+ startChar = endChar;
+ endChar = temp;
+ }
+
+ endChar -= 1;
+ TextGenerator gen = m_TextComponent.cachedTextGenerator;
+
+ if (gen.lineCount <= 0)
+ return;
+
+ int currentLineIndex = DetermineCharacterLine(startChar, gen);
+
+ int lastCharInLineIndex = GetLineEndPosition(gen, currentLineIndex);
+
+ UIVertex vert = UIVertex.simpleVert;
+ vert.uv0 = Vector2.zero;
+ vert.color = selectionColor;
+
+ int currentChar = startChar;
+ while (currentChar <= endChar && currentChar < gen.characterCount)
+ {
+ if (currentChar == lastCharInLineIndex || currentChar == endChar)
+ {
+ UICharInfo startCharInfo = gen.characters[startChar];
+ UICharInfo endCharInfo = gen.characters[currentChar];
+ Vector2 startPosition = new Vector2(startCharInfo.cursorPos.x / m_TextComponent.pixelsPerUnit, gen.lines[currentLineIndex].topY / m_TextComponent.pixelsPerUnit);
+ Vector2 endPosition = new Vector2((endCharInfo.cursorPos.x + endCharInfo.charWidth) / m_TextComponent.pixelsPerUnit, startPosition.y - gen.lines[currentLineIndex].height / m_TextComponent.pixelsPerUnit);
+
+ // Checking xMin as well due to text generator not setting position if char is not rendered.
+ if (endPosition.x > m_TextComponent.rectTransform.rect.xMax || endPosition.x < m_TextComponent.rectTransform.rect.xMin)
+ endPosition.x = m_TextComponent.rectTransform.rect.xMax;
+
+ var startIndex = vbo.currentVertCount;
+ vert.position = new Vector3(startPosition.x, endPosition.y, 0.0f) + (Vector3)roundingOffset;
+ vbo.AddVert(vert);
+
+ vert.position = new Vector3(endPosition.x, endPosition.y, 0.0f) + (Vector3)roundingOffset;
+ vbo.AddVert(vert);
+
+ vert.position = new Vector3(endPosition.x, startPosition.y, 0.0f) + (Vector3)roundingOffset;
+ vbo.AddVert(vert);
+
+ vert.position = new Vector3(startPosition.x, startPosition.y, 0.0f) + (Vector3)roundingOffset;
+ vbo.AddVert(vert);
+
+ vbo.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
+ vbo.AddTriangle(startIndex + 2, startIndex + 3, startIndex + 0);
+
+ startChar = currentChar + 1;
+ currentLineIndex++;
+
+ lastCharInLineIndex = GetLineEndPosition(gen, currentLineIndex);
+ }
+ currentChar++;
+ }
+ }
+
+ /// <summary>
+ /// Validate the specified input.
+ /// </summary>
+
+ protected char Validate(string text, int pos, char ch)
+ {
+ // Validation is disabled
+ if (characterValidation == CharacterValidation.None || !enabled)
+ return ch;
+
+ if (characterValidation == CharacterValidation.Integer || characterValidation == CharacterValidation.Decimal)
+ {
+ // Integer and decimal
+ bool cursorBeforeDash = (pos == 0 && text.Length > 0 && text[0] == '-');
+ bool dashInSelection = text.Length > 0 && text[0] == '-' && ((caretPositionInternal == 0 && caretSelectPositionInternal > 0) || (caretSelectPositionInternal == 0 && caretPositionInternal > 0));
+ bool selectionAtStart = caretPositionInternal == 0 || caretSelectPositionInternal == 0;
+ if (!cursorBeforeDash || dashInSelection)
+ {
+ if (ch >= '0' && ch <= '9') return ch;
+ if (ch == '-' && (pos == 0 || selectionAtStart)) return ch;
+ if (ch == '.' && characterValidation == CharacterValidation.Decimal && !text.Contains(".")) return ch;
+ }
+ }
+ else if (characterValidation == CharacterValidation.Alphanumeric)
+ {
+ // All alphanumeric characters
+ if (ch >= 'A' && ch <= 'Z') return ch;
+ if (ch >= 'a' && ch <= 'z') return ch;
+ if (ch >= '0' && ch <= '9') return ch;
+ }
+ else if (characterValidation == CharacterValidation.Name)
+ {
+ // FIXME: some actions still lead to invalid input:
+ // - Hitting delete in front of an uppercase letter
+ // - Selecting an uppercase letter and deleting it
+ // - Typing some text, hitting Home and typing more text (we then have an uppercase letter in the middle of a word)
+ // - Typing some text, hitting Home and typing a space (we then have a leading space)
+ // - Erasing a space between two words (we then have an uppercase letter in the middle of a word)
+ // - We accept a trailing space
+ // - We accept the insertion of a space between two lowercase letters.
+ // - Typing text in front of an existing uppercase letter
+ // - ... and certainly more
+ //
+ // The rule we try to implement are too complex for this kind of verification.
+
+ if (char.IsLetter(ch))
+ {
+ // Character following a space should be in uppercase.
+ if (char.IsLower(ch) && ((pos == 0) || (text[pos - 1] == ' ')))
+ {
+ return char.ToUpper(ch);
+ }
+
+ // Character not following a space or an apostrophe should be in lowercase.
+ if (char.IsUpper(ch) && (pos > 0) && (text[pos - 1] != ' ') && (text[pos - 1] != '\''))
+ {
+ return char.ToLower(ch);
+ }
+
+ return ch;
+ }
+
+ if (ch == '\'')
+ {
+ // Don't allow more than one apostrophe
+ if (!text.Contains("'"))
+ // Don't allow consecutive spaces and apostrophes.
+ if (!(((pos > 0) && ((text[pos - 1] == ' ') || (text[pos - 1] == '\''))) ||
+ ((pos < text.Length) && ((text[pos] == ' ') || (text[pos] == '\'')))))
+ return ch;
+ }
+
+ if (ch == ' ')
+ {
+ // Don't allow consecutive spaces and apostrophes.
+ if (!(((pos > 0) && ((text[pos - 1] == ' ') || (text[pos - 1] == '\''))) ||
+ ((pos < text.Length) && ((text[pos] == ' ') || (text[pos] == '\'')))))
+ return ch;
+ }
+ }
+ else if (characterValidation == CharacterValidation.EmailAddress)
+ {
+ // From StackOverflow about allowed characters in email addresses:
+ // Uppercase and lowercase English letters (a-z, A-Z)
+ // Digits 0 to 9
+ // Characters ! # $ % & ' * + - / = ? ^ _ ` { | } ~
+ // Character . (dot, period, full stop) provided that it is not the first or last character,
+ // and provided also that it does not appear two or more times consecutively.
+
+ if (ch >= 'A' && ch <= 'Z') return ch;
+ if (ch >= 'a' && ch <= 'z') return ch;
+ if (ch >= '0' && ch <= '9') return ch;
+ if (ch == '@' && text.IndexOf('@') == -1) return ch;
+ if (kEmailSpecialCharacters.IndexOf(ch) != -1) return ch;
+ if (ch == '.')
+ {
+ char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
+ char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';
+ if (lastChar != '.' && nextChar != '.')
+ return ch;
+ }
+ }
+ return (char)0;
+ }
+
+ 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;
+ }
+
+ private void ActivateInputFieldInternal()
+ {
+ if (EventSystem.current == null)
+ return;
+
+ if (EventSystem.current.currentSelectedGameObject != gameObject)
+ EventSystem.current.SetSelectedGameObject(gameObject);
+
+ if (TouchScreenKeyboard.isSupported)
+ {
+ if (input.touchSupported)
+ {
+ TouchScreenKeyboard.hideInput = shouldHideMobileInput;
+ }
+
+ m_Keyboard = (inputType == InputType.Password) ?
+ TouchScreenKeyboard.Open(m_Text, keyboardType, false, multiLine, true) :
+ TouchScreenKeyboard.Open(m_Text, keyboardType, inputType == InputType.AutoCorrect, multiLine);
+
+ // Mimics OnFocus but as mobile doesn't properly support select all
+ // just set it to the end of the text (where it would move when typing starts)
+ MoveTextEnd(false);
+ }
+ else
+ {
+ input.imeCompositionMode = IMECompositionMode.On;
+ OnFocus();
+ }
+
+ m_AllowInput = true;
+ m_OriginalText = text;
+ m_WasCanceled = false;
+ SetCaretVisible();
+ UpdateLabel();
+ }
+
+ public override void OnSelect(BaseEventData eventData)
+ {
+ base.OnSelect(eventData);
+
+ if (shouldActivateOnSelect)
+ ActivateInputField();
+ }
+
+ public virtual void OnPointerClick(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ ActivateInputField();
+ }
+
+ 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;
+
+ if (m_Keyboard != null)
+ {
+ m_Keyboard.active = false;
+ m_Keyboard = null;
+ }
+
+ m_CaretPosition = m_CaretSelectPosition = 0;
+
+ SendOnSubmit();
+
+ input.imeCompositionMode = IMECompositionMode.Auto;
+ }
+
+ MarkGeometryAsDirty();
+ }
+
+ public override void OnDeselect(BaseEventData eventData)
+ {
+ DeactivateInputField();
+ base.OnDeselect(eventData);
+ }
+
+ public virtual void OnSubmit(BaseEventData eventData)
+ {
+ if (!IsActive() || !IsInteractable())
+ return;
+
+ if (!isFocused)
+ m_ShouldActivateNextUpdate = true;
+ }
+
+ private void EnforceContentType()
+ {
+ switch (contentType)
+ {
+ case ContentType.Standard:
+ {
+ // Don't enforce line type for this content type.
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.Default;
+ m_CharacterValidation = CharacterValidation.None;
+ break;
+ }
+ case ContentType.Autocorrected:
+ {
+ // Don't enforce line type for this content type.
+ m_InputType = InputType.AutoCorrect;
+ m_KeyboardType = TouchScreenKeyboardType.Default;
+ m_CharacterValidation = CharacterValidation.None;
+ break;
+ }
+ case ContentType.IntegerNumber:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.NumberPad;
+ m_CharacterValidation = CharacterValidation.Integer;
+ break;
+ }
+ case ContentType.DecimalNumber:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.NumbersAndPunctuation;
+ m_CharacterValidation = CharacterValidation.Decimal;
+ break;
+ }
+ case ContentType.Alphanumeric:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.ASCIICapable;
+ m_CharacterValidation = CharacterValidation.Alphanumeric;
+ break;
+ }
+ case ContentType.Name:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.NamePhonePad;
+ m_CharacterValidation = CharacterValidation.Name;
+ break;
+ }
+ case ContentType.EmailAddress:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.EmailAddress;
+ m_CharacterValidation = CharacterValidation.EmailAddress;
+ break;
+ }
+ case ContentType.Password:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Password;
+ m_KeyboardType = TouchScreenKeyboardType.Default;
+ m_CharacterValidation = CharacterValidation.None;
+ break;
+ }
+ case ContentType.Pin:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Password;
+ m_KeyboardType = TouchScreenKeyboardType.NumberPad;
+ m_CharacterValidation = CharacterValidation.Integer;
+ break;
+ }
+ default:
+ {
+ // Includes Custom type. Nothing should be enforced.
+ break;
+ }
+ }
+
+ EnforceTextHOverflow();
+ }
+
+ void EnforceTextHOverflow()
+ {
+ if (m_TextComponent != null)
+ if (multiLine)
+ m_TextComponent.horizontalOverflow = HorizontalWrapMode.Wrap;
+ else
+ m_TextComponent.horizontalOverflow = HorizontalWrapMode.Overflow;
+ }
+
+ void SetToCustomIfContentTypeIsNot(params ContentType[] allowedContentTypes)
+ {
+ if (contentType == ContentType.Custom)
+ return;
+
+ for (int i = 0; i < allowedContentTypes.Length; i++)
+ if (contentType == allowedContentTypes[i])
+ return;
+
+ contentType = ContentType.Custom;
+ }
+
+ void SetToCustom()
+ {
+ if (contentType == ContentType.Custom)
+ return;
+
+ contentType = ContentType.Custom;
+ }
+
+ protected override void DoStateTransition(SelectionState state, bool instant)
+ {
+ if (m_HasDoneFocusTransition)
+ state = SelectionState.Highlighted;
+ else if (state == SelectionState.Pressed)
+ m_HasDoneFocusTransition = true;
+
+ base.DoStateTransition(state, instant);
+ }
+
+ public virtual void CalculateLayoutInputHorizontal() {}
+ public virtual void CalculateLayoutInputVertical() {}
+
+ public virtual float minWidth { get { return 0; } }
+
+ public virtual float preferredWidth
+ {
+ get
+ {
+ if (textComponent == null)
+ return 0;
+ var settings = textComponent.GetGenerationSettings(Vector2.zero);
+ return textComponent.cachedTextGeneratorForLayout.GetPreferredWidth(m_Text, settings) / textComponent.pixelsPerUnit;
+ }
+ }
+ public virtual float flexibleWidth { get { return -1; } }
+ public virtual float minHeight { get { return 0; } }
+
+ public virtual float preferredHeight
+ {
+ get
+ {
+ if (textComponent == null)
+ return 0;
+ var settings = textComponent.GetGenerationSettings(new Vector2(textComponent.rectTransform.rect.size.x, 0.0f));
+ return textComponent.cachedTextGeneratorForLayout.GetPreferredHeight(m_Text, settings) / textComponent.pixelsPerUnit;
+ }
+ }
+
+ public virtual float flexibleHeight { get { return -1; } }
+ public virtual int layoutPriority { get { return 1; } }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/InputField.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/InputField.cs.meta
new file mode 100644
index 0000000..a11780b
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/InputField.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fd5762ccacc914a428b1e5e5ae0f0edb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Navigation.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Navigation.cs
new file mode 100644
index 0000000..98e77f9
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Navigation.cs
@@ -0,0 +1,74 @@
+using System;
+using UnityEngine.Serialization;
+
+namespace UnityEngine.UI
+{
+ [Serializable]
+ public struct Navigation : IEquatable<Navigation>
+ {
+ /*
+ * This looks like it's not flags, but it is flags,
+ * the reason is that Automatic is considered horizontal
+ * and verical mode combined
+ */
+ [Flags]
+ public enum Mode
+ {
+ None = 0, // 0 No navigation
+ Horizontal = 1, // 1 Automatic horizontal navigation
+ Vertical = 2, // 10 Automatic vertical navigation
+ Automatic = 3, // 11 Automatic navigation in both dimensions
+ Explicit = 4, // Explicitly specified only
+ }
+
+ // Which method of navigation will be used.
+ [FormerlySerializedAs("mode")]
+ [SerializeField]
+ private Mode m_Mode;
+
+ // Game object selected when the joystick moves up. Used when navigation is set to "Explicit".
+ [FormerlySerializedAs("selectOnUp")]
+ [SerializeField]
+ private Selectable m_SelectOnUp;
+
+ // Game object selected when the joystick moves down. Used when navigation is set to "Explicit".
+ [FormerlySerializedAs("selectOnDown")]
+ [SerializeField]
+ private Selectable m_SelectOnDown;
+
+ // Game object selected when the joystick moves left. Used when navigation is set to "Explicit".
+ [FormerlySerializedAs("selectOnLeft")]
+ [SerializeField]
+ private Selectable m_SelectOnLeft;
+
+ // Game object selected when the joystick moves right. Used when navigation is set to "Explicit".
+ [FormerlySerializedAs("selectOnRight")]
+ [SerializeField]
+ private Selectable m_SelectOnRight;
+
+ public Mode mode { get { return m_Mode; } set { m_Mode = value; } }
+ public Selectable selectOnUp { get { return m_SelectOnUp; } set { m_SelectOnUp = value; } }
+ public Selectable selectOnDown { get { return m_SelectOnDown; } set { m_SelectOnDown = value; } }
+ public Selectable selectOnLeft { get { return m_SelectOnLeft; } set { m_SelectOnLeft = value; } }
+ public Selectable selectOnRight { get { return m_SelectOnRight; } set { m_SelectOnRight = value; } }
+
+ static public Navigation defaultNavigation
+ {
+ get
+ {
+ var defaultNav = new Navigation();
+ defaultNav.m_Mode = Mode.Automatic;
+ return defaultNav;
+ }
+ }
+
+ public bool Equals(Navigation other)
+ {
+ return mode == other.mode &&
+ selectOnUp == other.selectOnUp &&
+ selectOnDown == other.selectOnDown &&
+ selectOnLeft == other.selectOnLeft &&
+ selectOnRight == other.selectOnRight;
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Navigation.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Navigation.cs.meta
new file mode 100644
index 0000000..a5f02d3
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Navigation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7565fbbe496d6e749a2a17d8acb60c80
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ScrollRect.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ScrollRect.cs
new file mode 100644
index 0000000..106f15c
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ScrollRect.cs
@@ -0,0 +1,873 @@
+using System;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.UI
+{
+ [AddComponentMenu("UI/Scroll Rect", 37)]
+ [SelectionBase]
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(RectTransform))]
+ public class ScrollRect
+ : UIBehaviour
+ , IInitializePotentialDragHandler
+ , IBeginDragHandler
+ , IEndDragHandler
+ , IDragHandler
+ , IScrollHandler
+ , ICanvasElement
+ , ILayoutElement
+ , ILayoutGroup
+ {
+ public enum MovementType
+ {
+ Unrestricted, // Unrestricted movement -- can scroll forever
+ Elastic, // Restricted but flexible -- can go past the edges, but springs back in place
+ Clamped, // Restricted movement where it's not possible to go past the edges
+ }
+
+ public enum ScrollbarVisibility
+ {
+ Permanent,
+ AutoHide,
+ AutoHideAndExpandViewport,
+ }
+
+ [Serializable]
+ public class ScrollRectEvent : UnityEvent<Vector2> {}
+
+ [SerializeField]
+ private RectTransform m_Content;
+ public RectTransform content { get { return m_Content; } set { m_Content = value; } }
+
+ [SerializeField]
+ private bool m_Horizontal = true;
+ public bool horizontal { get { return m_Horizontal; } set { m_Horizontal = value; } }
+
+ [SerializeField]
+ private bool m_Vertical = true;
+ public bool vertical { get { return m_Vertical; } set { m_Vertical = value; } }
+
+ [SerializeField]
+ private MovementType m_MovementType = MovementType.Elastic;
+ public MovementType movementType { get { return m_MovementType; } set { m_MovementType = value; } }
+
+ [SerializeField]
+ private float m_Elasticity = 0.1f; // Only used for MovementType.Elastic
+ public float elasticity { get { return m_Elasticity; } set { m_Elasticity = value; } }
+
+ [SerializeField]
+ private bool m_Inertia = true;
+ public bool inertia { get { return m_Inertia; } set { m_Inertia = value; } }
+
+ [SerializeField]
+ private float m_DecelerationRate = 0.135f; // Only used when inertia is enabled
+ public float decelerationRate { get { return m_DecelerationRate; } set { m_DecelerationRate = value; } }
+
+ [SerializeField]
+ private float m_ScrollSensitivity = 1.0f;
+ public float scrollSensitivity { get { return m_ScrollSensitivity; } set { m_ScrollSensitivity = value; } }
+
+ [SerializeField]
+ private RectTransform m_Viewport;
+ public RectTransform viewport { get { return m_Viewport; } set { m_Viewport = value; SetDirtyCaching(); } }
+
+ [SerializeField]
+ private Scrollbar m_HorizontalScrollbar;
+ public Scrollbar horizontalScrollbar
+ {
+ get
+ {
+ return m_HorizontalScrollbar;
+ }
+ set
+ {
+ if (m_HorizontalScrollbar)
+ m_HorizontalScrollbar.onValueChanged.RemoveListener(SetHorizontalNormalizedPosition);
+ m_HorizontalScrollbar = value;
+ if (m_HorizontalScrollbar)
+ m_HorizontalScrollbar.onValueChanged.AddListener(SetHorizontalNormalizedPosition);
+ SetDirtyCaching();
+ }
+ }
+
+ [SerializeField]
+ private Scrollbar m_VerticalScrollbar;
+ public Scrollbar verticalScrollbar
+ {
+ get
+ {
+ return m_VerticalScrollbar;
+ }
+ set
+ {
+ if (m_VerticalScrollbar)
+ m_VerticalScrollbar.onValueChanged.RemoveListener(SetVerticalNormalizedPosition);
+ m_VerticalScrollbar = value;
+ if (m_VerticalScrollbar)
+ m_VerticalScrollbar.onValueChanged.AddListener(SetVerticalNormalizedPosition);
+ SetDirtyCaching();
+ }
+ }
+
+ [SerializeField]
+ private ScrollbarVisibility m_HorizontalScrollbarVisibility;
+ public ScrollbarVisibility horizontalScrollbarVisibility { get { return m_HorizontalScrollbarVisibility; } set { m_HorizontalScrollbarVisibility = value; SetDirtyCaching(); } }
+
+ [SerializeField]
+ private ScrollbarVisibility m_VerticalScrollbarVisibility;
+ public ScrollbarVisibility verticalScrollbarVisibility { get { return m_VerticalScrollbarVisibility; } set { m_VerticalScrollbarVisibility = value; SetDirtyCaching(); } }
+
+ [SerializeField]
+ private float m_HorizontalScrollbarSpacing;
+ public float horizontalScrollbarSpacing { get { return m_HorizontalScrollbarSpacing; } set { m_HorizontalScrollbarSpacing = value; SetDirty(); } }
+
+ [SerializeField]
+ private float m_VerticalScrollbarSpacing;
+ public float verticalScrollbarSpacing { get { return m_VerticalScrollbarSpacing; } set { m_VerticalScrollbarSpacing = value; SetDirty(); } }
+
+ [SerializeField]
+ private ScrollRectEvent m_OnValueChanged = new ScrollRectEvent();
+ public ScrollRectEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
+
+ // The offset from handle position to mouse down position
+ private Vector2 m_PointerStartLocalCursor = Vector2.zero;
+ protected Vector2 m_ContentStartPosition = Vector2.zero;
+
+ private RectTransform m_ViewRect;
+
+ protected RectTransform viewRect
+ {
+ get
+ {
+ if (m_ViewRect == null)
+ m_ViewRect = m_Viewport;
+ if (m_ViewRect == null)
+ m_ViewRect = (RectTransform)transform;
+ return m_ViewRect;
+ }
+ }
+
+ protected Bounds m_ContentBounds;
+ private Bounds m_ViewBounds;
+
+ private Vector2 m_Velocity;
+ public Vector2 velocity { get { return m_Velocity; } set { m_Velocity = value; } }
+
+ private bool m_Dragging;
+
+ private Vector2 m_PrevPosition = Vector2.zero;
+ private Bounds m_PrevContentBounds;
+ private Bounds m_PrevViewBounds;
+ [NonSerialized]
+ private bool m_HasRebuiltLayout = false;
+
+ private bool m_HSliderExpand;
+ private bool m_VSliderExpand;
+ private float m_HSliderHeight;
+ private float m_VSliderWidth;
+
+ [System.NonSerialized] private RectTransform m_Rect;
+ private RectTransform rectTransform
+ {
+ get
+ {
+ if (m_Rect == null)
+ m_Rect = GetComponent<RectTransform>();
+ return m_Rect;
+ }
+ }
+
+ private RectTransform m_HorizontalScrollbarRect;
+ private RectTransform m_VerticalScrollbarRect;
+
+ private DrivenRectTransformTracker m_Tracker;
+
+ protected ScrollRect()
+ {}
+
+ public virtual void Rebuild(CanvasUpdate executing)
+ {
+ if (executing == CanvasUpdate.Prelayout)
+ {
+ UpdateCachedData();
+ }
+
+ if (executing == CanvasUpdate.PostLayout)
+ {
+ UpdateBounds();
+ UpdateScrollbars(Vector2.zero);
+ UpdatePrevData();
+
+ m_HasRebuiltLayout = true;
+ }
+ }
+
+ public virtual void LayoutComplete()
+ {}
+
+ public virtual void GraphicUpdateComplete()
+ {}
+
+ void UpdateCachedData()
+ {
+ Transform transform = this.transform;
+ m_HorizontalScrollbarRect = m_HorizontalScrollbar == null ? null : m_HorizontalScrollbar.transform as RectTransform;
+ m_VerticalScrollbarRect = m_VerticalScrollbar == null ? null : m_VerticalScrollbar.transform as RectTransform;
+
+ // These are true if either the elements are children, or they don't exist at all.
+ bool viewIsChild = (viewRect.parent == transform);
+ bool hScrollbarIsChild = (!m_HorizontalScrollbarRect || m_HorizontalScrollbarRect.parent == transform);
+ bool vScrollbarIsChild = (!m_VerticalScrollbarRect || m_VerticalScrollbarRect.parent == transform);
+ bool allAreChildren = (viewIsChild && hScrollbarIsChild && vScrollbarIsChild);
+
+ m_HSliderExpand = allAreChildren && m_HorizontalScrollbarRect && horizontalScrollbarVisibility == ScrollbarVisibility.AutoHideAndExpandViewport;
+ m_VSliderExpand = allAreChildren && m_VerticalScrollbarRect && verticalScrollbarVisibility == ScrollbarVisibility.AutoHideAndExpandViewport;
+ m_HSliderHeight = (m_HorizontalScrollbarRect == null ? 0 : m_HorizontalScrollbarRect.rect.height);
+ m_VSliderWidth = (m_VerticalScrollbarRect == null ? 0 : m_VerticalScrollbarRect.rect.width);
+ }
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ if (m_HorizontalScrollbar)
+ m_HorizontalScrollbar.onValueChanged.AddListener(SetHorizontalNormalizedPosition);
+ if (m_VerticalScrollbar)
+ m_VerticalScrollbar.onValueChanged.AddListener(SetVerticalNormalizedPosition);
+
+ CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
+ }
+
+ protected override void OnDisable()
+ {
+ CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
+
+ if (m_HorizontalScrollbar)
+ m_HorizontalScrollbar.onValueChanged.RemoveListener(SetHorizontalNormalizedPosition);
+ if (m_VerticalScrollbar)
+ m_VerticalScrollbar.onValueChanged.RemoveListener(SetVerticalNormalizedPosition);
+
+ m_HasRebuiltLayout = false;
+ m_Tracker.Clear();
+ m_Velocity = Vector2.zero;
+ LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
+ base.OnDisable();
+ }
+
+ public override bool IsActive()
+ {
+ return base.IsActive() && m_Content != null;
+ }
+
+ private void EnsureLayoutHasRebuilt()
+ {
+ if (!m_HasRebuiltLayout && !CanvasUpdateRegistry.IsRebuildingLayout())
+ Canvas.ForceUpdateCanvases();
+ }
+
+ public virtual void StopMovement()
+ {
+ m_Velocity = Vector2.zero;
+ }
+
+ public virtual void OnScroll(PointerEventData data)
+ {
+ if (!IsActive())
+ return;
+
+ EnsureLayoutHasRebuilt();
+ UpdateBounds();
+
+ Vector2 delta = data.scrollDelta;
+ // Down is positive for scroll events, while in UI system up is positive.
+ delta.y *= -1;
+ if (vertical && !horizontal)
+ {
+ if (Mathf.Abs(delta.x) > Mathf.Abs(delta.y))
+ delta.y = delta.x;
+ delta.x = 0;
+ }
+ if (horizontal && !vertical)
+ {
+ if (Mathf.Abs(delta.y) > Mathf.Abs(delta.x))
+ delta.x = delta.y;
+ delta.y = 0;
+ }
+
+ Vector2 position = m_Content.anchoredPosition;
+ position += delta * m_ScrollSensitivity;
+ if (m_MovementType == MovementType.Clamped)
+ position += CalculateOffset(position - m_Content.anchoredPosition);
+
+ SetContentAnchoredPosition(position);
+ UpdateBounds();
+ }
+
+ public virtual void OnInitializePotentialDrag(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ m_Velocity = Vector2.zero;
+ }
+
+ public virtual void OnBeginDrag(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ if (!IsActive())
+ return;
+
+ UpdateBounds();
+
+ m_PointerStartLocalCursor = Vector2.zero;
+ RectTransformUtility.ScreenPointToLocalPointInRectangle(viewRect, eventData.position, eventData.pressEventCamera, out m_PointerStartLocalCursor);
+ m_ContentStartPosition = m_Content.anchoredPosition;
+ m_Dragging = true;
+ }
+
+ public virtual void OnEndDrag(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ m_Dragging = false;
+ }
+
+ public virtual void OnDrag(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ if (!IsActive())
+ return;
+
+ Vector2 localCursor;
+ if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(viewRect, eventData.position, eventData.pressEventCamera, out localCursor))
+ return;
+
+ UpdateBounds();
+
+ var pointerDelta = localCursor - m_PointerStartLocalCursor;
+ Vector2 position = m_ContentStartPosition + pointerDelta;
+
+ // Offset to get content into place in the view.
+ Vector2 offset = CalculateOffset(position - m_Content.anchoredPosition);
+ position += offset;
+ if (m_MovementType == MovementType.Elastic)
+ {
+ if (offset.x != 0)
+ position.x = position.x - RubberDelta(offset.x, m_ViewBounds.size.x);
+ if (offset.y != 0)
+ position.y = position.y - RubberDelta(offset.y, m_ViewBounds.size.y);
+ }
+
+ SetContentAnchoredPosition(position);
+ }
+
+ protected virtual void SetContentAnchoredPosition(Vector2 position)
+ {
+ if (!m_Horizontal)
+ position.x = m_Content.anchoredPosition.x;
+ if (!m_Vertical)
+ position.y = m_Content.anchoredPosition.y;
+
+ if (position != m_Content.anchoredPosition)
+ {
+ m_Content.anchoredPosition = position;
+ UpdateBounds();
+ }
+ }
+
+ protected virtual void LateUpdate()
+ {
+ if (!m_Content)
+ return;
+
+ EnsureLayoutHasRebuilt();
+ UpdateScrollbarVisibility();
+ UpdateBounds();
+ float deltaTime = Time.unscaledDeltaTime;
+ Vector2 offset = CalculateOffset(Vector2.zero);
+ if (!m_Dragging && (offset != Vector2.zero || m_Velocity != Vector2.zero))
+ {
+ Vector2 position = m_Content.anchoredPosition;
+ for (int axis = 0; axis < 2; axis++)
+ {
+ // Apply spring physics if movement is elastic and content has an offset from the view.
+ if (m_MovementType == MovementType.Elastic && offset[axis] != 0)
+ {
+ float speed = m_Velocity[axis];
+ position[axis] = Mathf.SmoothDamp(m_Content.anchoredPosition[axis], m_Content.anchoredPosition[axis] + offset[axis], ref speed, m_Elasticity, Mathf.Infinity, deltaTime);
+ if (Mathf.Abs(speed) < 1)
+ speed = 0;
+ m_Velocity[axis] = speed;
+ }
+ // Else move content according to velocity with deceleration applied.
+ else if (m_Inertia)
+ {
+ m_Velocity[axis] *= Mathf.Pow(m_DecelerationRate, deltaTime);
+ if (Mathf.Abs(m_Velocity[axis]) < 1)
+ m_Velocity[axis] = 0;
+ position[axis] += m_Velocity[axis] * deltaTime;
+ }
+ // If we have neither elaticity or friction, there shouldn't be any velocity.
+ else
+ {
+ m_Velocity[axis] = 0;
+ }
+ }
+
+ if (m_Velocity != Vector2.zero)
+ {
+ if (m_MovementType == MovementType.Clamped)
+ {
+ offset = CalculateOffset(position - m_Content.anchoredPosition);
+ position += offset;
+ }
+
+ SetContentAnchoredPosition(position);
+ }
+ }
+
+ if (m_Dragging && m_Inertia)
+ {
+ Vector3 newVelocity = (m_Content.anchoredPosition - m_PrevPosition) / deltaTime;
+ m_Velocity = Vector3.Lerp(m_Velocity, newVelocity, deltaTime * 10);
+ }
+
+ if (m_ViewBounds != m_PrevViewBounds || m_ContentBounds != m_PrevContentBounds || m_Content.anchoredPosition != m_PrevPosition)
+ {
+ UpdateScrollbars(offset);
+ UISystemProfilerApi.AddMarker("ScrollRect.value", this);
+ m_OnValueChanged.Invoke(normalizedPosition);
+ UpdatePrevData();
+ }
+ }
+
+ protected void UpdatePrevData()
+ {
+ if (m_Content == null)
+ m_PrevPosition = Vector2.zero;
+ else
+ m_PrevPosition = m_Content.anchoredPosition;
+ m_PrevViewBounds = m_ViewBounds;
+ m_PrevContentBounds = m_ContentBounds;
+ }
+
+ private void UpdateScrollbars(Vector2 offset)
+ {
+ if (m_HorizontalScrollbar)
+ {
+ if (m_ContentBounds.size.x > 0)
+ m_HorizontalScrollbar.size = Mathf.Clamp01((m_ViewBounds.size.x - Mathf.Abs(offset.x)) / m_ContentBounds.size.x);
+ else
+ m_HorizontalScrollbar.size = 1;
+
+ m_HorizontalScrollbar.value = horizontalNormalizedPosition;
+ }
+
+ if (m_VerticalScrollbar)
+ {
+ if (m_ContentBounds.size.y > 0)
+ m_VerticalScrollbar.size = Mathf.Clamp01((m_ViewBounds.size.y - Mathf.Abs(offset.y)) / m_ContentBounds.size.y);
+ else
+ m_VerticalScrollbar.size = 1;
+
+ m_VerticalScrollbar.value = verticalNormalizedPosition;
+ }
+ }
+
+ public Vector2 normalizedPosition
+ {
+ get
+ {
+ return new Vector2(horizontalNormalizedPosition, verticalNormalizedPosition);
+ }
+ set
+ {
+ SetNormalizedPosition(value.x, 0);
+ SetNormalizedPosition(value.y, 1);
+ }
+ }
+
+ public float horizontalNormalizedPosition
+ {
+ get
+ {
+ UpdateBounds();
+ if (m_ContentBounds.size.x <= m_ViewBounds.size.x)
+ return (m_ViewBounds.min.x > m_ContentBounds.min.x) ? 1 : 0;
+ return (m_ViewBounds.min.x - m_ContentBounds.min.x) / (m_ContentBounds.size.x - m_ViewBounds.size.x);
+ }
+ set
+ {
+ SetNormalizedPosition(value, 0);
+ }
+ }
+
+ public float verticalNormalizedPosition
+ {
+ get
+ {
+ UpdateBounds();
+ if (m_ContentBounds.size.y <= m_ViewBounds.size.y)
+ return (m_ViewBounds.min.y > m_ContentBounds.min.y) ? 1 : 0;
+ ;
+ return (m_ViewBounds.min.y - m_ContentBounds.min.y) / (m_ContentBounds.size.y - m_ViewBounds.size.y);
+ }
+ set
+ {
+ SetNormalizedPosition(value, 1);
+ }
+ }
+
+ private void SetHorizontalNormalizedPosition(float value) { SetNormalizedPosition(value, 0); }
+ private void SetVerticalNormalizedPosition(float value) { SetNormalizedPosition(value, 1); }
+
+ protected virtual void SetNormalizedPosition(float value, int axis)
+ {
+ EnsureLayoutHasRebuilt();
+ UpdateBounds();
+ // How much the content is larger than the view.
+ float hiddenLength = m_ContentBounds.size[axis] - m_ViewBounds.size[axis];
+ // Where the position of the lower left corner of the content bounds should be, in the space of the view.
+ float contentBoundsMinPosition = m_ViewBounds.min[axis] - value * hiddenLength;
+ // The new content localPosition, in the space of the view.
+ float newLocalPosition = m_Content.localPosition[axis] + contentBoundsMinPosition - m_ContentBounds.min[axis];
+
+ Vector3 localPosition = m_Content.localPosition;
+ if (Mathf.Abs(localPosition[axis] - newLocalPosition) > 0.01f)
+ {
+ localPosition[axis] = newLocalPosition;
+ m_Content.localPosition = localPosition;
+ m_Velocity[axis] = 0;
+ UpdateBounds();
+ }
+ }
+
+ private static float RubberDelta(float overStretching, float viewSize)
+ {
+ return (1 - (1 / ((Mathf.Abs(overStretching) * 0.55f / viewSize) + 1))) * viewSize * Mathf.Sign(overStretching);
+ }
+
+ protected override void OnRectTransformDimensionsChange()
+ {
+ SetDirty();
+ }
+
+ private bool hScrollingNeeded
+ {
+ get
+ {
+ if (Application.isPlaying)
+ return m_ContentBounds.size.x > m_ViewBounds.size.x + 0.01f;
+ return true;
+ }
+ }
+ private bool vScrollingNeeded
+ {
+ get
+ {
+ if (Application.isPlaying)
+ return m_ContentBounds.size.y > m_ViewBounds.size.y + 0.01f;
+ return true;
+ }
+ }
+
+ public virtual void CalculateLayoutInputHorizontal() {}
+ public virtual void CalculateLayoutInputVertical() {}
+
+ public virtual float minWidth { get { return -1; } }
+ public virtual float preferredWidth { get { return -1; } }
+ public virtual float flexibleWidth { get { return -1; } }
+
+ public virtual float minHeight { get { return -1; } }
+ public virtual float preferredHeight { get { return -1; } }
+ public virtual float flexibleHeight { get { return -1; } }
+
+ public virtual int layoutPriority { get { return -1; } }
+
+ public virtual void SetLayoutHorizontal()
+ {
+ m_Tracker.Clear();
+
+ if (m_HSliderExpand || m_VSliderExpand)
+ {
+ m_Tracker.Add(this, viewRect,
+ DrivenTransformProperties.Anchors |
+ DrivenTransformProperties.SizeDelta |
+ DrivenTransformProperties.AnchoredPosition);
+
+ // Make view full size to see if content fits.
+ viewRect.anchorMin = Vector2.zero;
+ viewRect.anchorMax = Vector2.one;
+ viewRect.sizeDelta = Vector2.zero;
+ viewRect.anchoredPosition = Vector2.zero;
+
+ // Recalculate content layout with this size to see if it fits when there are no scrollbars.
+ LayoutRebuilder.ForceRebuildLayoutImmediate(content);
+ m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
+ m_ContentBounds = GetBounds();
+ }
+
+ // If it doesn't fit vertically, enable vertical scrollbar and shrink view horizontally to make room for it.
+ if (m_VSliderExpand && vScrollingNeeded)
+ {
+ viewRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), viewRect.sizeDelta.y);
+
+ // Recalculate content layout with this size to see if it fits vertically
+ // when there is a vertical scrollbar (which may reflowed the content to make it taller).
+ LayoutRebuilder.ForceRebuildLayoutImmediate(content);
+ m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
+ m_ContentBounds = GetBounds();
+ }
+
+ // If it doesn't fit horizontally, enable horizontal scrollbar and shrink view vertically to make room for it.
+ if (m_HSliderExpand && hScrollingNeeded)
+ {
+ viewRect.sizeDelta = new Vector2(viewRect.sizeDelta.x, -(m_HSliderHeight + m_HorizontalScrollbarSpacing));
+ m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
+ m_ContentBounds = GetBounds();
+ }
+
+ // If the vertical slider didn't kick in the first time, and the horizontal one did,
+ // we need to check again if the vertical slider now needs to kick in.
+ // If it doesn't fit vertically, enable vertical scrollbar and shrink view horizontally to make room for it.
+ if (m_VSliderExpand && vScrollingNeeded && viewRect.sizeDelta.x == 0 && viewRect.sizeDelta.y < 0)
+ {
+ viewRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), viewRect.sizeDelta.y);
+ }
+ }
+
+ public virtual void SetLayoutVertical()
+ {
+ UpdateScrollbarLayout();
+ m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
+ m_ContentBounds = GetBounds();
+ }
+
+ void UpdateScrollbarVisibility()
+ {
+ UpdateOneScrollbarVisibility(vScrollingNeeded, m_Vertical, m_VerticalScrollbarVisibility, m_VerticalScrollbar);
+ UpdateOneScrollbarVisibility(hScrollingNeeded, m_Horizontal, m_HorizontalScrollbarVisibility, m_HorizontalScrollbar);
+ }
+
+ private static void UpdateOneScrollbarVisibility(bool xScrollingNeeded, bool xAxisEnabled, ScrollbarVisibility scrollbarVisibility, Scrollbar scrollbar)
+ {
+ if (scrollbar)
+ {
+ if (scrollbarVisibility == ScrollbarVisibility.Permanent)
+ {
+ if (scrollbar.gameObject.activeSelf != xAxisEnabled)
+ scrollbar.gameObject.SetActive(xAxisEnabled);
+ }
+ else
+ {
+ if (scrollbar.gameObject.activeSelf != xScrollingNeeded)
+ scrollbar.gameObject.SetActive(xScrollingNeeded);
+ }
+ }
+ }
+
+ void UpdateScrollbarLayout()
+ {
+ if (m_VSliderExpand && m_HorizontalScrollbar)
+ {
+ m_Tracker.Add(this, m_HorizontalScrollbarRect,
+ DrivenTransformProperties.AnchorMinX |
+ DrivenTransformProperties.AnchorMaxX |
+ DrivenTransformProperties.SizeDeltaX |
+ DrivenTransformProperties.AnchoredPositionX);
+ m_HorizontalScrollbarRect.anchorMin = new Vector2(0, m_HorizontalScrollbarRect.anchorMin.y);
+ m_HorizontalScrollbarRect.anchorMax = new Vector2(1, m_HorizontalScrollbarRect.anchorMax.y);
+ m_HorizontalScrollbarRect.anchoredPosition = new Vector2(0, m_HorizontalScrollbarRect.anchoredPosition.y);
+ if (vScrollingNeeded)
+ m_HorizontalScrollbarRect.sizeDelta = new Vector2(-(m_VSliderWidth + m_VerticalScrollbarSpacing), m_HorizontalScrollbarRect.sizeDelta.y);
+ else
+ m_HorizontalScrollbarRect.sizeDelta = new Vector2(0, m_HorizontalScrollbarRect.sizeDelta.y);
+ }
+
+ if (m_HSliderExpand && m_VerticalScrollbar)
+ {
+ m_Tracker.Add(this, m_VerticalScrollbarRect,
+ DrivenTransformProperties.AnchorMinY |
+ DrivenTransformProperties.AnchorMaxY |
+ DrivenTransformProperties.SizeDeltaY |
+ DrivenTransformProperties.AnchoredPositionY);
+ m_VerticalScrollbarRect.anchorMin = new Vector2(m_VerticalScrollbarRect.anchorMin.x, 0);
+ m_VerticalScrollbarRect.anchorMax = new Vector2(m_VerticalScrollbarRect.anchorMax.x, 1);
+ m_VerticalScrollbarRect.anchoredPosition = new Vector2(m_VerticalScrollbarRect.anchoredPosition.x, 0);
+ if (hScrollingNeeded)
+ m_VerticalScrollbarRect.sizeDelta = new Vector2(m_VerticalScrollbarRect.sizeDelta.x, -(m_HSliderHeight + m_HorizontalScrollbarSpacing));
+ else
+ m_VerticalScrollbarRect.sizeDelta = new Vector2(m_VerticalScrollbarRect.sizeDelta.x, 0);
+ }
+ }
+
+ protected void UpdateBounds()
+ {
+ m_ViewBounds = new Bounds(viewRect.rect.center, viewRect.rect.size);
+ m_ContentBounds = GetBounds();
+
+ if (m_Content == null)
+ return;
+
+ Vector3 contentSize = m_ContentBounds.size;
+ Vector3 contentPos = m_ContentBounds.center;
+ var contentPivot = m_Content.pivot;
+ AdjustBounds(ref m_ViewBounds, ref contentPivot, ref contentSize, ref contentPos);
+ m_ContentBounds.size = contentSize;
+ m_ContentBounds.center = contentPos;
+
+ if (movementType == MovementType.Clamped)
+ {
+ // Adjust content so that content bounds bottom (right side) is never higher (to the left) than the view bounds bottom (right side).
+ // top (left side) is never lower (to the right) than the view bounds top (left side).
+ // All this can happen if content has shrunk.
+ // This works because content size is at least as big as view size (because of the call to InternalUpdateBounds above).
+ Vector2 delta = Vector2.zero;
+ if (m_ViewBounds.max.x > m_ContentBounds.max.x)
+ {
+ delta.x = Math.Min(m_ViewBounds.min.x - m_ContentBounds.min.x, m_ViewBounds.max.x - m_ContentBounds.max.x);
+ }
+ else if (m_ViewBounds.min.x < m_ContentBounds.min.x)
+ {
+ delta.x = Math.Max(m_ViewBounds.min.x - m_ContentBounds.min.x, m_ViewBounds.max.x - m_ContentBounds.max.x);
+ }
+
+ if (m_ViewBounds.min.y < m_ContentBounds.min.y)
+ {
+ delta.y = Math.Max(m_ViewBounds.min.y - m_ContentBounds.min.y, m_ViewBounds.max.y - m_ContentBounds.max.y);
+ }
+ else if (m_ViewBounds.max.y > m_ContentBounds.max.y)
+ {
+ delta.y = Math.Min(m_ViewBounds.min.y - m_ContentBounds.min.y, m_ViewBounds.max.y - m_ContentBounds.max.y);
+ }
+ if (delta.sqrMagnitude > float.Epsilon)
+ {
+ contentPos = m_Content.anchoredPosition + delta;
+ if (!m_Horizontal)
+ contentPos.x = m_Content.anchoredPosition.x;
+ if (!m_Vertical)
+ contentPos.y = m_Content.anchoredPosition.y;
+ AdjustBounds(ref m_ViewBounds, ref contentPivot, ref contentSize, ref contentPos);
+ }
+ }
+ }
+
+ internal static void AdjustBounds(ref Bounds viewBounds, ref Vector2 contentPivot, ref Vector3 contentSize, ref Vector3 contentPos)
+ {
+ // Make sure content bounds are at least as large as view by adding padding if not.
+ // One might think at first that if the content is smaller than the view, scrolling should be allowed.
+ // However, that's not how scroll views normally work.
+ // Scrolling is *only* possible when content is *larger* than view.
+ // We use the pivot of the content rect to decide in which directions the content bounds should be expanded.
+ // E.g. if pivot is at top, bounds are expanded downwards.
+ // This also works nicely when ContentSizeFitter is used on the content.
+ Vector3 excess = viewBounds.size - contentSize;
+ if (excess.x > 0)
+ {
+ contentPos.x -= excess.x * (contentPivot.x - 0.5f);
+ contentSize.x = viewBounds.size.x;
+ }
+ if (excess.y > 0)
+ {
+ contentPos.y -= excess.y * (contentPivot.y - 0.5f);
+ contentSize.y = viewBounds.size.y;
+ }
+ }
+
+ private readonly Vector3[] m_Corners = new Vector3[4];
+ private Bounds GetBounds()
+ {
+ if (m_Content == null)
+ return new Bounds();
+ m_Content.GetWorldCorners(m_Corners);
+ var viewWorldToLocalMatrix = viewRect.worldToLocalMatrix;
+ return InternalGetBounds(m_Corners, ref viewWorldToLocalMatrix);
+ }
+
+ internal static Bounds InternalGetBounds(Vector3[] corners, ref Matrix4x4 viewWorldToLocalMatrix)
+ {
+ var vMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
+ var vMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
+
+ for (int j = 0; j < 4; j++)
+ {
+ Vector3 v = viewWorldToLocalMatrix.MultiplyPoint3x4(corners[j]);
+ vMin = Vector3.Min(v, vMin);
+ vMax = Vector3.Max(v, vMax);
+ }
+
+ var bounds = new Bounds(vMin, Vector3.zero);
+ bounds.Encapsulate(vMax);
+ return bounds;
+ }
+
+ private Vector2 CalculateOffset(Vector2 delta)
+ {
+ return InternalCalculateOffset(ref m_ViewBounds, ref m_ContentBounds, m_Horizontal, m_Vertical, m_MovementType, ref delta);
+ }
+
+ internal static Vector2 InternalCalculateOffset(ref Bounds viewBounds, ref Bounds contentBounds, bool horizontal, bool vertical, MovementType movementType, ref Vector2 delta)
+ {
+ Vector2 offset = Vector2.zero;
+ if (movementType == MovementType.Unrestricted)
+ return offset;
+
+ Vector2 min = contentBounds.min;
+ Vector2 max = contentBounds.max;
+
+ if (horizontal)
+ {
+ min.x += delta.x;
+ max.x += delta.x;
+ if (min.x > viewBounds.min.x)
+ offset.x = viewBounds.min.x - min.x;
+ else if (max.x < viewBounds.max.x)
+ offset.x = viewBounds.max.x - max.x;
+ }
+
+ if (vertical)
+ {
+ min.y += delta.y;
+ max.y += delta.y;
+ if (max.y < viewBounds.max.y)
+ offset.y = viewBounds.max.y - max.y;
+ else if (min.y > viewBounds.min.y)
+ offset.y = viewBounds.min.y - min.y;
+ }
+
+ return offset;
+ }
+
+ protected void SetDirty()
+ {
+ if (!IsActive())
+ return;
+
+ LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
+ }
+
+ protected void SetDirtyCaching()
+ {
+ if (!IsActive())
+ return;
+
+ CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
+ LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
+ }
+
+ #if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ SetDirtyCaching();
+ }
+
+ #endif
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ScrollRect.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ScrollRect.cs.meta
new file mode 100644
index 0000000..98d9f63
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ScrollRect.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 209674c43848ab24e8778e3f7bcd0f11
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Scrollbar.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Scrollbar.cs
new file mode 100644
index 0000000..c280305
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Scrollbar.cs
@@ -0,0 +1,422 @@
+using System;
+using System.Collections;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.UI
+{
+ [AddComponentMenu("UI/Scrollbar", 34)]
+ [RequireComponent(typeof(RectTransform))]
+ public class Scrollbar
+ : Selectable
+ , IBeginDragHandler
+ , IDragHandler
+ , IInitializePotentialDragHandler
+ , ICanvasElement
+ {
+ public enum Direction
+ {
+ LeftToRight,
+ RightToLeft,
+ BottomToTop,
+ TopToBottom,
+ }
+
+ [Serializable]
+ public class ScrollEvent : UnityEvent<float> {}
+
+ [SerializeField]
+ private RectTransform m_HandleRect;
+ public RectTransform handleRect { get { return m_HandleRect; } set { if (SetPropertyUtility.SetClass(ref m_HandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
+
+ // Direction of movement.
+ [SerializeField]
+ private Direction m_Direction = Direction.LeftToRight;
+ public Direction direction { get { return m_Direction; } set { if (SetPropertyUtility.SetStruct(ref m_Direction, value)) UpdateVisuals(); } }
+
+ protected Scrollbar()
+ {}
+
+ // Scroll bar's current value in 0 to 1 range.
+ [Range(0f, 1f)]
+ [SerializeField]
+ private float m_Value;
+ public float value
+ {
+ get
+ {
+ float val = m_Value;
+ if (m_NumberOfSteps > 1)
+ val = Mathf.Round(val * (m_NumberOfSteps - 1)) / (m_NumberOfSteps - 1);
+ return val;
+ }
+ set
+ {
+ Set(value);
+ }
+ }
+
+ // Scroll bar's current size in 0 to 1 range.
+ [Range(0f, 1f)]
+ [SerializeField]
+ private float m_Size = 0.2f;
+ public float size { get { return m_Size; } set { if (SetPropertyUtility.SetStruct(ref m_Size, Mathf.Clamp01(value))) UpdateVisuals(); } }
+
+ // Number of steps the scroll bar should be divided into. For example 5 means possible values of 0, 0.25, 0.5, 0.75, and 1.0.
+ [Range(0, 11)]
+ [SerializeField]
+ private int m_NumberOfSteps = 0;
+ public int numberOfSteps { get { return m_NumberOfSteps; } set { if (SetPropertyUtility.SetStruct(ref m_NumberOfSteps, value)) { Set(m_Value); UpdateVisuals(); } } }
+
+ [Space(6)]
+
+ // Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
+ [SerializeField]
+ private ScrollEvent m_OnValueChanged = new ScrollEvent();
+ public ScrollEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
+
+ // Private fields
+
+ private RectTransform m_ContainerRect;
+
+ // The offset from handle position to mouse down position
+ private Vector2 m_Offset = Vector2.zero;
+
+ // Size of each step.
+ float stepSize { get { return (m_NumberOfSteps > 1) ? 1f / (m_NumberOfSteps - 1) : 0.1f; } }
+
+ private DrivenRectTransformTracker m_Tracker;
+ private Coroutine m_PointerDownRepeat;
+ private bool isPointerDownAndNotDragging = false;
+
+#if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+
+ m_Size = Mathf.Clamp01(m_Size);
+
+ //This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
+ if (IsActive())
+ {
+ UpdateCachedReferences();
+ Set(m_Value, false);
+ // Update rects since other things might affect them even if value didn't change.
+ UpdateVisuals();
+ }
+
+ var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
+ if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
+ CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
+ }
+
+#endif // if UNITY_EDITOR
+
+ public virtual void Rebuild(CanvasUpdate executing)
+ {
+#if UNITY_EDITOR
+ if (executing == CanvasUpdate.Prelayout)
+ onValueChanged.Invoke(value);
+#endif
+ }
+
+ public virtual void LayoutComplete()
+ {}
+
+ public virtual void GraphicUpdateComplete()
+ {}
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+ UpdateCachedReferences();
+ Set(m_Value, false);
+ // Update rects since they need to be initialized correctly.
+ UpdateVisuals();
+ }
+
+ protected override void OnDisable()
+ {
+ m_Tracker.Clear();
+ base.OnDisable();
+ }
+
+ void UpdateCachedReferences()
+ {
+ if (m_HandleRect && m_HandleRect.parent != null)
+ m_ContainerRect = m_HandleRect.parent.GetComponent<RectTransform>();
+ else
+ m_ContainerRect = null;
+ }
+
+ // Update the visible Image.
+ void Set(float input)
+ {
+ Set(input, true);
+ }
+
+ void Set(float input, bool sendCallback)
+ {
+ float currentValue = m_Value;
+ // Clamp the input
+ m_Value = Mathf.Clamp01(input);
+
+ // If the stepped value doesn't match the last one, it's time to update
+ if (currentValue == value)
+ return;
+
+ UpdateVisuals();
+ if (sendCallback)
+ {
+ UISystemProfilerApi.AddMarker("Scrollbar.value", this);
+ m_OnValueChanged.Invoke(value);
+ }
+ }
+
+ protected override void OnRectTransformDimensionsChange()
+ {
+ base.OnRectTransformDimensionsChange();
+
+ //This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
+ if (!IsActive())
+ return;
+
+ UpdateVisuals();
+ }
+
+ enum Axis
+ {
+ Horizontal = 0,
+ Vertical = 1
+ }
+
+ Axis axis { get { return (m_Direction == Direction.LeftToRight || m_Direction == Direction.RightToLeft) ? Axis.Horizontal : Axis.Vertical; } }
+ bool reverseValue { get { return m_Direction == Direction.RightToLeft || m_Direction == Direction.TopToBottom; } }
+
+ // Force-update the scroll bar. Useful if you've changed the properties and want it to update visually.
+ private void UpdateVisuals()
+ {
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ UpdateCachedReferences();
+#endif
+ m_Tracker.Clear();
+
+ if (m_ContainerRect != null)
+ {
+ m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors);
+ Vector2 anchorMin = Vector2.zero;
+ Vector2 anchorMax = Vector2.one;
+
+ float movement = value * (1 - size);
+ if (reverseValue)
+ {
+ anchorMin[(int)axis] = 1 - movement - size;
+ anchorMax[(int)axis] = 1 - movement;
+ }
+ else
+ {
+ anchorMin[(int)axis] = movement;
+ anchorMax[(int)axis] = movement + size;
+ }
+
+ m_HandleRect.anchorMin = anchorMin;
+ m_HandleRect.anchorMax = anchorMax;
+ }
+ }
+
+ // Update the scroll bar's position based on the mouse.
+ void UpdateDrag(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ if (m_ContainerRect == null)
+ return;
+
+ Vector2 localCursor;
+ if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(m_ContainerRect, eventData.position, eventData.pressEventCamera, out localCursor))
+ return;
+
+ Vector2 handleCenterRelativeToContainerCorner = localCursor - m_Offset - m_ContainerRect.rect.position;
+ Vector2 handleCorner = handleCenterRelativeToContainerCorner - (m_HandleRect.rect.size - m_HandleRect.sizeDelta) * 0.5f;
+
+ float parentSize = axis == 0 ? m_ContainerRect.rect.width : m_ContainerRect.rect.height;
+ float remainingSize = parentSize * (1 - size);
+ if (remainingSize <= 0)
+ return;
+
+ switch (m_Direction)
+ {
+ case Direction.LeftToRight:
+ Set(handleCorner.x / remainingSize);
+ break;
+ case Direction.RightToLeft:
+ Set(1f - (handleCorner.x / remainingSize));
+ break;
+ case Direction.BottomToTop:
+ Set(handleCorner.y / remainingSize);
+ break;
+ case Direction.TopToBottom:
+ Set(1f - (handleCorner.y / remainingSize));
+ break;
+ }
+ }
+
+ private bool MayDrag(PointerEventData eventData)
+ {
+ return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
+ }
+
+ public virtual void OnBeginDrag(PointerEventData eventData)
+ {
+ isPointerDownAndNotDragging = false;
+
+ if (!MayDrag(eventData))
+ return;
+
+ if (m_ContainerRect == null)
+ return;
+
+ m_Offset = Vector2.zero;
+ if (RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera))
+ {
+ Vector2 localMousePos;
+ if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
+ m_Offset = localMousePos - m_HandleRect.rect.center;
+ }
+ }
+
+ public virtual void OnDrag(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ if (m_ContainerRect != null)
+ UpdateDrag(eventData);
+ }
+
+ public override void OnPointerDown(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ base.OnPointerDown(eventData);
+ isPointerDownAndNotDragging = true;
+ m_PointerDownRepeat = StartCoroutine(ClickRepeat(eventData));
+ }
+
+ protected IEnumerator ClickRepeat(PointerEventData eventData)
+ {
+ while (isPointerDownAndNotDragging)
+ {
+ if (!RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera))
+ {
+ Vector2 localMousePos;
+ if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
+ {
+ var axisCoordinate = axis == 0 ? localMousePos.x : localMousePos.y;
+ if (axisCoordinate < 0)
+ value -= size;
+ else
+ value += size;
+ }
+ }
+ yield return new WaitForEndOfFrame();
+ }
+ StopCoroutine(m_PointerDownRepeat);
+ }
+
+ public override void OnPointerUp(PointerEventData eventData)
+ {
+ base.OnPointerUp(eventData);
+ isPointerDownAndNotDragging = false;
+ }
+
+ public override void OnMove(AxisEventData eventData)
+ {
+ if (!IsActive() || !IsInteractable())
+ {
+ base.OnMove(eventData);
+ return;
+ }
+
+ switch (eventData.moveDir)
+ {
+ case MoveDirection.Left:
+ if (axis == Axis.Horizontal && FindSelectableOnLeft() == null)
+ Set(reverseValue ? value + stepSize : value - stepSize);
+ else
+ base.OnMove(eventData);
+ break;
+ case MoveDirection.Right:
+ if (axis == Axis.Horizontal && FindSelectableOnRight() == null)
+ Set(reverseValue ? value - stepSize : value + stepSize);
+ else
+ base.OnMove(eventData);
+ break;
+ case MoveDirection.Up:
+ if (axis == Axis.Vertical && FindSelectableOnUp() == null)
+ Set(reverseValue ? value - stepSize : value + stepSize);
+ else
+ base.OnMove(eventData);
+ break;
+ case MoveDirection.Down:
+ if (axis == Axis.Vertical && FindSelectableOnDown() == null)
+ Set(reverseValue ? value + stepSize : value - stepSize);
+ else
+ base.OnMove(eventData);
+ break;
+ }
+ }
+
+ public override Selectable FindSelectableOnLeft()
+ {
+ if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
+ return null;
+ return base.FindSelectableOnLeft();
+ }
+
+ public override Selectable FindSelectableOnRight()
+ {
+ if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
+ return null;
+ return base.FindSelectableOnRight();
+ }
+
+ public override Selectable FindSelectableOnUp()
+ {
+ if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
+ return null;
+ return base.FindSelectableOnUp();
+ }
+
+ public override Selectable FindSelectableOnDown()
+ {
+ if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
+ return null;
+ return base.FindSelectableOnDown();
+ }
+
+ public virtual void OnInitializePotentialDrag(PointerEventData eventData)
+ {
+ eventData.useDragThreshold = false;
+ }
+
+ public void SetDirection(Direction direction, bool includeRectLayouts)
+ {
+ Axis oldAxis = axis;
+ bool oldReverse = reverseValue;
+ this.direction = direction;
+
+ if (!includeRectLayouts)
+ return;
+
+ if (axis != oldAxis)
+ RectTransformUtility.FlipLayoutAxes(transform as RectTransform, true, true);
+
+ if (reverseValue != oldReverse)
+ RectTransformUtility.FlipLayoutOnAxis(transform as RectTransform, (int)axis, true, true);
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Scrollbar.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Scrollbar.cs.meta
new file mode 100644
index 0000000..04b16c4
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Scrollbar.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 340d739069b7eae4daa7634ca999bcf8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Selectable.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Selectable.cs
new file mode 100644
index 0000000..9cc6178
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Selectable.cs
@@ -0,0 +1,672 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine.Serialization;
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.UI
+{
+ // Simple selectable object - derived from to create a control.
+ [AddComponentMenu("UI/Selectable", 70)]
+ [ExecuteInEditMode]
+ [SelectionBase]
+ [DisallowMultipleComponent]
+ public class Selectable
+ :
+ UIBehaviour,
+ IMoveHandler, // Input>Horizontal\Vertical移动时收到这个消息
+ IPointerDownHandler, IPointerUpHandler, // 点击click
+ IPointerEnterHandler, IPointerExitHandler, // 悬停hover
+ ISelectHandler, IDeselectHandler // navigation获得焦点时收到这个消息
+ {
+ // Selection state
+
+ // 当前场景中的Selectable组件
+ // List of all the selectable objects currently active in the scene
+ private static List<Selectable> s_List = new List<Selectable>();
+ public static List<Selectable> allSelectables { get { return s_List; } }
+
+ // Navigation information.
+ [FormerlySerializedAs("navigation")]
+ [SerializeField]
+ private Navigation m_Navigation = Navigation.defaultNavigation;
+
+ // Highlighting state
+ public enum Transition
+ {
+ None,
+ ColorTint,
+ SpriteSwap,
+ Animation
+ }
+
+ // Type of the transition that occurs when the button state changes.
+ [FormerlySerializedAs("transition")]
+ [SerializeField]
+ private Transition m_Transition = Transition.ColorTint;
+
+ // Colors used for a color tint-based transition.
+ [FormerlySerializedAs("colors")]
+ [SerializeField]
+ private ColorBlock m_Colors = ColorBlock.defaultColorBlock;
+
+ // Sprites used for a Image swap-based transition.
+ [FormerlySerializedAs("spriteState")]
+ [SerializeField]
+ private SpriteState m_SpriteState;
+
+ [FormerlySerializedAs("animationTriggers")]
+ [SerializeField]
+ private AnimationTriggers m_AnimationTriggers = new AnimationTriggers();
+
+ [Tooltip("Can the Selectable be interacted with?")]
+ [SerializeField]
+ private bool m_Interactable = true;
+
+ //c m_TargetGraphic 只是用来做展示,这个变量不会用来进行射线检测,在Awake()里注释掉也还会触发点击事件
+ // Graphic that will be colored.
+ [FormerlySerializedAs("highlightGraphic")]
+ [FormerlySerializedAs("m_HighlightGraphic")]
+ [SerializeField]
+ private Graphic m_TargetGraphic;
+
+
+ private bool m_GroupsAllowInteraction = true;
+
+ private SelectionState m_CurrentSelectionState;
+
+ public Navigation navigation { get { return m_Navigation; } set { if (SetPropertyUtility.SetStruct(ref m_Navigation, value)) OnSetProperty(); } }
+ public Transition transition { get { return m_Transition; } set { if (SetPropertyUtility.SetStruct(ref m_Transition, value)) OnSetProperty(); } }
+ public ColorBlock colors { get { return m_Colors; } set { if (SetPropertyUtility.SetStruct(ref m_Colors, value)) OnSetProperty(); } }
+ public SpriteState spriteState { get { return m_SpriteState; } set { if (SetPropertyUtility.SetStruct(ref m_SpriteState, value)) OnSetProperty(); } }
+ public AnimationTriggers animationTriggers { get { return m_AnimationTriggers; } set { if (SetPropertyUtility.SetClass(ref m_AnimationTriggers, value)) OnSetProperty(); } }
+ public Graphic targetGraphic {
+ get {
+ return m_TargetGraphic;
+ }
+ set {
+ if (SetPropertyUtility.SetClass(ref m_TargetGraphic, value))
+ OnSetProperty();
+ }
+ }
+ public bool interactable
+ {
+ get { return m_Interactable; }
+ set
+ {
+ if (SetPropertyUtility.SetStruct(ref m_Interactable, value))
+ {
+ if (!m_Interactable && EventSystem.current != null && EventSystem.current.currentSelectedGameObject == gameObject)
+ EventSystem.current.SetSelectedGameObject(null);
+ if (m_Interactable)
+ UpdateSelectionState(null);
+ OnSetProperty();
+ }
+ }
+ }
+
+ private bool isPointerInside { get; set; }
+ private bool isPointerDown { get; set; }
+ private bool hasSelection { get; set; }
+
+ protected Selectable()
+ {}
+
+ // Convenience function that converts the Graphic to a Image, if possible
+ public Image image
+ {
+ get {
+ return m_TargetGraphic as Image;
+ }
+ set {
+ m_TargetGraphic = value;
+ }
+ }
+
+ // Get the animator
+ public Animator animator
+ {
+ get { return GetComponent<Animator>(); }
+ }
+
+ protected override void Awake()
+ {
+ if (m_TargetGraphic == null)
+ m_TargetGraphic = GetComponent<Graphic>();
+ }
+
+ private readonly List<CanvasGroup> m_CanvasGroupCache = new List<CanvasGroup>();
+ protected override void OnCanvasGroupChanged()
+ {
+ // Figure out if parent groups allow interaction
+ // If no interaction is alowed... then we need
+ // to not do that :)
+ var groupAllowInteraction = true;
+ Transform t = transform;
+ while (t != null)
+ {
+ t.GetComponents(m_CanvasGroupCache);
+ bool shouldBreak = false;
+ for (var i = 0; i < m_CanvasGroupCache.Count; i++)
+ {
+ // if the parent group does not allow interaction
+ // we need to break
+ if (!m_CanvasGroupCache[i].interactable)
+ {
+ groupAllowInteraction = false;
+ shouldBreak = true;
+ }
+ // if this is a 'fresh' group, then break
+ // as we should not consider parents
+ if (m_CanvasGroupCache[i].ignoreParentGroups)
+ shouldBreak = true;
+ }
+ if (shouldBreak)
+ break;
+
+ t = t.parent;
+ }
+
+ if (groupAllowInteraction != m_GroupsAllowInteraction)
+ {
+ m_GroupsAllowInteraction = groupAllowInteraction;
+ OnSetProperty();
+ }
+ }
+
+ public virtual bool IsInteractable()
+ {
+ return m_GroupsAllowInteraction && m_Interactable;
+ }
+
+ // Call from unity if animation properties have changed
+ protected override void OnDidApplyAnimationProperties()
+ {
+ OnSetProperty();
+ }
+
+ // Select on enable and add to the list.
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ s_List.Add(this);
+ var state = SelectionState.Normal;
+
+ // The button will be highlighted even in some cases where it shouldn't.
+ // For example: We only want to set the State as Highlighted if the StandaloneInputModule.m_CurrentInputMode == InputMode.Buttons
+ // But we dont have access to this, and it might not apply to other InputModules.
+ // TODO: figure out how to solve this. Case 617348.
+ if (hasSelection)
+ state = SelectionState.Highlighted;
+
+ m_CurrentSelectionState = state;
+ InternalEvaluateAndTransitionToSelectionState(true);
+ }
+
+ private void OnSetProperty()
+ {
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ InternalEvaluateAndTransitionToSelectionState(true);
+ else
+#endif
+ InternalEvaluateAndTransitionToSelectionState(false);
+ }
+
+ // Remove from the list.
+ protected override void OnDisable()
+ {
+ s_List.Remove(this);
+ InstantClearState();
+ base.OnDisable();
+ }
+
+#if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+ m_Colors.fadeDuration = Mathf.Max(m_Colors.fadeDuration, 0.0f);
+
+ // OnValidate can be called before OnEnable, this makes it unsafe to access other components
+ // since they might not have been initialized yet.
+ // OnSetProperty potentially access Animator or Graphics. (case 618186)
+ if (isActiveAndEnabled)
+ {
+ if (!interactable && EventSystem.current != null && EventSystem.current.currentSelectedGameObject == gameObject)
+ EventSystem.current.SetSelectedGameObject(null);
+ // Need to clear out the override image on the target...
+ DoSpriteSwap(null);
+
+ // If the transition mode got changed, we need to clear all the transitions, since we don't know what the old transition mode was.
+ StartColorTween(Color.white, true);
+ TriggerAnimation(m_AnimationTriggers.normalTrigger);
+
+ // And now go to the right state.
+ InternalEvaluateAndTransitionToSelectionState(true);
+ }
+ }
+
+ protected override void Reset()
+ {
+ m_TargetGraphic = GetComponent<Graphic>();
+ }
+
+#endif // if UNITY_EDITOR
+
+ protected SelectionState currentSelectionState
+ {
+ get { return m_CurrentSelectionState; }
+ }
+
+ protected virtual void InstantClearState()
+ {
+ string triggerName = m_AnimationTriggers.normalTrigger;
+
+ isPointerInside = false;
+ isPointerDown = false;
+ hasSelection = false;
+
+ switch (m_Transition)
+ {
+ case Transition.ColorTint:
+ StartColorTween(Color.white, true);
+ break;
+ case Transition.SpriteSwap:
+ DoSpriteSwap(null);
+ break;
+ case Transition.Animation:
+ TriggerAnimation(triggerName);
+ break;
+ }
+ }
+
+ protected virtual void DoStateTransition(SelectionState state, bool instant)
+ {
+ Color tintColor;
+ Sprite transitionSprite;
+ string triggerName;
+
+ switch (state)
+ {
+ case SelectionState.Normal:
+ tintColor = m_Colors.normalColor;
+ transitionSprite = null;
+ triggerName = m_AnimationTriggers.normalTrigger;
+ break;
+ case SelectionState.Highlighted:
+ tintColor = m_Colors.highlightedColor;
+ transitionSprite = m_SpriteState.highlightedSprite;
+ triggerName = m_AnimationTriggers.highlightedTrigger;
+ break;
+ case SelectionState.Pressed:
+ tintColor = m_Colors.pressedColor;
+ transitionSprite = m_SpriteState.pressedSprite;
+ triggerName = m_AnimationTriggers.pressedTrigger;
+ break;
+ case SelectionState.Disabled:
+ tintColor = m_Colors.disabledColor;
+ transitionSprite = m_SpriteState.disabledSprite;
+ triggerName = m_AnimationTriggers.disabledTrigger;
+ break;
+ default:
+ tintColor = Color.black;
+ transitionSprite = null;
+ triggerName = string.Empty;
+ break;
+ }
+
+ if (gameObject.activeInHierarchy)
+ {
+ switch (m_Transition)
+ {
+ case Transition.ColorTint:
+ StartColorTween(tintColor * m_Colors.colorMultiplier, instant);
+ break;
+ case Transition.SpriteSwap:
+ DoSpriteSwap(transitionSprite);
+ break;
+ case Transition.Animation:
+ TriggerAnimation(triggerName);
+ break;
+ }
+ }
+ }
+
+ protected enum SelectionState
+ {
+ Normal,
+ Highlighted,
+ Pressed,
+ Disabled
+ }
+
+ // Selection logic
+
+ // Find the next selectable object in the specified world-space direction.
+ public Selectable FindSelectable(Vector3 dir)
+ {
+ dir = dir.normalized;
+ Vector3 localDir = Quaternion.Inverse(transform.rotation) * dir;
+ Vector3 pos = transform.TransformPoint(GetPointOnRectEdge(transform as RectTransform, localDir));
+ float maxScore = Mathf.NegativeInfinity;
+ Selectable bestPick = null;
+ for (int i = 0; i < s_List.Count; ++i) // 遍历当前场景中的所有selectable组件
+ {
+ Selectable sel = s_List[i];
+
+ if (sel == this || sel == null)
+ continue;
+
+ if (!sel.IsInteractable() || sel.navigation.mode == Navigation.Mode.None)
+ continue;
+
+ var selRect = sel.transform as RectTransform;
+ Vector3 selCenter = selRect != null ? (Vector3)selRect.rect.center : Vector3.zero;
+ Vector3 myVector = sel.transform.TransformPoint(selCenter) - pos;
+
+ // Value that is the distance out along the direction.
+ float dot = Vector3.Dot(dir, myVector);
+
+ // Skip elements that are in the wrong direction or which have zero distance.
+ // This also ensures that the scoring formula below will not have a division by zero error.
+ if (dot <= 0)
+ continue;
+
+ // This scoring function has two priorities:
+ // - Score higher for positions that are closer.
+ // - Score higher for positions that are located in the right direction.
+ // This scoring function combines both of these criteria.
+ // It can be seen as this:
+ // Dot (dir, myVector.normalized) / myVector.magnitude
+ // The first part equals 1 if the direction of myVector is the same as dir, and 0 if it's orthogonal.
+ // The second part scores lower the greater the distance is by dividing by the distance.
+ // The formula below is equivalent but more optimized.
+ //
+ // If a given score is chosen, the positions that evaluate to that score will form a circle
+ // that touches pos and whose center is located along dir. A way to visualize the resulting functionality is this:
+ // From the position pos, blow up a circular balloon so it grows in the direction of dir.
+ // The first Selectable whose center the circular balloon touches is the one that's chosen.
+ float score = dot / myVector.sqrMagnitude;
+
+ if (score > maxScore)
+ {
+ maxScore = score;
+ bestPick = sel;
+ }
+ }
+ return bestPick;
+ }
+
+ private static Vector3 GetPointOnRectEdge(RectTransform rect, Vector2 dir)
+ {
+ if (rect == null)
+ return Vector3.zero;
+ if (dir != Vector2.zero)
+ dir /= Mathf.Max(Mathf.Abs(dir.x), Mathf.Abs(dir.y));
+ dir = rect.rect.center + Vector2.Scale(rect.rect.size, dir * 0.5f);
+ return dir;
+ }
+
+ // Convenience function -- change the selection to the specified object if it's not null and happens to be active.
+ void Navigate(AxisEventData eventData, Selectable sel)
+ {
+ if (sel != null && sel.IsActive())
+ eventData.selectedObject = sel.gameObject; // 会发送一个selectHandler事件
+ }
+
+ // Find the selectable object to the left of this one.
+ public virtual Selectable FindSelectableOnLeft()
+ {
+ if (m_Navigation.mode == Navigation.Mode.Explicit)
+ {
+ return m_Navigation.selectOnLeft;
+ }
+ if ((m_Navigation.mode & Navigation.Mode.Horizontal) != 0)
+ {
+ return FindSelectable(transform.rotation * Vector3.left);
+ }
+ return null;
+ }
+
+ // Find the selectable object to the right of this one.
+ public virtual Selectable FindSelectableOnRight()
+ {
+ if (m_Navigation.mode == Navigation.Mode.Explicit)
+ {
+ return m_Navigation.selectOnRight;
+ }
+ if ((m_Navigation.mode & Navigation.Mode.Horizontal) != 0)
+ {
+ return FindSelectable(transform.rotation * Vector3.right);
+ }
+ return null;
+ }
+
+ // Find the selectable object above this one
+ public virtual Selectable FindSelectableOnUp()
+ {
+ if (m_Navigation.mode == Navigation.Mode.Explicit)
+ {
+ return m_Navigation.selectOnUp;
+ }
+ if ((m_Navigation.mode & Navigation.Mode.Vertical) != 0)
+ {
+ return FindSelectable(transform.rotation * Vector3.up);
+ }
+ return null;
+ }
+
+ // Find the selectable object below this one.
+ public virtual Selectable FindSelectableOnDown()
+ {
+ if (m_Navigation.mode == Navigation.Mode.Explicit)
+ {
+ return m_Navigation.selectOnDown;
+ }
+ if ((m_Navigation.mode & Navigation.Mode.Vertical) != 0)
+ {
+ return FindSelectable(transform.rotation * Vector3.down);
+ }
+ return null;
+ }
+
+ // input>horizontal\vertical产生这个事件,
+ public virtual void OnMove(AxisEventData eventData)
+ {
+ LogHelper.Log("OnMove() " + gameObject.name );
+
+ switch (eventData.moveDir)
+ {
+ case MoveDirection.Right:
+ Navigate(eventData, FindSelectableOnRight()); // 会向下一个selectableObj发送一个OnSelect消息
+ break;
+
+ case MoveDirection.Up:
+ Navigate(eventData, FindSelectableOnUp());
+ break;
+
+ case MoveDirection.Left:
+ Navigate(eventData, FindSelectableOnLeft());
+ break;
+
+ case MoveDirection.Down:
+ Navigate(eventData, FindSelectableOnDown());
+ break;
+ }
+ }
+
+ void StartColorTween(Color targetColor, bool instant)
+ {
+ if (m_TargetGraphic == null)
+ return;
+
+ // 起一个协程做tween动画
+ m_TargetGraphic.CrossFadeColor(targetColor, instant ? 0f : m_Colors.fadeDuration, true, true);
+ }
+
+ void DoSpriteSwap(Sprite newSprite)
+ {
+ if (image == null)
+ return;
+
+ image.overrideSprite = newSprite;
+ }
+
+ void TriggerAnimation(string triggername)
+ {
+ if (transition != Transition.Animation || animator == null || !animator.isActiveAndEnabled || !animator.hasBoundPlayables || string.IsNullOrEmpty(triggername))
+ return;
+
+ LogHelper.Log("trigger animation");
+
+ animator.ResetTrigger(m_AnimationTriggers.normalTrigger);
+ animator.ResetTrigger(m_AnimationTriggers.pressedTrigger);
+ animator.ResetTrigger(m_AnimationTriggers.highlightedTrigger);
+ animator.ResetTrigger(m_AnimationTriggers.disabledTrigger);
+
+ animator.SetTrigger(triggername);
+ }
+
+ // Whether the control should be 'selected'.
+ protected bool IsHighlighted(BaseEventData eventData)
+ {
+ if (!IsActive())
+ return false;
+
+ if (IsPressed())
+ return false;
+
+ bool selected = hasSelection;
+ if (eventData is PointerEventData)
+ {
+ var pointerData = eventData as PointerEventData;
+ selected |=
+ (isPointerDown && !isPointerInside && pointerData.pointerPress == gameObject) // This object pressed, but pointer moved off
+ || (!isPointerDown && isPointerInside && pointerData.pointerPress == gameObject) // This object pressed, but pointer released over (PointerUp event)
+ || (!isPointerDown && isPointerInside && pointerData.pointerPress == null); // Nothing pressed, but pointer is over
+ }
+ else
+ {
+ selected |= isPointerInside;
+ }
+ return selected;
+ }
+
+ [Obsolete("Is Pressed no longer requires eventData", false)]
+ protected bool IsPressed(BaseEventData eventData)
+ {
+ return IsPressed();
+ }
+
+ // Whether the control should be pressed.
+ protected bool IsPressed()
+ {
+ if (!IsActive())
+ return false;
+
+ return isPointerInside && isPointerDown;
+ }
+
+ // The current visual state of the control.
+ protected void UpdateSelectionState(BaseEventData eventData)
+ {
+ if (IsPressed())
+ {
+ m_CurrentSelectionState = SelectionState.Pressed;
+ return;
+ }
+
+ if (IsHighlighted(eventData))
+ {
+ m_CurrentSelectionState = SelectionState.Highlighted;
+ return;
+ }
+
+ m_CurrentSelectionState = SelectionState.Normal;
+ }
+
+ // 更新状态播放动画
+ // Change the button to the correct state
+ private void EvaluateAndTransitionToSelectionState(BaseEventData eventData)
+ {
+ if (!IsActive() || !IsInteractable())
+ return;
+
+ UpdateSelectionState(eventData);
+ InternalEvaluateAndTransitionToSelectionState(false);
+ }
+
+ private void InternalEvaluateAndTransitionToSelectionState(bool instant)
+ {
+ var transitionState = m_CurrentSelectionState;
+ if (IsActive() && !IsInteractable())
+ transitionState = SelectionState.Disabled;
+ DoStateTransition(transitionState, instant);
+ }
+
+ public virtual void OnPointerDown(PointerEventData eventData)
+ {
+ LogHelper.Log("OnPointerDown() "+ gameObject.name);
+
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ // Selection tracking
+ if (IsInteractable() && navigation.mode != Navigation.Mode.None && EventSystem.current != null)
+ EventSystem.current.SetSelectedGameObject(gameObject, eventData); // 选中这个UI组件
+
+ isPointerDown = true;
+ EvaluateAndTransitionToSelectionState(eventData);
+ }
+
+ public virtual void OnPointerUp(PointerEventData eventData)
+ {
+ LogHelper.Log("OnPointerUp() " + gameObject.name);
+
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ isPointerDown = false;
+ EvaluateAndTransitionToSelectionState(eventData);
+ }
+
+ public virtual void OnPointerEnter(PointerEventData eventData)
+ {
+ LogHelper.Log("OnPointerEnter() " + gameObject.name);
+
+ isPointerInside = true;
+ EvaluateAndTransitionToSelectionState(eventData);
+ }
+
+ public virtual void OnPointerExit(PointerEventData eventData)
+ {
+ LogHelper.Log("OnPointerExit() " + gameObject.name);
+
+ isPointerInside = false;
+ EvaluateAndTransitionToSelectionState(eventData);
+ }
+
+ // 被选中
+ public virtual void OnSelect(BaseEventData eventData)
+ {
+ LogHelper.Log("OnSelect() " + gameObject.name);
+ hasSelection = true;
+ EvaluateAndTransitionToSelectionState(eventData);
+ }
+
+ public virtual void OnDeselect(BaseEventData eventData)
+ {
+ LogHelper.Log("OnDeselect() " + gameObject.name);
+ hasSelection = false;
+ EvaluateAndTransitionToSelectionState(eventData);
+ }
+
+ public virtual void Select()
+ {
+ if (EventSystem.current == null || EventSystem.current.alreadySelecting)
+ return;
+
+ EventSystem.current.SetSelectedGameObject(gameObject);
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Selectable.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Selectable.cs.meta
new file mode 100644
index 0000000..394fcbd
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Selectable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: af75b3d5d7b48814da1f058ff4ae7653
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Slider.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Slider.cs
new file mode 100644
index 0000000..4aad4e8
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Slider.cs
@@ -0,0 +1,450 @@
+using System;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.UI
+{
+ [AddComponentMenu("UI/Slider", 33)]
+ [RequireComponent(typeof(RectTransform))]
+ public class Slider
+ : Selectable
+ , IDragHandler
+ , IInitializePotentialDragHandler // 拖拽之前
+ , ICanvasElement // 编辑器下才会用到
+ {
+ public enum Direction
+ {
+ LeftToRight,
+ RightToLeft,
+ BottomToTop,
+ TopToBottom,
+ }
+
+ [Serializable]
+ public class SliderEvent : UnityEvent<float> {}
+
+ [SerializeField]
+ private RectTransform m_FillRect;
+ public RectTransform fillRect { get { return m_FillRect; } set { if (SetPropertyUtility.SetClass(ref m_FillRect, value)) {UpdateCachedReferences(); UpdateVisuals(); } } }
+
+ [SerializeField]
+ private RectTransform m_HandleRect;
+ public RectTransform handleRect { get { return m_HandleRect; } set { if (SetPropertyUtility.SetClass(ref m_HandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
+
+ [Space]
+
+ [SerializeField]
+ private Direction m_Direction = Direction.LeftToRight;
+ public Direction direction { get { return m_Direction; } set { if (SetPropertyUtility.SetStruct(ref m_Direction, value)) UpdateVisuals(); } }
+
+ [SerializeField]
+ private float m_MinValue = 0;
+ public float minValue { get { return m_MinValue; } set { if (SetPropertyUtility.SetStruct(ref m_MinValue, value)) { Set(m_Value); UpdateVisuals(); } } }
+
+ [SerializeField]
+ private float m_MaxValue = 1;
+ public float maxValue { get { return m_MaxValue; } set { if (SetPropertyUtility.SetStruct(ref m_MaxValue, value)) { Set(m_Value); UpdateVisuals(); } } }
+
+ [SerializeField]
+ private bool m_WholeNumbers = false;
+ public bool wholeNumbers { get { return m_WholeNumbers; } set { if (SetPropertyUtility.SetStruct(ref m_WholeNumbers, value)) { Set(m_Value); UpdateVisuals(); } } }
+
+ [SerializeField]
+ protected float m_Value;
+ public virtual float value
+ {
+ get
+ {
+ if (wholeNumbers)
+ return Mathf.Round(m_Value);
+ return m_Value;
+ }
+ set
+ {
+ Set(value);
+ }
+ }
+
+ public float normalizedValue
+ {
+ get
+ {
+ if (Mathf.Approximately(minValue, maxValue))
+ return 0;
+ return Mathf.InverseLerp(minValue, maxValue, value);
+ }
+ set
+ {
+ this.value = Mathf.Lerp(minValue, maxValue, value);
+ }
+ }
+
+ [Space]
+
+ // Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
+ [SerializeField]
+ private SliderEvent m_OnValueChanged = new SliderEvent();
+ public SliderEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
+
+ // Private fields
+
+ private Image m_FillImage;
+ private Transform m_FillTransform;
+ private RectTransform m_FillContainerRect;
+ private Transform m_HandleTransform;
+ private RectTransform m_HandleContainerRect;
+
+ // The offset from handle position to mouse down position
+ private Vector2 m_Offset = Vector2.zero;
+
+ private DrivenRectTransformTracker m_Tracker;
+
+ // Size of each step.
+ float stepSize { get { return wholeNumbers ? 1 : (maxValue - minValue) * 0.1f; } } // 1/10十分之一
+
+ protected Slider()
+ {}
+
+#if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+
+ if (wholeNumbers)
+ {
+ m_MinValue = Mathf.Round(m_MinValue);
+ m_MaxValue = Mathf.Round(m_MaxValue);
+ }
+
+ //Onvalidate is called before OnEnabled. We need to make sure not to touch any other objects before OnEnable is run.
+ if (IsActive())
+ {
+ UpdateCachedReferences();
+ Set(m_Value, false);
+ // Update rects since other things might affect them even if value didn't change.
+ UpdateVisuals();
+ }
+
+ var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
+ if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
+ CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
+ }
+
+#endif // if UNITY_EDITOR
+
+ public virtual void Rebuild(CanvasUpdate executing)
+ {
+#if UNITY_EDITOR
+ if (executing == CanvasUpdate.Prelayout)
+ onValueChanged.Invoke(value);
+#endif
+ }
+
+ public virtual void LayoutComplete()
+ {}
+
+ public virtual void GraphicUpdateComplete()
+ {}
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+ UpdateCachedReferences();
+ Set(m_Value, false);
+ // Update rects since they need to be initialized correctly.
+ UpdateVisuals();
+ }
+
+ protected override void OnDisable()
+ {
+ m_Tracker.Clear();
+ base.OnDisable();
+ }
+
+ protected override void OnDidApplyAnimationProperties()
+ {
+ // Has value changed? Various elements of the slider have the old normalisedValue assigned, we can use this to perform a comparison.
+ // We also need to ensure the value stays within min/max.
+ m_Value = ClampValue(m_Value);
+ float oldNormalizedValue = normalizedValue;
+ if (m_FillContainerRect != null)
+ {
+ if (m_FillImage != null && m_FillImage.type == Image.Type.Filled)
+ oldNormalizedValue = m_FillImage.fillAmount;
+ else
+ oldNormalizedValue = (reverseValue ? 1 - m_FillRect.anchorMin[(int)axis] : m_FillRect.anchorMax[(int)axis]);
+ }
+ else if (m_HandleContainerRect != null)
+ oldNormalizedValue = (reverseValue ? 1 - m_HandleRect.anchorMin[(int)axis] : m_HandleRect.anchorMin[(int)axis]);
+
+ UpdateVisuals();
+
+ if (oldNormalizedValue != normalizedValue)
+ {
+ UISystemProfilerApi.AddMarker("Slider.value", this);
+ onValueChanged.Invoke(m_Value);
+ }
+ }
+
+ void UpdateCachedReferences()
+ {
+ if (m_FillRect)
+ {
+ m_FillTransform = m_FillRect.transform;
+ m_FillImage = m_FillRect.GetComponent<Image>();
+ if (m_FillTransform.parent != null)
+ m_FillContainerRect = m_FillTransform.parent.GetComponent<RectTransform>();
+ }
+ else
+ {
+ m_FillContainerRect = null;
+ m_FillImage = null;
+ }
+
+ if (m_HandleRect)
+ {
+ m_HandleTransform = m_HandleRect.transform;
+ if (m_HandleTransform.parent != null)
+ m_HandleContainerRect = m_HandleTransform.parent.GetComponent<RectTransform>();
+ }
+ else
+ {
+ m_HandleContainerRect = null;
+ }
+ }
+
+ float ClampValue(float input)
+ {
+ float newValue = Mathf.Clamp(input, minValue, maxValue);
+ if (wholeNumbers)
+ newValue = Mathf.Round(newValue);
+ return newValue;
+ }
+
+ // Set the valueUpdate the visible Image.
+ void Set(float input)
+ {
+ Set(input, true);
+ }
+
+ protected virtual void Set(float input, bool sendCallback)
+ {
+ // Clamp the input
+ float newValue = ClampValue(input);
+
+ // If the stepped value doesn't match the last one, it's time to update
+ if (m_Value == newValue)
+ return;
+
+ m_Value = newValue;
+ UpdateVisuals();
+ if (sendCallback)
+ {
+ UISystemProfilerApi.AddMarker("Slider.value", this);
+ m_OnValueChanged.Invoke(newValue);
+ }
+ }
+
+ protected override void OnRectTransformDimensionsChange()
+ {
+ base.OnRectTransformDimensionsChange();
+
+ //This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
+ if (!IsActive())
+ return;
+
+ UpdateVisuals();
+ }
+
+ enum Axis
+ {
+ Horizontal = 0,
+ Vertical = 1
+ }
+
+ Axis axis { get { return (m_Direction == Direction.LeftToRight || m_Direction == Direction.RightToLeft) ? Axis.Horizontal : Axis.Vertical; } }
+ bool reverseValue { get { return m_Direction == Direction.RightToLeft || m_Direction == Direction.TopToBottom; } }
+
+ // Force-update the slider. Useful if you've changed the properties and want it to update visually.
+ private void UpdateVisuals()
+ {
+ LogHelper.Log("UpdateVisuals");
+
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ UpdateCachedReferences();
+#endif
+
+ m_Tracker.Clear();
+
+ if (m_FillContainerRect != null)
+ {
+ m_Tracker.Add(this, m_FillRect, DrivenTransformProperties.Anchors);
+ Vector2 anchorMin = Vector2.zero;
+ Vector2 anchorMax = Vector2.one;
+
+ if (m_FillImage != null && m_FillImage.type == Image.Type.Filled)
+ {
+ m_FillImage.fillAmount = normalizedValue;
+ }
+ else
+ {
+ if (reverseValue)
+ anchorMin[(int)axis] = 1 - normalizedValue;
+ else
+ anchorMax[(int)axis] = normalizedValue;
+ }
+
+ m_FillRect.anchorMin = anchorMin;
+ m_FillRect.anchorMax = anchorMax;
+ }
+
+ if (m_HandleContainerRect != null)
+ {
+ m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors);
+ Vector2 anchorMin = Vector2.zero;
+ Vector2 anchorMax = Vector2.one;
+ anchorMin[(int)axis] = anchorMax[(int)axis] = (reverseValue ? (1 - normalizedValue) : normalizedValue);
+ m_HandleRect.anchorMin = anchorMin;
+ m_HandleRect.anchorMax = anchorMax;
+ }
+ }
+
+ // Update the slider's position based on the mouse.
+ void UpdateDrag(PointerEventData eventData, Camera cam)
+ {
+ RectTransform clickRect = m_HandleContainerRect ?? m_FillContainerRect;
+ if (clickRect != null && clickRect.rect.size[(int)axis] > 0)
+ {
+ Vector2 localCursor;
+ if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
+ return;
+ localCursor -= clickRect.rect.position;
+
+ float val = Mathf.Clamp01((localCursor - m_Offset)[(int)axis] / clickRect.rect.size[(int)axis]);
+ normalizedValue = (reverseValue ? 1f - val : val);
+ }
+ }
+
+ private bool MayDrag(PointerEventData eventData)
+ {
+ return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
+ }
+
+ public override void OnPointerDown(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ base.OnPointerDown(eventData);
+
+ m_Offset = Vector2.zero;
+ if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera))
+ {
+ Vector2 localMousePos;
+ if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
+ m_Offset = localMousePos;
+ }
+ else
+ {
+ // Outside the slider handle - jump to this point instead
+ UpdateDrag(eventData, eventData.pressEventCamera);
+ }
+ }
+
+ public virtual void OnDrag(PointerEventData eventData)
+ {
+ LogHelper.Log("OnDrag() " + gameObject.name);
+ if (!MayDrag(eventData))
+ return;
+ UpdateDrag(eventData, eventData.pressEventCamera);
+ }
+
+ public override void OnMove(AxisEventData eventData)
+ {
+ if (!IsActive() || !IsInteractable())
+ {
+ base.OnMove(eventData);
+ return;
+ }
+
+ switch (eventData.moveDir)
+ {
+ case MoveDirection.Left:
+ if (axis == Axis.Horizontal && FindSelectableOnLeft() == null)
+ Set(reverseValue ? value + stepSize : value - stepSize);
+ else
+ base.OnMove(eventData);
+ break;
+ case MoveDirection.Right:
+ if (axis == Axis.Horizontal && FindSelectableOnRight() == null)
+ Set(reverseValue ? value - stepSize : value + stepSize);
+ else
+ base.OnMove(eventData);
+ break;
+ case MoveDirection.Up:
+ if (axis == Axis.Vertical && FindSelectableOnUp() == null)
+ Set(reverseValue ? value - stepSize : value + stepSize);
+ else
+ base.OnMove(eventData);
+ break;
+ case MoveDirection.Down:
+ if (axis == Axis.Vertical && FindSelectableOnDown() == null)
+ Set(reverseValue ? value + stepSize : value - stepSize);
+ else
+ base.OnMove(eventData);
+ break;
+ }
+ }
+
+ public override Selectable FindSelectableOnLeft()
+ {
+ if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
+ return null;
+ return base.FindSelectableOnLeft();
+ }
+
+ public override Selectable FindSelectableOnRight()
+ {
+ if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
+ return null;
+ return base.FindSelectableOnRight();
+ }
+
+ public override Selectable FindSelectableOnUp()
+ {
+ if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
+ return null;
+ return base.FindSelectableOnUp();
+ }
+
+ public override Selectable FindSelectableOnDown()
+ {
+ if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
+ return null;
+ return base.FindSelectableOnDown();
+ }
+
+ //
+ public virtual void OnInitializePotentialDrag(PointerEventData eventData)
+ {
+ eventData.useDragThreshold = false;
+ }
+
+ public void SetDirection(Direction direction, bool includeRectLayouts)
+ {
+ Axis oldAxis = axis;
+ bool oldReverse = reverseValue;
+ this.direction = direction;
+
+ if (!includeRectLayouts)
+ return;
+
+ if (axis != oldAxis)
+ RectTransformUtility.FlipLayoutAxes(transform as RectTransform, true, true);
+
+ if (reverseValue != oldReverse)
+ RectTransformUtility.FlipLayoutOnAxis(transform as RectTransform, (int)axis, true, true);
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Slider.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Slider.cs.meta
new file mode 100644
index 0000000..dce4469
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Slider.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4735d34897dc608419d8f2b983b58420
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Toggle.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Toggle.cs
new file mode 100644
index 0000000..f2dc8b9
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Toggle.cs
@@ -0,0 +1,246 @@
+using System;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using UnityEngine.Serialization;
+
+namespace UnityEngine.UI
+{
+ /// <summary>
+ /// Simple toggle -- something that has an 'on' and 'off' states: checkbox, toggle button, radio button, etc.
+ /// </summary>
+ [AddComponentMenu("UI/Toggle", 31)]
+ [RequireComponent(typeof(RectTransform))]
+ public class Toggle
+ : Selectable
+ , IPointerClickHandler
+ , ISubmitHandler
+ , ICanvasElement // 缂栬緫鍣ㄤ笅鐢ㄥ埌
+ {
+ public enum ToggleTransition
+ {
+ None,
+ Fade
+ }
+
+ [Serializable]
+ public class ToggleEvent : UnityEvent<bool>
+ {}
+
+ /// <summary>
+ /// Transition type.
+ /// </summary>
+ public ToggleTransition toggleTransition = ToggleTransition.Fade;
+
+ /// <summary>
+ /// Graphic the toggle should be working with.
+ /// </summary>
+ public Graphic graphic;
+
+ // group that this toggle can belong to
+ [SerializeField]
+ private ToggleGroup m_Group;
+
+ public ToggleGroup group
+ {
+ get { return m_Group; }
+ set
+ {
+ m_Group = value;
+#if UNITY_EDITOR
+ if (Application.isPlaying)
+#endif
+ {
+ SetToggleGroup(m_Group, true);
+ PlayEffect(true);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
+ /// </summary>
+ public ToggleEvent onValueChanged = new ToggleEvent();
+
+ // Whether the toggle is on
+ [FormerlySerializedAs("m_IsActive")]
+ [Tooltip("Is the toggle currently on or off?")]
+ [SerializeField]
+ private bool m_IsOn;
+
+ protected Toggle()
+ {}
+
+#if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+
+ var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
+ if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
+ CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
+ }
+
+#endif // if UNITY_EDITOR
+
+ public virtual void Rebuild(CanvasUpdate executing)
+ {
+#if UNITY_EDITOR
+ if (executing == CanvasUpdate.Prelayout)
+ onValueChanged.Invoke(m_IsOn);
+#endif
+ }
+
+ public virtual void LayoutComplete()
+ {}
+
+ public virtual void GraphicUpdateComplete()
+ {}
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+ SetToggleGroup(m_Group, false);
+ PlayEffect(true);
+ }
+
+ protected override void OnDisable()
+ {
+ SetToggleGroup(null, false);
+ base.OnDisable();
+ }
+
+ protected override void OnDidApplyAnimationProperties()
+ {
+ // Check if isOn has been changed by the animation.
+ // Unfortunately there is no way to check if we don锟絫 have a graphic.
+ if (graphic != null)
+ {
+ bool oldValue = !Mathf.Approximately(graphic.canvasRenderer.GetColor().a, 0);
+ if (m_IsOn != oldValue)
+ {
+ m_IsOn = oldValue;
+ Set(!oldValue);
+ }
+ }
+
+ base.OnDidApplyAnimationProperties();
+ }
+
+ private void SetToggleGroup(ToggleGroup newGroup, bool setMemberValue)
+ {
+ ToggleGroup oldGroup = m_Group;
+
+ // Sometimes IsActive returns false in OnDisable so don't check for it.
+ // Rather remove the toggle too often than too little.
+ if (m_Group != null)
+ m_Group.UnregisterToggle(this);
+
+ // At runtime the group variable should be set but not when calling this method from OnEnable or OnDisable.
+ // That's why we use the setMemberValue parameter.
+ if (setMemberValue)
+ m_Group = newGroup;
+
+ // Only register to the new group if this Toggle is active.
+ if (newGroup != null && IsActive())
+ newGroup.RegisterToggle(this);
+
+ // If we are in a new group, and this toggle is on, notify group.
+ // Note: Don't refer to m_Group here as it's not guaranteed to have been set.
+ if (newGroup != null && newGroup != oldGroup && isOn && IsActive())
+ newGroup.NotifyToggleOn(this);
+ }
+
+ /// <summary>
+ /// Whether the toggle is currently active.
+ /// </summary>
+ public bool isOn
+ {
+ get { return m_IsOn; }
+ set
+ {
+ Set(value);
+ }
+ }
+
+ void Set(bool value)
+ {
+ Set(value, true);
+ }
+
+ void Set(bool value, bool sendCallback)
+ {
+ if (m_IsOn == value)
+ return;
+
+ // if we are in a group and set to true, do group logic
+ m_IsOn = value;
+ if (m_Group != null && IsActive())
+ {
+ if (m_IsOn || (!m_Group.AnyTogglesOn() && !m_Group.allowSwitchOff))
+ {
+ m_IsOn = true;
+ m_Group.NotifyToggleOn(this);
+ }
+ }
+
+ // Always send event when toggle is clicked, even if value didn't change
+ // due to already active toggle in a toggle group being clicked.
+ // Controls like Dropdown rely on this.
+ // It's up to the user to ignore a selection being set to the same value it already was, if desired.
+ PlayEffect(toggleTransition == ToggleTransition.None);
+ if (sendCallback)
+ {
+ UISystemProfilerApi.AddMarker("Toggle.value", this);
+ onValueChanged.Invoke(m_IsOn);
+ }
+ }
+
+ /// <summary>
+ /// Play the appropriate effect.
+ /// </summary>
+ private void PlayEffect(bool instant)
+ {
+ if (graphic == null)
+ return;
+
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ graphic.canvasRenderer.SetAlpha(m_IsOn ? 1f : 0f);
+ else
+#endif
+ graphic.CrossFadeAlpha(m_IsOn ? 1f : 0f, instant ? 0f : 0.1f, true);
+ }
+
+ /// <summary>
+ /// Assume the correct visual state.
+ /// </summary>
+ protected override void Start()
+ {
+ PlayEffect(true);
+ }
+
+ private void InternalToggle()
+ {
+ if (!IsActive() || !IsInteractable())
+ return;
+
+ isOn = !isOn;
+ }
+
+ /// <summary>
+ /// React to clicks.
+ /// </summary>
+ public virtual void OnPointerClick(PointerEventData eventData)
+ {
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ InternalToggle();
+ }
+
+ public virtual void OnSubmit(BaseEventData eventData)
+ {
+ InternalToggle();
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Toggle.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Toggle.cs.meta
new file mode 100644
index 0000000..b4a15b7
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/Toggle.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 34e74f28b4633794eb61b9c3f6d97b37
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ToggleGroup.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ToggleGroup.cs
new file mode 100644
index 0000000..da5e021
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ToggleGroup.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.UI
+{
+ [AddComponentMenu("UI/Toggle Group", 32)]
+ [DisallowMultipleComponent]
+ public class ToggleGroup : UIBehaviour
+ {
+ [SerializeField] private bool m_AllowSwitchOff = false;
+ public bool allowSwitchOff { get { return m_AllowSwitchOff; } set { m_AllowSwitchOff = value; } }
+
+ private List<Toggle> m_Toggles = new List<Toggle>();
+
+ protected ToggleGroup()
+ {}
+
+ private void ValidateToggleIsInGroup(Toggle toggle)
+ {
+ if (toggle == null || !m_Toggles.Contains(toggle))
+ throw new ArgumentException(string.Format("Toggle {0} is not part of ToggleGroup {1}", new object[] {toggle, this}));
+ }
+
+ public void NotifyToggleOn(Toggle toggle)
+ {
+ ValidateToggleIsInGroup(toggle);
+
+ // disable all toggles in the group
+ for (var i = 0; i < m_Toggles.Count; i++)
+ {
+ if (m_Toggles[i] == toggle)
+ continue;
+
+ m_Toggles[i].isOn = false;
+ }
+ }
+
+ public void UnregisterToggle(Toggle toggle)
+ {
+ if (m_Toggles.Contains(toggle))
+ m_Toggles.Remove(toggle);
+ }
+
+ public void RegisterToggle(Toggle toggle)
+ {
+ if (!m_Toggles.Contains(toggle))
+ m_Toggles.Add(toggle);
+ }
+
+ public bool AnyTogglesOn()
+ {
+ return m_Toggles.Find(x => x.isOn) != null;
+ }
+
+ public IEnumerable<Toggle> ActiveToggles()
+ {
+ return m_Toggles.Where(x => x.isOn);
+ }
+
+ public void SetAllTogglesOff()
+ {
+ bool oldAllowSwitchOff = m_AllowSwitchOff;
+ m_AllowSwitchOff = true;
+
+ for (var i = 0; i < m_Toggles.Count; i++)
+ m_Toggles[i].isOn = false;
+
+ m_AllowSwitchOff = oldAllowSwitchOff;
+ }
+ }
+}
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ToggleGroup.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ToggleGroup.cs.meta
new file mode 100644
index 0000000..0649cf7
--- /dev/null
+++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls/ToggleGroup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 10b2fd7b649a75145b1e4a36a3c91dfd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: