Browse Source

ConsoleWindow:
- Refresh visible elements when selection changes
- Open console when clicking on the status bar
- Properly parse compiler warnings/errors
Moved stack trace parsing methods into Debug so it is close to the stack trace generation method to more easily ensure they are kept in sync

BearishSun 10 years ago
parent
commit
b7e345e7a0

+ 2 - 2
MBansheeEditor/CodeEditor.cs

@@ -65,7 +65,7 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="path">Path to the script file to open, either absolute or relative to the project resources folder.</param>
         /// <param name="line">Line in the file to focus the editor on.</param>
-        public static void OpenFile(string path, UInt32 line)
+        public static void OpenFile(string path, int line)
         {
             if (IsSolutionDirty)
                 SyncSolution();
@@ -100,7 +100,7 @@ namespace BansheeEditor
         internal static extern CodeEditorType[] Internal_GetAvailableEditors();
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern void Internal_OpenFile(string path, UInt32 line);
+        internal static extern void Internal_OpenFile(string path, int line);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern void Internal_SyncSolution();

+ 31 - 43
MBansheeEditor/ConsoleWindow.cs

@@ -1,6 +1,7 @@
 using BansheeEngine;
 using System;
 using System.Collections.Generic;
+using System.Text;
 using System.Text.RegularExpressions;
 
 namespace BansheeEditor
@@ -56,6 +57,10 @@ namespace BansheeEditor
             // TODO - Add button that splits the window vertically and displays details about an entry + callstack
             // TODO - On entry double-click open VS at that line
             // TODO - On callstack entry double-click open VS at that line
+
+
+            for (int i = 0; i < 10; i++)
+                Debug.Log("DUMMY ENTRY #" + i);
         }
 
         private void OnEditorUpdate()
@@ -83,32 +88,15 @@ namespace BansheeEditor
         /// <param name="message">Message string.</param>
         private void OnEntryAdded(DebugMessageType type, string message)
         {
-            ConsoleEntryData newEntry = new ConsoleEntryData();
+            // Check if compiler message, otherwise parse it normally
+            LogEntryData logEntry = ScriptCodeManager.ParseCompilerMessage(message);
+            if (logEntry == null)
+                logEntry = Debug.ParseLogMessage(message);
 
+            ConsoleEntryData newEntry = new ConsoleEntryData();
             newEntry.type = type;
-
-            int firstMatchIdx = -1;
-            Regex regex = new Regex(@"\tat (.*) in (.*), line (\d*), column .*, namespace .*");
-            var matches = regex.Matches(message);
-
-            newEntry.callstack = new ConsoleEntryData.CallStackEntry[matches.Count];
-            for(int i = 0; i < matches.Count; i++)
-            {
-                ConsoleEntryData.CallStackEntry callstackEntry = new ConsoleEntryData.CallStackEntry();
-                callstackEntry.method = matches[i].Groups[1].Value;
-                callstackEntry.file = matches[i].Groups[2].Value;
-                int.TryParse(matches[i].Groups[3].Value, out callstackEntry.line);
-
-                newEntry.callstack[i] = callstackEntry;
-
-                if (firstMatchIdx == -1)
-                    firstMatchIdx = matches[i].Index;
-            }
-
-            if (firstMatchIdx != -1)
-                newEntry.message = message.Substring(0, firstMatchIdx);
-            else
-                newEntry.message = message;
+            newEntry.callstack = logEntry.callstack;
+            newEntry.message = logEntry.message;
 
             entries.Add(newEntry);
 
@@ -170,16 +158,6 @@ namespace BansheeEditor
         /// </summary>
         private class ConsoleEntryData : GUIListViewData
         {
-            /// <summary>
-            /// Contains data for a single entry in a call stack associated with a console entry.
-            /// </summary>
-            public class CallStackEntry
-            {
-                public string method;
-                public string file;
-                public int line;
-            }
-
             public DebugMessageType type;
             public string message;
             public CallStackEntry[] callstack;
@@ -190,12 +168,6 @@ namespace BansheeEditor
         /// </summary>
         private class ConsoleGUIEntry : GUIListViewEntry<ConsoleEntryData>
         {
-            // TODO - Create two separate labels for text and first callstack entry
-            // TODO - Add invisible button for overlay, toggle background selection when clicked
-            // TODO - Don't use toggle group, instead manually track which element is selected and update
-            //        selection state in UpdateContents()
-            // TODO - Remove ListView GUI states
-
             private const int CALLER_LABEL_HEIGHT = 11;
             private const int MESSAGE_HEIGHT = ENTRY_HEIGHT - CALLER_LABEL_HEIGHT;
             private static readonly Color BG_COLOR = Color.DarkGray;
@@ -210,6 +182,8 @@ namespace BansheeEditor
             private GUITexture background;
 
             private int entryIdx;
+            private string file;
+            private int line;
 
             /// <inheritdoc/>
             public override void BuildGUI()
@@ -263,7 +237,20 @@ namespace BansheeEditor
 
                 string method = "";
                 if (data.callstack != null && data.callstack.Length > 0)
-                    method = data.callstack[0].method + " at " + data.callstack[0].file + ":" + data.callstack[0].line;
+                {
+                    file = data.callstack[0].file;
+                    line = data.callstack[0].line;
+
+                    if (string.IsNullOrEmpty(data.callstack[0].method))
+                        method = "\tat " + file + ":" + line;
+                    else
+                        method = "\t" + data.callstack[0].method + " at " + file + ":" + line;
+                }
+                else
+                {
+                    file = "";
+                    line = 0;
+                }
 
                 functionLabel.SetContent(new LocEdString(method));
 
@@ -277,7 +264,7 @@ namespace BansheeEditor
             {
                 sSelectedElementIdx = entryIdx;
 
-                // TODO - Refresh all entries (especially previously selected one and this one to update their graphic)
+                RefreshEntries();
             }
 
             /// <summary>
@@ -285,7 +272,8 @@ namespace BansheeEditor
             /// </summary>
             private void OnDoubleClicked()
             {
-                // TODO - Open code editor
+                if(!string.IsNullOrEmpty(file))
+                    CodeEditor.OpenFile(file, line);
             }
         }
     }

+ 71 - 63
MBansheeEditor/EditorApplication.cs

@@ -443,69 +443,6 @@ namespace BansheeEditor
             return dirtyResources.Contains(resource.UUID);
         }
 
