Rewrite Explorer View Logic based on MVVM Pattern to avoid complicated View Logic

This commit is contained in:
Hongtao Zhang 2022-06-30 23:56:15 -05:00
parent 9b471de3cf
commit d5e1b7cb22
No known key found for this signature in database
GPG key ID: 75F655B91C7AC9BB
15 changed files with 401 additions and 345 deletions

View file

@ -181,6 +181,13 @@ namespace Flow.Launcher.Plugin
/// <param name="newActionKeyword">The actionkeyword that is supposed to be removed</param>
void RemoveActionKeyword(string pluginId, string oldActionKeyword);
/// <summary>
/// Check whether specific ActionKeyword is assigned to any of the plugin
/// </summary>
/// <param name="actionKeyword">The actionkeyword for checking</param>
/// <returns>True if the actionkeyword is already assigned, False otherwise</returns>
bool ActionKeywordAssigned(string actionKeyword);
/// <summary>
/// Log debug message
/// Message will only be logged in Debug mode

View file

@ -142,6 +142,8 @@ namespace Flow.Launcher
public void AddActionKeyword(string pluginId, string newActionKeyword) =>
PluginManager.AddActionKeyword(pluginId, newActionKeyword);
public bool ActionKeywordAssigned(string actionKeyword) => PluginManager.ActionKeywordRegistered(actionKeyword);
public void RemoveActionKeyword(string pluginId, string oldActionKeyword) =>
PluginManager.RemoveActionKeyword(pluginId, oldActionKeyword);

View file

@ -50,5 +50,4 @@
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
</ItemGroup>
</Project>

View file

@ -20,6 +20,7 @@
<system:String x:Key="plugin_explorer_delete">Delete</system:String>
<system:String x:Key="plugin_explorer_edit">Edit</system:String>
<system:String x:Key="plugin_explorer_add">Add</system:String>
<system:String x:Key="plugin_explorer_generalsetting_header">General Setting</system:String>
<system:String x:Key="plugin_explorer_manageactionkeywords_header">Customise Action Keywords</system:String>
<system:String x:Key="plugin_explorer_quickaccesslinks_header">Quick Access Links</system:String>
<system:String x:Key="plugin_explorer_indexsearchexcludedpaths_header">Index Search Excluded Paths</system:String>

View file

@ -44,7 +44,7 @@ namespace Flow.Launcher.Plugin.Explorer
if (Settings.QuickFolderAccessLinks.Any())
{
Settings.QuickAccessLinks = Settings.QuickFolderAccessLinks;
Settings.QuickFolderAccessLinks = new List<AccessLink>();
Settings.QuickFolderAccessLinks = new();
}
contextMenu = new ContextMenu(Context, Settings, viewModel);

View file

@ -8,7 +8,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks
{
private const int quickAccessResultScore = 100;
internal static List<Result> AccessLinkListMatched(Query query, List<AccessLink> accessLinks)
internal static List<Result> AccessLinkListMatched(Query query, IEnumerable<AccessLink> accessLinks)
{
if (string.IsNullOrEmpty(query.Search))
return new List<Result>();
@ -29,7 +29,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks
}).ToList();
}
internal static List<Result> AccessLinkListAll(Query query, List<AccessLink> accessLinks)
internal static List<Result> AccessLinkListAll(Query query, IEnumerable<AccessLink> accessLinks)
=> accessLinks
.OrderBy(x => x.Type)
.ThenBy(x => x.Name)

View file

