Merge branch 'dev' into plugin_settings_cache_path

This commit is contained in:
Jack Ye 2025-03-01 08:51:06 +08:00 committed by GitHub
commit 7975ab5882
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 780 additions and 396 deletions

View file

@ -9,11 +9,15 @@ using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.SharedCommands;
using System.Linq;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.Configuration
{
public class Portable : IPortable
{
private readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
/// <summary>
/// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish
/// </summary>
@ -40,7 +44,7 @@ namespace Flow.Launcher.Core.Configuration
#endif
IndicateDeletion(DataLocation.PortableDataPath);
MessageBoxEx.Show("Flow Launcher needs to restart to finish disabling portable mode, " +
API.ShowMsgBox("Flow Launcher needs to restart to finish disabling portable mode, " +
"after the restart your portable data profile will be deleted and roaming data profile kept");
UpdateManager.RestartApp(Constant.ApplicationFileName);
@ -64,7 +68,7 @@ namespace Flow.Launcher.Core.Configuration
#endif
IndicateDeletion(DataLocation.RoamingDataPath);
MessageBoxEx.Show("Flow Launcher needs to restart to finish enabling portable mode, " +
API.ShowMsgBox("Flow Launcher needs to restart to finish enabling portable mode, " +
"after the restart your roaming data profile will be deleted and portable data profile kept");
UpdateManager.RestartApp(Constant.ApplicationFileName);
@ -95,13 +99,13 @@ namespace Flow.Launcher.Core.Configuration
public void MoveUserDataFolder(string fromLocation, string toLocation)
{
FilesFolders.CopyAll(fromLocation, toLocation, MessageBoxEx.Show);
FilesFolders.CopyAll(fromLocation, toLocation, (s) => API.ShowMsgBox(s));
VerifyUserDataAfterMove(fromLocation, toLocation);
}
public void VerifyUserDataAfterMove(string fromLocation, string toLocation)
{
FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, MessageBoxEx.Show);
FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation, (s) => API.ShowMsgBox(s));
}
public void CreateShortcuts()
@ -157,13 +161,13 @@ namespace Flow.Launcher.Core.Configuration
// delete it and prompt the user to pick the portable data location
if (File.Exists(roamingDataDeleteFilePath))
{
FilesFolders.RemoveFolderIfExists(roamingDataDir, MessageBoxEx.Show);
FilesFolders.RemoveFolderIfExists(roamingDataDir, (s) => API.ShowMsgBox(s));
if (MessageBoxEx.Show("Flow Launcher has detected you enabled portable mode, " +
if (API.ShowMsgBox("Flow Launcher has detected you enabled portable mode, " +
"would you like to move it to a different location?", string.Empty,
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
FilesFolders.OpenPath(Constant.RootDirectory, MessageBoxEx.Show);
FilesFolders.OpenPath(Constant.RootDirectory, (s) => API.ShowMsgBox(s));
Environment.Exit(0);
}
@ -172,9 +176,9 @@ namespace Flow.Launcher.Core.Configuration
// delete it and notify the user about it.
else if (File.Exists(portableDataDeleteFilePath))
{
FilesFolders.RemoveFolderIfExists(portableDataDir, MessageBoxEx.Show);
FilesFolders.RemoveFolderIfExists(portableDataDir, (s) => API.ShowMsgBox(s));
MessageBoxEx.Show("Flow Launcher has detected you disabled portable mode, " +
API.ShowMsgBox("Flow Launcher has detected you disabled portable mode, " +
"the relevant shortcuts and uninstaller entry have been created");
}
}
@ -186,7 +190,7 @@ namespace Flow.Launcher.Core.Configuration
if (roamingLocationExists && portableLocationExists)
{
MessageBoxEx.Show(string.Format("Flow Launcher detected your user data exists both in {0} and " +
API.ShowMsgBox(string.Format("Flow Launcher detected your user data exists both in {0} and " +
"{1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.",
DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine));

View file

@ -9,11 +9,14 @@ using System.Linq;
using System.Windows;
using System.Windows.Forms;
using Flow.Launcher.Core.Resource;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Core.ExternalPlugins.Environments
{
public abstract class AbstractPluginEnvironment
{
protected readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
internal abstract string Language { get; }
internal abstract string EnvName { get; }
@ -26,7 +29,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal virtual string FileDialogFilter => string.Empty;
internal abstract string PluginsSettingsFilePath { get; set; }
internal abstract string PluginsSettingsFilePath { get; set; }
internal List<PluginMetadata> PluginMetadataList;
@ -58,7 +61,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
EnvName,
Environment.NewLine
);
if (MessageBoxEx.Show(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
if (API.ShowMsgBox(noRuntimeMessage, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
{
var msg = string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginChooseRuntimeExecutable"), EnvName);
string selectedFile;
@ -83,7 +86,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
}
else
{
MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
API.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
Log.Error("PluginsLoader",
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
$"{Language}Environment");
@ -99,7 +102,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
if (expectedPath == currentPath)
return;
FilesFolders.RemoveFolderIfExists(installedDirPath, MessageBoxEx.Show);
FilesFolders.RemoveFolderIfExists(installedDirPath, (s) => API.ShowMsgBox(s));
InstallEnvironment();

View file

@ -28,7 +28,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal override void InstallEnvironment()
{
FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show);
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
// Python 3.11.4 is no longer Windows 7 compatible. If user is on Win 7 and
// uses Python plugin they need to custom install and use v3.8.9

View file

@ -25,7 +25,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal override void InstallEnvironment()
{
FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show);
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();

View file

@ -25,7 +25,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
internal override void InstallEnvironment()
{
FilesFolders.RemoveFolderIfExists(InstallPath, MessageBoxEx.Show);
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();

View file

@ -14,6 +14,7 @@ using ISavable = Flow.Launcher.Plugin.ISavable;
using Flow.Launcher.Plugin.SharedCommands;
using System.Text.Json;
using Flow.Launcher.Core.Resource;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Core.Plugin
{
@ -28,7 +29,9 @@ namespace Flow.Launcher.Core.Plugin
public static readonly HashSet<PluginPair> GlobalPlugins = new();
public static readonly Dictionary<string, PluginPair> NonGlobalPlugins = new();
public static IPublicAPI API { private set; get; }
// We should not initialize API in static constructor because it will create another API instance
private static IPublicAPI api = null;
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
private static PluginsSettings Settings;
private static List<PluginMetadata> _metadatas;
@ -186,9 +189,8 @@ namespace Flow.Launcher.Core.Plugin
/// Call initialize for all plugins
/// </summary>
/// <returns>return the list of failed to init plugins or null for none</returns>
public static async Task InitializePluginsAsync(IPublicAPI api)
public static async Task InitializePluginsAsync()
{
API = api;
var failedPlugins = new ConcurrentQueue<PluginPair>();
var InitTasks = AllPlugins.Select(pair => Task.Run(async delegate
@ -232,7 +234,7 @@ namespace Flow.Launcher.Core.Plugin
}
InternationalizationManager.Instance.AddPluginLanguageDirectories(GetPluginsForInterface<IPluginI18n>());
InternationalizationManager.Instance.ChangeLanguage(InternationalizationManager.Instance.Settings.Language);
InternationalizationManager.Instance.ChangeLanguage(Ioc.Default.GetRequiredService<Settings>().Language);
if (failedPlugins.Any())
{
@ -545,7 +547,7 @@ namespace Flow.Launcher.Core.Plugin
var newPluginPath = Path.Combine(installDirectory, folderName);
FilesFolders.CopyAll(pluginFolderPath, newPluginPath, MessageBoxEx.Show);
FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => API.ShowMsgBox(s));
try
{

View file

@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core.ExternalPlugins.Environments;
#pragma warning disable IDE0005
using Flow.Launcher.Infrastructure.Logger;
@ -122,7 +123,7 @@ namespace Flow.Launcher.Core.Plugin
_ = Task.Run(() =>
{
MessageBoxEx.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsgBox($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" +
$"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" +
$"Please refer to the logs for more information", "",
MessageBoxButton.OK, MessageBoxImage.Warning);

View file

@ -11,22 +11,24 @@ using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using System.Globalization;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Core.Resource
{
public class Internationalization
{
public Settings Settings { get; set; }
private const string Folder = "Languages";
private const string DefaultLanguageCode = "en";
private const string DefaultFile = "en.xaml";
private const string Extension = ".xaml";
private readonly Settings _settings;
private readonly List<string> _languageDirectories = new List<string>();
private readonly List<ResourceDictionary> _oldResources = new List<ResourceDictionary>();
private readonly string SystemLanguageCode;
public Internationalization()
public Internationalization(Settings settings)
{
_settings = settings;
AddFlowLauncherLanguageDirectory();
SystemLanguageCode = GetSystemLanguageCodeAtStartup();
}
@ -141,7 +143,7 @@ namespace Flow.Launcher.Core.Resource
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture;
// Raise event after culture is set
Settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
_settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
_ = Task.Run(() =>
{
UpdatePluginMetadataTranslations();
@ -152,7 +154,7 @@ namespace Flow.Launcher.Core.Resource
{
var languageToSet = GetLanguageByLanguageCode(languageCodeToSet);
if (Settings.ShouldUsePinyin)
if (_settings.ShouldUsePinyin)
return false;
if (languageToSet != AvailableLanguages.Chinese && languageToSet != AvailableLanguages.Chinese_TW)
@ -162,7 +164,7 @@ namespace Flow.Launcher.Core.Resource
// "Do you want to search with pinyin?"
string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?" ;
if (MessageBoxEx.Show(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
if (Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
return false;
return true;

View file

@ -1,26 +1,12 @@
namespace Flow.Launcher.Core.Resource
using System;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Core.Resource
{
[Obsolete("InternationalizationManager.Instance is obsolete. Use Ioc.Default.GetRequiredService<Internationalization>() instead.")]
public static class InternationalizationManager
{
private static Internationalization instance;
private static object syncObject = new object();
public static Internationalization Instance
{
get
{
if (instance == null)
{
lock (syncObject)
{
if (instance == null)
{
instance = new Internationalization();
}
}
}
return instance;
}
}
=> Ioc.Default.GetRequiredService<Internationalization>();
}
}
}

View file

@ -8,10 +8,12 @@ using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Shell;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using System.Windows.Shell;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.Core.Resource
{
@ -23,10 +25,11 @@ namespace Flow.Launcher.Core.Resource
private const int ShadowExtraMargin = 32;
private readonly List<string> _themeDirectories = new List<string>();
private readonly IPublicAPI _api;
private readonly Settings _settings;
private readonly List<string> _themeDirectories = new();
private ResourceDictionary _oldResource;
private string _oldTheme;
public Settings Settings { get; set; }
private const string Folder = Constant.Themes;
private const string Extension = ".xaml";
private string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder);
@ -36,8 +39,11 @@ namespace Flow.Launcher.Core.Resource
private double mainWindowWidth;
public Theme()
public Theme(IPublicAPI publicAPI, Settings settings)
{
_api = publicAPI;
_settings = settings;
_themeDirectories.Add(DirectoryPath);
_themeDirectories.Add(UserDirectoryPath);
MakeSureThemeDirectoriesExist();
@ -88,7 +94,7 @@ namespace Flow.Launcher.Core.Resource
// to things like fonts
UpdateResourceDictionary(GetResourceDictionary(theme));
Settings.Theme = theme;
_settings.Theme = theme;
//always allow re-loading default theme, in case of failure of switching to a new theme from default theme
@ -99,7 +105,7 @@ namespace Flow.Launcher.Core.Resource
BlurEnabled = Win32Helper.IsBlurTheme();
if (Settings.UseDropShadowEffect && !BlurEnabled)
if (_settings.UseDropShadowEffect && !BlurEnabled)
AddDropShadowEffectToCurrentTheme();
Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled);
@ -109,7 +115,7 @@ namespace Flow.Launcher.Core.Resource
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found");
if (theme != defaultTheme)
{
MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme));
_api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_path_not_exists"), theme));
ChangeTheme(defaultTheme);
}
return false;
@ -119,7 +125,7 @@ namespace Flow.Launcher.Core.Resource
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse");
if (theme != defaultTheme)
{
MessageBoxEx.Show(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme));
_api.ShowMsgBox(string.Format(InternationalizationManager.Instance.GetTranslation("theme_load_failure_parse_error"), theme));
ChangeTheme(defaultTheme);
}
return false;
@ -147,7 +153,7 @@ namespace Flow.Launcher.Core.Resource
return dict;
}
private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(Settings.Theme);
private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(_settings.Theme);
public ResourceDictionary GetResourceDictionary(string theme)
{
@ -156,10 +162,10 @@ namespace Flow.Launcher.Core.Resource
if (dict["QueryBoxStyle"] is Style queryBoxStyle &&
dict["QuerySuggestionBoxStyle"] is Style querySuggestionBoxStyle)
{
var fontFamily = new FontFamily(Settings.QueryBoxFont);
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.QueryBoxFontStyle);
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.QueryBoxFontWeight);
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.QueryBoxFontStretch);
var fontFamily = new FontFamily(_settings.QueryBoxFont);
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.QueryBoxFontStyle);
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight);
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch);
queryBoxStyle.Setters.Add(new Setter(TextBox.FontFamilyProperty, fontFamily));
queryBoxStyle.Setters.Add(new Setter(TextBox.FontStyleProperty, fontStyle));
@ -184,10 +190,10 @@ namespace Flow.Launcher.Core.Resource
dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle &&
dict["ItemHotkeySelectedStyle"] is Style resultHotkeyItemSelectedStyle)
{
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultFont));
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultFontStyle));
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultFontWeight));
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultFontStretch));
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultFont));
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultFontStyle));
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultFontWeight));
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultFontStretch));
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
Array.ForEach(
@ -199,10 +205,10 @@ namespace Flow.Launcher.Core.Resource
dict["ItemSubTitleStyle"] is Style resultSubItemStyle &&
dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle)
{
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(Settings.ResultSubFont));
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(Settings.ResultSubFontStyle));
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(Settings.ResultSubFontWeight));
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(Settings.ResultSubFontStretch));
Setter fontFamily = new Setter(TextBlock.FontFamilyProperty, new FontFamily(_settings.ResultSubFont));
Setter fontStyle = new Setter(TextBlock.FontStyleProperty, FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.ResultSubFontStyle));
Setter fontWeight = new Setter(TextBlock.FontWeightProperty, FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.ResultSubFontWeight));
Setter fontStretch = new Setter(TextBlock.FontStretchProperty, FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.ResultSubFontStretch));
Setter[] setters = { fontFamily, fontStyle, fontWeight, fontStretch };
Array.ForEach(
@ -212,7 +218,7 @@ namespace Flow.Launcher.Core.Resource
/* Ignore Theme Window Width and use setting */
var windowStyle = dict["WindowStyle"] as Style;
var width = Settings.WindowSize;
var width = _settings.WindowSize;
windowStyle.Setters.Add(new Setter(Window.WidthProperty, width));
mainWindowWidth = (double)width;
return dict;
@ -220,7 +226,7 @@ namespace Flow.Launcher.Core.Resource
private ResourceDictionary GetCurrentResourceDictionary( )
{
return GetResourceDictionary(Settings.Theme);
return GetResourceDictionary(_settings.Theme);
}
public List<ThemeData> LoadAvailableThemes()

View file

@ -1,26 +1,12 @@
namespace Flow.Launcher.Core.Resource
using System;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Core.Resource
{
[Obsolete("ThemeManager.Instance is obsolete. Use Ioc.Default.GetRequiredService<Theme>() instead.")]
public class ThemeManager
{
private static Theme instance;
private static object syncObject = new object();
public static Theme Instance
{
get
{
if (instance == null)
{
lock (syncObject)
{
if (instance == null)
{
instance = new Theme();
}
}
}
return instance;
}
}
=> Ioc.Default.GetRequiredService<Theme>();
}
}

View file

@ -22,23 +22,26 @@ namespace Flow.Launcher.Core
{
public class Updater
{
public string GitHubRepository { get; }
public string GitHubRepository { get; init; }
public Updater(string gitHubRepository)
private readonly IPublicAPI _api;
public Updater(IPublicAPI publicAPI, string gitHubRepository)
{
_api = publicAPI;
GitHubRepository = gitHubRepository;
}
private SemaphoreSlim UpdateLock { get; } = new SemaphoreSlim(1);
public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true)
public async Task UpdateAppAsync(bool silentUpdate = true)
{
await UpdateLock.WaitAsync().ConfigureAwait(false);
try
{
if (!silentUpdate)
api.ShowMsg(api.GetTranslation("pleaseWait"),
api.GetTranslation("update_flowlauncher_update_check"));
_api.ShowMsg(_api.GetTranslation("pleaseWait"),
_api.GetTranslation("update_flowlauncher_update_check"));
using var updateManager = await GitHubUpdateManagerAsync(GitHubRepository).ConfigureAwait(false);
@ -53,13 +56,13 @@ namespace Flow.Launcher.Core
if (newReleaseVersion <= currentVersion)
{
if (!silentUpdate)
MessageBoxEx.Show(api.GetTranslation("update_flowlauncher_already_on_latest"));
_api.ShowMsgBox(_api.GetTranslation("update_flowlauncher_already_on_latest"));
return;
}
if (!silentUpdate)
api.ShowMsg(api.GetTranslation("update_flowlauncher_update_found"),
api.GetTranslation("update_flowlauncher_updating"));
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_update_found"),
_api.GetTranslation("update_flowlauncher_updating"));
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
@ -68,9 +71,9 @@ namespace Flow.Launcher.Core
if (DataLocation.PortableDataLocationInUse())
{
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}";
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, MessageBoxEx.Show);
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, MessageBoxEx.Show))
MessageBoxEx.Show(string.Format(api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s));
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s)))
_api.ShowMsgBox(string.Format(_api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
DataLocation.PortableDataPath,
targetDestination));
}
@ -83,7 +86,7 @@ namespace Flow.Launcher.Core
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
if (MessageBoxEx.Show(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes)
if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
@ -96,8 +99,8 @@ namespace Flow.Launcher.Core
Log.Exception($"|Updater.UpdateApp|Error Occurred", e);
if (!silentUpdate)
api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"),
api.GetTranslation("update_flowlauncher_check_connection"));
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_fail"),
_api.GetTranslation("update_flowlauncher_check_connection"));
}
finally
{

View file

@ -54,6 +54,7 @@
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="BitFaster.Caching" Version="2.5.3" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Fody" Version="6.5.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View file

@ -8,6 +8,7 @@ using Flow.Launcher.Infrastructure.UserSettings;
using System;
using System.Threading;
using Flow.Launcher.Plugin;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Infrastructure.Http
{
@ -17,8 +18,6 @@ namespace Flow.Launcher.Infrastructure.Http
private static HttpClient client = new HttpClient();
public static IPublicAPI API { get; set; }
static Http()
{
// need to be added so it would work on a win10 machine
@ -78,7 +77,7 @@ namespace Flow.Launcher.Infrastructure.Http
}
catch (UriFormatException e)
{
API.ShowMsg("Please try again", "Unable to parse Http Proxy");
Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsg("Please try again", "Unable to parse Http Proxy");
Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e);
}
}

