天津23维预案
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

706 lines
17 KiB

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using System.Linq;
namespace UIWidgets
{
/// <summary>
/// IResizableItem.
/// </summary>
public interface IResizableItem {
/// <summary>
/// Gets the objects to resize.
/// </summary>
/// <value>The objects to resize.</value>
GameObject[] ObjectsToResize {
get;
}
}
/// <summary>
/// ResizableHeader.
/// </summary>
[AddComponentMenu("UI/UIWidgets/ResizableHeader")]
[RequireComponent(typeof(LayoutGroup))]
public class ResizableHeader : MonoBehaviour, IDropSupport<ResizableHeaderDragCell>, IPointerEnterHandler, IPointerExitHandler
{
/// <summary>
/// ListView instance.
/// </summary>
[SerializeField]
public ListViewBase List;
/// <summary>
/// Allow resize.
/// </summary>
[SerializeField]
public bool AllowResize = true;
/// <summary>
/// Allow reorder.
/// </summary>
[SerializeField]
public bool AllowReorder = true;
/// <summary>
/// Is now processed cell reorder?
/// </summary>
[System.NonSerialized]
[HideInInspector]
public bool ProcessCellReorder = false;
/// <summary>
/// Update ListView columns width on drag.
/// </summary>
[SerializeField]
public bool OnDragUpdate = true;
/// <summary>
/// The active region in points from left or right border where resize allowed.
/// </summary>
[SerializeField]
public float ActiveRegion = 5;
/// <summary>
/// The current camera. For Screen Space - Overlay let it empty.
/// </summary>
[SerializeField]
public Camera CurrentCamera;
/// <summary>
/// The cursor texture.
/// </summary>
[SerializeField]
public Texture2D CursorTexture;
/// <summary>
/// The cursor hot spot.
/// </summary>
[SerializeField]
public Vector2 CursorHotSpot = new Vector2(16, 16);
/// <summary>
/// The cursor texture.
/// </summary>
[SerializeField]
public Texture2D AllowDropCursor;
/// <summary>
/// The cursor hot spot.
/// </summary>
[SerializeField]
public Vector2 AllowDropCursorHotSpot = new Vector2(4, 2);
/// <summary>
/// The cursor texture.
/// </summary>
[SerializeField]
public Texture2D DeniedDropCursor;
/// <summary>
/// The cursor hot spot.
/// </summary>
[SerializeField]
public Vector2 DeniedDropCursorHotSpot = new Vector2(4, 2);
/// <summary>
/// The default cursor texture.
/// </summary>
[SerializeField]
public Texture2D DefaultCursorTexture;
/// <summary>
/// The default cursor hot spot.
/// </summary>
[SerializeField]
public Vector2 DefaultCursorHotSpot;
RectTransform rectTransform;
/// <summary>
/// Gets the rect transform.
/// </summary>
/// <value>The rect transform.</value>
public RectTransform RectTransform {
get {
if (rectTransform==null)
{
rectTransform = transform as RectTransform;
}
return rectTransform;
}
}
Canvas canvas;
RectTransform canvasRect;
List<LayoutElement> childrenLayouts = new List<LayoutElement>();
List<RectTransform> children = new List<RectTransform>();
List<int> positions;
LayoutElement leftTarget;
LayoutElement rightTarget;
bool processDrag;
List<float> widths;
LayoutGroup layout;
void Start()
{
layout = GetComponent<LayoutGroup>();
if (layout!=null)
{
Utilites.UpdateLayout(layout);
}
Init();
}
/// <summary>
/// Raises the initialize potential drag event.
/// </summary>
/// <param name="eventData">Event data.</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
//Init();
}
/// <summary>
/// Init this instance.
/// </summary>
public void Init()
{
canvasRect = Utilites.FindTopmostCanvas(transform) as RectTransform;
canvas = canvasRect.GetComponent<Canvas>();
children.Clear();
childrenLayouts.Clear();
int i = 0;
foreach (Transform child in transform)
{
var element = child.GetComponent<LayoutElement>();
if (element==null)
{
element = child.gameObject.AddComponent<LayoutElement>();
}
children.Add(child as RectTransform);
childrenLayouts.Add(element);
var cell = child.gameObject.AddComponent<ResizableHeaderDragCell>();
cell.Position = i;
cell.ResizableHeader = this;
cell.AllowDropCursor = AllowDropCursor;
cell.AllowDropCursorHotSpot = AllowDropCursorHotSpot;
cell.DeniedDropCursor = DeniedDropCursor;
cell.DeniedDropCursorHotSpot = DeniedDropCursorHotSpot;
var events = child.gameObject.AddComponent<ResizableHeaderCell>();
events.OnInitializePotentialDragEvent.AddListener(OnInitializePotentialDrag);
events.OnBeginDragEvent.AddListener(OnBeginDrag);
events.OnDragEvent.AddListener(OnDrag);
events.OnEndDragEvent.AddListener(OnEndDrag);
i++;
}
positions = Enumerable.Range(0, i).ToList();
CalculateWidths();
//ResetChildren();
//Resize();
}
bool inActiveRegion;
/// <summary>
/// Gets a value indicating whether mouse position in active region.
/// </summary>
/// <value><c>true</c> if in active region; otherwise, <c>false</c>.</value>
public bool InActiveRegion {
get {
return inActiveRegion;
}
}
/// <summary>
/// Is cursor over gameobject?
/// </summary>
protected bool IsCursorOver;
/// <summary>
/// Called by a BaseInputModule when an OnPointerEnter event occurs.
/// </summary>
/// <param name="eventData">Event data.</param>
public void OnPointerEnter(PointerEventData eventData)
{
IsCursorOver = true;
}
/// <summary>
/// Called by a BaseInputModule when an OnPointerExit event occurs.
/// </summary>
/// <param name="eventData">Event data.</param>
public void OnPointerExit(PointerEventData eventData)
{
IsCursorOver = false;
cursorChanged = false;
Cursor.SetCursor(DefaultCursorTexture, DefaultCursorHotSpot, Utilites.GetCursorMode());
}
/// <summary>
/// Is cursor changed?
/// </summary>
protected bool cursorChanged;
void LateUpdate()
{
if (!AllowResize)
{
return ;
}
if (!IsCursorOver)
{
return ;
}
if (processDrag || ProcessCellReorder)
{
return ;
}
if ((CursorTexture==null) || (!Input.mousePresent))
{
return ;
}
inActiveRegion = CheckInActiveRegion(Input.mousePosition, CurrentCamera);
if (inActiveRegion)
{
Cursor.SetCursor(CursorTexture, CursorHotSpot, Utilites.GetCursorMode());
cursorChanged = true;
}
else if (cursorChanged)
{
Cursor.SetCursor(DefaultCursorTexture, DefaultCursorHotSpot, Utilites.GetCursorMode());
cursorChanged = false;
}
}
bool CheckInActiveRegion(Vector2 position, Camera currentCamera)
{
Vector2 point;
bool in_active_region = false;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform, position, currentCamera, out point))
{
return false;
}
var rect = RectTransform.rect;
if (!rect.Contains(point))
{
return false;
}
point += new Vector2(rect.width * RectTransform.pivot.x, 0);
int i = 0;
foreach (var child in children)
{
var is_first = i==0;
if (!is_first)
{
in_active_region = CheckLeft(child, point);
if (in_active_region)
{
break;
}
}
var is_last = i==(children.Count - 1);
if (!is_last)
{
in_active_region = CheckRight(child, point);
if (in_active_region)
{
break;
}
}
i++;
}
return in_active_region;
}
float widthLimit;
/// <summary>
/// Raises the begin drag event.
/// </summary>
/// <param name="eventData">Event data.</param>
public virtual void OnBeginDrag(PointerEventData eventData)
{
if (!AllowResize)
{
return ;
}
if (ProcessCellReorder)
{
return ;
}
Vector2 point;
processDrag = false;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform, eventData.pressPosition, eventData.pressEventCamera, out point))
{
return ;
}
var r = RectTransform.rect;
point += new Vector2(r.width * RectTransform.pivot.x, 0);
int i = 0;
foreach (var child in children)
{
var is_first = i==0;
if (!is_first)
{
processDrag = CheckLeft(child, point);
if (processDrag)
{
leftTarget = childrenLayouts[i - 1];
rightTarget = childrenLayouts[i];
widthLimit = children[i - 1].rect.width + children[i].rect.width;
break;
}
}
var is_last = i==(children.Count - 1);
if (!is_last)
{
processDrag = CheckRight(child, point);
if (processDrag)
{
leftTarget = childrenLayouts[i];
rightTarget = childrenLayouts[i + 1];
widthLimit = children[i].rect.width + children[i + 1].rect.width;
break;
}
}
i++;
}
}
/// <summary>
/// Checks if point in the left region.
/// </summary>
/// <returns><c>true</c>, if point in the left region, <c>false</c> otherwise.</returns>
/// <param name="childRectTransform">RectTransform.</param>
/// <param name="point">Point.</param>
bool CheckLeft(RectTransform childRectTransform, Vector2 point)
{
var r = childRectTransform.rect;
r.position += new Vector2(childRectTransform.anchoredPosition.x, 0);
r.width = ActiveRegion;
return r.Contains(point);
}
/// <summary>
/// Checks if point in the right region.
/// </summary>
/// <returns><c>true</c>, if right was checked, <c>false</c> otherwise.</returns>
/// <param name="childRectTransform">Child rect transform.</param>
/// <param name="point">Point.</param>
bool CheckRight(RectTransform childRectTransform, Vector2 point)
{
var r = childRectTransform.rect;
r.position += new Vector2(childRectTransform.anchoredPosition.x, 0);
r.position = new Vector2(r.position.x + r.width - ActiveRegion - 1, r.position.y);
r.width = ActiveRegion + 1;
return r.Contains(point);
}
/// <summary>
/// Raises the end drag event.
/// </summary>
/// <param name="eventData">Event data.</param>
public void OnEndDrag(PointerEventData eventData)
{
if (!processDrag)
{
return ;
}
Cursor.SetCursor(DefaultCursorTexture, DefaultCursorHotSpot, Utilites.GetCursorMode());
CalculateWidths();
ResetChildren();
if (!OnDragUpdate)
{
Resize();
}
processDrag = false;
}
/// <summary>
/// Raises the drag event.
/// </summary>
/// <param name="eventData">Event data.</param>
public void OnDrag(PointerEventData eventData)
{
if (!processDrag)
{
return ;
}
if (canvas==null)
{
throw new MissingComponentException(gameObject.name + " not in Canvas hierarchy.");
}
Cursor.SetCursor(CursorTexture, CursorHotSpot, Utilites.GetCursorMode());
Vector2 p1;
RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform, eventData.position, CurrentCamera, out p1);
Vector2 p2;
RectTransformUtility.ScreenPointToLocalPointInRectangle(RectTransform, eventData.position - eventData.delta, CurrentCamera, out p2);
var delta = p1 - p2;
if (delta.x > 0)
{
leftTarget.preferredWidth = Mathf.Min(leftTarget.preferredWidth + delta.x, widthLimit - rightTarget.minWidth);
rightTarget.preferredWidth = widthLimit - leftTarget.preferredWidth;
}
else
{
rightTarget.preferredWidth = Mathf.Min(rightTarget.preferredWidth - delta.x, widthLimit - leftTarget.minWidth);
leftTarget.preferredWidth = widthLimit - rightTarget.preferredWidth;
}
if (layout!=null)
{
Utilites.UpdateLayout(layout);
}
if (OnDragUpdate)
{
CalculateWidths();
Resize();
}
}
float GetRectWidth(RectTransform rect)
{
return rect.rect.width;
}
/// <summary>
/// Calculates the widths.
/// </summary>
void CalculateWidths()
{
widths = children.Select<RectTransform,float>(GetRectWidth).ToList();
}
/// <summary>
/// Resets the children widths.
/// </summary>
void ResetChildren()
{
childrenLayouts.ForEach(ResetChildrenWidth);
}
void ResetChildrenWidth(LayoutElement element, int index)
{
element.preferredWidth = widths[index];
}
/// <summary>
/// Resize items in ListView.
/// </summary>
public void Resize()
{
if (List==null)
{
return ;
}
if (widths.Count < 2)
{
return ;
}
List.Start();
List.ForEachComponent(ResizeComponent);
}
void Reorder()
{
if (List==null)
{
return ;
}
if (widths.Count < 2)
{
return ;
}
List.Start();
List.ForEachComponent(ReorderComponent);
}
/// <summary>
/// Resizes the game object.
/// </summary>
/// <param name="go">Go.</param>
/// <param name="i">The index.</param>
void ResizeGameObject(GameObject go, int i)
{
var layoutElement = go.GetComponent<LayoutElement>();
var position = positions.IndexOf(i);
if (layoutElement)
{
layoutElement.preferredWidth = widths[position];
}
else
{
(go.transform as RectTransform).SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, widths[position]);
}
}
/// <summary>
/// Resizes the component.
/// </summary>
/// <param name="component">Component.</param>
void ResizeComponent(ListViewItem component)
{
var resizable_item = component as IResizableItem;
if (resizable_item!=null)
{
resizable_item.ObjectsToResize.ForEach(ResizeGameObject);
}
}
void ReorderComponent(ListViewItem component)
{
var resizable_item = component as IResizableItem;
if (resizable_item!=null)
{
var objects = resizable_item.ObjectsToResize;
int i = 0;
foreach (var pos in positions)
{
objects[pos].transform.SetSiblingIndex(i);
i++;
}
}
}
/// <summary>
/// Move column from oldColumnPosition to newColumnPosition.
/// </summary>
/// <param name="oldColumnPosition">Old column position.</param>
/// <param name="newColumnPosition">New column position.</param>
public void Reorder(int oldColumnPosition, int newColumnPosition)
{
var old_pos = positions.IndexOf(oldColumnPosition);
var new_pos = positions.IndexOf(newColumnPosition);
children[old_pos].SetSiblingIndex(new_pos);
ChangePosition(childrenLayouts, old_pos, new_pos);
ChangePosition(children, old_pos, new_pos);
ChangePosition(positions, old_pos, new_pos);
Reorder();
}
#region IDropSupport<ResizableHeaderCell>
/// <summary>
/// Determines whether this instance can receive drop with the specified data and eventData.
/// </summary>
/// <returns><c>true</c> if this instance can receive drop with the specified data and eventData; otherwise, <c>false</c>.</returns>
/// <param name="cell">Cell.</param>
/// <param name="eventData">Event data.</param>
public bool CanReceiveDrop(ResizableHeaderDragCell cell, PointerEventData eventData)
{
if (!AllowReorder)
{
return false;
}
var target = FindTarget(eventData);
return target!=null && target!=cell;
}
/// <summary>
/// Process dropped data.
/// </summary>
/// <param name="cell">Cell.</param>
/// <param name="eventData">Event data.</param>
public void Drop(ResizableHeaderDragCell cell, PointerEventData eventData)
{
var target = FindTarget(eventData);
Reorder(cell.Position, target.Position);
}
/// <summary>
/// Process canceled drop.
/// </summary>
/// <param name="cell">Cell.</param>
/// <param name="eventData">Event data.</param>
public void DropCanceled(ResizableHeaderDragCell cell, PointerEventData eventData)
{
}
protected static void ChangePosition<T>(List<T> list, int oldPosition, int newPosition)
{
var item = list[oldPosition];
list.RemoveAt(oldPosition);
list.Insert(newPosition, item);
}
List<RaycastResult> raycastResults = new List<RaycastResult>();
protected ResizableHeaderDragCell FindTarget(PointerEventData eventData)
{
raycastResults.Clear();
EventSystem.current.RaycastAll(eventData, raycastResults);
foreach (var raycastResult in raycastResults)
{
if (!raycastResult.isValid)
{
continue ;
}
#if UNITY_4_6 || UNITY_4_7
var target = raycastResult.gameObject.GetComponent(typeof(ResizableHeaderDragCell)) as ResizableHeaderDragCell;
#else
var target = raycastResult.gameObject.GetComponent<ResizableHeaderDragCell>();
#endif
if ((target!=null) && target.transform.IsChildOf(transform))
{
return target;
}
}
return null;
}
#endregion
protected virtual void OnDestroy()
{
children.Clear();
childrenLayouts.Clear();
foreach (Transform child in transform)
{
var events = child.GetComponent<ResizableHeaderCell>();
if (events==null)
{
continue ;
}
events.OnInitializePotentialDragEvent.RemoveListener(OnInitializePotentialDrag);
events.OnBeginDragEvent.RemoveListener(OnBeginDrag);
events.OnDragEvent.RemoveListener(OnDrag);
events.OnEndDragEvent.RemoveListener(OnEndDrag);
}
}
}
}