@ -81,7 +81,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
string searchString,
Func<CSearchQueryHelper> createQueryHelper,
Func<string, string> constructQuery,
List<AccessLink> exclusionList,
IEnumerable<AccessLink> exclusionList,
CancellationToken token)
{
var regexMatch = Regex.Match(searchString, ReservedStringPattern);

View file

@ -5,6 +5,7 @@ using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using Flow.Launcher.Plugin.Explorer.Search.WindowsIndex;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.Json.Serialization;
@ -14,14 +15,14 @@ namespace Flow.Launcher.Plugin.Explorer
{
public int MaxResult { get; set; } = 100;
public List<AccessLink> QuickAccessLinks { get; set; } = new List<AccessLink>();
public ObservableCollection<AccessLink> QuickAccessLinks { get; set; } = new ();
// as at v1.7.0 this is to maintain backwards compatibility, need to be removed afterwards.
public List<AccessLink> QuickFolderAccessLinks { get; set; } = new List<AccessLink>();
public ObservableCollection<AccessLink> QuickFolderAccessLinks { get; set; } = new ();
public bool UseWindowsIndexForDirectorySearch { get; set; } = true;
public List<AccessLink> IndexSearchExcludedSubdirectoryPaths { get; set; } = new List<AccessLink>();
public ObservableCollection<AccessLink> IndexSearchExcludedSubdirectoryPaths { get; set; } = new ObservableCollection<AccessLink>();
public string SearchActionKeyword { get; set; } = Query.GlobalPluginWildcardSign;

View file

@ -0,0 +1,57 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Flow.Launcher.Plugin.Explorer.Views
{
public class ActionKeywordModel : INotifyPropertyChanged
{
private static Settings _settings;
public event PropertyChangedEventHandler PropertyChanged;
public static void Init(Settings settings)
{
_settings = settings;
}
internal ActionKeywordModel(Settings.ActionKeyword actionKeyword, string description)
{
KeywordProperty = actionKeyword;
Description = description;
}
public string Description { get; private init; }
internal Settings.ActionKeyword KeywordProperty { get; }
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string? keyword;
public string Keyword
{
get => keyword ??= _settings.GetActionKeyword(KeywordProperty);
set
{
keyword = value;
_settings.SetActionKeyword(KeywordProperty, value);
OnPropertyChanged();
}
}
private bool? enabled;
public bool Enabled
{
get => enabled ??= _settings.GetActionKeywordEnabled(KeywordProperty);
set
{
enabled = value;
_settings.SetActionKeywordEnabled(KeywordProperty, value);
OnPropertyChanged();
}
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.Windows.Input;
namespace Flow.Launcher.Plugin.Explorer.ViewModels
{
internal class RelayCommand : ICommand
{
private Action<object> _action;
public RelayCommand(Action<object> action)
{
_action = action;
}
public virtual bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public virtual void Execute(object parameter)
{
_action?.Invoke(parameter);
}
}
}

View file

@ -2,8 +2,13 @@
using Flow.Launcher.Infrastructure.Storage;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using Flow.Launcher.Plugin.Explorer.Views;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Input;
namespace Flow.Launcher.Plugin.Explorer.ViewModels
{
@ -25,6 +30,175 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Context.API.SaveSettingJsonStorage<Settings>();
}
public AccessLink SelectedQuickAccessLink { get; set; }
public AccessLink SelectedIndexSearchExcludedPath { get; set; }
public ActionKeywordModel SelectedActionKeyword { get; set; }
public ICommand RemoveLinkCommand => new RelayCommand(RemoveLink);
public ICommand EditLinkCommand => new RelayCommand(EditLink);
public ICommand AddLinkCommand => new RelayCommand(AddLink);
public ICommand EditActionKeywordCommand => new RelayCommand(EditActionKeyword);
private void EditActionKeyword(object obj)
{
if (SelectedActionKeyword is not ActionKeywordModel actionKeyword)
{
ShowUnselectedMessage();
return;
}
var actionKeywordWindow = new ActionKeywordSetting(actionKeyword, Context.API);
if (actionKeywordWindow.ShowDialog() ?? false)
{
if (actionKeyword.Enabled && !actionKeywordWindow.KeywordEnabled)
{
Context.API.RemoveActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
}
else if (!actionKeyword.Enabled && actionKeywordWindow.KeywordEnabled)
{
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
}
else if (actionKeyword.Enabled && actionKeywordWindow.KeywordEnabled)
{
// same keyword will have dialog result false
Context.API.RemoveActionKeyword(Context.CurrentPluginMetadata.ID, actionKeyword.Keyword);
Context.API.AddActionKeyword(Context.CurrentPluginMetadata.ID, actionKeywordWindow.ActionKeyword);
}
(actionKeyword.Keyword, actionKeyword.Enabled) = (actionKeywordWindow.ActionKeyword, actionKeywordWindow.KeywordEnabled);
}
}
private AccessLink? PromptUserSelectPath(ResultType type, string initialDirectory = null)
{
AccessLink newAccessLink = null;
if (type is ResultType.Folder)
{
var folderBrowserDialog = new FolderBrowserDialog();
if (initialDirectory is not null)
folderBrowserDialog.InitialDirectory = initialDirectory;
if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
return newAccessLink;
newAccessLink = new AccessLink { Path = folderBrowserDialog.SelectedPath };
}
else if (type is ResultType.File)
{
var openFileDialog = new OpenFileDialog();
if (initialDirectory is not null)
openFileDialog.InitialDirectory = initialDirectory;
if (openFileDialog.ShowDialog() != DialogResult.OK)
return newAccessLink;
newAccessLink = new AccessLink { Path = openFileDialog.FileName };
}
return newAccessLink;
}
private void EditLink(object obj)
{
if (obj is not string container) return;
AccessLink selectedLink;
ObservableCollection<AccessLink> collection;
switch (container)
{
case "QuickAccessLink":
if (SelectedQuickAccessLink == null)
{
ShowUnselectedMessage();
return;
}
selectedLink = SelectedQuickAccessLink;
collection = Settings.QuickAccessLinks;
break;
case "IndexSearchExcludedPaths":
if (SelectedIndexSearchExcludedPath == null)
{
ShowUnselectedMessage();
return;
}
selectedLink = SelectedIndexSearchExcludedPath;
collection = Settings.IndexSearchExcludedSubdirectoryPaths;
break;
default:
return;
}
var link = PromptUserSelectPath(selectedLink.Type,
selectedLink.Type == ResultType.Folder
? selectedLink.Path
: Path.GetDirectoryName(selectedLink.Path));
if (link is null)
return;
collection.Remove(selectedLink);
collection.Add(link);
}
private void ShowUnselectedMessage()
{
string warning = Context.API.GetTranslation("plugin_explorer_make_selection_warning");
MessageBox.Show(warning);
}
private void AddLink(object obj)
{
if (obj is not string container) return;
var folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() != DialogResult.OK)
return;
var newAccessLink = new AccessLink { Path = folderBrowserDialog.SelectedPath };
switch (container)
{
case "QuickAccessLink":
if (SelectedQuickAccessLink == null) return;
Settings.QuickAccessLinks.Add(newAccessLink);
break;
case "IndexSearchExcludedPaths":
if (SelectedIndexSearchExcludedPath == null) return;
Settings.IndexSearchExcludedSubdirectoryPaths.Add(newAccessLink);
break;
}
}
private void RemoveLink(object obj)
{
if (obj is not string container) return;
switch (container)
{
case "QuickAccessLink":
if (SelectedQuickAccessLink == null) return;
Settings.QuickAccessLinks.Remove(SelectedQuickAccessLink);
break;
case "IndexSearchExcludedPaths":
if (SelectedIndexSearchExcludedPath == null) return;
Settings.IndexSearchExcludedSubdirectoryPaths.Remove(SelectedIndexSearchExcludedPath);
break;
}
Save();
}
internal void RemoveLinkFromQuickAccess(AccessLink selectedRow) => Settings.QuickAccessLinks.Remove(selectedRow);
internal void RemoveAccessLinkFromExcludedIndexPaths(AccessLink selectedRow) => Settings.IndexSearchExcludedSubdirectoryPaths.Remove(selectedRow);
@ -41,16 +215,11 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
Process.Start(psi);
}
internal void UpdateActionKeyword(Settings.ActionKeyword modifiedActionKeyword, string newActionKeyword, string oldActionKeyword)
{
internal void UpdateActionKeyword(Settings.ActionKeyword modifiedActionKeyword, string newActionKeyword, string oldActionKeyword) =>
PluginManager.ReplaceActionKeyword(Context.CurrentPluginMetadata.ID, oldActionKeyword, newActionKeyword);
}
internal bool IsActionKeywordAlreadyAssigned(string newActionKeyword)
{
return PluginManager.ActionKeywordRegistered(newActionKeyword);
}
internal bool IsActionKeywordAlreadyAssigned(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
internal bool IsNewActionKeywordGlobal(string newActionKeyword) => newActionKeyword == Query.GlobalPluginWildcardSign;
}
}
}

View file

@ -96,7 +96,7 @@
Name="ChkActionKeywordEnabled"
Width="auto"
VerticalAlignment="Center"
IsChecked="{Binding Enabled}"
IsChecked="{Binding KeywordEnabled}"
ToolTip="{DynamicResource plugin_explorer_actionkeyword_enabled_tooltip}" />
</StackPanel>
</StackPanel>

View file

@ -13,9 +13,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
/// </summary>
public partial class ActionKeywordSetting : Window
{
private SettingsViewModel settingsViewModel;
public ActionKeywordView CurrentActionKeyword { get; set; }
public ActionKeywordModel CurrentActionKeyword { get; set; }
public string ActionKeyword
{
@ -23,24 +21,22 @@ namespace Flow.Launcher.Plugin.Explorer.Views
set
{
// Set Enable to be true if user change ActionKeyword
Enabled = true;
KeywordEnabled = true;
actionKeyword = value;
}
}
public bool Enabled { get; set; }
public bool KeywordEnabled { get; set; }
private string actionKeyword;
private readonly IPublicAPI api;
public ActionKeywordSetting(SettingsViewModel settingsViewModel,
ActionKeywordView selectedActionKeyword)
public ActionKeywordSetting(ActionKeywordModel selectedActionKeyword, IPublicAPI api)
{
this.settingsViewModel = settingsViewModel;
CurrentActionKeyword = selectedActionKeyword;
this.api = api;
ActionKeyword = selectedActionKeyword.Keyword;
Enabled = selectedActionKeyword.Enabled;
KeywordEnabled = selectedActionKeyword.Enabled;
InitializeComponent();
@ -52,56 +48,43 @@ namespace Flow.Launcher.Plugin.Explorer.Views
if (string.IsNullOrEmpty(ActionKeyword))
ActionKeyword = Query.GlobalPluginWildcardSign;
if (CurrentActionKeyword.Keyword == ActionKeyword && CurrentActionKeyword.Enabled == Enabled)
if (CurrentActionKeyword.Keyword == ActionKeyword && CurrentActionKeyword.Enabled == KeywordEnabled)
{
DialogResult = false;
Close();
return;
}
if (ActionKeyword == "")
{
ActionKeyword = "*";
}
if (ActionKeyword == Query.GlobalPluginWildcardSign)
switch (CurrentActionKeyword.KeywordProperty)
{
case Settings.ActionKeyword.FileContentSearchActionKeyword:
MessageBox.Show(settingsViewModel.Context.API.GetTranslation("plugin_explorer_globalActionKeywordInvalid"));
MessageBox.Show(api.GetTranslation("plugin_explorer_globalActionKeywordInvalid"));
return;
case Settings.ActionKeyword.QuickAccessActionKeyword:
MessageBox.Show(settingsViewModel.Context.API.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid"));
MessageBox.Show(api.GetTranslation("plugin_explorer_quickaccess_globalActionKeywordInvalid"));
return;
}
var oldActionKeyword = CurrentActionKeyword.Keyword;
if (!Enabled || !settingsViewModel.IsActionKeywordAlreadyAssigned(ActionKeyword))
if (!KeywordEnabled || !api.ActionKeywordAssigned(ActionKeyword))
{
// Update View Data
CurrentActionKeyword.Keyword = Enabled == true ? ActionKeyword : Query.GlobalPluginWildcardSign;
CurrentActionKeyword.Enabled = Enabled;
switch (Enabled)
{
// reset to global so it does not take up an action keyword when disabled
// not for null Enable plugin
case false when oldActionKeyword != Query.GlobalPluginWildcardSign:
settingsViewModel.UpdateActionKeyword(CurrentActionKeyword.KeywordProperty,
Query.GlobalPluginWildcardSign, oldActionKeyword);
break;
default:
settingsViewModel.UpdateActionKeyword(CurrentActionKeyword.KeywordProperty,
CurrentActionKeyword.Keyword, oldActionKeyword);
break;
}
DialogResult = true;
Close();
return;
}
// The keyword is not valid, so show message
MessageBox.Show(settingsViewModel.Context.API.GetTranslation("newActionKeywordsHasBeenAssigned"));
MessageBox.Show(api.GetTranslation("newActionKeywordsHasBeenAssigned"));
}
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
private void TxtCurrentActionKeyword_OnKeyDown(object sender, KeyEventArgs e)

View file

@ -4,13 +4,13 @@
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:viewModels="clr-namespace:Flow.Launcher.Plugin.Explorer.ViewModels"
xmlns:views="clr-namespace:Flow.Launcher.Plugin.Explorer.Views"
xmlns:qa="clr-namespace:Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks"
d:DataContext="{d:DesignInstance viewModels:SettingsViewModel}"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance viewModels:SettingsViewModel}">
mc:Ignorable="d">
<UserControl.Resources>
<DataTemplate x:Key="ListViewTemplateAccessLinks" DataType="qa:AccessLink">
<TextBlock Margin="0,5,0,5" Text="{Binding Path, Mode=OneTime}" />
@ -37,7 +37,7 @@
<TextBlock
Margin="250,5,0,0"
IsEnabled="{Binding Enabled}"
Text="{Binding Keyword, Mode=OneTime}">
Text="{Binding Keyword}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
@ -59,94 +59,111 @@
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<ScrollViewer
Grid.Row="0"
Margin="20,35,0,0"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Auto">
<StackPanel Grid.Row="0" Margin="20,35,0,0">
<StackPanel>
<Expander
Header="{DynamicResource plugin_explorer_generalsetting_header}">
<StackPanel Orientation="Vertical">
<ComboBox
Width="200"
ItemsSource="{Binding Settings.SortOptions, Mode=OneWay}"
SelectedItem="{Binding Settings.SortOption}" />
</StackPanel>
</Expander>
<Expander
Name="expActionKeywords"
Collapsed="expActionKeywords_Collapsed"
Height="auto"
Expanded="expActionKeywords_Click"
Header="{DynamicResource plugin_explorer_manageactionkeywords_header}">
<ListView x:Name="lbxActionKeywords" ItemTemplate="{StaticResource ListViewActionKeywords}" />
<DockPanel HorizontalAlignment="Stretch">
<ListView
x:Name="lbxActionKeywords"
DockPanel.Dock="Top"
ItemTemplate="{StaticResource ListViewActionKeywords}"
SelectedItem="{Binding SelectedActionKeyword}"/>
<Button
MinWidth="100"
Margin="10"
Command="{Binding EditActionKeywordCommand}"
Content="{DynamicResource plugin_explorer_edit}" />
</DockPanel>
</Expander>
<Expander
Name="expAccessLinks"
Margin="0,10,0,0"
Collapsed="expAccessLinks_Collapsed"
Expanded="expAccessLinks_Click"
Header="{DynamicResource plugin_explorer_quickaccesslinks_header}">
<ListView
x:Name="lbxAccessLinks"
AllowDrop="True"
DragEnter="lbxAccessLinks_DragEnter"
Drop="lbxAccessLinks_Drop"
ItemTemplate="{StaticResource ListViewTemplateAccessLinks}" />
<DockPanel HorizontalAlignment="Stretch">
<ListView
x:Name="lbxAccessLinks"
Height="200"
AllowDrop="True"
DockPanel.Dock="Top"
ItemTemplate="{StaticResource ListViewTemplateAccessLinks}"
ItemsSource="{Binding Settings.QuickAccessLinks}"
SelectedItem="{Binding SelectedQuickAccessLink}" />
<StackPanel
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
MinWidth="100"
Margin="10"
Command="{Binding RemoveLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding EditLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding AddLinkCommand}"
CommandParameter="QuickAccessLink"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</DockPanel>
</Expander>
<Expander
x:Name="expExcludedPaths"
Margin="0,10,0,0"
Collapsed="expExcludedPaths_Collapsed"
Expanded="expExcludedPaths_Click"
Header="{DynamicResource plugin_explorer_indexsearchexcludedpaths_header}">
<ListView
x:Name="lbxExcludedPaths"
AllowDrop="True"
DragEnter="lbxAccessLinks_DragEnter"
Drop="lbxAccessLinks_Drop"
ItemTemplate="{StaticResource ListViewActionKeywords}" />
<DockPanel HorizontalAlignment="Stretch">
<ListView
Name="lbxExcludedPaths"
AllowDrop="True"
DockPanel.Dock="Top"
ItemTemplate="{StaticResource ListViewActionKeywords}"
ItemsSource="{Binding Settings.IndexSearchExcludedSubdirectoryPaths}"
SelectedItem="{Binding SelectedIndexSearchExcludedPath}" />
<StackPanel
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
MinWidth="100"
Margin="10"
Command="{Binding RemoveLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding EditLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
MinWidth="100"
Margin="10"
Command="{Binding AddLinkCommand}"
CommandParameter="IndexSearchExcludedPaths"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</DockPanel>
</Expander>
<ComboBox Grid.Row="4"
Grid.Column="1"
Width="200"
SelectedItem="{Binding Settings.SortOption}"
ItemsSource="{Binding Settings.SortOptions, Mode=OneWay}">
</ComboBox>
</StackPanel>
</ScrollViewer>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="450" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Left"
Orientation="Horizontal">
<Button
x:Name="btnIndexingOptions"
MinWidth="130"
Margin="10"
Click="btnOpenIndexingOptions_Click"
Content="{DynamicResource plugin_explorer_manageindexoptions}" />
</StackPanel>
<StackPanel
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
x:Name="btnDelete"
MinWidth="100"
Margin="10"
Click="btnDelete_Click"
Content="{DynamicResource plugin_explorer_delete}" />
<Button
x:Name="btnEdit"
MinWidth="100"
Margin="10"
Click="btnEdit_Click"
Content="{DynamicResource plugin_explorer_edit}" />
<Button
x:Name="btnAdd"
MinWidth="100"
Margin="10"
Click="btnAdd_Click"
Content="{DynamicResource plugin_explorer_add}" />
</StackPanel>
</Grid>
</StackPanel>
</Grid>
</UserControl>

View file

@ -21,12 +21,12 @@ namespace Flow.Launcher.Plugin.Explorer.Views
{
private readonly SettingsViewModel viewModel;
private List<ActionKeywordView> actionKeywordsListView;
private List<ActionKeywordModel> actionKeywordsListView;
public ExplorerSettings(SettingsViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
this.viewModel = viewModel;
@ -35,7 +35,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
lbxExcludedPaths.ItemsSource = this.viewModel.Settings.IndexSearchExcludedSubdirectoryPaths;
actionKeywordsListView = new List<ActionKeywordView>
actionKeywordsListView = new List<ActionKeywordModel>
{
new(Settings.ActionKeyword.SearchActionKeyword,
viewModel.Context.API.GetTranslation("plugin_explorer_actionkeywordview_search")),
@ -51,208 +51,42 @@ namespace Flow.Launcher.Plugin.Explorer.Views
lbxActionKeywords.ItemsSource = actionKeywordsListView;
ActionKeywordView.Init(viewModel.Settings);
ActionKeywordModel.Init(viewModel.Settings);
RefreshView();
}
public void RefreshView()
{
lbxAccessLinks.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
lbxExcludedPaths.Items.SortDescriptions.Add(new SortDescription("Path", ListSortDirection.Ascending));
SetButtonVisibilityToHidden();
if (expAccessLinks.IsExpanded || expExcludedPaths.IsExpanded || expActionKeywords.IsExpanded)
{
if (!expActionKeywords.IsExpanded)
btnAdd.Visibility = Visibility.Visible;
if (expActionKeywords.IsExpanded
&& btnEdit.Visibility == Visibility.Hidden)
btnEdit.Visibility = Visibility.Visible;
if (lbxAccessLinks.Items.Count == 0 && lbxExcludedPaths.Items.Count == 0
&& btnDelete.Visibility == Visibility.Visible
&& btnEdit.Visibility == Visibility.Visible)
{
btnDelete.Visibility = Visibility.Hidden;
btnEdit.Visibility = Visibility.Hidden;
}
if (expAccessLinks.IsExpanded
&& lbxAccessLinks.Items.Count > 0
&& btnDelete.Visibility == Visibility.Hidden
&& btnEdit.Visibility == Visibility.Hidden)
{
btnDelete.Visibility = Visibility.Visible;
btnEdit.Visibility = Visibility.Visible;
}
if (expExcludedPaths.IsExpanded
&& lbxExcludedPaths.Items.Count > 0
&& btnDelete.Visibility == Visibility.Hidden
&& btnEdit.Visibility == Visibility.Hidden)
{
btnDelete.Visibility = Visibility.Visible;
btnEdit.Visibility = Visibility.Visible;
}
}
lbxAccessLinks.Items.Refresh();
lbxExcludedPaths.Items.Refresh();
lbxActionKeywords.Items.Refresh();
}
private void expActionKeywords_Click(object sender, RoutedEventArgs e)
{
if (expActionKeywords.IsExpanded)
expActionKeywords.Height = 205;
if (expExcludedPaths.IsExpanded)
expExcludedPaths.IsExpanded = false;
if (expAccessLinks.IsExpanded)
expAccessLinks.IsExpanded = false;
RefreshView();
}
private void expActionKeywords_Collapsed(object sender, RoutedEventArgs e)
{
expActionKeywords.Height = double.NaN;
SetButtonVisibilityToHidden();
}
private void expAccessLinks_Click(object sender, RoutedEventArgs e)
{
if (expAccessLinks.IsExpanded)
expAccessLinks.Height = 205;
if (expExcludedPaths.IsExpanded)
expExcludedPaths.IsExpanded = false;
if (expActionKeywords.IsExpanded)
expActionKeywords.IsExpanded = false;
RefreshView();
}
private void expAccessLinks_Collapsed(object sender, RoutedEventArgs e)
{
expAccessLinks.Height = double.NaN;
SetButtonVisibilityToHidden();
}
private void expExcludedPaths_Click(object sender, RoutedEventArgs e)
{
if (expExcludedPaths.IsExpanded)
expAccessLinks.Height = double.NaN;
if (expAccessLinks.IsExpanded)
expAccessLinks.IsExpanded = false;
if (expActionKeywords.IsExpanded)
expActionKeywords.IsExpanded = false;
RefreshView();
}
private void expExcludedPaths_Collapsed(object sender, RoutedEventArgs e)
{
SetButtonVisibilityToHidden();
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
var selectedRow = lbxAccessLinks.SelectedItem as AccessLink ?? lbxExcludedPaths.SelectedItem as AccessLink;
if (selectedRow != null)
{
string msg = string.Format(viewModel.Context.API.GetTranslation("plugin_explorer_delete_folder_link"),
selectedRow.Path);
if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
if (expAccessLinks.IsExpanded)
viewModel.RemoveLinkFromQuickAccess(selectedRow);
if (expExcludedPaths.IsExpanded)
viewModel.RemoveAccessLinkFromExcludedIndexPaths(selectedRow);
RefreshView();
}
}
else
{
string warning = viewModel.Context.API.GetTranslation("plugin_explorer_select_folder_link_warning");
MessageBox.Show(warning);
}
}
private void btnEdit_Click(object sender, RoutedEventArgs e)
{
if (lbxActionKeywords.SelectedItem is ActionKeywordView)
{
var selectedActionKeyword = lbxActionKeywords.SelectedItem as ActionKeywordView;
var actionKeywordWindow = new ActionKeywordSetting(viewModel,
selectedActionKeyword);
actionKeywordWindow.ShowDialog();
RefreshView();
}
else
{
var selectedRow = lbxAccessLinks.SelectedItem as AccessLink ??
lbxExcludedPaths.SelectedItem as AccessLink;
if (selectedRow != null)
{
var folderBrowserDialog = new FolderBrowserDialog();
folderBrowserDialog.SelectedPath = selectedRow.Path;
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
if (expAccessLinks.IsExpanded)
{
var link = viewModel.Settings.QuickAccessLinks.First(x => x.Path == selectedRow.Path);
link.Path = folderBrowserDialog.SelectedPath;
}
if (expExcludedPaths.IsExpanded)
{
var link = viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.First(x =>
x.Path == selectedRow.Path);
link.Path = folderBrowserDialog.SelectedPath;
}
}
RefreshView();
}
else
{
string warning = viewModel.Context.API.GetTranslation("plugin_explorer_make_selection_warning");
MessageBox.Show(warning);
}
}
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
var folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
var newAccessLink = new AccessLink {Path = folderBrowserDialog.SelectedPath};
AddAccessLink(newAccessLink);
}
RefreshView();
}
private void lbxAccessLinks_Drop(object sender, DragEventArgs e)
{
@ -261,18 +95,16 @@ namespace Flow.Launcher.Plugin.Explorer.Views
if (files != null && files.Count() > 0)
{
if (expAccessLinks.IsExpanded && viewModel.Settings.QuickAccessLinks == null)
viewModel.Settings.QuickAccessLinks = new List<AccessLink>();
viewModel.Settings.QuickAccessLinks = new();
foreach (string s in files)
{
if (Directory.Exists(s))
{
var newFolderLink = new AccessLink {Path = s};
var newFolderLink = new AccessLink { Path = s };
AddAccessLink(newFolderLink);
}
RefreshView();
}
}
}
@ -283,7 +115,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
&& !viewModel.Settings.QuickAccessLinks.Any(x => x.Path == newAccessLink.Path))
{
if (viewModel.Settings.QuickAccessLinks == null)
viewModel.Settings.QuickAccessLinks = new List<AccessLink>();
viewModel.Settings.QuickAccessLinks = new();
viewModel.Settings.QuickAccessLinks.Add(newAccessLink);
}
@ -292,7 +124,7 @@ namespace Flow.Launcher.Plugin.Explorer.Views
&& !viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.Any(x => x.Path == newAccessLink.Path))
{
if (viewModel.Settings.IndexSearchExcludedSubdirectoryPaths == null)
viewModel.Settings.IndexSearchExcludedSubdirectoryPaths = new List<AccessLink>();
viewModel.Settings.IndexSearchExcludedSubdirectoryPaths = new ();
viewModel.Settings.IndexSearchExcludedSubdirectoryPaths.Add(newAccessLink);
}
@ -314,44 +146,5 @@ namespace Flow.Launcher.Plugin.Explorer.Views
{
SettingsViewModel.OpenWindowsIndexingOptions();
}
public void SetButtonVisibilityToHidden()
{
btnDelete.Visibility = Visibility.Hidden;
btnEdit.Visibility = Visibility.Hidden;
btnAdd.Visibility = Visibility.Hidden;
}
}
public class ActionKeywordView
{
private static Settings _settings;
public static void Init(Settings settings)
{
_settings = settings;
}
internal ActionKeywordView(Settings.ActionKeyword actionKeyword, string description)
{
KeywordProperty = actionKeyword;
Description = description;
}
public string Description { get; private init; }
internal Settings.ActionKeyword KeywordProperty { get; }
public string Keyword
{
get => _settings.GetActionKeyword(KeywordProperty);
set => _settings.SetActionKeyword(KeywordProperty, value);
}
public bool Enabled
{
get => _settings.GetActionKeywordEnabled(KeywordProperty);
set => _settings.SetActionKeywordEnabled(KeywordProperty, value);
}
}
}