Release 1.9.3 (#1052)

## Bug fixes
- Addresses the issue where WindowsSettings plugin results appear higher than other results #1020 

- Fixed an issue where full screen mode does not disable hotkey when enabled #1037 

- Fixed some potential issues when loading plugins that use shared assembly #1036 

- Sorted out a race condition issue causing image loading on some results to fail #1040 

- Revised ttf/otf support #935 

- Resolved the issue where WebSearch plugin would crash if not connected to internet #977 

- Fixed incorrect text for "New Tab" and "New Window" buttons under Settings' default browser section #951 

- Fixed typos in plugin title and WindowsSettings name inside the context menu #1056
This commit is contained in:
Jeremy Wu 2022-03-03 13:59:43 +11:00 committed by GitHub
parent 68101e0bda
commit 84a806bf75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 92 additions and 90 deletions

View file

@ -15,20 +15,6 @@ namespace Flow.Launcher.Core.Plugin
private readonly AssemblyName assemblyName;
private static readonly ConcurrentDictionary<string, byte> loadedAssembly;
static PluginAssemblyLoader()
{
var currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
loadedAssembly = new ConcurrentDictionary<string, byte>(
currentAssemblies.Select(x => new KeyValuePair<string, byte>(x.FullName, default)));
AppDomain.CurrentDomain.AssemblyLoad += (sender, args) =>
{
loadedAssembly[args.LoadedAssembly.FullName] = default;
};
}
internal PluginAssemblyLoader(string assemblyFilePath)
{
dependencyResolver = new AssemblyDependencyResolver(assemblyFilePath);
@ -47,10 +33,9 @@ namespace Flow.Launcher.Core.Plugin
// When resolving dependencies, ignore assembly depenedencies that already exits with Flow.Launcher
// Otherwise duplicate assembly will be loaded and some weird behavior will occur, such as WinRT.Runtime.dll
// will fail due to loading multiple versions in process, each with their own static instance of registration state
if (assemblyPath == null || ExistsInReferencedPackage(assemblyName))
return null;
var existAssembly = Default.Assemblies.FirstOrDefault(x => x.FullName == assemblyName.FullName);
return LoadFromAssemblyPath(assemblyPath);
return existAssembly ?? (assemblyPath == null ? null : LoadFromAssemblyPath(assemblyPath));
}
internal Type FromAssemblyGetTypeOfInterface(Assembly assembly, Type type)
@ -58,10 +43,5 @@ namespace Flow.Launcher.Core.Plugin
var allTypes = assembly.ExportedTypes;
return allTypes.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Any(t => t == type));
}
internal bool ExistsInReferencedPackage(AssemblyName assemblyName)
{
return loadedAssembly.ContainsKey(assemblyName.FullName);
}
}
}

View file

@ -74,7 +74,7 @@ namespace Flow.Launcher.Infrastructure.Image
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary
// Double Check to avoid concurrent remove
if (Data.Count > permissibleFactor * MaxCached)
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key).ToArray())
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
Data.TryRemove(key, out _);
semaphore.Release();
}

View file

@ -25,7 +25,7 @@ namespace Flow.Launcher.Helper
internal static void OnToggleHotkey(object sender, HotkeyEventArgs args)
{
if (!mainViewModel.GameModeStatus)
if (!mainViewModel.ShouldIgnoreHotkeys() && !mainViewModel.GameModeStatus)
mainViewModel.ToggleFlowLauncher();
}

View file

@ -176,8 +176,8 @@
<system:String x:Key="defaultBrowser_name">Browser</system:String>
<system:String x:Key="defaultBrowser_profile_name">Browser Name</system:String>
<system:String x:Key="defaultBrowser_path">Browser Path</system:String>
<system:String x:Key="defaultBrowser_newtab">New Window</system:String>
<system:String x:Key="defaultBrowser_newWindow">New Tab</system:String>
<system:String x:Key="defaultBrowser_newWindow">New Window</system:String>
<system:String x:Key="defaultBrowser_newTab">New Tab</system:String>
<system:String x:Key="defaultBrowser_parameter">Private Mode</system:String>
<!-- Priority Setting Dialog -->

View file