-        /// <summary>
-        /// Triggered when <see cref="LoadProject"/> method completes.
-        /// </summary>
-        private static void OnProjectLoaded()
-        {
-            SetStatusProject(false);
-            if (!unitTestsExecuted)
-            {
-                RunUnitTests();
-                unitTestsExecuted = true;
-            }
-
-            if (!IsProjectLoaded)
-            {
-                ProjectWindow.Open();
-                return;
-            }
-
-            string projectPath = ProjectPath;
-
-            RecentProject[] recentProjects = EditorSettings.RecentProjects;
-            bool foundPath = false;
-            for (int i = 0; i < recentProjects.Length; i++)
-            {
-                if (PathEx.Compare(recentProjects[i].path, projectPath))
-                {
-                    recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks;
-                    EditorSettings.RecentProjects = recentProjects;
-                    foundPath = true;
-                    break;
-                }
-            }
-
-            if (!foundPath)
-            {
-                List<RecentProject> extendedRecentProjects = new List<RecentProject>();
-                extendedRecentProjects.AddRange(recentProjects);
-
-                RecentProject newProject = new RecentProject();
-                newProject.path = projectPath;
-                newProject.accessTimestamp = (ulong)DateTime.Now.Ticks;
-
-                extendedRecentProjects.Add(newProject);
-
-                EditorSettings.RecentProjects = extendedRecentProjects.ToArray();
-            }
-
-            EditorSettings.LastOpenProject = projectPath;
-            EditorSettings.Save();
-
-            ProjectLibrary.Refresh();
-            monitor = new FolderMonitor(ProjectLibrary.ResourceFolder);
-            monitor.OnAdded += OnAssetModified;
-            monitor.OnRemoved += OnAssetModified;
-            monitor.OnModified += OnAssetModified;
-
-            if (!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene))
-            {
-                Scene.Load(ProjectSettings.LastOpenScene);
-                SetSceneDirty(false);
-            }
-        }
-
         /// <summary>
         /// Unloads the currently loaded project. Offers the user a chance to save the current scene if it is modified.
         /// Automatically saves all project data before unloading.
