fix(explorer): complete Avalonia migration and fix action keyword crashes

- Remove WPF views (ActionKeywordSetting, ExplorerSettings, PreviewPanel, QuickAccessLinkSettings)
- Fix NullReferenceException in ActionKeywordModel by initializing settings
- Fix (false,false) crash case in EditActionKeywordAsync
- Add null-safe dialog patterns for owner window
- Move Focus() to OnOpened for reliable control attachment
- Add using for Avalonia PreviewPanel namespace in ResultManager
- Update Main.cs to use AvaloniaControl return type
This commit is contained in:
Shengkai Lin 2026-01-31 23:09:41 +08:00
parent e337f9e6c5
commit 0681332238
28 changed files with 613 additions and 1925 deletions

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Exceptions/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -8,7 +8,7 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Nullable>warnings</Nullable>
<Nullable>enable</Nullable>
<ApplicationIcon />
<StartupObject />
</PropertyGroup>

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Helper/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -2,16 +2,15 @@ using Flow.Launcher.Plugin.Explorer.Helper;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.Everything;
using Flow.Launcher.Plugin.Explorer.ViewModels;
using Flow.Launcher.Plugin.Explorer.Views;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using Flow.Launcher.Plugin.Explorer.Exceptions;
using System.Linq;
using System.Globalization;
using System.Windows.Controls;
using AvaloniaControl = Avalonia.Controls.Control;
namespace Flow.Launcher.Plugin.Explorer
@ -32,7 +31,7 @@ namespace Flow.Launcher.Plugin.Explorer
public Control CreateSettingPanel()
{
return new ExplorerSettings(viewModel);
throw new NotSupportedException("WPF settings are no longer supported. Use Avalonia version instead.");
}
public AvaloniaControl CreateSettingPanelAvalonia()
@ -45,6 +44,7 @@ namespace Flow.Launcher.Plugin.Explorer
Context = context;
Settings = context.API.LoadSettingJsonStorage<Settings>();
ActionKeywordModel.Init(Settings);
FillQuickAccessLinkNames();
viewModel = new SettingsViewModel(context, Settings);

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Search/DirectoryInfo/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/Exceptions/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Search/Everything/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Search/IProvider/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Search/QuickAccessLinks/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -1,11 +1,10 @@
using System;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
using Flow.Launcher.Plugin.Explorer.Search.Everything;
using Flow.Launcher.Plugin.Explorer.Views;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Plugin.SharedModels;
using Peter;
@ -106,7 +105,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
{
FilePath = path,
},
PreviewPanel = new Lazy<UserControl>(() => new PreviewPanel(Settings, path, ResultType.Folder)),
Action = c =>
{
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt)
@ -305,7 +303,6 @@ namespace Flow.Launcher.Plugin.Explorer.Search
TitleHighlightData = Context.API.FuzzySearch(query.Search, title).MatchData,
Score = score,
CopyText = filePath,
PreviewPanel = new Lazy<UserControl>(() => new PreviewPanel(Settings, filePath, ResultType.File)),
Action = c =>
{
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt)
@ -371,9 +368,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search
{
try
{
var fileSize = PreviewPanel.GetFileSize(filePath);
var fileCreatedAt = PreviewPanel.GetFileCreatedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
var fileModifiedAt = PreviewPanel.GetFileLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
var fileSize = Views.Avalonia.PreviewPanel.GetFileSize(filePath);
var fileCreatedAt = Views.Avalonia.PreviewPanel.GetFileCreatedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
var fileModifiedAt = Views.Avalonia.PreviewPanel.GetFileLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
return Localize.plugin_explorer_plugin_tooltip_more_info(filePath, fileSize, fileCreatedAt, fileModifiedAt, Environment.NewLine);
}
catch (Exception e)
@ -387,9 +384,9 @@ namespace Flow.Launcher.Plugin.Explorer.Search
{
try
{
var folderSize = PreviewPanel.GetFolderSize(folderPath);
var folderCreatedAt = PreviewPanel.GetFolderCreatedAt(folderPath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
var folderModifiedAt = PreviewPanel.GetFolderLastModifiedAt(folderPath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
var folderSize = Views.Avalonia.PreviewPanel.GetFolderSize(folderPath);
var folderCreatedAt = Views.Avalonia.PreviewPanel.GetFolderCreatedAt(folderPath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
var folderModifiedAt = Views.Avalonia.PreviewPanel.GetFolderLastModifiedAt(folderPath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
return Localize.plugin_explorer_plugin_tooltip_more_info(folderPath, folderSize, folderCreatedAt, folderModifiedAt, Environment.NewLine);
}
catch (Exception e)

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Search/WindowsIndex/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Search/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -8,26 +8,36 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
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;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using Flow.Launcher.Plugin.Explorer.Views;
using Flow.Launcher.Plugin.Explorer.Views.Avalonia;
using AvaloniaApp = Avalonia.Application;
using AvaloniaQuickAccessLinkSettings = Flow.Launcher.Plugin.Explorer.Views.Avalonia.QuickAccessLinkSettings;
namespace Flow.Launcher.Plugin.Explorer.ViewModels
{
public partial class SettingsViewModel : BaseModel
{
/// <summary>
/// Detects if we're running in an Avalonia application context
/// Gets the current active Avalonia window to use as dialog owner
/// </summary>
private static bool IsAvalonia => AvaloniaApp.Current != null;
private static global::Avalonia.Controls.Window? GetAvaloniaOwnerWindow()
{
if (AvaloniaApp.Current?.ApplicationLifetime is not global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop)
return null;
// First try to find an active window
var activeWindow = desktop.Windows.FirstOrDefault(w => w.IsActive);
if (activeWindow != null)
return activeWindow;
// Fall back to main window
return desktop.MainWindow;
}
public Settings Settings { get; set; }
internal PluginInitContext Context { get; set; }
@ -41,6 +51,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Context = context;
Settings = settings;
ActionKeywordModel.Init(settings);
InitializeEngineSelection();
InitializeActionKeywordModels();
}
@ -162,7 +173,6 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Settings.ShowCreatedDateInPreviewPanel = value;
OnPropertyChanged();
OnPropertyChanged(nameof(ShowPreviewPanelDateTimeChoices));
OnPropertyChanged(nameof(PreviewPanelDateTimeChoicesVisibility));
}
}
@ -174,7 +184,6 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Settings.ShowModifiedDateInPreviewPanel = value;
OnPropertyChanged();
OnPropertyChanged(nameof(ShowPreviewPanelDateTimeChoices));
OnPropertyChanged(nameof(PreviewPanelDateTimeChoicesVisibility));
}
}
@ -186,7 +195,6 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Settings.ShowFileAgeInPreviewPanel = value;
OnPropertyChanged();
OnPropertyChanged(nameof(ShowPreviewPanelDateTimeChoices));
OnPropertyChanged(nameof(PreviewPanelDateTimeChoicesVisibility));
}
}
@ -217,9 +225,6 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
public bool ShowPreviewPanelDateTimeChoices => ShowCreatedDateInPreviewPanel || ShowModifiedDateInPreviewPanel;
public Visibility PreviewPanelDateTimeChoicesVisibility => ShowCreatedDateInPreviewPanel || ShowModifiedDateInPreviewPanel ? Visibility.Visible : Visibility.Collapsed;
public List<string> TimeFormatList { get; } = new()
{
"h:mm",
@ -296,7 +301,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
public ActionKeywordModel? SelectedActionKeyword { get; set; }
[RelayCommand]
private void EditActionKeyword(object obj)
private async Task EditActionKeywordAsync(object obj)
{
if (SelectedActionKeyword is not { } actionKeyword)
{
@ -304,14 +309,32 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
return;
}
var actionKeywordWindow = new ActionKeywordSetting(actionKeyword);
var dialog = new ActionKeywordSetting(actionKeyword);
var ownerWindow = GetAvaloniaOwnerWindow();
bool dialogResult;
if (ownerWindow != null)
{
dialogResult = await dialog.ShowDialog<bool?>(ownerWindow) ?? false;
}
else
{
// Fallback: show as normal window if owner is not available
dialog.Show();
var tcs = new TaskCompletionSource<bool?>();
dialog.Closed += (_, _) => tcs.TrySetResult(true);
await tcs.Task;
dialogResult = true;
}
if (!(actionKeywordWindow.ShowDialog() ?? false))
if (!dialogResult)
{
return;
}
switch (actionKeyword.Enabled, actionKeywordWindow.KeywordEnabled)
var newKeyword = dialog.ActionKeyword;
var newEnabled = dialog.KeywordEnabled;
switch (actionKeyword.Enabled, newEnabled)
{
case (true, false):
Context.API.RemoveActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
@ -319,18 +342,16 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
case (true, true):
// same keyword will have dialog result false
Context.API.RemoveActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, actionKeywordWindow.ActionKeyword);
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, newKeyword);
break;
case (false, true):
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, actionKeywordWindow.ActionKeyword);
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, newKeyword);
break;
case (false, false):
throw new ArgumentException(
$"Both false in {nameof(actionKeyword)}.{nameof(actionKeyword.Enabled)} and {nameof(actionKeywordWindow)}.{nameof(actionKeywordWindow.KeywordEnabled)} should suggest that the ShowDialog() result is false");
break;
}
(actionKeyword.Keyword, actionKeyword.Enabled) = (actionKeywordWindow.ActionKeyword, actionKeywordWindow.KeywordEnabled);
(actionKeyword.Keyword, actionKeyword.Enabled) = (newKeyword, newEnabled);
}
#endregion
@ -352,7 +373,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
[RelayCommand]
private void EditIndexSearchExcludePaths()
private async Task EditIndexSearchExcludePathsAsync()
{
var selectedLink = SelectedIndexSearchExcludedPath;
var collection = Settings.IndexSearchExcludedSubdirectoryPaths;
@ -363,7 +384,7 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
return;
}
var path = PromptUserSelectPath(selectedLink.Type,
var path = await PromptUserSelectPathAsync(selectedLink.Type,
selectedLink.Type == ResultType.Folder
? selectedLink.Path
: Path.GetDirectoryName(selectedLink.Path));
@ -380,21 +401,21 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
[RelayCommand]
private void AddIndexSearchExcludePaths()
private async Task AddIndexSearchExcludePathsAsync()
{
var container = Settings.IndexSearchExcludedSubdirectoryPaths;
if (container is null) return;
var folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
var path = await PromptUserSelectFolderAsync();
if (path is null)
return;
var newAccessLink = new AccessLink
{
Name = folderBrowserDialog.SelectedPath.GetPathName(),
Path = folderBrowserDialog.SelectedPath
Name = path.GetPathName(),
Path = path
};
container.Add(newAccessLink);
@ -413,50 +434,52 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
return;
}
if (IsAvalonia)
var dialog = new QuickAccessLinkSettings(collection, selectedLink);
var ownerWindow = GetAvaloniaOwnerWindow();
bool dialogResult;
if (ownerWindow != null)
{
var dialog = new AvaloniaQuickAccessLinkSettings(collection, selectedLink);
var mainWindow = AvaloniaApp.Current?.ApplicationLifetime is global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop
? desktop.MainWindow
: null;
var result = await dialog.ShowDialog<bool?>(mainWindow!);
if (result == true)
{
Save();
}
dialogResult = await dialog.ShowDialog<bool?>(ownerWindow) ?? false;
}
else
{
var quickAccessLinkSettings = new QuickAccessLinkSettings(collection, SelectedQuickAccessLink);
if (quickAccessLinkSettings.ShowDialog() == true)
{
Save();
}
// Fallback: show as normal window if owner is not available
dialog.Show();
var tcs = new TaskCompletionSource<bool?>();
dialog.Closed += (_, _) => tcs.TrySetResult(true);
await tcs.Task;
dialogResult = true;
}
if (dialogResult)
{
Save();
}
}
[RelayCommand]
private async Task AddQuickAccessLinkAsync()
{
if (IsAvalonia)
var dialog = new QuickAccessLinkSettings(Settings.QuickAccessLinks);
var ownerWindow = GetAvaloniaOwnerWindow();
bool dialogResult;
if (ownerWindow != null)
{
var dialog = new AvaloniaQuickAccessLinkSettings(Settings.QuickAccessLinks);
var mainWindow = AvaloniaApp.Current?.ApplicationLifetime is global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop
? desktop.MainWindow
: null;
var result = await dialog.ShowDialog<bool?>(mainWindow!);
if (result == true)
{
Save();
}
dialogResult = await dialog.ShowDialog<bool?>(ownerWindow) ?? false;
}
else
{
var quickAccessLinkSettings = new QuickAccessLinkSettings(Settings.QuickAccessLinks);
if (quickAccessLinkSettings.ShowDialog() == true)
{
Save();
}
// Fallback: show as normal window if owner is not available
dialog.Show();
var tcs = new TaskCompletionSource<bool?>();
dialog.Closed += (_, _) => tcs.TrySetResult(true);
await tcs.Task;
dialogResult = true;
}
if (dialogResult)
{
Save();
}
}
@ -492,45 +515,60 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
Save();
}
private void ShowUnselectedMessage()
{
var warning = Localize.plugin_explorer_make_selection_warning();
Context.API.ShowMsgBox(warning);
}
#endregion
private static string? PromptUserSelectPath(ResultType type, string? initialDirectory = null)
private static async Task<string?> PromptUserSelectPathAsync(ResultType type, string? initialDirectory = null)
{
string? path = null;
if (type is ResultType.Folder)
{
var folderBrowserDialog = new FolderBrowserDialog();
if (initialDirectory is not null)
folderBrowserDialog.InitialDirectory = initialDirectory;
if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
return path;
path = folderBrowserDialog.SelectedPath;
path = await PromptUserSelectFolderAsync(initialDirectory);
}
else if (type is ResultType.File)
{
var openFileDialog = new OpenFileDialog();
if (initialDirectory is not null)
openFileDialog.InitialDirectory = initialDirectory;
if (openFileDialog.ShowDialog() != DialogResult.OK)
return path;
path = openFileDialog.FileName;
path = await PromptUserSelectFileAsync(initialDirectory);
}
return path;
}
private static async Task<string?> PromptUserSelectFolderAsync(string? initialDirectory = null)
{
var mainWindow = AvaloniaApp.Current?.ApplicationLifetime is global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop
? desktop.MainWindow
: null;
if (mainWindow == null) return null;
var folders = await mainWindow.StorageProvider.OpenFolderPickerAsync(new global::Avalonia.Platform.Storage.FolderPickerOpenOptions
{
AllowMultiple = false
});
return folders.Count > 0 ? folders[0].Path.LocalPath : null;
}
private static async Task<string?> PromptUserSelectFileAsync(string? initialDirectory = null)
{
var mainWindow = AvaloniaApp.Current?.ApplicationLifetime is global::Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime desktop
? desktop.MainWindow
: null;
if (mainWindow == null) return null;
var files = await mainWindow.StorageProvider.OpenFilePickerAsync(new global::Avalonia.Platform.Storage.FilePickerOpenOptions
{
AllowMultiple = false
});
return files.Count > 0 ? files[0].Path.LocalPath : null;
}
internal static void OpenWindowsIndexingOptions()
{
var psi = new ProcessStartInfo
@ -544,9 +582,9 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
[RelayCommand]
private void OpenFileEditorPath()
private async Task OpenFileEditorPathAsync()
{
var path = PromptUserSelectPath(ResultType.File, Settings.EditorPath != null ? Path.GetDirectoryName(Settings.EditorPath) : null);
var path = await PromptUserSelectFileAsync(Settings.EditorPath != null ? Path.GetDirectoryName(Settings.EditorPath) : null);
if (path is null)
return;
@ -554,9 +592,9 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
[RelayCommand]
private void OpenFolderEditorPath()
private async Task OpenFolderEditorPathAsync()
{
var path = PromptUserSelectPath(ResultType.File, Settings.FolderEditorPath != null ? Path.GetDirectoryName(Settings.FolderEditorPath) : null);
var path = await PromptUserSelectFolderAsync(Settings.FolderEditorPath != null ? Path.GetDirectoryName(Settings.FolderEditorPath) : null);
if (path is null)
return;
@ -564,9 +602,9 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
[RelayCommand]
private void OpenShellPath()
private async Task OpenShellPathAsync()
{
var path = PromptUserSelectPath(ResultType.File, Settings.EditorPath != null ? Path.GetDirectoryName(Settings.EditorPath) : null);
var path = await PromptUserSelectFileAsync(Settings.EditorPath != null ? Path.GetDirectoryName(Settings.EditorPath) : null);
if (path is null)
return;
@ -628,6 +666,8 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
}
#endregion
#region Everything FastSortWarning
public List<EverythingSortOptionLocalized> AllEverythingSortOptions { get; } = EverythingSortOptionLocalized.GetValues();
@ -646,23 +686,23 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
}
}
public Visibility FastSortWarningVisibility
public bool FastSortWarningVisibility
{
get
{
try
{
return EverythingApi.IsFastSortOption(Settings.SortOption) ? Visibility.Collapsed : Visibility.Visible;
return !EverythingApi.IsFastSortOption(Settings.SortOption);
}
catch (IPCErrorException)
{
// this error occurs if the Everything service is not running, in this instance show the warning and
// update the message to let user know in the settings panel.
return Visibility.Visible;
return true;
}
catch (DllNotFoundException)
{
return Visibility.Collapsed;
return false;
}
}
}

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/ViewModels/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -1,129 +0,0 @@
<Window
x:Class="Flow.Launcher.Plugin.Explorer.Views.ActionKeywordSetting"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Flow.Launcher.Plugin.Explorer.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{DynamicResource plugin_explorer_manageactionkeywords_header}"
Width="Auto"
Height="255"
Background="{DynamicResource PopuBGColor}"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Width"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</WindowChrome.WindowChrome>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="80" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path
Width="46"
Height="32"
Data="M 18,11 27,20 M 18,20 27,11"
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
StrokeThickness="1">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Button>
</Grid>
</StackPanel>
<StackPanel Margin="26 0 26 0">
<StackPanel Margin="0 0 0 12">
<TextBlock
Margin="0 0 0 0"
FontSize="20"
FontWeight="SemiBold"
Text="{DynamicResource plugin_explorer_manageactionkeywords_header}"
TextAlignment="Left" />
</StackPanel>
<StackPanel Margin="0 10 0 0" Orientation="Horizontal">
<TextBlock
MinWidth="150"
Margin="0 10 15 10"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="14"
Text="{DynamicResource plugin_explorer_actionkeyword_current}" />
<TextBox
Name="TxtCurrentActionKeyword"
Width="135"
HorizontalAlignment="Left"
VerticalAlignment="Center"
DataObject.Pasting="TextBox_Pasting"
PreviewKeyDown="TxtCurrentActionKeyword_OnKeyDown"
Text="{Binding ActionKeyword}" />
</StackPanel>
<StackPanel Margin="0 10 0 15" Orientation="Horizontal">
<TextBlock
MinWidth="150"
Margin="0 0 18 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="14"
Text="{DynamicResource plugin_explorer_actionkeyword_enabled}" />
<CheckBox
Name="ChkActionKeywordEnabled"
Width="auto"
VerticalAlignment="Center"
IsChecked="{Binding KeywordEnabled, Mode=TwoWay}"
ToolTip="{DynamicResource plugin_explorer_actionkeyword_enabled_tooltip}" />
</StackPanel>
</StackPanel>
</StackPanel>
<Border
Grid.Row="1"
Background="{DynamicResource PopupButtonAreaBGColor}"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="0 1 0 0">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Button
x:Name="btnCancel"
Width="145"
Height="30"
Margin="0 0 5 0"
Click="BtnCancel_OnClick"
Content="{DynamicResource cancel}" />
<Button
Name="DownButton"
Width="145"
Height="30"
Margin="5 0 0 0"
Click="OnDoneButtonClick"
Style="{StaticResource AccentButtonStyle}">
<TextBlock x:Name="lblAdd" Text="{DynamicResource plugin_explorer_actionkeyword_done}" />
</Button>
</StackPanel>
</Border>
</Grid>
</Window>

View file

@ -1,115 +0,0 @@
using System.Linq;
using System.Windows;
using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using Flow.Launcher.Plugin.Explorer.ViewModels;
namespace Flow.Launcher.Plugin.Explorer.Views
{
[INotifyPropertyChanged]
public partial class ActionKeywordSetting
{
private ActionKeywordModel CurrentActionKeyword { get; }
public string ActionKeyword
{
get => actionKeyword;
set
{
// Set Enable to be true if user change ActionKeyword
KeywordEnabled = true;
_ = SetProperty(ref actionKeyword, value);
}
}
public bool KeywordEnabled
{
get => _keywordEnabled;
set => _ = SetProperty(ref _keywordEnabled, value);
}
private string actionKeyword;
private bool _keywordEnabled;
public ActionKeywordSetting(ActionKeywordModel selectedActionKeyword)
{
CurrentActionKeyword = selectedActionKeyword;
ActionKeyword = selectedActionKeyword.Keyword;
KeywordEnabled = selectedActionKeyword.Enabled;
InitializeComponent();
TxtCurrentActionKeyword.Focus();
}
private void OnDoneButtonClick(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(ActionKeyword))
ActionKeyword = Query.GlobalPluginWildcardSign;
if (CurrentActionKeyword.Keyword == ActionKeyword && CurrentActionKeyword.Enabled == KeywordEnabled)
{
DialogResult = false;
Close();
return;
}
if (ActionKeyword == Query.GlobalPluginWildcardSign)
switch (CurrentActionKeyword.KeywordProperty, KeywordEnabled)
{
case (Settings.ActionKeyword.FileContentSearchActionKeyword, true):
Main.Context.API.ShowMsgBox(Localize.plugin_explorer_globalActionKeywordInvalid());
return;
case (Settings.ActionKeyword.QuickAccessActionKeyword, true):
Main.Context.API.ShowMsgBox(Localize.plugin_explorer_quickaccess_globalActionKeywordInvalid());
return;
}
if (!KeywordEnabled || !Main.Context.API.ActionKeywordAssigned(ActionKeyword))
{
DialogResult = true;
Close();
return;
}
// The keyword is not valid, so show message
Main.Context.API.ShowMsgBox(Localize.plugin_explorer_new_action_keyword_assigned());
}
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
private void TxtCurrentActionKeyword_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
DownButton.Focus();
OnDoneButtonClick(sender, e);
e.Handled = true;
}
if (e.Key == Key.Space)
{
e.Handled = true;
}
}
private void TextBox_Pasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(DataFormats.Text))
{
string text = e.DataObject.GetData(DataFormats.Text) as string;
if (!string.IsNullOrEmpty(text) && text.Any(char.IsWhiteSpace))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
}
}