View file

@ -6,6 +6,7 @@ using System.Text;
using JetBrains.Annotations;
using Flow.Launcher.Infrastructure.UserSettings;
using ToolGood.Words.Pinyin;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Infrastructure
{
@ -129,7 +130,12 @@ namespace Flow.Launcher.Infrastructure
private Settings _settings;
public void Initialize([NotNull] Settings settings)
public PinyinAlphabet()
{
Initialize(Ioc.Default.GetRequiredService<Settings>());
}
private void Initialize([NotNull] Settings settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}

View file

@ -31,11 +31,12 @@ namespace Flow.Launcher.Infrastructure.Storage
protected JsonStorage()
{
}
public JsonStorage(string filePath)
{
FilePath = filePath;
DirectoryPath = Path.GetDirectoryName(filePath) ?? throw new ArgumentException("Invalid file path");
Helper.ValidateDirectory(DirectoryPath);
}
@ -97,6 +98,7 @@ namespace Flow.Launcher.Infrastructure.Storage
return default;
}
}
private void RestoreBackup()
{
Log.Info($"|JsonStorage.Load|Failed to load settings.json, {BackupFilePath} restored successfully");
@ -179,25 +181,21 @@ namespace Flow.Launcher.Infrastructure.Storage
public void Save()
{
string serialized = JsonSerializer.Serialize(Data,
new JsonSerializerOptions
{
WriteIndented = true
});
new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(TempFilePath, serialized);
AtomicWriteSetting();
}
public async Task SaveAsync()
{
var tempOutput = File.OpenWrite(TempFilePath);
await using var tempOutput = File.OpenWrite(TempFilePath);
await JsonSerializer.SerializeAsync(tempOutput, Data,
new JsonSerializerOptions
{
WriteIndented = true
});
new JsonSerializerOptions { WriteIndented = true });
AtomicWriteSetting();
}
private void AtomicWriteSetting()
{
if (!File.Exists(FilePath))
@ -206,9 +204,9 @@ namespace Flow.Launcher.Infrastructure.Storage
}
else
{
File.Replace(TempFilePath, FilePath, BackupFilePath);
var finalFilePath = new FileInfo(FilePath).LinkTarget ?? FilePath;
File.Replace(TempFilePath, finalFilePath, BackupFilePath);
}
}
}
}

