NGUI Version 3.9.0

//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright © 2011-2015 Tasharen Entertainment
//---------------------------------------------- using UnityEngine;
using UnityEditor;
using System.Collections.Generic; /// <summary>
/// Inspector class used to edit UIWidgets.
/// </summary> [CanEditMultipleObjects]
[CustomEditor(typeof(UIWidget), true)]
public class UIWidgetInspector : UIRectEditor
{
static public new UIWidgetInspector instance; // public enum Action // YUTODO这个什么作用?
{
None,
Move, // 移动
Scale,// 缩放
Rotate,
} Action mAction = Action.None;
Action mActionUnderMouse = Action.None;
bool mAllowSelection = true; protected UIWidget mWidget; // 保存一个widget static protected bool mUseShader = false;// 默认不使用shader // GUIStyle 是什么鬼?
// 各种颜色的点
static GUIStyle mBlueDot = null; // 蓝点?
static GUIStyle mYellowDot = null;
static GUIStyle mRedDot = null;
static GUIStyle mOrangeDot = null;
static GUIStyle mGreenDot = null;
static GUIStyle mGreyDot = null;
static MouseCursor mCursor = MouseCursor.Arrow; // 还能控制鼠标光标?666 // 枢轴点
static public UIWidget.Pivot[] pivotPoints =
{
UIWidget.Pivot.BottomLeft,
UIWidget.Pivot.TopLeft,
UIWidget.Pivot.TopRight,
UIWidget.Pivot.BottomRight,
UIWidget.Pivot.Left,
UIWidget.Pivot.Top,
UIWidget.Pivot.Right,
UIWidget.Pivot.Bottom,
}; //
static int s_Hash = "WidgetHash".GetHashCode(); // 坐标
Vector3 mLocalPos = Vector3.zero;
Vector3 mWorldPos = Vector3.zero; // 起始宽高
int mStartWidth = 0;
int mStartHeight = 0; // 起始拖动和鼠标?
Vector3 mStartDrag = Vector3.zero;
Vector2 mStartMouse = Vector2.zero; // 起始方向和旋转角度
Vector3 mStartRot = Vector3.zero;
Vector3 mStartDir = Vector3.right; // 起始的上下左右?
Vector2 mStartLeft = Vector2.zero;
Vector2 mStartRight = Vector2.zero;
Vector2 mStartBottom = Vector2.zero;
Vector2 mStartTop = Vector2.zero; // 拖动枢轴点 默认为中心
UIWidget.Pivot mDragPivot = UIWidget.Pivot.Center; /// <summary>
/// Raycast into the screen.
/// 判断当前鼠标是否射中传入的平面
/// </summary>
/// <param name="corners">传入待检测平面的三个点</param>
/// <param name="hit">射到的点的坐标</param>
/// <returns>射到返回true</returns>
static public bool Raycast (Vector3[] corners, out Vector3 hit)
{
Plane plane = new Plane(corners[0], corners[1], corners[2]); // 三点确定一个平面
Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); // 转换2D GUI位置到世界空间的ray
float dist = 0f;
bool isHit = plane.Raycast(ray, out dist); // 检查射线有没有射中平面
hit = isHit ? ray.GetPoint(dist) : Vector3.zero; // GetPoint 返回离射线距离为 dist 的那个点的位置。
return isHit;
} /// <summary>
/// Color used by the handles based on the current color scheme.
/// 根据当前颜色主题返回颜色
/// </summary> static public Color handlesColor
{
get
{
if (NGUISettings.colorMode == NGUISettings.ColorMode.Orange)
{
return new Color(1f, 0.5f, 0f);
}
else if (NGUISettings.colorMode == NGUISettings.ColorMode.Green)
{
return Color.green;
}
return Color.white;
}
} /// <summary>
/// Draw a control dot at the specified world position.
/// 在世界坐标绘制控制点
/// </summary> static public void DrawKnob (Vector3 point, bool selected, bool canResize, int id)
{
// TODO 这是一种什么赋值方法?
if (mGreyDot == null) mGreyDot = "sv_label_0"; // 一种内置的编辑器风格
if (mBlueDot == null) mBlueDot = "sv_label_1";
if (mGreenDot == null) mGreenDot = "sv_label_3";
if (mYellowDot == null) mYellowDot = "sv_label_4";
if (mOrangeDot == null) mOrangeDot = "sv_label_5";
if (mRedDot == null) mRedDot = "sv_label_6"; //
Vector2 screenPoint = HandleUtility.WorldToGUIPoint(point); // 矩阵
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6
Rect rect = new Rect(screenPoint.x - 7f, screenPoint.y - 7f, 14f, 14f);
#else
Rect rect = new Rect(screenPoint.x - 5f, screenPoint.y - 9f, 14f, 14f);
#endif // 根据传入的状态判断显示的颜色
if (selected)
{
if (NGUISettings.colorMode == NGUISettings.ColorMode.Orange)
{
mRedDot.Draw(rect, GUIContent.none, id);
}
else
{
mOrangeDot.Draw(rect, GUIContent.none, id);
}
}
else if (canResize)
{
if (NGUISettings.colorMode == NGUISettings.ColorMode.Orange)
{
mOrangeDot.Draw(rect, GUIContent.none, id);
}
else if (NGUISettings.colorMode == NGUISettings.ColorMode.Green)
{
mGreenDot.Draw(rect, GUIContent.none, id);
}
else
{
mBlueDot.Draw(rect, GUIContent.none, id);
}
}
else mGreyDot.Draw(rect, GUIContent.none, id); //
} /// <summary>
/// Screen-space distance from the mouse position to the specified world position.
/// 屏幕空间下 鼠标到某点的距离
/// </summary> static public float GetScreenDistance (Vector3 worldPos, Vector2 mousePos)
{
Vector2 screenPos = HandleUtility.WorldToGUIPoint(worldPos);
return Vector2.Distance(mousePos, screenPos);
} /// <summary>
/// Closest screen-space distance from the mouse position to one of the specified world points.
/// 屏幕空间下 在鼠标到各点的距离中取最小距离
/// </summary> static public float GetScreenDistance (Vector3[] worldPoints, Vector2 mousePos, out int index)
{
float min = float.MaxValue;
index = 0; for (int i = 0; i < worldPoints.Length; ++i)
{
float distance = GetScreenDistance(worldPoints[i], mousePos); if (distance < min)
{
index = i;
min = distance;
}
}
return min;
} /// <summary>
/// Set the mouse cursor rectangle, refreshing the screen when it gets changed.
/// 鼠标光标类型发生改变时重新设置它的矩形区域
/// </summary> static public void SetCursorRect (Rect rect, MouseCursor cursor)
{
EditorGUIUtility.AddCursorRect(rect, cursor); if (Event.current.type == EventType.MouseMove)
{
if (mCursor != cursor)
{
mCursor = cursor;
Event.current.Use();
}
}
} /// <summary>
///
/// </summary>
protected override void OnDisable ()
{
base.OnDisable();
NGUIEditorTools.HideMoveTool(false);
instance = null;
} /// <summary>
/// Convert the specified 4 corners into 8 pivot points (adding left, top, right, bottom -- in that order).
/// 把四个角落的坐标数组扩展为八个点的坐标数组
/// </summary> static public Vector3[] GetHandles (Vector3[] corners)
{
Vector3[] v = new Vector3[8]; v[0] = corners[0];
v[1] = corners[1];
v[2] = corners[2];
v[3] = corners[3]; v[4] = (corners[0] + corners[1]) * 0.5f;
v[5] = (corners[1] + corners[2]) * 0.5f;
v[6] = (corners[2] + corners[3]) * 0.5f;
v[7] = (corners[0] + corners[3]) * 0.5f; return v;
} /// <summary>
/// Determine what kind of pivot point is under the mouse and update the cursor accordingly.
/// 通过鼠标的位置判断应当进行什么样的操作。
/// 默认返回 center 锚点,能够缩放时返回 目标锚点
/// </summary> static public UIWidget.Pivot GetPivotUnderMouse (Vector3[] worldPos, Event e, bool[] resizable, bool movable, ref Action action)
{
// Time to figure out what kind of action is underneath the mouse
UIWidget.Pivot pivotUnderMouse = UIWidget.Pivot.Center;// 初始化为 锚点中心 //
if (action == Action.None)
{
int index = 0;
float dist = GetScreenDistance(worldPos, e.mousePosition, out index); // 获取最小的距离,并记录最小距离对应点的下标
bool alt = (e.modifiers & EventModifiers.Alt) != 0; // 判断是不是按了组合键 //
if (resizable[index] && dist < 10f) // 如果这个位置能动,并且距离小于10f
{
pivotUnderMouse = pivotPoints[index]; //鼠标点到的枢轴点是index标示出的index
action = Action.Scale;// 进行缩放动作
}
else if (!alt && NGUIEditorTools.SceneViewDistanceToRectangle(worldPos, e.mousePosition) == 0f) // 没有按组合键并且鼠标点在矩形框里面
{
action = movable ? Action.Move : Action.Rotate; // 能移动仅进行移动,不能移动就进行旋转
}
else if (dist < 30f)
{
action = Action.Rotate; // 其他情况就进行旋转
}
} // Change the mouse cursor to a more appropriate one
//
Vector2[] screenPos = new Vector2[8];
for (int i = 0; i < 8; ++i) screenPos[i] = HandleUtility.WorldToGUIPoint(worldPos[i]); Bounds b = new Bounds(screenPos[0], Vector3.zero); // 给定中心点和大小。,后面那个大小代表(width,height,depth)
for (int i = 1; i < 8; ++i) b.Encapsulate(screenPos[i]);// 包含八个点的最小矩形框,在这里八个点都恰好在矩形框的边上。 Vector2 min = b.min;// min = center - extents; extents 是 size 的一半。例如中心点为(0,0,0),extents(2,3,5),那么min(-2,-3,-5)
Vector2 max = b.max; // 在原来的基础上扩展 30f。depth 没用到所以这里没处理
min.x -= 30f;
max.x += 30f;
min.y -= 30f;
max.y += 30f; Rect rect = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); // 获取矩形框 // 根据动作的状态 显示鼠标光标的形状
if (action == Action.Rotate)
{
SetCursorRect(rect, MouseCursor.RotateArrow);
}
else if (action == Action.Move)
{
SetCursorRect(rect, MouseCursor.MoveArrow);
}
else if (action == Action.Scale)
{
SetCursorRect(rect, MouseCursor.ScaleArrow);
}
else SetCursorRect(rect, MouseCursor.Arrow); return pivotUnderMouse;
} /// <summary>
/// Draw the specified anchor point.
/// 绘制黄色的锚点以及连接线、距离标签
/// </summary> static public void DrawAnchorHandle (UIRect.AnchorPoint anchor, Transform myTrans, Vector3[] myCorners, int side, int id)
{
if (!anchor.target) return; // 没有定锚点就不用画了 int i0, i1; //矩形四个点在数组中的序列与相对位置的关系如下
//12
//03 //根据是哪边的锚点来选出决定那条边的两个顶点
if (side == 0)
{
// Left
i0 = 0;
i1 = 1;
}
else if (side == 1)
{
// Top
i0 = 1;
i1 = 2;
}
else if (side == 2)
{
// Right
i0 = 3;
i1 = 2;
}
else
{
// Bottom
i0 = 0;
i1 = 3;
} //
Vector3 myPos = (myCorners[i0] + myCorners[i1]) * 0.5f; // 锚点相关位置是对应边的中点 Vector3[] sides = null; // 目标对象矩形的四个节点 if (anchor.rect != null)
{
sides = anchor.rect.worldCorners;
}
else
{
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6
Camera cam = anchor.target.camera; // 获取锚点目标的 camera
#else
Camera cam = anchor.target.GetComponent<Camera>();
#endif
if (cam != null) sides = cam.GetWorldCorners(); // 获取相机的边界
} Vector3 theirPos; // 黄点位置,必然在目标矩形上面 if (sides != null)
{
Vector3 v0, v1; if (side == 0 || side == 2)
{
// Left or right
v0 = Vector3.Lerp(sides[0], sides[3], anchor.relative); // 在两个向量V0、V1间做线性插值,根据第三个参数t决定返回某个插值。例如 t=0 返回V0,t=0.5 返回V0、V1的中间插值。
v1 = Vector3.Lerp(sides[1], sides[2], anchor.relative); //
}
else
{
// Top or bottom
v0 = Vector3.Lerp(sides[0], sides[1], anchor.relative);
v1 = Vector3.Lerp(sides[3], sides[2], anchor.relative);
} // 黄点是 myPos 在 v0 和 v1 连接线上的投影点,如果超过v0v1线段,则黄点设置为v0或v1。
theirPos = HandleUtility.ProjectPointLine(myPos, v0, v1);
}
else
{
theirPos = anchor.target.position;
} // 画连接线
NGUIHandles.DrawShadowedLine(myCorners, myPos, theirPos, Color.yellow); // 位置偏离的时候在一定条件下绘制一个标记负值距离的标签
if (Event.current.GetTypeForControl(id) == EventType.Repaint) // 控制ID id 当前事件类型如果是重绘
{
Vector2 screenPoint = HandleUtility.WorldToGUIPoint(theirPos); #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6
Rect rect = new Rect(screenPoint.x - 7f, screenPoint.y - 7f, 14f, 14f);
#else
Rect rect = new Rect(screenPoint.x - 5f, screenPoint.y - 9f, 14f, 14f);
#endif
if (mYellowDot == null) mYellowDot = "sv_label_4"; Vector3 v0 = HandleUtility.WorldToGUIPoint(myPos);
Vector3 v1 = HandleUtility.WorldToGUIPoint(theirPos); Handles.BeginGUI(); // 开始一个 2D 绘制块 mYellowDot.Draw(rect, GUIContent.none, id); // 绘制黄点 Vector3 diff = v1 - v0;
bool isHorizontal = Mathf.Abs(diff.x) > Mathf.Abs(diff.y);// 判断是否为水平线
float mag = diff.magnitude; // 获取线的长度 if ((isHorizontal && mag > 60f) || (!isHorizontal && mag > 30f)) // 长度超过 60 的水平线或者长度大于 30 的竖直线
{
Vector3 pos = (myPos + theirPos) * 0.5f; //
string text = anchor.absolute.ToString();// GUI.color = Color.yellow; if (side == 0)
{
if (theirPos.x < myPos.x) //如果目标点的位置比出发点的位置还要靠左,那么显示一个标记距离的label
NGUIHandles.DrawCenteredLabel(pos, text);
}
else if (side == 1)
{
if (theirPos.y > myPos.y)
NGUIHandles.DrawCenteredLabel(pos, text);
}
else if (side == 2)
{
if (theirPos.x > myPos.x)
NGUIHandles.DrawCenteredLabel(pos, text);
}
else if (side == 3)
{
if (theirPos.y < myPos.y)
NGUIHandles.DrawCenteredLabel(pos, text);
}
GUI.color = Color.white;
}
Handles.EndGUI();
}
} /// <summary>
/// Draw the on-screen selection, knobs, and handle all interaction logic.
///
/// </summary> public void OnSceneGUI ()
{
if (Selection.objects.Length > 1) return; // 选中了多个对象,返回
NGUIEditorTools.HideMoveTool(true); // 隐藏移动工具
if (!UIWidget.showHandles) return; // 没有需要显示的 handles,返回 mWidget = target as UIWidget; // 把选中对象作为一个 UIWidget 组件处理 Transform t = mWidget.cachedTransform; // 获取对象的 transform Event e = Event.current; //
int id = GUIUtility.GetControlID(s_Hash, FocusType.Passive); // 获取一个控制ID,这个控制不能获取键盘焦点
EventType type = e.GetTypeForControl(id); // 获取控制ID的事件类型 Action actionUnderMouse = mAction; // 移动、缩放或是旋转
Vector3[] handles = GetHandles(mWidget.worldCorners); // 获取组件的八个枢轴点点 // 绘制矩形框
NGUIHandles.DrawShadowedLine(handles, handles[0], handles[1], handlesColor);
NGUIHandles.DrawShadowedLine(handles, handles[1], handles[2], handlesColor);
NGUIHandles.DrawShadowedLine(handles, handles[2], handles[3], handlesColor);
NGUIHandles.DrawShadowedLine(handles, handles[0], handles[3], handlesColor); // If the widget is anchored, draw the anchors
// 组件有锚点的话就绘制锚点
if (mWidget.isAnchored)
{
DrawAnchorHandle(mWidget.leftAnchor, mWidget.cachedTransform, handles, 0, id);
DrawAnchorHandle(mWidget.topAnchor, mWidget.cachedTransform, handles, 1, id);
DrawAnchorHandle(mWidget.rightAnchor, mWidget.cachedTransform, handles, 2, id);
DrawAnchorHandle(mWidget.bottomAnchor, mWidget.cachedTransform, handles, 3, id);
} // 需要时显示细节
if (type == EventType.Repaint)
{
bool showDetails = (mAction == UIWidgetInspector.Action.Scale) || NGUISettings.drawGuides; // 进行缩放时显示细节
if (mAction == UIWidgetInspector.Action.None && e.modifiers == EventModifiers.Control) showDetails = true;
if (NGUITools.GetActive(mWidget) && mWidget.parent == null) showDetails = true;
if (showDetails) NGUIHandles.DrawSize(handles, mWidget.width, mWidget.height);
} // Presence of the legacy stretch component prevents resizing
// TODO 显示原来的轨迹组件放置重绘 ?? bool canResize = (mWidget.GetComponent<UIStretch>() == null); // 没有这个组件说明是可以自由缩放的
bool[] resizable = new bool[8]; // resizable[4] = canResize; // left
resizable[5] = canResize; // top
resizable[6] = canResize; // right
resizable[7] = canResize; // bottom UILabel lbl = mWidget as UILabel; // 如果这个 widget 组件实际上是个一个 Label 组件 if (lbl != null)
{
if (lbl.overflowMethod == UILabel.Overflow.ResizeFreely) // 如果 label 设置为可以随意缩放,那么其锚点都定住
{
resizable[4] = false; // left
resizable[5] = false; // top
resizable[6] = false; // right
resizable[7] = false; // bottom
}
else if (lbl.overflowMethod == UILabel.Overflow.ResizeHeight) // 如果是高度可以自由缩放,那么上下两个锚点也定住
{
resizable[5] = false; // top
resizable[7] = false; // bottom
}
} if (mWidget.keepAspectRatio == UIWidget.AspectRatioSource.BasedOnHeight) // 基于高度变换,那么左右锚点就不需要设置了
{
resizable[4] = false;
resizable[6] = false;
}
else if (mWidget.keepAspectRatio == UIWidget.AspectRatioSource.BasedOnWidth)
{
resizable[5] = false;
resizable[7] = false;
} // 角落的锚点情况根据上下左右的可用情况设置
resizable[0] = resizable[7] && resizable[4]; // bottom-left
resizable[1] = resizable[5] && resizable[4]; // top-left
resizable[2] = resizable[5] && resizable[6]; // top-right
resizable[3] = resizable[7] && resizable[6]; // bottom-right // 获取锚点,没有的话返回 center
UIWidget.Pivot pivotUnderMouse = GetPivotUnderMouse(handles, e, resizable, true, ref actionUnderMouse); switch (type)
{
case EventType.Repaint: // 绘制控制点
{
Vector3 v0 = HandleUtility.WorldToGUIPoint(handles[0]);
Vector3 v2 = HandleUtility.WorldToGUIPoint(handles[2]); if ((v2 - v0).magnitude > 60f) // 斜边 长度大于 60 才绘制控制点
{
Vector3 v1 = HandleUtility.WorldToGUIPoint(handles[1]);
Vector3 v3 = HandleUtility.WorldToGUIPoint(handles[3]); Handles.BeginGUI();
{
for (int i = 0; i < 4; ++i)
DrawKnob(handles[i], mWidget.pivot == pivotPoints[i], resizable[i], id); if ((v1 - v0).magnitude > 80f)
{
if (mWidget.leftAnchor.target == null || mWidget.leftAnchor.absolute != 0)
DrawKnob(handles[4], mWidget.pivot == pivotPoints[4], resizable[4], id); if (mWidget.rightAnchor.target == null || mWidget.rightAnchor.absolute != 0)
DrawKnob(handles[6], mWidget.pivot == pivotPoints[6], resizable[6], id);
} if ((v3 - v0).magnitude > 80f)
{
if (mWidget.topAnchor.target == null || mWidget.topAnchor.absolute != 0)
DrawKnob(handles[5], mWidget.pivot == pivotPoints[5], resizable[5], id); if (mWidget.bottomAnchor.target == null || mWidget.bottomAnchor.absolute != 0)
DrawKnob(handles[7], mWidget.pivot == pivotPoints[7], resizable[7], id);
}
}
Handles.EndGUI();
}
}
break; case EventType.MouseDown:
{
if (actionUnderMouse != Action.None)
{
mStartMouse = e.mousePosition; // 记录鼠标起始位置
mAllowSelection = true; // 标记拖动的开始,拖动过程中无法再次选中 if (e.button == 1) //
{
if (e.modifiers == 0) // 没有用组合键
{
GUIUtility.hotControl = GUIUtility.keyboardControl = id;
e.Use(); // 使用该事件,用于表示这个事件已经处理过了
}
}
else if (e.button == 0 && actionUnderMouse != Action.None && Raycast(handles, out mStartDrag)) // 鼠标左键&&能够操作&&点到了这个平面 // TODO 不知道为什么需要这么多判断
{
mWorldPos = t.position;
mLocalPos = t.localPosition;
mStartRot = t.localRotation.eulerAngles;
mStartDir = mStartDrag - t.position; // 用向量来表示角度,鼠标与中点连线的夹角
mStartWidth = mWidget.width;
mStartHeight = mWidget.height;
// TODO 上下左右,
mStartLeft.x = mWidget.leftAnchor.relative;
mStartLeft.y = mWidget.leftAnchor.absolute;
mStartRight.x = mWidget.rightAnchor.relative;
mStartRight.y = mWidget.rightAnchor.absolute;
mStartBottom.x = mWidget.bottomAnchor.relative;
mStartBottom.y = mWidget.bottomAnchor.absolute;
mStartTop.x = mWidget.topAnchor.relative;
mStartTop.y = mWidget.topAnchor.absolute; //
mDragPivot = pivotUnderMouse;
mActionUnderMouse = actionUnderMouse;
//
GUIUtility.hotControl = GUIUtility.keyboardControl = id;
e.Use();
}
}
}
break; case EventType.MouseDrag:
{
// Prevent selection once the drag operation begins
// 如果已经进行了拖动操作,那么无法再进行选中了
bool dragStarted = (e.mousePosition - mStartMouse).magnitude > 3f;
if (dragStarted) mAllowSelection = false; if (GUIUtility.hotControl == id) // 如果热键控制 与 id 一致
{
e.Use(); // TODO 这个为什么设置在前面? if (mAction != Action.None || mActionUnderMouse != Action.None) //
{
Vector3 pos; if (Raycast(handles, out pos))
{
// 拖动开始时进行数据记录
if (mAction == Action.None && mActionUnderMouse != Action.None) // 前面的 mAction 用于标记是否是拖动刚开始
{
// Wait until the mouse moves by more than a few pixels
if (dragStarted)
{
if (mActionUnderMouse == Action.Move) //
{
NGUISnap.Recalculate(mWidget); // 重新计算
}
else if (mActionUnderMouse == Action.Rotate) //
{
mStartRot = t.localRotation.eulerAngles; // 返回欧垃角
mStartDir = mStartDrag - t.position; // 旋转的角度
}
else if (mActionUnderMouse == Action.Scale)
{
mStartWidth = mWidget.width;
mStartHeight = mWidget.height;
mDragPivot = pivotUnderMouse;
}
mAction = actionUnderMouse;
}
} //
if (mAction != Action.None)
{
// 设置撤销点
NGUIEditorTools.RegisterUndo("Change Rect", t);
NGUIEditorTools.RegisterUndo("Change Rect", mWidget); // Reset the widget before adjusting anything
// 在进行任何调整前先进行重置
t.position = mWorldPos;
mWidget.width = mStartWidth;
mWidget.height = mStartHeight;
// widget 的上下左右锚点都重置为初始值
mWidget.leftAnchor.Set(mStartLeft.x, mStartLeft.y);
mWidget.rightAnchor.Set(mStartRight.x, mStartRight.y);
mWidget.bottomAnchor.Set(mStartBottom.x, mStartBottom.y);
mWidget.topAnchor.Set(mStartTop.x, mStartTop.y); // 移动操作
if (mAction == Action.Move)
{
// Move the widget
t.position = mWorldPos + (pos - mStartDrag); // t.position = t.positon + (pos - mStartDrag);//mWorldPos 和 mStartDrag 应该是同一个值,不知道为什么要分开来
Vector3 after = t.localPosition; // bool snapped = false; //
Transform parent = t.parent; if (parent != null)
{
UIGrid grid = parent.GetComponent<UIGrid>(); // // 如果说这个widget的父节点具有 Grid 组件,并且按照 CellSnap 的布局方式进行布局
// 那么根据grid的cell大小进行对齐操作。
if (grid != null && grid.arrangement == UIGrid.Arrangement.CellSnap)
{
snapped = true;
if (grid.cellWidth > 0) after.x = Mathf.Round(after.x / grid.cellWidth) * grid.cellWidth;
if (grid.cellHeight > 0) after.y = Mathf.Round(after.y / grid.cellHeight) * grid.cellHeight;
}
} // 如果还没对齐过,进行对齐
if (!snapped)
{
// Snap the widget
after = NGUISnap.Snap(after, mWidget.localCorners, e.modifiers != EventModifiers.Control);
} // Calculate the final delta
// 本地坐标的变化量
Vector3 localDelta = (after - mLocalPos); // Restore the position
t.position = mWorldPos; // 恢复坐标位置 // Adjust the widget by the delta
// 根据 delta 来移动 Widget 。 // TODO 这跟直接移动 postion 有什么区别?
NGUIMath.MoveRect(mWidget, localDelta.x, localDelta.y);
}
else if (mAction == Action.Rotate)
{
// 这部分代码不是太懂呢。。
Vector3 dir = pos - t.position;
float angle = Vector3.Angle(mStartDir, dir); if (angle > 0f)
{
float dot = Vector3.Dot(Vector3.Cross(mStartDir, dir), t.forward);
if (dot < 0f) angle = -angle;
angle = mStartRot.z + angle; // 为什么要加 z 值
angle = (NGUISnap.allow && e.modifiers != EventModifiers.Control) ?
Mathf.Round(angle / 15f) * 15f : Mathf.Round(angle);
// 计算欧拉角
t.localRotation = Quaternion.Euler(mStartRot.x, mStartRot.y, angle);
}
}
else if (mAction == Action.Scale)
{
// Move the widget
//
t.position = mWorldPos + (pos - mStartDrag); // Calculate the final delta
Vector3 localDelta = (t.localPosition - mLocalPos); // Restore the position
t.position = mWorldPos; // Adjust the widget's position and scale based on the delta, restricted by the pivot
// 以枢轴点为中心进行缩放
NGUIMath.ResizeWidget(mWidget, mDragPivot, localDelta.x, localDelta.y, 2, 2);
ReEvaluateAnchorType();
}
}
}
}
}
}
break; // 释放鼠标
case EventType.MouseUp:
{
if (e.button == 2) break; // 中键不处理 if (GUIUtility.hotControl == id)
{
// 收尾处理
GUIUtility.hotControl = 0;
GUIUtility.keyboardControl = 0; //
if (e.button < 2)
{
bool handled = false; //
if (e.button == 1)
{
// Right-click: Open a context menu listing all widgets underneath
NGUIEditorTools.ShowSpriteSelectionMenu(e.mousePosition);
handled = true;
}
else if (mAction == Action.None)
{
// 选择对象
if (mAllowSelection)
{
// Left-click: Select the topmost widget
NGUIEditorTools.SelectWidget(e.mousePosition);
handled = true;
}
}
else
{
// Finished dragging something
Vector3 pos = t.localPosition;
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
pos.z = Mathf.Round(pos.z);
t.localPosition = pos; //对相对位置进行圆整
handled = true;
} if (handled) e.Use(); // 如果处理过了,那么该事件结束
} // Clear the actions
mActionUnderMouse = Action.None;
mAction = Action.None;
}
else if (mAllowSelection) // 没控制但是允许选中,那么选择第一个 widget
{
List<UIWidget> widgets = NGUIEditorTools.SceneViewRaycast(e.mousePosition);
if (widgets.Count > 0) Selection.activeGameObject = widgets[0].gameObject;
}
mAllowSelection = true;
}
break; // 上下左右键对矩形框进行移动
case EventType.KeyDown:
{
if (e.keyCode == KeyCode.UpArrow)
{
NGUIEditorTools.RegisterUndo("Nudge Rect", t);
NGUIEditorTools.RegisterUndo("Nudge Rect", mWidget);
NGUIMath.MoveRect(mWidget, 0f, 1f);
e.Use();
}
else if (e.keyCode == KeyCode.DownArrow)
{
NGUIEditorTools.RegisterUndo("Nudge Rect", t);
NGUIEditorTools.RegisterUndo("Nudge Rect", mWidget);
NGUIMath.MoveRect(mWidget, 0f, -1f);
e.Use();
}
else if (e.keyCode == KeyCode.LeftArrow)
{
NGUIEditorTools.RegisterUndo("Nudge Rect", t);
NGUIEditorTools.RegisterUndo("Nudge Rect", mWidget);
NGUIMath.MoveRect(mWidget, -1f, 0f);
e.Use();
}
else if (e.keyCode == KeyCode.RightArrow)
{
NGUIEditorTools.RegisterUndo("Nudge Rect", t);
NGUIEditorTools.RegisterUndo("Nudge Rect", mWidget);
NGUIMath.MoveRect(mWidget, 1f, 0f);
e.Use();
}
else if (e.keyCode == KeyCode.Escape)
{
if (GUIUtility.hotControl == id)
{
if (mAction != Action.None)
Undo.PerformUndo(); GUIUtility.hotControl = 0;
GUIUtility.keyboardControl = 0; mActionUnderMouse = Action.None;
mAction = Action.None;
e.Use();
}
else Selection.activeGameObject = null;
}
}
break;
}
} /// <summary>
/// Cache the reference.
/// 记录引用
/// </summary> protected override void OnEnable ()
{
base.OnEnable();
instance = this;
mWidget = target as UIWidget; // 在对 mWidget 进行操作时为什么不需要判空?
} /// <summary>
/// All widgets have depth, color and make pixel-perfect options
/// 所有组件都有 depth,color和 pixel-perfect 选项
/// </summary> protected override void DrawCustomProperties ()
{
if (NGUISettings.unifiedTransform) // 这个不知道是哪里设置的。。如果是统一的transform,某些部分就不需要显示了。
{
DrawColor(serializedObject, mWidget);
}
else DrawInspectorProperties(serializedObject, mWidget, true); // 在检视器中显示Widget组件的操作面板
} /// <summary>
/// Draw common widget properties that can be shown as a part of the Transform Inspector.
/// 在检视器中显示通用的 widget 属性,不显示颜色部分
/// </summary> public void DrawWidgetTransform () { DrawInspectorProperties(serializedObject, mWidget, false); } /// <summary>
/// Draw the widget's color.
/// 在检视器中显示,设置 widget 的颜色和透明度
/// </summary> static public void DrawColor (SerializedObject so, UIWidget w)
{
if ((w.GetType() != typeof(UIWidget)))
{
NGUIEditorTools.DrawProperty("Color Tint", so, "mColor", GUILayout.MinWidth(20f));
}
else if (so.isEditingMultipleObjects)
{
NGUIEditorTools.DrawProperty("Alpha", so, "mColor.a", GUILayout.Width(120f));
}
else
{
GUI.changed = false;
float alpha = EditorGUILayout.Slider("Alpha", w.alpha, 0f, 1f); if (GUI.changed)
{
NGUIEditorTools.RegisterUndo("Alpha change", w);
w.alpha = alpha;
}
}
} /// <summary>
/// Draw common widget properties.
/// 在检视器中显示通用 widget 属性
/// </summary> static public void DrawInspectorProperties (SerializedObject so, UIWidget w, bool drawColor)
{
if (drawColor)
{
DrawColor(so, w);
GUILayout.Space(3f);
} PrefabType type = PrefabUtility.GetPrefabType(w.gameObject); // TODO 为什么要辨别出 prefab 的类型?有些情况不需要显示? if (NGUIEditorTools.DrawHeader("Widget")) // 包裹起来
{
NGUIEditorTools.BeginContents();
if (NGUISettings.minimalisticLook) NGUIEditorTools.SetLabelWidth(70f); DrawPivot(so, w); //枢轴点
DrawDepth(so, w, type == PrefabType.Prefab);// 深度
DrawDimensions(so, w, type == PrefabType.Prefab);// 大小
if (NGUISettings.minimalisticLook) NGUIEditorTools.SetLabelWidth(70f); SerializedProperty ratio = so.FindProperty("aspectRatio");
SerializedProperty aspect = so.FindProperty("keepAspectRatio"); GUILayout.BeginHorizontal();
{
if (!aspect.hasMultipleDifferentValues && aspect.intValue == 0) // 单个或多个对象选中时 aspect 都是第一种情况
{
EditorGUI.BeginDisabledGroup(true); // 设置为禁用
NGUIEditorTools.DrawProperty("Aspect", ratio, false, GUILayout.Width(130f));
EditorGUI.EndDisabledGroup();
}
else NGUIEditorTools.DrawProperty("Aspect", ratio, false, GUILayout.Width(130f)); NGUIEditorTools.DrawProperty("", aspect, false, GUILayout.MinWidth(20f));
}
GUILayout.EndHorizontal(); // 编辑多个对象或者对象存在 碰撞盒的时候显示
if (so.isEditingMultipleObjects || w.hasBoxCollider)
{
GUILayout.BeginHorizontal();
{
NGUIEditorTools.DrawProperty("Collider", so, "autoResizeBoxCollider", GUILayout.Width(100f));
GUILayout.Label("auto-adjust to match");
}
GUILayout.EndHorizontal();
}
NGUIEditorTools.EndContents();
}
} /// <summary>
/// Draw widget's dimensions.
/// </summary> static void DrawDimensions (SerializedObject so, UIWidget w, bool isPrefab)
{
GUILayout.BeginHorizontal();
{
bool freezeSize = so.isEditingMultipleObjects; UILabel lbl = w as UILabel; if (!freezeSize && lbl) freezeSize = (lbl.overflowMethod == UILabel.Overflow.ResizeFreely); if (freezeSize)
{
EditorGUI.BeginDisabledGroup(true);
NGUIEditorTools.DrawProperty("Size", so, "mWidth", GUILayout.MinWidth(100f));
EditorGUI.EndDisabledGroup();
}
else
{
GUI.changed = false;
int val = EditorGUILayout.IntField("Size", w.width, GUILayout.MinWidth(100f)); if (GUI.changed)
{
NGUIEditorTools.RegisterUndo("Dimensions Change", w);
w.width = val;
}
} if (!freezeSize && lbl)
{
UILabel.Overflow ov = lbl.overflowMethod;
freezeSize = (ov == UILabel.Overflow.ResizeFreely || ov == UILabel.Overflow.ResizeHeight);
} NGUIEditorTools.SetLabelWidth(12f); if (freezeSize)
{
EditorGUI.BeginDisabledGroup(true);
NGUIEditorTools.DrawProperty("x", so, "mHeight", GUILayout.MinWidth(30f));
EditorGUI.EndDisabledGroup();
}
else
{
GUI.changed = false;
int val = EditorGUILayout.IntField("x", w.height, GUILayout.MinWidth(30f)); if (GUI.changed)
{
NGUIEditorTools.RegisterUndo("Dimensions Change", w);
w.height = val;
}
} NGUIEditorTools.SetLabelWidth(80f); if (isPrefab)
{
GUILayout.Space(70f);
}
else
{
EditorGUI.BeginDisabledGroup(so.isEditingMultipleObjects); if (GUILayout.Button("Snap", GUILayout.Width(60f)))
{
foreach (GameObject go in Selection.gameObjects)
{
UIWidget pw = go.GetComponent<UIWidget>(); if (pw != null)
{
NGUIEditorTools.RegisterUndo("Snap Dimensions", pw);
NGUIEditorTools.RegisterUndo("Snap Dimensions", pw.transform);
pw.MakePixelPerfect();
}
}
}
EditorGUI.EndDisabledGroup();
}
}
GUILayout.EndHorizontal();
} /// <summary>
/// Draw widget's depth.
/// </summary> static void DrawDepth (SerializedObject so, UIWidget w, bool isPrefab)
{
if (isPrefab) return; GUILayout.Space(2f);
GUILayout.BeginHorizontal();
{
EditorGUILayout.PrefixLabel("Depth"); if (GUILayout.Button("Back", GUILayout.MinWidth(46f)))
{
foreach (GameObject go in Selection.gameObjects)
{
UIWidget pw = go.GetComponent<UIWidget>();
if (pw != null) pw.depth = w.depth - 1;
}
} NGUIEditorTools.DrawProperty("", so, "mDepth", GUILayout.MinWidth(20f)); if (GUILayout.Button("Forward", GUILayout.MinWidth(60f)))
{
foreach (GameObject go in Selection.gameObjects)
{
UIWidget pw = go.GetComponent<UIWidget>();
if (pw != null) pw.depth = w.depth + 1;
}
}
}
GUILayout.EndHorizontal(); int matchingDepths = 1; UIPanel p = w.panel; if (p != null)
{
for (int i = 0, imax = p.widgets.Count; i < imax; ++i)
{
UIWidget pw = p.widgets[i];
if (pw != w && pw.depth == w.depth)
++matchingDepths;
}
} if (matchingDepths > 1)
{
EditorGUILayout.HelpBox(matchingDepths + " widgets are sharing the depth value of " + w.depth, MessageType.Info);
}
} /// <summary>
/// Draw the widget's pivot.
/// </summary> static void DrawPivot (SerializedObject so, UIWidget w)
{
SerializedProperty pv = so.FindProperty("mPivot"); if (pv.hasMultipleDifferentValues)
{
// TODO: Doing this doesn't keep the widget's position where it was. Another approach is needed.
NGUIEditorTools.DrawProperty("Pivot", so, "mPivot");
}
else
{
// Pivot point -- the new, more visual style
GUILayout.BeginHorizontal();
GUILayout.Label("Pivot", GUILayout.Width(NGUISettings.minimalisticLook ? 66f : 76f));
Toggle(w, "\u25C4", "ButtonLeft", UIWidget.Pivot.Left, true);
Toggle(w, "\u25AC", "ButtonMid", UIWidget.Pivot.Center, true);
Toggle(w, "\u25BA", "ButtonRight", UIWidget.Pivot.Right, true);
Toggle(w, "\u25B2", "ButtonLeft", UIWidget.Pivot.Top, false);
Toggle(w, "\u258C", "ButtonMid", UIWidget.Pivot.Center, false);
Toggle(w, "\u25BC", "ButtonRight", UIWidget.Pivot.Bottom, false); GUILayout.EndHorizontal();
pv.enumValueIndex = (int)w.pivot;
}
} /// <summary>
/// Draw a toggle button for the pivot point.
/// </summary> static void Toggle (UIWidget w, string text, string style, UIWidget.Pivot pivot, bool isHorizontal)
{
bool isActive = false; switch (pivot)
{
case UIWidget.Pivot.Left:
isActive = IsLeft(w.pivot);
break; case UIWidget.Pivot.Right:
isActive = IsRight(w.pivot);
break; case UIWidget.Pivot.Top:
isActive = IsTop(w.pivot);
break; case UIWidget.Pivot.Bottom:
isActive = IsBottom(w.pivot);
break; case UIWidget.Pivot.Center:
isActive = isHorizontal ? pivot == GetHorizontal(w.pivot) : pivot == GetVertical(w.pivot);
break;
} if (GUILayout.Toggle(isActive, text, style) != isActive)
SetPivot(w, pivot, isHorizontal);
} static bool IsLeft (UIWidget.Pivot pivot)
{
return pivot == UIWidget.Pivot.Left ||
pivot == UIWidget.Pivot.TopLeft ||
pivot == UIWidget.Pivot.BottomLeft;
} static bool IsRight (UIWidget.Pivot pivot)
{
return pivot == UIWidget.Pivot.Right ||
pivot == UIWidget.Pivot.TopRight ||
pivot == UIWidget.Pivot.BottomRight;
} static bool IsTop (UIWidget.Pivot pivot)
{
return pivot == UIWidget.Pivot.Top ||
pivot == UIWidget.Pivot.TopLeft ||
pivot == UIWidget.Pivot.TopRight;
} static bool IsBottom (UIWidget.Pivot pivot)
{
return pivot == UIWidget.Pivot.Bottom ||
pivot == UIWidget.Pivot.BottomLeft ||
pivot == UIWidget.Pivot.BottomRight;
} static UIWidget.Pivot GetHorizontal (UIWidget.Pivot pivot)
{
if (IsLeft(pivot)) return UIWidget.Pivot.Left;
if (IsRight(pivot)) return UIWidget.Pivot.Right;
return UIWidget.Pivot.Center;
} static UIWidget.Pivot GetVertical (UIWidget.Pivot pivot)
{
if (IsTop(pivot)) return UIWidget.Pivot.Top;
if (IsBottom(pivot)) return UIWidget.Pivot.Bottom;
return UIWidget.Pivot.Center;
} static UIWidget.Pivot Combine (UIWidget.Pivot horizontal, UIWidget.Pivot vertical)
{
if (horizontal == UIWidget.Pivot.Left)
{
if (vertical == UIWidget.Pivot.Top) return UIWidget.Pivot.TopLeft;
if (vertical == UIWidget.Pivot.Bottom) return UIWidget.Pivot.BottomLeft;
return UIWidget.Pivot.Left;
} if (horizontal == UIWidget.Pivot.Right)
{
if (vertical == UIWidget.Pivot.Top) return UIWidget.Pivot.TopRight;
if (vertical == UIWidget.Pivot.Bottom) return UIWidget.Pivot.BottomRight;
return UIWidget.Pivot.Right;
}
return vertical;
} static void SetPivot (UIWidget w, UIWidget.Pivot pivot, bool isHorizontal)
{
UIWidget.Pivot horizontal = GetHorizontal(w.pivot);
UIWidget.Pivot vertical = GetVertical(w.pivot); pivot = isHorizontal ? Combine(pivot, vertical) : Combine(horizontal, pivot); if (w.pivot != pivot)
{
NGUIEditorTools.RegisterUndo("Pivot change", w);
w.pivot = pivot;
}
} protected override void OnDrawFinalProperties ()
{
if (mAnchorType == AnchorType.Advanced || !mWidget.isAnchored) return; SerializedProperty sp = serializedObject.FindProperty("leftAnchor.target"); if (!IsRect(sp))
{
GUILayout.Space(3f);
GUILayout.BeginHorizontal();
GUILayout.Space(6f);
NGUIEditorTools.DrawProperty("", serializedObject, "hideIfOffScreen", GUILayout.Width(18f));
GUILayout.Label("Hide if off-screen", GUILayout.MinWidth(20f));
GUILayout.EndHorizontal();
}
}
}