@ -175,8 +175,8 @@
<system:String x:Key="defaultBrowser_name">브라우저</system:String>
<system:String x:Key="defaultBrowser_profile_name">브라우저 이름</system:String>
<system:String x:Key="defaultBrowser_path">브라우저 경로</system:String>
<system:String x:Key="defaultBrowser_newtab">새 창</system:String>
<system:String x:Key="defaultBrowser_newWindow">새 탭</system:String>
<system:String x:Key="defaultBrowser_newWindow">새 창</system:String>
<system:String x:Key="defaultBrowser_newTab">새 탭</system:String>
<system:String x:Key="defaultBrowser_parameter">프라이빗 모드</system:String>
<!-- Priority Setting Dialog -->

View file

@ -176,8 +176,8 @@
<system:String x:Key="defaultBrowser_name">Navegador</system:String>
<system:String x:Key="defaultBrowser_profile_name">Nome do navegador</system:String>
<system:String x:Key="defaultBrowser_path">Caminho do navegador</system:String>
<system:String x:Key="defaultBrowser_newtab">Nova janela</system:String>
<system:String x:Key="defaultBrowser_newWindow">Novo separador</system:String>
<system:String x:Key="defaultBrowser_newWindow">Nova janela</system:String>
<system:String x:Key="defaultBrowser_newTab">Novo separador</system:String>
<system:String x:Key="defaultBrowser_parameter">Modo privado</system:String>
<!-- Priority Setting Dialog -->

View file

@ -174,8 +174,8 @@
<system:String x:Key="defaultBrowser_name">Prehliadač</system:String>
<system:String x:Key="defaultBrowser_profile_name">Názov prehliadača</system:String>
<system:String x:Key="defaultBrowser_path">Cesta k prehliadaču</system:String>
<system:String x:Key="defaultBrowser_newtab">Nové okno</system:String>
<system:String x:Key="defaultBrowser_newWindow">Nová karta</system:String>
<system:String x:Key="defaultBrowser_newWindow">Nové okno</system:String>
<system:String x:Key="defaultBrowser_newTab">Nová karta</system:String>
<system:String x:Key="defaultBrowser_parameter">Privátny režim</system:String>
<!-- Priority Setting Dialog -->

View file

@ -52,8 +52,8 @@ namespace Flow.Launcher
var link = new Hyperlink { IsEnabled = true };
link.Inlines.Add(url);
link.NavigateUri = new Uri(url);
link.RequestNavigate += (s, e) => SearchWeb.NewTabInBrowser(e.Uri.ToString());
link.Click += (s, e) => SearchWeb.NewTabInBrowser(url);
link.RequestNavigate += (s, e) => SearchWeb.OpenInBrowserTab(e.Uri.ToString());
link.Click += (s, e) => SearchWeb.OpenInBrowserTab(url);
paragraph.Inlines.Add(textBeforeUrl);
paragraph.Inlines.Add(link);

View file

@ -196,7 +196,7 @@
VerticalAlignment="Center"
Orientation="Horizontal">
<RadioButton IsChecked="{Binding OpenInTab}"
Content="{DynamicResource defaultBrowser_newWindow}"></RadioButton>
Content="{DynamicResource defaultBrowser_newTab}"></RadioButton>
<RadioButton IsChecked="{Binding OpenInNewWindow, Mode=OneTime}"
Content="{DynamicResource defaultBrowser_newWindow}"></RadioButton>
</StackPanel>

View file

@ -232,7 +232,7 @@ namespace Flow.Launcher
var uri = new Uri(website);
if (Uri.CheckSchemeName(uri.Scheme))
{
website.NewTabInBrowser();
website.OpenInBrowserTab();
}
}
}

View file