View file

@ -1,28 +1,35 @@
using Flow.Launcher.Plugin.SharedModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Plugin.SharedModels;
using System;
using System.Collections.Generic;
using System.Linq;
using Flow.Launcher.Infrastructure.UserSettings;
namespace Flow.Launcher.Infrastructure
{
public class StringMatcher
{
private readonly MatchOption _defaultMatchOption = new MatchOption();
private readonly MatchOption _defaultMatchOption = new();
public SearchPrecisionScore UserSettingSearchPrecision { get; set; }
private readonly IAlphabet _alphabet;
public StringMatcher(IAlphabet alphabet = null)
public StringMatcher(IAlphabet alphabet, Settings settings)
{
_alphabet = alphabet;
UserSettingSearchPrecision = settings.QuerySearchPrecision;
}
// This is a workaround to allow unit tests to set the instance
public StringMatcher(IAlphabet alphabet)
{
_alphabet = alphabet;
}
public static StringMatcher Instance { get; internal set; }
public static MatchResult FuzzySearch(string query, string stringToCompare)
{
return Instance.FuzzyMatch(query, stringToCompare);
return Ioc.Default.GetRequiredService<StringMatcher>().FuzzyMatch(query, stringToCompare);
}
public MatchResult FuzzyMatch(string query, string stringToCompare)
@ -241,16 +248,16 @@ namespace Flow.Launcher.Infrastructure
return false;
}
private bool IsAcronymChar(string stringToCompare, int compareStringIndex)
private static bool IsAcronymChar(string stringToCompare, int compareStringIndex)
=> char.IsUpper(stringToCompare[compareStringIndex]) ||
compareStringIndex == 0 || // 0 index means char is the start of the compare string, which is an acronym
char.IsWhiteSpace(stringToCompare[compareStringIndex - 1]);
private bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
=> stringToCompare[compareStringIndex] >= 0 && stringToCompare[compareStringIndex] <= 9;
// To get the index of the closest space which preceeds the first matching index
private int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstMatchIndex)
private static int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstMatchIndex)
{
var closestSpaceIndex = -1;

View file

@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Text.Json.Serialization;
using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
using Flow.Launcher.ViewModel;
@ -13,6 +14,24 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
public class Settings : BaseModel, IHotkeySettings
{
private FlowLauncherJsonStorage<Settings> _storage;
private StringMatcher _stringMatcher = null;
public void SetStorage(FlowLauncherJsonStorage<Settings> storage)
{
_storage = storage;
}
public void Initialize()
{
_stringMatcher = Ioc.Default.GetRequiredService<StringMatcher>();
}
public void Save()
{
_storage.Save();
}
private string language = Constant.SystemLanguageCode;
private string _theme = Constant.DefaultTheme;
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
@ -180,7 +199,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
}
};
/// <summary>
/// when false Alphabet static service will always return empty results
/// </summary>
@ -198,8 +216,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
set
{
_querySearchPrecision = value;
if (StringMatcher.Instance != null)
StringMatcher.Instance.UserSettingSearchPrecision = value;
if (_stringMatcher != null)
_stringMatcher.UserSettingSearchPrecision = value;
}
}
@ -238,6 +256,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public bool EnableUpdateLog { get; set; }
public bool StartFlowLauncherOnSystemStartup { get; set; } = false;
public bool UseLogonTaskForStartup { get; set; } = false;
public bool HideOnStartup { get; set; } = true;
bool _hideNotifyIcon { get; set; }
public bool HideNotifyIcon

View file

@ -323,17 +323,16 @@ namespace Flow.Launcher.Plugin
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK);
/// <summary>
/// Displays a standardised Flow message box.
/// If there is issue when showing the message box, it will return null.
/// Displays a standardised Flow progress box.
/// </summary>
/// <param name="caption">The caption of the message box.</param>
/// <param name="caption">The caption of the progress box.</param>
/// <param name="reportProgressAsync">
/// Time-consuming task function, whose input is the action to report progress.
/// The input of the action is the progress value which is a double value between 0 and 100.
/// If there are any exceptions, this action will be null.
/// </param>
/// <param name="forceClosed">When user closes the progress box manually by button or esc key, this action will be called.</param>
/// <returns>A progress box interface.</returns>
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null);
/// <param name="cancelProgress">When user cancel the progress, this action will be called.</param>
/// <returns></returns>
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action cancelProgress = null);
}
}

View file

