Skip to content

Commit ee106e5

Browse files
committed
Merge branch 'feature/pinned-results' of https://github.com/01Dri/Flow.Launcher into feature/pinned-results
2 parents 91016e7 + 3e2199b commit ee106e5

16 files changed

Lines changed: 319 additions & 93 deletions

File tree

.github/actions/spelling/expect.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,7 @@ Msix
108108
dummyprofile
109109
browserbookmark
110110
copyurl
111+
uninstaller
112+
taskbar
113+
taskbars
114+
ikw

.github/workflows/spelling.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
steps:
7373
- name: check-spelling
7474
id: spelling
75-
uses: check-spelling/check-spelling@v0.0.25
75+
uses: check-spelling/check-spelling@v0.0.26
7676
with:
7777
suppress_push_for_open_pull_request: 1
7878
checkout: true
@@ -128,7 +128,7 @@ jobs:
128128
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
129129
steps:
130130
- name: comment
131-
uses: check-spelling/check-spelling@v0.0.25
131+
uses: check-spelling/check-spelling@v0.0.26
132132
with:
133133
checkout: true
134134
spell_check_this: check-spelling/spell-check-this@main

Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ internal AbstractPluginEnvironment(List<PluginMetadata> pluginMetadataList, Plug
4040
PluginSettings = pluginSettings;
4141
}
4242

43+
/// <summary>
44+
/// Resolves the configured runtime executable path to an absolute path.
45+
/// Supports both absolute paths and relative paths (relative to ProgramDirectory).
46+
/// </summary>
47+
private string ResolvedPluginsSettingsFilePath => FilesFolders.ResolveAbsolutePath(PluginsSettingsFilePath);
48+
4349
internal IEnumerable<PluginPair> Setup()
4450
{
4551
// If no plugin is using the language, return empty list
@@ -48,13 +54,16 @@ internal IEnumerable<PluginPair> Setup()
4854
return new List<PluginPair>();
4955
}
5056

51-
if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath))
57+
var resolvedPath = ResolvedPluginsSettingsFilePath;
58+
if (!string.IsNullOrEmpty(resolvedPath) && FilesFolders.FileExists(resolvedPath))
5259
{
5360
// Ensure latest only if user is using Flow's environment setup.
54-
if (PluginsSettingsFilePath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase))
55-
EnsureLatestInstalled(ExecutablePath, PluginsSettingsFilePath, EnvPath);
61+
if (resolvedPath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase))
62+
EnsureLatestInstalled(ExecutablePath, resolvedPath, EnvPath);
5663

57-
return SetPathForPluginPairs(PluginsSettingsFilePath, Language);
64+
// Ensure the path is updated in settings in case environment was updated
65+
resolvedPath = ResolvedPluginsSettingsFilePath;
66+
return SetPathForPluginPairs(resolvedPath, Language);
5867
}
5968

6069
var noRuntimeMessage = Localize.runtimePluginInstalledChooseRuntimePrompt(Language, EnvName, Environment.NewLine);
@@ -103,9 +112,11 @@ internal IEnumerable<PluginPair> Setup()
103112
InstallEnvironment();
104113
}
105114

106-
if (FilesFolders.FileExists(PluginsSettingsFilePath))
115+
// Ensure the path is updated when user has chosen to install or select environment executable
116+
resolvedPath = ResolvedPluginsSettingsFilePath;
117+
if (FilesFolders.FileExists(resolvedPath))
107118
{
108-
return SetPathForPluginPairs(PluginsSettingsFilePath, Language);
119+
return SetPathForPluginPairs(resolvedPath, Language);
109120
}
110121
else
111122
{

Flow.Launcher.Core/Resource/Theme.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,30 @@ private ResourceDictionary GetResourceDictionary(string theme)
318318
=> Array.ForEach(setters, p => o.Setters.Add(p)));
319319
}
320320