@ -7,11 +7,16 @@ using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using System.IO;
using System.Drawing.Text;
using System.Collections.Generic;
namespace Flow.Launcher.ViewModel
{
public class ResultViewModel : BaseModel
{
private static PrivateFontCollection fontCollection = new();
private static Dictionary<string, string> fonts = new();
public ResultViewModel(Result result, Settings settings)
{
if (result != null)
@ -23,13 +28,29 @@ namespace Flow.Launcher.ViewModel
// Checks if it's a system installed font, which does not require path to be provided.
if (glyph.FontFamily.EndsWith(".ttf") || glyph.FontFamily.EndsWith(".otf"))
{
var fontPath = Result.Glyph.FontFamily;
Glyph = Path.IsPathRooted(fontPath)
? Result.Glyph
: Result.Glyph with
string fontFamilyPath = glyph.FontFamily;
if (!Path.IsPathRooted(fontFamilyPath))
{
fontFamilyPath = Path.Combine(Result.PluginDirectory, fontFamilyPath);
}
if (fonts.ContainsKey(fontFamilyPath))
{
Glyph = glyph with
{
FontFamily = Path.Combine(Result.PluginDirectory, fontPath)
FontFamily = fonts[fontFamilyPath]
};
}
else
{
fontCollection.AddFontFile(fontFamilyPath);
fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}";
Glyph = glyph with
{
FontFamily = fonts[fontFamilyPath]
};
}
}
else
{

View file

@ -333,7 +333,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
if (e.SpecialKeyState.CtrlPressed)
{
SearchWeb.NewTabInBrowser(plugin.UrlDownload);
SearchWeb.OpenInBrowserTab(plugin.UrlDownload);
return ShouldHideWindow;
}
@ -397,7 +397,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
{
if (e.SpecialKeyState.CtrlPressed)
{
SearchWeb.NewTabInBrowser(x.Website);
SearchWeb.OpenInBrowserTab(x.Website);
return ShouldHideWindow;
}

View file

@ -41,8 +41,8 @@ namespace Flow.Launcher.Plugin.WebSearch
var results = new List<Result>();
foreach (SearchSource searchSource in _settings.SearchSources.Where(o => (o.ActionKeyword == query.ActionKeyword ||
o.ActionKeyword == SearchSourceGlobalPluginWildCardSign)
&& o.Enabled))
o.ActionKeyword == SearchSourceGlobalPluginWildCardSign)
&& o.Enabled))
{
string keyword = string.Empty;
keyword = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? query.ToString() : query.Search;
@ -105,11 +105,11 @@ namespace Flow.Launcher.Plugin.WebSearch
if (_settings.EnableSuggestion)
{
var suggestions = await SuggestionsAsync(keyword, subtitle, searchSource, token).ConfigureAwait(false);
if (token.IsCancellationRequested || !suggestions.Any())
var enumerable = suggestions?.ToList();
if (token.IsCancellationRequested || enumerable is not { Count: > 0 })
return;
results.AddRange(suggestions);
results.AddRange(enumerable);
token.ThrowIfCancellationRequested();
}
@ -118,32 +118,32 @@ namespace Flow.Launcher.Plugin.WebSearch
private async Task<IEnumerable<Result>> SuggestionsAsync(string keyword, string subtitle, SearchSource searchSource, CancellationToken token)
{
var source = _settings.SelectedSuggestion;
if (source != null)
if (source == null)
{
//Suggestions appear below actual result, and appear above global action keyword match if non-global;
var score = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? scoreSuggestions : scoreSuggestions + 1;
var suggestions = await source.Suggestions(keyword, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
var resultsFromSuggestion = suggestions?.Select(o => new Result
{
Title = o,
SubTitle = subtitle,
Score = score,
IcoPath = searchSource.IconPath,
ActionKeywordAssigned = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? string.Empty : searchSource.ActionKeyword,
Action = c =>
{
_context.API.OpenUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(o)));
return true;
}
});
return resultsFromSuggestion;
return new List<Result>();
}
return new List<Result>();
//Suggestions appear below actual result, and appear above global action keyword match if non-global;
var score = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? scoreSuggestions : scoreSuggestions + 1;
var suggestions = await source.SuggestionsAsync(keyword, token).ConfigureAwait(false);
token.ThrowIfCancellationRequested();
var resultsFromSuggestion = suggestions?.Select(o => new Result
{
Title = o,
SubTitle = subtitle,
Score = score,
IcoPath = searchSource.IconPath,
ActionKeywordAssigned = searchSource.ActionKeyword == SearchSourceGlobalPluginWildCardSign ? string.Empty : searchSource.ActionKeyword,
Action = c =>
{
_context.API.OpenUrl(searchSource.Url.Replace("{q}", Uri.EscapeDataString(o)));
return true;
}
});
return resultsFromSuggestion;
}
public Task InitAsync(PluginInitContext context)
@ -191,4 +191,4 @@ namespace Flow.Launcher.Plugin.WebSearch
public event ResultUpdatedEventHandler ResultsUpdated;
}
}
}

View file