@ -22,8 +22,10 @@ namespace Flow.Launcher.Test
private const string MicrosoftSqlServerManagementStudio = "Microsoft SQL Server Management Studio";
private const string VisualStudioCode = "Visual Studio Code";
public static List<string> GetSearchStrings()
=> new()
private readonly IAlphabet alphabet = null;
public List<string> GetSearchStrings()
=> new List<string>
{
Chrome,
"Choose which programs you want Windows to use for activities like web browsing, editing photos, sending e-mail, and playing music.",
@ -60,7 +62,7 @@ namespace Flow.Launcher.Test
};
var results = new List<Result>();
var matcher = new StringMatcher();
var matcher = new StringMatcher(alphabet);
foreach (var str in sources)
{
results.Add(new Result
@ -82,7 +84,7 @@ namespace Flow.Launcher.Test
public void WhenNotAllCharactersFoundInSearchString_ThenShouldReturnZeroScore(string searchString)
{
var compareString = "Can have rum only in my glass";
var matcher = new StringMatcher();
var matcher = new StringMatcher(alphabet);
var scoreResult = matcher.FuzzyMatch(searchString, compareString).RawScore;
ClassicAssert.True(scoreResult == 0);
@ -98,7 +100,7 @@ namespace Flow.Launcher.Test
string searchTerm)
{
var results = new List<Result>();
var matcher = new StringMatcher();
var matcher = new StringMatcher(alphabet);
foreach (var str in GetSearchStrings())
{
results.Add(new Result
@ -148,7 +150,7 @@ namespace Flow.Launcher.Test
string queryString, string compareString, int expectedScore)
{
// When, Given
var matcher = new StringMatcher {UserSettingSearchPrecision = SearchPrecisionScore.Regular};
var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = SearchPrecisionScore.Regular};
var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore;
// Should
@ -182,7 +184,7 @@ namespace Flow.Launcher.Test
bool expectedPrecisionResult)
{
// When
var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore};
var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = expectedPrecisionScore};
// Given
var matchResult = matcher.FuzzyMatch(queryString, compareString);
@ -233,7 +235,7 @@ namespace Flow.Launcher.Test
bool expectedPrecisionResult)
{
// When
var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore};
var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = expectedPrecisionScore};
// Given
var matchResult = matcher.FuzzyMatch(queryString, compareString);
@ -261,7 +263,7 @@ namespace Flow.Launcher.Test
string queryString, string compareString1, string compareString2)
{
// When
var matcher = new StringMatcher {UserSettingSearchPrecision = SearchPrecisionScore.Regular};
var matcher = new StringMatcher(alphabet) {UserSettingSearchPrecision = SearchPrecisionScore.Regular};
// Given
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
@ -294,7 +296,7 @@ namespace Flow.Launcher.Test
string queryString, string compareString1, string compareString2)
{
// When
var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular };
var matcher = new StringMatcher(alphabet) { UserSettingSearchPrecision = SearchPrecisionScore.Regular };
// Given
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
@ -324,7 +326,7 @@ namespace Flow.Launcher.Test
string secondName, string secondDescription, string secondExecutableName)
{
// Act
var matcher = new StringMatcher();
var matcher = new StringMatcher(alphabet);
var firstNameMatch = matcher.FuzzyMatch(queryString, firstName).RawScore;
var firstDescriptionMatch = matcher.FuzzyMatch(queryString, firstDescription).RawScore;
var firstExecutableNameMatch = matcher.FuzzyMatch(queryString, firstExecutableName).RawScore;
@ -359,7 +361,7 @@ namespace Flow.Launcher.Test
public void WhenGivenAnAcronymQuery_ShouldReturnAcronymScore(string queryString, string compareString,
int desiredScore)
{
var matcher = new StringMatcher();
var matcher = new StringMatcher(alphabet);
var score = matcher.FuzzyMatch(queryString, compareString).Score;
ClassicAssert.IsTrue(score == desiredScore,
$@"Query: ""{queryString}""

View file

@ -44,7 +44,7 @@ namespace Flow.Launcher
else
{
string msg = translater.GetTranslation("newActionKeywordsHasBeenAssigned");
MessageBoxEx.Show(msg);
App.API.ShowMsgBox(msg);
}
}
}

View file

@ -4,6 +4,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core;
using Flow.Launcher.Core.Configuration;
using Flow.Launcher.Core.ExternalPlugins.Environments;
@ -14,24 +15,85 @@ using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.ViewModel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
namespace Flow.Launcher
{
public partial class App : IDisposable, ISingleInstanceApp
{
public static PublicAPIInstance API { get; private set; }
public static IPublicAPI API { get; private set; }
private const string Unique = "Flow.Launcher_Unique_Application_Mutex";
private static bool _disposed;
private Settings _settings;
private MainViewModel _mainVM;
private SettingWindowViewModel _settingsVM;
private readonly Updater _updater = new Updater(Flow.Launcher.Properties.Settings.Default.GithubRepo);
private readonly Portable _portable = new Portable();
private readonly PinyinAlphabet _alphabet = new PinyinAlphabet();
private StringMatcher _stringMatcher;
private readonly Settings _settings;
public App()
{
// Initialize settings
try
{
var storage = new FlowLauncherJsonStorage<Settings>();
_settings = storage.Load();
_settings.SetStorage(storage);
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
}
catch (Exception e)
{
ShowErrorMsgBoxAndFailFast("Cannot load setting storage, please check local data directory", e);
return;
}
// Configure the dependency injection container
try
{
var host = Host.CreateDefaultBuilder()
.UseContentRoot(AppContext.BaseDirectory)
.ConfigureServices(services => services
.AddSingleton(_ => _settings)
.AddSingleton(sp => new Updater(sp.GetRequiredService<IPublicAPI>(), Launcher.Properties.Settings.Default.GithubRepo))
.AddSingleton<Portable>()
.AddSingleton<SettingWindowViewModel>()
.AddSingleton<IAlphabet, PinyinAlphabet>()
.AddSingleton<StringMatcher>()
.AddSingleton<Internationalization>()
.AddSingleton<IPublicAPI, PublicAPIInstance>()
.AddSingleton<MainViewModel>()
.AddSingleton<Theme>()
).Build();
Ioc.Default.ConfigureServices(host.Services);
}
catch (Exception e)
{
ShowErrorMsgBoxAndFailFast("Cannot configure dependency injection container, please open new issue in Flow.Launcher", e);
return;
}
// Initialize the public API and Settings first
try
{
API = Ioc.Default.GetRequiredService<IPublicAPI>();
_settings.Initialize();
}
catch (Exception e)
{
ShowErrorMsgBoxAndFailFast("Cannot initialize api and settings, please open new issue in Flow.Launcher", e);
return;
}
}
private static void ShowErrorMsgBoxAndFailFast(string message, Exception e)
{
// Firstly show users the message
MessageBox.Show(e.ToString(), message, MessageBoxButton.OK, MessageBoxImage.Error);
// Flow cannot construct its App instance, so ensure Flow crashes w/ the exception info.
Environment.FailFast(message, e);
}
[STAThread]
public static void Main()
@ -50,52 +112,40 @@ namespace Flow.Launcher
{
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
{
_portable.PreStartCleanUpAfterPortabilityUpdate();
Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();
Log.Info(
"|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------");
Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------");
Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}");
RegisterAppDomainExceptions();
RegisterDispatcherUnhandledException();
var imageLoadertask = ImageLoader.InitializeAsync();
_settingsVM = new SettingWindowViewModel(_updater, _portable);
_settings = _settingsVM.Settings;
_settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
_alphabet.Initialize(_settings);
_stringMatcher = new StringMatcher(_alphabet);
StringMatcher.Instance = _stringMatcher;
_stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
InternationalizationManager.Instance.Settings = _settings;
// TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future
InternationalizationManager.Instance.ChangeLanguage(_settings.Language);
PluginManager.LoadPlugins(_settings.PluginSettings);
_mainVM = new MainViewModel(_settings);
API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);
Http.API = API;
Http.Proxy = _settings.Proxy;
await PluginManager.InitializePluginsAsync(API);
await PluginManager.InitializePluginsAsync();
await imageLoadertask;
var window = new MainWindow(_settings, _mainVM);
var mainVM = Ioc.Default.GetRequiredService<MainViewModel>();
var window = new MainWindow(_settings, mainVM);
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
Current.MainWindow = window;
Current.MainWindow.Title = Constant.FlowLauncher;
HotKeyMapper.Initialize(_mainVM);
HotKeyMapper.Initialize();
// main windows needs initialized before theme change because of blur settings
ThemeManager.Instance.Settings = _settings;
// TODO: Clean ThemeManager.Instance in future
ThemeManager.Instance.ChangeTheme(_settings.Theme);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
@ -119,7 +169,14 @@ namespace Flow.Launcher
{
try
{
Helper.AutoStartup.Enable();
if (_settings.UseLogonTaskForStartup)
{
Helper.AutoStartup.EnableViaLogonTask();
}
else
{
Helper.AutoStartup.EnableViaRegistry();
}
}
catch (Exception e)
{
@ -141,11 +198,11 @@ namespace Flow.Launcher
{
// check update every 5 hours
var timer = new PeriodicTimer(TimeSpan.FromHours(5));
await _updater.UpdateAppAsync(API);
await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync();
while (await timer.WaitForNextTickAsync())
// check updates on startup
await _updater.UpdateAppAsync(API);
await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync();
}
});
}
@ -188,7 +245,7 @@ namespace Flow.Launcher
public void OnSecondAppStarted()
{
_mainVM.Show();
Ioc.Default.GetRequiredService<MainViewModel>().Show();
}
}
}

