Merge pull request #892 from Flow-Launcher/PluginSearchTextBox

Implement PluginSearch TextBox & ContentControl Lazy Load
This commit is contained in:
Jeremy Wu 2022-10-11 20:56:29 +11:00 committed by GitHub
commit 3920574d87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 347 additions and 58 deletions

View file

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29806.167
# Visual Studio Version 17
VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Test", "Flow.Launcher.Test\Flow.Launcher.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}"
ProjectSection(ProjectDependencies) = postProject

View file

@ -60,6 +60,10 @@
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
<!-- Setting Plugin -->
<system:String x:Key="searchplugin">Search Plugin</system:String>
<system:String x:Key="searchpluginToolTip">Ctrl+F to search plugins</system:String>
<system:String x:Key="searchplugin_Noresult_Title">No results found</system:String>
<system:String x:Key="searchplugin_Noresult_Subtitle">Please try a different search.</system:String>
<system:String x:Key="plugin">Plugin</system:String>
<system:String x:Key="browserMorePlugins">Find more plugins</system:String>
<system:String x:Key="enable">On</system:String>

View file

@ -323,8 +323,6 @@
<Setter Property="Padding" Value="0,0,0,0" />
<Setter Property="Margin" Value="0,0,8,5" />
<Setter Property="BorderBrush" Value="{DynamicResource Color03B}" />
<Setter Property="MinWidth" Value="330" />
<Setter Property="Height" Value="98" />
<!--#region Template for blue highlight win10-->
<Setter Property="Template">
<Setter.Value>
@ -388,6 +386,55 @@
<!--#endregion-->
</Style>
<Style
x:Key="PluginListStyle"
BasedOn="{StaticResource {x:Type ListBox}}"
TargetType="ListBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Items.Count}" Value="0">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Margin="20,0,0,0">
<StackPanel>
<TextBlock
Margin="0,20,0,4"
FontWeight="Bold"
Text="{DynamicResource searchplugin_Noresult_Title}" />
<TextBlock Text="{DynamicResource searchplugin_Noresult_Subtitle}" />
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="StoreListStyle"
BasedOn="{StaticResource {x:Type ListBox}}"
TargetType="ListBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Items.Count}" Value="0">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Margin="20,0,0,0">
<StackPanel>
<TextBlock
Margin="0,20,0,4"
FontWeight="Bold"
Text="{DynamicResource searchplugin_Noresult_Title}" />
<TextBlock Text="{DynamicResource searchplugin_Noresult}" />
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- For Tab Header responsive Width -->
<Style TargetType="{x:Type TabControl}">
<Setter Property="FontSize" Value="14" />
@ -880,7 +927,7 @@
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem>
<TabItem KeyDown="OnPluginSettingKeydown">
<TabItem.Header>
<Grid>
<Grid.ColumnDefinitions>
@ -905,13 +952,66 @@
<RowDefinition Height="73" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" Padding="5,18,0,0">
<TextBlock
Margin="0,5,0,0"
FontSize="30"
Style="{StaticResource PageTitle}"
Text="{DynamicResource plugin}"
TextAlignment="left" />
<Border
Grid.Row="0"
Padding="5,18,0,0"
HorizontalAlignment="Stretch">
<DockPanel>
<TextBlock
Margin="0,5,0,0"
DockPanel.Dock="Left"
FontSize="30"
Style="{StaticResource PageTitle}"
Text="{DynamicResource plugin}"
TextAlignment="Left" />
<DockPanel DockPanel.Dock="Right">
<TextBox
Name="pluginFilterTxb"
Width="150"
Height="34"
Margin="0,5,26,0"
HorizontalAlignment="Right"
DockPanel.Dock="Right"
FontSize="14"
KeyDown="PluginFilterTxb_OnKeyDown"
LostFocus="RefreshPluginListEventHandler"
Text=""
TextAlignment="Left"
ToolTip="{DynamicResource searchpluginToolTip}"
ToolTipService.InitialShowDelay="200"
ToolTipService.Placement="Top">
<TextBox.Style>
<Style BasedOn="{StaticResource DefaultTextBoxStyle}" TargetType="TextBox">
<Style.Resources>
<VisualBrush
x:Key="CueBannerBrush"
AlignmentX="Left"
AlignmentY="Center"
Stretch="None">
<VisualBrush.Visual>
<Label
Padding="10,0,0,0"
Content="{DynamicResource searchplugin}"
Foreground="{DynamicResource CustomContextDisabled}" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="{DynamicResource Color02B}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DockPanel>
</DockPanel>
</Border>
<Border
Grid.Row="1"
@ -930,8 +1030,8 @@
ScrollViewer.CanContentScroll="False"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectedItem="{Binding SelectedPlugin}"
SelectionChanged="SelectedPluginChanged"
SnapsToDevicePixels="True">
SnapsToDevicePixels="True"
Style="{DynamicResource PluginListStyle}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="0,0,0,18" />
@ -945,7 +1045,7 @@
Padding="0"
Background="Transparent"
FlowDirection="RightToLeft"
IsExpanded="{Binding Mode=TwoWay, Path=IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}"
IsExpanded="{Binding Mode=TwoWay, Path=IsExpanded}"
Style="{StaticResource ExpanderStyle1}">
<Expander.Header>
<Grid
@ -1119,8 +1219,7 @@
Margin="0"
Padding="1"
VerticalAlignment="Stretch"
Content="{Binding SettingControl}"
SizeChanged="ItemSizeChanged" />
Content="{Binding SettingControl}" />
</StackPanel>
<StackPanel>
@ -1248,7 +1347,7 @@
</TabItem>
<!--#region Plugin Store-->
<TabItem>
<TabItem KeyDown="PluginStore_OnKeyDown">
<TabItem.Header>
<Grid>
<Grid.ColumnDefinitions>
@ -1285,19 +1384,66 @@
Text="{DynamicResource pluginStore}"
TextAlignment="left" />
</Border>
<Border
<DockPanel
Grid.Row="0"
Grid.Column="1"
Padding="5,18,32,0">
Margin="5,24,0,0">
<TextBox
Name="pluginStoreFilterTxb"
Width="150"
Height="34"
Margin="0,0,26,0"
HorizontalAlignment="Right"
DockPanel.Dock="Right"
FontSize="14"
KeyDown="PluginStoreFilterTxb_OnKeyDown"
LostFocus="RefreshPluginStoreEventHandler"
Text=""
TextAlignment="Left"
ToolTip="{DynamicResource searchpluginToolTip}"
ToolTipService.InitialShowDelay="200"
ToolTipService.Placement="Top">
<TextBox.Style>
<Style BasedOn="{StaticResource DefaultTextBoxStyle}" TargetType="TextBox">
<Style.Resources>
<VisualBrush
x:Key="CueBannerBrush"
AlignmentX="Left"
AlignmentY="Center"
Stretch="None">
<VisualBrush.Visual>
<Label
Padding="10,0,0,0"
Content="{DynamicResource searchplugin}"
Foreground="{DynamicResource CustomContextDisabled}" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="{DynamicResource Color02B}" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button
Height="34"
Margin="0,5,0,5"
Margin="0,5,10,5"
Padding="12,4,12,4"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Click="OnPluginStoreRefreshClick"
Content="{DynamicResource refresh}"
DockPanel.Dock="Right"
FontSize="13" />
</Border>
</DockPanel>
<Border
Grid.Row="1"
Grid.Column="0"
@ -1306,7 +1452,7 @@
<ListBox
x:Name="StoreListBox"
Width="Auto"
Margin="6,0,0,0"
Margin="5,1,0,0"
Padding="0,0,0,0"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center"
@ -1314,14 +1460,15 @@
ItemContainerStyle="{StaticResource StoreList}"
ItemsSource="{Binding ExternalPlugins}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionMode="Single">
SelectionMode="Single"
Style="{DynamicResource StoreListStyle}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid
Margin="0,0,6,18"
Margin="0,0,17,18"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Columns="2"
Columns="3"
IsItemsHost="True"
SnapsToDevicePixels="True" />
</ItemsPanelTemplate>
@ -1354,15 +1501,56 @@
</ControlTemplate>
</ToggleButton.Template>
<Grid HorizontalAlignment="Left" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="72" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid
Height="160"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="56" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0,0,2,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Border
x:Name="LabelNew"
Margin="0,10,8,0"
Padding="6,2,6,2"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="#f14551"
CornerRadius="4"
Visibility="Collapsed">
<TextBlock
FontSize="11"
Foreground="White"
Text="New" />
</Border>
<Border
x:Name="Labelinstalled"
Margin="0,10,8,0"
Padding="6,2,6,2"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{DynamicResource ToggleSwitchFillOn}"
CornerRadius="4"
Visibility="Collapsed">
<TextBlock
FontSize="11"
Foreground="White"
Text="Installed" />
</Border>
</StackPanel>
<StackPanel
Grid.Row="0"
Margin="0,0,0,0"
VerticalAlignment="Center">
VerticalAlignment="Top">
<StackPanel.Style>
<Style>
<Setter Property="StackPanel.Visibility" Value="Visible" />
@ -1376,16 +1564,18 @@
<Image
Width="32"
Height="32"
Margin="8,0,6,0"
VerticalAlignment="Center"
Margin="20,20,6,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding IcoPath, IsAsync=True}"
Stretch="Uniform" />
</StackPanel>
<StackPanel
Grid.Column="1"
Margin="0,0,8,0"
VerticalAlignment="Center"
Grid.Row="1"
Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"
Margin="0,6,0,0"
VerticalAlignment="Top"
Panel.ZIndex="0">
<StackPanel.Style>
<Style>
@ -1398,14 +1588,16 @@
</Style>
</StackPanel.Style>
<TextBlock
Padding="0,0,20,0"
Padding="20,0,20,0"
VerticalAlignment="Top"
FontWeight="SemiBold"
Foreground="{DynamicResource Color05B}"
Text="{Binding Name}"
TextWrapping="WrapWithOverflow"
ToolTip="{Binding Version}" />
<TextBlock
Margin="0,2,0,0"
Padding="0,0,20,0"
Margin="0,6,0,0"
Padding="20,0,22,20"
Foreground="{DynamicResource Color04B}"
TextWrapping="WrapWithOverflow">
<Run
@ -1416,6 +1608,7 @@
</StackPanel>
<StackPanel
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
@ -1433,7 +1626,7 @@
<Grid
Height="94"
Height="180"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Panel.ZIndex="1">
@ -1445,12 +1638,13 @@
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
VerticalAlignment="Top">
<StackPanel Orientation="Vertical">
<TextBlock
Margin="20,0,0,0"
Margin="20,20,20,0"
Padding="0,0,0,0"
FontWeight="Bold"
Foreground="{DynamicResource Color05B}"
@ -1458,14 +1652,14 @@
TextWrapping="WrapWithOverflow"
ToolTip="{Binding Name}" />
<TextBlock
Margin="10,0,0,0"
Margin="20,4,20,0"
Padding="0,0,20,0"
Foreground="{DynamicResource Color05B}"
Text="{Binding Version}"
TextWrapping="WrapWithOverflow"
ToolTip="{Binding Version}" />
</StackPanel>
<StackPanel Margin="20,2,120,0" Orientation="Vertical">
<StackPanel Margin="20,6,20,0" Orientation="Vertical">
<TextBlock Padding="0,0,0,0" TextWrapping="Wrap">
<Hyperlink
Foreground="{DynamicResource Color04B}"
@ -1476,12 +1670,14 @@
</TextBlock>
</StackPanel>
</StackPanel>
<Border Padding="0,0,20,0">
<Border
Grid.Row="0"
Grid.Column="1"
Padding="0,0,0,0">
<Button
Name="ShortCutButtonPrev"
Grid.Column="1"
MinHeight="40"
Margin="0,0,0,0"
Margin="0,70,20,0"
Padding="15,5,15,5"
HorizontalAlignment="Right"
VerticalAlignment="Center"
@ -1497,7 +1693,6 @@
</ListBox.ItemTemplate>
</ListBox>
</Border>
</Grid>
</TabItem>
<!--#endregion-->
@ -2171,7 +2366,7 @@
<Button
MinWidth="100"
Margin="10,10,0,10"
Click="OnAddCustomeHotkeyClick"
Click="OnAddCustomHotkeyClick"
Content="{DynamicResource add}" />
</StackPanel>
</Grid>