@ -16,7 +16,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
private readonly Regex _reg = new Regex("window.baidu.sug\\((.*)\\)");
public override async Task<List<string>> Suggestions(string query, CancellationToken token)
public override async Task<List<string>> SuggestionsAsync(string query, CancellationToken token)
{
string result;
@ -25,7 +25,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
const string api = "http://suggestion.baidu.com/su?json=1&wd=";
result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false);
}
catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException)
catch (Exception e) when (e is HttpRequestException or {InnerException: TimeoutException})
{
Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e);
return null;

View file

@ -15,7 +15,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
class Bing : SuggestionSource
{
public override async Task<List<string>> Suggestions(string query, CancellationToken token)
public override async Task<List<string>> SuggestionsAsync(string query, CancellationToken token)
{
try
@ -40,7 +40,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
}
catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException)
catch (Exception e) when (e is HttpRequestException or {InnerException: TimeoutException})
{
Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e);
return null;

View file

@ -14,7 +14,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
public class Google : SuggestionSource
{
public override async Task<List<string>> Suggestions(string query, CancellationToken token)
public override async Task<List<string>> SuggestionsAsync(string query, CancellationToken token)
{
try
{
@ -32,7 +32,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
return results.EnumerateArray().Select(o => o.GetString()).ToList();
}
catch (Exception e) when (e is HttpRequestException || e.InnerException is TimeoutException)
catch (Exception e) when (e is HttpRequestException or {InnerException: TimeoutException})
{
Log.Exception("|Baidu.Suggestions|Can't get suggestion from baidu", e);
return null;

View file

@ -6,6 +6,6 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{
public abstract class SuggestionSource
{
public abstract Task<List<string>> Suggestions(string query, CancellationToken token);
public abstract Task<List<string>> SuggestionsAsync(string query, CancellationToken token);
}
}

View file

@ -34,8 +34,9 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
var resultList = new List<Result>();
foreach (var entry in list)
{
const int highScore = 20;
const int midScore = 10;
// Adjust the score to lower the order of many irrelevant matches from area strings
// that may only be for description.
const int nonNameMatchScoreAdj = 10;
Result? result;
Debug.Assert(_api != null, nameof(_api) + " != null");
@ -44,7 +45,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
if (nameMatch.IsSearchPrecisionScoreMet())
{
var settingResult = NewSettingResult(nameMatch.Score + highScore, entry.Type);
var settingResult = NewSettingResult(nameMatch.Score, entry.Type);
settingResult.TitleHighlightData = nameMatch.MatchData;
result = settingResult;
}
@ -53,7 +54,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
var areaMatch = _api.FuzzySearch(query.Search, entry.Area);
if (areaMatch.IsSearchPrecisionScoreMet())
{
var settingResult = NewSettingResult(areaMatch.Score + midScore, entry.Type);
var settingResult = NewSettingResult(areaMatch.Score - nonNameMatchScoreAdj, entry.Type);
result = settingResult;
}
else
@ -61,7 +62,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
result = entry.AltNames?
.Select(altName => _api.FuzzySearch(query.Search, altName))
.Where(match => match.IsSearchPrecisionScoreMet())
.Select(altNameMatch => NewSettingResult(altNameMatch.Score + midScore, entry.Type))
.Select(altNameMatch => NewSettingResult(altNameMatch.Score - nonNameMatchScoreAdj, entry.Type))
.FirstOrDefault();
}
@ -75,7 +76,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
.SelectMany(x => x)
.Contains(x, StringComparer.CurrentCultureIgnoreCase))
)
result = NewSettingResult(midScore, entry.Type);
result = NewSettingResult(nonNameMatchScoreAdj, entry.Type);
}
}
@ -115,7 +116,7 @@ namespace Flow.Launcher.Plugin.WindowsSettings.Helper
private static void AddOptionalToolTip(WindowsSetting entry, Result result)
{
var toolTipText = new StringBuilder();
var settingType = entry.Type == "AppSettingsApp" ? "System settings" : "Control Panel";
toolTipText.AppendLine($"{Resources.Application}: {settingType}");

View file

@ -4,7 +4,7 @@
"Description": "Search settings inside Control Panel and Settings App",
"Name": "Windows Settings",
"Author": "TobiasSekan",
"Version": "2.0.1",
"Version": "2.0.2",
"Language": "csharp",
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
"ExecuteFileName": "Flow.Launcher.Plugin.WindowsSettings.dll",

View file

@ -1,4 +1,4 @@
version: '1.9.2.{build}'
version: '1.9.3.{build}'
init:
- ps: |