321+
var baseHomeIconStyle = dict["BaseHomeIconFontStyle"] as Style;
322+
var itemTitleStyle = (dict["ItemTitleStyle"] ?? dict["BaseItemTitleStyle"]) as Style;
323+
var homeIconFontStyle = dict["HomeIconFontStyle"] as Style;
324+
325+
if (homeIconFontStyle == null && itemTitleStyle != null)
326+
{
327+
var styleToExtend = baseHomeIconStyle ?? itemTitleStyle;
328+
var dynamicHomeStyle = new Style(typeof(TextBlock), styleToExtend);
329+
330+
var foregroundSetter = itemTitleStyle.Setters
331+
.OfType<Setter>()
332+
.FirstOrDefault(s => s.Property == TextBlock.ForegroundProperty);
333+
334+
if (foregroundSetter != null)
335+
{
336+
dynamicHomeStyle.Setters.Add(new Setter(TextBlock.ForegroundProperty, foregroundSetter.Value));
337+
}
338+
else if (baseHomeIconStyle != null)
339+
{
340+
dynamicHomeStyle.Setters.Add(new Setter(TextBlock.ForegroundProperty, new DynamicResourceExtension("ItemTitleStyle")));
341+
}
342+
dict["HomeIconFontStyle"] = dynamicHomeStyle;
343+
}
344+
321345
/* Ignore Theme Window Width and use setting */
322346
var windowStyle = dict["WindowStyle"] as Style;
323347
var width = _settings.WindowSize;

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ public string Theme
126126
public double QueryBoxFontSize { get; set; } = 16;
127127
public double ResultItemFontSize { get; set; } = 16;
128128
public double ResultSubItemFontSize { get; set; } = 13;
129+
public double HomeScreenFontSize { get; set; } = 12;
129130
public string QueryBoxFont { get; set; } = Win32Helper.GetSystemDefaultFont();
130131
public string QueryBoxFontStyle { get; set; }
131132
public string QueryBoxFontWeight { get; set; }

Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Diagnostics;
33
using System.IO;
44
using System.Linq;
5+
using System.Reflection;
6+
57
#pragma warning disable IDE0005
68
using System.Windows;
79
#pragma warning restore IDE0005
@@ -487,5 +489,34 @@ public static void ValidateDataDirectory(string bundledDataDirectory, string dat
487489
}
488490
}
489491
}
492+
493+
/// <summary>
494+
/// Resolves a path that may be relative to an absolute path.
495+
/// If the path is already absolute, returns it as-is.
496+
/// If the path is not rooted (as determined by <see cref="Path.IsPathRooted(string)"/>), resolves it relative to ProgramDirectory.
497+
/// </summary>
498+
/// <param name="path">The path to resolve</param>
499+
/// <returns>An absolute path</returns>
500+
public static string ResolveAbsolutePath(string path)
501+
{
502+
if (string.IsNullOrEmpty(path))
503+
return path;
504+
505+
// If already absolute, return as-is
506+
if (Path.IsPathFullyQualified(path))
507+
return path;
508+
509+
// Resolve relative to ProgramDirectory, handling invalid path formats gracefully
510+
try
511+
{
512+
return Path.GetFullPath(Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location).ToString(), path));
513+
}
514+
catch (Exception)
515+
{
516+
// If the path cannot be resolved (invalid characters, format, or too long),
517+
// return the original path to avoid crashing the application.
518+
return path;
519+
}
520+
}
490521
}
491522
}

Flow.Launcher/Languages/en.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@
253253
<system:String x:Key="pluginStore_RecentlyUpdated">Recently Updated</system:String>
254254
<system:String x:Key="pluginStore_None">Plugins</system:String>
255255
<system:String x:Key="pluginStore_Installed">Installed</system:String>
256+
<system:String x:Key="PluginStoreSortModeDefault">Default</system:String>
257+
<system:String x:Key="PluginStoreSortModeName">Name</system:String>
258+
<system:String x:Key="PluginStoreSortModeReleaseDate">Release Date</system:String>
259+
<system:String x:Key="PluginStoreSortModeUpdatedDate">Updated Date</system:String>
260+
<system:String x:Key="PluginStoreSortingModeComboboxLabel">Sorting Mode:</system:String>
256261
<system:String x:Key="refresh">Refresh</system:String>
257262
<system:String x:Key="installbtn">Install</system:String>
258263
<system:String x:Key="uninstallbtn">Uninstall</system:String>
@@ -309,6 +314,7 @@
309314
<system:String x:Key="queryBoxFont">Query Box Font</system:String>
310315
<system:String x:Key="resultItemFont">Result Title Font</system:String>
311316
<system:String x:Key="resultSubItemFont">Result Subtitle Font</system:String>
317+
<system:String x:Key="homeScreenFontSize">Home Font Size</system:String>
312318
<system:String x:Key="resetCustomize">Reset</system:String>
313319
<system:String x:Key="resetCustomizeToolTip">Reset to the recommended font and size settings.</system:String>
314320
<system:String x:Key="ImportThemeSize">Import Theme Size</system:String>

Flow.Launcher/PinnedResultGrid.xaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
1010
mc:Ignorable="d">
1111
<Border
12-
Margin="8 10 8 10"
13-
Padding="8 8"
12+
Margin="8 8 8 8"
13+
Padding="0"
1414
CornerRadius="8">
1515
<Border.Style>
1616
<Style TargetType="Border">

Flow.Launcher/ResultGrid.xaml

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
<EventSetter Event="MouseEnter" Handler="OnMouseEnter" />
3535
<EventSetter Event="MouseMove" Handler="OnMouseMove" />
3636
<Setter Property="Background" Value="Transparent" />
37-
<Setter Property="Width" Value="64" />
38-
<Setter Property="Height" Value="64" />
39-
<Setter Property="Margin" Value="5" />
37+
<Setter Property="Width" Value="84" />
38+
<Setter Property="Height" Value="84" />
39+
<Setter Property="Margin" Value="2" />
4040
<Setter Property="BorderThickness" Value="0" />
41-
<Setter Property="Padding" Value="0" />
41+
<Setter Property="Padding" Value="10" />
4242
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
4343
<Setter Property="Template">
4444
<Setter.Value>
@@ -98,11 +98,9 @@
9898
</Grid>
9999
</Border>
100100
<TextBlock
101-
Margin="2 0 2 1"
102101
HorizontalAlignment="Center"
103-
FontSize="9"
104-
Foreground="White"
105-
Opacity="0.9"
102+
FontSize="{Binding Settings.HomeScreenFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
103+
Style="{DynamicResource HomeIconFontStyle}"
106104
Text="{Binding Result.Title}"
107105
TextTrimming="CharacterEllipsis"
108106
ToolTip="{Binding ShowTitleToolTip}" />

Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,31 @@ namespace Flow.Launcher.SettingPages.ViewModels;
1212

