Icon Slot System
UISlotBase.cs
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using System;
using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.UI
{
[AddComponentMenu("UI/Icon Slots/Base Slot"), ExecuteInEditMode, DisallowMultipleComponent]
public clast UISlotBase : UIBehaviour, IEventSystemHandler, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, IDropHandler {
public enum Transition
{
None,
ColorTint,
SpriteSwap,
Animation
}
public enum DragKeyModifier
{
None,
Control,
Alt,
Shift
}
///
/// The current dragged object.
///
protected GameObject m_CurrentDraggedObject;
///
/// The current dragging plane.
///
protected RectTransform m_CurrentDraggingPlane;
///
/// The target icon graphic.
///
public Graphic iconGraphic;
[SerializeField, Tooltip("Should the drag and drop functionallty be enabled.")]
private bool m_DragAndDropEnabled = true;
[SerializeField, Tooltip("If set to static the slot won't be unastigned when drag and drop is preformed.")]
private bool m_IsStatic = false;
[SerializeField, Tooltip("Should the icon astigned to the slot be throwable.")]
private bool m_AllowThrowAway = true;
[SerializeField, Tooltip("The key which should be held down in order to begin the drag.")]
private DragKeyModifier m_DragKeyModifier = DragKeyModifier.None;
[SerializeField, Tooltip("Should the tooltip functionallty be enabled.")]
private bool m_TooltipEnabled = true;
[SerializeField, Tooltip("How long of a delay to expect before showing the tooltip.")]
private float m_TooltipDelay = 1f;
public Transition hoverTransition = Transition.None;
public Graphic hoverTargetGraphic;
public Color hoverNormalColor = Color.white;
public Color hoverHighlightColor = Color.white;
public float hoverTransitionDuration = 0.15f;
public Sprite hoverOverrideSprite;
public string hoverNormalTrigger = "Normal";
public string hoverHighlightTrigger = "Highlighted";
public Transition pressTransition = Transition.None;
public Graphic pressTargetGraphic;
public Color pressNormalColor = Color.white;
public Color pressPressColor = Color.white;
public float pressTransitionDuration = 0.15f;
public Sprite pressOverrideSprite;
public string pressNormalTrigger = "Normal";
public string pressPressTrigger = "Pressed";
[SerializeField, Tooltip("Should the pressed state transition to normal state instantly.")]
private bool m_PressTransitionInstaOut = true;
[SerializeField, Tooltip("Should the pressed state force normal state transition on the hover target.")]
private bool m_PressForceHoverNormal = true;
private bool isPointerDown = false;
private bool isPointerInside = false;
private bool m_DragHasBegan = false;
private bool m_DropPreformed = false;
private bool m_IsTooltipShown = false;
///
/// Gets or sets a value indicating whether this drag and drop is enabled.
///
/// true if drag and drop enabled; otherwise, false.
public bool dragAndDropEnabled
{
get
{
return this.m_DragAndDropEnabled;
}
set
{
this.m_DragAndDropEnabled = value;
}
}
///
/// Gets or sets a value indicating whether this is static.
///
/// true if is static; otherwise, false.
public bool isStatic
{
get
{
return this.m_IsStatic;
}
set
{
this.m_IsStatic = value;
}
}
///
/// Gets or sets a value indicating whether this can be throw away.
///
/// true if allow throw away; otherwise, false.
public bool allowThrowAway
{
get
{
return this.m_AllowThrowAway;
}
set
{
this.m_AllowThrowAway = value;
}
}
///
/// Gets or sets the drag key modifier.
///
/// The drag key modifier.
public DragKeyModifier dragKeyModifier
{
get
{
return this.m_DragKeyModifier;
}
set
{
this.m_DragKeyModifier = value;
}
}
///
/// Gets or sets a value indicating whether this tooltip should be enabled.
///
/// true if tooltip enabled; otherwise, false.
public bool tooltipEnabled
{
get
{
return this.m_TooltipEnabled;
}
set
{
this.m_TooltipEnabled = value;
}
}
///
/// Gets or sets the tooltip delay.
///
/// The tooltip delay.
public float tooltipDelay
{
get
{
return this.m_TooltipDelay;
}
set
{
this.m_TooltipDelay = value;
}
}
///
/// Gets or sets a value indicating whether this pressed state should transition out instantly.
///
/// true if press transition insta out; otherwise, false.
public bool pressTransitionInstaOut
{
get
{
return this.m_PressTransitionInstaOut;
}
set
{
this.m_PressTransitionInstaOut = value;
}
}
///
/// Gets or sets a value indicating whether this pressed state should force normal state transition on the hover target.
///
/// true if press force hover normal; otherwise, false.
public bool pressForceHoverNormal
{
get
{
return this.m_PressForceHoverNormal;
}
set
{
this.m_PressForceHoverNormal = value;
}
}
///
/// Gets or sets a value indicating whether this drop was preformed.
///
/// true if drop preformed; otherwise, false.
public bool dropPreformed
{
get
{
return this.m_DropPreformed;
}
set
{
this.m_DropPreformed = value;
}
}
protected override void Start()
{
// Check if the slot is not astigned but the icon graphic is active
if (!this.Isastigned() && this.iconGraphic != null && this.iconGraphic.gameObject.activeSelf)
{
// Disable the icon graphic object
this.iconGraphic.gameObject.SetActive(false);
}
}
protected override void OnEnable()
{
base.OnEnable();
// Instant transition
this.EvaluateAndTransitionHoveredState(true);
this.EvaluateAndTransitionPressedState(true);
}
protected override void OnDisable()
{
base.OnDisable();
this.isPointerInside = false;
this.isPointerDown = false;
// Instant transition
this.EvaluateAndTransitionHoveredState(true);
this.EvaluateAndTransitionPressedState(true);
}
#if UNITY_EDITOR
protected override void OnValidate()
{
this.hoverTransitionDuration = Mathf.Max(this.hoverTransitionDuration, 0f);
this.pressTransitionDuration = Mathf.Max(this.pressTransitionDuration, 0f);
if (this.isActiveAndEnabled)
{
this.DoSpriteSwap(this.hoverTargetGraphic, null);
this.DoSpriteSwap(this.pressTargetGraphic, null);
if (!EditorApplication.isPlayingOrWillChangePlaymode)
{
// Instant transition
this.EvaluateAndTransitionHoveredState(true);
this.EvaluateAndTransitionPressedState(true);
}
else
{
// Regular transition
this.EvaluateAndTransitionHoveredState(false);
this.EvaluateAndTransitionPressedState(false);
}
}
}
#endif
///
/// Raises the pointer enter event.
///
/// Event data.
public virtual void OnPointerEnter(PointerEventData eventData)
{
this.isPointerInside = true;
this.EvaluateAndTransitionHoveredState(false);
// Check if tooltip is enabled
if (this.enabled && this.IsActive() && this.m_TooltipEnabled)
{
// Start the tooltip delayed show coroutine
// If delay is set at all
if (this.m_TooltipDelay > 0f)
{
this.StartCoroutine("TooltipDelayedShow");
}
else
{
this.InternalShowTooltip();
}
}
}
///
/// Raises the pointer exit event.
///
/// Event data.
public virtual void OnPointerExit(PointerEventData eventData)
{
this.isPointerInside = false;
this.EvaluateAndTransitionHoveredState(false);
this.InternalHideTooltip();
}
///
/// Raises the tooltip event.
///
/// If set to true show.
public virtual void OnTooltip(bool show)
{
}
///
/// Raises the pointer down event.
///
/// Event data.
public virtual void OnPointerDown(PointerEventData eventData)
{
this.isPointerDown = true;
this.EvaluateAndTransitionPressedState(false);
// Hide the tooltip
this.InternalHideTooltip();
}
///
/// Raises the pointer up event.
///
/// Event data.
public virtual void OnPointerUp(PointerEventData eventData)
{
this.isPointerDown = false;
this.EvaluateAndTransitionPressedState(this.m_PressTransitionInstaOut);
}
///
/// Raises the pointer click event.
///
/// Event data.
public virtual void OnPointerClick(PointerEventData eventData) { }
///
/// Determines whether this slot is highlighted based on the specified eventData.
///
/// true if this instance is highlighted the specified eventData; otherwise, false.
/// Event data.
protected bool IsHighlighted(BaseEventData eventData)
{
if (!this.IsActive())
return false;
if (eventData is PointerEventData)
{
PointerEventData pointerEventData = eventData as PointerEventData;
return ((this.isPointerDown && !this.isPointerInside && pointerEventData.pointerPress == base.gameObject) || (!this.isPointerDown && this.isPointerInside && pointerEventData.pointerPress == base.gameObject) || (!this.isPointerDown && this.isPointerInside && pointerEventData.pointerPress == null));
}
return false;
}
///
/// Determines whether this slot is pressed based on the specified eventData.
///
/// true if this instance is pressed the specified eventData; otherwise, false.
/// Event data.
protected bool IsPressed(BaseEventData eventData)
{
return this.IsActive() && this.isPointerInside && this.isPointerDown;
}
///
/// Evaluates and transitions the hovered state.
///
/// If set to true instant.
protected virtual void EvaluateAndTransitionHoveredState(bool instant)
{
if (!this.IsActive() || this.hoverTargetGraphic == null || !this.hoverTargetGraphic.gameObject.activeInHierarchy)
return;
// Determine what should the state of the hover target be
bool highlighted = (this.m_PressForceHoverNormal ? (this.isPointerInside && !this.isPointerDown) : this.isPointerInside);
// Do the transition
switch (this.hoverTransition)
{
case Transition.ColorTint:
{
this.StartColorTween(this.hoverTargetGraphic, (highlighted ? this.hoverHighlightColor : this.hoverNormalColor), (instant ? 0f : this.hoverTransitionDuration));
break;
}
case Transition.SpriteSwap:
{
this.DoSpriteSwap(this.hoverTargetGraphic, (highlighted ? this.hoverOverrideSprite : null));
break;
}
case Transition.Animation:
{
this.TriggerHoverStateAnimation(highlighted ? this.hoverHighlightTrigger : this.hoverNormalTrigger);
break;
}
}
}
///
/// Evaluates and transitions the pressed state.
///
/// If set to true instant.
protected virtual void EvaluateAndTransitionPressedState(bool instant)
{
if (!this.IsActive() || this.pressTargetGraphic == null || !this.pressTargetGraphic.gameObject.activeInHierarchy)
return;
// Do the transition
switch (this.pressTransition)
{
case Transition.ColorTint:
{
this.StartColorTween(this.pressTargetGraphic, (this.isPointerDown ? this.pressPressColor : this.pressNormalColor), (instant ? 0f : this.pressTransitionDuration));
break;
}
case Transition.SpriteSwap:
{
this.DoSpriteSwap(this.pressTargetGraphic, (this.isPointerDown ? this.pressOverrideSprite : null));
break;
}
case Transition.Animation:
{
this.TriggerPressStateAnimation(this.isPointerDown ? this.pressPressTrigger : this.pressNormalTrigger);
break;
}
}
// If we should force normal state transition on the hover target
if (this.m_PressForceHoverNormal)
this.EvaluateAndTransitionHoveredState(false);
}
///
/// Starts a color tween.
///
/// Target.
/// Target color.
/// Duration.
protected virtual void StartColorTween(Graphic target, Color targetColor, float duration)
{
if (target == null)
return;
target.CrossFadeColor(targetColor, duration, true, true);
}
///
/// Does a sprite swap.
///
/// Target.
/// New sprite.
protected virtual void DoSpriteSwap(Graphic target, Sprite newSprite)
{
if (target == null)
return;
Image image = target as Image;
if (image == null)
return;
image.overrideSprite = newSprite;
}
///
/// Triggers the hover state animation.
///
/// Triggername.
private void TriggerHoverStateAnimation(string triggername)
{
if (this.hoverTargetGraphic == null)
return;
// Get the animator on the target game object
Animator animator = this.hoverTargetGraphic.gameObject.GetComponent();
if (animator == null || !animator.isActiveAndEnabled || animator.runtimeAnimatorController == null || string.IsNullOrEmpty(triggername))
return;
animator.ResetTrigger(this.hoverNormalTrigger);
animator.ResetTrigger(this.hoverHighlightTrigger);
animator.SetTrigger(triggername);
}
///
/// Triggers the pressed state animation.
///
/// Triggername.
private void TriggerPressStateAnimation(string triggername)
{
if (this.pressTargetGraphic == null)
return;
// Get the animator on the target game object
Animator animator = this.pressTargetGraphic.gameObject.GetComponent();
if (animator == null || !animator.isActiveAndEnabled || animator.runtimeAnimatorController == null || string.IsNullOrEmpty(triggername))
return;
animator.ResetTrigger(this.pressNormalTrigger);
animator.ResetTrigger(this.pressPressTrigger);
animator.SetTrigger(triggername);
}
///
/// Determines whether this slot is astigned.
///
/// true if this instance is astigned; otherwise, false.
public virtual bool Isastigned()
{
return (this.GetIconSprite() != null || this.GetIconTexture() != null);
}
///
/// astign the specified slot by icon sprite.
///
/// Icon.
public bool astign(Sprite icon)
{
if (icon == null)
return false;
// Set the icon
this.SetIcon(icon);
return true;
}
///
/// astign the specified slot by icon texture.
///
/// Icon.
public bool astign(Texture icon)
{
if (icon == null)
return false;
// Set the icon
this.SetIcon(icon);
return true;
}
///
/// astign the specified slot by object.
///
/// Source.
public virtual bool astign(Object source)
{
if (source is UISlotBase)
{
UISlotBase sourceSlot = source as UISlotBase;
if (sourceSlot != null)
{
// astign by sprite or texture
if (sourceSlot.GetIconSprite() != null)
{
return this.astign(sourceSlot.GetIconSprite());
}
else if (sourceSlot.GetIconTexture() != null)
{
return this.astign(sourceSlot.GetIconTexture());
}
}
}
return false;
}
///
/// Unastign this slot.
///
public virtual void Unastign()
{
// Remove the icon
this.ClearIcon();
}
///
/// Gets the icon sprite of this slot if it's set and the icon graphic is .
///
/// The icon.
public Sprite GetIconSprite()
{
// Check if the icon graphic valid image
if (this.iconGraphic == null || !(this.iconGraphic is Image))
return null;
return (this.iconGraphic as Image).sprite;
}
///
/// Gets the icon texture of this slot if it's set and the icon graphic is .
///
/// The icon.
public Texture GetIconTexture()
{
// Check if the icon graphic valid image
if (this.iconGraphic == null || !(this.iconGraphic is RawImage))
return null;
return (this.iconGraphic as RawImage).texture;
}
///
/// Gets the icon as object.
///
/// The icon as object.
public Object GetIconAsObject()
{
if (this.iconGraphic == null)
return null;
if (this.iconGraphic is Image)
{
return this.GetIconSprite();
}
else if (this.iconGraphic is RawImage)
{
return this.GetIconTexture();
}
// Default
return null;
}
///
/// Sets the icon of this slot.
///
/// The icon sprite.
public void SetIcon(Sprite iconSprite)
{
// Check if the icon graphic valid image
if (this.iconGraphic == null || !(this.iconGraphic is Image))
return;
// Set the sprite
(this.iconGraphic as Image).sprite = iconSprite;
// Enable or disabled the icon graphic game object
if (iconSprite != null && !this.iconGraphic.gameObject.activeSelf) this.iconGraphic.gameObject.SetActive(true);
if (iconSprite == null && this.iconGraphic.gameObject.activeSelf) this.iconGraphic.gameObject.SetActive(false);
}
///
/// Sets the icon of this slot.
///
/// The icon texture.
public void SetIcon(Texture iconTex)
{
// Check if the icon graphic valid raw image
if (this.iconGraphic == null || !(this.iconGraphic is RawImage))
return;
// Set the sprite
(this.iconGraphic as RawImage).texture = iconTex;
// Enable or disabled the icon graphic game object
if (iconTex != null && !this.iconGraphic.gameObject.activeSelf) this.iconGraphic.gameObject.SetActive(true);
if (iconTex == null && this.iconGraphic.gameObject.activeSelf) this.iconGraphic.gameObject.SetActive(false);
}
///
/// Clears the icon of this slot.
///
public void ClearIcon()
{
// Check if the icon graphic valid
if (this.iconGraphic == null)
return;
// In case of image
if (this.iconGraphic is Image)
(this.iconGraphic as Image).sprite = null;
// In case of raw image
if (this.iconGraphic is RawImage)
(this.iconGraphic as RawImage).texture = null;
// Disable the game object
this.iconGraphic.gameObject.SetActive(false);
}
///
/// Raises the begin drag event.
///
/// Event data.
public virtual void OnBeginDrag(PointerEventData eventData)
{
if (!this.enabled || !this.Isastigned() || !this.m_DragAndDropEnabled)
{
eventData.Reset();
return;
}
// Check if we have a key modifier and if it's held down
if (!this.DragKeyModifierIsDown())
{
eventData.Reset();
return;
}
// Start the drag
this.m_DragHasBegan = true;
// Create the temporary icon for dragging
this.CreateTemporaryIcon(eventData);
// Prevent event propagation
eventData.Use();
}
///
/// Is the drag key modifier down.
///
/// true, if key modifier is down, false otherwise.
public virtual bool DragKeyModifierIsDown()
{
switch (this.m_DragKeyModifier)
{
case DragKeyModifier.Control:
return (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl));
case DragKeyModifier.Alt:
return (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt));
case DragKeyModifier.Shift:
return (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift));
}
// Default should be true
return true;
}
///
/// Raises the drag event.
///
/// Event data.
public virtual void OnDrag(PointerEventData eventData)
{
// Check if the dragging has been started
if (this.m_DragHasBegan)
{
// Update the dragged object's position
if (this.m_CurrentDraggedObject != null)
this.UpdateDraggedPosition(eventData);
// Use the event
eventData.Use();
}
}
///
/// Raises the drop event.
///
/// Event data.
public virtual void OnDrop(PointerEventData eventData)
{
// Get the source slot
UISlotBase source = (eventData.pointerPress != null) ? eventData.pointerPress.GetComponent() : null;
// Make sure we have the source slot
if (source == null || !source.Isastigned())
return;
// Notify the source that a drop was performed so it does not unastign
source.dropPreformed = true;
// Check if this slot is enabled and it's drag and drop feature is enabled
if (!this.enabled || !this.m_DragAndDropEnabled)
return;
// Prepare a variable indicating whether the astign process was successful
bool astignSuccess = false;
// Normal empty slot astignment
if (!this.Isastigned())
{
// astign the target slot with the info from the source
astignSuccess = this.astign(source);
// Unastign the source on successful astignment and the source is not static
if (astignSuccess && !source.isStatic)
source.Unastign();
}
// The target slot is astigned
else
{
// If the target slot is not static
// and we have a source slot that is not static
if (!this.isStatic && !source.isStatic)
{
// Check if we can swap
if (this.CanSwapWith(source) && source.CanSwapWith(this))
{
// Swap the slots
astignSuccess = source.PerformSlotSwap(this);
}
}
// If the target slot is not static
// and the source slot is a static one
else if (!this.isStatic && source.isStatic)
{
astignSuccess = this.astign(source);
}
}
// If this slot failed to be astigned
if (!astignSuccess)
{
this.OnastignBySlotFailed(source);
}
// Use the event
eventData.Use();
}
///
/// Raises the end drag event.
///
/// Event data.
public virtual void OnEndDrag(PointerEventData eventData)
{
// Check if a drag was initialized at all
if (!this.m_DragHasBegan)
return;
// Reset the drag begin bool
this.m_DragHasBegan = false;
// Destroy the dragged icon object
if (this.m_CurrentDraggedObject != null)
{
Destroy(this.m_CurrentDraggedObject);
}
// Reset the variables
this.m_CurrentDraggedObject = null;
this.m_CurrentDraggingPlane = null;
// Use the event
eventData.Use();
// Check if we are returning the icon to the same slot
// By checking if the slot is highlighted
if (this.IsHighlighted(eventData))
return;
// Check if no drop was preformed
if (!this.m_DropPreformed)
{
// Try to throw away the astigned content
this.OnThrowAway();
}
else
{
// Reset the drop preformed variable
this.m_DropPreformed = false;
}
}
///
/// Determines whether this slot can swap with the specified target slot.
///
/// true if this instance can swap with the specified target; otherwise, false.
/// Target.
public virtual bool CanSwapWith(Object target)
{
return (target is UISlotBase);
}
///
/// Performs a slot swap.
///
/// Target slot.
public virtual bool PerformSlotSwap(Object targetObject)
{
// Get the source slot
UISlotBase targetSlot = (targetObject as UISlotBase);
// Get the target slot icon
Object targetIcon = targetSlot.GetIconAsObject();
// astign the target slot with this one
bool astign1 = targetSlot.astign(this);
// astign this slot by the target slot icon
bool astign2 = this.astign(targetIcon);
// Return the status
return (astign1 && astign2);
}
///
/// Called when the slot fails to astign by another slot.
///
protected virtual void OnastignBySlotFailed(Object source)
{
Debug.Log("UISlotBase (" + this.gameObject.name + ") failed to get astigned by (" + (source as UISlotBase).gameObject.name + ").");
}
///
/// This method is raised to confirm throwing away the slot.
///
protected virtual void OnThrowAway()
{
// Check if throwing away is allowed
if (this.m_AllowThrowAway)
{
// Throw away successful, unastign the slot
this.Unastign();
}
else
{
// Throw away was denied
this.OnThrowAwayDenied();
}
}
///
/// This method is raised when the slot is denied to be thrown away and returned to it's source.
///
protected virtual void OnThrowAwayDenied() { }
///
/// Creates the temporary icon.
///
/// The temporary icon.
protected virtual void CreateTemporaryIcon(PointerEventData eventData)
{
Canvas canvas = UIUtility.FindInParents(this.gameObject);
if (canvas == null || this.iconGraphic == null)
return;
// Create temporary panel
GameObject iconObj = (GameObject)Instantiate(this.iconGraphic.gameObject);
iconObj.transform.SetParent(canvas.transform, false);
iconObj.transform.SetAsLastSibling();
(iconObj.transform as RectTransform).pivot = new Vector2(0.5f, 0.5f);
// The icon will be under the cursor.
// We want it to be ignored by the event system.
iconObj.AddComponent();
// Save the dragging plane
this.m_CurrentDraggingPlane = canvas.transform as RectTransform;
// Set as the current dragging object
this.m_CurrentDraggedObject = iconObj;
// Update the icon position
this.UpdateDraggedPosition(eventData);
}
///
/// Updates the dragged icon position.
///
/// Data.
private void UpdateDraggedPosition(PointerEventData data)
{
var rt = this.m_CurrentDraggedObject.GetComponent();
Vector3 globalMousePos;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(this.m_CurrentDraggingPlane, data.position, data.pressEventCamera, out globalMousePos))
{
rt.position = globalMousePos;
rt.rotation = this.m_CurrentDraggingPlane.rotation;
}
}
///
/// Internal call for show tooltip.
///
protected void InternalShowTooltip()
{
// Call the on tooltip only if it's currently not shown
if (!this.m_IsTooltipShown)
{
this.m_IsTooltipShown = true;
this.OnTooltip(true);
}
}
///
/// Internal call for hide tooltip.
///
protected void InternalHideTooltip()
{
// Cancel the delayed show coroutine
this.StopCoroutine("TooltipDelayedShow");
// Call the on tooltip only if it's currently shown
if (this.m_IsTooltipShown)
{
this.m_IsTooltipShown = false;
this.OnTooltip(false);
}
}
protected IEnumerator TooltipDelayedShow()
{
yield return new WaitForSeconds(this.m_TooltipDelay);
this.InternalShowTooltip();
}
}
}