View file

@ -7,7 +7,7 @@
Title="{DynamicResource plugin_explorer_manageactionkeywords_header}"
Width="400"
Height="255"
WindowStartupLocation="CenterOwner"
WindowStartupLocation="CenterScreen"
CanResize="False">
<Grid RowDefinitions="*,80">

View file

@ -1,4 +1,5 @@
#nullable enable
using System;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
@ -49,7 +50,11 @@ public partial class ActionKeywordSetting : Window, INotifyPropertyChanged
InitializeComponent();
DataContext = this;
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
this.FindControl<TextBox>("TxtCurrentActionKeyword")?.Focus();
}

View file

@ -0,0 +1,121 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="400"
x:Class="Flow.Launcher.Plugin.Explorer.Views.Avalonia.PreviewPanel">
<Grid x:Name="PreviewGrid" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Preview Image and File Name -->
<Grid Grid.Row="0" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition MinHeight="96" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0"
MaxWidth="96"
MaxHeight="96"
Margin="5,12,8,0"
Source="{Binding PreviewImage}" />
<Grid Grid.Row="1">
<TextBlock Margin="5,6,5,16"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Text="{Binding FileName}"
TextAlignment="Center"
TextWrapping="Wrap"
FontWeight="SemiBold" />
</Grid>
</Grid>
<!-- File Info -->
<StackPanel Grid.Row="1">
<Rectangle x:Name="PreviewSep"
Height="1"
Margin="0,0,5,0"
HorizontalAlignment="Stretch"
Fill="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<TextBlock Margin="5,8,8,8"
Text="{Binding FilePath}"
TextWrapping="Wrap"
Opacity="0.8" />
<Rectangle Height="1"
Margin="0,0,5,0"
HorizontalAlignment="Stretch"
Fill="{Binding ElementName=PreviewSep, Path=Fill}"
IsVisible="{Binding FileInfoVisibility}" />
<Grid Margin="0,10,0,0" IsVisible="{Binding FileInfoVisibility}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- File Size -->
<TextBlock Grid.Row="0"
Grid.Column="0"
Margin="5,0,0,0"
VerticalAlignment="Top"
Text="{DynamicResource FileSize}"
TextWrapping="Wrap"
IsVisible="{Binding FileSizeVisibility}" />
<TextBlock Grid.Row="0"
Grid.Column="1"
Margin="0,0,13,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Text="{Binding FileSize}"
TextWrapping="Wrap"
IsVisible="{Binding FileSizeVisibility}" />
<!-- Created Date -->
<TextBlock Grid.Row="1"
Grid.Column="0"
Margin="5,0,8,0"
VerticalAlignment="Top"
Text="{DynamicResource Created}"
TextWrapping="Wrap"
IsVisible="{Binding CreatedAtVisibility}" />
<TextBlock Grid.Row="1"
Grid.Column="1"
Margin="0,0,13,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Text="{Binding CreatedAt}"
TextWrapping="Wrap"
IsVisible="{Binding CreatedAtVisibility}" />
<!-- Last Modified Date -->
<TextBlock Grid.Row="2"
Grid.Column="0"
Margin="5,0,8,0"
VerticalAlignment="Top"
Text="{DynamicResource LastModified}"
TextWrapping="Wrap"
IsVisible="{Binding LastModifiedAtVisibility}" />
<TextBlock Grid.Row="2"
Grid.Column="1"
Margin="0,0,13,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Text="{Binding LastModifiedAt}"
TextWrapping="Wrap"
IsVisible="{Binding LastModifiedAtVisibility}" />
</Grid>
</StackPanel>
</Grid>
</UserControl>