NGUI 源码分析- UIWidgetInspector的更多相关文章

  1. NGUI 源码分析- AnchorPoint

    AnchorPoint 是 UIRect 的一个内部类,此处规定作为基准的那个对象称为锚点对象,基准对象对应的矩形框称为目标框,当前对象对应的矩形框称为源框. public class AnchorP ...

  2. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  3. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  4. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  5. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  6. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  7. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  8. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  9. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

随机推荐

  1. 安装Linux基本工具

    yum install wget httpd-tools vim lrzsz Linux安装wget:yum -y install wget Linux安装vim编辑器:yum -y install ...

  2. LESSON 3- Discrete Memory-less Sources

    1.     Entropy H[X] - bounds on Lmin 2.      Huffman’s algorithm for optimal source code

  3. 渗透测试初学者的靶场实战 1--墨者学院SQL注入—布尔盲注

    前言 大家好,我是一个渗透测试的爱好者和初学者,从事网络安全相关工作,由于爱好网上和朋友处找了好多关于渗透的视频.工具等资料,然后自己找了一个靶场,想把自己练习的体会和过程分享出来,希望能对其他渗透爱 ...

  4. centos7安装fail2ban

    fail2ban是一款非常实用的安全软件,通过监视系统日志,设置错误登陆次数,可阻挡暴力密码攻击. 1.安装epelyum install epel-release -y 2.安装fail2banyu ...

  5. 修改PHP上传文件大小限制

    1. 在php.ini中,做如下修改: file_uploads = on upload_tmp_dir = /home/upload upload_max_filesize = 4000M post ...

  6. css重置样式

    <style> html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, ...

  7. 性能达到原生 MySQL 七倍,华为云 Taurus 技术解读【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...

  8. 带着canvas去流浪系列之四 绘制散点图

    [摘要] 用原生canvasAPI实现百度Echarts图表 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvasAPI ...

  9. kubernetes学习笔记(一)——minikube安装记录

    想学习一下kubernetes,于是先安装一个单机版来学习一下.但是就是这个最简单的单机版安装方式都倒腾了我好久,记录下自己的安装过程.博主是在windows利用vmware workstation安 ...

  10. Prometheus笔记(二)监控go项目实时给grafana展示

    欢迎加入go语言学习交流群 636728449 Prometheus笔记(二)监控go项目实时给grafana展示 Prometheus笔记(一)metric type 文章目录 一.promethe ...