Skip to content

Commit 02b5373

Browse files
authored
Merge branch 'dev' into 250410-PluginBadge2
2 parents c66cbae + c8d16a8 commit 02b5373

File tree

22 files changed

+655
-62
lines changed

22 files changed

+655
-62
lines changed

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static class PluginManager
3737

3838
private static PluginsSettings Settings;
3939
private static List<PluginMetadata> _metadatas;
40-
private static List<string> _modifiedPlugins = new();
40+
private static readonly List<string> _modifiedPlugins = new();
4141

4242
/// <summary>
4343
/// Directories that will hold Flow Launcher plugin directory
@@ -61,10 +61,17 @@ private static void DeletePythonBinding()
6161
/// </summary>
6262
public static void Save()
6363
{
64-
foreach (var plugin in AllPlugins)
64+
foreach (var pluginPair in AllPlugins)
6565
{
66-
var savable = plugin.Plugin as ISavable;
67-
savable?.Save();
66+
var savable = pluginPair.Plugin as ISavable;
67+
try
68+
{
69+
savable?.Save();
70+
}
71+
catch (Exception e)
72+
{
73+
API.LogException(ClassName, $"Failed to save plugin {pluginPair.Metadata.Name}", e);
74+
}
6875
}
6976

7077
API.SavePluginSettings();
@@ -81,14 +88,21 @@ public static async ValueTask DisposePluginsAsync()
8188

8289
private static async Task DisposePluginAsync(PluginPair pluginPair)
8390
{
84-
switch (pluginPair.Plugin)
91+
try
92+
{
93+
switch (pluginPair.Plugin)
94+
{
95+
case IDisposable disposable:
96+
disposable.Dispose();
97+
break;
98+
case IAsyncDisposable asyncDisposable:
99+
await asyncDisposable.DisposeAsync();
100+
break;
101+
}
102+
}
103+
catch (Exception e)
85104
{
86-
case IDisposable disposable:
87-
disposable.Dispose();
88-
break;
89-
case IAsyncDisposable asyncDisposable:
90-
await asyncDisposable.DisposeAsync();
91-
break;
105+
API.LogException(ClassName, $"Failed to dispose plugin {pluginPair.Metadata.Name}", e);
92106
}
93107
}
94108

@@ -292,7 +306,7 @@ public static async Task<List<Result>> QueryForPluginAsync(PluginPair pair, Quer
292306
{
293307
Title = $"{metadata.Name}: Failed to respond!",
294308
SubTitle = "Select this result for more info",
295-
IcoPath = Flow.Launcher.Infrastructure.Constant.ErrorIcon,
309+
IcoPath = Constant.ErrorIcon,
296310
PluginDirectory = metadata.PluginDirectory,
297311
ActionKeywordAssigned = query.ActionKeyword,
298312
PluginID = metadata.ID,
@@ -369,8 +383,8 @@ public static bool ActionKeywordRegistered(string actionKeyword)
369383
{
370384
// this method is only checking for action keywords (defined as not '*') registration
371385
// hence the actionKeyword != Query.GlobalPluginWildcardSign logic
372-
return actionKeyword != Query.GlobalPluginWildcardSign
373-
&& NonGlobalPlugins.ContainsKey(actionKeyword);
386+
return actionKeyword != Query.GlobalPluginWildcardSign
387+
&& NonGlobalPlugins.ContainsKey(actionKeyword);
374388
}
375389

376390
/// <summary>

Flow.Launcher.Core/Resource/Internationalization.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ public class Internationalization
2222
private const string DefaultFile = "en.xaml";
2323
private const string Extension = ".xaml";
2424
private readonly Settings _settings;
25-
private readonly List<string> _languageDirectories = new List<string>();
26-
private readonly List<ResourceDictionary> _oldResources = new List<ResourceDictionary>();
25+
private readonly List<string> _languageDirectories = new();
26+
private readonly List<ResourceDictionary> _oldResources = new();
2727
private readonly string SystemLanguageCode;
2828

2929
public Internationalization(Settings settings)
@@ -144,7 +144,7 @@ public void ChangeLanguage(string languageCode)
144144
_settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
145145
}
146146

147-
private Language GetLanguageByLanguageCode(string languageCode)
147+
private static Language GetLanguageByLanguageCode(string languageCode)
148148
{
149149
var lowercase = languageCode.ToLower();
150150
var language = AvailableLanguages.GetAvailableLanguages().FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase);
@@ -239,7 +239,7 @@ public List<Language> LoadAvailableLanguages()
239239
return list;
240240
}
241241

242-
public string GetTranslation(string key)
242+
public static string GetTranslation(string key)
243243
{
244244
var translation = Application.Current.TryFindResource(key);
245245
if (translation is string)
@@ -257,8 +257,7 @@ private void UpdatePluginMetadataTranslations()
257257
{
258258
foreach (var p in PluginManager.GetPluginsForInterface<IPluginI18n>())
259259
{
260-
var pluginI18N = p.Plugin as IPluginI18n;
261-
if (pluginI18N == null) return;
260+
if (p.Plugin is not IPluginI18n pluginI18N) return;
262261
try
263262
{
264263
p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle();
@@ -272,19 +271,19 @@ private void UpdatePluginMetadataTranslations()
272271
}
273272
}
274273

275-
public string LanguageFile(string folder, string language)
274+
private static string LanguageFile(string folder, string language)
276275
{
277276
if (Directory.Exists(folder))
278277
{
279-
string path = Path.Combine(folder, language);
278+
var path = Path.Combine(folder, language);
280279
if (File.Exists(path))
281280
{
282281
return path;
283282
}
284283
else
285284
{
286285
Log.Error($"|Internationalization.LanguageFile|Language path can't be found <{path}>");
287-
string english = Path.Combine(folder, DefaultFile);
286+
var english = Path.Combine(folder, DefaultFile);
288287
if (File.Exists(english))
289288
{
290289
return english;

Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.IO;
1+
using System;
2+
using System.IO;
23
using System.Threading.Tasks;
34
using Flow.Launcher.Infrastructure.Logger;
45
using Flow.Launcher.Infrastructure.UserSettings;
@@ -40,6 +41,16 @@ public BinaryStorage(string filename)
4041
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
4142
}
4243

44+
// Let the old Program plugin get this constructor
45+
[Obsolete("This constructor is obsolete. Use BinaryStorage(string filename) instead.")]
46+
public BinaryStorage(string filename, string directoryPath = null!)
47+
{
48+
DirectoryPath = directoryPath ?? DataLocation.CacheDirectory;
49+
FilesFolders.ValidateDirectory(DirectoryPath);
50+
51+
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
52+
}
53+
4354
public async ValueTask<T> TryLoadAsync(T defaultData)
4455
{
4556
if (Data != null) return Data;
@@ -82,8 +93,10 @@ private static async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
8293

8394
public void Save()
8495
{
85-
var serialized = MemoryPackSerializer.Serialize(Data);
96+
// User may delete the directory, so we need to check it
97+
FilesFolders.ValidateDirectory(DirectoryPath);
8698

99+
var serialized = MemoryPackSerializer.Serialize(Data);
87100
File.WriteAllBytes(FilePath, serialized);
88101
}
89102

@@ -103,6 +116,9 @@ public void ClearData()
103116
// so we need to pass it to SaveAsync
104117
public async ValueTask SaveAsync(T data)
105118
{
119+
// User may delete the directory, so we need to check it
120+
FilesFolders.ValidateDirectory(DirectoryPath);
121+
106122
await using var stream = new FileStream(FilePath, FileMode.Create);
107123
await MemoryPackSerializer.SerializeAsync(stream, data);
108124
}

Flow.Launcher.Infrastructure/Storage/FlowLauncherJsonStorage.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ namespace Flow.Launcher.Infrastructure.Storage
1717

1818
public FlowLauncherJsonStorage()
1919
{
20-
var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
21-
FilesFolders.ValidateDirectory(directoryPath);
20+
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
21+
FilesFolders.ValidateDirectory(DirectoryPath);
2222

2323
var filename = typeof(T).Name;
24-
FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}");
24+
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
2525
}
2626

2727
public new void Save()

Flow.Launcher.Infrastructure/Storage/JsonStorage.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,10 @@ private void BackupOriginFile()
183183

184184
public void Save()
185185
{
186-
string serialized = JsonSerializer.Serialize(Data,
186+
// User may delete the directory, so we need to check it
187+
FilesFolders.ValidateDirectory(DirectoryPath);
188+
189+
var serialized = JsonSerializer.Serialize(Data,
187190
new JsonSerializerOptions { WriteIndented = true });
188191

189192
File.WriteAllText(TempFilePath, serialized);
@@ -193,6 +196,9 @@ public void Save()
193196

194197
public async Task SaveAsync()
195198
{
199+
// User may delete the directory, so we need to check it
200+
FilesFolders.ValidateDirectory(DirectoryPath);
201+
196202
await using var tempOutput = File.OpenWrite(TempFilePath);
197203
await JsonSerializer.SerializeAsync(tempOutput, Data,
198204
new JsonSerializerOptions { WriteIndented = true });

Flow.Launcher.Infrastructure/Win32Helper.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.ComponentModel;
3+
using System.Diagnostics;
34
using System.Globalization;
45
using System.Runtime.InteropServices;
56
using System.Windows;
@@ -517,5 +518,92 @@ public static bool IsNotificationSupported()
517518
}
518519

519520
#endregion
521+
522+
#region Korean IME
523+
524+
public static bool IsWindows11()
525+
{
526+
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
527+
Environment.OSVersion.Version.Build >= 22000;
528+
}
529+
530+
public static bool IsKoreanIMEExist()
531+
{
532+
return GetLegacyKoreanIMERegistryValue() != null;
533+
}
534+
535+
public static bool IsLegacyKoreanIMEEnabled()
536+
{
537+
object value = GetLegacyKoreanIMERegistryValue();
538+
539+
if (value is int intValue)
540+
{
541+
return intValue == 1;
542+
}
543+
else if (value != null && int.TryParse(value.ToString(), out int parsedValue))
544+
{
545+
return parsedValue == 1;
546+
}
547+
548+
return false;
549+
}
550+
551+
public static bool SetLegacyKoreanIMEEnabled(bool enable)
552+
{
553+
const string subKeyPath = @"Software\Microsoft\input\tsf\tsf3override\{A028AE76-01B1-46C2-99C4-ACD9858AE02F}";
554+
const string valueName = "NoTsf3Override5";
555+
556+
try
557+
{
558+
using RegistryKey key = Registry.CurrentUser.CreateSubKey(subKeyPath);
559+
if (key != null)
560+
{
561+
int value = enable ? 1 : 0;
562+
key.SetValue(valueName, value, RegistryValueKind.DWord);
563+
return true;
564+
}
565+
}
566+
catch (System.Exception)
567+
{
568+
// Ignored
569+
}
570+
571+
return false;
572+
}
573+
574+
public static object GetLegacyKoreanIMERegistryValue()
575+
{
576+
const string subKeyPath = @"Software\Microsoft\input\tsf\tsf3override\{A028AE76-01B1-46C2-99C4-ACD9858AE02F}";
577+
const string valueName = "NoTsf3Override5";
578+
579+
try
580+
{
581+
using RegistryKey key = Registry.CurrentUser.OpenSubKey(subKeyPath);
582+
if (key != null)
583+
{
584+
return key.GetValue(valueName);
585+
}
586+
}
587+
catch (System.Exception)
588+
{
589+
// Ignored
590+
}
591+
592+
return null;
593+
}
594+
595+
public static void OpenImeSettings()
596+
{
597+
try
598+
{
599+
Process.Start(new ProcessStartInfo("ms-settings:regionlanguage") { UseShellExecute = true });
600+
}
601+
catch (System.Exception)
602+
{
603+
// Ignored
604+
}
605+
}
606+
607+
#endregion
520608
}
521609
}

Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,12 +264,12 @@ public static string GetPreviousExistingDirectory(Func<string, bool> locationExi
264264
var index = path.LastIndexOf('\\');
265265
if (index > 0 && index < (path.Length - 1))
266266
{
267-
string previousDirectoryPath = path.Substring(0, index + 1);
268-
return locationExists(previousDirectoryPath) ? previousDirectoryPath : "";
267+
string previousDirectoryPath = path[..(index + 1)];
268+
return locationExists(previousDirectoryPath) ? previousDirectoryPath : string.Empty;
269269
}
270270
else
271271
{
272-
return "";
272+
return string.Empty;
273273
}
274274
}
275275

@@ -285,7 +285,7 @@ public static string ReturnPreviousDirectoryIfIncompleteString(string path)
285285
// not full path, get previous level directory string
286286
var indexOfSeparator = path.LastIndexOf('\\');
287287

288-
return path.Substring(0, indexOfSeparator + 1);
288+
return path[..(indexOfSeparator + 1)];
289289
}
290290

291291
return path;

Flow.Launcher/App.xaml.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () =>
153153

154154
RegisterAppDomainExceptions();
155155
RegisterDispatcherUnhandledException();
156+
RegisterTaskSchedulerUnhandledException();
156157

157158
var imageLoadertask = ImageLoader.InitializeAsync();
158159

@@ -284,6 +285,15 @@ private static void RegisterAppDomainExceptions()
284285
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
285286
}
286287

288+
/// <summary>
289+
/// let exception throw as normal is better for Debug
290+
/// </summary>
291+
[Conditional("RELEASE")]
292+
private static void RegisterTaskSchedulerUnhandledException()
293+
{
294+
TaskScheduler.UnobservedTaskException += ErrorReporting.TaskSchedulerUnobservedTaskException;
295+
}
296+
287297
#endregion
288298

289299
#region IDisposable

Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Globalization;
3+
using System.Windows;
34
using System.Windows.Controls;
45
using System.Windows.Data;
56
using System.Windows.Media;
@@ -43,8 +44,16 @@ values[2] is not string queryText ||
4344

4445
// Check if Text will be larger than our QueryTextBox
4546
Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch);
46-
// TODO: Obsolete warning?
47-
var ft = new FormattedText(queryTextBox.Text, CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black);
47+
var dpi = VisualTreeHelper.GetDpi(queryTextBox);
48+
var ft = new FormattedText(
49+
queryTextBox.Text,
50+
CultureInfo.CurrentCulture,
51+
FlowDirection.LeftToRight,
52+
typeface,
53+
queryTextBox.FontSize,
54+
Brushes.Black,
55+
dpi.PixelsPerDip
56+
);
4857

4958
var offset = queryTextBox.Padding.Right;
5059

0 commit comments

Comments
 (0)