diff --git a/src/Carnac.Logic/Carnac.Logic.csproj b/src/Carnac.Logic/Carnac.Logic.csproj index 915b663a..6b14133a 100644 --- a/src/Carnac.Logic/Carnac.Logic.csproj +++ b/src/Carnac.Logic/Carnac.Logic.csproj @@ -38,6 +38,9 @@ false + + ..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll + @@ -93,6 +96,7 @@ + diff --git a/src/Carnac.Logic/KeyProvider.cs b/src/Carnac.Logic/KeyProvider.cs index 66f05236..4e8beda1 100644 --- a/src/Carnac.Logic/KeyProvider.cs +++ b/src/Carnac.Logic/KeyProvider.cs @@ -10,6 +10,7 @@ using Carnac.Logic.Models; using Microsoft.Win32; using System.Windows.Media; +using Carnac.Logic.MouseMonitor; using SettingsProviderNet; using System.Text.RegularExpressions; @@ -96,7 +97,10 @@ public IObservable GetKeyStream() winKeyPressed = false; }, observer.OnError); - var keyStreamSubsription = interceptKeysSource.GetKeyStream() + var keyStreamSubsription = Observable.Merge( + new IObservable[2] { + interceptKeysSource.GetKeyStream(), + InterceptMouse.Current.GetKeyStream() }) .Select(DetectWindowsKey) .Where(k => !IsModifierKeyPress(k) && k.KeyDirection == KeyDirection.Down) .Select(ToCarnacKeyPress) @@ -160,6 +164,7 @@ static IEnumerable ToInputs(bool isLetter, bool isWinKeyPressed, Interce var controlPressed = interceptKeyEventArgs.ControlPressed; var altPressed = interceptKeyEventArgs.AltPressed; var shiftPressed = interceptKeyEventArgs.ShiftPressed; + var mouseAction = InterceptMouse.MouseKeys.Contains(interceptKeyEventArgs.Key); if (controlPressed) yield return "Ctrl"; if (altPressed) @@ -167,7 +172,7 @@ static IEnumerable ToInputs(bool isLetter, bool isWinKeyPressed, Interce if (isWinKeyPressed) yield return "Win"; - if (controlPressed || altPressed) + if (controlPressed || altPressed || mouseAction) { //Treat as a shortcut, don't be too smart if (shiftPressed) diff --git a/src/Carnac.Logic/Models/Message.cs b/src/Carnac.Logic/Models/Message.cs index 43c46cd0..84e9793e 100644 --- a/src/Carnac.Logic/Models/Message.cs +++ b/src/Carnac.Logic/Models/Message.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Windows.Media; +using Carnac.Logic.MouseMonitor; namespace Carnac.Logic.Models { @@ -30,7 +31,8 @@ public Message(KeyPress key) { processName = key.Process.ProcessName; processIcon = key.Process.ProcessIcon; - canBeMerged = !key.HasModifierPressed; + // allow to aggregate all key combination as to not spam if ctrl + mousewheel is used. + canBeMerged = true; // !key.HasModifierPressed; isModifier = key.HasModifierPressed; keys = new ReadOnlyCollection(new[] { key }); @@ -109,6 +111,28 @@ public static Message MergeIfNeeded(Message previousMessage, Message newMessage) return ShouldCreateNewMessage(previousMessage, newMessage) ? newMessage : previousMessage.Merge(newMessage); + /* + * Code used when mouse and show modifiers standalone are mixed together + */ + /* + // replace key was after standalone modifier keypress, replace by new Message + if (previousMessage.keys != null && KeyProvider.IsModifierKeyPress(previousMessage.keys[0].InterceptKeyEventArgs)) + { + return previousMessage.Replace(newMessage); + } + // if current is modifier and previous is a mouse action ignore modifierkeypress + if (previousMessage.keys != null && KeyProvider.IsModifierKeyPress(newMessage.keys[0].InterceptKeyEventArgs) + && InterceptMouse.MouseKeys.Contains(previousMessage.keys[0].Key)) + { + return previousMessage.Replace(previousMessage); + } + + if (ShouldCreateNewMessage(previousMessage, newMessage)) + { + return newMessage; + } + return previousMessage.Merge(newMessage); + */ } static bool ShouldCreateNewMessage(Message previous, Message current) @@ -116,7 +140,11 @@ static bool ShouldCreateNewMessage(Message previous, Message current) return previous.ProcessName != current.ProcessName || current.LastMessage.Subtract(previous.LastMessage) > OneSecond || !previous.CanBeMerged || - !current.CanBeMerged; + !current.CanBeMerged || + // new message for different mouse keys; + ((InterceptMouse.MouseKeys.Contains(current.keys[0].Key) || + (previous.keys != null && InterceptMouse.MouseKeys.Contains(previous.keys[0].Key))) + && !previous.keys[0].Input.SequenceEqual(current.keys[0].Input)); ; } public Message FadeOut() diff --git a/src/Carnac.Logic/Models/PopupSettings.cs b/src/Carnac.Logic/Models/PopupSettings.cs index b2be6486..8195e3b4 100644 --- a/src/Carnac.Logic/Models/PopupSettings.cs +++ b/src/Carnac.Logic/Models/PopupSettings.cs @@ -93,5 +93,40 @@ public string SortDescription public bool SettingsConfigured { get; set; } public bool ShowOnlyModifiers { get; set; } public bool ShowSpaceAsUnicode { get; set; } + + /* Following mouse-related settings */ + [DefaultValue(true)] + public bool ShowMouseClicks { get; set; } + + [DefaultValue(true)] + public bool ShowMousePopup { get; set; } + + [DefaultValue("YellowGreen")] + public string LeftClickColor { get; set; } + + [DefaultValue("RoyalBlue")] + public string RightClickColor { get; set; } + + [DefaultValue(1)] + public double ClickStartScale { get; set; } + + [DefaultValue(4)] + public double ClickStopScale { get; set; } + + [DefaultValue(3700)] + public int ClickFadeDelay { get; set; } + + [DefaultValue(1)] + public double ClickStartBorder { get; set; } + + [DefaultValue(0.8)] + public double ClickStartOpacity { get; set; } + + [DefaultValue(2)] + public double ClickStopBorder { get; set; } + + [DefaultValue(0)] + public double ClickStopOpacity { get; set; } + public string ClickColor { get; set; } } } diff --git a/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs b/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs new file mode 100644 index 00000000..b6562528 --- /dev/null +++ b/src/Carnac.Logic/MouseMonitor/InterceptMouse.cs @@ -0,0 +1,111 @@ +using Carnac.Logic.KeyMonitor; +using System; +using System.Diagnostics; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Windows.Forms; +using Gma.System.MouseKeyHook; +using System.Collections.Generic; + +namespace Carnac.Logic.MouseMonitor +{ + public class InterceptMouse : IInterceptKeys + { + + public static readonly InterceptMouse Current = new InterceptMouse(); + readonly IKeyboardMouseEvents m_GlobalHook = Hook.GlobalEvents(); + readonly IObservable keyStream; + private IObserver observer; + private readonly KeysConverter kc = new KeysConverter(); + + public static readonly List MouseKeys = new List() + { + Keys.LButton, + Keys.MButton, + Keys.RButton, + Keys.XButton1, + Keys.XButton2, + Keys.VolumeUp, + Keys.VolumeDown + }; + + InterceptMouse() + { + keyStream = Observable.Create(observer => + { + this.observer = observer; + m_GlobalHook.MouseClick += OnMouseClick; + m_GlobalHook.MouseDoubleClick += OnMouseDoubleClick; + m_GlobalHook.MouseWheel += HookManager_MouseWheel; + Debug.Write("Subscribed to mouse"); + + return Disposable.Create(() => + { + m_GlobalHook.MouseClick -= OnMouseClick; + m_GlobalHook.MouseDoubleClick -= OnMouseDoubleClick; + m_GlobalHook.MouseWheel -= HookManager_MouseWheel; + m_GlobalHook.Dispose(); + Debug.Write("Unsubscribed from mouse"); + }); + }) + .Publish().RefCount(); + + } + + private Keys MouseButtonsToKeys(MouseButtons button) + { + switch (button) + { + case MouseButtons.Left: + return Keys.LButton; + case MouseButtons.Middle: + return Keys.MButton; + case MouseButtons.Right: + return Keys.RButton; + case MouseButtons.XButton1: + return Keys.XButton1; + case MouseButtons.XButton2: + return Keys.XButton2; + default: + return Keys.None; + } + } + + private void OnMouseClick(object sender, MouseEventArgs e) + { + observer.OnNext(new InterceptKeyEventArgs( + MouseButtonsToKeys(e.Button), + KeyDirection.Down, + Control.ModifierKeys == Keys.Alt, + Control.ModifierKeys == Keys.Control, + Control.ModifierKeys == Keys.Shift)); + } + + private void OnMouseDoubleClick(object sender, MouseEventArgs e) + { + observer.OnNext(new InterceptKeyEventArgs( + MouseButtonsToKeys(e.Button), + KeyDirection.Down, + Control.ModifierKeys == Keys.Alt, + Control.ModifierKeys == Keys.Control, + Control.ModifierKeys == Keys.Shift)); + } + + private void HookManager_MouseWheel(object sender, MouseEventArgs e) + { + // for now using VolumeDown and Up as proxy could be refactored + observer.OnNext(new InterceptKeyEventArgs( + e.Delta > 0 ? Keys.VolumeUp : Keys.VolumeDown, + KeyDirection.Down, + Control.ModifierKeys == Keys.Alt, + Control.ModifierKeys == Keys.Control, + Control.ModifierKeys == Keys.Shift)); + } + + public IObservable GetKeyStream() + { + return keyStream; + } + + } +} \ No newline at end of file diff --git a/src/Carnac.Logic/Properties/AssemblyInfo.cs b/src/Carnac.Logic/Properties/AssemblyInfo.cs index fbaf1e20..89b0c8d4 100644 --- a/src/Carnac.Logic/Properties/AssemblyInfo.cs +++ b/src/Carnac.Logic/Properties/AssemblyInfo.cs @@ -31,8 +31,8 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("2.3.0.0")] -[assembly: AssemblyVersion("2.3.0.0")] -[assembly: AssemblyFileVersion("2.3.0.0")] +// [assembly: AssemblyVersion("2.4.0.0")] +[assembly: AssemblyVersion("2.4.0.0")] +[assembly: AssemblyFileVersion("2.4.0.0")] -[assembly: AssemblyInformationalVersion("2.3.0-ci.32+Branch.master.Sha.69515520313d5c26261a8b05660255cd9da08bfa")] +[assembly: AssemblyInformationalVersion("2.4.0-mouse-presenter.1+4.Branch.feature-mouse-presenter.Sha.ac8188c012650eb9eda0fba4233527d9c4933fb7")] diff --git a/src/Carnac.Logic/packages.config b/src/Carnac.Logic/packages.config index 6d600c42..041b9458 100644 --- a/src/Carnac.Logic/packages.config +++ b/src/Carnac.Logic/packages.config @@ -1,6 +1,7 @@  + diff --git a/src/Carnac.Tests/Properties/AssemblyInfo.cs b/src/Carnac.Tests/Properties/AssemblyInfo.cs index bd014ecc..cc79c23a 100644 --- a/src/Carnac.Tests/Properties/AssemblyInfo.cs +++ b/src/Carnac.Tests/Properties/AssemblyInfo.cs @@ -31,8 +31,8 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("2.3.0.0")] -[assembly: AssemblyVersion("2.3.0.0")] -[assembly: AssemblyFileVersion("2.3.0.0")] +// [assembly: AssemblyVersion("2.4.0.0")] +[assembly: AssemblyVersion("2.4.0.0")] +[assembly: AssemblyFileVersion("2.4.0.0")] -[assembly: AssemblyInformationalVersion("2.3.0-ci.32+Branch.master.Sha.69515520313d5c26261a8b05660255cd9da08bfa")] +[assembly: AssemblyInformationalVersion("2.4.0-mouse-presenter.1+4.Branch.feature-mouse-presenter.Sha.ac8188c012650eb9eda0fba4233527d9c4933fb7")] diff --git a/src/Carnac/App.xaml.cs b/src/Carnac/App.xaml.cs index 040c9b84..441b4309 100644 --- a/src/Carnac/App.xaml.cs +++ b/src/Carnac/App.xaml.cs @@ -83,6 +83,7 @@ protected override void OnExit(ExitEventArgs e) { trayIcon.Dispose(); carnac.Dispose(); + keyShowView.Dispose(); ProcessUtilities.DestroyMutex(); base.OnExit(e); diff --git a/src/Carnac/Carnac.csproj b/src/Carnac/Carnac.csproj index 04636d02..53c1c9ca 100644 --- a/src/Carnac/Carnac.csproj +++ b/src/Carnac/Carnac.csproj @@ -87,6 +87,9 @@ ..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.PatchApi.dll + + ..\packages\MouseKeyHook.5.6.0\lib\net40\Gma.System.MouseKeyHook.dll + ..\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll diff --git a/src/Carnac/CarnacTrayIcon.cs b/src/Carnac/CarnacTrayIcon.cs index cf776940..30e25c49 100644 --- a/src/Carnac/CarnacTrayIcon.cs +++ b/src/Carnac/CarnacTrayIcon.cs @@ -19,12 +19,17 @@ public CarnacTrayIcon() Text = Properties.Resources.ShellView_Exit }; + var PreferencesMenuItem = new MenuItem + { + Text = Properties.Resources.ShellView_Preferences + }; + var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Carnac.icon.embedded.ico"); trayIcon = new NotifyIcon { Icon = new Icon(iconStream), - ContextMenu = new ContextMenu(new[] { exitMenuItem }) + ContextMenu = new ContextMenu(new[] { PreferencesMenuItem, exitMenuItem }) }; exitMenuItem.Click += (sender, args) => @@ -32,6 +37,12 @@ public CarnacTrayIcon() trayIcon.Visible = false; Application.Current.Shutdown(); }; + + PreferencesMenuItem.Click += (sender, args) => + { + ShowPreferencesWindow(); + }; + trayIcon.MouseClick += NotifyIconClick; trayIcon.Visible = true; } @@ -42,15 +53,20 @@ void NotifyIconClick(object sender, MouseEventArgs mouseEventArgs) { if (mouseEventArgs.Button == MouseButtons.Left) { - var preferencesWindow = Application.Current.Windows.Cast().FirstOrDefault(x => x.Name == "PreferencesViewWindow"); - if (preferencesWindow != null) - { - preferencesWindow.Activate(); - } - else - { - OpenPreferences(); - } + ShowPreferencesWindow(); + } + } + + public void ShowPreferencesWindow() + { + var preferencesWindow = Application.Current.Windows.Cast().FirstOrDefault(x => x.Name == "PreferencesViewWindow"); + if (preferencesWindow != null) + { + preferencesWindow.Activate(); + } + else + { + OpenPreferences(); } } diff --git a/src/Carnac/Properties/AssemblyInfo.cs b/src/Carnac/Properties/AssemblyInfo.cs index 2ccb2557..322377f6 100644 --- a/src/Carnac/Properties/AssemblyInfo.cs +++ b/src/Carnac/Properties/AssemblyInfo.cs @@ -48,7 +48,7 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("2.3.0.0")] -[assembly: AssemblyVersion("2.3.0.0")] -[assembly: AssemblyFileVersion("2.3.0.0")] -[assembly: AssemblyInformationalVersion("2.3.0-ci.32+Branch.master.Sha.69515520313d5c26261a8b05660255cd9da08bfa")] +// [assembly: AssemblyVersion("2.4.0.0")] +[assembly: AssemblyVersion("2.4.0.0")] +[assembly: AssemblyFileVersion("2.4.0.0")] +[assembly: AssemblyInformationalVersion("2.4.0-mouse-presenter.1+4.Branch.feature-mouse-presenter.Sha.ac8188c012650eb9eda0fba4233527d9c4933fb7")] diff --git a/src/Carnac/Properties/Resources.Designer.cs b/src/Carnac/Properties/Resources.Designer.cs index 5a19a7ce..40a8c0e2 100644 --- a/src/Carnac/Properties/Resources.Designer.cs +++ b/src/Carnac/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Carnac.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -68,5 +68,14 @@ internal static string ShellView_Exit { return ResourceManager.GetString("ShellView_Exit", resourceCulture); } } + + /// + /// Looks up a localized string similar to Preferences. + /// + internal static string ShellView_Preferences { + get { + return ResourceManager.GetString("ShellView_Preferences", resourceCulture); + } + } } } diff --git a/src/Carnac/Properties/Resources.resx b/src/Carnac/Properties/Resources.resx index 8acde8dc..af198c3e 100644 --- a/src/Carnac/Properties/Resources.resx +++ b/src/Carnac/Properties/Resources.resx @@ -46,7 +46,7 @@ mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with - : System.Serialization.Formatters.Binary.BinaryFormatter + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 @@ -60,6 +60,7 @@ : and then encoded with base64 encoding. --> + @@ -68,9 +69,10 @@ - + + @@ -85,9 +87,10 @@ - + + @@ -109,12 +112,15 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Exit + + Preferences + \ No newline at end of file diff --git a/src/Carnac/UI/KeyShowView.xaml b/src/Carnac/UI/KeyShowView.xaml index 4fb16157..4d8a13c4 100644 --- a/src/Carnac/UI/KeyShowView.xaml +++ b/src/Carnac/UI/KeyShowView.xaml @@ -24,7 +24,7 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Carnac/UI/KeyShowView.xaml.cs b/src/Carnac/UI/KeyShowView.xaml.cs index 72abbdf7..1b615fd3 100644 --- a/src/Carnac/UI/KeyShowView.xaml.cs +++ b/src/Carnac/UI/KeyShowView.xaml.cs @@ -3,16 +3,22 @@ using System.Timers; using System.Windows; using System.Windows.Interop; +using System.Windows.Media.Animation; using Carnac.Logic; +using Gma.System.MouseKeyHook; namespace Carnac.UI { - public partial class KeyShowView + public partial class KeyShowView : IDisposable { + private Storyboard sb; + IKeyboardMouseEvents m_GlobalHook = null; + public KeyShowView(KeyShowViewModel keyShowViewModel) { DataContext = keyShowViewModel; InitializeComponent(); + keyShowViewModel.Settings.PropertyChanged += Settings_PropertyChanged; } protected override void OnSourceInitialized(EventArgs e) @@ -37,6 +43,18 @@ protected override void OnSourceInitialized(EventArgs e) Left = vm.Settings.Left; vm.Settings.LeftChanged += SettingsLeftChanged; WindowState = WindowState.Maximized; + if (vm.Settings.ShowMouseClicks) + { + SetupMouseEvents(); + } + } + + public void Dispose() + { + if (m_GlobalHook != null) + { + m_GlobalHook.Dispose(); + } } [DllImport("user32.dll", SetLastError = true)] @@ -82,7 +100,7 @@ public static readonly int private void WindowLoaded(object sender, RoutedEventArgs e) { - + sb = this.FindResource("clickHighlighterStoryboard") as Storyboard; } void SettingsLeftChanged(object sender, EventArgs e) @@ -92,5 +110,69 @@ void SettingsLeftChanged(object sender, EventArgs e) Left = vm.Settings.Left; WindowState = WindowState.Maximized; } + + void Settings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + var vm = ((KeyShowViewModel)DataContext); + switch (e.PropertyName) + { + case "ClickFadeDelay": + Duration d = TimeSpan.FromMilliseconds(vm.Settings.ClickFadeDelay); + foreach (DoubleAnimation da in sb.Children) + { + da.Duration = d; + } + break; + case "ShowMouseClicks": + if (vm.Settings.ShowMouseClicks) + { + SetupMouseEvents(); + } + else + { + DestroyMouseEvents(); + } + break; + } + } + + void SetupMouseEvents() + { + if (m_GlobalHook == null) + { + m_GlobalHook = Hook.GlobalEvents(); + } + m_GlobalHook.MouseDown += OnMouseDown; + m_GlobalHook.MouseMove += OnMouseMove; + } + + void DestroyMouseEvents() + { + if (m_GlobalHook == null) + { + return; + } + m_GlobalHook.MouseDown -= OnMouseDown; + m_GlobalHook.MouseMove -= OnMouseMove; + m_GlobalHook.Dispose(); + m_GlobalHook = null; + } + + private void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + var vm = ((KeyShowViewModel)DataContext); + vm.Settings.ClickColor = vm.Settings.LeftClickColor; + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + vm.Settings.ClickColor = vm.Settings.RightClickColor; + } + sb.Begin(); + } + + private void OnMouseMove(object sender, System.Windows.Forms.MouseEventArgs e) + { + var vm = ((KeyShowViewModel)DataContext); + vm.CursorPosition = PointFromScreen(new Point(e.X, e.Y)); + } } } diff --git a/src/Carnac/UI/KeyShowViewModel.cs b/src/Carnac/UI/KeyShowViewModel.cs index 590608cf..c2381988 100644 --- a/src/Carnac/UI/KeyShowViewModel.cs +++ b/src/Carnac/UI/KeyShowViewModel.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using System.Windows; using Carnac.Logic; using Carnac.Logic.Models; @@ -15,5 +16,12 @@ public KeyShowViewModel(PopupSettings popupSettings) public ObservableCollection Messages { get; private set; } public PopupSettings Settings { get; set; } + + public Point CursorPosition { get; set; } + + public Thickness CursorMargins + { + get { return new Thickness(CursorPosition.X - 10, CursorPosition.Y - 10, 0, 0); } + } } } diff --git a/src/Carnac/UI/PreferencesView.xaml b/src/Carnac/UI/PreferencesView.xaml index 583487c1..d4732207 100644 --- a/src/Carnac/UI/PreferencesView.xaml +++ b/src/Carnac/UI/PreferencesView.xaml @@ -95,7 +95,7 @@ - + @@ -166,6 +166,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +