Skip to content

Commit 980c1a1

Browse files
authored
Merge branch 'dev' into merge_2_1_0
2 parents cd98253 + 3b3a59f commit 980c1a1

17 files changed

Lines changed: 530 additions & 96 deletions

File tree

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22
<PropertyGroup>
33
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
4-
<!-- Work around https://github.com/dotnet/runtime/issues/109682 -->
4+
<!-- Workaround https://github.com/dotnet/runtime/issues/109682 -->
55
<CETCompat>false</CETCompat>
66
</PropertyGroup>
77
</Project>

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Flow.Launcher.Infrastructure.Hotkey;
88
using Flow.Launcher.Infrastructure.Logger;
99
using Flow.Launcher.Infrastructure.Storage;
10+
using Flow.Launcher.Localization.Attributes;
1011
using Flow.Launcher.Plugin;
1112
using Flow.Launcher.Plugin.SharedModels;
1213

@@ -514,6 +515,21 @@ public bool ShowAtTopmost
514515
[JsonConverter(typeof(JsonStringEnumConverter))]
515516
public LastQueryMode LastQueryMode { get; set; } = LastQueryMode.Selected;
516517

518+
private HistoryStyle _historyStyle = HistoryStyle.Query;
519+
[JsonConverter(typeof(JsonStringEnumConverter))]
520+
public HistoryStyle HistoryStyle
521+
{
522+
get => _historyStyle;
523+
set
524+
{
525+
if (_historyStyle != value)
526+
{
527+
_historyStyle = value;
528+
OnPropertyChanged();
529+
}
530+
}
531+
}
532+
517533
[JsonConverter(typeof(JsonStringEnumConverter))]
518534
public AnimationSpeeds AnimationSpeed { get; set; } = AnimationSpeeds.Medium;
519535
public int CustomAnimationLength { get; set; } = 360;
@@ -696,4 +712,14 @@ public enum DialogJumpFileResultBehaviours
696712
FullPathOpen,
697713
Directory
698714
}
715+
716+
[EnumLocalize]
717+
public enum HistoryStyle
718+
{
719+
[EnumLocalizeKey(nameof(Localize.queryHistory))]
720+
Query,
721+
722+
[EnumLocalizeKey(nameof(Localize.executedHistory))]
723+
LastOpened
724+
}
699725
}

Flow.Launcher.Plugin/Result.cs

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
using System.Threading.Tasks;
55
using System.Windows.Controls;
66
using System.Windows.Media;
7+
using System.Text.Json.Serialization;
78