@@ -606,6 +543,77 @@ namespace BansheeEditor
 #endif
         }
 
+        /// <summary>
+        /// Triggered by the runtime when <see cref="LoadProject"/> method completes.
+        /// </summary>
+        private static void Internal_OnProjectLoaded()
+        {
+            SetStatusProject(false);
+            if (!unitTestsExecuted)
+            {
+                RunUnitTests();
+                unitTestsExecuted = true;
+            }
+
+            if (!IsProjectLoaded)
+            {
+                ProjectWindow.Open();
+                return;
+            }
+
+            string projectPath = ProjectPath;
+
+            RecentProject[] recentProjects = EditorSettings.RecentProjects;
+            bool foundPath = false;
+            for (int i = 0; i < recentProjects.Length; i++)
+            {
+                if (PathEx.Compare(recentProjects[i].path, projectPath))
+                {
+                    recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks;
+                    EditorSettings.RecentProjects = recentProjects;
+                    foundPath = true;
+                    break;
+                }
+            }
+
+            if (!foundPath)
+            {
+                List<RecentProject> extendedRecentProjects = new List<RecentProject>();
+                extendedRecentProjects.AddRange(recentProjects);
+
+                RecentProject newProject = new RecentProject();
+                newProject.path = projectPath;
+                newProject.accessTimestamp = (ulong)DateTime.Now.Ticks;
+
+                extendedRecentProjects.Add(newProject);
+
+                EditorSettings.RecentProjects = extendedRecentProjects.ToArray();
+            }
+
+            EditorSettings.LastOpenProject = projectPath;
+            EditorSettings.Save();
+
+            ProjectLibrary.Refresh();
+            monitor = new FolderMonitor(ProjectLibrary.ResourceFolder);
+            monitor.OnAdded += OnAssetModified;
+            monitor.OnRemoved += OnAssetModified;
+            monitor.OnModified += OnAssetModified;
+
+            if (!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene))
+            {
+                Scene.Load(ProjectSettings.LastOpenScene);
+                SetSceneDirty(false);
+            }
+        }
+
+        /// <summary>
+        /// Triggered by the runtime when the user clicks on the status bar.
+        /// </summary>
+        private static void Internal_OnStatusBarClicked()
+        {
+            EditorWindow.OpenWindow<ConsoleWindow>();
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetStatusScene(string name, bool modified);
 

+ 40 - 0
MBansheeEditor/ScriptCodeManager.cs

@@ -1,5 +1,6 @@
 using System.IO;
 using System.Text;
+using System.Text.RegularExpressions;
 using BansheeEngine;
 
 namespace BansheeEditor
@@ -158,5 +159,44 @@ namespace BansheeEditor
 
             return sb.ToString();
         }
+
+        /// <summary>
+        /// Parses a log message and outputs a data object with a separate message and callstack entries. If the message
+        /// is not a valid compiler message null is returned.
+        /// </summary>
+        /// <param name="message">Message to parse.</param>
+        /// <returns>Parsed log message or null if not a valid compiler message.</returns>
+        public static LogEntryData ParseCompilerMessage(string message)
+        {
+            // Note: If modifying FormMessage method make sure to update this one as well to match the formattting
+
+            // Check for error
+            Regex regex = new Regex(@"Compiler error: (.*)\n\tin (.*)\[(.*):.*\]");
+            var match = regex.Match(message);
+
+            // Check for warning
+            if (!match.Success)
+            {
+                regex = new Regex(@"Compiler warning: (.*)\n\tin (.*)\[(.*):.*\]");
+                match = regex.Match(message);
+            }
+
+            // No match
+            if (!match.Success)
+                return null;
+
+            LogEntryData entry = new LogEntryData();
+            entry.callstack = new CallStackEntry[1];
+
+            entry.message = match.Groups[1].Value;
+
+            CallStackEntry callstackEntry = new CallStackEntry();
+            callstackEntry.method = "";
+            callstackEntry.file = match.Groups[2].Value;
+            int.TryParse(match.Groups[3].Value, out callstackEntry.line);
+
+            entry.callstack[0] = callstackEntry;
+            return entry;
+        }
     }
 }