View file

@ -1,59 +1,93 @@
using System;
#nullable enable
using System;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Flow.Launcher.Plugin.Explorer.Search;
using CommunityToolkit.Mvvm.ComponentModel;
namespace Flow.Launcher.Plugin.Explorer.Views;
namespace Flow.Launcher.Plugin.Explorer.Views.Avalonia;
#nullable enable
[INotifyPropertyChanged]
public partial class PreviewPanel : UserControl
public partial class PreviewPanel : UserControl, INotifyPropertyChanged
{
private static readonly string ClassName = nameof(PreviewPanel);
public string FilePath { get; }
public string FileName { get; }
[ObservableProperty]
private string _fileSize = Localize.plugin_explorer_plugin_tooltip_more_info_unknown();
[ObservableProperty]
private string _createdAt = "";
[ObservableProperty]
private string _lastModifiedAt = "";
private IImage? _previewImage;
[ObservableProperty]
private ImageSource _previewImage = new BitmapImage();
public string FileSize
{
get => _fileSize;
set
{
if (_fileSize != value)
{
_fileSize = value;
OnPropertyChanged();
}
}
}
public string CreatedAt
{
get => _createdAt;
set
{
if (_createdAt != value)
{
_createdAt = value;
OnPropertyChanged();
}
}
}
public string LastModifiedAt
{
get => _lastModifiedAt;
set
{
if (_lastModifiedAt != value)
{
_lastModifiedAt = value;
OnPropertyChanged();
}
}
}
public IImage? PreviewImage
{
get => _previewImage;
set
{
if (_previewImage != value)
{
_previewImage = value;
OnPropertyChanged();
}
}
}
private Settings Settings { get; }
public Visibility FileSizeVisibility => Settings.ShowFileSizeInPreviewPanel
? Visibility.Visible
: Visibility.Collapsed;
public Visibility CreatedAtVisibility => Settings.ShowCreatedDateInPreviewPanel
? Visibility.Visible
: Visibility.Collapsed;
public Visibility LastModifiedAtVisibility => Settings.ShowModifiedDateInPreviewPanel
? Visibility.Visible
: Visibility.Collapsed;
public bool FileSizeVisibility => Settings.ShowFileSizeInPreviewPanel;
public bool CreatedAtVisibility => Settings.ShowCreatedDateInPreviewPanel;
public bool LastModifiedAtVisibility => Settings.ShowModifiedDateInPreviewPanel;
public Visibility FileInfoVisibility =>
public bool FileInfoVisibility =>
Settings.ShowFileSizeInPreviewPanel ||
Settings.ShowCreatedDateInPreviewPanel ||
Settings.ShowModifiedDateInPreviewPanel
? Visibility.Visible
: Visibility.Collapsed;
Settings.ShowModifiedDateInPreviewPanel;
public PreviewPanel(Settings settings, string filePath, ResultType type)
{
@ -62,6 +96,7 @@ public partial class PreviewPanel : UserControl
FileName = Path.GetFileName(filePath);
InitializeComponent();
DataContext = this;
if (Settings.ShowFileSizeInPreviewPanel)
{
@ -73,8 +108,8 @@ public partial class PreviewPanel : UserControl
{
_ = Task.Run(() =>
{
FileSize = GetFolderSize(filePath);
OnPropertyChanged(nameof(FileSize));
var size = GetFolderSize(filePath);
global::Avalonia.Threading.Dispatcher.UIThread.Post(() => FileSize = size);
}).ConfigureAwait(false);
}
}
@ -88,7 +123,7 @@ public partial class PreviewPanel : UserControl
if (Settings.ShowModifiedDateInPreviewPanel)
{
LastModifiedAt = type == ResultType.File ?
LastModifiedAt = type == ResultType.File ?
GetFileLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel) :
GetFolderLastModifiedAt(filePath, Settings.PreviewPanelDateFormat, Settings.PreviewPanelTimeFormat, Settings.ShowFileAgeInPreviewPanel);
}
@ -98,7 +133,24 @@ public partial class PreviewPanel : UserControl
private async Task LoadImageAsync()
{
PreviewImage = await Main.Context.API.LoadImageAsync(FilePath, true).ConfigureAwait(false);
try
{
var imagePath = FilePath;
if (string.IsNullOrEmpty(imagePath) || !File.Exists(imagePath))
return;
var bitmap = new Bitmap(imagePath);
global::Avalonia.Threading.Dispatcher.UIThread.Post(() => PreviewImage = bitmap);
}
catch (Exception e)
{
Main.Context.API.LogException(ClassName, $"Failed to load image for {FilePath}", e);
}
}
private void InitializeComponent()
{
global::Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(this);
}
public static string GetFileSize(string filePath)
@ -323,8 +375,15 @@ public partial class PreviewPanel : UserControl
var yearsDiff = now.Year - fileDateTime.Year;
if (now.Month < fileDateTime.Month || (now.Month == fileDateTime.Month && now.Day < fileDateTime.Day))
yearsDiff--;
yearsDiff--;
return yearsDiff == 1 ? Localize.OneYearAgo(): Localize.YearsAgo(yearsDiff);
return yearsDiff == 1 ? Localize.OneYearAgo() : Localize.YearsAgo(yearsDiff);
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Views/Avalonia/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -1,861 +0,0 @@
<UserControl
x:Class="Flow.Launcher.Plugin.Explorer.Views.ExplorerSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:qa="clr-namespace:Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:viewModels="clr-namespace:Flow.Launcher.Plugin.Explorer.ViewModels"
d:DataContext="{d:DesignInstance viewModels:SettingsViewModel}"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<DataTemplate x:Key="ListViewTemplateAccessLinks" DataType="qa:AccessLink">
<TextBlock Margin="0 5 0 5" Text="{Binding Path, Mode=OneTime}" />
</DataTemplate>
<DataTemplate x:Key="ListViewActionKeywords" DataType="{x:Type viewModels:ActionKeywordModel}">
<Grid>
<TextBlock
Margin="0 5 0 0"
IsEnabled="{Binding Enabled}"
Text="{Binding LocalizedDescription, Mode=OneTime}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource Color18B}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock
Margin="250 5 0 0"
IsEnabled="{Binding Enabled}"
Text="{Binding Keyword}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource Color18B}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Margin="480 5 0 0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Enabled}" Value="True">
<Setter Property="Text" Value="{DynamicResource plugin_explorer_enabled}" />
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
</DataTrigger>
<DataTrigger Binding="{Binding Enabled}" Value="False">
<Setter Property="Text" Value="{DynamicResource plugin_explorer_disabled}" />
<Setter Property="Foreground" Value="{DynamicResource Color18B}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</DataTemplate>
<Style x:Key="CustomExpanderStyle" TargetType="Expander">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="BorderBrush" Value="{DynamicResource Color03B}" />
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
<Setter Property="BorderThickness" Value="0 0 0 1" />
<Setter Property="Padding" Value="-8 18 24 14" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<DockPanel>
<ToggleButton
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}" />
<Border
x:Name="ContentPresenterBorder"
BorderBrush="{DynamicResource Color03B}"
BorderThickness="0 1 0 0">
<ContentPresenter
x:Name="ExpandSite"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
DockPanel.Dock="Bottom"
Focusable="false" />
<Border.LayoutTransform>
<ScaleTransform ScaleY="0" />
</Border.LayoutTransform>
</Border>
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter TargetName="ExpandSite" Property="Visibility" Value="Visible" />
<Setter TargetName="ContentPresenterBorder" Property="BorderThickness" Value="0 1 0 0" />
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ContentPresenterBorder"
Storyboard.TargetProperty="(Border.LayoutTransform).(ScaleTransform.ScaleY)"
From="0.0"
To="1.0"
Duration="0:0:0" />
<DoubleAnimation
Storyboard.TargetName="ContentPresenterBorder"
Storyboard.TargetProperty="(Border.Opacity)"
From="0.0"
To="1.0"
Duration="0:0:0" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ContentPresenterBorder"
Storyboard.TargetProperty="(Border.LayoutTransform).(ScaleTransform.ScaleY)"
From="1.0"
To="0.0"
Duration="0:0:0" />
<DoubleAnimation
Storyboard.TargetName="ContentPresenterBorder"
Storyboard.TargetProperty="(Border.Opacity)"
From="1.0"
To="0.0"
Duration="0:0:0" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<!-- Margin="-2 0 -2 0" is to make sure separator between expanders are expanded to left & right boarder -->
<StackPanel x:Name="ExpanderContainer" Margin="-2 0 -2 0">
<!-- General Settings Expander -->
<Expander
x:Name="GeneralSettingsExpander"
Expanded="Expander_Expanded"
Header="{DynamicResource plugin_explorer_generalsetting_header}"
IsExpanded="False"
Style="{StaticResource CustomExpanderStyle}">
<!-- Margin="32 -10 0 0" is to make sure elements is left aligned to the text in the expander text -->
<Grid Margin="32 -10 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
Content="{DynamicResource plugin_explorer_use_location_as_working_dir}"
IsChecked="{Binding Settings.UseLocationAsWorkingDir}" />
<CheckBox
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
Content="{DynamicResource plugin_explorer_default_open_in_file_manager}"
IsChecked="{Binding Settings.DefaultOpenFolderInFileManager}" />
<CheckBox
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
Content="{DynamicResource plugin_explorer_display_more_info_in_tooltip}"
IsChecked="{Binding Settings.DisplayMoreInformationInToolTip}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_file_editor_path}" />
<StackPanel
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Orientation="Horizontal">
<TextBox
Width="{StaticResource SettingPanelPathTextBoxWidth}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding FileEditorPath}"
TextWrapping="NoWrap" />
<Button
Margin="{StaticResource SettingPanelItemLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding OpenFileEditorPathCommand}"
Content="{DynamicResource select}" />
</StackPanel>
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_folder_editor_path}" />
<StackPanel
Grid.Row="4"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Orientation="Horizontal">
<TextBox
Width="{StaticResource SettingPanelPathTextBoxWidth}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding FolderEditorPath}"
TextWrapping="NoWrap" />
<Button
Margin="{StaticResource SettingPanelItemLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding OpenFolderEditorPathCommand}"
Content="{DynamicResource select}" />
</StackPanel>
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_shell_path}" />
<StackPanel
Grid.Row="5"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Orientation="Horizontal">
<TextBox
Width="{StaticResource SettingPanelPathTextBoxWidth}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding ShellPath}"
TextWrapping="NoWrap" />
<Button
Margin="{StaticResource SettingPanelItemLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding OpenShellPathCommand}"
Content="{DynamicResource select}" />
</StackPanel>
<TextBlock
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_Index_Search_Engine}" />
<ComboBox
Grid.Row="6"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
DisplayMemberPath="Description"
ItemsSource="{Binding IndexSearchEngines}"
SelectedItem="{Binding SelectedIndexSearchEngine}" />
<TextBlock
Grid.Row="7"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_Content_Search_Engine}" />
<ComboBox
Grid.Row="7"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
DisplayMemberPath="Description"
ItemsSource="{Binding ContentIndexSearchEngines}"
SelectedItem="{Binding SelectedContentSearchEngine}" />
<TextBlock
Grid.Row="8"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_Directory_Recursive_Search_Engine}" />
<ComboBox
Grid.Row="8"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
DisplayMemberPath="Description"
ItemsSource="{Binding PathEnumerationEngines}"
SelectedItem="{Binding SelectedPathEnumerationEngine}" />
<TextBlock
Grid.Row="9"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_Excluded_File_Types}" />
<TextBox
Grid.Row="9"
Grid.Column="1"
MinWidth="{StaticResource SettingPanelTextBoxMinWidth}"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
HorizontalAlignment="Left"
Text="{Binding ExcludedFileTypes}"
ToolTip="{DynamicResource plugin_explorer_Excluded_File_Types_Tooltip}" />
<TextBlock
Grid.Row="10"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_Maximum_Results}" />
<TextBox
Grid.Row="10"
Grid.Column="1"
MinWidth="{StaticResource SettingPanelTextBoxMinWidth}"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
MaxLength="6"
PreviewTextInput="AllowOnlyNumericInput"
Text="{Binding MaxResult}"
ToolTip="{DynamicResource plugin_explorer_Maximum_Results_Tooltip}" />
<Button
Grid.Row="11"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
Click="btnOpenIndexingOptions_Click"
Content="{DynamicResource plugin_explorer_Open_Window_Index_Option}" />
</Grid>
</Expander>
<!-- Native Context Menu Expander -->
<Expander
x:Name="ContextMenuExpander"
Expanded="Expander_Expanded"
Header="{DynamicResource plugin_explorer_native_context_menu_header}"
IsExpanded="False"
Style="{StaticResource CustomExpanderStyle}">
<Grid Margin="32 -10 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
Content="{DynamicResource plugin_explorer_native_context_menu_display_context_menu}"
IsChecked="{Binding ShowWindowsContextMenu}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_native_context_menu_include_patterns_guide}"
TextWrapping="Wrap" />
<TextBox
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
MinHeight="{StaticResource SettingPanelAreaTextBoxMinHeight}"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Stretch"
AcceptsReturn="True"
Text="{Binding WindowsContextMenuIncludedItems}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_native_context_menu_exclude_patterns_guide}"
TextWrapping="Wrap" />
<TextBox
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="2"
MinHeight="{StaticResource SettingPanelAreaTextBoxMinHeight}"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Stretch"
AcceptsReturn="True"
Text="{Binding WindowsContextMenuExcludedItems}"
TextWrapping="Wrap" />
</Grid>
</Expander>
<!-- Preview Panel Settings Expander -->
<Expander
x:Name="PreviewPanelExpander"
Expanded="Expander_Expanded"
Header="{DynamicResource plugin_explorer_previewpanel_setting_header}"
IsExpanded="False"
Style="{StaticResource CustomExpanderStyle}">
<Grid Margin="32 -10 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<DockPanel
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
LastChildFill="True">
<TextBlock
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_previewpanel_file_info_label}" />
<WrapPanel
Width="Auto"
HorizontalAlignment="Right"
DockPanel.Dock="Right">
<CheckBox
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Content="{DynamicResource plugin_explorer_previewpanel_display_file_size_checkbox}"
IsChecked="{Binding ShowFileSizeInPreviewPanel}" />
<CheckBox
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Content="{DynamicResource plugin_explorer_previewpanel_display_file_creation_checkbox}"
IsChecked="{Binding ShowCreatedDateInPreviewPanel}" />
<CheckBox
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Content="{DynamicResource plugin_explorer_previewpanel_display_file_modification_checkbox}"
IsChecked="{Binding ShowModifiedDateInPreviewPanel}" />
<CheckBox
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
Content="{DynamicResource plugin_explorer_previewpanel_display_file_age_checkbox}"
IsChecked="{Binding ShowFileAgeInPreviewPanel}" />
</WrapPanel>
</DockPanel>
<DockPanel
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
LastChildFill="True">
<TextBlock
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
IsEnabled="{Binding ShowPreviewPanelDateTimeChoices}"
Text="{DynamicResource plugin_explorer_previewpanel_date_and_time_format_label}"
Visibility="{Binding PreviewPanelDateTimeChoicesVisibility}" />
<WrapPanel
Width="Auto"
HorizontalAlignment="Right"
DockPanel.Dock="Right"
IsEnabled="{Binding ShowPreviewPanelDateTimeChoices}"
Visibility="{Binding PreviewPanelDateTimeChoicesVisibility}">
<StackPanel Margin="{StaticResource SettingPanelItemTopBottomMargin}" Orientation="Horizontal">
<ComboBox
Margin="{StaticResource SettingPanelItemLeftMargin}"
ItemsSource="{Binding DateFormatList}"
SelectedItem="{Binding PreviewPanelDateFormat}" />
<TextBlock
Margin="{StaticResource SettingPanelItemLeftMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{Binding PreviewPanelDateFormatDemo}" />
</StackPanel>
<StackPanel Margin="{StaticResource SettingPanelItemTopBottomMargin}" Orientation="Horizontal">
<ComboBox
Margin="{StaticResource SettingPanelItemLeftMargin}"
ItemsSource="{Binding TimeFormatList}"
SelectedItem="{Binding PreviewPanelTimeFormat}" />
<TextBlock
Margin="{StaticResource SettingPanelItemLeftMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{Binding PreviewPanelTimeFormatDemo}" />
</StackPanel>
</WrapPanel>
</DockPanel>
</Grid>
</Expander>
<!-- Everything Settings Expander -->
<Expander
x:Name="EverythingExpander"
Expanded="Expander_Expanded"
Header="{DynamicResource plugin_explorer_everything_setting_header}"
IsExpanded="False"
Style="{StaticResource CustomExpanderStyle}">
<Grid Margin="32 -10 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_everything_search_fullpath}"
IsChecked="{Binding Settings.EverythingSearchFullPath}" />
<CheckBox
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_everything_enable_run_count}"
IsChecked="{Binding Settings.EverythingEnableRunCount}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_everything_sort_option}" />
<ComboBox
Grid.Row="2"
Grid.Column="1"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
VerticalAlignment="Center"
ItemsSource="{Binding AllEverythingSortOptions}"
SelectedValue="{Binding SelectedEverythingSortOption, Mode=TwoWay}"
SelectedValuePath="Value"
DisplayMemberPath="Display">
</ComboBox>
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_everything_installed_path}" />
<TextBox
Grid.Row="3"
Grid.Column="1"
Width="{StaticResource SettingPanelPathTextBoxWidth}"
Margin="{StaticResource SettingPanelItemLeftTopBottomMargin}"
HorizontalAlignment="Left"
Text="{Binding EverythingInstalledPath}" />
<TextBlock
Name="tbFastSortWarning"
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
VerticalAlignment="Center"
Foreground="Orange"
Text="{Binding SortOptionWarningMessage, Mode=OneWay}"
TextAlignment="Left"
TextWrapping="Wrap"
Visibility="{Binding FastSortWarningVisibility, Mode=OneWay}" />
</Grid>
</Expander>
<!-- Manage Action Keywords Expander -->
<Expander
x:Name="ActionKeywordsExpander"
Expanded="Expander_Expanded"
Header="{DynamicResource plugin_explorer_manageactionkeywords_header}"
IsExpanded="False"
Style="{StaticResource CustomExpanderStyle}">
<Grid Margin="32 -10 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Stretch"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="1">
<ListView
ItemTemplate="{StaticResource ListViewActionKeywords}"
ItemsSource="{Binding ActionKeywordsModels}"
SelectedItem="{Binding SelectedActionKeyword}" />
</Border>
<StackPanel
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
MinWidth="100"
Command="{Binding EditActionKeywordCommand}"
Content="{DynamicResource plugin_explorer_edit}" />
</StackPanel>
</Grid>
</Expander>
<!-- Quick Access Links Expander -->
<Expander
x:Name="QuickAccessExpander"
Expanded="Expander_Expanded"
Header="{DynamicResource plugin_explorer_quickaccesslinks_header}"
IsExpanded="False"
Style="{StaticResource CustomExpanderStyle}">
<Grid Margin="32 -10 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Stretch"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="1">
<ListView
x:Name="lbxAccessLinks"
Height="200"
AllowDrop="True"
BorderThickness="1"
DragEnter="lbxAccessLinks_DragEnter"
Drop="LbxAccessLinks_OnDrop"
ItemsSource="{Binding Settings.QuickAccessLinks}"
Loaded="lbxAccessLinks_Loaded"
SelectedItem="{Binding SelectedQuickAccessLink}"
SizeChanged="lbxAccessLinks_SizeChanged">
<ListView.View>
<GridView>
<GridViewColumn Width="600" Header="{DynamicResource plugin_explorer_name}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="900" Header="{DynamicResource plugin_explorer_path}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path}" TextTrimming="CharacterEllipsis" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Border>
<StackPanel
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
MinWidth="100"
Command="{Binding RemoveLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
MinWidth="100"
Margin="{StaticResource SettingPanelItemLeftMargin}"
Command="{Binding EditQuickAccessLinkCommand}"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
MinWidth="100"
Margin="{StaticResource SettingPanelItemLeftMargin}"
Command="{Binding AddQuickAccessLinkCommand}"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</Grid>
</Expander>
<!-- Index Search Excluded Paths Expander -->
<Expander
x:Name="ExcludedPathsExpander"
Margin="0"
BorderThickness="0 0 0 0"
Expanded="Expander_Expanded"
Header="{DynamicResource plugin_explorer_indexsearchexcludedpaths_header}"
IsExpanded="False"
Style="{StaticResource CustomExpanderStyle}">
<Grid Margin="32 -10 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Stretch"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="1">
<ListView
Name="lbxExcludedPaths"
Height="200"
AllowDrop="True"
DragEnter="lbxAccessLinks_DragEnter"
Drop="LbxExcludedPaths_OnDrop"
ItemTemplate="{StaticResource ListViewTemplateAccessLinks}"
ItemsSource="{Binding Settings.IndexSearchExcludedSubdirectoryPaths}"
Loaded="lbxExcludedPaths_Loaded"
SelectedItem="{Binding SelectedIndexSearchExcludedPath}" />
</Border>
<StackPanel
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="{StaticResource SettingPanelItemTopBottomMargin}"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
MinWidth="100"
Command="{Binding RemoveLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
MinWidth="100"
Margin="{StaticResource SettingPanelItemLeftMargin}"
Command="{Binding EditIndexSearchExcludePathsCommand}"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
MinWidth="100"
Margin="{StaticResource SettingPanelItemLeftMargin}"
Command="{Binding AddIndexSearchExcludePathsCommand}"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</Grid>
</Expander>
</StackPanel>
</UserControl>