View file

@ -32,14 +32,11 @@
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -6,20 +6,17 @@ using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
using Flow.Launcher.Core;
namespace Flow.Launcher
{
public partial class CustomQueryHotkeySetting : Window
{
private SettingWindow _settingWidow;
private readonly Settings _settings;
private bool update;
private CustomPluginHotkey updateCustomHotkey;
public CustomQueryHotkeySetting(SettingWindow settingWidow, Settings settings)
public CustomQueryHotkeySetting(Settings settings)
{
_settingWidow = settingWidow;
_settings = settings;
InitializeComponent();
}
@ -63,7 +60,7 @@ namespace Flow.Launcher
o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey);
if (updateCustomHotkey == null)
{
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey"));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey"));
Close();
return;
}

View file

@ -30,14 +30,11 @@
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -43,13 +43,13 @@ namespace Flow.Launcher
{
if (String.IsNullOrEmpty(Key) || String.IsNullOrEmpty(Value))
{
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("emptyShortcut"));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("emptyShortcut"));
return;
}
// Check if key is modified or adding a new one
if (((update && originalKey != Key) || !update) && _hotkeyVm.DoesShortcutExist(Key))
{
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("duplicateShortcut"));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("duplicateShortcut"));
return;
}
DialogResult = !update || originalKey != Key || originalValue != Value;

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
@ -90,6 +90,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="InputSimulator" Version="1.0.4" />
<!-- Do not upgrade Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting since we are .Net7.0 -->
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
<PrivateAssets>all</PrivateAssets>
@ -101,6 +104,7 @@
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
<PackageReference Include="Jack251970.TaskScheduler" Version="2.12.1" />
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
</ItemGroup>

View file

@ -1,18 +1,31 @@
using System;
using System.IO;
using System.Linq;
using System.Security.Principal;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Microsoft.Win32;
using Microsoft.Win32.TaskScheduler;
namespace Flow.Launcher.Helper;
public class AutoStartup
{
private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
public static bool IsEnabled
{
get
{
// Check if logon task is enabled
if (CheckLogonTask())
{
return true;
}
// Check if registry is enabled
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
@ -28,12 +41,74 @@ public class AutoStartup
}
}
public static void Disable()
private static bool CheckLogonTask()
{
using var taskService = new TaskService();
var task = taskService.RootFolder.AllTasks.FirstOrDefault(t => t.Name == LogonTaskName);
if (task != null)
{
try
{
// Check if the action is the same as the current executable path
var action = task.Definition.Actions.FirstOrDefault()!.ToString().Trim();
if (!Constant.ExecutablePath.Equals(action, StringComparison.OrdinalIgnoreCase) && !File.Exists(action))
{
UnscheduleLogonTask();
ScheduleLogonTask();
}
return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to check logon task: {e}");
}
}
return false;
}
public static void DisableViaLogonTaskAndRegistry()
{
Disable(true);
Disable(false);
}
public static void EnableViaLogonTask()
{
Enable(true);
}
public static void EnableViaRegistry()
{
Enable(false);
}
public static void ChangeToViaLogonTask()
{
Disable(false);
Enable(true);
}
public static void ChangeToViaRegistry()
{
Disable(true);
Enable(false);
}
private static void Disable(bool logonTask)
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.DeleteValue(Constant.FlowLauncher, false);
if (logonTask)
{
UnscheduleLogonTask();
}
else
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.DeleteValue(Constant.FlowLauncher, false);
}
}
catch (Exception e)
{
@ -42,12 +117,19 @@ public class AutoStartup
}
}
internal static void Enable()
private static void Enable(bool logonTask)
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
if (logonTask)
{
ScheduleLogonTask();
}
else
{
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
}
}
catch (Exception e)
{
@ -55,4 +137,54 @@ public class AutoStartup
throw;
}
}
private static bool ScheduleLogonTask()
{
using var td = TaskService.Instance.NewTask();
td.RegistrationInfo.Description = LogonTaskDesc;
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name, Delay = TimeSpan.FromSeconds(2) });
td.Actions.Add(Constant.ExecutablePath);
if (IsCurrentUserIsAdmin())
{
td.Principal.RunLevel = TaskRunLevel.Highest;
}
td.Settings.StopIfGoingOnBatteries = false;
td.Settings.DisallowStartIfOnBatteries = false;
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;
try
{
TaskService.Instance.RootFolder.RegisterTaskDefinition(LogonTaskName, td);
return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to schedule logon task: {e}");
return false;
}
}
private static bool UnscheduleLogonTask()
{
using var taskService = new TaskService();
try
{
taskService.RootFolder.DeleteTask(LogonTaskName);
return true;
}
catch (Exception e)
{
Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}");
return false;
}
}
private static bool IsCurrentUserIsAdmin()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
}

View file

@ -5,10 +5,9 @@ using NHotkey;
using NHotkey.Wpf;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.ViewModel;
using Flow.Launcher.Core;
using ChefKeys;
using System.Globalization;
using Flow.Launcher.Infrastructure.Logger;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.Helper;
@ -17,10 +16,10 @@ internal static class HotKeyMapper
private static Settings _settings;
private static MainViewModel _mainViewModel;
internal static void Initialize(MainViewModel mainVM)
internal static void Initialize()
{
_mainViewModel = mainVM;
_settings = _mainViewModel.Settings;
_mainViewModel = Ioc.Default.GetRequiredService<MainViewModel>();
_settings = Ioc.Default.GetService<Settings>();
SetHotkey(_settings.Hotkey, OnToggleHotkey);
LoadCustomPluginHotkey();
@ -85,7 +84,7 @@ internal static class HotKeyMapper
hotkeyStr));
string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
MessageBoxEx.Show(errorMsg, errorMsgTitle);
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
}
}

View file

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Windows.Win32;
using Windows.Win32.UI.WindowsAndMessaging;
@ -13,8 +15,70 @@ namespace Flow.Launcher.Helper;
public static class WallpaperPathRetrieval
{
private static readonly int MAX_PATH = 260;
private static readonly int MAX_CACHE_SIZE = 3;
public static unsafe string GetWallpaperPath()
private static readonly Dictionary<(string, DateTime), ImageBrush> wallpaperCache = new();
public static Brush GetWallpaperBrush()
{
// Invoke the method on the UI thread
if (!Application.Current.Dispatcher.CheckAccess())
{
return Application.Current.Dispatcher.Invoke(GetWallpaperBrush);
}
try
{
var wallpaperPath = GetWallpaperPath();
if (wallpaperPath is not null && File.Exists(wallpaperPath))
{
// Since the wallpaper file name can be the same (TranscodedWallpaper),
// we need to add the last modified date to differentiate them
var dateModified = File.GetLastWriteTime(wallpaperPath);
wallpaperCache.TryGetValue((wallpaperPath, dateModified), out var cachedWallpaper);
if (cachedWallpaper != null)
{
return cachedWallpaper;
}
// We should not dispose the memory stream since the bitmap is still in use
var memStream = new MemoryStream(File.ReadAllBytes(wallpaperPath));
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = memStream;
bitmap.DecodePixelWidth = 800;
bitmap.DecodePixelHeight = 600;
bitmap.EndInit();
bitmap.Freeze(); // Make the bitmap thread-safe
var wallpaperBrush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
wallpaperBrush.Freeze(); // Make the brush thread-safe
// Manage cache size
if (wallpaperCache.Count >= MAX_CACHE_SIZE)
{
// Remove the oldest wallpaper from the cache
var oldestCache = wallpaperCache.Keys.OrderBy(k => k.Item2).FirstOrDefault();
if (oldestCache != default)
{
wallpaperCache.Remove(oldestCache);
}
}
wallpaperCache.Add((wallpaperPath, dateModified), wallpaperBrush);
return wallpaperBrush;
}
var wallpaperColor = GetWallpaperColor();
return new SolidColorBrush(wallpaperColor);
}
catch (Exception ex)
{
App.API.LogException(nameof(WallpaperPathRetrieval), "Error retrieving wallpaper", ex);
return new SolidColorBrush(Colors.Transparent);
}
}
private static unsafe string GetWallpaperPath()
{
var wallpaperPtr = stackalloc char[MAX_PATH];
PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, (uint)MAX_PATH,
@ -25,7 +89,7 @@ public static class WallpaperPathRetrieval
return wallpaper.ToString();
}
public static Color GetWallpaperColor()
private static Color GetWallpaperColor()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true);
var result = key?.GetValue("Background", null);

View file

