diff --git a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs index fab1b3e8f..e3f0e2a2f 100644 --- a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs +++ b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs @@ -29,13 +29,13 @@ namespace Flow.Launcher.Core.ExternalPlugins var request = new HttpRequestMessage(HttpMethod.Get, manifestFileUrl); request.Headers.Add("If-None-Match", latestEtag); - var response = await Http.SendAsync(request, token).ConfigureAwait(false); + using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.OK) { Log.Info($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Fetched plugins from manifest repo"); - var json = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false); + await using var json = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false); UserPlugins = await JsonSerializer.DeserializeAsync>(json, cancellationToken: token).ConfigureAwait(false); @@ -56,4 +56,4 @@ namespace Flow.Launcher.Core.ExternalPlugins } } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Resource/Internationalization.cs b/Flow.Launcher.Core/Resource/Internationalization.cs index 4568e92f3..acc693ed5 100644 --- a/Flow.Launcher.Core/Resource/Internationalization.cs +++ b/Flow.Launcher.Core/Resource/Internationalization.cs @@ -96,9 +96,16 @@ namespace Flow.Launcher.Core.Resource { LoadLanguage(language); } - Settings.Language = language.LanguageCode; - CultureInfo.CurrentCulture = new CultureInfo(language.LanguageCode); + // Culture of this thread + // Use CreateSpecificCulture to preserve possible user-override settings in Windows + CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture(language.LanguageCode); CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture; + // App domain + CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture(language.LanguageCode); + CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.DefaultThreadCurrentCulture; + + // Raise event after culture is set + Settings.Language = language.LanguageCode; _ = Task.Run(() => { UpdatePluginMetadataTranslations(); @@ -186,7 +193,7 @@ namespace Flow.Launcher.Core.Resource { p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle(); p.Metadata.Description = pluginI18N.GetTranslatedPluginDescription(); - pluginI18N.OnCultureInfoChanged(CultureInfo.CurrentCulture); + pluginI18N.OnCultureInfoChanged(CultureInfo.DefaultThreadCurrentCulture); } catch (Exception e) { diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index bad0344eb..44c47cf28 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -79,7 +79,7 @@ namespace Flow.Launcher.Core await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false); } - var newVersionTips = NewVersinoTips(newReleaseVersion.ToString()); + var newVersionTips = NewVersionTips(newReleaseVersion.ToString()); Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); @@ -137,10 +137,10 @@ namespace Flow.Launcher.Core return manager; } - public string NewVersinoTips(string version) + public string NewVersionTips(string version) { - var translater = InternationalizationManager.Instance; - var tips = string.Format(translater.GetTranslation("newVersionTips"), version); + var translator = InternationalizationManager.Instance; + var tips = string.Format(translator.GetTranslation("newVersionTips"), version); return tips; } diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index 5ace46376..b0eebd2df 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -45,6 +45,7 @@ namespace Flow.Launcher.Infrastructure public const string Logs = "Logs"; public const string Website = "https://flowlauncher.com"; + public const string SponsorPage = "https://github.com/sponsors/Flow-Launcher"; public const string GitHub = "https://github.com/Flow-Launcher/Flow.Launcher"; public const string Docs = "https://flowlauncher.com/docs"; } diff --git a/Flow.Launcher.Infrastructure/Http/Http.cs b/Flow.Launcher.Infrastructure/Http/Http.cs index 9f4146b7b..e5be0701f 100644 --- a/Flow.Launcher.Infrastructure/Http/Http.cs +++ b/Flow.Launcher.Infrastructure/Http/Http.cs @@ -68,7 +68,7 @@ namespace Flow.Launcher.Infrastructure.Http var userName when string.IsNullOrEmpty(userName) => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null), _ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), - new NetworkCredential(Proxy.UserName, Proxy.Password)) + new NetworkCredential(Proxy.UserName, Proxy.Password)) }, _ => (null, null) }, @@ -79,7 +79,7 @@ namespace Flow.Launcher.Infrastructure.Http _ => throw new ArgumentOutOfRangeException() }; } - catch(UriFormatException e) + catch (UriFormatException e) { API.ShowMsg("Please try again", "Unable to parse Http Proxy"); Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e); @@ -94,7 +94,7 @@ namespace Flow.Launcher.Infrastructure.Http if (response.StatusCode == HttpStatusCode.OK) { await using var fileStream = new FileStream(filePath, FileMode.CreateNew); - await response.Content.CopyToAsync(fileStream); + await response.Content.CopyToAsync(fileStream, token); } else { @@ -117,7 +117,7 @@ namespace Flow.Launcher.Infrastructure.Http public static Task GetAsync([NotNull] string url, CancellationToken token = default) { Log.Debug($"|Http.Get|Url <{url}>"); - return GetAsync(new Uri(url.Replace("#", "%23")), token); + return GetAsync(new Uri(url), token); } /// @@ -130,36 +130,57 @@ namespace Flow.Launcher.Infrastructure.Http { Log.Debug($"|Http.Get|Url <{url}>"); using var response = await client.GetAsync(url, token); - var content = await response.Content.ReadAsStringAsync(); - if (response.StatusCode == HttpStatusCode.OK) - { - return content; - } - else + var content = await response.Content.ReadAsStringAsync(token); + if (response.StatusCode != HttpStatusCode.OK) { throw new HttpRequestException( $"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>"); } + + return content; } /// - /// Asynchrously get the result as stream from url. + /// Send a GET request to the specified Uri with an HTTP completion option and a cancellation token as an asynchronous operation. + /// + /// The Uri the request is sent to. + /// An HTTP completion option value that indicates when the operation should be considered completed. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static Task GetStreamAsync([NotNull] string url, + CancellationToken token = default) => GetStreamAsync(new Uri(url), token); + + + /// + /// Send a GET request to the specified Uri with an HTTP completion option and a cancellation token as an asynchronous operation. /// /// + /// /// - public static async Task GetStreamAsync([NotNull] string url, CancellationToken token = default) + public static async Task GetStreamAsync([NotNull] Uri url, + CancellationToken token = default) { Log.Debug($"|Http.Get|Url <{url}>"); - var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); - return await response.Content.ReadAsStreamAsync(); + return await client.GetStreamAsync(url, token); + } + + public static async Task GetResponseAsync(string url, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, + CancellationToken token = default) + => await GetResponseAsync(new Uri(url), completionOption, token); + + public static async Task GetResponseAsync([NotNull] Uri url, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, + CancellationToken token = default) + { + Log.Debug($"|Http.Get|Url <{url}>"); + return await client.GetAsync(url, completionOption, token); } /// /// Asynchrously send an HTTP request. /// - public static async Task SendAsync(HttpRequestMessage request, CancellationToken token = default) + public static async Task SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken token = default) { - return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token); + return await client.SendAsync(request, completionOption, token); } } } diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs index 04e11bf1a..2fd2291d4 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -25,11 +25,11 @@ namespace Flow.Launcher.Infrastructure.Image public class ImageCache { private const int MaxCached = 50; - public ConcurrentDictionary Data { get; private set; } = new ConcurrentDictionary(); + public ConcurrentDictionary<(string, bool), ImageUsage> Data { get; } = new(); private const int permissibleFactor = 2; private SemaphoreSlim semaphore = new(1, 1); - public void Initialization(Dictionary usage) + public void Initialization(Dictionary<(string, bool), int> usage) { foreach (var key in usage.Keys) { @@ -37,29 +37,29 @@ namespace Flow.Launcher.Infrastructure.Image } } - public ImageSource this[string path] + public ImageSource this[string path, bool isFullImage = false] { get { - if (Data.TryGetValue(path, out var value)) + if (!Data.TryGetValue((path, isFullImage), out var value)) { - value.usage++; - return value.imageSource; + return null; } + value.usage++; + return value.imageSource; - return null; } set { Data.AddOrUpdate( - path, - new ImageUsage(0, value), - (k, v) => - { - v.imageSource = value; - v.usage++; - return v; - } + (path, isFullImage), + new ImageUsage(0, value), + (k, v) => + { + v.imageSource = value; + v.usage++; + return v; + } ); SliceExtra(); @@ -82,9 +82,9 @@ namespace Flow.Launcher.Infrastructure.Image } } - public bool ContainsKey(string key) + public bool ContainsKey(string key, bool isFullImage) { - return key is not null && Data.ContainsKey(key) && Data[key].imageSource != null; + return key is not null && Data.ContainsKey((key, isFullImage)) && Data[(key, isFullImage)].imageSource != null; } public int CacheSize() @@ -100,4 +100,4 @@ namespace Flow.Launcher.Infrastructure.Image return Data.Values.Select(x => x.imageSource).Distinct().Count(); } } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 11f66c8af..130221379 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -3,57 +3,58 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; +using System.Net.Http; using System.Threading.Tasks; using System.Windows.Media; using System.Windows.Media.Imaging; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; +using static Flow.Launcher.Infrastructure.Http.Http; namespace Flow.Launcher.Infrastructure.Image { public static class ImageLoader { private static readonly ImageCache ImageCache = new(); - private static BinaryStorage> _storage; + private static BinaryStorage> _storage; private static readonly ConcurrentDictionary GuidToKey = new(); private static IImageHashGenerator _hashGenerator; private static readonly bool EnableImageHash = true; public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon)); + public const int SmallIconSize = 32; private static readonly string[] ImageExtensions = { - ".png", - ".jpg", - ".jpeg", - ".gif", - ".bmp", - ".tiff", - ".ico" + ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico" }; public static void Initialize() { - _storage = new BinaryStorage>("Image"); + _storage = new BinaryStorage>("Image"); _hashGenerator = new ImageHashGenerator(); var usage = LoadStorageToConcurrentDictionary(); - foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) + foreach (var icon in new[] + { + Constant.DefaultIcon, Constant.MissingImgIcon + }) { ImageSource img = new BitmapImage(new Uri(icon)); img.Freeze(); - ImageCache[icon] = img; + ImageCache[icon, false] = img; } - _ = Task.Run(() => + _ = Task.Run(async () => { - Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () => + await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () => { - ImageCache.Data.AsParallel().ForAll(x => + foreach (var ((path, isFullImage), _) in ImageCache.Data) { - Load(x.Key); - }); + await LoadAsync(path, isFullImage); + } }); Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}"); }); @@ -63,17 +64,20 @@ namespace Flow.Launcher.Infrastructure.Image { lock (_storage) { - _storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, x => x.usage)); + _storage.Save(ImageCache.Data + .ToDictionary( + x => x.Key, + x => x.Value.usage)); } } - private static ConcurrentDictionary LoadStorageToConcurrentDictionary() + private static ConcurrentDictionary<(string, bool), int> LoadStorageToConcurrentDictionary() { lock (_storage) { - var loaded = _storage.TryLoad(new Dictionary()); + var loaded = _storage.TryLoad(new Dictionary<(string, bool), int>()); - return new ConcurrentDictionary(loaded); + return new ConcurrentDictionary<(string, bool), int>(loaded); } } @@ -99,7 +103,7 @@ namespace Flow.Launcher.Infrastructure.Image Cache } - private static ImageResult LoadInternal(string path, bool loadFullImage = false) + private static async ValueTask LoadInternalAsync(string path, bool loadFullImage = false) { ImageResult imageResult; @@ -107,13 +111,21 @@ namespace Flow.Launcher.Infrastructure.Image { if (string.IsNullOrEmpty(path)) { - return new ImageResult(ImageCache[Constant.MissingImgIcon], ImageType.Error); - } - if (ImageCache.ContainsKey(path)) - { - return new ImageResult(ImageCache[path], ImageType.Cache); + return new ImageResult(DefaultImage, ImageType.Error); } + if (ImageCache.ContainsKey(path, loadFullImage)) + { + return new ImageResult(ImageCache[path, loadFullImage], ImageType.Cache); + } + + if (Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out var uriResult) + && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps)) + { + var image = await LoadRemoteImageAsync(loadFullImage, uriResult); + ImageCache[path, loadFullImage] = image; + return new ImageResult(image, ImageType.ImageFile); + } if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) { var imageSource = new BitmapImage(new Uri(path)); @@ -121,12 +133,7 @@ namespace Flow.Launcher.Infrastructure.Image return new ImageResult(imageSource, ImageType.Data); } - if (!Path.IsPathRooted(path)) - { - path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path)); - } - - imageResult = GetThumbnailResult(ref path, loadFullImage); + imageResult = await Task.Run(() => GetThumbnailResult(ref path, loadFullImage)); } catch (System.Exception e) { @@ -140,14 +147,35 @@ namespace Flow.Launcher.Infrastructure.Image Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e); Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2); - ImageSource image = ImageCache[Constant.MissingImgIcon]; - ImageCache[path] = image; + ImageSource image = ImageCache[Constant.MissingImgIcon, false]; + ImageCache[path, false] = image; imageResult = new ImageResult(image, ImageType.Error); } } return imageResult; } + private static async Task LoadRemoteImageAsync(bool loadFullImage, Uri uriResult) + { + // Download image from url + await using var resp = await GetStreamAsync(uriResult); + await using var buffer = new MemoryStream(); + await resp.CopyToAsync(buffer); + buffer.Seek(0, SeekOrigin.Begin); + var image = new BitmapImage(); + image.BeginInit(); + image.CacheOption = BitmapCacheOption.OnLoad; + if (!loadFullImage) + { + image.DecodePixelHeight = SmallIconSize; + image.DecodePixelWidth = SmallIconSize; + } + image.StreamSource = buffer; + image.EndInit(); + image.StreamSource = null; + image.Freeze(); + return image; + } private static ImageResult GetThumbnailResult(ref string path, bool loadFullImage = false) { @@ -192,7 +220,7 @@ namespace Flow.Launcher.Infrastructure.Image } else { - image = ImageCache[Constant.MissingImgIcon]; + image = ImageCache[Constant.MissingImgIcon, false]; path = Constant.MissingImgIcon; } @@ -213,14 +241,14 @@ namespace Flow.Launcher.Infrastructure.Image option); } - public static bool CacheContainImage(string path) + public static bool CacheContainImage(string path, bool loadFullImage = false) { - return ImageCache.ContainsKey(path) && ImageCache[path] != null; + return ImageCache.ContainsKey(path, false) && ImageCache[path, loadFullImage] != null; } - public static ImageSource Load(string path, bool loadFullImage = false) + public static async ValueTask LoadAsync(string path, bool loadFullImage = false) { - var imageResult = LoadInternal(path, loadFullImage); + var imageResult = await LoadInternalAsync(path, loadFullImage); var img = imageResult.ImageSource; if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache) @@ -231,7 +259,7 @@ namespace Flow.Launcher.Infrastructure.Image if (GuidToKey.TryGetValue(hash, out string key)) { // image already exists - img = ImageCache[key] ?? img; + img = ImageCache[key, false] ?? img; } else { // new guid @@ -240,7 +268,7 @@ namespace Flow.Launcher.Infrastructure.Image } // update cache - ImageCache[path] = img; + ImageCache[path, false] = img; } return img; diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 33072b53d..3561c6ffe 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -6,7 +6,6 @@ using System.Text.Json.Serialization; using System.Windows; using Flow.Launcher.Plugin; using Flow.Launcher.Plugin.SharedModels; -using Flow.Launcher; using Flow.Launcher.ViewModel; namespace Flow.Launcher.Infrastructure.UserSettings diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index a1d3b83ab..f2d9323ef 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -60,9 +60,14 @@ namespace Flow.Launcher.Plugin get { return _icoPath; } set { - if (!string.IsNullOrEmpty(PluginDirectory) && !Path.IsPathRooted(value)) + // As a standard this property will handle prepping and converting to absolute local path for icon image processing + if (!string.IsNullOrEmpty(value) + && !string.IsNullOrEmpty(PluginDirectory) + && !Path.IsPathRooted(value) + && !value.StartsWith("http://", StringComparison.OrdinalIgnoreCase) + && !value.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { - _icoPath = Path.Combine(value, IcoPath); + _icoPath = Path.Combine(PluginDirectory, value); } else { @@ -140,10 +145,11 @@ namespace Flow.Launcher.Plugin set { _pluginDirectory = value; - if (!string.IsNullOrEmpty(IcoPath) && !Path.IsPathRooted(IcoPath)) - { - IcoPath = Path.Combine(value, IcoPath); - } + + // When the Result object is returned from the query call, PluginDirectory is not provided until + // UpdatePluginMetadata call is made at PluginManager.cs L196. Once the PluginDirectory becomes available + // we need to update (only if not Uri path) the IcoPath with the full absolute path so the image can be loaded. + IcoPath = _icoPath; } } diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln index f59d3d26f..1d403c5a1 100644 --- a/Flow.Launcher.sln +++ b/Flow.Launcher.sln @@ -80,7 +80,7 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|Any CPU {FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|Any CPU diff --git a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs index ecdfc5851..1e39473e0 100644 --- a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs +++ b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs @@ -52,7 +52,8 @@ namespace Flow.Launcher.Converters // Check if Text will be larger then our QueryTextBox System.Windows.Media.Typeface typeface = new Typeface(QueryTextBox.FontFamily, QueryTextBox.FontStyle, QueryTextBox.FontWeight, QueryTextBox.FontStretch); - System.Windows.Media.FormattedText ft = new FormattedText(QueryTextBox.Text, System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, QueryTextBox.FontSize, Brushes.Black); + // TODO: Obsolete warning? + System.Windows.Media.FormattedText ft = new FormattedText(QueryTextBox.Text, System.Globalization.CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, QueryTextBox.FontSize, Brushes.Black); var offset = QueryTextBox.Padding.Right; @@ -75,4 +76,4 @@ namespace Flow.Launcher.Converters throw new NotImplementedException(); } } -} \ No newline at end of file +} diff --git a/Flow.Launcher/CustomQueryHotkeySetting.xaml b/Flow.Launcher/CustomQueryHotkeySetting.xaml index ddf0d0e45..1113cb24d 100644 --- a/Flow.Launcher/CustomQueryHotkeySetting.xaml +++ b/Flow.Launcher/CustomQueryHotkeySetting.xaml @@ -75,10 +75,14 @@ Text="{DynamicResource customeQueryHotkeyTips}" TextAlignment="Left" TextWrapping="WrapWithOverflow" /> + - + @@ -131,6 +135,7 @@ LastChildFill="True"> + + diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index 9ceb9789d..70758540d 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -10,7 +10,9 @@ using Flow.Launcher.ViewModel; using ModernWpf; using ModernWpf.Controls; using System; +using System.Diagnostics; using System.IO; +using System.Security.Policy; using System.Windows; using System.Windows.Data; using System.Windows.Forms; diff --git a/Flow.Launcher/Themes/Base.xaml b/Flow.Launcher/Themes/Base.xaml index cb0921b80..94740a730 100644 --- a/Flow.Launcher/Themes/Base.xaml +++ b/Flow.Launcher/Themes/Base.xaml @@ -3,18 +3,32 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:userSettings="clr-namespace:Flow.Launcher.Infrastructure.UserSettings;assembly=Flow.Launcher.Infrastructure"> - + 0 + 0 + 0 + + + + + + + diff --git a/Flow.Launcher/Themes/BlackAndWhite.xaml b/Flow.Launcher/Themes/BlackAndWhite.xaml deleted file mode 100644 index 395f5d614..000000000 --- a/Flow.Launcher/Themes/BlackAndWhite.xaml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - #494949 - - - - \ No newline at end of file diff --git a/Flow.Launcher/Themes/BlurBlack Darker.xaml b/Flow.Launcher/Themes/BlurBlack Darker.xaml index 6dc0db45e..88c48afee 100644 --- a/Flow.Launcher/Themes/BlurBlack Darker.xaml +++ b/Flow.Launcher/Themes/BlurBlack Darker.xaml @@ -71,7 +71,6 @@ x:Key="ItemTitleStyle" BasedOn="{StaticResource BaseItemTitleStyle}" TargetType="{x:Type TextBlock}"> - + + + + + + + + + + + + + #f1f1f1 + + + + + + + + + + 5 + 10 0 10 0 + 0 0 0 10 + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Atom.xaml b/Flow.Launcher/Themes/Circle Light.xaml similarity index 52% rename from Flow.Launcher/Themes/Atom.xaml rename to Flow.Launcher/Themes/Circle Light.xaml index f532c97d5..7e14a29a6 100644 --- a/Flow.Launcher/Themes/Atom.xaml +++ b/Flow.Launcher/Themes/Circle Light.xaml @@ -1,7 +1,8 @@ - + @@ -9,48 +10,43 @@ x:Key="ItemGlyph" BasedOn="{StaticResource BaseGlyphStyle}" TargetType="{x:Type TextBlock}"> - + + + - + TargetType="{x:Type Window}" /> + - #2c313c - - - - + #5046e5 + + + + + + + 8 + 10 0 10 0 + 0 0 0 10 + - + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Circle System.xaml b/Flow.Launcher/Themes/Circle System.xaml new file mode 100644 index 000000000..b00f03e76 --- /dev/null +++ b/Flow.Launcher/Themes/Circle System.xaml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 8 + 10 0 10 0 + 0 0 0 10 + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Cyan Dark.xaml b/Flow.Launcher/Themes/Cyan Dark.xaml new file mode 100644 index 000000000..c79044f00 --- /dev/null +++ b/Flow.Launcher/Themes/Cyan Dark.xaml @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + #1e292f + + + + + + + + + + 0 + 0 + 0 0 0 0 + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Darker Glass.xaml b/Flow.Launcher/Themes/Darker Glass.xaml index 13c9e2bc5..a33f98b09 100644 --- a/Flow.Launcher/Themes/Darker Glass.xaml +++ b/Flow.Launcher/Themes/Darker Glass.xaml @@ -24,7 +24,8 @@ x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - + + - - - - - - - #4d4d4d - - - - - - diff --git a/Flow.Launcher/Themes/Discord Dark.xaml b/Flow.Launcher/Themes/Discord Dark.xaml index 92b8d8da7..74c1719c4 100644 --- a/Flow.Launcher/Themes/Discord Dark.xaml +++ b/Flow.Launcher/Themes/Discord Dark.xaml @@ -16,7 +16,6 @@ BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/Dracula.xaml b/Flow.Launcher/Themes/Dracula.xaml index 146038ba9..c01b67c74 100644 --- a/Flow.Launcher/Themes/Dracula.xaml +++ b/Flow.Launcher/Themes/Dracula.xaml @@ -17,7 +17,6 @@ TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/Gray.xaml b/Flow.Launcher/Themes/Gray.xaml index 1cacc8ec2..eb8b48f43 100644 --- a/Flow.Launcher/Themes/Gray.xaml +++ b/Flow.Launcher/Themes/Gray.xaml @@ -1,36 +1,46 @@ - - + + - + + - + + - #787878 + #797d86 + + - + TargetType="{x:Type ScrollBar}" /> + + + + 0 + 0 + 0 0 0 0 + - + \ No newline at end of file diff --git a/Flow.Launcher/Themes/League.xaml b/Flow.Launcher/Themes/League.xaml index 771d38c39..9f4a9a628 100644 --- a/Flow.Launcher/Themes/League.xaml +++ b/Flow.Launcher/Themes/League.xaml @@ -9,7 +9,6 @@ x:Key="QueryBoxStyle" BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}"> - @@ -52,6 +51,14 @@ TargetType="{x:Type Line}"> + - - - - - - - - - - - - - - #d9d9d9 - - - - - - - - - \ No newline at end of file diff --git a/Flow.Launcher/Themes/Metro Server.xaml b/Flow.Launcher/Themes/Metro Server.xaml deleted file mode 100644 index 65659aec5..000000000 --- a/Flow.Launcher/Themes/Metro Server.xaml +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - #04152E - - - - - - - \ No newline at end of file diff --git a/Flow.Launcher/Themes/Nord.xaml b/Flow.Launcher/Themes/Midnight.xaml similarity index 50% rename from Flow.Launcher/Themes/Nord.xaml rename to Flow.Launcher/Themes/Midnight.xaml index 735041656..b52fe87e1 100644 --- a/Flow.Launcher/Themes/Nord.xaml +++ b/Flow.Launcher/Themes/Midnight.xaml @@ -1,5 +1,8 @@ - - + + @@ -7,33 +10,38 @@ x:Key="ItemGlyph" BasedOn="{StaticResource BaseGlyphStyle}" TargetType="{x:Type TextBlock}"> - + + TargetType="{x:Type Line}" /> + + - + + - #596479 + #202938 + + + TargetType="{x:Type ScrollBar}" /> + + + + 8 + 10 0 10 0 + 0 0 0 10 + - + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Nord Darker.xaml b/Flow.Launcher/Themes/Nord Darker.xaml index 7c29eda22..840e44b3c 100644 --- a/Flow.Launcher/Themes/Nord Darker.xaml +++ b/Flow.Launcher/Themes/Nord Darker.xaml @@ -13,7 +13,6 @@ x:Key="QueryBoxStyle" BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/Pink.xaml b/Flow.Launcher/Themes/Pink.xaml index c5f701c46..96dae2545 100644 --- a/Flow.Launcher/Themes/Pink.xaml +++ b/Flow.Launcher/Themes/Pink.xaml @@ -12,7 +12,6 @@ x:Key="QueryBoxStyle" BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/Sublime.xaml b/Flow.Launcher/Themes/Sublime.xaml index 1b6f25e83..417bd723e 100644 --- a/Flow.Launcher/Themes/Sublime.xaml +++ b/Flow.Launcher/Themes/Sublime.xaml @@ -16,7 +16,6 @@ BasedOn="{StaticResource BaseQueryBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/Ubuntu.xaml b/Flow.Launcher/Themes/Ubuntu.xaml new file mode 100644 index 000000000..ea10c0e82 --- /dev/null +++ b/Flow.Launcher/Themes/Ubuntu.xaml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + #4d4d4d + + + + + + + + + + 0 + 0 0 0 0 + 0 0 0 0 + + + + \ No newline at end of file diff --git a/Flow.Launcher/Themes/Win10Light.xaml b/Flow.Launcher/Themes/Win10Light.xaml index 6a4a07c56..12ba01f71 100644 --- a/Flow.Launcher/Themes/Win10Light.xaml +++ b/Flow.Launcher/Themes/Win10Light.xaml @@ -18,11 +18,9 @@ - - @@ -32,7 +30,7 @@ TargetType="{x:Type TextBox}"> - + diff --git a/Flow.Launcher/Themes/Win11Dark.xaml b/Flow.Launcher/Themes/Win11Dark.xaml index aa510acaf..4660eae8f 100644 --- a/Flow.Launcher/Themes/Win11Dark.xaml +++ b/Flow.Launcher/Themes/Win11Dark.xaml @@ -17,7 +17,6 @@ TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/Win11Light.xaml b/Flow.Launcher/Themes/Win11Light.xaml index 39c13d28c..4c0769d4d 100644 --- a/Flow.Launcher/Themes/Win11Light.xaml +++ b/Flow.Launcher/Themes/Win11Light.xaml @@ -11,6 +11,12 @@ TargetType="{x:Type TextBlock}"> + #198F8F8F diff --git a/Flow.Launcher/Themes/Win11System.xaml b/Flow.Launcher/Themes/Win11System.xaml index 3cf7fd123..42f0579a7 100644 --- a/Flow.Launcher/Themes/Win11System.xaml +++ b/Flow.Launcher/Themes/Win11System.xaml @@ -12,13 +12,18 @@ TargetType="{x:Type TextBlock}"> +