diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index ea2119e60..dd6517a7f 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.IO; @@ -45,8 +45,56 @@ namespace Flow.Launcher.Core.Plugin } } } - - return allPluginMetadata; + + (List uniqueList, List duplicateList) = GetUniqueLatestPluginMetadata(allPluginMetadata); + + duplicateList + .ForEach( + x => Log.Warn("PluginConfig", + string.Format("Duplicate plugin name: {0}, id: {1}, version: {2} " + + "not loaded due to version not the highest of the duplicates", + x.Name, x.ID, x.Version), + "GetUniqueLatestPluginMetadata")); + + return uniqueList; + } + + internal static (List, List) GetUniqueLatestPluginMetadata(List allPluginMetadata) + { + var duplicate_list = new List(); + var unique_list = new List(); + + var duplicateGroups = allPluginMetadata.GroupBy(x => x.ID).Where(g => g.Count() > 1).Select(y => y).ToList(); + + foreach (var metadata in allPluginMetadata) + { + var duplicatesExist = false; + foreach (var group in duplicateGroups) + { + if (metadata.ID == group.Key) + { + duplicatesExist = true; + + // If metadata's version greater than each duplicate's version, CompareTo > 0 + var count = group.Where(x => metadata.Version.CompareTo(x.Version) > 0).Count(); + + // Only add if the meatadata's version is the highest of all duplicates in the group + if (count == group.Count() - 1) + { + unique_list.Add(metadata); + } + else + { + duplicate_list.Add(metadata); + } + } + } + + if (!duplicatesExist) + unique_list.Add(metadata); + } + + return (unique_list, duplicate_list); } private static PluginMetadata GetPluginMetadata(string pluginDirectory) diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 4648aef63..6561419a1 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -189,22 +189,11 @@ namespace Flow.Launcher.Core.Resource new[] { resultItemStyle, resultSubItemStyle, resultItemSelectedStyle, resultSubItemSelectedStyle, resultHotkeyItemStyle, resultHotkeyItemSelectedStyle }, o => Array.ForEach(setters, p => o.Setters.Add(p))); } - + /* Ignore Theme Window Width and use setting */ var windowStyle = dict["WindowStyle"] as Style; - - var width = windowStyle?.Setters.OfType().Where(x => x.Property.Name == "Width") - .Select(x => x.Value).FirstOrDefault(); - - if (width == null) - { - windowStyle = dict["BaseWindowStyle"] as Style; - - width = windowStyle?.Setters.OfType().Where(x => x.Property.Name == "Width") - .Select(x => x.Value).FirstOrDefault(); - } - + var width = Settings.WindowSize; + windowStyle.Setters.Add(new Setter(Window.WidthProperty, width)); mainWindowWidth = (double)width; - return dict; } @@ -375,8 +364,6 @@ namespace Flow.Launcher.Core.Resource { var windowHelper = new WindowInteropHelper(w); - // this determines the width of the main query window - w.Width = mainWindowWidth; windowHelper.EnsureHandle(); var accent = new AccentPolicy { AccentState = state }; diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index a5b89c300..cd49217a4 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -33,10 +33,18 @@ namespace Flow.Launcher.Infrastructure public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.svg"; - public const string DefaultTheme = "Darker"; + public const string DefaultTheme = "Win11Light"; + + public const string Light = "Light"; + public const string Dark = "Dark"; + public const string System = "System"; public const string Themes = "Themes"; + public const string Settings = "Settings"; + public const string Logs = "Logs"; public const string Website = "https://flow-launcher.github.io"; + public const string GitHub = "https://github.com/Flow-Launcher/Flow.Launcher"; + public const string Docs = "https://flow-launcher.github.io/docs"; } } diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index ef76fd617..e01bc1efd 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -50,10 +50,15 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs new file mode 100644 index 000000000..7806debe1 --- /dev/null +++ b/Flow.Launcher.Infrastructure/UserSettings/CustomExplorerViewModel.cs @@ -0,0 +1,30 @@ +using Flow.Launcher.Plugin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Flow.Launcher.ViewModel +{ + public class CustomExplorerViewModel : BaseModel + { + public string Name { get; set; } + public string Path { get; set; } + public string FileArgument { get; set; } = "\"%d\""; + public string DirectoryArgument { get; set; } = "\"%d\""; + public bool Editable { get; init; } = true; + + public CustomExplorerViewModel Copy() + { + return new CustomExplorerViewModel + { + Name = Name, + Path = Path, + FileArgument = FileArgument, + DirectoryArgument = DirectoryArgument, + Editable = Editable + }; + } + } +} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 102446158..4c694b55a 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -1,22 +1,28 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Text.Json.Serialization; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; +using Flow.Launcher; +using Flow.Launcher.ViewModel; namespace Flow.Launcher.Infrastructure.UserSettings { public class Settings : BaseModel { private string language = "en"; - public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}"; public string OpenResultModifiers { get; set; } = KeyConstant.Alt; + public string ColorScheme { get; set; } = "System"; public bool ShowOpenResultHotkey { get; set; } = true; + public double WindowSize { get; set; } = 580; + public string Language { - get => language; set + get => language; + set { language = value; OnPropertyChanged(); @@ -34,6 +40,51 @@ namespace Flow.Launcher.Infrastructure.UserSettings public string ResultFontStretch { get; set; } public bool UseGlyphIcons { get; set; } = true; + public int CustomExplorerIndex { get; set; } = 0; + + [JsonIgnore] + public CustomExplorerViewModel CustomExplorer + { + get => CustomExplorerList[CustomExplorerIndex < CustomExplorerList.Count ? CustomExplorerIndex : 0]; + set => CustomExplorerList[CustomExplorerIndex] = value; + } + + public List CustomExplorerList { get; set; } = new() + { + new() + { + Name = "Explorer", + Path = "explorer", + DirectoryArgument = "\"%d\"", + FileArgument = "/select, \"%f\"", + Editable = false + }, + new() + { + Name = "Total Commander", + Path = @"C:\Program Files\totalcmd\TOTALCMD64.exe", + DirectoryArgument = "/O /A /S /T \"%d\"", + FileArgument = "/O /A /S /T \"%f\"" + }, + new() + { + Name = "Directory Opus", + Path = @"C:\Program Files\GPSoftware\Directory Opus\dopusrt.exe", + DirectoryArgument = "/cmd Go \"%d\" NEW", + FileArgument = "/cmd Go \"%f\" NEW" + + }, + new() + { + Name = "Files", + Path = "Files", + DirectoryArgument = "-select \"%d\"", + FileArgument = "-select \"%f\"" + } + }; + + public bool UseAnimation { get; set; } = true; + public bool UseSound { get; set; } = true; /// /// when false Alphabet static service will always return empty results @@ -52,7 +103,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings try { var precisionScore = (SearchPrecisionScore)Enum - .Parse(typeof(SearchPrecisionScore), value); + .Parse(typeof(SearchPrecisionScore), value); QuerySearchPrecision = precisionScore; StringMatcher.Instance.UserSettingSearchPrecision = precisionScore; @@ -82,7 +133,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool DontPromptUpdateMsg { get; set; } public bool EnableUpdateLog { get; set; } - public bool StartFlowLauncherOnSystemStartup { get; set; } = true; + public bool StartFlowLauncherOnSystemStartup { get; set; } = false; public bool HideOnStartup { get; set; } bool _hideNotifyIcon { get; set; } public bool HideNotifyIcon @@ -115,4 +166,11 @@ namespace Flow.Launcher.Infrastructure.UserSettings Empty, Preserved } + + public enum ColorSchemes + { + System, + Light, + Dark + } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 974eafa96..908284bb9 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -29,6 +29,15 @@ namespace Flow.Launcher.Plugin /// void RestartApp(); + /// + /// Run a shell command + /// + /// The command or program to run + /// the shell type to run, e.g. powershell.exe + /// Thrown when unable to find the file specified in the command + /// Thrown when error occurs during the execution of the command + void ShellRun(string cmd, string filename = "cmd.exe"); + /// /// Save everything, all of Flow Launcher and plugins' data and settings /// @@ -190,5 +199,12 @@ namespace Flow.Launcher.Plugin /// Type for Serialization /// void SaveSettingJsonStorage() where T : new(); + + /// + /// Open directory in an explorer configured by user via Flow's Settings. The default is Windows Explorer + /// + /// Directory Path to open + /// Extra FileName Info + public void OpenDirectory(string DirectoryPath, string FileName = null); } } diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 171b30c26..833ada9cd 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -50,7 +50,7 @@ namespace Flow.Launcher.Plugin /// /// Delegate to Get Image Source /// - public IconDelegate Icon { get; set; } + public IconDelegate Icon; /// /// Information for Glyph Icon diff --git a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs index c5d43a3d9..a2eea19a7 100644 --- a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs +++ b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -60,17 +60,39 @@ namespace Flow.Launcher.Plugin.SharedCommands return sb.ToString(); } - public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", string arguments = "", string verb = "") + public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", string arguments = "", string verb = "", bool createNoWindow = false) { var info = new ProcessStartInfo { FileName = fileName, WorkingDirectory = workingDirectory, Arguments = arguments, - Verb = verb + Verb = verb, + CreateNoWindow = createNoWindow }; return info; } + + /// + /// Runs a windows command using the provided ProcessStartInfo + /// + /// Thrown when unable to find the file specified in the command + /// Thrown when error occurs during the execution of the command + public static void Execute(ProcessStartInfo info) + { + Execute(Process.Start, info); + } + + /// + /// Runs a windows command using the provided ProcessStartInfo using a custom execute command function + /// + /// allows you to pass in a custom command execution function + /// Thrown when unable to find the file specified in the command + /// Thrown when error occurs during the execution of the command + public static void Execute(Func startProcess, ProcessStartInfo info) + { + startProcess(info); + } } } diff --git a/Flow.Launcher.Test/PluginLoadTest.cs b/Flow.Launcher.Test/PluginLoadTest.cs new file mode 100644 index 000000000..d6ba48f19 --- /dev/null +++ b/Flow.Launcher.Test/PluginLoadTest.cs @@ -0,0 +1,92 @@ +using NUnit.Framework; +using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Plugin; +using System.Collections.Generic; +using System.Linq; + +namespace Flow.Launcher.Test +{ + [TestFixture] + class PluginLoadTest + { + [Test] + public void GivenDuplicatePluginMetadatasWhenLoadedThenShouldReturnOnlyUniqueList() + { + // Given + var duplicateList = new List + { + new PluginMetadata + { + ID = "CEA0TYUC6D3B4085823D60DC76F28855", + Version = "1.0.0" + }, + new PluginMetadata + { + ID = "CEA0TYUC6D3B4085823D60DC76F28855", + Version = "1.0.1" + }, + new PluginMetadata + { + ID = "CEA0TYUC6D3B4085823D60DC76F28855", + Version = "1.0.2" + }, + new PluginMetadata + { + ID = "CEA0TYUC6D3B4085823D60DC76F28855", + Version = "1.0.0" + }, + new PluginMetadata + { + ID = "CEA0TYUC6D3B4085823D60DC76F28855", + Version = "1.0.0" + }, + new PluginMetadata + { + ID = "ABC0TYUC6D3B7855823D60DC76F28855", + Version = "1.0.0" + }, + new PluginMetadata + { + ID = "ABC0TYUC6D3B7855823D60DC76F28855", + Version = "1.0.0" + } + }; + + // When + (var unique, var duplicates) = PluginConfig.GetUniqueLatestPluginMetadata(duplicateList); + + // Then + Assert.True(unique.FirstOrDefault().ID == "CEA0TYUC6D3B4085823D60DC76F28855" && unique.FirstOrDefault().Version == "1.0.2"); + Assert.True(unique.Count() == 1); + + Assert.False(duplicates.Any(x => x.Version == "1.0.2" && x.ID == "CEA0TYUC6D3B4085823D60DC76F28855")); + Assert.True(duplicates.Count() == 6); + } + + [Test] + public void GivenDuplicatePluginMetadatasWithNoUniquePluginWhenLoadedThenShouldReturnEmptyList() + { + // Given + var duplicateList = new List + { + new PluginMetadata + { + ID = "CEA0TYUC6D3B7855823D60DC76F28855", + Version = "1.0.0" + }, + new PluginMetadata + { + ID = "CEA0TYUC6D3B7855823D60DC76F28855", + Version = "1.0.0" + } + }; + + // When + (var unique, var duplicates) = PluginConfig.GetUniqueLatestPluginMetadata(duplicateList); + + // Then + Assert.True(unique.Count() == 0); + Assert.True(duplicates.Count() == 2); + } + } +} diff --git a/Flow.Launcher.Test/Plugins/PluginInitTest.cs b/Flow.Launcher.Test/Plugins/PluginInitTest.cs deleted file mode 100644 index 299a837ee..000000000 --- a/Flow.Launcher.Test/Plugins/PluginInitTest.cs +++ /dev/null @@ -1,17 +0,0 @@ -using NUnit.Framework; -using Flow.Launcher.Core.Plugin; -using Flow.Launcher.Infrastructure.Exception; - -namespace Flow.Launcher.Test.Plugins -{ - - [TestFixture] - public class PluginInitTest - { - [Test] - public void PublicAPIIsNullTest() - { - //Assert.Throws(typeof(Flow.LauncherFatalException), () => PluginManager.Initialize(null)); - } - } -} diff --git a/Flow.Launcher/ActionKeywords.xaml b/Flow.Launcher/ActionKeywords.xaml index 9d032efd9..e94aac9f6 100644 --- a/Flow.Launcher/ActionKeywords.xaml +++ b/Flow.Launcher/ActionKeywords.xaml @@ -1,43 +1,137 @@ - + + + + - - - + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - + + + \ No newline at end of file diff --git a/Flow.Launcher/App.xaml b/Flow.Launcher/App.xaml index 13d88e1c6..b8e2a1cfe 100644 --- a/Flow.Launcher/App.xaml +++ b/Flow.Launcher/App.xaml @@ -1,236 +1,30 @@ - + - - - - - - - - - + + + + + + + + + + + + + + - + + diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 8c869e941..9ee486b3b 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -100,8 +100,6 @@ namespace Flow.Launcher AutoUpdates(); API.SaveAppAllSettings(); - - _mainVM.MainWindowVisibility = _settings.HideOnStartup ? Visibility.Hidden : Visibility.Visible; Log.Info("|App.OnStartup|End Flow Launcher startup ---------------------------------------------------- "); }); } @@ -178,7 +176,7 @@ namespace Flow.Launcher public void OnSecondAppStarted() { - Current.MainWindow.Visibility = Visibility.Visible; + Current.MainWindow.Show(); } } } \ No newline at end of file diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index a97f90733..4ba55b110 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -1,45 +1,160 @@ - + + + + - + - + - - + - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - + + + \ No newline at end of file diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs index b18bbcfad..ab4047cec 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml.cs @@ -1,12 +1,11 @@ using Flow.Launcher.Core.Resource; using Flow.Launcher.Helper; -using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Infrastructure.UserSettings; -using System; using System.Collections.ObjectModel; using System.Linq; using System.Windows; using System.Windows.Input; +using System.Windows.Controls; namespace Flow.Launcher { @@ -90,14 +89,24 @@ namespace Flow.Launcher private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e) { App.API.ChangeQuery(tbAction.Text); - Application.Current.MainWindow.Visibility = Visibility.Visible; + Application.Current.MainWindow.Show(); + Application.Current.MainWindow.Opacity = 1; Application.Current.MainWindow.Focus(); - } private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e) { Close(); } + + private void window_MouseDown(object sender, MouseButtonEventArgs e) /* for close hotkey popup */ + { + TextBox textBox = Keyboard.FocusedElement as TextBox; + if (textBox != null) + { + TraversalRequest tRequest = new TraversalRequest(FocusNavigationDirection.Next); + textBox.MoveFocus(tRequest); + } + } } } diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index fd2f5f1d9..f431504c2 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -104,6 +104,12 @@ + + + Always + + + diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index fe25ecf18..98327d8da 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -6,8 +6,6 @@ using NHotkey.Wpf; using Flow.Launcher.Core.Resource; using System.Windows; using Flow.Launcher.ViewModel; -using System.Threading.Tasks; -using System.Threading; namespace Flow.Launcher.Helper { @@ -27,7 +25,8 @@ namespace Flow.Launcher.Helper internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) { - mainViewModel.ToggleFlowLauncher(); + if (!mainViewModel.GameModeStatus) + mainViewModel.ToggleFlowLauncher(); } private static void SetHotkey(string hotkeyStr, EventHandler action) @@ -75,10 +74,10 @@ namespace Flow.Launcher.Helper { SetHotkey(hotkey.Hotkey, (s, e) => { - if (mainViewModel.ShouldIgnoreHotkeys()) + if (mainViewModel.ShouldIgnoreHotkeys() || mainViewModel.GameModeStatus) return; - mainViewModel.MainWindowVisibility = Visibility.Visible; + mainViewModel.Show(); mainViewModel.ChangeQueryText(hotkey.ActionKeyword, true); }); } diff --git a/Flow.Launcher/Helper/SingletonWindowOpener.cs b/Flow.Launcher/Helper/SingletonWindowOpener.cs index fdfaaa4fc..8efc9be99 100644 --- a/Flow.Launcher/Helper/SingletonWindowOpener.cs +++ b/Flow.Launcher/Helper/SingletonWindowOpener.cs @@ -10,7 +10,6 @@ namespace Flow.Launcher.Helper { var window = Application.Current.Windows.OfType().FirstOrDefault(x => x.GetType() == typeof(T)) ?? (T)Activator.CreateInstance(typeof(T), args); - Application.Current.MainWindow.Hide(); // Fix UI bug // Add `window.WindowState = WindowState.Normal` diff --git a/Flow.Launcher/HotkeyControl.xaml b/Flow.Launcher/HotkeyControl.xaml index ab786fd56..94cdc6703 100644 --- a/Flow.Launcher/HotkeyControl.xaml +++ b/Flow.Launcher/HotkeyControl.xaml @@ -1,19 +1,55 @@ - + - - + - - + + + + Press key + + + + + \ No newline at end of file diff --git a/Flow.Launcher/Images/gamemode.ico b/Flow.Launcher/Images/gamemode.ico new file mode 100644 index 000000000..27a46a904 Binary files /dev/null and b/Flow.Launcher/Images/gamemode.ico differ diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 051891a2b..15bf48838 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -1,7 +1,8 @@ - - + + Failed to register hotkey: {0} Could not start {0} Invalid Flow Launcher plugin file format @@ -14,8 +15,10 @@ About Exit Close + Game Mode + Suspend the use of Hotkeys. - + Flow Launcher Settings General Portable Mode @@ -33,6 +36,8 @@ Maximum results shown Ignore hotkeys in fullscreen mode Disable Flow Launcher activation when a full screen application is active (Recommended for games). + Default File Manager + Select the file manager to use when opening the folder. Python Directory Auto Update Select @@ -44,31 +49,35 @@ Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese Shadow effect is not allowed while current theme has blur effect enabled - + Plugins Find more plugins On Off + Action keyword Setting Action keyword - Current action keyword: - New action keyword: - Current Priority: - New Priority: + Current action keyword + New action keyword + Current Priority + New Priority Priority Plugin Directory Author: Init time: Query time: + | Version + Website - + Plugin Store Refresh Install - + Theme - Browse for more themes + Theme Gallery + How to create a theme Hi There Query Box Font Result Item Font @@ -78,8 +87,16 @@ Fail to load theme {0}, fallback to default theme Theme Folder Open Theme Folder + Color Scheme + System Default + Light + Dark + Sound Effect + Play a small sound when the search window opens + Animation + Use Animation in UI - + Hotkey Flow Launcher Hotkey Enter shortcut to show/hide Flow Launcher. @@ -96,10 +113,11 @@ Are you sure you want to delete {0} plugin hotkey? Query window shadow effect Shadow effect has a substantial usage of GPU. Not recommended if your computer performance is limited. + Window Width Size Use Segoe Fluent Icons Use Segoe Fluent Icons for query results where supported - + HTTP Proxy Enable HTTP Proxy HTTP Server @@ -115,27 +133,42 @@ Proxy configured correctly Proxy connection failed - + About Website + Github + Docs Version You have activated Flow Launcher {0} times Check for Updates New version {0} is available, would you like to restart Flow Launcher to use the update? Check updates failed, please check your connection and proxy settings to api.github.com. - Download updates failed, please check your connection and proxy settings to github-cloud.s3.amazonaws.com, + Download updates failed, please check your connection and proxy settings to github-cloud.s3.amazonaws.com, or go to https://github.com/Flow-Launcher/Flow.Launcher/releases to download updates manually. Release Notes - Usage Tips: + Usage Tips + DevTools + Setting Folder + Log Folder - + + Select File Manager + Please specify the file location of the file manager you using and add arguments if necessary. The default arguments is "%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 + File Manager Path + Arg For Folder + Arg For File + + Change Priority 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! - + Old Action Keyword New Action Keyword Cancel @@ -145,19 +178,20 @@ This new Action Keyword is already assigned to another plugin, please choose a different one Success Completed successfully - Use * if you don't want to specify an action keyword + Enter the action keyword you need to start the plug-in. Use * if you don't want to specify an action keyword. In the case, The plug-in works without keywords. - - Custom Plugin Hotkey + + Custom Query Hotkey + Press the custom hotkey to automatically insert the specified query. Preview Hotkey is unavailable, please select a new hotkey Invalid plugin hotkey Update - + Hotkey Unavailable - + Version Time Please tell us how application crashed so we can fix it @@ -173,16 +207,18 @@ Failed to send report Flow Launcher got an error - + Please wait... - + Checking for new update You already have the latest Flow Launcher version Update found Updating... - Flow Launcher was not able to move your user profile data to the new update version. - Please manually move your profile data folder from {0} to {1} + + Flow Launcher was not able to move your user profile data to the new update version. + Please manually move your profile data folder from {0} to {1} + New Update New Flow Launcher release {0} is now available An error occurred while trying to install software updates diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml index 649895cea..237d632ae 100644 --- a/Flow.Launcher/Languages/ko.xaml +++ b/Flow.Launcher/Languages/ko.xaml @@ -1,7 +1,8 @@ - - + + 핫키 등록 실패: {0} {0}을 실행할 수 없습니다. Flow Launcher 플러그인 파일 형식이 유효하지 않습니다. @@ -14,8 +15,10 @@ 정보 종료 닫기 + 게임 모드 + 핫키 사용을 일시중단합니다. - + Flow Launcher 설정 일반 포터블 모드 @@ -33,41 +36,48 @@ 표시할 결과 수 전체화면 모드에서는 핫키 무시 게이머라면 켜는 것을 추천합니다. + 기본 파일관리자 + 폴더를 열 때 사용할 파일관리자를 선택하세요. Python 디렉토리 자동 업데이트 선택 시작 시 Flow Launcher 숨김 트레이 아이콘 숨기기 - 쿼리 검색 정확도 - 검색 결과가 좀 더 정확해집니다. + 쿼리 검색 정밀도 + 검색 결과에 필요한 최소 매치 점수를 변경합니다. 항상 Pinyin 사용 Pinyin을 사용하여 검색할 수 있습니다. Pinyin(병음)은 로마자 중국어 입력 방식입니다. 반투명 흐림 효과를 사용하는 경우, 그림자 효과를 쓸 수 없습니다. - + 플러그인 플러그인 더 찾아보기 - On - Off + + 액션 키워드 + 현재 액션 키워드 + 새 액션 키워드 현재 중요도: 새 중요도: 중요도 - 플러그인 디렉토리 - 저자 + 플러그인 폴더 + 제작자 초기화 시간: 쿼리 시간: - - - + | 버전 + 웹사이트 + + + 플러그인 스토어 새로고침 설치 - + 테마 - 테마 더 찾아보기 + 테마 갤러리 + 테마 제작 안내 Hi There 쿼리 상자 글꼴 결과 항목 글꼴 @@ -77,8 +87,16 @@ {0} 테마 로드에 실패했습니다. 기본 테마로 변경합니다. 테마 폴더 테마 폴더 열기 + 앱 색상 + 시스템 기본 + 밝게 + 어둡게 + 소리 효과 + 검색창을 열 때 작은 소리를 재생합니다. + 애니메이션 + 일부 UI에 애니메이션을 사용합니다. - + 핫키 Flow Launcher 핫키 Flow Launcher를 열 때 사용할 단축키를 입력합니다. @@ -87,7 +105,7 @@ 단축키 표시 결과창에서 결과 선택 단축키를 표시합니다. 사용자지정 쿼리 핫키 - Query + 쿼리 삭제 편집 추가 @@ -95,10 +113,11 @@ {0} 플러그인 핫키를 삭제하시겠습니까? 그림자 효과 그림자 효과는 GPU를 사용합니다. 컴퓨터 퍼포먼스가 제한적인 경우 사용을 추천하지 않습니다. + 창 넓이 플루언트 아이콘 사용 결과 및 일부 메뉴에서 플루언트 아이콘을 사용합니다. - + HTTP 프록시 HTTP 프록시 켜기 HTTP 서버 @@ -114,26 +133,41 @@ 프록시 설정 정상 프록시 연결 실패 - + 정보 웹사이트 + Github + 문서 버전 Flow Launcher를 {0}번 실행했습니다. 업데이트 확인 새 버전({0})이 있습니다. Flow Launcher를 재시작하세요. 업데이트 확인을 실패했습니다. api.github.com로의 연결 또는 프록시 설정을 확인해주세요. - 업데이트 다운로드에 실패했습니다. github-cloud.s3.amazonaws.com의 연결 또는 프록시 설정을 확인해주세요. + 업데이트 다운로드에 실패했습니다. github-cloud.s3.amazonaws.com의 연결 또는 프록시 설정을 확인해주세요. 수동 다운로드를 하려면 https://github.com/Flow-Launcher/Flow.Launcher/releases 으로 방문하세요. - 릴리즈 노트: - 사용 팁: + 릴리즈 노트 + 사용 팁 + 개발자도구 + 설정 폴더 + 로그 폴더 - + + 파일관리자 선택 + 사용하려는 파일관리자를 선택하고 필요한 경우 인수를 추가하세요. 기본 인수는 "%d" 이며 해당 위치에 경로가 입력됩니다. 예를들어 "totalcmd.exe /A c:\windows"와 같은 명령이 필요한 경우, 인수는 /A "%d" 입니다. + "%f"는 특정 파일의 경로를 나타냅니다. 파일관리자에서 선택한 파일/폴더의 위치를 강조하는 기능에서 사용됩니다. 이 인수는 "파일경로 인수" 항목에서만 사용할 수 있습니다. 파일관리자에 해당 기능이 없거나 잘 모를 경우 "%d" 인수를 사용할 수 있습니다. + 파일관리자 + 프로필 이름 + 파일관리자 경로 + 폴더경로 인수 + 파일경로 인수 + + 중요도 변경 - 높은 수를 넣을수록 상위 결과에 표시됩니다. 5를 시도해보세요. 다른 플러그인 보다 결과를 낮추고 싶다면, 그보다 낮은 수를 입력하세요. + 높은 수를 넣을수록 상위 결과에 표시됩니다. 5를 시도해보세요. 다른 플러그인 보다 결과를 낮춰 표시하고 싶다면, 그보다 낮은 수를 입력하세요. 중요도에 올바른 정수를 입력하세요. - + 예전 액션 키워드 새 액션 키워드 취소 @@ -143,19 +177,20 @@ 새 액션 키워드가 할당된 플러그인이 이미 있습니다. 다른 액션 키워드를 입력하세요. 성공 성공적으로 완료했습니다. - 액션 키워드를 지정하지 않으려면 *를 사용하세요. + 플러그인을 시작하는데 필요한 액션 키워드를 입력하세요. 액션 키워드를 지정하지 않으려면 *를 사용하세요. 이 경우 키워드를 입력하지 않아도 동작합니다. - + 커스텀 플러그인 핫키 + 단축키를 지정하여 특정 쿼리를 자동으로 입력할 수 있습니다. 사용하고 싶은 단축키를 눌러 지정한 후, 사용할 쿼리를 입력하세요. 미리보기 핫키를 사용할 수 없습니다. 다른 핫키를 입력하세요. 플러그인 핫키가 유효하지 않습니다. 업데이트 - + 핫키를 사용할 수 없습니다. - + 버전 시간 수정을 위해 애플리케이션이 어떻게 충돌했는지 알려주세요. @@ -170,17 +205,19 @@ 보고서를 정상적으로 보냈습니다. 보고서를 보내지 못했습니다. Flow Launcher에 문제가 발생했습니다. - - + + 잠시 기다려주세요... - + 새 업데이트 확인 중 새 Flow Launcher 버전({0})을 사용할 수 있습니다. 이미 가장 최신 버전의 Flow Launcher를 사용중입니다. 업데이트 발견 업데이트 중... - Flow Launcher가 유저 정보 데이터를 새버전으로 옮길 수 없습니다. - 프로필 데이터 폴더를 수동으로 {0} 에서 {1}로 옮겨주세요. + + Flow Launcher가 유저 정보 데이터를 새버전으로 옮길 수 없습니다. + 프로필 데이터 폴더를 수동으로 {0} 에서 {1}로 옮겨주세요. + 새 업데이트 소프트웨어 업데이트를 설치하는 중에 오류가 발생했습니다. 업데이트 diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml index 70a5d3b7c..8c0a96f98 100644 --- a/Flow.Launcher/Languages/sk.xaml +++ b/Flow.Launcher/Languages/sk.xaml @@ -1,7 +1,8 @@ - - + Nepodarilo sa registrovať klávesovú skratku {0} Nepodarilo sa spustiť {0} Neplatný formát súboru pre plugin Flow Launchera @@ -13,51 +14,66 @@ Nastavenia O aplikácii Ukončiť + Zavrieť - + Nastavenia Flow Launchera Všeobecné Prenosný režim + Uloží všetky nastavenia a používateľské údaje do jedného priečinka (Užitočné pri vyberateľných diskoch a cloudových službách). Spustiť Flow Launcher po štarte systému Schovať Flow Launcher po strate fokusu Nezobrazovať upozornenia na novú verziu Zapamätať si posledné umiestnenie Jazyk Posledné vyhľadávanie + Zobrazí/skryje predchádzajúce výsledky pri opätovnej aktivácii Flow Launchera. Ponechať Označiť Vymazať Max. výsledkov Ignorovať klávesové skratky v režime na celú obrazovku + Zakázať aktiváciu Flow Launchera, keď je aktívna aplikácia na celú obrazovku (odporúčané pre hry). + Predvolený správca súborov + Vyberte správcu súborov, ktorý sa má použiť pri otváraní priečinka. Priečinok s Pythonom Automatická aktualizácia Vybrať Schovať Flow Launcher po spustení Schovať ikonu z oblasti oznámení Presnosť vyhľadávania + Mení minimálne skóre zhody potrebné na zobrazenie výsledkov. Použiť Pinyin Umožňuje vyhľadávanie pomocou Pinyin. Pinyin je štandardný systém romanizovaného pravopisu pre transliteráciu čínštiny Efekt tieňa nie je povolený, kým má aktuálny motív povolený efekt rozostrenia - - - Plugin + + + Pluginy Nájsť ďalšie pluginy - Povolené - Zakázané + Zap. + Vyp. + Nastavenie kľúčového slova akcie Skratka akcie Aktuálna akcia skratky: Nová akcia skratky: Aktuálna priorita: Nová priorita: - Priorita: + Priorita Priečinok s pluginmi - Autor + Autor: Príprava: Čas dopytu: + - + + Repozitár pluginov + Obnoviť + Inštalovať + + Motív - Prehliadať viac motívov + Galéria motívov + Ako vytvoriť motív Ahojte Písmo vyhľadávacieho poľa Písmo výsledkov @@ -65,12 +81,17 @@ Nepriehľadnosť Motív {0} neexistuje, návrat na predvolený motív Nepodarilo sa nečítať motív {0}, návrat na predvolený motív + Priečinok s motívmi + Otvoriť priečinok s motívmi - + Klávesové skratky Klávesová skratka pre Flow Launcher - Modifikáčné klávesy na otvorenie výsledkov + Zadajte skratku na zobrazenie/skrytie Flow Launchera. + Modifikačný kláves na otvorenie výsledkov + Vyberte modifikačný kláves na otvorenie vybraného výsledku pomocou klávesnice. Zobraziť klávesovú skratku + Zobrazí klávesovú skratku spolu s výsledkami. Vlastná klávesová skratka na vyhľadávanie Dopyt Odstrániť @@ -80,15 +101,16 @@ Ste si istý, že chcete odstrániť klávesovú skratku {0} pre plugin? Tieňový efekt v poli vyhľadávania Tieňový efekt významne využíva GPU. Neodporúča sa, ak je výkon počítača obmedzený. + Veľkosť šírky okna Použiť ikony Segoe Fluent Použiť ikony Segoe Fluent, ak sú podporované - + HTTP Proxy Povoliť HTTP Proxy HTTP Server Port - Použív. meno + Používateľské meno Heslo Test Proxy Uložiť @@ -99,7 +121,7 @@ Nastavenie proxy je v poriadku Pripojenie proxy zlyhalo - + O aplikácii Webstránka Verzia @@ -108,17 +130,28 @@ Je dostupná nová verzia {0}, chcete reštartovať Flow Launcher, aby sa mohol aktualizovať? Kontrola aktualizácií zlyhala, prosím, skontrolujte pripojenie na internet a nastavenie proxy k api.github.com. - Sťahovanie aktualizácií zlyhalo, skontrolujte pripojenie na internet a nastavenie proxy k github-cloud.s3.amazonaws.com, + Sťahovanie aktualizácií zlyhalo, skontrolujte pripojenie na internet a nastavenie proxy k github-cloud.s3.amazonaws.com, alebo prejdite na https://github.com/Flow-Launcher/Flow.Launcher/releases pre manuálne stiahnutie aktualizácie. Poznámky k vydaniu Tipy na používanie: - + + Vyberte správcu súborov + Zadajte umiestnenie súboru správcu súborov, ktorého používate, a v prípade potreby pridajte argumenty. Predvolené argumenty sú "%d" a cesta sa zadáva na tomto mieste. Napríklad, ak sa vyžaduje príkaz, ako napríklad "totalcmd.exe /A c:\windows", argument je /A "%d". + "%f" je argument, ktorý predstavuje cestu k súboru. Používa sa na zvýraznenie názvu súboru/priečinka pri otváraní konkrétneho umiestnenia súboru v správcovi súborov tretej strany. Tento argument je k dispozícii len v položke "Arg pre súbor". Ak správca súborov nemá túto funkciu, môžete použiť "%d". + Správca súborov + Názov profilu + Cesta k správcovi súborov + Arg. pre priečinok + Arg. pre súbor + + + Zmena priority Vyššie číslo znamená, že výsledok bude vyššie. Skúste nastaviť napr. 5. Ak chcete, aby boli výsledky nižšie ako ktorékoľvek iné doplnky, zadajte záporné číslo Prosím, zadajte platné číslo pre prioritu! - + Stará skratka akcie Nová skratka akcie Zrušiť @@ -128,19 +161,20 @@ Nová skratka pre akciu bola priradená pre iný plugin, prosím, zvoľte inú skratku Úspešné Úspešne dokončené - Použite * ak nechcete určiť skratku pre akciu + Zadajte skratku akcie, ktorá je potrebná na spustenie pluginu. Ak nechcete zadať skratku akcie, použite *. V tom prípade plugin funguje bez kľúčových slov. - - Vlastná klávesová skratka pre plugin + + Klávesová skratka pre vlastné vyhľadávanie + Stlačením klávesovej skratky sa automaticky vloží zadaný výraz. Náhľad Klávesová skratka je nedostupná, prosím, zadajte novú Neplatná klávesová skratka pluginu Aktualizovať - + Klávesová skratka nedostupná - + Verzia Čas Prosím, napíšte nám, ako došlo k pádu aplikácie, aby sme to mohli opraviť @@ -156,16 +190,18 @@ Odoslanie hlásenia zlyhalo Flow Launcher zaznamenal chybu - + Čakajte, prosím… - - Kontrolujú sa akutalizácie - Už máte najnovšiu verizu Flow Launchera + + Kontrolujú sa aktualizácie + Už máte najnovšiu verziu Flow Launchera Bola nájdená aktualizácia Aktualizuje sa… - Flow Launcher nedokázal presunúť používateľské údaje do aktualizovanej verzie. - Prosím, presuňte profilový priečinok data z {0} do {1} + + Flow Launcher nedokázal presunúť používateľské údaje do aktualizovanej verzie. + Prosím, presuňte profilový priečinok data z {0} do {1} + Nová aktualizácia Je dostupná nová verzia Flow Launchera {0} Počas inštalácie aktualizácií došlo k chybe diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index b517faf19..dd8979650 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -1,149 +1,284 @@ - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - + + - + - - - - - - + + + + + + - + - - - + + + + + + + - - - + + + - + - - - + + + - - - + + + - + - - - + + + - - - - + + + + diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 057c3f07d..ba0b63a52 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; @@ -11,14 +11,12 @@ using Flow.Launcher.Core.Resource; using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.ViewModel; -using Application = System.Windows.Application; using Screen = System.Windows.Forms.Screen; using ContextMenuStrip = System.Windows.Forms.ContextMenuStrip; -using DataFormats = System.Windows.DataFormats; using DragEventArgs = System.Windows.DragEventArgs; using KeyEventArgs = System.Windows.Input.KeyEventArgs; -using MessageBox = System.Windows.MessageBox; using NotifyIcon = System.Windows.Forms.NotifyIcon; +using Flow.Launcher.Infrastructure; namespace Flow.Launcher { @@ -30,7 +28,9 @@ namespace Flow.Launcher private bool isProgressBarStoryboardPaused; private Settings _settings; private NotifyIcon _notifyIcon; + private ContextMenu contextMenu; private MainViewModel _viewModel; + private bool _animating; #endregion @@ -40,6 +40,7 @@ namespace Flow.Launcher _viewModel = mainVM; _settings = settings; InitializeComponent(); + InitializePosition(); } public MainWindow() @@ -49,11 +50,13 @@ namespace Flow.Launcher private async void OnClosing(object sender, CancelEventArgs e) { + _settings.WindowTop = Top; + _settings.WindowLeft = Left; _notifyIcon.Visible = false; _viewModel.Save(); e.Cancel = true; await PluginManager.DisposePluginsAsync(); - Application.Current.Shutdown(); + Environment.Exit(0); } private void OnInitialized(object sender, EventArgs e) @@ -62,12 +65,12 @@ namespace Flow.Launcher private void OnLoaded(object sender, RoutedEventArgs _) { + HideStartup(); // show notify icon when flowlauncher is hidden InitializeNotifyIcon(); - + InitializeColorScheme(); WindowsInteropHelper.DisableControlBox(this); InitProgressbarAnimation(); - InitializePosition(); // since the default main window visibility is visible // so we need set focus during startup QueryTextBox.Focus(); @@ -76,13 +79,13 @@ namespace Flow.Launcher { switch (e.PropertyName) { - case nameof(MainViewModel.MainWindowVisibility): + case nameof(MainViewModel.MainWindowVisibilityStatus): { - if (_viewModel.MainWindowVisibility == Visibility.Visible) + if (_viewModel.MainWindowVisibilityStatus) { + UpdatePosition(); Activate(); QueryTextBox.Focus(); - UpdatePosition(); _settings.ActivateTimes++; if (!_viewModel.LastQuerySelected) { @@ -114,7 +117,7 @@ namespace Flow.Launcher _progressBarStoryboard.Stop(ProgressBar); isProgressBarStoryboardPaused = true; } - else if (_viewModel.MainWindowVisibility == Visibility.Visible && + else if (_viewModel.MainWindowVisibilityStatus && isProgressBarStoryboardPaused) { _progressBarStoryboard.Begin(ProgressBar, true); @@ -145,28 +148,29 @@ namespace Flow.Launcher break; } }; - - InitializePosition(); } private void InitializePosition() { - Top = WindowTop(); - Left = WindowLeft(); - _settings.WindowTop = Top; - _settings.WindowLeft = Left; + if (_settings.RememberLastLaunchLocation) + { + Top = _settings.WindowTop; + Left = _settings.WindowLeft; + } + else + { + Left = WindowLeft(); + Top = WindowTop(); + } } private void UpdateNotifyIconText() { - var menu = _notifyIcon.ContextMenuStrip; - var open = menu.Items[0]; - var setting = menu.Items[1]; - var exit = menu.Items[2]; - - open.Text = InternationalizationManager.Instance.GetTranslation("iconTrayOpen"); - setting.Text = InternationalizationManager.Instance.GetTranslation("iconTraySettings"); - exit.Text = InternationalizationManager.Instance.GetTranslation("iconTrayExit"); + var menu = contextMenu; + ((MenuItem)menu.Items[1]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen"); + ((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"); } private void InitializeNotifyIcon() @@ -177,34 +181,70 @@ namespace Flow.Launcher Icon = Properties.Resources.app, Visible = !_settings.HideNotifyIcon }; - var menu = new ContextMenuStrip(); - var items = menu.Items; + contextMenu = new ContextMenu(); - var open = items.Add(InternationalizationManager.Instance.GetTranslation("iconTrayOpen")); - open.Click += (o, e) => Visibility = Visibility.Visible; - var setting = items.Add(InternationalizationManager.Instance.GetTranslation("iconTraySettings")); - setting.Click += (o, e) => App.API.OpenSettingDialog(); - var exit = items.Add(InternationalizationManager.Instance.GetTranslation("iconTrayExit")); + var header = new MenuItem + { + Header = "Flow Launcher", + IsEnabled = false + }; + var open = new MenuItem + { + Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + }; + var gamemode = new MenuItem + { + Header = InternationalizationManager.Instance.GetTranslation("GameMode") + }; + var settings = new MenuItem + { + Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings") + }; + var exit = new MenuItem + { + Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit") + }; + + open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); + gamemode.Click += (o, e) => ToggleGameMode(); + 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"); + contextMenu.Items.Add(gamemode); + contextMenu.Items.Add(settings); + contextMenu.Items.Add(exit); - _notifyIcon.ContextMenuStrip = menu; + _notifyIcon.ContextMenuStrip = new ContextMenuStrip(); // it need for close the context menu. if not, context menu can't close. _notifyIcon.MouseClick += (o, e) => { - if (e.Button == MouseButtons.Left) + switch (e.Button) { - if (menu.Visible) - { - menu.Close(); - } - else - { - var p = System.Windows.Forms.Cursor.Position; - menu.Show(p); - } + case MouseButtons.Left: + _viewModel.ToggleFlowLauncher(); + break; + + case MouseButtons.Right: + contextMenu.IsOpen = true; + break; } }; } + private void ToggleGameMode() + { + if (_viewModel.GameModeStatus) + { + _notifyIcon.Icon = Properties.Resources.app; + _viewModel.GameModeStatus = false; + } + else + { + _notifyIcon.Icon = Properties.Resources.gamemode; + _viewModel.GameModeStatus = true; + } + } private void InitProgressbarAnimation() { var da = new DoubleAnimation(ProgressBar.X2, ActualWidth + 150, @@ -220,6 +260,54 @@ namespace Flow.Launcher isProgressBarStoryboardPaused = true; } + public void WindowAnimator() + { + if (_animating) + return; + + _animating = true; + UpdatePosition(); + Storyboard sb = new Storyboard(); + Storyboard iconsb = new Storyboard(); + CircleEase easing = new CircleEase(); // or whatever easing class you want + easing.EasingMode = EasingMode.EaseInOut; + var da = new DoubleAnimation + { + From = 0, + To = 1, + Duration = TimeSpan.FromSeconds(0.25), + FillBehavior = FillBehavior.Stop + }; + + var da2 = new DoubleAnimation + { + From = Top + 10, + To = Top, + Duration = TimeSpan.FromSeconds(0.25), + FillBehavior = FillBehavior.Stop + }; + var da3 = new DoubleAnimation + { + From = 12, + To = 0, + EasingFunction = easing, + Duration = TimeSpan.FromSeconds(0.36), + FillBehavior = FillBehavior.Stop + }; + Storyboard.SetTarget(da, this); + Storyboard.SetTargetProperty(da, new PropertyPath(Window.OpacityProperty)); + Storyboard.SetTargetProperty(da2, new PropertyPath(Window.TopProperty)); + Storyboard.SetTargetProperty(da3, new PropertyPath(TopProperty)); + sb.Children.Add(da); + sb.Children.Add(da2); + iconsb.Children.Add(da3); + sb.Completed += (_, _) => _animating = false; + _settings.WindowLeft = Left; + _settings.WindowTop = Top; + iconsb.Begin(SearchIcon); + sb.Begin(FlowMainWindow); + } + private void OnMouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) DragMove(); @@ -252,22 +340,41 @@ namespace Flow.Launcher e.Handled = true; } - private void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e) + private async void OnContextMenusForSettingsClick(object sender, RoutedEventArgs e) { + _viewModel.Hide(); + + if(_settings.UseAnimation) + await Task.Delay(100); + App.API.OpenSettingDialog(); } - private void OnDeactivated(object sender, EventArgs e) + private async void OnDeactivated(object sender, EventArgs e) { - if (_settings.HideWhenDeactive) + //This condition stops extra hide call when animator is on, + // which causes the toggling to occasional hide instead of show. + if (_viewModel.MainWindowVisibilityStatus) { - _viewModel.Hide(); + // Need time to initialize the main query window animation. + // This also stops the mainwindow from flickering occasionally after Settings window is opened + // and always after Settings window is closed. + if (_settings.UseAnimation) + await Task.Delay(100); + + if (_settings.HideWhenDeactive) + { + _viewModel.Hide(); + } } } private void UpdatePosition() { + if (_animating) + return; + if (_settings.RememberLastLaunchLocation) { Left = _settings.WindowLeft; @@ -282,6 +389,8 @@ namespace Flow.Launcher private void OnLocationChanged(object sender, EventArgs e) { + if (_animating) + return; if (_settings.RememberLastLaunchLocation) { _settings.WindowLeft = Left; @@ -289,7 +398,20 @@ namespace Flow.Launcher } } - private double WindowLeft() + public void HideStartup() + { + UpdatePosition(); + if (_settings.HideOnStartup) + { + _viewModel.Hide(); + } + else + { + _viewModel.Show(); + } + } + + public double WindowLeft() { var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, screen.WorkingArea.X, 0); @@ -298,7 +420,7 @@ namespace Flow.Launcher return left; } - private double WindowTop() + public double WindowTop() { var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position); var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y); @@ -357,5 +479,17 @@ namespace Flow.Launcher { QueryTextBox.CaretIndex = QueryTextBox.Text.Length; } + + public void InitializeColorScheme() + { + if (_settings.ColorScheme == Constant.Light) + { + ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Light; + } + else if (_settings.ColorScheme == Constant.Dark) + { + ModernWpf.ThemeManager.Current.ApplicationTheme = ModernWpf.ApplicationTheme.Dark; + } + } } } \ No newline at end of file diff --git a/Flow.Launcher/Notification.cs b/Flow.Launcher/Notification.cs index 2c82e1451..d8f9fd45e 100644 --- a/Flow.Launcher/Notification.cs +++ b/Flow.Launcher/Notification.cs @@ -11,8 +11,8 @@ namespace Flow.Launcher [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "")] public static void Show(string title, string subTitle, string iconPath) { - var legacy = Environment.OSVersion.Version.Major < 10; - // Handle notification for win7/8 + var legacy = Environment.OSVersion.Version.Build < 19041; + // Handle notification for win7/8/early win10 if (legacy) { LegacyShow(title, subTitle, iconPath); diff --git a/Flow.Launcher/PriorityChangeWindow.xaml b/Flow.Launcher/PriorityChangeWindow.xaml index 60a289e61..8fb27c470 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml +++ b/Flow.Launcher/PriorityChangeWindow.xaml @@ -1,59 +1,124 @@ - + + + + - - - + - - - - - -  - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - + + + + - - - - - - - - + + + diff --git a/Flow.Launcher/PriorityChangeWindow.xaml.cs b/Flow.Launcher/PriorityChangeWindow.xaml.cs index 0adb1f080..fe846e78b 100644 --- a/Flow.Launcher/PriorityChangeWindow.xaml.cs +++ b/Flow.Launcher/PriorityChangeWindow.xaml.cs @@ -26,7 +26,6 @@ namespace Flow.Launcher private Settings settings; private readonly Internationalization translater = InternationalizationManager.Instance; private readonly PluginViewModel pluginViewModel; - public PriorityChangeWindow(string pluginId, Settings settings, PluginViewModel pluginViewModel) { InitializeComponent(); @@ -62,8 +61,17 @@ namespace Flow.Launcher private void PriorityChangeWindow_Loaded(object sender, RoutedEventArgs e) { - OldPriority.Text = pluginViewModel.Priority.ToString(); + tbAction.Text = pluginViewModel.Priority.ToString(); tbAction.Focus(); } + private void window_MouseDown(object sender, MouseButtonEventArgs e) /* for close hotkey popup */ + { + TextBox textBox = Keyboard.FocusedElement as TextBox; + if (textBox != null) + { + TraversalRequest tRequest = new TraversalRequest(FocusNavigationDirection.Next); + textBox.MoveFocus(tRequest); + } + } } } \ No newline at end of file diff --git a/Flow.Launcher/Properties/Resources.Designer.cs b/Flow.Launcher/Properties/Resources.Designer.cs index a19716073..43927cb98 100644 --- a/Flow.Launcher/Properties/Resources.Designer.cs +++ b/Flow.Launcher/Properties/Resources.Designer.cs @@ -69,5 +69,13 @@ namespace Flow.Launcher.Properties { return ((System.Drawing.Icon)(obj)); } } + internal static System.Drawing.Icon gamemode + { + get + { + object obj = ResourceManager.GetObject("gamemode", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } } } diff --git a/Flow.Launcher/Properties/Resources.resx b/Flow.Launcher/Properties/Resources.resx index 3ae43d89b..2126cb185 100644 --- a/Flow.Launcher/Properties/Resources.resx +++ b/Flow.Launcher/Properties/Resources.resx @@ -112,13 +112,16 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + ..\Resources\app.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Images\gamemode.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 71f6f1be4..49788bfac 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; @@ -14,6 +14,7 @@ using Flow.Launcher.Infrastructure.Image; using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; using Flow.Launcher.Plugin.SharedModels; +using Flow.Launcher.Plugin.SharedCommands; using System.Threading; using System.IO; using Flow.Launcher.Infrastructure.Http; @@ -22,6 +23,8 @@ using System.Runtime.CompilerServices; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; using System.Collections.Concurrent; +using Flow.Launcher.Plugin.SharedCommands; +using System.Diagnostics; namespace Flow.Launcher { @@ -53,7 +56,7 @@ namespace Flow.Launcher public void RestartApp() { - _mainVM.MainWindowVisibility = Visibility.Hidden; + _mainVM.Hide(); // we must manually save // UpdateManager.RestartApp() will call Environment.Exit(0) @@ -68,7 +71,7 @@ namespace Flow.Launcher public void RestarApp() => RestartApp(); - public void ShowMainWindow() => _mainVM.MainWindowVisibility = Visibility.Visible; + public void ShowMainWindow() => _mainVM.Show(); public void CheckForNewUpdate() => _settingsVM.UpdateApp(); @@ -104,6 +107,14 @@ namespace Flow.Launcher }); } + public void ShellRun(string cmd, string filename = "cmd.exe") + { + var args = filename == "cmd.exe" ? $"/C {cmd}" : $"{cmd}"; + + var startInfo = ShellCommand.SetProcessStartInfo(filename, arguments: args, createNoWindow: true); + ShellCommand.Execute(startInfo); + } + public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible; public void StopLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Collapsed; @@ -158,7 +169,7 @@ namespace Flow.Launcher if (!_pluginJsonStorages.ContainsKey(type)) _pluginJsonStorages[type] = new PluginJsonStorage(); - return ((PluginJsonStorage) _pluginJsonStorages[type]).Load(); + return ((PluginJsonStorage)_pluginJsonStorages[type]).Load(); } public void SaveSettingJsonStorage() where T : new() @@ -167,7 +178,7 @@ namespace Flow.Launcher if (!_pluginJsonStorages.ContainsKey(type)) _pluginJsonStorages[type] = new PluginJsonStorage(); - ((PluginJsonStorage) _pluginJsonStorages[type]).Save(); + ((PluginJsonStorage)_pluginJsonStorages[type]).Save(); } public void SaveJsonStorage(T settings) where T : new() @@ -175,7 +186,22 @@ namespace Flow.Launcher var type = typeof(T); _pluginJsonStorages[type] = new PluginJsonStorage(settings); - ((PluginJsonStorage) _pluginJsonStorages[type]).Save(); + ((PluginJsonStorage)_pluginJsonStorages[type]).Save(); + } + + public void OpenDirectory(string DirectoryPath, string FileName = null) + { + using Process explorer = new Process(); + var explorerInfo = _settingsVM.Settings.CustomExplorer; + explorer.StartInfo = new ProcessStartInfo + { + FileName = explorerInfo.Path, + Arguments = FileName is null ? + explorerInfo.DirectoryArgument.Replace("%d", DirectoryPath) : + explorerInfo.FileArgument.Replace("%d", DirectoryPath).Replace("%f", + Path.IsPathRooted(FileName) ? FileName : Path.Combine(DirectoryPath, FileName)) + }; + explorer.Start(); } public event FlowLauncherGlobalKeyboardEventHandler GlobalKeyboardEvent; @@ -188,7 +214,7 @@ namespace Flow.Launcher { if (GlobalKeyboardEvent != null) { - return GlobalKeyboardEvent((int) keyevent, vkcode, state); + return GlobalKeyboardEvent((int)keyevent, vkcode, state); } return true; diff --git a/Flow.Launcher/Resources/CustomControlTemplate.xaml b/Flow.Launcher/Resources/CustomControlTemplate.xaml new file mode 100644 index 000000000..dd9dba391 --- /dev/null +++ b/Flow.Launcher/Resources/CustomControlTemplate.xaml @@ -0,0 +1,2755 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0,0,0,4 + 12,6,0,6 + 11,5,32,6 + 32 + + + + + + + + + + + + + + + 0,0,0,4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Flow.Launcher/SelectFileManagerWindow.xaml.cs b/Flow.Launcher/SelectFileManagerWindow.xaml.cs new file mode 100644 index 000000000..4f6fb3439 --- /dev/null +++ b/Flow.Launcher/SelectFileManagerWindow.xaml.cs @@ -0,0 +1,88 @@ +using Flow.Launcher.Infrastructure.UserSettings; +using Flow.Launcher.ViewModel; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Flow.Launcher +{ + public partial class SelectFileManagerWindow : Window, INotifyPropertyChanged + { + private int selectedCustomExplorerIndex; + + public event PropertyChangedEventHandler PropertyChanged; + + public Settings Settings { get; } + + public int SelectedCustomExplorerIndex + { + get => selectedCustomExplorerIndex; set + { + selectedCustomExplorerIndex = value; + PropertyChanged?.Invoke(this, new(nameof(CustomExplorer))); + } + } + public ObservableCollection CustomExplorers { get; set; } + + public CustomExplorerViewModel CustomExplorer => CustomExplorers[SelectedCustomExplorerIndex]; + public SelectFileManagerWindow(Settings settings) + { + Settings = settings; + CustomExplorers = new ObservableCollection(Settings.CustomExplorerList.Select(x => x.Copy())); + SelectedCustomExplorerIndex = Settings.CustomExplorerIndex; + InitializeComponent(); + } + + private void btnCancel_Click(object sender, RoutedEventArgs e) + { + Close(); + } + + private void btnDone_Click(object sender, RoutedEventArgs e) + { + Settings.CustomExplorerList = CustomExplorers.ToList(); + Settings.CustomExplorerIndex = SelectedCustomExplorerIndex; + Close(); + } + + private void btnAdd_Click(object sender, RoutedEventArgs e) + { + CustomExplorers.Add(new() + { + Name = "New Profile" + }); + SelectedCustomExplorerIndex = CustomExplorers.Count - 1; + } + + private void btnDelete_Click(object sender, RoutedEventArgs e) + { + CustomExplorers.RemoveAt(SelectedCustomExplorerIndex--); + } + + private void btnBrowseFile_Click(object sender, RoutedEventArgs e) + { + Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); + Nullable result = dlg.ShowDialog(); + + if (result == true) + { + TextBox path = (TextBox)(((FrameworkElement)sender).Parent as FrameworkElement).FindName("PathTextBox"); + path.Text = dlg.FileName; + path.Focus(); + ((Button)sender).Focus(); + } + } + } +} diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index ac6138644..4fac9ec27 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -1,27 +1,34 @@ - + + + + @@ -31,7 +38,7 @@ - + @@ -45,9 +52,15 @@ - + - + @@ -55,51 +68,64 @@ - - - + + - + - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - -  + + + + + + + + + + + + + + + + + + + + + + + +  - - + + + - - - - - - - -  - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - -  - - - - - - - - - - - -  - - - - - - - - - - - - -  - - - - - - - + - - + - - - - - - - - - - - - - - - - - - - -  + + +  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + - - -  + + +  - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - + + + + + + + + + - + + + + + + + + - + + + + + + + + +  + + + - + + + + + + + + +  + + + + + + + + + + + +  + + + + + + + + + + + + +  + + + + + - + - - + + - - -  - + - - + + - - - - - - - - - - - - - - - - + + + - -  - + - - + + - - - - - - - - - - - - - - - - - - + + + @@ -1273,313 +793,1721 @@ - - - - - - - - -  - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - - - - + - - + + + + + + + + + + + + + + + + + + + +  + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - -