From 7d62dedece537152dbb76035aeba99f42001ef2d Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 16 Mar 2025 23:10:12 +0800 Subject: [PATCH] Improve code quality --- Flow.Launcher/MainWindow.xaml | 6 +- Flow.Launcher/MainWindow.xaml.cs | 1062 +++++++++++++++--------------- 2 files changed, 535 insertions(+), 533 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 93d79d767..5b63303ac 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -20,13 +20,13 @@ Closing="OnClosing" Deactivated="OnDeactivated" Icon="Images/app.png" - Initialized="OnInitialized" Left="{Binding Settings.WindowLeft, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Loaded="OnLoaded" LocationChanged="OnLocationChanged" Opacity="{Binding MainWindowOpacity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" PreviewKeyDown="OnKeyDown" PreviewKeyUp="OnKeyUp" + PreviewMouseMove="OnPreviewMouseMove" ResizeMode="CanResize" ShowInTaskbar="False" SizeToContent="Height" @@ -240,14 +240,14 @@ FontSize="{Binding QueryBoxFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" InputMethod.PreferredImeConversionMode="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEConversionModeConverter}}" InputMethod.PreferredImeState="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEStateConverter}}" - PreviewDragOver="OnPreviewDragOver" + PreviewDragOver="QueryTextBox_OnPreviewDragOver" PreviewKeyUp="QueryTextBox_KeyUp" Style="{DynamicResource QueryBoxStyle}" Text="{Binding QueryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Visibility="Visible" WindowChrome.IsHitTestVisibleInChrome="True"> - + diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index f81f03f42..d0375e8f6 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -45,13 +45,19 @@ namespace Flow.Launcher private MediaPlayer animationSoundWMP; private SoundPlayer animationSoundWPF; + private int _initialWidth; + private int _initialHeight; + // Window Animations private Storyboard clocksb; private Storyboard iconsb; private Storyboard windowsb; + private bool _isClockPanelAnimating = false; // 애니메이션 실행 중인지 여부 #endregion + #region Constructor + public MainWindow(Settings settings, MainViewModel mainVM) { DataContext = mainVM; @@ -61,100 +67,14 @@ namespace Flow.Launcher InitializeComponent(); InitSoundEffects(); - DataObject.AddPastingHandler(QueryTextBox, OnPaste); + DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste); } - private int _initialWidth; - private int _initialHeight; + #endregion - private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) - { - if (Win32Helper.WM_ENTERSIZEMOVE(msg)) - { - _initialWidth = (int)Width; - _initialHeight = (int)Height; - handled = true; - } - else if (Win32Helper.WM_EXITSIZEMOVE(msg)) - { - if (_initialHeight != (int)Height) - { - OnResizeEnd(); - } + #region Window Event - if (_initialWidth != (int)Width) - { - FlowMainWindow.SizeToContent = SizeToContent.Height; - } - - handled = true; - } - - return IntPtr.Zero; - } - - private void OnResizeEnd() - { - int shadowMargin = 0; - if (_settings.UseDropShadowEffect) - { - shadowMargin = 32; - } - - if (!_settings.KeepMaxResults) - { - var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize; - - if (itemCount < 2) - { - _settings.MaxResultsToShow = 2; - } - else - { - _settings.MaxResultsToShow = Convert.ToInt32(Math.Truncate(itemCount)); - } - } - - FlowMainWindow.SizeToContent = SizeToContent.Height; - _viewModel.MainWindowWidth = Width; - } - - private void OnCopy(object sender, ExecutedRoutedEventArgs e) - { - var result = _viewModel.Results.SelectedItem?.Result; - if (QueryTextBox.SelectionLength == 0 && result != null) - { - string copyText = result.CopyText; - App.API.CopyToClipboard(copyText, directCopy: true); - } - else if (!string.IsNullOrEmpty(QueryTextBox.Text)) - { - App.API.CopyToClipboard(QueryTextBox.SelectedText, showDefaultNotification: false); - } - } - - private void OnPaste(object sender, DataObjectPastingEventArgs e) - { - var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true); - if (isText) - { - var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string; - text = text.Replace(Environment.NewLine, " "); - DataObject data = new DataObject(); - data.SetData(DataFormats.UnicodeText, text); - e.DataObject = data; - } - } - - private async void OnClosing(object sender, CancelEventArgs e) - { - _notifyIcon.Visible = false; - App.API.SaveAppAllSettings(); - e.Cancel = true; - await PluginManager.DisposePluginsAsync(); - Notification.Uninstall(); - Environment.Exit(0); - } +#pragma warning disable VSTHRD100 private void OnSourceInitialized(object sender, EventArgs e) { @@ -165,26 +85,52 @@ namespace Flow.Launcher Win32Helper.DisableControlBox(this); } - private void OnInitialized(object sender, EventArgs e) - { - } - private async void OnLoaded(object sender, RoutedEventArgs _) { - // MouseEventHandler - PreviewMouseMove += MainPreviewMouseMove; - CheckFirstLaunch(); - HideStartup(); + // Check first launch + if (_settings.FirstLaunch) + { + _settings.FirstLaunch = false; + App.API.SaveAppAllSettings(); + var WelcomeWindow = new WelcomeWindow(); + WelcomeWindow.Show(); + } + + // Hide window if need + if (_settings.HideOnStartup) + { + _viewModel.Hide(); + } + else + { + _viewModel.Show(); + } + // Show notify icon when flowlauncher is hidden InitializeNotifyIcon(); - InitializeColorScheme(); + + // Initialize color scheme + if (_settings.ColorScheme == Constant.Light) + { + ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Light; + } + else if (_settings.ColorScheme == Constant.Dark) + { + ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Dark; + } + + // Initialize position InitProgressbarAnimation(); - // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 - InitializePosition(); - InitializePosition(); + + // Force update position + UpdatePosition(true); + // Refresh frame await Ioc.Default.GetRequiredService().RefreshFrameAsync(); - PreviewReset(); + + // Reset preview + _viewModel.ResetPreview(); + // Since the default main window visibility is visible, so we need set focus during startup QueryTextBox.Focus(); @@ -193,37 +139,39 @@ namespace Flow.Launcher switch (e.PropertyName) { case nameof(MainViewModel.MainWindowVisibilityStatus): - { - Dispatcher.Invoke(() => { - if (_viewModel.MainWindowVisibilityStatus) + Dispatcher.Invoke(() => { - if (_settings.UseSound) + if (_viewModel.MainWindowVisibilityStatus) { - SoundPlay(); - } + if (_settings.UseSound) + { + SoundPlay(); + } - UpdatePosition(); - PreviewReset(); - Activate(); - QueryTextBox.Focus(); - _settings.ActivateTimes++; - if (!_viewModel.LastQuerySelected) - { - QueryTextBox.SelectAll(); - _viewModel.LastQuerySelected = true; - } + UpdatePosition(false); + _viewModel.ResetPreview(); + Activate(); + QueryTextBox.Focus(); + _settings.ActivateTimes++; + if (!_viewModel.LastQuerySelected) + { + QueryTextBox.SelectAll(); + _viewModel.LastQuerySelected = true; + } - if (_settings.UseAnimation) - WindowAnimator(); - } - }); - break; - } + if (_settings.UseAnimation) + WindowAnimation(); + } + }); + break; + } case nameof(MainViewModel.QueryTextCursorMovedToEnd): if (_viewModel.QueryTextCursorMovedToEnd) { - MoveQueryTextToEnd(); + // QueryTextBox seems to be update with a DispatcherPriority as low as ContextIdle. + // To ensure QueryTextBox is up to date with QueryText from the View, we need to Dispatch with such a priority + Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); _viewModel.QueryTextCursorMovedToEnd = false; } @@ -271,6 +219,356 @@ namespace Flow.Launcher .AddValueChanged(History, (s, e) => UpdateClockPanelVisibility()); } + private async void OnClosing(object sender, CancelEventArgs e) + { + _notifyIcon.Visible = false; + App.API.SaveAppAllSettings(); + e.Cancel = true; + await PluginManager.DisposePluginsAsync(); + Notification.Uninstall(); + Environment.Exit(0); + } + + private void OnLocationChanged(object sender, EventArgs e) + { + if (_animating) + return; + if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation) + { + _settings.WindowLeft = Left; + _settings.WindowTop = Top; + } + } + + private async void OnDeactivated(object sender, EventArgs e) + { + _settings.WindowLeft = Left; + _settings.WindowTop = Top; + //This condition stops extra hide call when animator is on, + // which causes the toggling to occasional hide instead of show. + if (_viewModel.MainWindowVisibilityStatus) + { + // Need time to initialize the main query window animation. + // This also stops the mainwindow from flickering occasionally after Settings window is opened + // and always after Settings window is closed. + if (_settings.UseAnimation) + await Task.Delay(100); + + if (_settings.HideWhenDeactivated && !_viewModel.ExternalPreviewVisible) + { + _viewModel.Hide(); + } + } + } + + private void OnKeyDown(object sender, KeyEventArgs e) + { + var specialKeyState = GlobalHotkey.CheckModifiers(); + switch (e.Key) + { + case Key.Down: + isArrowKeyPressed = true; + _viewModel.SelectNextItemCommand.Execute(null); + e.Handled = true; + break; + case Key.Up: + isArrowKeyPressed = true; + _viewModel.SelectPrevItemCommand.Execute(null); + e.Handled = true; + break; + case Key.PageDown: + _viewModel.SelectNextPageCommand.Execute(null); + e.Handled = true; + break; + case Key.PageUp: + _viewModel.SelectPrevPageCommand.Execute(null); + e.Handled = true; + break; + case Key.Right: + if (_viewModel.SelectedIsFromQueryResults() + && QueryTextBox.CaretIndex == QueryTextBox.Text.Length + && !string.IsNullOrEmpty(QueryTextBox.Text)) + { + _viewModel.LoadContextMenuCommand.Execute(null); + e.Handled = true; + } + + break; + case Key.Left: + if (!_viewModel.SelectedIsFromQueryResults() && QueryTextBox.CaretIndex == 0) + { + _viewModel.EscCommand.Execute(null); + e.Handled = true; + } + + break; + case Key.Back: + if (specialKeyState.CtrlPressed) + { + if (_viewModel.SelectedIsFromQueryResults() + && QueryTextBox.Text.Length > 0 + && QueryTextBox.CaretIndex == QueryTextBox.Text.Length) + { + var queryWithoutActionKeyword = + QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search; + + if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword)) + { + _viewModel.BackspaceCommand.Execute(null); + e.Handled = true; + } + } + } + + break; + default: + break; + } + } + + private void OnKeyUp(object sender, KeyEventArgs e) + { + if (e.Key == Key.Up || e.Key == Key.Down) + { + isArrowKeyPressed = false; + } + } + + private void OnPreviewMouseMove(object sender, MouseEventArgs e) + { + if (isArrowKeyPressed) + { + e.Handled = true; // Ignore Mouse Hover when press Arrowkeys + } + } + +#pragma warning restore VSTHRD100 + + #endregion + + #region Window Boarder Event + + private void OnMouseDown(object sender, MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Left) DragMove(); + } + + #endregion + + #region Window Context Menu Event + +#pragma warning disable VSTHRD100 + + private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e) + { + _viewModel.Hide(); + + if (_settings.UseAnimation) + await Task.Delay(100); + + App.API.OpenSettingDialog(); + } + +#pragma warning restore VSTHRD100 + + #endregion + + #region Window WndProc + + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + if (Win32Helper.WM_ENTERSIZEMOVE(msg)) + { + _initialWidth = (int)Width; + _initialHeight = (int)Height; + handled = true; + } + else if (Win32Helper.WM_EXITSIZEMOVE(msg)) + { + if (_initialHeight != (int)Height) + { + var shadowMargin = 0; + if (_settings.UseDropShadowEffect) + { + shadowMargin = 32; + } + + if (!_settings.KeepMaxResults) + { + var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize; + + if (itemCount < 2) + { + _settings.MaxResultsToShow = 2; + } + else + { + _settings.MaxResultsToShow = Convert.ToInt32(Math.Truncate(itemCount)); + } + } + + SizeToContent = SizeToContent.Height; + _viewModel.MainWindowWidth = Width; + } + + if (_initialWidth != (int)Width) + { + SizeToContent = SizeToContent.Height; + } + + handled = true; + } + + return IntPtr.Zero; + } + + #endregion + + #region Window Sound Effects + + private void InitSoundEffects() + { + if (_settings.WMPInstalled) + { + animationSoundWMP = new MediaPlayer(); + animationSoundWMP.Open(new Uri(AppContext.BaseDirectory + "Resources\\open.wav")); + } + else + { + animationSoundWPF = new SoundPlayer(AppContext.BaseDirectory + "Resources\\open.wav"); + } + } + + private void SoundPlay() + { + if (_settings.WMPInstalled) + { + animationSoundWMP.Position = TimeSpan.Zero; + animationSoundWMP.Volume = _settings.SoundVolume / 100.0; + animationSoundWMP.Play(); + } + else + { + animationSoundWPF.Play(); + } + } + + #endregion + + #region Window Notify Icon + + private void InitializeNotifyIcon() + { + _notifyIcon = new NotifyIcon + { + Text = Constant.FlowLauncherFullName, + Icon = Constant.Version == "1.0.0" ? Properties.Resources.dev : Properties.Resources.app, + Visible = !_settings.HideNotifyIcon + }; + var openIcon = new FontIcon { Glyph = "\ue71e" }; + var open = new MenuItem + { + Header = App.API.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", + Icon = openIcon + }; + var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" }; + var gamemode = new MenuItem + { + Header = App.API.GetTranslation("GameMode"), + Icon = gamemodeIcon + }; + var positionresetIcon = new FontIcon { Glyph = "\ue73f" }; + var positionreset = new MenuItem + { + Header = App.API.GetTranslation("PositionReset"), + Icon = positionresetIcon + }; + var settingsIcon = new FontIcon { Glyph = "\ue713" }; + var settings = new MenuItem + { + Header = App.API.GetTranslation("iconTraySettings"), + Icon = settingsIcon + }; + var exitIcon = new FontIcon { Glyph = "\ue7e8" }; + var exit = new MenuItem + { + Header = App.API.GetTranslation("iconTrayExit"), + Icon = exitIcon + }; + + open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); + gamemode.Click += (o, e) => _viewModel.ToggleGameMode(); + positionreset.Click += (o, e) => _ = PositionResetAsync(); + settings.Click += (o, e) => App.API.OpenSettingDialog(); + exit.Click += (o, e) => Close(); + + gamemode.ToolTip = App.API.GetTranslation("GameModeToolTip"); + positionreset.ToolTip = App.API.GetTranslation("PositionResetToolTip"); + + contextMenu.Items.Add(open); + contextMenu.Items.Add(gamemode); + contextMenu.Items.Add(positionreset); + contextMenu.Items.Add(settings); + contextMenu.Items.Add(exit); + + _notifyIcon.MouseClick += (o, e) => + { + switch (e.Button) + { + case System.Windows.Forms.MouseButtons.Left: + _viewModel.ToggleFlowLauncher(); + break; + case System.Windows.Forms.MouseButtons.Right: + + contextMenu.IsOpen = true; + // Get context menu handle and bring it to the foreground + if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource) + { + Win32Helper.SetForegroundWindow(hwndSource.Handle); + } + + contextMenu.Focus(); + break; + } + }; + } + + private void UpdateNotifyIconText() + { + var menu = contextMenu; + ((MenuItem)menu.Items[0]).Header = App.API.GetTranslation("iconTrayOpen") + + " (" + _settings.Hotkey + ")"; + ((MenuItem)menu.Items[1]).Header = App.API.GetTranslation("GameMode"); + ((MenuItem)menu.Items[2]).Header = App.API.GetTranslation("PositionReset"); + ((MenuItem)menu.Items[3]).Header = App.API.GetTranslation("iconTraySettings"); + ((MenuItem)menu.Items[4]).Header = App.API.GetTranslation("iconTrayExit"); + } + + #endregion + + #region Window Position + + private void UpdatePosition(bool force) + { + if (_animating && !force) + { + return; + } + + // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 + InitializePosition(); + InitializePosition(); + } + + private async Task PositionResetAsync() + { + _viewModel.Show(); + await Task.Delay(300); // If don't give a time, Positioning will be weird. + var screen = SelectedScreen(); + Left = HorizonCenter(screen); + Top = VerticalCenter(screen); + } + private void InitializePosition() { // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 @@ -317,119 +615,70 @@ namespace Flow.Launcher } } - private void UpdateNotifyIconText() + private Screen SelectedScreen() { - var menu = contextMenu; - ((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() - { - _notifyIcon = new NotifyIcon + Screen screen; + switch (_settings.SearchWindowScreen) { - Text = Constant.FlowLauncherFullName, - Icon = Constant.Version == "1.0.0" ? Properties.Resources.dev : Properties.Resources.app, - Visible = !_settings.HideNotifyIcon - }; - var openIcon = new FontIcon { Glyph = "\ue71e" }; - var open = new MenuItem - { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + - _settings.Hotkey + ")", - Icon = openIcon - }; - var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" }; - var gamemode = new MenuItem - { - 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 settings = new MenuItem - { - Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), - Icon = settingsIcon - }; - var exitIcon = new FontIcon { Glyph = "\ue7e8" }; - var exit = new MenuItem - { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), - Icon = exitIcon - }; - - open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); - gamemode.Click += (o, e) => _viewModel.ToggleGameMode(); - positionreset.Click += (o, e) => _ = PositionResetAsync(); - settings.Click += (o, e) => App.API.OpenSettingDialog(); - exit.Click += (o, e) => Close(); - - gamemode.ToolTip = InternationalizationManager.Instance.GetTranslation("GameModeToolTip"); - positionreset.ToolTip = InternationalizationManager.Instance.GetTranslation("PositionResetToolTip"); - - contextMenu.Items.Add(open); - contextMenu.Items.Add(gamemode); - contextMenu.Items.Add(positionreset); - contextMenu.Items.Add(settings); - contextMenu.Items.Add(exit); - - _notifyIcon.MouseClick += (o, e) => - { - switch (e.Button) - { - case System.Windows.Forms.MouseButtons.Left: - _viewModel.ToggleFlowLauncher(); - break; - case System.Windows.Forms.MouseButtons.Right: - - contextMenu.IsOpen = true; - // Get context menu handle and bring it to the foreground - if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource) - { - Win32Helper.SetForegroundWindow(hwndSource.Handle); - } - - contextMenu.Focus(); - break; - } - }; - } - - private void CheckFirstLaunch() - { - if (_settings.FirstLaunch) - { - _settings.FirstLaunch = false; - App.API.SaveAppAllSettings(); - OpenWelcomeWindow(); + case SearchWindowScreens.Cursor: + screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); + break; + case SearchWindowScreens.Primary: + screen = Screen.PrimaryScreen; + break; + case SearchWindowScreens.Focus: + var foregroundWindowHandle = Win32Helper.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]; } - private static void OpenWelcomeWindow() + private double HorizonCenter(Screen screen) { - var WelcomeWindow = new WelcomeWindow(); - WelcomeWindow.Show(); + var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); + var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); + var left = (dip2.X - ActualWidth) / 2 + dip1.X; + return left; } - private async Task PositionResetAsync() + private double VerticalCenter(Screen screen) { - _viewModel.Show(); - await Task.Delay(300); // If don't give a time, Positioning will be weird. - var screen = SelectedScreen(); - Left = HorizonCenter(screen); - Top = VerticalCenter(screen); + var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); + var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height); + var top = (dip2.Y - QueryTextBox.ActualHeight) / 4 + dip1.Y; + return top; } + private double HorizonRight(Screen screen) + { + var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); + var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); + var left = (dip1.X + dip2.X - ActualWidth) - 10; + return left; + } + + private double HorizonLeft(Screen screen) + { + var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); + var left = dip1.X + 10; + return left; + } + + #endregion + + #region Window Animation + private void InitProgressbarAnimation() { var progressBarStoryBoard = new Storyboard(); @@ -477,27 +726,14 @@ namespace Flow.Launcher _viewModel.ProgressBarVisibility = Visibility.Hidden; } - public void ResetAnimation() - { - // 애니메이션 중지 - clocksb?.Stop(ClockPanel); - iconsb?.Stop(SearchIcon); - windowsb?.Stop(FlowMainWindow); - - // UI 요소 상태 초기화 - //ClockPanel.Margin = new Thickness(0, 0, ClockPanel.Margin.Right, 0); - ClockPanel.Opacity = 0; - SearchIcon.Opacity = 0; - } - - public void WindowAnimator() + private void WindowAnimation() { if (_animating) return; isArrowKeyPressed = true; _animating = true; - UpdatePosition(); + UpdatePosition(false); windowsb = new Storyboard(); clocksb = new Storyboard(); @@ -604,43 +840,9 @@ namespace Flow.Launcher } iconsb.Begin(SearchIcon); - windowsb.Begin(FlowMainWindow); + windowsb.Begin(this); } - private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0) - { - if (style == null) - return defaultOpacity; - - foreach (Setter setter in style.Setters.Cast()) - { - if (setter.Property == OpacityProperty) - { - return setter.Value is double opacity ? opacity : defaultOpacity; - } - } - - return defaultOpacity; - } - - private static Thickness GetThicknessFromStyle(Style style, Thickness defaultThickness) - { - if (style == null) - return defaultThickness; - - foreach (Setter setter in style.Setters.Cast()) - { - if (setter.Property == MarginProperty) - { - return setter.Value is Thickness thickness ? thickness : defaultThickness; - } - } - - return defaultThickness; - } - - private bool _isClockPanelAnimating = false; // 애니메이션 실행 중인지 여부 - private void UpdateClockPanelVisibility() { if (QueryTextBox == null || ContextMenu == null || History == null || ClockPanel == null) @@ -726,273 +928,66 @@ namespace Flow.Launcher } } - private void InitSoundEffects() + private static double GetOpacityFromStyle(Style style, double defaultOpacity = 1.0) { - if (_settings.WMPInstalled) + if (style == null) + return defaultOpacity; + + foreach (Setter setter in style.Setters.Cast()) { - animationSoundWMP = new MediaPlayer(); - animationSoundWMP.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); - } - else - { - animationSoundWPF = new SoundPlayer(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav"); - } - } - - private void SoundPlay() - { - if (_settings.WMPInstalled) - { - animationSoundWMP.Position = TimeSpan.Zero; - animationSoundWMP.Volume = _settings.SoundVolume / 100.0; - animationSoundWMP.Play(); - } - else - { - animationSoundWPF.Play(); - } - } - - private void OnMouseDown(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) DragMove(); - } - - private void OnPreviewDragOver(object sender, DragEventArgs e) - { - e.Handled = true; - } - - private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e) - { - _viewModel.Hide(); - - if (_settings.UseAnimation) - await Task.Delay(100); - - App.API.OpenSettingDialog(); - } - - private async void OnDeactivated(object sender, EventArgs e) - { - _settings.WindowLeft = Left; - _settings.WindowTop = Top; - //This condition stops extra hide call when animator is on, - // which causes the toggling to occasional hide instead of show. - if (_viewModel.MainWindowVisibilityStatus) - { - // Need time to initialize the main query window animation. - // This also stops the mainwindow from flickering occasionally after Settings window is opened - // and always after Settings window is closed. - if (_settings.UseAnimation) - await Task.Delay(100); - - if (_settings.HideWhenDeactivated && !_viewModel.ExternalPreviewVisible) + if (setter.Property == OpacityProperty) { - _viewModel.Hide(); + return setter.Value is double opacity ? opacity : defaultOpacity; } } + + return defaultOpacity; } - private void UpdatePosition() + private static Thickness GetThicknessFromStyle(Style style, Thickness defaultThickness) { - if (_animating) - return; + if (style == null) + return defaultThickness; - // Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910 - InitializePosition(); - InitializePosition(); - } - - private void OnLocationChanged(object sender, EventArgs e) - { - if (_animating) - return; - if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation) + foreach (Setter setter in style.Setters.Cast()) { - _settings.WindowLeft = Left; - _settings.WindowTop = Top; + if (setter.Property == MarginProperty) + { + return setter.Value is Thickness thickness ? thickness : defaultThickness; + } + } + + return defaultThickness; + } + + #endregion + + #region QueryTextBox Event + + private void QueryTextBox_OnCopy(object sender, ExecutedRoutedEventArgs e) + { + var result = _viewModel.Results.SelectedItem?.Result; + if (QueryTextBox.SelectionLength == 0 && result != null) + { + string copyText = result.CopyText; + App.API.CopyToClipboard(copyText, directCopy: true); + } + else if (!string.IsNullOrEmpty(QueryTextBox.Text)) + { + App.API.CopyToClipboard(QueryTextBox.SelectedText, showDefaultNotification: false); } } - public void HideStartup() + private void QueryTextBox_OnPaste(object sender, DataObjectPastingEventArgs e) { - if (_settings.HideOnStartup) + var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true); + if (isText) { - _viewModel.Hide(); - } - else - { - _viewModel.Show(); - } - } - - public Screen SelectedScreen() - { - Screen screen; - 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: - var foregroundWindowHandle = Win32Helper.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 dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); - var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); - var left = (dip2.X - ActualWidth) / 2 + dip1.X; - return left; - } - - public double VerticalCenter(Screen screen) - { - var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); - var dip2 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height); - var top = (dip2.Y - QueryTextBox.ActualHeight) / 4 + dip1.Y; - return top; - } - - public double HorizonRight(Screen screen) - { - var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); - var dip2 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); - var left = (dip1.X + dip2.X - ActualWidth) - 10; - return left; - } - - public double HorizonLeft(Screen screen) - { - var dip1 = Win32Helper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); - var left = dip1.X + 10; - return left; - } - - /// - /// Register up and down key - /// todo: Put this in xaml? - /// - private void OnKeyDown(object sender, KeyEventArgs e) - { - var specialKeyState = GlobalHotkey.CheckModifiers(); - switch (e.Key) - { - case Key.Down: - isArrowKeyPressed = true; - _viewModel.SelectNextItemCommand.Execute(null); - e.Handled = true; - break; - case Key.Up: - isArrowKeyPressed = true; - _viewModel.SelectPrevItemCommand.Execute(null); - e.Handled = true; - break; - case Key.PageDown: - _viewModel.SelectNextPageCommand.Execute(null); - e.Handled = true; - break; - case Key.PageUp: - _viewModel.SelectPrevPageCommand.Execute(null); - e.Handled = true; - break; - case Key.Right: - if (_viewModel.SelectedIsFromQueryResults() - && QueryTextBox.CaretIndex == QueryTextBox.Text.Length - && !string.IsNullOrEmpty(QueryTextBox.Text)) - { - _viewModel.LoadContextMenuCommand.Execute(null); - e.Handled = true; - } - - break; - case Key.Left: - if (!_viewModel.SelectedIsFromQueryResults() && QueryTextBox.CaretIndex == 0) - { - _viewModel.EscCommand.Execute(null); - e.Handled = true; - } - - break; - case Key.Back: - if (specialKeyState.CtrlPressed) - { - if (_viewModel.SelectedIsFromQueryResults() - && QueryTextBox.Text.Length > 0 - && QueryTextBox.CaretIndex == QueryTextBox.Text.Length) - { - var queryWithoutActionKeyword = - QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search; - - if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword)) - { - _viewModel.BackspaceCommand.Execute(null); - e.Handled = true; - } - } - } - - break; - default: - break; - } - } - - private void OnKeyUp(object sender, KeyEventArgs e) - { - if (e.Key == Key.Up || e.Key == Key.Down) - { - isArrowKeyPressed = false; - } - } - - private void MainPreviewMouseMove(object sender, MouseEventArgs e) - { - if (isArrowKeyPressed) - { - e.Handled = true; // Ignore Mouse Hover when press Arrowkeys - } - } - - public void PreviewReset() - { - _viewModel.ResetPreview(); - } - - private void MoveQueryTextToEnd() - { - // QueryTextBox seems to be update with a DispatcherPriority as low as ContextIdle. - // To ensure QueryTextBox is up to date with QueryText from the View, we need to Dispatch with such a priority - Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); - } - - public void InitializeColorScheme() - { - if (_settings.ColorScheme == Constant.Light) - { - ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Light; - } - else if (_settings.ColorScheme == Constant.Dark) - { - ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Dark; + var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string; + text = text.Replace(Environment.NewLine, " "); + DataObject data = new DataObject(); + data.SetData(DataFormats.UnicodeText, text); + e.DataObject = data; } } @@ -1004,5 +999,12 @@ namespace Flow.Launcher be.UpdateSource(); } } + + private void QueryTextBox_OnPreviewDragOver(object sender, DragEventArgs e) + { + e.Handled = true; + } + + #endregion } }