NGUI 源碼分析- UIWidgetInspector

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();
        }
    }
}
相關文章
相關標籤/搜索