diff --git a/.github/workflows/release_deploy.yml b/.github/workflows/release_deploy.yml new file mode 100644 index 000000000..9e082b95f --- /dev/null +++ b/.github/workflows/release_deploy.yml @@ -0,0 +1,34 @@ +--- + +name: New Release Deployments +on: + release: + types: [published] + workflow_dispatch: + +jobs: + deploy-website: + runs-on: ubuntu-latest + steps: + - name: Trigger dispatch event for deploying website + run: | + http_status=$(curl -L -f -s -o /dev/null -w "%{http_code}" \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.DEPLOY_FLOW_WEBSITE }}" \ + https://api.github.com/repos/Flow-Launcher/flow-launcher.github.io/dispatches \ + -d '{"event_type":"deploy"}') + if [ "$http_status" -ne 204 ]; then echo "Error: Deploy website failed, HTTP status code is $http_status"; exit 1; fi + + publish-chocolatey: + runs-on: ubuntu-latest + steps: + - name: Trigger dispatch event for publishing to Chocolatey + run: | + http_status=$(curl -L -f -s -o /dev/null -w "%{http_code}" \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.Publish_Chocolatey }}" \ + https://api.github.com/repos/Flow-Launcher/chocolatey-package/dispatches \ + -d '{"event_type":"publish"}') + if [ "$http_status" -ne 204 ]; then echo "Error: Publish Chocolatey package failed, HTTP status code is $http_status"; exit 1; fi diff --git a/.github/workflows/website_deploy.yml b/.github/workflows/website_deploy.yml deleted file mode 100644 index 2d44e4a2c..000000000 --- a/.github/workflows/website_deploy.yml +++ /dev/null @@ -1,21 +0,0 @@ ---- - -name: Deploy Website On Release -on: - release: - types: [published] - workflow_dispatch: - -jobs: - dispatch: - runs-on: ubuntu-latest - steps: - - name: Dispatch event - run: | - http_status=$(curl -L -f -s -o /dev/null -w "%{http_code}" \ - -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${{ secrets.DEPLOY_FLOW_WEBSITE }}" \ - https://api.github.com/repos/Flow-Launcher/flow-launcher.github.io/dispatches \ - -d '{"event_type":"deploy"}') - if [ "$http_status" -ne 204 ]; then echo "Error: Deploy trigger failed, HTTP status code is $http_status"; exit 1; fi diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index a3e80a73f..aae8dd764 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -220,9 +220,19 @@ namespace Flow.Launcher.Core.Plugin catch (Exception e) { API.LogException(ClassName, $"Fail to Init plugin: {pair.Metadata.Name}", e); - pair.Metadata.Disabled = true; - pair.Metadata.HomeDisabled = true; - failedPlugins.Enqueue(pair); + if (pair.Metadata.Disabled && pair.Metadata.HomeDisabled) + { + // If this plugin is already disabled, do not show error message again + // Or else it will be shown every time + API.LogDebug(ClassName, $"Skipped init for <{pair.Metadata.Name}> due to error"); + } + else + { + pair.Metadata.Disabled = true; + pair.Metadata.HomeDisabled = true; + failedPlugins.Enqueue(pair); + API.LogDebug(ClassName, $"Disable plugin <{pair.Metadata.Name}> because init failed"); + } } })); diff --git a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs index 0b10382ee..c7eba05fd 100644 --- a/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs +++ b/Flow.Launcher.Infrastructure/Storage/JsonStorage.cs @@ -45,6 +45,22 @@ namespace Flow.Launcher.Infrastructure.Storage FilesFolders.ValidateDirectory(DirectoryPath); } + public bool Exists() + { + return File.Exists(FilePath); + } + + public void Delete() + { + foreach (var path in new[] { FilePath, BackupFilePath, TempFilePath }) + { + if (File.Exists(path)) + { + File.Delete(path); + } + } + } + public async Task LoadAsync() { if (Data != null) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 9d2d854f4..027eb3f92 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -306,6 +306,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings public double WindowLeft { get; set; } public double WindowTop { get; set; } + public double PreviousScreenWidth { get; set; } + public double PreviousScreenHeight { get; set; } + public double PreviousDpiX { get; set; } + public double PreviousDpiY { get; set; } /// /// Custom left position on selected monitor diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index 7cc6dfcb1..081c8e90e 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -488,7 +488,7 @@ Jeśli dodasz prefiks '@' podczas wprowadzania skrótu, będzie on pasował do d Anuluj Aktualizacja nie powiodła się Sprawdź połączenie i spróbuj zaktualizować ustawienia proxy do github-cloud.s3.amazonaws.com. - Aby dokończyć proces aktualizacji Flow Launcher musi zostać zresetowany + Aby dokończyć proces aktualizacji Flow Launcher musi zostać zrestartowany Następujące pliki zostaną zaktualizowane Aktualizuj pliki Opis aktualizacji diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index f91d9d559..bb29d78e5 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -22,6 +22,7 @@ using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.ViewModel; +using Microsoft.Win32; using ModernWpf.Controls; using DataObject = System.Windows.DataObject; using Key = System.Windows.Input.Key; @@ -88,6 +89,8 @@ namespace Flow.Launcher InitSoundEffects(); DataObject.AddPastingHandler(QueryTextBox, QueryTextBox_OnPaste); + ModernWpf.ThemeManager.Current.ActualApplicationThemeChanged += ThemeManager_ActualApplicationThemeChanged; + SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; } #endregion @@ -96,6 +99,11 @@ namespace Flow.Launcher #pragma warning disable VSTHRD100 // Avoid async void methods + private void ThemeManager_ActualApplicationThemeChanged(ModernWpf.ThemeManager sender, object args) + { + _theme.RefreshFrameAsync(); + } + private void OnSourceInitialized(object sender, EventArgs e) { var handle = Win32Helper.GetWindowHandle(this, true); @@ -541,16 +549,29 @@ namespace Flow.Launcher #region Window Sound Effects + private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) + { + // Fix for sound not playing after sleep / hibernate + // https://stackoverflow.com/questions/64805186/mediaplayer-doesnt-play-after-computer-sleeps + if (e.Mode == PowerModes.Resume) + { + InitSoundEffects(); + } + } + private void InitSoundEffects() { if (_settings.WMPInstalled) { + animationSoundWMP?.Close(); animationSoundWMP = new MediaPlayer(); animationSoundWMP.Open(new Uri(AppContext.BaseDirectory + "Resources\\open.wav")); } else { + animationSoundWPF?.Dispose(); animationSoundWPF = new SoundPlayer(AppContext.BaseDirectory + "Resources\\open.wav"); + animationSoundWPF.Load(); } } @@ -696,8 +717,26 @@ namespace Flow.Launcher { if (_settings.SearchWindowScreen == SearchWindowScreens.RememberLastLaunchLocation) { - Top = _settings.WindowTop; + var previousScreenWidth = _settings.PreviousScreenWidth; + var previousScreenHeight = _settings.PreviousScreenHeight; + GetDpi(out var previousDpiX, out var previousDpiY); + + _settings.PreviousScreenWidth = SystemParameters.VirtualScreenWidth; + _settings.PreviousScreenHeight = SystemParameters.VirtualScreenHeight; + GetDpi(out var currentDpiX, out var currentDpiY); + + if (previousScreenWidth != 0 && previousScreenHeight != 0 && + previousDpiX != 0 && previousDpiY != 0 && + (previousScreenWidth != SystemParameters.VirtualScreenWidth || + previousScreenHeight != SystemParameters.VirtualScreenHeight || + previousDpiX != currentDpiX || previousDpiY != currentDpiY)) + { + AdjustPositionForResolutionChange(); + return; + } + Left = _settings.WindowLeft; + Top = _settings.WindowTop; } else { @@ -710,27 +749,73 @@ namespace Flow.Launcher break; case SearchWindowAligns.CenterTop: Left = HorizonCenter(screen); - Top = 10; + Top = VerticalTop(screen); break; case SearchWindowAligns.LeftTop: Left = HorizonLeft(screen); - Top = 10; + Top = VerticalTop(screen); break; case SearchWindowAligns.RightTop: Left = HorizonRight(screen); - Top = 10; + Top = VerticalTop(screen); break; case SearchWindowAligns.Custom: - Left = Win32Helper.TransformPixelsToDIP(this, - screen.WorkingArea.X + _settings.CustomWindowLeft, 0).X; - Top = Win32Helper.TransformPixelsToDIP(this, 0, - screen.WorkingArea.Y + _settings.CustomWindowTop).Y; + var customLeft = Win32Helper.TransformPixelsToDIP(this, + screen.WorkingArea.X + _settings.CustomWindowLeft, 0); + var customTop = Win32Helper.TransformPixelsToDIP(this, 0, + screen.WorkingArea.Y + _settings.CustomWindowTop); + Left = customLeft.X; + Top = customTop.Y; break; } } } } + private void AdjustPositionForResolutionChange() + { + var screenWidth = SystemParameters.VirtualScreenWidth; + var screenHeight = SystemParameters.VirtualScreenHeight; + GetDpi(out var currentDpiX, out var currentDpiY); + + var previousLeft = _settings.WindowLeft; + var previousTop = _settings.WindowTop; + GetDpi(out var previousDpiX, out var previousDpiY); + + var widthRatio = screenWidth / _settings.PreviousScreenWidth; + var heightRatio = screenHeight / _settings.PreviousScreenHeight; + var dpiXRatio = currentDpiX / previousDpiX; + var dpiYRatio = currentDpiY / previousDpiY; + + var newLeft = previousLeft * widthRatio * dpiXRatio; + var newTop = previousTop * heightRatio * dpiYRatio; + + var screenLeft = SystemParameters.VirtualScreenLeft; + var screenTop = SystemParameters.VirtualScreenTop; + + var maxX = screenLeft + screenWidth - ActualWidth; + var maxY = screenTop + screenHeight - ActualHeight; + + Left = Math.Max(screenLeft, Math.Min(newLeft, maxX)); + Top = Math.Max(screenTop, Math.Min(newTop, maxY)); + } + + private void GetDpi(out double dpiX, out double dpiY) + { + var source = PresentationSource.FromVisual(this); + if (source != null && source.CompositionTarget != null) + { + var matrix = source.CompositionTarget.TransformToDevice; + dpiX = 96 * matrix.M11; + dpiY = 96 * matrix.M22; + } + else + { + dpiX = 96; + dpiY = 96; + } + } + private Screen SelectedScreen() { Screen screen; @@ -791,6 +876,13 @@ namespace Flow.Launcher return left; } + public double VerticalTop(Screen screen) + { + var dip1 = Win32Helper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); + var top = dip1.Y + 10; + return top; + } + #endregion #region Window Animation @@ -817,7 +909,7 @@ namespace Flow.Launcher { Name = progressBarAnimationName, Storyboard = progressBarStoryBoard }; - + var stopStoryboard = new StopStoryboard() { BeginStoryboardName = progressBarAnimationName @@ -838,7 +930,7 @@ namespace Flow.Launcher progressStyle.Triggers.Add(trigger); ProgressBar.Style = progressStyle; - + _viewModel.ProgressBarVisibility = Visibility.Hidden; } @@ -886,7 +978,7 @@ namespace Flow.Launcher Duration = TimeSpan.FromMilliseconds(animationLength), FillBehavior = FillBehavior.HoldEnd }; - + var rightMargin = GetThicknessFromStyle(ClockPanel.Style, new Thickness(0, 0, DefaultRightMargin, 0)).Right; var thicknessAnimation = new ThicknessAnimation @@ -914,10 +1006,10 @@ namespace Flow.Launcher clocksb.Children.Add(ClockOpacity); iconsb.Children.Add(IconMotion); iconsb.Children.Add(IconOpacity); - + _settings.WindowLeft = Left; _isArrowKeyPressed = false; - + clocksb.Begin(ClockPanel); iconsb.Begin(SearchIcon); } @@ -1089,7 +1181,7 @@ namespace Flow.Launcher { e.Handled = true; } - + #endregion #region Placeholder @@ -1141,7 +1233,7 @@ namespace Flow.Launcher } #endregion - + #region Search Delay private void QueryTextBox_TextChanged1(object sender, TextChangedEventArgs e) @@ -1163,6 +1255,10 @@ namespace Flow.Launcher { _hwndSource?.Dispose(); _notifyIcon?.Dispose(); + animationSoundWMP?.Close(); + animationSoundWPF?.Dispose(); + ModernWpf.ThemeManager.Current.ActualApplicationThemeChanged -= ThemeManager_ActualApplicationThemeChanged; + SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; } _disposed = true; diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 5b8e8c9af..796f65ae6 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -318,19 +318,37 @@ namespace Flow.Launcher { using var explorer = new Process(); var explorerInfo = _settings.CustomExplorer; + var explorerPath = explorerInfo.Path.Trim().ToLowerInvariant(); + var targetPath = FileNameOrFilePath is null + ? DirectoryPath + : Path.IsPathRooted(FileNameOrFilePath) + ? FileNameOrFilePath + : Path.Combine(DirectoryPath, FileNameOrFilePath); - explorer.StartInfo = new ProcessStartInfo + if (Path.GetFileNameWithoutExtension(explorerPath) == "explorer") { - FileName = explorerInfo.Path.Replace("%d", DirectoryPath), - UseShellExecute = true, - Arguments = FileNameOrFilePath is null - ? explorerInfo.DirectoryArgument.Replace("%d", DirectoryPath) - : explorerInfo.FileArgument - .Replace("%d", DirectoryPath) - .Replace("%f", - Path.IsPathRooted(FileNameOrFilePath) ? FileNameOrFilePath : Path.Combine(DirectoryPath, FileNameOrFilePath) - ) - }; + // Windows File Manager + // We should ignore and pass only the path to Shell to prevent zombie explorer.exe processes + explorer.StartInfo = new ProcessStartInfo + { + FileName = targetPath, // Not explorer, Only path. + UseShellExecute = true // Must be true to open folder + }; + } + else + { + // Custom File Manager + explorer.StartInfo = new ProcessStartInfo + { + FileName = explorerInfo.Path.Replace("%d", DirectoryPath), + UseShellExecute = true, + Arguments = FileNameOrFilePath is null + ? explorerInfo.DirectoryArgument.Replace("%d", DirectoryPath) + : explorerInfo.FileArgument + .Replace("%d", DirectoryPath) + .Replace("%f", targetPath) + }; + } explorer.Start(); } diff --git a/Flow.Launcher/Resources/Pages/WelcomePage1.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage1.xaml.cs index a3b008206..16252086e 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage1.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage1.xaml.cs @@ -9,20 +9,19 @@ namespace Flow.Launcher.Resources.Pages { public partial class WelcomePage1 { - public Settings Settings { get; private set; } - private WelcomeViewModel _viewModel; + public Settings Settings { get; } = Ioc.Default.GetRequiredService(); + private readonly WelcomeViewModel _viewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) - { - Settings = Ioc.Default.GetRequiredService(); - _viewModel = Ioc.Default.GetRequiredService(); - InitializeComponent(); - } // Sometimes the navigation is not triggered by button click, // so we need to reset the page number _viewModel.PageNum = 1; + + if (!IsInitialized) + { + InitializeComponent(); + } base.OnNavigatedTo(e); } diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs index a63edbcda..37767f128 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs @@ -11,20 +11,19 @@ namespace Flow.Launcher.Resources.Pages { public partial class WelcomePage2 { - public Settings Settings { get; private set; } - private WelcomeViewModel _viewModel; + public Settings Settings { get; } = Ioc.Default.GetRequiredService(); + private readonly WelcomeViewModel _viewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) - { - Settings = Ioc.Default.GetRequiredService(); - _viewModel = Ioc.Default.GetRequiredService(); - InitializeComponent(); - } // Sometimes the navigation is not triggered by button click, // so we need to reset the page number _viewModel.PageNum = 2; + + if (!IsInitialized) + { + InitializeComponent(); + } base.OnNavigatedTo(e); } diff --git a/Flow.Launcher/Resources/Pages/WelcomePage3.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage3.xaml.cs index 4a6610e61..4c3184f8c 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage3.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage3.xaml.cs @@ -7,20 +7,19 @@ namespace Flow.Launcher.Resources.Pages { public partial class WelcomePage3 { - public Settings Settings { get; private set; } - private WelcomeViewModel _viewModel; + public Settings Settings { get; } = Ioc.Default.GetRequiredService(); + private readonly WelcomeViewModel _viewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) - { - Settings = Ioc.Default.GetRequiredService(); - _viewModel = Ioc.Default.GetRequiredService(); - InitializeComponent(); - } // Sometimes the navigation is not triggered by button click, // so we need to reset the page number _viewModel.PageNum = 3; + + if (!IsInitialized) + { + InitializeComponent(); + } base.OnNavigatedTo(e); } } diff --git a/Flow.Launcher/Resources/Pages/WelcomePage4.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage4.xaml.cs index 1ee3284d2..63c9b9a7a 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage4.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage4.xaml.cs @@ -7,20 +7,19 @@ namespace Flow.Launcher.Resources.Pages { public partial class WelcomePage4 { - public Settings Settings { get; private set; } - private WelcomeViewModel _viewModel; + public Settings Settings { get; } = Ioc.Default.GetRequiredService(); + private readonly WelcomeViewModel _viewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) - { - Settings = Ioc.Default.GetRequiredService(); - _viewModel = Ioc.Default.GetRequiredService(); - InitializeComponent(); - } // Sometimes the navigation is not triggered by button click, // so we need to reset the page number _viewModel.PageNum = 4; + + if (!IsInitialized) + { + InitializeComponent(); + } base.OnNavigatedTo(e); } } diff --git a/Flow.Launcher/Resources/Pages/WelcomePage5.xaml.cs b/Flow.Launcher/Resources/Pages/WelcomePage5.xaml.cs index 6328c9914..8db0a9f7e 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage5.xaml.cs +++ b/Flow.Launcher/Resources/Pages/WelcomePage5.xaml.cs @@ -10,20 +10,19 @@ namespace Flow.Launcher.Resources.Pages { public partial class WelcomePage5 { - public Settings Settings { get; private set; } - private WelcomeViewModel _viewModel; + public Settings Settings { get; } = Ioc.Default.GetRequiredService(); + private readonly WelcomeViewModel _viewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) - { - Settings = Ioc.Default.GetRequiredService(); - _viewModel = Ioc.Default.GetRequiredService(); - InitializeComponent(); - } // Sometimes the navigation is not triggered by button click, // so we need to reset the page number _viewModel.PageNum = 5; + + if (!IsInitialized) + { + InitializeComponent(); + } base.OnNavigatedTo(e); } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index d542eb019..79465cd71 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -479,7 +479,8 @@ public partial class SettingsPaneThemeViewModel : BaseModel ) } }; - var vm = new ResultsViewModel(Settings); + // Set main view model to null because the results are for preview only + var vm = new ResultsViewModel(Settings, null); vm.AddResults(results, "PREVIEW"); PreviewResults = vm; } diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneAbout.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneAbout.xaml.cs index e57cba6d4..47532b243 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneAbout.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneAbout.xaml.cs @@ -1,19 +1,29 @@ using System.Windows.Navigation; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.SettingPages.ViewModels; +using Flow.Launcher.ViewModel; namespace Flow.Launcher.SettingPages.Views; public partial class SettingsPaneAbout { private SettingsPaneAboutViewModel _viewModel = null!; + private readonly SettingWindowViewModel _settingViewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) + // Sometimes the navigation is not triggered by button click, + // so we need to reset the page type + _settingViewModel.PageType = typeof(SettingsPaneAbout); + + // If the navigation is not triggered by button click, view model will be null again + if (_viewModel == null) { _viewModel = Ioc.Default.GetRequiredService(); DataContext = _viewModel; + } + if (!IsInitialized) + { InitializeComponent(); } base.OnNavigatedTo(e); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml.cs index 31653962d..753cb7b0e 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml.cs @@ -1,19 +1,29 @@ using System.Windows.Navigation; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.SettingPages.ViewModels; +using Flow.Launcher.ViewModel; namespace Flow.Launcher.SettingPages.Views; public partial class SettingsPaneGeneral { private SettingsPaneGeneralViewModel _viewModel = null!; + private readonly SettingWindowViewModel _settingViewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) + // Sometimes the navigation is not triggered by button click, + // so we need to reset the page type + _settingViewModel.PageType = typeof(SettingsPaneGeneral); + + // If the navigation is not triggered by button click, view model will be null again + if (_viewModel == null) { _viewModel = Ioc.Default.GetRequiredService(); DataContext = _viewModel; + } + if (!IsInitialized) + { InitializeComponent(); } base.OnNavigatedTo(e); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs index 8b757bd60..202869bc5 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs @@ -1,19 +1,29 @@ using System.Windows.Navigation; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.SettingPages.ViewModels; +using Flow.Launcher.ViewModel; namespace Flow.Launcher.SettingPages.Views; public partial class SettingsPaneHotkey { private SettingsPaneHotkeyViewModel _viewModel = null!; + private readonly SettingWindowViewModel _settingViewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) + // Sometimes the navigation is not triggered by button click, + // so we need to reset the page type + _settingViewModel.PageType = typeof(SettingsPaneHotkey); + + // If the navigation is not triggered by button click, view model will be null again + if (_viewModel == null) { _viewModel = Ioc.Default.GetRequiredService(); DataContext = _viewModel; + } + if (!IsInitialized) + { InitializeComponent(); } base.OnNavigatedTo(e); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs index 5ddfe4650..c0a77957a 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs @@ -11,13 +11,22 @@ namespace Flow.Launcher.SettingPages.Views; public partial class SettingsPanePluginStore { private SettingsPanePluginStoreViewModel _viewModel = null!; + private readonly SettingWindowViewModel _settingViewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) + // Sometimes the navigation is not triggered by button click, + // so we need to reset the page type + _settingViewModel.PageType = typeof(SettingsPanePluginStore); + + // If the navigation is not triggered by button click, view model will be null again + if (_viewModel == null) { _viewModel = Ioc.Default.GetRequiredService(); DataContext = _viewModel; + } + if (!IsInitialized) + { InitializeComponent(); } _viewModel.PropertyChanged += ViewModel_PropertyChanged; diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs index e9490804a..f486a3443 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs @@ -11,13 +11,22 @@ namespace Flow.Launcher.SettingPages.Views; public partial class SettingsPanePlugins { private SettingsPanePluginsViewModel _viewModel = null!; + private readonly SettingWindowViewModel _settingViewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) + // Sometimes the navigation is not triggered by button click, + // so we need to reset the page type + _settingViewModel.PageType = typeof(SettingsPanePlugins); + + // If the navigation is not triggered by button click, view model will be null again + if (_viewModel == null) { _viewModel = Ioc.Default.GetRequiredService(); DataContext = _viewModel; + } + if (!IsInitialized) + { InitializeComponent(); } _viewModel.PropertyChanged += ViewModel_PropertyChanged; diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneProxy.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneProxy.xaml.cs index 258f2a4ad..3e617229d 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneProxy.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneProxy.xaml.cs @@ -1,19 +1,29 @@ using System.Windows.Navigation; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.SettingPages.ViewModels; +using Flow.Launcher.ViewModel; namespace Flow.Launcher.SettingPages.Views; public partial class SettingsPaneProxy { private SettingsPaneProxyViewModel _viewModel = null!; + private readonly SettingWindowViewModel _settingViewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) + // Sometimes the navigation is not triggered by button click, + // so we need to reset the page type + _settingViewModel.PageType = typeof(SettingsPaneProxy); + + // If the navigation is not triggered by button click, view model will be null again + if (_viewModel == null) { _viewModel = Ioc.Default.GetRequiredService(); DataContext = _viewModel; + } + if (!IsInitialized) + { InitializeComponent(); } base.OnNavigatedTo(e); diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml.cs index cee8e4ae4..170003994 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml.cs +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml.cs @@ -1,19 +1,29 @@ using System.Windows.Navigation; using CommunityToolkit.Mvvm.DependencyInjection; using Flow.Launcher.SettingPages.ViewModels; +using Flow.Launcher.ViewModel; namespace Flow.Launcher.SettingPages.Views; public partial class SettingsPaneTheme { private SettingsPaneThemeViewModel _viewModel = null!; + private readonly SettingWindowViewModel _settingViewModel = Ioc.Default.GetRequiredService(); protected override void OnNavigatedTo(NavigationEventArgs e) { - if (!IsInitialized) + // Sometimes the navigation is not triggered by button click, + // so we need to reset the page type + _settingViewModel.PageType = typeof(SettingsPaneTheme); + + // If the navigation is not triggered by button click, view model will be null again + if (_viewModel == null) { _viewModel = Ioc.Default.GetRequiredService(); DataContext = _viewModel; + } + if (!IsInitialized) + { InitializeComponent(); } base.OnNavigatedTo(e); diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index a75a51c8f..da128266c 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -13,6 +13,7 @@ MinHeight="600" d:DataContext="{d:DesignInstance vm:SettingWindowViewModel}" Closed="OnClosed" + FontFamily="{DynamicResource SettingWindowFont}" Icon="Images\app.ico" Left="{Binding SettingWindowLeft, Mode=TwoWay}" Loaded="OnLoaded" diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index 79bd171ed..c53a4ea80 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -18,6 +19,7 @@ public partial class SettingWindow #region Private Fields private readonly Settings _settings; + private readonly SettingWindowViewModel _viewModel; #endregion @@ -26,11 +28,11 @@ public partial class SettingWindow public SettingWindow() { _settings = Ioc.Default.GetRequiredService(); - var viewModel = Ioc.Default.GetRequiredService(); - DataContext = viewModel; - InitializeComponent(); - + _viewModel = Ioc.Default.GetRequiredService(); + DataContext = _viewModel; + // Since WindowStartupLocation is set to Manual, initialize the window position before calling InitializeComponent UpdatePositionAndState(); + InitializeComponent(); } #endregion @@ -48,10 +50,37 @@ public partial class SettingWindow hwndTarget.RenderMode = RenderMode.SoftwareOnly; // Must use software only render mode here UpdatePositionAndState(); + + _viewModel.PropertyChanged += ViewModel_PropertyChanged; + } + + // Sometimes the navigation is not triggered by button click, + // so we need to update the selected item here + private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(SettingWindowViewModel.PageType): + var selectedIndex = _viewModel.PageType.Name switch + { + nameof(SettingsPaneGeneral) => 0, + nameof(SettingsPanePlugins) => 1, + nameof(SettingsPanePluginStore) => 2, + nameof(SettingsPaneTheme) => 3, + nameof(SettingsPaneHotkey) => 4, + nameof(SettingsPaneProxy) => 5, + nameof(SettingsPaneAbout) => 6, + _ => 0 + }; + NavView.SelectedItem = NavView.MenuItems[selectedIndex]; + break; + } } private void OnClosed(object sender, EventArgs e) { + _viewModel.PropertyChanged -= ViewModel_PropertyChanged; + // If app is exiting, settings save is not needed because main window closing event will handle this if (App.Exiting) return; // Save settings when window is closed @@ -137,18 +166,40 @@ public partial class SettingWindow if (previousTop == null || previousLeft == null || !IsPositionValid(previousTop.Value, previousLeft.Value)) { - Top = WindowTop(); - Left = WindowLeft(); + SetWindowPosition(WindowTop(), WindowLeft()); } else { - Top = previousTop.Value; - Left = previousLeft.Value; + var left = _settings.SettingWindowLeft.Value; + var top = _settings.SettingWindowTop.Value; + AdjustWindowPosition(ref top, ref left); + SetWindowPosition(top, left); } WindowState = _settings.SettingWindowState; } + private void SetWindowPosition(double top, double left) + { + // Ensure window does not exceed screen boundaries + top = Math.Max(top, SystemParameters.VirtualScreenTop); + left = Math.Max(left, SystemParameters.VirtualScreenLeft); + top = Math.Min(top, SystemParameters.VirtualScreenHeight - ActualHeight); + left = Math.Min(left, SystemParameters.VirtualScreenWidth - ActualWidth); + + Top = top; + Left = left; + } + + private void AdjustWindowPosition(ref double top, ref double left) + { + // Adjust window position if it exceeds screen boundaries + top = Math.Max(top, SystemParameters.VirtualScreenTop); + left = Math.Max(left, SystemParameters.VirtualScreenLeft); + top = Math.Min(top, SystemParameters.VirtualScreenHeight - ActualHeight); + left = Math.Min(left, SystemParameters.VirtualScreenWidth - ActualWidth); + } + private static bool IsPositionValid(double top, double left) { foreach (var screen in Screen.AllScreens) @@ -190,6 +241,7 @@ public partial class SettingWindow { if (args.IsSettingsSelected) { + _viewModel.SetPageType(typeof(SettingsPaneGeneral)); ContentFrame.Navigate(typeof(SettingsPaneGeneral)); } else @@ -212,7 +264,11 @@ public partial class SettingWindow nameof(About) => typeof(SettingsPaneAbout), _ => typeof(SettingsPaneGeneral) }; - ContentFrame.Navigate(pageType); + // Only navigate if the page type changes to fix navigation forward/back issue + if (_viewModel.SetPageType(pageType)) + { + ContentFrame.Navigate(pageType); + } } } @@ -230,7 +286,8 @@ public partial class SettingWindow private void ContentFrame_Loaded(object sender, RoutedEventArgs e) { - NavView.SelectedItem ??= NavView.MenuItems[0]; /* Set First Page */ + _viewModel.SetPageType(null); // Set page type to null so that NavigationView_SelectionChanged can navigate the frame + NavView.SelectedItem = NavView.MenuItems[0]; /* Set First Page */ } #endregion diff --git a/Flow.Launcher/Storage/TopMostRecord.cs b/Flow.Launcher/Storage/TopMostRecord.cs index 7714b5001..7fc9dcaaa 100644 --- a/Flow.Launcher/Storage/TopMostRecord.cs +++ b/Flow.Launcher/Storage/TopMostRecord.cs @@ -1,14 +1,115 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; +using System.Text.Json; using System.Text.Json.Serialization; +using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; namespace Flow.Launcher.Storage { - public class TopMostRecord + public class FlowLauncherJsonStorageTopMostRecord + { + private readonly FlowLauncherJsonStorage _topMostRecordStorage; + private readonly MultipleTopMostRecord _topMostRecord; + + public FlowLauncherJsonStorageTopMostRecord() + { +#pragma warning disable CS0618 // Type or member is obsolete + // Get old data & new data + var topMostRecordStorage = new FlowLauncherJsonStorage(); +#pragma warning restore CS0618 // Type or member is obsolete + _topMostRecordStorage = new FlowLauncherJsonStorage(); + + // Check if data exist + var oldDataExist = topMostRecordStorage.Exists(); + var newDataExist = _topMostRecordStorage.Exists(); + + // If new data exist, it means we have already migrated the old data + // So we can safely delete the old data and load the new data + if (newDataExist) + { + try + { + topMostRecordStorage.Delete(); + } + catch + { + // Ignored - Flow will delete the old data during next startup + } + _topMostRecord = _topMostRecordStorage.Load(); + } + // If new data does not exist and old data exist, we need to migrate the old data to the new data + else if (oldDataExist) + { + // Migrate old data to new data + _topMostRecord = _topMostRecordStorage.Load(); + var oldTopMostRecord = topMostRecordStorage.Load(); + if (oldTopMostRecord == null || oldTopMostRecord.records.IsEmpty) return; + foreach (var record in oldTopMostRecord.records) + { + var newValue = new ConcurrentQueue(); + newValue.Enqueue(record.Value); + _topMostRecord.records.AddOrUpdate(record.Key, newValue, (key, oldValue) => + { + oldValue.Enqueue(record.Value); + return oldValue; + }); + } + + // Delete old data and save the new data + try + { + topMostRecordStorage.Delete(); + } + catch + { + // Ignored - Flow will delete the old data during next startup + } + Save(); + } + // If both data do not exist, we just need to create a new data + else + { + _topMostRecord = _topMostRecordStorage.Load(); + } + } + + public void Save() + { + _topMostRecordStorage.Save(); + } + + public bool IsTopMost(Result result) + { + return _topMostRecord.IsTopMost(result); + } + + public int GetTopMostIndex(Result result) + { + return _topMostRecord.GetTopMostIndex(result); + } + + public void Remove(Result result) + { + _topMostRecord.Remove(result); + } + + public void AddOrUpdate(Result result) + { + _topMostRecord.AddOrUpdate(result); + } + } + + /// + /// Old data structure to support only one top most record for the same query + /// + [Obsolete("Use MultipleTopMostRecord instead. This class will be removed in future versions.")] + internal class TopMostRecord { [JsonInclude] - public ConcurrentDictionary records { get; private set; } = new ConcurrentDictionary(); + public ConcurrentDictionary records { get; private set; } = new(); internal bool IsTopMost(Result result) { @@ -39,12 +140,145 @@ namespace Flow.Launcher.Storage } } - public class Record + /// + /// New data structure to support multiple top most records for the same query + /// + internal class MultipleTopMostRecord { - public string Title { get; set; } - public string SubTitle { get; set; } - public string PluginID { get; set; } - public string RecordKey { get; set; } + [JsonInclude] + [JsonConverter(typeof(ConcurrentDictionaryConcurrentQueueConverter))] + public ConcurrentDictionary> records { get; private set; } = new(); + + internal bool IsTopMost(Result result) + { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to check if the result is top most + if (records.IsEmpty || result.OriginQuery == null || + !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + { + return false; + } + + // since this dictionary should be very small (or empty) going over it should be pretty fast. + return value.Any(record => record.Equals(result)); + } + + internal int GetTopMostIndex(Result result) + { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to check if the result is top most + if (records.IsEmpty || result.OriginQuery == null || + !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + { + return -1; + } + + // since this dictionary should be very small (or empty) going over it should be pretty fast. + // since the latter items should be more recent, we should return the smaller index for score to subtract + // which can make them more topmost + // A, B, C => 2, 1, 0 => (max - 2), (max - 1), (max - 0) + var index = 0; + foreach (var record in value) + { + if (record.Equals(result)) + { + return value.Count - 1 - index; + } + index++; + } + return -1; + } + + internal void Remove(Result result) + { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to remove the record + if (result.OriginQuery == null || + !records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + { + return; + } + + // remove the record from the queue + var queue = new ConcurrentQueue(value.Where(r => !r.Equals(result))); + if (queue.IsEmpty) + { + // if the queue is empty, remove the queue from the dictionary + records.TryRemove(result.OriginQuery.RawQuery, out _); + } + else + { + // change the queue in the dictionary + records[result.OriginQuery.RawQuery] = queue; + } + } + + internal void AddOrUpdate(Result result) + { + // origin query is null when user select the context menu item directly of one item from query list + // in this case, we do not need to add or update the record + if (result.OriginQuery == null) + { + return; + } + + var record = new Record + { + PluginID = result.PluginID, + Title = result.Title, + SubTitle = result.SubTitle, + RecordKey = result.RecordKey + }; + if (!records.TryGetValue(result.OriginQuery.RawQuery, out var value)) + { + // create a new queue if it does not exist + value = new ConcurrentQueue(); + value.Enqueue(record); + records.TryAdd(result.OriginQuery.RawQuery, value); + } + else + { + // add or update the record in the queue + var queue = new ConcurrentQueue(value.Where(r => !r.Equals(result))); // make sure we don't have duplicates + queue.Enqueue(record); + records[result.OriginQuery.RawQuery] = queue; + } + } + } + + /// + /// Because ConcurrentQueue does not support serialization, we need to convert it to a List + /// + internal class ConcurrentDictionaryConcurrentQueueConverter : JsonConverter>> + { + public override ConcurrentDictionary> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var dictionary = JsonSerializer.Deserialize>>(ref reader, options); + var concurrentDictionary = new ConcurrentDictionary>(); + foreach (var kvp in dictionary) + { + concurrentDictionary.TryAdd(kvp.Key, new ConcurrentQueue(kvp.Value)); + } + return concurrentDictionary; + } + + public override void Write(Utf8JsonWriter writer, ConcurrentDictionary> value, JsonSerializerOptions options) + { + var dict = new Dictionary>(); + foreach (var kvp in value) + { + dict.Add(kvp.Key, kvp.Value.ToList()); + } + JsonSerializer.Serialize(writer, dict, options); + } + } + + internal class Record + { + public string Title { get; init; } + public string SubTitle { get; init; } + public string PluginID { get; init; } + public string RecordKey { get; init; } public bool Equals(Result r) { diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 658eabde7..807275fcb 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -39,11 +39,10 @@ namespace Flow.Launcher.ViewModel private readonly FlowLauncherJsonStorage _historyItemsStorage; private readonly FlowLauncherJsonStorage _userSelectedRecordStorage; - private readonly FlowLauncherJsonStorage _topMostRecordStorage; + private readonly FlowLauncherJsonStorageTopMostRecord _topMostRecord; private readonly History _history; private int lastHistoryIndex = 1; private readonly UserSelectedRecord _userSelectedRecord; - private readonly TopMostRecord _topMostRecord; private CancellationTokenSource _updateSource; // Used to cancel old query flows private CancellationToken _updateToken; // Used to avoid ObjectDisposedException of _updateSource.Token @@ -143,24 +142,23 @@ namespace Flow.Launcher.ViewModel _historyItemsStorage = new FlowLauncherJsonStorage(); _userSelectedRecordStorage = new FlowLauncherJsonStorage(); - _topMostRecordStorage = new FlowLauncherJsonStorage(); + _topMostRecord = new FlowLauncherJsonStorageTopMostRecord(); _history = _historyItemsStorage.Load(); _userSelectedRecord = _userSelectedRecordStorage.Load(); - _topMostRecord = _topMostRecordStorage.Load(); - ContextMenu = new ResultsViewModel(Settings) + ContextMenu = new ResultsViewModel(Settings, this) { LeftClickResultCommand = OpenResultCommand, RightClickResultCommand = LoadContextMenuCommand, IsPreviewOn = Settings.AlwaysPreview }; - Results = new ResultsViewModel(Settings) + Results = new ResultsViewModel(Settings, this) { LeftClickResultCommand = OpenResultCommand, RightClickResultCommand = LoadContextMenuCommand, IsPreviewOn = Settings.AlwaysPreview }; - History = new ResultsViewModel(Settings) + History = new ResultsViewModel(Settings, this) { LeftClickResultCommand = OpenResultCommand, RightClickResultCommand = LoadContextMenuCommand, @@ -1692,6 +1690,12 @@ namespace Flow.Launcher.ViewModel return selected; } + internal bool ResultsSelected(ResultsViewModel results) + { + var selected = SelectedResults == results; + return selected; + } + #endregion #region Hotkey @@ -1847,7 +1851,7 @@ namespace Flow.Launcher.ViewModel { _historyItemsStorage.Save(); _userSelectedRecordStorage.Save(); - _topMostRecordStorage.Save(); + _topMostRecord.Save(); } /// @@ -1880,9 +1884,12 @@ namespace Flow.Launcher.ViewModel { foreach (var result in metaResults.Results) { - if (_topMostRecord.IsTopMost(result)) + var deviationIndex = _topMostRecord.GetTopMostIndex(result); + if (deviationIndex != -1) { - result.Score = Result.MaxScore; + // Adjust the score based on the result's position in the top-most list. + // A lower deviationIndex (closer to the top) results in a higher score. + result.Score = Result.MaxScore - deviationIndex; } else { diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index 46a92f750..9cd36e5c4 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -21,6 +21,7 @@ namespace Flow.Launcher.ViewModel private readonly object _collectionLock = new(); private readonly Settings _settings; + private readonly MainViewModel _mainVM; private int MaxResults => _settings?.MaxResultsToShow ?? 6; public ResultsViewModel() @@ -29,9 +30,10 @@ namespace Flow.Launcher.ViewModel BindingOperations.EnableCollectionSynchronization(Results, _collectionLock); } - public ResultsViewModel(Settings settings) : this() + public ResultsViewModel(Settings settings, MainViewModel mainVM) : this() { _settings = settings; + _mainVM = mainVM; _settings.PropertyChanged += (s, e) => { switch (e.PropertyName) @@ -179,6 +181,7 @@ namespace Flow.Launcher.ViewModel UpdateResults(newResults); } + /// /// To avoid deadlock, this method should not called from main thread /// @@ -202,11 +205,18 @@ namespace Flow.Launcher.ViewModel SelectedItem = Results[0]; } + if (token.IsCancellationRequested) + return; + switch (Visibility) { case Visibility.Collapsed when Results.Count > 0: - SelectedIndex = 0; - Visibility = Visibility.Visible; + if (_mainVM == null || // The results are for preview only in appearance page + _mainVM.ResultsSelected(this)) // The results are selected + { + SelectedIndex = 0; + Visibility = Visibility.Visible; + } break; case Visibility.Visible when Results.Count == 0: Visibility = Visibility.Collapsed; diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 17a1a2b50..1134a81b8 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -1,4 +1,5 @@ -using Flow.Launcher.Infrastructure.UserSettings; +using System; +using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; namespace Flow.Launcher.ViewModel; @@ -12,6 +13,28 @@ public partial class SettingWindowViewModel : BaseModel _settings = settings; } + public bool SetPageType(Type pageType) + { + if (_pageType == pageType) return false; + + _pageType = pageType; + return true; + } + + private Type _pageType = null; + public Type PageType + { + get => _pageType; + set + { + if (_pageType != value) + { + _pageType = value; + OnPropertyChanged(); + } + } + } + public double SettingWindowWidth { get => _settings.SettingWindowWidth; diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 66be08903..e859976bd 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -131,7 +131,17 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader } catch (Exception ex) { - File.Delete(tempDbPath); + try + { + if (File.Exists(tempDbPath)) + { + File.Delete(tempDbPath); + } + } + catch (Exception ex1) + { + Main._context.API.LogException(ClassName, $"Failed to delete temporary favicon DB: {tempDbPath}", ex1); + } Main._context.API.LogException(ClassName, $"Failed to copy favicon DB: {dbPath}", ex); return; } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 9ad31ad14..155069495 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -37,8 +37,6 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex context.CurrentPluginMetadata.PluginCacheDirectoryPath, "FaviconCache"); - FilesFolders.ValidateDirectory(_faviconCacheDir); - LoadBookmarksIfEnabled(); } @@ -50,6 +48,9 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex return; } + // Validate the cache directory before loading all bookmarks because Flow needs this directory to storage favicons + FilesFolders.ValidateDirectory(_faviconCacheDir); + _cachedBookmarks = BookmarkLoader.LoadAllBookmarks(_settings); _ = MonitorRefreshQueueAsync(); _initialized = true; diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs index 633af7b6b..eabd118fb 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs @@ -140,6 +140,29 @@ namespace Flow.Launcher.Plugin.Explorer Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8c8") }); + contextMenus.Add(new Result + { + Title = Context.API.GetTranslation("plugin_explorer_copyname"), + SubTitle = Context.API.GetTranslation("plugin_explorer_copyname_subtitle"), + Action = _ => + { + try + { + Context.API.CopyToClipboard(Path.GetFileName(record.FullPath)); + return true; + } + catch (Exception e) + { + var message = "Fail to set text in clipboard"; + LogException(message, e); + Context.API.ShowMsg(message); + return false; + } + }, + IcoPath = Constants.CopyImagePath, + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue8c8") + }); + contextMenus.Add(new Result { Title = Context.API.GetTranslation("plugin_explorer_copyfilefolder"), diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml index db85ef7b1..0a3bfc36a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ar.xaml @@ -80,6 +80,8 @@ نسخ المسار نسخ مسار العنصر الحالي إلى الحافظة + Copy name + Copy name of current item to clipboard نسخ نسخ الملف الحالي إلى الحافظة نسخ المجلد الحالي إلى الحافظة diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml index 9d23e3afe..4fc248a2a 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/cs.xaml @@ -80,6 +80,8 @@ Kopírovat cestu Zkopírovat cestu k aktuální položce do schránky + Copy name + Copy name of current item to clipboard Kopírovat Kopírovat aktuální soubor do schránky Kopírovat aktuální složku do schránky diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml index 0f7619954..e0ae6d9c8 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/da.xaml @@ -80,6 +80,8 @@ Copy path Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard Copy Copy current file to clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml index f2df655c7..1ee327adb 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/de.xaml @@ -80,6 +80,8 @@ Pfad kopieren Pfad des aktuellen Elements in Zwischenablage kopieren + Copy name + Copy name of current item to clipboard Kopieren Aktuelle Datei in Zwischenablage kopieren Aktuellen Ordner in Zwischenablage kopieren diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml index f7d5bdb18..79f8a5848 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml @@ -82,6 +82,8 @@ Copy path Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard Copy Copy current file to clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml index 304a41c23..490096013 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es-419.xaml @@ -80,6 +80,8 @@ Copy path Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard Copy Copy current file to clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml index e144b724c..8977eb6a7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/es.xaml @@ -80,6 +80,8 @@ Copiar ruta Copia la ruta del elemento actual al portapapeles + Copiar nombre + Copiar nombre del elemento actual al portapapeles Copiar Copia el archivo actual al portapapeles Copia la carpeta actual al portapapeles diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml index 37d557c37..79eab8918 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/fr.xaml @@ -80,6 +80,8 @@ Copier le chemin Copier le chemin de l'élément actuel dans le presse-papiers + Copier le nom + Copier le nom de l'élément actuel dans le presse-papiers Copier Copier le fichier actuel dans le presse-papiers Copier le dossier actuel dans le presse-papiers diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml index cabdd51f3..cc8415692 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/he.xaml @@ -80,6 +80,8 @@ העתק נתיב העתק את הנתיב של הפריט הנוכחי ללוח + העתק שם + העתק את שם הפריט הנוכחי ללוח העתק העתק את הקובץ הנוכחי ללוח העתק את התיקייה הנוכחית ללוח diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml index eb0ef2771..492e3e03d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/it.xaml @@ -80,6 +80,8 @@ Copia percorso Copia negli appunti il percorso dell'elemento corrente + Copy name + Copy name of current item to clipboard Copia Copia file corrente negli appunti Copia cartella corrente negli appunti diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml index 29c97a651..d815e2ddc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ja.xaml @@ -80,6 +80,8 @@ パスをコピー 現在の項目のパスをコピー + Copy name + Copy name of current item to clipboard コピー 現在のファイルをコピー 現在のフォルダーをコピー diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml index 0d96210e1..1d11556ad 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ko.xaml @@ -80,6 +80,8 @@ 경로 복사 이 항목의 경로를 클립보드에 복사 + Copy name + Copy name of current item to clipboard 복사하기 이 파일을 클립보드에 복사 이 파일을 클립보드에 복사 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml index 9c908c569..3488ad2f0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nb.xaml @@ -80,6 +80,8 @@ Kopier sti Kopier sti til gjeldende element til utklippstavlen + Copy name + Copy name of current item to clipboard Kopier Kopier gjeldende fil til utklippstavlen Kopier gjeldende mappe til utklippstavlen diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml index 38e45aaa8..b6816b63f 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/nl.xaml @@ -80,6 +80,8 @@ Copy path Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard Copy Copy current file to clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml index 324b8ad36..f4cecc440 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pl.xaml @@ -80,6 +80,8 @@ Skopiuj Ścieżkę Kopiuj ścieżkę bieżącego elementu do schowka + Copy name + Copy name of current item to clipboard Kopiuj Kopiuj bieżący plik do schowka Kopiuj bieżący folder do schowka diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml index 058bd353f..2754a5a99 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-br.xaml @@ -80,6 +80,8 @@ Copy path Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard Copiar Copy current file to clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml index 39d056e28..af7c58a95 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/pt-pt.xaml @@ -80,6 +80,8 @@ Copiar caminho Copiar caminho do item atual para área de transferência + Copiar nome + Copiar nome do item para a área de transferência Copiar Copiar ficheiro atual para a área de transferência Copiar pasta atual para a área de transferência diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml index 911364fdf..aa67373e2 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/ru.xaml @@ -80,6 +80,8 @@ Скопировать путь Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard Скопировать Copy current file to clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml index 35883a09c..b538ea9e1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sk.xaml @@ -80,6 +80,8 @@ Kopírovať cestu Skopírovať cestu k aktuálnej položke do schránky + Kopírovať názov + Skopírovať názov aktuálnej položky do schránky Kopírovať Skopírovať aktuálny súbor do schránky Skopírovať aktuálny priečinok do schránky diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml index f1227df40..54e419519 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/sr.xaml @@ -80,6 +80,8 @@ Copy path Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard Copy Copy current file to clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml index 1e09ba103..465a4fdd7 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/tr.xaml @@ -80,6 +80,8 @@ Yolu kopyala Mevcut dosya konumunu panoya kopyala + Copy name + Copy name of current item to clipboard Kopyala Mevcut dosyayı panoya kopyala Mevcut klasörü panoya kopyala diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml index d96b5b503..4df9ddaa1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/uk-UA.xaml @@ -80,6 +80,8 @@ Копіювати шлях Копіювати шлях до поточного елемента в буфер обміну + Copy name + Copy name of current item to clipboard Копіювати Копіювання поточного файлу в буфер обміну Копіювати поточну папку в буфер обміну @@ -161,6 +163,5 @@ Рідне контекстне меню Відображати рідне контекстне меню (експериментально) Нижче ви можете вказати елементи, які хочете включити до контекстного меню, вони можуть бути частковими (наприклад, «шир пера») або повними («Відкрити за допомогою»). - Нижче ви можете вказати елементи, які хочете виключити з контекстного меню, вони можуть бути частковими (наприклад, «шир пера») або повними («Відкрити за допомогою»). - + Below you can specify items you want to exclude from context menu, they can be partial (e.g. 'pen wit') or complete ('Open with'). diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml index 81b5a2f92..1fed3d067 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/vi.xaml @@ -80,6 +80,8 @@ Copy đường dẫn Sao chép đường dẫn của mục hiện tại vào clipboard + Copy name + Copy name of current item to clipboard Sao chép Sao chép tập tin hiện tại vào clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml index 64831ea13..67253d1e1 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-cn.xaml @@ -80,6 +80,8 @@ 复制路径 复制当前结果的路径到剪贴板 + Copy name + Copy name of current item to clipboard 复制 复制当前文件到剪贴板 复制当前文件夹到剪贴板 diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml index 9e9697255..873a3866d 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/zh-tw.xaml @@ -80,6 +80,8 @@ 複製路徑 Copy path of current item to clipboard + Copy name + Copy name of current item to clipboard 複製 Copy current file to clipboard Copy current folder to clipboard diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs index e4056131d..1c5d074a0 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; -using System.Windows; using System.Windows.Controls; using Flow.Launcher.Plugin.Explorer.Exceptions; diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index 811bec50c..f23ff71f0 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -10,6 +10,6 @@ public bool WarnFromUnknownSource { get; set; } = true; - public bool AutoRestartAfterChanging { get; set; } = true; + public bool AutoRestartAfterChanging { get; set; } = false; } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml index d352368cb..c65fa196b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ar.xaml @@ -46,6 +46,8 @@ الرجاء تحديد مصدر برنامج هل أنت متأكد أنك تريد حذف مصادر البرامج المحددة؟ + Please select program sources that are not added by you + Please select program sources that are added by you مصدر برنامج آخر بنفس الموقع موجود بالفعل. مصدر البرنامج diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml index ca57bf027..b694c5bdd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/cs.xaml @@ -46,6 +46,8 @@ Prosím vyberte zdroj programu Jste si jisti, že chcete odstranit vybrané zdroje programů? + Please select program sources that are not added by you + Please select program sources that are added by you Již existuje jiný zdroj programu se stejným umístěním. Zdroj programu diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml index a0f09a3b5..5958eba32 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/da.xaml @@ -46,6 +46,8 @@ Please select a program source Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml index ec5d273d6..7919ae7cb 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/de.xaml @@ -46,6 +46,8 @@ Bitte wählen Sie eine Programmquelle aus Sind Sie sicher, dass Sie die ausgewählten Programmquellen löschen wollen? + Please select program sources that are not added by you + Please select program sources that are added by you Eine andere Programmquelle mit dem gleichen Ort ist bereits vorhanden. Programmquelle diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml index ee6b4379d..e551a7dcb 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml @@ -48,6 +48,8 @@ Please select a program source Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml index d9ffa668c..9818e4226 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/es-419.xaml @@ -46,6 +46,8 @@ Please select a program source Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml index d0b6851d9..2e1eadb2b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/es.xaml @@ -46,6 +46,8 @@ Por favor, seleccione la ruta del programa ¿Está seguro de que desea eliminar las fuentes del programa seleccionadas? + Please select program sources that are not added by you + Please select program sources that are added by you Ya existe otra fuente de programa con la misma ubicación. Fuente de Programa diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml index e6fd67936..e509a875f 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/fr.xaml @@ -46,6 +46,8 @@ Veuillez sélectionner une source de programme Êtes-vous sûr de vouloir supprimer les sources de programmes sélectionnées ? + Veuillez sélectionner les sources de programmes qui n'ont pas été ajoutées par vous-même + Veuillez sélectionner les sources de programmes qui ont été ajoutées par vous-même Il existe déjà une autre source de programme ayant le même emplacement. Source du programme diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/he.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/he.xaml index 88053ac57..83f034646 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/he.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/he.xaml @@ -46,6 +46,8 @@ אנא בחר מקור תוכנה האם אתה בטוח שברצונך למחוק את מקורות התוכניות שנבחרו? + Please select program sources that are not added by you + Please select program sources that are added by you מקור תוכנה נוסף עם אותו מיקום כבר קיים. מקור תוכנה diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml index 5004d41cf..bb97a0085 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/it.xaml @@ -46,6 +46,8 @@ Seleziona la sorgente del programma Sei sicuro di voler cancellare le sorgenti dei programmi selezionate? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml index 96ca3af60..4a1d815ec 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ja.xaml @@ -46,6 +46,8 @@ Please select a program source Are your sure to delete {0}? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml index 266aa4b45..f2ac75762 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ko.xaml @@ -46,6 +46,8 @@ 프로그램 검색 출처를 선택하세요 선택하신 프로그램 출처를 삭제하시겠습니까? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml index c4b218204..25018d752 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/nb.xaml @@ -46,6 +46,8 @@ Vennligst velg en programkilde Er du sikker på at du vil slette de valgte programkildene? + Please select program sources that are not added by you + Please select program sources that are added by you Det finnes allerede en annen programkilde med samme plassering. Programkilde diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml index 495296694..eb8a6f1a6 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/nl.xaml @@ -46,6 +46,8 @@ Please select a program source Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml index f38fd9623..4d1483eee 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pl.xaml @@ -46,6 +46,8 @@ Musisz wybrać katalog programu Czy na pewno chcesz usunąć wybrane źródła programów? + Please select program sources that are not added by you + Please select program sources that are added by you Inne źródło programu z tą samą lokalizacją już istnieje. Źródło programu diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml index 3ff8be551..978c2833d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-br.xaml @@ -46,6 +46,8 @@ Please select a program source Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml index aa3c500a3..cc3e280b2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/pt-pt.xaml @@ -46,6 +46,8 @@ Por favor selecione uma origem de programas Tem a certeza de que deseja remover as origens selecionadas? + Selecione as origens de programas NÃO adicionadas por si + Selecione as origens de programas adicionadas por si Já existe uma origem de programas com esta localização. Origem de programas diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml index a75a6d836..7624d6517 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/ru.xaml @@ -46,6 +46,8 @@ Пожалуйста, выберите источник программы Вы уверены, что хотите удалить выбранные источники программ? + Please select program sources that are not added by you + Please select program sources that are added by you Другой источник программы с таким же расположением уже существует. Источник программы diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml index 7fda65160..b30b56a5b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/sk.xaml @@ -46,6 +46,8 @@ Prosím, zadajte zdroj programu Naozaj chcete odstrániť vybrané zdroje programov? + Vyberte zdroje programov, ktoré ste nepridali vy + Vyberte zdroje programov, ktoré ste pridali vy Iný zdroj programu s rovnakým umiestnením už existuje. Zdroj programu diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml index f2da895b0..a79655a7b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/sr.xaml @@ -46,6 +46,8 @@ Please select a program source Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml index 6bda659dd..126613065 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/tr.xaml @@ -46,6 +46,8 @@ İşlem yapmak istediğiniz klasörü seçin. Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml index 3686925a9..290954d5f 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/uk-UA.xaml @@ -46,6 +46,8 @@ Будь ласка, виберіть джерело програми Ви впевнені, що хочете видалити вибрані джерела програм? + Please select program sources that are not added by you + Please select program sources that are added by you Інше програмне джерело з тим самим розташуванням вже існує. Вихідний код програми diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/vi.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/vi.xaml index 6b238c638..0dede0f8b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/vi.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/vi.xaml @@ -46,6 +46,8 @@ Hãy chọn một nguồn dữ liệu Bạn có chắc chắn là muốn xóa các đặt hàng đã chọn? + Please select program sources that are not added by you + Please select program sources that are added by you Đã tồn tại một nguồn chương trình khác có cùng vị trí. Nguồn chương trình diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml index b36b72b9b..6bafe86e0 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-cn.xaml @@ -46,6 +46,8 @@ 请先选择一项 您确定要删除选定的程序源吗? + Please select program sources that are not added by you + Please select program sources that are added by you 相同位置存在另一个程序源。 程序源 diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml index 084adfbac..a8ff4ecbc 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/zh-tw.xaml @@ -46,6 +46,8 @@ 請先選擇一項 Are you sure you want to delete the selected program sources? + Please select program sources that are not added by you + Please select program sources that are added by you Another program source with the same location already exists. Program Source diff --git a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml index 5c0ba8d0b..3bf1c6ad1 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Views/ProgramSetting.xaml @@ -203,6 +203,12 @@ Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}" Click="btnEditProgramSource_OnClick" Content="{DynamicResource flowlauncher_plugin_program_edit}" /> +