@ -47,6 +47,8 @@
<system:String x:Key="portableMode">Portable Mode</system:String>
<system:String x:Key="portableModeToolTIp">Store all settings and user data in one folder (Useful when used with removable drives or cloud services).</system:String>
<system:String x:Key="startFlowLauncherOnSystemStartup">Start Flow Launcher on system startup</system:String>
<system:String x:Key="useLogonTaskForStartup">Use logon task instead of startup entry for faster startup experience</system:String>
<system:String x:Key="useLogonTaskForStartupTooltip">After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler</system:String>
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Hide Flow Launcher when focus is lost</system:String>
<system:String x:Key="dontPromptUpdateMsg">Do not show new version notifications</system:String>
@ -368,6 +370,7 @@
<system:String x:Key="commonOK">OK</system:String>
<system:String x:Key="commonYes">Yes</system:String>
<system:String x:Key="commonNo">No</system:String>
<system:String x:Key="commonBackground">Background</system:String>
<!-- Crash Reporter -->
<system:String x:Key="reportWindow_version">Version</system:String>
@ -384,6 +387,9 @@
<system:String x:Key="reportWindow_report_succeed">Report sent successfully</system:String>
<system:String x:Key="reportWindow_report_failed">Failed to send report</system:String>
<system:String x:Key="reportWindow_flowlauncher_got_an_error">Flow Launcher got an error</system:String>
<system:String x:Key="reportWindow_please_open_issue">Please open new issue in</system:String>
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
<!-- General Notice -->
<system:String x:Key="pleaseWait">Please wait...</system:String>

View file

@ -438,7 +438,7 @@ namespace Flow.Launcher
if (_settings.FirstLaunch)
{
_settings.FirstLaunch = false;
PluginManager.API.SaveAppAllSettings();
App.API.SaveAppAllSettings();
OpenWelcomeWindow();
}
}

View file

@ -1,9 +1,9 @@
<Window
x:Class="Flow.Launcher.Core.MessageBoxEx"
x:Class="Flow.Launcher.MessageBoxEx"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Flow.Launcher.Core"
xmlns:local="clr-namespace:Flow.Launcher"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="MessageBoxWindow"
Width="420"

View file

@ -7,7 +7,7 @@ using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
namespace Flow.Launcher.Core
namespace Flow.Launcher
{
public partial class MessageBoxEx : Window
{

View file

@ -29,14 +29,11 @@
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -24,7 +24,7 @@ namespace Flow.Launcher
this.pluginViewModel = pluginViewModel;
if (plugin == null)
{
MessageBoxEx.Show(translater.GetTranslation("cannotFindSpecifiedPlugin"));
App.API.ShowMsgBox(translater.GetTranslation("cannotFindSpecifiedPlugin"));
Close();
}
}
@ -44,7 +44,7 @@ namespace Flow.Launcher
else
{
string msg = translater.GetTranslation("invalidPriority");
MessageBoxEx.Show(msg);
App.API.ShowMsgBox(msg);
}
}

View file

@ -12,6 +12,7 @@
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Height"
Topmost="True"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WindowChrome.WindowChrome>
@ -35,9 +36,32 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Click="Button_Minimize"
RenderOptions.EdgeMode="Aliased"
Style="{DynamicResource TitleBarButtonStyle}">
<Path
Width="46"
Height="32"
Data="M 18,15 H 28"
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
StrokeThickness="1">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Button>
<Button
Grid.Column="2"
Click="Button_Cancel"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path
@ -94,11 +118,17 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button
x:Name="btnBackground"
MinWidth="120"
Margin="5 0 5 0"
Click="Button_Background"
Content="{DynamicResource commonBackground}" />
<Button
x:Name="btnCancel"
MinWidth="120"
Margin="5 0 5 0"
Click="Button_Click"
Click="Button_Cancel"
Content="{DynamicResource commonCancel}" />
</WrapPanel>
</Border>

View file

@ -8,15 +8,15 @@ namespace Flow.Launcher
{
public partial class ProgressBoxEx : Window
{
private readonly Action _forceClosed;
private readonly Action _cancelProgress;
private ProgressBoxEx(Action forceClosed)
private ProgressBoxEx(Action cancelProgress)
{
_forceClosed = forceClosed;
_cancelProgress = cancelProgress;
InitializeComponent();
}
public static async Task ShowAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null)
public static async Task ShowAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action cancelProgress = null)
{
ProgressBoxEx prgBox = null;
try
@ -25,7 +25,7 @@ namespace Flow.Launcher
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
prgBox = new ProgressBoxEx(forceClosed)
prgBox = new ProgressBoxEx(cancelProgress)
{
Title = caption
};
@ -35,7 +35,7 @@ namespace Flow.Launcher
}
else
{
prgBox = new ProgressBoxEx(forceClosed)
prgBox = new ProgressBoxEx(cancelProgress)
{
Title = caption
};
@ -95,20 +95,25 @@ namespace Flow.Launcher
ForceClose();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ForceClose();
}
private void Button_Cancel(object sender, RoutedEventArgs e)
{
ForceClose();
}
private void Button_Minimize(object sender, RoutedEventArgs e)
{
WindowState = WindowState.Minimized;
}
private void Button_Background(object sender, RoutedEventArgs e)
{
Hide();
}
private void ForceClose()
{
Close();
_forceClosed?.Invoke();
_cancelProgress?.Invoke();
}
}
}

View file

@ -25,23 +25,23 @@ using Flow.Launcher.Infrastructure.Storage;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Collections.Specialized;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core;
using Flow.Launcher.Infrastructure.UserSettings;
namespace Flow.Launcher
{
public class PublicAPIInstance : IPublicAPI
{
private readonly SettingWindowViewModel _settingsVM;
private readonly Settings _settings;
private readonly MainViewModel _mainVM;
private readonly PinyinAlphabet _alphabet;
#region Constructor
public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, PinyinAlphabet alphabet)
public PublicAPIInstance(Settings settings, MainViewModel mainVM)
{
_settingsVM = settingsVM;
_settings = settings;
_mainVM = mainVM;
_alphabet = alphabet;
GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback;
WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
}
@ -78,14 +78,15 @@ namespace Flow.Launcher
public event VisibilityChangedEventHandler VisibilityChanged { add => _mainVM.VisibilityChanged += value; remove => _mainVM.VisibilityChanged -= value; }
public void CheckForNewUpdate() => _settingsVM.UpdateApp();
// Must use Ioc.Default.GetRequiredService<Updater>() to avoid circular dependency
public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(false);
public void SaveAppAllSettings()
{
PluginManager.Save();
_mainVM.Save();
_settingsVM.Save();
ImageLoader.Save();
_settings.Save();
_ = ImageLoader.Save();
}
public Task ReloadAllPluginData() => PluginManager.ReloadDataAsync();
@ -105,7 +106,7 @@ namespace Flow.Launcher
{
Application.Current.Dispatcher.Invoke(() =>
{
SettingWindow sw = SingletonWindowOpener.Open<SettingWindow>(this, _settingsVM);
SettingWindow sw = SingletonWindowOpener.Open<SettingWindow>();
});
}
@ -244,7 +245,7 @@ namespace Flow.Launcher
public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null)
{
using var explorer = new Process();
var explorerInfo = _settingsVM.Settings.CustomExplorer;
var explorerInfo = _settings.CustomExplorer;
explorer.StartInfo = new ProcessStartInfo
{
@ -265,7 +266,7 @@ namespace Flow.Launcher
{
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
{
var browserInfo = _settingsVM.Settings.CustomBrowser;
var browserInfo = _settings.CustomBrowser;
var path = browserInfo.Path == "*" ? "" : browserInfo.Path;
@ -338,7 +339,7 @@ namespace Flow.Launcher
public MessageBoxResult ShowMsgBox(string messageBoxText, string caption = "", MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) =>
MessageBoxEx.Show(messageBoxText, caption, button, icon, defaultResult);
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action forceClosed = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, forceClosed);
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync, Action cancelProgress = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, cancelProgress);
#endregion

View file

@ -1,23 +1,82 @@
<Window x:Class="Flow.Launcher.ReportWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Icon="Images/app_error.png"
Topmost="True"
ResizeMode="NoResize"
Width="600"
Height="455"
Title="{DynamicResource reportWindow_flowlauncher_got_an_error}"
d:DesignHeight="300" d:DesignWidth="600" x:ClassModifier="internal">
<RichTextBox x:Name="ErrorTextbox"
IsDocumentEnabled="True"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
FontSize="14"
Margin="10"
BorderThickness="0"/>
<Window
x:Class="Flow.Launcher.ReportWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{DynamicResource reportWindow_flowlauncher_got_an_error}"
Width="600"
Height="455"
d:DesignHeight="300"
d:DesignWidth="600"
x:ClassModifier="internal"
Background="{DynamicResource PopuBGColor}"
Foreground="{DynamicResource PopupTextColor}"
Icon="/Images/app_error.png"
ResizeMode="NoResize"
Topmost="True"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</WindowChrome.WindowChrome>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Width="16"
Height="16"
Margin="10 4 4 4"
RenderOptions.BitmapScalingMode="HighQuality"
Source="/Images/app_error.png" />
<TextBlock
Grid.Column="1"
Margin="4 0 0 0"
VerticalAlignment="Center"
FontSize="12"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource reportWindow_flowlauncher_got_an_error}" />
<Button
Grid.Column="2"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path
Width="46"
Height="32"
Data="M 18,11 27,20 M 18,20 27,11"
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
StrokeThickness="1">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Button>
</Grid>
<RichTextBox
x:Name="ErrorTextbox"
Grid.Row="1"
Margin="10"
BorderThickness="0"
FontSize="14"
HorizontalScrollBarVisibility="Auto"
IsDocumentEnabled="True"
VerticalScrollBarVisibility="Auto" />
</Grid>
</Window>

View file

@ -43,20 +43,21 @@ namespace Flow.Launcher
var log = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();
var websiteUrl = exception switch
{
FlowPluginException pluginException =>GetIssuesUrl(pluginException.Metadata.Website),
_ => Constant.IssuesUrl
};
{
FlowPluginException pluginException =>GetIssuesUrl(pluginException.Metadata.Website),
_ => Constant.IssuesUrl
};
var paragraph = Hyperlink("Please open new issue in: ", websiteUrl);
paragraph.Inlines.Add($"1. upload log file: {log.FullName}\n");
paragraph.Inlines.Add($"2. copy below exception message");
var paragraph = Hyperlink(App.API.GetTranslation("reportWindow_please_open_issue"), websiteUrl);
paragraph.Inlines.Add(string.Format(App.API.GetTranslation("reportWindow_upload_log"), log.FullName));
paragraph.Inlines.Add("\n");
paragraph.Inlines.Add(App.API.GetTranslation("reportWindow_copy_below"));
ErrorTextbox.Document.Blocks.Add(paragraph);
StringBuilder content = new StringBuilder();
content.AppendLine(ErrorReporting.RuntimeInfo());
content.AppendLine(ErrorReporting.DependenciesInfo());
content.AppendLine();
content.AppendLine($"Date: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}");
content.AppendLine("Exception:");
content.AppendLine(exception.ToString());
@ -65,10 +66,12 @@ namespace Flow.Launcher
ErrorTextbox.Document.Blocks.Add(paragraph);
}
private Paragraph Hyperlink(string textBeforeUrl, string url)
private static Paragraph Hyperlink(string textBeforeUrl, string url)
{
var paragraph = new Paragraph();
paragraph.Margin = new Thickness(0);
var paragraph = new Paragraph
{
Margin = new Thickness(0)
};
var link = new Hyperlink
{
@ -79,10 +82,16 @@ namespace Flow.Launcher
link.Click += (s, e) => SearchWeb.OpenInBrowserTab(url);
paragraph.Inlines.Add(textBeforeUrl);
paragraph.Inlines.Add(" ");
paragraph.Inlines.Add(link);
paragraph.Inlines.Add("\n");
return paragraph;
}
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
{
Close();
}
}
}

