diff options
Diffstat (limited to 'Assets/uGUI-2017.1/UnityEngine.UI/UI/Core')
132 files changed, 14710 insertions, 0 deletions
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/CanvasUpdateRegistry.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/CanvasUpdateRegistry.cs new file mode 100644 index 0000000..200b4e5 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/CanvasUpdateRegistry.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using UnityEngine.UI.Collections; + +namespace UnityEngine.UI +{ + // canvas的不同阶段,用来设置layout、graphic做不同的事情,具体看CanvasUpdateRegistry + // prelayout -> layout -> postlayout -> prerender -> laterender =>=>=> render + public enum CanvasUpdate + { + Prelayout = 0, + Layout = 1, + PostLayout = 2, + PreRender = 3, + LatePreRender = 4, + + MaxUpdateValue = 5 + } + + public interface ICanvasElement + { + Transform transform { get; } + void Rebuild(CanvasUpdate executing); + void LayoutComplete(); + void GraphicUpdateComplete(); + // due to unity overriding null check + // we need this as something may not be null + // but may be destroyed + bool IsDestroyed(); + } + + // 需要注意整个游戏只有一个单例 + public class CanvasUpdateRegistry + { + private static CanvasUpdateRegistry s_Instance; + + private bool m_PerformingLayoutUpdate; + private bool m_PerformingGraphicUpdate; + + private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>(); + private readonly IndexedSet<ICanvasElement> m_GraphicRebuildQueue = new IndexedSet<ICanvasElement>(); + + protected CanvasUpdateRegistry() + { + // willRenderCanvases在渲染canvas之前调用,执行PerformUpdate来Rebuild处理m_LayoutRebuildQueue和m_GraphicRebuildQueue + Canvas.willRenderCanvases += PerformUpdate; + } + + // 简单单例 + public static CanvasUpdateRegistry instance + { + get + { + if (s_Instance == null) + s_Instance = new CanvasUpdateRegistry(); + return s_Instance; + } + } + + // 检查一下实现了ICanvasElement的对象合法性,实现了ICanvasElement接口的对象必须是Unity Object, + private bool ObjectValidForUpdate(ICanvasElement element) + { + var valid = element != null; + + var isUnityObject = element is Object; + if (isUnityObject) + valid = (element as Object) != null; //Here we make use of the overloaded UnityEngine.Object == null, that checks if the native object is alive. + + return valid; + } + + private void CleanInvalidItems() + { + // So MB's override the == operator for null equality, which checks + // if they are destroyed. This is fine if you are looking at a concrete + // mb, but in this case we are looking at a list of ICanvasElement + // this won't forward the == operator to the MB, but just check if the + // interface is null. IsDestroyed will return if the backend is destroyed. + + for (int i = m_LayoutRebuildQueue.Count - 1; i >= 0; --i) + { + var item = m_LayoutRebuildQueue[i]; + if (item == null) + { + m_LayoutRebuildQueue.RemoveAt(i); + continue; + } + + if (item.IsDestroyed()) + { + m_LayoutRebuildQueue.RemoveAt(i); + item.LayoutComplete(); + } + } + + for (int i = m_GraphicRebuildQueue.Count - 1; i >= 0; --i) + { + var item = m_GraphicRebuildQueue[i]; + if (item == null) + { + m_GraphicRebuildQueue.RemoveAt(i); + continue; + } + + if (item.IsDestroyed()) + { + m_GraphicRebuildQueue.RemoveAt(i); + item.GraphicUpdateComplete(); + } + } + } + + private static readonly Comparison<ICanvasElement> s_SortLayoutFunction = SortLayoutList; + // 在渲染canvas之前对canvas下的元素进行rebuild + // 进行 prelayout -> layout -> postlayout -> prerender -> LatePreRender 流程 + private void PerformUpdate() + { + UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Layout); + CleanInvalidItems(); + + m_PerformingLayoutUpdate = true; + + // 重建layout + + // 根据父节点从少到多排序,先布局父节点少的,由少到多,由内到外 + m_LayoutRebuildQueue.Sort(s_SortLayoutFunction); + // prelayout -> layout -> postlayout + for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++) // 不同的阶段 + { + for (int j = 0; j < m_LayoutRebuildQueue.Count; j++) + { + var rebuild = instance.m_LayoutRebuildQueue[j]; + try + { + if (ObjectValidForUpdate(rebuild)) + rebuild.Rebuild((CanvasUpdate)i); + } + catch (Exception e) + { + Debug.LogException(e, rebuild.transform); + } + } + } + + // 布局完成 + for (int i = 0; i < m_LayoutRebuildQueue.Count; ++i) + m_LayoutRebuildQueue[i].LayoutComplete(); + + // 清空队列,重置状态 + instance.m_LayoutRebuildQueue.Clear(); + m_PerformingLayoutUpdate = false; + + // 做剔除+裁剪 + // now layout is complete do culling... + ClipperRegistry.instance.Cull(); + + // 重建graphic + + m_PerformingGraphicUpdate = true; + // prerender -> lateprerender + for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++) + { + for (var k = 0; k < instance.m_GraphicRebuildQueue.Count; k++) + { + try + { + var element = instance.m_GraphicRebuildQueue[k]; + if (ObjectValidForUpdate(element)) + element.Rebuild((CanvasUpdate)i); + } + catch (Exception e) + { + Debug.LogException(e, instance.m_GraphicRebuildQueue[k].transform); + } + } + } + + // graphic设置mesh和材质完成 + for (int i = 0; i < m_GraphicRebuildQueue.Count; ++i) + m_GraphicRebuildQueue[i].GraphicUpdateComplete(); + + // 清空队列,重置状态 + instance.m_GraphicRebuildQueue.Clear(); + m_PerformingGraphicUpdate = false; + + // 到了这里布局和网格、材质都设置完毕,后面canvas会渲染 + + // profiler不用管 + UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Layout); + } + + // 统计父节点个数 + private static int ParentCount(Transform child) + { + if (child == null) + return 0; + + var parent = child.parent; + int count = 0; + while (parent != null) + { + count++; + parent = parent.parent; + } + return count; + } + + // 根据父节点由少到多排序 + private static int SortLayoutList(ICanvasElement x, ICanvasElement y) + { + Transform t1 = x.transform; + Transform t2 = y.transform; + + return ParentCount(t1) - ParentCount(t2); + }
+
+ public static bool IsRebuildingLayout() + { + return instance.m_PerformingLayoutUpdate; + } + + public static bool IsRebuildingGraphics() + { + return instance.m_PerformingGraphicUpdate; + }
+
+#region 将canvas elements注册到队列里的方法 +
+ public static void RegisterCanvasElementForLayoutRebuild(ICanvasElement element) + { + instance.InternalRegisterCanvasElementForLayoutRebuild(element); + } + + public static bool TryRegisterCanvasElementForLayoutRebuild(ICanvasElement element) + { + return instance.InternalRegisterCanvasElementForLayoutRebuild(element); + } + + private bool InternalRegisterCanvasElementForLayoutRebuild(ICanvasElement element) + { + if (m_LayoutRebuildQueue.Contains(element)) + return false; + + /* TODO: this likely should be here but causes the error to show just resizing the game view (case 739376) + if (m_PerformingLayoutUpdate) + { + Debug.LogError(string.Format("Trying to add {0} for layout rebuild while we are already inside a layout rebuild loop. This is not supported.", element)); + return false; + }*/ + + return m_LayoutRebuildQueue.AddUnique(element); + } + + public static void RegisterCanvasElementForGraphicRebuild(ICanvasElement element) + { + instance.InternalRegisterCanvasElementForGraphicRebuild(element); + } + + public static bool TryRegisterCanvasElementForGraphicRebuild(ICanvasElement element) + { + return instance.InternalRegisterCanvasElementForGraphicRebuild(element); + } + + private bool InternalRegisterCanvasElementForGraphicRebuild(ICanvasElement element) + { + if (m_PerformingGraphicUpdate) + { + Debug.LogError(string.Format("Trying to add {0} for graphic rebuild while we are already inside a graphic rebuild loop. This is not supported.", element)); + return false; + } + + return m_GraphicRebuildQueue.AddUnique(element); + } + + public static void UnRegisterCanvasElementForRebuild(ICanvasElement element) + { + instance.InternalUnRegisterCanvasElementForLayoutRebuild(element); + instance.InternalUnRegisterCanvasElementForGraphicRebuild(element); + } + + private void InternalUnRegisterCanvasElementForLayoutRebuild(ICanvasElement element) + { + if (m_PerformingLayoutUpdate) + { + Debug.LogError(string.Format("Trying to remove {0} from rebuild list while we are already inside a rebuild loop. This is not supported.", element)); + return; + } + + element.LayoutComplete(); + instance.m_LayoutRebuildQueue.Remove(element); + } + + private void InternalUnRegisterCanvasElementForGraphicRebuild(ICanvasElement element) + { + if (m_PerformingGraphicUpdate) + { + Debug.LogError(string.Format("Trying to remove {0} from rebuild list while we are already inside a rebuild loop. This is not supported.", element)); + return; + } + element.GraphicUpdateComplete(); + instance.m_GraphicRebuildQueue.Remove(element); + }
+
+#endregion + + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/CanvasUpdateRegistry.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/CanvasUpdateRegistry.cs.meta new file mode 100644 index 0000000..be77e1d --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/CanvasUpdateRegistry.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: b607eb8b4c72f944c9fda5d106491067 +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling.meta new file mode 100644 index 0000000..2b86263 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 2f77b3d02da9eb145be1dfc4c8684a80 +folderAsset: yes +timeCreated: 1602119280 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/ClipperRegistry.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/ClipperRegistry.cs new file mode 100644 index 0000000..ea7f527 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/ClipperRegistry.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using UnityEngine.UI.Collections; + +namespace UnityEngine.UI +{ + public class ClipperRegistry + { + static ClipperRegistry s_Instance; + + readonly IndexedSet<IClipper> m_Clippers = new IndexedSet<IClipper>(); + + protected ClipperRegistry() + { + // This is needed for AOT platforms. Without it the compile doesn't get the definition of the Dictionarys +#pragma warning disable 168 + Dictionary<IClipper, int> emptyIClipperDic; +#pragma warning restore 168 + } + + public static ClipperRegistry instance + { + get + { + if (s_Instance == null) + s_Instance = new ClipperRegistry(); + return s_Instance; + } + } + + public void Cull() + { + for (var i = 0; i < m_Clippers.Count; ++i) + { + m_Clippers[i].PerformClipping(); + } + } + + public static void Register(IClipper c) + { + if (c == null) + return; + instance.m_Clippers.AddUnique(c); + } + + public static void Unregister(IClipper c) + { + instance.m_Clippers.Remove(c); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/ClipperRegistry.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/ClipperRegistry.cs.meta new file mode 100644 index 0000000..32995ae --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/ClipperRegistry.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: b494fa40405522d49af6bca6e68e4ba6 +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/Clipping.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/Clipping.cs new file mode 100644 index 0000000..7dd39bf --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/Clipping.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; + +namespace UnityEngine.UI +{ + public static class Clipping + { + public static Rect FindCullAndClipWorldRect(List<RectMask2D> rectMaskParents, out bool validRect) + { + if (rectMaskParents.Count == 0) + { + validRect = false; + return new Rect(); + } + + var compoundRect = rectMaskParents[0].canvasRect; + for (var i = 0; i < rectMaskParents.Count; ++i) + compoundRect = RectIntersect(compoundRect, rectMaskParents[i].canvasRect); + + var cull = compoundRect.width <= 0 || compoundRect.height <= 0; + if (cull) + { + validRect = false; + return new Rect(); + } + + Vector3 point1 = new Vector3(compoundRect.x, compoundRect.y, 0.0f); + Vector3 point2 = new Vector3(compoundRect.x + compoundRect.width, compoundRect.y + compoundRect.height, 0.0f); + validRect = true; + return new Rect(point1.x, point1.y, point2.x - point1.x, point2.y - point1.y); + } + + // 计算矩形交集 + private static Rect RectIntersect(Rect a, Rect b) + { + float xMin = Mathf.Max(a.x, b.x); + float xMax = Mathf.Min(a.x + a.width, b.x + b.width); + float yMin = Mathf.Max(a.y, b.y); + float yMax = Mathf.Min(a.y + a.height, b.y + b.height); + if (xMax >= xMin && yMax >= yMin) + return new Rect(xMin, yMin, xMax - xMin, yMax - yMin); + return new Rect(0f, 0f, 0f, 0f); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/Clipping.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/Clipping.cs.meta new file mode 100644 index 0000000..698e06f --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/Clipping.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 88fc11e9a11bf1941abc08b9ed323b6f +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/IClipRegion.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/IClipRegion.cs new file mode 100644 index 0000000..4593f17 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/IClipRegion.cs @@ -0,0 +1,16 @@ +namespace UnityEngine.UI +{ + public interface IClipper + { + void PerformClipping(); + } + + public interface IClippable + { + GameObject gameObject { get; } + void RecalculateClipping(); + RectTransform rectTransform { get; } + void Cull(Rect clipRect, bool validRect); + void SetClipRect(Rect value, bool validRect); + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/IClipRegion.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/IClipRegion.cs.meta new file mode 100644 index 0000000..1852869 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/IClipRegion.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: d395da6bc4c1e6f4b9521bb99b22f3b4 +timeCreated: 1602119380 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/RectangularVertexClipper.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/RectangularVertexClipper.cs new file mode 100644 index 0000000..6d12322 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/RectangularVertexClipper.cs @@ -0,0 +1,21 @@ +namespace UnityEngine.UI +{ + internal class RectangularVertexClipper + { + readonly Vector3[] m_WorldCorners = new Vector3[4]; + readonly Vector3[] m_CanvasCorners = new Vector3[4]; + + public Rect GetCanvasRect(RectTransform t, Canvas c) + { + if (c == null) + return new Rect(); + + t.GetWorldCorners(m_WorldCorners); + var canvasTransform = c.GetComponent<Transform>(); + for (int i = 0; i < 4; ++i) + m_CanvasCorners[i] = canvasTransform.InverseTransformPoint(m_WorldCorners[i]); + + return new Rect(m_CanvasCorners[0].x, m_CanvasCorners[0].y, m_CanvasCorners[2].x - m_CanvasCorners[0].x, m_CanvasCorners[2].y - m_CanvasCorners[0].y); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/RectangularVertexClipper.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/RectangularVertexClipper.cs.meta new file mode 100644 index 0000000..f33cd90 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Culling/RectangularVertexClipper.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 545014a8997364a47b344095371a7332 +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font.meta new file mode 100644 index 0000000..28b5ef6 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 680fe3dddba51f54ca833b46dc6b5a82 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontData.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontData.cs new file mode 100644 index 0000000..3a85af1 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontData.cs @@ -0,0 +1,158 @@ +using System; +using UnityEngine.Serialization; + +namespace UnityEngine.UI +{ + /// <summary> + /// Labels reference specific font data used to draw them. This class contains that data. + /// </summary> + + [Serializable] + public class FontData : ISerializationCallbackReceiver + { + [SerializeField] + [FormerlySerializedAs("font")] + private Font m_Font; + + [SerializeField] + [FormerlySerializedAs("fontSize")] + private int m_FontSize; + + [SerializeField] + [FormerlySerializedAs("fontStyle")] + private FontStyle m_FontStyle; + + [SerializeField] + private bool m_BestFit; + + [SerializeField] + private int m_MinSize; + + [SerializeField] + private int m_MaxSize; + + [SerializeField] + [FormerlySerializedAs("alignment")] + private TextAnchor m_Alignment; + + [SerializeField] + private bool m_AlignByGeometry; + + [SerializeField] + [FormerlySerializedAs("richText")] + private bool m_RichText; + + [SerializeField] + private HorizontalWrapMode m_HorizontalOverflow; + + [SerializeField] + private VerticalWrapMode m_VerticalOverflow; + + [SerializeField] + private float m_LineSpacing; + + public static FontData defaultFontData + { + get + { + var fontData = new FontData + { + m_FontSize = 14, + m_LineSpacing = 1f, + m_FontStyle = FontStyle.Normal, + m_BestFit = false, + m_MinSize = 10, + m_MaxSize = 40, + m_Alignment = TextAnchor.UpperLeft, + m_HorizontalOverflow = HorizontalWrapMode.Wrap, + m_VerticalOverflow = VerticalWrapMode.Truncate, + m_RichText = true, + m_AlignByGeometry = false + }; + return fontData; + } + } + + public Font font + { + get { return m_Font; } + set { m_Font = value; } + } + + public int fontSize + { + get { return m_FontSize; } + set { Debug.Log("FontData.fontSize"); m_FontSize = value; } + } + + public FontStyle fontStyle + { + get { return m_FontStyle; } + set { m_FontStyle = value; } + } + + public bool bestFit + { + get { return m_BestFit; } + set { m_BestFit = value; } + } + + public int minSize + { + get { return m_MinSize; } + set { m_MinSize = value; } + } + + public int maxSize + { + get { return m_MaxSize; } + set { m_MaxSize = value; } + } + + public TextAnchor alignment + { + get { return m_Alignment; } + set { m_Alignment = value; } + } + + public bool alignByGeometry + { + get { return m_AlignByGeometry; } + set { m_AlignByGeometry = value; } + } + + public bool richText + { + get { return m_RichText; } + set { m_RichText = value; } + } + + public HorizontalWrapMode horizontalOverflow + { + get { return m_HorizontalOverflow; } + set { m_HorizontalOverflow = value; } + } + + public VerticalWrapMode verticalOverflow + { + get { return m_VerticalOverflow; } + set { m_VerticalOverflow = value; } + } + + public float lineSpacing + { + get { return m_LineSpacing; } + set { m_LineSpacing = value; } + } + + void ISerializationCallbackReceiver.OnBeforeSerialize() + {} + + void ISerializationCallbackReceiver.OnAfterDeserialize() + { + m_FontSize = Mathf.Clamp(m_FontSize, 0, 300); + m_MinSize = Mathf.Clamp(m_MinSize, 0, m_FontSize); + m_MaxSize = Mathf.Clamp(m_MaxSize, m_FontSize, 300); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontData.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontData.cs.meta new file mode 100644 index 0000000..f4bb2a1 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontData.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 7c47b221885e53f479140fc48712f249 +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontUpdateTracker.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontUpdateTracker.cs new file mode 100644 index 0000000..a63d62b --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontUpdateTracker.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UnityEngine.UI +{ + // 全局记录使用了某个Font字体的所有Text,当这个Font的GlyphAtlas大小改变的时候通知这些Text重建mesh(更新UV) + // 因为GlyphAtlas大小改变了,原先的UV已经不对了 + public static class FontUpdateTracker + { + static Dictionary<Font, HashSet<Text>> m_Tracked = new Dictionary<Font, HashSet<Text>>(); + + public static void TrackText(Text t) + { + if (t.font == null) + return; + + HashSet<Text> exists; + m_Tracked.TryGetValue(t.font, out exists); + if (exists == null) + {
+ // font texture重新生成的时候会调这个回调,然后需要Text组件重建mesh的UV数据,因为原先的fontGlyph里的UV已经不准了
+ // https://docs.unity3d.com/ScriptReference/Font-textureRebuilt.html
+ // The textureRebuilt event is global for all fonts, so we add our delegate the first time we register *any* Text
+ if (m_Tracked.Count == 0) + Font.textureRebuilt += RebuildForFont; + + exists = new HashSet<Text>(); + m_Tracked.Add(t.font, exists); + } + + if (!exists.Contains(t)) + exists.Add(t); + } + + private static void RebuildForFont(Font f) + { + HashSet<Text> texts; + m_Tracked.TryGetValue(f, out texts); + + if (texts == null) + return; + + foreach (var text in texts) + text.FontTextureChanged(); + } + + public static void UntrackText(Text t) + { + if (t.font == null) + return; + + HashSet<Text> texts; + m_Tracked.TryGetValue(t.font, out texts); + + if (texts == null) + return; + + texts.Remove(t); + + if (texts.Count == 0) + { + m_Tracked.Remove(t.font); + + // There is a global textureRebuilt event for all fonts, so once the last Text reference goes away, remove our delegate + if (m_Tracked.Count == 0) + Font.textureRebuilt -= RebuildForFont; + } + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontUpdateTracker.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontUpdateTracker.cs.meta new file mode 100644 index 0000000..8e6ddeb --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/FontUpdateTracker.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: fba9c6c6325db2d49bdf2b608066f0e0 +timeCreated: 1602119380 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/Text.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/Text.cs new file mode 100644 index 0000000..3d3d17b --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/Text.cs @@ -0,0 +1,576 @@ +using System; +using System.Collections.Generic; + +namespace UnityEngine.UI +{ + /// <summary> + /// Labels are graphics that display text. + /// </summary> + + [AddComponentMenu("UI/Text", 10)] + public class Text : MaskableGraphic, ILayoutElement + { + [SerializeField] private FontData m_FontData = FontData.defaultFontData; + +#if UNITY_EDITOR + // needed to track font changes from the inspector + private Font m_LastTrackedFont; +#endif + + [TextArea(3, 10)][SerializeField] protected string m_Text = String.Empty; + + private TextGenerator m_TextCache; + private TextGenerator m_TextCacheForLayout; + + static protected Material s_DefaultText = null; + + // We use this flag instead of Unregistering/Registering the callback to avoid allocation. + [NonSerialized] protected bool m_DisableFontTextureRebuiltCallback = false; + + protected Text() + { + useLegacyMeshGeneration = false; + } + + // text generator,得到字符串在glyph atlas中的UV,构建mesh + /// <summary> + /// Get or set the material used by this Text. + /// </summary> + + public TextGenerator cachedTextGenerator + { + get { return m_TextCache ?? (m_TextCache = (m_Text.Length != 0 ? new TextGenerator(m_Text.Length) : new TextGenerator())); } + } + + public TextGenerator cachedTextGeneratorForLayout + { + get { return m_TextCacheForLayout ?? (m_TextCacheForLayout = new TextGenerator()); } + } + + /// <summary> + /// Text's texture comes from the font. + /// </summary> + public override Texture mainTexture + { + get + { + if (font != null && font.material != null && font.material.mainTexture != null) + return font.material.mainTexture; + + if (m_Material != null) + return m_Material.mainTexture; + + return base.mainTexture; + } + } + + public void FontTextureChanged() + { + // Only invoke if we are not destroyed. + if (!this) + return; + + if (m_DisableFontTextureRebuiltCallback) + return; + + cachedTextGenerator.Invalidate(); + + if (!IsActive()) + return; + + // this is a bit hacky, but it is currently the + // cleanest solution.... + // if we detect the font texture has changed and are in a rebuild loop + // we just regenerate the verts for the new UV's + if (CanvasUpdateRegistry.IsRebuildingGraphics() || CanvasUpdateRegistry.IsRebuildingLayout()) + UpdateGeometry(); + else + SetAllDirty(); + } + + public Font font + { + get + { + return m_FontData.font; + } + set + { + if (m_FontData.font == value) + return; + + FontUpdateTracker.UntrackText(this); + + m_FontData.font = value; + + FontUpdateTracker.TrackText(this); + +#if UNITY_EDITOR + // needed to track font changes from the inspector + m_LastTrackedFont = value; +#endif + + SetAllDirty(); + } + } + + /// <summary> + /// Text that's being displayed by the Text. + /// </summary> + + public virtual string text + { + get + { + return m_Text; + } + set + { + if (String.IsNullOrEmpty(value)) + { + if (String.IsNullOrEmpty(m_Text)) + return; + m_Text = ""; + SetVerticesDirty(); + } + else if (m_Text != value) + { + m_Text = value; + SetVerticesDirty(); + SetLayoutDirty(); + } + } + } + + /// <summary> + /// Whether this Text will support rich text. + /// </summary> + + public bool supportRichText + { + get + { + return m_FontData.richText; + } + set + { + if (m_FontData.richText == value) + return; + m_FontData.richText = value; + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + /// <summary> + /// Wrap mode used by the text. + /// </summary> + + public bool resizeTextForBestFit + { + get + { + return m_FontData.bestFit; + } + set + { + if (m_FontData.bestFit == value) + return; + m_FontData.bestFit = value; + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + public int resizeTextMinSize + { + get + { + return m_FontData.minSize; + } + set + { + if (m_FontData.minSize == value) + return; + m_FontData.minSize = value; + + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + public int resizeTextMaxSize + { + get + { + return m_FontData.maxSize; + } + set + { + if (m_FontData.maxSize == value) + return; + m_FontData.maxSize = value; + + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + /// <summary> + /// Alignment anchor used by the text. + /// </summary> + + public TextAnchor alignment + { + get + { + return m_FontData.alignment; + } + set + { + if (m_FontData.alignment == value) + return; + m_FontData.alignment = value; + + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + public bool alignByGeometry + { + get + { + return m_FontData.alignByGeometry; + } + set + { + if (m_FontData.alignByGeometry == value) + return; + m_FontData.alignByGeometry = value; + + SetVerticesDirty(); + } + } + + public int fontSize + { + get + { + return m_FontData.fontSize; + } + set + { + // font size改变的时候重建mesh + if (m_FontData.fontSize == value) + return; + m_FontData.fontSize = value; + + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + public HorizontalWrapMode horizontalOverflow + { + get + { + return m_FontData.horizontalOverflow; + } + set + { + if (m_FontData.horizontalOverflow == value) + return; + m_FontData.horizontalOverflow = value; + + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + public VerticalWrapMode verticalOverflow + { + get + { + return m_FontData.verticalOverflow; + } + set + { + if (m_FontData.verticalOverflow == value) + return; + m_FontData.verticalOverflow = value; + + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + public float lineSpacing + { + get + { + return m_FontData.lineSpacing; + } + set + { + if (m_FontData.lineSpacing == value) + return; + m_FontData.lineSpacing = value; + + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + /// <summary> + /// Font style used by the Text's text. + /// </summary> + + public FontStyle fontStyle + { + get + { + return m_FontData.fontStyle; + } + set + { + if (m_FontData.fontStyle == value) + return; + m_FontData.fontStyle = value; + + SetVerticesDirty(); + SetLayoutDirty(); + } + } + + public float pixelsPerUnit + { + get + { + var localCanvas = canvas; + if (!localCanvas) + return 1; + // For dynamic fonts, ensure we use one pixel per pixel on the screen. + if (!font || font.dynamic) + return localCanvas.scaleFactor; + // For non-dynamic fonts, calculate pixels per unit based on specified font size relative to font object's own font size. + if (m_FontData.fontSize <= 0 || font.fontSize <= 0) + return 1; + return font.fontSize / (float)m_FontData.fontSize; + } + } + + protected override void OnEnable() + { + base.OnEnable(); + cachedTextGenerator.Invalidate(); + FontUpdateTracker.TrackText(this); + } + + protected override void OnDisable() + { + FontUpdateTracker.UntrackText(this); + base.OnDisable(); + } + + protected override void UpdateGeometry() + { + if (font != null) + { + base.UpdateGeometry(); + } + } + +#if UNITY_EDITOR + protected override void Reset() + { + AssignDefaultFont(); + } + +#endif + internal void AssignDefaultFont() + { + font = Resources.GetBuiltinResource<Font>("Arial.ttf"); + } + + // 根据FontData设置TextGenerationSettings + public TextGenerationSettings GetGenerationSettings(Vector2 extents) + { + var settings = new TextGenerationSettings(); + + settings.generationExtents = extents; + if (font != null && font.dynamic) + { + settings.fontSize = m_FontData.fontSize; + settings.resizeTextMinSize = m_FontData.minSize; + settings.resizeTextMaxSize = m_FontData.maxSize; + } + + // Other settings + settings.textAnchor = m_FontData.alignment; + settings.alignByGeometry = m_FontData.alignByGeometry; + settings.scaleFactor = pixelsPerUnit; + settings.color = color; // 这里是顶点颜色 + settings.font = font; + settings.pivot = rectTransform.pivot; + settings.richText = m_FontData.richText; + settings.lineSpacing = m_FontData.lineSpacing; + settings.fontStyle = m_FontData.fontStyle; + settings.resizeTextForBestFit = m_FontData.bestFit; + settings.updateBounds = false; + settings.horizontalOverflow = m_FontData.horizontalOverflow; + settings.verticalOverflow = m_FontData.verticalOverflow; + + return settings; + } + + static public Vector2 GetTextAnchorPivot(TextAnchor anchor) + { + switch (anchor) + { + case TextAnchor.LowerLeft: return new Vector2(0, 0); + case TextAnchor.LowerCenter: return new Vector2(0.5f, 0); + case TextAnchor.LowerRight: return new Vector2(1, 0); + case TextAnchor.MiddleLeft: return new Vector2(0, 0.5f); + case TextAnchor.MiddleCenter: return new Vector2(0.5f, 0.5f); + case TextAnchor.MiddleRight: return new Vector2(1, 0.5f); + case TextAnchor.UpperLeft: return new Vector2(0, 1); + case TextAnchor.UpperCenter: return new Vector2(0.5f, 1); + case TextAnchor.UpperRight: return new Vector2(1, 1); + default: return Vector2.zero; + } + } + + readonly UIVertex[] m_TempVerts = new UIVertex[4]; + protected override void OnPopulateMesh(VertexHelper toFill) + { + if (font == null) + return; + + // We don't care if we the font Texture changes while we are doing our Update. + // The end result of cachedTextGenerator will be valid for this instance. + // Otherwise we can get issues like Case 619238. + m_DisableFontTextureRebuiltCallback = true; + + Vector2 extents = rectTransform.rect.size; + + // 生成对应的网格数据(顶点、UV) + var settings = GetGenerationSettings(extents); + cachedTextGenerator.PopulateWithErrors(text, settings, gameObject); + + // Apply the offset to the vertices + IList<UIVertex> verts = cachedTextGenerator.verts; + float unitsPerPixel = 1 / pixelsPerUnit; + //Last 4 verts are always a new line... (\n) + int vertCount = verts.Count - 4; + + Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel; + roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset; + toFill.Clear(); + if (roundingOffset != Vector2.zero) + { + for (int i = 0; i < vertCount; ++i) + { + int tempVertsIndex = i & 3; + m_TempVerts[tempVertsIndex] = verts[i]; + m_TempVerts[tempVertsIndex].position *= unitsPerPixel; + m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; + m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; + if (tempVertsIndex == 3) + toFill.AddUIVertexQuad(m_TempVerts); + } + } + else + { + for (int i = 0; i < vertCount; ++i) + { + int tempVertsIndex = i & 3; + m_TempVerts[tempVertsIndex] = verts[i]; + m_TempVerts[tempVertsIndex].position *= unitsPerPixel; + if (tempVertsIndex == 3) + toFill.AddUIVertexQuad(m_TempVerts); + } + } + + m_DisableFontTextureRebuiltCallback = false; + } + + public virtual void CalculateLayoutInputHorizontal() {} + public virtual void CalculateLayoutInputVertical() {} + + public virtual float minWidth + { + get { return 0; } + } + + public virtual float preferredWidth + { + get + { + var settings = GetGenerationSettings(Vector2.zero); + return cachedTextGeneratorForLayout.GetPreferredWidth(m_Text, settings) / pixelsPerUnit; + } + } + + public virtual float flexibleWidth { get { return -1; } } + + public virtual float minHeight + { + get { return 0; } + } + + public virtual float preferredHeight + { + get + { + var settings = GetGenerationSettings(new Vector2(GetPixelAdjustedRect().size.x, 0.0f)); + return cachedTextGeneratorForLayout.GetPreferredHeight(m_Text, settings) / pixelsPerUnit; + } + } + + public virtual float flexibleHeight { get { return -1; } } + + public virtual int layoutPriority { get { return 0; } } + +#if UNITY_EDITOR + public override void OnRebuildRequested() + { + // After a Font asset gets re-imported the managed side gets deleted and recreated, + // that means the delegates are not persisted. + // so we need to properly enforce a consistent state here. + FontUpdateTracker.UntrackText(this); + FontUpdateTracker.TrackText(this); + + // Also the textgenerator is no longer valid. + cachedTextGenerator.Invalidate(); + + base.OnRebuildRequested(); + } + + // 当编辑器里修改了参数值的时候调这个回调,setDirty,后续重建mesh + // The Text inspector editor can change the font, and we need a way to track changes so that we get the appropriate rebuild callbacks + // We can intercept changes in OnValidate, and keep track of the previous font reference + protected override void OnValidate() + { + if (!IsActive()) + { + base.OnValidate(); + return; + } + + if (m_FontData.font != m_LastTrackedFont) + { + Font newFont = m_FontData.font; + m_FontData.font = m_LastTrackedFont; + FontUpdateTracker.UntrackText(this); + m_FontData.font = newFont; + FontUpdateTracker.TrackText(this); + + m_LastTrackedFont = newFont; + } + base.OnValidate(); + } + +#endif // if UNITY_EDITOR + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/Text.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/Text.cs.meta new file mode 100644 index 0000000..ea6d1d8 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Font/Text.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 48696ad002dcb2146ab8d6ad72d455d7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics.meta new file mode 100644 index 0000000..f28155d --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 68d41682b219698458518d531a2805cc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Graphic.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Graphic.cs new file mode 100644 index 0000000..21acfb4 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Graphic.cs @@ -0,0 +1,669 @@ +using System; +#if UNITY_EDITOR +using System.Reflection; +#endif +using UnityEngine.Events; +using UnityEngine.EventSystems; +using UnityEngine.Serialization; +using UnityEngine.UI.CoroutineTween; + +namespace UnityEngine.UI +{ + /// <summary> + /// Base class for all UI components that should be derived from when creating new Graphic types. + /// </summary> + [DisallowMultipleComponent] + [RequireComponent(typeof(CanvasRenderer))] + [RequireComponent(typeof(RectTransform))] + [ExecuteInEditMode] + public abstract class Graphic + : UIBehaviour, + ICanvasElement + { + static protected Material s_DefaultUI = null; + static protected Texture2D s_WhiteTexture = null; + + /// <summary> + /// Default material used to draw everything if no explicit material was specified. + /// </summary> + + static public Material defaultGraphicMaterial + { + get + { + if (s_DefaultUI == null) + s_DefaultUI = Canvas.GetDefaultCanvasMaterial(); + return s_DefaultUI; + } + } + + // Cached and saved values + [FormerlySerializedAs("m_Mat")] + [SerializeField] protected Material m_Material; + + [SerializeField] private Color m_Color = Color.white; + public virtual Color color { get { return m_Color; } set { if (SetPropertyUtility.SetColor(ref m_Color, value)) SetVerticesDirty(); } } + + [SerializeField] private bool m_RaycastTarget = true; + public virtual bool raycastTarget { get { return m_RaycastTarget; } set { m_RaycastTarget = value; } } + + [NonSerialized] private RectTransform m_RectTransform; + [NonSerialized] private CanvasRenderer m_CanvasRender; + [NonSerialized] private Canvas m_Canvas; + + [NonSerialized] private bool m_VertsDirty; + [NonSerialized] private bool m_MaterialDirty; + + [NonSerialized] protected UnityAction m_OnDirtyLayoutCallback; + [NonSerialized] protected UnityAction m_OnDirtyVertsCallback; + [NonSerialized] protected UnityAction m_OnDirtyMaterialCallback; + + [NonSerialized] protected static Mesh s_Mesh; + [NonSerialized] private static readonly VertexHelper s_VertexHelper = new VertexHelper(); + + // Tween controls for the Graphic + [NonSerialized] + private readonly TweenRunner<ColorTween> m_ColorTweenRunner; + + protected bool useLegacyMeshGeneration { get; set; } + + // Called by Unity prior to deserialization, + // should not be called by users + protected Graphic() + { + if (m_ColorTweenRunner == null) + m_ColorTweenRunner = new TweenRunner<ColorTween>(); + m_ColorTweenRunner.Init(this); + useLegacyMeshGeneration = true; + } + + public virtual void SetAllDirty() + { + SetLayoutDirty(); + SetVerticesDirty(); + SetMaterialDirty(); + } + + public virtual void SetLayoutDirty() + { + if (!IsActive()) + return; + + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + + if (m_OnDirtyLayoutCallback != null) + m_OnDirtyLayoutCallback(); + } + + public virtual void SetVerticesDirty() + { + if (!IsActive()) + return; + + m_VertsDirty = true; + CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); + + if (m_OnDirtyVertsCallback != null) + m_OnDirtyVertsCallback(); + } + + public virtual void SetMaterialDirty() + { + if (!IsActive()) + return; + + m_MaterialDirty = true; + CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); + + if (m_OnDirtyMaterialCallback != null) + m_OnDirtyMaterialCallback(); + } + + protected override void OnRectTransformDimensionsChange() + { + if (gameObject.activeInHierarchy) + { + // prevent double dirtying... + if (CanvasUpdateRegistry.IsRebuildingLayout()) + SetVerticesDirty(); + else + { + SetVerticesDirty(); + SetLayoutDirty(); + } + } + } + + protected override void OnBeforeTransformParentChanged() + { + GraphicRegistry.UnregisterGraphicForCanvas(canvas, this); + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + } + + protected override void OnTransformParentChanged() + { + base.OnTransformParentChanged(); + + m_Canvas = null; + + if (!IsActive()) + return; + + CacheCanvas(); + GraphicRegistry.RegisterGraphicForCanvas(canvas, this); + SetAllDirty(); + } + + // 在hierachy里的深度,依次增加 + /// <summary> + /// Absolute depth of the graphic, used by rendering and events -- lowest to highest. + /// </summary> + public int depth { get { return canvasRenderer.absoluteDepth; } } + + /// <summary> + /// Transform gets cached for speed. + /// </summary> + public RectTransform rectTransform + { + get { return m_RectTransform ?? (m_RectTransform = GetComponent<RectTransform>()); } + } + + public Canvas canvas + { + get + { + if (m_Canvas == null) + CacheCanvas(); + return m_Canvas; + } + } + + // 找到父节点中最近的canvas + private void CacheCanvas() + { + var list = ListPool<Canvas>.Get(); + // 找到所有父节点中所有canvas,从里到外 + gameObject.GetComponentsInParent(false, list); + if (list.Count > 0) + { + // Find the first active and enabled canvas. + for (int i = 0; i < list.Count; ++i) + { + if (list[i].isActiveAndEnabled) + { + m_Canvas = list[i]; + break; + } + } + } + else + m_Canvas = null; + ListPool<Canvas>.Release(list); + } + + /// <summary> + /// UI Renderer component. + /// </summary> + public CanvasRenderer canvasRenderer + { + get + { + if (m_CanvasRender == null) + m_CanvasRender = GetComponent<CanvasRenderer>(); + return m_CanvasRender; + } + } + + public virtual Material defaultMaterial + { + get { return defaultGraphicMaterial; } + } + + /// <summary> + /// Returns the material used by this Graphic. + /// </summary> + public virtual Material material + { + get + { + return (m_Material != null) ? m_Material : defaultMaterial; + } + set + { + if (m_Material == value) + return; + + m_Material = value; + SetMaterialDirty(); + } + } + + /// <summary>
+ /// 提交到CanvasRenderer用这个,要应用IMaterialModifier的修改结果
+ /// </summary> + public virtual Material materialForRendering + { + get + { + // 在这里调用IMaterialModifier的修改 + var components = ListPool<Component>.Get(); + GetComponents(typeof(IMaterialModifier), components); + + var currentMat = material; + for (var i = 0; i < components.Count; i++) + currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat); + ListPool<Component>.Release(components); + return currentMat; + } + } + + /// <summary> + /// Returns the texture used to draw this Graphic. + /// </summary> + public virtual Texture mainTexture + { + get + { + return s_WhiteTexture; + } + } + + /// <summary> + /// Mark the Graphic and the canvas as having been changed. + /// </summary> + protected override void OnEnable() + { + base.OnEnable(); + CacheCanvas(); + GraphicRegistry.RegisterGraphicForCanvas(canvas, this); + +#if UNITY_EDITOR + GraphicRebuildTracker.TrackGraphic(this); +#endif + if (s_WhiteTexture == null) + s_WhiteTexture = Texture2D.whiteTexture; + + SetAllDirty(); + } + + /// <summary> + /// Clear references. + /// </summary> + protected override void OnDisable() + { +#if UNITY_EDITOR + GraphicRebuildTracker.UnTrackGraphic(this); +#endif + GraphicRegistry.UnregisterGraphicForCanvas(canvas, this); + CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this); + + if (canvasRenderer != null) + canvasRenderer.Clear(); + + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + + base.OnDisable(); + } + + protected override void OnCanvasHierarchyChanged() + { + // Use m_Cavas so we dont auto call CacheCanvas + Canvas currentCanvas = m_Canvas; + + // Clear the cached canvas. Will be fetched below if active. + m_Canvas = null; + + if (!IsActive()) + return; + + CacheCanvas(); + + if (currentCanvas != m_Canvas) + { + GraphicRegistry.UnregisterGraphicForCanvas(currentCanvas, this); + + // Only register if we are active and enabled as OnCanvasHierarchyChanged can get called + // during object destruction and we dont want to register ourself and then become null. + if (IsActive()) + GraphicRegistry.RegisterGraphicForCanvas(canvas, this); + } + } + + // canvas重建 + public virtual void Rebuild(CanvasUpdate update) + { + if (canvasRenderer.cull) + return; + + switch (update) + { + case CanvasUpdate.PreRender: + if (m_VertsDirty) + { + UpdateGeometry(); + m_VertsDirty = false; + } + if (m_MaterialDirty) + { + UpdateMaterial(); + m_MaterialDirty = false; + } + break; + } + } + + public virtual void LayoutComplete() + {} + + public virtual void GraphicUpdateComplete() + {} + + /// <summary> + /// Update the renderer's material. + /// </summary> + protected virtual void UpdateMaterial() + { + if (!IsActive()) + return; + + canvasRenderer.materialCount = 1; + canvasRenderer.SetMaterial(materialForRendering, 0); + canvasRenderer.SetTexture(mainTexture); // 设置_MainTex,会覆盖材质上设置的_MainTex + } + + //c 顶点重建 + /// <summary> + /// Update the renderer's vertices. + /// </summary> + protected virtual void UpdateGeometry() + { + if (useLegacyMeshGeneration) + DoLegacyMeshGeneration(); + else + DoMeshGeneration(); + } + + // 顶点重建,生成mesh + private void DoMeshGeneration() + { + if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0) + OnPopulateMesh(s_VertexHelper); // 填充vertexHelper + else + s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw. + + // 可以通过实现IMeshModifer修改mesh + var components = ListPool<Component>.Get(); + GetComponents(typeof(IMeshModifier), components); + + for (var i = 0; i < components.Count; i++) + ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper); + + ListPool<Component>.Release(components); + + s_VertexHelper.FillMesh(workerMesh); + + // 传入canvasRenderer作为mesh + canvasRenderer.SetMesh(workerMesh); + } + + private void DoLegacyMeshGeneration() + { + if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0) + { +#pragma warning disable 618 + OnPopulateMesh(workerMesh); +#pragma warning restore 618 + } + else + { + workerMesh.Clear(); + } + + // 自定义流程 + var components = ListPool<Component>.Get(); + GetComponents(typeof(IMeshModifier), components); + + for (var i = 0; i < components.Count; i++) + { +#pragma warning disable 618 + ((IMeshModifier)components[i]).ModifyMesh(workerMesh); +#pragma warning restore 618 + } + + ListPool<Component>.Release(components); + canvasRenderer.SetMesh(workerMesh); + } + + protected static Mesh workerMesh + { + get + { + if (s_Mesh == null) + { + s_Mesh = new Mesh(); + s_Mesh.name = "Shared UI Mesh"; + s_Mesh.hideFlags = HideFlags.HideAndDontSave; + } + return s_Mesh; + } + } + + [Obsolete("Use OnPopulateMesh instead.", true)] + protected virtual void OnFillVBO(System.Collections.Generic.List<UIVertex> vbo) {} + + [Obsolete("Use OnPopulateMesh(VertexHelper vh) instead.", false)] + protected virtual void OnPopulateMesh(Mesh m) + { + OnPopulateMesh(s_VertexHelper); + s_VertexHelper.FillMesh(m); + } + + /// <summary> + /// Fill the vertex buffer data. + /// </summary> + protected virtual void OnPopulateMesh(VertexHelper vh) + { + var r = GetPixelAdjustedRect(); + var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height); + + Color32 color32 = color; + vh.Clear(); + vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(0f, 0f)); + vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(0f, 1f)); + vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(1f, 1f)); + vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(1f, 0f)); + + vh.AddTriangle(0, 1, 2); + vh.AddTriangle(2, 3, 0); + } + +#if UNITY_EDITOR + public virtual void OnRebuildRequested() + { + // when rebuild is requested we need to rebuild all the graphics / + // and associated components... The correct way to do this is by + // calling OnValidate... Because MB's don't have a common base class + // we do this via reflection. It's nasty and ugly... Editor only. + var mbs = gameObject.GetComponents<MonoBehaviour>(); + foreach (var mb in mbs) + { + if (mb == null) + continue; + var methodInfo = mb.GetType().GetMethod("OnValidate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (methodInfo != null) + methodInfo.Invoke(mb, null); + } + } + + protected override void Reset() + { + SetAllDirty(); + } + +#endif + + // Call from unity if animation properties have changed + + protected override void OnDidApplyAnimationProperties() + { + SetAllDirty(); + } + + /// <summary> + /// Make the Graphic have the native size of its content. + /// </summary> + public virtual void SetNativeSize() {} + public virtual bool Raycast(Vector2 sp, Camera eventCamera) + { + if (!isActiveAndEnabled) + return false; + + var t = transform; + var components = ListPool<Component>.Get(); + + bool ignoreParentGroups = false; + bool continueTraversal = true; + + while (t != null) + { + t.GetComponents(components); + for (var i = 0; i < components.Count; i++) + { + var canvas = components[i] as Canvas; + if (canvas != null && canvas.overrideSorting) + continueTraversal = false; + + // 如果实现了这个接口再进一步判断一下 + var filter = components[i] as ICanvasRaycastFilter; + + if (filter == null) + continue; + + var raycastValid = true; + + var group = components[i] as CanvasGroup; + if (group != null) + { + if (ignoreParentGroups == false && group.ignoreParentGroups) + { + ignoreParentGroups = true; + raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); + } + else if (!ignoreParentGroups) + raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); + } + else + { + raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); + } + + if (!raycastValid) + { + ListPool<Component>.Release(components); + return false; + } + } + t = continueTraversal ? t.parent : null; + } + ListPool<Component>.Release(components); + return true; + } + +#if UNITY_EDITOR + protected override void OnValidate() + { + base.OnValidate(); + SetAllDirty(); + } + +#endif + + public Vector2 PixelAdjustPoint(Vector2 point) + { + if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f || !canvas.pixelPerfect) + return point; + else + { + return RectTransformUtility.PixelAdjustPoint(point, transform, canvas); + } + } + + public Rect GetPixelAdjustedRect() + { + if (!canvas || canvas.renderMode == RenderMode.WorldSpace || canvas.scaleFactor == 0.0f || !canvas.pixelPerfect) + return rectTransform.rect; + else + return RectTransformUtility.PixelAdjustRect(rectTransform, canvas); + } + + public virtual void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha) + { + CrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha, true); + } + + public virtual void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha, bool useRGB) + { + if (canvasRenderer == null || (!useRGB && !useAlpha)) + return; + + Color currentColor = canvasRenderer.GetColor(); + if (currentColor.Equals(targetColor)) + { + m_ColorTweenRunner.StopTween(); + return; + } + + ColorTween.ColorTweenMode mode = (useRGB && useAlpha ? + ColorTween.ColorTweenMode.All : + (useRGB ? ColorTween.ColorTweenMode.RGB : ColorTween.ColorTweenMode.Alpha)); + + var colorTween = new ColorTween {duration = duration, startColor = canvasRenderer.GetColor(), targetColor = targetColor}; + colorTween.AddOnChangedCallback(canvasRenderer.SetColor); + colorTween.ignoreTimeScale = ignoreTimeScale; + colorTween.tweenMode = mode; + m_ColorTweenRunner.StartTween(colorTween); + } + + static private Color CreateColorFromAlpha(float alpha) + { + var alphaColor = Color.black; + alphaColor.a = alpha; + return alphaColor; + } + + public virtual void CrossFadeAlpha(float alpha, float duration, bool ignoreTimeScale) + { + CrossFadeColor(CreateColorFromAlpha(alpha), duration, ignoreTimeScale, true, false); + } + + public void RegisterDirtyLayoutCallback(UnityAction action) + { + m_OnDirtyLayoutCallback += action; + } + + public void UnregisterDirtyLayoutCallback(UnityAction action) + { + m_OnDirtyLayoutCallback -= action; + } + + public void RegisterDirtyVerticesCallback(UnityAction action) + { + m_OnDirtyVertsCallback += action; + } + + public void UnregisterDirtyVerticesCallback(UnityAction action) + { + m_OnDirtyVertsCallback -= action; + } + + public void RegisterDirtyMaterialCallback(UnityAction action) + { + m_OnDirtyMaterialCallback += action; + } + + public void UnregisterDirtyMaterialCallback(UnityAction action) + { + m_OnDirtyMaterialCallback -= action; + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Graphic.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Graphic.cs.meta new file mode 100644 index 0000000..e02d697 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Graphic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3acb46ac7ba14ff4e938138943716b11 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRaycaster.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRaycaster.cs new file mode 100644 index 0000000..303689a --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRaycaster.cs @@ -0,0 +1,290 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine.EventSystems; +using UnityEngine.Serialization; + +namespace UnityEngine.UI +{ + [AddComponentMenu("Event/Graphic Raycaster")] + [RequireComponent(typeof(Canvas))] + public class GraphicRaycaster : BaseRaycaster + { + protected const int kNoEventMaskSet = -1; + public enum BlockingObjects + { + None = 0, + TwoD = 1, + ThreeD = 2, + All = 3, + } + + public override int sortOrderPriority + { + get + { + // We need to return the sorting order here as distance will all be 0 for overlay. + if (canvas.renderMode == RenderMode.ScreenSpaceOverlay) + return canvas.sortingOrder; + + return base.sortOrderPriority; + } + } + + public override int renderOrderPriority + { + get + { + // We need to return the sorting order here as distance will all be 0 for overlay. + if (canvas.renderMode == RenderMode.ScreenSpaceOverlay) + return canvas.rootCanvas.renderOrder; + + return base.renderOrderPriority; + } + } + + [FormerlySerializedAs("ignoreReversedGraphics")] + [SerializeField] + private bool m_IgnoreReversedGraphics = true; + [FormerlySerializedAs("blockingObjects")] + [SerializeField] + private BlockingObjects m_BlockingObjects = BlockingObjects.None; + + public bool ignoreReversedGraphics { get {return m_IgnoreReversedGraphics; } set { m_IgnoreReversedGraphics = value; } } + public BlockingObjects blockingObjects { get {return m_BlockingObjects; } set { m_BlockingObjects = value; } } + + [SerializeField] + protected LayerMask m_BlockingMask = kNoEventMaskSet; + + private Canvas m_Canvas; + + protected GraphicRaycaster() + {} + + private Canvas canvas + { + get + { + if (m_Canvas != null) + return m_Canvas; + + m_Canvas = GetComponent<Canvas>(); + return m_Canvas; + } + } + + [NonSerialized] private List<Graphic> m_RaycastResults = new List<Graphic>(); + public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList) + { + if (canvas == null) + return; + + // 拿到canvas下的所有graphic + var canvasGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas); + if (canvasGraphics == null || canvasGraphics.Count == 0) + return; + + int displayIndex; + var currentEventCamera = eventCamera; // Propery can call Camera.main, so cache the reference + + if (canvas.renderMode == RenderMode.ScreenSpaceOverlay || currentEventCamera == null) + displayIndex = canvas.targetDisplay; + else + displayIndex = currentEventCamera.targetDisplay; + + var eventPosition = Display.RelativeMouseAt(eventData.position); + if (eventPosition != Vector3.zero) + { + // We support multiple display and display identification based on event position. + + int eventDisplayIndex = (int)eventPosition.z; + + // Discard events that are not part of this display so the user does not interact with multiple displays at once. + if (eventDisplayIndex != displayIndex) + return; + } + else + { + // The multiple display system is not supported on all platforms, when it is not supported the returned position + // will be all zeros so when the returned index is 0 we will default to the event data to be safe. + eventPosition = eventData.position; + + // We dont really know in which display the event occured. We will process the event assuming it occured in our display. + } + + // Convert to view space + Vector2 pos; + if (currentEventCamera == null) + { + // 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) + float w = Screen.width; + float h = Screen.height; + if (displayIndex > 0 && displayIndex < Display.displays.Length) + { + w = Display.displays[displayIndex].systemWidth; + h = Display.displays[displayIndex].systemHeight; + } + pos = new Vector2(eventPosition.x / w, eventPosition.y / h); + } + else + pos = currentEventCamera.ScreenToViewportPoint(eventPosition); + + // If it's outside the camera's viewport, do nothing + if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f) + return; + + float hitDistance = float.MaxValue; + + Ray ray = new Ray(); + + if (currentEventCamera != null) + ray = currentEventCamera.ScreenPointToRay(eventPosition); + + if (canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None) + { + float distanceToClipPlane = 100.0f; + + if (currentEventCamera != null) + { + float projectionDirection = ray.direction.z; + distanceToClipPlane = Mathf.Approximately(0.0f, projectionDirection) + ? Mathf.Infinity + : Mathf.Abs((currentEventCamera.farClipPlane - currentEventCamera.nearClipPlane) / projectionDirection); + } + + if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All) + { + if (ReflectionMethodsCache.Singleton.raycast3D != null) + { + var hits = ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, (int)m_BlockingMask); + if (hits.Length > 0) + hitDistance = hits[0].distance; + } + } + + if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All) + { + if (ReflectionMethodsCache.Singleton.raycast2D != null) + { + var hits = ReflectionMethodsCache.Singleton.getRayIntersectionAll(ray, distanceToClipPlane, (int)m_BlockingMask); + if (hits.Length > 0) + hitDistance = hits[0].distance; + } + } + } + + m_RaycastResults.Clear(); + Raycast(canvas, currentEventCamera, eventPosition, canvasGraphics, m_RaycastResults); + + int totalCount = m_RaycastResults.Count; + for (var index = 0; index < totalCount; index++) + { + var go = m_RaycastResults[index].gameObject; + bool appendGraphic = true; + + if (ignoreReversedGraphics) + { + if (currentEventCamera == null) + { + // If we dont have a camera we know that we should always be facing forward + var dir = go.transform.rotation * Vector3.forward; + appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0; + } + else + { + // If we have a camera compare the direction against the cameras forward. + var cameraFoward = currentEventCamera.transform.rotation * Vector3.forward; + var dir = go.transform.rotation * Vector3.forward; + appendGraphic = Vector3.Dot(cameraFoward, dir) > 0; + } + } + + if (appendGraphic) + { + float distance = 0; + + if (currentEventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay) + distance = 0; + else + { + Transform trans = go.transform; + Vector3 transForward = trans.forward; + // http://geomalgorithms.com/a06-_intersect-2.html + distance = (Vector3.Dot(transForward, trans.position - currentEventCamera.transform.position) / Vector3.Dot(transForward, ray.direction)); + + // Check to see if the go is behind the camera. + if (distance < 0) + continue; + } + + if (distance >= hitDistance) + continue; + + var castResult = new RaycastResult + { + gameObject = go, + module = this, + distance = distance, + screenPosition = eventPosition, + index = resultAppendList.Count, + depth = m_RaycastResults[index].depth, + sortingLayer = canvas.sortingLayerID, + sortingOrder = canvas.sortingOrder + }; + resultAppendList.Add(castResult); + } + } + } + + public override Camera eventCamera + { + get + { + if (canvas.renderMode == RenderMode.ScreenSpaceOverlay || (canvas.renderMode == RenderMode.ScreenSpaceCamera && canvas.worldCamera == null)) + return null; + + return canvas.worldCamera != null ? canvas.worldCamera : Camera.main; + } + } + + // + /// <summary> + /// Perform a raycast into the screen and collect all graphics underneath it. + /// </summary> + [NonSerialized] static readonly List<Graphic> s_SortedGraphics = new List<Graphic>(); + private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results) + { + // Debug.Log("ttt" + pointerPoision + ":::" + camera); + // Necessary for the event system + int totalCount = foundGraphics.Count; + for (int i = 0; i < totalCount; ++i) + { + Graphic graphic = foundGraphics[i]; + + // -1 means it hasn't been processed by the canvas, which means it isn't actually drawn + if (graphic.depth == -1 || !graphic.raycastTarget || graphic.canvasRenderer.cull) + continue; + + if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera)) + continue; + + if (graphic.Raycast(pointerPosition, eventCamera)) + { + s_SortedGraphics.Add(graphic); + } + } + + // 从高到低,所以会以深度优先。越深的越先响应。 + s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth)); + // StringBuilder cast = new StringBuilder(); + totalCount = s_SortedGraphics.Count; + for (int i = 0; i < totalCount; ++i) + results.Add(s_SortedGraphics[i]); + // Debug.Log (cast.ToString()); + + s_SortedGraphics.Clear(); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRaycaster.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRaycaster.cs.meta new file mode 100644 index 0000000..d51fee9 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRaycaster.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0dc7198a94080b64e9e51e6f934b3e35 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRebuildTracker.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRebuildTracker.cs new file mode 100644 index 0000000..c081269 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRebuildTracker.cs @@ -0,0 +1,38 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using UnityEngine.UI.Collections; + +namespace UnityEngine.UI +{ + public static class GraphicRebuildTracker + { + static IndexedSet<Graphic> m_Tracked = new IndexedSet<Graphic>(); + static bool s_Initialized; + + public static void TrackGraphic(Graphic g) + { + if (!s_Initialized) + { + CanvasRenderer.onRequestRebuild += OnRebuildRequested; + s_Initialized = true; + } + + m_Tracked.AddUnique(g); + } + + public static void UnTrackGraphic(Graphic g) + { + m_Tracked.Remove(g); + } + + static void OnRebuildRequested() + { + StencilMaterial.ClearAll(); + for (int i = 0; i < m_Tracked.Count; i++) + { + m_Tracked[i].OnRebuildRequested(); + } + } + } +} +#endif // if UNITY_EDITOR diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRebuildTracker.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRebuildTracker.cs.meta new file mode 100644 index 0000000..d0fff87 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRebuildTracker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40f1f16390b7d7842b9e0c5df6baa9d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRegistry.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRegistry.cs new file mode 100644 index 0000000..0642792 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRegistry.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using UnityEngine.UI.Collections; + +namespace UnityEngine.UI +{ + // 游戏中只有一份单例 + public class GraphicRegistry + { + private static GraphicRegistry s_Instance; + + // 按Canvas分类统计Graphic + private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics = new Dictionary<Canvas, IndexedSet<Graphic>>(); + + protected GraphicRegistry() + { + // This is needed for AOT on IOS. Without it the compile doesn't get the definition of the Dictionarys +#pragma warning disable 168 + Dictionary<Graphic, int> emptyGraphicDic; + Dictionary<ICanvasElement, int> emptyElementDic; +#pragma warning restore 168 + } + + public static GraphicRegistry instance + { + get + { + if (s_Instance == null) + s_Instance = new GraphicRegistry(); + return s_Instance; + } + } + + public static void RegisterGraphicForCanvas(Canvas c, Graphic graphic) + { + if (c == null) + return; + + IndexedSet<Graphic> graphics; + instance.m_Graphics.TryGetValue(c, out graphics); + + if (graphics != null) + { + graphics.AddUnique(graphic); + return; + } + + // Dont need to AddUnique as we know its the only item in the list + graphics = new IndexedSet<Graphic>(); + graphics.Add(graphic); + instance.m_Graphics.Add(c, graphics); + } + + public static void UnregisterGraphicForCanvas(Canvas c, Graphic graphic) + { + if (c == null) + return; + + IndexedSet<Graphic> graphics; + if (instance.m_Graphics.TryGetValue(c, out graphics)) + { + graphics.Remove(graphic); + + if (graphics.Count == 0) + instance.m_Graphics.Remove(c); + } + } + + private static readonly List<Graphic> s_EmptyList = new List<Graphic>(); + public static IList<Graphic> GetGraphicsForCanvas(Canvas canvas) + { + IndexedSet<Graphic> graphics; + if (instance.m_Graphics.TryGetValue(canvas, out graphics)) + return graphics; + + return s_EmptyList; + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRegistry.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRegistry.cs.meta new file mode 100644 index 0000000..bc0ed05 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/GraphicRegistry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0a078c41319d0f4ab759c3ad44fd733 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/IGraphicEnabledDisabled.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/IGraphicEnabledDisabled.cs new file mode 100644 index 0000000..f2becbb --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/IGraphicEnabledDisabled.cs @@ -0,0 +1,10 @@ +using System; + +namespace UnityEngine.UI +{ + [Obsolete("Not supported anymore")] + interface IGraphicEnabledDisabled + { + void OnSiblingGraphicEnabledDisabled(); + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/IGraphicEnabledDisabled.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/IGraphicEnabledDisabled.cs.meta new file mode 100644 index 0000000..24c8266 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/IGraphicEnabledDisabled.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6892dbec884b7b4482fe7cdb11bc565 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Image.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Image.cs new file mode 100644 index 0000000..65e0cfd --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Image.cs @@ -0,0 +1,1171 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine.Serialization; + +namespace UnityEngine.UI +{ + /// <summary> + /// Image is a textured element in the UI hierarchy. + /// </summary> + + [AddComponentMenu("UI/Image", 11)] + public class Image : MaskableGraphic, ISerializationCallbackReceiver, ILayoutElement, ICanvasRaycastFilter + { + public enum Type + { + Simple, + Sliced, + Tiled, + Filled + } + + public enum FillMethod + { + Horizontal, + Vertical, + Radial90, + Radial180, + Radial360, + } + + public enum OriginHorizontal + { + Left, + Right, + } + + public enum OriginVertical + { + Bottom, + Top, + } + + public enum Origin90 + { + BottomLeft, + TopLeft, + TopRight, + BottomRight, + } + + public enum Origin180 + { + Bottom, + Left, + Top, + Right, + } + + public enum Origin360 + { + Bottom, + Right, + Top, + Left, + } + + static protected Material s_ETC1DefaultUI = null; + + [FormerlySerializedAs("m_Frame")] + [SerializeField] private Sprite m_Sprite; + public Sprite sprite { get { return m_Sprite; } set { if (SetPropertyUtility.SetClass(ref m_Sprite, value)) SetAllDirty(); } } + + [NonSerialized] + private Sprite m_OverrideSprite; + public Sprite overrideSprite { get { return activeSprite; } set { if (SetPropertyUtility.SetClass(ref m_OverrideSprite, value)) SetAllDirty(); } } + + private Sprite activeSprite { get { return m_OverrideSprite != null ? m_OverrideSprite : sprite; } } + + /// How the Image is drawn. + [SerializeField] private Type m_Type = Type.Simple; + public Type type { get { return m_Type; } set { if (SetPropertyUtility.SetStruct(ref m_Type, value)) SetVerticesDirty(); } } + + [SerializeField] private bool m_PreserveAspect = false; + public bool preserveAspect { get { return m_PreserveAspect; } set { if (SetPropertyUtility.SetStruct(ref m_PreserveAspect, value)) SetVerticesDirty(); } } + + [SerializeField] private bool m_FillCenter = true; + public bool fillCenter { get { return m_FillCenter; } set { if (SetPropertyUtility.SetStruct(ref m_FillCenter, value)) SetVerticesDirty(); } } + + /// Filling method for filled sprites. + [SerializeField] private FillMethod m_FillMethod = FillMethod.Radial360; + public FillMethod fillMethod { get { return m_FillMethod; } set { if (SetPropertyUtility.SetStruct(ref m_FillMethod, value)) { SetVerticesDirty(); m_FillOrigin = 0; } } } + + /// Amount of the Image shown. 0-1 range with 0 being nothing shown, and 1 being the full Image. + [Range(0, 1)] + [SerializeField] private float m_FillAmount = 1.0f; + public float fillAmount { get { return m_FillAmount; } set { if (SetPropertyUtility.SetStruct(ref m_FillAmount, Mathf.Clamp01(value))) SetVerticesDirty(); } } + + /// Whether the Image should be filled clockwise (true) or counter-clockwise (false). + [SerializeField] private bool m_FillClockwise = true; + public bool fillClockwise { get { return m_FillClockwise; } set { if (SetPropertyUtility.SetStruct(ref m_FillClockwise, value)) SetVerticesDirty(); } } + + /// Controls the origin point of the Fill process. Value means different things with each fill method. + [SerializeField] private int m_FillOrigin; + public int fillOrigin { get { return m_FillOrigin; } set { if (SetPropertyUtility.SetStruct(ref m_FillOrigin, value)) SetVerticesDirty(); } } + + // Not serialized until we support read-enabled sprites better. + private float m_AlphaHitTestMinimumThreshold = 0; + + [Obsolete("eventAlphaThreshold has been deprecated. Use eventMinimumAlphaThreshold instead (UnityUpgradable) -> alphaHitTestMinimumThreshold")] + public float eventAlphaThreshold { get { return 1 - alphaHitTestMinimumThreshold; } set { alphaHitTestMinimumThreshold = 1 - value; } } + public float alphaHitTestMinimumThreshold { get { return m_AlphaHitTestMinimumThreshold; } set { m_AlphaHitTestMinimumThreshold = value; } } + + protected Image() + { + useLegacyMeshGeneration = false; + } + + /// <summary> + /// Default material used to draw everything if no explicit material was specified. + /// </summary> + + static public Material defaultETC1GraphicMaterial + { + get + { + if (s_ETC1DefaultUI == null) + s_ETC1DefaultUI = Canvas.GetETC1SupportedCanvasMaterial(); + return s_ETC1DefaultUI; + } + } + + /// <summary> + /// Image's texture comes from the UnityEngine.Image. + /// </summary> + public override Texture mainTexture + { + get + { + if (activeSprite == null) + { + if (material != null && material.mainTexture != null) + { + return material.mainTexture; + } + return s_WhiteTexture; + } + + return activeSprite.texture; + } + } + + /// <summary> + /// Whether the Image has a border to work with. + /// </summary> + + public bool hasBorder + { + get + { + if (activeSprite != null) + { + Vector4 v = activeSprite.border; + return v.sqrMagnitude > 0f; + } + return false; + } + } + + public float pixelsPerUnit + { + get + { + float spritePixelsPerUnit = 100; + if (activeSprite) + spritePixelsPerUnit = activeSprite.pixelsPerUnit; + + float referencePixelsPerUnit = 100; + if (canvas) + referencePixelsPerUnit = canvas.referencePixelsPerUnit; + + return spritePixelsPerUnit / referencePixelsPerUnit; + } + } + + public override Material material + { + get + { + if (m_Material != null) + return m_Material; + + if (activeSprite && activeSprite.associatedAlphaSplitTexture != null) + return defaultETC1GraphicMaterial; + + return defaultMaterial; + } + + set + { + base.material = value; + } + } + + public virtual void OnBeforeSerialize()
+ {
+ //Debug.Log("OnBeforeSerialize");
+ } + + // 序列化之后,即修改了序列化参数之后。校正一下结果 + public virtual void OnAfterDeserialize() + { + if (m_FillOrigin < 0) + m_FillOrigin = 0; + else if (m_FillMethod == FillMethod.Horizontal && m_FillOrigin > 1) + m_FillOrigin = 0; + else if (m_FillMethod == FillMethod.Vertical && m_FillOrigin > 1) + m_FillOrigin = 0; + else if (m_FillOrigin > 3) + m_FillOrigin = 0; + + m_FillAmount = Mathf.Clamp(m_FillAmount, 0f, 1f); + } + + /// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top. + private Vector4 GetDrawingDimensions(bool shouldPreserveAspect) + { + var padding = activeSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(activeSprite); + var size = activeSprite == null ? Vector2.zero : new Vector2(activeSprite.rect.width, activeSprite.rect.height); + + Rect r = GetPixelAdjustedRect(); + // Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r)); + + int spriteW = Mathf.RoundToInt(size.x); + int spriteH = Mathf.RoundToInt(size.y); + + var v = new Vector4( + padding.x / spriteW, + padding.y / spriteH, + (spriteW - padding.z) / spriteW, + (spriteH - padding.w) / spriteH); + + if (shouldPreserveAspect && size.sqrMagnitude > 0.0f) + { + var spriteRatio = size.x / size.y; + var rectRatio = r.width / r.height; + + if (spriteRatio > rectRatio) + { + var oldHeight = r.height; + r.height = r.width * (1.0f / spriteRatio); + r.y += (oldHeight - r.height) * rectTransform.pivot.y; + } + else + { + var oldWidth = r.width; + r.width = r.height * spriteRatio; + r.x += (oldWidth - r.width) * rectTransform.pivot.x; + } + } + + v = new Vector4( + r.x + r.width * v.x, + r.y + r.height * v.y, + r.x + r.width * v.z, + r.y + r.height * v.w + ); + + return v; + } + + public override void SetNativeSize() + { + if (activeSprite != null) + { + float w = activeSprite.rect.width / pixelsPerUnit; + float h = activeSprite.rect.height / pixelsPerUnit; + rectTransform.anchorMax = rectTransform.anchorMin; + rectTransform.sizeDelta = new Vector2(w, h); + SetAllDirty(); + } + } + + /// <summary> + /// Update the UI renderer mesh. + /// </summary> + protected override void OnPopulateMesh(VertexHelper toFill) + { + if (activeSprite == null) + { + base.OnPopulateMesh(toFill); + return; + } + + switch (type) + { + case Type.Simple: + GenerateSimpleSprite(toFill, m_PreserveAspect); + break; + case Type.Sliced: + GenerateSlicedSprite(toFill); + break; + case Type.Tiled: + GenerateTiledSprite(toFill); + break; + case Type.Filled: + GenerateFilledSprite(toFill, m_PreserveAspect); + break; + } + } + + /// <summary> + /// Update the renderer's material. + /// </summary> + + protected override void UpdateMaterial() + { + base.UpdateMaterial(); + + // check if this sprite has an associated alpha texture (generated when splitting RGBA = RGB + A as two textures without alpha) + + if (activeSprite == null) + { + canvasRenderer.SetAlphaTexture(null); + return; + } + + //如果是ETC压缩格式,会生成一份alphaTex只保存透明度信息,不在_MainTex里 + Texture2D alphaTex = activeSprite.associatedAlphaSplitTexture; + + if (alphaTex != null) + { + canvasRenderer.SetAlphaTexture(alphaTex); // _AlphaTex + } + } + + /// <summary> + /// Generate vertices for a simple Image. + /// </summary> + void GenerateSimpleSprite(VertexHelper vh, bool lPreserveAspect) + { + Vector4 v = GetDrawingDimensions(lPreserveAspect); + var uv = (activeSprite != null) ? Sprites.DataUtility.GetOuterUV(activeSprite) : Vector4.zero; + + var color32 = color; + vh.Clear(); + vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(uv.x, uv.y)); + vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(uv.x, uv.w)); + vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(uv.z, uv.w)); + vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(uv.z, uv.y)); + + vh.AddTriangle(0, 1, 2); + vh.AddTriangle(2, 3, 0); + } + + /// <summary> + /// Generate vertices for a 9-sliced Image. + /// </summary> + + static readonly Vector2[] s_VertScratch = new Vector2[4]; + static readonly Vector2[] s_UVScratch = new Vector2[4]; + + private void GenerateSlicedSprite(VertexHelper toFill) + { + if (!hasBorder) + { + GenerateSimpleSprite(toFill, false); + return; + } + + Vector4 outer, inner, padding, border; + + if (activeSprite != null) + { + outer = Sprites.DataUtility.GetOuterUV(activeSprite); + inner = Sprites.DataUtility.GetInnerUV(activeSprite); + padding = Sprites.DataUtility.GetPadding(activeSprite); + border = activeSprite.border; + } + else + { + outer = Vector4.zero; + inner = Vector4.zero; + padding = Vector4.zero; + border = Vector4.zero; + } + + Rect rect = GetPixelAdjustedRect(); + Vector4 adjustedBorders = GetAdjustedBorders(border / pixelsPerUnit, rect); + padding = padding / pixelsPerUnit; + + s_VertScratch[0] = new Vector2(padding.x, padding.y); + s_VertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w); + + s_VertScratch[1].x = adjustedBorders.x; + s_VertScratch[1].y = adjustedBorders.y; + + s_VertScratch[2].x = rect.width - adjustedBorders.z; + s_VertScratch[2].y = rect.height - adjustedBorders.w; + + for (int i = 0; i < 4; ++i) + { + s_VertScratch[i].x += rect.x; + s_VertScratch[i].y += rect.y; + } + + s_UVScratch[0] = new Vector2(outer.x, outer.y); + s_UVScratch[1] = new Vector2(inner.x, inner.y); + s_UVScratch[2] = new Vector2(inner.z, inner.w); + s_UVScratch[3] = new Vector2(outer.z, outer.w); + + toFill.Clear(); + + for (int x = 0; x < 3; ++x) + { + int x2 = x + 1; + + for (int y = 0; y < 3; ++y) + { + if (!m_FillCenter && x == 1 && y == 1) + continue; + + int y2 = y + 1; + + + AddQuad(toFill, + new Vector2(s_VertScratch[x].x, s_VertScratch[y].y), + new Vector2(s_VertScratch[x2].x, s_VertScratch[y2].y), + color, + new Vector2(s_UVScratch[x].x, s_UVScratch[y].y), + new Vector2(s_UVScratch[x2].x, s_UVScratch[y2].y)); + } + } + } + + /// <summary> + /// Generate vertices for a tiled Image. + /// </summary> + + void GenerateTiledSprite(VertexHelper toFill) + { + Vector4 outer, inner, border; + Vector2 spriteSize; + + if (activeSprite != null) + { + outer = Sprites.DataUtility.GetOuterUV(activeSprite); + inner = Sprites.DataUtility.GetInnerUV(activeSprite); + border = activeSprite.border; + spriteSize = activeSprite.rect.size; + } + else + { + outer = Vector4.zero; + inner = Vector4.zero; + border = Vector4.zero; + spriteSize = Vector2.one * 100; + } + + Rect rect = GetPixelAdjustedRect(); + float tileWidth = (spriteSize.x - border.x - border.z) / pixelsPerUnit; + float tileHeight = (spriteSize.y - border.y - border.w) / pixelsPerUnit; + border = GetAdjustedBorders(border / pixelsPerUnit, rect); + + var uvMin = new Vector2(inner.x, inner.y); + var uvMax = new Vector2(inner.z, inner.w); + + // Min to max max range for tiled region in coordinates relative to lower left corner. + float xMin = border.x; + float xMax = rect.width - border.z; + float yMin = border.y; + float yMax = rect.height - border.w; + + toFill.Clear(); + var clipped = uvMax; + + // if either width is zero we cant tile so just assume it was the full width. + if (tileWidth <= 0) + tileWidth = xMax - xMin; + + if (tileHeight <= 0) + tileHeight = yMax - yMin; + + if (activeSprite != null && (hasBorder || activeSprite.packed || activeSprite.texture.wrapMode != TextureWrapMode.Repeat)) + { + // Sprite has border, or is not in repeat mode, or cannot be repeated because of packing. + // We cannot use texture tiling so we will generate a mesh of quads to tile the texture. + + // Evaluate how many vertices we will generate. Limit this number to something sane, + // especially since meshes can not have more than 65000 vertices. + + long nTilesW = 0; + long nTilesH = 0; + if (m_FillCenter) + { + nTilesW = (long)Math.Ceiling((xMax - xMin) / tileWidth); + nTilesH = (long)Math.Ceiling((yMax - yMin) / tileHeight); + + double nVertices = 0; + if (hasBorder) + { + nVertices = (nTilesW + 2.0) * (nTilesH + 2.0) * 4.0; // 4 vertices per tile + } + else + { + nVertices = nTilesW * nTilesH * 4.0; // 4 vertices per tile + } + + if (nVertices > 65000.0) + { + Debug.LogError("Too many sprite tiles on Image \"" + name + "\". The tile size will be increased. To remove the limit on the number of tiles, convert the Sprite to an Advanced texture, remove the borders, clear the Packing tag and set the Wrap mode to Repeat.", this); + + double maxTiles = 65000.0 / 4.0; // Max number of vertices is 65000; 4 vertices per tile. + double imageRatio; + if (hasBorder) + { + imageRatio = (nTilesW + 2.0) / (nTilesH + 2.0); + } + else + { + imageRatio = (double)nTilesW / nTilesH; + } + + double targetTilesW = Math.Sqrt(maxTiles / imageRatio); + double targetTilesH = targetTilesW * imageRatio; + if (hasBorder) + { + targetTilesW -= 2; + targetTilesH -= 2; + } + + nTilesW = (long)Math.Floor(targetTilesW); + nTilesH = (long)Math.Floor(targetTilesH); + tileWidth = (xMax - xMin) / nTilesW; + tileHeight = (yMax - yMin) / nTilesH; + } + } + else + { + if (hasBorder) + { + // Texture on the border is repeated only in one direction. + nTilesW = (long)Math.Ceiling((xMax - xMin) / tileWidth); + nTilesH = (long)Math.Ceiling((yMax - yMin) / tileHeight); + double nVertices = (nTilesH + nTilesW + 2.0 /*corners*/) * 2.0 /*sides*/ * 4.0 /*vertices per tile*/; + if (nVertices > 65000.0) + { + Debug.LogError("Too many sprite tiles on Image \"" + name + "\". The tile size will be increased. To remove the limit on the number of tiles, convert the Sprite to an Advanced texture, remove the borders, clear the Packing tag and set the Wrap mode to Repeat.", this); + + double maxTiles = 65000.0 / 4.0; // Max number of vertices is 65000; 4 vertices per tile. + double imageRatio = (double)nTilesW / nTilesH; + double targetTilesW = (maxTiles - 4 /*corners*/) / (2 * (1.0 + imageRatio)); + double targetTilesH = targetTilesW * imageRatio; + + nTilesW = (long)Math.Floor(targetTilesW); + nTilesH = (long)Math.Floor(targetTilesH); + tileWidth = (xMax - xMin) / nTilesW; + tileHeight = (yMax - yMin) / nTilesH; + } + } + else + { + nTilesH = nTilesW = 0; + } + } + + if (m_FillCenter) + { + // TODO: we could share vertices between quads. If vertex sharing is implemented. update the computation for the number of vertices accordingly. + for (long j = 0; j < nTilesH; j++) + { + float y1 = yMin + j * tileHeight; + float y2 = yMin + (j + 1) * tileHeight; + if (y2 > yMax) + { + clipped.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1); + y2 = yMax; + } + clipped.x = uvMax.x; + for (long i = 0; i < nTilesW; i++) + { + float x1 = xMin + i * tileWidth; + float x2 = xMin + (i + 1) * tileWidth; + if (x2 > xMax) + { + clipped.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1); + x2 = xMax; + } + AddQuad(toFill, new Vector2(x1, y1) + rect.position, new Vector2(x2, y2) + rect.position, color, uvMin, clipped); + } + } + } + if (hasBorder) + { + clipped = uvMax; + for (long j = 0; j < nTilesH; j++) + { + float y1 = yMin + j * tileHeight; + float y2 = yMin + (j + 1) * tileHeight; + if (y2 > yMax) + { + clipped.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1); + y2 = yMax; + } + AddQuad(toFill, + new Vector2(0, y1) + rect.position, + new Vector2(xMin, y2) + rect.position, + color, + new Vector2(outer.x, uvMin.y), + new Vector2(uvMin.x, clipped.y)); + AddQuad(toFill, + new Vector2(xMax, y1) + rect.position, + new Vector2(rect.width, y2) + rect.position, + color, + new Vector2(uvMax.x, uvMin.y), + new Vector2(outer.z, clipped.y)); + } + + // Bottom and top tiled border + clipped = uvMax; + for (long i = 0; i < nTilesW; i++) + { + float x1 = xMin + i * tileWidth; + float x2 = xMin + (i + 1) * tileWidth; + if (x2 > xMax) + { + clipped.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1); + x2 = xMax; + } + AddQuad(toFill, + new Vector2(x1, 0) + rect.position, + new Vector2(x2, yMin) + rect.position, + color, + new Vector2(uvMin.x, outer.y), + new Vector2(clipped.x, uvMin.y)); + AddQuad(toFill, + new Vector2(x1, yMax) + rect.position, + new Vector2(x2, rect.height) + rect.position, + color, + new Vector2(uvMin.x, uvMax.y), + new Vector2(clipped.x, outer.w)); + } + + // Corners + AddQuad(toFill, + new Vector2(0, 0) + rect.position, + new Vector2(xMin, yMin) + rect.position, + color, + new Vector2(outer.x, outer.y), + new Vector2(uvMin.x, uvMin.y)); + AddQuad(toFill, + new Vector2(xMax, 0) + rect.position, + new Vector2(rect.width, yMin) + rect.position, + color, + new Vector2(uvMax.x, outer.y), + new Vector2(outer.z, uvMin.y)); + AddQuad(toFill, + new Vector2(0, yMax) + rect.position, + new Vector2(xMin, rect.height) + rect.position, + color, + new Vector2(outer.x, uvMax.y), + new Vector2(uvMin.x, outer.w)); + AddQuad(toFill, + new Vector2(xMax, yMax) + rect.position, + new Vector2(rect.width, rect.height) + rect.position, + color, + new Vector2(uvMax.x, uvMax.y), + new Vector2(outer.z, outer.w)); + } + } + else + { + // Texture has no border, is in repeat mode and not packed. Use texture tiling. + Vector2 uvScale = new Vector2((xMax - xMin) / tileWidth, (yMax - yMin) / tileHeight); + + if (m_FillCenter) + { + AddQuad(toFill, new Vector2(xMin, yMin) + rect.position, new Vector2(xMax, yMax) + rect.position, color, Vector2.Scale(uvMin, uvScale), Vector2.Scale(uvMax, uvScale)); + } + } + } + + static void AddQuad(VertexHelper vertexHelper, Vector3[] quadPositions, Color32 color, Vector3[] quadUVs) + { + int startIndex = vertexHelper.currentVertCount; + + for (int i = 0; i < 4; ++i) + vertexHelper.AddVert(quadPositions[i], color, quadUVs[i]); + + vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2); + vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex); + } + + static void AddQuad(VertexHelper vertexHelper, Vector2 posMin, Vector2 posMax, Color32 color, Vector2 uvMin, Vector2 uvMax) + { + int startIndex = vertexHelper.currentVertCount; + + vertexHelper.AddVert(new Vector3(posMin.x, posMin.y, 0), color, new Vector2(uvMin.x, uvMin.y)); + vertexHelper.AddVert(new Vector3(posMin.x, posMax.y, 0), color, new Vector2(uvMin.x, uvMax.y)); + vertexHelper.AddVert(new Vector3(posMax.x, posMax.y, 0), color, new Vector2(uvMax.x, uvMax.y)); + vertexHelper.AddVert(new Vector3(posMax.x, posMin.y, 0), color, new Vector2(uvMax.x, uvMin.y)); + + vertexHelper.AddTriangle(startIndex, startIndex + 1, startIndex + 2); + vertexHelper.AddTriangle(startIndex + 2, startIndex + 3, startIndex); + } + + private Vector4 GetAdjustedBorders(Vector4 border, Rect adjustedRect) + { + Rect originalRect = rectTransform.rect; + + for (int axis = 0; axis <= 1; axis++) + { + float borderScaleRatio; + + // The adjusted rect (adjusted for pixel correctness) + // may be slightly larger than the original rect. + // Adjust the border to match the adjustedRect to avoid + // small gaps between borders (case 833201). + if (originalRect.size[axis] != 0) + { + borderScaleRatio = adjustedRect.size[axis] / originalRect.size[axis]; + border[axis] *= borderScaleRatio; + border[axis + 2] *= borderScaleRatio; + } + + // If the rect is smaller than the combined borders, then there's not room for the borders at their normal size. + // In order to avoid artefacts with overlapping borders, we scale the borders down to fit. + float combinedBorders = border[axis] + border[axis + 2]; + if (adjustedRect.size[axis] < combinedBorders && combinedBorders != 0) + { + borderScaleRatio = adjustedRect.size[axis] / combinedBorders; + border[axis] *= borderScaleRatio; + border[axis + 2] *= borderScaleRatio; + } + } + return border; + } + + /// <summary> + /// Generate vertices for a filled Image. + /// </summary> + + static readonly Vector3[] s_Xy = new Vector3[4]; + static readonly Vector3[] s_Uv = new Vector3[4]; + void GenerateFilledSprite(VertexHelper toFill, bool preserveAspect) + { + toFill.Clear(); + + if (m_FillAmount < 0.001f) + return; + + Vector4 v = GetDrawingDimensions(preserveAspect); + Vector4 outer = activeSprite != null ? Sprites.DataUtility.GetOuterUV(activeSprite) : Vector4.zero; + UIVertex uiv = UIVertex.simpleVert; + uiv.color = color; + + float tx0 = outer.x; + float ty0 = outer.y; + float tx1 = outer.z; + float ty1 = outer.w; + + // Horizontal and vertical filled sprites are simple -- just end the Image prematurely + if (m_FillMethod == FillMethod.Horizontal || m_FillMethod == FillMethod.Vertical) + { + if (fillMethod == FillMethod.Horizontal) + { + float fill = (tx1 - tx0) * m_FillAmount; + + if (m_FillOrigin == 1) + { + v.x = v.z - (v.z - v.x) * m_FillAmount; + tx0 = tx1 - fill; + } + else + { + v.z = v.x + (v.z - v.x) * m_FillAmount; + tx1 = tx0 + fill; + } + } + else if (fillMethod == FillMethod.Vertical) + { + float fill = (ty1 - ty0) * m_FillAmount; + + if (m_FillOrigin == 1) + { + v.y = v.w - (v.w - v.y) * m_FillAmount; + ty0 = ty1 - fill; + } + else + { + v.w = v.y + (v.w - v.y) * m_FillAmount; + ty1 = ty0 + fill; + } + } + } + + s_Xy[0] = new Vector2(v.x, v.y); + s_Xy[1] = new Vector2(v.x, v.w); + s_Xy[2] = new Vector2(v.z, v.w); + s_Xy[3] = new Vector2(v.z, v.y); + + s_Uv[0] = new Vector2(tx0, ty0); + s_Uv[1] = new Vector2(tx0, ty1); + s_Uv[2] = new Vector2(tx1, ty1); + s_Uv[3] = new Vector2(tx1, ty0); + + { + if (m_FillAmount < 1f && m_FillMethod != FillMethod.Horizontal && m_FillMethod != FillMethod.Vertical) + { + if (fillMethod == FillMethod.Radial90) + { + if (RadialCut(s_Xy, s_Uv, m_FillAmount, m_FillClockwise, m_FillOrigin)) + AddQuad(toFill, s_Xy, color, s_Uv); + } + else if (fillMethod == FillMethod.Radial180) + { + for (int side = 0; side < 2; ++side) + { + float fx0, fx1, fy0, fy1; + int even = m_FillOrigin > 1 ? 1 : 0; + + if (m_FillOrigin == 0 || m_FillOrigin == 2) + { + fy0 = 0f; + fy1 = 1f; + if (side == even) + { + fx0 = 0f; + fx1 = 0.5f; + } + else + { + fx0 = 0.5f; + fx1 = 1f; + } + } + else + { + fx0 = 0f; + fx1 = 1f; + if (side == even) + { + fy0 = 0.5f; + fy1 = 1f; + } + else + { + fy0 = 0f; + fy1 = 0.5f; + } + } + + s_Xy[0].x = Mathf.Lerp(v.x, v.z, fx0); + s_Xy[1].x = s_Xy[0].x; + s_Xy[2].x = Mathf.Lerp(v.x, v.z, fx1); + s_Xy[3].x = s_Xy[2].x; + + s_Xy[0].y = Mathf.Lerp(v.y, v.w, fy0); + s_Xy[1].y = Mathf.Lerp(v.y, v.w, fy1); + s_Xy[2].y = s_Xy[1].y; + s_Xy[3].y = s_Xy[0].y; + + s_Uv[0].x = Mathf.Lerp(tx0, tx1, fx0); + s_Uv[1].x = s_Uv[0].x; + s_Uv[2].x = Mathf.Lerp(tx0, tx1, fx1); + s_Uv[3].x = s_Uv[2].x; + + s_Uv[0].y = Mathf.Lerp(ty0, ty1, fy0); + s_Uv[1].y = Mathf.Lerp(ty0, ty1, fy1); + s_Uv[2].y = s_Uv[1].y; + s_Uv[3].y = s_Uv[0].y; + + float val = m_FillClockwise ? fillAmount * 2f - side : m_FillAmount * 2f - (1 - side); + + if (RadialCut(s_Xy, s_Uv, Mathf.Clamp01(val), m_FillClockwise, ((side + m_FillOrigin + 3) % 4))) + { + AddQuad(toFill, s_Xy, color, s_Uv); + } + } + } + else if (fillMethod == FillMethod.Radial360) + { + for (int corner = 0; corner < 4; ++corner) + { + float fx0, fx1, fy0, fy1; + + if (corner < 2) + { + fx0 = 0f; + fx1 = 0.5f; + } + else + { + fx0 = 0.5f; + fx1 = 1f; + } + + if (corner == 0 || corner == 3) + { + fy0 = 0f; + fy1 = 0.5f; + } + else + { + fy0 = 0.5f; + fy1 = 1f; + } + + s_Xy[0].x = Mathf.Lerp(v.x, v.z, fx0); + s_Xy[1].x = s_Xy[0].x; + s_Xy[2].x = Mathf.Lerp(v.x, v.z, fx1); + s_Xy[3].x = s_Xy[2].x; + + s_Xy[0].y = Mathf.Lerp(v.y, v.w, fy0); + s_Xy[1].y = Mathf.Lerp(v.y, v.w, fy1); + s_Xy[2].y = s_Xy[1].y; + s_Xy[3].y = s_Xy[0].y; + + s_Uv[0].x = Mathf.Lerp(tx0, tx1, fx0); + s_Uv[1].x = s_Uv[0].x; + s_Uv[2].x = Mathf.Lerp(tx0, tx1, fx1); + s_Uv[3].x = s_Uv[2].x; + + s_Uv[0].y = Mathf.Lerp(ty0, ty1, fy0); + s_Uv[1].y = Mathf.Lerp(ty0, ty1, fy1); + s_Uv[2].y = s_Uv[1].y; + s_Uv[3].y = s_Uv[0].y; + + float val = m_FillClockwise ? + m_FillAmount * 4f - ((corner + m_FillOrigin) % 4) : + m_FillAmount * 4f - (3 - ((corner + m_FillOrigin) % 4)); + + if (RadialCut(s_Xy, s_Uv, Mathf.Clamp01(val), m_FillClockwise, ((corner + 2) % 4))) + AddQuad(toFill, s_Xy, color, s_Uv); + } + } + } + else + { + AddQuad(toFill, s_Xy, color, s_Uv); + } + } + } + + /// <summary> + /// Adjust the specified quad, making it be radially filled instead. + /// </summary> + + static bool RadialCut(Vector3[] xy, Vector3[] uv, float fill, bool invert, int corner) + { + // Nothing to fill + if (fill < 0.001f) return false; + + // Even corners invert the fill direction + if ((corner & 1) == 1) invert = !invert; + + // Nothing to adjust + if (!invert && fill > 0.999f) return true; + + // Convert 0-1 value into 0 to 90 degrees angle in radians + float angle = Mathf.Clamp01(fill); + if (invert) angle = 1f - angle; + angle *= 90f * Mathf.Deg2Rad; + + // Calculate the effective X and Y factors + float cos = Mathf.Cos(angle); + float sin = Mathf.Sin(angle); + + RadialCut(xy, cos, sin, invert, corner); + RadialCut(uv, cos, sin, invert, corner); + return true; + } + + /// <summary> + /// Adjust the specified quad, making it be radially filled instead. + /// </summary> + + static void RadialCut(Vector3[] xy, float cos, float sin, bool invert, int corner) + { + int i0 = corner; + int i1 = ((corner + 1) % 4); + int i2 = ((corner + 2) % 4); + int i3 = ((corner + 3) % 4); + + if ((corner & 1) == 1) + { + if (sin > cos) + { + cos /= sin; + sin = 1f; + + if (invert) + { + xy[i1].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos); + xy[i2].x = xy[i1].x; + } + } + else if (cos > sin) + { + sin /= cos; + cos = 1f; + + if (!invert) + { + xy[i2].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin); + xy[i3].y = xy[i2].y; + } + } + else + { + cos = 1f; + sin = 1f; + } + + if (!invert) xy[i3].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos); + else xy[i1].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin); + } + else + { + if (cos > sin) + { + sin /= cos; + cos = 1f; + + if (!invert) + { + xy[i1].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin); + xy[i2].y = xy[i1].y; + } + } + else if (sin > cos) + { + cos /= sin; + sin = 1f; + + if (invert) + { + xy[i2].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos); + xy[i3].x = xy[i2].x; + } + } + else + { + cos = 1f; + sin = 1f; + } + + if (invert) xy[i3].y = Mathf.Lerp(xy[i0].y, xy[i2].y, sin); + else xy[i1].x = Mathf.Lerp(xy[i0].x, xy[i2].x, cos); + } + } + + public virtual void CalculateLayoutInputHorizontal() {} + public virtual void CalculateLayoutInputVertical() {} + + public virtual float minWidth { get { return 0; } } + + public virtual float preferredWidth + { + get + { + if (activeSprite == null) + return 0; + if (type == Type.Sliced || type == Type.Tiled) + return Sprites.DataUtility.GetMinSize(activeSprite).x / pixelsPerUnit; + return activeSprite.rect.size.x / pixelsPerUnit; + } + } + + public virtual float flexibleWidth { get { return -1; } } + + public virtual float minHeight { get { return 0; } } + + public virtual float preferredHeight + { + get + { + if (activeSprite == null) + return 0; + if (type == Type.Sliced || type == Type.Tiled) + return Sprites.DataUtility.GetMinSize(activeSprite).y / pixelsPerUnit; + return activeSprite.rect.size.y / pixelsPerUnit; + } + } + + public virtual float flexibleHeight { get { return -1; } } + + public virtual int layoutPriority { get { return 0; } } + + //会根据透明度做更细致的射线检测,透明的地方不算 + public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) + { + if (alphaHitTestMinimumThreshold <= 0) + return true; + + if (alphaHitTestMinimumThreshold > 1) + return false; + + if (activeSprite == null) + return true; + + Vector2 local; + if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local)) + return false; + + Rect rect = GetPixelAdjustedRect(); + + // Convert to have lower left corner as reference point. + local.x += rectTransform.pivot.x * rect.width; + local.y += rectTransform.pivot.y * rect.height; + + local = MapCoordinate(local, rect); + + // Normalize local coordinates. + Rect spriteRect = activeSprite.textureRect; + Vector2 normalized = new Vector2(local.x / spriteRect.width, local.y / spriteRect.height); + + // Convert to texture space. + float x = Mathf.Lerp(spriteRect.x, spriteRect.xMax, normalized.x) / activeSprite.texture.width; + float y = Mathf.Lerp(spriteRect.y, spriteRect.yMax, normalized.y) / activeSprite.texture.height; + + try + { + return activeSprite.texture.GetPixelBilinear(x, y).a >= alphaHitTestMinimumThreshold; + } + catch (UnityException e) + { + Debug.LogError("Using alphaHitTestMinimumThreshold greater than 0 on Image whose sprite texture cannot be read. " + e.Message + " Also make sure to disable sprite packing for this sprite.", this); + return true; + } + } + + private Vector2 MapCoordinate(Vector2 local, Rect rect) + { + Rect spriteRect = activeSprite.rect; + if (type == Type.Simple || type == Type.Filled) + return new Vector2(local.x * spriteRect.width / rect.width, local.y * spriteRect.height / rect.height); + + Vector4 border = activeSprite.border; + Vector4 adjustedBorder = GetAdjustedBorders(border / pixelsPerUnit, rect); + + for (int i = 0; i < 2; i++) + { + if (local[i] <= adjustedBorder[i]) + continue; + + if (rect.size[i] - local[i] <= adjustedBorder[i + 2]) + { + local[i] -= (rect.size[i] - spriteRect.size[i]); + continue; + } + + if (type == Type.Sliced) + { + float lerp = Mathf.InverseLerp(adjustedBorder[i], rect.size[i] - adjustedBorder[i + 2], local[i]); + local[i] = Mathf.Lerp(border[i], spriteRect.size[i] - border[i + 2], lerp); + continue; + } + else + { + local[i] -= adjustedBorder[i]; + local[i] = Mathf.Repeat(local[i], spriteRect.size[i] - border[i] - border[i + 2]); + local[i] += border[i]; + continue; + } + } + + return local; + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Image.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Image.cs.meta new file mode 100644 index 0000000..91ab591 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/Image.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0cba8e295482e184f9f6a5a8f2b559c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs new file mode 100644 index 0000000..0d0f327 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs @@ -0,0 +1,235 @@ +using System; +using UnityEngine.Events; +using UnityEngine.Rendering; + +namespace UnityEngine.UI +{ + public abstract class MaskableGraphic : Graphic, IClippable, IMaskable, IMaterialModifier + { + [NonSerialized] + protected bool m_ShouldRecalculateStencil = true; + + [NonSerialized] + protected Material m_MaskMaterial; + + [NonSerialized] + private RectMask2D m_ParentMask; + + // m_Maskable is whether this graphic is allowed to be masked or not. It has the matching public property maskable. + // The default for m_Maskable is true, so graphics under a mask are masked out of the box. + // The maskable property can be turned off from script by the user if masking is not desired. + // m_IncludeForMasking is whether we actually consider this graphic for masking or not - this is an implementation detail. + // m_IncludeForMasking should only be true if m_Maskable is true AND a parent of the graphic has an IMask component. + // Things would still work correctly if m_IncludeForMasking was always true when m_Maskable is, but performance would suffer. + [NonSerialized] + private bool m_Maskable = true; + + [NonSerialized] + [Obsolete("Not used anymore.", true)] + protected bool m_IncludeForMasking = false; + + [Serializable] + public class CullStateChangedEvent : UnityEvent<bool> {} + + // Event delegates triggered on click. + [SerializeField] + private CullStateChangedEvent m_OnCullStateChanged = new CullStateChangedEvent(); + + public CullStateChangedEvent onCullStateChanged + { + get { return m_OnCullStateChanged; } + set { m_OnCullStateChanged = value; } + } + + public bool maskable + { + get { return m_Maskable; } + set + { + if (value == m_Maskable) + return; + m_Maskable = value; + m_ShouldRecalculateStencil = true; + SetMaterialDirty(); + } + } + + [NonSerialized] + [Obsolete("Not used anymore", true)] + protected bool m_ShouldRecalculate = true; + + [NonSerialized] + protected int m_StencilValue; + + public virtual Material GetModifiedMaterial(Material baseMaterial) + { + var toUse = baseMaterial; + + if (m_ShouldRecalculateStencil) + { + var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); + // Graphic在masks下的深度,如果不是0且没有mask组件意味着是普通的非mask用graphic(如果是0一定是mask用的graphic) + m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0; + m_ShouldRecalculateStencil = false; + } + + // if we have a enabled Mask component then it will + // generate the mask material. This is an optimisation + // it adds some coupling between components though :( + Mask maskComponent = GetComponent<Mask>(); + if (m_StencilValue > 0 && (maskComponent == null || !maskComponent.IsActive())) + { + // Ref = (1 << stencilValue) - 1 + // Op = Keep + // Func = Equal + // ReadMask = (1 << stencilValue) - 1 + // WriteMask = 0 + var maskMat = StencilMaterial.Add(toUse, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0); + StencilMaterial.Remove(m_MaskMaterial); + m_MaskMaterial = maskMat; + toUse = m_MaskMaterial; + } + return toUse; + } + + public virtual void Cull(Rect clipRect, bool validRect) + { + var cull = !validRect || !clipRect.Overlaps(rootCanvasRect, true); + UpdateCull(cull); + } + + private void UpdateCull(bool cull) + { + var cullingChanged = canvasRenderer.cull != cull; + canvasRenderer.cull = cull; + + if (cullingChanged) + { + UISystemProfilerApi.AddMarker("MaskableGraphic.cullingChanged", this); + m_OnCullStateChanged.Invoke(cull); + SetVerticesDirty(); // 这里需要更新一下canvasRenderer的网格数据 + } + } + + public virtual void SetClipRect(Rect clipRect, bool validRect) + { + if (validRect) + canvasRenderer.EnableRectClipping(clipRect); + else + canvasRenderer.DisableRectClipping(); + } + + protected override void OnEnable() + { + base.OnEnable(); + m_ShouldRecalculateStencil = true; + UpdateClipParent(); + SetMaterialDirty(); + + if (GetComponent<Mask>() != null) + { + MaskUtilities.NotifyStencilStateChanged(this); + } + } + + protected override void OnDisable() + { + base.OnDisable(); + m_ShouldRecalculateStencil = true; + SetMaterialDirty(); + UpdateClipParent(); + StencilMaterial.Remove(m_MaskMaterial); + m_MaskMaterial = null; + + if (GetComponent<Mask>() != null) + { + MaskUtilities.NotifyStencilStateChanged(this); + } + } + +#if UNITY_EDITOR + protected override void OnValidate() + { + base.OnValidate(); + m_ShouldRecalculateStencil = true; + UpdateClipParent(); + SetMaterialDirty(); + } + +#endif + + protected override void OnTransformParentChanged() + { + base.OnTransformParentChanged(); + + if (!isActiveAndEnabled) + return; + + m_ShouldRecalculateStencil = true; + UpdateClipParent(); + SetMaterialDirty(); + } + + [Obsolete("Not used anymore.", true)] + public virtual void ParentMaskStateChanged() {} + + protected override void OnCanvasHierarchyChanged() + { + base.OnCanvasHierarchyChanged(); + + if (!isActiveAndEnabled) + return; + + m_ShouldRecalculateStencil = true; + UpdateClipParent(); + SetMaterialDirty(); + } + + readonly Vector3[] m_Corners = new Vector3[4]; + private Rect rootCanvasRect + { + get + { + rectTransform.GetWorldCorners(m_Corners); + + if (canvas) + { + Canvas rootCanvas = canvas.rootCanvas; + for (int i = 0; i < 4; ++i) + m_Corners[i] = rootCanvas.transform.InverseTransformPoint(m_Corners[i]); + } + + return new Rect(m_Corners[0].x, m_Corners[0].y, m_Corners[2].x - m_Corners[0].x, m_Corners[2].y - m_Corners[0].y); + } + } + + private void UpdateClipParent() + { + var newParent = (maskable && IsActive()) ? MaskUtilities.GetRectMaskForClippable(this) : null; + + // if the new parent is different OR is now inactive + if (m_ParentMask != null && (newParent != m_ParentMask || !newParent.IsActive())) + { + m_ParentMask.RemoveClippable(this); + UpdateCull(false); + } + + // don't re-add it if the newparent is inactive + if (newParent != null && newParent.IsActive()) + newParent.AddClippable(this); + + m_ParentMask = newParent; + } + + public virtual void RecalculateClipping() + { + UpdateClipParent(); + } + + public virtual void RecalculateMasking() + { + m_ShouldRecalculateStencil = true; + SetMaterialDirty(); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs.meta new file mode 100644 index 0000000..07f0d51 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/MaskableGraphic.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1316d79994e789c448714be540d28f4f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/RawImage.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/RawImage.cs new file mode 100644 index 0000000..e6dab1b --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/RawImage.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Serialization; + +namespace UnityEngine.UI +{ + /// <summary> + /// If you don't have or don't wish to create an atlas, you can simply use this script to draw a texture. + /// Keep in mind though that this will create an extra draw call with each RawImage present, so it's + /// best to use it only for backgrounds or temporary visible graphics. + /// </summary> + [AddComponentMenu("UI/Raw Image", 12)] + public class RawImage : MaskableGraphic + { + [FormerlySerializedAs("m_Tex")] + [SerializeField] Texture m_Texture; + [SerializeField] Rect m_UVRect = new Rect(0f, 0f, 1f, 1f); + + protected RawImage() + { + useLegacyMeshGeneration = false; + } + + /// <summary> + /// Returns the texture used to draw this Graphic. + /// </summary> + public override Texture mainTexture + { + get + { + if (m_Texture == null) + {
+ if (material != null && material.mainTexture != null) + { + return material.mainTexture; + } + return s_WhiteTexture; + } + return m_Texture; + } + } + + /// <summary> + /// Texture to be used. + /// </summary> + public Texture texture + { + get + { + return m_Texture; + } + set + { + if (m_Texture == value) + return; + + m_Texture = value; + SetVerticesDirty(); + SetMaterialDirty(); + } + } + + /// <summary> + /// UV rectangle used by the texture. + /// </summary> + public Rect uvRect + { + get + { + return m_UVRect; + } + set + { + if (m_UVRect == value) + return; + m_UVRect = value; + SetVerticesDirty(); + } + } + + /// <summary> + /// Adjust the scale of the Graphic to make it pixel-perfect. + /// </summary> + + public override void SetNativeSize() + { + Texture tex = mainTexture; + if (tex != null) + { + int w = Mathf.RoundToInt(tex.width * uvRect.width); + int h = Mathf.RoundToInt(tex.height * uvRect.height); + rectTransform.anchorMax = rectTransform.anchorMin; + rectTransform.sizeDelta = new Vector2(w, h); + } + } + + protected override void OnPopulateMesh(VertexHelper vh) + { + Texture tex = mainTexture; + vh.Clear(); + if (tex != null) + { + var r = GetPixelAdjustedRect(); + var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height); + var scaleX = tex.width * tex.texelSize.x; + var scaleY = tex.height * tex.texelSize.y; + { + var color32 = color; + vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(m_UVRect.xMin * scaleX, m_UVRect.yMin * scaleY)); + vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(m_UVRect.xMin * scaleX, m_UVRect.yMax * scaleY)); + vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(m_UVRect.xMax * scaleX, m_UVRect.yMax * scaleY)); + vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(m_UVRect.xMax * scaleX, m_UVRect.yMin * scaleY)); + + vh.AddTriangle(0, 1, 2); + vh.AddTriangle(2, 3, 0); + } + } + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/RawImage.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/RawImage.cs.meta new file mode 100644 index 0000000..2a70e35 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/RawImage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 570773b73e8b0334fb410b7457eb8b6a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/SpriteState.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/SpriteState.cs new file mode 100644 index 0000000..c95e146 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/SpriteState.cs @@ -0,0 +1,33 @@ +using System; +using UnityEngine.Serialization; + +namespace UnityEngine.UI +{ + [Serializable] + public struct SpriteState : IEquatable<SpriteState> + { + [FormerlySerializedAs("highlightedSprite")] + [FormerlySerializedAs("m_SelectedSprite")] + [SerializeField] + private Sprite m_HighlightedSprite; + + [FormerlySerializedAs("pressedSprite")] + [SerializeField] + private Sprite m_PressedSprite; + + [FormerlySerializedAs("disabledSprite")] + [SerializeField] + private Sprite m_DisabledSprite; + + public Sprite highlightedSprite { get { return m_HighlightedSprite; } set { m_HighlightedSprite = value; } } + public Sprite pressedSprite { get { return m_PressedSprite; } set { m_PressedSprite = value; } } + public Sprite disabledSprite { get { return m_DisabledSprite; } set { m_DisabledSprite = value; } } + + public bool Equals(SpriteState other) + { + return highlightedSprite == other.highlightedSprite && + pressedSprite == other.pressedSprite && + disabledSprite == other.disabledSprite; + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/SpriteState.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/SpriteState.cs.meta new file mode 100644 index 0000000..b389ded --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Graphics/SpriteState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cc6d0c85730bc354697c6b7d971eda44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout.meta new file mode 100644 index 0000000..28d0b9d --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 0bcd17013ce6d5a4ab037dcc71a87811 +folderAsset: yes +timeCreated: 1602119280 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/AspectRatioFitter.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/AspectRatioFitter.cs new file mode 100644 index 0000000..96ee135 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/AspectRatioFitter.cs @@ -0,0 +1,145 @@ +using UnityEngine.EventSystems; + +namespace UnityEngine.UI +{ + [AddComponentMenu("Layout/Aspect Ratio Fitter", 142)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + public class AspectRatioFitter : UIBehaviour, ILayoutSelfController + { + public enum AspectMode { None, WidthControlsHeight, HeightControlsWidth, FitInParent, EnvelopeParent } + + [SerializeField] private AspectMode m_AspectMode = AspectMode.None; + public AspectMode aspectMode { get { return m_AspectMode; } set { if (SetPropertyUtility.SetStruct(ref m_AspectMode, value)) SetDirty(); } } + + [SerializeField] private float m_AspectRatio = 1; + public float aspectRatio { get { return m_AspectRatio; } set { if (SetPropertyUtility.SetStruct(ref m_AspectRatio, value)) SetDirty(); } } + + [System.NonSerialized] + private RectTransform m_Rect; + + private RectTransform rectTransform + { + get + { + if (m_Rect == null) + m_Rect = GetComponent<RectTransform>(); + return m_Rect; + } + } + + private DrivenRectTransformTracker m_Tracker; + + protected AspectRatioFitter() {} + + protected override void OnEnable() + { + base.OnEnable(); + SetDirty(); + } + + protected override void OnDisable() + { + m_Tracker.Clear(); + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + base.OnDisable(); + } + + protected override void OnRectTransformDimensionsChange() + { + UpdateRect(); + } + + private void UpdateRect() + { + if (!IsActive()) + return; + + m_Tracker.Clear(); + + switch (m_AspectMode) + { +#if UNITY_EDITOR + case AspectMode.None: + { + if (!Application.isPlaying) + m_AspectRatio = Mathf.Clamp(rectTransform.rect.width / rectTransform.rect.height, 0.001f, 1000f); + + break; + } +#endif + case AspectMode.HeightControlsWidth: + { + m_Tracker.Add(this, rectTransform, DrivenTransformProperties.SizeDeltaX); + rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, rectTransform.rect.height * m_AspectRatio); + break; + } + case AspectMode.WidthControlsHeight: + { + m_Tracker.Add(this, rectTransform, DrivenTransformProperties.SizeDeltaY); + rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, rectTransform.rect.width / m_AspectRatio); + break; + } + case AspectMode.FitInParent: + case AspectMode.EnvelopeParent: + { + m_Tracker.Add(this, rectTransform, + DrivenTransformProperties.Anchors | + DrivenTransformProperties.AnchoredPosition | + DrivenTransformProperties.SizeDeltaX | + DrivenTransformProperties.SizeDeltaY); + + rectTransform.anchorMin = Vector2.zero; + rectTransform.anchorMax = Vector2.one; + rectTransform.anchoredPosition = Vector2.zero; + + Vector2 sizeDelta = Vector2.zero; + Vector2 parentSize = GetParentSize(); + if ((parentSize.y * aspectRatio < parentSize.x) ^ (m_AspectMode == AspectMode.FitInParent)) + { + sizeDelta.y = GetSizeDeltaToProduceSize(parentSize.x / aspectRatio, 1); + } + else + { + sizeDelta.x = GetSizeDeltaToProduceSize(parentSize.y * aspectRatio, 0); + } + rectTransform.sizeDelta = sizeDelta; + + break; + } + } + } + + private float GetSizeDeltaToProduceSize(float size, int axis) + { + return size - GetParentSize()[axis] * (rectTransform.anchorMax[axis] - rectTransform.anchorMin[axis]); + } + + private Vector2 GetParentSize() + { + RectTransform parent = rectTransform.parent as RectTransform; + if (!parent) + return Vector2.zero; + return parent.rect.size; + } + + public virtual void SetLayoutHorizontal() {} + + public virtual void SetLayoutVertical() {} + + protected void SetDirty() + { + UpdateRect(); + } + + #if UNITY_EDITOR + protected override void OnValidate() + { + m_AspectRatio = Mathf.Clamp(m_AspectRatio, 0.001f, 1000f); + SetDirty(); + } + + #endif + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/AspectRatioFitter.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/AspectRatioFitter.cs.meta new file mode 100644 index 0000000..e89adeb --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/AspectRatioFitter.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 49604c30279a7aa44b531ad6ffea458c +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/CanvasScaler.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/CanvasScaler.cs new file mode 100644 index 0000000..6504ac2 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/CanvasScaler.cs @@ -0,0 +1,241 @@ +using UnityEngine.EventSystems; + +namespace UnityEngine.UI +{ + [RequireComponent(typeof(Canvas))] + [ExecuteInEditMode] + [AddComponentMenu("Layout/Canvas Scaler", 101)] + public class CanvasScaler : UIBehaviour + { + public enum ScaleMode { ConstantPixelSize, ScaleWithScreenSize, ConstantPhysicalSize } + + [Tooltip("Determines how UI elements in the Canvas are scaled.")] + [SerializeField] private ScaleMode m_UiScaleMode = ScaleMode.ConstantPixelSize; + public ScaleMode uiScaleMode { get { return m_UiScaleMode; } set { m_UiScaleMode = value; } } + + [Tooltip("If a sprite has this 'Pixels Per Unit' setting, then one pixel in the sprite will cover one unit in the UI.")] + [SerializeField] protected float m_ReferencePixelsPerUnit = 100; + public float referencePixelsPerUnit { get { return m_ReferencePixelsPerUnit; } set { m_ReferencePixelsPerUnit = value; } } + + + // Constant Pixel Size settings + + [Tooltip("Scales all UI elements in the Canvas by this factor.")] + [SerializeField] protected float m_ScaleFactor = 1; + public float scaleFactor { get { return m_ScaleFactor; } set { m_ScaleFactor = Mathf.Max(0.01f, value); } } + + + // Scale With Screen Size settings + + public enum ScreenMatchMode { MatchWidthOrHeight = 0, Expand = 1, Shrink = 2 } + + [Tooltip("The resolution the UI layout is designed for. If the screen resolution is larger, the UI will be scaled up, and if it's smaller, the UI will be scaled down. This is done in accordance with the Screen Match Mode.")] + [SerializeField] protected Vector2 m_ReferenceResolution = new Vector2(800, 600); + public Vector2 referenceResolution + { + get + { + return m_ReferenceResolution; + } + set + { + m_ReferenceResolution = value; + + const float k_MinimumResolution = 0.00001f; + + if (m_ReferenceResolution.x > -k_MinimumResolution && m_ReferenceResolution.x < k_MinimumResolution) m_ReferenceResolution.x = k_MinimumResolution * Mathf.Sign(m_ReferenceResolution.x); + if (m_ReferenceResolution.y > -k_MinimumResolution && m_ReferenceResolution.y < k_MinimumResolution) m_ReferenceResolution.y = k_MinimumResolution * Mathf.Sign(m_ReferenceResolution.y); + } + } + + [Tooltip("A mode used to scale the canvas area if the aspect ratio of the current resolution doesn't fit the reference resolution.")] + [SerializeField] protected ScreenMatchMode m_ScreenMatchMode = ScreenMatchMode.MatchWidthOrHeight; + public ScreenMatchMode screenMatchMode { get { return m_ScreenMatchMode; } set { m_ScreenMatchMode = value; } } + + [Tooltip("Determines if the scaling is using the width or height as reference, or a mix in between.")] + [Range(0, 1)] + [SerializeField] protected float m_MatchWidthOrHeight = 0; + public float matchWidthOrHeight { get { return m_MatchWidthOrHeight; } set { m_MatchWidthOrHeight = value; } } + + // The log base doesn't have any influence on the results whatsoever, as long as the same base is used everywhere. + private const float kLogBase = 2; + + + // Constant Physical Size settings + + public enum Unit { Centimeters, Millimeters, Inches, Points, Picas } + + [Tooltip("The physical unit to specify positions and sizes in.")] + [SerializeField] protected Unit m_PhysicalUnit = Unit.Points; + public Unit physicalUnit { get { return m_PhysicalUnit; } set { m_PhysicalUnit = value; } } + + [Tooltip("The DPI to assume if the screen DPI is not known.")] + [SerializeField] protected float m_FallbackScreenDPI = 96; + public float fallbackScreenDPI { get { return m_FallbackScreenDPI; } set { m_FallbackScreenDPI = value; } } + + [Tooltip("The pixels per inch to use for sprites that have a 'Pixels Per Unit' setting that matches the 'Reference Pixels Per Unit' setting.")] + [SerializeField] protected float m_DefaultSpriteDPI = 96; + public float defaultSpriteDPI { get { return m_DefaultSpriteDPI; } set { m_DefaultSpriteDPI = Mathf.Max(1, value); } } + + + // World Canvas settings + + [Tooltip("The amount of pixels per unit to use for dynamically created bitmaps in the UI, such as Text.")] + [SerializeField] protected float m_DynamicPixelsPerUnit = 1; + public float dynamicPixelsPerUnit { get { return m_DynamicPixelsPerUnit; } set { m_DynamicPixelsPerUnit = value; } } + + + // General variables + + private Canvas m_Canvas; + [System.NonSerialized] + private float m_PrevScaleFactor = 1; + [System.NonSerialized] + private float m_PrevReferencePixelsPerUnit = 100; + + + protected CanvasScaler() {} + + protected override void OnEnable() + { + base.OnEnable(); + m_Canvas = GetComponent<Canvas>(); + Handle(); + } + + protected override void OnDisable() + { + SetScaleFactor(1); + SetReferencePixelsPerUnit(100); + base.OnDisable(); + } + + protected virtual void Update() + { + Handle(); + } + + protected virtual void Handle() + { + if (m_Canvas == null || !m_Canvas.isRootCanvas) + return; + + if (m_Canvas.renderMode == RenderMode.WorldSpace) + { + HandleWorldCanvas(); + return; + } + + switch (m_UiScaleMode) + { + case ScaleMode.ConstantPixelSize: HandleConstantPixelSize(); break; + case ScaleMode.ScaleWithScreenSize: HandleScaleWithScreenSize(); break; + case ScaleMode.ConstantPhysicalSize: HandleConstantPhysicalSize(); break; + } + } + + protected virtual void HandleWorldCanvas() + { + SetScaleFactor(m_DynamicPixelsPerUnit); + SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit); + } + + protected virtual void HandleConstantPixelSize() + { + SetScaleFactor(m_ScaleFactor); + SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit); + } + + protected virtual void HandleScaleWithScreenSize() + { + Vector2 screenSize = new Vector2(Screen.width, 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_Canvas.targetDisplay; + if (displayIndex > 0 && displayIndex < Display.displays.Length) + { + Display disp = Display.displays[displayIndex]; + screenSize = new Vector2(disp.renderingWidth, disp.renderingHeight); + } + + float scaleFactor = 0; + switch (m_ScreenMatchMode) + { + case ScreenMatchMode.MatchWidthOrHeight: + { + // We take the log of the relative width and height before taking the average. + // Then we transform it back in the original space. + // the reason to transform in and out of logarithmic space is to have better behavior. + // If one axis has twice resolution and the other has half, it should even out if widthOrHeight value is at 0.5. + // In normal space the average would be (0.5 + 2) / 2 = 1.25 + // In logarithmic space the average is (-1 + 1) / 2 = 0 + float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase); + float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase); + float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight); + scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage); + break; + } + case ScreenMatchMode.Expand: + { + scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y); + break; + } + case ScreenMatchMode.Shrink: + { + scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y); + break; + } + } + + SetScaleFactor(scaleFactor); + SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit); + } + + protected virtual void HandleConstantPhysicalSize() + { + float currentDpi = Screen.dpi; + float dpi = (currentDpi == 0 ? m_FallbackScreenDPI : currentDpi); + float targetDPI = 1; + switch (m_PhysicalUnit) + { + case Unit.Centimeters: targetDPI = 2.54f; break; + case Unit.Millimeters: targetDPI = 25.4f; break; + case Unit.Inches: targetDPI = 1; break; + case Unit.Points: targetDPI = 72; break; + case Unit.Picas: targetDPI = 6; break; + } + + SetScaleFactor(dpi / targetDPI); + SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit * targetDPI / m_DefaultSpriteDPI); + } + + protected void SetScaleFactor(float scaleFactor) + { + if (scaleFactor == m_PrevScaleFactor) + return; + + m_Canvas.scaleFactor = scaleFactor; + m_PrevScaleFactor = scaleFactor; + } + + protected void SetReferencePixelsPerUnit(float referencePixelsPerUnit) + { + if (referencePixelsPerUnit == m_PrevReferencePixelsPerUnit) + return; + + m_Canvas.referencePixelsPerUnit = referencePixelsPerUnit; + m_PrevReferencePixelsPerUnit = referencePixelsPerUnit; + } + +#if UNITY_EDITOR + protected override void OnValidate() + { + m_ScaleFactor = Mathf.Max(0.01f, m_ScaleFactor); + m_DefaultSpriteDPI = Mathf.Max(1, m_DefaultSpriteDPI); + } + +#endif + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/CanvasScaler.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/CanvasScaler.cs.meta new file mode 100644 index 0000000..4d67413 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/CanvasScaler.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: debcf26b89013e446a6de5574715c5af +timeCreated: 1602119380 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ContentSizeFitter.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ContentSizeFitter.cs new file mode 100644 index 0000000..a31d14c --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ContentSizeFitter.cs @@ -0,0 +1,103 @@ +using UnityEngine.EventSystems; + +namespace UnityEngine.UI +{ + [AddComponentMenu("Layout/Content Size Fitter", 141)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + public class ContentSizeFitter : UIBehaviour, ILayoutSelfController + { + public enum FitMode + { + Unconstrained, + MinSize, + PreferredSize + } + + [SerializeField] protected FitMode m_HorizontalFit = FitMode.Unconstrained; + public FitMode horizontalFit { get { return m_HorizontalFit; } set { if (SetPropertyUtility.SetStruct(ref m_HorizontalFit, value)) SetDirty(); } } + + [SerializeField] protected FitMode m_VerticalFit = FitMode.Unconstrained; + public FitMode verticalFit { get { return m_VerticalFit; } set { if (SetPropertyUtility.SetStruct(ref m_VerticalFit, value)) SetDirty(); } } + + [System.NonSerialized] private RectTransform m_Rect; + private RectTransform rectTransform + { + get + { + if (m_Rect == null) + m_Rect = GetComponent<RectTransform>(); + return m_Rect; + } + } + + private DrivenRectTransformTracker m_Tracker; + + protected ContentSizeFitter() + {} + + protected override void OnEnable() + { + base.OnEnable(); + SetDirty(); + } + + protected override void OnDisable() + { + m_Tracker.Clear(); + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + base.OnDisable(); + } + + protected override void OnRectTransformDimensionsChange() + { + SetDirty(); + } + + private void HandleSelfFittingAlongAxis(int axis) + { + FitMode fitting = (axis == 0 ? horizontalFit : verticalFit); + if (fitting == FitMode.Unconstrained) + { + // Keep a reference to the tracked transform, but don't control its properties: + m_Tracker.Add(this, rectTransform, DrivenTransformProperties.None); + return; + } + + m_Tracker.Add(this, rectTransform, (axis == 0 ? DrivenTransformProperties.SizeDeltaX : DrivenTransformProperties.SizeDeltaY)); + + // Set size to min or preferred size + if (fitting == FitMode.MinSize) + rectTransform.SetSizeWithCurrentAnchors((RectTransform.Axis)axis, LayoutUtility.GetMinSize(m_Rect, axis)); + else + rectTransform.SetSizeWithCurrentAnchors((RectTransform.Axis)axis, LayoutUtility.GetPreferredSize(m_Rect, axis)); + } + + public virtual void SetLayoutHorizontal() + { + m_Tracker.Clear(); + HandleSelfFittingAlongAxis(0); + } + + public virtual void SetLayoutVertical() + { + HandleSelfFittingAlongAxis(1); + } + + protected void SetDirty() + { + if (!IsActive()) + return; + + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + } + + #if UNITY_EDITOR + protected override void OnValidate() + { + SetDirty(); + } + + #endif + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ContentSizeFitter.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ContentSizeFitter.cs.meta new file mode 100644 index 0000000..9f31a6d --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ContentSizeFitter.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 742ec49dc902c1f4db49d19ada047c0e +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/GridLayoutGroup.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/GridLayoutGroup.cs new file mode 100644 index 0000000..170e410 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/GridLayoutGroup.cs @@ -0,0 +1,207 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace UnityEngine.UI +{ + [AddComponentMenu("Layout/Grid Layout Group", 152)] + public class GridLayoutGroup : LayoutGroup + { + public enum Corner { UpperLeft = 0, UpperRight = 1, LowerLeft = 2, LowerRight = 3 } + public enum Axis { Horizontal = 0, Vertical = 1 } + public enum Constraint { Flexible = 0, FixedColumnCount = 1, FixedRowCount = 2 } + + [SerializeField] protected Corner m_StartCorner = Corner.UpperLeft; + public Corner startCorner { get { return m_StartCorner; } set { SetProperty(ref m_StartCorner, value); } } + + [SerializeField] protected Axis m_StartAxis = Axis.Horizontal; + public Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } } + + [SerializeField] protected Vector2 m_CellSize = new Vector2(100, 100); + public Vector2 cellSize { get { return m_CellSize; } set { SetProperty(ref m_CellSize, value); } } + + [SerializeField] protected Vector2 m_Spacing = Vector2.zero; + public Vector2 spacing { get { return m_Spacing; } set { SetProperty(ref m_Spacing, value); } } + + [SerializeField] protected Constraint m_Constraint = Constraint.Flexible; + public Constraint constraint { get { return m_Constraint; } set { SetProperty(ref m_Constraint, value); } } + + [SerializeField] protected int m_ConstraintCount = 2; + public int constraintCount { get { return m_ConstraintCount; } set { SetProperty(ref m_ConstraintCount, Mathf.Max(1, value)); } } + + protected GridLayoutGroup() + {} + + #if UNITY_EDITOR + protected override void OnValidate() + { + base.OnValidate(); + constraintCount = constraintCount; + } + + #endif + + public override void CalculateLayoutInputHorizontal() + { + base.CalculateLayoutInputHorizontal(); + + int minColumns = 0; + int preferredColumns = 0; + if (m_Constraint == Constraint.FixedColumnCount) + { + minColumns = preferredColumns = m_ConstraintCount; + } + else if (m_Constraint == Constraint.FixedRowCount) + { + minColumns = preferredColumns = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f); + } + else + { + minColumns = 1; + preferredColumns = Mathf.CeilToInt(Mathf.Sqrt(rectChildren.Count)); + } + + SetLayoutInputForAxis( + padding.horizontal + (cellSize.x + spacing.x) * minColumns - spacing.x, + padding.horizontal + (cellSize.x + spacing.x) * preferredColumns - spacing.x, + -1, 0); + } + + public override void CalculateLayoutInputVertical() + { + int minRows = 0; + if (m_Constraint == Constraint.FixedColumnCount) + { + minRows = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f); + } + else if (m_Constraint == Constraint.FixedRowCount) + { + minRows = m_ConstraintCount; + } + else + { + float width = rectTransform.rect.size.x; + int cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x))); + minRows = Mathf.CeilToInt(rectChildren.Count / (float)cellCountX); + } + + float minSpace = padding.vertical + (cellSize.y + spacing.y) * minRows - spacing.y; + SetLayoutInputForAxis(minSpace, minSpace, -1, 1); + } + + public override void SetLayoutHorizontal() + { + SetCellsAlongAxis(0); + } + + public override void SetLayoutVertical() + { + SetCellsAlongAxis(1); + } + + private void SetCellsAlongAxis(int axis) + { + // Normally a Layout Controller should only set horizontal values when invoked for the horizontal axis + // and only vertical values when invoked for the vertical axis. + // However, in this case we set both the horizontal and vertical position when invoked for the vertical axis. + // Since we only set the horizontal position and not the size, it shouldn't affect children's layout, + // and thus shouldn't break the rule that all horizontal layout must be calculated before all vertical layout. + + if (axis == 0) + { + // Only set the sizes when invoked for horizontal axis, not the positions. + for (int i = 0; i < rectChildren.Count; i++) + { + RectTransform rect = rectChildren[i]; + + m_Tracker.Add(this, rect, + DrivenTransformProperties.Anchors | + DrivenTransformProperties.AnchoredPosition | + DrivenTransformProperties.SizeDelta); + + rect.anchorMin = Vector2.up; + rect.anchorMax = Vector2.up; + rect.sizeDelta = cellSize; + } + return; + } + + float width = rectTransform.rect.size.x; + float height = rectTransform.rect.size.y; + + int cellCountX = 1; + int cellCountY = 1; + if (m_Constraint == Constraint.FixedColumnCount) + { + cellCountX = m_ConstraintCount; + cellCountY = Mathf.CeilToInt(rectChildren.Count / (float)cellCountX - 0.001f); + } + else if (m_Constraint == Constraint.FixedRowCount) + { + cellCountY = m_ConstraintCount; + cellCountX = Mathf.CeilToInt(rectChildren.Count / (float)cellCountY - 0.001f); + } + else + { + if (cellSize.x + spacing.x <= 0) + cellCountX = int.MaxValue; + else + cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x))); + + if (cellSize.y + spacing.y <= 0) + cellCountY = int.MaxValue; + else + cellCountY = Mathf.Max(1, Mathf.FloorToInt((height - padding.vertical + spacing.y + 0.001f) / (cellSize.y + spacing.y))); + } + + int cornerX = (int)startCorner % 2; + int cornerY = (int)startCorner / 2; + + int cellsPerMainAxis, actualCellCountX, actualCellCountY; + if (startAxis == Axis.Horizontal) + { + cellsPerMainAxis = cellCountX; + actualCellCountX = Mathf.Clamp(cellCountX, 1, rectChildren.Count); + actualCellCountY = Mathf.Clamp(cellCountY, 1, Mathf.CeilToInt(rectChildren.Count / (float)cellsPerMainAxis)); + } + else + { + cellsPerMainAxis = cellCountY; + actualCellCountY = Mathf.Clamp(cellCountY, 1, rectChildren.Count); + actualCellCountX = Mathf.Clamp(cellCountX, 1, Mathf.CeilToInt(rectChildren.Count / (float)cellsPerMainAxis)); + } + + Vector2 requiredSpace = new Vector2( + actualCellCountX * cellSize.x + (actualCellCountX - 1) * spacing.x, + actualCellCountY * cellSize.y + (actualCellCountY - 1) * spacing.y + ); + Vector2 startOffset = new Vector2( + GetStartOffset(0, requiredSpace.x), + GetStartOffset(1, requiredSpace.y) + ); + + for (int i = 0; i < rectChildren.Count; i++) + { + int positionX; + int positionY; + if (startAxis == Axis.Horizontal) + { + positionX = i % cellsPerMainAxis; + positionY = i / cellsPerMainAxis; + } + else + { + positionX = i / cellsPerMainAxis; + positionY = i % cellsPerMainAxis; + } + + if (cornerX == 1) + positionX = actualCellCountX - 1 - positionX; + if (cornerY == 1) + positionY = actualCellCountY - 1 - positionY; + + SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (cellSize[0] + spacing[0]) * positionX, cellSize[0]); + SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]); + } + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/GridLayoutGroup.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/GridLayoutGroup.cs.meta new file mode 100644 index 0000000..0ca5454 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/GridLayoutGroup.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 295d2cd868a0c204f850fde4917c4551 +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalLayoutGroup.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalLayoutGroup.cs new file mode 100644 index 0000000..efe8d8e --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalLayoutGroup.cs @@ -0,0 +1,30 @@ +namespace UnityEngine.UI +{ + [AddComponentMenu("Layout/Horizontal Layout Group", 150)] + public class HorizontalLayoutGroup : HorizontalOrVerticalLayoutGroup + { + protected HorizontalLayoutGroup() + {} + + public override void CalculateLayoutInputHorizontal() + { + base.CalculateLayoutInputHorizontal(); + CalcAlongAxis(0, false); + } + + public override void CalculateLayoutInputVertical() + { + CalcAlongAxis(1, false); + } + + public override void SetLayoutHorizontal() + { + SetChildrenAlongAxis(0, false); + } + + public override void SetLayoutVertical() + { + SetChildrenAlongAxis(1, false); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalLayoutGroup.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalLayoutGroup.cs.meta new file mode 100644 index 0000000..0b116be --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalLayoutGroup.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 2e0c30f216d393d4b93475ba573aea85 +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalOrVerticalLayoutGroup.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalOrVerticalLayoutGroup.cs new file mode 100644 index 0000000..3e1c923 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalOrVerticalLayoutGroup.cs @@ -0,0 +1,179 @@ +namespace UnityEngine.UI +{ + // 布局算法的核心都在这个基类里面 + public abstract class HorizontalOrVerticalLayoutGroup : LayoutGroup + { + [SerializeField] protected float m_Spacing = 0; + public float spacing { get { return m_Spacing; } set { SetProperty(ref m_Spacing, value); } } + + [SerializeField] protected bool m_ChildForceExpandWidth = true; + public bool childForceExpandWidth { get { return m_ChildForceExpandWidth; } set { SetProperty(ref m_ChildForceExpandWidth, value); } } + + [SerializeField] protected bool m_ChildForceExpandHeight = true; + public bool childForceExpandHeight { get { return m_ChildForceExpandHeight; } set { SetProperty(ref m_ChildForceExpandHeight, value); } } + + [SerializeField] protected bool m_ChildControlWidth = true; + public bool childControlWidth { get { return m_ChildControlWidth; } set { SetProperty(ref m_ChildControlWidth, value); } } + + [SerializeField] protected bool m_ChildControlHeight = true; + public bool childControlHeight { get { return m_ChildControlHeight; } set { SetProperty(ref m_ChildControlHeight, value); } } + + // 根据子节点计算得到自身的min, preferred, flexible + protected void CalcAlongAxis(int axis, bool isVertical) + { + float combinedPadding = (axis == 0 ? padding.horizontal : padding.vertical); // padding是left+right, top+bottom + bool controlSize = (axis == 0 ? m_ChildControlWidth : m_ChildControlHeight); + bool childForceExpandSize = (axis == 0 ? childForceExpandWidth : childForceExpandHeight); + + float totalMin = combinedPadding; // 所有子节点的min之和 + float totalPreferred = combinedPadding; // 所有子节点的preferred之和 + float totalFlexible = 0; // 是一个无单位数值,等于所有子节点的flexible值相加 + + bool alongOtherAxis = (isVertical ^ (axis == 1)); + for (int i = 0; i < rectChildren.Count; i++) + { + RectTransform child = rectChildren[i]; + float min, preferred, flexible; // 子节点的三个属性值 + GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible); + + if (alongOtherAxis) + { + totalMin = Mathf.Max(min + combinedPadding, totalMin); + totalPreferred = Mathf.Max(preferred + combinedPadding, totalPreferred); + totalFlexible = Mathf.Max(flexible, totalFlexible); + } + else + { + totalMin += min + spacing; + totalPreferred += preferred + spacing; + + // Increment flexible size with element's flexible size. + totalFlexible += flexible; + } + } + + // 减去最后一个子节点多加的spacing + if (!alongOtherAxis && rectChildren.Count > 0) + { + totalMin -= spacing; + totalPreferred -= spacing; + } + + totalPreferred = Mathf.Max(totalMin, totalPreferred); + // 保存到m_TotalMinSize, m_TotalPreferredSize, m_TotalFlexibleSize + SetLayoutInputForAxis(totalMin, totalPreferred, totalFlexible, axis); + } + + protected void SetChildrenAlongAxis(int axis, bool isVertical) + { + float size = rectTransform.rect.size[axis]; // RectTransform大小 + bool controlSize = (axis == 0 ? m_ChildControlWidth : m_ChildControlHeight); + bool childForceExpandSize = (axis == 0 ? childForceExpandWidth : childForceExpandHeight); + float alignmentOnAxis = GetAlignmentOnAxis(axis); + + bool alongOtherAxis = (isVertical ^ (axis == 1)); + if (alongOtherAxis) + { + float innerSize = size - (axis == 0 ? padding.horizontal : padding.vertical); // 容纳子节点的空间 + for (int i = 0; i < rectChildren.Count; i++) + { + RectTransform child = rectChildren[i]; + float min, preferred, flexible; + GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible); + + float requiredSpace = Mathf.Clamp(innerSize, min, flexible > 0 ? size : preferred); + float startOffset = GetStartOffset(axis, requiredSpace); + if (controlSize) + { + SetChildAlongAxis(child, axis, startOffset, requiredSpace); + } + else + { + float offsetInCell = (requiredSpace - child.sizeDelta[axis]) * alignmentOnAxis; + SetChildAlongAxis(child, axis, startOffset + offsetInCell); + } + } + } + else + { + float pos = (axis == 0 ? padding.left : padding.top); + if (GetTotalFlexibleSize(axis) == 0 && GetTotalPreferredSize(axis) < size) + pos = GetStartOffset(axis, GetTotalPreferredSize(axis) - (axis == 0 ? padding.horizontal : padding.vertical)); + + float minMaxLerp = 0; // 塞进min之后的剩余空间/preferred-min的空间,是子节点从min向preferred扩大的依据 + if (GetTotalMinSize(axis) != GetTotalPreferredSize(axis)) + minMaxLerp = Mathf.Clamp01((size - GetTotalMinSize(axis)) / (GetTotalPreferredSize(axis) - GetTotalMinSize(axis))); + + float itemFlexibleMultiplier = 0; // 塞进preferred之后,进一步塞满剩余空间的依据,即flexible + if (size > GetTotalPreferredSize(axis)) + { + if (GetTotalFlexibleSize(axis) > 0) + itemFlexibleMultiplier = (size - GetTotalPreferredSize(axis)) / GetTotalFlexibleSize(axis); + } + + for (int i = 0; i < rectChildren.Count; i++) + { + RectTransform child = rectChildren[i]; + float min, preferred, flexible; + GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible); + + float childSize = Mathf.Lerp(min, preferred, minMaxLerp); + childSize += flexible * itemFlexibleMultiplier; + if (controlSize) + { + // 设置子节点的transform + SetChildAlongAxis(child, axis, pos, childSize); + } + else + { + float offsetInCell = (childSize - child.sizeDelta[axis]) * alignmentOnAxis; + SetChildAlongAxis(child, axis, pos + offsetInCell); + } + pos += childSize + spacing; + } + } + } + + // 返回节点的min, prefered, flexible大小 + // axis 0是x轴,1是y轴 + // controlSize 是否勾选ChildControlSize + // childForceExpand 是否勾选ChildForceExpand + private void GetChildSizes(RectTransform child, int axis, bool controlSize, bool childForceExpand, + out float min, out float preferred, out float flexible) + { + if (!controlSize) // 如果没勾ChildControlSize,那么会忽略 + { + min = child.sizeDelta[axis];//sizeDelta这里等价于size,因为动态布局系统里面anchor都是一起的 + preferred = min; + flexible = 0; + } + else + { + min = LayoutUtility.GetMinSize(child, axis); + preferred = LayoutUtility.GetPreferredSize(child, axis); + flexible = LayoutUtility.GetFlexibleSize(child, axis); + } + + if (childForceExpand) + flexible = Mathf.Max(flexible, 1); + } + +#if UNITY_EDITOR + protected override void Reset() + { + base.Reset(); + + // For new added components we want these to be set to false, + // so that the user's sizes won't be overwritten before they + // have a chance to turn these settings off. + // However, for existing components that were added before this + // feature was introduced, we want it to be on be default for + // backwardds compatibility. + // Hence their default value is on, but we set to off in reset. + m_ChildControlWidth = false; + m_ChildControlHeight = false; + } + +#endif + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalOrVerticalLayoutGroup.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalOrVerticalLayoutGroup.cs.meta new file mode 100644 index 0000000..4eaf7af --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/HorizontalOrVerticalLayoutGroup.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: e33d83c2ebb040d459eaafaae66cc5e9 +timeCreated: 1602119380 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ILayoutElement.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ILayoutElement.cs new file mode 100644 index 0000000..d0e1510 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ILayoutElement.cs @@ -0,0 +1,50 @@ +using UnityEngine; +using System.Collections; + +namespace UnityEngine.UI +{ + public interface ILayoutElement + {
+#region LayoutGroup的派生类才会实现,其他ILayoutElement比如Image,Text不会实现 + // After this method is invoked, layout horizontal input properties should return up-to-date values.
+ // Children will already have up-to-date layout horizontal inputs when this methods is called.
+ void CalculateLayoutInputHorizontal(); + // After this method is invoked, layout vertical input properties should return up-to-date values. + // Children will already have up-to-date layout vertical inputs when this methods is called. + void CalculateLayoutInputVertical();
+#endregion +
+ // Layout horizontal inputs
+ float minWidth { get; } + float preferredWidth { get; } + float flexibleWidth { get; } + // Layout vertical inputs + float minHeight { get; } + float preferredHeight { get; } + float flexibleHeight { get; } + + int layoutPriority { get; } + } + + public interface ILayoutController + { + void SetLayoutHorizontal(); + void SetLayoutVertical(); + } + + // An ILayoutGroup component should drive the RectTransforms of its children. + public interface ILayoutGroup : ILayoutController + { + } + + // An ILayoutSelfController component should drive its own RectTransform. + public interface ILayoutSelfController : ILayoutController + { + } + + // An ILayoutIgnorer component is ignored by the auto-layout system. + public interface ILayoutIgnorer + { + bool ignoreLayout { get; } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ILayoutElement.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ILayoutElement.cs.meta new file mode 100644 index 0000000..f115b8f --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/ILayoutElement.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 9437925aea643a84f8ab4da3d03c183d +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutElement.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutElement.cs new file mode 100644 index 0000000..cdee245 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutElement.cs @@ -0,0 +1,78 @@ +using UnityEngine.EventSystems; + +namespace UnityEngine.UI +{ + [AddComponentMenu("Layout/Layout Element", 140)] + [RequireComponent(typeof(RectTransform))] + [ExecuteInEditMode] + public class LayoutElement : UIBehaviour, ILayoutElement, ILayoutIgnorer + { + [SerializeField] private bool m_IgnoreLayout = false; + [SerializeField] private float m_MinWidth = -1; + [SerializeField] private float m_MinHeight = -1; + [SerializeField] private float m_PreferredWidth = -1; + [SerializeField] private float m_PreferredHeight = -1; + [SerializeField] private float m_FlexibleWidth = -1; + [SerializeField] private float m_FlexibleHeight = -1; + [SerializeField] private int m_LayoutPriority = 1; + + + public virtual bool ignoreLayout { get { return m_IgnoreLayout; } set { if (SetPropertyUtility.SetStruct(ref m_IgnoreLayout, value)) SetDirty(); } } + + public virtual void CalculateLayoutInputHorizontal() {} + public virtual void CalculateLayoutInputVertical() {} + public virtual float minWidth { get { return m_MinWidth; } set { if (SetPropertyUtility.SetStruct(ref m_MinWidth, value)) SetDirty(); } } + public virtual float minHeight { get { return m_MinHeight; } set { if (SetPropertyUtility.SetStruct(ref m_MinHeight, value)) SetDirty(); } } + public virtual float preferredWidth { get { return m_PreferredWidth; } set { if (SetPropertyUtility.SetStruct(ref m_PreferredWidth, value)) SetDirty(); } } + public virtual float preferredHeight { get { return m_PreferredHeight; } set { if (SetPropertyUtility.SetStruct(ref m_PreferredHeight, value)) SetDirty(); } } + public virtual float flexibleWidth { get { return m_FlexibleWidth; } set { if (SetPropertyUtility.SetStruct(ref m_FlexibleWidth, value)) SetDirty(); } } + public virtual float flexibleHeight { get { return m_FlexibleHeight; } set { if (SetPropertyUtility.SetStruct(ref m_FlexibleHeight, value)) SetDirty(); } } + public virtual int layoutPriority { get { return m_LayoutPriority; } set { if (SetPropertyUtility.SetStruct(ref m_LayoutPriority, value)) SetDirty(); } } + + + protected LayoutElement() + {} + + protected override void OnEnable() + { + base.OnEnable(); + SetDirty(); + } + + protected override void OnTransformParentChanged() + { + SetDirty(); + } + + protected override void OnDisable() + { + SetDirty(); + base.OnDisable(); + } + + protected override void OnDidApplyAnimationProperties() + { + SetDirty(); + } + + protected override void OnBeforeTransformParentChanged() + { + SetDirty(); + } + + protected void SetDirty() + { + if (!IsActive()) + return; + LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform); + } + + #if UNITY_EDITOR + protected override void OnValidate() + { + SetDirty(); + } + + #endif + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutElement.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutElement.cs.meta new file mode 100644 index 0000000..40cc6f9 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutElement.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 1ae73daeb8941804886a5ec8aa41dc2a +timeCreated: 1602119377 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutGroup.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutGroup.cs new file mode 100644 index 0000000..6a17e00 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutGroup.cs @@ -0,0 +1,237 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine.EventSystems; +using UnityEngine.Serialization; + +namespace UnityEngine.UI +{ + [DisallowMultipleComponent] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + public abstract class LayoutGroup : UIBehaviour, ILayoutElement, ILayoutGroup + { + [SerializeField] protected RectOffset m_Padding = new RectOffset(); + public RectOffset padding { get { return m_Padding; } set { SetProperty(ref m_Padding, value); } } + + [FormerlySerializedAs("m_Alignment")] + [SerializeField] protected TextAnchor m_ChildAlignment = TextAnchor.UpperLeft; + public TextAnchor childAlignment { get { return m_ChildAlignment; } set { SetProperty(ref m_ChildAlignment, value); } } + + [System.NonSerialized] private RectTransform m_Rect; + protected RectTransform rectTransform + { + get + { + if (m_Rect == null) + m_Rect = GetComponent<RectTransform>(); + return m_Rect; + } + } + + protected DrivenRectTransformTracker m_Tracker; + private Vector2 m_TotalMinSize = Vector2.zero; + private Vector2 m_TotalPreferredSize = Vector2.zero; + private Vector2 m_TotalFlexibleSize = Vector2.zero; + + [System.NonSerialized] private List<RectTransform> m_RectChildren = new List<RectTransform>(); + protected List<RectTransform> rectChildren { get { return m_RectChildren; } }
+
+
+ // ILayoutElement Interface
+ // 收集子节点中参与布局的节点
+ public virtual void CalculateLayoutInputHorizontal() + { + m_RectChildren.Clear(); + var toIgnoreList = ListPool<Component>.Get(); + for (int i = 0; i < rectTransform.childCount; i++) + { + var rect = rectTransform.GetChild(i) as RectTransform; + if (rect == null || !rect.gameObject.activeInHierarchy) + continue; + + rect.GetComponents(typeof(ILayoutIgnorer), toIgnoreList); + + if (toIgnoreList.Count == 0) + { + m_RectChildren.Add(rect); + continue; + } + + for (int j = 0; j < toIgnoreList.Count; j++) + { + var ignorer = (ILayoutIgnorer)toIgnoreList[j]; + if (!ignorer.ignoreLayout) + { + m_RectChildren.Add(rect); + break; + } + } + } + ListPool<Component>.Release(toIgnoreList); + m_Tracker.Clear(); + } + + // 这个方法不收集是因为在CalculateLayoutInputHorizontal收集了,在LayoutRebuilder的Rebuild()方法中有注释 + public abstract void CalculateLayoutInputVertical(); + public virtual float minWidth { get { return GetTotalMinSize(0); } } + public virtual float preferredWidth { get { return GetTotalPreferredSize(0); } } + public virtual float flexibleWidth { get { return GetTotalFlexibleSize(0); } } + public virtual float minHeight { get { return GetTotalMinSize(1); } } + public virtual float preferredHeight { get { return GetTotalPreferredSize(1); } } + public virtual float flexibleHeight { get { return GetTotalFlexibleSize(1); } } + public virtual int layoutPriority { get { return 0; } } + + // ILayoutController Interface + + public abstract void SetLayoutHorizontal(); + public abstract void SetLayoutVertical(); + + // Implementation + + protected LayoutGroup() + { + if (m_Padding == null) + m_Padding = new RectOffset(); + } + + protected override void OnEnable() + { + base.OnEnable(); + SetDirty(); + } + + protected override void OnDisable() + { + m_Tracker.Clear(); + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + base.OnDisable(); + } + + protected override void OnDidApplyAnimationProperties() + { + SetDirty(); + } + + protected float GetTotalMinSize(int axis) + { + return m_TotalMinSize[axis]; + } + + protected float GetTotalPreferredSize(int axis) + { + return m_TotalPreferredSize[axis]; + } + + protected float GetTotalFlexibleSize(int axis) + { + return m_TotalFlexibleSize[axis]; + } + + protected float GetStartOffset(int axis, float requiredSpaceWithoutPadding) + { + float requiredSpace = requiredSpaceWithoutPadding + (axis == 0 ? padding.horizontal : padding.vertical); // 算上padding后的大小 + float availableSpace = rectTransform.rect.size[axis]; // RectTransform大小 + float surplusSpace = availableSpace - requiredSpace; // 剩余空间 + float alignmentOnAxis = GetAlignmentOnAxis(axis); // 0\0.5\1 + return (axis == 0 ? padding.left : padding.top) + surplusSpace * alignmentOnAxis; + } + + protected float GetAlignmentOnAxis(int axis) + { + if (axis == 0) + return ((int)childAlignment % 3) * 0.5f; // 0, 0.5, 1 + else + return ((int)childAlignment / 3) * 0.5f; // 0, 0.5, 1 + } + + protected void SetLayoutInputForAxis(float totalMin, float totalPreferred, float totalFlexible, int axis) + { + m_TotalMinSize[axis] = totalMin; + m_TotalPreferredSize[axis] = totalPreferred; + m_TotalFlexibleSize[axis] = totalFlexible; + } + + protected void SetChildAlongAxis(RectTransform rect, int axis, float pos) + { + if (rect == null) + return; + + m_Tracker.Add(this, rect, + DrivenTransformProperties.Anchors | + (axis == 0 ? DrivenTransformProperties.AnchoredPositionX : DrivenTransformProperties.AnchoredPositionY)); + + rect.SetInsetAndSizeFromParentEdge(axis == 0 ? RectTransform.Edge.Left : RectTransform.Edge.Top, pos, rect.sizeDelta[axis]); + } + + protected void SetChildAlongAxis(RectTransform rect, int axis, float pos, float size) + { + if (rect == null) + return; + + m_Tracker.Add(this, rect, + DrivenTransformProperties.Anchors | + (axis == 0 ? + (DrivenTransformProperties.AnchoredPositionX | DrivenTransformProperties.SizeDeltaX) : + (DrivenTransformProperties.AnchoredPositionY | DrivenTransformProperties.SizeDeltaY) + )); + + rect.SetInsetAndSizeFromParentEdge(axis == 0 ? RectTransform.Edge.Left : RectTransform.Edge.Top, pos, size); + } + + private bool isRootLayoutGroup + { + get + { + Transform parent = transform.parent; + if (parent == null) + return true; + return transform.parent.GetComponent(typeof(ILayoutGroup)) == null; + } + } + + protected override void OnRectTransformDimensionsChange() + { + base.OnRectTransformDimensionsChange(); + if (isRootLayoutGroup) + SetDirty(); + } + + protected virtual void OnTransformChildrenChanged() + { + SetDirty(); + } + + protected void SetProperty<T>(ref T currentValue, T newValue) + { + if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue))) + return; + currentValue = newValue; + SetDirty(); + } + + protected void SetDirty() + { + if (!IsActive()) + return; + + if (!CanvasUpdateRegistry.IsRebuildingLayout()) + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + else + StartCoroutine(DelayedSetDirty(rectTransform)); + } + + IEnumerator DelayedSetDirty(RectTransform rectTransform) + { + yield return null; + LayoutRebuilder.MarkLayoutForRebuild(rectTransform); + } + + #if UNITY_EDITOR + protected override void OnValidate() + { + SetDirty(); + } + + #endif + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutGroup.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutGroup.cs.meta new file mode 100644 index 0000000..2c5525c --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutGroup.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 5094cec73437de8479168e16d201c2c6 +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs new file mode 100644 index 0000000..5fba1cc --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs @@ -0,0 +1,233 @@ +using System.Collections.Generic; +using UnityEngine.Events; + +namespace UnityEngine.UI +{ + public class LayoutRebuilder : ICanvasElement + { + // m_ToRebuild是一个ILayoutGroup + private RectTransform m_ToRebuild; + //There are a few of reasons we need to cache the Hash from the transform: + // - This is a ValueType (struct) and .Net calculates Hash from the Value Type fields. + // - The key of a Dictionary should have a constant Hash value. + // - It's possible for the Transform to get nulled from the Native side. + // We use this struct with the IndexedSet container, which uses a dictionary as part of it's implementation + // So this struct gets used as a key to a dictionary, so we need to guarantee a constant Hash value. + private int m_CachedHashFromTransform; + + // 一个rebuilder结构的池子 + static ObjectPool<LayoutRebuilder> s_Rebuilders = new ObjectPool<LayoutRebuilder>(null, x => x.Clear()); + + private void Initialize(RectTransform controller) + { + m_ToRebuild = controller; + m_CachedHashFromTransform = controller.GetHashCode(); + } + + private void Clear() + { + m_ToRebuild = null; + m_CachedHashFromTransform = 0; + } + + static LayoutRebuilder() + { + RectTransform.reapplyDrivenProperties += ReapplyDrivenProperties; + } + + static void ReapplyDrivenProperties(RectTransform driven) + { + MarkLayoutForRebuild(driven); + } + + public Transform transform { get { return m_ToRebuild; }} + + public bool IsDestroyed() + { + return m_ToRebuild == null; + } + + static void StripDisabledBehavioursFromList(List<Component> components) + { + components.RemoveAll(e => e is Behaviour && !((Behaviour)e).isActiveAndEnabled); + } + + // 立即重新布局一次,而不用等到帧末尾CanvasUpdateReigstry.PerformUpdate的时候 + public static void ForceRebuildLayoutImmediate(RectTransform layoutRoot) + { + var rebuilder = s_Rebuilders.Get(); + rebuilder.Initialize(layoutRoot); + rebuilder.Rebuild(CanvasUpdate.Layout); + s_Rebuilders.Release(rebuilder); + } + + public void Rebuild(CanvasUpdate executing) + { + switch (executing) + { + case CanvasUpdate.Layout: + // It's unfortunate that we'll perform the same GetComponents querys for the tree 2 times, + // but each tree have to be fully iterated before going to the next action, + // so reusing the results would entail storing results in a Dictionary or similar, + // which is probably a bigger overhead than performing GetComponents multiple times. + PerformLayoutCalculation(m_ToRebuild, e => (e as ILayoutElement).CalculateLayoutInputHorizontal()); + PerformLayoutControl(m_ToRebuild, e => (e as ILayoutController).SetLayoutHorizontal()); + PerformLayoutCalculation(m_ToRebuild, e => (e as ILayoutElement).CalculateLayoutInputVertical()); + PerformLayoutControl(m_ToRebuild, e => (e as ILayoutController).SetLayoutVertical()); + break; + } + } + + // 从上到下遍历,执行action + private void PerformLayoutControl(RectTransform rect, UnityAction<Component> action) + { + if (rect == null) + return; + + var components = ListPool<Component>.Get(); + rect.GetComponents(typeof(ILayoutController), components); + StripDisabledBehavioursFromList(components); + + // If there are no controllers on this rect we can skip this entire sub-tree + // We don't need to consider controllers on children deeper in the sub-tree either, + // since they will be their own roots. + if (components.Count > 0) + { + // + // Layout control needs to executed top down with parents being done before their children, + // because the children rely on the sizes of the parents. + + // 做两次遍历,先执行ILayoutSelfController比如ContentSizeFitter,再执行ILayoutGroup + + // First call layout controllers that may change their own RectTransform + for (int i = 0; i < components.Count; i++) + if (components[i] is ILayoutSelfController) + action(components[i]); + + // Then call the remaining, such as layout groups that change their children, taking their own RectTransform size into account. + for (int i = 0; i < components.Count; i++) + if (!(components[i] is ILayoutSelfController)) + action(components[i]); + + for (int i = 0; i < rect.childCount; i++) + PerformLayoutControl(rect.GetChild(i) as RectTransform, action); + } + + ListPool<Component>.Release(components); + } + + private void PerformLayoutCalculation(RectTransform rect, UnityAction<Component> action) + { + if (rect == null) + return; + + var components = ListPool<Component>.Get(); + rect.GetComponents(typeof(ILayoutElement), components); + StripDisabledBehavioursFromList(components); + + // If there are no controllers on this rect we can skip this entire sub-tree + // We don't need to consider controllers on children deeper in the sub-tree either, + // since they will be their own roots. + if (components.Count > 0 || rect.GetComponent(typeof(ILayoutGroup))) + { + + // 先从子节点开始,最后到父节点,这样父节点可以得到子节点的信息 + // Layout calculations needs to executed bottom up with children being done before their parents, + // because the parent calculated sizes rely on the sizes of the children. + + for (int i = 0; i < rect.childCount; i++) + PerformLayoutCalculation(rect.GetChild(i) as RectTransform, action); + + for (int i = 0; i < components.Count; i++) + action(components[i]); + } + + ListPool<Component>.Release(components); + } + + // 找到rect祖先节点中的layoutGroup并加入队列 + public static void MarkLayoutForRebuild(RectTransform rect) + { + if (rect == null) + return; + + var comps = ListPool<Component>.Get(); + RectTransform layoutRoot = rect; // 祖先节点中的layoutGroup + while (true) + { + var parent = layoutRoot.parent as RectTransform; + if (!ValidLayoutGroup(parent, comps)) + break; + layoutRoot = parent; + } + + // We know the layout root is valid if it's not the same as the rect, + // since we checked that above. But if they're the same we still need to check. + if (layoutRoot == rect && !ValidController(layoutRoot, comps)) + { + ListPool<Component>.Release(comps); + return; + } + + MarkLayoutRootForRebuild(layoutRoot); + ListPool<Component>.Release(comps); + } + + private static bool ValidLayoutGroup(RectTransform parent, List<Component> comps) + { + if (parent == null) + return false; + + parent.GetComponents(typeof(ILayoutGroup), comps); + StripDisabledBehavioursFromList(comps); + var validCount = comps.Count > 0; + return validCount; + } + + private static bool ValidController(RectTransform layoutRoot, List<Component> comps) + { + if (layoutRoot == null) + return false; + layoutRoot.GetComponents(typeof(ILayoutController), comps); + StripDisabledBehavioursFromList(comps); + var valid = comps.Count > 0; + return valid; + } + + private static void MarkLayoutRootForRebuild(RectTransform controller) + { + if (controller == null) + return; + + var rebuilder = s_Rebuilders.Get(); + rebuilder.Initialize(controller); + // 注册到CanvasUpdateRegistry里 + if (!CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild(rebuilder)) + s_Rebuilders.Release(rebuilder); + } + + public void LayoutComplete() + { + // 放回池子里 + s_Rebuilders.Release(this); + } + + public void GraphicUpdateComplete() + {} + + public override int GetHashCode() + { + return m_CachedHashFromTransform; + } + + public override bool Equals(object obj) + { + return obj.GetHashCode() == GetHashCode(); + } + + public override string ToString() + { + return "(Layout Rebuilder for) " + m_ToRebuild; + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs.meta new file mode 100644 index 0000000..ad21a40 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 297af0346d9a6b24e88b4dbdfae053e0 +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutUtility.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutUtility.cs new file mode 100644 index 0000000..c58abd5 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutUtility.cs @@ -0,0 +1,112 @@ +using UnityEngine; +using System.Collections.Generic; +using UnityEngine.Events; + +namespace UnityEngine.UI +{ + public static class LayoutUtility + { + public static float GetMinSize(RectTransform rect, int axis) + { + if (axis == 0) + return GetMinWidth(rect); + return GetMinHeight(rect); + } + + public static float GetPreferredSize(RectTransform rect, int axis) + { + if (axis == 0) + return GetPreferredWidth(rect); + return GetPreferredHeight(rect); + } + + public static float GetFlexibleSize(RectTransform rect, int axis) + { + if (axis == 0) + return GetFlexibleWidth(rect); + return GetFlexibleHeight(rect); + } + + public static float GetMinWidth(RectTransform rect) + { + return GetLayoutProperty(rect, e => e.minWidth, 0); + } + + public static float GetPreferredWidth(RectTransform rect) + { + return Mathf.Max(GetLayoutProperty(rect, e => e.minWidth, 0), GetLayoutProperty(rect, e => e.preferredWidth, 0)); + } + + public static float GetFlexibleWidth(RectTransform rect) + { + return GetLayoutProperty(rect, e => e.flexibleWidth, 0); + } + + public static float GetMinHeight(RectTransform rect) + { + return GetLayoutProperty(rect, e => e.minHeight, 0); + } + + public static float GetPreferredHeight(RectTransform rect) + { + return Mathf.Max(GetLayoutProperty(rect, e => e.minHeight, 0), GetLayoutProperty(rect, e => e.preferredHeight, 0)); + } + + public static float GetFlexibleHeight(RectTransform rect) + { + return GetLayoutProperty(rect, e => e.flexibleHeight, 0); + } + + public static float GetLayoutProperty(RectTransform rect, System.Func<ILayoutElement, float> property, float defaultValue) + { + ILayoutElement dummy; + return GetLayoutProperty(rect, property, defaultValue, out dummy); + } + + public static float GetLayoutProperty(RectTransform rect, System.Func<ILayoutElement, float> property, float defaultValue, out ILayoutElement source) + { + source = null; + if (rect == null) + return 0; + float min = defaultValue; + int maxPriority = System.Int32.MinValue; + var components = ListPool<Component>.Get(); + rect.GetComponents(typeof(ILayoutElement), components); + + for (int i = 0; i < components.Count; i++) + { + var layoutComp = components[i] as ILayoutElement; // + if (layoutComp is Behaviour && !((Behaviour)layoutComp).isActiveAndEnabled) + continue; + + int priority = layoutComp.layoutPriority; + // If this layout components has lower priority than a previously used, ignore it. + if (priority < maxPriority) + continue; + float prop = property(layoutComp); + // If this layout property is set to a negative value, it means it should be ignored. + if (prop < 0) + continue; + + // If this layout component has higher priority than all previous ones, + // overwrite with this one's value. + if (priority > maxPriority) + { + min = prop; + maxPriority = priority; + source = layoutComp; + } + // If the layout component has the same priority as a previously used, + // use the largest of the values with the same priority. + else if (prop > min) + { + min = prop; + source = layoutComp; + } + } + + ListPool<Component>.Release(components); + return min; + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutUtility.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutUtility.cs.meta new file mode 100644 index 0000000..e0d1878 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutUtility.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: d180430718b19fe4786c73f6e7c798fa +timeCreated: 1602119380 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/VerticalLayoutGroup.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/VerticalLayoutGroup.cs new file mode 100644 index 0000000..e24b842 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/VerticalLayoutGroup.cs @@ -0,0 +1,30 @@ +namespace UnityEngine.UI +{ + [AddComponentMenu("Layout/Vertical Layout Group", 151)] + public class VerticalLayoutGroup : HorizontalOrVerticalLayoutGroup + { + protected VerticalLayoutGroup() + {} + + public override void CalculateLayoutInputHorizontal() + { + base.CalculateLayoutInputHorizontal(); + CalcAlongAxis(0, true); + } + + public override void CalculateLayoutInputVertical() + { + CalcAlongAxis(1, true); + } + + public override void SetLayoutHorizontal() + { + SetChildrenAlongAxis(0, true); + } + + public override void SetLayoutVertical() + { + SetChildrenAlongAxis(1, true); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/VerticalLayoutGroup.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/VerticalLayoutGroup.cs.meta new file mode 100644 index 0000000..9b945bf --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/VerticalLayoutGroup.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: f050f1af13388a145b34f3d0b59c8955 +timeCreated: 1602119380 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask.meta new file mode 100644 index 0000000..ad6af48 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3bc3203fe03d7b049ae99a951f541714 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMask.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMask.cs new file mode 100644 index 0000000..ffe7aed --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMask.cs @@ -0,0 +1,11 @@ +using System; + +namespace UnityEngine.UI +{ + [Obsolete("Not supported anymore.", true)] + public interface IMask + { + bool Enabled(); + RectTransform rectTransform { get; } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMask.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMask.cs.meta new file mode 100644 index 0000000..2eab58a --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d54c702726ac4fa4daf18719b41de69f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMaskable.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMaskable.cs new file mode 100644 index 0000000..8f8a28c --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMaskable.cs @@ -0,0 +1,9 @@ +using System; + +namespace UnityEngine.UI +{ + public interface IMaskable + { + void RecalculateMasking(); + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMaskable.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMaskable.cs.meta new file mode 100644 index 0000000..298aac8 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/IMaskable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5775a30dcbc6e484c8795bb7e18ada44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/Mask.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/Mask.cs new file mode 100644 index 0000000..c67ec42 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/Mask.cs @@ -0,0 +1,203 @@ +using System; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.Rendering; +using UnityEngine.Serialization; + +namespace UnityEngine.UI +{ + [AddComponentMenu("UI/Mask", 13)] + [ExecuteInEditMode] + [RequireComponent(typeof(RectTransform))] + [DisallowMultipleComponent] + public class Mask : UIBehaviour, ICanvasRaycastFilter, IMaterialModifier + { + [NonSerialized] + private RectTransform m_RectTransform; + public RectTransform rectTransform + { + get { return m_RectTransform ?? (m_RectTransform = GetComponent<RectTransform>()); } + } + + [SerializeField] + [FormerlySerializedAs("m_ShowGraphic")] + private bool m_ShowMaskGraphic = true; + public bool showMaskGraphic + { + get { return m_ShowMaskGraphic; } + set + { + if (m_ShowMaskGraphic == value) + return; + + m_ShowMaskGraphic = value; + if (graphic != null) + graphic.SetMaterialDirty(); + } + } + + [NonSerialized] private Graphic m_Graphic; + public Graphic graphic + { + get { return m_Graphic ?? (m_Graphic = GetComponent<Graphic>()); } + }
+
+ [NonSerialized] private Material m_MaskMaterial;
+
+ [NonSerialized] private Material m_UnmaskMaterial; + + protected Mask() + {} + + public virtual bool MaskEnabled() { return IsActive() && graphic != null; } + + [Obsolete("Not used anymore.")] + public virtual void OnSiblingGraphicEnabledDisabled() {} + + protected override void OnEnable() + { + base.OnEnable(); + if (graphic != null) + { + graphic.canvasRenderer.hasPopInstruction = true; + graphic.SetMaterialDirty(); + } + + MaskUtilities.NotifyStencilStateChanged(this); + } + + protected override void OnDisable() + { + // we call base OnDisable first here + // as we need to have the IsActive return the + // correct value when we notify the children + // that the mask state has changed. + base.OnDisable(); + if (graphic != null) + { + graphic.SetMaterialDirty(); + graphic.canvasRenderer.hasPopInstruction = false; + graphic.canvasRenderer.popMaterialCount = 0; + } + + StencilMaterial.Remove(m_MaskMaterial); + m_MaskMaterial = null; + StencilMaterial.Remove(m_UnmaskMaterial); + m_UnmaskMaterial = null; + + MaskUtilities.NotifyStencilStateChanged(this); + } + +#if UNITY_EDITOR + protected override void OnValidate() + { + base.OnValidate(); + + if (!IsActive()) + return; + + if (graphic != null) + graphic.SetMaterialDirty(); + + MaskUtilities.NotifyStencilStateChanged(this); + } + +#endif + + public virtual bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera) + { + if (!isActiveAndEnabled) + return true; + + return RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera); + } + + /// Stencil calculation time! + public virtual Material GetModifiedMaterial(Material baseMaterial) + { + if (!MaskEnabled()) + return baseMaterial; + + var rootSortCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); + var stencilDepth = MaskUtilities.GetStencilDepth(transform, rootSortCanvas); + if (stencilDepth >= 8) // 不支持8层及以上的mask, 0-7支持,这是因为stencilRef和stencilMask只有一个字节 + { + Debug.LogError("Attempting to use a stencil mask with depth > 8", gameObject); + return baseMaterial; + } + + int desiredStencilBit = 1 << stencilDepth; + + // if we are at the first level... + // we want to destroy what is there + if (desiredStencilBit == 1) // 最上层的mask + {
+ // Ref = 1
+ // Op = Replace
+ // Func = Always
+ // ReadMask = 255
+ // WriteMask = 255
+ var maskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Replace, CompareFunction.Always, m_ShowMaskGraphic ? ColorWriteMask.All : 0); + StencilMaterial.Remove(m_MaskMaterial); + m_MaskMaterial = maskMaterial;
+
+ // Ref = 1
+ // Op = Zero
+ // Func = Always
+ // ReadMask = 255
+ // WriteMask = 255
+ var unmaskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Zero, CompareFunction.Always, 0); + StencilMaterial.Remove(m_UnmaskMaterial); + m_UnmaskMaterial = unmaskMaterial; + graphic.canvasRenderer.popMaterialCount = 1; + graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0); + + return m_MaskMaterial; + } + + // Ref = desiredStencilBit | (desiredStencilBit - 1) + // Op = Replace + // Func = Equal + // ReadMask = desiredStencilBit - 1 + // WriteMask = desiredStencilBit | (desiredStencilBit - 1) + + //otherwise we need to be a bit smarter and set some read / write masks + var maskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit | (desiredStencilBit - 1), StencilOp.Replace, CompareFunction.Equal, m_ShowMaskGraphic ? ColorWriteMask.All : 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1)); + StencilMaterial.Remove(m_MaskMaterial); + m_MaskMaterial = maskMaterial2; + + // Ref = desiredStencilBit - 1 + // Op = Replace + // Func = Equal + // ReadMask = desiredStencilBit - 1 + // WriteMask = desiredStencilBit | (desiredStencilBit - 1) + graphic.canvasRenderer.hasPopInstruction = true; + var unmaskMaterial2 = StencilMaterial.Add(baseMaterial, desiredStencilBit - 1, StencilOp.Replace, CompareFunction.Equal, 0, desiredStencilBit - 1, desiredStencilBit | (desiredStencilBit - 1)); + StencilMaterial.Remove(m_UnmaskMaterial); + m_UnmaskMaterial = unmaskMaterial2; + graphic.canvasRenderer.popMaterialCount = 1; + graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0); + + return m_MaskMaterial; + }
+
+
+ #region UGUI_DEBUG +
+ Vector3[] cornors = new Vector3[4];
+ + void OnDrawGizmos()
+ {
+ Rect rect = rectTransform.rect;
+ Gizmos.color = Color.yellow;
+ rectTransform.GetWorldCorners(cornors);
+ for (int i = 0; i < cornors.Length; ++i)
+ {
+ Gizmos.DrawLine(cornors[i], cornors[(i + 1) % cornors.Length]);
+ }
+ } + + #endregion + + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/Mask.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/Mask.cs.meta new file mode 100644 index 0000000..50caa3d --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/Mask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bfc675de41855c44aaedb7af695ba899 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/MaskUtilities.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/MaskUtilities.cs new file mode 100644 index 0000000..6abce5a --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/MaskUtilities.cs @@ -0,0 +1,187 @@ +using System.Collections.Generic; +using UnityEngine.EventSystems; + +namespace UnityEngine.UI +{ + public class MaskUtilities + { + public static void Notify2DMaskStateChanged(Component mask) + { + var components = ListPool<Component>.Get(); + mask.GetComponentsInChildren(components); + for (var i = 0; i < components.Count; i++) + { + if (components[i] == null || components[i].gameObject == mask.gameObject) + continue; + + var toNotify = components[i] as IClippable; + if (toNotify != null) + toNotify.RecalculateClipping(); + } + ListPool<Component>.Release(components); + } + + public static void NotifyStencilStateChanged(Component mask) + { + var components = ListPool<Component>.Get(); + mask.GetComponentsInChildren(components); + for (var i = 0; i < components.Count; i++) + { + if (components[i] == null || components[i].gameObject == mask.gameObject) + continue; + + var toNotify = components[i] as IMaskable; + if (toNotify != null) + toNotify.RecalculateMasking(); + } + ListPool<Component>.Release(components); + } + + //从下往上找到第一个OverrideSorting的canvas或者root(即null) + public static Transform FindRootSortOverrideCanvas(Transform start) + { + var canvasList = ListPool<Canvas>.Get(); + start.GetComponentsInParent(false, canvasList); + Canvas canvas = null; + + for (int i = 0; i < canvasList.Count; ++i) + { + canvas = canvasList[i]; + + // We found the canvas we want to use break + if (canvas.overrideSorting) + break; + } + ListPool<Canvas>.Release(canvasList); + + return canvas != null ? canvas.transform : null; + } + + // 返回Mask在canvas下(canvas下所有的masks下)的深度 + public static int GetStencilDepth(Transform transform, Transform stopAfter) + { + var depth = 0; + if (transform == stopAfter) + return depth; + + var t = transform.parent; + var components = ListPool<Mask>.Get(); + while (t != null) + { + t.GetComponents<Mask>(components); + for (var i = 0; i < components.Count; ++i) + { + if (components[i] != null && components[i].MaskEnabled() && components[i].graphic.IsActive()) + { + ++depth; + break; + } + } + + if (t == stopAfter) + break; + + t = t.parent; + } + ListPool<Mask>.Release(components); + return depth; + } + + // father是child的祖先节点或者father == child + public static bool IsDescendantOrSelf(Transform father, Transform child) + { + if (father == null || child == null) + return false; + + if (father == child) + return true; + + while (child.parent != null) + { + if (child.parent == father) + return true; + + child = child.parent; + } + + return false; + } + + public static RectMask2D GetRectMaskForClippable(IClippable clippable) + { + List<RectMask2D> rectMaskComponents = ListPool<RectMask2D>.Get(); + List<Canvas> canvasComponents = ListPool<Canvas>.Get(); + RectMask2D componentToReturn = null; + + clippable.rectTransform.GetComponentsInParent(false, rectMaskComponents); + + if (rectMaskComponents.Count > 0) + { + for (int rmi = 0; rmi < rectMaskComponents.Count; rmi++) + { + componentToReturn = rectMaskComponents[rmi]; + if (componentToReturn.gameObject == clippable.gameObject) + { + componentToReturn = null; + continue; + } + if (!componentToReturn.isActiveAndEnabled) + { + componentToReturn = null; + continue; + } + clippable.rectTransform.GetComponentsInParent(false, canvasComponents); + for (int i = canvasComponents.Count - 1; i >= 0; i--) + { + if (!IsDescendantOrSelf(canvasComponents[i].transform, componentToReturn.transform) && canvasComponents[i].overrideSorting) + { + componentToReturn = null; + break; + } + } + return componentToReturn; + } + } + + ListPool<RectMask2D>.Release(rectMaskComponents); + ListPool<Canvas>.Release(canvasComponents); + + return componentToReturn; + } + + public static void GetRectMasksForClip(RectMask2D clipper, List<RectMask2D> masks) + { + masks.Clear(); + + List<Canvas> canvasComponents = ListPool<Canvas>.Get(); + List<RectMask2D> rectMaskComponents = ListPool<RectMask2D>.Get(); + clipper.transform.GetComponentsInParent(false, rectMaskComponents); + + if (rectMaskComponents.Count > 0) + { + clipper.transform.GetComponentsInParent(false, canvasComponents); + for (int i = rectMaskComponents.Count - 1; i >= 0; i--) + { + if (!rectMaskComponents[i].IsActive()) + continue; + bool shouldAdd = true; + for (int j = canvasComponents.Count - 1; j >= 0; j--) + { + // 如果rectMask2D是canvas的祖先节点且这个canvas勾选了OverrideSorting,那么这个rectMask2D就会失效 + if (!IsDescendantOrSelf(canvasComponents[j].transform, rectMaskComponents[i].transform) + && canvasComponents[j].overrideSorting) + { + shouldAdd = false; + break; + } + } + if (shouldAdd) + masks.Add(rectMaskComponents[i]); + } + } + + ListPool<RectMask2D>.Release(rectMaskComponents); + ListPool<Canvas>.Release(canvasComponents); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/MaskUtilities.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/MaskUtilities.cs.meta new file mode 100644 index 0000000..5e7829c --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/MaskUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 469f0aa53de5d4c4fb69e83419090a01 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/RectMask2D.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/RectMask2D.cs new file mode 100644 index 0000000..d973b34 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/RectMask2D.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using UnityEngine.EventSystems; + +namespace UnityEngine.UI +{ + [AddComponentMenu("UI/Rect Mask 2D", 13)] + [ExecuteInEditMode] + [DisallowMultipleComponent] + [RequireComponent(typeof(RectTransform))] + public class RectMask2D : UIBehaviour, IClipper, ICanvasRaycastFilter + { + [NonSerialized] + private readonly RectangularVertexClipper m_VertexClipper = new RectangularVertexClipper(); + + [NonSerialized] + private RectTransform m_RectTransform; + + // 可裁剪的Graphics + [NonSerialized] + private HashSet<IClippable> m_ClipTargets = new HashSet<IClippable>(); + + [NonSerialized] + private bool m_ShouldRecalculateClipRects; + + // 父节点的所有RectMask2D,用来算交集 + [NonSerialized] + private List<RectMask2D> m_Clippers = new List<RectMask2D>(); + + [NonSerialized] + private Rect m_LastClipRectCanvasSpace; + [NonSerialized] + private bool m_LastValidClipRect; + [NonSerialized] + private bool m_ForceClip; + + public Rect canvasRect + { + get + { + Canvas canvas = null; + var list = ListPool<Canvas>.Get(); + gameObject.GetComponentsInParent(false, list); + if (list.Count > 0) + canvas = list[list.Count - 1]; + ListPool<Canvas>.Release(list); + + return m_VertexClipper.GetCanvasRect(rectTransform, canvas); + } + } + + public RectTransform rectTransform + { + get { return m_RectTransform ?? (m_RectTransform = GetComponent<RectTransform>()); } + } + + protected RectMask2D() + {} + + protected override void OnEnable() + { + base.OnEnable(); + m_ShouldRecalculateClipRects = true; + ClipperRegistry.Register(this); + MaskUtilities.Notify2DMaskStateChanged(this); + } + + protected override void OnDisable() + { + // we call base OnDisable first here + // as we need to have the IsActive return the + // correct value when we notify the children + // that the mask state has changed. + base.OnDisable(); + m_ClipTargets.Clear(); + m_Clippers.Clear(); + ClipperRegistry.Unregister(this); + MaskUtilities.Notify2DMaskStateChanged(this); + } + +#if UNITY_EDITOR + protected override void OnValidate() + { + base.OnValidate(); + m_ShouldRecalculateClipRects = true; + + if (!IsActive()) + return; + + MaskUtilities.Notify2DMaskStateChanged(this); + } + +#endif + + public virtual bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera) + { + if (!isActiveAndEnabled) + return true; + + return RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera); + } + + public virtual void PerformClipping() + { + //TODO See if an IsActive() test would work well here or whether it might cause unexpected side effects (re case 776771) + + // if the parents are changed + // or something similar we + // do a recalculate here + if (m_ShouldRecalculateClipRects) + { + MaskUtilities.GetRectMasksForClip(this, m_Clippers); + m_ShouldRecalculateClipRects = false; + } + + // 裁剪 + + // get the compound rects from + // the clippers that are valid + bool validRect = true; + Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect); + bool clipRectChanged = clipRect != m_LastClipRectCanvasSpace; + if (clipRectChanged || m_ForceClip) + { + foreach (IClippable clipTarget in m_ClipTargets) + clipTarget.SetClipRect(clipRect, validRect); + + m_LastClipRectCanvasSpace = clipRect; + m_LastValidClipRect = validRect; + } + + // 剔除 + + foreach (IClippable clipTarget in m_ClipTargets) + { + var maskable = clipTarget as MaskableGraphic; + if (maskable != null && !maskable.canvasRenderer.hasMoved && !clipRectChanged) + continue; + + clipTarget.Cull(m_LastClipRectCanvasSpace, m_LastValidClipRect); + } + } + + public void AddClippable(IClippable clippable) + { + if (clippable == null) + return; + m_ShouldRecalculateClipRects = true; + if (!m_ClipTargets.Contains(clippable)) + m_ClipTargets.Add(clippable); + + m_ForceClip = true; + } + + public void RemoveClippable(IClippable clippable) + { + if (clippable == null) + return; + + m_ShouldRecalculateClipRects = true; + clippable.SetClipRect(new Rect(), false); + m_ClipTargets.Remove(clippable); + + m_ForceClip = true; + } + + protected override void OnTransformParentChanged() + { + base.OnTransformParentChanged(); + m_ShouldRecalculateClipRects = true; + } + + protected override void OnCanvasHierarchyChanged() + { + base.OnCanvasHierarchyChanged(); + m_ShouldRecalculateClipRects = true; + }
+
+
+ #region UGUI_DEBUG +
+ Vector3[] cornors = new Vector3[4];
+ + void OnDrawGizmos()
+ {
+ Rect rect = rectTransform.rect;
+ Gizmos.color = Color.red;
+ rectTransform.GetWorldCorners(cornors);
+ for (int i = 0; i < cornors.Length; ++i)
+ {
+ Gizmos.DrawLine(cornors[i], cornors[(i + 1) % cornors.Length]);
+ }
+ } + + #endregion +
+ } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/RectMask2D.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/RectMask2D.cs.meta new file mode 100644 index 0000000..3f31967 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/RectMask2D.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c06d6b758f7cbc148afa25153c439510 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/StencilMaterial.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/StencilMaterial.cs new file mode 100644 index 0000000..c12f209 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/StencilMaterial.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Rendering; + +namespace UnityEngine.UI +{ + /// <summary> + /// Dynamic material class makes it possible to create custom materials on the fly on a per-Graphic basis, + /// and still have them get cleaned up correctly. + /// </summary> + public static class StencilMaterial + { + private class MatEntry + { + public Material baseMat; + public Material customMat; + public int count; + + public int stencilId; + public StencilOp operation = StencilOp.Keep; + public CompareFunction compareFunction = CompareFunction.Always; + public int readMask; + public int writeMask; + public bool useAlphaClip; + public ColorWriteMask colorMask; + } + + private static List<MatEntry> m_List = new List<MatEntry>(); + + [Obsolete("Use Material.Add instead.", true)] + public static Material Add(Material baseMat, int stencilID) { return null; } + + /// <summary> + /// Add a new material using the specified base and stencil ID. + /// </summary> + public static Material Add(Material baseMat, int stencilID, StencilOp operation, CompareFunction compareFunction, ColorWriteMask colorWriteMask) + { + return Add(baseMat, stencilID, operation, compareFunction, colorWriteMask, 255, 255); + } + + /// <summary> + /// Add a new material using the specified base and stencil ID. + /// </summary> + public static Material Add(Material baseMat, int stencilID, StencilOp operation, CompareFunction compareFunction, ColorWriteMask colorWriteMask, int readMask, int writeMask) + { + if ((stencilID <= 0 && colorWriteMask == ColorWriteMask.All) || baseMat == null) + return baseMat; + + if (!baseMat.HasProperty("_Stencil")) + { + Debug.LogWarning("Material " + baseMat.name + " doesn't have _Stencil property", baseMat); + return baseMat; + } + if (!baseMat.HasProperty("_StencilOp")) + { + Debug.LogWarning("Material " + baseMat.name + " doesn't have _StencilOp property", baseMat); + return baseMat; + } + if (!baseMat.HasProperty("_StencilComp")) + { + Debug.LogWarning("Material " + baseMat.name + " doesn't have _StencilComp property", baseMat); + return baseMat; + } + if (!baseMat.HasProperty("_StencilReadMask")) + { + Debug.LogWarning("Material " + baseMat.name + " doesn't have _StencilReadMask property", baseMat); + return baseMat; + } + if (!baseMat.HasProperty("_StencilWriteMask")) + { + Debug.LogWarning("Material " + baseMat.name + " doesn't have _StencilWriteMask property", baseMat); + return baseMat; + } + if (!baseMat.HasProperty("_ColorMask")) + { + Debug.LogWarning("Material " + baseMat.name + " doesn't have _ColorMask property", baseMat); + return baseMat; + } + + for (int i = 0; i < m_List.Count; ++i) + { + MatEntry ent = m_List[i]; + + if (ent.baseMat == baseMat + && ent.stencilId == stencilID + && ent.operation == operation + && ent.compareFunction == compareFunction + && ent.readMask == readMask + && ent.writeMask == writeMask + && ent.colorMask == colorWriteMask) + { + ++ent.count; + return ent.customMat; + } + } + + var newEnt = new MatEntry(); + newEnt.count = 1; + newEnt.baseMat = baseMat; + newEnt.customMat = new Material(baseMat); + newEnt.customMat.hideFlags = HideFlags.HideAndDontSave; + newEnt.stencilId = stencilID; + newEnt.operation = operation; + newEnt.compareFunction = compareFunction; + newEnt.readMask = readMask; + newEnt.writeMask = writeMask; + newEnt.colorMask = colorWriteMask; + newEnt.useAlphaClip = operation != StencilOp.Keep && writeMask > 0; + + newEnt.customMat.name = string.Format("Stencil Id:{0}, Op:{1}, Comp:{2}, WriteMask:{3}, ReadMask:{4}, ColorMask:{5} AlphaClip:{6} ({7})", stencilID, operation, compareFunction, writeMask, readMask, colorWriteMask, newEnt.useAlphaClip, baseMat.name); + + newEnt.customMat.SetInt("_Stencil", stencilID); + newEnt.customMat.SetInt("_StencilOp", (int)operation); + newEnt.customMat.SetInt("_StencilComp", (int)compareFunction); + newEnt.customMat.SetInt("_StencilReadMask", readMask); + newEnt.customMat.SetInt("_StencilWriteMask", writeMask); + newEnt.customMat.SetInt("_ColorMask", (int)colorWriteMask); + + // left for backwards compatability + if (newEnt.customMat.HasProperty("_UseAlphaClip")) + newEnt.customMat.SetInt("_UseAlphaClip", newEnt.useAlphaClip ? 1 : 0); + + if (newEnt.useAlphaClip) + newEnt.customMat.EnableKeyword("UNITY_UI_ALPHACLIP"); + else + newEnt.customMat.DisableKeyword("UNITY_UI_ALPHACLIP"); + + m_List.Add(newEnt); + return newEnt.customMat; + } + + /// <summary> + /// Remove an existing material, automatically cleaning it up if it's no longer in use. + /// </summary> + public static void Remove(Material customMat) + { + if (customMat == null) + return; + + for (int i = 0; i < m_List.Count; ++i) + { + MatEntry ent = m_List[i]; + + if (ent.customMat != customMat) + continue; + + if (--ent.count == 0) + { + Misc.DestroyImmediate(ent.customMat); + ent.baseMat = null; + m_List.RemoveAt(i); + } + return; + } + } + + public static void ClearAll() + { + for (int i = 0; i < m_List.Count; ++i) + { + MatEntry ent = m_List[i]; + + Misc.DestroyImmediate(ent.customMat); + ent.baseMat = null; + } + m_List.Clear(); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/StencilMaterial.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/StencilMaterial.cs.meta new file mode 100644 index 0000000..3fd6a69 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Mask/StencilMaterial.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d445d28188ffd745990df17f1ce8914 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers.meta new file mode 100644 index 0000000..73eacd4 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 50b4029219e854d4c9407fad9b48266b +folderAsset: yes +timeCreated: 1602119280 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers/IMaterialModifier.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers/IMaterialModifier.cs new file mode 100644 index 0000000..56e4777 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers/IMaterialModifier.cs @@ -0,0 +1,7 @@ +namespace UnityEngine.UI +{ + public interface IMaterialModifier + { + Material GetModifiedMaterial(Material baseMaterial); + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers/IMaterialModifier.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers/IMaterialModifier.cs.meta new file mode 100644 index 0000000..a234594 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/MaterialModifiers/IMaterialModifier.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: fff5a5576fa3b2944a873226445ef7ef +timeCreated: 1602119380 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Misc.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Misc.cs new file mode 100644 index 0000000..66e2892 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Misc.cs @@ -0,0 +1,44 @@ +namespace UnityEngine.UI +{ + /// <summary> + /// Helper class containing generic functions used throughout the UI library. + /// </summary> + + internal static class Misc + { + /// <summary> + /// Destroy the specified object, immediately if in edit mode. + /// </summary> + + static public void Destroy(UnityEngine.Object obj) + { + if (obj != null) + { + if (Application.isPlaying) + { + if (obj is GameObject) + { + GameObject go = obj as GameObject; + go.transform.parent = null; + } + + Object.Destroy(obj); + } + else Object.DestroyImmediate(obj); + } + } + + /// <summary> + /// Destroy the specified object immediately, unless not in the editor, in which case the regular Destroy is used instead. + /// </summary> + + static public void DestroyImmediate(Object obj) + { + if (obj != null) + { + if (Application.isEditor) Object.DestroyImmediate(obj); + else Object.Destroy(obj); + } + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Misc.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Misc.cs.meta new file mode 100644 index 0000000..51bdc37 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Misc.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 78074a60b29244441988db79bcd84a23 +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections.meta new file mode 100644 index 0000000..af9c70d --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 0402250d11c7a2042bdcab33f69be1a6 +folderAsset: yes +timeCreated: 1602119280 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections/IndexedSet.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections/IndexedSet.cs new file mode 100644 index 0000000..0f37983 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections/IndexedSet.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace UnityEngine.UI.Collections +{ + internal class IndexedSet<T> : IList<T> + { + //This is a container that gives: + // - Unique items + // - Fast random removal + // - Fast unique inclusion to the end + // - Sequential access + //Downsides: + // - Uses more memory + // - Ordering is not persistent + // - Not Serialization Friendly. + + //We use a Dictionary to speed up list lookup, this makes it cheaper to guarantee no duplicates (set) + //When removing we move the last item to the removed item position, this way we only need to update the index cache of a single item. (fast removal) + //Order of the elements is not guaranteed. A removal will change the order of the items. + + readonly List<T> m_List = new List<T>(); + Dictionary<T, int> m_Dictionary = new Dictionary<T, int>(); + + public void Add(T item) + { + m_List.Add(item); + m_Dictionary.Add(item, m_List.Count - 1); + } + + public bool AddUnique(T item) + { + if (m_Dictionary.ContainsKey(item)) + return false; + + m_List.Add(item); + m_Dictionary.Add(item, m_List.Count - 1); + + return true; + } + + public bool Remove(T item) + { + int index = -1; + if (!m_Dictionary.TryGetValue(item, out index)) + return false; + + RemoveAt(index); + return true; + } + + public IEnumerator<T> GetEnumerator() + { + throw new System.NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Clear() + { + m_List.Clear(); + m_Dictionary.Clear(); + } + + public bool Contains(T item) + { + return m_Dictionary.ContainsKey(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + m_List.CopyTo(array, arrayIndex); + } + + public int Count { get { return m_List.Count; } } + public bool IsReadOnly { get { return false; } } + public int IndexOf(T item) + { + int index = -1; + m_Dictionary.TryGetValue(item, out index); + return index; + } + + public void Insert(int index, T item) + { + //We could support this, but the semantics would be weird. Order is not guaranteed.. + throw new NotSupportedException("Random Insertion is semantically invalid, since this structure does not guarantee ordering."); + } + + public void RemoveAt(int index) + { + T item = m_List[index]; + m_Dictionary.Remove(item); + if (index == m_List.Count - 1) + m_List.RemoveAt(index); + else + { + int replaceItemIndex = m_List.Count - 1; + T replaceItem = m_List[replaceItemIndex]; + m_List[index] = replaceItem; + m_Dictionary[replaceItem] = index; + m_List.RemoveAt(replaceItemIndex); + } + } + + public T this[int index] + { + get { return m_List[index]; } + set + { + T item = m_List[index]; + m_Dictionary.Remove(item); + m_List[index] = value; + m_Dictionary.Add(item, index); // 这里有问题,应该是value + } + } + + public void RemoveAll(Predicate<T> match) + { + //I guess this could be optmized by instead of removing the items from the list immediatly, + //We move them to the end, and then remove all in one go. + //But I don't think this is going to be the bottleneck, so leaving as is for now. + int i = 0; + while (i < m_List.Count) + { + T item = m_List[i]; + if (match(item)) + Remove(item); + else + i++; + } + } + + //Sorts the internal list, this makes the exposed index accessor sorted as well. + //But note that any insertion or deletion, can unorder the collection again. + public void Sort(Comparison<T> sortLayoutFunction) + { + //There might be better ways to sort and keep the dictionary index up to date. + m_List.Sort(sortLayoutFunction); + //Rebuild the dictionary index. + for (int i = 0; i < m_List.Count; ++i) + { + T item = m_List[i]; + m_Dictionary[item] = i; + } + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections/IndexedSet.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections/IndexedSet.cs.meta new file mode 100644 index 0000000..942f3d7 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/SpecializedCollections/IndexedSet.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: cfa32e72467bc124294369b8b0b2045e +timeCreated: 1602119380 +licenseType: Free +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.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls.meta new file mode 100644 index 0000000..4dfd750 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/UIControls.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5a1494dd81f8d794282718b40b1ea46e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: 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: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility.meta new file mode 100644 index 0000000..117221b --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 1cb75217bdcbf9f429e9f763df2b27d4 +folderAsset: yes +timeCreated: 1602119280 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ListPool.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ListPool.cs new file mode 100644 index 0000000..1a7cc5e --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ListPool.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace UnityEngine.UI +{ + internal static class ListPool<T> + { + // Object pool to avoid allocations. + private static readonly ObjectPool<List<T>> s_ListPool = new ObjectPool<List<T>>(null, l => l.Clear()); + + public static List<T> Get() + { + return s_ListPool.Get(); + } + + public static void Release(List<T> toRelease) + { + s_ListPool.Release(toRelease); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ListPool.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ListPool.cs.meta new file mode 100644 index 0000000..e54f9f7 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ListPool.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 12520643c37454344b5a1dd60cc2bdbd +timeCreated: 1602119377 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ObjectPool.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ObjectPool.cs new file mode 100644 index 0000000..b506d80 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ObjectPool.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using UnityEngine.Events; + +namespace UnityEngine.UI +{ + internal class ObjectPool<T> where T : new() + { + private readonly Stack<T> m_Stack = new Stack<T>(); + private readonly UnityAction<T> m_ActionOnGet; + private readonly UnityAction<T> m_ActionOnRelease; + + public int countAll { get; private set; } + public int countActive { get { return countAll - countInactive; } } + public int countInactive { get { return m_Stack.Count; } } + + public ObjectPool(UnityAction<T> actionOnGet, UnityAction<T> actionOnRelease) + { + m_ActionOnGet = actionOnGet; + m_ActionOnRelease = actionOnRelease; + } + + public T Get() + { + T element; + if (m_Stack.Count == 0) + { + element = new T(); + countAll++; + } + else + { + element = m_Stack.Pop(); + } + if (m_ActionOnGet != null) + m_ActionOnGet(element); + return element; + } + + public void Release(T element) + { + if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element)) + Debug.LogError("Internal error. Trying to destroy object that is already released to pool."); + if (m_ActionOnRelease != null) + m_ActionOnRelease(element); + m_Stack.Push(element); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ObjectPool.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ObjectPool.cs.meta new file mode 100644 index 0000000..942cd48 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ObjectPool.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: e74077e6b05099a4589f683a80cc92ff +timeCreated: 1602119380 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ReflectionMethodsCache.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ReflectionMethodsCache.cs new file mode 100644 index 0000000..638fa03 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ReflectionMethodsCache.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace UnityEngine.UI +{ + internal class ReflectionMethodsCache + { + public delegate bool Raycast3DCallback(Ray r, out RaycastHit hit, float f, int i); + public delegate RaycastHit2D Raycast2DCallback(Vector2 p1, Vector2 p2, float f, int i); + public delegate RaycastHit[] RaycastAllCallback(Ray r, float f, int i); + public delegate RaycastHit2D[] GetRayIntersectionAllCallback(Ray r, float f, int i); + + // We call Physics.Raycast and Physics2D.Raycast through reflection to avoid creating a hard dependency from + // this class to the Physics/Physics2D modules, which would otherwise make it impossible to make content with UI + // without force-including both modules. + public ReflectionMethodsCache() + { + var raycast3DMethodInfo = typeof(Physics).GetMethod("Raycast", new[] {typeof(Ray), typeof(RaycastHit).MakeByRefType(), typeof(float), typeof(int)}); + if (raycast3DMethodInfo != null) + raycast3D = (Raycast3DCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(Raycast3DCallback), raycast3DMethodInfo); + + var raycast2DMethodInfo = typeof(Physics2D).GetMethod("Raycast", new[] {typeof(Vector2), typeof(Vector2), typeof(float), typeof(int)}); + if (raycast2DMethodInfo != null) + raycast2D = (Raycast2DCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(Raycast2DCallback), raycast2DMethodInfo); + + var raycastAllMethodInfo = typeof(Physics).GetMethod("RaycastAll", new[] {typeof(Ray), typeof(float), typeof(int)}); + if (raycastAllMethodInfo != null) + raycast3DAll = (RaycastAllCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(RaycastAllCallback), raycastAllMethodInfo); + + var getRayIntersectionAllMethodInfo = typeof(Physics2D).GetMethod("GetRayIntersectionAll", new[] {typeof(Ray), typeof(float), typeof(int)}); + if (getRayIntersectionAllMethodInfo != null) + getRayIntersectionAll = (GetRayIntersectionAllCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(GetRayIntersectionAllCallback), getRayIntersectionAllMethodInfo); + } + + public Raycast3DCallback raycast3D = null; + public RaycastAllCallback raycast3DAll = null; + public Raycast2DCallback raycast2D = null; + public GetRayIntersectionAllCallback getRayIntersectionAll = null; + + private static ReflectionMethodsCache s_ReflectionMethodsCache = null; + + public static ReflectionMethodsCache Singleton + { + get + { + if (s_ReflectionMethodsCache == null) + s_ReflectionMethodsCache = new ReflectionMethodsCache(); + return s_ReflectionMethodsCache; + } + } + }; +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ReflectionMethodsCache.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ReflectionMethodsCache.cs.meta new file mode 100644 index 0000000..8aa76ec --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/ReflectionMethodsCache.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: a4c9f67a29a841b479c2abbec238e83c +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/SetPropertyUtility.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/SetPropertyUtility.cs new file mode 100644 index 0000000..15d8519 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/SetPropertyUtility.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Events; + +namespace UnityEngine.UI +{ + internal static class SetPropertyUtility + { + public static bool SetColor(ref Color currentValue, Color newValue) + { + if (currentValue.r == newValue.r && currentValue.g == newValue.g && currentValue.b == newValue.b && currentValue.a == newValue.a) + return false; + + currentValue = newValue; + return true; + } + + public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct + { + if (EqualityComparer<T>.Default.Equals(currentValue, newValue)) + return false; + + currentValue = newValue; + return true; + } + + public static bool SetClass<T>(ref T currentValue, T newValue) where T : class + { + if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue))) + return false; + + currentValue = newValue; + return true; + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/SetPropertyUtility.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/SetPropertyUtility.cs.meta new file mode 100644 index 0000000..ee3936e --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/SetPropertyUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c7eb311d761ee3d409de0593312d6fa2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/VertexHelper.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/VertexHelper.cs new file mode 100644 index 0000000..3020d4d --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/VertexHelper.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; + +namespace UnityEngine.UI +{ + public class VertexHelper : IDisposable + { + //c 改成public方便看 + /*private*/ public List<Vector3> m_Positions = ListPool<Vector3>.Get(); + /*private*/ public List<Color32> m_Colors = ListPool<Color32>.Get(); + /*private*/ public List<Vector2> m_Uv0S = ListPool<Vector2>.Get(); + /*private*/ public List<Vector2> m_Uv1S = ListPool<Vector2>.Get(); + /*private*/ public List<Vector2> m_Uv2S = ListPool<Vector2>.Get(); + /*private*/ public List<Vector2> m_Uv3S = ListPool<Vector2>.Get(); + /*private*/ public List<Vector3> m_Normals = ListPool<Vector3>.Get(); + /*private*/ public List<Vector4> m_Tangents = ListPool<Vector4>.Get(); + /*private*/ public List<int> m_Indices = ListPool<int>.Get(); + + private static readonly Vector4 s_DefaultTangent = new Vector4(1.0f, 0.0f, 0.0f, -1.0f); + private static readonly Vector3 s_DefaultNormal = Vector3.back; + + public VertexHelper() + {} + + public VertexHelper(Mesh m) + { + m_Positions.AddRange(m.vertices); + m_Colors.AddRange(m.colors32); + m_Uv0S.AddRange(m.uv); + m_Uv1S.AddRange(m.uv2); + m_Uv2S.AddRange(m.uv3); + m_Uv3S.AddRange(m.uv4); + m_Normals.AddRange(m.normals); + m_Tangents.AddRange(m.tangents); + m_Indices.AddRange(m.GetIndices(0)); + } + + public void Clear() + { + m_Positions.Clear(); + m_Colors.Clear(); + m_Uv0S.Clear(); + m_Uv1S.Clear(); + m_Uv2S.Clear(); + m_Uv3S.Clear(); + m_Normals.Clear(); + m_Tangents.Clear(); + m_Indices.Clear(); + } + + public int currentVertCount + { + get { return m_Positions.Count; } + } + public int currentIndexCount + { + get { return m_Indices.Count; } + } + + public void PopulateUIVertex(ref UIVertex vertex, int i) + { + vertex.position = m_Positions[i]; + vertex.color = m_Colors[i]; + vertex.uv0 = m_Uv0S[i]; + vertex.uv1 = m_Uv1S[i]; + vertex.uv2 = m_Uv2S[i]; + vertex.uv3 = m_Uv3S[i]; + vertex.normal = m_Normals[i]; + vertex.tangent = m_Tangents[i]; + } + + public void SetUIVertex(UIVertex vertex, int i) + { + m_Positions[i] = vertex.position; + m_Colors[i] = vertex.color; + m_Uv0S[i] = vertex.uv0; + m_Uv1S[i] = vertex.uv1; + m_Uv2S[i] = vertex.uv2; + m_Uv3S[i] = vertex.uv3; + m_Normals[i] = vertex.normal; + m_Tangents[i] = vertex.tangent; + } + + public void FillMesh(Mesh mesh) + { + mesh.Clear(); + + if (m_Positions.Count >= 65000) + throw new ArgumentException("Mesh can not have more than 65000 vertices"); + + mesh.SetVertices(m_Positions); + mesh.SetColors(m_Colors); + mesh.SetUVs(0, m_Uv0S); + mesh.SetUVs(1, m_Uv1S); + mesh.SetUVs(2, m_Uv2S); + mesh.SetUVs(3, m_Uv3S); + mesh.SetNormals(m_Normals); + mesh.SetTangents(m_Tangents); + mesh.SetTriangles(m_Indices, 0); + mesh.RecalculateBounds(); + } + + public void Dispose() + { + ListPool<Vector3>.Release(m_Positions); + ListPool<Color32>.Release(m_Colors); + ListPool<Vector2>.Release(m_Uv0S); + ListPool<Vector2>.Release(m_Uv1S); + ListPool<Vector2>.Release(m_Uv2S); + ListPool<Vector2>.Release(m_Uv3S); + ListPool<Vector3>.Release(m_Normals); + ListPool<Vector4>.Release(m_Tangents); + ListPool<int>.Release(m_Indices); + + m_Positions = null; + m_Colors = null; + m_Uv0S = null; + m_Uv1S = null; + m_Uv2S = null; + m_Uv3S = null; + m_Normals = null; + m_Tangents = null; + m_Indices = null; + } + + public void AddVert(Vector3 position, Color32 color, Vector2 uv0, Vector2 uv1, Vector3 normal, Vector4 tangent) + { + m_Positions.Add(position); + m_Colors.Add(color); + m_Uv0S.Add(uv0); + m_Uv1S.Add(uv1); + m_Uv2S.Add(Vector2.zero); + m_Uv3S.Add(Vector2.zero); + m_Normals.Add(normal); + m_Tangents.Add(tangent); + } + + public void AddVert(Vector3 position, Color32 color, Vector2 uv0) + { + AddVert(position, color, uv0, Vector2.zero, s_DefaultNormal, s_DefaultTangent); + } + + public void AddVert(UIVertex v) + { + AddVert(v.position, v.color, v.uv0, v.uv1, v.normal, v.tangent); + } + + public void AddTriangle(int idx0, int idx1, int idx2) + { + m_Indices.Add(idx0); + m_Indices.Add(idx1); + m_Indices.Add(idx2); + } + + public void AddUIVertexQuad(UIVertex[] verts) + { + int startIndex = currentVertCount; + + for (int i = 0; i < 4; i++) + AddVert(verts[i].position, verts[i].color, verts[i].uv0, verts[i].uv1, verts[i].normal, verts[i].tangent); + + AddTriangle(startIndex, startIndex + 1, startIndex + 2); + AddTriangle(startIndex + 2, startIndex + 3, startIndex); + } + + public void AddUIVertexStream(List<UIVertex> verts, List<int> indices) + { + if (verts != null) + { + CanvasRenderer.AddUIVertexStream(verts, m_Positions, m_Colors, m_Uv0S, m_Uv1S, m_Uv2S, m_Uv3S, m_Normals, m_Tangents); + } + + if (indices != null) + { + m_Indices.AddRange(indices); + } + } + + public void AddUIVertexTriangleStream(List<UIVertex> verts) + { + if (verts == null) + return; + CanvasRenderer.SplitUIVertexStreams(verts, m_Positions, m_Colors, m_Uv0S, m_Uv1S, m_Uv2S, m_Uv3S, m_Normals, m_Tangents, m_Indices); + } + + public void GetUIVertexStream(List<UIVertex> stream) + { + if (stream == null) + return; + + CanvasRenderer.CreateUIVertexStream(stream, m_Positions, m_Colors, m_Uv0S, m_Uv1S, m_Uv2S, m_Uv3S, m_Normals, m_Tangents, m_Indices); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/VertexHelper.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/VertexHelper.cs.meta new file mode 100644 index 0000000..df0d085 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Utility/VertexHelper.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 462296566c1ff9443bbf18a80a326dda +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers.meta new file mode 100644 index 0000000..f58dea5 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 78044a61d69e4ef48bba3cbf60320235 +folderAsset: yes +timeCreated: 1602119280 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/BaseMeshEffect.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/BaseMeshEffect.cs new file mode 100644 index 0000000..aac679d --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/BaseMeshEffect.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using UnityEngine.EventSystems; + +namespace UnityEngine.UI +{ + [Obsolete("Use BaseMeshEffect instead", true)] + public abstract class BaseVertexEffect + { + [Obsolete("Use BaseMeshEffect.ModifyMeshes instead", true)] //We can't upgrade automatically since the signature changed. + public abstract void ModifyVertices(List<UIVertex> vertices); + }
+
+ // 关键在于实现IMeshModifier接口,自己写效果,不一定要继承这个类
+ [ExecuteInEditMode] + public abstract class BaseMeshEffect : UIBehaviour, IMeshModifier + { + [NonSerialized] + private Graphic m_Graphic; + + protected Graphic graphic + { + get + { + if (m_Graphic == null) + m_Graphic = GetComponent<Graphic>(); + + return m_Graphic; + } + } + + protected override void OnEnable() + { + base.OnEnable(); + if (graphic != null) + graphic.SetVerticesDirty(); + } + + protected override void OnDisable() + { + if (graphic != null) + graphic.SetVerticesDirty(); + base.OnDisable(); + } + + protected override void OnDidApplyAnimationProperties() + { + if (graphic != null) + graphic.SetVerticesDirty(); + base.OnDidApplyAnimationProperties(); + } + +#if UNITY_EDITOR + protected override void OnValidate() + { + base.OnValidate(); + if (graphic != null) + graphic.SetVerticesDirty(); + } + +#endif + + public virtual void ModifyMesh(Mesh mesh) + { + using (var vh = new VertexHelper(mesh)) + { + ModifyMesh(vh); + vh.FillMesh(mesh); + } + } + + public abstract void ModifyMesh(VertexHelper vh); + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/BaseMeshEffect.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/BaseMeshEffect.cs.meta new file mode 100644 index 0000000..9f4a3df --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/BaseMeshEffect.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 9e33e594b9e3bfb41b97c11432334978 +timeCreated: 1602119379 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/IMeshModifier.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/IMeshModifier.cs new file mode 100644 index 0000000..582a820 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/IMeshModifier.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace UnityEngine.UI +{ + + //[Obsolete("Use IMeshModifier instead", true)] + //public interface IVertexModifier + //{ + // [Obsolete("use IMeshModifier.ModifyMesh (VertexHelper verts) instead", true)] + // void ModifyVertices(List<UIVertex> verts); + //} + + public interface IMeshModifier + { + [Obsolete("use IMeshModifier.ModifyMesh (VertexHelper verts) instead", false)] + void ModifyMesh(Mesh mesh); + void ModifyMesh(VertexHelper verts); + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/IMeshModifier.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/IMeshModifier.cs.meta new file mode 100644 index 0000000..df53941 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/IMeshModifier.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 6b4c52564d8d47540a091497d8f9dd49 +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Outline.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Outline.cs new file mode 100644 index 0000000..e14c862 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Outline.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; + +namespace UnityEngine.UI +{ + [AddComponentMenu("UI/Effects/Outline", 15)] + public class Outline : Shadow + { + protected Outline() + {} + + public override void ModifyMesh(VertexHelper vh) + { + if (!IsActive()) + return; + + var verts = ListPool<UIVertex>.Get(); + vh.GetUIVertexStream(verts); + + var neededCpacity = verts.Count * 5; + if (verts.Capacity < neededCpacity) + verts.Capacity = neededCpacity; + + var start = 0; + var end = verts.Count; + ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, effectDistance.x, effectDistance.y); + + start = end; + end = verts.Count; + ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, effectDistance.x, -effectDistance.y); + + start = end; + end = verts.Count; + ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, -effectDistance.x, effectDistance.y); + + start = end; + end = verts.Count; + ApplyShadowZeroAlloc(verts, effectColor, start, verts.Count, -effectDistance.x, -effectDistance.y); + + vh.Clear(); + vh.AddUIVertexTriangleStream(verts); + ListPool<UIVertex>.Release(verts); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Outline.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Outline.cs.meta new file mode 100644 index 0000000..a27dd26 --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Outline.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 5978620b88847af4a8835c7008c813b8 +timeCreated: 1602119378 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/PositionAsUV1.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/PositionAsUV1.cs new file mode 100644 index 0000000..d396bfa --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/PositionAsUV1.cs @@ -0,0 +1,22 @@ +using System.Linq; + +namespace UnityEngine.UI +{ + [AddComponentMenu("UI/Effects/Position As UV1", 16)] + public class PositionAsUV1 : BaseMeshEffect + { + protected PositionAsUV1() + {} + + public override void ModifyMesh(VertexHelper vh) + { + UIVertex vert = new UIVertex(); + for (int i = 0; i < vh.currentVertCount; i++) + { + vh.PopulateUIVertex(ref vert, i); + vert.uv1 = new Vector2(vert.position.x, vert.position.y); + vh.SetUIVertex(vert, i); + } + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/PositionAsUV1.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/PositionAsUV1.cs.meta new file mode 100644 index 0000000..847c79f --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/PositionAsUV1.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 19ed27a46ca366b439439e63862c762b +timeCreated: 1602119377 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Shadow.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Shadow.cs new file mode 100644 index 0000000..e0a813c --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Shadow.cs @@ -0,0 +1,126 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace UnityEngine.UI +{ + [AddComponentMenu("UI/Effects/Shadow", 14)] + public class Shadow : BaseMeshEffect + { + [SerializeField] + private Color m_EffectColor = new Color(0f, 0f, 0f, 0.5f); + + [SerializeField] + private Vector2 m_EffectDistance = new Vector2(1f, -1f); + + [SerializeField] + private bool m_UseGraphicAlpha = true; + + private const float kMaxEffectDistance = 600f; + + protected Shadow() + {} + +#if UNITY_EDITOR + protected override void OnValidate() + { + effectDistance = m_EffectDistance; + base.OnValidate(); + } + +#endif + + public Color effectColor + { + get { return m_EffectColor; } + set + { + m_EffectColor = value; + if (graphic != null) + graphic.SetVerticesDirty(); + } + } + + public Vector2 effectDistance + { + get { return m_EffectDistance; } + set + { + if (value.x > kMaxEffectDistance) + value.x = kMaxEffectDistance; + if (value.x < -kMaxEffectDistance) + value.x = -kMaxEffectDistance; + + if (value.y > kMaxEffectDistance) + value.y = kMaxEffectDistance; + if (value.y < -kMaxEffectDistance) + value.y = -kMaxEffectDistance; + + if (m_EffectDistance == value) + return; + + m_EffectDistance = value; + + if (graphic != null) + graphic.SetVerticesDirty(); + } + } + + public bool useGraphicAlpha + { + get { return m_UseGraphicAlpha; } + set + { + m_UseGraphicAlpha = value; + if (graphic != null) + graphic.SetVerticesDirty(); + } + } + + //x,y是偏移量 + protected void ApplyShadowZeroAlloc(List<UIVertex> verts, Color32 color, int start, int end, float x, float y) + { + UIVertex vt; + + var neededCapacity = verts.Count + end - start; + if (verts.Capacity < neededCapacity) + verts.Capacity = neededCapacity; + + for (int i = start; i < end; ++i) + { + vt = verts[i]; + verts.Add(vt); + + Vector3 v = vt.position; + v.x += x; + v.y += y; + vt.position = v; + + var newColor = color; + if (m_UseGraphicAlpha) + newColor.a = (byte)((newColor.a * verts[i].color.a) / 255); + + vt.color = newColor; + verts[i] = vt; + } + } + + protected void ApplyShadow(List<UIVertex> verts, Color32 color, int start, int end, float x, float y) + { + ApplyShadowZeroAlloc(verts, color, start, end, x, y); + } + + public override void ModifyMesh(VertexHelper vh) + { + if (!IsActive()) + return; + + var output = ListPool<UIVertex>.Get(); + vh.GetUIVertexStream(output); + + ApplyShadow(output, effectColor, 0, output.Count, effectDistance.x, effectDistance.y); + vh.Clear(); + vh.AddUIVertexTriangleStream(output); + ListPool<UIVertex>.Release(output); + } + } +} diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Shadow.cs.meta b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Shadow.cs.meta new file mode 100644 index 0000000..75dc2fc --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/VertexModifiers/Shadow.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 128369c085053c246bab60ca7bb68a64 +timeCreated: 1602119377 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |