Skip to content

Commit 643e531

Browse files
authored
Merge branch 'dev' into 250412-ImportThemePreset
2 parents 4ed9137 + 9b5d22a commit 643e531

File tree

14 files changed

+354
-148
lines changed

14 files changed

+354
-148
lines changed

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ public string Theme
166166
public bool UseAnimation { get; set; } = true;
167167
public bool UseSound { get; set; } = true;
168168
public double SoundVolume { get; set; } = 50;
169+
public bool ShowBadges { get; set; } = false;
170+
public bool ShowBadgesGlobalOnly { get; set; } = false;
169171

170172
public bool UseClock { get; set; } = true;
171173
public bool UseDate { get; set; } = false;

Flow.Launcher.Plugin/Result.cs

Lines changed: 104 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,19 @@ namespace Flow.Launcher.Plugin
1212
/// </summary>
1313
public class Result
1414
{
15+
/// <summary>
16+
/// Maximum score. This can be useful when set one result to the top by default. This is the score for the results set to the topmost by users.
17+
/// </summary>
18+
public const int MaxScore = int.MaxValue;
19+
1520
private string _pluginDirectory;
1621

1722
private string _icoPath;
1823

1924
private string _copyText = string.Empty;
2025

26+
private string _badgeIcoPath;
27+
2128
/// <summary>
2229
/// The title of the result. This is always required.
2330
/// </summary>
@@ -60,7 +67,7 @@ public string CopyText
6067
/// <remarks>GlyphInfo is prioritized if not null</remarks>
6168
public string IcoPath
6269
{
63-
get { return _icoPath; }
70+
get => _icoPath;
6471
set
6572
{
6673
// As a standard this property will handle prepping and converting to absolute local path for icon image processing
@@ -80,6 +87,33 @@ public string IcoPath
8087
}
8188
}
8289

90+
/// <summary>
91+
/// The image to be displayed for the badge of the result.
92+
/// </summary>
93+
/// <value>Can be a local file path or a URL.</value>
94+
/// <remarks>If null or empty, will use plugin icon</remarks>
95+
public string BadgeIcoPath
96+
{
97+
get => _badgeIcoPath;
98+
set
99+
{
100+
// As a standard this property will handle prepping and converting to absolute local path for icon image processing
101+
if (!string.IsNullOrEmpty(value)
102+
&& !string.IsNullOrEmpty(PluginDirectory)
103+
&& !Path.IsPathRooted(value)
104+
&& !value.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
105+
&& !value.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
106+
&& !value.StartsWith("data:image", StringComparison.OrdinalIgnoreCase))
107+
{
108+
_badgeIcoPath = Path.Combine(PluginDirectory, value);
109+
}
110+
else
111+
{
112+
_badgeIcoPath = value;
113+
}
114+
}
115+
}
116+
83117
/// <summary>
84118
/// Determines if Icon has a border radius
85119
/// </summary>
@@ -94,14 +128,18 @@ public string IcoPath
94128
/// <summary>
95129
/// Delegate to load an icon for this result.
96130
/// </summary>
97-
public IconDelegate Icon;
131+
public IconDelegate Icon { get; set; }
132+
133+
/// <summary>
134+
/// Delegate to load an icon for the badge of this result.
135+
/// </summary>
136+
public IconDelegate BadgeIcon { get; set; }
98137

99138
/// <summary>
100139
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)
101140
/// </summary>
102141
public GlyphInfo Glyph { get; init; }
103142

104-
105143
/// <summary>
106144
/// An action to take in the form of a function call when the result has been selected.
107145
/// </summary>
@@ -143,59 +181,19 @@ public string IcoPath
143181
/// </summary>
144182
public string PluginDirectory
145183
{
146-
get { return _pluginDirectory; }
184+
get => _pluginDirectory;
147185
set
148186
{
149187
_pluginDirectory = value;
150188

151189
// When the Result object is returned from the query call, PluginDirectory is not provided until
152190
// UpdatePluginMetadata call is made at PluginManager.cs L196. Once the PluginDirectory becomes available
153-
// we need to update (only if not Uri path) the IcoPath with the full absolute path so the image can be loaded.
191+
// we need to update (only if not Uri path) the IcoPath and BadgeIcoPath with the full absolute path so the image can be loaded.
154192
IcoPath = _icoPath;
193+
BadgeIcoPath = _badgeIcoPath;
155194
}
156195
}
157196

