mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
526 lines
18 KiB
C#
526 lines
18 KiB
C#
using Flow.Launcher.Core.Plugin;
|
|
using Flow.Launcher.Core.Resource;
|
|
using Flow.Launcher.Helper;
|
|
using Flow.Launcher.Infrastructure;
|
|
using Flow.Launcher.Infrastructure.Hotkey;
|
|
using Flow.Launcher.Infrastructure.UserSettings;
|
|
using Flow.Launcher.Plugin;
|
|
using Flow.Launcher.ViewModel;
|
|
using ModernWpf;
|
|
using ModernWpf.Controls;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Security.Policy;
|
|
using System.Windows;
|
|
using System.Windows.Data;
|
|
using System.Windows.Forms;
|
|
using System.Windows.Input;
|
|
using System.Windows.Interop;
|
|
using System.Windows.Navigation;
|
|
using Button = System.Windows.Controls.Button;
|
|
using Control = System.Windows.Controls.Control;
|
|
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
|
|
using MessageBox = System.Windows.MessageBox;
|
|
using TextBox = System.Windows.Controls.TextBox;
|
|
using ThemeManager = ModernWpf.ThemeManager;
|
|
|
|
namespace Flow.Launcher
|
|
{
|
|
public partial class SettingWindow
|
|
{
|
|
public readonly IPublicAPI API;
|
|
private Settings settings;
|
|
private SettingWindowViewModel viewModel;
|
|
|
|
public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel)
|
|
{
|
|
settings = viewModel.Settings;
|
|
DataContext = viewModel;
|
|
this.viewModel = viewModel;
|
|
API = api;
|
|
InitializePosition();
|
|
InitializeComponent();
|
|
|
|
}
|
|
|
|
#region General
|
|
|
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
|
{
|
|
RefreshMaximizeRestoreButton();
|
|
// Fix (workaround) for the window freezes after lock screen (Win+L)
|
|
// https://stackoverflow.com/questions/4951058/software-rendering-mode-wpf
|
|
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
|
|
HwndTarget hwndTarget = hwndSource.CompositionTarget;
|
|
hwndTarget.RenderMode = RenderMode.SoftwareOnly;
|
|
|
|
pluginListView = (CollectionView)CollectionViewSource.GetDefaultView(Plugins.ItemsSource);
|
|
pluginListView.Filter = PluginListFilter;
|
|
|
|
pluginStoreView = (CollectionView)CollectionViewSource.GetDefaultView(StoreListBox.ItemsSource);
|
|
pluginStoreView.Filter = PluginStoreFilter;
|
|
|
|
InitializePosition();
|
|
}
|
|
|
|
private void OnSelectPythonPathClick(object sender, RoutedEventArgs e)
|
|
{
|
|
var selectedFile = viewModel.GetFileFromDialog(
|
|
InternationalizationManager.Instance.GetTranslation("selectPythonExecutable"),
|
|
"Python|pythonw.exe");
|
|
|
|
if (!string.IsNullOrEmpty(selectedFile))
|
|
settings.PluginSettings.PythonExecutablePath = selectedFile;
|
|
}
|
|
|
|
private void OnSelectNodePathClick(object sender, RoutedEventArgs e)
|
|
{
|
|
var selectedFile = viewModel.GetFileFromDialog(
|
|
InternationalizationManager.Instance.GetTranslation("selectNodeExecutable"));
|
|
|
|
if (!string.IsNullOrEmpty(selectedFile))
|
|
settings.PluginSettings.NodeExecutablePath = selectedFile;
|
|
}
|
|
|
|
private void OnSelectFileManagerClick(object sender, RoutedEventArgs e)
|
|
{
|
|
SelectFileManagerWindow fileManagerChangeWindow = new SelectFileManagerWindow(settings);
|
|
fileManagerChangeWindow.ShowDialog();
|
|
}
|
|
|
|
private void OnSelectDefaultBrowserClick(object sender, RoutedEventArgs e)
|
|
{
|
|
var browserWindow = new SelectBrowserWindow(settings);
|
|
browserWindow.ShowDialog();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Hotkey
|
|
|
|
private void OnHotkeyControlLoaded(object sender, RoutedEventArgs e)
|
|
{
|
|
_ = HotkeyControl.SetHotkeyAsync(viewModel.Settings.Hotkey, false);
|
|
}
|
|
|
|
private void OnHotkeyControlFocused(object sender, RoutedEventArgs e)
|
|
{
|
|
HotKeyMapper.RemoveHotkey(settings.Hotkey);
|
|
}
|
|
|
|
private void OnHotkeyControlFocusLost(object sender, RoutedEventArgs e)
|
|
{
|
|
if (HotkeyControl.CurrentHotkeyAvailable)
|
|
{
|
|
HotKeyMapper.SetHotkey(HotkeyControl.CurrentHotkey, HotKeyMapper.OnToggleHotkey);
|
|
HotKeyMapper.RemoveHotkey(settings.Hotkey);
|
|
settings.Hotkey = HotkeyControl.CurrentHotkey.ToString();
|
|
}
|
|
else
|
|
{
|
|
HotKeyMapper.SetHotkey(new HotkeyModel(settings.Hotkey), HotKeyMapper.OnToggleHotkey);
|
|
}
|
|
}
|
|
|
|
private void OnPreviewHotkeyControlLoaded(object sender, RoutedEventArgs e)
|
|
{
|
|
_ = PreviewHotkeyControl.SetHotkeyAsync(settings.PreviewHotkey, false);
|
|
}
|
|
|
|
private void OnPreviewHotkeyControlFocusLost(object sender, RoutedEventArgs e)
|
|
{
|
|
if (PreviewHotkeyControl.CurrentHotkeyAvailable)
|
|
{
|
|
settings.PreviewHotkey = PreviewHotkeyControl.CurrentHotkey.ToString();
|
|
}
|
|
}
|
|
|
|
private void OnDeleteCustomHotkeyClick(object sender, RoutedEventArgs e)
|
|
{
|
|
var item = viewModel.SelectedCustomPluginHotkey;
|
|
if (item == null)
|
|
{
|
|
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
|
return;
|
|
}
|
|
|
|
string deleteWarning =
|
|
string.Format(InternationalizationManager.Instance.GetTranslation("deleteCustomHotkeyWarning"),
|
|
item.Hotkey);
|
|
if (
|
|
MessageBox.Show(deleteWarning, InternationalizationManager.Instance.GetTranslation("delete"),
|
|
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
|
{
|
|
settings.CustomPluginHotkeys.Remove(item);
|
|
HotKeyMapper.RemoveHotkey(item.Hotkey);
|
|
}
|
|
}
|
|
|
|
private void OnEditCustomHotkeyClick(object sender, RoutedEventArgs e)
|
|
{
|
|
var item = viewModel.SelectedCustomPluginHotkey;
|
|
if (item != null)
|
|
{
|
|
CustomQueryHotkeySetting window = new CustomQueryHotkeySetting(this, settings);
|
|
window.UpdateItem(item);
|
|
window.ShowDialog();
|
|
}
|
|
else
|
|
{
|
|
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
|
}
|
|
}
|
|
|
|
private void OnAddCustomHotkeyClick(object sender, RoutedEventArgs e)
|
|
{
|
|
new CustomQueryHotkeySetting(this, settings).ShowDialog();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Plugin
|
|
|
|
private void OnPluginToggled(object sender, RoutedEventArgs e)
|
|
{
|
|
var id = viewModel.SelectedPlugin.PluginPair.Metadata.ID;
|
|
// used to sync the current status from the plugin manager into the setting to keep consistency after save
|
|
settings.PluginSettings.Plugins[id].Disabled = viewModel.SelectedPlugin.PluginPair.Metadata.Disabled;
|
|
}
|
|
|
|
private void OnPluginPriorityClick(object sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is Control { DataContext: PluginViewModel pluginViewModel })
|
|
{
|
|
PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(pluginViewModel.PluginPair.Metadata.ID, pluginViewModel);
|
|
priorityChangeWindow.ShowDialog();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Proxy
|
|
|
|
private void OnTestProxyClick(object sender, RoutedEventArgs e)
|
|
{ // TODO: change to command
|
|
var msg = viewModel.TestProxy();
|
|
MessageBox.Show(msg); // TODO: add message box service
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void OnCheckUpdates(object sender, RoutedEventArgs e)
|
|
{
|
|
viewModel.UpdateApp(); // TODO: change to command
|
|
}
|
|
|
|
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
|
|
{
|
|
API.OpenUrl(e.Uri.AbsoluteUri);
|
|
e.Handled = true;
|
|
}
|
|
|
|
private void OnClosed(object sender, EventArgs e)
|
|
{
|
|
settings.SettingWindowState = WindowState;
|
|
settings.SettingWindowTop = Top;
|
|
settings.SettingWindowLeft = Left;
|
|
viewModel.Save();
|
|
}
|
|
|
|
private void OnCloseExecuted(object sender, ExecutedRoutedEventArgs e)
|
|
{
|
|
Close();
|
|
}
|
|
|
|
private void OpenThemeFolder(object sender, RoutedEventArgs e)
|
|
{
|
|
PluginManager.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Themes));
|
|
}
|
|
|
|
private void OpenSettingFolder(object sender, RoutedEventArgs e)
|
|
{
|
|
PluginManager.API.OpenDirectory(Path.Combine(DataLocation.DataDirectory(), Constant.Settings));
|
|
}
|
|
|
|
private void OpenWelcomeWindow(object sender, RoutedEventArgs e)
|
|
{
|
|
var WelcomeWindow = new WelcomeWindow(settings);
|
|
WelcomeWindow.ShowDialog();
|
|
}
|
|
private void OpenLogFolder(object sender, RoutedEventArgs e)
|
|
{
|
|
viewModel.OpenLogFolder();
|
|
}
|
|
private void ClearLogFolder(object sender, RoutedEventArgs e)
|
|
{
|
|
var confirmResult = MessageBox.Show(
|
|
InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"),
|
|
InternationalizationManager.Instance.GetTranslation("clearlogfolder"),
|
|
MessageBoxButton.YesNo);
|
|
|
|
if (confirmResult == MessageBoxResult.Yes)
|
|
{
|
|
viewModel.ClearLogFolder();
|
|
}
|
|
}
|
|
|
|
private void OnExternalPluginInstallClick(object sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is not Button { DataContext: PluginStoreItemViewModel plugin } button)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (storeClickedButton != null)
|
|
{
|
|
FlyoutService.GetFlyout(storeClickedButton).Hide();
|
|
}
|
|
|
|
viewModel.DisplayPluginQuery($"install {plugin.Name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
|
|
}
|
|
|
|
private void OnExternalPluginUninstallClick(object sender, MouseButtonEventArgs e)
|
|
{
|
|
if (e.ChangedButton == MouseButton.Left)
|
|
{
|
|
var name = viewModel.SelectedPlugin.PluginPair.Metadata.Name;
|
|
viewModel.DisplayPluginQuery($"uninstall {name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
|
|
}
|
|
}
|
|
|
|
private void OnExternalPluginUninstallClick(object sender, RoutedEventArgs e)
|
|
{
|
|
if (storeClickedButton != null)
|
|
{
|
|
FlyoutService.GetFlyout(storeClickedButton).Hide();
|
|
}
|
|
|
|
if (sender is Button { DataContext: PluginStoreItemViewModel plugin })
|
|
viewModel.DisplayPluginQuery($"uninstall {plugin.Name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
|
|
|
|
}
|
|
|
|
private void OnExternalPluginUpdateClick(object sender, RoutedEventArgs e)
|
|
{
|
|
if (storeClickedButton != null)
|
|
{
|
|
FlyoutService.GetFlyout(storeClickedButton).Hide();
|
|
}
|
|
if (sender is Button { DataContext: PluginStoreItemViewModel plugin })
|
|
viewModel.DisplayPluginQuery($"update {plugin.Name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
|
|
|
|
}
|
|
|
|
private void window_MouseDown(object sender, MouseButtonEventArgs e) /* for close hotkey popup */
|
|
{
|
|
if (Keyboard.FocusedElement is not TextBox textBox)
|
|
{
|
|
return;
|
|
}
|
|
var tRequest = new TraversalRequest(FocusNavigationDirection.Next);
|
|
textBox.MoveFocus(tRequest);
|
|
}
|
|
|
|
private void ColorSchemeSelectedIndexChanged(object sender, EventArgs e)
|
|
=> ThemeManager.Current.ApplicationTheme = settings.ColorScheme switch
|
|
{
|
|
Constant.Light => ApplicationTheme.Light,
|
|
Constant.Dark => ApplicationTheme.Dark,
|
|
Constant.System => null,
|
|
_ => ThemeManager.Current.ApplicationTheme
|
|
};
|
|
|
|
/* Custom TitleBar */
|
|
|
|
private void OnMinimizeButtonClick(object sender, RoutedEventArgs e)
|
|
{
|
|
WindowState = WindowState.Minimized;
|
|
}
|
|
|
|
private void OnMaximizeRestoreButtonClick(object sender, RoutedEventArgs e)
|
|
{
|
|
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
|
}
|
|
|
|
private void OnCloseButtonClick(object sender, RoutedEventArgs e)
|
|
{
|
|
|
|
Close();
|
|
}
|
|
|
|
private void RefreshMaximizeRestoreButton()
|
|
{
|
|
if (WindowState == WindowState.Maximized)
|
|
{
|
|
maximizeButton.Visibility = Visibility.Collapsed;
|
|
restoreButton.Visibility = Visibility.Visible;
|
|
}
|
|
else
|
|
{
|
|
maximizeButton.Visibility = Visibility.Visible;
|
|
restoreButton.Visibility = Visibility.Collapsed;
|
|
}
|
|
}
|
|
|
|
private void Window_StateChanged(object sender, EventArgs e)
|
|
{
|
|
RefreshMaximizeRestoreButton();
|
|
}
|
|
|
|
#region Shortcut
|
|
|
|
private void OnDeleteCustomShortCutClick(object sender, RoutedEventArgs e)
|
|
{
|
|
viewModel.DeleteSelectedCustomShortcut();
|
|
}
|
|
|
|
private void OnEditCustomShortCutClick(object sender, RoutedEventArgs e)
|
|
{
|
|
if (viewModel.EditSelectedCustomShortcut())
|
|
{
|
|
customShortcutView.Items.Refresh();
|
|
}
|
|
}
|
|
|
|
private void OnAddCustomShortCutClick(object sender, RoutedEventArgs e)
|
|
{
|
|
viewModel.AddCustomShortcut();
|
|
}
|
|
|
|
#endregion
|
|
|
|
private CollectionView pluginListView;
|
|
private CollectionView pluginStoreView;
|
|
|
|
private bool PluginListFilter(object item)
|
|
{
|
|
if (string.IsNullOrEmpty(pluginFilterTxb.Text))
|
|
return true;
|
|
if (item is PluginViewModel model)
|
|
{
|
|
return StringMatcher.FuzzySearch(pluginFilterTxb.Text, model.PluginPair.Metadata.Name).IsSearchPrecisionScoreMet();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool PluginStoreFilter(object item)
|
|
{
|
|
if (string.IsNullOrEmpty(pluginStoreFilterTxb.Text))
|
|
return true;
|
|
if (item is PluginStoreItemViewModel model)
|
|
{
|
|
return StringMatcher.FuzzySearch(pluginStoreFilterTxb.Text, model.Name).IsSearchPrecisionScoreMet()
|
|
|| StringMatcher.FuzzySearch(pluginStoreFilterTxb.Text, model.Description).IsSearchPrecisionScoreMet();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private string lastPluginListSearch = "";
|
|
private string lastPluginStoreSearch = "";
|
|
|
|
private void RefreshPluginListEventHandler(object sender, RoutedEventArgs e)
|
|
{
|
|
if (pluginFilterTxb.Text != lastPluginListSearch)
|
|
{
|
|
lastPluginListSearch = pluginFilterTxb.Text;
|
|
pluginListView.Refresh();
|
|
}
|
|
}
|
|
|
|
private void RefreshPluginStoreEventHandler(object sender, RoutedEventArgs e)
|
|
{
|
|
if (pluginStoreFilterTxb.Text != lastPluginStoreSearch)
|
|
{
|
|
lastPluginStoreSearch = pluginStoreFilterTxb.Text;
|
|
pluginStoreView.Refresh();
|
|
}
|
|
}
|
|
|
|
private void PluginFilterTxb_OnKeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Enter)
|
|
RefreshPluginListEventHandler(sender, e);
|
|
}
|
|
|
|
private void PluginStoreFilterTxb_OnKeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Enter)
|
|
RefreshPluginStoreEventHandler(sender, e);
|
|
}
|
|
|
|
private void OnPluginSettingKeydown(object sender, KeyEventArgs e)
|
|
{
|
|
if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control && e.Key == Key.F)
|
|
pluginFilterTxb.Focus();
|
|
}
|
|
|
|
private void PluginStore_OnKeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.F && (Keyboard.Modifiers & ModifierKeys.Control) != 0)
|
|
{
|
|
pluginStoreFilterTxb.Focus();
|
|
}
|
|
}
|
|
|
|
public void InitializePosition()
|
|
{
|
|
if (settings.SettingWindowTop >= 0 && settings.SettingWindowLeft >= 0)
|
|
{
|
|
Top = settings.SettingWindowTop;
|
|
Left = settings.SettingWindowLeft;
|
|
}
|
|
else
|
|
{
|
|
Top = WindowTop();
|
|
Left = WindowLeft();
|
|
}
|
|
WindowState = settings.SettingWindowState;
|
|
}
|
|
|
|
public double WindowLeft()
|
|
{
|
|
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
|
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0);
|
|
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0);
|
|
var left = (dip2.X - this.ActualWidth) / 2 + dip1.X;
|
|
return left;
|
|
}
|
|
|
|
public double WindowTop()
|
|
{
|
|
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
|
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
|
|
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
|
|
var top = (dip2.Y - this.ActualHeight) / 2 + dip1.Y - 20;
|
|
return top;
|
|
}
|
|
|
|
private Button storeClickedButton;
|
|
|
|
private void StoreListItem_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is not Button button)
|
|
return;
|
|
|
|
storeClickedButton = button;
|
|
|
|
var flyout = FlyoutService.GetFlyout(button);
|
|
flyout.Closed += (_, _) =>
|
|
{
|
|
storeClickedButton = null;
|
|
};
|
|
|
|
}
|
|
|
|
private void PluginStore_GotFocus(object sender, RoutedEventArgs e)
|
|
{
|
|
Keyboard.Focus(pluginStoreFilterTxb);
|
|
}
|
|
|
|
private void Plugin_GotFocus(object sender, RoutedEventArgs e)
|
|
{
|
|
Keyboard.Focus(pluginFilterTxb);
|
|
}
|
|
}
|
|
}
|