View file

@ -17,6 +17,7 @@ using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Interop;
@ -25,6 +26,7 @@ using System.Windows.Navigation;
using Button = System.Windows.Controls.Button;
using Control = System.Windows.Controls.Control;
using ListViewItem = System.Windows.Controls.ListViewItem;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using MessageBox = System.Windows.MessageBox;
using TextBox = System.Windows.Controls.TextBox;
using ThemeManager = ModernWpf.ThemeManager;
@ -57,6 +59,13 @@ namespace Flow.Launcher
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
HwndTarget hwndTarget = hwndSource.CompositionTarget;
hwndTarget.RenderMode = RenderMode.SoftwareOnly;
pluginListView = (CollectionView)CollectionViewSource.GetDefaultView(Plugins.ItemsSource);
pluginListView.Filter = PluginListFilter;
pluginStoreView = (CollectionView)CollectionViewSource.GetDefaultView(StoreListBox.ItemsSource);
pluginStoreView.Filter = PluginStoreFilter;
InitializePosition();
}
@ -163,7 +172,7 @@ namespace Flow.Launcher
}
}
private void OnAddCustomeHotkeyClick(object sender, RoutedEventArgs e)
private void OnAddCustomHotkeyClick(object sender, RoutedEventArgs e)
{
new CustomQueryHotkeySetting(this, settings).ShowDialog();
}
@ -361,13 +370,77 @@ namespace Flow.Launcher
RefreshMaximizeRestoreButton();
}
private void SelectedPluginChanged(object sender, SelectionChangedEventArgs e)
private CollectionView pluginListView;
private CollectionView pluginStoreView;
private bool PluginListFilter(object item)
{
Plugins.ScrollIntoView(Plugins.SelectedItem);
if (string.IsNullOrEmpty(pluginFilterTxb.Text))
return true;
if (item is PluginViewModel model)
{
return StringMatcher.FuzzySearch(pluginFilterTxb.Text, model.PluginPair.Metadata.Name).IsSearchPrecisionScoreMet();
}
return false;
}
private void ItemSizeChanged(object sender, SizeChangedEventArgs e)
private bool PluginStoreFilter(object item)
{
Plugins.ScrollIntoView(Plugins.SelectedItem);
if (string.IsNullOrEmpty(pluginStoreFilterTxb.Text))
return true;
if (item is UserPlugin model)
{
return StringMatcher.FuzzySearch(pluginStoreFilterTxb.Text, model.Name).IsSearchPrecisionScoreMet()
|| StringMatcher.FuzzySearch(pluginStoreFilterTxb.Text, model.Description).IsSearchPrecisionScoreMet();
}
return false;
}
private string lastPluginListSearch = "";
private string lastPluginStoreSearch = "";
private void RefreshPluginListEventHandler(object sender, RoutedEventArgs e)
{
if (pluginFilterTxb.Text != lastPluginListSearch)
{
lastPluginListSearch = pluginFilterTxb.Text;
pluginListView.Refresh();
}
}
private void RefreshPluginStoreEventHandler(object sender, RoutedEventArgs e)
{
if (pluginStoreFilterTxb.Text != lastPluginStoreSearch)
{
lastPluginStoreSearch = pluginStoreFilterTxb.Text;
pluginStoreView.Refresh();
}
}
private void PluginFilterTxb_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
RefreshPluginListEventHandler(sender, e);
}
private void PluginStoreFilterTxb_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
RefreshPluginStoreEventHandler(sender, e);
}
private void OnPluginSettingKeydown(object sender, KeyEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control && e.Key == Key.F)
pluginFilterTxb.Focus();
}
private void PluginStore_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.F && (Keyboard.Modifiers & ModifierKeys.Control) != 0)
{
pluginStoreFilterTxb.Focus();
}
}
public void InitializePosition()

View file

@ -1,4 +1,4 @@
using System.Windows;
using System.Windows;
using System.Windows.Media;
using Flow.Launcher.Plugin;
using Flow.Launcher.Infrastructure.Image;
@ -30,9 +30,26 @@ namespace Flow.Launcher.ViewModel
get => !PluginPair.Metadata.Disabled;
set => PluginPair.Metadata.Disabled = !value;
}
public bool IsExpanded
{
get => _isExpanded;
set
{
_isExpanded = value;
OnPropertyChanged();
OnPropertyChanged(nameof(SettingControl));
}
}
private Control _settingControl;
public Control SettingControl => _settingControl ??= PluginPair.Plugin is not ISettingProvider settingProvider ? new Control() : settingProvider.CreateSettingPanel();
private bool _isExpanded;
public Control SettingControl
=> IsExpanded
? _settingControl
??= PluginPair.Plugin is not ISettingProvider settingProvider
? new Control()
: settingProvider.CreateSettingPanel()
: null;
public Visibility ActionKeywordsVisibility => PluginPair.Metadata.ActionKeywords.Count == 1 ? Visibility.Visible : Visibility.Collapsed;
public string InitilizaTime => PluginPair.Metadata.InitTime + "ms";