diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs
index 896fc9922..c331c4985 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs
@@ -75,7 +75,9 @@ namespace Flow.Launcher.Plugin.Explorer
{
Settings.QuickAccessLinks.Add(new AccessLink
{
- Path = record.FullPath, Type = record.Type
+ Name = record.FullPath.GetPathName(),
+ Path = record.FullPath,
+ Type = record.Type
});
Context.API.ShowMsg(Context.API.GetTranslation("plugin_explorer_addfilefoldersuccess"),
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Helper/PathHelper.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/PathHelper.cs
new file mode 100644
index 000000000..7763d9662
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Helper/PathHelper.cs
@@ -0,0 +1,21 @@
+using System;
+using System.IO;
+using System.Linq;
+using Flow.Launcher.Plugin.Explorer.Search;
+
+namespace Flow.Launcher.Plugin.Explorer.Helper;
+
+public static class PathHelper
+{
+ public static string GetPathName(this string selectedPath)
+ {
+ if (string.IsNullOrEmpty(selectedPath)) return string.Empty;
+ var path = selectedPath.EndsWith(Constants.DirectorySeparator) ? selectedPath[0..^1] : selectedPath;
+
+ if (path.EndsWith(':'))
+ return path[0..^1] + " Drive";
+
+ return path.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None)
+ .Last();
+ }
+}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
index 9f60aaa43..27d1bfeab 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
@@ -5,6 +5,8 @@
Please make a selection first
+ Please select a folder path.
+ Please choose a different name or folder path.
Please select a folder link
Are you sure you want to delete {0}?
Are you sure you want to permanently delete this file?
@@ -27,6 +29,7 @@
Add
General Setting
Customise Action Keywords
+ Customise Quick Access
Quick Access Links
Everything Setting
Preview Panel
@@ -92,6 +95,7 @@
Permanently delete current file
Permanently delete current folder
Path:
+ Name:
Delete the selected
Run as different user
Run the selected using a different user account
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
index 1c5d074a0..283820204 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
@@ -35,6 +35,7 @@ namespace Flow.Launcher.Plugin.Explorer
Context = context;
Settings = context.API.LoadSettingJsonStorage();
+ FillQuickAccessLinkNames();
viewModel = new SettingsViewModel(context, Settings);
@@ -95,5 +96,17 @@ namespace Flow.Launcher.Plugin.Explorer
{
return Context.API.GetTranslation("plugin_explorer_plugin_description");
}
+
+ private void FillQuickAccessLinkNames()
+ {
+ // Legacy version does not have names for quick access links, so we fill them with the path name.
+ foreach (var link in Settings.QuickAccessLinks)
+ {
+ if (string.IsNullOrWhiteSpace(link.Name))
+ {
+ link.Name = link.Path.GetPathName();
+ }
+ }
+ }
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/AccessLink.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/AccessLink.cs
index 1975211f9..5dd6162e8 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/AccessLink.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/AccessLink.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Linq;
-using System.Text.Json.Serialization;
-
-namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks
+namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks
{
public class AccessLink
{
@@ -10,20 +6,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks
public ResultType Type { get; set; } = ResultType.Folder;
- [JsonIgnore]
- public string Name
- {
- get
- {
- var path = Path.EndsWith(Constants.DirectorySeparator) ? Path[0..^1] : Path;
-
- if (path.EndsWith(':'))
- return path[0..^1] + " Drive";
-
- return path.Split(new[] { System.IO.Path.DirectorySeparatorChar }, StringSplitOptions.None)
- .Last();
- }
- }
+ public string Name { get; set; }
}
-
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
index fb33dacab..59d3a5cfd 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/SettingsViewModel.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Forms;
using CommunityToolkit.Mvvm.Input;
+using Flow.Launcher.Plugin.Explorer.Helper;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.Everything;
using Flow.Launcher.Plugin.Explorer.Search.Everything.Exceptions;
@@ -328,14 +329,10 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
[RelayCommand]
- private void EditLink(object commandParameter)
+ private void EditIndexSearchExcludePaths()
{
- var (selectedLink, collection) = commandParameter switch
- {
- "QuickAccessLink" => (SelectedQuickAccessLink, Settings.QuickAccessLinks),
- "IndexSearchExcludedPaths" => (SelectedIndexSearchExcludedPath, Settings.IndexSearchExcludedSubdirectoryPaths),
- _ => throw new ArgumentOutOfRangeException(nameof(commandParameter))
- };
+ var selectedLink = SelectedIndexSearchExcludedPath;
+ var collection = Settings.IndexSearchExcludedSubdirectoryPaths;
if (selectedLink is null)
{
@@ -354,28 +351,18 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
collection.Remove(selectedLink);
collection.Add(new AccessLink
{
- Path = path, Type = selectedLink.Type,
+ Path = path, Type = selectedLink.Type, Name = path.GetPathName()
});
- }
-
- private void ShowUnselectedMessage()
- {
- var warning = Context.API.GetTranslation("plugin_explorer_make_selection_warning");
- Context.API.ShowMsgBox(warning);
+ Save();
}
[RelayCommand]
- private void AddLink(object commandParameter)
+ private void AddIndexSearchExcludePaths()
{
- var container = commandParameter switch
- {
- "QuickAccessLink" => Settings.QuickAccessLinks,
- "IndexSearchExcludedPaths" => Settings.IndexSearchExcludedSubdirectoryPaths,
- _ => throw new ArgumentOutOfRangeException(nameof(commandParameter))
- };
-
- ArgumentNullException.ThrowIfNull(container);
+ var container = Settings.IndexSearchExcludedSubdirectoryPaths;
+ if (container is null) return;
+
var folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
@@ -383,16 +370,47 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
var newAccessLink = new AccessLink
{
+ Name = folderBrowserDialog.SelectedPath.GetPathName(),
Path = folderBrowserDialog.SelectedPath
};
container.Add(newAccessLink);
+ Save();
}
[RelayCommand]
- private void RemoveLink(object obj)
+ private void EditQuickAccessLink()
{
- if (obj is not string container) return;
+ var selectedLink = SelectedQuickAccessLink;
+ var collection = Settings.QuickAccessLinks;
+
+ if (selectedLink is null)
+ {
+ ShowUnselectedMessage();
+ return;
+ }
+
+ var quickAccessLinkSettings = new QuickAccessLinkSettings(collection, SelectedQuickAccessLink);
+ if (quickAccessLinkSettings.ShowDialog() == true)
+ {
+ Save();
+ }
+ }
+
+ [RelayCommand]
+ private void AddQuickAccessLink()
+ {
+ var quickAccessLinkSettings = new QuickAccessLinkSettings(Settings.QuickAccessLinks);
+ if (quickAccessLinkSettings.ShowDialog() == true)
+ {
+ Save();
+ }
+ }
+
+ [RelayCommand]
+ private void RemoveLink(object commandParameter)
+ {
+ if (commandParameter is not string container) return;
switch (container)
{
@@ -407,10 +425,16 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
Save();
}
+
+ private void ShowUnselectedMessage()
+ {
+ var warning = Context.API.GetTranslation("plugin_explorer_make_selection_warning");
+ Context.API.ShowMsgBox(warning);
+ }
#endregion
- private string? PromptUserSelectPath(ResultType type, string? initialDirectory = null)
+ private static string? PromptUserSelectPath(ResultType type, string? initialDirectory = null)
{
string? path = null;
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml
index 47d53d463..001a9e168 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml
@@ -92,41 +92,43 @@
+ Background="{TemplateBinding Background}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}"
+ SnapsToDevicePixels="true">
-
+ x:Name="HeaderSite"
+ MinWidth="0"
+ MinHeight="0"
+ Margin="0"
+ Padding="{TemplateBinding Padding}"
+ HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
+ VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
+ Content="{TemplateBinding Header}"
+ ContentTemplate="{TemplateBinding HeaderTemplate}"
+ ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
+ DockPanel.Dock="Top"
+ FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}"
+ FontFamily="{TemplateBinding FontFamily}"
+ FontSize="{TemplateBinding FontSize}"
+ FontStretch="{TemplateBinding FontStretch}"
+ FontStyle="{TemplateBinding FontStyle}"
+ FontWeight="{TemplateBinding FontWeight}"
+ Foreground="{TemplateBinding Foreground}"
+ IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
+ Style="{StaticResource ExpanderHeaderRightArrowStyle}" />
+
+ x:Name="ExpandSite"
+ Margin="{TemplateBinding Padding}"
+ HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
+ VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
+ DockPanel.Dock="Bottom"
+ Focusable="false" />
@@ -141,13 +143,17 @@
+ Storyboard.TargetName="ContentPresenterBorder"
+ Storyboard.TargetProperty="(Border.LayoutTransform).(ScaleTransform.ScaleY)"
+ From="0.0"
+ To="1.0"
+ Duration="0:0:0" />
+ Storyboard.TargetName="ContentPresenterBorder"
+ Storyboard.TargetProperty="(Border.Opacity)"
+ From="0.0"
+ To="1.0"
+ Duration="0:0:0" />
@@ -155,13 +161,17 @@
+ Storyboard.TargetName="ContentPresenterBorder"
+ Storyboard.TargetProperty="(Border.LayoutTransform).(ScaleTransform.ScaleY)"
+ From="1.0"
+ To="0.0"
+ Duration="0:0:0" />
+ Storyboard.TargetName="ContentPresenterBorder"
+ Storyboard.TargetProperty="(Border.Opacity)"
+ From="1.0"
+ To="0.0"
+ Duration="0:0:0" />
@@ -734,10 +744,29 @@
BorderThickness="1"
DragEnter="lbxAccessLinks_DragEnter"
Drop="LbxAccessLinks_OnDrop"
- ItemTemplate="{StaticResource ListViewTemplateAccessLinks}"
ItemsSource="{Binding Settings.QuickAccessLinks}"
Loaded="lbxAccessLinks_Loaded"
- SelectedItem="{Binding SelectedQuickAccessLink}" />
+ SelectedItem="{Binding SelectedQuickAccessLink}"
+ SizeChanged="lbxAccessLinks_SizeChanged">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -822,14 +849,12 @@
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs
index 2c50f1c6f..4256c2ae0 100644
--- a/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/ExplorerSettings.xaml.cs
@@ -125,5 +125,22 @@ namespace Flow.Launcher.Plugin.Explorer.Views
{
lbxExcludedPaths.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
}
+
+ private void lbxAccessLinks_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ if (sender is not ListView listView) return;
+ if (listView.View is not GridView gView) return;
+
+ var workingWidth =
+ listView.ActualWidth - SystemParameters.VerticalScrollBarWidth; // take into account vertical scrollbar
+
+ if (workingWidth <= 0) return;
+
+ var col1 = 0.4;
+ var col2 = 0.6;
+
+ gView.Columns[0].Width = workingWidth * col1;
+ gView.Columns[1].Width = workingWidth * col2;
+ }
}
}
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml
new file mode 100644
index 000000000..e9ba53618
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs
new file mode 100644
index 000000000..36a00e9e5
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Explorer/Views/QuickAccessLinkSettings.xaml.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Forms;
+using Flow.Launcher.Plugin.Explorer.Helper;
+using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
+
+namespace Flow.Launcher.Plugin.Explorer.Views;
+
+public partial class QuickAccessLinkSettings : INotifyPropertyChanged
+{
+ private string _selectedPath;
+ public string SelectedPath
+ {
+ get => _selectedPath;
+ set
+ {
+ if (_selectedPath != value)
+ {
+ _selectedPath = value;
+ OnPropertyChanged();
+ if (string.IsNullOrEmpty(_selectedName))
+ {
+ SelectedName = _selectedPath.GetPathName();
+ }
+ }
+ }
+ }
+
+ private string _selectedName;
+ public string SelectedName
+ {
+ get
+ {
+ return string.IsNullOrEmpty(_selectedName) ? _selectedPath.GetPathName() : _selectedName;
+ }
+ set
+ {
+ if (_selectedName != value)
+ {
+ _selectedName = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ private bool IsEdit { get; }
+ private AccessLink SelectedAccessLink { get; }
+
+ public ObservableCollection QuickAccessLinks { get; }
+
+ public QuickAccessLinkSettings(ObservableCollection quickAccessLinks)
+ {
+ IsEdit = false;
+ QuickAccessLinks = quickAccessLinks;
+ InitializeComponent();
+ }
+
+ public QuickAccessLinkSettings(ObservableCollection quickAccessLinks, AccessLink selectedAccessLink)
+ {
+ IsEdit = true;
+ _selectedName = selectedAccessLink.Name;
+ _selectedPath = selectedAccessLink.Path;
+ SelectedAccessLink = selectedAccessLink;
+ QuickAccessLinks = quickAccessLinks;
+ InitializeComponent();
+ }
+
+ private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
+ {
+ DialogResult = false;
+ Close();
+ }
+
+ private void OnDoneButtonClick(object sender, RoutedEventArgs e)
+ {
+ // Validate the input before proceeding
+ if (string.IsNullOrEmpty(SelectedName) || string.IsNullOrEmpty(SelectedPath))
+ {
+ var warning = Main.Context.API.GetTranslation("plugin_explorer_quick_access_link_no_folder_selected");
+ Main.Context.API.ShowMsgBox(warning);
+ return;
+ }
+
+ // Check if the path already exists in the quick access links
+ if (QuickAccessLinks.Any(x =>
+ x.Path.Equals(SelectedPath, StringComparison.OrdinalIgnoreCase) &&
+ x.Name.Equals(SelectedName, StringComparison.OrdinalIgnoreCase)))
+ {
+ var warning = Main.Context.API.GetTranslation("plugin_explorer_quick_access_link_path_already_exists");
+ Main.Context.API.ShowMsgBox(warning);
+ return;
+ }
+
+ // If editing, update the existing link
+ if (IsEdit)
+ {
+ if (SelectedAccessLink == null) return;
+
+ var index = QuickAccessLinks.IndexOf(SelectedAccessLink);
+ if (index >= 0)
+ {
+ var updatedLink = new AccessLink
+ {
+ Name = SelectedName,
+ Type = SelectedAccessLink.Type,
+ Path = SelectedPath
+ };
+ QuickAccessLinks[index] = updatedLink;
+ }
+ DialogResult = true;
+ Close();
+ }
+ // Otherwise, add a new one
+ else
+ {
+ var newAccessLink = new AccessLink
+ {
+ Name = SelectedName,
+ Path = SelectedPath
+ };
+ QuickAccessLinks.Add(newAccessLink);
+ DialogResult = true;
+ Close();
+ }
+ }
+
+ private void SelectPath_OnClick(object commandParameter, RoutedEventArgs e)
+ {
+ var folderBrowserDialog = new FolderBrowserDialog();
+
+ if (folderBrowserDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
+ return;
+
+ SelectedPath = folderBrowserDialog.SelectedPath;
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}