View file

@ -1,139 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using Flow.Launcher.Plugin.Explorer.ViewModels;
using DataFormats = System.Windows.DataFormats;
using DragDropEffects = System.Windows.DragDropEffects;
using DragEventArgs = System.Windows.DragEventArgs;
namespace Flow.Launcher.Plugin.Explorer.Views
{
public partial class ExplorerSettings
{
private readonly SettingsViewModel _viewModel;
private readonly List<Expander> _expanders;
public ExplorerSettings(SettingsViewModel viewModel)
{
_viewModel = viewModel;
DataContext = viewModel;
InitializeComponent();
DataContext = viewModel;
ActionKeywordModel.Init(viewModel.Settings);
_expanders = new List<Expander>
{
GeneralSettingsExpander,
ContextMenuExpander,
PreviewPanelExpander,
EverythingExpander,
ActionKeywordsExpander,
QuickAccessExpander,
ExcludedPathsExpander
};
}
private void AccessLinkDragDrop(string containerName, DragEventArgs e)
{
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
if (files == null || files.Length == 0)
{
return;
}
foreach (var s in files)
{
if (Directory.Exists(s))
{
var newFolderLink = new AccessLink
{
Path = s
};
_viewModel.AppendLink(containerName, newFolderLink);
}
}
}
private void lbxAccessLinks_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Link;
}
else
{
e.Effects = DragDropEffects.None;
}
}
private void btnOpenIndexingOptions_Click(object sender, RoutedEventArgs e)
{
SettingsViewModel.OpenWindowsIndexingOptions();
}
private void LbxAccessLinks_OnDrop(object sender, DragEventArgs e)
{
AccessLinkDragDrop("QuickAccessLink", e);
}
private void LbxExcludedPaths_OnDrop(object sender, DragEventArgs e)
{
AccessLinkDragDrop("IndexSearchExcludedPath", e);
}
private void AllowOnlyNumericInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = e.Text.ToCharArray().Any(c => !char.IsDigit(c));
}
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
if (sender is Expander expandedExpander)
{
// Ensure _expanders is not null and contains items
if (_expanders == null || _expanders.Count == 0) return;
foreach (var expander in _expanders)
{
if (expander != null && expander != expandedExpander && expander.IsExpanded)
{
expander.IsExpanded = false;
}
}
}
}
private void lbxAccessLinks_Loaded(object sender, RoutedEventArgs e)
{
lbxAccessLinks.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
}
private void lbxExcludedPaths_Loaded(object sender, RoutedEventArgs e)
{
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;
}
}
}

