diff --git a/Flow.Launcher.Infrastructure/UserSettings/Point2D.cs b/Flow.Launcher.Infrastructure/UserSettings/Point2D.cs new file mode 100644 index 000000000..4c47ba6c0 --- /dev/null +++ b/Flow.Launcher.Infrastructure/UserSettings/Point2D.cs @@ -0,0 +1,46 @@ +using System; + +namespace Flow.Launcher.Infrastructure.UserSettings; + +public record struct Point2D(double X, double Y) +{ + public static implicit operator Point2D((double X, double Y) point) + { + return new Point2D(point.X, point.Y); + } + + public static Point2D operator +(Point2D point1, Point2D point2) + { + return new Point2D(point1.X + point2.X, point1.Y + point2.Y); + } + + public static Point2D operator -(Point2D point1, Point2D point2) + { + return new Point2D(point1.X - point2.X, point1.Y - point2.Y); + } + + public static Point2D operator *(Point2D point, double scalar) + { + return new Point2D(point.X * scalar, point.Y * scalar); + } + + public static Point2D operator /(Point2D point, double scalar) + { + return new Point2D(point.X / scalar, point.Y / scalar); + } + + public static Point2D operator /(Point2D point1, Point2D point2) + { + return new Point2D(point1.X / point2.X, point1.Y / point2.Y); + } + + public static Point2D operator *(Point2D point1, Point2D point2) + { + return new Point2D(point1.X * point2.X, point1.Y * point2.Y); + } + + public Point2D Clamp(Point2D min, Point2D max) + { + return new Point2D(Math.Clamp(X, min.X, max.X), Math.Clamp(Y, min.Y, max.Y)); + } +} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index c44e0369a..c216efad2 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -203,12 +203,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool AutoUpdates { get; set; } = false; - public double WindowLeft { get; set; } - public double WindowTop { get; set; } - public double PreviousScreenWidth { get; set; } - public double PreviousScreenHeight { get; set; } - public double PreviousDpiX { get; set; } - public double PreviousDpiY { get; set; } + + public Point2D WindowPosition { get; set; } + public Point2D PreviousScreen { get; set; } + public Point2D PreviousDpi { get; set; } /// /// Custom left position on selected monitor diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index b9b82909d..3516d7e69 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -73,11 +73,7 @@ namespace Flow.Launcher }; } - DispatcherTimer timer = new DispatcherTimer - { - Interval = new TimeSpan(0, 0, 0, 0, 500), - IsEnabled = false - }; + DispatcherTimer timer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 500), IsEnabled = false }; public MainWindow() { @@ -88,6 +84,7 @@ namespace Flow.Launcher private const int WM_EXITSIZEMOVE = 0x0232; private int _initialWidth; private int _initialHeight; + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_ENTERSIZEMOVE) @@ -96,18 +93,22 @@ namespace Flow.Launcher _initialHeight = (int)Height; handled = true; } + if (msg == WM_EXITSIZEMOVE) { - if ( _initialHeight != (int)Height) + if (_initialHeight != (int)Height) { OnResizeEnd(); } + if (_initialWidth != (int)Width) { FlowMainWindow.SizeToContent = SizeToContent.Height; } + handled = true; } + return IntPtr.Zero; } @@ -132,6 +133,7 @@ namespace Flow.Launcher _settings.MaxResultsToShow = Convert.ToInt32(Math.Truncate(itemCount)); } } + FlowMainWindow.SizeToContent = SizeToContent.Height; _viewModel.MainWindowWidth = Width; } @@ -176,6 +178,7 @@ namespace Flow.Launcher private void OnInitialized(object sender, EventArgs e) { } + private void OnLoaded(object sender, RoutedEventArgs _) { // MouseEventHandler @@ -207,6 +210,7 @@ namespace Flow.Launcher { SoundPlay(); } + UpdatePosition(); PreviewReset(); Activate(); @@ -218,7 +222,8 @@ namespace Flow.Launcher _viewModel.LastQuerySelected = true; } - if (_viewModel.ProgressBarVisibility == Visibility.Visible && isProgressBarStoryboardPaused) + if (_viewModel.ProgressBarVisibility == Visibility.Visible && + isProgressBarStoryboardPaused) { _progressBarStoryboard.Begin(ProgressBar, true); isProgressBarStoryboardPaused = false; @@ -259,9 +264,12 @@ namespace Flow.Launcher MoveQueryTextToEnd(); _viewModel.QueryTextCursorMovedToEnd = false; } + break; case nameof(MainViewModel.GameModeStatus): - _notifyIcon.Icon = _viewModel.GameModeStatus ? Properties.Resources.gamemode : Properties.Resources.app; + _notifyIcon.Icon = _viewModel.GameModeStatus + ? Properties.Resources.gamemode + : Properties.Resources.app; break; } }; @@ -279,11 +287,8 @@ namespace Flow.Launcher case nameof(Settings.Hotkey): UpdateNotifyIconText(); break; - case nameof(Settings.WindowLeft): - Left = _settings.WindowLeft; - break; - case nameof(Settings.WindowTop): - Top = _settings.WindowTop; + case nameof(Settings.WindowPosition): + (Left, Top) = _settings.WindowPosition; break; } }; @@ -293,28 +298,24 @@ namespace Flow.Launcher { if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation) { - double previousScreenWidth = _settings.PreviousScreenWidth; - double previousScreenHeight = _settings.PreviousScreenHeight; - double previousDpiX, previousDpiY; - GetDpi(out previousDpiX, out previousDpiY); + var previousScreen = _settings.PreviousScreen; - _settings.PreviousScreenWidth = SystemParameters.VirtualScreenWidth; - _settings.PreviousScreenHeight = SystemParameters.VirtualScreenHeight; - double currentDpiX, currentDpiY; - GetDpi(out currentDpiX, out currentDpiY); + var previousDpi = _settings.PreviousDpi; - if (previousScreenWidth != 0 && previousScreenHeight != 0 && - previousDpiX != 0 && previousDpiY != 0 && - (previousScreenWidth != SystemParameters.VirtualScreenWidth || - previousScreenHeight != SystemParameters.VirtualScreenHeight || - previousDpiX != currentDpiX || previousDpiY != currentDpiY)) + _settings.PreviousScreen = (SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight); + + var currentDpi = GetDpi(); + + if (previousScreen != default || + previousDpi != default || + (previousScreen != (SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight) || + previousDpi != currentDpi)) { AdjustPositionForResolutionChange(); return; } - Left = _settings.WindowLeft; - Top = _settings.WindowTop; + (Left, Top) = _settings.WindowPosition; } else { @@ -338,8 +339,10 @@ namespace Flow.Launcher Top = VerticalTop(screen); break; case SearchWindowAligns.Custom: - var customLeft = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X + _settings.CustomWindowLeft, 0); - var customTop = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y + _settings.CustomWindowTop); + var customLeft = WindowsInteropHelper.TransformPixelsToDIP(this, + screen.WorkingArea.X + _settings.CustomWindowLeft, 0); + var customTop = WindowsInteropHelper.TransformPixelsToDIP(this, 0, + screen.WorkingArea.Y + _settings.CustomWindowTop); Left = customLeft.X; Top = customTop.Y; break; @@ -349,58 +352,48 @@ namespace Flow.Launcher private void AdjustPositionForResolutionChange() { - double screenWidth = SystemParameters.VirtualScreenWidth; - double screenHeight = SystemParameters.VirtualScreenHeight; - double currentDpiX, currentDpiY; - GetDpi(out currentDpiX, out currentDpiY); + Point2D screenBound = (SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight); - double previousLeft = _settings.WindowLeft; - double previousTop = _settings.WindowTop; - double previousDpiX, previousDpiY; - GetDpi(out previousDpiX, out previousDpiY); + var currentDpi = GetDpi(); - double widthRatio = screenWidth / _settings.PreviousScreenWidth; - double heightRatio = screenHeight / _settings.PreviousScreenHeight; - double dpiXRatio = currentDpiX / previousDpiX; - double dpiYRatio = currentDpiY / previousDpiY; + var previousPosition = _settings.WindowPosition; - double newLeft = previousLeft * widthRatio * dpiXRatio; - double newTop = previousTop * heightRatio * dpiYRatio; + var ratio = screenBound / _settings.PreviousScreen; - double screenLeft = SystemParameters.VirtualScreenLeft; - double screenTop = SystemParameters.VirtualScreenTop; + var dpiXRatio = currentDpi / _settings.PreviousDpi; - double maxX = screenLeft + screenWidth - ActualWidth; - double maxY = screenTop + screenHeight - ActualHeight; + var newPosition = previousPosition * ratio * dpiXRatio; - Left = Math.Max(screenLeft, Math.Min(newLeft, maxX)); - Top = Math.Max(screenTop, Math.Min(newTop, maxY)); + Point2D screenPosition = (SystemParameters.VirtualScreenLeft, SystemParameters.VirtualScreenTop); + + var maxPosition = screenPosition + screenBound - (ActualWidth, ActualHeight); + + (Left, Top) = newPosition.Clamp(screenPosition, maxPosition); } - private void GetDpi(out double dpiX, out double dpiY) + private Point2D GetDpi() { PresentationSource source = PresentationSource.FromVisual(this); - if (source != null && source.CompositionTarget != null) + Point2D point = (96, 96); + if (source is { CompositionTarget: not null }) { Matrix m = source.CompositionTarget.TransformToDevice; - dpiX = 96 * m.M11; - dpiY = 96 * m.M22; - } - else - { - dpiX = 96; - dpiY = 96; + point.X = 96 * m.M11; + point.Y = 96 * m.M22; } + + return point; } + private void UpdateNotifyIconText() { var menu = contextMenu; - ((MenuItem)menu.Items[0]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")"; + ((MenuItem)menu.Items[0]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + + " (" + _settings.Hotkey + ")"; ((MenuItem)menu.Items[1]).Header = InternationalizationManager.Instance.GetTranslation("GameMode"); ((MenuItem)menu.Items[2]).Header = InternationalizationManager.Instance.GetTranslation("PositionReset"); ((MenuItem)menu.Items[3]).Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"); ((MenuItem)menu.Items[4]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"); - } private void InitializeNotifyIcon() @@ -412,50 +405,34 @@ namespace Flow.Launcher Visible = !_settings.HideNotifyIcon }; - var openIcon = new FontIcon - { - Glyph = "\ue71e" - }; + var openIcon = new FontIcon { Glyph = "\ue71e" }; var open = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", + Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + + _settings.Hotkey + ")", Icon = openIcon }; - var gamemodeIcon = new FontIcon - { - Glyph = "\ue7fc" - }; + var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" }; var gamemode = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("GameMode"), - Icon = gamemodeIcon - }; - var positionresetIcon = new FontIcon - { - Glyph = "\ue73f" + Header = InternationalizationManager.Instance.GetTranslation("GameMode"), Icon = gamemodeIcon }; + var positionresetIcon = new FontIcon { Glyph = "\ue73f" }; var positionreset = new MenuItem { Header = InternationalizationManager.Instance.GetTranslation("PositionReset"), Icon = positionresetIcon }; - var settingsIcon = new FontIcon - { - Glyph = "\ue713" - }; + var settingsIcon = new FontIcon { Glyph = "\ue713" }; var settings = new MenuItem { Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), Icon = settingsIcon }; - var exitIcon = new FontIcon - { - Glyph = "\ue7e8" - }; + var exitIcon = new FontIcon { Glyph = "\ue7e8" }; var exit = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), - Icon = exitIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), Icon = exitIcon }; open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); @@ -488,6 +465,7 @@ namespace Flow.Launcher { _ = SetForegroundWindow(hwndSource.Handle); } + contextMenu.Focus(); break; } @@ -521,8 +499,10 @@ namespace Flow.Launcher private void InitProgressbarAnimation() { - var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); - var da1 = new DoubleAnimation(ProgressBar.X1, ActualWidth + 0, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 100, + new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da1 = new DoubleAnimation(ProgressBar.X1, ActualWidth + 0, + new Duration(new TimeSpan(0, 0, 0, 0, 1600))); Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)")); Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)")); _progressBarStoryboard.Children.Add(da); @@ -624,14 +604,14 @@ namespace Flow.Launcher iconsb.Children.Add(IconOpacity); windowsb.Completed += (_, _) => _animating = false; - _settings.WindowLeft = Left; - _settings.WindowTop = Top; + _settings.WindowPosition = (Left, Top); isArrowKeyPressed = false; if (QueryTextBox.Text.Length == 0) { clocksb.Begin(ClockPanel); } + iconsb.Begin(SearchIcon); windowsb.Begin(FlowMainWindow); } @@ -685,8 +665,7 @@ namespace Flow.Launcher private async void OnDeactivated(object sender, EventArgs e) { - _settings.WindowLeft = Left; - _settings.WindowTop = Top; + _settings.WindowPosition = (Left, Top); //This condition stops extra hide call when animator is on, // which causes the toggling to occasional hide instead of show. if (_viewModel.MainWindowVisibilityStatus) @@ -717,8 +696,7 @@ namespace Flow.Launcher return; if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation) { - _settings.WindowLeft = Left; - _settings.WindowTop = Top; + _settings.WindowPosition = (Left, Top); } } @@ -738,7 +716,7 @@ namespace Flow.Launcher public Screen SelectedScreen() { Screen screen = null; - switch(_settings.SearchWindowScreen) + switch (_settings.SearchWindowScreen) { case SearchWindowScreens.Cursor: screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); @@ -760,6 +738,7 @@ namespace Flow.Launcher screen = Screen.AllScreens[0]; break; } + return screen ?? Screen.AllScreens[0]; } @@ -797,7 +776,7 @@ namespace Flow.Launcher public double VerticalTop(Screen screen) { var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); - var top = dip1.Y +10; + var top = dip1.Y + 10; return top; } @@ -836,6 +815,7 @@ namespace Flow.Launcher _viewModel.LoadContextMenuCommand.Execute(null); e.Handled = true; } + break; case Key.Left: if (!_viewModel.SelectedIsFromQueryResults() && QueryTextBox.CaretIndex == 0) @@ -843,6 +823,7 @@ namespace Flow.Launcher _viewModel.EscCommand.Execute(null); e.Handled = true; } + break; case Key.Back: if (specialKeyState.CtrlPressed) @@ -861,12 +842,13 @@ namespace Flow.Launcher } } } + break; default: break; - } } + private void OnKeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Up || e.Key == Key.Down) @@ -882,6 +864,7 @@ namespace Flow.Launcher e.Handled = true; // Ignore Mouse Hover when press Arrowkeys } } + public void PreviewReset() { _viewModel.ResetPreview(); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index a793ceaa5..406d2af58 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -543,20 +543,20 @@ namespace Flow.Launcher.ViewModel private void IncreaseWidth() { Settings.WindowSize += 100; - Settings.WindowLeft -= 50; + Settings.WindowPosition -= (50, 0); OnPropertyChanged(nameof(MainWindowWidth)); } [RelayCommand] private void DecreaseWidth() { - if (MainWindowWidth - 100 < 400 || Settings.WindowSize == 400) + if (MainWindowWidth - 100 < 400 || Math.Abs(Settings.WindowSize - 400) < 0.01) { Settings.WindowSize = 400; } else { - Settings.WindowLeft += 50; + Settings.WindowPosition += (50, 0); Settings.WindowSize -= 100; }