-
-
Notifications
You must be signed in to change notification settings - Fork 622
Expand file tree
/
Copy pathResult.cs
More file actions
422 lines (372 loc) · 16 KB
/
Copy pathResult.cs
File metadata and controls
422 lines (372 loc) · 16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
using System.Text.Json.Serialization;
namespace Flow.Launcher.Plugin
{
/// <summary>
/// Describes a result of a <see cref="Query"/> executed by a plugin.
/// This or its child classes is serializable.
/// </summary>
public class Result
{
/// <summary>
/// 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.
/// </summary>
public const int MaxScore = int.MaxValue;
private string _pluginDirectory;
private string _icoPath;
private string _icoPathAbsolute;
private string _copyText = string.Empty;
private string _badgeIcoPath;
/// <summary>
/// The title of the result. This is always required.
/// </summary>
public string Title { get; set; }
/// <summary>
/// Provides additional details for the result. This is optional
/// </summary>
public string SubTitle { get; set; } = string.Empty;
/// <summary>
/// This holds the action keyword that triggered the result.
/// If result is triggered by global keyword: *, this should be empty.
/// </summary>
public string ActionKeywordAssigned { get; set; }
/// <summary>
/// This holds the text which can be provided by plugin to be copied to the
/// user's clipboard when Ctrl + C is pressed on a result. If the text is a file/directory path
/// flow will copy the actual file/folder instead of just the path text.
/// </summary>
public string CopyText
{
get => string.IsNullOrEmpty(_copyText) ? SubTitle : _copyText;
set => _copyText = value;
}
/// <summary>
/// This holds the text which can be provided by plugin to help Flow autocomplete text
/// for user on the plugin result. If autocomplete action for example is tab, pressing tab will have
/// the default constructed autocomplete text (result's Title), or the text provided here if not empty.
/// </summary>
/// <remarks>
/// When a value is not set, the <see cref="Title"/> will be used.
/// Please include the action keyword prefix when necessary because Flow does not prepend it automatically.
/// </remarks>
public string AutoCompleteText { get; set; }
/// <summary>
/// Path or URI to the icon image for this result.
/// Updates <see cref="IcoPathAbsolute"/> appropriately when set.
/// </summary>
/// <remarks>
/// Preferred usage: provide a path relative to the plugin directory (for example: "Images\icon.png").
/// Because <see cref="IcoPath"/> is serialized, using relative paths keeps the icon reference portable
/// when Flow is moved.
///
/// Accepted formats:
/// - Relative file paths (resolved against <see cref="PluginDirectory"/> into <see cref="IcoPathAbsolute"/>)
/// - Absolute file paths (left as-is)
/// - HTTP/HTTPS URLs (left as-is)
/// - Data URIs (left as-is)
/// </remarks>
public string IcoPath
{
get => _icoPath;
set
{
_icoPath = value;
// As a standard this property will handle prepping and converting to absolute local path for icon image processing
if (!string.IsNullOrEmpty(value)
&& !string.IsNullOrEmpty(PluginDirectory)
&& !Path.IsPathRooted(value)
&& !value.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
&& !value.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
&& !value.StartsWith("data:image", StringComparison.OrdinalIgnoreCase))
{
_icoPathAbsolute = Path.Combine(PluginDirectory, value);
}
else
{
_icoPathAbsolute = value;
}
}
}
/// <summary>
/// Absolute path or URI which is used to load and display the result icon for Flow.
/// This is populated by the <see cref="IcoPath"/> setter.
/// If a relative path was provided to <see cref="IcoPath"/>, this property will contain the resolved
/// absolute local path after combining with <see cref="PluginDirectory"/>.
/// </summary>
public string IcoPathAbsolute => _icoPathAbsolute;
/// <summary>
/// The image to be displayed for the badge of the result.
/// </summary>
/// <value>Can be a local file path or a URL.</value>
/// <remarks>If null or empty, will use plugin icon</remarks>
public string BadgeIcoPath
{
get => _badgeIcoPath;
set
{
// As a standard this property will handle prepping and converting to absolute local path for icon image processing
if (!string.IsNullOrEmpty(value)
&& !string.IsNullOrEmpty(PluginDirectory)
&& !Path.IsPathRooted(value)
&& !value.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
&& !value.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
&& !value.StartsWith("data:image", StringComparison.OrdinalIgnoreCase))
{
_badgeIcoPath = Path.Combine(PluginDirectory, value);
}
else
{
_badgeIcoPath = value;
}
}
}
/// <summary>
/// Determines if Icon has a border radius
/// </summary>
public bool RoundedIcon { get; set; } = false;
/// <summary>
/// Delegate function that produces an <see cref="ImageSource"/>
/// </summary>
/// <returns></returns>
public delegate ImageSource IconDelegate();
/// <summary>
/// Delegate to load an icon for this result.
/// </summary>
[JsonIgnore]
public IconDelegate Icon = null;
/// <summary>
/// Delegate to load an icon for the badge of this result.
/// </summary>
[JsonIgnore]
public IconDelegate BadgeIcon = null;
private GlyphInfo _glyph;
/// <summary>
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)
/// </summary>
public GlyphInfo Glyph
{
get => _glyph;
init => _glyph = value;
}
/// <summary>
/// Set the Glyph Icon after initialization
/// </summary>
/// <param name="glyph"></param>
public void SetGlyph(GlyphInfo glyph)
{
_glyph = glyph;
}
/// <summary>
/// An action to take in the form of a function call when the result has been selected.
/// </summary>
/// <remarks>
/// The function is invoked with an <see cref="ActionContext"/> as the only parameter.
/// Its result determines what happens to Flow Launcher's query form:
/// when true, the form will be hidden; when false, it will stay in focus.
/// </remarks>
[JsonIgnore]
public Func<ActionContext, bool> Action { get; set; }
/// <summary>
/// An async action to take in the form of a function call when the result has been selected.
/// </summary>
/// <remarks>
/// The function is invoked with an <see cref="ActionContext"/> as the only parameter and awaited.
/// Its result determines what happens to Flow Launcher's query form:
/// when true, the form will be hidden; when false, it will stay in focus.
/// </remarks>
[JsonIgnore]
public Func<ActionContext, ValueTask<bool>> AsyncAction { get; set; }
/// <summary>
/// Priority of the current result
/// </summary>
/// <value>default: 0</value>
public int Score { get; set; }
/// <summary>
/// A list of indexes for the characters to be highlighted in Title
/// </summary>
public IList<int> TitleHighlightData { get; set; }
/// <summary>
/// Query information associated with the result
/// </summary>
internal Query OriginQuery { get; set; }
/// <summary>
/// Plugin directory
/// </summary>
public string PluginDirectory
{
get => _pluginDirectory;
set
{
_pluginDirectory = value;
// When the Result object is returned from the query call, PluginDirectory is not provided until
// UpdatePluginMetadata call is made at PluginManager.cs L196. Once the PluginDirectory becomes available
// we need to update (only if not Uri path) the IcoPath and BadgeIcoPath with the full absolute path so the image can be loaded.
IcoPath = _icoPath;
BadgeIcoPath = _badgeIcoPath;
}
}
/// <summary>
/// Additional data associated with this result
/// </summary>
/// <example>
/// As external information for ContextMenu
/// </example>
[JsonIgnore]
public object ContextData { get; set; }
/// <summary>
/// Plugin ID that generated this result
/// </summary>
[JsonInclude]
public string PluginID { get; internal set; }
/// <summary>
/// Show message as ToolTip on result Title hover over
/// </summary>
public string TitleToolTip { get; set; }
/// <summary>
/// Show message as ToolTip on result SubTitle hover over
/// </summary>
public string SubTitleToolTip { get; set; }
/// <summary>
/// Customized Preview Panel
/// </summary>
[JsonIgnore]
public Lazy<UserControl> PreviewPanel { get; set; }
/// <summary>
/// Progress bar display. Providing an int value between 0-100 will trigger the progress bar to be displayed on the result
/// </summary>
public int? ProgressBar { get; set; }
/// <summary>
/// Optionally set the color of the progress bar
/// </summary>
/// <default>#26a0da (blue)</default>
public string ProgressBarColor { get; set; } = "#26a0da";
/// <summary>
/// Contains data used to populate the preview section of this result.
/// </summary>
public PreviewInfo Preview { get; set; } = PreviewInfo.Default;
/// <summary>
/// Determines if the user selection count should be added to the score. This can be useful when set to false to allow the result sequence order to be the same everytime instead of changing based on selection.
/// </summary>
public bool AddSelectedCount { get; set; } = true;
/// <summary>
/// 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.
/// This can be useful when your plugin will change the Title or SubTitle of the result dynamically.
/// If the plugin does not specific this, FL just uses Title and SubTitle to identify this result.
/// Note: Because old data does not have this key, we should use null as the default value for consistency.
/// </summary>
public string RecordKey { get; set; } = null;
/// <summary>
/// Determines if the badge icon should be shown.
/// If users want to show the result badges and here you set this to true, the results will show the badge icon.
/// </summary>
public bool ShowBadge { get; set; } = false;
/// <summary>
/// This holds the text which can be shown as a query suggestion.
/// </summary>
/// <remarks>
/// When a value is not set, the <see cref="Title"/> will be used.
/// Do not include the action keyword prefix because Flow prepends it automatically.
/// If the it does not start with the query text, it will not be shown as a suggestion.
/// So make sure to set this value to start with the query text.
/// </remarks>
public string QuerySuggestionText { get; set; }
/// <summary>
/// Run this result, asynchronously
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public ValueTask<bool> ExecuteAsync(ActionContext context)
{
return AsyncAction?.Invoke(context) ?? ValueTask.FromResult(Action?.Invoke(context) ?? false);
}
/// <inheritdoc />
public override string ToString()
{
return Title + SubTitle + Score;
}
/// <summary>
/// Clones the current result
/// </summary>
public Result Clone()
{
return new Result
{
Title = Title,
SubTitle = SubTitle,
ActionKeywordAssigned = ActionKeywordAssigned,
CopyText = CopyText,
AutoCompleteText = AutoCompleteText,
IcoPath = IcoPath,
BadgeIcoPath = BadgeIcoPath,
RoundedIcon = RoundedIcon,
Icon = Icon,
BadgeIcon = BadgeIcon,
Glyph = Glyph,
Action = Action,
AsyncAction = AsyncAction,
Score = Score,
TitleHighlightData = TitleHighlightData,
OriginQuery = OriginQuery,
PluginDirectory = PluginDirectory,
ContextData = ContextData,
PluginID = PluginID,
TitleToolTip = TitleToolTip,
SubTitleToolTip = SubTitleToolTip,
PreviewPanel = PreviewPanel,
ProgressBar = ProgressBar,
ProgressBarColor = ProgressBarColor,
Preview = Preview,
AddSelectedCount = AddSelectedCount,
RecordKey = RecordKey,
ShowBadge = ShowBadge,
QuerySuggestionText = QuerySuggestionText
};
}
/// <summary>
/// Info of the preview section of a <see cref="Result"/>
/// </summary>
public record PreviewInfo
{
/// <summary>
/// Full image used for preview panel
/// </summary>
public string PreviewImagePath { get; set; } = null;
/// <summary>
/// Determines if the preview image should occupy the full width of the preview panel.
/// </summary>
public bool IsMedia { get; set; } = false;
/// <summary>
/// Result description text that is shown at the bottom of the preview panel.
/// </summary>
/// <remarks>
/// When a value is not set, the <see cref="SubTitle"/> will be used.
/// </remarks>
public string Description { get; set; } = null;
/// <summary>
/// Delegate to get the preview panel's image
/// </summary>
[JsonIgnore]
public IconDelegate PreviewDelegate { get; set; } = null;
/// <summary>
/// File path of the result. For third-party programs providing external preview.
/// </summary>
public string FilePath { get; set; } = null;
/// <summary>
/// Default instance of <see cref="PreviewInfo"/>
/// </summary>
public static PreviewInfo Default { get; } = new()
{
PreviewImagePath = null,
Description = null,
IsMedia = false,
PreviewDelegate = null,
FilePath = null,
};
}
}
}