Просмотр исходного кода

Default code editor is hooked up at editor startup
Added code editor options to the editor settings window
Visual Studio code editor:
- VS will now open in all cases instead of silently failing most of the time
- Opening a file in VS solution that is already open will now properly focus on the existing VS instance instead of opening new one

BearishSun 10 лет назад
Родитель
Сommit
a2124e6be1

+ 6 - 0
BansheeEditor/Include/BsCodeEditor.h

@@ -65,6 +65,11 @@ namespace BansheeEngine
 		 */
 		void setActive(CodeEditorType editor);
 
+		/**
+		 * @brief	Returns the currently active code editor.
+		 */
+		CodeEditorType getActive() const { return mActiveEditorType; }
+
 		/**
 		 * @brief	Opens a code file in the active external editor. 
 		 *
@@ -89,6 +94,7 @@ namespace BansheeEngine
 		Path getSolutionPath() const;
 
 		CodeEditor* mActiveEditor;
+		CodeEditorType mActiveEditorType;
 		Map<CodeEditorType, CodeEditorFactory*> mFactoryPerEditor;
 		Vector<CodeEditorType> mEditors;
 		Vector<CodeEditorFactory*> mFactories;

+ 2 - 1
BansheeEditor/Include/BsEditorPrerequisites.h

@@ -103,7 +103,8 @@ namespace BansheeEngine
 		VS2010,
 		VS2012,
 		VS2013,
-		VS2015
+		VS2015,
+		None
 	};
 
 	/**

+ 2 - 1
BansheeEditor/Source/BsCodeEditor.cpp

@@ -14,7 +14,7 @@ static_assert("Make sure to add implementations for other platforms.");
 namespace BansheeEngine
 {
 	CodeEditorManager::CodeEditorManager()
-		:mActiveEditor(nullptr)
+		:mActiveEditor(nullptr), mActiveEditorType(CodeEditorType::None)
 	{
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 		VSCodeEditorFactory* vsCodeEditorFactory = bs_new<VSCodeEditorFactory>();
@@ -53,6 +53,7 @@ namespace BansheeEngine
 			return;
 
 		mActiveEditor = findIter->second->create(editor);
+		mActiveEditorType = editor;
 	}
 
 	void CodeEditorManager::openFile(const Path& path, UINT32 lineNumber) const

+ 100 - 1
BansheeEditor/Source/Win32/BsVSCodeEditor.cpp

@@ -42,6 +42,86 @@ namespace BansheeEngine
 		Path path;
 	};
 
+	/**
+	 * @brief	Handles retrying of calls that fail to access Visual Studio. This is due to the weird nature of VS
+	 * 			when calling its methods from external code. If this message filter isn't registered some calls will
+	 * 			just fail silently.
+	 */
+	class VSMessageFilter : public IMessageFilter
+	{
+		DWORD __stdcall HandleInComingCall(DWORD dwCallType, HTASK htaskCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo) override
+		{
+			return SERVERCALL_ISHANDLED;
+		}
+
+		DWORD __stdcall RetryRejectedCall(HTASK htaskCallee, DWORD dwTickCount, DWORD dwRejectType) override
+		{
+			if (dwRejectType == SERVERCALL_RETRYLATER)
+			{
+				// Retry immediatey
+				return 99;
+			}
+			// Cancel the call
+			return -1;
+		}
+
+		DWORD __stdcall MessagePending(HTASK htaskCallee, DWORD dwTickCount, DWORD dwPendingType) override
+		{
+			return PENDINGMSG_WAITDEFPROCESS;
+		}
+
+		/**
+		 * @brief	COM requirement. Returns instance of an interface of
+		 * 			provided type.
+		 */
+		HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject) override
+		{
+			if(iid == IID_IDropTarget || iid == IID_IUnknown)
+			{
+				AddRef();
+				*ppvObject = this;
+				return S_OK;
+			}
+			else
+			{
+				*ppvObject = nullptr;
+				return E_NOINTERFACE;
+			}
+		}
+
+		/**
+		 * @brief	COM requirement. Increments objects
+		 * 			reference count.
+		 */
+		ULONG __stdcall AddRef() override
+		{
+			return InterlockedIncrement(&mRefCount);
+		} 
+
+		/**
+		 * @brief	COM requirement. Decreases the objects 
+		 * 			reference count and deletes the object
+		 * 			if its zero.
+		 */
+		ULONG __stdcall Release() override
+		{
+			LONG count = InterlockedDecrement(&mRefCount);
+
+			if(count == 0)
+			{
+				bs_delete(this);
+				return 0;
+			}
+			else
+			{
+				return count;
+			}
+		} 
+
+	private:
+		LONG mRefCount;
+	};
+
 	/**
 	 * @brief	Contains various helper classes for interacting with a Visual Studio instance
 	 *			running on this machine.
@@ -103,7 +183,7 @@ namespace BansheeEngine
 					curObject->QueryInterface(__uuidof(EnvDTE::_DTE), (void**)&dte);
 
 					if (dte == nullptr)
-						return nullptr;
+						continue;
 
 					CComPtr<EnvDTE::_Solution> solution;
 					if (FAILED(dte->get_Solution(&solution)))
@@ -458,18 +538,37 @@ EndProject)";
 
 	void VSCodeEditor::openFile(const Path& solutionPath, const Path& filePath, UINT32 lineNumber) const
 	{
+		CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+
 		CLSID clsID;
 		if (FAILED(CLSIDFromString(mCLSID.c_str(), &clsID)))
+		{
+			CoUninitialize();
 			return;
+		}
 
 		CComPtr<EnvDTE::_DTE> dte = VisualStudio::findRunningInstance(clsID, solutionPath);
 		if (dte == nullptr)
 			dte = VisualStudio::openInstance(clsID, solutionPath);
 
 		if (dte == nullptr)
+		{
+			CoUninitialize();
 			return;
+		}
+
+		VSMessageFilter* newFilter = new VSMessageFilter();
+		IMessageFilter* oldFilter;
+
+		CoRegisterMessageFilter(newFilter, &oldFilter);
+		EnvDTE::Window* window = nullptr;
+		if (SUCCEEDED(dte->get_MainWindow(&window)))
+			window->Activate();
 
 		VisualStudio::openFile(dte, filePath, lineNumber);
+		CoRegisterMessageFilter(oldFilter, nullptr);
+
+		CoUninitialize();
 	}
 
 	void VSCodeEditor::syncSolution(const CodeSolutionData& data, const Path& outputPath) const

+ 6 - 1
MBansheeEditor/CodeEditor.cs

@@ -13,7 +13,8 @@ namespace BansheeEditor
         VS2010,
         VS2012,
         VS2013,
-        VS2015
+        VS2015,
+        None
     }
 
     /// <summary>
@@ -40,6 +41,7 @@ namespace BansheeEditor
         public static CodeEditorType ActiveEditor
         {
             set { Internal_SetActiveEditor(value); }
+            get { return Internal_GetActiveEditor(); }
         }
 
         /// <summary>
@@ -91,6 +93,9 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern void Internal_SetActiveEditor(CodeEditorType type);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern CodeEditorType Internal_GetActiveEditor();
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern CodeEditorType[] Internal_GetAvailableEditors();
 

+ 10 - 0
MBansheeEditor/Program.cs

@@ -35,6 +35,16 @@ namespace BansheeEditor
             }
             else
                 ProjectWindow.Open();
+
+            CodeEditorType activeCodeEditor = (CodeEditorType)EditorSettings.GetInt(SettingsWindow.ActiveCodeEditorKey, (int) CodeEditorType.None);
+            CodeEditorType[] availableEditors = CodeEditor.AvailableEditors;
+            if (Array.Exists(availableEditors, x => x == activeCodeEditor))
+                CodeEditor.ActiveEditor = activeCodeEditor;
+            else
+            {
+                if (availableEditors.Length > 0)
+                    CodeEditor.ActiveEditor = availableEditors[0];
+            }
         }
 
         /// <summary>

+ 25 - 1
MBansheeEditor/SettingsWindow.cs

@@ -1,4 +1,5 @@
-using BansheeEngine;
+using System;
+using BansheeEngine;
 
 namespace BansheeEditor
 {
@@ -7,8 +8,11 @@ namespace BansheeEditor
     /// </summary>
     internal sealed class SettingsWindow : EditorWindow
     {
+        internal const string ActiveCodeEditorKey = "__ActiveCodeEditor";
+
         private GUIFloatField defaultHandleSizeField;
         private GUIToggleField autoLoadLastProjectField;
+        private GUIListBoxField codeEditorField;
 
         /// <summary>
         /// Opens the settings window if its not open already.
@@ -38,6 +42,21 @@ namespace BansheeEditor
             autoLoadLastProjectField = new GUIToggleField(new LocEdString("Automatically load last project"), 200);
             autoLoadLastProjectField.OnChanged += (x) => { EditorSettings.AutoLoadLastProject = x; };
 
+            CodeEditorType[] availableEditors = CodeEditor.AvailableEditors;
+            Array.Resize(ref availableEditors, availableEditors.Length + 1);
+            availableEditors[availableEditors.Length - 1] = CodeEditorType.None;
+
+            string[] availableEditorNames = new string[availableEditors.Length];
+            for (int i = 0; i < availableEditors.Length; i++)
+                availableEditorNames[i] = Enum.GetName(typeof (CodeEditorType), availableEditors[i]);
+
+            codeEditorField = new GUIListBoxField(availableEditorNames, new LocEdString("Code editor"), 200);
+            codeEditorField.OnSelectionChanged += x =>
+            {
+                EditorSettings.SetInt(ActiveCodeEditorKey, (int)availableEditors[x]);
+                CodeEditor.ActiveEditor = availableEditors[x];
+            };
+
             GUILayout mainLayout = GUI.AddLayoutY();
             mainLayout.AddElement(projectFoldout);
             GUILayout projectLayoutOuterY = mainLayout.AddLayoutY();
@@ -73,6 +92,11 @@ namespace BansheeEditor
         {
             defaultHandleSizeField.Value = EditorSettings.DefaultHandleSize;
             autoLoadLastProjectField.Value = EditorSettings.AutoLoadLastProject;
+
+            CodeEditorType[] availableEditors = CodeEditor.AvailableEditors;
+            int idx = Array.IndexOf(availableEditors, CodeEditor.ActiveEditor);
+            if (idx != -1)
+                codeEditorField.Index = idx;
         }
     }
 }

+ 1 - 0
SBansheeEditor/Include/BsScriptCodeEditor.h

@@ -20,6 +20,7 @@ namespace BansheeEngine
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static void internal_SetActiveEditor(CodeEditorType type);
+		static CodeEditorType internal_GetActiveEditor();
 		static MonoArray* internal_GetAvailableEditors();
 		static void internal_OpenFile(MonoString* path, UINT32 line);
 		static void internal_SyncSolution();

+ 6 - 0
SBansheeEditor/Source/BsScriptCodeEditor.cpp

@@ -18,11 +18,17 @@ namespace BansheeEngine
 	void ScriptCodeEditor::initRuntimeData()
 	{
 		metaData.scriptClass->addInternalCall("Internal_SetActiveEditor", &ScriptCodeEditor::internal_SetActiveEditor);
+		metaData.scriptClass->addInternalCall("Internal_GetActiveEditor", &ScriptCodeEditor::internal_GetActiveEditor);
 		metaData.scriptClass->addInternalCall("Internal_GetAvailableEditors", &ScriptCodeEditor::internal_GetAvailableEditors);
 		metaData.scriptClass->addInternalCall("Internal_OpenFile", &ScriptCodeEditor::internal_OpenFile);
 		metaData.scriptClass->addInternalCall("Internal_SyncSolution", &ScriptCodeEditor::internal_SyncSolution);
 	}
 
+	CodeEditorType ScriptCodeEditor::internal_GetActiveEditor()
+	{
+		return CodeEditorManager::instance().getActive();
+	}
+
 	void ScriptCodeEditor::internal_SetActiveEditor(CodeEditorType type)
 	{
 		CodeEditorManager::instance().setActive(type);