View file

@ -1,145 +0,0 @@
<UserControl
x:Class="Flow.Launcher.Plugin.Explorer.Views.PreviewPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=System.Runtime"
d:DesignHeight="300"
d:DesignWidth="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid x:Name="PreviewGrid" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition MinHeight="96" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Grid.Row="0"
MaxWidth="96"
MaxHeight="96"
Margin="5 12 8 0"
Source="{Binding PreviewImage, IsAsync=True, Mode=OneWay}" />
<Grid Grid.Row="1">
<TextBlock
Margin="5 6 5 16"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Style="{DynamicResource PreviewItemTitleStyle}"
Text="{Binding FileName, Mode=OneTime}"
TextAlignment="Center"
TextWrapping="Wrap" />
</Grid>
</Grid>
<StackPanel Grid.Row="1">
<Rectangle
x:Name="PreviewSep"
Width="Auto"
Height="1"
Margin="0 0 5 0"
HorizontalAlignment="Stretch"
Style="{DynamicResource SeparatorStyle}" />
<TextBlock
Margin="5 8 8 8"
Style="{DynamicResource PreviewItemSubTitleStyle}"
Text="{Binding FilePath, Mode=OneTime}" />
<Rectangle
Width="Auto"
Height="1"
Margin="0 0 5 0"
HorizontalAlignment="Stretch"
Fill="{Binding ElementName=PreviewSep, Path=Fill}">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding FileSizeVisibility}" Value="Collapsed" />
<Condition Binding="{Binding CreatedAtVisibility}" Value="Collapsed" />
<Condition Binding="{Binding LastModifiedAtVisibility}" Value="Collapsed" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Collapsed" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<Grid Margin="0 10 0 0" Visibility="{Binding FileInfoVisibility, Mode=OneTime}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="5 0 0 0"
VerticalAlignment="Top"
Style="{DynamicResource PreviewItemSubTitleStyle}"
Text="{DynamicResource FileSize}"
TextWrapping="Wrap"
Visibility="{Binding FileSizeVisibility, Mode=OneTime}" />
<TextBlock
Grid.Row="0"
Grid.Column="1"
Margin="0 0 13 0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Style="{DynamicResource PreviewItemSubTitleStyle}"
Text="{Binding FileSize, Mode=OneWay}"
TextWrapping="Wrap"
Visibility="{Binding FileSizeVisibility, Mode=OneTime}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="5 0 8 0"
VerticalAlignment="Top"
Style="{DynamicResource PreviewItemSubTitleStyle}"
Text="{DynamicResource Created}"
TextWrapping="Wrap"
Visibility="{Binding CreatedAtVisibility, Mode=OneTime}" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
Margin="0 0 13 0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Style="{DynamicResource PreviewItemSubTitleStyle}"
Text="{Binding CreatedAt, Mode=OneWay}"
TextWrapping="Wrap"
Visibility="{Binding CreatedAtVisibility, Mode=OneTime}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="5 0 8 0"
VerticalAlignment="Top"
Style="{DynamicResource PreviewItemSubTitleStyle}"
Text="{DynamicResource LastModified}"
TextWrapping="Wrap"
Visibility="{Binding LastModifiedAtVisibility, Mode=OneTime}" />
<TextBlock
Grid.Row="2"
Grid.Column="1"
Margin="0 0 13 0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Style="{DynamicResource PreviewItemSubTitleStyle}"
Text="{Binding LastModifiedAt, Mode=OneWay}"
TextWrapping="Wrap"
Visibility="{Binding LastModifiedAtVisibility, Mode=OneTime}" />
</Grid>
</StackPanel>
</Grid>
</UserControl>