1313
public partial class SettingsPanePluginStoreViewModel : BaseModel
1414
{
15+
public class SortModeData : DropdownDataGeneric<PluginStoreSortMode> { }
16+
17+
public List<SortModeData> SortModes { get; } =
18+
DropdownDataGeneric<PluginStoreSortMode>.GetValues<SortModeData>("PluginStoreSortMode");
19+
20+
public SettingsPanePluginStoreViewModel()
21+
{
22+
UpdateEnumDropdownLocalizations();
23+
}
24+
25+
private PluginStoreSortMode _selectedSortMode = PluginStoreSortMode.Default;
26+
public PluginStoreSortMode SelectedSortMode
27+
{
28+
get => _selectedSortMode;
29+
set
30+
{
31+
if (_selectedSortMode != value)
32+
{
33+
_selectedSortMode = value;
34+
OnPropertyChanged();
35+
OnPropertyChanged(nameof(ExternalPlugins));
36+
}
37+
}
38+
}
39+
1540
private string filterText = string.Empty;
1641
public string FilterText
1742
{
@@ -82,13 +107,8 @@ public bool ShowExecutable
82107
}
83108
}
84109

85-
public IList<PluginStoreItemViewModel> ExternalPlugins => App.API.GetPluginManifest()
86-
.Select(p => new PluginStoreItemViewModel(p))
87-
.OrderByDescending(p => p.Category == PluginStoreItemViewModel.NewRelease)
88-
.ThenByDescending(p => p.Category == PluginStoreItemViewModel.RecentlyUpdated)
89-
.ThenByDescending(p => p.Category == PluginStoreItemViewModel.None)
90-
.ThenByDescending(p => p.Category == PluginStoreItemViewModel.Installed)
91-
.ToList();
110+
public IList<PluginStoreItemViewModel> ExternalPlugins => GetSortedPlugins(
111+
App.API.GetPluginManifest().Select(p => new PluginStoreItemViewModel(p)));
92112

93113
[RelayCommand]
94114
private async Task RefreshExternalPluginsAsync()
@@ -168,4 +188,48 @@ public bool SatisfiesFilter(PluginStoreItemViewModel plugin)
168188
App.API.FuzzySearch(FilterText, plugin.Name).IsSearchPrecisionScoreMet() ||
169189
App.API.FuzzySearch(FilterText, plugin.Description).IsSearchPrecisionScoreMet();
170190
}
191+
192+
private void UpdateEnumDropdownLocalizations()
193+
{
194+
DropdownDataGeneric<PluginStoreSortMode>.UpdateLabels(SortModes);
195+
}
196+
197+
private IList<PluginStoreItemViewModel> GetSortedPlugins(IEnumerable<PluginStoreItemViewModel> plugins)
198+
{
199+
return SelectedSortMode switch
200+
{
201+
PluginStoreSortMode.Name => plugins
202+
.OrderBy(p => p.LabelInstalled)
203+
.ThenBy(p => p.Name)
204+
.ToList(),
205+
206+
PluginStoreSortMode.ReleaseDate => plugins
207+
.OrderBy(p => p.LabelInstalled)
208+
.ThenByDescending(p => p.DateAdded.HasValue)
209+
.ThenByDescending(p => p.DateAdded)
210+
.ToList(),
211+
212+
PluginStoreSortMode.UpdatedDate => plugins
213+
.OrderBy(p => p.LabelInstalled)
214+
.ThenByDescending(p => p.UpdatedDate.HasValue)
215+
.ThenByDescending(p => p.UpdatedDate)
216+
.ToList(),
217+
218+
_ => plugins
219+
.OrderByDescending(p => p.DefaultCategory == PluginStoreItemViewModel.NewRelease)
220+
.ThenByDescending(p => p.DefaultCategory == PluginStoreItemViewModel.RecentlyUpdated)
221+
.ThenByDescending(p => p.DefaultCategory == PluginStoreItemViewModel.None)
222+
.ThenByDescending(p => p.DefaultCategory == PluginStoreItemViewModel.Installed)
223+
.ToList(),
224+
};
225+
}
226+
227+
}
228+
229+
public enum PluginStoreSortMode
230+
{
231+
Default,
232+
Name,
233+
ReleaseDate,
234+
UpdatedDate
171235
}

0 commit comments

Comments
 (0)