diff --git a/Flow.Launcher.Core/Resource/LocalizationConverter.cs b/Flow.Launcher.Core/Resource/LocalizationConverter.cs index 1d835a831..81600e023 100644 --- a/Flow.Launcher.Core/Resource/LocalizationConverter.cs +++ b/Flow.Launcher.Core/Resource/LocalizationConverter.cs @@ -4,7 +4,7 @@ using System.Globalization; using System.Reflection; using System.Windows.Data; -namespace Flow.Launcher.Core +namespace Flow.Launcher.Core.Resource { public class LocalizationConverter : IValueConverter { diff --git a/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs b/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs index af8b23136..52a232334 100644 --- a/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs +++ b/Flow.Launcher.Core/Resource/LocalizedDescriptionAttribute.cs @@ -1,7 +1,6 @@ using System.ComponentModel; -using Flow.Launcher.Core.Resource; -namespace Flow.Launcher.Core +namespace Flow.Launcher.Core.Resource { public class LocalizedDescriptionAttribute : DescriptionAttribute { diff --git a/Flow.Launcher/Converters/TranslationConverter.cs b/Flow.Launcher.Core/Resource/TranslationConverter.cs similarity index 81% rename from Flow.Launcher/Converters/TranslationConverter.cs rename to Flow.Launcher.Core/Resource/TranslationConverter.cs index e1e8a58e3..ebab99e5b 100644 --- a/Flow.Launcher/Converters/TranslationConverter.cs +++ b/Flow.Launcher.Core/Resource/TranslationConverter.cs @@ -1,11 +1,10 @@ using System; using System.Globalization; using System.Windows.Data; -using Flow.Launcher.Core.Resource; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Core.Resource { - public class TranlationConverter : IValueConverter + public class TranslationConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 09fad990b..9ae1748da 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -147,6 +147,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings /// public bool ShouldUsePinyin { get; set; } = false; public bool AlwaysPreview { get; set; } = false; + public bool AlwaysStartEn { get; set; } = false; [JsonInclude, JsonConverter(typeof(JsonStringEnumConverter))] public SearchPrecisionScore QuerySearchPrecision { get; private set; } = SearchPrecisionScore.Regular; 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 e0cc9b4c2..36f0294a9 100644 --- a/Flow.Launcher.Test/Plugins/ExplorerTest.cs +++ b/Flow.Launcher.Test/Plugins/ExplorerTest.cs @@ -269,5 +269,129 @@ namespace Flow.Launcher.Test.Plugins // Then Assert.AreEqual(expectedString, resultString); } + + [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "irrelevant", false, true, "c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder\\", ResultType.Folder, "irrelevant", true, true, "c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "irrelevant", true, false, "p c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder\\", ResultType.Folder, "irrelevant", false, false, "c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "p", true, false, "p c:\\somefolder\\someotherfolder\\")] + [TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "", true, true, "c:\\somefolder\\someotherfolder\\")] + public void GivenFolderResult_WhenGetPath_ThenPathShouldBeExpectedString( + string path, + ResultType type, + string actionKeyword, + bool pathSearchKeywordEnabled, + bool searchActionKeywordEnabled, + string expectedResult) + { + // Given + var settings = new Settings() + { + PathSearchKeywordEnabled = pathSearchKeywordEnabled, + PathSearchActionKeyword = "p", + SearchActionKeywordEnabled = searchActionKeywordEnabled, + SearchActionKeyword = Query.GlobalPluginWildcardSign + }; + ResultManager.Init(new PluginInitContext(), settings); + + // When + var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword); + + // Then + Assert.AreEqual(result, expectedResult); + } + + [TestCase("c:\\somefolder\\somefile", ResultType.File, "irrelevant", false, true, "e c:\\somefolder\\somefile")] + [TestCase("c:\\somefolder\\somefile", ResultType.File, "p", true, false, "p c:\\somefolder\\somefile")] + [TestCase("c:\\somefolder\\somefile", ResultType.File, "e", true, true, "e c:\\somefolder\\somefile")] + [TestCase("c:\\somefolder\\somefile", ResultType.File, "irrelevant", false, false, "e c:\\somefolder\\somefile")] + public void GivenFileResult_WhenGetPath_ThenPathShouldBeExpectedString( + string path, + ResultType type, + string actionKeyword, + bool pathSearchKeywordEnabled, + bool searchActionKeywordEnabled, + string expectedResult) + { + // Given + var settings = new Settings() + { + PathSearchKeywordEnabled = pathSearchKeywordEnabled, + PathSearchActionKeyword = "p", + SearchActionKeywordEnabled = searchActionKeywordEnabled, + SearchActionKeyword = "e" + }; + ResultManager.Init(new PluginInitContext(), settings); + + // When + var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword); + + // Then + Assert.AreEqual(result, expectedResult); + } + + [TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "q", false, false, "q somefolder")] + [TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "i", true, false, "p c:\\somefolder\\")] + [TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "irrelevant", true, true, "c:\\somefolder\\")] + public void GivenQueryWithFolderTypeResult_WhenGetAutoComplete_ThenResultShouldBeExpectedString( + string title, + string path, + ResultType resultType, + string actionKeyword, + bool pathSearchKeywordEnabled, + bool searchActionKeywordEnabled, + string expectedResult) + { + // Given + var query = new Query() { ActionKeyword = actionKeyword }; + var settings = new Settings() + { + PathSearchKeywordEnabled = pathSearchKeywordEnabled, + PathSearchActionKeyword = "p", + SearchActionKeywordEnabled = searchActionKeywordEnabled, + SearchActionKeyword = Query.GlobalPluginWildcardSign, + QuickAccessActionKeyword = "q", + IndexSearchActionKeyword = "i" + }; + ResultManager.Init(new PluginInitContext(), settings); + + // When + var result = ResultManager.GetAutoCompleteText(title, query, path, resultType); + + // Then + Assert.AreEqual(result, expectedResult); + } + + [TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "q", false, false, "q somefile")] + [TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "i", true, false, "p c:\\somefolder\\somefile")] + [TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "irrelevant", true, true, "c:\\somefolder\\somefile")] + public void GivenQueryWithFileTypeResult_WhenGetAutoComplete_ThenResultShouldBeExpectedString( + string title, + string path, + ResultType resultType, + string actionKeyword, + bool pathSearchKeywordEnabled, + bool searchActionKeywordEnabled, + string expectedResult) + { + // Given + var query = new Query() { ActionKeyword = actionKeyword }; + var settings = new Settings() + { + QuickAccessActionKeyword = "q", + IndexSearchActionKeyword = "i", + PathSearchActionKeyword = "p", + PathSearchKeywordEnabled = pathSearchKeywordEnabled, + SearchActionKeywordEnabled = searchActionKeywordEnabled, + SearchActionKeyword = Query.GlobalPluginWildcardSign + }; + ResultManager.Init(new PluginInitContext(), settings); + + // When + var result = ResultManager.GetAutoCompleteText(title, query, path, resultType); + + // Then + Assert.AreEqual(result, expectedResult); + } } } diff --git a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs new file mode 100644 index 000000000..0bff23fe1 --- /dev/null +++ b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs @@ -0,0 +1,55 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Input; + +namespace Flow.Launcher.Converters +{ + internal class BoolToIMEConversionModeConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is bool v) + { + if (v) + { + return ImeConversionModeValues.Alphanumeric; + } + else + { + return ImeConversionModeValues.DoNotCare; + } + } + return ImeConversionModeValues.DoNotCare; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + + internal class BoolToIMEStateConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is bool v) + { + if (v) + { + return InputMethodState.Off; + } + else + { + return InputMethodState.DoNotCare; + } + } + return InputMethodState.DoNotCare; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Flow.Launcher/CustomShortcutSetting.xaml b/Flow.Launcher/CustomShortcutSetting.xaml index bbf6ff9f2..5a40a77b1 100644 --- a/Flow.Launcher/CustomShortcutSetting.xaml +++ b/Flow.Launcher/CustomShortcutSetting.xaml @@ -64,7 +64,6 @@ { - if (mainViewModel.ShouldIgnoreHotkeys() || mainViewModel.GameModeStatus) + if (mainViewModel.ShouldIgnoreHotkeys()) return; mainViewModel.Show(); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index d0bdfee9b..550b73f85 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -18,6 +18,8 @@ Copy Cut Paste + Undo + Select All File Folder Text @@ -55,6 +57,8 @@ Select the file manager to use when opening the folder. Default Web Browser Setting for New Tab, New Window, Private Mode. + Always Start Typing in English Mode + Temporarily change your input method to English mode when activating Flow. Python Directory Auto Update Select @@ -66,7 +70,7 @@ 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. + Always open preview panel when Flow starts. Press F1 to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 9f8d523e0..5ed9ba802 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -9,6 +9,7 @@ xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" xmlns:ui="http://schemas.modernwpf.com/2019" xmlns:vm="clr-namespace:Flow.Launcher.ViewModel" + d:DataContext="{d:DesignInstance Type=vm:MainViewModel}" Name="FlowMainWindow" Title="Flow Launcher" MinWidth="{Binding MainWindowWidth, Mode=OneWay}" @@ -37,6 +38,8 @@ + + @@ -83,14 +86,10 @@ Key="O" Command="{Binding LoadContextMenuCommand}" Modifiers="Ctrl" /> - - - - + @@ -204,6 +207,8 @@ PreviewKeyUp="QueryTextBox_KeyUp" Style="{DynamicResource QueryBoxStyle}" Text="{Binding QueryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" + InputMethod.PreferredImeConversionMode="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEConversionModeConverter}}" + InputMethod.PreferredImeState="{Binding StartWithEnglishMode, Converter={StaticResource BoolToIMEStateConverter}}" Visibility="Visible"> @@ -244,6 +249,7 @@ + { switch (e.PropertyName) @@ -178,9 +170,12 @@ namespace Flow.Launcher _viewModel.QueryTextCursorMovedToEnd = false; } break; - + case nameof(MainViewModel.GameModeStatus): + _notifyIcon.Icon = _viewModel.GameModeStatus ? Properties.Resources.gamemode : Properties.Resources.app; + break; } }; + _settings.PropertyChanged += (o, e) => { switch (e.PropertyName) @@ -295,7 +290,7 @@ namespace Flow.Launcher }; open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); - gamemode.Click += (o, e) => ToggleGameMode(); + gamemode.Click += (o, e) => _viewModel.ToggleGameMode(); positionreset.Click += (o, e) => PositionReset(); settings.Click += (o, e) => App.API.OpenSettingDialog(); exit.Click += (o, e) => Close(); @@ -334,24 +329,13 @@ namespace Flow.Launcher OpenWelcomeWindow(); } } + private void OpenWelcomeWindow() { var WelcomeWindow = new WelcomeWindow(_settings); WelcomeWindow.Show(); } - private void ToggleGameMode() - { - if (_viewModel.GameModeStatus) - { - _notifyIcon.Icon = Properties.Resources.app; - _viewModel.GameModeStatus = false; - } - else - { - _notifyIcon.Icon = Properties.Resources.gamemode; - _viewModel.GameModeStatus = true; - } - } + private async void PositionReset() { _viewModel.Show(); @@ -359,6 +343,7 @@ namespace Flow.Launcher Left = HorizonCenter(); Top = VerticalCenter(); } + private void InitProgressbarAnimation() { var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 100, new Duration(new TimeSpan(0, 0, 0, 0, 1600))); @@ -371,6 +356,7 @@ namespace Flow.Launcher _viewModel.ProgressBarVisibility = Visibility.Hidden; isProgressBarStoryboardPaused = true; } + public void WindowAnimator() { if (_animating) @@ -485,7 +471,6 @@ namespace Flow.Launcher App.API.OpenSettingDialog(); } - private async void OnDeactivated(object sender, EventArgs e) { _settings.WindowLeft = Left; @@ -606,12 +591,6 @@ namespace Flow.Launcher e.Handled = true; } break; - case Key.F12: - if (specialKeyState.CtrlPressed) - { - ToggleGameMode(); - } - break; case Key.Back: if (specialKeyState.CtrlPressed) { @@ -654,6 +633,7 @@ namespace Flow.Launcher Preview.Visibility = Visibility.Collapsed; } } + public void PreviewToggle() { diff --git a/Flow.Launcher/Resources/CustomControlTemplate.xaml b/Flow.Launcher/Resources/CustomControlTemplate.xaml index 451dc5344..1031ab32d 100644 --- a/Flow.Launcher/Resources/CustomControlTemplate.xaml +++ b/Flow.Launcher/Resources/CustomControlTemplate.xaml @@ -4,6 +4,30 @@ xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:ui="http://schemas.modernwpf.com/2019"> + + + + + + + + + + + + + + + + + + + + + + + + - + + + + diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 5c3c12710..f10964fe0 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -3,6 +3,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="clr-namespace:Flow.Launcher.Converters" + xmlns:core="clr-namespace:Flow.Launcher.Core.Resource;assembly=Flow.Launcher.Core" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:flowlauncher="clr-namespace:Flow.Launcher" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" @@ -42,7 +43,7 @@ - + @@ -737,30 +738,14 @@ - - -  - - - - - - - - - - + ToolTip="{DynamicResource AlwaysPreviewToolTip}" /> -  +  @@ -943,13 +928,52 @@ Text="{Binding Settings.PluginSettings.PythonDirectory, TargetNullValue='No Setting'}" />