Skip to content

Focus/expand Unity windows, when Rider is showing usages in it #1344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

This file was deleted.

1 change: 1 addition & 0 deletions resharper/resharper-unity.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Highlightings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Higlighting/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=HLSL/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=hwnd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Inplace/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Intellisense/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=lnks/@EntryIndexedValue">True</s:Boolean>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -42,6 +46,7 @@ public UnityEditorFindUsageResultCreator(Lifetime lifetime, ISolution solution,
myBackgroundTaskHost = backgroundTaskHost;
myYamlSearchDomain = searchDomainFactory.CreateSearchDomain(externalFilesModuleFactory.PsiModule);
myUnityHost = unityHost;
myEditorProtocol = editorProtocol;
mySolutionDirectoryPath = solution.SolutionDirectory;
}

Expand Down Expand Up @@ -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));
}
});
Expand All @@ -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,
Expand Down Expand Up @@ -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();
}
}
}
Expand Down
9 changes: 1 addition & 8 deletions resharper/resharper-unity/src/Rider/UnityEditorProtocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
26 changes: 0 additions & 26 deletions resharper/resharper-unity/src/UnityFocusUtil.cs

This file was deleted.

3 changes: 1 addition & 2 deletions rider/protocol/src/main/kotlin/model/rider/RdUnityModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -89,11 +93,34 @@ class UnityHost(project: Project, runManager: RunManager) : LifetimedProjectComp
}
task
}

model.allowSetForegroundWindow.set { _, _ ->
val task = RdTask<Boolean>()
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<UnityHost>()
}

@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)
2 changes: 2 additions & 0 deletions rider/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@
</ul>
<em>Fixed:</em>
<ul>
<li>Rider: Fix Clear on Play in Rider's Unity log viewer (<a href="https://github.com/JetBrains/resharper-unity/issues/1281">#1281</a>, <a href="https://github.com/JetBrains/resharper-unity/pull/1294">#1294</a>)</li>
<li>Unity window should get focus, when Rider is showing usages in it ([#1344](https://github.com/JetBrains/resharper-unity/pull/1344)</li>
<li>Unity Editor: Fix exception calling <tt>EditorApplication.isPlaying</tt> on wrong thread (<a href="https://github.com/JetBrains/resharper-unity/pull/1308">#1308</a>)</li>
</ul>
</p>
Expand Down
27 changes: 25 additions & 2 deletions unity/EditorPlugin/AfterUnity56/Navigation/Initialization.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -19,7 +20,10 @@ public static void OnModelInitializationHandler(UnityModelAndLifetime modelAndLi
{
MainThreadDispatcher.Instance.Queue(() =>
{
ExpandMinimizedUnityWindow();

EditorUtility.FocusProjectWindow();

if (findUsagesResult.IsPrefab)
{
ShowUtil.ShowFileUsage(findUsagesResult.FilePath);
Expand Down Expand Up @@ -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);
}
}
}
}
}
}
4 changes: 4 additions & 0 deletions unity/EditorPlugin/NonUnity/User32Dll.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,9 @@ public static List<IntPtr> 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);

}
}