Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5472815444de2ce45bf2053a4af04b9d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,244 @@
|
|||
using System.Collections;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UnityEngine.UI.CoroutineTween
|
||||
{
|
||||
// Base interface for tweeners,
|
||||
// using an interface instead of
|
||||
// an abstract class as we want the
|
||||
// tweens to be structs.
|
||||
internal interface ITweenValue
|
||||
{
|
||||
void TweenValue(float floatPercentage);
|
||||
bool ignoreTimeScale { get; }
|
||||
float duration { get; }
|
||||
bool ValidTarget();
|
||||
}
|
||||
|
||||
// Color tween class, receives the
|
||||
// TweenValue callback and then sets
|
||||
// the value on the target.
|
||||
internal struct ColorTween : ITweenValue
|
||||
{
|
||||
public enum ColorTweenMode
|
||||
{
|
||||
All,
|
||||
RGB,
|
||||
Alpha
|
||||
}
|
||||
|
||||
public class ColorTweenCallback : UnityEvent<Color> {}
|
||||
|
||||
private ColorTweenCallback m_Target;
|
||||
private Color m_StartColor;
|
||||
private Color m_TargetColor;
|
||||
private ColorTweenMode m_TweenMode;
|
||||
|
||||
private float m_Duration;
|
||||
private bool m_IgnoreTimeScale;
|
||||
|
||||
public Color startColor
|
||||
{
|
||||
get { return m_StartColor; }
|
||||
set { m_StartColor = value; }
|
||||
}
|
||||
|
||||
public Color targetColor
|
||||
{
|
||||
get { return m_TargetColor; }
|
||||
set { m_TargetColor = value; }
|
||||
}
|
||||
|
||||
public ColorTweenMode tweenMode
|
||||
{
|
||||
get { return m_TweenMode; }
|
||||
set { m_TweenMode = value; }
|
||||
}
|
||||
|
||||
public float duration
|
||||
{
|
||||
get { return m_Duration; }
|
||||
set { m_Duration = value; }
|
||||
}
|
||||
|
||||
public bool ignoreTimeScale
|
||||
{
|
||||
get { return m_IgnoreTimeScale; }
|
||||
set { m_IgnoreTimeScale = value; }
|
||||
}
|
||||
|
||||
public void TweenValue(float floatPercentage)
|
||||
{
|
||||
if (!ValidTarget())
|
||||
return;
|
||||
|
||||
var newColor = Color.Lerp(m_StartColor, m_TargetColor, floatPercentage);
|
||||
|
||||
if (m_TweenMode == ColorTweenMode.Alpha)
|
||||
{
|
||||
newColor.r = m_StartColor.r;
|
||||
newColor.g = m_StartColor.g;
|
||||
newColor.b = m_StartColor.b;
|
||||
}
|
||||
else if (m_TweenMode == ColorTweenMode.RGB)
|
||||
{
|
||||
newColor.a = m_StartColor.a;
|
||||
}
|
||||
m_Target.Invoke(newColor);
|
||||
}
|
||||
|
||||
public void AddOnChangedCallback(UnityAction<Color> callback)
|
||||
{
|
||||
if (m_Target == null)
|
||||
m_Target = new ColorTweenCallback();
|
||||
|
||||
m_Target.AddListener(callback);
|
||||
}
|
||||
|
||||
public bool GetIgnoreTimescale()
|
||||
{
|
||||
return m_IgnoreTimeScale;
|
||||
}
|
||||
|
||||
public float GetDuration()
|
||||
{
|
||||
return m_Duration;
|
||||
}
|
||||
|
||||
public bool ValidTarget()
|
||||
{
|
||||
return m_Target != null;
|
||||
}
|
||||
}
|
||||
|
||||
// Float tween class, receives the
|
||||
// TweenValue callback and then sets
|
||||
// the value on the target.
|
||||
internal struct FloatTween : ITweenValue
|
||||
{
|
||||
public class FloatTweenCallback : UnityEvent<float> {}
|
||||
|
||||
private FloatTweenCallback m_Target;
|
||||
private float m_StartValue;
|
||||
private float m_TargetValue;
|
||||
|
||||
private float m_Duration;
|
||||
private bool m_IgnoreTimeScale;
|
||||
|
||||
public float startValue
|
||||
{
|
||||
get { return m_StartValue; }
|
||||
set { m_StartValue = value; }
|
||||
}
|
||||
|
||||
public float targetValue
|
||||
{
|
||||
get { return m_TargetValue; }
|
||||
set { m_TargetValue = value; }
|
||||
}
|
||||
|
||||
public float duration
|
||||
{
|
||||
get { return m_Duration; }
|
||||
set { m_Duration = value; }
|
||||
}
|
||||
|
||||
public bool ignoreTimeScale
|
||||
{
|
||||
get { return m_IgnoreTimeScale; }
|
||||
set { m_IgnoreTimeScale = value; }
|
||||
}
|
||||
|
||||
public void TweenValue(float floatPercentage)
|
||||
{
|
||||
if (!ValidTarget())
|
||||
return;
|
||||
|
||||
var newValue = Mathf.Lerp(m_StartValue, m_TargetValue, floatPercentage);
|
||||
m_Target.Invoke(newValue);
|
||||
}
|
||||
|
||||
public void AddOnChangedCallback(UnityAction<float> callback)
|
||||
{
|
||||
if (m_Target == null)
|
||||
m_Target = new FloatTweenCallback();
|
||||
|
||||
m_Target.AddListener(callback);
|
||||
}
|
||||
|
||||
public bool GetIgnoreTimescale()
|
||||
{
|
||||
return m_IgnoreTimeScale;
|
||||
}
|
||||
|
||||
public float GetDuration()
|
||||
{
|
||||
return m_Duration;
|
||||
}
|
||||
|
||||
public bool ValidTarget()
|
||||
{
|
||||
return m_Target != null;
|
||||
}
|
||||
}
|
||||
|
||||
// Tween runner, executes the given tween.
|
||||
// The coroutine will live within the given
|
||||
// behaviour container.
|
||||
internal class TweenRunner<T> where T : struct, ITweenValue
|
||||
{
|
||||
protected MonoBehaviour m_CoroutineContainer;
|
||||
protected IEnumerator m_Tween;
|
||||
|
||||
// utility function for starting the tween
|
||||
private static IEnumerator Start(T tweenInfo)
|
||||
{
|
||||
if (!tweenInfo.ValidTarget())
|
||||
yield break;
|
||||
|
||||
var elapsedTime = 0.0f;
|
||||
while (elapsedTime < tweenInfo.duration)
|
||||
{
|
||||
elapsedTime += tweenInfo.ignoreTimeScale ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
var percentage = Mathf.Clamp01(elapsedTime / tweenInfo.duration);
|
||||
tweenInfo.TweenValue(percentage);
|
||||
yield return null;
|
||||
}
|
||||
tweenInfo.TweenValue(1.0f);
|
||||
}
|
||||
|
||||
public void Init(MonoBehaviour coroutineContainer)
|
||||
{
|
||||
m_CoroutineContainer = coroutineContainer;
|
||||
}
|
||||
|
||||
public void StartTween(T info)
|
||||
{
|
||||
if (m_CoroutineContainer == null)
|
||||
{
|
||||
Debug.LogWarning("Coroutine container not configured... did you forget to call Init?");
|
||||
return;
|
||||
}
|
||||
|
||||
StopTween();
|
||||
|
||||
if (!m_CoroutineContainer.gameObject.activeInHierarchy)
|
||||
{
|
||||
info.TweenValue(1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
m_Tween = Start(info);
|
||||
m_CoroutineContainer.StartCoroutine(m_Tween);
|
||||
}
|
||||
|
||||
public void StopTween()
|
||||
{
|
||||
if (m_Tween != null)
|
||||
{
|
||||
m_CoroutineContainer.StopCoroutine(m_Tween);
|
||||
m_Tween = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7be84a49bb2cd7e4a9ed097ba22794d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 12c42068351bb084abde965d725b9887
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure that stores the state of an animation transition on a Selectable.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AnimationTriggers
|
||||
{
|
||||
private const string kDefaultNormalAnimName = "Normal";
|
||||
private const string kDefaultHighlightedAnimName = "Highlighted";
|
||||
private const string kDefaultPressedAnimName = "Pressed";
|
||||
private const string kDefaultSelectedAnimName = "Selected";
|
||||
private const string kDefaultDisabledAnimName = "Disabled";
|
||||
|
||||
[FormerlySerializedAs("normalTrigger")]
|
||||
[SerializeField]
|
||||
private string m_NormalTrigger = kDefaultNormalAnimName;
|
||||
|
||||
[FormerlySerializedAs("highlightedTrigger")]
|
||||
[SerializeField]
|
||||
private string m_HighlightedTrigger = kDefaultHighlightedAnimName;
|
||||
|
||||
[FormerlySerializedAs("pressedTrigger")]
|
||||
[SerializeField]
|
||||
private string m_PressedTrigger = kDefaultPressedAnimName;
|
||||
|
||||
[FormerlySerializedAs("m_HighlightedTrigger")]
|
||||
[SerializeField]
|
||||
private string m_SelectedTrigger = kDefaultSelectedAnimName;
|
||||
|
||||
[FormerlySerializedAs("disabledTrigger")]
|
||||
[SerializeField]
|
||||
private string m_DisabledTrigger = kDefaultDisabledAnimName;
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to send to animator when entering normal state.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Animator buttonAnimator;
|
||||
/// public Button button;
|
||||
/// void SomeFunction()
|
||||
/// {
|
||||
/// //Sets the button to the Normal state (Useful when making tutorials).
|
||||
/// buttonAnimator.SetTrigger(button.animationTriggers.normalTrigger);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public string normalTrigger { get { return m_NormalTrigger; } set { m_NormalTrigger = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to send to animator when entering highlighted state.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Animator buttonAnimator;
|
||||
/// public Button button;
|
||||
/// void SomeFunction()
|
||||
/// {
|
||||
/// //Sets the button to the Highlighted state (Useful when making tutorials).
|
||||
/// buttonAnimator.SetTrigger(button.animationTriggers.highlightedTrigger);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public string highlightedTrigger { get { return m_HighlightedTrigger; } set { m_HighlightedTrigger = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to send to animator when entering pressed state.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Animator buttonAnimator;
|
||||
/// public Button button;
|
||||
/// void SomeFunction()
|
||||
/// {
|
||||
/// //Sets the button to the Pressed state (Useful when making tutorials).
|
||||
/// buttonAnimator.SetTrigger(button.animationTriggers.pressedTrigger);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public string pressedTrigger { get { return m_PressedTrigger; } set { m_PressedTrigger = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to send to animator when entering selected state.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Animator buttonAnimator;
|
||||
/// public Button button;
|
||||
/// void SomeFunction()
|
||||
/// {
|
||||
/// //Sets the button to the Selected state (Useful when making tutorials).
|
||||
/// buttonAnimator.SetTrigger(button.animationTriggers.selectedTrigger);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public string selectedTrigger { get { return m_SelectedTrigger; } set { m_SelectedTrigger = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Trigger to send to animator when entering disabled state.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Animator buttonAnimator;
|
||||
/// public Button button;
|
||||
/// void SomeFunction()
|
||||
/// {
|
||||
/// //Sets the button to the Disabled state (Useful when making tutorials).
|
||||
/// buttonAnimator.SetTrigger(button.animationTriggers.disabledTrigger);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public string disabledTrigger { get { return m_DisabledTrigger; } set { m_DisabledTrigger = value; } }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c67ac6e40bbb6fe47a095b949b609ce0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,175 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A standard button that sends an event when clicked.
|
||||
/// </summary>
|
||||
[AddComponentMenu("UI/Button", 30)]
|
||||
public class Button : Selectable, IPointerClickHandler, ISubmitHandler
|
||||
{
|
||||
[Serializable]
|
||||
/// <summary>
|
||||
/// Function definition for a button click event.
|
||||
/// </summary>
|
||||
public class ButtonClickedEvent : UnityEvent {}
|
||||
|
||||
// Event delegates triggered on click.
|
||||
[FormerlySerializedAs("onClick")]
|
||||
[SerializeField]
|
||||
private ButtonClickedEvent m_OnClick = new ButtonClickedEvent();
|
||||
|
||||
protected Button()
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// UnityEvent that is triggered when the button is pressed.
|
||||
/// Note: Triggered on MouseUp after MouseDown on the same object.
|
||||
/// </summary>
|
||||
///<example>
|
||||
///<code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.UI;
|
||||
/// using System.Collections;
|
||||
///
|
||||
/// public class ClickExample : MonoBehaviour
|
||||
/// {
|
||||
/// public Button yourButton;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// Button btn = yourButton.GetComponent<Button>();
|
||||
/// btn.onClick.AddListener(TaskOnClick);
|
||||
/// }
|
||||
///
|
||||
/// void TaskOnClick()
|
||||
/// {
|
||||
/// Debug.Log("You have clicked the button!");
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
///</example>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call all registered IPointerClickHandlers.
|
||||
/// Register button presses using the IPointerClickHandler. You can also use it to tell what type of click happened (left, right etc.).
|
||||
/// Make sure your Scene has an EventSystem.
|
||||
/// </summary>
|
||||
/// <param name="eventData">Pointer Data associated with the event. Typically by the event system.</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// //Attatch this script to a Button GameObject
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.EventSystems;
|
||||
///
|
||||
/// public class Example : MonoBehaviour, IPointerClickHandler
|
||||
/// {
|
||||
/// //Detect if a click occurs
|
||||
/// public void OnPointerClick(PointerEventData pointerEventData)
|
||||
/// {
|
||||
/// //Use this to tell when the user right-clicks on the Button
|
||||
/// if (pointerEventData.button == PointerEventData.InputButton.Right)
|
||||
/// {
|
||||
/// //Output to console the clicked GameObject's name and the following message. You can replace this with your own actions for when clicking the GameObject.
|
||||
/// Debug.Log(name + " Game Object Right Clicked!");
|
||||
/// }
|
||||
///
|
||||
/// //Use this to tell when the user left-clicks on the Button
|
||||
/// if (pointerEventData.button == PointerEventData.InputButton.Left)
|
||||
/// {
|
||||
/// Debug.Log(name + " Game Object Left Clicked!");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
|
||||
public virtual void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button != PointerEventData.InputButton.Left)
|
||||
return;
|
||||
|
||||
Press();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call all registered ISubmitHandler.
|
||||
/// </summary>
|
||||
/// <param name="eventData">Associated data with the event. Typically by the event system.</param>
|
||||
/// <remarks>
|
||||
/// This detects when a Button has been selected via a "submit" key you specify (default is the return key).
|
||||
///
|
||||
/// To change the submit key, either:
|
||||
///
|
||||
/// 1. Go to Edit->Project Settings->Input.
|
||||
///
|
||||
/// 2. Next, expand the Axes section and go to the Submit section if it exists.
|
||||
///
|
||||
/// 3. If Submit doesn’t exist, add 1 number to the Size field. This creates a new section at the bottom. Expand the new section and change the Name field to “Submit”.
|
||||
///
|
||||
/// 4. Change the Positive Button field to the key you want (e.g. space).
|
||||
///
|
||||
///
|
||||
/// Or:
|
||||
///
|
||||
/// 1. Go to your EventSystem in your Project
|
||||
///
|
||||
/// 2. Go to the Inspector window and change the Submit Button field to one of the sections in the Input Manager (e.g. "Submit"), or create your own by naming it what you like, then following the next few steps.
|
||||
///
|
||||
/// 3. Go to Edit->Project Settings->Input to get to the Input Manager.
|
||||
///
|
||||
/// 4. Expand the Axes section in the Inspector window. Add 1 to the number in the Size field. This creates a new section at the bottom.
|
||||
///
|
||||
/// 5. Expand the new section and name it the same as the name you inserted in the Submit Button field in the EventSystem. Set the Positive Button field to the key you want (e.g. space)
|
||||
/// </remarks>
|
||||
|
||||
public virtual void OnSubmit(BaseEventData eventData)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4e29b1a8efbd4b44bb3f3716e73f07ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,381 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.UI.Collections;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Values of 'update' called on a Canvas update.
|
||||
/// </summary>
|
||||
/// <remarks> If modifying also modify m_CanvasUpdateProfilerStrings to match.</remarks>
|
||||
public enum CanvasUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// Called before layout.
|
||||
/// </summary>
|
||||
Prelayout = 0,
|
||||
/// <summary>
|
||||
/// Called for layout.
|
||||
/// </summary>
|
||||
Layout = 1,
|
||||
/// <summary>
|
||||
/// Called after layout.
|
||||
/// </summary>
|
||||
PostLayout = 2,
|
||||
/// <summary>
|
||||
/// Called before rendering.
|
||||
/// </summary>
|
||||
PreRender = 3,
|
||||
/// <summary>
|
||||
/// Called late, before render.
|
||||
/// </summary>
|
||||
LatePreRender = 4,
|
||||
/// <summary>
|
||||
/// Max enum value. Always last.
|
||||
/// </summary>
|
||||
MaxUpdateValue = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is an element that can live on a Canvas.
|
||||
/// </summary>
|
||||
public interface ICanvasElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Rebuild the element for the given stage.
|
||||
/// </summary>
|
||||
/// <param name="executing">The current CanvasUpdate stage being rebuild.</param>
|
||||
void Rebuild(CanvasUpdate executing);
|
||||
|
||||
/// <summary>
|
||||
/// Get the transform associated with the ICanvasElement.
|
||||
/// </summary>
|
||||
Transform transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Callback sent when this ICanvasElement has completed layout.
|
||||
/// </summary>
|
||||
void LayoutComplete();
|
||||
|
||||
/// <summary>
|
||||
/// Callback sent when this ICanvasElement has completed Graphic rebuild.
|
||||
/// </summary>
|
||||
void GraphicUpdateComplete();
|
||||
|
||||
/// <summary>
|
||||
/// Used if the native representation has been destroyed.
|
||||
/// </summary>
|
||||
/// <returns>Return true if the element is considered destroyed.</returns>
|
||||
bool IsDestroyed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A place where CanvasElements can register themselves for rebuilding.
|
||||
/// </summary>
|
||||
public class CanvasUpdateRegistry
|
||||
{
|
||||
private static CanvasUpdateRegistry s_Instance;
|
||||
|
||||
private bool m_PerformingLayoutUpdate;
|
||||
private bool m_PerformingGraphicUpdate;
|
||||
|
||||
// This list matches the CanvasUpdate enum above. Keep in sync
|
||||
private string[] m_CanvasUpdateProfilerStrings = new string[] { "CanvasUpdate.Prelayout", "CanvasUpdate.Layout", "CanvasUpdate.PostLayout", "CanvasUpdate.PreRender", "CanvasUpdate.LatePreRender" };
|
||||
private const string m_CullingUpdateProfilerString = "ClipperRegistry.Cull";
|
||||
|
||||
private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>();
|
||||
private readonly IndexedSet<ICanvasElement> m_GraphicRebuildQueue = new IndexedSet<ICanvasElement>();
|
||||
|
||||
protected CanvasUpdateRegistry()
|
||||
{
|
||||
Canvas.willRenderCanvases += PerformUpdate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the singleton registry instance.
|
||||
/// </summary>
|
||||
public static CanvasUpdateRegistry instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
s_Instance = new CanvasUpdateRegistry();
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
var layoutRebuildQueueCount = m_LayoutRebuildQueue.Count;
|
||||
for (int i = layoutRebuildQueueCount - 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();
|
||||
}
|
||||
}
|
||||
|
||||
var graphicRebuildQueueCount = m_GraphicRebuildQueue.Count;
|
||||
for (int i = graphicRebuildQueueCount - 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;
|
||||
private void PerformUpdate()
|
||||
{
|
||||
UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Layout);
|
||||
CleanInvalidItems();
|
||||
|
||||
m_PerformingLayoutUpdate = true;
|
||||
|
||||
m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);
|
||||
var layoutRebuildQueueCount = m_LayoutRebuildQueue.Count;
|
||||
|
||||
for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
|
||||
|
||||
for (int j = 0; j < layoutRebuildQueueCount; j++)
|
||||
{
|
||||
var rebuild = m_LayoutRebuildQueue[j];
|
||||
try
|
||||
{
|
||||
if (ObjectValidForUpdate(rebuild))
|
||||
rebuild.Rebuild((CanvasUpdate)i);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e, rebuild.transform);
|
||||
}
|
||||
}
|
||||
UnityEngine.Profiling.Profiler.EndSample();
|
||||
}
|
||||
|
||||
for (int i = 0; i < layoutRebuildQueueCount; ++i)
|
||||
m_LayoutRebuildQueue[i].LayoutComplete();
|
||||
|
||||
m_LayoutRebuildQueue.Clear();
|
||||
m_PerformingLayoutUpdate = false;
|
||||
UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Layout);
|
||||
UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Render);
|
||||
|
||||
// now layout is complete do culling...
|
||||
UnityEngine.Profiling.Profiler.BeginSample(m_CullingUpdateProfilerString);
|
||||
ClipperRegistry.instance.Cull();
|
||||
UnityEngine.Profiling.Profiler.EndSample();
|
||||
|
||||
m_PerformingGraphicUpdate = true;
|
||||
|
||||
var graphicRebuildQueueCount = m_GraphicRebuildQueue.Count;
|
||||
for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
|
||||
for (var k = 0; k < graphicRebuildQueueCount; k++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var element = m_GraphicRebuildQueue[k];
|
||||
if (ObjectValidForUpdate(element))
|
||||
element.Rebuild((CanvasUpdate)i);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e, m_GraphicRebuildQueue[k].transform);
|
||||
}
|
||||
}
|
||||
UnityEngine.Profiling.Profiler.EndSample();
|
||||
}
|
||||
|
||||
for (int i = 0; i < graphicRebuildQueueCount; ++i)
|
||||
m_GraphicRebuildQueue[i].GraphicUpdateComplete();
|
||||
|
||||
m_GraphicRebuildQueue.Clear();
|
||||
m_PerformingGraphicUpdate = false;
|
||||
UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Render);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try and add the given element to the layout rebuild list.
|
||||
/// Will not return if successfully added.
|
||||
/// </summary>
|
||||
/// <param name="element">The element that is needing rebuilt.</param>
|
||||
public static void RegisterCanvasElementForLayoutRebuild(ICanvasElement element)
|
||||
{
|
||||
instance.InternalRegisterCanvasElementForLayoutRebuild(element);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try and add the given element to the layout rebuild list.
|
||||
/// </summary>
|
||||
/// <param name="element">The element that is needing rebuilt.</param>
|
||||
/// <returns>
|
||||
/// True if the element was successfully added to the rebuilt list.
|
||||
/// False if either already inside a Graphic Update loop OR has already been added to the list.
|
||||
/// </returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try and add the given element to the rebuild list.
|
||||
/// Will not return if successfully added.
|
||||
/// </summary>
|
||||
/// <param name="element">The element that is needing rebuilt.</param>
|
||||
public static void RegisterCanvasElementForGraphicRebuild(ICanvasElement element)
|
||||
{
|
||||
instance.InternalRegisterCanvasElementForGraphicRebuild(element);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try and add the given element to the rebuild list.
|
||||
/// </summary>
|
||||
/// <param name="element">The element that is needing rebuilt.</param>
|
||||
/// <returns>
|
||||
/// True if the element was successfully added to the rebuilt list.
|
||||
/// False if either already inside a Graphic Update loop OR has already been added to the list.
|
||||
/// </returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the given element from both the graphic and the layout rebuild lists.
|
||||
/// </summary>
|
||||
/// <param name="element"></param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Are graphics layouts currently being calculated..
|
||||
/// </summary>
|
||||
/// <returns>True if the rebuild loop is CanvasUpdate.Prelayout, CanvasUpdate.Layout or CanvasUpdate.Postlayout</returns>
|
||||
public static bool IsRebuildingLayout()
|
||||
{
|
||||
return instance.m_PerformingLayoutUpdate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Are graphics currently being rebuild.
|
||||
/// </summary>
|
||||
/// <returns>True if the rebuild loop is CanvasUpdate.PreRender or CanvasUpdate.Render</returns>
|
||||
public static bool IsRebuildingGraphics()
|
||||
{
|
||||
return instance.m_PerformingGraphicUpdate;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bfb788a43e03363419155b8af77da971
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,243 @@
|
|||
using System;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[Serializable]
|
||||
/// <summary>
|
||||
/// Structure that stores the state of a color transition on a Selectable.
|
||||
/// </summary>
|
||||
public struct ColorBlock : IEquatable<ColorBlock>
|
||||
{
|
||||
[FormerlySerializedAs("normalColor")]
|
||||
[SerializeField]
|
||||
private Color m_NormalColor;
|
||||
|
||||
[FormerlySerializedAs("highlightedColor")]
|
||||
[SerializeField]
|
||||
private Color m_HighlightedColor;
|
||||
|
||||
[FormerlySerializedAs("pressedColor")]
|
||||
[SerializeField]
|
||||
private Color m_PressedColor;
|
||||
|
||||
[FormerlySerializedAs("m_HighlightedColor")]
|
||||
[SerializeField]
|
||||
private Color m_SelectedColor;
|
||||
|
||||
[FormerlySerializedAs("disabledColor")]
|
||||
[SerializeField]
|
||||
private Color m_DisabledColor;
|
||||
|
||||
[Range(1, 5)]
|
||||
[SerializeField]
|
||||
private float m_ColorMultiplier;
|
||||
|
||||
[FormerlySerializedAs("fadeDuration")]
|
||||
[SerializeField]
|
||||
private float m_FadeDuration;
|
||||
|
||||
/// <summary>
|
||||
/// The normal color for this color block.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Button button;
|
||||
/// public Color newColor;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// //Changes the button's Normal color to the new color.
|
||||
/// ColorBlock cb = button.colors;
|
||||
/// cb.normalColor = newColor;
|
||||
/// button.colors = cb;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Color normalColor { get { return m_NormalColor; } set { m_NormalColor = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// The highlight color for this color block.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Button button;
|
||||
/// public Color newColor;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// //Changes the button's Highlighted color to the new color.
|
||||
/// ColorBlock cb = button.colors;
|
||||
/// cb.highlightedColor = newColor;
|
||||
/// button.colors = cb;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Color highlightedColor { get { return m_HighlightedColor; } set { m_HighlightedColor = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// The pressed color for this color block.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Button button;
|
||||
/// public Color newColor;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// //Changes the button's Pressed color to the new color.
|
||||
/// ColorBlock cb = button.colors;
|
||||
/// cb.pressedColor = newColor;
|
||||
/// button.colors = cb;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Color pressedColor { get { return m_PressedColor; } set { m_PressedColor = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// The selected color for this color block.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Button button;
|
||||
/// public Color newColor;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// //Changes the button's Selected color to the new color.
|
||||
/// ColorBlock cb = button.colors;
|
||||
/// cb.selectedColor = newColor;
|
||||
/// button.colors = cb;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Color selectedColor { get { return m_SelectedColor; } set { m_SelectedColor = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// The disabled color for this color block.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Button button;
|
||||
/// public Color newColor;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// //Changes the button's Disabled color to the new color.
|
||||
/// ColorBlock cb = button.colors;
|
||||
/// cb.disabledColor = newColor;
|
||||
/// button.colors = cb;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Color disabledColor { get { return m_DisabledColor; } set { m_DisabledColor = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier applied to colors (allows brightening greater then base color).
|
||||
/// </summary>
|
||||
public float colorMultiplier { get { return m_ColorMultiplier; } set { m_ColorMultiplier = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// How long a color transition between states should take.
|
||||
/// </summary>
|
||||
public float fadeDuration { get { return m_FadeDuration; } set { m_FadeDuration = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Simple getter for a code generated default ColorBlock.
|
||||
/// </summary>
|
||||
public static ColorBlock defaultColorBlock;
|
||||
|
||||
static ColorBlock()
|
||||
{
|
||||
defaultColorBlock = 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_SelectedColor = new Color32(245, 245, 245, 255),
|
||||
m_DisabledColor = new Color32(200, 200, 200, 128),
|
||||
colorMultiplier = 1.0f,
|
||||
fadeDuration = 0.1f
|
||||
};
|
||||
}
|
||||
|
||||
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 &&
|
||||
selectedColor == other.selectedColor &&
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 83088ba2132cbc940b7ca0c679a02b0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 27ed3e221887b3544bd9d6505d4a789f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,71 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.UI.Collections;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Registry class to keep track of all IClippers that exist in the scene
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is used during the CanvasUpdate loop to cull clippable elements. The clipping is called after layout, but before Graphic update.
|
||||
/// </remarks>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The singleton instance of the clipper registry.
|
||||
/// </summary>
|
||||
public static ClipperRegistry instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
s_Instance = new ClipperRegistry();
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform the clipping on all registered IClipper
|
||||
/// </summary>
|
||||
public void Cull()
|
||||
{
|
||||
var clippersCount = m_Clippers.Count;
|
||||
for (var i = 0; i < clippersCount; ++i)
|
||||
{
|
||||
m_Clippers[i].PerformClipping();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a unique IClipper element
|
||||
/// </summary>
|
||||
/// <param name="c">The clipper element to add</param>
|
||||
public static void Register(IClipper c)
|
||||
{
|
||||
if (c == null)
|
||||
return;
|
||||
instance.m_Clippers.AddUnique(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UnRegister a IClipper element
|
||||
/// </summary>
|
||||
/// <param name="c">The Element to try and remove.</param>
|
||||
public static void Unregister(IClipper c)
|
||||
{
|
||||
instance.m_Clippers.Remove(c);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9f1026265f8e3d54fb6e9f082c43debf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,51 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class to help when clipping using IClipper.
|
||||
/// </summary>
|
||||
public static class Clipping
|
||||
{
|
||||
/// <summary>
|
||||
/// Find the Rect to use for clipping.
|
||||
/// Given the input RectMask2ds find a rectangle that is the overlap of all the inputs.
|
||||
/// </summary>
|
||||
/// <param name="rectMaskParents">RectMasks to build the overlap rect from.</param>
|
||||
/// <param name="validRect">Was there a valid Rect found.</param>
|
||||
/// <returns>The final compounded overlapping rect</returns>
|
||||
public static Rect FindCullAndClipWorldRect(List<RectMask2D> rectMaskParents, out bool validRect)
|
||||
{
|
||||
if (rectMaskParents.Count == 0)
|
||||
{
|
||||
validRect = false;
|
||||
return new Rect();
|
||||
}
|
||||
|
||||
Rect current = rectMaskParents[0].canvasRect;
|
||||
Vector4 offset = rectMaskParents[0].padding;
|
||||
float xMin = current.xMin + offset.x;
|
||||
float xMax = current.xMax - offset.z;
|
||||
float yMin = current.yMin + offset.y;
|
||||
float yMax = current.yMax - offset.w;
|
||||
|
||||
var rectMaskParentsCount = rectMaskParents.Count;
|
||||
for (var i = 1; i < rectMaskParentsCount; ++i)
|
||||
{
|
||||
current = rectMaskParents[i].canvasRect;
|
||||
offset = rectMaskParents[i].padding;
|
||||
if (xMin < current.xMin + offset.x)
|
||||
xMin = current.xMin + offset.x;
|
||||
if (yMin < current.yMin + offset.y)
|
||||
yMin = current.yMin + offset.y;
|
||||
if (xMax > current.xMax - offset.z)
|
||||
xMax = current.xMax - offset.z;
|
||||
if (yMax > current.yMax - offset.w)
|
||||
yMax = current.yMax - offset.w;
|
||||
}
|
||||
|
||||
validRect = xMax > xMin && yMax > yMin;
|
||||
return validRect ? new Rect(xMin, yMin, xMax - xMin, yMax - yMin) : new Rect();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 67e3583b91179094094c6a188b232262
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,60 @@
|
|||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that can be used to recieve clipping callbacks as part of the canvas update loop.
|
||||
/// </summary>
|
||||
public interface IClipper
|
||||
{
|
||||
/// <summary>
|
||||
/// Function to to cull / clip children elements.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Called after layout and before Graphic update of the Canvas update loop.
|
||||
/// </remarks>
|
||||
|
||||
void PerformClipping();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for elements that can be clipped if they are under an IClipper
|
||||
/// </summary>
|
||||
public interface IClippable
|
||||
{
|
||||
/// <summary>
|
||||
/// GameObject of the IClippable object
|
||||
/// </summary>
|
||||
GameObject gameObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Will be called when the state of a parent IClippable changed.
|
||||
/// </summary>
|
||||
void RecalculateClipping();
|
||||
|
||||
/// <summary>
|
||||
/// The RectTransform of the clippable.
|
||||
/// </summary>
|
||||
RectTransform rectTransform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clip and cull the IClippable given a specific clipping rect
|
||||
/// </summary>
|
||||
/// <param name="clipRect">The Rectangle in which to clip against.</param>
|
||||
/// <param name="validRect">Is the Rect valid. If not then the rect has 0 size.</param>
|
||||
void Cull(Rect clipRect, bool validRect);
|
||||
|
||||
/// <summary>
|
||||
/// Set the clip rect for the IClippable.
|
||||
/// </summary>
|
||||
/// <param name="value">The Rectangle for the clipping</param>
|
||||
/// <param name="validRect">Is the rect valid.</param>
|
||||
void SetClipRect(Rect value, bool validRect);
|
||||
|
||||
/// <summary>
|
||||
/// Set the clip softness for the IClippable.
|
||||
///
|
||||
/// The softness is a linear alpha falloff over clipSoftness pixels.
|
||||
/// </summary>
|
||||
/// <param name="clipSoftness">The number of pixels to apply the softness to </param>
|
||||
void SetClipSoftness(Vector2 clipSoftness);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 371b6b9c8adc50745ace66a6fdf67481
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c606041f02fc7d542b1429806872292b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,802 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for creating default implementations of builtin UI controls.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The recommended workflow for using UI controls with the UI system is to create a prefab for each type of control and instantiate those when needed. This way changes can be made to the prefabs which immediately have effect on all used instances.
|
||||
///
|
||||
/// However, in certain cases there can be reasons to create UI controls entirely from code. The DefaultControls class provide methods to create each of the builtin UI controls. The resulting objects are the same as are obtained from using the corresponding UI menu entries in the GameObject menu in the Editor.
|
||||
///
|
||||
/// An example use of this is creating menu items for custom new UI controls that mimics the ones that are builtin in Unity. Some such UI controls may contain other UI controls. For example, a scroll view contains scrollbars.By using the DefaultControls methods to create those parts, it is ensured that they are identical in look and setup to the ones provided in the menu items builtin with Unity.
|
||||
///
|
||||
/// Note that the details of the setup of the UI controls created by the methods in this class may change with later revisions of the UI system.As such, they are not guaranteed to be 100% backwards compatible. It is recommended not to rely on the specific hierarchies of the GameObjects created by these methods, and limit your code to only interface with the root GameObject created by each method.
|
||||
/// </remarks>
|
||||
public static class DefaultControls
|
||||
{
|
||||
static IFactoryControls m_CurrentFactory = DefaultRuntimeFactory.Default;
|
||||
public static IFactoryControls factory
|
||||
{
|
||||
get { return m_CurrentFactory; }
|
||||
#if UNITY_EDITOR
|
||||
set { m_CurrentFactory = value; }
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory interface to create a GameObject in this class.
|
||||
/// It is necessary to use this interface in the whole class so MenuOption and Editor can work using ObjectFactory and default Presets.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The only available method is CreateGameObject.
|
||||
/// It needs to be called with every Components the created Object will need because of a bug with Undo and RectTransform.
|
||||
/// Adding a UI component on the created GameObject may crash if done after Undo.SetTransformParent,
|
||||
/// So it's better to prevent such behavior in this class by asking for full creation with all the components.
|
||||
/// </remarks>
|
||||
public interface IFactoryControls
|
||||
{
|
||||
GameObject CreateGameObject(string name, params Type[] components);
|
||||
}
|
||||
|
||||
private class DefaultRuntimeFactory : IFactoryControls
|
||||
{
|
||||
public static IFactoryControls Default = new DefaultRuntimeFactory();
|
||||
|
||||
public GameObject CreateGameObject(string name, params Type[] components)
|
||||
{
|
||||
return new GameObject(name, components);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Object used to pass resources to use for the default controls.
|
||||
/// </summary>
|
||||
public struct Resources
|
||||
{
|
||||
/// <summary>
|
||||
/// The primary sprite to be used for graphical UI elements, used by the button, toggle, and dropdown controls, among others.
|
||||
/// </summary>
|
||||
public Sprite standard;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite used for background elements.
|
||||
/// </summary>
|
||||
public Sprite background;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite used as background for input fields.
|
||||
/// </summary>
|
||||
public Sprite inputField;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite used for knobs that can be dragged, such as on a slider.
|
||||
/// </summary>
|
||||
public Sprite knob;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite used for representation of an "on" state when present, such as a checkmark.
|
||||
/// </summary>
|
||||
public Sprite checkmark;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite used to indicate that a button will open a dropdown when clicked.
|
||||
/// </summary>
|
||||
public Sprite dropdown;
|
||||
|
||||
/// <summary>
|
||||
/// Sprite used for masking purposes, for example to be used for the viewport of a scroll view.
|
||||
/// </summary>
|
||||
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, params Type[] components)
|
||||
{
|
||||
GameObject child = factory.CreateGameObject(name, components);
|
||||
RectTransform rectTransform = child.GetComponent<RectTransform>();
|
||||
rectTransform.sizeDelta = size;
|
||||
return child;
|
||||
}
|
||||
|
||||
private static GameObject CreateUIObject(string name, GameObject parent, params Type[] components)
|
||||
{
|
||||
GameObject go = factory.CreateGameObject(name, components);
|
||||
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;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Undo.SetTransformParent(child.transform, parent.transform, "");
|
||||
#else
|
||||
child.transform.SetParent(parent.transform, false);
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI Panel.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Image
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreatePanel(Resources resources)
|
||||
{
|
||||
GameObject panelRoot = CreateUIElementRoot("Panel", s_ThickElementSize, typeof(Image));
|
||||
|
||||
// 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.GetComponent<Image>();
|
||||
image.sprite = resources.background;
|
||||
image.type = Image.Type.Sliced;
|
||||
image.color = s_PanelColor;
|
||||
|
||||
return panelRoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI button.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Button
|
||||
/// -Text
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateButton(Resources resources)
|
||||
{
|
||||
GameObject buttonRoot = CreateUIElementRoot("Button", s_ThickElementSize, typeof(Image), typeof(Button));
|
||||
|
||||
GameObject childText = CreateUIObject("Text", buttonRoot, typeof(Text));
|
||||
|
||||
Image image = buttonRoot.GetComponent<Image>();
|
||||
image.sprite = resources.standard;
|
||||
image.type = Image.Type.Sliced;
|
||||
image.color = s_DefaultSelectableColor;
|
||||
|
||||
Button bt = buttonRoot.GetComponent<Button>();
|
||||
SetDefaultColorTransitionValues(bt);
|
||||
|
||||
Text text = childText.GetComponent<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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI Text.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Text
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateText(Resources resources)
|
||||
{
|
||||
GameObject go = CreateUIElementRoot("Text", s_ThickElementSize, typeof(Text));
|
||||
|
||||
Text lbl = go.GetComponent<Text>();
|
||||
lbl.text = "New Text";
|
||||
SetDefaultTextValues(lbl);
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI Image.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Image
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateImage(Resources resources)
|
||||
{
|
||||
GameObject go = CreateUIElementRoot("Image", s_ImageElementSize, typeof(Image));
|
||||
return go;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI RawImage.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// RawImage
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateRawImage(Resources resources)
|
||||
{
|
||||
GameObject go = CreateUIElementRoot("RawImage", s_ImageElementSize, typeof(RawImage));
|
||||
return go;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI Slider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Slider
|
||||
/// - Background
|
||||
/// - Fill Area
|
||||
/// - Fill
|
||||
/// - Handle Slide Area
|
||||
/// - Handle
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateSlider(Resources resources)
|
||||
{
|
||||
// Create GOs Hierarchy
|
||||
GameObject root = CreateUIElementRoot("Slider", s_ThinElementSize, typeof(Slider));
|
||||
|
||||
GameObject background = CreateUIObject("Background", root, typeof(Image));
|
||||
GameObject fillArea = CreateUIObject("Fill Area", root, typeof(RectTransform));
|
||||
GameObject fill = CreateUIObject("Fill", fillArea, typeof(Image));
|
||||
GameObject handleArea = CreateUIObject("Handle Slide Area", root, typeof(RectTransform));
|
||||
GameObject handle = CreateUIObject("Handle", handleArea, typeof(Image));
|
||||
|
||||
// Background
|
||||
Image backgroundImage = background.GetComponent<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.GetComponent<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.GetComponent<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.GetComponent<Slider>();
|
||||
slider.fillRect = fill.GetComponent<RectTransform>();
|
||||
slider.handleRect = handle.GetComponent<RectTransform>();
|
||||
slider.targetGraphic = handleImage;
|
||||
slider.direction = Slider.Direction.LeftToRight;
|
||||
SetDefaultColorTransitionValues(slider);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI Scrollbar.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Scrollbar
|
||||
/// - Sliding Area
|
||||
/// - Handle
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateScrollbar(Resources resources)
|
||||
{
|
||||
// Create GOs Hierarchy
|
||||
GameObject scrollbarRoot = CreateUIElementRoot("Scrollbar", s_ThinElementSize, typeof(Image), typeof(Scrollbar));
|
||||
|
||||
GameObject sliderArea = CreateUIObject("Sliding Area", scrollbarRoot, typeof(RectTransform));
|
||||
GameObject handle = CreateUIObject("Handle", sliderArea, typeof(Image));
|
||||
|
||||
Image bgImage = scrollbarRoot.GetComponent<Image>();
|
||||
bgImage.sprite = resources.background;
|
||||
bgImage.type = Image.Type.Sliced;
|
||||
bgImage.color = s_DefaultSelectableColor;
|
||||
|
||||
Image handleImage = handle.GetComponent<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.GetComponent<Scrollbar>();
|
||||
scrollbar.handleRect = handleRect;
|
||||
scrollbar.targetGraphic = handleImage;
|
||||
SetDefaultColorTransitionValues(scrollbar);
|
||||
|
||||
return scrollbarRoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI Toggle.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Toggle
|
||||
/// - Background
|
||||
/// - Checkmark
|
||||
/// - Label
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateToggle(Resources resources)
|
||||
{
|
||||
// Set up hierarchy
|
||||
GameObject toggleRoot = CreateUIElementRoot("Toggle", s_ThinElementSize, typeof(Toggle));
|
||||
|
||||
GameObject background = CreateUIObject("Background", toggleRoot, typeof(Image));
|
||||
GameObject checkmark = CreateUIObject("Checkmark", background, typeof(Image));
|
||||
GameObject childLabel = CreateUIObject("Label", toggleRoot, typeof(Text));
|
||||
|
||||
// Set up components
|
||||
Toggle toggle = toggleRoot.GetComponent<Toggle>();
|
||||
toggle.isOn = true;
|
||||
|
||||
Image bgImage = background.GetComponent<Image>();
|
||||
bgImage.sprite = resources.standard;
|
||||
bgImage.type = Image.Type.Sliced;
|
||||
bgImage.color = s_DefaultSelectableColor;
|
||||
|
||||
Image checkmarkImage = checkmark.GetComponent<Image>();
|
||||
checkmarkImage.sprite = resources.checkmark;
|
||||
|
||||
Text label = childLabel.GetComponent<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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI input field.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// InputField
|
||||
/// - PlaceHolder
|
||||
/// - Text
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateInputField(Resources resources)
|
||||
{
|
||||
GameObject root = CreateUIElementRoot("InputField", s_ThickElementSize, typeof(Image), typeof(InputField));
|
||||
|
||||
GameObject childPlaceholder = CreateUIObject("Placeholder", root, typeof(Text));
|
||||
GameObject childText = CreateUIObject("Text", root, typeof(Text));
|
||||
|
||||
Image image = root.GetComponent<Image>();
|
||||
image.sprite = resources.inputField;
|
||||
image.type = Image.Type.Sliced;
|
||||
image.color = s_DefaultSelectableColor;
|
||||
|
||||
InputField inputField = root.GetComponent<InputField>();
|
||||
SetDefaultColorTransitionValues(inputField);
|
||||
|
||||
Text text = childText.GetComponent<Text>();
|
||||
text.text = "";
|
||||
text.supportRichText = false;
|
||||
SetDefaultTextValues(text);
|
||||
|
||||
Text placeholder = childPlaceholder.GetComponent<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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI dropdown.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Dropdown
|
||||
/// - Label
|
||||
/// - Arrow
|
||||
/// - Template
|
||||
/// - Viewport
|
||||
/// - Content
|
||||
/// - Item
|
||||
/// - Item Background
|
||||
/// - Item Checkmark
|
||||
/// - Item Label
|
||||
/// - Scrollbar
|
||||
/// - Sliding Area
|
||||
/// - Handle
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateDropdown(Resources resources)
|
||||
{
|
||||
GameObject root = CreateUIElementRoot("Dropdown", s_ThickElementSize, typeof(Image), typeof(Dropdown));
|
||||
|
||||
GameObject label = CreateUIObject("Label", root, typeof(Text));
|
||||
GameObject arrow = CreateUIObject("Arrow", root, typeof(Image));
|
||||
GameObject template = CreateUIObject("Template", root, typeof(Image), typeof(ScrollRect));
|
||||
GameObject viewport = CreateUIObject("Viewport", template, typeof(Image), typeof(Mask));
|
||||
GameObject content = CreateUIObject("Content", viewport, typeof(RectTransform));
|
||||
GameObject item = CreateUIObject("Item", content, typeof(Toggle));
|
||||
GameObject itemBackground = CreateUIObject("Item Background", item, typeof(Image));
|
||||
GameObject itemCheckmark = CreateUIObject("Item Checkmark", item, typeof(Image));
|
||||
GameObject itemLabel = CreateUIObject("Item Label", item, typeof(Text));
|
||||
|
||||
// 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.GetComponent<Text>();
|
||||
SetDefaultTextValues(itemLabelText);
|
||||
itemLabelText.alignment = TextAnchor.MiddleLeft;
|
||||
|
||||
Image itemBackgroundImage = itemBackground.GetComponent<Image>();
|
||||
itemBackgroundImage.color = new Color32(245, 245, 245, 255);
|
||||
|
||||
Image itemCheckmarkImage = itemCheckmark.GetComponent<Image>();
|
||||
itemCheckmarkImage.sprite = resources.checkmark;
|
||||
|
||||
Toggle itemToggle = item.GetComponent<Toggle>();
|
||||
itemToggle.targetGraphic = itemBackgroundImage;
|
||||
itemToggle.graphic = itemCheckmarkImage;
|
||||
itemToggle.isOn = true;
|
||||
|
||||
// Setup template UI components.
|
||||
|
||||
Image templateImage = template.GetComponent<Image>();
|
||||
templateImage.sprite = resources.standard;
|
||||
templateImage.type = Image.Type.Sliced;
|
||||
|
||||
ScrollRect templateScrollRect = template.GetComponent<ScrollRect>();
|
||||
templateScrollRect.content = content.GetComponent<RectTransform>();
|
||||
templateScrollRect.viewport = viewport.GetComponent<RectTransform>();
|
||||
templateScrollRect.horizontal = false;
|
||||
templateScrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||
templateScrollRect.verticalScrollbar = scrollbarScrollbar;
|
||||
templateScrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
|
||||
templateScrollRect.verticalScrollbarSpacing = -3;
|
||||
|
||||
Mask scrollRectMask = viewport.GetComponent<Mask>();
|
||||
scrollRectMask.showMaskGraphic = false;
|
||||
|
||||
Image viewportImage = viewport.GetComponent<Image>();
|
||||
viewportImage.sprite = resources.mask;
|
||||
viewportImage.type = Image.Type.Sliced;
|
||||
|
||||
// Setup dropdown UI components.
|
||||
|
||||
Text labelText = label.GetComponent<Text>();
|
||||
SetDefaultTextValues(labelText);
|
||||
labelText.alignment = TextAnchor.MiddleLeft;
|
||||
|
||||
Image arrowImage = arrow.GetComponent<Image>();
|
||||
arrowImage.sprite = resources.dropdown;
|
||||
|
||||
Image backgroundImage = root.GetComponent<Image>();
|
||||
backgroundImage.sprite = resources.standard;
|
||||
backgroundImage.color = s_DefaultSelectableColor;
|
||||
backgroundImage.type = Image.Type.Sliced;
|
||||
|
||||
Dropdown dropdown = root.GetComponent<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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the basic UI Scrollview.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Hierarchy:
|
||||
/// (root)
|
||||
/// Scrollview
|
||||
/// - Viewport
|
||||
/// - Content
|
||||
/// - Scrollbar Horizontal
|
||||
/// - Sliding Area
|
||||
/// - Handle
|
||||
/// - Scrollbar Vertical
|
||||
/// - Sliding Area
|
||||
/// - Handle
|
||||
/// </remarks>
|
||||
/// <param name="resources">The resources to use for creation.</param>
|
||||
/// <returns>The root GameObject of the created element.</returns>
|
||||
public static GameObject CreateScrollView(Resources resources)
|
||||
{
|
||||
GameObject root = CreateUIElementRoot("Scroll View", new Vector2(200, 200), typeof(Image), typeof(ScrollRect));
|
||||
|
||||
GameObject viewport = CreateUIObject("Viewport", root, typeof(Image), typeof(Mask));
|
||||
GameObject content = CreateUIObject("Content", viewport, typeof(RectTransform));
|
||||
|
||||
// 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.GetComponent<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.GetComponent<Image>();
|
||||
rootImage.sprite = resources.background;
|
||||
rootImage.type = Image.Type.Sliced;
|
||||
rootImage.color = s_PanelColor;
|
||||
|
||||
Mask viewportMask = viewport.GetComponent<Mask>();
|
||||
viewportMask.showMaskGraphic = false;
|
||||
|
||||
Image viewportImage = viewport.GetComponent<Image>();
|
||||
viewportImage.sprite = resources.mask;
|
||||
viewportImage.type = Image.Type.Sliced;
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bf87e20fb5440ec498f441167b5291f8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0d0b652f32a2cc243917e4028fa0f046
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,199 @@
|
|||
using System;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[Serializable]
|
||||
/// <summary>
|
||||
/// Struct for storing Text generation settings.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Get a font data with sensible defaults.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Font to use for this generated Text object.
|
||||
/// </summary>
|
||||
public Font font
|
||||
{
|
||||
get { return m_Font; }
|
||||
set { m_Font = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Font size to use for this generated Text object.
|
||||
/// </summary>
|
||||
public int fontSize
|
||||
{
|
||||
get { return m_FontSize; }
|
||||
set { m_FontSize = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The font style to use for this generated Text object.
|
||||
/// </summary>
|
||||
public FontStyle fontStyle
|
||||
{
|
||||
get { return m_FontStyle; }
|
||||
set { m_FontStyle = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is best fit used for this generated Text object.
|
||||
/// </summary>
|
||||
public bool bestFit
|
||||
{
|
||||
get { return m_BestFit; }
|
||||
set { m_BestFit = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The min size for this generated Text object.
|
||||
/// </summary>
|
||||
public int minSize
|
||||
{
|
||||
get { return m_MinSize; }
|
||||
set { m_MinSize = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The max size for this generated Text object.
|
||||
/// </summary>
|
||||
public int maxSize
|
||||
{
|
||||
get { return m_MaxSize; }
|
||||
set { m_MaxSize = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How is the text aligned for this generated Text object.
|
||||
/// </summary>
|
||||
public TextAnchor alignment
|
||||
{
|
||||
get { return m_Alignment; }
|
||||
set { m_Alignment = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use the extents of glyph geometry to perform horizontal alignment rather than glyph metrics.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can result in better fitting left and right alignment, but may result in incorrect positioning when attempting to overlay multiple fonts (such as a specialized outline font) on top of each other.
|
||||
/// </remarks>
|
||||
public bool alignByGeometry
|
||||
{
|
||||
get { return m_AlignByGeometry; }
|
||||
set { m_AlignByGeometry = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should rich text be used for this generated Text object.
|
||||
/// </summary>
|
||||
public bool richText
|
||||
{
|
||||
get { return m_RichText; }
|
||||
set { m_RichText = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The horizontal overflow policy for this generated Text object.
|
||||
/// </summary>
|
||||
public HorizontalWrapMode horizontalOverflow
|
||||
{
|
||||
get { return m_HorizontalOverflow; }
|
||||
set { m_HorizontalOverflow = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The vertical overflow policy for this generated Text object.
|
||||
/// </summary>
|
||||
public VerticalWrapMode verticalOverflow
|
||||
{
|
||||
get { return m_VerticalOverflow; }
|
||||
set { m_VerticalOverflow = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The line spaceing for this generated Text object.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 13cd966480ec3354bb318ee1aeccff6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,79 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class that is used to help with Text update.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When Unity rebuilds a font atlas a callback is sent to the font. Using this class you can register your text as needing to be rebuilt if the font atlas is updated.
|
||||
/// </remarks>
|
||||
public static class FontUpdateTracker
|
||||
{
|
||||
static Dictionary<Font, HashSet<Text>> m_Tracked = new Dictionary<Font, HashSet<Text>>();
|
||||
|
||||
/// <summary>
|
||||
/// Register a Text element for receiving texture atlas rebuild calls.
|
||||
/// </summary>
|
||||
/// <param name="t">The Text object to track</param>
|
||||
public static void TrackText(Text t)
|
||||
{
|
||||
if (t.font == null)
|
||||
return;
|
||||
|
||||
HashSet<Text> exists;
|
||||
m_Tracked.TryGetValue(t.font, out exists);
|
||||
if (exists == null)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deregister a Text element from receiving texture atlas rebuild calls.
|
||||
/// </summary>
|
||||
/// <param name="t">The Text object to no longer track</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 39ab466162988eb4f83443f911bbf5c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1030
Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Graphic.cs
Normal file
1030
Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Graphic.cs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a6238e9452bfc704f82ff36791fe1a45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,352 @@
|
|||
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))]
|
||||
/// <summary>
|
||||
/// A derived BaseRaycaster to raycast against Graphic elements.
|
||||
/// </summary>
|
||||
public class GraphicRaycaster : BaseRaycaster
|
||||
{
|
||||
protected const int kNoEventMaskSet = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Type of raycasters to check against to check for canvas blocking elements.
|
||||
/// </summary>
|
||||
public enum BlockingObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform no raycasts.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Perform a 2D raycast check to check for blocking 2D elements
|
||||
/// </summary>
|
||||
TwoD = 1,
|
||||
/// <summary>
|
||||
/// Perform a 3D raycast check to check for blocking 3D elements
|
||||
/// </summary>
|
||||
ThreeD = 2,
|
||||
/// <summary>
|
||||
/// Perform a 2D and a 3D raycasts to check for blocking 2D and 3D elements.
|
||||
/// </summary>
|
||||
All = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority of the raycaster based upon sort order.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The sortOrder priority.
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority of the raycaster based upon render order.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The renderOrder priority.
|
||||
/// </returns>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Whether Graphics facing away from the raycaster are checked for raycasts.
|
||||
/// </summary>
|
||||
public bool ignoreReversedGraphics { get {return m_IgnoreReversedGraphics; } set { m_IgnoreReversedGraphics = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// The type of objects that are checked to determine if they block graphic raycasts.
|
||||
/// </summary>
|
||||
public BlockingObjects blockingObjects { get {return m_BlockingObjects; } set { m_BlockingObjects = value; } }
|
||||
|
||||
[SerializeField]
|
||||
protected LayerMask m_BlockingMask = kNoEventMaskSet;
|
||||
|
||||
/// <summary>
|
||||
/// The type of objects specified through LayerMask that are checked to determine if they block graphic raycasts.
|
||||
/// </summary>
|
||||
public LayerMask blockingMask { get { return m_BlockingMask; } set { m_BlockingMask = value; } }
|
||||
|
||||
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>();
|
||||
|
||||
/// <summary>
|
||||
/// Perform the raycast against the list of graphics associated with the Canvas.
|
||||
/// </summary>
|
||||
/// <param name="eventData">Current event data</param>
|
||||
/// <param name="resultAppendList">List of hit objects to append new results to.</param>
|
||||
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
|
||||
{
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
var canvasGraphics = GraphicRegistry.GetRaycastableGraphicsForCanvas(canvas);
|
||||
if (canvasGraphics == null || canvasGraphics.Count == 0)
|
||||
return;
|
||||
|
||||
int displayIndex;
|
||||
var currentEventCamera = eventCamera; // Property 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 PACKAGE_PHYSICS
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if PACKAGE_PHYSICS2D
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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 cameraForward = currentEventCamera.transform.rotation * Vector3.forward * currentEventCamera.nearClipPlane;
|
||||
appendGraphic = Vector3.Dot(go.transform.position - currentEventCamera.transform.position - cameraForward, go.transform.forward) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (appendGraphic)
|
||||
{
|
||||
float distance = 0;
|
||||
Transform trans = go.transform;
|
||||
Vector3 transForward = trans.forward;
|
||||
|
||||
if (currentEventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
distance = 0;
|
||||
else
|
||||
{
|
||||
// http://geomalgorithms.com/a06-_intersect-2.html
|
||||
distance = (Vector3.Dot(transForward, trans.position - ray.origin) / 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,
|
||||
displayIndex = displayIndex,
|
||||
index = resultAppendList.Count,
|
||||
depth = m_RaycastResults[index].depth,
|
||||
sortingLayer = canvas.sortingLayerID,
|
||||
sortingOrder = canvas.sortingOrder,
|
||||
worldPosition = ray.origin + ray.direction * distance,
|
||||
worldNormal = -transForward
|
||||
};
|
||||
resultAppendList.Add(castResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The camera that will generate rays for this raycaster.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// - Null if Camera mode is ScreenSpaceOverlay or ScreenSpaceCamera and has no camera.
|
||||
/// - canvas.worldCanvas if not null
|
||||
/// - Camera.main.
|
||||
/// </returns>
|
||||
public override Camera eventCamera
|
||||
{
|
||||
get
|
||||
{
|
||||
var canvas = this.canvas;
|
||||
var renderMode = canvas.renderMode;
|
||||
if (renderMode == RenderMode.ScreenSpaceOverlay
|
||||
|| (renderMode == RenderMode.ScreenSpaceCamera && canvas.worldCamera == null))
|
||||
return null;
|
||||
|
||||
return 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)
|
||||
{
|
||||
// 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.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
|
||||
continue;
|
||||
|
||||
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera, graphic.raycastPadding))
|
||||
continue;
|
||||
|
||||
if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
|
||||
continue;
|
||||
|
||||
if (graphic.Raycast(pointerPosition, eventCamera))
|
||||
{
|
||||
s_SortedGraphics.Add(graphic);
|
||||
}
|
||||
}
|
||||
|
||||
s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
|
||||
totalCount = s_SortedGraphics.Count;
|
||||
for (int i = 0; i < totalCount; ++i)
|
||||
results.Add(s_SortedGraphics[i]);
|
||||
|
||||
s_SortedGraphics.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dc42784cf147c0c48a680349fa168899
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,50 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.UI.Collections;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// EditorOnly class for tracking all Graphics.
|
||||
/// Used when a source asset is reimported into the editor to ensure that Graphics are updated as intended.
|
||||
/// </summary>
|
||||
public static class GraphicRebuildTracker
|
||||
{
|
||||
static IndexedSet<Graphic> m_Tracked = new IndexedSet<Graphic>();
|
||||
static bool s_Initialized;
|
||||
|
||||
/// <summary>
|
||||
/// Add a Graphic to the list of tracked Graphics
|
||||
/// </summary>
|
||||
/// <param name="g">The graphic to track</param>
|
||||
public static void TrackGraphic(Graphic g)
|
||||
{
|
||||
if (!s_Initialized)
|
||||
{
|
||||
CanvasRenderer.onRequestRebuild += OnRebuildRequested;
|
||||
s_Initialized = true;
|
||||
}
|
||||
|
||||
m_Tracked.AddUnique(g);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a Graphic to the list of tracked Graphics
|
||||
/// </summary>
|
||||
/// <param name="g">The graphic to remove from tracking.</param>
|
||||
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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 445cdcfc747eba94288b97f5869aa2fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,170 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.UI.Collections;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Registry which maps a Graphic to the canvas it belongs to.
|
||||
/// </summary>
|
||||
public class GraphicRegistry
|
||||
{
|
||||
private static GraphicRegistry s_Instance;
|
||||
|
||||
private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
|
||||
private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_RaycastableGraphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
|
||||
|
||||
protected GraphicRegistry()
|
||||
{
|
||||
// Avoid runtime generation of these types. Some platforms are AOT only and do not support
|
||||
// JIT. What's more we actually create a instance of the required types instead of
|
||||
// just declaring an unused variable which may be optimized away by some compilers (Mono vs MS).
|
||||
|
||||
// See: 877060
|
||||
|
||||
System.GC.KeepAlive(new Dictionary<Graphic, int>());
|
||||
System.GC.KeepAlive(new Dictionary<ICanvasElement, int>());
|
||||
System.GC.KeepAlive(new Dictionary<IClipper, int>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The singleton instance of the GraphicRegistry. Creates a new instance if it does not exist.
|
||||
/// </summary>
|
||||
public static GraphicRegistry instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Instance == null)
|
||||
s_Instance = new GraphicRegistry();
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Associates a Graphic with a Canvas and stores this association in the registry.
|
||||
/// </summary>
|
||||
/// <param name="c">The canvas being associated with the Graphic.</param>
|
||||
/// <param name="graphic">The Graphic being associated with the Canvas.</param>
|
||||
public static void RegisterGraphicForCanvas(Canvas c, Graphic graphic)
|
||||
{
|
||||
if (c == null || graphic == null)
|
||||
return;
|
||||
|
||||
IndexedSet<Graphic> graphics;
|
||||
instance.m_Graphics.TryGetValue(c, out graphics);
|
||||
|
||||
if (graphics != null)
|
||||
{
|
||||
graphics.AddUnique(graphic);
|
||||
|
||||
RegisterRaycastGraphicForCanvas(c, 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);
|
||||
|
||||
RegisterRaycastGraphicForCanvas(c, graphic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Associates a raycastable Graphic with a Canvas and stores this association in the registry.
|
||||
/// </summary>
|
||||
/// <param name="c">The canvas being associated with the Graphic.</param>
|
||||
/// <param name="graphic">The Graphic being associated with the Canvas.</param>
|
||||
public static void RegisterRaycastGraphicForCanvas(Canvas c, Graphic graphic)
|
||||
{
|
||||
if (c == null || graphic == null || !graphic.raycastTarget)
|
||||
return;
|
||||
|
||||
IndexedSet<Graphic> graphics;
|
||||
instance.m_RaycastableGraphics.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_RaycastableGraphics.Add(c, graphics);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dissociates a Graphic from a Canvas, removing this association from the registry.
|
||||
/// </summary>
|
||||
/// <param name="c">The Canvas to dissociate from the Graphic.</param>
|
||||
/// <param name="graphic">The Graphic to dissociate from the Canvas.</param>
|
||||
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);
|
||||
|
||||
UnregisterRaycastGraphicForCanvas(c, graphic);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dissociates a Graphic from a Canvas, removing this association from the registry.
|
||||
/// </summary>
|
||||
/// <param name="c">The Canvas to dissociate from the Graphic.</param>
|
||||
/// <param name="graphic">The Graphic to dissociate from the Canvas.</param>
|
||||
public static void UnregisterRaycastGraphicForCanvas(Canvas c, Graphic graphic)
|
||||
{
|
||||
if (c == null || !graphic.raycastTarget)
|
||||
return;
|
||||
|
||||
IndexedSet<Graphic> graphics;
|
||||
if (instance.m_RaycastableGraphics.TryGetValue(c, out graphics))
|
||||
{
|
||||
graphics.Remove(graphic);
|
||||
|
||||
if (graphics.Count == 0)
|
||||
instance.m_RaycastableGraphics.Remove(c);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<Graphic> s_EmptyList = new List<Graphic>();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of Graphics associated with a Canvas.
|
||||
/// </summary>
|
||||
/// <param name="canvas">The Canvas to search</param>
|
||||
/// <returns>Returns a list of Graphics. Returns an empty list if no Graphics are associated with the specified Canvas.</returns>
|
||||
public static IList<Graphic> GetGraphicsForCanvas(Canvas canvas)
|
||||
{
|
||||
IndexedSet<Graphic> graphics;
|
||||
if (instance.m_Graphics.TryGetValue(canvas, out graphics))
|
||||
return graphics;
|
||||
|
||||
return s_EmptyList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the list of Graphics that are raycastable and associated with a Canvas.
|
||||
/// </summary>
|
||||
/// <param name="canvas">The Canvas to search</param>
|
||||
/// <returns>Returns a list of Graphics. Returns an empty list if no Graphics are associated with the specified Canvas.</returns>
|
||||
public static IList<Graphic> GetRaycastableGraphicsForCanvas(Canvas canvas)
|
||||
{
|
||||
IndexedSet<Graphic> graphics;
|
||||
if (instance.m_RaycastableGraphics.TryGetValue(canvas, out graphics))
|
||||
return graphics;
|
||||
|
||||
return s_EmptyList;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 69e3979b7029e8a4da2d96b714ba5c3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[Obsolete("Not supported anymore")]
|
||||
interface IGraphicEnabledDisabled
|
||||
{
|
||||
void OnSiblingGraphicEnabledDisabled();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 928dc55e2c8c3ee4dad33b6d561cb6ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
[Obsolete("Not supported anymore.", true)]
|
||||
public interface IMask
|
||||
{
|
||||
bool Enabled();
|
||||
RectTransform rectTransform { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ceb629fc661813d40986b4abbefe72c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// This element is capable of being masked out.
|
||||
/// </summary>
|
||||
public interface IMaskable
|
||||
{
|
||||
/// <summary>
|
||||
/// Recalculate masking for this element and all children elements.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use this to update the internal state (recreate materials etc).
|
||||
/// </remarks>
|
||||
void RecalculateMasking();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 61a20120cddc53849bbc10fc805ffe3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1901
Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Image.cs
Normal file
1901
Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Image.cs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fe87c0e1cc204ed48ad3b37840f39efc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d199490a83bb2b844b9695cbf13b01ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1999349e7f492c947bb6eb70f624382e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,256 @@
|
|||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("Layout/Aspect Ratio Fitter", 142)]
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[DisallowMultipleComponent]
|
||||
/// <summary>
|
||||
/// Resizes a RectTransform to fit a specified aspect ratio.
|
||||
/// </summary>
|
||||
public class AspectRatioFitter : UIBehaviour, ILayoutSelfController
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a mode to use to enforce an aspect ratio.
|
||||
/// </summary>
|
||||
public enum AspectMode
|
||||
{
|
||||
/// <summary>
|
||||
/// The aspect ratio is not enforced
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Changes the height of the rectangle to match the aspect ratio.
|
||||
/// </summary>
|
||||
WidthControlsHeight,
|
||||
/// <summary>
|
||||
/// Changes the width of the rectangle to match the aspect ratio.
|
||||
/// </summary>
|
||||
HeightControlsWidth,
|
||||
/// <summary>
|
||||
/// Sizes the rectangle such that it's fully contained within the parent rectangle.
|
||||
/// </summary>
|
||||
FitInParent,
|
||||
/// <summary>
|
||||
/// Sizes the rectangle such that the parent rectangle is fully contained within.
|
||||
/// </summary>
|
||||
EnvelopeParent
|
||||
}
|
||||
|
||||
[SerializeField] private AspectMode m_AspectMode = AspectMode.None;
|
||||
|
||||
/// <summary>
|
||||
/// The mode to use to enforce the aspect ratio.
|
||||
/// </summary>
|
||||
public AspectMode aspectMode { get { return m_AspectMode; } set { if (SetPropertyUtility.SetStruct(ref m_AspectMode, value)) SetDirty(); } }
|
||||
|
||||
[SerializeField] private float m_AspectRatio = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The aspect ratio to enforce. This means width divided by height.
|
||||
/// </summary>
|
||||
public float aspectRatio { get { return m_AspectRatio; } set { if (SetPropertyUtility.SetStruct(ref m_AspectRatio, value)) SetDirty(); } }
|
||||
|
||||
[System.NonSerialized]
|
||||
private RectTransform m_Rect;
|
||||
|
||||
// This "delayed" mechanism is required for case 1014834.
|
||||
private bool m_DelayedSetDirty = false;
|
||||
|
||||
//Does the gameobject has a parent for reference to enable FitToParent/EnvelopeParent modes.
|
||||
private bool m_DoesParentExist = false;
|
||||
|
||||
private RectTransform rectTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Rect == null)
|
||||
m_Rect = GetComponent<RectTransform>();
|
||||
return m_Rect;
|
||||
}
|
||||
}
|
||||
|
||||
// field is never assigned warning
|
||||
#pragma warning disable 649
|
||||
private DrivenRectTransformTracker m_Tracker;
|
||||
#pragma warning restore 649
|
||||
|
||||
protected AspectRatioFitter() {}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
m_DoesParentExist = rectTransform.parent ? true : false;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
//Disable the component if the aspect mode is not valid or the object state/setup is not supported with AspectRatio setup.
|
||||
if (!IsComponentValidOnObject() || !IsAspectModeValid())
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
m_Tracker.Clear();
|
||||
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
protected override void OnTransformParentChanged()
|
||||
{
|
||||
base.OnTransformParentChanged();
|
||||
|
||||
m_DoesParentExist = rectTransform.parent ? true : false;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the rect based on the delayed dirty.
|
||||
/// Got around issue of calling onValidate from OnEnable function.
|
||||
/// </summary>
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (m_DelayedSetDirty)
|
||||
{
|
||||
m_DelayedSetDirty = false;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function called when this RectTransform or parent RectTransform has changed dimensions.
|
||||
/// </summary>
|
||||
protected override void OnRectTransformDimensionsChange()
|
||||
{
|
||||
UpdateRect();
|
||||
}
|
||||
|
||||
private void UpdateRect()
|
||||
{
|
||||
if (!IsActive() || !IsComponentValidOnObject())
|
||||
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:
|
||||
{
|
||||
if (!DoesParentExists())
|
||||
break;
|
||||
|
||||
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;
|
||||
return !parent ? Vector2.zero : parent.rect.size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method called by the layout system. Has no effect
|
||||
/// </summary>
|
||||
public virtual void SetLayoutHorizontal() {}
|
||||
|
||||
/// <summary>
|
||||
/// Method called by the layout system. Has no effect
|
||||
/// </summary>
|
||||
public virtual void SetLayoutVertical() {}
|
||||
|
||||
/// <summary>
|
||||
/// Mark the AspectRatioFitter as dirty.
|
||||
/// </summary>
|
||||
protected void SetDirty()
|
||||
{
|
||||
UpdateRect();
|
||||
}
|
||||
|
||||
public bool IsComponentValidOnObject()
|
||||
{
|
||||
Canvas canvas = gameObject.GetComponent<Canvas>();
|
||||
if (canvas && canvas.isRootCanvas && canvas.renderMode != RenderMode.WorldSpace)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsAspectModeValid()
|
||||
{
|
||||
if (!DoesParentExists() && (aspectMode == AspectMode.EnvelopeParent || aspectMode == AspectMode.FitInParent))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool DoesParentExists()
|
||||
{
|
||||
return m_DoesParentExist;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
m_AspectRatio = Mathf.Clamp(m_AspectRatio, 0.001f, 1000f);
|
||||
m_DelayedSetDirty = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 86710e43de46f6f4bac7c8e50813a599
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,405 @@
|
|||
using System;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[RequireComponent(typeof(Canvas))]
|
||||
[ExecuteAlways]
|
||||
[AddComponentMenu("Layout/Canvas Scaler", 101)]
|
||||
[DisallowMultipleComponent]
|
||||
/// <summary>
|
||||
/// The Canvas Scaler component is used for controlling the overall scale and pixel density of UI elements in the Canvas. This scaling affects everything under the Canvas, including font sizes and image borders.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For a Canvas set to 'Screen Space - Overlay' or 'Screen Space - Camera', the Canvas Scaler UI Scale Mode can be set to Constant Pixel Size, Scale With Screen Size, or Constant Physical Size.
|
||||
///
|
||||
/// Using the Constant Pixel Size mode, positions and sizes of UI elements are specified in pixels on the screen. This is also the default functionality of the Canvas when no Canvas Scaler is attached. However, With the Scale Factor setting in the Canvas Scaler, a constant scaling can be applied to all UI elements in the Canvas.
|
||||
///
|
||||
/// Using the Scale With Screen Size mode, positions and sizes can be specified according to the pixels of a specified reference resolution. If the current screen resolution is larger than the reference resolution, the Canvas will keep having only the resolution of the reference resolution, but will scale up in order to fit the screen. If the current screen resolution is smaller than the reference resolution, the Canvas will similarly be scaled down to fit. If the current screen resolution has a different aspect ratio than the reference resolution, scaling each axis individually to fit the screen would result in non-uniform scaling, which is generally undesirable. Instead of this, the ReferenceResolution component will make the Canvas resolution deviate from the reference resolution in order to respect the aspect ratio of the screen. It is possible to control how this deviation should behave using the ::ref::screenMatchMode setting.
|
||||
///
|
||||
/// Using the Constant Physical Size mode, positions and sizes of UI elements are specified in physical units, such as millimeters, points, or picas. This mode relies on the device reporting its screen DPI correctly. You can specify a fallback DPI to use for devices that do not report a DPI.
|
||||
///
|
||||
/// For a Canvas set to 'World Space' the Canvas Scaler can be used to control the pixel density of UI elements in the Canvas.
|
||||
/// </remarks>
|
||||
public class CanvasScaler : UIBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines how UI elements in the Canvas are scaled.
|
||||
/// </summary>
|
||||
public enum ScaleMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Using the Constant Pixel Size mode, positions and sizes of UI elements are specified in pixels on the screen.
|
||||
/// </summary>
|
||||
ConstantPixelSize,
|
||||
/// <summary>
|
||||
/// Using the Scale With Screen Size mode, positions and sizes can be specified according to the pixels of a specified reference resolution.
|
||||
/// If the current screen resolution is larger than the reference resolution, the Canvas will keep having only the resolution of the reference resolution, but will scale up in order to fit the screen. If the current screen resolution is smaller than the reference resolution, the Canvas will similarly be scaled down to fit.
|
||||
/// </summary>
|
||||
ScaleWithScreenSize,
|
||||
/// <summary>
|
||||
/// Using the Constant Physical Size mode, positions and sizes of UI elements are specified in physical units, such as millimeters, points, or picas.
|
||||
/// </summary>
|
||||
ConstantPhysicalSize
|
||||
}
|
||||
|
||||
[Tooltip("Determines how UI elements in the Canvas are scaled.")]
|
||||
[SerializeField] private ScaleMode m_UiScaleMode = ScaleMode.ConstantPixelSize;
|
||||
|
||||
///<summary>
|
||||
///Determines how UI elements in the Canvas are scaled.
|
||||
///</summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// If a sprite has this 'Pixels Per Unit' setting, then one pixel in the sprite will cover one unit in the UI.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Scales all UI elements in the Canvas by this factor.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Scales all UI elements in the Canvas by this factor.
|
||||
/// </summary>
|
||||
public float scaleFactor { get { return m_ScaleFactor; } set { m_ScaleFactor = Mathf.Max(0.01f, value); } }
|
||||
|
||||
/// Scale the canvas area with the width as reference, the height as reference, or something in between.
|
||||
/// <summary>
|
||||
/// Scale the canvas area with the width as reference, the height as reference, or something in between.
|
||||
/// </summary>
|
||||
public enum ScreenMatchMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Scale the canvas area with the width as reference, the height as reference, or something in between.
|
||||
/// </summary>
|
||||
MatchWidthOrHeight = 0,
|
||||
/// <summary>
|
||||
/// Expand the canvas area either horizontally or vertically, so the size of the canvas will never be smaller than the reference.
|
||||
/// </summary>
|
||||
Expand = 1,
|
||||
/// <summary>
|
||||
/// Crop the canvas area either horizontally or vertically, so the size of the canvas will never be larger than the reference.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// The resolution the UI layout is designed for.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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;
|
||||
/// <summary>
|
||||
/// A mode used to scale the canvas area if the aspect ratio of the current resolution doesn't fit the reference resolution.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Setting to scale the Canvas to match the width or height of the reference resolution, or a combination.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the setting is set to 0, the Canvas is scaled according to the difference between the current screen resolution width and the reference resolution width. If the setting is set to 1, the Canvas is scaled according to the difference between the current screen resolution height and the reference resolution height.
|
||||
///
|
||||
/// For values in between 0 and 1, the scaling is based on a combination of the relative width and height.
|
||||
///
|
||||
/// Consider an example where the reference resolution of 640x480, and the current screen resolution is a landscape mode of 480x640.
|
||||
///
|
||||
/// If the scaleWidthOrHeight setting is set to 0, the Canvas is scaled by 0.75 because the current resolution width of 480 is 0.75 times the reference resolution width of 640. The Canvas resolution gets a resolution of 640x853.33. This resolution has the same width as the reference resolution width, but has the aspect ratio of the current screen resolution. Note that the Canvas resolution of 640x853.33 is the current screen resolution divided by the scale factor of 0.75.
|
||||
///
|
||||
/// If the scaleWidthOrHeight setting is set to 1, the Canvas is scaled by 1.33 because the current resolution height of 640 is 1.33 times the reference resolution height of 480. The Canvas resolution gets a resolution of 360x480. This resolution has the same height as the reference resolution width, but has the aspect ratio of the current screen resolution. Note that the Canvas resolution of 360x480 is the current screen resolution divided by the scale factor of 1.33.
|
||||
///
|
||||
/// If the scaleWidthOrHeight setting is set to 0.5, we find the horizontal scaling needed (0.75) and the vertical scaling needed (1.33) and find the average. However, we do the average in logarithmic space. A regular average of 0.75 and 1.33 would produce a result of 1.04. However, since multiplying by 1.33 is the same as diving by 0.75, the two scale factor really corresponds to multiplying by 0.75 versus dividing by 0.75, and the average of those two things should even out and produce a neutral result. The average in logarithmic space of 0.75 and 1.33 is exactly 1.0, which is what we want. The Canvas resolution hence ends up being 480x640 which is the current resolution divided by the scale factor of 1.0.
|
||||
///
|
||||
/// The logic works the same for all values. The average between the horizontal and vertical scale factor is a weighted average based on the matchWidthOrHeight value.
|
||||
/// </remarks>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The possible physical unit types
|
||||
/// </summary>
|
||||
public enum Unit
|
||||
{
|
||||
/// <summary>
|
||||
/// Use centimeters.
|
||||
/// A centimeter is 1/100 of a meter
|
||||
/// </summary>
|
||||
Centimeters,
|
||||
/// <summary>
|
||||
/// Use millimeters.
|
||||
/// A millimeter is 1/10 of a centimeter, and 1/1000 of a meter.
|
||||
/// </summary>
|
||||
Millimeters,
|
||||
/// <summary>
|
||||
/// Use inches.
|
||||
/// </summary>
|
||||
Inches,
|
||||
/// <summary>
|
||||
/// Use points.
|
||||
/// One point is 1/12 of a pica, and 1/72 of an inch.
|
||||
/// </summary>
|
||||
Points,
|
||||
/// <summary>
|
||||
/// Use picas.
|
||||
/// One pica is 1/6 of an inch.
|
||||
/// </summary>
|
||||
Picas
|
||||
}
|
||||
|
||||
[Tooltip("The physical unit to specify positions and sizes in.")]
|
||||
[SerializeField] protected Unit m_PhysicalUnit = Unit.Points;
|
||||
|
||||
/// <summary>
|
||||
/// The physical unit to specify positions and sizes in.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The DPI to assume if the screen DPI is not known.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The pixels per inch to use for sprites that have a 'Pixels Per Unit' setting that matches the 'Reference Pixels Per Unit' setting.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of pixels per unit to use for dynamically created bitmaps in the UI, such as Text.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
[SerializeField] protected bool m_PresetInfoIsWorld = false;
|
||||
|
||||
protected CanvasScaler() {}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
m_Canvas = GetComponent<Canvas>();
|
||||
Handle();
|
||||
Canvas.preWillRenderCanvases += Canvas_preWillRenderCanvases;
|
||||
}
|
||||
|
||||
private void Canvas_preWillRenderCanvases()
|
||||
{
|
||||
Handle();
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
SetScaleFactor(1);
|
||||
SetReferencePixelsPerUnit(100);
|
||||
Canvas.preWillRenderCanvases -= Canvas_preWillRenderCanvases;
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///Method that handles calculations of canvas scaling.
|
||||
///</summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles canvas scaling for world canvas.
|
||||
/// </summary>
|
||||
protected virtual void HandleWorldCanvas()
|
||||
{
|
||||
SetScaleFactor(m_DynamicPixelsPerUnit);
|
||||
SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles canvas scaling for a constant pixel size.
|
||||
/// </summary>
|
||||
protected virtual void HandleConstantPixelSize()
|
||||
{
|
||||
SetScaleFactor(m_ScaleFactor);
|
||||
SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles canvas scaling that scales with the screen size.
|
||||
/// </summary>
|
||||
protected virtual void HandleScaleWithScreenSize()
|
||||
{
|
||||
Vector2 screenSize = m_Canvas.renderingDisplaySize;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///Handles canvas scaling for a constant physical size.
|
||||
///</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the scale factor on the canvas.
|
||||
/// </summary>
|
||||
/// <param name="scaleFactor">The scale factor to use.</param>
|
||||
protected void SetScaleFactor(float scaleFactor)
|
||||
{
|
||||
if (scaleFactor == m_PrevScaleFactor)
|
||||
return;
|
||||
|
||||
m_Canvas.scaleFactor = scaleFactor;
|
||||
m_PrevScaleFactor = scaleFactor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the referencePixelsPerUnit on the Canvas.
|
||||
/// </summary>
|
||||
/// <param name="referencePixelsPerUnit">The new reference pixels per Unity value</param>
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0cd44c1031e13a943bb63640046fad76
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,138 @@
|
|||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("Layout/Content Size Fitter", 141)]
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
/// <summary>
|
||||
/// Resizes a RectTransform to fit the size of its content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The ContentSizeFitter can be used on GameObjects that have one or more ILayoutElement components, such as Text, Image, HorizontalLayoutGroup, VerticalLayoutGroup, and GridLayoutGroup.
|
||||
/// </remarks>
|
||||
public class ContentSizeFitter : UIBehaviour, ILayoutSelfController
|
||||
{
|
||||
/// <summary>
|
||||
/// The size fit modes avaliable to use.
|
||||
/// </summary>
|
||||
public enum FitMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't perform any resizing.
|
||||
/// </summary>
|
||||
Unconstrained,
|
||||
/// <summary>
|
||||
/// Resize to the minimum size of the content.
|
||||
/// </summary>
|
||||
MinSize,
|
||||
/// <summary>
|
||||
/// Resize to the preferred size of the content.
|
||||
/// </summary>
|
||||
PreferredSize
|
||||
}
|
||||
|
||||
[SerializeField] protected FitMode m_HorizontalFit = FitMode.Unconstrained;
|
||||
|
||||
/// <summary>
|
||||
/// The fit mode to use to determine the width.
|
||||
/// </summary>
|
||||
public FitMode horizontalFit { get { return m_HorizontalFit; } set { if (SetPropertyUtility.SetStruct(ref m_HorizontalFit, value)) SetDirty(); } }
|
||||
|
||||
[SerializeField] protected FitMode m_VerticalFit = FitMode.Unconstrained;
|
||||
|
||||
/// <summary>
|
||||
/// The fit mode to use to determine the height.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// field is never assigned warning
|
||||
#pragma warning disable 649
|
||||
private DrivenRectTransformTracker m_Tracker;
|
||||
#pragma warning restore 649
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate and apply the horizontal component of the size to the RectTransform
|
||||
/// </summary>
|
||||
public virtual void SetLayoutHorizontal()
|
||||
{
|
||||
m_Tracker.Clear();
|
||||
HandleSelfFittingAlongAxis(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate and apply the vertical component of the size to the RectTransform
|
||||
/// </summary>
|
||||
public virtual void SetLayoutVertical()
|
||||
{
|
||||
HandleSelfFittingAlongAxis(1);
|
||||
}
|
||||
|
||||
protected void SetDirty()
|
||||
{
|
||||
if (!IsActive())
|
||||
return;
|
||||
|
||||
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3245ec927659c4140ac4f8d17403cc18
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,320 @@
|
|||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("Layout/Grid Layout Group", 152)]
|
||||
/// <summary>
|
||||
/// Layout class to arrange child elements in a grid format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The GridLayoutGroup component is used to layout child layout elements in a uniform grid where all cells have the same size. The size and the spacing between cells is controlled by the GridLayoutGroup itself. The children have no influence on their sizes.
|
||||
/// </remarks>
|
||||
public class GridLayoutGroup : LayoutGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// Which corner is the starting corner for the grid.
|
||||
/// </summary>
|
||||
public enum Corner
|
||||
{
|
||||
/// <summary>
|
||||
/// Upper Left corner.
|
||||
/// </summary>
|
||||
UpperLeft = 0,
|
||||
/// <summary>
|
||||
/// Upper Right corner.
|
||||
/// </summary>
|
||||
UpperRight = 1,
|
||||
/// <summary>
|
||||
/// Lower Left corner.
|
||||
/// </summary>
|
||||
LowerLeft = 2,
|
||||
/// <summary>
|
||||
/// Lower Right corner.
|
||||
/// </summary>
|
||||
LowerRight = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The grid axis we are looking at.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As the storage is a [][] we make access easier by passing a axis.
|
||||
/// </remarks>
|
||||
public enum Axis
|
||||
{
|
||||
/// <summary>
|
||||
/// Horizontal axis
|
||||
/// </summary>
|
||||
Horizontal = 0,
|
||||
/// <summary>
|
||||
/// Vertical axis.
|
||||
/// </summary>
|
||||
Vertical = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constraint type on either the number of columns or rows.
|
||||
/// </summary>
|
||||
public enum Constraint
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't constrain the number of rows or columns.
|
||||
/// </summary>
|
||||
Flexible = 0,
|
||||
/// <summary>
|
||||
/// Constrain the number of columns to a specified number.
|
||||
/// </summary>
|
||||
FixedColumnCount = 1,
|
||||
/// <summary>
|
||||
/// Constraint the number of rows to a specified number.
|
||||
/// </summary>
|
||||
FixedRowCount = 2
|
||||
}
|
||||
|
||||
[SerializeField] protected Corner m_StartCorner = Corner.UpperLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Which corner should the first cell be placed in?
|
||||
/// </summary>
|
||||
public Corner startCorner { get { return m_StartCorner; } set { SetProperty(ref m_StartCorner, value); } }
|
||||
|
||||
[SerializeField] protected Axis m_StartAxis = Axis.Horizontal;
|
||||
|
||||
/// <summary>
|
||||
/// Which axis should cells be placed along first
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When startAxis is set to horizontal, an entire row will be filled out before proceeding to the next row. When set to vertical, an entire column will be filled out before proceeding to the next column.
|
||||
/// </remarks>
|
||||
public Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } }
|
||||
|
||||
[SerializeField] protected Vector2 m_CellSize = new Vector2(100, 100);
|
||||
|
||||
/// <summary>
|
||||
/// The size to use for each cell in the grid.
|
||||
/// </summary>
|
||||
public Vector2 cellSize { get { return m_CellSize; } set { SetProperty(ref m_CellSize, value); } }
|
||||
|
||||
[SerializeField] protected Vector2 m_Spacing = Vector2.zero;
|
||||
|
||||
/// <summary>
|
||||
/// The spacing to use between layout elements in the grid on both axises.
|
||||
/// </summary>
|
||||
public Vector2 spacing { get { return m_Spacing; } set { SetProperty(ref m_Spacing, value); } }
|
||||
|
||||
[SerializeField] protected Constraint m_Constraint = Constraint.Flexible;
|
||||
|
||||
/// <summary>
|
||||
/// Which constraint to use for the GridLayoutGroup.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Specifying a constraint can make the GridLayoutGroup work better in conjunction with a [[ContentSizeFitter]] component. When GridLayoutGroup is used on a RectTransform with a manually specified size, there's no need to specify a constraint.
|
||||
/// </remarks>
|
||||
public Constraint constraint { get { return m_Constraint; } set { SetProperty(ref m_Constraint, value); } }
|
||||
|
||||
[SerializeField] protected int m_ConstraintCount = 2;
|
||||
|
||||
/// <summary>
|
||||
/// How many cells there should be along the constrained axis.
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system to calculate the horizontal layout size.
|
||||
/// Also see ILayoutElement
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system to calculate the vertical layout size.
|
||||
/// Also see ILayoutElement
|
||||
/// </summary>
|
||||
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.width;
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system
|
||||
/// Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void SetLayoutHorizontal()
|
||||
{
|
||||
SetCellsAlongAxis(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system
|
||||
/// Also see ILayoutElement
|
||||
/// </summary>
|
||||
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.
|
||||
var rectChildrenCount = rectChildren.Count;
|
||||
if (axis == 0)
|
||||
{
|
||||
// Only set the sizes when invoked for horizontal axis, not the positions.
|
||||
|
||||
for (int i = 0; i < rectChildrenCount; 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;
|
||||
|
||||
if (rectChildrenCount > cellCountX)
|
||||
cellCountY = rectChildrenCount / cellCountX + (rectChildrenCount % cellCountX > 0 ? 1 : 0);
|
||||
}
|
||||
else if (m_Constraint == Constraint.FixedRowCount)
|
||||
{
|
||||
cellCountY = m_ConstraintCount;
|
||||
|
||||
if (rectChildrenCount > cellCountY)
|
||||
cellCountX = rectChildrenCount / cellCountY + (rectChildrenCount % cellCountY > 0 ? 1 : 0);
|
||||
}
|
||||
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, rectChildrenCount);
|
||||
actualCellCountY = Mathf.Clamp(cellCountY, 1, Mathf.CeilToInt(rectChildrenCount / (float)cellsPerMainAxis));
|
||||
}
|
||||
else
|
||||
{
|
||||
cellsPerMainAxis = cellCountY;
|
||||
actualCellCountY = Mathf.Clamp(cellCountY, 1, rectChildrenCount);
|
||||
actualCellCountX = Mathf.Clamp(cellCountX, 1, Mathf.CeilToInt(rectChildrenCount / (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 < rectChildrenCount; 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8a8695521f0d02e499659fee002a26c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,45 @@
|
|||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("Layout/Horizontal Layout Group", 150)]
|
||||
/// <summary>
|
||||
/// Layout class for arranging child elements side by side.
|
||||
/// </summary>
|
||||
public class HorizontalLayoutGroup : HorizontalOrVerticalLayoutGroup
|
||||
{
|
||||
protected HorizontalLayoutGroup()
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system. Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void CalculateLayoutInputHorizontal()
|
||||
{
|
||||
base.CalculateLayoutInputHorizontal();
|
||||
CalcAlongAxis(0, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system. Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void CalculateLayoutInputVertical()
|
||||
{
|
||||
CalcAlongAxis(1, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system. Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void SetLayoutHorizontal()
|
||||
{
|
||||
SetChildrenAlongAxis(0, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system. Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void SetLayoutVertical()
|
||||
{
|
||||
SetChildrenAlongAxis(1, false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 30649d3a9faa99c48a7b1166b86bf2a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,295 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for HorizontalLayoutGroup and VerticalLayoutGroup to generalize common functionality.
|
||||
/// </summary>
|
||||
///
|
||||
[ExecuteAlways]
|
||||
public abstract class HorizontalOrVerticalLayoutGroup : LayoutGroup
|
||||
{
|
||||
[SerializeField] protected float m_Spacing = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The spacing to use between layout elements in the layout group.
|
||||
/// </summary>
|
||||
public float spacing { get { return m_Spacing; } set { SetProperty(ref m_Spacing, value); } }
|
||||
|
||||
[SerializeField] protected bool m_ChildForceExpandWidth = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to force the children to expand to fill additional available horizontal space.
|
||||
/// </summary>
|
||||
public bool childForceExpandWidth { get { return m_ChildForceExpandWidth; } set { SetProperty(ref m_ChildForceExpandWidth, value); } }
|
||||
|
||||
[SerializeField] protected bool m_ChildForceExpandHeight = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to force the children to expand to fill additional available vertical space.
|
||||
/// </summary>
|
||||
public bool childForceExpandHeight { get { return m_ChildForceExpandHeight; } set { SetProperty(ref m_ChildForceExpandHeight, value); } }
|
||||
|
||||
[SerializeField] protected bool m_ChildControlWidth = true;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the Layout Group controls the widths of its children. Returns false if children control their own widths.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If set to false, the layout group will only affect the positions of the children while leaving the widths untouched. The widths of the children can be set via the respective RectTransforms in this case.
|
||||
///
|
||||
/// If set to true, the widths of the children are automatically driven by the layout group according to their respective minimum, preferred, and flexible widths. This is useful if the widths of the children should change depending on how much space is available.In this case the width of each child cannot be set manually in the RectTransform, but the minimum, preferred and flexible width for each child can be controlled by adding a LayoutElement component to it.
|
||||
/// </remarks>
|
||||
public bool childControlWidth { get { return m_ChildControlWidth; } set { SetProperty(ref m_ChildControlWidth, value); } }
|
||||
|
||||
[SerializeField] protected bool m_ChildControlHeight = true;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the Layout Group controls the heights of its children. Returns false if children control their own heights.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If set to false, the layout group will only affect the positions of the children while leaving the heights untouched. The heights of the children can be set via the respective RectTransforms in this case.
|
||||
///
|
||||
/// If set to true, the heights of the children are automatically driven by the layout group according to their respective minimum, preferred, and flexible heights. This is useful if the heights of the children should change depending on how much space is available.In this case the height of each child cannot be set manually in the RectTransform, but the minimum, preferred and flexible height for each child can be controlled by adding a LayoutElement component to it.
|
||||
/// </remarks>
|
||||
public bool childControlHeight { get { return m_ChildControlHeight; } set { SetProperty(ref m_ChildControlHeight, value); } }
|
||||
|
||||
[SerializeField] protected bool m_ChildScaleWidth = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use the x scale of each child when calculating its width.
|
||||
/// </summary>
|
||||
public bool childScaleWidth { get { return m_ChildScaleWidth; } set { SetProperty(ref m_ChildScaleWidth, value); } }
|
||||
|
||||
[SerializeField] protected bool m_ChildScaleHeight = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use the y scale of each child when calculating its height.
|
||||
/// </summary>
|
||||
public bool childScaleHeight { get { return m_ChildScaleHeight; } set { SetProperty(ref m_ChildScaleHeight, value); } }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the order of children objects should be sorted in reverse.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If False the first child object will be positioned first.
|
||||
/// If True the last child object will be positioned first.
|
||||
/// </remarks>
|
||||
public bool reverseArrangement { get { return m_ReverseArrangement; } set { SetProperty(ref m_ReverseArrangement, value); } }
|
||||
|
||||
[SerializeField] protected bool m_ReverseArrangement = false;
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the layout element properties for this layout element along the given axis.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis to calculate for. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <param name="isVertical">Is this group a vertical group?</param>
|
||||
protected void CalcAlongAxis(int axis, bool isVertical)
|
||||
{
|
||||
float combinedPadding = (axis == 0 ? padding.horizontal : padding.vertical);
|
||||
bool controlSize = (axis == 0 ? m_ChildControlWidth : m_ChildControlHeight);
|
||||
bool useScale = (axis == 0 ? m_ChildScaleWidth : m_ChildScaleHeight);
|
||||
bool childForceExpandSize = (axis == 0 ? m_ChildForceExpandWidth : m_ChildForceExpandHeight);
|
||||
|
||||
float totalMin = combinedPadding;
|
||||
float totalPreferred = combinedPadding;
|
||||
float totalFlexible = 0;
|
||||
|
||||
bool alongOtherAxis = (isVertical ^ (axis == 1));
|
||||
var rectChildrenCount = rectChildren.Count;
|
||||
for (int i = 0; i < rectChildrenCount; i++)
|
||||
{
|
||||
RectTransform child = rectChildren[i];
|
||||
float min, preferred, flexible;
|
||||
GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible);
|
||||
|
||||
if (useScale)
|
||||
{
|
||||
float scaleFactor = child.localScale[axis];
|
||||
min *= scaleFactor;
|
||||
preferred *= scaleFactor;
|
||||
flexible *= scaleFactor;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alongOtherAxis && rectChildren.Count > 0)
|
||||
{
|
||||
totalMin -= spacing;
|
||||
totalPreferred -= spacing;
|
||||
}
|
||||
totalPreferred = Mathf.Max(totalMin, totalPreferred);
|
||||
SetLayoutInputForAxis(totalMin, totalPreferred, totalFlexible, axis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the positions and sizes of the child layout elements for the given axis.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis to handle. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <param name="isVertical">Is this group a vertical group?</param>
|
||||
protected void SetChildrenAlongAxis(int axis, bool isVertical)
|
||||
{
|
||||
float size = rectTransform.rect.size[axis];
|
||||
bool controlSize = (axis == 0 ? m_ChildControlWidth : m_ChildControlHeight);
|
||||
bool useScale = (axis == 0 ? m_ChildScaleWidth : m_ChildScaleHeight);
|
||||
bool childForceExpandSize = (axis == 0 ? m_ChildForceExpandWidth : m_ChildForceExpandHeight);
|
||||
float alignmentOnAxis = GetAlignmentOnAxis(axis);
|
||||
|
||||
bool alongOtherAxis = (isVertical ^ (axis == 1));
|
||||
int startIndex = m_ReverseArrangement ? rectChildren.Count - 1 : 0;
|
||||
int endIndex = m_ReverseArrangement ? 0 : rectChildren.Count;
|
||||
int increment = m_ReverseArrangement ? -1 : 1;
|
||||
if (alongOtherAxis)
|
||||
{
|
||||
float innerSize = size - (axis == 0 ? padding.horizontal : padding.vertical);
|
||||
|
||||
for (int i = startIndex; m_ReverseArrangement ? i >= endIndex : i < endIndex; i += increment)
|
||||
{
|
||||
RectTransform child = rectChildren[i];
|
||||
float min, preferred, flexible;
|
||||
GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible);
|
||||
float scaleFactor = useScale ? child.localScale[axis] : 1f;
|
||||
|
||||
float requiredSpace = Mathf.Clamp(innerSize, min, flexible > 0 ? size : preferred);
|
||||
float startOffset = GetStartOffset(axis, requiredSpace * scaleFactor);
|
||||
if (controlSize)
|
||||
{
|
||||
SetChildAlongAxisWithScale(child, axis, startOffset, requiredSpace, scaleFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
float offsetInCell = (requiredSpace - child.sizeDelta[axis]) * alignmentOnAxis;
|
||||
SetChildAlongAxisWithScale(child, axis, startOffset + offsetInCell, scaleFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float pos = (axis == 0 ? padding.left : padding.top);
|
||||
float itemFlexibleMultiplier = 0;
|
||||
float surplusSpace = size - GetTotalPreferredSize(axis);
|
||||
|
||||
if (surplusSpace > 0)
|
||||
{
|
||||
if (GetTotalFlexibleSize(axis) == 0)
|
||||
pos = GetStartOffset(axis, GetTotalPreferredSize(axis) - (axis == 0 ? padding.horizontal : padding.vertical));
|
||||
else if (GetTotalFlexibleSize(axis) > 0)
|
||||
itemFlexibleMultiplier = surplusSpace / GetTotalFlexibleSize(axis);
|
||||
}
|
||||
|
||||
float minMaxLerp = 0;
|
||||
if (GetTotalMinSize(axis) != GetTotalPreferredSize(axis))
|
||||
minMaxLerp = Mathf.Clamp01((size - GetTotalMinSize(axis)) / (GetTotalPreferredSize(axis) - GetTotalMinSize(axis)));
|
||||
|
||||
for (int i = startIndex; m_ReverseArrangement ? i >= endIndex : i < endIndex; i += increment)
|
||||
{
|
||||
RectTransform child = rectChildren[i];
|
||||
float min, preferred, flexible;
|
||||
GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible);
|
||||
float scaleFactor = useScale ? child.localScale[axis] : 1f;
|
||||
|
||||
float childSize = Mathf.Lerp(min, preferred, minMaxLerp);
|
||||
childSize += flexible * itemFlexibleMultiplier;
|
||||
if (controlSize)
|
||||
{
|
||||
SetChildAlongAxisWithScale(child, axis, pos, childSize, scaleFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
float offsetInCell = (childSize - child.sizeDelta[axis]) * alignmentOnAxis;
|
||||
SetChildAlongAxisWithScale(child, axis, pos + offsetInCell, scaleFactor);
|
||||
}
|
||||
pos += childSize * scaleFactor + spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetChildSizes(RectTransform child, int axis, bool controlSize, bool childForceExpand,
|
||||
out float min, out float preferred, out float flexible)
|
||||
{
|
||||
if (!controlSize)
|
||||
{
|
||||
min = child.sizeDelta[axis];
|
||||
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;
|
||||
}
|
||||
|
||||
private int m_Capacity = 10;
|
||||
private Vector2[] m_Sizes = new Vector2[10];
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
return;
|
||||
|
||||
int count = transform.childCount;
|
||||
|
||||
if (count > m_Capacity)
|
||||
{
|
||||
if (count > m_Capacity * 2)
|
||||
m_Capacity = count;
|
||||
else
|
||||
m_Capacity *= 2;
|
||||
|
||||
m_Sizes = new Vector2[m_Capacity];
|
||||
}
|
||||
|
||||
// If children size change in editor, update layout (case 945680 - Child GameObjects in a Horizontal/Vertical Layout Group don't display their correct position in the Editor)
|
||||
bool dirty = false;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
RectTransform t = transform.GetChild(i) as RectTransform;
|
||||
if (t != null && t.sizeDelta != m_Sizes[i])
|
||||
{
|
||||
dirty = true;
|
||||
m_Sizes[i] = t.sizeDelta;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a5c214e5846a99242b348c37e49b2f59
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,268 @@
|
|||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A component is treated as a layout element by the auto layout system if it implements ILayoutElement.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The layout system will invoke CalculateLayoutInputHorizontal before querying minWidth, preferredWidth, and flexibleWidth. It can potentially save performance if these properties are cached when CalculateLayoutInputHorizontal is invoked, so they don't need to be recalculated every time the properties are queried.
|
||||
///
|
||||
/// The layout system will invoke CalculateLayoutInputVertical before querying minHeight, preferredHeight, and flexibleHeight.It can potentially save performance if these properties are cached when CalculateLayoutInputVertical is invoked, so they don't need to be recalculated every time the properties are queried.
|
||||
///
|
||||
/// The minWidth, preferredWidth, and flexibleWidth properties should not rely on any properties of the RectTransform of the layout element, otherwise the behavior will be non-deterministic.
|
||||
/// The minHeight, preferredHeight, and flexibleHeight properties may rely on horizontal aspects of the RectTransform, such as the width or the X component of the position.
|
||||
/// Any properties of the RectTransforms on child layout elements may always be relied on.
|
||||
/// </remarks>
|
||||
public interface ILayoutElement
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
void CalculateLayoutInputHorizontal();
|
||||
|
||||
/// <summary>
|
||||
///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.
|
||||
/// </summary>
|
||||
void CalculateLayoutInputVertical();
|
||||
|
||||
/// <summary>
|
||||
/// The minimum width this layout element may be allocated.
|
||||
/// </summary>
|
||||
float minWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The preferred width this layout element should be allocated if there is sufficient space.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// PreferredWidth can be set to -1 to remove the size.
|
||||
/// </remarks>
|
||||
float preferredWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The extra relative width this layout element should be allocated if there is additional available space.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Setting preferredWidth to -1 removed the preferredWidth.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
///<code>
|
||||
///<![CDATA[
|
||||
///using UnityEngine;
|
||||
///using System.Collections;
|
||||
///using UnityEngine.UI; // Required when using UI elements.
|
||||
///
|
||||
///public class ExampleClass : MonoBehaviour
|
||||
///{
|
||||
/// public Transform MyContentPanel;
|
||||
///
|
||||
/// //Sets the flexible height on on all children in the content panel.
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //Assign all the children of the content panel to an array.
|
||||
/// LayoutElement[] myLayoutElements = MyContentPanel.GetComponentsInChildren<LayoutElement>();
|
||||
///
|
||||
/// //For each child in the array change its LayoutElement's flexible width to 200.
|
||||
/// foreach (LayoutElement element in myLayoutElements)
|
||||
/// {
|
||||
/// element.flexibleWidth = 200f;
|
||||
/// }
|
||||
/// }
|
||||
///}
|
||||
///]]>
|
||||
///</code>
|
||||
///</example>
|
||||
|
||||
float flexibleWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The minimum height this layout element may be allocated.
|
||||
/// </summary>
|
||||
float minHeight { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The preferred height this layout element should be allocated if there is sufficient space.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// PreferredHeight can be set to -1 to remove the size.
|
||||
/// </remarks>
|
||||
float preferredHeight { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The extra relative height this layout element should be allocated if there is additional available space.
|
||||
/// </summary>
|
||||
///<example>
|
||||
///<code>
|
||||
///<![CDATA[
|
||||
///using UnityEngine;
|
||||
///using System.Collections;
|
||||
///using UnityEngine.UI; // Required when using UI elements.
|
||||
///
|
||||
///public class ExampleClass : MonoBehaviour
|
||||
///{
|
||||
/// public Transform MyContentPanel;
|
||||
///
|
||||
/// //Sets the flexible height on on all children in the content panel.
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //Assign all the children of the content panel to an array.
|
||||
/// LayoutElement[] myLayoutElements = MyContentPanel.GetComponentsInChildren<LayoutElement>();
|
||||
///
|
||||
/// //For each child in the array change its LayoutElement's flexible height to 100.
|
||||
/// foreach (LayoutElement element in myLayoutElements)
|
||||
/// {
|
||||
/// element.flexibleHeight = 100f;
|
||||
/// }
|
||||
/// }
|
||||
///}
|
||||
///]]>
|
||||
///</code>
|
||||
///</example>
|
||||
float flexibleHeight { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The layout priority of this component.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If multiple components on the same GameObject implement the ILayoutElement interface, the values provided by components that return a higher priority value are given priority. However, values less than zero are ignored. This way a component can override only select properties by leaving the remaning values to be -1 or other values less than zero.
|
||||
/// </remarks>
|
||||
int layoutPriority { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base interface to be implemented by components that control the layout of RectTransforms.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a component is driving its own RectTransform it should implement the interface [[ILayoutSelfController]].
|
||||
/// If a component is driving the RectTransforms of its children, it should implement [[ILayoutGroup]].
|
||||
///
|
||||
/// The layout system will first invoke SetLayoutHorizontal and then SetLayoutVertical.
|
||||
///
|
||||
/// In the SetLayoutHorizontal call it is valid to call LayoutUtility.GetMinWidth, LayoutUtility.GetPreferredWidth, and LayoutUtility.GetFlexibleWidth on the RectTransform of itself or any of its children.
|
||||
/// In the SetLayoutVertical call it is valid to call LayoutUtility.GetMinHeight, LayoutUtility.GetPreferredHeight, and LayoutUtility.GetFlexibleHeight on the RectTransform of itself or any of its children.
|
||||
///
|
||||
/// The component may use this information to determine the width and height to use for its own RectTransform or the RectTransforms of its children.
|
||||
/// </remarks>
|
||||
public interface ILayoutController
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback invoked by the auto layout system which handles horizontal aspects of the layout.
|
||||
/// </summary>
|
||||
void SetLayoutHorizontal();
|
||||
|
||||
/// <summary>
|
||||
/// Callback invoked by the auto layout system which handles vertical aspects of the layout.
|
||||
/// </summary>
|
||||
void SetLayoutVertical();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ILayoutGroup is an ILayoutController that should drive the RectTransforms of its children.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ILayoutGroup derives from ILayoutController and requires the same members to be implemented.
|
||||
/// </remarks>
|
||||
public interface ILayoutGroup : ILayoutController
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ILayoutSelfController is an ILayoutController that should drive its own RectTransform.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The iLayoutSelfController derives from the base controller [[ILayoutController]] and controls the layout of a RectTransform.
|
||||
///
|
||||
/// Use the ILayoutSelfController to manipulate a GameObject’s own RectTransform component, which you attach in the Inspector.Use ILayoutGroup to manipulate RectTransforms belonging to the children of the GameObject.
|
||||
///
|
||||
/// Call ILayoutController.SetLayoutHorizontal to handle horizontal parts of the layout, and call ILayoutController.SetLayoutVertical to handle vertical parts.
|
||||
/// You can change the height, width, position and rotation of the RectTransform.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// //This script shows how the GameObject’s own RectTransforms can be changed.
|
||||
/// //This creates a rectangle on the screen of the scale, positition and rotation you define in the Inspector.
|
||||
/// //Make sure to set the X and Y scale to be more than 0 to see it
|
||||
///
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.UI;
|
||||
/// using UnityEngine.EventSystems;
|
||||
///
|
||||
/// public class Example : UIBehaviour, ILayoutSelfController
|
||||
/// {
|
||||
/// //Fields in the inspector used to manipulate the RectTransform
|
||||
/// public Vector3 m_Position;
|
||||
/// public Vector3 m_Rotation;
|
||||
/// public Vector2 m_Scale;
|
||||
///
|
||||
/// //This handles horizontal aspects of the layout (derived from ILayoutController)
|
||||
/// public virtual void SetLayoutHorizontal()
|
||||
/// {
|
||||
/// //Move and Rotate the RectTransform appropriately
|
||||
/// UpdateRectTransform();
|
||||
/// }
|
||||
///
|
||||
/// //This handles vertical aspects of the layout
|
||||
/// public virtual void SetLayoutVertical()
|
||||
/// {
|
||||
/// //Move and Rotate the RectTransform appropriately
|
||||
/// UpdateRectTransform();
|
||||
/// }
|
||||
///
|
||||
/// //This tells when there is a change in the inspector
|
||||
/// #if UNITY_EDITOR
|
||||
/// protected override void OnValidate()
|
||||
/// {
|
||||
/// Debug.Log("Validate");
|
||||
/// //Update the RectTransform position, rotation and scale
|
||||
/// UpdateRectTransform();
|
||||
/// }
|
||||
///
|
||||
/// #endif
|
||||
///
|
||||
/// //This tells when there has been a change to the RectTransform's settings in the inspector
|
||||
/// protected override void OnRectTransformDimensionsChange()
|
||||
/// {
|
||||
/// //Update the RectTransform position, rotation and scale
|
||||
/// UpdateRectTransform();
|
||||
/// }
|
||||
///
|
||||
/// void UpdateRectTransform()
|
||||
/// {
|
||||
/// //Fetch the RectTransform from the GameObject
|
||||
/// RectTransform rectTransform = GetComponent<RectTransform>();
|
||||
///
|
||||
/// //Change the scale of the RectTransform using the fields in the inspector
|
||||
/// rectTransform.localScale = new Vector3(m_Scale.x, m_Scale.y, 0);
|
||||
///
|
||||
/// //Change the position and rotation of the RectTransform
|
||||
/// rectTransform.SetPositionAndRotation(m_Position, Quaternion.Euler(m_Rotation));
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public interface ILayoutSelfController : ILayoutController
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A RectTransform will be ignored by the layout system if it has a component which implements ILayoutIgnorer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A components that implements ILayoutIgnorer can be used to make a parent layout group component not consider this RectTransform part of the group. The RectTransform can then be manually positioned despite being a child GameObject of a layout group.
|
||||
/// </remarks>
|
||||
public interface ILayoutIgnorer
|
||||
{
|
||||
/// <summary>
|
||||
/// Should this RectTransform be ignored bvy the layout system?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Setting this property to true will make a parent layout group component not consider this RectTransform part of the group. The RectTransform can then be manually positioned despite being a child GameObject of a layout group.
|
||||
/// </remarks>
|
||||
bool ignoreLayout { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 368d3a0498e78014da578aa5f45e2797
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,228 @@
|
|||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("Layout/Layout Element", 140)]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[ExecuteAlways]
|
||||
/// <summary>
|
||||
/// Add this component to a GameObject to make it into a layout element or override values on an existing layout element.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Should this RectTransform be ignored by the layout system?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Setting this property to true will make a parent layout group component not consider this RectTransform part of the group. The RectTransform can then be manually positioned despite being a child GameObject of a layout group.
|
||||
/// </remarks>
|
||||
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() {}
|
||||
|
||||
/// <summary>
|
||||
/// The minimum width this layout element may be allocated.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Transform MyContentPanel;
|
||||
///
|
||||
/// //Sets the flexible height on on all children in the content panel.
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //Assign all the children of the content panel to an array.
|
||||
/// LayoutElement[] myLayoutElements = MyContentPanel.GetComponentsInChildren<LayoutElement>();
|
||||
///
|
||||
/// //For each child in the array change its LayoutElement's minimum width size to 200.
|
||||
/// foreach (LayoutElement element in myLayoutElements)
|
||||
/// {
|
||||
/// element.minWidth = 200f;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public virtual float minWidth { get { return m_MinWidth; } set { if (SetPropertyUtility.SetStruct(ref m_MinWidth, value)) SetDirty(); } }
|
||||
|
||||
/// <summary>
|
||||
/// The minimum height this layout element may be allocated.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Transform MyContentPanel;
|
||||
///
|
||||
/// //Sets the flexible height on on all children in the content panel.
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //Assign all the children of the content panel to an array.
|
||||
/// LayoutElement[] myLayoutElements = MyContentPanel.GetComponentsInChildren<LayoutElement>();
|
||||
///
|
||||
/// //For each child in the array change its LayoutElement's minimum height size to 64.
|
||||
/// foreach (LayoutElement element in myLayoutElements)
|
||||
/// {
|
||||
/// element.minHeight = 64f;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public virtual float minHeight { get { return m_MinHeight; } set { if (SetPropertyUtility.SetStruct(ref m_MinHeight, value)) SetDirty(); } }
|
||||
|
||||
/// <summary>
|
||||
/// The preferred width this layout element should be allocated if there is sufficient space. The preferredWidth can be set to -1 to remove the size.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Transform MyContentPanel;
|
||||
///
|
||||
/// //Sets the flexible height on on all children in the content panel.
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //Assign all the children of the content panel to an array.
|
||||
/// LayoutElement[] myLayoutElements = MyContentPanel.GetComponentsInChildren<LayoutElement>();
|
||||
///
|
||||
/// //For each child in the array change its LayoutElement's preferred width size to 250.
|
||||
/// foreach (LayoutElement element in myLayoutElements)
|
||||
/// {
|
||||
/// element.preferredWidth = 250f;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public virtual float preferredWidth { get { return m_PreferredWidth; } set { if (SetPropertyUtility.SetStruct(ref m_PreferredWidth, value)) SetDirty(); } }
|
||||
|
||||
/// <summary>
|
||||
/// The preferred height this layout element should be allocated if there is sufficient space.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Transform MyContentPanel;
|
||||
///
|
||||
/// //Sets the flexible height on on all children in the content panel.
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //Assign all the children of the content panel to an array.
|
||||
/// LayoutElement[] myLayoutElements = MyContentPanel.GetComponentsInChildren<LayoutElement>();
|
||||
///
|
||||
/// //For each child in the array change its LayoutElement's preferred height size to 100.
|
||||
/// foreach (LayoutElement element in myLayoutElements)
|
||||
/// {
|
||||
/// element.preferredHeight = 100f;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public virtual float preferredHeight { get { return m_PreferredHeight; } set { if (SetPropertyUtility.SetStruct(ref m_PreferredHeight, value)) SetDirty(); } }
|
||||
|
||||
/// <summary>
|
||||
/// The extra relative width this layout element should be allocated if there is additional available space.
|
||||
/// </summary>
|
||||
public virtual float flexibleWidth { get { return m_FlexibleWidth; } set { if (SetPropertyUtility.SetStruct(ref m_FlexibleWidth, value)) SetDirty(); } }
|
||||
|
||||
/// <summary>
|
||||
/// The extra relative height this layout element should be allocated if there is additional available space.
|
||||
/// </summary>
|
||||
public virtual float flexibleHeight { get { return m_FlexibleHeight; } set { if (SetPropertyUtility.SetStruct(ref m_FlexibleHeight, value)) SetDirty(); } }
|
||||
|
||||
/// <summary>
|
||||
/// The Priority of layout this element has.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark the LayoutElement as dirty.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will make the auto layout system process this element on the next layout pass. This method should be called by the LayoutElement whenever a change is made that potentially affects the layout.
|
||||
/// </remarks>
|
||||
protected void SetDirty()
|
||||
{
|
||||
if (!IsActive())
|
||||
return;
|
||||
LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 306cc8c2b49d7114eaa3623786fc2126
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,381 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Pool;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
/// <summary>
|
||||
/// Abstract base class to use for layout groups.
|
||||
/// </summary>
|
||||
public abstract class LayoutGroup : UIBehaviour, ILayoutElement, ILayoutGroup
|
||||
{
|
||||
[SerializeField] protected RectOffset m_Padding = new RectOffset();
|
||||
|
||||
/// <summary>
|
||||
/// The padding to add around the child layout elements.
|
||||
/// </summary>
|
||||
public RectOffset padding { get { return m_Padding; } set { SetProperty(ref m_Padding, value); } }
|
||||
|
||||
[SerializeField] protected TextAnchor m_ChildAlignment = TextAnchor.UpperLeft;
|
||||
|
||||
/// <summary>
|
||||
/// The alignment to use for the child layout elements in the layout group.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a layout element does not specify a flexible width or height, its child elements many not use the available space within the layout group. In this case, use the alignment settings to specify how to align child elements within their layout group.
|
||||
/// </remarks>
|
||||
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; } }
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public abstract void CalculateLayoutInputVertical();
|
||||
|
||||
/// <summary>
|
||||
/// See LayoutElement.minWidth
|
||||
/// </summary>
|
||||
public virtual float minWidth { get { return GetTotalMinSize(0); } }
|
||||
|
||||
/// <summary>
|
||||
/// See LayoutElement.preferredWidth
|
||||
/// </summary>
|
||||
public virtual float preferredWidth { get { return GetTotalPreferredSize(0); } }
|
||||
|
||||
/// <summary>
|
||||
/// See LayoutElement.flexibleWidth
|
||||
/// </summary>
|
||||
public virtual float flexibleWidth { get { return GetTotalFlexibleSize(0); } }
|
||||
|
||||
/// <summary>
|
||||
/// See LayoutElement.minHeight
|
||||
/// </summary>
|
||||
public virtual float minHeight { get { return GetTotalMinSize(1); } }
|
||||
|
||||
/// <summary>
|
||||
/// See LayoutElement.preferredHeight
|
||||
/// </summary>
|
||||
public virtual float preferredHeight { get { return GetTotalPreferredSize(1); } }
|
||||
|
||||
/// <summary>
|
||||
/// See LayoutElement.flexibleHeight
|
||||
/// </summary>
|
||||
public virtual float flexibleHeight { get { return GetTotalFlexibleSize(1); } }
|
||||
|
||||
/// <summary>
|
||||
/// See LayoutElement.layoutPriority
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback for when properties have been changed by animation.
|
||||
/// </summary>
|
||||
protected override void OnDidApplyAnimationProperties()
|
||||
{
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The min size for the layout group on the given axis.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis index. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <returns>The min size</returns>
|
||||
protected float GetTotalMinSize(int axis)
|
||||
{
|
||||
return m_TotalMinSize[axis];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The preferred size for the layout group on the given axis.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis index. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <returns>The preferred size.</returns>
|
||||
protected float GetTotalPreferredSize(int axis)
|
||||
{
|
||||
return m_TotalPreferredSize[axis];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The flexible size for the layout group on the given axis.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis index. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <returns>The flexible size</returns>
|
||||
protected float GetTotalFlexibleSize(int axis)
|
||||
{
|
||||
return m_TotalFlexibleSize[axis];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the calculated position of the first child layout element along the given axis.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis index. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <param name="requiredSpaceWithoutPadding">The total space required on the given axis for all the layout elements including spacing and excluding padding.</param>
|
||||
/// <returns>The position of the first child along the given axis.</returns>
|
||||
protected float GetStartOffset(int axis, float requiredSpaceWithoutPadding)
|
||||
{
|
||||
float requiredSpace = requiredSpaceWithoutPadding + (axis == 0 ? padding.horizontal : padding.vertical);
|
||||
float availableSpace = rectTransform.rect.size[axis];
|
||||
float surplusSpace = availableSpace - requiredSpace;
|
||||
float alignmentOnAxis = GetAlignmentOnAxis(axis);
|
||||
return (axis == 0 ? padding.left : padding.top) + surplusSpace * alignmentOnAxis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the alignment on the specified axis as a fraction where 0 is left/top, 0.5 is middle, and 1 is right/bottom.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis to get alignment along. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <returns>The alignment as a fraction where 0 is left/top, 0.5 is middle, and 1 is right/bottom.</returns>
|
||||
protected float GetAlignmentOnAxis(int axis)
|
||||
{
|
||||
if (axis == 0)
|
||||
return ((int)childAlignment % 3) * 0.5f;
|
||||
else
|
||||
return ((int)childAlignment / 3) * 0.5f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to set the calculated layout properties for the given axis.
|
||||
/// </summary>
|
||||
/// <param name="totalMin">The min size for the layout group.</param>
|
||||
/// <param name="totalPreferred">The preferred size for the layout group.</param>
|
||||
/// <param name="totalFlexible">The flexible size for the layout group.</param>
|
||||
/// <param name="axis">The axis to set sizes for. 0 is horizontal and 1 is vertical.</param>
|
||||
protected void SetLayoutInputForAxis(float totalMin, float totalPreferred, float totalFlexible, int axis)
|
||||
{
|
||||
m_TotalMinSize[axis] = totalMin;
|
||||
m_TotalPreferredSize[axis] = totalPreferred;
|
||||
m_TotalFlexibleSize[axis] = totalFlexible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the position and size of a child layout element along the given axis.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the child layout element.</param>
|
||||
/// <param name="axis">The axis to set the position and size along. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <param name="pos">The position from the left side or top.</param>
|
||||
protected void SetChildAlongAxis(RectTransform rect, int axis, float pos)
|
||||
{
|
||||
if (rect == null)
|
||||
return;
|
||||
|
||||
SetChildAlongAxisWithScale(rect, axis, pos, 1.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the position and size of a child layout element along the given axis.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the child layout element.</param>
|
||||
/// <param name="axis">The axis to set the position and size along. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <param name="pos">The position from the left side or top.</param>
|
||||
protected void SetChildAlongAxisWithScale(RectTransform rect, int axis, float pos, float scaleFactor)
|
||||
{
|
||||
if (rect == null)
|
||||
return;
|
||||
|
||||
m_Tracker.Add(this, rect,
|
||||
DrivenTransformProperties.Anchors |
|
||||
(axis == 0 ? DrivenTransformProperties.AnchoredPositionX : DrivenTransformProperties.AnchoredPositionY));
|
||||
|
||||
// Inlined rect.SetInsetAndSizeFromParentEdge(...) and refactored code in order to multiply desired size by scaleFactor.
|
||||
// sizeDelta must stay the same but the size used in the calculation of the position must be scaled by the scaleFactor.
|
||||
|
||||
rect.anchorMin = Vector2.up;
|
||||
rect.anchorMax = Vector2.up;
|
||||
|
||||
Vector2 anchoredPosition = rect.anchoredPosition;
|
||||
anchoredPosition[axis] = (axis == 0) ? (pos + rect.sizeDelta[axis] * rect.pivot[axis] * scaleFactor) : (-pos - rect.sizeDelta[axis] * (1f - rect.pivot[axis]) * scaleFactor);
|
||||
rect.anchoredPosition = anchoredPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the position and size of a child layout element along the given axis.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the child layout element.</param>
|
||||
/// <param name="axis">The axis to set the position and size along. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <param name="pos">The position from the left side or top.</param>
|
||||
/// <param name="size">The size.</param>
|
||||
protected void SetChildAlongAxis(RectTransform rect, int axis, float pos, float size)
|
||||
{
|
||||
if (rect == null)
|
||||
return;
|
||||
|
||||
SetChildAlongAxisWithScale(rect, axis, pos, size, 1.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the position and size of a child layout element along the given axis.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the child layout element.</param>
|
||||
/// <param name="axis">The axis to set the position and size along. 0 is horizontal and 1 is vertical.</param>
|
||||
/// <param name="pos">The position from the left side or top.</param>
|
||||
/// <param name="size">The size.</param>
|
||||
protected void SetChildAlongAxisWithScale(RectTransform rect, int axis, float pos, float size, float scaleFactor)
|
||||
{
|
||||
if (rect == null)
|
||||
return;
|
||||
|
||||
m_Tracker.Add(this, rect,
|
||||
DrivenTransformProperties.Anchors |
|
||||
(axis == 0 ?
|
||||
(DrivenTransformProperties.AnchoredPositionX | DrivenTransformProperties.SizeDeltaX) :
|
||||
(DrivenTransformProperties.AnchoredPositionY | DrivenTransformProperties.SizeDeltaY)
|
||||
)
|
||||
);
|
||||
|
||||
// Inlined rect.SetInsetAndSizeFromParentEdge(...) and refactored code in order to multiply desired size by scaleFactor.
|
||||
// sizeDelta must stay the same but the size used in the calculation of the position must be scaled by the scaleFactor.
|
||||
|
||||
rect.anchorMin = Vector2.up;
|
||||
rect.anchorMax = Vector2.up;
|
||||
|
||||
Vector2 sizeDelta = rect.sizeDelta;
|
||||
sizeDelta[axis] = size;
|
||||
rect.sizeDelta = sizeDelta;
|
||||
|
||||
Vector2 anchoredPosition = rect.anchoredPosition;
|
||||
anchoredPosition[axis] = (axis == 0) ? (pos + size * rect.pivot[axis] * scaleFactor) : (-pos - size * (1f - rect.pivot[axis]) * scaleFactor);
|
||||
rect.anchoredPosition = anchoredPosition;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method used to set a given property if it has changed.
|
||||
/// </summary>
|
||||
/// <param name="currentValue">A reference to the member value.</param>
|
||||
/// <param name="newValue">The new value.</param>
|
||||
protected void SetProperty<T>(ref T currentValue, T newValue)
|
||||
{
|
||||
if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
|
||||
return;
|
||||
currentValue = newValue;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark the LayoutGroup as dirty.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cb7c939a806f03341b682c180dc13f08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,267 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Pool;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper class for managing layout rebuilding of CanvasElement.
|
||||
/// </summary>
|
||||
public class LayoutRebuilder : ICanvasElement
|
||||
{
|
||||
private RectTransform m_ToRebuild;
|
||||
//There are a few of reasons we need to cache the Hash fromt he 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;
|
||||
|
||||
static ObjectPool<LayoutRebuilder> s_Rebuilders = new ObjectPool<LayoutRebuilder>(() => new 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; }}
|
||||
|
||||
/// <summary>
|
||||
/// Has the native representation of this LayoutRebuilder been destroyed?
|
||||
/// </summary>
|
||||
public bool IsDestroyed()
|
||||
{
|
||||
return m_ToRebuild == null;
|
||||
}
|
||||
|
||||
static void StripDisabledBehavioursFromList(List<Component> components)
|
||||
{
|
||||
components.RemoveAll(e => e is Behaviour && !((Behaviour)e).isActiveAndEnabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces an immediate rebuild of the layout element and child layout elements affected by the calculations.
|
||||
/// </summary>
|
||||
/// <param name="layoutRoot">The layout element to perform the layout rebuild on.</param>
|
||||
/// <remarks>
|
||||
/// Normal use of the layout system should not use this method. Instead MarkLayoutForRebuild should be used instead, which triggers a delayed layout rebuild during the next layout pass. The delayed rebuild automatically handles objects in the entire layout hierarchy in the correct order, and prevents multiple recalculations for the same layout elements.
|
||||
/// However, for special layout calculation needs, ::ref::ForceRebuildLayoutImmediate can be used to get the layout of a sub-tree resolved immediately. This can even be done from inside layout calculation methods such as ILayoutController.SetLayoutHorizontal orILayoutController.SetLayoutVertical. Usage should be restricted to cases where multiple layout passes are unavaoidable despite the extra cost in performance.
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
// 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))
|
||||
{
|
||||
var scrollRect = components[i];
|
||||
|
||||
if (scrollRect && scrollRect is UnityEngine.UI.ScrollRect)
|
||||
{
|
||||
if (((UnityEngine.UI.ScrollRect)scrollRect).content != rect)
|
||||
action(components[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark the given RectTransform as needing it's layout to be recalculated during the next layout pass.
|
||||
/// </summary>
|
||||
/// <param name="rect">Rect to rebuild.</param>
|
||||
public static void MarkLayoutForRebuild(RectTransform rect)
|
||||
{
|
||||
if (rect == null || rect.gameObject == null)
|
||||
return;
|
||||
|
||||
var comps = ListPool<Component>.Get();
|
||||
bool validLayoutGroup = true;
|
||||
RectTransform layoutRoot = rect;
|
||||
var parent = layoutRoot.parent as RectTransform;
|
||||
while (validLayoutGroup && !(parent == null || parent.gameObject == null))
|
||||
{
|
||||
validLayoutGroup = false;
|
||||
parent.GetComponents(typeof(ILayoutGroup), comps);
|
||||
|
||||
for (int i = 0; i < comps.Count; ++i)
|
||||
{
|
||||
var cur = comps[i];
|
||||
if (cur != null && cur is Behaviour && ((Behaviour)cur).isActiveAndEnabled)
|
||||
{
|
||||
validLayoutGroup = true;
|
||||
layoutRoot = parent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parent = parent.parent as RectTransform;
|
||||
}
|
||||
|
||||
// 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 ValidController(RectTransform layoutRoot, List<Component> comps)
|
||||
{
|
||||
if (layoutRoot == null || layoutRoot.gameObject == null)
|
||||
return false;
|
||||
|
||||
layoutRoot.GetComponents(typeof(ILayoutController), comps);
|
||||
for (int i = 0; i < comps.Count; ++i)
|
||||
{
|
||||
var cur = comps[i];
|
||||
if (cur != null && cur is Behaviour && ((Behaviour)cur).isActiveAndEnabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void MarkLayoutRootForRebuild(RectTransform controller)
|
||||
{
|
||||
if (controller == null)
|
||||
return;
|
||||
|
||||
var rebuilder = s_Rebuilders.Get();
|
||||
rebuilder.Initialize(controller);
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the passed rebuilder point to the same CanvasElement.
|
||||
/// </summary>
|
||||
/// <param name="obj">The other object to compare</param>
|
||||
/// <returns>Are they equal</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj.GetHashCode() == GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "(Layout Rebuilder for) " + m_ToRebuild;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 006633c8a6f4ae94aa9babf72234e1a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,187 @@
|
|||
using UnityEngine.Pool;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility functions for querying layout elements for their minimum, preferred, and flexible sizes.
|
||||
/// </summary>
|
||||
public static class LayoutUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the minimum size of the layout element.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
/// <param name="axis">The axis to query. This can be 0 or 1.</param>
|
||||
/// <remarks>All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used.</remarks>
|
||||
public static float GetMinSize(RectTransform rect, int axis)
|
||||
{
|
||||
return axis == 0 ? GetMinWidth(rect) : GetMinHeight(rect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the preferred size of the layout element.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
/// <param name="axis">The axis to query. This can be 0 or 1.</param>
|
||||
/// <remarks>
|
||||
/// All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used.
|
||||
/// </remarks>
|
||||
public static float GetPreferredSize(RectTransform rect, int axis)
|
||||
{
|
||||
return axis == 0 ? GetPreferredWidth(rect) : GetPreferredHeight(rect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the flexible size of the layout element.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used.
|
||||
/// </remarks>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
/// <param name="axis">The axis to query. This can be 0 or 1.</param>
|
||||
public static float GetFlexibleSize(RectTransform rect, int axis)
|
||||
{
|
||||
return axis == 0 ? GetFlexibleWidth(rect) : GetFlexibleHeight(rect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum width of the layout element.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
/// <remarks>
|
||||
/// All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used.
|
||||
/// </remarks>
|
||||
public static float GetMinWidth(RectTransform rect)
|
||||
{
|
||||
return GetLayoutProperty(rect, e => e.minWidth, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the preferred width of the layout element.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
/// <returns>
|
||||
/// All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used.
|
||||
/// </returns>
|
||||
public static float GetPreferredWidth(RectTransform rect)
|
||||
{
|
||||
return Mathf.Max(GetLayoutProperty(rect, e => e.minWidth, 0), GetLayoutProperty(rect, e => e.preferredWidth, 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the flexible width of the layout element.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used
|
||||
/// </remarks>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
public static float GetFlexibleWidth(RectTransform rect)
|
||||
{
|
||||
return GetLayoutProperty(rect, e => e.flexibleWidth, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum height of the layout element.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
/// <remarks>
|
||||
/// All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used.
|
||||
/// </remarks>
|
||||
public static float GetMinHeight(RectTransform rect)
|
||||
{
|
||||
return GetLayoutProperty(rect, e => e.minHeight, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the preferred height of the layout element.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
/// <remarks>
|
||||
/// All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used.
|
||||
/// </remarks>
|
||||
public static float GetPreferredHeight(RectTransform rect)
|
||||
{
|
||||
return Mathf.Max(GetLayoutProperty(rect, e => e.minHeight, 0), GetLayoutProperty(rect, e => e.preferredHeight, 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the flexible height of the layout element.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All components on the GameObject that implement the ILayoutElement are queried. The one with the highest priority which has a value for this setting is used. If multiple componets have this setting and have the same priority, the maximum value out of those is used.
|
||||
/// </remarks>
|
||||
/// <param name="rect">The RectTransform of the layout element to query.</param>
|
||||
public static float GetFlexibleHeight(RectTransform rect)
|
||||
{
|
||||
return GetLayoutProperty(rect, e => e.flexibleHeight, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a calculated layout property for the layout element with the given RectTransform.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the layout element to get a property for.</param>
|
||||
/// <param name="property">The property to calculate.</param>
|
||||
/// <param name="defaultValue">The default value to use if no component on the layout element supplies the given property</param>
|
||||
/// <returns>The calculated value of the layout property.</returns>
|
||||
public static float GetLayoutProperty(RectTransform rect, System.Func<ILayoutElement, float> property, float defaultValue)
|
||||
{
|
||||
ILayoutElement dummy;
|
||||
return GetLayoutProperty(rect, property, defaultValue, out dummy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a calculated layout property for the layout element with the given RectTransform.
|
||||
/// </summary>
|
||||
/// <param name="rect">The RectTransform of the layout element to get a property for.</param>
|
||||
/// <param name="property">The property to calculate.</param>
|
||||
/// <param name="defaultValue">The default value to use if no component on the layout element supplies the given property</param>
|
||||
/// <param name="source">Optional out parameter to get the component that supplied the calculated value.</param>
|
||||
/// <returns>The calculated value of the layout property.</returns>
|
||||
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);
|
||||
|
||||
var componentsCount = components.Count;
|
||||
for (int i = 0; i < componentsCount; 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 82bf3f737dec0be43a60891958a8da87
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,45 @@
|
|||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("Layout/Vertical Layout Group", 151)]
|
||||
/// <summary>
|
||||
/// Layout child layout elements below each other.
|
||||
/// </summary>
|
||||
public class VerticalLayoutGroup : HorizontalOrVerticalLayoutGroup
|
||||
{
|
||||
protected VerticalLayoutGroup()
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system. Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void CalculateLayoutInputHorizontal()
|
||||
{
|
||||
base.CalculateLayoutInputHorizontal();
|
||||
CalcAlongAxis(0, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system. Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void CalculateLayoutInputVertical()
|
||||
{
|
||||
CalcAlongAxis(1, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system. Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void SetLayoutHorizontal()
|
||||
{
|
||||
SetChildrenAlongAxis(0, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the layout system. Also see ILayoutElement
|
||||
/// </summary>
|
||||
public override void SetLayoutVertical()
|
||||
{
|
||||
SetChildrenAlongAxis(1, true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 59f8146938fff824cb5fd77236b75775
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,192 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("UI/Mask", 13)]
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[DisallowMultipleComponent]
|
||||
/// <summary>
|
||||
/// A component for masking children elements.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By using this element any children elements that have masking enabled will mask where a sibling Graphic would write 0 to the stencil buffer.
|
||||
/// </remarks>
|
||||
public class Mask : UIBehaviour, ICanvasRaycastFilter, IMaterialModifier
|
||||
{
|
||||
[NonSerialized]
|
||||
private RectTransform m_RectTransform;
|
||||
public RectTransform rectTransform
|
||||
{
|
||||
get { return m_RectTransform ?? (m_RectTransform = GetComponent<RectTransform>()); }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private bool m_ShowMaskGraphic = true;
|
||||
|
||||
/// <summary>
|
||||
/// Show the graphic that is associated with the Mask render area.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The graphic associated with the Mask.
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
// Default the graphic to being the maskable graphic if its found.
|
||||
if (graphic is MaskableGraphic)
|
||||
(graphic as MaskableGraphic).isMaskingGraphic = true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (graphic is MaskableGraphic)
|
||||
(graphic as MaskableGraphic).isMaskingGraphic = false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// Default the graphic to being the maskable graphic if its found.
|
||||
if (graphic is MaskableGraphic)
|
||||
(graphic as MaskableGraphic).isMaskingGraphic = true;
|
||||
|
||||
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)
|
||||
{
|
||||
Debug.LogWarning("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)
|
||||
{
|
||||
var maskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Replace, CompareFunction.Always, m_ShowMaskGraphic ? ColorWriteMask.All : 0);
|
||||
StencilMaterial.Remove(m_MaskMaterial);
|
||||
m_MaskMaterial = maskMaterial;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 31a19414c41e5ae4aae2af33fee712f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,220 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine.Pool;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Mask related utility class. This class provides masking-specific utility functions.
|
||||
/// </summary>
|
||||
public class MaskUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Notify all IClippables under the given component that they need to recalculate clipping.
|
||||
/// </summary>
|
||||
/// <param name="mask">The object thats changed for whose children should be notified.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notify all IMaskable under the given component that they need to recalculate masking.
|
||||
/// </summary>
|
||||
/// <param name="mask">The object thats changed for whose children should be notified.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a root Canvas.
|
||||
/// </summary>
|
||||
/// <param name="start">Transform to start the search at going up the hierarchy.</param>
|
||||
/// <returns>Finds either the most root canvas, or the first canvas that overrides sorting.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the stencil depth for a given element.
|
||||
/// </summary>
|
||||
/// <param name="transform">The starting transform to search.</param>
|
||||
/// <param name="stopAfter">Where the search of parents should stop</param>
|
||||
/// <returns>What the proper stencil buffer index should be.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to determine if the child is a descendant of father or is father.
|
||||
/// </summary>
|
||||
/// <param name="father">The transform to compare against.</param>
|
||||
/// <param name="child">The starting transform to search up the hierarchy.</param>
|
||||
/// <returns>Is child equal to father or is a descendant.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the correct RectMask2D for a given IClippable.
|
||||
/// </summary>
|
||||
/// <param name="clippable">Clippable to search from.</param>
|
||||
/// <returns>The Correct RectMask2D</returns>
|
||||
public static RectMask2D GetRectMaskForClippable(IClippable clippable)
|
||||
{
|
||||
List<RectMask2D> rectMaskComponents = ListPool<RectMask2D>.Get();
|
||||
List<Canvas> canvasComponents = ListPool<Canvas>.Get();
|
||||
RectMask2D componentToReturn = null;
|
||||
|
||||
clippable.gameObject.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.gameObject.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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ListPool<RectMask2D>.Release(rectMaskComponents);
|
||||
ListPool<Canvas>.Release(canvasComponents);
|
||||
|
||||
return componentToReturn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for all RectMask2D that apply to the given RectMask2D (includes self).
|
||||
/// </summary>
|
||||
/// <param name="clipper">Starting clipping object.</param>
|
||||
/// <param name="masks">The list of Rect masks</param>
|
||||
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--)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7bd96d76711152648a736c4d28d865f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,304 @@
|
|||
using System;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// A Graphic that is capable of being masked out.
|
||||
/// </summary>
|
||||
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.
|
||||
[SerializeField]
|
||||
private bool m_Maskable = true;
|
||||
|
||||
private bool m_IsMaskingGraphic = false;
|
||||
|
||||
[NonSerialized]
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
[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();
|
||||
|
||||
/// <summary>
|
||||
/// Callback issued when culling changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Called whene the culling state of this MaskableGraphic either becomes culled or visible. You can use this to control other elements of your UI as culling happens.
|
||||
/// </remarks>
|
||||
public CullStateChangedEvent onCullStateChanged
|
||||
{
|
||||
get { return m_OnCullStateChanged; }
|
||||
set { m_OnCullStateChanged = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does this graphic allow masking.
|
||||
/// </summary>
|
||||
public bool maskable
|
||||
{
|
||||
get { return m_Maskable; }
|
||||
set
|
||||
{
|
||||
if (value == m_Maskable)
|
||||
return;
|
||||
m_Maskable = value;
|
||||
m_ShouldRecalculateStencil = true;
|
||||
SetMaterialDirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Is this graphic the graphic on the same object as a Mask that is enabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If toggled ensure to call MaskUtilities.NotifyStencilStateChanged(this); manually as it changes how stenciles are calculated for this image.
|
||||
/// </remarks>
|
||||
public bool isMaskingGraphic
|
||||
{
|
||||
get { return m_IsMaskingGraphic; }
|
||||
set
|
||||
{
|
||||
if (value == m_IsMaskingGraphic)
|
||||
return;
|
||||
|
||||
m_IsMaskingGraphic = value;
|
||||
}
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
[Obsolete("Not used anymore", true)]
|
||||
protected bool m_ShouldRecalculate = true;
|
||||
|
||||
[NonSerialized]
|
||||
protected int m_StencilValue;
|
||||
|
||||
/// <summary>
|
||||
/// See IMaterialModifier.GetModifiedMaterial
|
||||
/// </summary>
|
||||
public virtual Material GetModifiedMaterial(Material baseMaterial)
|
||||
{
|
||||
var toUse = baseMaterial;
|
||||
|
||||
if (m_ShouldRecalculateStencil)
|
||||
{
|
||||
if (maskable)
|
||||
{
|
||||
var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
|
||||
m_StencilValue = MaskUtilities.GetStencilDepth(transform, rootCanvas);
|
||||
}
|
||||
else
|
||||
m_StencilValue = 0;
|
||||
|
||||
m_ShouldRecalculateStencil = false;
|
||||
}
|
||||
|
||||
// if we have a enabled Mask component then it will
|
||||
// generate the mask material. This is an optimization
|
||||
// it adds some coupling between components though :(
|
||||
if (m_StencilValue > 0 && !isMaskingGraphic)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See IClippable.Cull
|
||||
/// </summary>
|
||||
public virtual void Cull(Rect clipRect, bool validRect)
|
||||
{
|
||||
var cull = !validRect || !clipRect.Overlaps(rootCanvasRect, true);
|
||||
UpdateCull(cull);
|
||||
}
|
||||
|
||||
private void UpdateCull(bool cull)
|
||||
{
|
||||
if (canvasRenderer.cull != cull)
|
||||
{
|
||||
canvasRenderer.cull = cull;
|
||||
UISystemProfilerApi.AddMarker("MaskableGraphic.cullingChanged", this);
|
||||
m_OnCullStateChanged.Invoke(cull);
|
||||
OnCullingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See IClippable.SetClipRect
|
||||
/// </summary>
|
||||
public virtual void SetClipRect(Rect clipRect, bool validRect)
|
||||
{
|
||||
if (validRect)
|
||||
canvasRenderer.EnableRectClipping(clipRect);
|
||||
else
|
||||
canvasRenderer.DisableRectClipping();
|
||||
}
|
||||
|
||||
public virtual void SetClipSoftness(Vector2 clipSoftness)
|
||||
{
|
||||
canvasRenderer.clippingSoftness = clipSoftness;
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
m_ShouldRecalculateStencil = true;
|
||||
UpdateClipParent();
|
||||
SetMaterialDirty();
|
||||
|
||||
if (isMaskingGraphic)
|
||||
{
|
||||
MaskUtilities.NotifyStencilStateChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
m_ShouldRecalculateStencil = true;
|
||||
SetMaterialDirty();
|
||||
UpdateClipParent();
|
||||
StencilMaterial.Remove(m_MaskMaterial);
|
||||
m_MaskMaterial = null;
|
||||
|
||||
if (isMaskingGraphic)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
[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)
|
||||
{
|
||||
Matrix4x4 mat = canvas.rootCanvas.transform.worldToLocalMatrix;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_Corners[i] = mat.MultiplyPoint(m_Corners[i]);
|
||||
}
|
||||
|
||||
// bounding box is now based on the min and max of all corners (case 1013182)
|
||||
|
||||
Vector2 min = m_Corners[0];
|
||||
Vector2 max = m_Corners[0];
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
min.x = Mathf.Min(m_Corners[i].x, min.x);
|
||||
min.y = Mathf.Min(m_Corners[i].y, min.y);
|
||||
max.x = Mathf.Max(m_Corners[i].x, max.x);
|
||||
max.y = Mathf.Max(m_Corners[i].y, max.y);
|
||||
}
|
||||
|
||||
return new Rect(min, max - min);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See IClippable.RecalculateClipping
|
||||
/// </summary>
|
||||
public virtual void RecalculateClipping()
|
||||
{
|
||||
UpdateClipParent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See IMaskable.RecalculateMasking
|
||||
/// </summary>
|
||||
public virtual void RecalculateMasking()
|
||||
{
|
||||
// Remove the material reference as either the graphic of the mask has been enable/ disabled.
|
||||
// This will cause the material to be repopulated from the original if need be. (case 994413)
|
||||
StencilMaterial.Remove(m_MaskMaterial);
|
||||
m_MaskMaterial = null;
|
||||
m_ShouldRecalculateStencil = true;
|
||||
SetMaterialDirty();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 976acc75bfafe594cb01142ba21947be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7c6295db74da28645bf49db58b7c9c65
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this interface to modify a Material that renders a Graphic. The Material is modified before the it is passed to the CanvasRenderer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When a Graphic sets a material that is passed (in order) to any components on the GameObject that implement IMaterialModifier. This component can modify the material to be used for rendering.
|
||||
/// </remarks>
|
||||
public interface IMaterialModifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform material modification in this function.
|
||||
/// </summary>
|
||||
/// <param name="baseMaterial">The material that is to be modified</param>
|
||||
/// <returns>The modified material.</returns>
|
||||
Material GetModifiedMaterial(Material baseMaterial);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 20ff4283f687e044087714f82c4d6d3f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 828075bc53f2de84982a943870529b7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,58 @@
|
|||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
internal static class MultipleDisplayUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the current drag position into a relative position for the display.
|
||||
/// </summary>
|
||||
/// <param name="eventData"></param>
|
||||
/// <param name="position"></param>
|
||||
/// <returns>Returns true except when the drag operation is not on the same display as it originated</returns>
|
||||
public static bool GetRelativeMousePositionForDrag(PointerEventData eventData, ref Vector2 position)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
position = eventData.position;
|
||||
#else
|
||||
int pressDisplayIndex = eventData.pointerPressRaycast.displayIndex;
|
||||
var relativePosition = Display.RelativeMouseAt(eventData.position);
|
||||
int currentDisplayIndex = (int)relativePosition.z;
|
||||
|
||||
// Discard events on a different display.
|
||||
if (currentDisplayIndex != pressDisplayIndex)
|
||||
return false;
|
||||
|
||||
// If we are not on the main display then we must use the relative position.
|
||||
position = pressDisplayIndex != 0 ? (Vector2)relativePosition : eventData.position;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the position when the main display has a different rendering resolution to the system resolution.
|
||||
/// By default, the mouse position is relative to the main render area, we need to adjust this so it is relative to the system resolution
|
||||
/// in order to correctly determine the position on other displays.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Vector2 GetMousePositionRelativeToMainDisplayResolution()
|
||||
{
|
||||
var position = Input.mousePosition;
|
||||
#if !UNITY_EDITOR && !UNITY_WSA
|
||||
if (Display.main.renderingHeight != Display.main.systemHeight)
|
||||
{
|
||||
// The position is relative to the main render area, we need to adjust this so
|
||||
// it is relative to the system resolution in order to correctly determine the position on other displays.
|
||||
|
||||
// Correct the y position if we are outside the main display.
|
||||
if ((position.y < 0 || position.y > Display.main.renderingHeight ||
|
||||
position.x < 0 || position.x > Display.main.renderingWidth) && (Screen.fullScreenMode != FullScreenMode.Windowed))
|
||||
{
|
||||
position.y += Display.main.systemHeight - Display.main.renderingHeight;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5ebeb75bd91642048bb3f6c1939fde66
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,308 @@
|
|||
using System;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[Serializable]
|
||||
/// <summary>
|
||||
/// Structure storing details related to navigation.
|
||||
/// </summary>
|
||||
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]
|
||||
/// <summary>
|
||||
/// Navigation mode enumeration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This looks like it's not flags, but it is flags, the reason is that Automatic is considered horizontal and vertical mode combined
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Button button;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// //Set the navigation to the mode "Vertical".
|
||||
/// if (button.navigation.mode == Navigation.Mode.Vertical)
|
||||
/// {
|
||||
/// Debug.Log("The button's navigation mode is Vertical");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public enum Mode
|
||||
{
|
||||
/// <summary>
|
||||
/// No navigation is allowed from this object.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal Navigation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Navigation should only be allowed when left / right move events happen.
|
||||
/// </remarks>
|
||||
Horizontal = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Vertical navigation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Navigation should only be allowed when up / down move events happen.
|
||||
/// </remarks>
|
||||
Vertical = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Automatic navigation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Attempt to find the 'best' next object to select. This should be based on a sensible heuristic.
|
||||
/// </remarks>
|
||||
Automatic = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Explicit navigation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// User should explicitly specify what is selected by each move event.
|
||||
/// </remarks>
|
||||
Explicit = 4,
|
||||
}
|
||||
|
||||
// Which method of navigation will be used.
|
||||
[SerializeField]
|
||||
private Mode m_Mode;
|
||||
|
||||
[Tooltip("Enables navigation to wrap around from last to first or first to last element. Does not work for automatic grid navigation")]
|
||||
[SerializeField]
|
||||
private bool m_WrapAround;
|
||||
|
||||
// Game object selected when the joystick moves up. Used when navigation is set to "Explicit".
|
||||
[SerializeField]
|
||||
private Selectable m_SelectOnUp;
|
||||
|
||||
// Game object selected when the joystick moves down. Used when navigation is set to "Explicit".
|
||||
[SerializeField]
|
||||
private Selectable m_SelectOnDown;
|
||||
|
||||
// Game object selected when the joystick moves left. Used when navigation is set to "Explicit".
|
||||
[SerializeField]
|
||||
private Selectable m_SelectOnLeft;
|
||||
|
||||
// Game object selected when the joystick moves right. Used when navigation is set to "Explicit".
|
||||
[SerializeField]
|
||||
private Selectable m_SelectOnRight;
|
||||
|
||||
/// <summary>
|
||||
/// Navigation mode.
|
||||
/// </summary>
|
||||
public Mode mode { get { return m_Mode; } set { m_Mode = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Enables navigation to wrap around from last to first or first to last element.
|
||||
/// Will find the furthest element from the current element in the opposite direction of movement.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Note: If you have a grid of elements and you are on the last element in a row it will not wrap over to the next row it will pick the furthest element in the opposite direction.
|
||||
/// </example>
|
||||
public bool wrapAround { get { return m_WrapAround; } set { m_WrapAround = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Specify a Selectable UI GameObject to highlight when the Up arrow key is pressed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class HighlightOnKey : MonoBehaviour
|
||||
/// {
|
||||
/// public Button btnSave;
|
||||
/// public Button btnLoad;
|
||||
///
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// // get the Navigation data
|
||||
/// Navigation navigation = btnLoad.navigation;
|
||||
///
|
||||
/// // switch mode to Explicit to allow for custom assigned behavior
|
||||
/// navigation.mode = Navigation.Mode.Explicit;
|
||||
///
|
||||
/// // highlight the Save button if the up arrow key is pressed
|
||||
/// navigation.selectOnUp = btnSave;
|
||||
///
|
||||
/// // reassign the struct data to the button
|
||||
/// btnLoad.navigation = navigation;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Selectable selectOnUp { get { return m_SelectOnUp; } set { m_SelectOnUp = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Specify a Selectable UI GameObject to highlight when the down arrow key is pressed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class HighlightOnKey : MonoBehaviour
|
||||
/// {
|
||||
/// public Button btnSave;
|
||||
/// public Button btnLoad;
|
||||
///
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// // get the Navigation data
|
||||
/// Navigation navigation = btnLoad.navigation;
|
||||
///
|
||||
/// // switch mode to Explicit to allow for custom assigned behavior
|
||||
/// navigation.mode = Navigation.Mode.Explicit;
|
||||
///
|
||||
/// // highlight the Save button if the down arrow key is pressed
|
||||
/// navigation.selectOnDown = btnSave;
|
||||
///
|
||||
/// // reassign the struct data to the button
|
||||
/// btnLoad.navigation = navigation;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Selectable selectOnDown { get { return m_SelectOnDown; } set { m_SelectOnDown = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Specify a Selectable UI GameObject to highlight when the left arrow key is pressed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class HighlightOnKey : MonoBehaviour
|
||||
/// {
|
||||
/// public Button btnSave;
|
||||
/// public Button btnLoad;
|
||||
///
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// // get the Navigation data
|
||||
/// Navigation navigation = btnLoad.navigation;
|
||||
///
|
||||
/// // switch mode to Explicit to allow for custom assigned behavior
|
||||
/// navigation.mode = Navigation.Mode.Explicit;
|
||||
///
|
||||
/// // highlight the Save button if the left arrow key is pressed
|
||||
/// navigation.selectOnLeft = btnSave;
|
||||
///
|
||||
/// // reassign the struct data to the button
|
||||
/// btnLoad.navigation = navigation;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Selectable selectOnLeft { get { return m_SelectOnLeft; } set { m_SelectOnLeft = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Specify a Selectable UI GameObject to highlight when the right arrow key is pressed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class HighlightOnKey : MonoBehaviour
|
||||
/// {
|
||||
/// public Button btnSave;
|
||||
/// public Button btnLoad;
|
||||
///
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// // get the Navigation data
|
||||
/// Navigation navigation = btnLoad.navigation;
|
||||
///
|
||||
/// // switch mode to Explicit to allow for custom assigned behavior
|
||||
/// navigation.mode = Navigation.Mode.Explicit;
|
||||
///
|
||||
/// // highlight the Save button if the right arrow key is pressed
|
||||
/// navigation.selectOnRight = btnSave;
|
||||
///
|
||||
/// // reassign the struct data to the button
|
||||
/// btnLoad.navigation = navigation;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Selectable selectOnRight { get { return m_SelectOnRight; } set { m_SelectOnRight = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Return a Navigation with sensible default values.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class ExampleClass : MonoBehaviour
|
||||
/// {
|
||||
/// public Button button;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// //Set the navigation to the default value. ("Automatic" is the default value).
|
||||
/// button.navigation = Navigation.defaultNavigation;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
static public Navigation defaultNavigation
|
||||
{
|
||||
get
|
||||
{
|
||||
var defaultNav = new Navigation();
|
||||
defaultNav.m_Mode = Mode.Automatic;
|
||||
defaultNav.m_WrapAround = false;
|
||||
return defaultNav;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(Navigation other)
|
||||
{
|
||||
return mode == other.mode &&
|
||||
selectOnUp == other.selectOnUp &&
|
||||
selectOnDown == other.selectOnDown &&
|
||||
selectOnLeft == other.selectOnLeft &&
|
||||
selectOnRight == other.selectOnRight;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 61d491d99e9292c4a81d7d01a74781ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays a Texture2D for the UI System.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
|
||||
[RequireComponent(typeof(CanvasRenderer))]
|
||||
[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>
|
||||
/// The RawImage's texture to be used.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use this to alter or return the Texture the RawImage displays. The Raw Image can display any Texture whereas an Image component can only show a Sprite Texture.
|
||||
/// Note : Keep in mind that using a RawImage creates an extra draw call with each RawImage present, so it's best to use it only for backgrounds or temporary visible graphics.Note: Keep in mind that using a RawImage creates an extra draw call with each RawImage present, so it's best to use it only for backgrounds or temporary visible graphics.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// //Create a new RawImage by going to Create>UI>Raw Image in the hierarchy.
|
||||
/// //Attach this script to the RawImage GameObject.
|
||||
///
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.UI;
|
||||
///
|
||||
/// public class RawImageTexture : MonoBehaviour
|
||||
/// {
|
||||
/// RawImage m_RawImage;
|
||||
/// //Select a Texture in the Inspector to change to
|
||||
/// public Texture m_Texture;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// //Fetch the RawImage component from the GameObject
|
||||
/// m_RawImage = GetComponent<RawImage>();
|
||||
/// //Change the Texture to be the one you define in the Inspector
|
||||
/// m_RawImage.texture = m_Texture;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
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>
|
||||
/// <remarks>
|
||||
/// This means setting the RawImage's RectTransform.sizeDelta to be equal to the Texture dimensions.
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDidApplyAnimationProperties()
|
||||
{
|
||||
SetMaterialDirty();
|
||||
SetVerticesDirty();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1344c3c82d62a2a41a3576d8abb8e3ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,352 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Pool;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("UI/Rect Mask 2D", 13)]
|
||||
[ExecuteAlways]
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
/// <summary>
|
||||
/// A 2D rectangular mask that allows for clipping / masking of areas outside the mask.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The RectMask2D behaves in a similar way to a standard Mask component. It differs though in some of the restrictions that it has.
|
||||
/// A RectMask2D:
|
||||
/// *Only works in the 2D plane
|
||||
/// *Requires elements on the mask to be coplanar.
|
||||
/// *Does not require stencil buffer / extra draw calls
|
||||
/// *Requires fewer draw calls
|
||||
/// *Culls elements that are outside the mask area.
|
||||
/// </remarks>
|
||||
public class RectMask2D : UIBehaviour, IClipper, ICanvasRaycastFilter
|
||||
{
|
||||
[NonSerialized]
|
||||
private readonly RectangularVertexClipper m_VertexClipper = new RectangularVertexClipper();
|
||||
|
||||
[NonSerialized]
|
||||
private RectTransform m_RectTransform;
|
||||
|
||||
[NonSerialized]
|
||||
private HashSet<MaskableGraphic> m_MaskableTargets = new HashSet<MaskableGraphic>();
|
||||
|
||||
[NonSerialized]
|
||||
private HashSet<IClippable> m_ClipTargets = new HashSet<IClippable>();
|
||||
|
||||
[NonSerialized]
|
||||
private bool m_ShouldRecalculateClipRects;
|
||||
|
||||
[NonSerialized]
|
||||
private List<RectMask2D> m_Clippers = new List<RectMask2D>();
|
||||
|
||||
[NonSerialized]
|
||||
private Rect m_LastClipRectCanvasSpace;
|
||||
[NonSerialized]
|
||||
private bool m_ForceClip;
|
||||
|
||||
[SerializeField]
|
||||
private Vector4 m_Padding = new Vector4();
|
||||
|
||||
/// <summary>
|
||||
/// Padding to be applied to the masking
|
||||
/// X = Left
|
||||
/// Y = Bottom
|
||||
/// Z = Right
|
||||
/// W = Top
|
||||
/// </summary>
|
||||
public Vector4 padding
|
||||
{
|
||||
get { return m_Padding; }
|
||||
set
|
||||
{
|
||||
m_Padding = value;
|
||||
MaskUtilities.Notify2DMaskStateChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private Vector2Int m_Softness;
|
||||
|
||||
/// <summary>
|
||||
/// The softness to apply to the horizontal and vertical axis.
|
||||
/// </summary>
|
||||
public Vector2Int softness
|
||||
{
|
||||
get { return m_Softness; }
|
||||
set
|
||||
{
|
||||
m_Softness.x = Mathf.Max(0, value.x);
|
||||
m_Softness.y = Mathf.Max(0, value.y);
|
||||
MaskUtilities.Notify2DMaskStateChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Returns a non-destroyed instance or a null reference.
|
||||
/// </remarks>
|
||||
[NonSerialized] private Canvas m_Canvas;
|
||||
private Canvas Canvas
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Canvas == null)
|
||||
{
|
||||
var list = ListPool<Canvas>.Get();
|
||||
gameObject.GetComponentsInParent(false, list);
|
||||
if (list.Count > 0)
|
||||
m_Canvas = list[list.Count - 1];
|
||||
else
|
||||
m_Canvas = null;
|
||||
ListPool<Canvas>.Release(list);
|
||||
}
|
||||
|
||||
return m_Canvas;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Rect for the mask in canvas space.
|
||||
/// </summary>
|
||||
public Rect canvasRect
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_VertexClipper.GetCanvasRect(rectTransform, Canvas);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to get the RectTransform for the mask.
|
||||
/// </summary>
|
||||
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_MaskableTargets.Clear();
|
||||
m_Clippers.Clear();
|
||||
ClipperRegistry.Unregister(this);
|
||||
MaskUtilities.Notify2DMaskStateChanged(this);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
m_ShouldRecalculateClipRects = true;
|
||||
|
||||
// Dont allow negative softness.
|
||||
m_Softness.x = Mathf.Max(0, m_Softness.x);
|
||||
m_Softness.y = Mathf.Max(0, m_Softness.y);
|
||||
|
||||
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, m_Padding);
|
||||
}
|
||||
|
||||
private Vector3[] m_Corners = new Vector3[4];
|
||||
|
||||
private Rect rootCanvasRect
|
||||
{
|
||||
get
|
||||
{
|
||||
rectTransform.GetWorldCorners(m_Corners);
|
||||
|
||||
if (!ReferenceEquals(Canvas, null))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void PerformClipping()
|
||||
{
|
||||
if (ReferenceEquals(Canvas, null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
// If the mask is in ScreenSpaceOverlay/Camera render mode, its content is only rendered when its rect
|
||||
// overlaps that of the root canvas.
|
||||
RenderMode renderMode = Canvas.rootCanvas.renderMode;
|
||||
bool maskIsCulled =
|
||||
(renderMode == RenderMode.ScreenSpaceCamera || renderMode == RenderMode.ScreenSpaceOverlay) &&
|
||||
!clipRect.Overlaps(rootCanvasRect, true);
|
||||
|
||||
if (maskIsCulled)
|
||||
{
|
||||
// Children are only displayed when inside the mask. If the mask is culled, then the children
|
||||
// inside the mask are also culled. In that situation, we pass an invalid rect to allow callees
|
||||
// to avoid some processing.
|
||||
clipRect = Rect.zero;
|
||||
validRect = false;
|
||||
}
|
||||
|
||||
if (clipRect != m_LastClipRectCanvasSpace)
|
||||
{
|
||||
foreach (IClippable clipTarget in m_ClipTargets)
|
||||
{
|
||||
clipTarget.SetClipRect(clipRect, validRect);
|
||||
}
|
||||
|
||||
foreach (MaskableGraphic maskableTarget in m_MaskableTargets)
|
||||
{
|
||||
maskableTarget.SetClipRect(clipRect, validRect);
|
||||
maskableTarget.Cull(clipRect, validRect);
|
||||
}
|
||||
}
|
||||
else if (m_ForceClip)
|
||||
{
|
||||
foreach (IClippable clipTarget in m_ClipTargets)
|
||||
{
|
||||
clipTarget.SetClipRect(clipRect, validRect);
|
||||
}
|
||||
|
||||
foreach (MaskableGraphic maskableTarget in m_MaskableTargets)
|
||||
{
|
||||
maskableTarget.SetClipRect(clipRect, validRect);
|
||||
|
||||
if (maskableTarget.canvasRenderer.hasMoved)
|
||||
maskableTarget.Cull(clipRect, validRect);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (MaskableGraphic maskableTarget in m_MaskableTargets)
|
||||
{
|
||||
//Case 1170399 - hasMoved is not a valid check when animating on pivot of the object
|
||||
maskableTarget.Cull(clipRect, validRect);
|
||||
}
|
||||
}
|
||||
|
||||
m_LastClipRectCanvasSpace = clipRect;
|
||||
m_ForceClip = false;
|
||||
|
||||
UpdateClipSoftness();
|
||||
}
|
||||
|
||||
public virtual void UpdateClipSoftness()
|
||||
{
|
||||
if (ReferenceEquals(Canvas, null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (IClippable clipTarget in m_ClipTargets)
|
||||
{
|
||||
clipTarget.SetClipSoftness(m_Softness);
|
||||
}
|
||||
|
||||
foreach (MaskableGraphic maskableTarget in m_MaskableTargets)
|
||||
{
|
||||
maskableTarget.SetClipSoftness(m_Softness);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a IClippable to be tracked by the mask.
|
||||
/// </summary>
|
||||
/// <param name="clippable">Add the clippable object for this mask</param>
|
||||
public void AddClippable(IClippable clippable)
|
||||
{
|
||||
if (clippable == null)
|
||||
return;
|
||||
m_ShouldRecalculateClipRects = true;
|
||||
MaskableGraphic maskable = clippable as MaskableGraphic;
|
||||
|
||||
if (maskable == null)
|
||||
m_ClipTargets.Add(clippable);
|
||||
else
|
||||
m_MaskableTargets.Add(maskable);
|
||||
|
||||
m_ForceClip = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove an IClippable from being tracked by the mask.
|
||||
/// </summary>
|
||||
/// <param name="clippable">Remove the clippable object from this mask</param>
|
||||
public void RemoveClippable(IClippable clippable)
|
||||
{
|
||||
if (clippable == null)
|
||||
return;
|
||||
|
||||
m_ShouldRecalculateClipRects = true;
|
||||
clippable.SetClipRect(new Rect(), false);
|
||||
|
||||
MaskableGraphic maskable = clippable as MaskableGraphic;
|
||||
|
||||
if (maskable == null)
|
||||
m_ClipTargets.Remove(clippable);
|
||||
else
|
||||
m_MaskableTargets.Remove(maskable);
|
||||
|
||||
m_ForceClip = true;
|
||||
}
|
||||
|
||||
protected override void OnTransformParentChanged()
|
||||
{
|
||||
base.OnTransformParentChanged();
|
||||
m_ShouldRecalculateClipRects = true;
|
||||
}
|
||||
|
||||
protected override void OnCanvasHierarchyChanged()
|
||||
{
|
||||
m_Canvas = null;
|
||||
base.OnCanvasHierarchyChanged();
|
||||
m_ShouldRecalculateClipRects = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3312d7739989d2b4e91e6319e9a96d76
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1aa08ab6e0800fa44ae55d278d1423e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,552 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("UI/Scrollbar", 34)]
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
/// <summary>
|
||||
/// A standard scrollbar with a variable sized handle that can be dragged between 0 and 1.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The slider component is a Selectable that controls a handle which follow the current value and is sized according to the size property.
|
||||
/// The anchors of the handle RectTransforms are driven by the Scrollbar. The handle can be a direct child of the GameObject with the Scrollbar, or intermediary RectTransforms can be placed in between for additional control.
|
||||
/// When a change to the scrollbar value occurs, a callback is sent to any registered listeners of onValueChanged.
|
||||
/// </remarks>
|
||||
public class Scrollbar : Selectable, IBeginDragHandler, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Setting that indicates one of four directions the scrollbar will travel.
|
||||
/// </summary>
|
||||
public enum Direction
|
||||
{
|
||||
/// <summary>
|
||||
/// Starting position is the Left.
|
||||
/// </summary>
|
||||
LeftToRight,
|
||||
|
||||
/// <summary>
|
||||
/// Starting position is the Right
|
||||
/// </summary>
|
||||
RightToLeft,
|
||||
|
||||
/// <summary>
|
||||
/// Starting position is the Bottom.
|
||||
/// </summary>
|
||||
BottomToTop,
|
||||
|
||||
/// <summary>
|
||||
/// Starting position is the Top.
|
||||
/// </summary>
|
||||
TopToBottom,
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
/// <summary>
|
||||
/// UnityEvent callback for when a scrollbar is scrolled.
|
||||
/// </summary>
|
||||
public class ScrollEvent : UnityEvent<float> {}
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform m_HandleRect;
|
||||
|
||||
/// <summary>
|
||||
/// The RectTransform to use for the handle.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The direction of the scrollbar from minimum to maximum value.
|
||||
/// </summary>
|
||||
public Direction direction { get { return m_Direction; } set { if (SetPropertyUtility.SetStruct(ref m_Direction, value)) UpdateVisuals(); } }
|
||||
|
||||
protected Scrollbar()
|
||||
{}
|
||||
|
||||
[Range(0f, 1f)]
|
||||
[SerializeField]
|
||||
private float m_Value;
|
||||
|
||||
/// <summary>
|
||||
/// The current value of the scrollbar, between 0 and 1.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the scrollbar without invoking onValueChanged callback.
|
||||
/// </summary>
|
||||
/// <param name="input">The new value for the scrollbar.</param>
|
||||
public virtual void SetValueWithoutNotify(float input)
|
||||
{
|
||||
Set(input, false);
|
||||
}
|
||||
|
||||
[Range(0f, 1f)]
|
||||
[SerializeField]
|
||||
private float m_Size = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// The size of the scrollbar handle where 1 means it fills the entire scrollbar.
|
||||
/// </summary>
|
||||
public float size { get { return m_Size; } set { if (SetPropertyUtility.SetStruct(ref m_Size, Mathf.Clamp01(value))) UpdateVisuals(); } }
|
||||
|
||||
[Range(0, 11)]
|
||||
[SerializeField]
|
||||
private int m_NumberOfSteps = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The number of steps to use for the value. A value of 0 disables use of steps.
|
||||
/// </summary>
|
||||
public int numberOfSteps { get { return m_NumberOfSteps; } set { if (SetPropertyUtility.SetStruct(ref m_NumberOfSteps, value)) { Set(m_Value); UpdateVisuals(); } } }
|
||||
|
||||
[Space(6)]
|
||||
|
||||
[SerializeField]
|
||||
private ScrollEvent m_OnValueChanged = new ScrollEvent();
|
||||
|
||||
/// <summary>
|
||||
/// Handling for when the scrollbar value is changed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
|
||||
/// </remarks>
|
||||
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; } }
|
||||
|
||||
// field is never assigned warning
|
||||
#pragma warning disable 649
|
||||
private DrivenRectTransformTracker m_Tracker;
|
||||
#pragma warning restore 649
|
||||
private Coroutine m_PointerDownRepeat;
|
||||
private bool isPointerDownAndNotDragging = false;
|
||||
|
||||
// This "delayed" mechanism is required for case 1037681.
|
||||
private bool m_DelayedUpdateVisuals = 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 (in next update) since other things might affect them even if value didn't change.
|
||||
m_DelayedUpdateVisuals = true;
|
||||
}
|
||||
|
||||
if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See ICanvasElement.LayoutComplete.
|
||||
/// </summary>
|
||||
public virtual void LayoutComplete()
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// See ICanvasElement.GraphicUpdateComplete.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the rect based on the delayed update visuals.
|
||||
/// Got around issue of calling sendMessage from onValidate.
|
||||
/// </summary>
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (m_DelayedUpdateVisuals)
|
||||
{
|
||||
m_DelayedUpdateVisuals = false;
|
||||
UpdateVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateCachedReferences()
|
||||
{
|
||||
if (m_HandleRect && m_HandleRect.parent != null)
|
||||
m_ContainerRect = m_HandleRect.parent.GetComponent<RectTransform>();
|
||||
else
|
||||
m_ContainerRect = null;
|
||||
}
|
||||
|
||||
void Set(float input, bool sendCallback = true)
|
||||
{
|
||||
float currentValue = m_Value;
|
||||
|
||||
// bugfix (case 802330) clamp01 input in callee before calling this function, this allows inertia from dragging content to go past extremities without being clamped
|
||||
m_Value = 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 = Mathf.Clamp01(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 position = Vector2.zero;
|
||||
if (!MultipleDisplayUtilities.GetRelativeMousePositionForDrag(eventData, ref position))
|
||||
return;
|
||||
|
||||
Vector2 localCursor;
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(m_ContainerRect, 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;
|
||||
|
||||
DoUpdateDrag(handleCorner, remainingSize);
|
||||
}
|
||||
|
||||
//this function is testable, it is found using reflection in ScrollbarClamp test
|
||||
private void DoUpdateDrag(Vector2 handleCorner, float remainingSize)
|
||||
{
|
||||
switch (m_Direction)
|
||||
{
|
||||
case Direction.LeftToRight:
|
||||
Set(Mathf.Clamp01(handleCorner.x / remainingSize));
|
||||
break;
|
||||
case Direction.RightToLeft:
|
||||
Set(Mathf.Clamp01(1f - (handleCorner.x / remainingSize)));
|
||||
break;
|
||||
case Direction.BottomToTop:
|
||||
Set(Mathf.Clamp01(handleCorner.y / remainingSize));
|
||||
break;
|
||||
case Direction.TopToBottom:
|
||||
Set(Mathf.Clamp01(1f - (handleCorner.y / remainingSize)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool MayDrag(PointerEventData eventData)
|
||||
{
|
||||
return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handling for when the scrollbar value is begin being dragged.
|
||||
/// </summary>
|
||||
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.pointerPressRaycast.screenPosition, eventData.enterEventCamera))
|
||||
{
|
||||
Vector2 localMousePos;
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.pointerPressRaycast.screenPosition, eventData.pressEventCamera, out localMousePos))
|
||||
m_Offset = localMousePos - m_HandleRect.rect.center;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handling for when the scrollbar value is dragged.
|
||||
/// </summary>
|
||||
public virtual void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!MayDrag(eventData))
|
||||
return;
|
||||
|
||||
if (m_ContainerRect != null)
|
||||
UpdateDrag(eventData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when pointer is pressed down on the scrollbar.
|
||||
/// </summary>
|
||||
public override void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
if (!MayDrag(eventData))
|
||||
return;
|
||||
|
||||
base.OnPointerDown(eventData);
|
||||
isPointerDownAndNotDragging = true;
|
||||
m_PointerDownRepeat = StartCoroutine(ClickRepeat(eventData.pointerPressRaycast.screenPosition, eventData.enterEventCamera));
|
||||
}
|
||||
|
||||
protected IEnumerator ClickRepeat(PointerEventData eventData)
|
||||
{
|
||||
return ClickRepeat(eventData.pointerPressRaycast.screenPosition, eventData.enterEventCamera);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine function for handling continual press during Scrollbar.OnPointerDown.
|
||||
/// </summary>
|
||||
protected IEnumerator ClickRepeat(Vector2 screenPosition, Camera camera)
|
||||
{
|
||||
while (isPointerDownAndNotDragging)
|
||||
{
|
||||
if (!RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, screenPosition, camera))
|
||||
{
|
||||
Vector2 localMousePos;
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, screenPosition, camera, out localMousePos))
|
||||
{
|
||||
var axisCoordinate = axis == 0 ? localMousePos.x : localMousePos.y;
|
||||
|
||||
// modifying value depending on direction, fixes (case 925824)
|
||||
|
||||
float change = axisCoordinate < 0 ? size : -size;
|
||||
value += reverseValue ? change : -change;
|
||||
}
|
||||
}
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
StopCoroutine(m_PointerDownRepeat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when pointer is released after pressing on the scrollbar.
|
||||
/// </summary>
|
||||
public override void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
base.OnPointerUp(eventData);
|
||||
isPointerDownAndNotDragging = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handling for movement events.
|
||||
/// </summary>
|
||||
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(Mathf.Clamp01(reverseValue ? value + stepSize : value - stepSize));
|
||||
else
|
||||
base.OnMove(eventData);
|
||||
break;
|
||||
case MoveDirection.Right:
|
||||
if (axis == Axis.Horizontal && FindSelectableOnRight() == null)
|
||||
Set(Mathf.Clamp01(reverseValue ? value - stepSize : value + stepSize));
|
||||
else
|
||||
base.OnMove(eventData);
|
||||
break;
|
||||
case MoveDirection.Up:
|
||||
if (axis == Axis.Vertical && FindSelectableOnUp() == null)
|
||||
Set(Mathf.Clamp01(reverseValue ? value - stepSize : value + stepSize));
|
||||
else
|
||||
base.OnMove(eventData);
|
||||
break;
|
||||
case MoveDirection.Down:
|
||||
if (axis == Axis.Vertical && FindSelectableOnDown() == null)
|
||||
Set(Mathf.Clamp01(reverseValue ? value + stepSize : value - stepSize));
|
||||
else
|
||||
base.OnMove(eventData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prevents selection if we we move on the Horizontal axis. See Selectable.FindSelectableOnLeft.
|
||||
/// </summary>
|
||||
public override Selectable FindSelectableOnLeft()
|
||||
{
|
||||
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
|
||||
return null;
|
||||
return base.FindSelectableOnLeft();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prevents selection if we we move on the Horizontal axis. See Selectable.FindSelectableOnRight.
|
||||
/// </summary>
|
||||
public override Selectable FindSelectableOnRight()
|
||||
{
|
||||
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
|
||||
return null;
|
||||
return base.FindSelectableOnRight();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prevents selection if we we move on the Vertical axis. See Selectable.FindSelectableOnUp.
|
||||
/// </summary>
|
||||
public override Selectable FindSelectableOnUp()
|
||||
{
|
||||
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
|
||||
return null;
|
||||
return base.FindSelectableOnUp();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prevents selection if we we move on the Vertical axis. See Selectable.FindSelectableOnDown.
|
||||
/// </summary>
|
||||
public override Selectable FindSelectableOnDown()
|
||||
{
|
||||
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
|
||||
return null;
|
||||
return base.FindSelectableOnDown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See: IInitializePotentialDragHandler.OnInitializePotentialDrag
|
||||
/// </summary>
|
||||
public virtual void OnInitializePotentialDrag(PointerEventData eventData)
|
||||
{
|
||||
eventData.useDragThreshold = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the direction of the scrollbar, optionally setting the layout as well.
|
||||
/// </summary>
|
||||
/// <param name="direction">The direction of the scrollbar.</param>
|
||||
/// <param name="includeRectLayouts">Should the layout be flipped together with the direction?</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2a4db7a114972834c8e4117be1d82ba3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7a98125502f715b4b83cfb77b434e436
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b9ba34e0b1884454fbc4257260e22b64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,779 @@
|
|||
using System;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[AddComponentMenu("UI/Slider", 33)]
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
/// <summary>
|
||||
/// A standard slider that can be moved between a minimum and maximum value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The slider component is a Selectable that controls a fill, a handle, or both. The fill, when used, spans from the minimum value to the current value while the handle, when used, follow the current value.
|
||||
/// The anchors of the fill and handle RectTransforms are driven by the Slider. The fill and handle can be direct children of the GameObject with the Slider, or intermediary RectTransforms can be placed in between for additional control.
|
||||
/// When a change to the slider value occurs, a callback is sent to any registered listeners of UI.Slider.onValueChanged.
|
||||
/// </remarks>
|
||||
public class Slider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Setting that indicates one of four directions.
|
||||
/// </summary>
|
||||
public enum Direction
|
||||
{
|
||||
/// <summary>
|
||||
/// From the left to the right
|
||||
/// </summary>
|
||||
LeftToRight,
|
||||
|
||||
/// <summary>
|
||||
/// From the right to the left
|
||||
/// </summary>
|
||||
RightToLeft,
|
||||
|
||||
/// <summary>
|
||||
/// From the bottom to the top.
|
||||
/// </summary>
|
||||
BottomToTop,
|
||||
|
||||
/// <summary>
|
||||
/// From the top to the bottom.
|
||||
/// </summary>
|
||||
TopToBottom,
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
/// <summary>
|
||||
/// Event type used by the UI.Slider.
|
||||
/// </summary>
|
||||
public class SliderEvent : UnityEvent<float> {}
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform m_FillRect;
|
||||
|
||||
/// <summary>
|
||||
/// Optional RectTransform to use as fill for the slider.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
/// //Reference to new "RectTransform"(Child of FillArea).
|
||||
/// public RectTransform newFillRect;
|
||||
///
|
||||
/// //Deactivates the old FillRect and assigns a new one.
|
||||
/// void Start()
|
||||
/// {
|
||||
/// mainSlider.fillRect.gameObject.SetActive(false);
|
||||
/// mainSlider.fillRect = newFillRect;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public RectTransform fillRect { get { return m_FillRect; } set { if (SetPropertyUtility.SetClass(ref m_FillRect, value)) {UpdateCachedReferences(); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform m_HandleRect;
|
||||
|
||||
/// <summary>
|
||||
/// Optional RectTransform to use as a handle for the slider.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
/// //Reference to new "RectTransform" (Child of "Handle Slide Area").
|
||||
/// public RectTransform handleHighlighted;
|
||||
///
|
||||
/// //Deactivates the old Handle, then assigns and enables the new one.
|
||||
/// void Start()
|
||||
/// {
|
||||
/// mainSlider.handleRect.gameObject.SetActive(false);
|
||||
/// mainSlider.handleRect = handleHighlighted;
|
||||
/// mainSlider.handleRect.gameObject.SetActive(true);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The direction of the slider, from minimum to maximum value.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
///
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //Changes the direction of the slider.
|
||||
/// if (mainSlider.direction == Slider.Direction.BottomToTop)
|
||||
/// {
|
||||
/// mainSlider.direction = Slider.Direction.TopToBottom;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public Direction direction { get { return m_Direction; } set { if (SetPropertyUtility.SetStruct(ref m_Direction, value)) UpdateVisuals(); } }
|
||||
|
||||
[SerializeField]
|
||||
private float m_MinValue = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum allowed value of the slider.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// // Changes the minimum value of the slider to 10;
|
||||
/// mainSlider.minValue = 10;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum allowed value of the slider.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
///
|
||||
/// void Start()
|
||||
/// {
|
||||
/// // Changes the max value of the slider to 20;
|
||||
/// mainSlider.maxValue = 20;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Should the value only be allowed to be whole numbers?
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
///
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //sets the slider's value to accept whole numbers only.
|
||||
/// mainSlider.wholeNumbers = true;
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public bool wholeNumbers { get { return m_WholeNumbers; } set { if (SetPropertyUtility.SetStruct(ref m_WholeNumbers, value)) { Set(m_Value); UpdateVisuals(); } } }
|
||||
|
||||
[SerializeField]
|
||||
protected float m_Value;
|
||||
|
||||
/// <summary>
|
||||
/// The current value of the slider.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
///
|
||||
/// //Invoked when a submit button is clicked.
|
||||
/// public void SubmitSliderSetting()
|
||||
/// {
|
||||
/// //Displays the value of the slider in the console.
|
||||
/// Debug.Log(mainSlider.value);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
public virtual float value
|
||||
{
|
||||
get
|
||||
{
|
||||
return wholeNumbers ? Mathf.Round(m_Value) : m_Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the slider without invoking onValueChanged callback.
|
||||
/// </summary>
|
||||
/// <param name="input">The new value for the slider.</param>
|
||||
public virtual void SetValueWithoutNotify(float input)
|
||||
{
|
||||
Set(input, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current value of the slider normalized into a value between 0 and 1.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
///
|
||||
/// //Set to invoke when "OnValueChanged" method is called.
|
||||
/// void CheckNormalisedValue()
|
||||
/// {
|
||||
/// //Displays the normalised value of the slider everytime the value changes.
|
||||
/// Debug.Log(mainSlider.normalizedValue);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
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]
|
||||
|
||||
[SerializeField]
|
||||
private SliderEvent m_OnValueChanged = new SliderEvent();
|
||||
|
||||
/// <summary>
|
||||
/// Callback executed when the value of the slider is changed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
///
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// //Adds a listener to the main slider and invokes a method when the value changes.
|
||||
/// mainSlider.onValueChanged.AddListener(delegate {ValueChangeCheck(); });
|
||||
/// }
|
||||
///
|
||||
/// // Invoked when the value of the slider changes.
|
||||
/// public void ValueChangeCheck()
|
||||
/// {
|
||||
/// Debug.Log(mainSlider.value);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
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;
|
||||
|
||||
// field is never assigned warning
|
||||
#pragma warning disable 649
|
||||
private DrivenRectTransformTracker m_Tracker;
|
||||
#pragma warning restore 649
|
||||
|
||||
// This "delayed" mechanism is required for case 1037681.
|
||||
private bool m_DelayedUpdateVisuals = false;
|
||||
|
||||
// Size of each step.
|
||||
float stepSize { get { return wholeNumbers ? 1 : (maxValue - minValue) * 0.1f; } }
|
||||
|
||||
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();
|
||||
// Update rects in next update since other things might affect them even if value didn't change.
|
||||
m_DelayedUpdateVisuals = true;
|
||||
}
|
||||
|
||||
if (!UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) && !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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See ICanvasElement.LayoutComplete
|
||||
/// </summary>
|
||||
public virtual void LayoutComplete()
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// See ICanvasElement.GraphicUpdateComplete
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the rect based on the delayed update visuals.
|
||||
/// Got around issue of calling sendMessage from onValidate.
|
||||
/// </summary>
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (m_DelayedUpdateVisuals)
|
||||
{
|
||||
m_DelayedUpdateVisuals = false;
|
||||
Set(m_Value, false);
|
||||
UpdateVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
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_FillRect != (RectTransform)transform)
|
||||
{
|
||||
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_FillRect = null;
|
||||
m_FillContainerRect = null;
|
||||
m_FillImage = null;
|
||||
}
|
||||
|
||||
if (m_HandleRect && m_HandleRect != (RectTransform)transform)
|
||||
{
|
||||
m_HandleTransform = m_HandleRect.transform;
|
||||
if (m_HandleTransform.parent != null)
|
||||
m_HandleContainerRect = m_HandleTransform.parent.GetComponent<RectTransform>();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HandleRect = null;
|
||||
m_HandleContainerRect = null;
|
||||
}
|
||||
}
|
||||
|
||||
float ClampValue(float input)
|
||||
{
|
||||
float newValue = Mathf.Clamp(input, minValue, maxValue);
|
||||
if (wholeNumbers)
|
||||
newValue = Mathf.Round(newValue);
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of the slider.
|
||||
/// </summary>
|
||||
/// <param name="input">The new value for the slider.</param>
|
||||
/// <param name="sendCallback">If the OnValueChanged callback should be invoked.</param>
|
||||
/// <remarks>
|
||||
/// Process the input to ensure the value is between min and max value. If the input is different set the value and send the callback is required.
|
||||
/// </remarks>
|
||||
protected virtual void Set(float input, bool sendCallback = true)
|
||||
{
|
||||
// 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()
|
||||
{
|
||||
#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 position = Vector2.zero;
|
||||
if (!MultipleDisplayUtilities.GetRelativeMousePositionForDrag(eventData, ref position))
|
||||
return;
|
||||
|
||||
Vector2 localCursor;
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, 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.pointerPressRaycast.screenPosition, eventData.enterEventCamera))
|
||||
{
|
||||
Vector2 localMousePos;
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.pointerPressRaycast.screenPosition, 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See Selectable.FindSelectableOnLeft
|
||||
/// </summary>
|
||||
public override Selectable FindSelectableOnLeft()
|
||||
{
|
||||
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
|
||||
return null;
|
||||
return base.FindSelectableOnLeft();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See Selectable.FindSelectableOnRight
|
||||
/// </summary>
|
||||
public override Selectable FindSelectableOnRight()
|
||||
{
|
||||
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Horizontal)
|
||||
return null;
|
||||
return base.FindSelectableOnRight();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See Selectable.FindSelectableOnUp
|
||||
/// </summary>
|
||||
public override Selectable FindSelectableOnUp()
|
||||
{
|
||||
if (navigation.mode == Navigation.Mode.Automatic && axis == Axis.Vertical)
|
||||
return null;
|
||||
return base.FindSelectableOnUp();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See Selectable.FindSelectableOnDown
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the direction of this slider, optionally changing the layout as well.
|
||||
/// </summary>
|
||||
/// <param name="direction">The direction of the slider</param>
|
||||
/// <param name="includeRectLayouts">Should the layout be flipped together with the slider direction</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using System.Collections;
|
||||
/// using UnityEngine.UI; // Required when Using UI elements.
|
||||
///
|
||||
/// public class Example : MonoBehaviour
|
||||
/// {
|
||||
/// public Slider mainSlider;
|
||||
///
|
||||
/// public void Start()
|
||||
/// {
|
||||
/// mainSlider.SetDirection(Slider.Direction.LeftToRight, false);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
///</code>
|
||||
/// </example>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue