Flow.Launcher/Flow.Launcher/MainWindow.axaml.cs
Hongtao Zhang 90ec4c083a Merge branch 'velopack' into avalonia
# Conflicts:
#	Flow.Launcher.Core/Flow.Launcher.Core.csproj
#	Flow.Launcher.Infrastructure/Image/ImageCache.cs
#	Flow.Launcher.Infrastructure/Image/ImageLoader.cs
#	Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
#	Flow.Launcher/App.xaml.cs
#	Flow.Launcher/Flow.Launcher.csproj
#	Flow.Launcher/Helper/SingleInstance.cs
#	Flow.Launcher/MainWindow.axaml.cs
#	Flow.Launcher/MainWindow.xaml
#	Flow.Launcher/ResultListBox.xaml.cs
#	Scripts/post_build.ps1
2024-04-05 21:10:28 -05:00

726 lines
29 KiB
C#

using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Forms;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.ViewModel;
using Screen = System.Windows.Forms.Screen;
using ContextMenuStrip = System.Windows.Forms.ContextMenuStrip;
using DragEventArgs = System.Windows.DragEventArgs;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using NotifyIcon = System.Windows.Forms.NotifyIcon;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Plugin.SharedCommands;
using ModernWpf.Controls;
using Key = System.Windows.Input.Key;
using System.Media;
using System.Windows;
using Avalonia;
using Avalonia.Input;
using Avalonia.Threading;
using PropertyChanged;
using static Flow.Launcher.ViewModel.SettingWindowViewModel;
using ContextMenu = System.Windows.Controls.ContextMenu;
using MenuItem = System.Windows.Controls.MenuItem;
using MouseButton = System.Windows.Input.MouseButton;
using RoutedEventArgs = Avalonia.Interactivity.RoutedEventArgs;
using Thickness = System.Windows.Thickness;
using Window = Avalonia.Controls.Window;
using DataObject = System.Windows.DataObject;
namespace Flow.Launcher
{
[DoNotNotify]
public partial class MainWindow : Window
{
#region Private Fields
// private readonly Storyboard _progressBarStoryboard = new Storyboard();
private bool isProgressBarStoryboardPaused;
private Settings _settings;
private NotifyIcon _notifyIcon;
private ContextMenu contextMenu;
private MainViewModel _viewModel;
private bool _animating;
SoundPlayer animationSound = new SoundPlayer(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav");
#endregion
public MainWindow(Settings settings, MainViewModel mainVM)
{
DataContext = mainVM;
_viewModel = mainVM;
_settings = settings;
InitializeComponent();
// InitializePosition();DataObject.AddPastingHandler(QueryTextBox, OnPaste);
}
public MainWindow()
{
InitializeComponent();
}
// 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, ExecutedRoutedEventArgs e)
// {
// if (System.Windows.Clipboard.ContainsText())
// {
// _viewModel.ChangeQueryText(System.Windows.Clipboard.GetText().Replace("\n", String.Empty)
// .Replace("\r", String.Empty));
// e.Handled = true;
// }
// }
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.UnicodeText, true);
if (isText)
{
var text = e.SourceDataObject.GetData(System.Windows.DataFormats.UnicodeText) as string;
text = text.Replace(Environment.NewLine, " ");
DataObject data = new DataObject();
data.SetData(System.Windows.DataFormats.UnicodeText, text);
e.DataObject = data;
}
}
public async void OnClosing(object sender, CancelEventArgs e)
{
_notifyIcon.Visible = false;
App.API.SaveAppAllSettings();
e.Cancel = true;
await PluginManager.DisposePluginsAsync();
Notification.Uninstall();
Environment.Exit(0);
}
public void OnInitialized(object sender, EventArgs e)
{
}
public void OnLoaded(object sender, RoutedEventArgs _)
{
CheckFirstLaunch();
HideStartup();
// show notify icon when flowlauncher is hidden
InitializeNotifyIcon();
InitializeColorScheme();
// WindowsInteropHelper.DisableControlBox(this);
InitProgressbarAnimation();
// InitializePosition();
PreviewReset();
// since the default main window visibility is visible
// so we need set focus during startup
QueryTextBox.Focus();
_viewModel.PropertyChanged += (o, e) =>
{
switch (e.PropertyName)
{
case nameof(MainViewModel.MainWindowVisibilityStatus):
{
Dispatcher.UIThread.Invoke(() =>
{
if (_viewModel.MainWindowVisibilityStatus)
{
if (_settings.UseSound)
{
animationSound.Play();
}
UpdatePosition();
PreviewReset();
Activate();
QueryTextBox.Focus();
_settings.ActivateTimes++;
if (!_viewModel.LastQuerySelected)
{
QueryTextBox.SelectAll();
_viewModel.LastQuerySelected = true;
}
if (_viewModel.ProgressBarVisibility && isProgressBarStoryboardPaused)
{
// _progressBarStoryboard.Begin(ProgressBar, true);
isProgressBarStoryboardPaused = false;
}
// if (_settings.UseAnimation)
// WindowAnimator();
}
else if (!isProgressBarStoryboardPaused)
{
// _progressBarStoryboard.Stop(ProgressBar);
isProgressBarStoryboardPaused = true;
}
});
break;
}
case nameof(MainViewModel.ProgressBarVisibility):
{
Dispatcher.UIThread.Invoke(() =>
{
if (!_viewModel.ProgressBarVisibility && !isProgressBarStoryboardPaused)
{
// _progressBarStoryboard.Stop(ProgressBar);
isProgressBarStoryboardPaused = true;
}
else if (_viewModel.MainWindowVisibilityStatus &&
isProgressBarStoryboardPaused)
{
// _progressBarStoryboard.Begin(ProgressBar, true);
isProgressBarStoryboardPaused = false;
}
});
break;
}
case nameof(MainViewModel.QueryTextCursorMovedToEnd):
if (_viewModel.QueryTextCursorMovedToEnd)
{
MoveQueryTextToEnd();
_viewModel.QueryTextCursorMovedToEnd = false;
}
break;
case nameof(MainViewModel.GameModeStatus):
_notifyIcon.Icon = _viewModel.GameModeStatus
? Properties.Resources.gamemode
: Properties.Resources.app;
break;
}
};
_settings.PropertyChanged += (o, e) =>
{
switch (e.PropertyName)
{
case nameof(Settings.HideNotifyIcon):
_notifyIcon.Visible = !_settings.HideNotifyIcon;
break;
case nameof(Settings.Language):
UpdateNotifyIconText();
break;
case nameof(Settings.Hotkey):
UpdateNotifyIconText();
break;
case nameof(Settings.WindowLeft):
Position = new PixelPoint(_settings.WindowLeft, _settings.WindowTop);
break;
case nameof(Settings.WindowTop):
Position = new PixelPoint(_settings.WindowLeft, _settings.WindowTop);
break;
}
};
}
private void InitializePosition()
{
if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation)
{
Position = new PixelPoint(_settings.WindowLeft, _settings.WindowTop);
}
else
{
var screen = SelectedScreen();
switch (_settings.SearchWindowAlign)
{
case SearchWindowAligns.Center:
Position = new PixelPoint(HorizonCenter(screen), VerticalCenter(screen));
break;
case SearchWindowAligns.CenterTop:
Position = new PixelPoint(HorizonCenter(screen), 10);
break;
case SearchWindowAligns.LeftTop:
Position = new PixelPoint(HorizonLeft(screen), 10);
break;
case SearchWindowAligns.RightTop:
Position = new PixelPoint(HorizonRight(screen), 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()
{
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
{
Text = Infrastructure.Constant.FlowLauncher,
Icon = Properties.Resources.app,
Visible = !_settings.HideNotifyIcon
};
contextMenu = new ContextMenu();
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) => PositionReset();
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.ContextMenuStrip =
new ContextMenuStrip(); // it need for close the context menu. if not, context menu can't close.
_notifyIcon.MouseClick += (o, e) =>
{
switch (e.Button)
{
case MouseButtons.Left:
_viewModel.ToggleFlowLauncher();
break;
case MouseButtons.Right:
contextMenu.IsOpen = true;
break;
}
};
}
private void CheckFirstLaunch()
{
// if (_settings.FirstLaunch)
{
_settings.FirstLaunch = false;
PluginManager.API.SaveAppAllSettings();
OpenWelcomeWindow();
}
}
private void OpenWelcomeWindow()
{
var WelcomeWindow = new WelcomeWindow(_settings);
WelcomeWindow.Show(this);
}
private async void PositionReset()
{
_viewModel.Show();
await Task.Delay(300); // If don't give a time, Positioning will be weird.
var screen = SelectedScreen();
Position = new PixelPoint(HorizonCenter(screen), VerticalCenter(screen));
}
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)));
// Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)"));
// Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)"));
// _progressBarStoryboard.Children.Add(da);
// _progressBarStoryboard.Children.Add(da1);
// _progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever;
// _viewModel.ProgressBarVisibility = Visibility.Hidden;
// isProgressBarStoryboardPaused = true;
}
// public void WindowAnimator()
// {
// if (_animating)
// return;
//
// _animating = true;
// UpdatePosition();
//
// Storyboard windowsb = new Storyboard();
// Storyboard clocksb = new Storyboard();
// Storyboard iconsb = new Storyboard();
// CircleEase easing = new CircleEase();
// easing.EasingMode = EasingMode.EaseInOut;
//
// var animationLength = _settings.AnimationSpeed switch
// {
// AnimationSpeeds.Slow => 560,
// AnimationSpeeds.Medium => 360,
// AnimationSpeeds.Fast => 160,
// _ => _settings.CustomAnimationLength
// };
//
// var WindowOpacity = new DoubleAnimation
// {
// From = 0,
// To = 1,
// Duration = TimeSpan.FromMilliseconds(animationLength * 2 / 3),
// FillBehavior = FillBehavior.Stop
// };
//
// var WindowMotion = new DoubleAnimation
// {
// From = Top + 10,
// To = Top,
// Duration = TimeSpan.FromMilliseconds(animationLength * 2 / 3),
// FillBehavior = FillBehavior.Stop
// };
// var IconMotion = new DoubleAnimation
// {
// From = 12,
// To = 0,
// EasingFunction = easing,
// Duration = TimeSpan.FromMilliseconds(animationLength),
// FillBehavior = FillBehavior.Stop
// };
//
// var ClockOpacity = new DoubleAnimation
// {
// From = 0,
// To = 1,
// EasingFunction = easing,
// Duration = TimeSpan.FromMilliseconds(animationLength),
// FillBehavior = FillBehavior.Stop
// };
// double TargetIconOpacity = SearchIcon.Opacity; // Animation Target Opacity from Style
// var IconOpacity = new DoubleAnimation
// {
// From = 0,
// To = TargetIconOpacity,
// EasingFunction = easing,
// Duration = TimeSpan.FromMilliseconds(animationLength),
// FillBehavior = FillBehavior.Stop
// };
//
// double right = ClockPanel.Margin.Right;
// var thicknessAnimation = new ThicknessAnimation
// {
// From = new Thickness(0, 12, right, 0),
// To = new Thickness(0, 0, right, 0),
// EasingFunction = easing,
// Duration = TimeSpan.FromMilliseconds(animationLength),
// FillBehavior = FillBehavior.Stop
// };
//
// Storyboard.SetTargetProperty(ClockOpacity, new PropertyPath(OpacityProperty));
// Storyboard.SetTargetName(thicknessAnimation, "ClockPanel");
// Storyboard.SetTargetProperty(thicknessAnimation, new PropertyPath(MarginProperty));
// Storyboard.SetTarget(WindowOpacity, this);
// Storyboard.SetTargetProperty(WindowOpacity, new PropertyPath(Window.OpacityProperty));
// Storyboard.SetTargetProperty(WindowMotion, new PropertyPath(Window.TopProperty));
// Storyboard.SetTargetProperty(IconMotion, new PropertyPath(TopProperty));
// Storyboard.SetTargetProperty(IconOpacity, new PropertyPath(OpacityProperty));
//
// clocksb.Children.Add(thicknessAnimation);
// clocksb.Children.Add(ClockOpacity);
// windowsb.Children.Add(WindowOpacity);
// windowsb.Children.Add(WindowMotion);
// iconsb.Children.Add(IconMotion);
// iconsb.Children.Add(IconOpacity);
//
// windowsb.Completed += (_, _) => _animating = false;
// _settings.WindowLeft = Left;
// _settings.WindowTop = Top;
//
// if (QueryTextBox.Text.Length == 0)
// {
// clocksb.Begin(ClockPanel);
// }
//
// iconsb.Begin(SearchIcon);
// windowsb.Begin(FlowMainWindow);
// }
private void OnMouseDown(object sender, PointerPressedEventArgs e)
{
// if (e.Pointer.IsPrimary) 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)
{
return;
_settings.WindowLeft = Position.X;
_settings.WindowTop = Position.Y;
//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.Hide();
}
}
}
private void UpdatePosition()
{
if (_animating)
return;
// InitializePosition();
}
private void OnLocationChanged(object sender, EventArgs e)
{
if (_animating)
return;
if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation)
{
_settings.WindowLeft = Position.X;
_settings.WindowTop = Position.Y;
}
}
public void HideStartup()
{
UpdatePosition();
// if (_settings.HideOnStartup)
// {
// _viewModel.Hide();
// }
// else
{
_viewModel.Show();
}
}
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 int HorizonCenter(Screen screen)
{
var dip1 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
var dip2 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
var left = (dip2.X - Width) / 2 + dip1.X;
return (int) left;
}
public int VerticalCenter(Screen screen)
{
var dip1 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
var dip2 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
var top = (dip2.Y - QueryTextBox.Height) / 4 + dip1.Y;
return (int)top;
}
public int HorizonRight(Screen screen)
{
var dip1 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
var dip2 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
var left = (dip1.X + dip2.X - Width) - 10;
return (int) left;
}
public int HorizonLeft(Screen screen)
{
var dip1 = new PixelPoint(0,0); // WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
var left = dip1.X + 10;
return (int) left;
}
/// <summary>
/// Register up and down key
/// todo: any way to put this in xaml ?
/// </summary>
private void OnKeyDown(object sender, KeyEventArgs e)
{
var specialKeyState = GlobalHotkey.CheckModifiers();
switch (e.Key)
{
case Key.Down:
_viewModel.SelectNextItemCommand.Execute(null);
e.Handled = true;
break;
case Key.Up:
_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;
}
}
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.UIThread.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;
}
}
private void QueryTextBox_KeyUp(object sender, KeyEventArgs e)
{
if (_viewModel.QueryText != QueryTextBox.Text)
{
// BindingExpression be = QueryTextBox.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty);
// be.UpdateSource();
}
}
}
}