diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6e3c965 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*] +indent_style = space +indent_size = 4 diff --git a/install/presentation-assistant.nuspec b/install/presentation-assistant.nuspec index 5ebb08e..5393742 100644 --- a/install/presentation-assistant.nuspec +++ b/install/presentation-assistant.nuspec @@ -3,7 +3,7 @@ JetBrains.PresentationAssistant Presentation Assistant - 2.3.0 + 2.3.1 JetBrains JetBrains Shows the name and keyboard shortcuts of invoked actions. diff --git a/src/resharper-presentation-assistant/ActionIdBlacklist.cs b/src/resharper-presentation-assistant/ActionIdBlacklist.cs index 37026fe..f5dde1a 100644 --- a/src/resharper-presentation-assistant/ActionIdBlacklist.cs +++ b/src/resharper-presentation-assistant/ActionIdBlacklist.cs @@ -25,7 +25,11 @@ public static class ActionIdBlacklist "WordNextExtend", // VS commands, not R# commands - "Edit.Up", "Edit.Down", "Edit.Left", "Edit.Right", "Edit.PageUp", "Edit.PageDown" + "Edit.Up", "Edit.Down", "Edit.Left", "Edit.Right", "Edit.PageUp", "Edit.PageDown", + + // Make sure we don't try to show the presentation assistant popup just as we're + // killing the popups + PresentationAssistantAction.ActionId }; public static bool IsBlacklisted(string actionId) diff --git a/src/resharper-presentation-assistant/PresentationAssistant.cs b/src/resharper-presentation-assistant/PresentationAssistant.cs index 6c2ece8..19878a7 100644 --- a/src/resharper-presentation-assistant/PresentationAssistant.cs +++ b/src/resharper-presentation-assistant/PresentationAssistant.cs @@ -3,6 +3,7 @@ using JetBrains.Application.ActivityTrackingNew; using JetBrains.Application.Parts; using JetBrains.DataFlow; +using JetBrains.Threading; using JetBrains.UI.ActionsRevised.Loader; namespace JetBrains.ReSharper.Plugins.PresentationAssistant @@ -15,24 +16,47 @@ public class PresentationAssistant : IActivityTracking private readonly ActionFinder actionFinder; private readonly PresentationAssistantWindowOwner presentationAssistantWindowOwner; private readonly ShortcutFactory shortcutFactory; + private readonly PresentationAssistantSettingsStore settingsStore; private DateTime lastDisplayed; private string lastActionId; private int multiplier; + private bool enabled; public PresentationAssistant(Lifetime lifetime, ActionFinder actionFinder, PresentationAssistantWindowOwner presentationAssistantWindowOwner, - ShortcutFactory shortcutFactory) + ShortcutFactory shortcutFactory, PresentationAssistantSettingsStore settingsStore, + IThreading threading) { this.actionFinder = actionFinder; this.presentationAssistantWindowOwner = presentationAssistantWindowOwner; this.shortcutFactory = shortcutFactory; + this.settingsStore = settingsStore; - Enabled = new Property(lifetime, "PresentationAssistant::Enabled"); - Enabled.FlowInto(lifetime, presentationAssistantWindowOwner.Enabled); + // Post to the UI thread so that the app has time to start before we show the message + threading.ExecuteOrQueue("Presentation Assistant initial message", () => + { + var settings = settingsStore.GetSettings(); + UpdateSettings(!settings.WelcomeMessageShown); + settings.WelcomeMessageShown = true; + settingsStore.SetSettings(settings); + }); + + // Post to the UI thread because settings change are raised on a background thread. + // Has the unfortunate side effect that the action disabling the assistant is shown + // very briefly, so we blacklist it + settingsStore.SettingsChanged.Advise(lifetime, _ => threading.ExecuteOrQueue("Presentation Assistant update enabled", () => UpdateSettings(true))); } - public Property Enabled { get; private set; } + private void UpdateSettings(bool showOwnActionImmediately) + { + var localSettings = settingsStore.GetSettings(); + presentationAssistantWindowOwner.Enabled.SetValue(localSettings.Enabled); + enabled = localSettings.Enabled; + + if (enabled && showOwnActionImmediately) + OnAction(PresentationAssistantAction.ActionId); + } // Implementing IActivityTracking is better than subscribing to ActionEvents, as // extensible workflow based actions (Refactor This, Navigate To, Generate, etc.) @@ -40,7 +64,7 @@ public PresentationAssistant(Lifetime lifetime, ActionFinder actionFinder, // get reported by ActionEvents public void TrackAction(string actionId) { - if (!Enabled.Value) + if (!enabled || ActionIdBlacklist.IsBlacklisted(actionId)) return; OnAction(actionId); @@ -48,18 +72,15 @@ public void TrackAction(string actionId) public void TrackActivity(string activityGroup, string activityId, int count = 1) { - if (!Enabled.Value) + if (!enabled) return; - if (activityGroup == "VsAction") + if (activityGroup == "VsAction" && !ActionIdBlacklist.IsBlacklisted(activityId)) OnAction(activityId); } private void OnAction(string actionId) { - if (ActionIdBlacklist.IsBlacklisted(actionId)) - return; - var def = actionFinder.Find(actionId); if (def == null) return; diff --git a/src/resharper-presentation-assistant/PresentationAssistantAction.cs b/src/resharper-presentation-assistant/PresentationAssistantAction.cs index b7d86bd..25bcb90 100644 --- a/src/resharper-presentation-assistant/PresentationAssistantAction.cs +++ b/src/resharper-presentation-assistant/PresentationAssistantAction.cs @@ -5,20 +5,24 @@ namespace JetBrains.ReSharper.Plugins.PresentationAssistant { - [Action("PresentationAssistant.Toggle", "Presentation Assistant", Description = "Enable and disable the presentation assistant", Id = 987987)] + [Action(ActionId, "Presentation Assistant", Description = "Enable and disable the presentation assistant", Id = 987987)] public class PresentationAssistantAction : ICheckableAction, IInsertLast { + public const string ActionId = "PresentationAssistant.Toggle"; + public bool Update(IDataContext context, CheckedActionPresentation presentation) { - var presentationAssistant = context.GetComponent(); - presentation.Checked = presentationAssistant.Enabled.Value; + var settings = context.GetComponent().GetSettings(); + presentation.Checked = settings.Enabled; return true; } public void Execute(IDataContext context) { - var presentationAssistant = context.GetComponent(); - presentationAssistant.Enabled.SetValue(!presentationAssistant.Enabled.Value); + var settingsStore = context.GetComponent(); + var settings = settingsStore.GetSettings(); + settings.Enabled = !settings.Enabled; + settingsStore.SetSettings(settings); } } } \ No newline at end of file diff --git a/src/resharper-presentation-assistant/PresentationAssistantSettings.cs b/src/resharper-presentation-assistant/PresentationAssistantSettings.cs new file mode 100644 index 0000000..a7fe9e2 --- /dev/null +++ b/src/resharper-presentation-assistant/PresentationAssistantSettings.cs @@ -0,0 +1,15 @@ +using JetBrains.Application.Settings; +using JetBrains.UI; + +namespace JetBrains.ReSharper.Plugins.PresentationAssistant +{ + [SettingsKey(typeof(UserInterfaceSettings), "Presentation Assistant settings")] + public class PresentationAssistantSettings + { + [SettingsEntry(true, "Enabled")] + public bool Enabled { get; set; } + + [SettingsEntry(false, "Welcome message shown")] + public bool WelcomeMessageShown { get; set; } + } +} \ No newline at end of file diff --git a/src/resharper-presentation-assistant/PresentationAssistantSettingsStore.cs b/src/resharper-presentation-assistant/PresentationAssistantSettingsStore.cs new file mode 100644 index 0000000..6fdbe47 --- /dev/null +++ b/src/resharper-presentation-assistant/PresentationAssistantSettingsStore.cs @@ -0,0 +1,56 @@ +using JetBrains.Application; +using JetBrains.Application.DataContext; +using JetBrains.Application.Settings; +using JetBrains.DataFlow; + +namespace JetBrains.ReSharper.Plugins.PresentationAssistant +{ + [ShellComponent] + public class PresentationAssistantSettingsStore + { + private readonly ISettingsStore settingsStore; + private readonly DataContexts dataContexts; + + public PresentationAssistantSettingsStore(Lifetime lifetime, ISettingsStore settingsStore, DataContexts dataContexts) + { + this.settingsStore = settingsStore; + this.dataContexts = dataContexts; + + SettingsChanged = new SimpleSignal(lifetime, "Presentation Assistant settings changed"); + + var key = settingsStore.Schema.GetKey(); + settingsStore.Changed.Advise(lifetime, args => + { + foreach (var changedEntry in args.ChangedEntries) + { + if (changedEntry.Parent == key) + { + if (changedEntry.LocalName == "Enabled") + SettingsChanged.Fire(); + break; + } + } + }); + } + + public SimpleSignal SettingsChanged { get; private set; } + + public PresentationAssistantSettings GetSettings() + { + var boundSettings = BindSettingsStore(); + return boundSettings.GetKey(SettingsOptimization.OptimizeDefault); + } + + public void SetSettings(PresentationAssistantSettings settings) + { + var boundSettings = BindSettingsStore(); + boundSettings.SetKey(settings, SettingsOptimization.OptimizeDefault); + } + + private IContextBoundSettingsStore BindSettingsStore() + { + var store = settingsStore.BindToContextTransient(ContextRange.Smart((l, _) => dataContexts.CreateOnSelection(l))); + return store; + } + } +} \ No newline at end of file diff --git a/src/resharper-presentation-assistant/PresentationAssistantWindowOwner.cs b/src/resharper-presentation-assistant/PresentationAssistantWindowOwner.cs index 1d011e6..76f2e26 100644 --- a/src/resharper-presentation-assistant/PresentationAssistantWindowOwner.cs +++ b/src/resharper-presentation-assistant/PresentationAssistantWindowOwner.cs @@ -1,9 +1,6 @@ using System; -using System.Runtime.InteropServices; -using System.Windows.Interop; using JetBrains.Application; using JetBrains.DataFlow; -using JetBrains.Interop.WinApi; using JetBrains.Threading; using JetBrains.UI.PopupWindowManager; using JetBrains.UI.Theming; @@ -14,7 +11,6 @@ namespace JetBrains.ReSharper.Plugins.PresentationAssistant public class PresentationAssistantWindowOwner { private static readonly TimeSpan VisibleTimeSpan = TimeSpan.FromSeconds(4); - private static readonly TimeSpan TopmostTimeSpan = TimeSpan.FromMilliseconds(200); private readonly IThreading threading; private readonly PresentationAssistantPopupWindowContext context; @@ -46,7 +42,6 @@ private void EnableShortcuts(Lifetime enabledLifetime) var popupWindowLifetimeDefinition = Lifetimes.Define(enabledLifetime, "PresentationAssistant::PopupWindow"); var window = new PresentationAssistantWindow(); - var windowInteropHelper = new WindowInteropHelper(window) { Owner = context.MainWindow.Handle }; theming.PopulateResourceDictionary(popupWindowLifetimeDefinition, window.Resources); diff --git a/src/resharper-presentation-assistant/Properties/AssemblyInfo.cs b/src/resharper-presentation-assistant/Properties/AssemblyInfo.cs index 6bbc8eb..dbafa34 100644 --- a/src/resharper-presentation-assistant/Properties/AssemblyInfo.cs +++ b/src/resharper-presentation-assistant/Properties/AssemblyInfo.cs @@ -7,9 +7,9 @@ [assembly: AssemblyTitle("resharper-presentation-assistant")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] +[assembly: AssemblyCompany("JetBrains")] [assembly: AssemblyProduct("resharper-presentation-assistant")] -[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,4 +31,4 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.3.0.*")] +[assembly: AssemblyVersion("2.3.1.0")] diff --git a/src/resharper-presentation-assistant/presentation-assistant.csproj b/src/resharper-presentation-assistant/presentation-assistant.csproj index 8e6718e..a27936e 100644 --- a/src/resharper-presentation-assistant/presentation-assistant.csproj +++ b/src/resharper-presentation-assistant/presentation-assistant.csproj @@ -239,6 +239,8 @@ + + PresentationAssistantWindow.xaml