Example #1
0
    /// <summary>
    /// Draws the main log panel
    /// </summary>
    public void DrawLogList(float height)
    {
        var oldColor = GUI.backgroundColor;


        float buttonY = 0;

        System.Text.RegularExpressions.Regex filterRegex = null;

        if (!String.IsNullOrEmpty(FilterRegex))
        {
            filterRegex = new Regex(FilterRegex);
        }

        var collapseBadgeStyle = EditorStyles.miniButton;
        var logLineStyle       = EntryStyleBackEven;

        // If we've been marked dirty, we need to recalculate the elements to be displayed
        if (Dirty)
        {
            LogListMaxWidth       = 0;
            LogListLineHeight     = 0;
            CollapseBadgeMaxWidth = 0;
            RenderLogs.Clear();

            //When collapsed, count up the unique elements and use those to display
            if (Collapse)
            {
                var collapsedLines     = new Dictionary <string, CountedLog>();
                var collapsedLinesList = new List <CountedLog>();

                foreach (var log in CurrentLogList)
                {
                    if (ShouldShowLog(filterRegex, log))
                    {
                        var matchString = log.Message + "!$" + log.Severity + "!$" + log.Channel;

                        CountedLog countedLog;
                        if (collapsedLines.TryGetValue(matchString, out countedLog))
                        {
                            countedLog.Count++;
                        }
                        else
                        {
                            countedLog = new CountedLog(log, 1);
                            collapsedLines.Add(matchString, countedLog);
                            collapsedLinesList.Add(countedLog);
                        }
                    }
                }

                foreach (var countedLog in collapsedLinesList)
                {
                    var content = GetLogLineGUIContent(countedLog.Log, ShowTimes);
                    RenderLogs.Add(countedLog);
                    var logLineSize = logLineStyle.CalcSize(content);
                    LogListMaxWidth   = Mathf.Max(LogListMaxWidth, logLineSize.x);
                    LogListLineHeight = Mathf.Max(LogListLineHeight, logLineSize.y);

                    var collapseBadgeContent = new GUIContent(countedLog.Count.ToString());
                    var collapseBadgeSize    = collapseBadgeStyle.CalcSize(collapseBadgeContent);
                    CollapseBadgeMaxWidth = Mathf.Max(CollapseBadgeMaxWidth, collapseBadgeSize.x);
                }
            }
            //If we're not collapsed, display everything in order
            else
            {
                foreach (var log in CurrentLogList)
                {
                    if (ShouldShowLog(filterRegex, log))
                    {
                        var content = GetLogLineGUIContent(log, ShowTimes);
                        RenderLogs.Add(new CountedLog(log, 1));
                        var logLineSize = logLineStyle.CalcSize(content);
                        LogListMaxWidth   = Mathf.Max(LogListMaxWidth, logLineSize.x);
                        LogListLineHeight = Mathf.Max(LogListLineHeight, logLineSize.y);
                    }
                }
            }

            LogListMaxWidth += CollapseBadgeMaxWidth;
        }

        var   scrollRect = new Rect(DrawPos, new Vector2(position.width, height));
        float lineWidth  = Mathf.Max(LogListMaxWidth, scrollRect.width);

        var     contentRect        = new Rect(0, 0, lineWidth, RenderLogs.Count * LogListLineHeight);
        Vector2 lastScrollPosition = LogListScrollPosition;

        LogListScrollPosition = GUI.BeginScrollView(scrollRect, LogListScrollPosition, contentRect);

        //If we're following the messages but the user has moved, cancel following
        if (ScrollFollowMessages)
        {
            if (lastScrollPosition.y - LogListScrollPosition.y > LogListLineHeight)
            {
                UberDebug.UnityLog(String.Format("{0} {1}", lastScrollPosition.y, LogListScrollPosition.y));
                ScrollFollowMessages = false;
            }
        }

        float logLineX = CollapseBadgeMaxWidth;

        //Render all the elements
        int firstRenderLogIndex = (int)(LogListScrollPosition.y / LogListLineHeight);
        int lastRenderLogIndex  = firstRenderLogIndex + (int)(height / LogListLineHeight);

        firstRenderLogIndex = Mathf.Clamp(firstRenderLogIndex, 0, RenderLogs.Count);
        lastRenderLogIndex  = Mathf.Clamp(lastRenderLogIndex, 0, RenderLogs.Count);
        buttonY             = firstRenderLogIndex * LogListLineHeight;

        for (int renderLogIndex = firstRenderLogIndex; renderLogIndex < lastRenderLogIndex; renderLogIndex++)
        {
            var countedLog = RenderLogs[renderLogIndex];
            var log        = countedLog.Log;
            logLineStyle = (renderLogIndex % 2 == 0) ? EntryStyleBackEven : EntryStyleBackOdd;
            if (renderLogIndex == SelectedRenderLog)
            {
                GUI.backgroundColor = new Color(0.5f, 0.5f, 1);
            }
            else
            {
                GUI.backgroundColor = Color.white;
            }

            //Make all messages single line
            var content  = GetLogLineGUIContent(log, ShowTimes);
            var drawRect = new Rect(logLineX, buttonY, contentRect.width, LogListLineHeight);
            if (GUI.Button(drawRect, content, logLineStyle))
            {
                //Select a message, or jump to source if it's double-clicked
                if (renderLogIndex == SelectedRenderLog)
                {
                    if (EditorApplication.timeSinceStartup - LastMessageClickTime < DoubleClickInterval)
                    {
                        LastMessageClickTime = 0;
                        // Attempt to display source code associated with messages. Search through all stackframes,
                        //   until we find a stackframe that can be displayed in source code view
                        for (int frame = 0; frame < log.Callstack.Count; frame++)
                        {
                            if (JumpToSource(log.Callstack[frame]))
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        LastMessageClickTime = EditorApplication.timeSinceStartup;
                    }
                }
                else
                {
                    SelectedRenderLog      = renderLogIndex;
                    SelectedCallstackFrame = -1;
                    LastMessageClickTime   = EditorApplication.timeSinceStartup;
                }


                //Always select the game object that is the source of this message
                var go = log.Source as GameObject;
                if (go != null)
                {
                    Selection.activeGameObject = go;
                }
            }

            if (Collapse)
            {
                var collapseBadgeContent = new GUIContent(countedLog.Count.ToString());
                var collapseBadgeSize    = collapseBadgeStyle.CalcSize(collapseBadgeContent);
                var collapseBadgeRect    = new Rect(0, buttonY, collapseBadgeSize.x, collapseBadgeSize.y);
                GUI.Button(collapseBadgeRect, collapseBadgeContent, collapseBadgeStyle);
            }
            buttonY += LogListLineHeight;
        }

        //If we're following the log, move to the end
        if (ScrollFollowMessages && RenderLogs.Count > 0)
        {
            LogListScrollPosition.y = ((RenderLogs.Count + 1) * LogListLineHeight) - scrollRect.height;
        }

        GUI.EndScrollView();
        DrawPos.y          += height;
        DrawPos.x           = 0;
        GUI.backgroundColor = oldColor;
    }
    /// <summary>
    /// Draws the main log panel
    /// </summary>
    public void DrawLogList(float height)
    {
        var oldColor = GUI.backgroundColor;

        float buttonY = 0;

        System.Text.RegularExpressions.Regex filterRegex = null;

        if(!String.IsNullOrEmpty(FilterRegex))
        {
            filterRegex = new Regex(FilterRegex);
        }

        var collapseBadgeStyle = EditorStyles.miniButton;
        var logLineStyle = EntryStyleBackEven;

        // If we've been marked dirty, we need to recalculate the elements to be displayed
        if(Dirty)
        {
            LogListMaxWidth = 0;
            LogListLineHeight = 0;
            CollapseBadgeMaxWidth = 0;
            RenderLogs.Clear();

            //When collapsed, count up the unique elements and use those to display
            if(Collapse)
            {
                var collapsedLines = new Dictionary<string, CountedLog>();
                var collapsedLinesList = new List<CountedLog>();

                foreach(var log in EditorLogger.LogInfo)
                {
                    if(ShouldShowLog(filterRegex, log))
                    {
                        var matchString = log.Message + "!$" + log.Severity + "!$" + log.Channel;

                        CountedLog countedLog;
                        if(collapsedLines.TryGetValue(matchString, out countedLog))
                        {
                            countedLog.Count++;
                        }
                        else
                        {
                            countedLog = new CountedLog(log, 1);
                            collapsedLines.Add(matchString, countedLog);
                            collapsedLinesList.Add(countedLog);
                        }
                    }
                }

                foreach(var countedLog in collapsedLinesList)
                {
                    var content = GetLogLineGUIContent(countedLog.Log, ShowTimes);
                    RenderLogs.Add(countedLog);
                    var logLineSize = logLineStyle.CalcSize(content);
                    LogListMaxWidth = Mathf.Max(LogListMaxWidth, logLineSize.x);
                    LogListLineHeight = Mathf.Max(LogListLineHeight, logLineSize.y);

                    var collapseBadgeContent = new GUIContent(countedLog.Count.ToString());
                    var collapseBadgeSize = collapseBadgeStyle.CalcSize(collapseBadgeContent);
                    CollapseBadgeMaxWidth = Mathf.Max(CollapseBadgeMaxWidth, collapseBadgeSize.x);
                }
            }
            //If we're not collapsed, display everything in order
            else
            {
                foreach(var log in EditorLogger.LogInfo)
                {
                    if(ShouldShowLog(filterRegex, log))
                    {
                        var content = GetLogLineGUIContent(log, ShowTimes);
                        RenderLogs.Add(new CountedLog(log, 1));
                        var logLineSize = logLineStyle.CalcSize(content);
                        LogListMaxWidth = Mathf.Max(LogListMaxWidth, logLineSize.x);
                        LogListLineHeight = Mathf.Max(LogListLineHeight, logLineSize.y);
                    }
                }
            }

            LogListMaxWidth += CollapseBadgeMaxWidth;
        }

        var scrollRect = new Rect(DrawPos, new Vector2(position.width, height));
        float lineWidth = Mathf.Max(LogListMaxWidth, scrollRect.width);

        var contentRect = new Rect(0, 0, lineWidth, RenderLogs.Count*LogListLineHeight);
        Vector2 lastScrollPosition = LogListScrollPosition;
        LogListScrollPosition = GUI.BeginScrollView(scrollRect, LogListScrollPosition, contentRect);

        //If we're following the messages but the user has moved, cancel following
        if(ScrollFollowMessages)
        {
            if(lastScrollPosition.y - LogListScrollPosition.y > LogListLineHeight)
            {
                UberDebug.UnityLog(String.Format("{0} {1}", lastScrollPosition.y, LogListScrollPosition.y));
                ScrollFollowMessages = false;
            }
        }

        float logLineX = CollapseBadgeMaxWidth;

        //Render all the elements
        int firstRenderLogIndex = (int) (LogListScrollPosition.y/LogListLineHeight);
        int lastRenderLogIndex = firstRenderLogIndex + (int) (height/LogListLineHeight);

        firstRenderLogIndex = Mathf.Clamp(firstRenderLogIndex, 0, RenderLogs.Count);
        lastRenderLogIndex = Mathf.Clamp(lastRenderLogIndex, 0, RenderLogs.Count);
        buttonY = firstRenderLogIndex*LogListLineHeight;

        for(int renderLogIndex=firstRenderLogIndex; renderLogIndex<lastRenderLogIndex; renderLogIndex++)
        {
            var countedLog = RenderLogs[renderLogIndex];
            var log = countedLog.Log;
            logLineStyle = (renderLogIndex%2==0) ? EntryStyleBackEven : EntryStyleBackOdd;
            if(renderLogIndex==SelectedRenderLog)
            {
                GUI.backgroundColor = new Color(0.5f, 0.5f, 1);
            }
            else
            {
                GUI.backgroundColor = Color.white;
            }

            //Make all messages single line
            var content = GetLogLineGUIContent(log, ShowTimes);
            var drawRect = new Rect(logLineX, buttonY, contentRect.width, LogListLineHeight);
            if(GUI.Button(drawRect, content, logLineStyle))
            {
                //Select a message, or jump to source if it's double-clicked
                if(renderLogIndex==SelectedRenderLog)
                {
                    if(EditorApplication.timeSinceStartup-LastMessageClickTime<0.3f)
                    {
                        LastMessageClickTime = 0;
                        if(log.Callstack.Count>0)
                        {
                            JumpToSource(log.Callstack[0]);
                        }
                    }
                    else
                    {
                        LastMessageClickTime = EditorApplication.timeSinceStartup;
                    }
                }
                else
                {
                    SelectedRenderLog = renderLogIndex;
                    SelectedCallstackFrame = -1;
                }

                //Always select the game object that is the source of this message
                var go = log.Source as GameObject;
                if(go!=null)
                {
                    Selection.activeGameObject = go;
                }
            }

            if(Collapse)
            {
                var collapseBadgeContent = new GUIContent(countedLog.Count.ToString());
                var collapseBadgeSize = collapseBadgeStyle.CalcSize(collapseBadgeContent);
                var collapseBadgeRect = new Rect(0, buttonY, collapseBadgeSize.x, collapseBadgeSize.y);
                GUI.Button(collapseBadgeRect, collapseBadgeContent, collapseBadgeStyle);
            }
            buttonY += LogListLineHeight;
        }

        //If we're following the log, move to the end
        if(ScrollFollowMessages && RenderLogs.Count>0)
        {
            LogListScrollPosition.y = ((RenderLogs.Count+1)*LogListLineHeight)-scrollRect.height;
        }

        GUI.EndScrollView();
        DrawPos.y += height;
        DrawPos.x = 0;
        GUI.backgroundColor = oldColor;
    }
Example #3
0
        public void DrawLogList(float height)
        {
            var oldColor = GUI.backgroundColor;


            float buttonY = 0;

            System.Text.RegularExpressions.Regex filterRegex = null;

            //if(!String.IsNullOrEmpty(FilterRegex)) {
            //	filterRegex = new Regex(FilterRegex);
            //}

            var collapseBadgeStyle = EditorStyles.miniButton;
            var logLineStyle       = EntryStyleBackEven;

            // If we've been marked dirty, we need to recalculate the elements to be displayed
            if (Dirty)
            {
                LogListMaxWidth       = 0;
                LogListLineHeight     = 0;
                CollapseBadgeMaxWidth = 0;
                RenderLogs.Clear();

                //When collapsed, count up the unique elements and use those to display
                if (Collapse)
                {
                    var collapsedLines     = new Dictionary <string, CountedLog>();
                    var collapsedLinesList = new List <CountedLog>();

                    foreach (var log in CurrentLogList)
                    {
                        if (ShouldShowLog(filterRegex, log))
                        {
                            var matchString = log.Message + "!$" + log.Severity + "!$" + log.Channel;

                            CountedLog countedLog;
                            if (collapsedLines.TryGetValue(matchString, out countedLog))
                            {
                                countedLog.Count++;
                            }
                            else
                            {
                                countedLog = new CountedLog(log, 1);
                                collapsedLines.Add(matchString, countedLog);
                                collapsedLinesList.Add(countedLog);
                            }
                        }
                    }

                    foreach (var countedLog in collapsedLinesList)
                    {
                        var content = GetLogLineGUIContent(countedLog.Log, ShowTimes, ShowChannels);
                        RenderLogs.Add(countedLog);
                        var logLineSize = logLineStyle.CalcSize(content);
                        LogListMaxWidth   = Mathf.Max(LogListMaxWidth, logLineSize.x);
                        LogListLineHeight = Mathf.Max(LogListLineHeight, logLineSize.y);

                        var collapseBadgeContent = new GUIContent(countedLog.Count.ToString());
                        var collapseBadgeSize    = collapseBadgeStyle.CalcSize(collapseBadgeContent);
                        CollapseBadgeMaxWidth = Mathf.Max(CollapseBadgeMaxWidth, collapseBadgeSize.x);
                    }
                }
                //If we're not collapsed, display everything in order
                else
                {
                    foreach (var log in CurrentLogList)
                    {
                        if (ShouldShowLog(filterRegex, log))
                        {
                            var content = GetLogLineGUIContent(log, ShowTimes, ShowChannels);
                            RenderLogs.Add(new CountedLog(log, 1));
                            var logLineSize = logLineStyle.CalcSize(content);
                            LogListMaxWidth   = Mathf.Max(LogListMaxWidth, logLineSize.x);
                            LogListLineHeight = Mathf.Max(LogListLineHeight, logLineSize.y);
                        }
                    }
                }

                LogListMaxWidth += CollapseBadgeMaxWidth;
            }

            var   scrollRect = new Rect(DrawPos, new Vector2(position.width, height));
            float lineWidth  = Mathf.Max(LogListMaxWidth, scrollRect.width);

            var contentRect = new Rect(0, 0, lineWidth, RenderLogs.Count * LogListLineHeight);

            LogListScrollPosition = GUI.BeginScrollView(scrollRect, LogListScrollPosition, contentRect);

            float logLineX = CollapseBadgeMaxWidth;

            //Render all the elements
            int firstRenderLogIndex = (int)(LogListScrollPosition.y / LogListLineHeight);
            int lastRenderLogIndex  = firstRenderLogIndex + (int)(height / LogListLineHeight);

            firstRenderLogIndex = Mathf.Clamp(firstRenderLogIndex, 0, RenderLogs.Count);
            lastRenderLogIndex  = Mathf.Clamp(lastRenderLogIndex, 0, RenderLogs.Count);
            buttonY             = firstRenderLogIndex * LogListLineHeight;

            for (int renderLogIndex = firstRenderLogIndex; renderLogIndex < lastRenderLogIndex; renderLogIndex++)
            {
                var countedLog = RenderLogs[renderLogIndex];
                var log        = countedLog.Log;
                logLineStyle = (renderLogIndex % 2 == 0) ? EntryStyleBackEven : EntryStyleBackOdd;
                if (renderLogIndex == SelectedRenderLog)
                {
                    GUI.backgroundColor = new Color(0.5f, 0.5f, 1);
                }
                else
                {
                    GUI.backgroundColor = Color.white;
                }

                //Make all messages single line
                var content  = GetLogLineGUIContent(log, ShowTimes, ShowChannels);
                var drawRect = new Rect(logLineX, buttonY, contentRect.width, LogListLineHeight);
                if (contextMenu != null && (Event.current.type == EventType.Layout || Event.current.type == EventType.Repaint))
                {
                    contextMenu.ShowAsContext();
                    contextMenu = null;
                }
                if (GUI.Button(drawRect, content, logLineStyle))
                {
                    if (Event.current.button == 1)
                    {
                        contextMenu = new GenericMenu();
                        if (log.Source != null)
                        {
                            if (log.Source as NodeComponent)
                            {
                                contextMenu.AddItem(new GUIContent("HighlightNode"), false, () => {
                                    uNodeEditor.HighlightNode(log.Source as NodeComponent);
                                });
                            }
                            if (log.Source as NodeComponent || log.Source as RootObject)
                            {
                                contextMenu.AddItem(new GUIContent("Open uNode"), false, () => {
                                    uNodeEditor.ChangeMainSelection(log.Source as Component, true);
                                });
                                contextMenu.AddSeparator("");
                            }
                            contextMenu.AddItem(new GUIContent("Find Object"), false, () => {
                                if (log.Source as NodeComponent)
                                {
                                    var owner = (log.Source as NodeComponent).owner;
                                    if (owner != null)
                                    {
                                        EditorGUIUtility.PingObject(owner);
                                    }
                                }
                                else if (log.Source as RootObject)
                                {
                                    var owner = (log.Source as RootObject).owner;
                                    if (owner != null)
                                    {
                                        EditorGUIUtility.PingObject(owner);
                                    }
                                }
                                else
                                {
                                    EditorGUIUtility.PingObject(log.Source);
                                }
                            });
                            contextMenu.AddItem(new GUIContent("Select Object"), false, () => {
                                if (log.Source as NodeComponent)
                                {
                                    var owner = (log.Source as NodeComponent).owner;
                                    if (owner != null)
                                    {
                                        EditorGUIUtility.PingObject(owner);
                                        Selection.activeObject = owner;
                                    }
                                }
                                else if (log.Source as RootObject)
                                {
                                    var owner = (log.Source as RootObject).owner;
                                    if (owner != null)
                                    {
                                        EditorGUIUtility.PingObject(owner);
                                        Selection.activeObject = owner;
                                    }
                                }
                                else
                                {
                                    EditorGUIUtility.PingObject(log.Source);
                                    Selection.activeObject = log.Source;
                                }
                            });
                            contextMenu.AddSeparator("");
                        }
                        contextMenu.AddItem(new GUIContent("JumpToSource"), false, () => {
                            // Attempt to display source code associated with messages. Search through all stackframes,
                            //   until we find a stackframe that can be displayed in source code view
                            for (int frame = 0; frame < log.Callstack.Count; frame++)
                            {
                                if (JumpToSource(log.Callstack[frame]))
                                {
                                    break;
                                }
                            }
                        });
                        SelectedRenderLog      = renderLogIndex;
                        SelectedCallstackFrame = -1;
                        LastMessageClickTime   = EditorApplication.timeSinceStartup;
                        Repaint();
                    }
                    else
                    {
                        //Select a message, or jump to source if it's double-clicked
                        if (renderLogIndex == SelectedRenderLog)
                        {
                            if (EditorApplication.timeSinceStartup - LastMessageClickTime < DoubleClickInterval)
                            {
                                LastMessageClickTime = 0;
                                if (log.Source as NodeComponent)
                                {
                                    uNodeEditor.HighlightNode(log.Source as NodeComponent);
                                }
                                else if (log.Source as RootObject)
                                {
                                    uNodeEditor.ChangeMainSelection(log.Source as RootObject, true);
                                }
                                else
                                {
                                    // Attempt to display source code associated with messages. Search through all stackframes,
                                    //   until we find a stackframe that can be displayed in source code view
                                    for (int frame = 0; frame < log.Callstack.Count; frame++)
                                    {
                                        if (JumpToSource(log.Callstack[frame]))
                                        {
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                LastMessageClickTime = EditorApplication.timeSinceStartup;
                            }
                        }
                        else
                        {
                            SelectedRenderLog      = renderLogIndex;
                            SelectedCallstackFrame = -1;
                            LastMessageClickTime   = EditorApplication.timeSinceStartup;
                        }

                        //Always select the game object that is the source of this message
                        if (log.Source != null)
                        {
                            if (log.Source as NodeComponent)
                            {
                                var owner = (log.Source as NodeComponent).owner;
                                if (owner != null)
                                {
                                    EditorGUIUtility.PingObject(owner);
                                }
                            }
                            else if (log.Source as RootObject)
                            {
                                var owner = (log.Source as RootObject).owner;
                                if (owner != null)
                                {
                                    EditorGUIUtility.PingObject(owner);
                                }
                            }
                            else
                            {
                                EditorGUIUtility.PingObject(log.Source);
                                //Selection.activeObject = log.Source;
                            }
                        }
                    }
                }

                if (Collapse)
                {
                    var collapseBadgeContent = new GUIContent(countedLog.Count.ToString());
                    var collapseBadgeSize    = collapseBadgeStyle.CalcSize(collapseBadgeContent);
                    var collapseBadgeRect    = new Rect(0, buttonY, collapseBadgeSize.x, collapseBadgeSize.y);
                    GUI.Button(collapseBadgeRect, collapseBadgeContent, collapseBadgeStyle);
                }
                buttonY += LogListLineHeight;
            }

            //If we're following the log, move to the end
            if (ScrollFollowMessages && RenderLogs.Count > 0)
            {
                LogListScrollPosition.y = ((RenderLogs.Count + 1) * LogListLineHeight) - scrollRect.height;
            }

            GUI.EndScrollView();
            DrawPos.y          += height;
            DrawPos.x           = 0;
            GUI.backgroundColor = oldColor;
        }
    /// <summary>
    /// Draws the main log panel
    /// </summary>
    public void DrawLogList(float height)
    {
        var oldColor = GUI.backgroundColor;

        GUI.SetNextControlName(LogListControlName);

        float buttonY = 0;

        System.Text.RegularExpressions.Regex filterRegex = null;

        if (!String.IsNullOrEmpty(FilterRegex))
        {
            filterRegex = new Regex(FilterRegex);
        }

        var collapseBadgeStyle = EditorStyles.miniButton;
        var logLineStyle       = EntryStyleBackEven;

        // If we've been marked dirty, we need to recalculate the elements to be displayed
        if (Dirty)
        {
            if (FilterChanged)
            {
                CollapseBadgeMaxWidth = 0;
                MaxCollapseCount      = 0;
                RenderLogs.Clear();
                NextIndexToAdd = 0;

                CollapsedLines.Clear();
                CollapsedLinesList.Clear();
            }

            //When collapsed, count up the unique elements and use those to display
            if (Collapse)
            {
                for (var i = NextIndexToAdd; i < CurrentLogList.Count; i++)
                {
                    var log = CurrentLogList[i];
                    if (ShouldShowLog(filterRegex, log))
                    {
                        var matchString = log.Message + "!$" + log.Severity + "!$" + log.Channel;

                        CountedLog countedLog;
                        if (CollapsedLines.TryGetValue(matchString, out countedLog))
                        {
                            countedLog.Count++;
                        }
                        else
                        {
                            countedLog = new CountedLog(log, 1);
                            CollapsedLines.Add(matchString, countedLog);
                            CollapsedLinesList.Add(countedLog);
                            RenderLogs.Add(countedLog);
                        }

                        if (MaxCollapseCount < countedLog.Count)
                        {
                            MaxCollapseCount = countedLog.Count;
                        }
                    }
                }

                var collapseBadgeContent = new GUIContent(MaxCollapseCount.ToString());
                var collapseBadgeSize    = collapseBadgeStyle.CalcSize(collapseBadgeContent);
                CollapseBadgeMaxWidth = Mathf.Max(CollapseBadgeMaxWidth, collapseBadgeSize.x);
            }
            //If we're not collapsed, display everything in order
            else
            {
                for (var i = NextIndexToAdd; i < CurrentLogList.Count; i++)
                {
                    var log = CurrentLogList[i];
                    if (ShouldShowLog(filterRegex, log))
                    {
                        RenderLogs.Add(new CountedLog(log, 1));
                    }
                }
            }
        }

        var scrollRect = new Rect(DrawPos, new Vector2(position.width, height));

        var contentRect = new Rect(0, 0, scrollRect.width, RenderLogs.Count * LogListLineHeight);
        var viewRect    = contentRect;

        viewRect.width -= 50;
        Vector2 lastScrollPosition = LogListScrollPosition;

        LogListScrollPosition = GUI.BeginScrollView(scrollRect, LogListScrollPosition, viewRect, GUIStyle.none, GUI.skin.verticalScrollbar);

        //If we're following the messages but the user has moved, cancel following
        if (ScrollFollowMessages)
        {
            if (lastScrollPosition.y - LogListScrollPosition.y > LogListLineHeight)
            {
                ScrollFollowMessages = false;
            }
        }

        EntryStyleBackEven.padding.left = (int)(CollapseBadgeMaxWidth + LogListLineHeight + 4);
        EntryStyleBackOdd.padding.left  = EntryStyleBackEven.padding.left;

        //Render all the elements
        int firstRenderLogIndex = (int)(LogListScrollPosition.y / LogListLineHeight);
        int lastRenderLogIndex  = firstRenderLogIndex + (int)(height / LogListLineHeight);

        firstRenderLogIndex = Mathf.Clamp(firstRenderLogIndex, 0, RenderLogs.Count);
        lastRenderLogIndex  = Mathf.Clamp(lastRenderLogIndex, 0, RenderLogs.Count);
        buttonY             = firstRenderLogIndex * LogListLineHeight;
        for (int renderLogIndex = firstRenderLogIndex; renderLogIndex < lastRenderLogIndex; renderLogIndex++)
        {
            var countedLog = RenderLogs[renderLogIndex];
            var log        = countedLog.Log;
            logLineStyle = (renderLogIndex % 2 == 0) ? EntryStyleBackEven : EntryStyleBackOdd;
            if (renderLogIndex == SelectedRenderLog)
            {
                GUI.backgroundColor = new Color(0.5f, 0.5f, 1);
            }
            else
            {
                GUI.backgroundColor = Color.white;
            }

            //Make all messages single line
            var content  = GetLogLineGUIContent(log, ShowTimes, ShowChannels);
            var drawRect = new Rect(0, buttonY, contentRect.width, LogListLineHeight);

            if (GUI.Button(drawRect, content, logLineStyle))
            {
                GUI.FocusControl(LogListControlName);
                //Select a message, or jump to source if it's double-clicked
                if (renderLogIndex == SelectedRenderLog)
                {
                    if (EditorApplication.timeSinceStartup - LastMessageClickTime < DoubleClickInterval)
                    {
                        LastMessageClickTime = 0;
                        // Attempt to display source code associated with messages. Search through all stackframes,
                        //   until we find a stackframe that can be displayed in source code view
                        for (int frame = 0; frame < log.Callstack.Count; frame++)
                        {
                            if (JumpToSource(log.Callstack[frame]))
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        LastMessageClickTime = EditorApplication.timeSinceStartup;
                    }
                }
                else
                {
                    SelectedRenderLog      = renderLogIndex;
                    SelectedCallstackFrame = -1;
                    LastMessageClickTime   = EditorApplication.timeSinceStartup;
                }


                //Always select the game object that is the source of this message
                var go = log.Source as GameObject;
                if (go != null)
                {
                    Selection.activeGameObject = go;
                }
            }

            var iconRect = drawRect;
            iconRect.x     = CollapseBadgeMaxWidth + 2;
            iconRect.width = LogListLineHeight;

            GUI.DrawTexture(iconRect, GetIconForLog(log), ScaleMode.ScaleAndCrop);

            if (Collapse)
            {
                GUI.backgroundColor = Color.white;
                var collapseBadgeContent = new GUIContent(countedLog.Count.ToString());
                var collapseBadgeRect    = new Rect(0, buttonY, CollapseBadgeMaxWidth, LogListLineHeight);
                GUI.Button(collapseBadgeRect, collapseBadgeContent, collapseBadgeStyle);
            }
            buttonY += LogListLineHeight;
        }

        //If we're following the log, move to the end
        if (ScrollFollowMessages && RenderLogs.Count > 0)
        {
            LogListScrollPosition.y = ((RenderLogs.Count + 1) * LogListLineHeight) - scrollRect.height;
        }

        NextIndexToAdd = CurrentLogList.Count;

        GUI.EndScrollView();
        DrawPos.y          += height;
        DrawPos.x           = 0;
        GUI.backgroundColor = oldColor;
    }