View file

@ -2,11 +2,10 @@
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.UserSettings;
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Navigation;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.ViewModel;
using System.Windows.Media;
namespace Flow.Launcher.Resources.Pages
{
@ -29,5 +28,10 @@ namespace Flow.Launcher.Resources.Pages
{
HotKeyMapper.SetHotkey(hotkey, HotKeyMapper.OnToggleHotkey);
}
public Brush PreviewBackground
{
get => WallpaperPathRetrieval.GetWallpaperBrush();
}
}
}

View file

@ -28,14 +28,11 @@
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="btnCancel_Click"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -28,14 +28,11 @@
<StackPanel>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Grid.Column="1"
Click="btnCancel_Click"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -6,7 +6,6 @@ using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
@ -62,7 +61,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
[RelayCommand]
private void AskClearLogFolderConfirmation()
{
var confirmResult = MessageBoxEx.Show(
var confirmResult = App.API.ShowMsgBox(
InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"),
InternationalizationManager.Instance.GetTranslation("clearlogfolder"),
MessageBoxButton.YesNo
@ -77,7 +76,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
[RelayCommand]
private void OpenSettingsFolder()
{
PluginManager.API.OpenDirectory(DataLocation.SettingsDirectory);
App.API.OpenDirectory(DataLocation.SettingsDirectory);
}
[RelayCommand]
@ -85,7 +84,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
{
string settingsFolderPath = Path.Combine(DataLocation.SettingsDirectory);
string parentFolderPath = Path.GetDirectoryName(settingsFolderPath);
PluginManager.API.OpenDirectory(parentFolderPath);
App.API.OpenDirectory(parentFolderPath);
}
@ -96,7 +95,7 @@ public partial class SettingsPaneAboutViewModel : BaseModel
}
[RelayCommand]
private Task UpdateApp() => _updater.UpdateAppAsync(App.API, false);
private Task UpdateApp() => _updater.UpdateAppAsync(false);
private void ClearLogFolder()
{

View file

@ -42,9 +42,20 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
try
{
if (value)
AutoStartup.Enable();
{
if (UseLogonTaskForStartup)
{
AutoStartup.EnableViaLogonTask();
}
else
{
AutoStartup.EnableViaRegistry();
}
}
else
AutoStartup.Disable();
{
AutoStartup.DisableViaLogonTaskAndRegistry();
}
}
catch (Exception e)
{
@ -54,6 +65,34 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
}
}
public bool UseLogonTaskForStartup
{
get => Settings.UseLogonTaskForStartup;
set
{
Settings.UseLogonTaskForStartup = value;
if (StartFlowLauncherOnSystemStartup)
{
try
{
if (UseLogonTaskForStartup)
{
AutoStartup.ChangeToViaLogonTask();
}
else
{
AutoStartup.ChangeToViaRegistry();
}
}
catch (Exception e)
{
Notification.Show(InternationalizationManager.Instance.GetTranslation("setAutoStartFailed"),
e.Message);
}
}
}
}
public List<SearchWindowScreenData> SearchWindowScreens { get; } =
DropdownDataGeneric<SearchWindowScreens>.GetValues<SearchWindowScreenData>("SearchWindowScreen");
@ -160,7 +199,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
private void UpdateApp()
{
_ = _updater.UpdateAppAsync(App.API, false);
_ = _updater.UpdateAppAsync(false);
}
public bool AutoUpdates

View file

@ -7,7 +7,6 @@ using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Core;
namespace Flow.Launcher.SettingPages.ViewModels;
@ -42,11 +41,11 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
var item = SelectedCustomPluginHotkey;
if (item is null)
{
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
return;
}
var result = MessageBoxEx.Show(
var result = App.API.ShowMsgBox(
string.Format(
InternationalizationManager.Instance.GetTranslation("deleteCustomHotkeyWarning"), item.Hotkey
),
@ -67,11 +66,11 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
var item = SelectedCustomPluginHotkey;
if (item is null)
{
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
return;
}
var window = new CustomQueryHotkeySetting(null, Settings);
var window = new CustomQueryHotkeySetting(Settings);
window.UpdateItem(item);
window.ShowDialog();
}
@ -79,7 +78,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
[RelayCommand]
private void CustomHotkeyAdd()
{
new CustomQueryHotkeySetting(null, Settings).ShowDialog();
new CustomQueryHotkeySetting(Settings).ShowDialog();
}
[RelayCommand]
@ -88,11 +87,11 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
var item = SelectedCustomShortcut;
if (item is null)
{
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
return;
}
var result = MessageBoxEx.Show(
var result = App.API.ShowMsgBox(
string.Format(
InternationalizationManager.Instance.GetTranslation("deleteCustomShortcutWarning"), item.Key, item.Value
),
@ -112,7 +111,7 @@ public partial class SettingsPaneHotkeyViewModel : BaseModel
var item = SelectedCustomShortcut;
if (item is null)
{
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
return;
}

View file

@ -22,7 +22,7 @@ public partial class SettingsPaneProxyViewModel : BaseModel
private void OnTestProxyClicked()
{
var message = TestProxy();
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation(message));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation(message));
}
private string TestProxy()

View file

@ -5,7 +5,6 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
@ -14,7 +13,6 @@ using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.ViewModel;
using ModernWpf;
using Flow.Launcher.Core;
using ThemeManager = Flow.Launcher.Core.Resource.ThemeManager;
using ThemeManagerForColorSchemeSwitch = ModernWpf.ThemeManager;
@ -49,7 +47,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
{
if (ThemeManager.Instance.BlurEnabled && value)
{
MessageBoxEx.Show(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed"));
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("shadowEffectNotAllowed"));
return;
}
@ -212,24 +210,7 @@ public partial class SettingsPaneThemeViewModel : BaseModel
public Brush PreviewBackground
{
get
{
var wallpaper = WallpaperPathRetrieval.GetWallpaperPath();
if (wallpaper is not null && File.Exists(wallpaper))
{
var memStream = new MemoryStream(File.ReadAllBytes(wallpaper));
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = memStream;
bitmap.DecodePixelWidth = 800;
bitmap.DecodePixelHeight = 600;
bitmap.EndInit();
return new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
}
var wallpaperColor = WallpaperPathRetrieval.GetWallpaperColor();
return new SolidColorBrush(wallpaperColor);
}
get => WallpaperPathRetrieval.GetWallpaperBrush();
}
public ResultsViewModel PreviewResults

View file

@ -36,6 +36,13 @@
OnContent="{DynamicResource enable}" />
</cc:Card>
<cc:Card Title="{DynamicResource useLogonTaskForStartup}" Sub="{DynamicResource useLogonTaskForStartupTooltip}">
<ui:ToggleSwitch
IsOn="{Binding UseLogonTaskForStartup}"
OffContent="{DynamicResource disable}"
OnContent="{DynamicResource enable}" />
</cc:Card>
<cc:Card
Title="{DynamicResource hideOnStartup}"
Icon="&#xed1a;"

View file

@ -3,7 +3,6 @@ using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Navigation;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.SettingPages.ViewModels;
using Flow.Launcher.ViewModel;
@ -49,7 +48,7 @@ public partial class SettingsPanePluginStore
private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
PluginManager.API.OpenUrl(e.Uri.AbsoluteUri);
App.API.OpenUrl(e.Uri.AbsoluteUri);
e.Handled = true;
}

View file

@ -3,6 +3,7 @@ using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Interop;
using CommunityToolkit.Mvvm.DependencyInjection;
using Flow.Launcher.Core;
using Flow.Launcher.Core.Configuration;
using Flow.Launcher.Helper;
@ -17,16 +18,21 @@ namespace Flow.Launcher;
public partial class SettingWindow
{
private readonly Updater _updater;
private readonly IPortable _portable;
private readonly IPublicAPI _api;
private readonly Settings _settings;
private readonly SettingWindowViewModel _viewModel;
public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel)
public SettingWindow()
{
_settings = viewModel.Settings;
var viewModel = Ioc.Default.GetRequiredService<SettingWindowViewModel>();
_settings = Ioc.Default.GetRequiredService<Settings>();
DataContext = viewModel;
_viewModel = viewModel;
_api = api;
_updater = Ioc.Default.GetRequiredService<Updater>();
_portable = Ioc.Default.GetRequiredService<Portable>();
_api = Ioc.Default.GetRequiredService<IPublicAPI>();
InitializePosition();
InitializeComponent();
}
@ -125,7 +131,7 @@ public partial class SettingWindow
WindowState = _settings.SettingWindowState;
}
private bool IsPositionValid(double top, double left)
private static bool IsPositionValid(double top, double left)
{
foreach (var screen in Screen.AllScreens)
{
@ -145,7 +151,7 @@ public partial class SettingWindow
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 - this.ActualWidth) / 2 + dip1.X;
var left = (dip2.X - ActualWidth) / 2 + dip1.X;
return left;
}
@ -154,13 +160,13 @@ public partial class SettingWindow
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
var dip1 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Y);
var dip2 = WindowsInteropHelper.TransformPixelsToDIP(this, 0, screen.WorkingArea.Height);
var top = (dip2.Y - this.ActualHeight) / 2 + dip1.Y - 20;
var top = (dip2.Y - ActualHeight) / 2 + dip1.Y - 20;
return top;
}
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
var paneData = new PaneData(_settings, _viewModel.Updater, _viewModel.Portable);
var paneData = new PaneData(_settings, _updater, _portable);
if (args.IsSettingsSelected)
{
ContentFrame.Navigate(typeof(SettingsPaneGeneral), paneData);

View file

@ -25,6 +25,7 @@ using System.Windows.Input;
using System.ComponentModel;
using Flow.Launcher.Infrastructure.Image;
using System.Windows.Media;
using CommunityToolkit.Mvvm.DependencyInjection;
namespace Flow.Launcher.ViewModel
{
@ -56,13 +57,13 @@ namespace Flow.Launcher.ViewModel
#region Constructor
public MainViewModel(Settings settings)
public MainViewModel()
{
_queryTextBeforeLeaveResults = "";
_queryText = "";
_lastQuery = new Query();
Settings = settings;
Settings = Ioc.Default.GetRequiredService<Settings>();
Settings.PropertyChanged += (_, args) =>
{
switch (args.PropertyName)
@ -441,7 +442,7 @@ namespace Flow.Launcher.ViewModel
[RelayCommand]
private void SelectHelp()
{
PluginManager.API.OpenUrl("https://www.flowlauncher.com/docs/#/usage-tips");
App.API.OpenUrl("https://www.flowlauncher.com/docs/#/usage-tips");
}
[RelayCommand]

View file

@ -134,20 +134,20 @@ namespace Flow.Launcher.ViewModel
{
var directory = PluginPair.Metadata.PluginDirectory;
if (!string.IsNullOrEmpty(directory))
PluginManager.API.OpenDirectory(directory);
App.API.OpenDirectory(directory);
}
[RelayCommand]
private void OpenSourceCodeLink()
{
PluginManager.API.OpenUrl(PluginPair.Metadata.Website);
App.API.OpenUrl(PluginPair.Metadata.Website);
}
[RelayCommand]
private void OpenDeletePluginWindow()
{
PluginManager.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true);
PluginManager.API.ShowMainWindow();
App.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true);
App.API.ShowMainWindow();
}
public static bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);

View file

@ -1,66 +1,46 @@
using Flow.Launcher.Core;
using Flow.Launcher.Core.Configuration;
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.ViewModel;
public class SettingWindowViewModel : BaseModel
public partial class SettingWindowViewModel : BaseModel
{
private readonly FlowLauncherJsonStorage<Settings> _storage;
private readonly Settings _settings;
public Updater Updater { get; }
public IPortable Portable { get; }
public Settings Settings { get; }
public SettingWindowViewModel(Updater updater, IPortable portable)
public SettingWindowViewModel(Settings settings)
{
_storage = new FlowLauncherJsonStorage<Settings>();
Updater = updater;
Portable = portable;
Settings = _storage.Load();
_settings = settings;
}
public async void UpdateApp()
{
await Updater.UpdateAppAsync(App.API, false);
}
/// <summary>
/// Save Flow settings. Plugins settings are not included.
/// </summary>
public void Save()
{
_storage.Save();
_settings.Save();
}
public double SettingWindowWidth
{
get => Settings.SettingWindowWidth;
set => Settings.SettingWindowWidth = value;
get => _settings.SettingWindowWidth;
set => _settings.SettingWindowWidth = value;
}
public double SettingWindowHeight
{
get => Settings.SettingWindowHeight;
set => Settings.SettingWindowHeight = value;
get => _settings.SettingWindowHeight;
set => _settings.SettingWindowHeight = value;
}
public double? SettingWindowTop
{
get => Settings.SettingWindowTop;
set => Settings.SettingWindowTop = value;
get => _settings.SettingWindowTop;
set => _settings.SettingWindowTop = value;
}
public double? SettingWindowLeft
{
get => Settings.SettingWindowLeft;
set => Settings.SettingWindowLeft = value;
get => _settings.SettingWindowLeft;
set => _settings.SettingWindowLeft = value;
}
}

View file

@ -36,8 +36,6 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
@ -53,9 +51,8 @@
FontSize="12"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource Welcome_Page1_Title}" />
<Button
Grid.Column="4"
Grid.Column="2"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path

