diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index d20fa94dc..beb2925bf 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -55,7 +55,7 @@ - + diff --git a/Flow.Launcher.Core/Plugin/JsonRPCConfigurationModel.cs b/Flow.Launcher.Core/Plugin/JsonRPCConfigurationModel.cs index 1f63f85a8..6dc4be881 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCConfigurationModel.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCConfigurationModel.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Flow.Launcher.Core.Plugin { @@ -26,6 +27,8 @@ namespace Flow.Launcher.Core.Plugin public string Name { get; set; } public string Label { get; set; } public string Description { get; set; } + public string urlLabel { get; set; } + public Uri url { get; set; } public bool Validation { get; set; } public List Options { get; set; } public string DefaultValue { get; set; } @@ -40,4 +43,4 @@ namespace Flow.Launcher.Core.Plugin DefaultValue = this.DefaultValue; } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index d9ba4dd40..28d57501b 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -22,6 +22,10 @@ using Control = System.Windows.Controls.Control; using Orientation = System.Windows.Controls.Orientation; using TextBox = System.Windows.Controls.TextBox; using UserControl = System.Windows.Controls.UserControl; +using System.Windows.Documents; +using static System.Windows.Forms.LinkLabel; +using Droplex; +using System.Windows.Forms; namespace Flow.Launcher.Core.Plugin { @@ -336,9 +340,14 @@ namespace Flow.Launcher.Core.Plugin this.context = context; await InitSettingAsync(); } - private static readonly Thickness settingControlMargin = new(10, 4, 10, 4); - private static readonly Thickness settingPanelMargin = new(15, 20, 15, 20); - private static readonly Thickness settingTextBlockMargin = new(10, 4, 10, 4); + private static readonly Thickness settingControlMargin = new(0, 9, 18, 9); + private static readonly Thickness settingCheckboxMargin = new(0, 9, 9, 9); + private static readonly Thickness settingPanelMargin = new(0, 0, 0, 0); + private static readonly Thickness settingTextBlockMargin = new(70, 9, 18, 9); + private static readonly Thickness settingLabelPanelMargin = new(70, 9, 18, 9); + private static readonly Thickness settingLabelMargin = new(0, 0, 0, 0); + private static readonly Thickness settingDescMargin = new(0, 2, 0, 0); + private static readonly Thickness settingSepMargin = new(0, 0, 0, 2); private JsonRpcConfigurationModel _settingsTemplate; public Control CreateSettingPanel() @@ -346,26 +355,60 @@ namespace Flow.Launcher.Core.Plugin if (Settings == null) return new(); var settingWindow = new UserControl(); - var mainPanel = new StackPanel + var mainPanel = new Grid { - Margin = settingPanelMargin, Orientation = Orientation.Vertical + Margin = settingPanelMargin, VerticalAlignment = VerticalAlignment.Center }; - settingWindow.Content = mainPanel; + ColumnDefinition gridCol1 = new ColumnDefinition(); + ColumnDefinition gridCol2 = new ColumnDefinition(); + gridCol1.Width = new GridLength(70, GridUnitType.Star); + gridCol2.Width = new GridLength(30, GridUnitType.Star); + mainPanel.ColumnDefinitions.Add(gridCol1); + mainPanel.ColumnDefinitions.Add(gridCol2); + settingWindow.Content = mainPanel; + int rowCount = 0; foreach (var (type, attribute) in _settingsTemplate.Body) { + Separator sep = new Separator(); + sep.VerticalAlignment = VerticalAlignment.Top; + sep.Margin = settingSepMargin; + sep.SetResourceReference(Separator.BackgroundProperty, "Color03B"); /* for theme change */ var panel = new StackPanel { - Orientation = Orientation.Horizontal, Margin = settingControlMargin + Orientation = Orientation.Vertical, VerticalAlignment = VerticalAlignment.Center, + Margin = settingLabelPanelMargin }; + RowDefinition gridRow = new RowDefinition(); + mainPanel.RowDefinitions.Add(gridRow); var name = new TextBlock() { Text = attribute.Label, - Width = 120, VerticalAlignment = VerticalAlignment.Center, - Margin = settingControlMargin, + Margin = settingLabelMargin, TextWrapping = TextWrapping.WrapWithOverflow }; + var desc = new TextBlock() + { + Text = attribute.Description, FontSize = 12, + VerticalAlignment = VerticalAlignment.Center,Margin = settingDescMargin, + TextWrapping = TextWrapping.WrapWithOverflow + }; + desc.SetResourceReference(TextBlock.ForegroundProperty, "Color04B"); + + if (attribute.Description == null) /* if no description, hide */ + desc.Visibility = Visibility.Collapsed; + + + if (type != "textBlock") /* if textBlock, hide desc */ + { + panel.Children.Add(name); + panel.Children.Add(desc); + } + + + Grid.SetColumn(panel, 0); + Grid.SetRow(panel, rowCount); FrameworkElement contentControl; @@ -377,18 +420,28 @@ namespace Flow.Launcher.Core.Plugin { Text = attribute.Description.Replace("\\r\\n", "\r\n"), Margin = settingTextBlockMargin, - MaxWidth = 500, - TextWrapping = TextWrapping.WrapWithOverflow + Padding = new Thickness(0,0,0,0), + HorizontalAlignment = System.Windows.HorizontalAlignment.Left, + TextAlignment = TextAlignment.Left, + TextWrapping = TextWrapping.Wrap }; - break; + Grid.SetColumn(contentControl, 0); + Grid.SetColumnSpan(contentControl, 2); + Grid.SetRow(contentControl, rowCount); + if (rowCount != 0) + mainPanel.Children.Add(sep); + Grid.SetRow(sep, rowCount); + Grid.SetColumn(sep, 0); + Grid.SetColumnSpan(sep, 2); + break; } case "input": { var textBox = new TextBox() { - Width = 300, Text = Settings[attribute.Name] as string ?? string.Empty, Margin = settingControlMargin, + HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch, ToolTip = attribute.Description }; textBox.TextChanged += (_, _) => @@ -396,17 +449,60 @@ namespace Flow.Launcher.Core.Plugin Settings[attribute.Name] = textBox.Text; }; contentControl = textBox; - break; + Grid.SetColumn(contentControl, 1); + Grid.SetRow(contentControl, rowCount); + if (rowCount != 0) + mainPanel.Children.Add(sep); + Grid.SetRow(sep, rowCount); + Grid.SetColumn(sep, 0); + Grid.SetColumnSpan(sep, 2); + break; } + case "inputWithFileBtn": + { + var textBox = new TextBox() + { + Margin = new Thickness(10, 0, 0, 0), + Text = Settings[attribute.Name] as string ?? string.Empty, + HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch, + ToolTip = attribute.Description + }; + textBox.TextChanged += (_, _) => + { + Settings[attribute.Name] = textBox.Text; + }; + var Btn = new System.Windows.Controls.Button() + { + Margin = new Thickness(10,0,0,0), + Content = "Browse" + }; + var dockPanel = new DockPanel() + { + Margin = settingControlMargin + }; + DockPanel.SetDock(Btn, Dock.Right); + dockPanel.Children.Add(Btn); + dockPanel.Children.Add(textBox); + contentControl = dockPanel; + Grid.SetColumn(contentControl, 1); + Grid.SetRow(contentControl, rowCount); + if (rowCount != 0) + mainPanel.Children.Add(sep); + Grid.SetRow(sep, rowCount); + Grid.SetColumn(sep, 0); + Grid.SetColumnSpan(sep, 2); + break; + } case "textarea": { var textBox = new TextBox() { - Width = 300, Height = 120, Margin = settingControlMargin, + VerticalAlignment = VerticalAlignment.Center, TextWrapping = TextWrapping.WrapWithOverflow, AcceptsReturn = true, + HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch, Text = Settings[attribute.Name] as string ?? string.Empty, ToolTip = attribute.Description }; @@ -415,16 +511,23 @@ namespace Flow.Launcher.Core.Plugin Settings[attribute.Name] = ((TextBox)sender).Text; }; contentControl = textBox; - break; + Grid.SetColumn(contentControl, 1); + Grid.SetRow(contentControl, rowCount); + if (rowCount != 0) + mainPanel.Children.Add(sep); + Grid.SetRow(sep, rowCount); + Grid.SetColumn(sep, 0); + Grid.SetColumnSpan(sep, 2); + break; } case "passwordBox": { var passwordBox = new PasswordBox() { - Width = 300, Margin = settingControlMargin, Password = Settings[attribute.Name] as string ?? string.Empty, PasswordChar = attribute.passwordChar == default ? '*' : attribute.passwordChar, + HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch, ToolTip = attribute.Description }; passwordBox.PasswordChanged += (sender, _) => @@ -432,29 +535,45 @@ namespace Flow.Launcher.Core.Plugin Settings[attribute.Name] = ((PasswordBox)sender).Password; }; contentControl = passwordBox; - break; + Grid.SetColumn(contentControl, 1); + Grid.SetRow(contentControl, rowCount); + if (rowCount != 0) + mainPanel.Children.Add(sep); + Grid.SetRow(sep, rowCount); + Grid.SetColumn(sep, 0); + Grid.SetColumnSpan(sep, 2); + break; } case "dropdown": { - var comboBox = new ComboBox() + var comboBox = new System.Windows.Controls.ComboBox() { ItemsSource = attribute.Options, SelectedItem = Settings[attribute.Name], Margin = settingControlMargin, + HorizontalAlignment = System.Windows.HorizontalAlignment.Right, ToolTip = attribute.Description }; comboBox.SelectionChanged += (sender, _) => { - Settings[attribute.Name] = (string)((ComboBox)sender).SelectedItem; + Settings[attribute.Name] = (string)((System.Windows.Controls.ComboBox)sender).SelectedItem; }; contentControl = comboBox; - break; + Grid.SetColumn(contentControl, 1); + Grid.SetRow(contentControl, rowCount); + if (rowCount != 0) + mainPanel.Children.Add(sep); + Grid.SetRow(sep, rowCount); + Grid.SetColumn(sep, 0); + Grid.SetColumnSpan(sep, 2); + break; } case "checkbox": var checkBox = new CheckBox { IsChecked = Settings[attribute.Name] is bool isChecked ? isChecked : bool.Parse(attribute.DefaultValue), - Margin = settingControlMargin, + Margin = settingCheckboxMargin, + HorizontalAlignment = System.Windows.HorizontalAlignment.Right, ToolTip = attribute.Description }; checkBox.Click += (sender, _) => @@ -462,15 +581,45 @@ namespace Flow.Launcher.Core.Plugin Settings[attribute.Name] = ((CheckBox)sender).IsChecked; }; contentControl = checkBox; + Grid.SetColumn(contentControl, 1); + Grid.SetRow(contentControl, rowCount); + if (rowCount != 0) + mainPanel.Children.Add(sep); + Grid.SetRow(sep, rowCount); + Grid.SetColumn(sep, 0); + Grid.SetColumnSpan(sep, 2); + break; + case "hyperlink": + var hyperlink = new Hyperlink + { + ToolTip = attribute.Description, + NavigateUri = attribute.url + }; + var linkbtn = new System.Windows.Controls.Button + { + HorizontalAlignment = System.Windows.HorizontalAlignment.Right, + Margin = settingControlMargin + }; + linkbtn.Content = attribute.urlLabel; + + contentControl = linkbtn; + Grid.SetColumn(contentControl, 1); + Grid.SetRow(contentControl, rowCount); + if (rowCount != 0) + mainPanel.Children.Add(sep); + Grid.SetRow(sep, rowCount); + Grid.SetColumn(sep, 0); + Grid.SetColumnSpan(sep, 2); break; default: continue; } if (type != "textBlock") _settingControls[attribute.Name] = contentControl; - panel.Children.Add(name); - panel.Children.Add(contentControl); mainPanel.Children.Add(panel); + mainPanel.Children.Add(contentControl); + rowCount++; + } return settingWindow; } @@ -506,7 +655,7 @@ namespace Flow.Launcher.Core.Plugin case PasswordBox passwordBox: passwordBox.Dispatcher.Invoke(() => passwordBox.Password = value as string); break; - case ComboBox comboBox: + case System.Windows.Controls.ComboBox comboBox: comboBox.Dispatcher.Invoke(() => comboBox.SelectedItem = value); break; case CheckBox checkBox: diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index 15a19b210..ab5e4722b 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -29,6 +29,7 @@ namespace Flow.Launcher.Infrastructure public static readonly string DefaultIcon = Path.Combine(ImagesDirectory, "app.png"); public static readonly string ErrorIcon = Path.Combine(ImagesDirectory, "app_error.png"); public static readonly string MissingImgIcon = Path.Combine(ImagesDirectory, "app_missing_img.png"); + public static readonly string LoadingImgIcon = Path.Combine(ImagesDirectory, "loading.png"); public static string PythonPath; public static string NodePath; diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index 5a9122e01..fb773f562 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -58,7 +58,7 @@ - + diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 130221379..deb858a79 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -21,8 +22,10 @@ namespace Flow.Launcher.Infrastructure.Image private static readonly ConcurrentDictionary GuidToKey = new(); private static IImageHashGenerator _hashGenerator; private static readonly bool EnableImageHash = true; - public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon)); - public const int SmallIconSize = 32; + public static ImageSource MissingImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon)); + public static ImageSource LoadingImage { get; } = new BitmapImage(new Uri(Constant.LoadingImgIcon)); + public const int SmallIconSize = 64; + public const int FullIconSize = 256; private static readonly string[] ImageExtensions = @@ -99,6 +102,7 @@ namespace Flow.Launcher.Infrastructure.Image Folder, Data, ImageFile, + FullImageFile, Error, Cache } @@ -111,7 +115,7 @@ namespace Flow.Launcher.Infrastructure.Image { if (string.IsNullOrEmpty(path)) { - return new ImageResult(DefaultImage, ImageType.Error); + return new ImageResult(MissingImage, ImageType.Error); } if (ImageCache.ContainsKey(path, loadFullImage)) @@ -201,6 +205,7 @@ namespace Flow.Launcher.Infrastructure.Image if (loadFullImage) { image = LoadFullImage(path); + type = ImageType.FullImageFile; } else { @@ -215,7 +220,7 @@ namespace Flow.Launcher.Infrastructure.Image else { type = ImageType.File; - image = GetThumbnail(path, ThumbnailOptions.None); + image = GetThumbnail(path, ThumbnailOptions.None, loadFullImage ? FullIconSize : SmallIconSize); } } else @@ -232,12 +237,12 @@ namespace Flow.Launcher.Infrastructure.Image return new ImageResult(image, type); } - private static BitmapSource GetThumbnail(string path, ThumbnailOptions option = ThumbnailOptions.ThumbnailOnly) + private static BitmapSource GetThumbnail(string path, ThumbnailOptions option = ThumbnailOptions.ThumbnailOnly, int size = SmallIconSize) { return WindowsThumbnailProvider.GetThumbnail( path, - Constant.ThumbnailSize, - Constant.ThumbnailSize, + size, + size, option); } @@ -254,6 +259,10 @@ namespace Flow.Launcher.Infrastructure.Image if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache) { // we need to get image hash string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null; + if (imageResult.ImageType == ImageType.FullImageFile) + { + path = $"{path}_{ImageType.FullImageFile}"; + } if (hash != null) { @@ -263,6 +272,7 @@ namespace Flow.Launcher.Infrastructure.Image } else { // new guid + GuidToKey[hash] = path; } } @@ -279,9 +289,33 @@ namespace Flow.Launcher.Infrastructure.Image BitmapImage image = new BitmapImage(); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; - image.UriSource = new Uri(path); + image.UriSource = new Uri(path); image.CreateOptions = BitmapCreateOptions.IgnoreColorProfile; image.EndInit(); + + if (image.PixelWidth > 320) + { + BitmapImage resizedWidth = new BitmapImage(); + resizedWidth.BeginInit(); + resizedWidth.CacheOption = BitmapCacheOption.OnLoad; + resizedWidth.UriSource = new Uri(path); + resizedWidth.CreateOptions = BitmapCreateOptions.IgnoreColorProfile; + resizedWidth.DecodePixelWidth = 320; + resizedWidth.EndInit(); + + if (resizedWidth.PixelHeight > 320) + { + BitmapImage resizedHeight = new BitmapImage(); + resizedHeight.BeginInit(); + resizedHeight.CacheOption = BitmapCacheOption.OnLoad; + resizedHeight.UriSource = new Uri(path); + resizedHeight.CreateOptions = BitmapCreateOptions.IgnoreColorProfile; + resizedHeight.DecodePixelHeight = 320; + resizedHeight.EndInit(); + return resizedHeight; + } + return resizedWidth; + } return image; } } diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 3561c6ffe..09fad990b 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -146,6 +146,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings /// when false Alphabet static service will always return empty results /// public bool ShouldUsePinyin { get; set; } = false; + public bool AlwaysPreview { get; set; } = false; [JsonInclude, JsonConverter(typeof(JsonStringEnumConverter))] public SearchPrecisionScore QuerySearchPrecision { get; private set; } = SearchPrecisionScore.Regular; diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 41d062570..79d106ef2 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -183,6 +183,13 @@ namespace Flow.Launcher.Plugin /// The actionkeyword that is supposed to be removed void RemoveActionKeyword(string pluginId, string oldActionKeyword); + /// + /// Check whether specific ActionKeyword is assigned to any of the plugin + /// + /// The actionkeyword for checking + /// True if the actionkeyword is already assigned, False otherwise + bool ActionKeywordAssigned(string actionKeyword); + /// /// Log debug message /// Message will only be logged in Debug mode diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index f2d9323ef..912a23a6f 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using System.Windows.Controls; using System.Windows.Media; namespace Flow.Launcher.Plugin @@ -203,6 +204,11 @@ namespace Flow.Launcher.Plugin /// public string SubTitleToolTip { get; set; } + /// + /// Customized Preview Panel + /// + public Lazy PreviewPanel { get; set; } + /// /// Run this result, asynchronously /// @@ -223,5 +229,30 @@ namespace Flow.Launcher.Plugin /// /// #26a0da (blue) public string ProgressBarColor { get; set; } = "#26a0da"; + + public PreviewInfo Preview { get; set; } = PreviewInfo.Default; + + /// + /// Info of the preview image. + /// + public record PreviewInfo + { + /// + /// Full image used for preview panel + /// + public string PreviewImagePath { get; set; } + /// + /// Determines if the preview image should occupy the full width of the preveiw panel. + /// + public bool IsMedia { get; set; } + public string Description { get; set; } + + public static PreviewInfo Default { get; } = new() + { + PreviewImagePath = null, + Description = null, + IsMedia = false, + }; + } } } diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index c4341288f..c67a5cf22 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -49,7 +49,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Test/Plugins/ExplorerTest.cs b/Flow.Launcher.Test/Plugins/ExplorerTest.cs index 78be463e4..e0cc9b4c2 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -7,6 +7,7 @@ using Flow.Launcher.Plugin.SharedCommands; using NUnit.Framework; using System; using System.Collections.Generic; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; @@ -32,12 +33,11 @@ namespace Flow.Launcher.Test.Plugins { new Result { - Title="Result 1" + Title = "Result 1" }, - new Result { - Title="Result 2" + Title = "Result 2" } }; } @@ -46,15 +46,13 @@ namespace Flow.Launcher.Test.Plugins private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false; + [SupportedOSPlatform("windows7.0")] [TestCase("C:\\SomeFolder\\", "directory='file:C:\\SomeFolder\\'")] public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString) { - // Given - var queryConstructor = new QueryConstructor(new Settings()); - // When var folderPath = path; - var result = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(folderPath); + var result = QueryConstructor.TopLevelDirectoryConstraint(folderPath); // Then Assert.IsTrue(result == expectedString, @@ -62,6 +60,7 @@ namespace Flow.Launcher.Test.Plugins $"Actual: {result}{Environment.NewLine}"); } + [SupportedOSPlatform("windows7.0")] [TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY System.FileName")] [TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY System.FileName")] public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString) @@ -70,130 +69,68 @@ namespace Flow.Launcher.Test.Plugins var queryConstructor = new QueryConstructor(new Settings()); //When - var queryString = queryConstructor.QueryForTopLevelDirectorySearch(folderPath); + var queryString = queryConstructor.Directory(folderPath); // Then - Assert.IsTrue(queryString == expectedString, + Assert.IsTrue(queryString.Replace(" ", " ") == expectedString.Replace(" ", " "), $"Expected string: {expectedString}{Environment.NewLine} " + $"Actual string was: {queryString}{Environment.NewLine}"); } - [TestCase("C:\\SomeFolder\\flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " + - "FROM SystemIndex WHERE (System.FileName LIKE 'flow.launcher.sln%' " + - "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033))" + - " AND directory='file:C:\\SomeFolder' ORDER BY System.FileName")] + [SupportedOSPlatform("windows7.0")] + [TestCase("C:\\SomeFolder", "flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType" + + " FROM SystemIndex WHERE directory='file:C:\\SomeFolder'" + + " AND (System.FileName LIKE 'flow.launcher.sln%' OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"'))" + + " ORDER BY System.FileName")] public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString( - string userSearchString, string expectedString) + string folderPath, string userSearchString, string expectedString) { // Given var queryConstructor = new QueryConstructor(new Settings()); //When - var queryString = queryConstructor.QueryForTopLevelDirectorySearch(userSearchString); + var queryString = queryConstructor.Directory(folderPath, userSearchString); // Then - Assert.IsTrue(queryString == expectedString, - $"Expected string: {expectedString}{Environment.NewLine} " + - $"Actual string was: {queryString}{Environment.NewLine}"); - } - - [TestCase("C:\\SomeFolder\\SomeApp", "(System.FileName LIKE 'SomeApp%' " + - "OR CONTAINS(System.FileName,'\"SomeApp*\"',1033))" + - " AND directory='file:C:\\SomeFolder'")] - public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryWhereRestrictionsShouldUseDirectoryString( - string userSearchString, string expectedString) - { - // Given - var queryConstructor = new QueryConstructor(new Settings()); - - //When - var queryString = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(userSearchString); - - // Then - Assert.IsTrue(queryString == expectedString, - $"Expected string: {expectedString}{Environment.NewLine} " + - $"Actual string was: {queryString}{Environment.NewLine}"); + Assert.AreEqual(expectedString, queryString); } + [SupportedOSPlatform("windows7.0")] [TestCase("scope='file:'")] public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString) { //When - var resultString = QueryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch; + const string resultString = QueryConstructor.RestrictionsForAllFilesAndFoldersSearch; // Then - Assert.IsTrue(resultString == expectedString, - $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + - $"Actual string was: {resultString}{Environment.NewLine}"); + Assert.AreEqual(expectedString, resultString); } + [SupportedOSPlatform("windows7.0")] [TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " + - "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + - "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")] + "FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " + + "OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")] + [TestCase("", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY System.FileName")] public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString( string userSearchString, string expectedString) { // Given var queryConstructor = new QueryConstructor(new Settings()); - var baseQuery = queryConstructor.CreateBaseQuery(); - + var baseQuery = queryConstructor.CreateBaseQuery(); + // system running this test could have different locale than the hard-coded 1033 LCID en-US. var queryKeywordLocale = baseQuery.QueryKeywordLocale; expectedString = expectedString.Replace("1033", queryKeywordLocale.ToString()); - - //When - var resultString = queryConstructor.QueryForAllFilesAndFolders(userSearchString); + var resultString = queryConstructor.FilesAndFolders(userSearchString); // Then - Assert.IsTrue(resultString == expectedString, - $"Expected query string: {expectedString}{Environment.NewLine} " + - $"Actual string was: {resultString}{Environment.NewLine}"); + Assert.AreEqual(expectedString, resultString); } - [TestCase] - public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearchAsync() - { - // Given - var searchManager = new SearchManager(new Settings(), new PluginInitContext()); - - // When - var results = await searchManager.TopLevelDirectorySearchBehaviourAsync( - MethodWindowsIndexSearchReturnsZeroResultsAsync, - MethodDirectoryInfoClassSearchReturnsTwoResults, - false, - new Query(), - "string not used", - default); - - // Then - Assert.IsTrue(results.Count == 2, - $"Expected to have 2 results from DirectoryInfoClassSearch {Environment.NewLine} " + - $"Actual number of results is {results.Count} {Environment.NewLine}"); - } - - [TestCase] - public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearchAsync() - { - // Given - var searchManager = new SearchManager(new Settings(), new PluginInitContext()); - - // When - var results = await searchManager.TopLevelDirectorySearchBehaviourAsync( - MethodWindowsIndexSearchReturnsZeroResultsAsync, - MethodDirectoryInfoClassSearchReturnsTwoResults, - true, - new Query(), - "string not used", - default); - - // Then - Assert.IsTrue(results.Count == 0, - $"Expected to have 0 results because location is indexed {Environment.NewLine} " + - $"Actual number of results is {results.Count} {Environment.NewLine}"); - } + [SupportedOSPlatform("windows7.0")] [TestCase(@"some words", @"FREETEXT('some words')")] public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString( string querySearchString, string expectedString) @@ -202,7 +139,7 @@ namespace Flow.Launcher.Test.Plugins var queryConstructor = new QueryConstructor(new Settings()); //When - var resultString = queryConstructor.QueryWhereRestrictionsForFileContentSearch(querySearchString); + var resultString = QueryConstructor.RestrictionsForFileContentSearch(querySearchString); // Then Assert.IsTrue(resultString == expectedString, @@ -210,8 +147,9 @@ namespace Flow.Launcher.Test.Plugins $"Actual string was: {resultString}{Environment.NewLine}"); } + [SupportedOSPlatform("windows7.0")] [TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " + - "FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")] + "FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")] public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString( string userSearchString, string expectedString) { @@ -219,7 +157,7 @@ namespace Flow.Launcher.Test.Plugins var queryConstructor = new QueryConstructor(new Settings()); //When - var resultString = queryConstructor.QueryForFileContentSearch(userSearchString); + var resultString = queryConstructor.FileContent(userSearchString); // Then Assert.IsTrue(resultString == expectedString, @@ -230,12 +168,15 @@ namespace Flow.Launcher.Test.Plugins public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileContentSearchRequiredShouldReturnTrue() { // Given - var query = new Query { ActionKeyword = "doc:", Search = "search term" }; + var query = new Query + { + ActionKeyword = "doc:", Search = "search term" + }; var searchManager = new SearchManager(new Settings(), new PluginInitContext()); // When - var result = searchManager.IsFileContentSearch(query.ActionKeyword); + var result = SearchManager.IsFileContentSearch(query.ActionKeyword); // Then Assert.IsTrue(result, @@ -303,24 +244,19 @@ namespace Flow.Launcher.Test.Plugins $"Actual path string is {returnedPath} {Environment.NewLine}"); } - [TestCase("c:\\SomeFolder\\>", "scope='file:c:\\SomeFolder'")] - [TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' " - + "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND " - + "scope='file:c:\\SomeFolder'")] - public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString) + [SupportedOSPlatform("windows7.0")] + [TestCase("c:\\SomeFolder", "scope='file:c:\\SomeFolder'")] + [TestCase("c:\\OtherFolder", "scope='file:c:\\OtherFolder'")] + public void GivenFilePath_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString) { - // Given - var queryConstructor = new QueryConstructor(new Settings()); - //When - var resultString = queryConstructor.QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path); + var resultString = QueryConstructor.RecursiveDirectoryConstraint(path); // Then - Assert.IsTrue(resultString == expectedString, - $"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " + - $"Actual string was: {resultString}{Environment.NewLine}"); + Assert.AreEqual(expectedString, resultString); } + [SupportedOSPlatform("windows7.0")] [TestCase("c:\\somefolder\\>somefile", "*somefile*")] [TestCase("c:\\somefolder\\somefile", "somefile*")] [TestCase("c:\\somefolder\\", "*")] @@ -331,9 +267,7 @@ namespace Flow.Launcher.Test.Plugins var resultString = DirectoryInfoSearch.ConstructSearchCriteria(path); // Then - Assert.IsTrue(resultString == expectedString, - $"Expected criteria string: {expectedString}{Environment.NewLine} " + - $"Actual criteria string was: {resultString}{Environment.NewLine}"); + Assert.AreEqual(expectedString, resultString); } } } diff --git a/Flow.Launcher.Test/Plugins/ProgramTest.cs b/Flow.Launcher.Test/Plugins/ProgramTest.cs deleted file mode 100644 index e3a05f484..000000000 --- a/Flow.Launcher.Test/Plugins/ProgramTest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Flow.Launcher.Plugin.Program.Programs; -using NUnit.Framework; -using System; -using Windows.ApplicationModel; - -namespace Flow.Launcher.Test.Plugins -{ - [TestFixture] - public class ProgramTest - { - [TestCase("Microsoft.WindowsCamera", "ms-resource:LensSDK/Resources/AppTitle", "ms-resource://Microsoft.WindowsCamera/LensSDK/Resources/AppTitle")] - [TestCase("microsoft.windowscommunicationsapps", "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName", - "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName")] - [TestCase("windows.immersivecontrolpanel", "ms-resource:DisplayName", "ms-resource://windows.immersivecontrolpanel/Resources/DisplayName")] - [TestCase("Microsoft.MSPaint", "ms-resource:AppName", "ms-resource://Microsoft.MSPaint/Resources/AppName")] - public void WhenGivenPriReferenceValueShouldReturnCorrectFormat(string packageName, string rawPriReferenceValue, string expectedFormat) - { - // Arrange - var app = new UWP.Application(); - - // Act - var result = UWP.Application.FormattedPriReferenceValue(packageName, rawPriReferenceValue); - - // Assert - Assert.IsTrue(result == expectedFormat, - $"Expected Pri reference format: {expectedFormat}{Environment.NewLine} " + - $"Actual: {result}{Environment.NewLine}"); - } - } -} diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index f59d3d26f..1d403c5a1 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -80,7 +80,7 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|Any CPU diff --git a/Flow.Launcher/ActionKeywords.xaml.cs b/Flow.Launcher/ActionKeywords.xaml.cs index e116e6f4d..012c9ff4e 100644 --- a/Flow.Launcher/ActionKeywords.xaml.cs +++ b/Flow.Launcher/ActionKeywords.xaml.cs @@ -8,24 +8,17 @@ using Flow.Launcher.ViewModel; namespace Flow.Launcher { - public partial class ActionKeywords : Window + public partial class ActionKeywords { private readonly PluginPair plugin; - private Settings settings; private readonly Internationalization translater = InternationalizationManager.Instance; private readonly PluginViewModel pluginViewModel; - public ActionKeywords(string pluginId, Settings settings, PluginViewModel pluginViewModel) + public ActionKeywords(PluginViewModel pluginViewModel) { InitializeComponent(); - plugin = PluginManager.GetPluginForId(pluginId); - this.settings = settings; + plugin = pluginViewModel.PluginPair; this.pluginViewModel = pluginViewModel; - if (plugin == null) - { - MessageBox.Show(translater.GetTranslation("cannotFindSpecifiedPlugin")); - Close(); - } } private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e) diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 0857fb55b..43fa0eddb 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -186,7 +186,7 @@ namespace Flow.Launcher public void OnSecondAppStarted() { - Current.MainWindow.Show(); + _mainVM.Show(); } } } diff --git a/Flow.Launcher/Converters/OrdinalConverter.cs b/Flow.Launcher/Converters/OrdinalConverter.cs index 0c716ac7e..02b9bdbde 100644 --- a/Flow.Launcher/Converters/OrdinalConverter.cs +++ b/Flow.Launcher/Converters/OrdinalConverter.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Windows.Controls; using System.Windows.Data; diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index b1a822fb6..1f74ea8a5 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -1,4 +1,4 @@ - + WinExe @@ -92,12 +92,12 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Flow.Launcher/Images/Browser.png b/Flow.Launcher/Images/Browser.png index 5d475f82e..a5bc848c7 100644 Binary files a/Flow.Launcher/Images/Browser.png and b/Flow.Launcher/Images/Browser.png differ diff --git a/Flow.Launcher/Images/app_missing_img.png b/Flow.Launcher/Images/app_missing_img.png index 27e366bbc..0bb16e5d8 100644 Binary files a/Flow.Launcher/Images/app_missing_img.png and b/Flow.Launcher/Images/app_missing_img.png differ diff --git a/Flow.Launcher/Images/loading.png b/Flow.Launcher/Images/loading.png new file mode 100644 index 000000000..1600b5967 Binary files /dev/null and b/Flow.Launcher/Images/loading.png differ diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml index cf69fd853..cc3d9d527 100644 --- a/Flow.Launcher/Languages/da.xaml +++ b/Flow.Launcher/Languages/da.xaml @@ -62,6 +62,8 @@ Changes minimum match score required for results. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ af Initaliseringstid: Søgetid: - | Version + Version Website Uninstall @@ -113,6 +115,14 @@ Søg efter flere temaer How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Søgefelt skrifttype Resultat skrifttype Vindue mode diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml index 0853b4b91..dc7d669f4 100644 --- a/Flow.Launcher/Languages/de.xaml +++ b/Flow.Launcher/Languages/de.xaml @@ -62,6 +62,8 @@ Erforderliche Suchergebnisse. Pinyin aktivieren Ermöglicht die Verwendung von Pinyin für die Suche. Pinyin ist das Standardsystem der romanisierten Schreibweise für die Übersetzung von chinesischen Texten. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Der Schatteneffekt ist nicht zulässig, wenn das aktuelle Thema den Weichzeichneffekt aktiviert hat @@ -113,6 +115,14 @@ Suche nach weiteren Themes Wie man ein Design erstellt Hallo! + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Programm + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Abfragebox Schriftart Ergebnis Schriftart Fenstermodus @@ -163,9 +173,9 @@ Press Key - HTTP Proxy + HTTP-Proxy Aktiviere HTTP Proxy - HTTP Server + HTTP-Server Port Benutzername Passwort @@ -180,9 +190,9 @@ Über - Website + Webseite Github - Docs + Dokumentation Version Icons Sie haben Flow Launcher {0} mal aktiviert @@ -207,24 +217,24 @@ Select File Manager Please specify the file location of the file manager you using and add arguments if necessary. The default arguments are "%d", and a path is entered at that location. For example, If a command is required such as "totalcmd.exe /A c:\windows", argument is /A "%d". "%f" is an argument that represent the file path. It is used to emphasize the file/folder name when opening a specific file location in 3rd party file manager. This argument is only available in the "Arg for File" item. If the file manager does not have that function, you can use "%d". - File Manager - Profile Name + Datei-Manager + Profilname File Manager Path Arg For Folder Arg For File - Default Web Browser + Standard-Webbrowser The default setting follows the OS default browser setting. If specified separately, flow uses that browser. Browser Browser-Name - Browser Path + Browserpfad New Window New Tab - Private Mode + Privater Modus - Change Priority + Priorität ändern Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number Please provide an valid integer for Priority! diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 661c66bf0..fcc364dc2 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -68,6 +68,8 @@ Changes minimum match score required for results. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -92,7 +94,7 @@ by Init time: Query time: - | Version + Version Website Uninstall @@ -119,6 +121,14 @@ Theme Gallery How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Query Box Font Result Item Font Window Mode diff --git a/Flow.Launcher/Languages/es-419.xaml b/Flow.Launcher/Languages/es-419.xaml index a3f61ef05..3ac0e22d8 100644 --- a/Flow.Launcher/Languages/es-419.xaml +++ b/Flow.Launcher/Languages/es-419.xaml @@ -62,6 +62,8 @@ Cambia la puntuación mínima de similitud requerida para resultados. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. El efecto de sombra no está permitido mientras el tema actual tenga el efecto de desenfoque habilitado @@ -86,7 +88,7 @@ por Tiempo de inicio: Tiempo de consulta: - | Versión + Versión Sitio web Uninstall @@ -113,6 +115,14 @@ Galería de Temas Cómo crear un tema Hola + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Fuente del cuadro de consulta Fuente de los resultados Modo Ventana diff --git a/Flow.Launcher/Languages/es.xaml b/Flow.Launcher/Languages/es.xaml index b158dbb57..03fdfb21c 100644 --- a/Flow.Launcher/Languages/es.xaml +++ b/Flow.Launcher/Languages/es.xaml @@ -62,6 +62,8 @@ Cambia la puntuación mínima requerida para la coincidencia de los resultados. Buscar con Pinyin Permite utilizar Pinyin para la búsqueda. Pinyin es el sistema estándar de ortografía romanizado para traducir chino. + Mostrar siempre vista previa + Muestra siempre el panel de vista previa al iniciar Flow. Pulse F1 para mostrar/ocultar la vista previa. El efecto de sombra no está permitido mientras el tema actual tenga el efecto de desenfoque activado @@ -86,7 +88,7 @@ por Tiempo de inicio: Tiempo de consulta: - | Versión + Versión Sitio web Desinstalar @@ -113,6 +115,14 @@ Galería de temas Cómo crear un tema Hola + Explorador + Buscar archivos, carpetas y contenido de archivos + Búsqueda Web + Buscar en la web con el apoyo de diferentes motores de búsqueda + Programa + Iniciar programas como administrador o como usuario diferente + Eliminar Procesos + Terminar procesos no deseados Fuente del texto del cuadro de consulta Fuente del texto de los resultados Modo Ventana diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml index 7f874a334..7ebd158c2 100644 --- a/Flow.Launcher/Languages/fr.xaml +++ b/Flow.Launcher/Languages/fr.xaml @@ -62,6 +62,8 @@ Changes minimum match score required for results. Devrait utiliser le pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ by Chargement : Utilisation : - | Version + Version Website Désinstaller @@ -113,6 +115,14 @@ Trouver plus de thèmes How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Police (barre de recherche) Police (liste des résultats) Mode fenêtré diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml index 0367cc4ac..1aabba84d 100644 --- a/Flow.Launcher/Languages/it.xaml +++ b/Flow.Launcher/Languages/it.xaml @@ -62,6 +62,8 @@ Modifica il punteggio minimo richiesto per i risultati. Dovrebbe usare il Pinyin Consente di utilizzare il Pinyin per la ricerca. Il Pinyin è il sistema standard di ortografia romanizzata per la traduzione del cinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. L'effetto ombra non è consentito mentre il tema corrente ha un effetto di sfocatura abilitato @@ -86,7 +88,7 @@ da Tempo di avvio: Tempo ricerca: - | Versione + Versione Sito Web Disinstalla @@ -113,6 +115,14 @@ Sfoglia per altri temi Come creare un tema Ciao + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Font campo di ricerca Font campo risultati Modalità finestra diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml index 31b33a653..d93021301 100644 --- a/Flow.Launcher/Languages/ja.xaml +++ b/Flow.Launcher/Languages/ja.xaml @@ -62,6 +62,8 @@ Changes minimum match score required for results. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ by 初期化時間: クエリ時間: - | バージョン + バージョン ウェブサイト アンインストール @@ -113,6 +115,14 @@ テーマを探す How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes 検索ボックスのフォント 検索結果一覧のフォント ウィンドウモード diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index cd4134c7b..3c79cc045 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -34,7 +34,7 @@ 포커스 잃으면 Flow Launcher 숨김 새 버전 알림 끄기 검색 창 위치 - Remember Last Position + 마지막 위치 기억 마우스 위치 화면 - 중앙 마우스 위치 화면 - 중앙 상단 마우스 위치 화면 - 좌측 상단 @@ -62,6 +62,8 @@ 검색 결과에 필요한 최소 매치 점수를 변경합니다. 항상 Pinyin 사용 Pinyin을 사용하여 검색할 수 있습니다. Pinyin (병음) 은 로마자 중국어 입력 방식입니다. + 항상 미리보기 + 항상 미리보기 패널이 열린 상태로 Flow를 시작합니다. F1키로 미리보기를 on/off 합니다. 반투명 흐림 효과를 사용하는 경우, 그림자 효과를 쓸 수 없습니다. @@ -86,7 +88,7 @@ 제작자 초기화 시간: 쿼리 시간: - | 버전 + 버전 웹사이트 제거 @@ -113,6 +115,14 @@ 테마 갤러리 테마 제작 안내 안녕하세요! + 탐색기 + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + 프로그램 + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes 쿼리 상자 글꼴 결과 항목 글꼴 윈도우 모드 @@ -137,14 +147,14 @@ Flow Launcher 단축키 Flow Launcher를 열 때 사용할 단축키를 입력합니다. 결과 선택 단축키 - 결과 목록을 선택하는 단축키입니다. + 결과 항목을 선택하는 단축키입니다. 단축키 표시 결과창에서 결과 선택 단축키를 표시합니다. 사용자지정 쿼리 단축키 - Custom Query Shortcut + 사용자 지정 쿼리 단축어 Built-in Shortcut 쿼리 - Shortcut + 단축어 확장 설명 삭제 @@ -187,7 +197,7 @@ 아이콘 Flow Launcher를 {0}번 실행했습니다. 업데이트 확인 - Become A Sponsor + 후원하기 새 버전({0})이 있습니다. Flow Launcher를 재시작하세요. 업데이트 확인을 실패했습니다. api.github.com로의 연결 또는 프록시 설정을 확인해주세요. @@ -196,7 +206,7 @@ 릴리즈 노트 사용 팁 - 개발자도구 + 개발자 도구 설정 폴더 로그 폴더 로그 삭제 @@ -249,7 +259,7 @@ 업데이트 - Custom Query Shortcut + 사용자 지정 쿼리 단축어 Enter a shortcut that automatically expands to the specified query. Shortcut already exists, please enter a new Shortcut or edit the existing one. Shortcut and/or its expansion is empty. diff --git a/Flow.Launcher/Languages/nb.xaml b/Flow.Launcher/Languages/nb.xaml index f7808909b..017a2064a 100644 --- a/Flow.Launcher/Languages/nb.xaml +++ b/Flow.Launcher/Languages/nb.xaml @@ -62,6 +62,8 @@ Changes minimum match score required for results. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ by Init time: Query time: - | Version + Version Website Uninstall @@ -113,6 +115,14 @@ Theme Gallery How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Query Box Font Result Item Font Window Mode diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index b599b293f..19ea7cda6 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -62,6 +62,8 @@ Wijzigt de minimale overeenkomst-score die vereist is voor resultaten. Zou Pinyin moeten gebruiken Zorgt ervoor dat Pinyin gebruikt kan worden om te zoeken. Pinyin is het standaard systeem van geromaniseerde spelling voor het vertalen van Chinees. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Schaduw effect is niet toegestaan omdat het huidige thema een vervagingseffect heeft @@ -86,7 +88,7 @@ door Init tijd: Query tijd: - | Versie + Versie Website Uninstall @@ -113,6 +115,14 @@ Zoek meer thema´s Hoe maak je een thema Hallo daar + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Query Box lettertype Resultaat Item lettertype Venster Modus diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index 53688b3f5..17ad0fec7 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -62,6 +62,8 @@ Changes minimum match score required for results. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ by Czas ładowania: Czas zapytania: - | Version + Wersja Website Odinstalowywanie @@ -113,6 +115,14 @@ Znajdź więcej skórek How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Programy + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Czcionka okna zapytania Czcionka okna wyników Tryb w oknie diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml index 99850fdc7..23dfbc561 100644 --- a/Flow.Launcher/Languages/pt-br.xaml +++ b/Flow.Launcher/Languages/pt-br.xaml @@ -12,14 +12,14 @@ Configurações Sobre Sair - Close + Fechar Copy - Cut - Paste + Cortar + Colar File Folder Text - Game Mode + Modo Gamer Suspend the use of Hotkeys. Position Reset Reset search window position @@ -62,6 +62,8 @@ Changes minimum match score required for results. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ by Tempo de inicialização: Tempo de consulta: - | Version + Versão Website Desinstalar @@ -113,6 +115,14 @@ Ver mais temas How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Fonte da caixa de Consulta Fonte do Resultado Modo Janela diff --git a/Flow.Launcher/Languages/pt-pt.xaml b/Flow.Launcher/Languages/pt-pt.xaml index 5e05f3741..1590d0896 100644 --- a/Flow.Launcher/Languages/pt-pt.xaml +++ b/Flow.Launcher/Languages/pt-pt.xaml @@ -62,6 +62,8 @@ Altera a precisão mínima necessário para obter resultados Pesquisar com Pinyin Permite a utilização de Pinyin para pesquisar. Pinyin é um sistema normalizado de ortografia romanizada para tradução de mandarim. + Pré-visualizar sempre + Abrir painel de pré-visualização ao iniciar a aplicação. Prima F1 para comutar esta opção. O efeito sombra não é permitido com este tema porque o efeito desfocar está ativo @@ -86,7 +88,7 @@ de Tempo de arranque: Tempo de consulta: - | Versão + Versão Site Desinstalar @@ -113,6 +115,14 @@ Galeria de temas Como criar um tema Olá + Explorador + Pesquisar por ficheiros, pastas e conteúdo dos ficheiros + Pesquisa Web + Pesquisa na web com suporte a diversos motores de pesquisa + Programas + Iniciar programas como administrador ou utilizador + Terminador de processos + Terminar processos indesejados Tipo de letra da consulta Tipo de letra dos resultados Modo da janela diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index 22d134ef2..d6582c588 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -62,6 +62,8 @@ Changes minimum match score required for results. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ by Инициализация: Запрос: - | Version + Версия Website Удалить @@ -113,6 +115,14 @@ Найти больше тем How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Шрифт запросов Шрифт результатов Оконный режим diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index 9d1e7a9fe..00ce6b7ca 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -62,6 +62,8 @@ Mení minimálne skóre zhody potrebné na zobrazenie výsledkov. Vyhľadávanie pomocou pchin-jin Umožňuje vyhľadávanie pomocou pchin-jin. Pchin-jin je systém zápisu čínskeho jazyka pomocou písmen latinky. + Vždy zobraziť náhľad + Pri spustení Flowu vždy otvoriť panel s náhľadom. Stlačením klávesu F1 prepnete náhľad. Efekt tieňa nie je povolený, kým má aktuálny motív povolený efekt rozostrenia @@ -86,7 +88,7 @@ od Inicializácia: Trvanie dopytu: - | Verzia + Verzia Webstránka Odinštalovať @@ -113,6 +115,14 @@ Galéria motívov Ako vytvoriť motív Ahojte + Prieskumník + Vyhľadávanie súborov, priečinkov a obsahu súborov + Webové vyhľadávanie + Vyhľadávanie na webe s podporou rôznych vyhľadávačov + Program + Spúšťanie programov ako správca alebo iný používateľ + ProcessKiller + Ukončenie nežiaducich procesov Písmo vyhľadávacieho poľa Písmo výsledkov Režim okno diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml index 2fe9f7e30..f32b44652 100644 --- a/Flow.Launcher/Languages/sr.xaml +++ b/Flow.Launcher/Languages/sr.xaml @@ -62,6 +62,8 @@ Changes minimum match score required for results. Search with Pinyin Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ by Vreme inicijalizacije: Vreme upita: - | Version + Verzija Website Uninstall @@ -113,6 +115,14 @@ Pretražite još tema How to create a theme Hi There + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Font upita Font rezultata Režim prozora diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml index c81eed114..d47f0a740 100644 --- a/Flow.Launcher/Languages/tr.xaml +++ b/Flow.Launcher/Languages/tr.xaml @@ -62,6 +62,8 @@ Sonuçlar için gereken minimum maç puanını değiştirir. Pinyin kullanılmalı Arama yapmak için Pinyin'in kullanılmasına izin verir. Pinyin, Çince'yi çevirmek için standart romanlaştırılmış yazım sistemidir. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Mevcut temada bulanıklık efekti etkinken gölge efektine izin verilmez @@ -113,6 +115,14 @@ Daha fazla tema bul Nasıl bir tema yaratılır Merhaba + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Pencere Yazı Tipi Sonuç Yazı Tipi Pencere Modu diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml index 306abff9e..c31bebe25 100644 --- a/Flow.Launcher/Languages/uk-UA.xaml +++ b/Flow.Launcher/Languages/uk-UA.xaml @@ -62,6 +62,8 @@ Змінює мінімальний бал збігів, необхідних для результатів. Використовувати піньїнь Дозволяє використовувати пінїнь для пошуку. Піньїнь - це стандартна система написання для перекладу китайської. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Ефект тіні не дозволено, коли поточна тема має ефект розмиття @@ -86,7 +88,7 @@ за Ініціалізація: Запит: - | Версія + Версія Сайт Uninstall @@ -113,6 +115,14 @@ Знайти більше тем Як створити тему Привіт усім + Explorer + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + Program + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes Шрифт запитів Шрифт результатів Віконний режим diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index e586e68ba..d96948951 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -34,7 +34,7 @@ 失去焦点时自动隐藏 Flow Launcher 不显示新版本提示 搜索窗口位置 - Remember Last Position + 记住上次的位置 鼠标所在的屏幕 - 中央 鼠标所在的屏幕 - 顶部中央 鼠标所在的屏幕 - 左上角 @@ -62,6 +62,8 @@ 更改匹配成功所需的最低分数。 使用 Pinyin 搜索 允许使用拼音进行搜索. + 始终打开预览 + Flow 启动时总是打开预览面板。按 F1 以切换预览。 当前主题已启用模糊效果,不允许启用阴影效果 @@ -86,7 +88,7 @@ 出自 加载耗时: 查询耗时: - | 版本 + 版本 官方网站 卸载 @@ -113,6 +115,14 @@ 浏览更多主题 如何创建一个主题 你好! + 文件管理器 + 搜索文件、 文件夹和文件内容 + 网络搜索 + 使用多个搜索引擎搜索网络 + 程序 + 以管理员或其他用户身份启动程序 + 进程杀手 + 终止不需要的进程 查询框字体 结果项字体 窗口模式 @@ -141,18 +151,18 @@ 显示热键 显示用于打开结果的快捷键。 自定义查询热键 - Custom Query Shortcut - Built-in Shortcut + 自定义查询捷径 + 内置捷径 查询 捷径 - Expansion + 展开 描述 删除 编辑 增加 请选择一项 你确定要删除插件 {0} 的热键吗? - Are you sure you want to delete shortcut: {0} with expansion {1}? + 你确定要删除捷径 {0} (展开为 {1})? 从剪贴板获取文本。 查询窗口阴影效果 阴影效果将占用大量的GPU资源。 如果您的计算机性能有限,则不建议使用。 @@ -187,7 +197,7 @@ 图标 你已经激活了 Flow Launcher {0} 次 检查更新 - Become A Sponsor + 成为赞助者 发现新版本 {0}, 请重启 Flow Launcher 下载更新失败,请检查您与 api.github.com 的连接状态或检查代理设置 @@ -242,7 +252,7 @@ 自定义查询热键 - Press a custom hotkey to open Flow Laucher and input the specified query automatically. + 输入一个自定义的快捷键来打开 Flow Laucher 并自动输入指定的查询。 预览 热键不可用,请选择一个新的热键 插件热键不合法 @@ -250,9 +260,9 @@ 自定义查询捷径 - Enter a shortcut that automatically expands to the specified query. - Shortcut already exists, please enter a new Shortcut or edit the existing one. - Shortcut and/or its expansion is empty. + 输入一个捷径,它将自动展开为一个查询。 + 捷径已存在,请输入一个新的或者编辑已有的。 + 捷径及其展开均不能为空。 热键不可用 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index c3e6f42f6..3757c7d4d 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -62,6 +62,8 @@ Changes minimum match score required for results. 拼音搜索 允許使用拼音來搜索. + Always Preview + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled @@ -86,7 +88,7 @@ 作者 載入耗時: 查詢耗時: - | 版本 + 版本 官方網站 解除安裝 @@ -113,6 +115,14 @@ 瀏覽更多主題 如何創建一個主題 你好呀 + 檔案總管 + Search for files, folders and file contents + WebSearch + Search the web with different search engine support + 程式 + Launch programs as admin or a different user + ProcessKiller + Terminate unwanted processes 查詢框字體 結果項字體 視窗模式 diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 49854ed81..9f8d523e0 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -13,7 +13,6 @@ Title="Flow Launcher" MinWidth="{Binding MainWindowWidth, Mode=OneWay}" MaxWidth="{Binding MainWindowWidth, Mode=OneWay}" - d:DataContext="{d:DesignInstance vm:MainViewModel}" AllowDrop="True" AllowsTransparency="True" Background="Transparent" @@ -285,6 +284,20 @@ + @@ -306,72 +319,158 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 6d6549250..56c45aeb0 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -57,9 +57,9 @@ namespace Flow.Launcher DataContext = mainVM; _viewModel = mainVM; _settings = settings; - + InitializeComponent(); - InitializePosition(); + InitializePosition(); animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); } @@ -67,7 +67,7 @@ namespace Flow.Launcher { InitializeComponent(); } - + private void OnCopy(object sender, ExecutedRoutedEventArgs e) { if (QueryTextBox.SelectionLength == 0) @@ -106,6 +106,7 @@ namespace Flow.Launcher WindowsInteropHelper.DisableControlBox(this); InitProgressbarAnimation(); InitializePosition(); + PreviewReset(); // since the default main window visibility is visible // so we need set focus during startup QueryTextBox.Focus(); @@ -114,6 +115,8 @@ namespace Flow.Launcher switch (e.PropertyName) { case nameof(MainViewModel.MainWindowVisibilityStatus): + { + Dispatcher.Invoke(() => { if (_viewModel.MainWindowVisibilityStatus) { @@ -123,6 +126,7 @@ namespace Flow.Launcher animationSound.Play(); } UpdatePosition(); + PreviewReset(); Activate(); QueryTextBox.Focus(); _settings.ActivateTimes++; @@ -138,7 +142,7 @@ namespace Flow.Launcher isProgressBarStoryboardPaused = false; } - if(_settings.UseAnimation) + if (_settings.UseAnimation) WindowAnimator(); } else if (!isProgressBarStoryboardPaused) @@ -146,27 +150,27 @@ namespace Flow.Launcher _progressBarStoryboard.Stop(ProgressBar); isProgressBarStoryboardPaused = true; } - - break; - } + }); + break; + } case nameof(MainViewModel.ProgressBarVisibility): + { + Dispatcher.Invoke(() => { - Dispatcher.Invoke(() => + if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) { - if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Stop(ProgressBar); - isProgressBarStoryboardPaused = true; - } - else if (_viewModel.MainWindowVisibilityStatus && - isProgressBarStoryboardPaused) - { - _progressBarStoryboard.Begin(ProgressBar, true); - isProgressBarStoryboardPaused = false; - } - }); - break; - } + _progressBarStoryboard.Stop(ProgressBar); + isProgressBarStoryboardPaused = true; + } + else if (_viewModel.MainWindowVisibilityStatus && + isProgressBarStoryboardPaused) + { + _progressBarStoryboard.Begin(ProgressBar, true); + isProgressBarStoryboardPaused = false; + } + }); + break; + } case nameof(MainViewModel.QueryTextCursorMovedToEnd): if (_viewModel.QueryTextCursorMovedToEnd) { @@ -249,35 +253,45 @@ namespace Flow.Launcher contextMenu = new ContextMenu(); - var openIcon = new FontIcon { Glyph = "\ue71e" }; + var openIcon = new FontIcon + { + Glyph = "\ue71e" + }; var open = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", - Icon = openIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", Icon = openIcon + }; + var gamemodeIcon = new FontIcon + { + Glyph = "\ue7fc" }; - var gamemodeIcon = new FontIcon { Glyph = "\ue7fc" }; var gamemode = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("GameMode"), - Icon = gamemodeIcon + Header = InternationalizationManager.Instance.GetTranslation("GameMode"), Icon = gamemodeIcon + }; + var positionresetIcon = new FontIcon + { + Glyph = "\ue73f" }; - var positionresetIcon = new FontIcon { Glyph = "\ue73f" }; var positionreset = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("PositionReset"), - Icon = positionresetIcon + Header = InternationalizationManager.Instance.GetTranslation("PositionReset"), Icon = positionresetIcon + }; + var settingsIcon = new FontIcon + { + Glyph = "\ue713" }; - var settingsIcon = new FontIcon { Glyph = "\ue713" }; var settings = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), - Icon = settingsIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), Icon = settingsIcon + }; + var exitIcon = new FontIcon + { + Glyph = "\ue7e8" }; - var exitIcon = new FontIcon { Glyph = "\ue7e8" }; var exit = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), - Icon = exitIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), Icon = exitIcon }; open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); @@ -340,22 +354,20 @@ namespace Flow.Launcher } private async void PositionReset() { - _viewModel.Show(); - await Task.Delay(300); // If don't give a time, Positioning will be weird. - Left = HorizonCenter(); - Top = VerticalCenter(); + _viewModel.Show(); + await Task.Delay(300); // If don't give a time, Positioning will be weird. + Left = HorizonCenter(); + Top = VerticalCenter(); } private void InitProgressbarAnimation() { - var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 150, - new Duration(new TimeSpan(0, 0, 0, 0, 1600))); - var da1 = new DoubleAnimation(ProgressBar.X1, ActualWidth + 50, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); + var da1 = new DoubleAnimation(ProgressBar.X1, ActualWidth + 0, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)")); Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)")); _progressBarStoryboard.Children.Add(da); _progressBarStoryboard.Children.Add(da1); _progressBarStoryboard.RepeatBehavior = RepeatBehavior.Forever; - _viewModel.ProgressBarVisibility = Visibility.Hidden; isProgressBarStoryboardPaused = true; } @@ -390,11 +402,11 @@ namespace Flow.Launcher }; var IconMotion = new DoubleAnimation { - From = 12, - To = 0, - EasingFunction = easing, - Duration = TimeSpan.FromSeconds(0.36), - FillBehavior = FillBehavior.Stop + From = 12, + To = 0, + EasingFunction = easing, + Duration = TimeSpan.FromSeconds(0.36), + FillBehavior = FillBehavior.Stop }; var ClockOpacity = new DoubleAnimation @@ -466,10 +478,10 @@ namespace Flow.Launcher private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e) { _viewModel.Hide(); - - if(_settings.UseAnimation) + + if (_settings.UseAnimation) await Task.Delay(100); - + App.API.OpenSettingDialog(); } @@ -487,7 +499,7 @@ namespace Flow.Launcher // and always after Settings window is closed. if (_settings.UseAnimation) await Task.Delay(100); - + if (_settings.HideWhenDeactive) { _viewModel.Hide(); @@ -525,7 +537,7 @@ namespace Flow.Launcher _viewModel.Show(); } } - + public double HorizonCenter() { var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); @@ -607,9 +619,9 @@ namespace Flow.Launcher && QueryTextBox.Text.Length > 0 && QueryTextBox.CaretIndex == QueryTextBox.Text.Length) { - var queryWithoutActionKeyword = - QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins).Search; - + var queryWithoutActionKeyword = + QueryBuilder.Build(QueryTextBox.Text.Trim(), PluginManager.NonGlobalPlugins)?.Search; + if (FilesFolders.IsLocationPathString(queryWithoutActionKeyword)) { _viewModel.BackspaceCommand.Execute(null); @@ -618,17 +630,50 @@ namespace Flow.Launcher } } break; + case Key.F1: + PreviewToggle(); + e.Handled = true; + break; + default: break; } } + public void PreviewReset() + { + if (_settings.AlwaysPreview == true) + { + ResultArea.SetValue(Grid.ColumnSpanProperty, 1); + Preview.Visibility = Visibility.Visible; + } + else + { + ResultArea.SetValue(Grid.ColumnSpanProperty, 2); + Preview.Visibility = Visibility.Collapsed; + } + } + public void PreviewToggle() + { + + if (Preview.Visibility == Visibility.Collapsed) + { + ResultArea.SetValue(Grid.ColumnSpanProperty, 1); + Preview.Visibility = Visibility.Visible; + } + else + { + ResultArea.SetValue(Grid.ColumnSpanProperty, 2); + Preview.Visibility = Visibility.Collapsed; + } + } + private void MoveQueryTextToEnd() { // QueryTextBox seems to be update with a DispatcherPriority as low as ContextIdle. // To ensure QueryTextBox is up to date with QueryText from the View, we need to Dispatch with such a priority - Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length, System.Windows.Threading.DispatcherPriority.ContextIdle); + Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); } public void InitializeColorScheme() @@ -645,7 +690,7 @@ namespace Flow.Launcher private void QueryTextBox_KeyUp(object sender, KeyEventArgs e) { - if(_viewModel.QueryText != QueryTextBox.Text) + if (_viewModel.QueryText != QueryTextBox.Text) { BindingExpression be = QueryTextBox.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty); be.UpdateSource(); diff --git a/Flow.Launcher/PriorityChangeWindow.xaml.cs b/Flow.Launcher/PriorityChangeWindow.xaml.cs index fe846e78b..e2fe46adc 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml.cs +++ b/Flow.Launcher/PriorityChangeWindow.xaml.cs @@ -1,6 +1,5 @@ using Flow.Launcher.Core.Plugin; using Flow.Launcher.Core.Resource; -using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; using System; @@ -23,14 +22,12 @@ namespace Flow.Launcher public partial class PriorityChangeWindow : Window { private readonly PluginPair plugin; - private Settings settings; private readonly Internationalization translater = InternationalizationManager.Instance; private readonly PluginViewModel pluginViewModel; - public PriorityChangeWindow(string pluginId, Settings settings, PluginViewModel pluginViewModel) + public PriorityChangeWindow(string pluginId, PluginViewModel pluginViewModel) { InitializeComponent(); plugin = PluginManager.GetPluginForId(pluginId); - this.settings = settings; this.pluginViewModel = pluginViewModel; if (plugin == null) { @@ -74,4 +71,4 @@ namespace Flow.Launcher } } } -} \ No newline at end of file +} diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 5fef5499b..927055971 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -141,6 +141,8 @@ namespace Flow.Launcher public void AddActionKeyword(string pluginId, string newActionKeyword) => PluginManager.AddActionKeyword(pluginId, newActionKeyword); + public bool ActionKeywordAssigned(string actionKeyword) => PluginManager.ActionKeywordRegistered(actionKeyword); + public void RemoveActionKeyword(string pluginId, string oldActionKeyword) => PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword); diff --git a/Flow.Launcher/Resources/CustomControlTemplate.xaml b/Flow.Launcher/Resources/CustomControlTemplate.xaml index 07897361c..451dc5344 100644 --- a/Flow.Launcher/Resources/CustomControlTemplate.xaml +++ b/Flow.Launcher/Resources/CustomControlTemplate.xaml @@ -3230,4 +3230,26 @@ + + + diff --git a/Flow.Launcher/ResultListBox.xaml b/Flow.Launcher/ResultListBox.xaml index d44830f52..8f3238303 100644 --- a/Flow.Launcher/ResultListBox.xaml +++ b/Flow.Launcher/ResultListBox.xaml @@ -7,7 +7,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:Flow.Launcher.ViewModel" MaxHeight="{Binding MaxHeight}" - Margin="{DynamicResource ResultMargin}" HorizontalContentAlignment="Stretch" d:DataContext="{d:DesignInstance vm:ResultsViewModel}" d:DesignHeight="100" @@ -58,6 +57,7 @@ diff --git a/Flow.Launcher/ResultListBox.xaml.cs b/Flow.Launcher/ResultListBox.xaml.cs index 6bd1490c3..78720e86a 100644 --- a/Flow.Launcher/ResultListBox.xaml.cs +++ b/Flow.Launcher/ResultListBox.xaml.cs @@ -92,24 +92,36 @@ namespace Flow.Launcher private Point start; private string path; private string query; + // this method is called by the UI thread, which is single threaded, so we can be sloppy with locking + private bool isDragging; private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { - if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result }) - return; + if (Mouse.DirectlyOver is not FrameworkElement + { + DataContext: ResultViewModel + { + Result: + { + CopyText: { } copyText, + OriginQuery.RawQuery: { } rawQuery + } + } + }) return; - path = result.Result.CopyText; - query = result.Result.OriginQuery.RawQuery; + path = copyText; + query = rawQuery; start = e.GetPosition(null); + isDragging = true; } - private void ResultList_MouseMove(object sender, MouseEventArgs e) { - if (e.LeftButton != MouseButtonState.Pressed) + if (e.LeftButton != MouseButtonState.Pressed || !isDragging) { start = default; path = string.Empty; query = string.Empty; + isDragging = false; return; } @@ -123,15 +135,19 @@ namespace Flow.Launcher || Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance) return; + isDragging = false; + var data = new DataObject(DataFormats.FileDrop, new[] { path }); - DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy); - App.API.ChangeQuery(query, true); - - e.Handled = true; + // Reassigning query to a new variable because for some reason + // after DragDrop.DoDragDrop call, 'query' loses its content, i.e. becomes empty string + var rawQuery = query; + var effect = DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy); + if (effect == DragDropEffects.Move) + App.API.ChangeQuery(rawQuery, true); } private void ResultListBox_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 64ed98002..3a15fea8a 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -443,10 +443,9 @@ Margin="2,2,2,0" Panel.ZIndex="1" Background="Transparent" - IsItemsHost="true" + IsItemsHost="true" LastChildFill="False" /> - + @@ -732,6 +731,22 @@ + + + + + + + + +  + + + + @@ -1022,6 +1037,7 @@ DockPanel.Dock="Right" FontSize="14" KeyDown="PluginFilterTxb_OnKeyDown" + Loaded="Plugin_GotFocus" LostFocus="RefreshPluginListEventHandler" Text="" TextAlignment="Left" @@ -1146,7 +1162,7 @@ x:Name="PriorityButton" Margin="0,0,22,0" VerticalAlignment="Center" - Click="OnPluginPriorityClick" + Command="{Binding EditPluginPriorityCommand}" Content="{Binding Priority, UpdateSourceTrigger=PropertyChanged}" Cursor="Hand" ToolTip="{DynamicResource priorityToolTip}"> @@ -1224,7 +1240,7 @@ Height="34" Margin="5,0,0,0" HorizontalAlignment="Right" - Click="OnPluginActionKeywordsClick" + Command="{Binding SetActionKeywordsCommand}" Content="{Binding ActionKeywordsText}" Cursor="Hand" DockPanel.Dock="Right" @@ -1274,7 +1290,7 @@ - - - - - - - - - + FontSize="11" + Foreground="{DynamicResource PluginInfoColor}" + Text="|" /> + + + - + RequestNavigate="OnRequestNavigate" + Style="{DynamicResource HyperLinkBtnStyle}" + TextDecorations="None"> + - - + Style="{DynamicResource LinkBtnStyle}" + Text="" + ToolTip="{DynamicResource plugin_uninstall}" /> + Margin="10,0,5,0" + Style="{DynamicResource LinkBtnStyle}" + Text="" + ToolTip="{DynamicResource pluginDirectory}"> + + + + - - - - @@ -1448,6 +1428,7 @@ DockPanel.Dock="Right" FontSize="14" KeyDown="PluginStoreFilterTxb_OnKeyDown" + Loaded="PluginStore_GotFocus" LostFocus="RefreshPluginStoreEventHandler" Text="" TextAlignment="Left" @@ -1798,7 +1779,12 @@ HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal"> - + @@ -1816,6 +1802,7 @@ @@ -1853,7 +1840,10 @@ - + diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index 73926c091..924ee4ae2 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -5,7 +5,6 @@ using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; -using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.ViewModel; using ModernWpf; using ModernWpf.Controls; @@ -18,7 +17,6 @@ using System.Windows.Data; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Interop; -using System.Windows.Media; using System.Windows.Navigation; using Button = System.Windows.Controls.Button; using Control = System.Windows.Controls.Control; @@ -181,44 +179,11 @@ namespace Flow.Launcher { if (sender is Control { DataContext: PluginViewModel pluginViewModel }) { - PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(pluginViewModel.PluginPair.Metadata.ID, settings, pluginViewModel); + PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(pluginViewModel.PluginPair.Metadata.ID, pluginViewModel); priorityChangeWindow.ShowDialog(); } } - private void OnPluginActionKeywordsClick(object sender, RoutedEventArgs e) - { - var id = viewModel.SelectedPlugin.PluginPair.Metadata.ID; - ActionKeywords changeKeywordsWindow = new ActionKeywords(id, settings, viewModel.SelectedPlugin); - changeKeywordsWindow.ShowDialog(); - } - - private void OnPluginNameClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - { - var website = viewModel.SelectedPlugin.PluginPair.Metadata.Website; - if (!string.IsNullOrEmpty(website)) - { - var uri = new Uri(website); - if (Uri.CheckSchemeName(uri.Scheme)) - { - website.OpenInBrowserTab(); - } - } - } - } - - private void OnPluginDirecotyClick(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - { - var directory = viewModel.SelectedPlugin.PluginPair.Metadata.PluginDirectory; - if (!string.IsNullOrEmpty(directory)) - PluginManager.API.OpenDirectory(directory); - } - } - #endregion #region Proxy @@ -289,22 +254,6 @@ namespace Flow.Launcher } } - private static T FindParent(DependencyObject child) where T : DependencyObject - { - //get parent item - DependencyObject parentObject = VisualTreeHelper.GetParent(child); - - //we've reached the end of the tree - if (parentObject == null) return null; - - //check if the parent matches the type we're looking for - T parent = parentObject as T; - if (parent != null) - return parent; - else - return FindParent(parentObject); - } - private void OnExternalPluginInstallClick(object sender, RoutedEventArgs e) { if (sender is not Button { DataContext: PluginStoreItemViewModel plugin } button) @@ -327,8 +276,6 @@ namespace Flow.Launcher 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) @@ -554,5 +501,15 @@ namespace Flow.Launcher }; } + + private void PluginStore_GotFocus(object sender, RoutedEventArgs e) + { + Keyboard.Focus(pluginStoreFilterTxb); + } + + private void Plugin_GotFocus(object sender, RoutedEventArgs e) + { + Keyboard.Focus(pluginFilterTxb); + } } } diff --git a/Flow.Launcher/Themes/Base.xaml b/Flow.Launcher/Themes/Base.xaml index 94740a730..0b4460d46 100644 --- a/Flow.Launcher/Themes/Base.xaml +++ b/Flow.Launcher/Themes/Base.xaml @@ -248,8 +248,9 @@ - + + @@ -273,6 +274,11 @@ + + + + + @@ -356,7 +362,22 @@ - @@ -384,7 +405,64 @@ + + + + + + + + + + + + diff --git a/Flow.Launcher/Themes/BlurBlack Darker.xaml b/Flow.Launcher/Themes/BlurBlack Darker.xaml index 88c48afee..67a47f4cf 100644 --- a/Flow.Launcher/Themes/BlurBlack Darker.xaml +++ b/Flow.Launcher/Themes/BlurBlack Darker.xaml @@ -37,7 +37,6 @@ x:Key="WindowBorderStyle" BasedOn="{StaticResource BaseWindowBorderStyle}" TargetType="{x:Type Border}"> - @@ -48,6 +47,15 @@ + + + + + + diff --git a/Flow.Launcher/Themes/BlurBlack.xaml b/Flow.Launcher/Themes/BlurBlack.xaml index f74578a74..0f1264292 100644 --- a/Flow.Launcher/Themes/BlurBlack.xaml +++ b/Flow.Launcher/Themes/BlurBlack.xaml @@ -45,6 +45,15 @@ + + + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/BlurWhite.xaml b/Flow.Launcher/Themes/BlurWhite.xaml index 2d42d49bc..4406724b8 100644 --- a/Flow.Launcher/Themes/BlurWhite.xaml +++ b/Flow.Launcher/Themes/BlurWhite.xaml @@ -155,4 +155,28 @@ + + + + diff --git a/Flow.Launcher/Themes/Bullet Light.xaml b/Flow.Launcher/Themes/Bullet Light.xaml index 0dc3b33d2..1f776a2ee 100644 --- a/Flow.Launcher/Themes/Bullet Light.xaml +++ b/Flow.Launcher/Themes/Bullet Light.xaml @@ -87,7 +87,7 @@ x:Key="SeparatorStyle" BasedOn="{StaticResource BaseSeparatorStyle}" TargetType="{x:Type Rectangle}"> - + @@ -156,6 +156,7 @@ + @@ -181,4 +182,28 @@ TargetType="{x:Type TextBlock}"> + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Circle Light.xaml b/Flow.Launcher/Themes/Circle Light.xaml index 7e14a29a6..e52e3a957 100644 --- a/Flow.Launcher/Themes/Circle Light.xaml +++ b/Flow.Launcher/Themes/Circle Light.xaml @@ -69,7 +69,7 @@ x:Key="SeparatorStyle" BasedOn="{StaticResource BaseSeparatorStyle}" TargetType="{x:Type Rectangle}"> - + @@ -163,4 +163,28 @@ TargetType="{x:Type TextBlock}"> - \ No newline at end of file + + + + + diff --git a/Flow.Launcher/Themes/Circle System.xaml b/Flow.Launcher/Themes/Circle System.xaml index b00f03e76..2b2ce7ca3 100644 --- a/Flow.Launcher/Themes/Circle System.xaml +++ b/Flow.Launcher/Themes/Circle System.xaml @@ -70,7 +70,7 @@ x:Key="SeparatorStyle" BasedOn="{StaticResource BaseSeparatorStyle}" TargetType="{x:Type Rectangle}"> - + @@ -164,4 +164,28 @@ TargetType="{x:Type TextBlock}"> + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Cyan Dark.xaml b/Flow.Launcher/Themes/Cyan Dark.xaml index c79044f00..60bc09002 100644 --- a/Flow.Launcher/Themes/Cyan Dark.xaml +++ b/Flow.Launcher/Themes/Cyan Dark.xaml @@ -164,7 +164,7 @@ 0 0 - 0 0 0 0 + 0 0 0 4 + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Darker Glass.xaml b/Flow.Launcher/Themes/Darker Glass.xaml index a33f98b09..89b6dfa01 100644 --- a/Flow.Launcher/Themes/Darker Glass.xaml +++ b/Flow.Launcher/Themes/Darker Glass.xaml @@ -5,7 +5,7 @@ - + 0 0 0 8 + + + + + diff --git a/Flow.Launcher/Themes/Darker.xaml b/Flow.Launcher/Themes/Darker.xaml new file mode 100644 index 000000000..d1abbe978 --- /dev/null +++ b/Flow.Launcher/Themes/Darker.xaml @@ -0,0 +1,100 @@ + + + + + 0 0 0 8 + + + + + + + + #4d4d4d + + + + + + diff --git a/Flow.Launcher/Themes/Discord Dark.xaml b/Flow.Launcher/Themes/Discord Dark.xaml index 74c1719c4..5315c7644 100644 --- a/Flow.Launcher/Themes/Discord Dark.xaml +++ b/Flow.Launcher/Themes/Discord Dark.xaml @@ -5,6 +5,7 @@ + 0 0 0 6 + + + + diff --git a/Flow.Launcher/Themes/Dracula.xaml b/Flow.Launcher/Themes/Dracula.xaml index c01b67c74..ce3350728 100644 --- a/Flow.Launcher/Themes/Dracula.xaml +++ b/Flow.Launcher/Themes/Dracula.xaml @@ -5,6 +5,7 @@ + 0 0 0 6 + + + + diff --git a/Flow.Launcher/Themes/Gray.xaml b/Flow.Launcher/Themes/Gray.xaml index eb8b48f43..d8d344e21 100644 --- a/Flow.Launcher/Themes/Gray.xaml +++ b/Flow.Launcher/Themes/Gray.xaml @@ -70,7 +70,7 @@ TargetType="{x:Type Rectangle}"> - + - \ No newline at end of file + + + + + + diff --git a/Flow.Launcher/Themes/League.xaml b/Flow.Launcher/Themes/League.xaml index 9f4a9a628..7fbe56187 100644 --- a/Flow.Launcher/Themes/League.xaml +++ b/Flow.Launcher/Themes/League.xaml @@ -137,4 +137,29 @@ TargetType="{x:Type TextBlock}"> + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Midnight.xaml b/Flow.Launcher/Themes/Midnight.xaml index b52fe87e1..91ff620d5 100644 --- a/Flow.Launcher/Themes/Midnight.xaml +++ b/Flow.Launcher/Themes/Midnight.xaml @@ -163,4 +163,29 @@ TargetType="{x:Type TextBlock}"> - \ No newline at end of file + + + + + diff --git a/Flow.Launcher/Themes/Nord Darker.xaml b/Flow.Launcher/Themes/Nord Darker.xaml index 840e44b3c..d9ddb3076 100644 --- a/Flow.Launcher/Themes/Nord Darker.xaml +++ b/Flow.Launcher/Themes/Nord Darker.xaml @@ -3,6 +3,7 @@ + 0 0 0 8 + + + + + diff --git a/Flow.Launcher/Themes/Pink.xaml b/Flow.Launcher/Themes/Pink.xaml index 96dae2545..dc97e4320 100644 --- a/Flow.Launcher/Themes/Pink.xaml +++ b/Flow.Launcher/Themes/Pink.xaml @@ -2,6 +2,7 @@ + 0 0 0 4 + + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Sublime.xaml b/Flow.Launcher/Themes/Sublime.xaml index 417bd723e..6df69ad3e 100644 --- a/Flow.Launcher/Themes/Sublime.xaml +++ b/Flow.Launcher/Themes/Sublime.xaml @@ -5,6 +5,7 @@ + 0 0 0 8 + + + + diff --git a/Flow.Launcher/Themes/Ubuntu.xaml b/Flow.Launcher/Themes/Ubuntu.xaml index ea10c0e82..33f232699 100644 --- a/Flow.Launcher/Themes/Ubuntu.xaml +++ b/Flow.Launcher/Themes/Ubuntu.xaml @@ -169,7 +169,7 @@ 0 0 0 0 0 - 0 0 0 0 + 0 0 0 8 + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Win10Light.xaml b/Flow.Launcher/Themes/Win10Light.xaml index 12ba01f71..eaa66e7fd 100644 --- a/Flow.Launcher/Themes/Win10Light.xaml +++ b/Flow.Launcher/Themes/Win10Light.xaml @@ -5,12 +5,19 @@ + 0 0 0 4 + + + + + diff --git a/Flow.Launcher/Themes/Win11Dark.xaml b/Flow.Launcher/Themes/Win11Dark.xaml index 4660eae8f..5abb96cce 100644 --- a/Flow.Launcher/Themes/Win11Dark.xaml +++ b/Flow.Launcher/Themes/Win11Dark.xaml @@ -5,6 +5,7 @@ + 0 0 0 8 + + + + diff --git a/Flow.Launcher/Themes/Win11Light.xaml b/Flow.Launcher/Themes/Win11Light.xaml index 4c0769d4d..e2c37236c 100644 --- a/Flow.Launcher/Themes/Win11Light.xaml +++ b/Flow.Launcher/Themes/Win11Light.xaml @@ -5,6 +5,7 @@ + 0 0 0 8 + + + + diff --git a/Flow.Launcher/Themes/Win11System.xaml b/Flow.Launcher/Themes/Win11System.xaml index 42f0579a7..df09249be 100644 --- a/Flow.Launcher/Themes/Win11System.xaml +++ b/Flow.Launcher/Themes/Win11System.xaml @@ -6,6 +6,7 @@ + 0 0 0 8 + + + + diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 4db8e3df3..e05e47041 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -24,6 +24,7 @@ using System.IO; using System.Collections.Specialized; using CommunityToolkit.Mvvm.Input; using System.Globalization; +using System.Windows.Threading; namespace Flow.Launcher.ViewModel { @@ -65,7 +66,8 @@ namespace Flow.Launcher.ViewModel Settings = settings; Settings.PropertyChanged += (_, args) => { - switch (args.PropertyName) { + switch (args.PropertyName) + { case nameof(Settings.WindowSize): OnPropertyChanged(nameof(MainWindowWidth)); break; @@ -365,6 +367,7 @@ namespace Flow.Launcher.ViewModel set { _queryText = value; + OnPropertyChanged(); Query(); } } @@ -426,19 +429,24 @@ namespace Flow.Launcher.ViewModel /// Force query even when Query Text doesn't change public void ChangeQueryText(string queryText, bool reQuery = false) { - if (QueryText != queryText) + Application.Current.Dispatcher.Invoke(() => { - // re-query is done in QueryText's setter method - QueryText = queryText; - // set to false so the subsequent set true triggers - // PropertyChanged and MoveQueryTextToEnd is called - QueryTextCursorMovedToEnd = false; - } - else if (reQuery) - { - Query(); - } - QueryTextCursorMovedToEnd = true; + if (QueryText != queryText) + { + + // re-query is done in QueryText's setter method + QueryText = queryText; + // set to false so the subsequent set true triggers + // PropertyChanged and MoveQueryTextToEnd is called + QueryTextCursorMovedToEnd = false; + + } + else if (reQuery) + { + Query(); + } + QueryTextCursorMovedToEnd = true; + }); } public bool LastQuerySelected { get; set; } @@ -752,11 +760,14 @@ namespace Flow.Launcher.ViewModel queryBuilder.Replace('@' + shortcut.Key, shortcut.Expand()); } - foreach (var shortcut in builtInShortcuts) + Application.Current.Dispatcher.Invoke(() => { - queryBuilder.Replace(shortcut.Key, shortcut.Expand()); - queryBuilderTmp.Replace(shortcut.Key, shortcut.Expand()); - } + foreach (var shortcut in builtInShortcuts) + { + queryBuilder.Replace(shortcut.Key, shortcut.Expand()); + queryBuilderTmp.Replace(shortcut.Key, shortcut.Expand()); + } + }); // show expanded builtin shortcuts // use private field to avoid infinite recursion @@ -803,7 +814,7 @@ namespace Flow.Launcher.ViewModel Action = _ => { _topMostRecord.AddOrUpdate(result); - App.API.ShowMsg("Success"); + App.API.ShowMsg(InternationalizationManager.Instance.GetTranslation("success")); return false; } }; @@ -879,11 +890,14 @@ namespace Flow.Launcher.ViewModel public void Show() { - MainWindowVisibility = Visibility.Visible; + Application.Current.Dispatcher.Invoke(() => + { + MainWindowVisibility = Visibility.Visible; - MainWindowVisibilityStatus = true; + MainWindowOpacity = 1; - MainWindowOpacity = 1; + MainWindowVisibilityStatus = true; + }); } public async void Hide() diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index 725857e2f..65ba657ba 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -1,14 +1,16 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using Flow.Launcher.Plugin; using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Core.Plugin; using System.Windows.Controls; +using CommunityToolkit.Mvvm.Input; +using Flow.Launcher.Core.Resource; namespace Flow.Launcher.ViewModel { - public class PluginViewModel : BaseModel + public partial class PluginViewModel : BaseModel { private readonly PluginPair _pluginPair; public PluginPair PluginPair @@ -35,7 +37,7 @@ namespace Flow.Launcher.ViewModel { get { - if (_image == ImageLoader.DefaultImage) + if (_image == ImageLoader.MissingImage) LoadIconAsync(); return _image; @@ -53,6 +55,7 @@ namespace Flow.Launcher.ViewModel set { _isExpanded = value; + OnPropertyChanged(); OnPropertyChanged(nameof(SettingControl)); } @@ -60,18 +63,20 @@ namespace Flow.Launcher.ViewModel private Control _settingControl; private bool _isExpanded; - public Control SettingControl + public Control SettingControl => IsExpanded ? _settingControl ??= PluginPair.Plugin is not ISettingProvider settingProvider - ? new Control() - : settingProvider.CreateSettingPanel() + ? new Control() + : settingProvider.CreateSettingPanel() : null; - private ImageSource _image = ImageLoader.DefaultImage; + private ImageSource _image = ImageLoader.MissingImage; public Visibility ActionKeywordsVisibility => PluginPair.Metadata.ActionKeywords.Count == 1 ? Visibility.Visible : Visibility.Collapsed; public string InitilizaTime => PluginPair.Metadata.InitTime + "ms"; public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms"; + public string Version => InternationalizationManager.Instance.GetTranslation("plugin_query_version") + " " + PluginPair.Metadata.Version; + public string InitAndQueryTime => InternationalizationManager.Instance.GetTranslation("plugin_init_time") + " " + PluginPair.Metadata.InitTime + "ms, " + InternationalizationManager.Instance.GetTranslation("plugin_query_time") + " " + PluginPair.Metadata.AvgQueryTime + "ms"; public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords); public int Priority => PluginPair.Metadata.Priority; @@ -87,7 +92,29 @@ namespace Flow.Launcher.ViewModel OnPropertyChanged(nameof(Priority)); } + [RelayCommand] + private void EditPluginPriority() + { + PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(PluginPair.Metadata.ID, this); + priorityChangeWindow.ShowDialog(); + } + + [RelayCommand] + private void OpenPluginDirectory() + { + var directory = PluginPair.Metadata.PluginDirectory; + if (!string.IsNullOrEmpty(directory)) + PluginManager.API.OpenDirectory(directory); + } + public static bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword); + + [RelayCommand] + private void SetActionKeywords() + { + ActionKeywords changeKeywordsWindow = new ActionKeywords(this); + changeKeywordsWindow.ShowDialog(); + } } } diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs index 2d61f6cab..0fadeccdf 100644 --- a/Flow.Launcher/ViewModel/ResultViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultViewModel.cs @@ -19,47 +19,49 @@ namespace Flow.Launcher.ViewModel public ResultViewModel(Result result, Settings settings) { - if (result != null) + Settings = settings; + + if (result == null) { - Result = result; + return; + } + Result = result; - if (Result.Glyph is { FontFamily: not null } glyph) + if (Result.Glyph is { FontFamily: not null } glyph) + { + // Checks if it's a system installed font, which does not require path to be provided. + if (glyph.FontFamily.EndsWith(".ttf") || glyph.FontFamily.EndsWith(".otf")) { - // Checks if it's a system installed font, which does not require path to be provided. - if (glyph.FontFamily.EndsWith(".ttf") || glyph.FontFamily.EndsWith(".otf")) + string fontFamilyPath = glyph.FontFamily; + + if (!Path.IsPathRooted(fontFamilyPath)) { - string fontFamilyPath = glyph.FontFamily; + fontFamilyPath = Path.Combine(Result.PluginDirectory, fontFamilyPath); + } - if (!Path.IsPathRooted(fontFamilyPath)) + if (fonts.ContainsKey(fontFamilyPath)) + { + Glyph = glyph with { - fontFamilyPath = Path.Combine(Result.PluginDirectory, fontFamilyPath); - } - - if (fonts.ContainsKey(fontFamilyPath)) - { - Glyph = glyph with - { - FontFamily = fonts[fontFamilyPath] - }; - } - else - { - fontCollection.AddFontFile(fontFamilyPath); - fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}"; - Glyph = glyph with - { - FontFamily = fonts[fontFamilyPath] - }; - } + FontFamily = fonts[fontFamilyPath] + }; } else { - Glyph = glyph; + fontCollection.AddFontFile(fontFamilyPath); + fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}"; + Glyph = glyph with + { + FontFamily = fonts[fontFamilyPath] + }; } } + else + { + Glyph = glyph; + } } - Settings = settings; } private Settings Settings { get; } @@ -67,6 +69,10 @@ namespace Flow.Launcher.ViewModel public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Collapsed; + public Visibility ShowDefaultPreview => Result.PreviewPanel == null ? Visibility.Visible : Visibility.Collapsed; + + public Visibility ShowCustomizedPreview => Result.PreviewPanel == null ? Visibility.Collapsed : Visibility.Visible; + public Visibility ShowIcon { get @@ -106,7 +112,7 @@ namespace Flow.Launcher.ViewModel if (!Settings.UseGlyphIcons && !ImgIconAvailable && GlyphAvailable) return Visibility.Visible; - return Settings.UseGlyphIcons && GlyphAvailable ? Visibility.Visible : Visibility.Hidden; + return Settings.UseGlyphIcons && GlyphAvailable ? Visibility.Visible : Visibility.Collapsed; } } @@ -125,8 +131,10 @@ namespace Flow.Launcher.ViewModel : Result.SubTitleToolTip; private volatile bool ImageLoaded; + private volatile bool PreviewImageLoaded; - private ImageSource image = ImageLoader.DefaultImage; + private ImageSource image = ImageLoader.LoadingImage; + private ImageSource previewImage = ImageLoader.LoadingImage; public ImageSource Image { @@ -143,37 +151,76 @@ namespace Flow.Launcher.ViewModel private set => image = value; } + public ImageSource PreviewImage + { + get + { + if (!PreviewImageLoaded) + { + PreviewImageLoaded = true; + _ = LoadPreviewImageAsync(); + } + + return previewImage; + } + private set => previewImage = value; + } + + /// + /// Determines if to use the full width of the preview panel + /// + public bool UseBigThumbnail => Result.Preview.IsMedia; + public GlyphInfo Glyph { get; set; } - private async Task LoadImageAsync() + private async Task LoadImageInternalAsync(string imagePath, Result.IconDelegate icon, bool loadFullImage) { - var imagePath = Result.IcoPath; - if (string.IsNullOrEmpty(imagePath) && Result.Icon != null) + if (string.IsNullOrEmpty(imagePath) && icon != null) { try { - image = Result.Icon(); - return; + var image = await Task.Run(() => icon()).ConfigureAwait(false); + return image; } catch (Exception e) { Log.Exception( - $"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", + $"|ResultViewModel.LoadImageInternalAsync|IcoPath is empty and exception when calling IconDelegate for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e); } } - var loadFullImage = (Path.GetExtension(imagePath) ?? "").Equals(".url", StringComparison.OrdinalIgnoreCase); + return await ImageLoader.LoadAsync(imagePath, loadFullImage).ConfigureAwait(false); + } - if (ImageLoader.CacheContainImage(imagePath)) + private async Task LoadImageAsync() + { + var imagePath = Result.IcoPath; + var iconDelegate = Result.Icon; + if (ImageLoader.CacheContainImage(imagePath, false)) { - // will get here either when icoPath has value\icon delegate is null\when had exception in delegate - image = await ImageLoader.LoadAsync(imagePath, loadFullImage); - return; + image = await LoadImageInternalAsync(imagePath, iconDelegate, false).ConfigureAwait(false); } + else + { + // We need to modify the property not field here to trigger the OnPropertyChanged event + Image = await LoadImageInternalAsync(imagePath, iconDelegate, false).ConfigureAwait(false); + } + } - // We need to modify the property not field here to trigger the OnPropertyChanged event - Image = await ImageLoader.LoadAsync(imagePath, loadFullImage).ConfigureAwait(false); + private async Task LoadPreviewImageAsync() + { + var imagePath = string.IsNullOrEmpty(Result.Preview.PreviewImagePath) ? Result.IcoPath : Result.Preview.PreviewImagePath; + var iconDelegate = Result.Icon; + if (ImageLoader.CacheContainImage(imagePath, true)) + { + previewImage = await LoadImageInternalAsync(imagePath, iconDelegate, true).ConfigureAwait(false); + } + else + { + // We need to modify the property not field here to trigger the OnPropertyChanged event + PreviewImage = await LoadImageInternalAsync(imagePath, iconDelegate, true).ConfigureAwait(false); + } } public Result Result { get; } diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 5514951b1..4e4de697b 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -379,6 +379,8 @@ namespace Flow.Launcher.ViewModel await PluginsManifest.UpdateManifestAsync(); OnPropertyChanged(nameof(ExternalPlugins)); } + + internal void DisplayPluginQuery(string queryToDisplay, PluginPair plugin, int actionKeywordPosition = 0) { @@ -632,26 +634,26 @@ namespace Flow.Launcher.ViewModel { new Result { - Title = "Explorer", - SubTitle = "Search for files, folders and file contents", + Title = InternationalizationManager.Instance.GetTranslation("SampleTitleExplorer"), + SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleExplorer"), IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.Explorer\Images\explorer.png") }, new Result { - Title = "WebSearch", - SubTitle = "Search the web with different search engine support", + Title = InternationalizationManager.Instance.GetTranslation("SampleTitleWebSearch"), + SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleWebSearch"), IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png") }, new Result { - Title = "Program", - SubTitle = "Launch programs as admin or a different user", + Title = InternationalizationManager.Instance.GetTranslation("SampleTitleProgram"), + SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleProgram"), IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.Program\Images\program.png") }, new Result { - Title = "ProcessKiller", - SubTitle = "Terminate unwanted processes", + Title = InternationalizationManager.Instance.GetTranslation("SampleTitleProcessKiller"), + SubTitle = InternationalizationManager.Instance.GetTranslation("SampleSubTitleProcessKiller"), IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png") } }; diff --git a/Flow.Launcher/WelcomeWindow.xaml b/Flow.Launcher/WelcomeWindow.xaml index d797a623b..c7820d436 100644 --- a/Flow.Launcher/WelcomeWindow.xaml +++ b/Flow.Launcher/WelcomeWindow.xaml @@ -7,13 +7,13 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ui="http://schemas.modernwpf.com/2019" Name="FlowWelcomeWindow" - Title="Welcome to Flow Launcher" - Activated="OnActivated" + Title="{DynamicResource Welcome_Page1_Title}" Width="550" Height="650" - MouseDown="window_MouseDown" + Activated="OnActivated" Background="{DynamicResource Color00B}" Foreground="{DynamicResource PopupTextColor}" + MouseDown="window_MouseDown" WindowStartupLocation="CenterScreen" mc:Ignorable="d"> @@ -48,7 +48,7 @@ VerticalAlignment="Center" FontSize="12" Foreground="{DynamicResource Color05B}" - Text="Welcome to Flow Launcher" /> + Text="{DynamicResource Welcome_Page1_Title}" />