diff --git a/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml
new file mode 100644
index 000000000..cf8a01004
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml.cs b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml.cs
new file mode 100644
index 000000000..dfa03a204
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/InstalledPluginDisplay.xaml.cs
@@ -0,0 +1,9 @@
+namespace Flow.Launcher.Resources.Controls;
+
+public partial class InstalledPluginDisplay
+{
+ public InstalledPluginDisplay()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginsViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginsViewModel.cs
new file mode 100644
index 000000000..0fa147075
--- /dev/null
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginsViewModel.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Linq;
+using CommunityToolkit.Mvvm.Input;
+using Flow.Launcher.Core.Plugin;
+using Flow.Launcher.Infrastructure;
+using Flow.Launcher.Infrastructure.UserSettings;
+using Flow.Launcher.Plugin;
+using Flow.Launcher.ViewModel;
+
+#nullable enable
+
+namespace Flow.Launcher.SettingPages.ViewModels;
+
+public partial class SettingsPanePluginsViewModel : BaseModel
+{
+ private readonly Settings _settings;
+
+ public SettingsPanePluginsViewModel(Settings settings)
+ {
+ _settings = settings;
+ }
+
+ public string FilterText { get; set; } = string.Empty;
+
+ public PluginViewModel? SelectedPlugin { get; set; }
+ private IEnumerable? _pluginViewModels;
+ private IEnumerable PluginViewModels => _pluginViewModels ??= PluginManager.AllPlugins
+ .OrderBy(x => x.Metadata.Disabled)
+ .ThenBy(y => y.Metadata.Name)
+ .Select(p => new PluginViewModel { PluginPair = p })
+ .ToList();
+ public List FilteredPluginViewModels => PluginViewModels
+ .Where(v =>
+ string.IsNullOrEmpty(FilterText) ||
+ StringMatcher.FuzzySearch(FilterText, v.PluginPair.Metadata.Name).IsSearchPrecisionScoreMet() ||
+ StringMatcher.FuzzySearch(FilterText, v.PluginPair.Metadata.Description).IsSearchPrecisionScoreMet()
+ )
+ .ToList();
+
+ [RelayCommand]
+ private void TogglePlugin()
+ {
+ if (SelectedPlugin is null) return;
+ var id = SelectedPlugin.PluginPair.Metadata.ID;
+ // used to sync the current status from the plugin manager into the setting to keep consistency after save
+ _settings.PluginSettings.Plugins[id].Disabled = SelectedPlugin.PluginPair.Metadata.Disabled;
+ }
+}
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml
index 9a9b05183..9be27a088 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml
+++ b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml
@@ -3,27 +3,22 @@
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.SettingPages.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ui="http://schemas.modernwpf.com/2019"
+ xmlns:viewModels="clr-namespace:Flow.Launcher.SettingPages.ViewModels"
+ xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
Title="Plugins"
+ FocusManager.FocusedElement="{Binding ElementName=PluginFilterTextbox}"
+ KeyDown="SettingsPanePlugins_OnKeyDown"
+ d:DataContext="{d:DesignInstance viewModels:SettingsPanePluginsViewModel}"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
-
-
-
-
-
-
+
-
-
-
-
@@ -40,52 +35,50 @@
Style="{StaticResource PageTitle}"
Text="{DynamicResource plugins}"
TextAlignment="Left" />
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs
index 91c12e7d6..d48505c3d 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs
+++ b/Flow.Launcher/SettingPages/Views/SettingsPanePlugins.xaml.cs
@@ -1,28 +1,30 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
using System.Windows.Navigation;
-using System.Windows.Shapes;
+using Flow.Launcher.SettingPages.ViewModels;
-namespace Flow.Launcher.SettingPages.Views
+namespace Flow.Launcher.SettingPages.Views;
+
+public partial class SettingsPanePlugins
{
- ///
- /// Plugins.xaml에 대한 상호 작용 논리
- ///
- public partial class SettingsPanePlugins
+ private SettingsPanePluginsViewModel _viewModel = null!;
+
+ protected override void OnNavigatedTo(NavigationEventArgs e)
{
- public SettingsPanePlugins()
+ if (!IsInitialized)
{
+ if (e.ExtraData is not SettingWindow.PaneData { Settings: { } settings })
+ throw new ArgumentException("Settings are required for SettingsPaneHotkey.");
+ _viewModel = new SettingsPanePluginsViewModel(settings);
+ DataContext = _viewModel;
InitializeComponent();
}
+ base.OnNavigatedTo(e);
+ }
+
+ private void SettingsPanePlugins_OnKeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key is not Key.F || Keyboard.Modifiers is not ModifierKeys.Control) return;
+ PluginFilterTextbox.Focus();
}
}
diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs
index d2919507d..f3d201f0b 100644
--- a/Flow.Launcher/ViewModel/PluginViewModel.cs
+++ b/Flow.Launcher/ViewModel/PluginViewModel.cs
@@ -1,4 +1,5 @@
-using System.Windows;
+using System.Linq;
+using System.Windows;
using System.Windows.Media;
using Flow.Launcher.Plugin;
using Flow.Launcher.Infrastructure.Image;
@@ -26,6 +27,21 @@ namespace Flow.Launcher.ViewModel
}
}
+ private string PluginManagerActionKeyword
+ {
+ get
+ {
+ var keyword = PluginManager
+ .GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7")
+ .Metadata.ActionKeywords.FirstOrDefault();
+ return keyword switch
+ {
+ null or "*" => string.Empty,
+ _ => keyword
+ };
+ }
+ }
+
private async void LoadIconAsync()
{
@@ -62,11 +78,13 @@ namespace Flow.Launcher.ViewModel
private Control _settingControl;
private bool _isExpanded;
+
+ public bool HasSettingControl => PluginPair.Plugin is ISettingProvider;
public Control SettingControl
=> IsExpanded
? _settingControl
??= PluginPair.Plugin is not ISettingProvider settingProvider
- ? new Control()
+ ? null
: settingProvider.CreateSettingPanel()
: null;
private ImageSource _image = ImageLoader.MissingImage;
@@ -94,7 +112,7 @@ namespace Flow.Launcher.ViewModel
[RelayCommand]
private void EditPluginPriority()
{
- PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(PluginPair.Metadata.ID, this);
+ PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(PluginPair. Metadata.ID, this);
priorityChangeWindow.ShowDialog();
}
@@ -106,6 +124,19 @@ namespace Flow.Launcher.ViewModel
PluginManager.API.OpenDirectory(directory);
}
+ [RelayCommand]
+ private void OpenSourceCodeLink()
+ {
+ PluginManager.API.OpenUrl(PluginPair.Metadata.Website);
+ }
+
+ [RelayCommand]
+ private void OpenDeletePluginWindow()
+ {
+ PluginManager.API.ChangeQuery($"{PluginManagerActionKeyword} uninstall {PluginPair.Metadata.Name}".Trim(), true);
+ PluginManager.API.ShowMainWindow();
+ }
+
public static bool IsActionKeywordRegistered(string newActionKeyword) => PluginManager.ActionKeywordRegistered(newActionKeyword);
[RelayCommand]