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
}
}