158-
/// <inheritdoc />
159-
public override string ToString()
160-
{
161-
return Title + SubTitle + Score;
162-
}
163-
164-
/// <summary>
165-
/// Clones the current result
166-
/// </summary>
167-
public Result Clone()
168-
{
169-
return new Result
170-
{
171-
Title = Title,
172-
SubTitle = SubTitle,
173-
ActionKeywordAssigned = ActionKeywordAssigned,
174-
CopyText = CopyText,
175-
AutoCompleteText = AutoCompleteText,
176-
IcoPath = IcoPath,
177-
RoundedIcon = RoundedIcon,
178-
Icon = Icon,
179-
Glyph = Glyph,
180-
Action = Action,
181-
AsyncAction = AsyncAction,
182-
Score = Score,
183-
TitleHighlightData = TitleHighlightData,
184-
OriginQuery = OriginQuery,
185-
PluginDirectory = PluginDirectory,
186-
ContextData = ContextData,
187-
PluginID = PluginID,
188-
TitleToolTip = TitleToolTip,
189-
SubTitleToolTip = SubTitleToolTip,
190-
PreviewPanel = PreviewPanel,
191-
ProgressBar = ProgressBar,
192-
ProgressBarColor = ProgressBarColor,
193-
Preview = Preview,
194-
AddSelectedCount = AddSelectedCount,
195-
RecordKey = RecordKey
196-
};
197-
}
198-
199197
/// <summary>
200198
/// Additional data associated with this result
201199
/// </summary>
@@ -224,16 +222,6 @@ public Result Clone()
224222
/// </summary>
225223
public Lazy<UserControl> PreviewPanel { get; set; }
226224

227-
/// <summary>
228-
/// Run this result, asynchronously
229-
/// </summary>
230-
/// <param name="context"></param>
231-
/// <returns></returns>
232-
public ValueTask<bool> ExecuteAsync(ActionContext context)
233-
{
234-
return AsyncAction?.Invoke(context) ?? ValueTask.FromResult(Action?.Invoke(context) ?? false);
235-
}
236-
237225
/// <summary>
238226
/// Progress bar display. Providing an int value between 0-100 will trigger the progress bar to be displayed on the result
239227
/// </summary>
@@ -255,11 +243,6 @@ public ValueTask<bool> ExecuteAsync(ActionContext context)
255243
/// </summary>
256244
public bool AddSelectedCount { get; set; } = true;
257245

258-
/// <summary>
259-
/// Maximum score. This can be useful when set one result to the top by default. This is the score for the results set to the topmost by users.
260-
/// </summary>
261-
public const int MaxScore = int.MaxValue;
262-
263246
/// <summary>
264247
/// The key to identify the record. This is used when FL checks whether the result is the topmost record. Or FL calculates the hashcode of the result for user selected records.
265248
/// This can be useful when your plugin will change the Title or SubTitle of the result dynamically.
@@ -268,6 +251,66 @@ public ValueTask<bool> ExecuteAsync(ActionContext context)
268251
/// </summary>
269252
public string RecordKey { get; set; } = null;
270253

254+
/// <summary>
255+
/// Determines if the badge icon should be shown.
256+
/// If users want to show the result badges and here you set this to true, the results will show the badge icon.
257+
/// </summary>
258+
public bool ShowBadge { get; set; } = false;
259+
260+
/// <summary>
261+
/// Run this result, asynchronously
262+
/// </summary>
263+
/// <param name="context"></param>
264+
/// <returns></returns>
265+
public ValueTask<bool> ExecuteAsync(ActionContext context)
266+
{
267+
return AsyncAction?.Invoke(context) ?? ValueTask.FromResult(Action?.Invoke(context) ?? false);
268+
}
269+
270+
/// <inheritdoc />
271+
public override string ToString()
272+
{
273+
return Title + SubTitle + Score;
274+
}
275+
276+
/// <summary>
277+
/// Clones the current result
278+
/// </summary>
279+
public Result Clone()
280+
{
281+
return new Result
282+
{
283+
Title = Title,
284+
SubTitle = SubTitle,
285+
ActionKeywordAssigned = ActionKeywordAssigned,
286+
CopyText = CopyText,
287+
AutoCompleteText = AutoCompleteText,
288+
IcoPath = IcoPath,
289+
BadgeIcoPath = BadgeIcoPath,
290+
RoundedIcon = RoundedIcon,
291+
Icon = Icon,
292+
BadgeIcon = BadgeIcon,
293+
Glyph = Glyph,
294+
Action = Action,
295+
AsyncAction = AsyncAction,
296+
Score = Score,
297+
TitleHighlightData = TitleHighlightData,
298+
OriginQuery = OriginQuery,
299+
PluginDirectory = PluginDirectory,
300+
ContextData = ContextData,
301+
PluginID = PluginID,
302+
TitleToolTip = TitleToolTip,
303+
SubTitleToolTip = SubTitleToolTip,
304+
PreviewPanel = PreviewPanel,
305+
ProgressBar = ProgressBar,
306+
ProgressBarColor = ProgressBarColor,
307+
Preview = Preview,
308+
AddSelectedCount = AddSelectedCount,
309+
RecordKey = RecordKey,
310+
ShowBadge = ShowBadge,
311+
};
312+
}
313+
271314
/// <summary>
272315
/// Info of the preview section of a <see cref="Result"/>
273316
/// </summary>

