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",