View file

@ -1,174 +0,0 @@
<Window
x:Class="Flow.Launcher.Plugin.Explorer.Views.QuickAccessLinkSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Flow.Launcher.Plugin.Explorer.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="{DynamicResource plugin_explorer_manage_quick_access_links_header}"
Width="Auto"
Height="300"
Background="{DynamicResource PopuBGColor}"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Width"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</WindowChrome.WindowChrome>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="80" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="4"
Click="BtnCancel_OnClick"
Style="{StaticResource TitleBarCloseButtonStyle}">
<Path
Width="46"
Height="32"
Data="M 18,11 27,20 M 18,20 27,11"
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
StrokeThickness="1">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
<Setter Property="Opacity" Value="0.5" />
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Button>
</Grid>
</StackPanel>
<StackPanel Margin="26 0 26 0">
<StackPanel Margin="0 0 0 12">
<TextBlock
FontSize="20"
FontWeight="SemiBold"
Text="{DynamicResource plugin_explorer_manage_quick_access_links_header}"
TextAlignment="Left" />
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="100" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Name -->
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="0 10 0 0"
VerticalAlignment="Center"
FontSize="14"
Text="{DynamicResource plugin_explorer_name}" />
<TextBox
Grid.Row="0"
Grid.Column="1"
Margin="10 10 0 0"
VerticalAlignment="Center"
FontSize="12"
Text="{Binding SelectedName, Mode=TwoWay}" />
<!-- Type -->
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="0 10 0 0"
VerticalAlignment="Center"
FontSize="14"
Text="{DynamicResource plugin_explorer_type}" />
<StackPanel
Grid.Row="1"
Grid.Column="1"
Orientation="Horizontal">
<RadioButton
Margin="10 10 0 0"
Content="{DynamicResource plugin_explorer_file}"
GroupName="PathType"
IsChecked="{Binding IsFileSelected}" />
<RadioButton
Margin="10 10 0 0"
Content="{DynamicResource plugin_explorer_folder}"
GroupName="PathType"
IsChecked="{Binding IsFolderSelected}" />
</StackPanel>
<!-- Path -->
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="0 10 0 0"
VerticalAlignment="Center"
FontSize="14"
Text="{DynamicResource plugin_explorer_path}" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Width="250"
Margin="10 10 0 0"
VerticalAlignment="Center"
FontSize="12"
IsReadOnly="True"
Text="{Binding SelectedPath, Mode=TwoWay}" />
<Button
Grid.Row="2"
Grid.Column="2"
Height="Auto"
MinWidth="80"
Margin="10 10 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Click="SelectPath_OnClick"
Content="{DynamicResource select}" />
</Grid>
</StackPanel>
</StackPanel>
<Border
Grid.Row="1"
Background="{DynamicResource PopupButtonAreaBGColor}"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="0 1 0 0">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Button
x:Name="btnCancel"
Width="145"
Height="34"
Margin="0 0 5 1"
Click="BtnCancel_OnClick"
Content="{DynamicResource cancel}" />
<Button
Name="DownButton"
Width="145"
Height="34"
Margin="5 0 0 1"
Click="OnDoneButtonClick"
Style="{StaticResource AccentButtonStyle}">
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
</Button>
</StackPanel>
</Border>
</Grid>
</Window>

