diff --git a/Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs b/Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs index f98815c1a9f..bb1279b2c61 100644 --- a/Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs +++ b/Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs @@ -1,4 +1,6 @@ -namespace Flow.Launcher.Core.ExternalPlugins +using System; + +namespace Flow.Launcher.Core.ExternalPlugins { public record UserPlugin { @@ -12,5 +14,8 @@ public record UserPlugin public string UrlDownload { get; set; } public string UrlSourceCode { get; set; } public string IcoPath { get; set; } + public DateTime LatestReleaseDate { get; set; } + public DateTime DateAdded { get; set; } + } } diff --git a/Flow.Launcher.Infrastructure/Logger/Log.cs b/Flow.Launcher.Infrastructure/Logger/Log.cs index 75f208c9ed1..b8f1408e7cb 100644 --- a/Flow.Launcher.Infrastructure/Logger/Log.cs +++ b/Flow.Launcher.Infrastructure/Logger/Log.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using NLog; diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index 3ffa9f7b11c..46165a84921 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -202,7 +202,11 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption if (allQuerySubstringsMatched) { var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex); - var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, + + // firstMatchIndex - nearestSpaceIndex - 1 is to set the firstIndex as the index of the first matched char + // preceded by a space e.g. 'world' matching 'hello world' firstIndex would be 0 not 6 + // giving more weight than 'we or donald' by allowing the distance calculation to treat the starting position at after the space. + var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, spaceIndices, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString); var resultList = indexList.Select(x => translationMapping?.MapToOriginalIndex(x) ?? x).Distinct().ToList(); @@ -296,7 +300,7 @@ private static bool AllQuerySubstringsMatched(int currentQuerySubstringIndex, in return currentQuerySubstringIndex >= querySubstringsLength; } - private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen, + private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, List spaceIndices, int matchLen, bool allSubstringsContainedInCompareString) { // A match found near the beginning of a string is scored more than a match found near the end @@ -304,6 +308,14 @@ private static int CalculateSearchScore(string query, string stringToCompare, in // while the score is lower if they are more spread out var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1)); + // Give more weight to a match that is closer to the start of the string. + // if the first matched char is immediately before space and all strings are contained in the compare string e.g. 'world' matching 'hello world' + // and 'world hello', because both have 'world' immediately preceded by space, their firstIndex will be 0 when distance is calculated, + // to prevent them scoring the same, we adjust the score by deducting the number of spaces it has from the start of the string, so 'world hello' + // will score slightly higher than 'hello world' because 'hello world' has one additional space. + if (firstIndex == 0 && allSubstringsContainedInCompareString) + score -= spaceIndices.Count; + // A match with less characters assigning more weights if (stringToCompare.Length - query.Length < 5) { diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomShortcutModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomShortcutModel.cs new file mode 100644 index 00000000000..71020369a60 --- /dev/null +++ b/Flow.Launcher.Infrastructure/UserSettings/CustomShortcutModel.cs @@ -0,0 +1,65 @@ +using System; +using System.Text.Json.Serialization; + +namespace Flow.Launcher.Infrastructure.UserSettings +{ + public abstract class ShortcutBaseModel + { + public string Key { get; set; } + + [JsonIgnore] + public Func Expand { get; set; } = () => { return ""; }; + + public override bool Equals(object obj) + { + return obj is ShortcutBaseModel other && + Key == other.Key; + } + + public override int GetHashCode() + { + return Key.GetHashCode(); + } + } + + public class CustomShortcutModel : ShortcutBaseModel + { + public string Value { get; set; } + + [JsonConstructorAttribute] + public CustomShortcutModel(string key, string value) + { + Key = key; + Value = value; + Expand = () => { return Value; }; + } + + public void Deconstruct(out string key, out string value) + { + key = Key; + value = Value; + } + + public static implicit operator (string Key, string Value)(CustomShortcutModel shortcut) + { + return (shortcut.Key, shortcut.Value); + } + + public static implicit operator CustomShortcutModel((string Key, string Value) shortcut) + { + return new CustomShortcutModel(shortcut.Key, shortcut.Value); + } + } + + public class BuiltinShortcutModel : ShortcutBaseModel + { + public string Description { get; set; } + + public BuiltinShortcutModel(string key, string description, Func expand) + { + Key = key; + Description = description; + Expand = expand ?? (() => { return ""; }); + } + } +} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 211bc179876..7ead7459f55 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Drawing; using System.Text.Json.Serialization; +using System.Windows; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher; @@ -41,6 +42,10 @@ public string Language public bool UseGlyphIcons { get; set; } = true; public bool UseAnimation { get; set; } = true; public bool UseSound { get; set; } = true; + public bool UseClock { get; set; } = true; + public bool UseDate { get; set; } = false; + public string TimeFormat { get; set; } = "hh:mm tt"; + public string DateFormat { get; set; } = "MM'/'dd ddd"; public bool FirstLaunch { get; set; } = true; public double SettingWindowWidth { get; set; } = 1000; @@ -125,8 +130,7 @@ public CustomBrowserViewModel CustomBrowser PrivateArg = "-private", EnablePrivate = false, Editable = false - } - , + }, new() { Name = "MS Edge", @@ -182,6 +186,13 @@ public string QuerySearchPrecisionString public ObservableCollection CustomPluginHotkeys { get; set; } = new ObservableCollection(); + public ObservableCollection CustomShortcuts { get; set; } = new ObservableCollection(); + + [JsonIgnore] + public ObservableCollection BuiltinShortcuts { get; set; } = new ObservableCollection() { + new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText) + }; + public bool DontPromptUpdateMsg { get; set; } public bool EnableUpdateLog { get; set; } @@ -199,7 +210,7 @@ public bool HideNotifyIcon } public bool LeaveCmdOpen { get; set; } public bool HideWhenDeactive { get; set; } = true; - public bool RememberLastLaunchLocation { get; set; } + public SearchWindowPositions SearchWindowPosition { get; set; } = SearchWindowPositions.MouseScreenCenter; public bool IgnoreHotkeysOnFullscreen { get; set; } public HttpProxy Proxy { get; set; } = new HttpProxy(); @@ -225,4 +236,12 @@ public enum ColorSchemes Light, Dark } + public enum SearchWindowPositions + { + RememberLastLaunchLocation, + MouseScreenCenter, + MouseScreenCenterTop, + MouseScreenLeftTop, + MouseScreenRightTop + } } diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 7633e34a70c..a1d3b83abf2 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -37,7 +37,11 @@ public class Result /// user's clipboard when Ctrl + C is pressed on a result. If the text is a file/directory path /// flow will copy the actual file/folder instead of just the path text. /// - public string CopyText { get; set; } = string.Empty; + public string CopyText + { + get => string.IsNullOrEmpty(_copyText) ? SubTitle : _copyText; + set => _copyText = value; + } /// /// This holds the text which can be provided by plugin to help Flow autocomplete text @@ -81,6 +85,7 @@ public string IcoPath /// Delegate to Get Image Source /// public IconDelegate Icon; + private string _copyText = string.Empty; /// /// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons) diff --git a/Flow.Launcher.Test/FuzzyMatcherTest.cs b/Flow.Launcher.Test/FuzzyMatcherTest.cs index bbddcbd2ad4..46c848c7a82 100644 --- a/Flow.Launcher.Test/FuzzyMatcherTest.cs +++ b/Flow.Launcher.Test/FuzzyMatcherTest.cs @@ -129,14 +129,20 @@ public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreat } } + + /// + /// These are standard match scenarios + /// The intention of this test is provide a bench mark for how much the score has increased from a change. + /// Usually the increase in scoring should not be drastic, increase of less than 10 is acceptable. + /// [TestCase(Chrome, Chrome, 157)] - [TestCase(Chrome, LastIsChrome, 147)] + [TestCase(Chrome, LastIsChrome, 145)] [TestCase("chro", HelpCureHopeRaiseOnMindEntityChrome, 50)] [TestCase("chr", HelpCureHopeRaiseOnMindEntityChrome, 30)] [TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)] [TestCase(Chrome, CandyCrushSagaFromKing, 0)] - [TestCase("sql", MicrosoftSqlServerManagementStudio, 110)] - [TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)] //double spacing intended + [TestCase("sql", MicrosoftSqlServerManagementStudio, 109)] + [TestCase("sql manag", MicrosoftSqlServerManagementStudio, 120)] //double spacing intended public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring( string queryString, string compareString, int expectedScore) { @@ -275,7 +281,40 @@ public void WhenGivenAQuery_Scoring_ShouldGiveMoreWeightToStartOfNewWord( $"Query: \"{queryString}\"{Environment.NewLine} " + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" + $"Should be greater than{Environment.NewLine}" + - $"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}"); + $"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}"); + } + + [TestCase("red", "red colour", "metro red")] + [TestCase("red", "this red colour", "this colour red")] + [TestCase("red", "this red colour", "this colour is very red")] + [TestCase("red", "this red colour", "this colour is surprisingly super awesome red and cool")] + [TestCase("red", "this colour is surprisingly super red very and cool", "this colour is surprisingly super very red and cool")] + public void WhenGivenTwoStrings_Scoring_ShouldGiveMoreWeightToTheStringCloserToIndexZero( + string queryString, string compareString1, string compareString2) + { + // When + var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular }; + + // Given + var compareString1Result = matcher.FuzzyMatch(queryString, compareString1); + var compareString2Result = matcher.FuzzyMatch(queryString, compareString2); + + Debug.WriteLine(""); + Debug.WriteLine("###############################################"); + Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}"); + Debug.WriteLine( + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}"); + Debug.WriteLine( + $"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}"); + Debug.WriteLine("###############################################"); + Debug.WriteLine(""); + + // Should + Assert.True(compareString1Result.Score > compareString2Result.Score, + $"Query: \"{queryString}\"{Environment.NewLine} " + + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" + + $"Should be greater than{Environment.NewLine}" + + $"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}"); } [TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")] diff --git a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs new file mode 100644 index 00000000000..ad474d693b1 --- /dev/null +++ b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; + +namespace Flow.Launcher.Converters +{ + public class BoolToVisibilityConverter : IValueConverter + { + public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) + { + if (parameter != null) + { + if (value is true) + { + return Visibility.Collapsed; + } + + else + { + return Visibility.Visible; + } + } + else { + if (value is true) + { + return Visibility.Visible; + } + + else { + return Visibility.Collapsed; + } + } + } + + public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); + } +} diff --git a/Flow.Launcher/Converters/TextConverter.cs b/Flow.Launcher/Converters/TextConverter.cs new file mode 100644 index 00000000000..90d445776df --- /dev/null +++ b/Flow.Launcher/Converters/TextConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Flow.Launcher.Core.Resource; +using Flow.Launcher.ViewModel; + +namespace Flow.Launcher.Converters +{ + public class TextConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var ID = value.ToString(); + switch(ID) + { + case PluginStoreItemViewModel.NewRelease: + return InternationalizationManager.Instance.GetTranslation("pluginStore_NewRelease"); + case PluginStoreItemViewModel.RecentlyUpdated: + return InternationalizationManager.Instance.GetTranslation("pluginStore_RecentlyUpdated"); + case PluginStoreItemViewModel.None: + return InternationalizationManager.Instance.GetTranslation("pluginStore_None"); + case PluginStoreItemViewModel.Installed: + return InternationalizationManager.Instance.GetTranslation("pluginStore_Installed"); + default: + return ID; + } + + } + + public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); + } +} diff --git a/Flow.Launcher/Converters/TranslationConverter.cs b/Flow.Launcher/Converters/TranslationConverter.cs new file mode 100644 index 00000000000..e1e8a58e368 --- /dev/null +++ b/Flow.Launcher/Converters/TranslationConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Flow.Launcher.Core.Resource; + +namespace Flow.Launcher.Converters +{ + public class TranlationConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var key = value.ToString(); + if (String.IsNullOrEmpty(key)) + return key; + return InternationalizationManager.Instance.GetTranslation(key); + } + + public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); + } +} diff --git a/Flow.Launcher/CustomShortcutSetting.xaml b/Flow.Launcher/CustomShortcutSetting.xaml new file mode 100644 index 00000000000..78b392f3e52 --- /dev/null +++ b/Flow.Launcher/CustomShortcutSetting.xaml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Flow.Launcher/CustomShortcutSetting.xaml.cs b/Flow.Launcher/CustomShortcutSetting.xaml.cs new file mode 100644 index 00000000000..097d6a53b8a --- /dev/null +++ b/Flow.Launcher/CustomShortcutSetting.xaml.cs @@ -0,0 +1,73 @@ +using Flow.Launcher.Core.Resource; +using Flow.Launcher.ViewModel; +using System; +using System.Windows; +using System.Windows.Input; + +namespace Flow.Launcher +{ + public partial class CustomShortcutSetting : Window + { + private SettingWindowViewModel viewModel; + public string Key { get; set; } = String.Empty; + public string Value { get; set; } = String.Empty; + private string originalKey { get; init; } = null; + private string originalValue { get; init; } = null; + private bool update { get; init; } = false; + + public CustomShortcutSetting(SettingWindowViewModel vm) + { + viewModel = vm; + InitializeComponent(); + } + + public CustomShortcutSetting(string key, string value, SettingWindowViewModel vm) + { + viewModel = vm; + Key = key; + Value = value; + originalKey = key; + originalValue = value; + update = true; + InitializeComponent(); + } + + private void BtnCancel_OnClick(object sender, RoutedEventArgs e) + { + DialogResult = false; + Close(); + } + + private void BtnAdd_OnClick(object sender, RoutedEventArgs e) + { + if (String.IsNullOrEmpty(Key) || String.IsNullOrEmpty(Value)) + { + MessageBox.Show(InternationalizationManager.Instance.GetTranslation("emptyShortcut")); + return; + } + // Check if key is modified or adding a new one + if (((update && originalKey != Key) || !update) + && viewModel.ShortcutExists(Key)) + { + MessageBox.Show(InternationalizationManager.Instance.GetTranslation("duplicateShortcut")); + return; + } + DialogResult = !update || originalKey != Key || originalValue != Value; + Close(); + } + + private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e) + { + DialogResult = false; + Close(); + } + + private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e) + { + App.API.ChangeQuery(tbExpand.Text); + Application.Current.MainWindow.Show(); + Application.Current.MainWindow.Opacity = 1; + Application.Current.MainWindow.Focus(); + } + } +} diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 813a44527be..1e7f70b0a86 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -98,6 +98,7 @@ + @@ -120,7 +121,7 @@ - + diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index a3ad20f7748..b9ac6afb3cd 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -17,7 +17,7 @@ internal static class HotKeyMapper internal static void Initialize(MainViewModel mainVM) { mainViewModel = mainVM; - settings = mainViewModel._settings; + settings = mainViewModel.Settings; SetHotkey(settings.Hotkey, OnToggleHotkey); LoadCustomPluginHotkey(); diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml index 7ec94fed109..25bd195dd04 100644 --- a/Flow.Launcher/Languages/da.xaml +++ b/Flow.Launcher/Languages/da.xaml @@ -77,13 +77,13 @@ Søgetid: | Version Website - Uninstall + Uninstall Plugin Store Refresh - Install + Install Tema diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml index a1b0b3b9b37..ebd549adf9a 100644 --- a/Flow.Launcher/Languages/de.xaml +++ b/Flow.Launcher/Languages/de.xaml @@ -77,13 +77,13 @@ Abfragezeit: Version Webseite - Deinstallieren + Deinstallieren Erweiterungen laden Aktualisieren - Installieren + Installieren Design diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 824201f7d66..2e3922d06d7 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -23,6 +23,8 @@ Text Game Mode Suspend the use of Hotkeys. + Position Reset + Reset search window position Flow Launcher Settings @@ -33,7 +35,13 @@ Error setting launch on startup Hide Flow Launcher when focus is lost Do not show new version notifications + Search Window Position Remember last launch location + Remember Last Location + Mouse Focused Screen - Center + Mouse Focused Screen - Center Top + Mouse Focused Screen - Left Top + Mouse Focused Screen - Right Top Language Last Query Style Show/Hide previous results when Flow Launcher is reactivated. @@ -84,13 +92,24 @@ Query time: | Version Website - Uninstall Plugin Store + New Release + Recently Updated + Plugins + Installed Refresh - Install + Install + Uninstall + Update + Plug-in already installed + New Version + This plug-in has been updated within the last 7 days + New Update is Available + + Theme @@ -113,6 +132,8 @@ Play a small sound when the search window opens Animation Use Animation in UI + Clock + Date Hotkey @@ -123,12 +144,18 @@ Show Hotkey Show result selection hotkey with results. Custom Query Hotkey + Custom Query Shortcut Query + Shortcut + Expanded + Description Delete Edit Add Please select an item Are you sure you want to delete {0} plugin hotkey? + Are you sure you want to delete shortcut: {0} with expansion {1}? + Get text from clipboard. Query window shadow effect Shadow effect has a substantial usage of GPU. Not recommended if your computer performance is limited. Window Width Size @@ -220,6 +247,12 @@ Invalid plugin hotkey Update + + 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. + Hotkey Unavailable diff --git a/Flow.Launcher/Languages/es-419.xaml b/Flow.Launcher/Languages/es-419.xaml index 84a492d1553..a410f4b326b 100644 --- a/Flow.Launcher/Languages/es-419.xaml +++ b/Flow.Launcher/Languages/es-419.xaml @@ -77,13 +77,13 @@ Tiempo de consulta: | Versión Sitio web - Uninstall + Uninstall Tienda de Plugins Recargar - Instalar + Instalar Tema diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml index 141d868a2d9..edc5e4f0701 100644 --- a/Flow.Launcher/Languages/fr.xaml +++ b/Flow.Launcher/Languages/fr.xaml @@ -77,13 +77,13 @@ Utilisation : | Version Website - Désinstaller + Désinstaller Plugin Store Refresh - Install + Install Thèmes diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml index 4304aead83e..30a4018759f 100644 --- a/Flow.Launcher/Languages/it.xaml +++ b/Flow.Launcher/Languages/it.xaml @@ -77,13 +77,13 @@ Tempo ricerca: | Versione Sito Web - Disinstalla + Disinstalla Negozio dei Plugin Aggiorna - Installa + Installa Tema diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml index 8c13863710c..a2dcfb6a098 100644 --- a/Flow.Launcher/Languages/ja.xaml +++ b/Flow.Launcher/Languages/ja.xaml @@ -77,13 +77,13 @@ クエリ時間: | バージョン ウェブサイト - アンインストール + アンインストール プラグインストア Refresh - Install + Install テーマ diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index 809c5ccf817..acb68cb4f42 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -77,13 +77,13 @@ 쿼리 시간: | 버전 웹사이트 - 제거 + 제거 플러그인 스토어 새로고침 - 설치 + 설치 테마 diff --git a/Flow.Launcher/Languages/nb.xaml b/Flow.Launcher/Languages/nb.xaml index e90a32347a5..0848e9d6478 100644 --- a/Flow.Launcher/Languages/nb.xaml +++ b/Flow.Launcher/Languages/nb.xaml @@ -77,13 +77,13 @@ Query time: | Version Website - Uninstall + Uninstall Plugin Store Refresh - Install + Install Theme diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml index 77a8e9e2902..e398afa5134 100644 --- a/Flow.Launcher/Languages/nl.xaml +++ b/Flow.Launcher/Languages/nl.xaml @@ -77,13 +77,13 @@ Query tijd: | Versie Website - Uninstall + Uninstall Plugin Winkel Vernieuwen - Installeren + Installeren Thema diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml index bd7acd4e88b..fc5badd6972 100644 --- a/Flow.Launcher/Languages/pl.xaml +++ b/Flow.Launcher/Languages/pl.xaml @@ -77,13 +77,13 @@ Czas zapytania: | Version Website - Odinstalowywanie + Odinstalowywanie Plugin Store Refresh - Install + Install Skórka diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml index 28ed3091674..f6fc062c638 100644 --- a/Flow.Launcher/Languages/pt-br.xaml +++ b/Flow.Launcher/Languages/pt-br.xaml @@ -77,13 +77,13 @@ Tempo de consulta: | Version Website - Desinstalar + Desinstalar Plugin Store Refresh - Install + Install Tema diff --git a/Flow.Launcher/Languages/pt-pt.xaml b/Flow.Launcher/Languages/pt-pt.xaml index e7179e6e873..b19fc992413 100644 --- a/Flow.Launcher/Languages/pt-pt.xaml +++ b/Flow.Launcher/Languages/pt-pt.xaml @@ -77,13 +77,13 @@ Tempo de consulta: | Versão Site - Desinstalar + Desinstalar Loja de plugins Recarregar - Instalar + Instalar Tema diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml index 2c8ca9fa3d7..87b3dd4ef35 100644 --- a/Flow.Launcher/Languages/ru.xaml +++ b/Flow.Launcher/Languages/ru.xaml @@ -77,13 +77,13 @@ Запрос: | Version Website - Удалить + Удалить Plugin Store Refresh - Install + Install Тема diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index c4d5de6bf29..ee703bcf8c8 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -77,13 +77,13 @@ Trvanie dopytu: | Verzia Webstránka - Odinštalovať + Odinštalovať Repozitár pluginov Obnoviť - Inštalovať + Inštalovať Motív diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml index 61f619fd038..e805860dcc1 100644 --- a/Flow.Launcher/Languages/sr.xaml +++ b/Flow.Launcher/Languages/sr.xaml @@ -77,13 +77,13 @@ Vreme upita: | Version Website - Uninstall + Uninstall Plugin Store Refresh - Install + Install Tema diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml index fb8e1d6f621..4a016ced8bd 100644 --- a/Flow.Launcher/Languages/tr.xaml +++ b/Flow.Launcher/Languages/tr.xaml @@ -77,13 +77,13 @@ Sorgu Süresi: Sürüm İnternet Sitesi - Kaldır + Kaldır Eklenti Mağazası Yenile - İndir + İndir Temalar diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml index 8115a5dd1a8..a34ed4e8bce 100644 --- a/Flow.Launcher/Languages/uk-UA.xaml +++ b/Flow.Launcher/Languages/uk-UA.xaml @@ -77,13 +77,13 @@ Запит: | Версія Сайт - Uninstall + Uninstall Магазин плагінів Оновити - Встановити + Встановити Тема diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml index d80bfdaf66a..b62736d16af 100644 --- a/Flow.Launcher/Languages/zh-cn.xaml +++ b/Flow.Launcher/Languages/zh-cn.xaml @@ -77,13 +77,13 @@ 查询耗时: | 版本 官方网站 - 卸载 + 卸载 插件商店 刷新 - 安装 + 安装 主题 diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml index 71cc887cf8f..69abbe40160 100644 --- a/Flow.Launcher/Languages/zh-tw.xaml +++ b/Flow.Launcher/Languages/zh-tw.xaml @@ -77,13 +77,13 @@ 查詢耗時: | 版本 官方網站 - 解除安裝 + 解除安裝 外掛商店 重新整理 - 安裝 + 安裝 主題 diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index cc605edc90b..922f8b9338f 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -1,5 +1,4 @@ - + mc:Ignorable="d"> + - - - - - + + + + - - - - - - - - - + + + + Command="{Binding IncreaseWidthCommand}" + Modifiers="Control" /> + Command="{Binding DecreaseWidthCommand}" + Modifiers="Control" /> + Command="{Binding IncreaseMaxResultCommand}" + Modifiers="Control" /> + Command="{Binding DecreaseMaxResultCommand}" + Modifiers="Control" /> - - - - + - - - - - - - - - - - - + - - - - + + + - - + - - - - + + + - - + + + + + + + + - - - - - - - + + + - + PreviewMouseLeftButtonUp="OnPreviewMouseButtonDown" /> - - - + + + - @@ -309,14 +323,16 @@ - - - + + + - @@ -324,4 +340,4 @@ - + \ No newline at end of file diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 1c68456232a..6a65a38f2ae 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.Threading.Tasks; using System.Windows; @@ -20,8 +20,13 @@ using System.Windows.Media; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Plugin.SharedCommands; -using System.Windows.Data; +using System.Text; +using DataObject = System.Windows.DataObject; using System.Diagnostics; +using Microsoft.AspNetCore.Http; +using System.IO; +using System.Windows.Threading; +using System.Windows.Data; namespace Flow.Launcher { @@ -45,7 +50,9 @@ public MainWindow(Settings settings, MainViewModel mainVM) DataContext = mainVM; _viewModel = mainVM; _settings = settings; + InitializeComponent(); + InitializePosition(); animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); } @@ -53,6 +60,7 @@ public MainWindow() { InitializeComponent(); } + private void OnCopy(object sender, ExecutedRoutedEventArgs e) { if (QueryTextBox.SelectionLength == 0) @@ -108,7 +116,6 @@ private void OnLoaded(object sender, RoutedEventArgs _) animationSound.Position = TimeSpan.Zero; animationSound.Play(); } - UpdatePosition(); Activate(); QueryTextBox.Focus(); @@ -161,6 +168,7 @@ private void OnLoaded(object sender, RoutedEventArgs _) _viewModel.QueryTextCursorMovedToEnd = false; } break; + } }; _settings.PropertyChanged += (o, e) => @@ -176,17 +184,51 @@ private void OnLoaded(object sender, RoutedEventArgs _) case nameof(Settings.Hotkey): UpdateNotifyIconText(); break; + case nameof(Settings.WindowLeft): + Left = _settings.WindowLeft; + break; + case nameof(Settings.WindowTop): + Top = _settings.WindowTop; + break; } }; } + private void InitializePosition() + { + switch (_settings.SearchWindowPosition) + { + case SearchWindowPositions.RememberLastLaunchLocation: + Top = _settings.WindowTop; + Left = _settings.WindowLeft; + break; + case SearchWindowPositions.MouseScreenCenter: + Left = HorizonCenter(); + Top = VerticalCenter(); + break; + case SearchWindowPositions.MouseScreenCenterTop: + Left = HorizonCenter(); + Top = 10; + break; + case SearchWindowPositions.MouseScreenLeftTop: + Left = 10; + Top = 10; + break; + case SearchWindowPositions.MouseScreenRightTop: + Left = HorizonRight(); + Top = 10; + break; + } + } + private void UpdateNotifyIconText() { var menu = contextMenu; ((MenuItem)menu.Items[1]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")"; ((MenuItem)menu.Items[2]).Header = InternationalizationManager.Instance.GetTranslation("GameMode"); - ((MenuItem)menu.Items[3]).Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"); - ((MenuItem)menu.Items[4]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"); + ((MenuItem)menu.Items[3]).Header = InternationalizationManager.Instance.GetTranslation("PositionReset"); + ((MenuItem)menu.Items[4]).Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"); + ((MenuItem)menu.Items[5]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"); } private void InitializeNotifyIcon() @@ -212,6 +254,10 @@ private void InitializeNotifyIcon() { Header = InternationalizationManager.Instance.GetTranslation("GameMode") }; + var positionreset = new MenuItem + { + Header = InternationalizationManager.Instance.GetTranslation("PositionReset") + }; var settings = new MenuItem { Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings") @@ -223,12 +269,15 @@ private void InitializeNotifyIcon() open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); gamemode.Click += (o, e) => ToggleGameMode(); + positionreset.Click += (o, e) => PositionReset(); settings.Click += (o, e) => App.API.OpenSettingDialog(); exit.Click += (o, e) => Close(); contextMenu.Items.Add(header); contextMenu.Items.Add(open); gamemode.ToolTip = InternationalizationManager.Instance.GetTranslation("GameModeToolTip"); + positionreset.ToolTip = InternationalizationManager.Instance.GetTranslation("PositionResetToolTip"); contextMenu.Items.Add(gamemode); + contextMenu.Items.Add(positionreset); contextMenu.Items.Add(settings); contextMenu.Items.Add(exit); @@ -275,10 +324,17 @@ private void ToggleGameMode() _viewModel.GameModeStatus = true; } } + private async void PositionReset() + { + _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))); + 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))); Storyboard.SetTargetProperty(da, new PropertyPath("(Line.X2)")); Storyboard.SetTargetProperty(da1, new PropertyPath("(Line.X1)")); @@ -382,6 +438,8 @@ private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs private async void OnDeactivated(object sender, EventArgs e) { + _settings.WindowLeft = Left; + _settings.WindowTop = Top; //This condition stops extra hide call when animator is on, // which causes the toggling to occasional hide instead of show. if (_viewModel.MainWindowVisibilityStatus) @@ -403,24 +461,14 @@ private void UpdatePosition() { if (_animating) return; - - if (_settings.RememberLastLaunchLocation) - { - Left = _settings.WindowLeft; - Top = _settings.WindowTop; - } - else - { - Left = WindowLeft(); - Top = WindowTop(); - } + InitializePosition(); } private void OnLocationChanged(object sender, EventArgs e) { if (_animating) return; - if (_settings.RememberLastLaunchLocation) + if (_settings.SearchWindowPosition == SearchWindowPositions.RememberLastLaunchLocation) { _settings.WindowLeft = Left; _settings.WindowTop = Top; @@ -439,17 +487,8 @@ public void HideStartup() _viewModel.Show(); } } - - private void InitializePosition() - { - if (!_settings.RememberLastLaunchLocation) - { - Left = WindowLeft(); - Top = WindowTop(); - } - } - public double WindowLeft() + public double HorizonCenter() { var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); @@ -458,7 +497,7 @@ public double WindowLeft() return left; } - public double WindowTop() + public double VerticalCenter() { var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); @@ -467,6 +506,15 @@ public double WindowTop() return top; } + public double HorizonRight() + { + var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); + var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); + var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.Width, 0); + var left = (dip2.X - ActualWidth) - 10; + return left; + } + /// /// Register up and down key /// todo: any way to put this in xaml ? diff --git a/Flow.Launcher/Resources/CustomControlTemplate.xaml b/Flow.Launcher/Resources/CustomControlTemplate.xaml index 6ab7ab5ad68..6e82940313a 100644 --- a/Flow.Launcher/Resources/CustomControlTemplate.xaml +++ b/Flow.Launcher/Resources/CustomControlTemplate.xaml @@ -2044,31 +2044,43 @@ - + - - + + + + + + + @@ -2077,12 +2089,12 @@ - + - + @@ -2110,7 +2122,7 @@ x:Name="HeaderSite" MinWidth="0" MinHeight="0" - Margin="18,0,0,0" + Margin="18,0,18,0" Padding="{TemplateBinding Padding}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" @@ -2127,19 +2139,62 @@ Foreground="{TemplateBinding Foreground}" IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpanderDownHeaderStyle}" /> - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Flow.Launcher/Resources/Dark.xaml b/Flow.Launcher/Resources/Dark.xaml index 445e07c6336..674e04debc2 100644 --- a/Flow.Launcher/Resources/Dark.xaml +++ b/Flow.Launcher/Resources/Dark.xaml @@ -69,6 +69,8 @@ + #272727 + #202020 #2b2b2b #1d1d1d diff --git a/Flow.Launcher/Resources/Light.xaml b/Flow.Launcher/Resources/Light.xaml index cee8b63cd94..8b04196dda0 100644 --- a/Flow.Launcher/Resources/Light.xaml +++ b/Flow.Launcher/Resources/Light.xaml @@ -62,6 +62,8 @@ + #f6f6f6 + #f3f3f3 #ffffff #e5e5e5 diff --git a/Flow.Launcher/ResultListBox.xaml b/Flow.Launcher/ResultListBox.xaml index 5c497b925d2..1ece390d120 100644 --- a/Flow.Launcher/ResultListBox.xaml +++ b/Flow.Launcher/ResultListBox.xaml @@ -25,12 +25,15 @@ VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Standard" Visibility="{Binding Visbility}" - mc:Ignorable="d"> + mc:Ignorable="d" + PreviewMouseMove="ResultList_MouseMove" + PreviewMouseLeftButtonDown="ResultList_PreviewMouseLeftButtonDown"> - + + + + @@ -1719,7 +1741,9 @@ + ScrollViewer.CanContentScroll="True" + VirtualizingStackPanel.IsVirtualizing="True" + VirtualizingStackPanel.ScrollUnit="Pixel"> @@ -1766,8 +1790,11 @@ IsReadOnly="True" Style="{DynamicResource QueryBoxStyle}" Text="{DynamicResource hiThere}" /> - + + + + + + + + + + + + + + + +  + + + + + + + + + + + + + + +  + + + + + + + @@ -2211,16 +2319,26 @@ + ScrollViewer.CanContentScroll="True" + VirtualizingStackPanel.IsVirtualizing="True" + VirtualizingStackPanel.ScrollUnit="Pixel"> + + + + + + + + - + IsOn="{Binding Settings.ShowOpenResultHotkey}" + Style="{DynamicResource SideToggleSwitch}" /> @@ -2325,6 +2442,7 @@ Text="{DynamicResource customQueryHotkey}" />