From cee50a6d63e6f62384eee843ff7c41ebf3693ea4 Mon Sep 17 00:00:00 2001 From: "LARS-LAPTOP2\\larsme" Date: Fri, 22 Feb 2019 18:05:03 +0100 Subject: [PATCH] More Options for UI Vertical Scroller - Option to stop scrolling momentum when scrolling past first / last item (still draggable in that direction) - Explicit function to recognize / set array elements in case they were changed - Resize elements to match elementSize rect object (defaults to center object), scaled using modifiable elementShrinkage and minSize - Detect focusedElement and trigger event if focus changes - Option whether to make unfocused elements interactible or not --- Editor/UIExtensionsMenuOptions.cs | 2 +- .../Scenes/VerticalCalendar.unity | 97 ++++--- .../Scripts/ScrollingCalendar.cs | 26 +- Scripts/Layout/UIVerticalScroller.cs | 268 +++++++++++------- 4 files changed, 231 insertions(+), 162 deletions(-) diff --git a/Editor/UIExtensionsMenuOptions.cs b/Editor/UIExtensionsMenuOptions.cs index 6bba15a..b2b895f 100644 --- a/Editor/UIExtensionsMenuOptions.cs +++ b/Editor/UIExtensionsMenuOptions.cs @@ -607,7 +607,7 @@ namespace UnityEditor.UI rectTransformCenter.anchorMax = new Vector2(1f, 0.6f); rectTransformCenter.sizeDelta = Vector2.zero; - uiscr._center = uiScrollerCenter.GetComponent(); + uiscr.center = uiScrollerCenter.GetComponent(); //Setup Content container RectTransform rectTransformContent = childContent.GetComponent(); diff --git a/Examples/UIVerticalScrollerDemo/Scenes/VerticalCalendar.unity b/Examples/UIVerticalScrollerDemo/Scenes/VerticalCalendar.unity index f8be639..f80c2ce 100644 --- a/Examples/UIVerticalScrollerDemo/Scenes/VerticalCalendar.unity +++ b/Examples/UIVerticalScrollerDemo/Scenes/VerticalCalendar.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_IndirectSpecularColor: {r: 0.44657874, g: 0.4964127, b: 0.5748172, a: 1} --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 @@ -472,11 +472,11 @@ RectTransform: m_Father: {fileID: 1858671176} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.35940847, y: 0.5} - m_AnchorMax: {x: 0.6406339, y: 1} - m_AnchoredPosition: {x: 0, y: -50} - m_SizeDelta: {x: 0, y: 100} - m_Pivot: {x: 0.5, y: 0.5} + m_AnchorMin: {x: 0.39, y: 0} + m_AnchorMax: {x: 0.61, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} --- !u!1 &172038071 GameObject: m_ObjectHideFlags: 0 @@ -507,10 +507,10 @@ RectTransform: m_Father: {fileID: 674720969} m_RootOrder: 12 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 301, y: -209} - m_SizeDelta: {x: 66, y: 67} + m_AnchorMin: {x: 0.7869379, y: 0.12806748} + m_AnchorMax: {x: 0.8576017, y: 0.23082823} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &172038073 MonoBehaviour: @@ -577,10 +577,10 @@ RectTransform: m_Father: {fileID: 674720969} m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: -123} - m_SizeDelta: {x: 30, y: 30} + m_AnchorMin: {x: 0.485, y: 0.28834355} + m_AnchorMax: {x: 0.515, y: 0.33435583} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &278688116 MonoBehaviour: @@ -979,10 +979,10 @@ RectTransform: m_Father: {fileID: 1858671176} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0.5} - m_AnchorMax: {x: 0.28054926, y: 1} - m_AnchoredPosition: {x: 0, y: -50} - m_SizeDelta: {x: 1, y: 100} + m_AnchorMin: {x: 0.12, y: 0} + m_AnchorMax: {x: 0.34, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0.5} --- !u!1 &399961834 GameObject: @@ -1016,10 +1016,10 @@ RectTransform: m_Father: {fileID: 674720969} m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -260, y: -123} - m_SizeDelta: {x: 30, y: 30} + m_AnchorMin: {x: 0.215, y: 0.28834355} + m_AnchorMax: {x: 0.245, y: 0.33435583} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &399961836 MonoBehaviour: @@ -1212,10 +1212,10 @@ RectTransform: m_Father: {fileID: 674720969} m_RootOrder: 11 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 260, y: -123} - m_SizeDelta: {x: 30, y: 30} + m_AnchorMin: {x: 0.755, y: 0.28834355} + m_AnchorMax: {x: 0.785, y: 0.33435583} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &608866809 MonoBehaviour: @@ -1365,11 +1365,11 @@ RectTransform: m_Father: {fileID: 1858671176} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.71677464, y: 0.5} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -50} - m_SizeDelta: {x: -1, y: 100} - m_Pivot: {x: 1, y: 0.5} + m_AnchorMin: {x: 0.66, y: 0} + m_AnchorMax: {x: 0.88, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0.5} --- !u!1 &674720968 GameObject: m_ObjectHideFlags: 0 @@ -1508,10 +1508,10 @@ RectTransform: m_Father: {fileID: 674720969} m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 260, y: 125} - m_SizeDelta: {x: 30, y: 30} + m_AnchorMin: {x: 0.755, y: 0.66871166} + m_AnchorMax: {x: 0.785, y: 0.71472394} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &738460844 MonoBehaviour: @@ -2321,6 +2321,9 @@ MonoBehaviour: monthsScrollingPanel: {fileID: 900655611} yearsScrollingPanel: {fileID: 1788125454} daysScrollingPanel: {fileID: 1390764938} + monthsScrollRect: {fileID: 1704132813} + yearsScrollRect: {fileID: 1811286347} + daysScrollRect: {fileID: 1598544346} yearsButtonPrefab: {fileID: 118010, guid: 65ce75600991941569988ab4538a21a6, type: 2} monthsButtonPrefab: {fileID: 194106, guid: 6d45e8f0aa19044ffb3553ca52febef2, type: 2} daysButtonPrefab: {fileID: 138598, guid: ec6f9b1230e4a4896b7be2e60d3be758, type: 2} @@ -2374,10 +2377,10 @@ RectTransform: m_Father: {fileID: 674720969} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: -168} - m_SizeDelta: {x: 300, y: 100} + m_AnchorMin: {x: 0.34, y: 0.11196319} + m_AnchorMax: {x: 0.66, y: 0.26533744} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1285461119 MonoBehaviour: @@ -2625,10 +2628,10 @@ RectTransform: m_Father: {fileID: 674720969} m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 125} - m_SizeDelta: {x: 30, y: 30} + m_AnchorMin: {x: 0.485, y: 0.66871166} + m_AnchorMax: {x: 0.515, y: 0.71472394} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1580916629 MonoBehaviour: @@ -3252,10 +3255,10 @@ RectTransform: m_Father: {fileID: 674720969} m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -260, y: 125} - m_SizeDelta: {x: 30, y: 30} + m_AnchorMin: {x: 0.215, y: 0.66871166} + m_AnchorMax: {x: 0.245, y: 0.71472394} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1820653142 MonoBehaviour: diff --git a/Examples/UIVerticalScrollerDemo/Scripts/ScrollingCalendar.cs b/Examples/UIVerticalScrollerDemo/Scripts/ScrollingCalendar.cs index 65b1f7e..3cebb53 100644 --- a/Examples/UIVerticalScrollerDemo/Scripts/ScrollingCalendar.cs +++ b/Examples/UIVerticalScrollerDemo/Scripts/ScrollingCalendar.cs @@ -13,6 +13,10 @@ namespace UnityEngine.UI.Extensions.Examples public RectTransform yearsScrollingPanel; public RectTransform daysScrollingPanel; + public ScrollRect monthsScrollRect; + public ScrollRect yearsScrollRect; + public ScrollRect daysScrollRect; + public GameObject yearsButtonPrefab; public GameObject monthsButtonPrefab; public GameObject daysButtonPrefab; @@ -51,8 +55,7 @@ namespace UnityEngine.UI.Extensions.Examples { arrayYears[i] = 1900 + i; - GameObject clone = (GameObject)Instantiate(yearsButtonPrefab, new Vector3(0, i * 80, 0), Quaternion.Euler(new Vector3(0, 0, 0))) as GameObject; - clone.transform.SetParent(yearsScrollingPanel, false); + GameObject clone = Instantiate(yearsButtonPrefab, yearsScrollingPanel); clone.transform.localScale = new Vector3(1, 1, 1); clone.GetComponentInChildren().text = "" + arrayYears[i]; clone.name = "Year_" + arrayYears[i]; @@ -74,8 +77,7 @@ namespace UnityEngine.UI.Extensions.Examples string month = ""; months[i] = i; - GameObject clone = (GameObject)Instantiate(monthsButtonPrefab, new Vector3(0, i * 80, 0), Quaternion.Euler(new Vector3(0, 0, 0))) as GameObject; - clone.transform.SetParent(monthsScrollingPanel, false); + GameObject clone = Instantiate(monthsButtonPrefab, monthsScrollingPanel); clone.transform.localScale = new Vector3(1, 1, 1); switch (i) @@ -133,9 +135,7 @@ namespace UnityEngine.UI.Extensions.Examples for (var i = 0; i < days.Length; i++) { days[i] = i + 1; - GameObject clone = (GameObject)Instantiate(daysButtonPrefab, new Vector3(0, i * 80, 0), Quaternion.Euler(new Vector3(0, 0, 0))) as GameObject; - clone.transform.SetParent(daysScrollingPanel, false); - clone.transform.localScale = new Vector3(1, 1, 1); + GameObject clone = Instantiate(daysButtonPrefab, daysScrollingPanel); clone.GetComponentInChildren().text = "" + days[i]; clone.name = "Day_" + days[i]; clone.AddComponent(); @@ -151,9 +151,9 @@ namespace UnityEngine.UI.Extensions.Examples InitializeDays(); //Yes Unity complains about this but it doesn't matter in this case. - monthsVerticalScroller = new UIVerticalScroller(monthsScrollingPanel, monthsButtons, monthCenter); - yearsVerticalScroller = new UIVerticalScroller(yearsScrollingPanel, yearsButtons, yearsCenter); - daysVerticalScroller = new UIVerticalScroller(daysScrollingPanel, daysButtons, daysCenter); + monthsVerticalScroller = new UIVerticalScroller(monthCenter, monthCenter, monthsScrollRect, monthsButtons); + yearsVerticalScroller = new UIVerticalScroller(yearsCenter, yearsCenter, yearsScrollRect, yearsButtons); + daysVerticalScroller = new UIVerticalScroller(daysCenter, daysCenter, daysScrollRect, daysButtons); monthsVerticalScroller.Start(); yearsVerticalScroller.Start(); @@ -177,9 +177,9 @@ namespace UnityEngine.UI.Extensions.Examples yearsVerticalScroller.Update(); daysVerticalScroller.Update(); - string dayString = daysVerticalScroller.GetResults(); - string monthString = monthsVerticalScroller.GetResults(); - string yearsString = yearsVerticalScroller.GetResults(); + string dayString = daysVerticalScroller.result; + string monthString = monthsVerticalScroller.result; + string yearsString = yearsVerticalScroller.result; if (dayString.EndsWith("1") && dayString != "11") dayString = dayString + "st"; diff --git a/Scripts/Layout/UIVerticalScroller.cs b/Scripts/Layout/UIVerticalScroller.cs index 3e6927d..c3550ee 100644 --- a/Scripts/Layout/UIVerticalScroller.cs +++ b/Scripts/Layout/UIVerticalScroller.cs @@ -10,114 +10,160 @@ namespace UnityEngine.UI.Extensions [AddComponentMenu("Layout/Extensions/Vertical Scroller")] public class UIVerticalScroller : MonoBehaviour { - [Tooltip("Scrollable area (content of desired ScrollRect)")] - public RectTransform _scrollingPanel; - [Tooltip("Elements to populate inside the scroller")] - public GameObject[] _arrayOfElements; + [Tooltip("desired ScrollRect")] + public ScrollRect scrollRect; [Tooltip("Center display area (position of zoomed content)")] - public RectTransform _center; - [Tooltip("Select the item to be in center on start. (optional)")] - public int StartingIndex = -1; + public RectTransform center; + [Tooltip("Size / spacing of elements")] + public RectTransform elementSize; + [Tooltip("Scale = 1/ (1+distance fom center * shrinkage)")] + public Vector2 elementShrinkage = new Vector2(1f / 200, 1f / 200); + [Tooltip("Minimum element scale (furthest from center)")] + public Vector2 minScale = new Vector2(0.7f, 0.7f); + [Tooltip("Select the item to be in center on start.")] + public int startingIndex = -1; + [Tooltip("Stop scrolling past last element from inertia.")] + public bool stopMomentumOnEnd = true; + [Tooltip("Set Items out of center to not interactible.")] + public bool disableUnfocused = true; [Tooltip("Button to go to the next page. (optional)")] - public GameObject ScrollUpButton; + public GameObject scrollUpButton; [Tooltip("Button to go to the previous page. (optional)")] - public GameObject ScrollDownButton; + public GameObject scrollDownButton; [Tooltip("Event fired when a specific item is clicked, exposes index number of item. (optional)")] - public UnityEvent ButtonClicked; + public IntEvent OnButtonClicked; + [Tooltip("Event fired when the focused item is Changed. (optional)")] + public IntEvent OnFocusChanged; + [HideInInspector] + public GameObject[] _arrayOfElements; + public int focusedElementIndex { get; private set; } + + public string result { get; private set; } private float[] distReposition; private float[] distance; //private int elementsDistance; - private int minElementsNum; - private int elementLength; - //private int elementHalfLength; - private float deltaY; - private string result; - public UIVerticalScroller() { } - public UIVerticalScroller(RectTransform scrollingPanel, GameObject[] arrayOfElements, RectTransform center) + //Scrollable area (content of desired ScrollRect) + [HideInInspector] + public RectTransform scrollingPanel{ get { return scrollRect.content; } } + + + /// + /// Constructor when not used as component but called from other script, don't forget to set the non-optional properties. + /// + public UIVerticalScroller() { - _scrollingPanel = scrollingPanel; - _arrayOfElements = arrayOfElements; - _center = center; } + /// + /// Constructor when not used as component but called from other script + /// + public UIVerticalScroller(RectTransform center, RectTransform elementSize, ScrollRect scrollRect, GameObject[] arrayOfElements) + { + this.center = center; + this.elementSize = elementSize; + this.scrollRect = scrollRect; + _arrayOfElements = arrayOfElements; + } + /// + /// Awake this instance. + /// public void Awake() { - var scrollRect = GetComponent(); - if (!_scrollingPanel) + if (!scrollRect) { - _scrollingPanel = scrollRect.content; + scrollRect = GetComponent(); } - if (!_center) + if (!center) { Debug.LogError("Please define the RectTransform for the Center viewport of the scrollable area"); } + if (!elementSize) + { + elementSize = center; + } if (_arrayOfElements == null || _arrayOfElements.Length == 0) { - var childCount = scrollRect.content.childCount; - if (childCount > 0) + _arrayOfElements = new GameObject[scrollingPanel.childCount]; + for (int i = 0; i < scrollingPanel.childCount; i++) { - _arrayOfElements = new GameObject[childCount]; - for (int i = 0; i < childCount; i++) - { - _arrayOfElements[i] = scrollRect.content.GetChild(i).gameObject; - } + _arrayOfElements[i] = scrollingPanel.GetChild(i).gameObject; + } + } + } + + /// + /// Recognises and resizes the children. + /// + /// Starting index. + /// Array of elements. + public void updateChildren(int startingIndex = -1, GameObject[] arrayOfElements = null) + { + // Set _arrayOfElements to arrayOfElements if given, otherwise to child objects of the scrolling panel. + if (arrayOfElements != null) + { + _arrayOfElements = arrayOfElements; + } + else + { + _arrayOfElements = new GameObject[scrollingPanel.childCount]; + for (int i = 0; i < scrollingPanel.childCount; i++) + { + _arrayOfElements[i] = scrollingPanel.GetChild(i).gameObject; } } + + // resize the elements to match elementSize rect + for (var i = 0; i < _arrayOfElements.Length; i++) + { + int j = i; + _arrayOfElements[i].GetComponent