View file

@ -1,218 +0,0 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using Flow.Launcher.Plugin.Explorer.Helper;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using CommunityToolkit.Mvvm.ComponentModel;
namespace Flow.Launcher.Plugin.Explorer.Views;
[INotifyPropertyChanged]
public partial class QuickAccessLinkSettings
{
private static readonly string ClassName = nameof(QuickAccessLinkSettings);
private string _selectedPath;
public string SelectedPath
{
get => _selectedPath;
set
{
if (_selectedPath != value)
{
_selectedPath = value;
OnPropertyChanged();
if (string.IsNullOrEmpty(_selectedName))
{
SelectedName = _selectedPath.GetPathName();
}
if (!string.IsNullOrEmpty(_selectedPath))
{
_accessLinkType = GetResultType(_selectedPath);
}
}
}
}
private string _selectedName;
public string SelectedName
{
get
{
return string.IsNullOrEmpty(_selectedName) ? _selectedPath.GetPathName() : _selectedName;
}
set
{
if (_selectedName != value)
{
_selectedName = value;
OnPropertyChanged();
}
}
}
public bool IsFileSelected { get; set; }
public bool IsFolderSelected { get; set; } = true; // Default to Folder
private bool IsEdit { get; }
private AccessLink SelectedAccessLink { get; }
public ObservableCollection<AccessLink> QuickAccessLinks { get; }
private ResultType _accessLinkType = ResultType.Folder; // Default to Folder
public QuickAccessLinkSettings(ObservableCollection<AccessLink> quickAccessLinks)
{
IsEdit = false;
QuickAccessLinks = quickAccessLinks;
InitializeComponent();
}
public QuickAccessLinkSettings(ObservableCollection<AccessLink> quickAccessLinks, AccessLink selectedAccessLink)
{
IsEdit = true;
_selectedName = selectedAccessLink.Name;
_selectedPath = selectedAccessLink.Path;
_accessLinkType = GetResultType(_selectedPath); // Initialize link type
IsFileSelected = selectedAccessLink.Type == ResultType.File; // Initialize default selection
IsFolderSelected = !IsFileSelected;
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 = Localize.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 = Localize.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)
{
var index = QuickAccessLinks.IndexOf(SelectedAccessLink);
if (index >= 0)
{
var updatedLink = new AccessLink
{
Name = SelectedName,
Type = _accessLinkType,
Path = SelectedPath
};
QuickAccessLinks[index] = updatedLink;
}
DialogResult = true;
Close();
}
// Add a new one if the selected access link is null (should not happen in edit mode, but just in case)
else
{
AddNewAccessLink();
}
}
// Otherwise, add a new one
else
{
AddNewAccessLink();
}
void AddNewAccessLink()
{
var newAccessLink = new AccessLink
{
Name = SelectedName,
Type = _accessLinkType,
Path = SelectedPath
};
QuickAccessLinks.Add(newAccessLink);
DialogResult = true;
Close();
}
}
private void SelectPath_OnClick(object commandParameter, RoutedEventArgs e)
{
// Open file or folder selection dialog based on the selected radio button
if (IsFileSelected)
{
var openFileDialog = new OpenFileDialog
{
Multiselect = false,
CheckFileExists = true,
CheckPathExists = true
};
if (openFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK ||
string.IsNullOrEmpty(openFileDialog.FileName))
return;
SelectedPath = openFileDialog.FileName;
}
else // Folder selection
{
var folderBrowserDialog = new FolderBrowserDialog
{
ShowNewFolderButton = true
};
if (folderBrowserDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK ||
string.IsNullOrEmpty(folderBrowserDialog.SelectedPath))
return;
SelectedPath = folderBrowserDialog.SelectedPath;
}
}
private static ResultType GetResultType(string path)
{
// Check if the path is a file or folder
if (File.Exists(path))
{
return ResultType.File;
}
else if (Directory.Exists(path))
{
if (string.Equals(Path.GetPathRoot(path), path, StringComparison.OrdinalIgnoreCase))
{
return ResultType.Volume;
}
else
{
return ResultType.Folder;
}
}
else
{
// This should not happen, but just in case, we assume it's a folder
Main.Context.API.LogError(ClassName, $"The path '{path}' does not exist or is invalid. Defaulting to Folder type.");
return ResultType.Folder;
}
}
}

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/Views/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->

View file

@ -0,0 +1,19 @@
# Plugins/Flow.Launcher.Plugin.Explorer/
<!-- Explorer: Fill in this section with architectural understanding -->
## Responsibility
<!-- What is this folder's job in the system? -->
## Design
<!-- Key patterns, abstractions, architectural decisions -->
## Flow
<!-- How does data/control flow through this module? -->
## Integration
<!-- How does it connect to other parts of the system? -->