89
namespace Flow.Launcher.Plugin
910
{
1011
/// <summary>
11-
/// Describes a result of a <see cref="Query"/> executed by a plugin
12+
/// Describes a result of a <see cref="Query"/> executed by a plugin.
13+
/// This or its child classes is serializable.
1214
/// </summary>
1315
public class Result
1416
{
@@ -21,6 +23,8 @@ public class Result
2123

2224
private string _icoPath;
2325

26+
private string _icoPathAbsolute;
27+
2428
private string _copyText = string.Empty;
2529

2630
private string _badgeIcoPath;
@@ -64,15 +68,27 @@ public string CopyText
6468
public string AutoCompleteText { get; set; }
6569

6670
/// <summary>
67-
/// The image to be displayed for the result.
71+
/// Path or URI to the icon image for this result.
72+
/// Updates <see cref="IcoPathAbsolute"/> appropriately when set.
6873
/// </summary>
69-
/// <value>Can be a local file path or a URL.</value>
70-
/// <remarks>GlyphInfo is prioritized if not null</remarks>
74+
/// <remarks>
75+
/// Preferred usage: provide a path relative to the plugin directory (for example: "Images\icon.png").
76+
/// Because <see cref="IcoPath"/> is serialized, using relative paths keeps the icon reference portable
77+
/// when Flow is moved.
78+
///
79+
/// Accepted formats:
80+
/// - Relative file paths (resolved against <see cref="PluginDirectory"/> into <see cref="IcoPathAbsolute"/>)
81+
/// - Absolute file paths (left as-is)
82+
/// - HTTP/HTTPS URLs (left as-is)
83+
/// - Data URIs (left as-is)
84+
/// </remarks>
7185
public string IcoPath
7286
{
7387
get => _icoPath;
7488
set
7589
{
90+
_icoPath = value;
91+
7692
// As a standard this property will handle prepping and converting to absolute local path for icon image processing
7793
if (!string.IsNullOrEmpty(value)
7894
&& !string.IsNullOrEmpty(PluginDirectory)
@@ -81,15 +97,23 @@ public string IcoPath
8197
&& !value.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
8298
&& !value.StartsWith("data:image", StringComparison.OrdinalIgnoreCase))
8399
{
84-
_icoPath = Path.Combine(PluginDirectory, value);
100+
_icoPathAbsolute = Path.Combine(PluginDirectory, value);
85101
}
86102
else
87103
{
88-
_icoPath = value;
104+
_icoPathAbsolute = value;
89105
}
90106
}
91107
}
92108

109+
/// <summary>
110+
/// Absolute path or URI which is used to load and display the result icon for Flow.
111+
/// This is populated by the <see cref="IcoPath"/> setter.
112+
/// If a relative path was provided to <see cref="IcoPath"/>, this property will contain the resolved
113+
/// absolute local path after combining with <see cref="PluginDirectory"/>.
114+
/// </summary>
115+
public string IcoPathAbsolute => _icoPathAbsolute;
116+
93117
/// <summary>
94118
/// The image to be displayed for the badge of the result.
95119
/// </summary>
@@ -131,17 +155,34 @@ public string BadgeIcoPath
131155
/// <summary>
132156
/// Delegate to load an icon for this result.
133157
/// </summary>
158+
[JsonIgnore]
134159
public IconDelegate Icon = null;
135160

136161
/// <summary>
137162
/// Delegate to load an icon for the badge of this result.
138163
/// </summary>
164+
[JsonIgnore]
139165
public IconDelegate BadgeIcon = null;
140166

167+
private GlyphInfo _glyph;
168+
141169
/// <summary>
142170
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)
143171
/// </summary>
144-
public GlyphInfo Glyph { get; init; }
172+
public GlyphInfo Glyph
173+
{
174+
get => _glyph;
175+
init => _glyph = value;
176+
}
177+
178+
/// <summary>
179+
/// Set the Glyph Icon after initialization
180+
/// </summary>
181+
/// <param name="glyph"></param>
182+
public void SetGlyph(GlyphInfo glyph)
183+
{
184+
_glyph = glyph;
185+
}
145186

146187
/// <summary>
147188
/// An action to take in the form of a function call when the result has been selected.
@@ -151,6 +192,7 @@ public string BadgeIcoPath
151192
/// Its result determines what happens to Flow Launcher's query form:
152193
/// when true, the form will be hidden; when false, it will stay in focus.
153194
/// </remarks>
195+
[JsonIgnore]
154196
public Func<ActionContext, bool> Action { get; set; }
155197

156198
/// <summary>
@@ -161,6 +203,7 @@ public string BadgeIcoPath
161203
/// Its result determines what happens to Flow Launcher's query form:
162204
/// when true, the form will be hidden; when false, it will stay in focus.
163205
/// </remarks>
206+
[JsonIgnore]
164207
public Func<ActionContext, ValueTask<bool>> AsyncAction { get; set; }
165208

166209
/// <summary>
@@ -203,11 +246,13 @@ public string PluginDirectory
203246
/// <example>
204247
/// As external information for ContextMenu
205248
/// </example>
249+
[JsonIgnore]
206250
public object ContextData { get; set; }
207251

208252
/// <summary>
209253
/// Plugin ID that generated this result
210254
/// </summary>
255+
[JsonInclude]
211256
public string PluginID { get; internal set; }
212257

213258
/// <summary>
@@ -223,6 +268,7 @@ public string PluginDirectory
223268
/// <summary>
224269
/// Customized Preview Panel
225270
/// </summary>
271+
[JsonIgnore]
226272
public Lazy<UserControl> PreviewPanel { get; set; }
227273

228274
/// <summary>
@@ -352,6 +398,7 @@ public record PreviewInfo
352398
/// <summary>
353399
/// Delegate to get the preview panel's image
354400
/// </summary>
401+
[JsonIgnore]
355402
public IconDelegate PreviewDelegate { get; set; } = null;
356403

357404
/// <summary>

Flow.Launcher/App.xaml.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () =>
259259

260260
await PluginManager.InitializePluginsAsync(_mainVM);
261261

262+
// Refresh the history results after plugins are initialized so that we can parse the absolute icon paths
263+
_mainVM.RefreshLastOpenedHistoryResults();
264+
262265
// Refresh home page after plugins are initialized because users may open main window during plugin initialization
263266
// And home page is created without full plugin list
264267
if (_settings.ShowHomePage && _mainVM.QueryResultsSelected() && string.IsNullOrEmpty(_mainVM.QueryText))

Flow.Launcher/Flow.Launcher.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@
185185
</Target>
186186

187187
<Target Name="RemoveDuplicateAnalyzers" BeforeTargets="CoreCompile">
188-
<!-- Work around https://github.com/dotnet/wpf/issues/6792 -->
188+
<!-- Workaround https://github.com/dotnet/wpf/issues/6792 -->
189189
<ItemGroup>
190190
<FilteredAnalyzer Include="@(Analyzer-&gt;Distinct())" />
191191
<Analyzer Remove="@(Analyzer)" />
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System.Linq;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Flow.Launcher.Core.Plugin;
5+
using Flow.Launcher.Plugin;
6+
using Flow.Launcher.Storage;
7+
8+
namespace Flow.Launcher.Helper;
9+
10+
#nullable enable
11+
12+
public static class ResultHelper
13+
{
14+
public static async Task<Result?> PopulateResultsAsync(LastOpenedHistoryResult item)
15+
{
16+
return await PopulateResultsAsync(item.PluginID, item.Query, item.Title, item.SubTitle, item.RecordKey);
17+
}
18+
19+
public static async Task<Result?> PopulateResultsAsync(string pluginId, string trimmedQuery, string title, string subTitle, string recordKey)
20+
{
21+
var plugin = PluginManager.GetPluginForId(pluginId);
22+
if (plugin == null) return null;
23+
var query = QueryBuilder.Build(trimmedQuery, trimmedQuery, PluginManager.GetNonGlobalPlugins());
24+
if (query == null) return null;
25+
try
26+
{
27+
var freshResults = await PluginManager.QueryForPluginAsync(plugin, query, CancellationToken.None);
28+
// Try to match by record key first if it is valid, otherwise fall back to title + subtitle match
29+
if (string.IsNullOrEmpty(recordKey))
30+
{
31+
return freshResults?.FirstOrDefault(r => r.Title == title && r.SubTitle == subTitle);
32+
}
33+
else
34+
{
35+
return freshResults?.FirstOrDefault(r => r.RecordKey == recordKey) ??
36+
freshResults?.FirstOrDefault(r => r.Title == title && r.SubTitle == subTitle);
37+
}
38+
}
39+
catch (System.Exception e)
40+
{
41+
App.API.LogException(nameof(ResultHelper), $"Failed to query results for {plugin.Metadata.Name}", e);
42+
return null;
43+
}
44+
}
45+
}

Flow.Launcher/Languages/en.xaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@
172172
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
173173
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
174174
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
175+
<system:String x:Key="historyStyle">History Style</system:String>
176+
<system:String x:Key="historyStyleTooltip">Choose the type of history to show in the History and Home Page</system:String>
177+
<system:String x:Key="queryHistory">Query history</system:String>
178+
<system:String x:Key="executedHistory">Last opened history</system:String>
175179
<system:String x:Key="homeToggleBoxToolTip">This can only be edited if plugin supports Home feature and Home Page is enabled.</system:String>
176180
<system:String x:Key="showAtTopmost">Show Search Window at Foremost</system:String>
177181
<system:String x:Key="showAtTopmostToolTip">Overrides other programs' 'Always on Top' setting and displays Flow in the foremost position.</system:String>

Flow.Launcher/MainWindow.xaml.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ private void OnLoaded(object sender, RoutedEventArgs e)
322322
break;
323323
case nameof(Settings.ShowHomePage):
324324
case nameof(Settings.ShowHistoryResultsForHomePage):
325+
case nameof(Settings.HistoryStyle):
325326
if (_viewModel.QueryResultsSelected() && string.IsNullOrEmpty(_viewModel.QueryText))
326327
{
327328
_viewModel.QueryResults();
@@ -859,7 +860,7 @@ private void InitializeContextMenu()
859860

860861
public void UpdatePosition()
861862
{
862-
// Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
863+
// Initialize call twice to workaround multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
863864
if (_viewModel.IsDialogJumpWindowUnderDialog())
864865
{
865866
InitializeDialogJumpPosition();
@@ -883,7 +884,7 @@ private async Task PositionResetAsync()
883884

884885
private void InitializePosition()
885886
{
886-
// Initialize call twice to work around multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
887+
// Initialize call twice to workaround multi-display alignment issue- https://github.com/Flow-Launcher/Flow.Launcher/issues/2910
887888
InitializePositionInner();
888889
InitializePositionInner();
889890
return;

Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ public bool PortableMode
147147
public List<LastQueryModeData> LastQueryModes { get; } =
148148
DropdownDataGeneric<LastQueryMode>.GetValues<LastQueryModeData>("LastQuery");
149149

150+
public List<HistoryStyleLocalized> HistoryStyles { get; } = HistoryStyleLocalized.GetValues();
151+
150152
public bool EnableDialogJump
151153
{
152154
get => Settings.EnableDialogJump;
@@ -213,6 +215,7 @@ private void UpdateEnumDropdownLocalizations()
213215
DropdownDataGeneric<SearchWindowAligns>.UpdateLabels(SearchWindowAligns);
214216
DropdownDataGeneric<SearchPrecisionScore>.UpdateLabels(SearchPrecisionScores);
215217
DropdownDataGeneric<LastQueryMode>.UpdateLabels(LastQueryModes);
218+
HistoryStyleLocalized.UpdateLabels(HistoryStyles);
216219
DropdownDataGeneric<DoublePinyinSchemas>.UpdateLabels(DoublePinyinSchemas);
217220
DropdownDataGeneric<DialogJumpWindowPositions>.UpdateLabels(DialogJumpWindowPositions);
218221
DropdownDataGeneric<DialogJumpResultBehaviours>.UpdateLabels(DialogJumpResultBehaviours);

Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,22 @@
255255
SelectedValuePath="Value" />
256256
</ui:SettingsCard>
257257

258+
<ui:SettingsCard
259+
Margin="0 14 0 0"
260+
Description="{DynamicResource historyStyleTooltip}"
261+
Header="{DynamicResource historyStyle}">
262+
<ui:SettingsCard.HeaderIcon>
263+
<ui:FontIcon Glyph="&#xE81C;" />
264+
</ui:SettingsCard.HeaderIcon>
265+
266+
<ComboBox
267+
MaxWidth="200"
268+
DisplayMemberPath="Display"
269+
ItemsSource="{Binding HistoryStyles}"
270+
SelectedValue="{Binding Settings.HistoryStyle}"
271+
SelectedValuePath="Value" />
272+
</ui:SettingsCard>
273+
258274
<ui:SettingsCard
259275
Margin="0 14 0 0"
260276
Description="{DynamicResource autoRestartAfterChangingToolTip}"

0 commit comments

Comments
 (0)