+ 55 - 0
MBansheeEngine/Debug.cs

@@ -4,6 +4,7 @@ using System.Diagnostics;
 using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Text;
+using System.Text.RegularExpressions;
 
 namespace BansheeEngine
 {
@@ -15,6 +16,25 @@ namespace BansheeEngine
         Info, Warning, Error
     }
 
+    /// <summary>
+    /// Contains data for a single entry in a call stack associated with a log entry.
+    /// </summary>
+    public class CallStackEntry
+    {
+        public string method;
+        public string file;
+        public int line;
+    }
+
+    /// <summary>
+    /// Contains data for a single log entry.
+    /// </summary>
+    public class LogEntryData
+    {
+        public string message;
+        public CallStackEntry[] callstack;
+    }
+
     /// <summary>
     /// Utility class providing various debug functionality.
     /// </summary>
@@ -121,6 +141,41 @@ namespace BansheeEngine
             return sb.ToString();
         }
 
+        /// <summary>
+        /// Parses a log message and outputs a data object with a separate message and callstack entries.
+        /// </summary>
+        /// <param name="message">Message to parse.</param>
+        /// <returns>Parsed log message.</returns>
+        public static LogEntryData ParseLogMessage(string message)
+        {
+            // Note: If you are modifying GetStackTrace method make sure to also update this one to match the formattting
+            int firstMatchIdx = -1;
+            Regex regex = new Regex(@"\tat (.*) in (.*), line (\d*), column .*, namespace .*");
+            var matches = regex.Matches(message);
+
+            LogEntryData newEntry = new LogEntryData();
+            newEntry.callstack = new CallStackEntry[matches.Count];
+            for (int i = 0; i < matches.Count; i++)
+            {
+                CallStackEntry callstackEntry = new CallStackEntry();
+                callstackEntry.method = matches[i].Groups[1].Value;
+                callstackEntry.file = matches[i].Groups[2].Value;
+                int.TryParse(matches[i].Groups[3].Value, out callstackEntry.line);
+
+                newEntry.callstack[i] = callstackEntry;
+
+                if (firstMatchIdx == -1)
+                    firstMatchIdx = matches[i].Index;
+            }
+
+            if (firstMatchIdx != -1)
+                newEntry.message = message.Substring(0, firstMatchIdx);
+            else
+                newEntry.message = message;
+
+            return newEntry;
+        }
+
         /// <summary>
         /// Triggered by the runtime when a new message is added to the debug log.
         /// </summary>

+ 73 - 26
MBansheeEngine/GUI/GUIListView.cs