View file

@ -31,13 +31,13 @@ namespace Flow.Launcher.Plugin.Explorer.Views
}
private string actionKeyword;
private readonly IPublicAPI api;
private readonly IPublicAPI _api;
private bool _keywordEnabled;
public ActionKeywordSetting(ActionKeywordModel selectedActionKeyword, IPublicAPI api)
{
CurrentActionKeyword = selectedActionKeyword;
this.api = api;
_api = api;
ActionKeyword = selectedActionKeyword.Keyword;
KeywordEnabled = selectedActionKeyword.Enabled;
@ -62,14 +62,14 @@ namespace Flow.Launcher.Plugin.Explorer.Views
switch (CurrentActionKeyword.KeywordProperty, KeywordEnabled)
{
case (Settings.ActionKeyword.FileContentSearchActionKeyword, true):
api.ShowMsgBox(api.GetTranslation("plugin_explorer_globalActionKeywordInvalid"));
_api.ShowMsgBox(_api.GetTranslation("plugin_explorer_globalActionKeywordInvalid"));
return;
case (Settings.ActionKeyword.QuickAccessActionKeyword, true):
api.ShowMsgBox(api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid"));
_api.ShowMsgBox(_api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid"));
return;
}
if (!KeywordEnabled || !api.ActionKeywordAssigned(ActionKeyword))
if (!KeywordEnabled || !_api.ActionKeywordAssigned(ActionKeyword))
{
DialogResult = true;
Close();
@ -77,7 +77,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
}
// The keyword is not valid, so show message
api.ShowMsgBox(api.GetTranslation("newActionKeywordsHasBeenAssigned"));
_api.ShowMsgBox(_api.GetTranslation("newActionKeywordsHasBeenAssigned"));
}
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)