diff --git a/CHANGELOG.md b/CHANGELOG.md index d753d206e5..7f2d45df58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,8 @@ This plugin has functionality that is common to both ReSharper and Rider. It als * Fix issues with completion of Unity event functions ([RIDER-33167](https://youtrack.jetbrains.com/issue/RIDER-33167), [#1326](https://github.com/JetBrains/resharper-unity/pull/1326)) * Rider: Fix missing "Install Mono" notification ([#1329](https://github.com/JetBrains/resharper-unity/pull/1329)) +- Rider: Fix Clear on Play in Rider's Unity log viewer ([#1281](https://github.com/JetBrains/resharper-unity/issues/1281), [#1294](https://github.com/JetBrains/resharper-unity/pull/1294)) +- Unity window should get focus, when Rider is showing usages in it ([#1344](https://github.com/JetBrains/resharper-unity/pull/1344) ## 2019.2.2 diff --git a/resharper/.idea/.idea.resharper-unity/.idea/markdown-navigator/profiles_settings.xml b/resharper/.idea/.idea.resharper-unity/.idea/markdown-navigator/profiles_settings.xml deleted file mode 100644 index db06266325..0000000000 --- a/resharper/.idea/.idea.resharper-unity/.idea/markdown-navigator/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/resharper/resharper-unity.sln.DotSettings b/resharper/resharper-unity.sln.DotSettings index 41a21102f5..e017825e2d 100644 --- a/resharper/resharper-unity.sln.DotSettings +++ b/resharper/resharper-unity.sln.DotSettings @@ -38,6 +38,7 @@ True True True + True True True True diff --git a/resharper/resharper-unity/src/Rider/CodeInsights/UnityCodeInsightFieldUsageProvider.cs b/resharper/resharper-unity/src/Rider/CodeInsights/UnityCodeInsightFieldUsageProvider.cs index 3037ede4a9..8cc75f97b2 100644 --- a/resharper/resharper-unity/src/Rider/CodeInsights/UnityCodeInsightFieldUsageProvider.cs +++ b/resharper/resharper-unity/src/Rider/CodeInsights/UnityCodeInsightFieldUsageProvider.cs @@ -54,6 +54,7 @@ public enum UnityPresentationType private readonly UnitySceneDataLocalCache myUnitySceneDataLocalCache; private readonly ITooltipManager myTooltipManager; private readonly TextControlManager myTextControlManager; + private readonly UnityEditorProtocol myProtocol; public override string ProviderId => "Unity serialized field"; public override string DisplayName => "Unity serialized field"; public override CodeLensAnchorKind DefaultAnchor => CodeLensAnchorKind.Right; @@ -63,7 +64,7 @@ public enum UnityPresentationType public UnityCodeInsightFieldUsageProvider(Lifetime lifetime, UnitySolutionTracker unitySolutionTracker, ConnectionTracker connectionTracker, UnityApi unityApi, UnityHost host, BulbMenuComponent bulbMenu, IPsiFiles files, UnityHost unityHost, UnitySceneDataLocalCache sceneDataCache, - ITooltipManager tooltipManager, TextControlManager textControlManager) + ITooltipManager tooltipManager, TextControlManager textControlManager, UnityEditorProtocol protocol) : base(unitySolutionTracker, host, bulbMenu) { myLifetime = lifetime; @@ -74,6 +75,7 @@ public UnityCodeInsightFieldUsageProvider(Lifetime lifetime, UnitySolutionTracke myUnitySceneDataLocalCache = sceneDataCache; myTooltipManager = tooltipManager; myTextControlManager = textControlManager; + myProtocol = protocol; } private static (string guid, string propertyName)? GetAssetGuidAndPropertyName(ISolution solution, IDeclaredElement declaredElement) @@ -177,7 +179,7 @@ public override void OnClick(CodeInsightsHighlighting highlighting, ISolution so var value = (key as MonoBehaviourPropertyValueWithLocation).NotNull("value != null"); - UnityEditorFindUsageResultCreator.CreateRequestAndShow(myUnityHost, solution.SolutionDirectory, myUnitySceneDataLocalCache, + UnityEditorFindUsageResultCreator.CreateRequestAndShow(myProtocol, myUnityHost, myLifetime, solution.SolutionDirectory, myUnitySceneDataLocalCache, value.Value.MonoBehaviour, value.File); }); }); diff --git a/resharper/resharper-unity/src/Rider/UnityEditorFindUsageResultCreator.cs b/resharper/resharper-unity/src/Rider/UnityEditorFindUsageResultCreator.cs index 4b187d23af..aa4e068018 100644 --- a/resharper/resharper-unity/src/Rider/UnityEditorFindUsageResultCreator.cs +++ b/resharper/resharper-unity/src/Rider/UnityEditorFindUsageResultCreator.cs @@ -4,6 +4,7 @@ using JetBrains.Application.Progress; using JetBrains.Application.Threading; using JetBrains.Application.Threading.Tasks; +using JetBrains.Core; using JetBrains.Lifetimes; using JetBrains.ProjectModel; using JetBrains.ReSharper.Host.Features.BackgroundTasks; @@ -30,10 +31,13 @@ public class UnityEditorFindUsageResultCreator private readonly UnitySceneDataLocalCache myUnitySceneDataLocalCache; private readonly RiderBackgroundTaskHost myBackgroundTaskHost; private readonly UnityHost myUnityHost; + private readonly UnityEditorProtocol myEditorProtocol; private readonly FileSystemPath mySolutionDirectoryPath; public UnityEditorFindUsageResultCreator(Lifetime lifetime, ISolution solution, SearchDomainFactory searchDomainFactory, IShellLocks locks, - UnitySceneDataLocalCache sceneDataCache, UnityHost unityHost, UnityExternalFilesModuleFactory externalFilesModuleFactory, [CanBeNull] RiderBackgroundTaskHost backgroundTaskHost = null) + UnitySceneDataLocalCache sceneDataCache, UnityHost unityHost, UnityExternalFilesModuleFactory externalFilesModuleFactory, + UnityEditorProtocol editorProtocol, + [CanBeNull] RiderBackgroundTaskHost backgroundTaskHost = null) { myLifetime = lifetime; mySolution = solution; @@ -42,6 +46,7 @@ public UnityEditorFindUsageResultCreator(Lifetime lifetime, ISolution solution, myBackgroundTaskHost = backgroundTaskHost; myYamlSearchDomain = searchDomainFactory.CreateSearchDomain(externalFilesModuleFactory.PsiModule); myUnityHost = unityHost; + myEditorProtocol = editorProtocol; mySolutionDirectoryPath = solution.SolutionDirectory; } @@ -89,7 +94,7 @@ public void CreateRequestToUnity([NotNull] IDeclaredElement declaredElement, IPs { finder.FindAsync(new[] {declaredElement}, myYamlSearchDomain, consumer, SearchPattern.FIND_USAGES ,pi, - FinderSearchRoot.Empty, new UnityUsagesAsyncFinderCallback(lifetimeDef, consumer, myUnityHost, myLocks, + FinderSearchRoot.Empty, new UnityUsagesAsyncFinderCallback(lifetimeDef, myLifetime, consumer, myUnityHost, myEditorProtocol, myLocks, declaredElement.ShortName, selectRequest, focusUnity)); } }); @@ -109,16 +114,20 @@ public static FindUsageResultElement CreateRequest([NotNull] FileSystemPath solu return new FindUsageResultElement(isPrefab, needExpand, pathFromAsset, fileName, consumer.NameParts.ToArray(), consumer.RootIndexes.ToArray()); } - public static void CreateRequestAndShow([NotNull] UnityHost unityHost, [NotNull] FileSystemPath solutionDirPath, [NotNull]UnitySceneDataLocalCache unitySceneDataLocalCache, + public static void CreateRequestAndShow([NotNull] UnityEditorProtocol editor, UnityHost host, Lifetime lifetime, [NotNull] FileSystemPath solutionDirPath, [NotNull]UnitySceneDataLocalCache unitySceneDataLocalCache, [NotNull] string anchor, IPsiSourceFile sourceFile, bool needExpand = false) { - + FindUsageResultElement request; using (ReadLockCookie.Create()) { - var request = CreateRequest(solutionDirPath, unitySceneDataLocalCache, anchor, sourceFile, needExpand); - unityHost.PerformModelAction(t => t.ShowGameObjectOnScene.Fire(request)); + request = CreateRequest(solutionDirPath, unitySceneDataLocalCache, anchor, sourceFile, needExpand); } - UnityFocusUtil.FocusUnity(unityHost.GetValue(t => t.UnityProcessId.Value)); + + host.PerformModelAction(a => a.AllowSetForegroundWindow.Start(Unit.Instance).Result.Advise(lifetime, + result => + { + editor.UnityModel.Value.ShowGameObjectOnScene.Fire(request.ConvertToUnityModel()); + })); } private static bool GetPathFromAssetFolder([NotNull] FileSystemPath solutionDirPath, [NotNull] IPsiSourceFile file, @@ -181,50 +190,56 @@ public FindExecution Merge(IUnityYamlReference data) private class UnityUsagesAsyncFinderCallback : IFinderAsyncCallback { - private readonly LifetimeDefinition myLifetimeDef; + private readonly LifetimeDefinition myProgressBarLifetimeDefinition; + private readonly Lifetime myComponentLifetime; private readonly UnityUsagesFinderConsumer myConsumer; private readonly UnityHost myUnityHost; + private readonly UnityEditorProtocol myEditorProtocol; private readonly IShellLocks myShellLocks; private readonly string myDisplayName; private readonly FindUsageResultElement mySelected; - private readonly bool myFocusUnity; - public UnityUsagesAsyncFinderCallback(LifetimeDefinition lifetimeDef, UnityUsagesFinderConsumer consumer, UnityHost unityHost, IShellLocks shellLocks, + public UnityUsagesAsyncFinderCallback(LifetimeDefinition progressBarLifetimeDefinition, Lifetime componentLifetime, UnityUsagesFinderConsumer consumer, UnityHost unityHost, UnityEditorProtocol editorProtocol, IShellLocks shellLocks, string displayName, FindUsageResultElement selected, bool focusUnity) { - myLifetimeDef = lifetimeDef; + myProgressBarLifetimeDefinition = progressBarLifetimeDefinition; + myComponentLifetime = componentLifetime; myConsumer = consumer; myUnityHost = unityHost; + myEditorProtocol = editorProtocol; myShellLocks = shellLocks; myDisplayName = displayName; mySelected = selected; - myFocusUnity = focusUnity; } public void Complete() { - myShellLocks.Tasks.StartNew(myLifetimeDef.Lifetime, Scheduling.MainGuard, () => + myShellLocks.Tasks.StartNew(myComponentLifetime, Scheduling.MainGuard, () => { if (myConsumer.Result.Count != 0) { - - if (myFocusUnity) - UnityFocusUtil.FocusUnity(myUnityHost.GetValue(t => t.UnityProcessId.Value)); - - if (mySelected != null) - myUnityHost.PerformModelAction(t => t.ShowGameObjectOnScene.Fire(mySelected)); - myUnityHost.PerformModelAction(t => - t.FindUsageResults.Fire(new FindUsageResult(myDisplayName, myConsumer.Result.ToArray()))); - + if (myEditorProtocol.UnityModel.Value == null) return; + + myUnityHost.PerformModelAction(a => a.AllowSetForegroundWindow.Start(Unit.Instance).Result + .Advise(myComponentLifetime, + result => + { + var model = myEditorProtocol.UnityModel.Value; + if (mySelected != null) + model.ShowGameObjectOnScene.Fire(mySelected.ConvertToUnityModel()); + // pass all references to Unity TODO temp workaround, replace with async api + model.FindUsageResults.Fire(new FindUsageResult(myDisplayName, + myConsumer.Result.ToArray()).ConvertToUnityModel()); + })); } - - myLifetimeDef.Terminate(); + + myProgressBarLifetimeDefinition.Terminate(); }); } public void Error(string message) { - myLifetimeDef.Terminate(); + myProgressBarLifetimeDefinition.Terminate(); } } } diff --git a/resharper/resharper-unity/src/Rider/UnityEditorProtocol.cs b/resharper/resharper-unity/src/Rider/UnityEditorProtocol.cs index 8a816a0d9c..37b96e922f 100644 --- a/resharper/resharper-unity/src/Rider/UnityEditorProtocol.cs +++ b/resharper/resharper-unity/src/Rider/UnityEditorProtocol.cs @@ -186,19 +186,12 @@ private void CreateProtocols(FileSystemPath protocolInstancePath) editor.UnityProcessId.View(lf, (_, pid) => myHost.PerformModelAction(t => t.UnityProcessId.Set(pid))); // I have split this into groups, because want to use async api for finding reference and pass them via groups to Unity - myHost.PerformModelAction(t => t.ShowGameObjectOnScene.Advise(lf, v => editor.ShowGameObjectOnScene.Fire(v.ConvertToUnityModel()))); myHost.PerformModelAction(t => t.ShowFileInUnity.Advise(lf, v => editor.ShowFileInUnity.Fire(v))); myHost.PerformModelAction(t => t.ShowPreferences.Advise(lf, v => { - if (t.UnityProcessId.HasValue()) - UnityFocusUtil.FocusUnity(t.UnityProcessId.Value); - editor.ShowPreferences.Fire(); })); - - // pass all references to Unity TODO temp workaround, replace with async api - myHost.PerformModelAction(t => t.FindUsageResults.Advise(lf, v =>editor.FindUsageResults.Fire(v.ConvertToUnityModel()))); - + editor.EditorLogPath.Advise(lifetime, s => myHost.PerformModelAction(a => a.EditorLogPath.SetValue(s))); editor.PlayerLogPath.Advise(lifetime, diff --git a/resharper/resharper-unity/src/UnityFocusUtil.cs b/resharper/resharper-unity/src/UnityFocusUtil.cs deleted file mode 100644 index 60d21da4d9..0000000000 --- a/resharper/resharper-unity/src/UnityFocusUtil.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using JetBrains.Interop.WinApi; -using JetBrains.Util; - -namespace JetBrains.ReSharper.Plugins.Unity -{ - public static class UnityFocusUtil - { - - [DllImport("User32.dll")] - private static extern bool SwitchToThisWindow (IntPtr hwnd, bool fUnknown); - - public static void FocusUnity(int pid) - { - if (PlatformUtil.RuntimePlatform == PlatformUtil.Platform.Windows) - { - var process = Process.GetProcessById(pid); - var hWnd = process.MainWindowHandle; - - SwitchToThisWindow(hWnd, true); - } - } - } -} \ No newline at end of file diff --git a/rider/protocol/src/main/kotlin/model/rider/RdUnityModel.kt b/rider/protocol/src/main/kotlin/model/rider/RdUnityModel.kt index 9a8f3c0a00..ae5ba0f281 100644 --- a/rider/protocol/src/main/kotlin/model/rider/RdUnityModel.kt +++ b/rider/protocol/src/main/kotlin/model/rider/RdUnityModel.kt @@ -89,12 +89,11 @@ object RdUnityModel : Ext(SolutionModel.Solution) { property("ScriptCompilationDuringPlay", ScriptCompilationDuringPlay) source("enableYamlParsing", void) - signal("findUsageResults", FindUsageResult) - signal("showGameObjectOnScene", FindUsageResultElement) signal("showFileInUnity", string) property("unityProcessId", int) sink("onEditorModelOutOfSync", void) callback("attachDebuggerToUnityEditor", void, bool) + callback("allowSetForegroundWindow", void, bool) } } \ No newline at end of file diff --git a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/UnityHost.kt b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/UnityHost.kt index 762ee58eb9..d8fddb50a1 100644 --- a/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/UnityHost.kt +++ b/rider/src/main/kotlin/com/jetbrains/rider/plugins/unity/UnityHost.kt @@ -4,7 +4,9 @@ import com.intellij.execution.ProgramRunnerUtil import com.intellij.execution.RunManager import com.intellij.execution.executors.DefaultDebugExecutor import com.intellij.ide.impl.ProjectUtil +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project +import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.wm.WindowManager import com.intellij.util.BitUtil import com.intellij.xdebugger.XDebuggerManager @@ -26,11 +28,13 @@ import com.jetbrains.rider.plugins.unity.run.configurations.UnityAttachToEditorR import com.jetbrains.rider.plugins.unity.run.configurations.UnityDebugConfigurationType import com.jetbrains.rider.projectView.solution import com.jetbrains.rider.util.idea.getComponent +import com.sun.jna.Native +import com.sun.jna.win32.StdCallLibrary import java.awt.Frame class UnityHost(project: Project, runManager: RunManager) : LifetimedProjectComponent(project) { val model = project.solution.rdUnityModel - + private val logger = Logger.getInstance(UnityHost::class.java) val sessionInitialized = model.sessionInitialized val unityState = model.editorState @@ -89,11 +93,34 @@ class UnityHost(project: Project, runManager: RunManager) : LifetimedProjectComp } task } + + model.allowSetForegroundWindow.set { _, _ -> + val task = RdTask() + if (SystemInfo.isWindows) { + val id = model.unityProcessId.valueOrNull + if (id != null && id > 0) + task.set(user32.AllowSetForegroundWindow(id)) + else + logger.warn("unityProcessId is null or 0") + } + else + task.set(true) + + task + } + } companion object { fun getInstance(project: Project) = project.getComponent() } + + @Suppress("FunctionName") + private interface User32 : StdCallLibrary { + fun AllowSetForegroundWindow(id:Int) : Boolean + } + + private val user32 = Native.load("user32", User32::class.java) } fun Project.isConnectedToEditor() = UnityHost.getInstance(this).sessionInitialized.valueOrDefault(false) \ No newline at end of file diff --git a/rider/src/main/resources/META-INF/plugin.xml b/rider/src/main/resources/META-INF/plugin.xml index c4290fa208..c28483a011 100644 --- a/rider/src/main/resources/META-INF/plugin.xml +++ b/rider/src/main/resources/META-INF/plugin.xml @@ -265,6 +265,8 @@ Fixed:

diff --git a/unity/EditorPlugin/AfterUnity56/Navigation/Initialization.cs b/unity/EditorPlugin/AfterUnity56/Navigation/Initialization.cs index 5bc86971c1..b88e7eac55 100644 --- a/unity/EditorPlugin/AfterUnity56/Navigation/Initialization.cs +++ b/unity/EditorPlugin/AfterUnity56/Navigation/Initialization.cs @@ -1,7 +1,8 @@ -using System; -using System.Reflection; +using System.Diagnostics; +using System.Linq; using JetBrains.Rider.Unity.Editor.Navigation; using JetBrains.Rider.Unity.Editor.Navigation.Window; +using JetBrains.Rider.Unity.Editor.NonUnity; using UnityEditor; using UnityEngine; @@ -19,7 +20,10 @@ public static void OnModelInitializationHandler(UnityModelAndLifetime modelAndLi { MainThreadDispatcher.Instance.Queue(() => { + ExpandMinimizedUnityWindow(); + EditorUtility.FocusProjectWindow(); + if (findUsagesResult.IsPrefab) { ShowUtil.ShowFileUsage(findUsagesResult.FilePath); @@ -60,5 +64,24 @@ public static void OnModelInitializationHandler(UnityModelAndLifetime modelAndLi } }); } + + private static void ExpandMinimizedUnityWindow() + { + if (PluginSettings.SystemInfoRiderPlugin.operatingSystemFamily == OperatingSystemFamilyRider.Windows) + { + var topLevelWindows = User32Dll.GetTopLevelWindowHandles(); + var windowHandles = topLevelWindows + .Where(hwnd => User32Dll.GetWindowProcessId(hwnd) == Process.GetCurrentProcess().Id).ToArray(); + + foreach (var windowHandle in windowHandles) + { + if (User32Dll.IsIconic(windowHandle)) + { + User32Dll.ShowWindow(windowHandle, 9); + User32Dll.SetForegroundWindow(windowHandle); + } + } + } + } } } \ No newline at end of file diff --git a/unity/EditorPlugin/NonUnity/User32Dll.cs b/unity/EditorPlugin/NonUnity/User32Dll.cs index e72a95480a..987fbb623f 100644 --- a/unity/EditorPlugin/NonUnity/User32Dll.cs +++ b/unity/EditorPlugin/NonUnity/User32Dll.cs @@ -53,5 +53,9 @@ public static List GetTopLevelWindowHandles() [DllImport("user32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)] public static extern uint ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("User32.dll")] + public static extern bool IsIconic(IntPtr handle); + } } \ No newline at end of file