diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index bfd7e4b87..7f62d82c8 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -195,6 +195,17 @@ namespace Flow.Launcher.Infrastructure.UserSettings public double WindowLeft { get; set; } public double WindowTop { get; set; } + + /// + /// Custom left position on selected monitor + /// + public double CustomWindowLeft { get; set; } = 0; + + /// + /// Custom top position on selected monitor + /// + public double CustomWindowTop { get; set; } = 0; + public int MaxResultsToShow { get; set; } = 5; public int ActivateTimes { get; set; } @@ -227,7 +238,15 @@ namespace Flow.Launcher.Infrastructure.UserSettings } public bool LeaveCmdOpen { get; set; } public bool HideWhenDeactivated { get; set; } = true; - public SearchWindowPositions SearchWindowPosition { get; set; } = SearchWindowPositions.MouseScreenCenter; + + [JsonConverter(typeof(JsonStringEnumConverter))] + public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.RememberLastLaunchLocation; + + [JsonConverter(typeof(JsonStringEnumConverter))] + public SearchWindowAligns SearchWindowAlign { get; set; } = SearchWindowAligns.Center; + + public int CustomScreenNumber { get; set; } = 1; + public bool IgnoreHotkeysOnFullscreen { get; set; } public HttpProxy Proxy { get; set; } = new HttpProxy(); @@ -253,12 +272,22 @@ namespace Flow.Launcher.Infrastructure.UserSettings Light, Dark } - public enum SearchWindowPositions + + public enum SearchWindowScreens { RememberLastLaunchLocation, - MouseScreenCenter, - MouseScreenCenterTop, - MouseScreenLeftTop, - MouseScreenRightTop + Cursor, + Focus, + Primary, + Custom + } + + public enum SearchWindowAligns + { + Center, + CenterTop, + LeftTop, + RightTop, + Custom } } diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs index 4811eb224..16a96cd64 100644 --- a/Flow.Launcher/Helper/WindowsInteropHelper.cs +++ b/Flow.Launcher/Helper/WindowsInteropHelper.cs @@ -35,25 +35,25 @@ namespace Flow.Launcher.Helper } [DllImport("user32.dll", SetLastError = true)] - private static extern int GetWindowLong(IntPtr hWnd, int nIndex); + internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll")] - private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + internal static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] - private static extern IntPtr GetForegroundWindow(); + internal static extern IntPtr GetDesktopWindow(); [DllImport("user32.dll")] - private static extern IntPtr GetDesktopWindow(); - - [DllImport("user32.dll")] - private static extern IntPtr GetShellWindow(); + internal static extern IntPtr GetShellWindow(); [DllImport("user32.dll", SetLastError = true)] - private static extern int GetWindowRect(IntPtr hwnd, out RECT rc); + internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.DLL")] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 9e86d9305..f18553903 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -38,11 +38,16 @@ Hide Flow Launcher when focus is lost Do not show new version notifications Search Window Position - Remember Last Position - Mouse Focused Screen - Center - Mouse Focused Screen - Center Top - Mouse Focused Screen - Left Top - Mouse Focused Screen - Right Top + Remember Last Position + Monitor with Mouse Cursor + Monitor with Focused Window + Primary Monitor + Custom Monitor + Center + Center Top + Left Top + Right Top + Custom Position Language Last Query Style Show/Hide previous results when Flow Launcher is reactivated. diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 2031bf3d8..4477da5f4 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -25,6 +25,7 @@ using System.Windows.Data; using ModernWpf.Controls; using Key = System.Windows.Input.Key; using System.Media; +using System.Linq; namespace Flow.Launcher { @@ -200,29 +201,39 @@ namespace Flow.Launcher private void InitializePosition() { - switch (_settings.SearchWindowPosition) + if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation) { - case SearchWindowPositions.RememberLastLaunchLocation: - Top = _settings.WindowTop; - Left = _settings.WindowLeft; - break; - case SearchWindowPositions.MouseScreenCenter: - Left = HorizonCenter(); - Top = VerticalCenter(); - break; - case SearchWindowPositions.MouseScreenCenterTop: - Left = HorizonCenter(); - Top = 10; - break; - case SearchWindowPositions.MouseScreenLeftTop: - Left = 10; - Top = 10; - break; - case SearchWindowPositions.MouseScreenRightTop: - Left = HorizonRight(); - Top = 10; - break; + Top = _settings.WindowTop; + Left = _settings.WindowLeft; } + else + { + var screen = SelectedScreen(); + switch (_settings.SearchWindowAlign) + { + case SearchWindowAligns.Center: + Left = HorizonCenter(screen); + Top = VerticalCenter(screen); + break; + case SearchWindowAligns.CenterTop: + Left = HorizonCenter(screen); + Top = 10; + break; + case SearchWindowAligns.LeftTop: + Left = HorizonLeft(screen); + Top = 10; + break; + case SearchWindowAligns.RightTop: + Left = HorizonRight(screen); + Top = 10; + break; + case SearchWindowAligns.Custom: + Left = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X + _settings.CustomWindowLeft, 0).X; + Top = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y + _settings.CustomWindowTop).Y; + break; + } + } + } private void UpdateNotifyIconText() @@ -339,8 +350,9 @@ namespace Flow.Launcher { _viewModel.Show(); await Task.Delay(300); // If don't give a time, Positioning will be weird. - Left = HorizonCenter(); - Top = VerticalCenter(); + var screen = SelectedScreen(); + Left = HorizonCenter(screen); + Top = VerticalCenter(screen); } private void InitProgressbarAnimation() @@ -502,7 +514,7 @@ namespace Flow.Launcher { if (_animating) return; - if (_settings.SearchWindowPosition == SearchWindowPositions.RememberLastLaunchLocation) + if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation) { _settings.WindowLeft = Left; _settings.WindowTop = Top; @@ -522,30 +534,62 @@ namespace Flow.Launcher } } - public double HorizonCenter() + public Screen SelectedScreen() + { + Screen screen = null; + switch(_settings.SearchWindowScreen) + { + case SearchWindowScreens.Cursor: + screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); + break; + case SearchWindowScreens.Primary: + screen = Screen.PrimaryScreen; + break; + case SearchWindowScreens.Focus: + IntPtr foregroundWindowHandle = WindowsInteropHelper.GetForegroundWindow(); + screen = Screen.FromHandle(foregroundWindowHandle); + break; + case SearchWindowScreens.Custom: + if (_settings.CustomScreenNumber <= Screen.AllScreens.Length) + screen = Screen.AllScreens[_settings.CustomScreenNumber - 1]; + else + screen = Screen.AllScreens[0]; + break; + default: + screen = Screen.AllScreens[0]; + break; + } + return screen ?? Screen.AllScreens[0]; + } + + public double HorizonCenter(Screen screen) { - var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); var left = (dip2.X - ActualWidth) / 2 + dip1.X; return left; } - public double VerticalCenter() + public double VerticalCenter(Screen screen) { - var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height); var top = (dip2.Y - QueryTextBox.ActualHeight) / 4 + dip1.Y; return top; } - public double HorizonRight() + public double HorizonRight(Screen screen) { - var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); - var left = (dip2.X - ActualWidth) - 10; + var left = (dip1.X + dip2.X - ActualWidth) - 10; + return left; + } + + public double HorizonLeft(Screen screen) + { + var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); + var left = dip1.X + 10; return left; } diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 07f23ff6a..a3b76ffe6 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -693,26 +693,135 @@ - - - - - - - -  - - + + + + + + + + + + + + + + + + +  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +  + + + + diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 37587ce59..f704b8200 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -22,6 +22,7 @@ using Flow.Launcher.Plugin.SharedModels; using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.Input; using System.Globalization; +using static Flow.Launcher.ViewModel.SettingWindowViewModel; namespace Flow.Launcher.ViewModel { @@ -463,23 +464,50 @@ namespace Flow.Launcher.ViewModel } } - public class SearchWindowPosition + public class SearchWindowScreen { public string Display { get; set; } - public SearchWindowPositions Value { get; set; } + public SearchWindowScreens Value { get; set; } } - public List SearchWindowPositions + public List SearchWindowScreens { get { - List modes = new List(); - var enums = (SearchWindowPositions[])Enum.GetValues(typeof(SearchWindowPositions)); + List modes = new List(); + var enums = (SearchWindowScreens[])Enum.GetValues(typeof(SearchWindowScreens)); foreach (var e in enums) { - var key = $"SearchWindowPosition{e}"; + var key = $"SearchWindowScreen{e}"; var display = _translater.GetTranslation(key); - var m = new SearchWindowPosition + var m = new SearchWindowScreen + { + Display = display, + Value = e, + }; + modes.Add(m); + } + return modes; + } + } + + public class SearchWindowAlign + { + public string Display { get; set; } + public SearchWindowAligns Value { get; set; } + } + + public List SearchWindowAligns + { + get + { + List modes = new List(); + var enums = (SearchWindowAligns[])Enum.GetValues(typeof(SearchWindowAligns)); + foreach (var e in enums) + { + var key = $"SearchWindowAlign{e}"; + var display = _translater.GetTranslation(key); + var m = new SearchWindowAlign { Display = display, Value = e, }; @@ -489,6 +517,20 @@ namespace Flow.Launcher.ViewModel } } + public List ScreenNumbers + { + get + { + var screens = System.Windows.Forms.Screen.AllScreens; + var screenNumbers = new List(); + for (int i = 1; i <= screens.Length; i++) + { + screenNumbers.Add(i); + } + return screenNumbers; + } + } + public List TimeFormatList { get; } = new() { "h:mm",