@@ -3,31 +3,30 @@
 namespace BansheeEngine
 {
     /// <summary>
-    /// GUI element that can efficiently display a list of entries that share the same height. This element is mostly an 
+    /// GUI element that can efficiently display a list of entries that share the same height. This element is mostly an
     /// optimization as only visible entries have actual GUI elements, as opposed to just adding GUI elements directly
     /// in a vertical GUI layout. This allows the list view to have thousands of elements with little performance impact.
+    /// 
+    /// Contains shared functionality used by all instances of <see cref="GUIListView{TEntry,TData}"/>.
     /// </summary>
-    /// <typeparam name="TEntry">Type used for creating and updating the GUI elements of the visible entries.</typeparam>
-    /// <typeparam name="TData">Type used for storing the data for all list entries.</typeparam>
-    public class GUIListView<TEntry, TData> 
-        where TEntry : GUIListViewEntry<TData>, new()
+    /// <typeparam name="TData">>Type used for storing the data for all list entries.</typeparam>
+    public abstract class GUIListViewBase<TData>
         where TData : GUIListViewData
     {
         // TODO - Only fixed size is supported. It should be nice if this object could just be placed in layout like any
         // other GUI element. Would likely need some kind of a way to get notified when parent layout changes.
         // (Possibly add a callback to GUIPanel when updateLayout is called?)
 
-        private List<TEntry> visibleEntries = new List<TEntry>();
-        private List<TData> entries = new List<TData>();
-        private GUIScrollArea scrollArea;
-        private GUILabel topPadding;
-        private GUILabel bottomPadding;
-        private int width;
-        private int height;
-        private int entryHeight;
-        private float scrollPct = 0.0f;
-        private bool scrollToLatest = true;
-        private bool contentsDirty = true;
+        protected List<TData> entries = new List<TData>();
+        protected GUIScrollArea scrollArea;
+        protected GUILabel topPadding;
+        protected GUILabel bottomPadding;
+        protected int width;
+        protected int height;
+        protected int entryHeight;
+        protected float scrollPct = 0.0f;
+        protected bool scrollToLatest = true;
+        protected internal bool contentsDirty = true;
 
         /// <summary>
         /// Total number of entries in the list.
@@ -46,6 +45,14 @@ namespace BansheeEngine
             set { entryHeight = value; }
         }
 
+        /// <summary>
+        /// Primary GUI scroll area that all entries are contained within.
+        /// </summary>
+        internal GUIScrollArea ScrollArea 
+        {
+            get { return scrollArea; }
+        }
+
         /// <summary>
         /// Creates a new empty list view.
         /// </summary>
@@ -53,7 +60,7 @@ namespace BansheeEngine
         /// <param name="height">Height of the list view, in pixels.</param>
         /// <param name="entryHeight">Height of a single element in the list, in pixels.</param>
         /// <param name="layout">GUI layout into which the list view will be placed into.</param>
-        public GUIListView(int width, int height, int entryHeight, GUILayout layout)
+        protected GUIListViewBase(int width, int height, int entryHeight, GUILayout layout)
         {
             scrollArea = new GUIScrollArea(GUIOption.FixedWidth(width), GUIOption.FixedHeight(height));
             layout.AddElement(scrollArea);
@@ -148,7 +155,35 @@ namespace BansheeEngine
         /// <summary>
         /// Updates the visuals of the list view. Should be called once per frame.
         /// </summary>
-        public void Update()
+        public abstract void Update();
+    }
+
+    /// <summary>
+    /// GUI element that can efficiently display a list of entries that share the same height. This element is mostly an 
+    /// optimization as only visible entries have actual GUI elements, as opposed to just adding GUI elements directly
+    /// in a vertical GUI layout. This allows the list view to have thousands of elements with little performance impact.
+    /// </summary>
+    /// <typeparam name="TEntry">Type used for creating and updating the GUI elements of the visible entries.</typeparam>
+    /// <typeparam name="TData">Type used for storing the data for all list entries.</typeparam>
+    public class GUIListView<TEntry, TData> : GUIListViewBase<TData>
+        where TEntry : GUIListViewEntry<TData>, new()
+        where TData : GUIListViewData
+    {
+        private List<TEntry> visibleEntries = new List<TEntry>();
+
+        /// <summary>
+        /// Creates a new empty list view.
+        /// </summary>
+        /// <param name="width">Width of the list view, in pixels.</param>
+        /// <param name="height">Height of the list view, in pixels.</param>
+        /// <param name="entryHeight">Height of a single element in the list, in pixels.</param>
+        /// <param name="layout">GUI layout into which the list view will be placed into.</param>
+        public GUIListView(int width, int height, int entryHeight, GUILayout layout)
+            :base(width, height, entryHeight, layout)
+        { }
+
+        /// <inheritdoc/>
+        public override void Update()
         {
             int numVisibleEntries = MathEx.CeilToInt(height / (float)entryHeight) + 1;
             numVisibleEntries = MathEx.Min(numVisibleEntries, entries.Count);
@@ -156,7 +191,7 @@ namespace BansheeEngine
             while (visibleEntries.Count < numVisibleEntries)
             {
                 TEntry newEntry = new TEntry();
-                newEntry.Initialize(scrollArea);
+                newEntry.Initialize(this);
                 newEntry.panel.SetHeight(entryHeight);
 
                 visibleEntries.Add(newEntry);
@@ -195,14 +230,14 @@ namespace BansheeEngine
             {
                 int maxScrollOffset = MathEx.Max(0, totalElementHeight - height - 1);
 
-                int startPos = MathEx.FloorToInt(scrollPct*maxScrollOffset);
-                int startIndex = MathEx.FloorToInt(startPos/(float)entryHeight);
+                int startPos = MathEx.FloorToInt(scrollPct * maxScrollOffset);
+                int startIndex = MathEx.FloorToInt(startPos / (float)entryHeight);
 
                 // Check if we're at the list bottom and the extra element is out of bounds
                 if ((startIndex + visibleEntries.Count) > entries.Count)
                     startIndex--; // Keep the extra element at the top always
 
-                topPadding.SetHeight(startIndex*entryHeight);
+                topPadding.SetHeight(startIndex * entryHeight);
 
                 for (int i = 0; i < visibleEntries.Count; i++)
                 {
@@ -210,7 +245,7 @@ namespace BansheeEngine
                     visibleEntries[i].panel.SetPosition(0, i * entryHeight);
                 }
 
-                int bottomPosition = MathEx.Min(totalElementHeight, (startIndex + visibleEntries.Count)*entryHeight);
+                int bottomPosition = MathEx.Min(totalElementHeight, (startIndex + visibleEntries.Count) * entryHeight);
                 bottomPadding.SetHeight(totalElementHeight - bottomPosition);
 
                 if (scrollToLatest)
@@ -241,6 +276,7 @@ namespace BansheeEngine
     public abstract class GUIListViewEntry<TData>
         where TData : GUIListViewData
     {
+        private GUIListViewBase<TData> parent;
         internal GUIPanel panel;
         internal GUILayoutY layout;
 
@@ -250,12 +286,15 @@ namespace BansheeEngine
         /// Initializes the GUI elements for the entry.
         /// </summary>
         /// <param name="parent">Scroll area into whose layout to insert the GUI elements.</param>
-        internal void Initialize(GUIScrollArea parent)
+        internal void Initialize(GUIListViewBase<TData> parent)
         {
-            int numElements = parent.Layout.ChildCount;
+            this.parent = parent;
+
+            GUIScrollArea scrollArea = parent.ScrollArea;
+            int numElements = scrollArea.Layout.ChildCount;
 
             // Last panel is always the padding panel, so keep it there
-            panel = parent.Layout.InsertPanel(numElements - 1);
+            panel = scrollArea.Layout.InsertPanel(numElements - 1);
             layout = panel.AddLayoutY();
 
             BuildGUI();
@@ -269,6 +308,14 @@ namespace BansheeEngine
             panel.Destroy();
         }
 
+        /// <summary>
+        /// Causes all visible entries in the parent list to be updated.
+        /// </summary>
+        protected void RefreshEntries()
+        {
+            parent.contentsDirty = true;
+        }
+
         /// <summary>
         /// Allows child classes to create GUI elements required by their entry specialization.
         /// </summary>

+ 18 - 0
SBansheeEditor/Include/BsScriptEditorApplication.h

@@ -13,6 +13,16 @@ namespace BansheeEngine
 	public:
 		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "EditorApplication")
 
+		/**
+		 * @brief	Registers internal callbacks. Must be called on scripting system load.
+		 */
+		static void startUp();
+
+		/**
+		 * @brief	Unregisters internal callbacks. Must be called on scripting system shutdown.
+		 */
+		static void shutDown();
+
 		/**
 		 * @brief	Called every frame. Triggers delayed project load.
 		 */
@@ -21,9 +31,15 @@ namespace BansheeEngine
 	private:
 		ScriptEditorApplication(MonoObject* instance);
 
+		/**
+		 * @brief	Triggered when the user clicks on the editor's status bar.
+		 */
+		static void onStatusBarClicked();
+
 		static bool mRequestProjectLoad;
 		static bool mRequestAssemblyReload;
 		static Path mProjectLoadPath;
+		static HEvent OnStatusBarClickedConn;
 
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
@@ -53,7 +69,9 @@ namespace BansheeEngine
 		static void internal_RunUnitTests();
 
 		typedef void(__stdcall *OnProjectLoadedThunkDef)(MonoException**);
+		typedef void(__stdcall *OnStatusBarClickedThunkDef) (MonoException**);
 
 		static OnProjectLoadedThunkDef onProjectLoadedThunk;
+		static OnStatusBarClickedThunkDef onStatusBarClickedThunk;
 	};
 }

+ 2 - 0
SBansheeEditor/Source/BsEditorScriptManager.cpp

@@ -35,6 +35,7 @@ namespace BansheeEngine
 		loadMonoTypes();
 		ScriptAssemblyManager::instance().loadAssemblyInfo(EDITOR_ASSEMBLY);
 
+		ScriptEditorApplication::startUp();
 		ScriptHandleSliderManager::startUp();
 		ScriptGizmoManager::startUp(ScriptAssemblyManager::instance());
 		HandleManager::startUp<ScriptHandleManager>(ScriptAssemblyManager::instance());
@@ -74,6 +75,7 @@ namespace BansheeEngine
 		ScriptHandleSliderManager::shutDown();
 		HandleManager::shutDown();
 		ScriptGizmoManager::shutDown();
+		ScriptEditorApplication::shutDown();
 	}
 
 	void EditorScriptManager::update()

+ 21 - 1
SBansheeEditor/Source/BsScriptEditorApplication.cpp

@@ -23,14 +23,17 @@
 #include "BsCGUIWidget.h"
 #include "BsSceneObject.h"
 #include "BsCCamera.h"
+#include <BsScriptInput.h>
 
 namespace BansheeEngine
 {
 	bool ScriptEditorApplication::mRequestProjectLoad = false;
 	bool ScriptEditorApplication::mRequestAssemblyReload = false;
 	Path ScriptEditorApplication::mProjectLoadPath;
+	HEvent ScriptEditorApplication::OnStatusBarClickedConn;
 
 	ScriptEditorApplication::OnProjectLoadedThunkDef ScriptEditorApplication::onProjectLoadedThunk;
+	ScriptEditorApplication::OnStatusBarClickedThunkDef ScriptEditorApplication::onStatusBarClickedThunk;
 
 	ScriptEditorApplication::ScriptEditorApplication(MonoObject* instance)
 		:ScriptObject(instance)
@@ -62,7 +65,19 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_OpenExternally", &ScriptEditorApplication::internal_OpenExternally);
 		metaData.scriptClass->addInternalCall("Internal_RunUnitTests", &ScriptEditorApplication::internal_RunUnitTests);
 
-		onProjectLoadedThunk = (OnProjectLoadedThunkDef)metaData.scriptClass->getMethod("OnProjectLoaded")->getThunk();
+		onProjectLoadedThunk = (OnProjectLoadedThunkDef)metaData.scriptClass->getMethod("Internal_OnProjectLoaded")->getThunk();
+		onStatusBarClickedThunk = (OnStatusBarClickedThunkDef)metaData.scriptClass->getMethod("Internal_OnStatusBarClicked")->getThunk();
+	}
+
+	void ScriptEditorApplication::startUp()
+	{
+		MainEditorWindow* mainWindow = EditorWindowManager::instance().getMainWindow();
+		OnStatusBarClickedConn = mainWindow->getStatusBar().onMessageClicked.connect(&ScriptEditorApplication::onStatusBarClicked);
+	}
+
+	void ScriptEditorApplication::shutDown()
+	{
+		OnStatusBarClickedConn.disconnect();
 	}
 
 	void ScriptEditorApplication::update()
@@ -85,6 +100,11 @@ namespace BansheeEngine
 		}
 	}
 
+	void ScriptEditorApplication::onStatusBarClicked()
+	{
+		MonoUtil::invokeThunk(onStatusBarClickedThunk);
+	}
+
 	void ScriptEditorApplication::internal_SetStatusScene(MonoString* name, bool modified)
 	{
 		WString nativeScene = MonoUtil::monoToWString(name);