Flow.Launcher.Test/Plugins/ExplorerTest.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestric
3939
}
4040

4141
[SupportedOSPlatform("windows7.0")]
42-
[TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY System.FileName")]
43-
[TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY System.FileName")]
42+
[TestCase("C:\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
43+
[TestCase("C:\\SomeFolder\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
4444
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
4545
{
4646
// Given
@@ -59,7 +59,7 @@ public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_Then
5959
[TestCase("C:\\SomeFolder", "flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType" +
6060
" FROM SystemIndex WHERE directory='file:C:\\SomeFolder'" +
6161
" AND (System.FileName LIKE 'flow.launcher.sln%' OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"'))" +
62-
" ORDER BY System.FileName")]
62+
$" ORDER BY {QueryConstructor.OrderIdentifier}")]
6363
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
6464
string folderPath, string userSearchString, string expectedString)
6565
{
@@ -87,8 +87,8 @@ public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereR
8787
[SupportedOSPlatform("windows7.0")]
8888
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " +
8989
"FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
90-
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")]
91-
[TestCase("", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY System.FileName")]
90+
$"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
91+
[TestCase("", $"SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
9292
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
9393
string userSearchString, string expectedString)
9494
{
@@ -107,7 +107,6 @@ public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShould
107107
ClassicAssert.AreEqual(expectedString, resultString);
108108
}
109109

110-
111110
[SupportedOSPlatform("windows7.0")]
112111
[TestCase(@"some words", @"FREETEXT('some words')")]
113112
public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString(
@@ -127,7 +126,7 @@ public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSe
127126

128127
[SupportedOSPlatform("windows7.0")]
129128
[TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
130-
"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")]
129+
$"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
131130
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
132131
string userSearchString, string expectedString)
133132
{
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows.Data;
4+
5+
namespace Flow.Launcher.Converters;
6+
7+
public class BadgePositionConverter : IValueConverter
8+
{
9+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10+
{
11+
if (value is double actualWidth && parameter is string param)
12+
{
13+
double offset = actualWidth / 2 - 8;
14+
15+
if (param == "1") // X-Offset
16+
{
17+
return offset + 2;
18+
}
19+
else if (param == "2") // Y-Offset
20+
{
21+
return offset + 2;
22+
}
23+
}
24+
25+
return 0.0;
26+
}
27+
28+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
29+
{
30+
throw new NotSupportedException();
31+
}
32+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Windows.Data;
2+
using System;
3+
using System.Globalization;
4+
using System.Windows;
5+
6+
namespace Flow.Launcher.Converters;
7+
8+
public class SizeRatioConverter : IValueConverter
9+
{
10+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
11+
{
12+
if (value is double size && parameter is string ratioString)
13+
{
14+
if (double.TryParse(ratioString, NumberStyles.Any, CultureInfo.InvariantCulture, out double ratio))
15+
{
16+
return size * ratio;
17+
}
18+
}
19+
20+
return 0.0;
21+
}
22+
23+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
24+
{
25+
throw new NotSupportedException();
26+
}
27+
}

Flow.Launcher/Languages/en.xaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@
302302
<system:String x:Key="useGlyphUI">Use Segoe Fluent Icons</system:String>
303303
<system:String x:Key="useGlyphUIEffect">Use Segoe Fluent Icons for query results where supported</system:String>
304304
<system:String x:Key="flowlauncherPressHotkey">Press Key</system:String>
305+
<system:String x:Key="showBadges">Show Result Badges</system:String>
306+
<system:String x:Key="showBadgesToolTip">Show badges for query results where supported</system:String>
307+
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
308+
<system:String x:Key="showBadgesGlobalOnlyToolTip">Show badges for global query results only</system:String>
305309

306310
<!-- Setting Proxy -->
307311
<system:String x:Key="proxy">HTTP Proxy</system:String>

0 commit comments

Comments
 (0)