mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #3350 from Jack251970/search_delay
Input Smooth & Search delay
This commit is contained in:
commit
6a964b031a
21 changed files with 632 additions and 118 deletions
|
|
@ -622,7 +622,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
API.ShowMsg(API.GetTranslation("failedToRemovePluginCacheTitle"),
|
||||
string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name));
|
||||
}
|
||||
Settings.Plugins.Remove(plugin.ID);
|
||||
Settings.RemovePluginSettings(plugin.ID);
|
||||
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return plugins;
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> DotNetPlugins(List<PluginMetadata> source)
|
||||
private static IEnumerable<PluginPair> DotNetPlugins(List<PluginMetadata> source)
|
||||
{
|
||||
var erroredPlugins = new List<string>();
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return plugins;
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
||||
private static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
||||
{
|
||||
return source
|
||||
.Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase))
|
||||
|
|
@ -146,7 +146,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
});
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> ExecutableV2Plugins(IEnumerable<PluginMetadata> source)
|
||||
private static IEnumerable<PluginPair> ExecutableV2Plugins(IEnumerable<PluginMetadata> source)
|
||||
{
|
||||
return source
|
||||
.Where(o => o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
|
|
@ -6,8 +7,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public class PluginsSettings : BaseModel
|
||||
{
|
||||
private string pythonExecutablePath = string.Empty;
|
||||
public string PythonExecutablePath {
|
||||
get { return pythonExecutablePath; }
|
||||
public string PythonExecutablePath
|
||||
{
|
||||
get => pythonExecutablePath;
|
||||
set
|
||||
{
|
||||
pythonExecutablePath = value;
|
||||
|
|
@ -18,7 +20,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
private string nodeExecutablePath = string.Empty;
|
||||
public string NodeExecutablePath
|
||||
{
|
||||
get { return nodeExecutablePath; }
|
||||
get => nodeExecutablePath;
|
||||
set
|
||||
{
|
||||
nodeExecutablePath = value;
|
||||
|
|
@ -26,17 +28,32 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, Plugin> Plugins { get; set; } = new Dictionary<string, Plugin>();
|
||||
/// <summary>
|
||||
/// Only used for serialization
|
||||
/// </summary>
|
||||
public Dictionary<string, Plugin> Plugins { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Update plugin settings with metadata.
|
||||
/// FL will get default values from metadata first and then load settings to metadata
|
||||
/// </summary>
|
||||
/// <param name="metadatas">Parsed plugin metadatas</param>
|
||||
public void UpdatePluginSettings(List<PluginMetadata> metadatas)
|
||||
{
|
||||
foreach (var metadata in metadatas)
|
||||
{
|
||||
if (Plugins.TryGetValue(metadata.ID, out var settings))
|
||||
{
|
||||
// If settings exist, update settings & metadata value
|
||||
// update settings values with metadata
|
||||
if (string.IsNullOrEmpty(settings.Version))
|
||||
{
|
||||
settings.Version = metadata.Version;
|
||||
}
|
||||
settings.DefaultActionKeywords = metadata.ActionKeywords; // metadata provides default values
|
||||
settings.DefaultSearchDelayTime = metadata.SearchDelayTime; // metadata provides default values
|
||||
|
||||
// update metadata values with settings
|
||||
if (settings.ActionKeywords?.Count > 0)
|
||||
{
|
||||
metadata.ActionKeywords = settings.ActionKeywords;
|
||||
|
|
@ -49,30 +66,65 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
metadata.Disabled = settings.Disabled;
|
||||
metadata.Priority = settings.Priority;
|
||||
metadata.SearchDelayTime = settings.SearchDelayTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If settings does not exist, create a new one
|
||||
Plugins[metadata.ID] = new Plugin
|
||||
{
|
||||
ID = metadata.ID,
|
||||
Name = metadata.Name,
|
||||
Version = metadata.Version,
|
||||
ActionKeywords = metadata.ActionKeywords,
|
||||
DefaultActionKeywords = metadata.ActionKeywords, // metadata provides default values
|
||||
ActionKeywords = metadata.ActionKeywords, // use default value
|
||||
Disabled = metadata.Disabled,
|
||||
Priority = metadata.Priority
|
||||
Priority = metadata.Priority,
|
||||
DefaultSearchDelayTime = metadata.SearchDelayTime, // metadata provides default values
|
||||
SearchDelayTime = metadata.SearchDelayTime, // use default value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Plugin GetPluginSettings(string id)
|
||||
{
|
||||
if (Plugins.TryGetValue(id, out var plugin))
|
||||
{
|
||||
return plugin;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Plugin RemovePluginSettings(string id)
|
||||
{
|
||||
Plugins.Remove(id, out var plugin);
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
||||
public class Plugin
|
||||
{
|
||||
public string ID { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Version { get; set; }
|
||||
public List<string> ActionKeywords { get; set; } // a reference of the action keywords from plugin manager
|
||||
|
||||
[JsonIgnore]
|
||||
public List<string> DefaultActionKeywords { get; set; }
|
||||
|
||||
// a reference of the action keywords from plugin manager
|
||||
public List<string> ActionKeywords { get; set; }
|
||||
|
||||
public int Priority { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public SearchDelayTime? DefaultSearchDelayTime { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchDelayTime? SearchDelayTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used only to save the state of the plugin in settings
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public double SettingWindowHeight { get; set; } = 700;
|
||||
public double? SettingWindowTop { get; set; } = null;
|
||||
public double? SettingWindowLeft { get; set; } = null;
|
||||
public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal;
|
||||
public WindowState SettingWindowState { get; set; } = WindowState.Normal;
|
||||
|
||||
bool _showPlaceholder { get; set; } = false;
|
||||
public bool ShowPlaceholder
|
||||
|
|
@ -310,7 +310,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
bool _hideNotifyIcon { get; set; }
|
||||
public bool HideNotifyIcon
|
||||
{
|
||||
get { return _hideNotifyIcon; }
|
||||
get => _hideNotifyIcon;
|
||||
set
|
||||
{
|
||||
_hideNotifyIcon = value;
|
||||
|
|
@ -320,6 +320,11 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public bool LeaveCmdOpen { get; set; }
|
||||
public bool HideWhenDeactivated { get; set; } = true;
|
||||
|
||||
public bool SearchQueryResultsWithDelay { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchDelayTime SearchDelayTime { get; set; } = SearchDelayTime.Normal;
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor;
|
||||
|
||||
|
|
@ -342,7 +347,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
[JsonIgnore]
|
||||
public bool WMPInstalled { get; set; } = true;
|
||||
|
||||
|
||||
// This needs to be loaded last by staying at the bottom
|
||||
public PluginsSettings PluginSettings { get; set; } = new PluginsSettings();
|
||||
|
||||
|
|
|
|||
|
|
@ -92,12 +92,18 @@ namespace Flow.Launcher.Plugin
|
|||
/// All action keywords of plugin.
|
||||
/// </summary>
|
||||
public List<string> ActionKeywords { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Hide plugin keyword setting panel.
|
||||
/// </summary>
|
||||
public bool HideActionKeywordPanel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plugin search delay time. Null means use default search delay time.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchDelayTime? SearchDelayTime { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Plugin icon path.
|
||||
/// </summary>
|
||||
|
|
|
|||
32
Flow.Launcher.Plugin/SearchDelayTime.cs
Normal file
32
Flow.Launcher.Plugin/SearchDelayTime.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
namespace Flow.Launcher.Plugin;
|
||||
|
||||
/// <summary>
|
||||
/// Enum for search delay time
|
||||
/// </summary>
|
||||
public enum SearchDelayTime
|
||||
{
|
||||
/// <summary>
|
||||
/// Very long search delay time. 250ms.
|
||||
/// </summary>
|
||||
VeryLong,
|
||||
|
||||
/// <summary>
|
||||
/// Long search delay time. 200ms.
|
||||
/// </summary>
|
||||
Long,
|
||||
|
||||
/// <summary>
|
||||
/// Normal search delay time. 150ms. Default value.
|
||||
/// </summary>
|
||||
Normal,
|
||||
|
||||
/// <summary>
|
||||
/// Short search delay time. 100ms.
|
||||
/// </summary>
|
||||
Short,
|
||||
|
||||
/// <summary>
|
||||
/// Very short search delay time. 50ms.
|
||||
/// </summary>
|
||||
VeryShort
|
||||
}
|
||||
|
|
@ -102,6 +102,15 @@
|
|||
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
|
||||
<system:String x:Key="searchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Delay for a while to search when typing. This reduces interface jumpiness and result load.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Default Search Delay Time</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Plugin default delay time after which search results appear when typing is stopped.</system:String>
|
||||
<system:String x:Key="SearchDelayTimeVeryLong">Very long</system:String>
|
||||
<system:String x:Key="SearchDelayTimeLong">Long</system:String>
|
||||
<system:String x:Key="SearchDelayTimeNormal">Normal</system:String>
|
||||
<system:String x:Key="SearchDelayTimeShort">Short</system:String>
|
||||
<system:String x:Key="SearchDelayTimeVeryShort">Very short</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
|
|
@ -118,6 +127,8 @@
|
|||
<system:String x:Key="currentActionKeywords">Current action keyword</system:String>
|
||||
<system:String x:Key="newActionKeyword">New action keyword</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Change Action Keywords</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Plugin seach delay time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Seach Delay Time</system:String>
|
||||
<system:String x:Key="currentPriority">Current Priority</system:String>
|
||||
<system:String x:Key="newPriority">New Priority</system:String>
|
||||
<system:String x:Key="priority">Priority</system:String>
|
||||
|
|
@ -133,6 +144,7 @@
|
|||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
|
||||
<system:String x:Key="default">Default</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Plugin Store</system:String>
|
||||
|
|
@ -353,6 +365,12 @@
|
|||
<system:String x:Key="completedSuccessfully">Completed successfully</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
|
||||
<system:String x:Key="searchDelayTime_tips">Select the search delay time you like to use for the plugin. Select "{0}" if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||
<system:String x:Key="currentSearchDelayTime">Current search delay time</system:String>
|
||||
<system:String x:Key="newSearchDelayTime">New search delay time</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Custom Query Hotkey</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Press a custom hotkey to open Flow Launcher and input the specified query automatically.</system:String>
|
||||
|
|
|
|||
|
|
@ -251,7 +251,8 @@
|
|||
PreviewDragOver="QueryTextBox_OnPreviewDragOver"
|
||||
PreviewKeyUp="QueryTextBox_KeyUp"
|
||||
Style="{DynamicResource QueryBoxStyle}"
|
||||
Text="{Binding QueryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Text="{Binding QueryText, Mode=OneWay}"
|
||||
TextChanged="QueryTextBox_TextChanged1"
|
||||
Visibility="Visible"
|
||||
WindowChrome.IsHitTestVisibleInChrome="True">
|
||||
<TextBox.CommandBindings>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ using Flow.Launcher.Infrastructure.UserSettings;
|
|||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using ModernWpf.Controls;
|
||||
using DataObject = System.Windows.DataObject;
|
||||
using Key = System.Windows.Input.Key;
|
||||
using MouseButtons = System.Windows.Forms.MouseButtons;
|
||||
using NotifyIcon = System.Windows.Forms.NotifyIcon;
|
||||
using Screen = System.Windows.Forms.Screen;
|
||||
|
|
@ -165,6 +167,8 @@ namespace Flow.Launcher
|
|||
// Set the initial state of the QueryTextBoxCursorMovedToEnd property
|
||||
// Without this part, when shown for the first time, switching the context menu does not move the cursor to the end.
|
||||
_viewModel.QueryTextCursorMovedToEnd = false;
|
||||
|
||||
// View model property changed event
|
||||
_viewModel.PropertyChanged += (o, e) =>
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
|
|
@ -227,6 +231,7 @@ namespace Flow.Launcher
|
|||
}
|
||||
};
|
||||
|
||||
// Settings property changed event
|
||||
_settings.PropertyChanged += (o, e) =>
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
|
|
@ -1050,7 +1055,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Placeholder
|
||||
|
|
@ -1101,6 +1106,17 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Search Delay
|
||||
|
||||
private void QueryTextBox_TextChanged1(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
var textBox = (TextBox)sender;
|
||||
_viewModel.QueryText = textBox.Text;
|
||||
_viewModel.Query(_settings.SearchQueryResultsWithDelay);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
<UserControl x:Class="Flow.Launcher.Resources.Controls.InstalledPluginDisplay"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
xmlns:viewModel="clr-namespace:Flow.Launcher.ViewModel"
|
||||
xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance viewModel:PluginViewModel}"
|
||||
d:DesignHeight="300" d:DesignWidth="300">
|
||||
<UserControl
|
||||
x:Class="Flow.Launcher.Resources.Controls.InstalledPluginDisplay"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
xmlns:viewModel="clr-namespace:Flow.Launcher.ViewModel"
|
||||
d:DataContext="{d:DesignInstance viewModel:PluginViewModel}"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="300"
|
||||
mc:Ignorable="d">
|
||||
<Expander
|
||||
Padding="0"
|
||||
BorderThickness="0"
|
||||
|
|
@ -28,7 +30,7 @@
|
|||
Grid.Column="0"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Source="{Binding Image, IsAsync=True}" />
|
||||
Source="{Binding Image, Mode=OneWay, IsAsync=True}" />
|
||||
<StackPanel Grid.Column="1" Margin="16 0 14 0">
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource Color05B}"
|
||||
|
|
@ -37,12 +39,15 @@
|
|||
ToolTip="{Binding PluginPair.Metadata.Version}" />
|
||||
<TextBlock
|
||||
Margin="0 2 0 0"
|
||||
Foreground="{DynamicResource Color04B}"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource Color04B}"
|
||||
Text="{Binding PluginPair.Metadata.Description}"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0 0 8 0"
|
||||
VerticalAlignment="Center"
|
||||
|
|
@ -70,9 +75,7 @@
|
|||
<Setter Property="FontWeight" Value="DemiBold" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource Color05B}" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger
|
||||
Binding="{Binding ElementName=PriorityButton, UpdateSourceTrigger=PropertyChanged, Path=Content}"
|
||||
Value="0">
|
||||
<DataTrigger Binding="{Binding ElementName=PriorityButton, UpdateSourceTrigger=PropertyChanged, Path=Content}" Value="0">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Color08B}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
</DataTrigger>
|
||||
|
|
@ -95,10 +98,12 @@
|
|||
<StackPanel>
|
||||
<ContentControl Content="{Binding BottomPart1}" />
|
||||
|
||||
<ContentControl Content="{Binding BottomPart2}" />
|
||||
|
||||
<Border
|
||||
BorderThickness="0 1 0 0"
|
||||
Background="{DynamicResource Color00B}"
|
||||
BorderBrush="{DynamicResource Color03B}"
|
||||
Background="{DynamicResource Color00B}">
|
||||
BorderThickness="0 1 0 0">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Style.Triggers>
|
||||
|
|
@ -115,7 +120,7 @@
|
|||
Content="{Binding SettingControl}" />
|
||||
</Border>
|
||||
|
||||
<ContentControl Content="{Binding BottomPart2}" />
|
||||
<ContentControl Content="{Binding BottomPart3}" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<UserControl
|
||||
x:Class="Flow.Launcher.Resources.Controls.InstalledPluginSearchDelay"
|
||||
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:viewModel="clr-namespace:Flow.Launcher.ViewModel"
|
||||
d:DataContext="{d:DesignInstance viewModel:PluginViewModel}"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="300"
|
||||
mc:Ignorable="d">
|
||||
<Border
|
||||
Width="Auto"
|
||||
Height="Auto"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
BorderThickness="0 1 0 0"
|
||||
CornerRadius="0"
|
||||
Style="{DynamicResource SettingGroupBox}">
|
||||
<DockPanel Margin="{StaticResource SettingPanelMargin}">
|
||||
<TextBlock
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Style="{StaticResource Glyph}">
|
||||

|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="{StaticResource SettingPanelItemRightMargin}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Style="{DynamicResource SettingTitleLabel}"
|
||||
Text="{DynamicResource pluginSearchDelayTime}" />
|
||||
<!-- Here Margin="0 -4.5 0 -4.5" is to remove redundant top bottom margin from Margin="{StaticResource SettingPanelMargin}" -->
|
||||
<Button
|
||||
Width="100"
|
||||
Margin="0 -4.5 0 -4.5"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{Binding SetSearchDelayTimeCommand}"
|
||||
Content="{Binding SearchDelayTimeText}"
|
||||
Cursor="Hand"
|
||||
DockPanel.Dock="Right"
|
||||
FontWeight="Bold"
|
||||
ToolTip="{DynamicResource pluginSearchDelayTimeTooltip}" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</UserControl>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System.Windows.Controls;
|
||||
|
||||
namespace Flow.Launcher.Resources.Controls;
|
||||
|
||||
public partial class InstalledPluginSearchDelay : UserControl
|
||||
{
|
||||
public InstalledPluginSearchDelay()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
138
Flow.Launcher/SearchDelayTimeWindow.xaml
Normal file
138
Flow.Launcher/SearchDelayTimeWindow.xaml
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<Window
|
||||
x:Class="Flow.Launcher.SearchDelayTimeWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{DynamicResource searchDelayTimeTitle}"
|
||||
Width="450"
|
||||
Background="{DynamicResource PopuBGColor}"
|
||||
Foreground="{DynamicResource PopupTextColor}"
|
||||
Icon="Images\app.png"
|
||||
Loaded="SearchDelayTimeWindow_OnLoaded"
|
||||
ResizeMode="NoResize"
|
||||
SizeToContent="Height"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||
</WindowChrome.WindowChrome>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="80" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<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 12 26 0">
|
||||
<StackPanel Grid.Row="0" Margin="0 0 0 12">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0 0 0 0"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource searchDelayTimeTitle}"
|
||||
TextAlignment="Left" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
x:Name="tbSearchDelayTimeTips"
|
||||
FontSize="14"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0 18 0 0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource currentSearchDelayTime}" />
|
||||
<TextBlock
|
||||
x:Name="tbOldSearchDelayTime"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="14 10 10 10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource Color05B}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0 0 0 10" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource newSearchDelayTime}" />
|
||||
<ComboBox
|
||||
x:Name="cbDelay"
|
||||
MaxWidth="200"
|
||||
Margin="10 10 15 10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
DisplayMemberPath="Display"
|
||||
SelectedValuePath="Value" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<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="10 0 5 0"
|
||||
Click="BtnCancel_OnClick"
|
||||
Content="{DynamicResource cancel}" />
|
||||
<Button
|
||||
x:Name="btnDone"
|
||||
Width="145"
|
||||
Height="30"
|
||||
Margin="5 0 10 0"
|
||||
Click="btnDone_OnClick"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
57
Flow.Launcher/SearchDelayTimeWindow.xaml.cs
Normal file
57
Flow.Launcher/SearchDelayTimeWindow.xaml.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using System.Linq;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.SettingPages.ViewModels;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using static Flow.Launcher.SettingPages.ViewModels.SettingsPaneGeneralViewModel;
|
||||
|
||||
namespace Flow.Launcher;
|
||||
|
||||
public partial class SearchDelayTimeWindow : Window
|
||||
{
|
||||
private readonly PluginViewModel _pluginViewModel;
|
||||
|
||||
public SearchDelayTimeWindow(PluginViewModel pluginViewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
_pluginViewModel = pluginViewModel;
|
||||
}
|
||||
|
||||
private void SearchDelayTimeWindow_OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
tbSearchDelayTimeTips.Text = string.Format(App.API.GetTranslation("searchDelayTime_tips"),
|
||||
App.API.GetTranslation("default"));
|
||||
tbOldSearchDelayTime.Text = _pluginViewModel.SearchDelayTimeText;
|
||||
var searchDelayTimes = DropdownDataGeneric<SearchDelayTime>.GetValues<SearchDelayTimeData>("SearchDelayTime");
|
||||
SearchDelayTimeData selected = null;
|
||||
// Because default value is SearchDelayTime.VeryShort, we need to get selected value before adding default value
|
||||
if (_pluginViewModel.PluginSearchDelayTime != null)
|
||||
{
|
||||
selected = searchDelayTimes.FirstOrDefault(x => x.Value == _pluginViewModel.PluginSearchDelayTime);
|
||||
}
|
||||
// Add default value to the beginning of the list
|
||||
// When _pluginViewModel.PluginSearchDelayTime equals null, we will select this
|
||||
searchDelayTimes.Insert(0, new SearchDelayTimeData { Display = App.API.GetTranslation("default"), LocalizationKey = "default" });
|
||||
selected ??= searchDelayTimes.FirstOrDefault();
|
||||
cbDelay.ItemsSource = searchDelayTimes;
|
||||
cbDelay.SelectedItem = selected;
|
||||
cbDelay.Focus();
|
||||
}
|
||||
|
||||
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnDone_OnClick(object sender, RoutedEventArgs _)
|
||||
{
|
||||
// Update search delay time
|
||||
var selected = cbDelay.SelectedItem as SearchDelayTimeData;
|
||||
SearchDelayTime? changedValue = selected?.LocalizationKey != "default" ? selected.Value : null;
|
||||
_pluginViewModel.PluginSearchDelayTime = changedValue;
|
||||
|
||||
// Update search delay time text and close window
|
||||
_pluginViewModel.OnSearchDelayTimeChanged();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.SettingPages.ViewModels;
|
||||
|
|
@ -9,7 +8,7 @@ public class DropdownDataGeneric<TValue> : BaseModel where TValue : Enum
|
|||
{
|
||||
public string Display { get; set; }
|
||||
public TValue Value { get; private init; }
|
||||
private string LocalizationKey { get; init; }
|
||||
public string LocalizationKey { get; set; }
|
||||
|
||||
public static List<TR> GetValues<TR>(string keyPrefix) where TR : DropdownDataGeneric<TValue>, new()
|
||||
{
|
||||
|
|
@ -19,7 +18,7 @@ public class DropdownDataGeneric<TValue> : BaseModel where TValue : Enum
|
|||
foreach (var value in enumValues)
|
||||
{
|
||||
var key = keyPrefix + value;
|
||||
var display = InternationalizationManager.Instance.GetTranslation(key);
|
||||
var display = App.API.GetTranslation(key);
|
||||
data.Add(new TR { Display = display, Value = value, LocalizationKey = key });
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +29,7 @@ public class DropdownDataGeneric<TValue> : BaseModel where TValue : Enum
|
|||
{
|
||||
foreach (var item in options)
|
||||
{
|
||||
item.Display = InternationalizationManager.Instance.GetTranslation(item.LocalizationKey);
|
||||
item.Display = App.API.GetTranslation(item.LocalizationKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Core;
|
||||
|
|
@ -30,6 +31,7 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
public class SearchWindowAlignData : DropdownDataGeneric<SearchWindowAligns> { }
|
||||
public class SearchPrecisionData : DropdownDataGeneric<SearchPrecisionScore> { }
|
||||
public class LastQueryModeData : DropdownDataGeneric<LastQueryMode> { }
|
||||
public class SearchDelayTimeData : DropdownDataGeneric<SearchDelayTime> { }
|
||||
|
||||
public bool StartFlowLauncherOnSystemStartup
|
||||
{
|
||||
|
|
@ -142,12 +144,33 @@ public partial class SettingsPaneGeneralViewModel : BaseModel
|
|||
public List<LastQueryModeData> LastQueryModes { get; } =
|
||||
DropdownDataGeneric<LastQueryMode>.GetValues<LastQueryModeData>("LastQuery");
|
||||
|
||||
public List<SearchDelayTimeData> SearchDelayTimes { get; } =
|
||||
DropdownDataGeneric<SearchDelayTime>.GetValues<SearchDelayTimeData>("SearchDelayTime");
|
||||
|
||||
public SearchDelayTimeData SearchDelayTime
|
||||
{
|
||||
get => SearchDelayTimes.FirstOrDefault(x => x.Value == Settings.SearchDelayTime) ??
|
||||
SearchDelayTimes.FirstOrDefault(x => x.Value == Plugin.SearchDelayTime.Normal) ??
|
||||
SearchDelayTimes.FirstOrDefault();
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
if (Settings.SearchDelayTime != value.Value)
|
||||
{
|
||||
Settings.SearchDelayTime = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEnumDropdownLocalizations()
|
||||
{
|
||||
DropdownDataGeneric<SearchWindowScreens>.UpdateLabels(SearchWindowScreens);
|
||||
DropdownDataGeneric<SearchWindowAligns>.UpdateLabels(SearchWindowAligns);
|
||||
DropdownDataGeneric<SearchPrecisionScore>.UpdateLabels(SearchPrecisionScores);
|
||||
DropdownDataGeneric<LastQueryMode>.UpdateLabels(LastQueryModes);
|
||||
DropdownDataGeneric<SearchDelayTime>.UpdateLabels(SearchDelayTimes);
|
||||
}
|
||||
|
||||
public string Language
|
||||
|
|
|
|||
|
|
@ -30,8 +30,9 @@ public class SettingsPanePluginsViewModel : BaseModel
|
|||
.Select(plugin => new PluginViewModel
|
||||
{
|
||||
PluginPair = plugin,
|
||||
PluginSettingsObject = _settings.PluginSettings.Plugins[plugin.Metadata.ID]
|
||||
PluginSettingsObject = _settings.PluginSettings.GetPluginSettings(plugin.Metadata.ID)
|
||||
})
|
||||
.Where(plugin => plugin.PluginSettingsObject != null)
|
||||
.ToList();
|
||||
|
||||
public List<PluginViewModel> FilteredPluginViewModels => PluginViewModels
|
||||
|
|
|
|||
|
|
@ -172,6 +172,30 @@
|
|||
OnContent="{DynamicResource enable}" />
|
||||
</cc:Card>
|
||||
|
||||
<cc:CardGroup Margin="0 14 0 0">
|
||||
<cc:Card
|
||||
Title="{DynamicResource searchDelay}"
|
||||
Icon=""
|
||||
Sub="{DynamicResource searchDelayToolTip}">
|
||||
<ui:ToggleSwitch
|
||||
IsOn="{Binding Settings.SearchQueryResultsWithDelay}"
|
||||
OffContent="{DynamicResource disable}"
|
||||
OnContent="{DynamicResource enable}" />
|
||||
</cc:Card>
|
||||
|
||||
<cc:Card
|
||||
Title="{DynamicResource searchDelayTime}"
|
||||
Icon=""
|
||||
Sub="{DynamicResource searchDelayTimeToolTip}">
|
||||
<ComboBox
|
||||
MaxWidth="200"
|
||||
DisplayMemberPath="Display"
|
||||
ItemsSource="{Binding SearchDelayTimes}"
|
||||
SelectedItem="{Binding SearchDelayTime}"
|
||||
SelectedValuePath="Value" />
|
||||
</cc:Card>
|
||||
</cc:CardGroup>
|
||||
|
||||
<cc:CardGroup Margin="0 14 0 0">
|
||||
<cc:Card Title="{DynamicResource querySearchPrecision}" Sub="{DynamicResource querySearchPrecisionToolTip}">
|
||||
<ComboBox
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ namespace Flow.Launcher.ViewModel
|
|||
};
|
||||
_selectedResults = Results;
|
||||
|
||||
Results.PropertyChanged += (_, args) =>
|
||||
Results.PropertyChanged += (o, args) =>
|
||||
{
|
||||
switch (args.PropertyName)
|
||||
{
|
||||
|
|
@ -171,7 +171,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
};
|
||||
|
||||
History.PropertyChanged += (_, args) =>
|
||||
History.PropertyChanged += (o, args) =>
|
||||
{
|
||||
switch (args.PropertyName)
|
||||
{
|
||||
|
|
@ -298,14 +298,16 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (QueryResultsSelected())
|
||||
{
|
||||
_ = QueryResultsAsync(isReQuery: true);
|
||||
// When we are re-querying, we should not delay the query
|
||||
_ = QueryResultsAsync(false, isReQuery: true);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReQuery(bool reselect)
|
||||
{
|
||||
BackToQueryResults();
|
||||
_ = QueryResultsAsync(isReQuery: true, reSelect: reselect);
|
||||
// When we are re-querying, we should not delay the query
|
||||
_ = QueryResultsAsync(false, isReQuery: true, reSelect: reselect);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
|
@ -313,7 +315,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (_history.Items.Count > 0)
|
||||
{
|
||||
ChangeQueryText(_history.Items[^lastHistoryIndex].Query.ToString());
|
||||
ChangeQueryText(_history.Items[^lastHistoryIndex].Query);
|
||||
if (lastHistoryIndex < _history.Items.Count)
|
||||
{
|
||||
lastHistoryIndex++;
|
||||
|
|
@ -326,7 +328,7 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
if (_history.Items.Count > 0)
|
||||
{
|
||||
ChangeQueryText(_history.Items[^lastHistoryIndex].Query.ToString());
|
||||
ChangeQueryText(_history.Items[^lastHistoryIndex].Query);
|
||||
if (lastHistoryIndex > 1)
|
||||
{
|
||||
lastHistoryIndex--;
|
||||
|
|
@ -577,7 +579,6 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
_queryText = value;
|
||||
OnPropertyChanged();
|
||||
Query();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -646,19 +647,21 @@ namespace Flow.Launcher.ViewModel
|
|||
return;
|
||||
}
|
||||
|
||||
BackToQueryResults();
|
||||
|
||||
if (QueryText != queryText)
|
||||
{
|
||||
// re-query is done in QueryText's setter method
|
||||
// Change query text first
|
||||
QueryText = queryText;
|
||||
// When we are changing query from codes, we should not delay the query
|
||||
await QueryAsync(false, isReQuery: false);
|
||||
|
||||
// set to false so the subsequent set true triggers
|
||||
// PropertyChanged and MoveQueryTextToEnd is called
|
||||
QueryTextCursorMovedToEnd = false;
|
||||
}
|
||||
else if (isReQuery)
|
||||
{
|
||||
await QueryAsync(isReQuery: true);
|
||||
// When we are re-querying, we should not delay the query
|
||||
await QueryAsync(false, isReQuery: true);
|
||||
}
|
||||
|
||||
QueryTextCursorMovedToEnd = true;
|
||||
|
|
@ -727,14 +730,10 @@ namespace Flow.Launcher.ViewModel
|
|||
// setter won't be called when property value is not changed.
|
||||
// so we need manually call Query()
|
||||
// http://stackoverflow.com/posts/25895769/revisions
|
||||
if (string.IsNullOrEmpty(QueryText))
|
||||
{
|
||||
Query();
|
||||
}
|
||||
else
|
||||
{
|
||||
QueryText = string.Empty;
|
||||
}
|
||||
QueryText = string.Empty;
|
||||
// When we are changing query because selected results are changed to history or context menu,
|
||||
// we should not delay the query
|
||||
Query(false);
|
||||
|
||||
if (HistorySelected())
|
||||
{
|
||||
|
|
@ -754,7 +753,7 @@ namespace Flow.Launcher.ViewModel
|
|||
public Visibility ProgressBarVisibility { get; set; }
|
||||
public Visibility MainWindowVisibility { get; set; }
|
||||
|
||||
// This is to be used for determining the visibility status of the mainwindow instead of MainWindowVisibility
|
||||
// This is to be used for determining the visibility status of the main window instead of MainWindowVisibility
|
||||
// because it is more accurate and reliable representation than using Visibility as a condition check
|
||||
public bool MainWindowVisibilityStatus { get; set; } = true;
|
||||
|
||||
|
|
@ -1041,16 +1040,16 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
#region Query
|
||||
|
||||
private void Query(bool isReQuery = false)
|
||||
public void Query(bool searchDelay, bool isReQuery = false)
|
||||
{
|
||||
_ = QueryAsync(isReQuery);
|
||||
_ = QueryAsync(searchDelay, isReQuery);
|
||||
}
|
||||
|
||||
private async Task QueryAsync(bool isReQuery = false)
|
||||
private async Task QueryAsync(bool searchDelay, bool isReQuery = false)
|
||||
{
|
||||
if (QueryResultsSelected())
|
||||
{
|
||||
await QueryResultsAsync(isReQuery);
|
||||
await QueryResultsAsync(searchDelay, isReQuery);
|
||||
}
|
||||
else if (ContextMenuSelected())
|
||||
{
|
||||
|
|
@ -1072,9 +1071,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
if (selected != null) // SelectedItem returns null if selection is empty.
|
||||
{
|
||||
List<Result> results;
|
||||
|
||||
results = PluginManager.GetContextMenusForPlugin(selected);
|
||||
var results = PluginManager.GetContextMenusForPlugin(selected);
|
||||
results.Add(ContextMenuTopMost(selected));
|
||||
results.Add(ContextMenuPluginInfo(selected.PluginID));
|
||||
|
||||
|
|
@ -1151,13 +1148,18 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
private async Task QueryResultsAsync(bool isReQuery = false, bool reSelect = true)
|
||||
private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, bool reSelect = true)
|
||||
{
|
||||
// TODO: Remove debug codes.
|
||||
System.Diagnostics.Debug.WriteLine("!!!QueryResults");
|
||||
|
||||
_updateSource?.Cancel();
|
||||
|
||||
var query = ConstructQuery(QueryText, Settings.CustomShortcuts, Settings.BuiltinShortcuts);
|
||||
|
||||
if (query == null) // shortcut expanded
|
||||
var plugins = PluginManager.ValidPluginsForQuery(query);
|
||||
|
||||
if (query == null || plugins.Count == 0) // shortcut expanded
|
||||
{
|
||||
Results.Clear();
|
||||
Results.Visibility = Visibility.Collapsed;
|
||||
|
|
@ -1166,6 +1168,18 @@ namespace Flow.Launcher.ViewModel
|
|||
SearchIconVisibility = Visibility.Visible;
|
||||
return;
|
||||
}
|
||||
else if (plugins.Count == 1)
|
||||
{
|
||||
PluginIconPath = plugins.Single().Metadata.IcoPath;
|
||||
PluginIconSource = await ImageLoader.LoadAsync(PluginIconPath);
|
||||
SearchIconVisibility = Visibility.Hidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginIconPath = null;
|
||||
PluginIconSource = null;
|
||||
SearchIconVisibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
_updateSource?.Dispose();
|
||||
|
||||
|
|
@ -1190,21 +1204,6 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
_lastQuery = query;
|
||||
|
||||
var plugins = PluginManager.ValidPluginsForQuery(query);
|
||||
|
||||
if (plugins.Count == 1)
|
||||
{
|
||||
PluginIconPath = plugins.Single().Metadata.IcoPath;
|
||||
PluginIconSource = await ImageLoader.LoadAsync(PluginIconPath);
|
||||
SearchIconVisibility = Visibility.Hidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginIconPath = null;
|
||||
PluginIconSource = null;
|
||||
SearchIconVisibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign)
|
||||
{
|
||||
// Wait 45 millisecond for query change in global query
|
||||
|
|
@ -1215,22 +1214,36 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
|
||||
_ = Task.Delay(200, _updateSource.Token).ContinueWith(_ =>
|
||||
{
|
||||
// start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
|
||||
if (!_updateSource.Token.IsCancellationRequested && _isQueryRunning)
|
||||
{
|
||||
ProgressBarVisibility = Visibility.Visible;
|
||||
}
|
||||
}, _updateSource.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
|
||||
// start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
|
||||
if (!_updateSource.Token.IsCancellationRequested && _isQueryRunning)
|
||||
{
|
||||
ProgressBarVisibility = Visibility.Visible;
|
||||
}
|
||||
},
|
||||
_updateSource.Token,
|
||||
TaskContinuationOptions.NotOnCanceled,
|
||||
TaskScheduler.Default);
|
||||
|
||||
// plugins is ICollection, meaning LINQ will get the Count and preallocate Array
|
||||
// plugins are ICollection, meaning LINQ will get the Count and preallocate Array
|
||||
|
||||
var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch
|
||||
{
|
||||
false => QueryTaskAsync(plugin, reSelect),
|
||||
false => QueryTaskAsync(plugin, _updateSource.Token),
|
||||
true => Task.CompletedTask
|
||||
}).ToArray();
|
||||
|
||||
// TODO: Remove debug codes.
|
||||
System.Diagnostics.Debug.Write($"!!!Querying {query.RawQuery}: search delay {searchDelay}");
|
||||
foreach (var plugin in plugins)
|
||||
{
|
||||
if (!plugin.Metadata.Disabled)
|
||||
{
|
||||
System.Diagnostics.Debug.Write($"{plugin.Metadata.Name}, ");
|
||||
}
|
||||
}
|
||||
System.Diagnostics.Debug.Write("\n");
|
||||
|
||||
try
|
||||
{
|
||||
// Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
|
||||
|
|
@ -1254,16 +1267,43 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
|
||||
// Local function
|
||||
async Task QueryTaskAsync(PluginPair plugin, bool reSelect = true)
|
||||
async Task QueryTaskAsync(PluginPair plugin, CancellationToken token)
|
||||
{
|
||||
if (searchDelay)
|
||||
{
|
||||
var searchDelayTime = (plugin.Metadata.SearchDelayTime ?? Settings.SearchDelayTime) switch
|
||||
{
|
||||
SearchDelayTime.VeryLong => 250,
|
||||
SearchDelayTime.Long => 200,
|
||||
SearchDelayTime.Normal => 150,
|
||||
SearchDelayTime.Short => 100,
|
||||
SearchDelayTime.VeryShort => 50,
|
||||
_ => 150
|
||||
};
|
||||
|
||||
// TODO: Remove debug codes.
|
||||
System.Diagnostics.Debug.WriteLine($"!!!{plugin.Metadata.Name} Waiting {searchDelayTime} ms");
|
||||
|
||||
await Task.Delay(searchDelayTime, token);
|
||||
|
||||
// TODO: Remove debug codes.
|
||||
System.Diagnostics.Debug.WriteLine($"!!!{plugin.Metadata.Name} Waited {searchDelayTime} ms");
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
}
|
||||
|
||||
// Since it is wrapped within a ThreadPool Thread, the synchronous context is null
|
||||
// Task.Yield will force it to run in ThreadPool
|
||||
await Task.Yield();
|
||||
|
||||
// TODO: Remove debug codes.
|
||||
System.Diagnostics.Debug.WriteLine($"!!!{query.RawQuery} Querying {plugin.Metadata.Name}");
|
||||
IReadOnlyList<Result> results =
|
||||
await PluginManager.QueryForPluginAsync(plugin, query, _updateSource.Token);
|
||||
await PluginManager.QueryForPluginAsync(plugin, query, token);
|
||||
|
||||
_updateSource.Token.ThrowIfCancellationRequested();
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
IReadOnlyList<Result> resultsCopy;
|
||||
if (results == null)
|
||||
|
|
@ -1273,11 +1313,11 @@ namespace Flow.Launcher.ViewModel
|
|||
else
|
||||
{
|
||||
// make a copy of results to avoid possible issue that FL changes some properties of the records, like score, etc.
|
||||
resultsCopy = DeepCloneResults(results);
|
||||
resultsCopy = DeepCloneResults(results, token);
|
||||
}
|
||||
|
||||
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, plugin.Metadata, query,
|
||||
_updateSource.Token, reSelect)))
|
||||
token, reSelect)))
|
||||
{
|
||||
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
|
||||
}
|
||||
|
|
@ -1591,7 +1631,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// To avoid deadlock, this method should not be called from main thread
|
||||
/// </summary>
|
||||
public void UpdateResultView(ICollection<ResultsForUpdate> resultsForUpdates)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Infrastructure.Image;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Resources.Controls;
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
|
|
@ -28,7 +28,7 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
private string PluginManagerActionKeyword
|
||||
private static string PluginManagerActionKeyword
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -43,9 +43,10 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
private async void LoadIconAsync()
|
||||
private async Task LoadIconAsync()
|
||||
{
|
||||
Image = await ImageLoader.LoadAsync(PluginPair.Metadata.IcoPath);
|
||||
OnPropertyChanged(nameof(Image));
|
||||
}
|
||||
|
||||
public ImageSource Image
|
||||
|
|
@ -53,12 +54,13 @@ namespace Flow.Launcher.ViewModel
|
|||
get
|
||||
{
|
||||
if (_image == ImageLoader.MissingImage)
|
||||
LoadIconAsync();
|
||||
_ = LoadIconAsync();
|
||||
|
||||
return _image;
|
||||
}
|
||||
set => _image = value;
|
||||
}
|
||||
|
||||
public bool PluginState
|
||||
{
|
||||
get => !PluginPair.Metadata.Disabled;
|
||||
|
|
@ -68,6 +70,7 @@ namespace Flow.Launcher.ViewModel
|
|||
PluginSettingsObject.Disabled = !value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => _isExpanded;
|
||||
|
|
@ -80,6 +83,16 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
public SearchDelayTime? PluginSearchDelayTime
|
||||
{
|
||||
get => PluginPair.Metadata.SearchDelayTime;
|
||||
set
|
||||
{
|
||||
PluginPair.Metadata.SearchDelayTime = value;
|
||||
PluginSettingsObject.SearchDelayTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Control _settingControl;
|
||||
private bool _isExpanded;
|
||||
|
||||
|
|
@ -87,9 +100,13 @@ namespace Flow.Launcher.ViewModel
|
|||
public Control BottomPart1 => IsExpanded ? _bottomPart1 ??= new InstalledPluginDisplayKeyword() : null;
|
||||
|
||||
private Control _bottomPart2;
|
||||
public Control BottomPart2 => IsExpanded ? _bottomPart2 ??= new InstalledPluginDisplayBottomData() : null;
|
||||
public Control BottomPart2 => IsExpanded ? _bottomPart2 ??= new InstalledPluginSearchDelay() : null;
|
||||
|
||||
public bool HasSettingControl => PluginPair.Plugin is ISettingProvider && (PluginPair.Plugin is not JsonRPCPluginBase jsonRPCPluginBase || jsonRPCPluginBase.NeedCreateSettingPanel());
|
||||
private Control _bottomPart3;
|
||||
public Control BottomPart3 => IsExpanded ? _bottomPart3 ??= new InstalledPluginDisplayBottomData() : null;
|
||||
|
||||
public bool HasSettingControl => PluginPair.Plugin is ISettingProvider &&
|
||||
(PluginPair.Plugin is not JsonRPCPluginBase jsonRPCPluginBase || jsonRPCPluginBase.NeedCreateSettingPanel());
|
||||
public Control SettingControl
|
||||
=> IsExpanded
|
||||
? _settingControl
|
||||
|
|
@ -99,20 +116,33 @@ namespace Flow.Launcher.ViewModel
|
|||
: null;
|
||||
private ImageSource _image = ImageLoader.MissingImage;
|
||||
|
||||
public Visibility ActionKeywordsVisibility => PluginPair.Metadata.HideActionKeywordPanel ? Visibility.Collapsed : Visibility.Visible;
|
||||
public string InitilizaTime => PluginPair.Metadata.InitTime + "ms";
|
||||
public Visibility ActionKeywordsVisibility => PluginPair.Metadata.HideActionKeywordPanel ?
|
||||
Visibility.Collapsed : Visibility.Visible;
|
||||
public string InitializeTime => PluginPair.Metadata.InitTime + "ms";
|
||||
public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms";
|
||||
public string Version => InternationalizationManager.Instance.GetTranslation("plugin_query_version") + " " + PluginPair.Metadata.Version;
|
||||
public string InitAndQueryTime => InternationalizationManager.Instance.GetTranslation("plugin_init_time") + " " + PluginPair.Metadata.InitTime + "ms, " + InternationalizationManager.Instance.GetTranslation("plugin_query_time") + " " + PluginPair.Metadata.AvgQueryTime + "ms";
|
||||
public string Version => App.API.GetTranslation("plugin_query_version") + " " + PluginPair.Metadata.Version;
|
||||
public string InitAndQueryTime =>
|
||||
App.API.GetTranslation("plugin_init_time") + " " +
|
||||
PluginPair.Metadata.InitTime + "ms, " +
|
||||
App.API.GetTranslation("plugin_query_time") + " " +
|
||||
PluginPair.Metadata.AvgQueryTime + "ms";
|
||||
public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords);
|
||||
public int Priority => PluginPair.Metadata.Priority;
|
||||
public Infrastructure.UserSettings.Plugin PluginSettingsObject { get; set; }
|
||||
public string SearchDelayTimeText => PluginPair.Metadata.SearchDelayTime == null ?
|
||||
App.API.GetTranslation("default") :
|
||||
App.API.GetTranslation($"SearchDelayTime{PluginPair.Metadata.SearchDelayTime}");
|
||||
public Infrastructure.UserSettings.Plugin PluginSettingsObject{ get; init; }
|
||||
|
||||
public void OnActionKeywordsChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(ActionKeywordsText));
|
||||
}
|
||||
|
||||
public void OnSearchDelayTimeChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(SearchDelayTimeText));
|
||||
}
|
||||
|
||||
public void ChangePriority(int newPriority)
|
||||
{
|
||||
PluginPair.Metadata.Priority = newPriority;
|
||||
|
|
@ -123,7 +153,7 @@ namespace Flow.Launcher.ViewModel
|
|||
[RelayCommand]
|
||||
private void EditPluginPriority()
|
||||
{
|
||||
PriorityChangeWindow priorityChangeWindow = new PriorityChangeWindow(PluginPair. Metadata.ID, this);
|
||||
var priorityChangeWindow = new PriorityChangeWindow(PluginPair. Metadata.ID, this);
|
||||
priorityChangeWindow.ShowDialog();
|
||||
}
|
||||
|
||||
|
|
@ -151,8 +181,15 @@ namespace Flow.Launcher.ViewModel
|
|||
[RelayCommand]
|
||||
private void SetActionKeywords()
|
||||
{
|
||||
ActionKeywords changeKeywordsWindow = new ActionKeywords(this);
|
||||
var changeKeywordsWindow = new ActionKeywords(this);
|
||||
changeKeywordsWindow.ShowDialog();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SetSearchDelayTime()
|
||||
{
|
||||
var searchDelayTimeWindow = new SearchDelayTimeWindow(this);
|
||||
searchDelayTimeWindow.ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,5 +31,6 @@
|
|||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll",
|
||||
"IcoPath": "Images\\web_search.png"
|
||||
"IcoPath": "Images\\web_search.png",
|
||||
"SearchDelayTime": "VeryLong"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue