Skip to content

Commit 8bb96d7

Browse files
authored
Fix copy to clipboard STA thread issue & Support retry for copy & Async build-in shortcut model & Fix build shortcuts text replace issue & Fix startup window hide issue (#3314)
1 parent 950a7f5 commit 8bb96d7

File tree

19 files changed

+439
-155
lines changed

19 files changed

+439
-155
lines changed

Flow.Launcher.Core/Plugin/QueryBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using Flow.Launcher.Plugin;
44

@@ -33,7 +33,7 @@ public static Query Build(string text, Dictionary<string, PluginPair> nonGlobalP
3333
searchTerms = terms;
3434
}
3535

36-
return new Query ()
36+
return new Query()
3737
{
3838
Search = search,
3939
RawQuery = rawQuery,
@@ -42,4 +42,4 @@ public static Query Build(string text, Dictionary<string, PluginPair> nonGlobalP
4242
};
4343
}
4444
}
45-
}
45+
}

Flow.Launcher.Core/Resource/Theme.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public Theme(IPublicAPI publicAPI, Settings settings)
7676
{
7777
_api.LogError(ClassName, "Current theme resource not found. Initializing with default theme.");
7878
_oldTheme = Constant.DefaultTheme;
79-
};
79+
}
8080
}
8181

8282
#endregion
@@ -126,7 +126,7 @@ public void UpdateFonts()
126126
// Load a ResourceDictionary for the specified theme.
127127
var themeName = _settings.Theme;
128128
var dict = GetThemeResourceDictionary(themeName);
129-
129+
130130
// Apply font settings to the theme resource.
131131
ApplyFontSettings(dict);
132132
UpdateResourceDictionary(dict);
@@ -152,11 +152,11 @@ private void ApplyFontSettings(ResourceDictionary dict)
152152
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.QueryBoxFontStyle);
153153
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight);
154154
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch);
155-
155+
156156
SetFontProperties(queryBoxStyle, fontFamily, fontStyle, fontWeight, fontStretch, true);
157157
SetFontProperties(querySuggestionBoxStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
158158
}
159-
159+
160160
if (dict["ItemTitleStyle"] is Style resultItemStyle &&
161161
dict["ItemTitleSelectedStyle"] is Style resultItemSelectedStyle &&
162162
dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle &&
@@ -172,7 +172,7 @@ private void ApplyFontSettings(ResourceDictionary dict)
172172
SetFontProperties(resultHotkeyItemStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
173173
SetFontProperties(resultHotkeyItemSelectedStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
174174
}
175-
175+
176176
if (dict["ItemSubTitleStyle"] is Style resultSubItemStyle &&
177177
dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle)
178178
{
@@ -197,7 +197,7 @@ private static void SetFontProperties(Style style, FontFamily fontFamily, FontSt
197197
// First, find the setters to remove and store them in a list
198198
var settersToRemove = style.Setters
199199
.OfType<Setter>()
200-
.Where(setter =>
200+
.Where(setter =>
201201
setter.Property == Control.FontFamilyProperty ||
202202
setter.Property == Control.FontStyleProperty ||
203203
setter.Property == Control.FontWeightProperty ||
@@ -227,18 +227,18 @@ private static void SetFontProperties(Style style, FontFamily fontFamily, FontSt
227227
{
228228
var settersToRemove = style.Setters
229229
.OfType<Setter>()
230-
.Where(setter =>
230+
.Where(setter =>
231231
setter.Property == TextBlock.FontFamilyProperty ||
232232
setter.Property == TextBlock.FontStyleProperty ||
233233
setter.Property == TextBlock.FontWeightProperty ||
234234
setter.Property == TextBlock.FontStretchProperty)
235235
.ToList();
236-
236+
237237
foreach (var setter in settersToRemove)
238238
{
239239
style.Setters.Remove(setter);
240240
}
241-
241+
242242
style.Setters.Add(new Setter(TextBlock.FontFamilyProperty, fontFamily));
243243
style.Setters.Add(new Setter(TextBlock.FontStyleProperty, fontStyle));
244244
style.Setters.Add(new Setter(TextBlock.FontWeightProperty, fontWeight));
@@ -421,7 +421,7 @@ public bool ChangeTheme(string theme = null)
421421

422422
// Retrieve theme resource – always use the resource with font settings applied.
423423
var resourceDict = GetResourceDictionary(theme);
424-
424+
425425
UpdateResourceDictionary(resourceDict);
426426

427427
_settings.Theme = theme;

Flow.Launcher.Infrastructure/NativeMethods.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ MONITORINFOEXW
4343
WM_ENTERSIZEMOVE
4444
WM_EXITSIZEMOVE
4545

46+
OleInitialize
47+
OleUninitialize
48+
4649
GetKeyboardLayout
4750
GetWindowThreadProcessId
4851
ActivateKeyboardLayout
@@ -53,4 +56,4 @@ INPUTLANGCHANGE_FORWARD
5356
LOCALE_TRANSIENT_KEYBOARD1
5457
LOCALE_TRANSIENT_KEYBOARD2
5558
LOCALE_TRANSIENT_KEYBOARD3
56-
LOCALE_TRANSIENT_KEYBOARD4
59+
LOCALE_TRANSIENT_KEYBOARD4
Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
using System;
22
using System.Text.Json.Serialization;
3+
using System.Threading.Tasks;
34

45
namespace Flow.Launcher.Infrastructure.UserSettings
56
{
7+
#region Base
8+
69
public abstract class ShortcutBaseModel
710
{
811
public string Key { get; set; }
912

10-
[JsonIgnore]
11-
public Func<string> Expand { get; set; } = () => { return ""; };
12-
1313
public override bool Equals(object obj)
1414
{
1515
return obj is ShortcutBaseModel other &&
@@ -22,16 +22,14 @@ public override int GetHashCode()
2222
}
2323
}
2424

25-
public class CustomShortcutModel : ShortcutBaseModel
25+
public class BaseCustomShortcutModel : ShortcutBaseModel
2626
{
2727
public string Value { get; set; }
2828

29-
[JsonConstructorAttribute]
30-
public CustomShortcutModel(string key, string value)
29+
public BaseCustomShortcutModel(string key, string value)
3130
{
3231
Key = key;
3332
Value = value;
34-
Expand = () => { return Value; };
3533
}
3634

3735
public void Deconstruct(out string key, out string value)
@@ -40,26 +38,69 @@ public void Deconstruct(out string key, out string value)
4038
value = Value;
4139
}
4240

43-
public static implicit operator (string Key, string Value)(CustomShortcutModel shortcut)
41+
public static implicit operator (string Key, string Value)(BaseCustomShortcutModel shortcut)
4442
{
4543
return (shortcut.Key, shortcut.Value);
4644
}
4745

48-
public static implicit operator CustomShortcutModel((string Key, string Value) shortcut)
46+
public static implicit operator BaseCustomShortcutModel((string Key, string Value) shortcut)
4947
{
50-
return new CustomShortcutModel(shortcut.Key, shortcut.Value);
48+
return new BaseCustomShortcutModel(shortcut.Key, shortcut.Value);
5149
}
5250
}
5351

54-
public class BuiltinShortcutModel : ShortcutBaseModel
52+
public class BaseBuiltinShortcutModel : ShortcutBaseModel
5553
{
5654
public string Description { get; set; }
5755

58-
public BuiltinShortcutModel(string key, string description, Func<string> expand)
56+
public BaseBuiltinShortcutModel(string key, string description)
5957
{
6058
Key = key;
6159
Description = description;
62-
Expand = expand ?? (() => { return ""; });
6360
}
6461
}
62+
63+
#endregion
64+
65+
#region Custom Shortcut
66+
67+
public class CustomShortcutModel : BaseCustomShortcutModel
68+
{
69+
[JsonIgnore]
70+
public Func<string> Expand { get; set; } = () => { return string.Empty; };
71+
72+
[JsonConstructor]
73+
public CustomShortcutModel(string key, string value) : base(key, value)
74+
{
75+
Expand = () => { return Value; };
76+
}
77+
}
78+
79+
#endregion
80+
81+
#region Builtin Shortcut
82+
83+
public class BuiltinShortcutModel : BaseBuiltinShortcutModel
84+
{
85+
[JsonIgnore]
86+
public Func<string> Expand { get; set; } = () => { return string.Empty; };
87+
88+
public BuiltinShortcutModel(string key, string description, Func<string> expand) : base(key, description)
89+
{
90+
Expand = expand ?? (() => { return string.Empty; });
91+
}
92+
}
93+
94+
public class AsyncBuiltinShortcutModel : BaseBuiltinShortcutModel
95+
{
96+
[JsonIgnore]
97+
public Func<Task<string>> ExpandAsync { get; set; } = () => { return Task.FromResult(string.Empty); };
98+
99+
public AsyncBuiltinShortcutModel(string key, string description, Func<Task<string>> expandAsync) : base(key, description)
100+
{
101+
ExpandAsync = expandAsync ?? (() => { return Task.FromResult(string.Empty); });
102+
}
103+
}
104+
105+
#endregion
65106
}

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,9 @@ public bool KeepMaxResults
312312
public ObservableCollection<CustomShortcutModel> CustomShortcuts { get; set; } = new ObservableCollection<CustomShortcutModel>();
313313

314314
[JsonIgnore]
315-
public ObservableCollection<BuiltinShortcutModel> BuiltinShortcuts { get; set; } = new()
315+
public ObservableCollection<BaseBuiltinShortcutModel> BuiltinShortcuts { get; set; } = new()
316316
{
317-
new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText),
317+
new AsyncBuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", () => Win32Helper.StartSTATaskAsync(Clipboard.GetText)),
318318
new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath)
319319
};
320320

Flow.Launcher.Infrastructure/Win32Helper.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Globalization;
66
using System.Linq;
77
using System.Runtime.InteropServices;
8+
using System.Threading;
9+
using System.Threading.Tasks;
810
using System.Windows;
911
using System.Windows.Interop;
1012
using System.Windows.Markup;
@@ -337,6 +339,78 @@ internal static HWND GetWindowHandle(Window window, bool ensure = false)
337339

338340
#endregion
339341

342+
#region STA Thread
343+
344+
/*
345+
Inspired by https://github.com/files-community/Files code on STA Thread handling.
346+
*/
347+
348+
public static Task StartSTATaskAsync(Action action)
349+
{
350+
var taskCompletionSource = new TaskCompletionSource();
351+
Thread thread = new(() =>
352+
{
353+
PInvoke.OleInitialize();
354+
355+
try
356+
{
357+
action();
358+
taskCompletionSource.SetResult();
359+
}
360+
catch (System.Exception ex)
361+
{
362+
taskCompletionSource.SetException(ex);
363+
}
364+
finally
365+
{
366+
PInvoke.OleUninitialize();
367+
}
368+
})
369+
{
370+
IsBackground = true,
371+
Priority = ThreadPriority.Normal
372+
};
373+
374+
thread.SetApartmentState(ApartmentState.STA);
375+
thread.Start();
376+
377+
return taskCompletionSource.Task;
378+
}
379+
380+
public static Task<T> StartSTATaskAsync<T>(Func<T> func)
381+
{
382+
var taskCompletionSource = new TaskCompletionSource<T>();
383+
384+
Thread thread = new(() =>
385+
{
386+
PInvoke.OleInitialize();
387+
388+
try
389+
{
390+
taskCompletionSource.SetResult(func());
391+
}
392+
catch (System.Exception ex)
393+
{
394+
taskCompletionSource.SetException(ex);
395+
}
396+
finally
397+
{
398+
PInvoke.OleUninitialize();
399+
}
400+
})
401+
{
402+
IsBackground = true,
403+
Priority = ThreadPriority.Normal
404+
};
405+
406+
thread.SetApartmentState(ApartmentState.STA);
407+
thread.Start();
408+
409+
return taskCompletionSource.Task;
410+
}
411+
412+
#endregion
413+
340414
#region Keyboard Layout
341415

342416
private const string UserProfileRegistryPath = @"Control Panel\International\User Profile";

Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,11 @@ public interface IPublicAPI
471471
public Task<bool> UpdatePluginManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default);
472472

473473
/// <summary>
474-
/// Get the plugin manifest
474+
/// Get the plugin manifest.
475475
/// </summary>
476+
/// <remarks>
477+
/// If Flow cannot get manifest data, this could be null
478+
/// </remarks>
476479
/// <returns></returns>
477480
public IReadOnlyList<UserPlugin> GetPluginManifest();
478481

Flow.Launcher.Plugin/Query.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ namespace Flow.Launcher.Plugin
88
public class Query
99
{
1010
/// <summary>
11-
/// Raw query, this includes action keyword if it has
11+
/// Raw query, this includes action keyword if it has.
12+
/// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace.
1213
/// We didn't recommend use this property directly. You should always use Search property.
1314
/// </summary>
1415
public string RawQuery { get; internal init; }
@@ -63,10 +64,10 @@ public class Query
6364
/// </remarks>
6465
[JsonIgnore]
6566
public string FirstSearch => SplitSearch(0);
66-
67+
6768
[JsonIgnore]
6869
private string _secondToEndSearch;
69-
70+
7071
/// <summary>
7172
/// strings from second search (including) to last search
7273
/// </summary>

0 commit comments

Comments
 (0)