1096 lines
27 KiB
C#
1096 lines
27 KiB
C#
//----------------------------------------------
|
|
// NGUI: Next-Gen UI kit
|
|
// Copyright © 2011-2015 Tasharen Entertainment
|
|
//----------------------------------------------
|
|
|
|
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
using System.Collections;
|
|
|
|
/// <summary>
|
|
/// Popup list can be used to display pop-up menus and drop-down lists.
|
|
/// </summary>
|
|
|
|
[ExecuteInEditMode]
|
|
[AddComponentMenu("NGUI/Interaction/Popup List")]
|
|
public class UIPopupList : UIWidgetContainer
|
|
{
|
|
/// <summary>
|
|
/// Current popup list. Only available during the OnSelectionChange event callback.
|
|
/// </summary>
|
|
|
|
static public UIPopupList current;
|
|
static GameObject mChild;
|
|
static float mFadeOutComplete = 0f;
|
|
|
|
const float animSpeed = 0.15f;
|
|
|
|
public enum Position
|
|
{
|
|
Auto,
|
|
Above,
|
|
Below,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Atlas used by the sprites.
|
|
/// </summary>
|
|
|
|
public UIAtlas atlas;
|
|
|
|
/// <summary>
|
|
/// Font used by the labels.
|
|
/// </summary>
|
|
|
|
public UIFont bitmapFont;
|
|
|
|
/// <summary>
|
|
/// True type font used by the labels. Alternative to specifying a bitmap font ('font').
|
|
/// </summary>
|
|
|
|
public Font trueTypeFont;
|
|
|
|
/// <summary>
|
|
/// Font used by the popup list. Conveniently wraps both dynamic and bitmap fonts into one property.
|
|
/// </summary>
|
|
|
|
public Object ambigiousFont
|
|
{
|
|
get
|
|
{
|
|
if (trueTypeFont != null) return trueTypeFont;
|
|
if (bitmapFont != null) return bitmapFont;
|
|
return font;
|
|
}
|
|
set
|
|
{
|
|
if (value is Font)
|
|
{
|
|
trueTypeFont = value as Font;
|
|
bitmapFont = null;
|
|
font = null;
|
|
}
|
|
else if (value is UIFont)
|
|
{
|
|
bitmapFont = value as UIFont;
|
|
trueTypeFont = null;
|
|
font = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Size of the font to use for the popup list's labels.
|
|
/// </summary>
|
|
|
|
public int fontSize = 16;
|
|
|
|
/// <summary>
|
|
/// Font style used by the dynamic font.
|
|
/// </summary>
|
|
|
|
public FontStyle fontStyle = FontStyle.Normal;
|
|
|
|
/// <summary>
|
|
/// Name of the sprite used to create the popup's background.
|
|
/// </summary>
|
|
|
|
public string backgroundSprite;
|
|
|
|
/// <summary>
|
|
/// Name of the sprite used to highlight items.
|
|
/// </summary>
|
|
|
|
public string highlightSprite;
|
|
|
|
/// <summary>
|
|
/// Popup list's display style.
|
|
/// </summary>
|
|
|
|
public Position position = Position.Auto;
|
|
|
|
/// <summary>
|
|
/// Label alignment to use.
|
|
/// </summary>
|
|
|
|
public NGUIText.Alignment alignment = NGUIText.Alignment.Left;
|
|
|
|
/// <summary>
|
|
/// New line-delimited list of items.
|
|
/// </summary>
|
|
|
|
public List<string> items = new List<string>();
|
|
|
|
/// <summary>
|
|
/// You can associate arbitrary data to be associated with your entries if you like.
|
|
/// The only downside is that this must be done via code.
|
|
/// </summary>
|
|
|
|
public List<object> itemData = new List<object>();
|
|
|
|
/// <summary>
|
|
/// Amount of padding added to labels.
|
|
/// </summary>
|
|
|
|
public Vector2 padding = new Vector3(4f, 4f);
|
|
|
|
/// <summary>
|
|
/// Color tint applied to labels inside the list.
|
|
/// </summary>
|
|
|
|
public Color textColor = Color.white;
|
|
|
|
/// <summary>
|
|
/// Color tint applied to the background.
|
|
/// </summary>
|
|
|
|
public Color backgroundColor = Color.white;
|
|
|
|
/// <summary>
|
|
/// Color tint applied to the highlighter.
|
|
/// </summary>
|
|
|
|
public Color highlightColor = new Color(225f / 255f, 200f / 255f, 150f / 255f, 1f);
|
|
|
|
/// <summary>
|
|
/// Whether the popup list is animated or not. Disable for better performance.
|
|
/// </summary>
|
|
|
|
public bool isAnimated = true;
|
|
|
|
/// <summary>
|
|
/// Whether the popup list's values will be localized.
|
|
/// </summary>
|
|
|
|
public bool isLocalized = false;
|
|
|
|
/// <summary>
|
|
/// Whether a separate panel will be used to ensure that the popup will appear on top of everything else.
|
|
/// </summary>
|
|
|
|
public bool separatePanel = true;
|
|
|
|
public enum OpenOn
|
|
{
|
|
ClickOrTap,
|
|
RightClick,
|
|
DoubleClick,
|
|
Manual,
|
|
}
|
|
|
|
/// <summary>
|
|
/// What kind of click is needed in order to open the popup list.
|
|
/// </summary>
|
|
|
|
public OpenOn openOn = OpenOn.ClickOrTap;
|
|
|
|
/// <summary>
|
|
/// Callbacks triggered when the popup list gets a new item selection.
|
|
/// </summary>
|
|
|
|
public List<EventDelegate> onChange = new List<EventDelegate>();
|
|
|
|
// Currently selected item
|
|
[HideInInspector][SerializeField] protected string mSelectedItem;
|
|
[HideInInspector][SerializeField] protected UIPanel mPanel;
|
|
[HideInInspector][SerializeField] protected UISprite mBackground;
|
|
[HideInInspector][SerializeField] protected UISprite mHighlight;
|
|
[HideInInspector][SerializeField] protected UILabel mHighlightedLabel = null;
|
|
[HideInInspector][SerializeField] protected List<UILabel> mLabelList = new List<UILabel>();
|
|
[HideInInspector][SerializeField] protected float mBgBorder = 0f;
|
|
|
|
[System.NonSerialized] protected GameObject mSelection;
|
|
[System.NonSerialized] protected int mOpenFrame = 0;
|
|
|
|
// Deprecated functionality
|
|
[HideInInspector][SerializeField] GameObject eventReceiver;
|
|
[HideInInspector][SerializeField] string functionName = "OnSelectionChange";
|
|
[HideInInspector][SerializeField] float textScale = 0f;
|
|
[HideInInspector][SerializeField] UIFont font; // Use 'bitmapFont' instead
|
|
|
|
// This functionality is no longer needed as the same can be achieved by choosing a
|
|
// OnValueChange notification targeting a label's SetCurrentSelection function.
|
|
// If your code was list.textLabel = myLabel, change it to:
|
|
// EventDelegate.Add(list.onChange, lbl.SetCurrentSelection);
|
|
[HideInInspector][SerializeField] UILabel textLabel;
|
|
|
|
public delegate void LegacyEvent (string val);
|
|
LegacyEvent mLegacyEvent;
|
|
|
|
[System.Obsolete("Use EventDelegate.Add(popup.onChange, YourCallback) instead, and UIPopupList.current.value to determine the state")]
|
|
public LegacyEvent onSelectionChange { get { return mLegacyEvent; } set { mLegacyEvent = value; } }
|
|
|
|
/// <summary>
|
|
/// Whether the popup list is currently open.
|
|
/// </summary>
|
|
|
|
static public bool isOpen { get { return current != null && (mChild != null || mFadeOutComplete > Time.unscaledTime); } }
|
|
|
|
/// <summary>
|
|
/// Current selection.
|
|
/// </summary>
|
|
|
|
public virtual string value
|
|
{
|
|
get
|
|
{
|
|
return mSelectedItem;
|
|
}
|
|
set
|
|
{
|
|
mSelectedItem = value;
|
|
if (mSelectedItem == null) return;
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying) return;
|
|
#endif
|
|
if (mSelectedItem != null)
|
|
TriggerCallbacks();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Item data associated with the current selection.
|
|
/// </summary>
|
|
|
|
public virtual object data
|
|
{
|
|
get
|
|
{
|
|
int index = items.IndexOf(mSelectedItem);
|
|
return (index > -1) && index < itemData.Count ? itemData[index] : null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the collider is enabled and the widget can be interacted with.
|
|
/// </summary>
|
|
|
|
public bool isColliderEnabled
|
|
{
|
|
get
|
|
{
|
|
Collider c = GetComponent<Collider>();
|
|
if (c != null) return c.enabled;
|
|
Collider2D b = GetComponent<Collider2D>();
|
|
return (b != null && b.enabled);
|
|
}
|
|
}
|
|
|
|
[System.Obsolete("Use 'value' instead")]
|
|
public string selection { get { return value; } set { this.value = value; } }
|
|
|
|
/// <summary>
|
|
/// Whether the popup list is actually usable.
|
|
/// </summary>
|
|
|
|
bool isValid { get { return bitmapFont != null || trueTypeFont != null; } }
|
|
|
|
/// <summary>
|
|
/// Active font size.
|
|
/// </summary>
|
|
|
|
int activeFontSize { get { return (trueTypeFont != null || bitmapFont == null) ? fontSize : bitmapFont.defaultSize; } }
|
|
|
|
/// <summary>
|
|
/// Font scale applied to the popup list's text.
|
|
/// </summary>
|
|
|
|
float activeFontScale { get { return (trueTypeFont != null || bitmapFont == null) ? 1f : (float)fontSize / bitmapFont.defaultSize; } }
|
|
|
|
/// <summary>
|
|
/// Clear the popup list's contents.
|
|
/// </summary>
|
|
|
|
public virtual void Clear ()
|
|
{
|
|
items.Clear();
|
|
itemData.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a new item to the popup list.
|
|
/// </summary>
|
|
|
|
public virtual void AddItem (string text)
|
|
{
|
|
items.Add(text);
|
|
itemData.Add(null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a new item to the popup list.
|
|
/// </summary>
|
|
|
|
public virtual void AddItem (string text, object data)
|
|
{
|
|
items.Add(text);
|
|
itemData.Add(data);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove the specified item.
|
|
/// </summary>
|
|
|
|
public virtual void RemoveItem (string text)
|
|
{
|
|
int index = items.IndexOf(text);
|
|
|
|
if (index != -1)
|
|
{
|
|
items.RemoveAt(index);
|
|
itemData.RemoveAt(index);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove the specified item.
|
|
/// </summary>
|
|
|
|
public virtual void RemoveItemByData (object data)
|
|
{
|
|
int index = itemData.IndexOf(data);
|
|
|
|
if (index != -1)
|
|
{
|
|
items.RemoveAt(index);
|
|
itemData.RemoveAt(index);
|
|
}
|
|
}
|
|
|
|
[System.NonSerialized] protected bool mExecuting = false;
|
|
|
|
/// <summary>
|
|
/// Trigger all event notification callbacks.
|
|
/// </summary>
|
|
|
|
protected void TriggerCallbacks ()
|
|
{
|
|
if (!mExecuting)
|
|
{
|
|
mExecuting = true;
|
|
UIPopupList old = current;
|
|
current = this;
|
|
|
|
// Legacy functionality
|
|
if (mLegacyEvent != null) mLegacyEvent(mSelectedItem);
|
|
|
|
if (EventDelegate.IsValid(onChange))
|
|
{
|
|
EventDelegate.Execute(onChange);
|
|
}
|
|
else if (eventReceiver != null && !string.IsNullOrEmpty(functionName))
|
|
{
|
|
// Legacy functionality support (for backwards compatibility)
|
|
eventReceiver.SendMessage(functionName, mSelectedItem, SendMessageOptions.DontRequireReceiver);
|
|
}
|
|
current = old;
|
|
mExecuting = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove legacy functionality.
|
|
/// </summary>
|
|
|
|
protected virtual void OnEnable ()
|
|
{
|
|
if (EventDelegate.IsValid(onChange))
|
|
{
|
|
eventReceiver = null;
|
|
functionName = null;
|
|
}
|
|
|
|
// 'font' is no longer used
|
|
if (font != null)
|
|
{
|
|
if (font.isDynamic)
|
|
{
|
|
trueTypeFont = font.dynamicFont;
|
|
fontStyle = font.dynamicFontStyle;
|
|
mUseDynamicFont = true;
|
|
}
|
|
else if (bitmapFont == null)
|
|
{
|
|
bitmapFont = font;
|
|
mUseDynamicFont = false;
|
|
}
|
|
font = null;
|
|
}
|
|
|
|
// 'textScale' is no longer used
|
|
if (textScale != 0f)
|
|
{
|
|
fontSize = (bitmapFont != null) ? Mathf.RoundToInt(bitmapFont.defaultSize * textScale) : 16;
|
|
textScale = 0f;
|
|
}
|
|
|
|
// Auto-upgrade to the true type font
|
|
if (trueTypeFont == null && bitmapFont != null && bitmapFont.isDynamic)
|
|
{
|
|
trueTypeFont = bitmapFont.dynamicFont;
|
|
bitmapFont = null;
|
|
}
|
|
}
|
|
|
|
protected bool mUseDynamicFont = false;
|
|
|
|
protected virtual void OnValidate ()
|
|
{
|
|
Font ttf = trueTypeFont;
|
|
UIFont fnt = bitmapFont;
|
|
|
|
bitmapFont = null;
|
|
trueTypeFont = null;
|
|
|
|
if (ttf != null && (fnt == null || !mUseDynamicFont))
|
|
{
|
|
bitmapFont = null;
|
|
trueTypeFont = ttf;
|
|
mUseDynamicFont = true;
|
|
}
|
|
else if (fnt != null)
|
|
{
|
|
// Auto-upgrade from 3.0.2 and earlier
|
|
if (fnt.isDynamic)
|
|
{
|
|
trueTypeFont = fnt.dynamicFont;
|
|
fontStyle = fnt.dynamicFontStyle;
|
|
fontSize = fnt.defaultSize;
|
|
mUseDynamicFont = true;
|
|
}
|
|
else
|
|
{
|
|
bitmapFont = fnt;
|
|
mUseDynamicFont = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trueTypeFont = ttf;
|
|
mUseDynamicFont = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send out the selection message on start.
|
|
/// </summary>
|
|
|
|
protected virtual void Start ()
|
|
{
|
|
// Auto-upgrade legacy functionality
|
|
if (textLabel != null)
|
|
{
|
|
EventDelegate.Add(onChange, textLabel.SetCurrentSelection);
|
|
textLabel = null;
|
|
#if UNITY_EDITOR
|
|
NGUITools.SetDirty(this);
|
|
#endif
|
|
}
|
|
|
|
// Automatically choose the first item
|
|
if (Application.isPlaying)
|
|
{
|
|
if (string.IsNullOrEmpty(mSelectedItem) && items.Count > 0)
|
|
mSelectedItem = items[0];
|
|
if (!string.IsNullOrEmpty(mSelectedItem))
|
|
TriggerCallbacks();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Localize the text label.
|
|
/// </summary>
|
|
|
|
protected virtual void OnLocalize () { if (isLocalized) TriggerCallbacks(); }
|
|
|
|
/// <summary>
|
|
/// Visibly highlight the specified transform by moving the highlight sprite to be over it.
|
|
/// </summary>
|
|
|
|
protected virtual void Highlight (UILabel lbl, bool instant)
|
|
{
|
|
if (mHighlight != null)
|
|
{
|
|
mHighlightedLabel = lbl;
|
|
|
|
UISpriteData sp = mHighlight.GetAtlasSprite();
|
|
if (sp == null) return;
|
|
|
|
Vector3 pos = GetHighlightPosition();
|
|
|
|
if (!instant && isAnimated)
|
|
{
|
|
TweenPosition.Begin(mHighlight.gameObject, 0.1f, pos).method = UITweener.Method.EaseOut;
|
|
|
|
if (!mTweening)
|
|
{
|
|
mTweening = true;
|
|
StartCoroutine("UpdateTweenPosition");
|
|
}
|
|
}
|
|
else mHighlight.cachedTransform.localPosition = pos;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function that calculates where the tweened position should be.
|
|
/// </summary>
|
|
|
|
protected virtual Vector3 GetHighlightPosition ()
|
|
{
|
|
if (mHighlightedLabel == null || mHighlight == null) return Vector3.zero;
|
|
UISpriteData sp = mHighlight.GetAtlasSprite();
|
|
if (sp == null) return Vector3.zero;
|
|
|
|
float scaleFactor = atlas.pixelSize;
|
|
float offsetX = sp.borderLeft * scaleFactor;
|
|
float offsetY = sp.borderTop * scaleFactor;
|
|
return mHighlightedLabel.cachedTransform.localPosition + new Vector3(-offsetX, offsetY, 1f);
|
|
}
|
|
|
|
protected bool mTweening = false;
|
|
|
|
/// <summary>
|
|
/// Periodically update the tweened target position.
|
|
/// It's needed because the popup list animates into view, and the target position changes.
|
|
/// </summary>
|
|
|
|
protected virtual IEnumerator UpdateTweenPosition ()
|
|
{
|
|
if (mHighlight != null && mHighlightedLabel != null)
|
|
{
|
|
TweenPosition tp = mHighlight.GetComponent<TweenPosition>();
|
|
|
|
while (tp != null && tp.enabled)
|
|
{
|
|
tp.to = GetHighlightPosition();
|
|
yield return null;
|
|
}
|
|
}
|
|
mTweening = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event function triggered when the mouse hovers over an item.
|
|
/// </summary>
|
|
|
|
protected virtual void OnItemHover (GameObject go, bool isOver)
|
|
{
|
|
if (isOver)
|
|
{
|
|
UILabel lbl = go.GetComponent<UILabel>();
|
|
Highlight(lbl, false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event function triggered when the drop-down list item gets clicked on.
|
|
/// </summary>
|
|
|
|
protected virtual void OnItemPress (GameObject go, bool isPressed)
|
|
{
|
|
if (isPressed)
|
|
{
|
|
Select(go.GetComponent<UILabel>(), true);
|
|
|
|
UIEventListener listener = go.GetComponent<UIEventListener>();
|
|
value = listener.parameter as string;
|
|
UIPlaySound[] sounds = GetComponents<UIPlaySound>();
|
|
|
|
for (int i = 0, imax = sounds.Length; i < imax; ++i)
|
|
{
|
|
UIPlaySound snd = sounds[i];
|
|
if (snd.trigger == UIPlaySound.Trigger.OnClick)
|
|
NGUITools.PlaySound(snd.audioClip, snd.volume, 1f);
|
|
}
|
|
CloseSelf();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Select the specified label.
|
|
/// </summary>
|
|
|
|
void Select (UILabel lbl, bool instant) { Highlight(lbl, instant); }
|
|
|
|
/// <summary>
|
|
/// React to key-based input.
|
|
/// </summary>
|
|
|
|
protected virtual void OnNavigate (KeyCode key)
|
|
{
|
|
if (enabled && current == this)
|
|
{
|
|
int index = mLabelList.IndexOf(mHighlightedLabel);
|
|
if (index == -1) index = 0;
|
|
|
|
if (key == KeyCode.UpArrow)
|
|
{
|
|
if (index > 0)
|
|
{
|
|
Select(mLabelList[--index], false);
|
|
}
|
|
}
|
|
else if (key == KeyCode.DownArrow)
|
|
{
|
|
if (index + 1 < mLabelList.Count)
|
|
{
|
|
Select(mLabelList[++index], false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// React to key-based input.
|
|
/// </summary>
|
|
|
|
protected virtual void OnKey (KeyCode key)
|
|
{
|
|
if (enabled && current == this)
|
|
{
|
|
if (key == UICamera.current.cancelKey0 || key == UICamera.current.cancelKey1)
|
|
OnSelect(false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close the popup list when disabled.
|
|
/// </summary>
|
|
|
|
protected virtual void OnDisable () { CloseSelf(); }
|
|
|
|
/// <summary>
|
|
/// Get rid of the popup dialog when the selection gets lost.
|
|
/// </summary>
|
|
|
|
protected virtual void OnSelect (bool isSelected) { if (!isSelected) CloseSelf(); }
|
|
|
|
/// <summary>
|
|
/// Manually close the popup list.
|
|
/// </summary>
|
|
|
|
static public void Close ()
|
|
{
|
|
if (current != null)
|
|
{
|
|
current.CloseSelf();
|
|
current = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Manually close the popup list.
|
|
/// </summary>
|
|
|
|
public virtual void CloseSelf ()
|
|
{
|
|
if (mChild != null && current == this)
|
|
{
|
|
StopCoroutine("CloseIfUnselected");
|
|
mSelection = null;
|
|
|
|
mLabelList.Clear();
|
|
|
|
if (isAnimated)
|
|
{
|
|
UIWidget[] widgets = mChild.GetComponentsInChildren<UIWidget>();
|
|
|
|
for (int i = 0, imax = widgets.Length; i < imax; ++i)
|
|
{
|
|
UIWidget w = widgets[i];
|
|
Color c = w.color;
|
|
c.a = 0f;
|
|
TweenColor.Begin(w.gameObject, animSpeed, c).method = UITweener.Method.EaseOut;
|
|
}
|
|
|
|
Collider[] cols = mChild.GetComponentsInChildren<Collider>();
|
|
for (int i = 0, imax = cols.Length; i < imax; ++i) cols[i].enabled = false;
|
|
Destroy(mChild, animSpeed);
|
|
|
|
mFadeOutComplete = Time.unscaledTime + Mathf.Max(0.1f, animSpeed);
|
|
}
|
|
else
|
|
{
|
|
Destroy(mChild);
|
|
mFadeOutComplete = Time.unscaledTime + 0.1f;
|
|
}
|
|
|
|
mBackground = null;
|
|
mHighlight = null;
|
|
mChild = null;
|
|
current = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function that causes the widget to smoothly fade in.
|
|
/// </summary>
|
|
|
|
protected virtual void AnimateColor (UIWidget widget)
|
|
{
|
|
Color c = widget.color;
|
|
widget.color = new Color(c.r, c.g, c.b, 0f);
|
|
TweenColor.Begin(widget.gameObject, animSpeed, c).method = UITweener.Method.EaseOut;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function that causes the widget to smoothly move into position.
|
|
/// </summary>
|
|
|
|
protected virtual void AnimatePosition (UIWidget widget, bool placeAbove, float bottom)
|
|
{
|
|
Vector3 target = widget.cachedTransform.localPosition;
|
|
Vector3 start = placeAbove ? new Vector3(target.x, bottom, target.z) : new Vector3(target.x, 0f, target.z);
|
|
|
|
widget.cachedTransform.localPosition = start;
|
|
|
|
GameObject go = widget.gameObject;
|
|
TweenPosition.Begin(go, animSpeed, target).method = UITweener.Method.EaseOut;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function that causes the widget to smoothly grow until it reaches its original size.
|
|
/// </summary>
|
|
|
|
protected virtual void AnimateScale (UIWidget widget, bool placeAbove, float bottom)
|
|
{
|
|
GameObject go = widget.gameObject;
|
|
Transform t = widget.cachedTransform;
|
|
|
|
float minHeight = activeFontSize * activeFontScale + mBgBorder * 2f;
|
|
t.localScale = new Vector3(1f, minHeight / widget.height, 1f);
|
|
TweenScale.Begin(go, animSpeed, Vector3.one).method = UITweener.Method.EaseOut;
|
|
|
|
if (placeAbove)
|
|
{
|
|
Vector3 pos = t.localPosition;
|
|
t.localPosition = new Vector3(pos.x, pos.y - widget.height + minHeight, pos.z);
|
|
TweenPosition.Begin(go, animSpeed, pos).method = UITweener.Method.EaseOut;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function used to animate widgets.
|
|
/// </summary>
|
|
|
|
void Animate (UIWidget widget, bool placeAbove, float bottom)
|
|
{
|
|
AnimateColor(widget);
|
|
AnimatePosition(widget, placeAbove, bottom);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display the drop-down list when the game object gets clicked on.
|
|
/// </summary>
|
|
|
|
protected virtual void OnClick ()
|
|
{
|
|
if (mOpenFrame == Time.frameCount) return;
|
|
|
|
if (mChild == null)
|
|
{
|
|
if (openOn == OpenOn.DoubleClick || openOn == OpenOn.Manual) return;
|
|
if (openOn == OpenOn.RightClick && UICamera.currentTouchID != -2) return;
|
|
Show();
|
|
}
|
|
else if (mHighlightedLabel != null)
|
|
{
|
|
OnItemPress(mHighlightedLabel.gameObject, true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the popup list on double-click.
|
|
/// </summary>
|
|
|
|
protected virtual void OnDoubleClick () { if (openOn == OpenOn.DoubleClick) Show(); }
|
|
|
|
/// <summary>
|
|
/// Used to keep an eye on the selected object, closing the popup if it changes.
|
|
/// </summary>
|
|
|
|
IEnumerator CloseIfUnselected ()
|
|
{
|
|
for (; ; )
|
|
{
|
|
yield return null;
|
|
|
|
if (UICamera.selectedObject != mSelection)
|
|
{
|
|
CloseSelf();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public GameObject source;
|
|
|
|
/// <summary>
|
|
/// Show the popup list dialog.
|
|
/// </summary>
|
|
|
|
public virtual void Show ()
|
|
{
|
|
if (enabled && NGUITools.GetActive(gameObject) && mChild == null && atlas != null && isValid && items.Count > 0)
|
|
{
|
|
mLabelList.Clear();
|
|
StopCoroutine("CloseIfUnselected");
|
|
|
|
// Ensure the popup's source has the selection
|
|
UICamera.selectedObject = (UICamera.hoveredObject ?? gameObject);
|
|
mSelection = UICamera.selectedObject;
|
|
source = UICamera.selectedObject;
|
|
|
|
if (source == null)
|
|
{
|
|
Debug.LogError("Popup list needs a source object...");
|
|
return;
|
|
}
|
|
|
|
mOpenFrame = Time.frameCount;
|
|
|
|
// Automatically locate the panel responsible for this object
|
|
if (mPanel == null)
|
|
{
|
|
mPanel = UIPanel.Find(transform);
|
|
if (mPanel == null) return;
|
|
}
|
|
|
|
// Calculate the dimensions of the object triggering the popup list so we can position it below it
|
|
Vector3 min;
|
|
Vector3 max;
|
|
|
|
// Create the root object for the list
|
|
mChild = new GameObject("Drop-down List");
|
|
mChild.layer = gameObject.layer;
|
|
|
|
if (separatePanel)
|
|
{
|
|
if (GetComponent<Collider>() != null)
|
|
{
|
|
Rigidbody rb = mChild.AddComponent<Rigidbody>();
|
|
rb.isKinematic = true;
|
|
}
|
|
else if (GetComponent<Collider2D>() != null)
|
|
{
|
|
Rigidbody2D rb = mChild.AddComponent<Rigidbody2D>();
|
|
rb.isKinematic = true;
|
|
}
|
|
mChild.AddComponent<UIPanel>().depth = 10000;
|
|
}
|
|
current = this;
|
|
|
|
Transform t = mChild.transform;
|
|
t.parent = mPanel.cachedTransform;
|
|
Vector3 pos;
|
|
|
|
// Manually triggered popup list on some other game object
|
|
if (openOn == OpenOn.Manual && mSelection != gameObject)
|
|
{
|
|
pos = UICamera.lastEventPosition;
|
|
min = mPanel.cachedTransform.InverseTransformPoint(mPanel.anchorCamera.ScreenToWorldPoint(pos));
|
|
max = min;
|
|
t.localPosition = min;
|
|
pos = t.position;
|
|
}
|
|
else
|
|
{
|
|
Bounds bounds = NGUIMath.CalculateRelativeWidgetBounds(mPanel.cachedTransform, transform, false, false);
|
|
min = bounds.min;
|
|
max = bounds.max;
|
|
t.localPosition = min;
|
|
pos = t.position;
|
|
}
|
|
|
|
StartCoroutine("CloseIfUnselected");
|
|
|
|
t.localRotation = Quaternion.identity;
|
|
t.localScale = Vector3.one;
|
|
|
|
// Add a sprite for the background
|
|
mBackground = NGUITools.AddSprite(mChild, atlas, backgroundSprite, separatePanel ? 0 : NGUITools.CalculateNextDepth(mPanel.gameObject));
|
|
mBackground.pivot = UIWidget.Pivot.TopLeft;
|
|
mBackground.color = backgroundColor;
|
|
|
|
// We need to know the size of the background sprite for padding purposes
|
|
Vector4 bgPadding = mBackground.border;
|
|
mBgBorder = bgPadding.y;
|
|
mBackground.cachedTransform.localPosition = new Vector3(0f, bgPadding.y, 0f);
|
|
|
|
// Add a sprite used for the selection
|
|
mHighlight = NGUITools.AddSprite(mChild, atlas, highlightSprite, mBackground.depth + 1);
|
|
mHighlight.pivot = UIWidget.Pivot.TopLeft;
|
|
mHighlight.color = highlightColor;
|
|
|
|
UISpriteData hlsp = mHighlight.GetAtlasSprite();
|
|
if (hlsp == null) return;
|
|
|
|
float hlspHeight = hlsp.borderTop;
|
|
float fontHeight = activeFontSize;
|
|
float dynScale = activeFontScale;
|
|
float labelHeight = fontHeight * dynScale;
|
|
float x = 0f, y = -padding.y;
|
|
List<UILabel> labels = new List<UILabel>();
|
|
|
|
// Clear the selection if it's no longer present
|
|
if (!items.Contains(mSelectedItem))
|
|
mSelectedItem = null;
|
|
|
|
// Run through all items and create labels for each one
|
|
for (int i = 0, imax = items.Count; i < imax; ++i)
|
|
{
|
|
string s = items[i];
|
|
|
|
UILabel lbl = NGUITools.AddWidget<UILabel>(mChild, mBackground.depth + 2);
|
|
lbl.name = i.ToString();
|
|
lbl.pivot = UIWidget.Pivot.TopLeft;
|
|
lbl.bitmapFont = bitmapFont;
|
|
lbl.trueTypeFont = trueTypeFont;
|
|
lbl.fontSize = fontSize;
|
|
lbl.fontStyle = fontStyle;
|
|
lbl.text = isLocalized ? Localization.Get(s) : s;
|
|
lbl.color = textColor;
|
|
lbl.cachedTransform.localPosition = new Vector3(bgPadding.x + padding.x - lbl.pivotOffset.x, y, -1f);
|
|
lbl.overflowMethod = UILabel.Overflow.ResizeFreely;
|
|
lbl.alignment = alignment;
|
|
labels.Add(lbl);
|
|
|
|
y -= labelHeight;
|
|
y -= padding.y;
|
|
x = Mathf.Max(x, lbl.printedSize.x);
|
|
|
|
// Add an event listener
|
|
UIEventListener listener = UIEventListener.Get(lbl.gameObject);
|
|
listener.onHover = OnItemHover;
|
|
listener.onPress = OnItemPress;
|
|
listener.parameter = s;
|
|
|
|
// Move the selection here if this is the right label
|
|
if (mSelectedItem == s || (i == 0 && string.IsNullOrEmpty(mSelectedItem)))
|
|
Highlight(lbl, true);
|
|
|
|
// Add this label to the list
|
|
mLabelList.Add(lbl);
|
|
}
|
|
|
|
// The triggering widget's width should be the minimum allowed width
|
|
x = Mathf.Max(x, (max.x - min.x) - (bgPadding.x + padding.x) * 2f);
|
|
|
|
float cx = x;
|
|
Vector3 bcCenter = new Vector3(cx * 0.5f, -labelHeight * 0.5f, 0f);
|
|
Vector3 bcSize = new Vector3(cx, (labelHeight + padding.y), 1f);
|
|
|
|
// Run through all labels and add colliders
|
|
for (int i = 0, imax = labels.Count; i < imax; ++i)
|
|
{
|
|
UILabel lbl = labels[i];
|
|
NGUITools.AddWidgetCollider(lbl.gameObject);
|
|
lbl.autoResizeBoxCollider = false;
|
|
BoxCollider bc = lbl.GetComponent<BoxCollider>();
|
|
|
|
if (bc != null)
|
|
{
|
|
bcCenter.z = bc.center.z;
|
|
bc.center = bcCenter;
|
|
bc.size = bcSize;
|
|
}
|
|
else
|
|
{
|
|
BoxCollider2D b2d = lbl.GetComponent<BoxCollider2D>();
|
|
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
|
|
b2d.center = bcCenter;
|
|
#else
|
|
b2d.offset = bcCenter;
|
|
#endif
|
|
b2d.size = bcSize;
|
|
}
|
|
}
|
|
|
|
int lblWidth = Mathf.RoundToInt(x);
|
|
x += (bgPadding.x + padding.x) * 2f;
|
|
y -= bgPadding.y;
|
|
|
|
// Scale the background sprite to envelop the entire set of items
|
|
mBackground.width = Mathf.RoundToInt(x);
|
|
mBackground.height = Mathf.RoundToInt(-y + bgPadding.y);
|
|
|
|
// Set the label width to make alignment work
|
|
for (int i = 0, imax = labels.Count; i < imax; ++i)
|
|
{
|
|
UILabel lbl = labels[i];
|
|
lbl.overflowMethod = UILabel.Overflow.ShrinkContent;
|
|
lbl.width = lblWidth;
|
|
}
|
|
|
|
// Scale the highlight sprite to envelop a single item
|
|
float scaleFactor = 2f * atlas.pixelSize;
|
|
float w = x - (bgPadding.x + padding.x) * 2f + hlsp.borderLeft * scaleFactor;
|
|
float h = labelHeight + hlspHeight * scaleFactor;
|
|
mHighlight.width = Mathf.RoundToInt(w);
|
|
mHighlight.height = Mathf.RoundToInt(h);
|
|
|
|
bool placeAbove = (position == Position.Above);
|
|
|
|
if (position == Position.Auto)
|
|
{
|
|
UICamera cam = UICamera.FindCameraForLayer(mSelection.layer);
|
|
|
|
if (cam != null)
|
|
{
|
|
Vector3 viewPos = cam.cachedCamera.WorldToViewportPoint(pos);
|
|
placeAbove = (viewPos.y < 0.5f);
|
|
}
|
|
}
|
|
|
|
// If the list should be animated, let's animate it by expanding it
|
|
if (isAnimated)
|
|
{
|
|
AnimateColor(mBackground);
|
|
|
|
if (Time.timeScale == 0f || Time.timeScale >= 0.1f)
|
|
{
|
|
float bottom = y + labelHeight;
|
|
Animate(mHighlight, placeAbove, bottom);
|
|
for (int i = 0, imax = labels.Count; i < imax; ++i)
|
|
Animate(labels[i], placeAbove, bottom);
|
|
AnimateScale(mBackground, placeAbove, bottom);
|
|
}
|
|
}
|
|
|
|
// If we need to place the popup list above the item, we need to reposition everything by the size of the list
|
|
if (placeAbove)
|
|
{
|
|
min.y = max.y - bgPadding.y;
|
|
max.y = min.y + mBackground.height;
|
|
max.x = min.x + mBackground.width;
|
|
t.localPosition = new Vector3(min.x, max.y - bgPadding.y, min.z);
|
|
}
|
|
else
|
|
{
|
|
max.y = min.y + bgPadding.y;
|
|
min.y = max.y - mBackground.height;
|
|
max.x = min.x + mBackground.width;
|
|
}
|
|
|
|
Transform pt = mPanel.cachedTransform.parent;
|
|
|
|
if (pt != null)
|
|
{
|
|
min = mPanel.cachedTransform.TransformPoint(min);
|
|
max = mPanel.cachedTransform.TransformPoint(max);
|
|
min = pt.InverseTransformPoint(min);
|
|
max = pt.InverseTransformPoint(max);
|
|
}
|
|
|
|
// Ensure that everything fits into the panel's visible range
|
|
Vector3 offset = mPanel.hasClipping ? Vector3.zero : mPanel.CalculateConstrainOffset(min, max);
|
|
pos = t.localPosition + offset;
|
|
pos.x = Mathf.Round(pos.x);
|
|
pos.y = Mathf.Round(pos.y);
|
|
t.localPosition = pos;
|
|
}
|
|
else OnSelect(false);
|
|
}
|
|
}
|