mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'FixMemoryLeak' of https://github.com/onesounds/Flow.Launcher into FixMemoryLeak
This commit is contained in:
commit
03c9a1aab4
23 changed files with 777 additions and 56 deletions
|
|
@ -1,4 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NLog;
|
||||
|
|
|
|||
|
|
@ -202,7 +202,11 @@ namespace Flow.Launcher.Infrastructure
|
|||
if (allQuerySubstringsMatched)
|
||||
{
|
||||
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1,
|
||||
|
||||
// firstMatchIndex - nearestSpaceIndex - 1 is to set the firstIndex as the index of the first matched char
|
||||
// preceded by a space e.g. 'world' matching 'hello world' firstIndex would be 0 not 6
|
||||
// giving more weight than 'we or donald' by allowing the distance calculation to treat the starting position at after the space.
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, spaceIndices,
|
||||
lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
|
||||
|
||||
var resultList = indexList.Select(x => translationMapping?.MapToOriginalIndex(x) ?? x).Distinct().ToList();
|
||||
|
|
@ -296,7 +300,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
return currentQuerySubstringIndex >= querySubstringsLength;
|
||||
}
|
||||
|
||||
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen,
|
||||
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, List<int> spaceIndices, int matchLen,
|
||||
bool allSubstringsContainedInCompareString)
|
||||
{
|
||||
// A match found near the beginning of a string is scored more than a match found near the end
|
||||
|
|
@ -304,6 +308,14 @@ namespace Flow.Launcher.Infrastructure
|
|||
// while the score is lower if they are more spread out
|
||||
var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1));
|
||||
|
||||
// Give more weight to a match that is closer to the start of the string.
|
||||
// if the first matched char is immediately before space and all strings are contained in the compare string e.g. 'world' matching 'hello world'
|
||||
// and 'world hello', because both have 'world' immediately preceded by space, their firstIndex will be 0 when distance is calculated,
|
||||
// to prevent them scoring the same, we adjust the score by deducting the number of spaces it has from the start of the string, so 'world hello'
|
||||
// will score slightly higher than 'hello world' because 'hello world' has one additional space.
|
||||
if (firstIndex == 0 && allSubstringsContainedInCompareString)
|
||||
score -= spaceIndices.Count;
|
||||
|
||||
// A match with less characters assigning more weights
|
||||
if (stringToCompare.Length - query.Length < 5)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
{
|
||||
public abstract class ShortcutBaseModel
|
||||
{
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Func<string> Expand { get; set; } = () => { return ""; };
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ShortcutBaseModel other &&
|
||||
Key == other.Key;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Key.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomShortcutModel : ShortcutBaseModel
|
||||
{
|
||||
public string Value { get; set; }
|
||||
|
||||
[JsonConstructorAttribute]
|
||||
public CustomShortcutModel(string key, string value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
Expand = () => { return Value; };
|
||||
}
|
||||
|
||||
public void Deconstruct(out string key, out string value)
|
||||
{
|
||||
key = Key;
|
||||
value = Value;
|
||||
}
|
||||
|
||||
public static implicit operator (string Key, string Value)(CustomShortcutModel shortcut)
|
||||
{
|
||||
return (shortcut.Key, shortcut.Value);
|
||||
}
|
||||
|
||||
public static implicit operator CustomShortcutModel((string Key, string Value) shortcut)
|
||||
{
|
||||
return new CustomShortcutModel(shortcut.Key, shortcut.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public class BuiltinShortcutModel : ShortcutBaseModel
|
||||
{
|
||||
public string Description { get; set; }
|
||||
|
||||
public BuiltinShortcutModel(string key, string description, Func<string> expand)
|
||||
{
|
||||
Key = key;
|
||||
Description = description;
|
||||
Expand = expand ?? (() => { return ""; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using Flow.Launcher;
|
||||
|
|
@ -129,8 +130,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
PrivateArg = "-private",
|
||||
EnablePrivate = false,
|
||||
Editable = false
|
||||
}
|
||||
,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = "MS Edge",
|
||||
|
|
@ -186,6 +186,13 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public ObservableCollection<CustomPluginHotkey> CustomPluginHotkeys { get; set; } = new ObservableCollection<CustomPluginHotkey>();
|
||||
|
||||
public ObservableCollection<CustomShortcutModel> CustomShortcuts { get; set; } = new ObservableCollection<CustomShortcutModel>();
|
||||
|
||||
[JsonIgnore]
|
||||
public ObservableCollection<BuiltinShortcutModel> BuiltinShortcuts { get; set; } = new ObservableCollection<BuiltinShortcutModel>() {
|
||||
new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText)
|
||||
};
|
||||
|
||||
public bool DontPromptUpdateMsg { get; set; }
|
||||
public bool EnableUpdateLog { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,11 @@ namespace Flow.Launcher.Plugin
|
|||
/// user's clipboard when Ctrl + C is pressed on a result. If the text is a file/directory path
|
||||
/// flow will copy the actual file/folder instead of just the path text.
|
||||
/// </summary>
|
||||
public string CopyText { get; set; } = string.Empty;
|
||||
public string CopyText
|
||||
{
|
||||
get => string.IsNullOrEmpty(_copyText) ? SubTitle : _copyText;
|
||||
set => _copyText = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This holds the text which can be provided by plugin to help Flow autocomplete text
|
||||
|
|
@ -81,6 +85,7 @@ namespace Flow.Launcher.Plugin
|
|||
/// Delegate to Get Image Source
|
||||
/// </summary>
|
||||
public IconDelegate Icon;
|
||||
private string _copyText = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)
|
||||
|
|
|
|||
|
|
@ -129,14 +129,20 @@ namespace Flow.Launcher.Test
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// These are standard match scenarios
|
||||
/// The intention of this test is provide a bench mark for how much the score has increased from a change.
|
||||
/// Usually the increase in scoring should not be drastic, increase of less than 10 is acceptable.
|
||||
/// </summary>
|
||||
[TestCase(Chrome, Chrome, 157)]
|
||||
[TestCase(Chrome, LastIsChrome, 147)]
|
||||
[TestCase(Chrome, LastIsChrome, 145)]
|
||||
[TestCase("chro", HelpCureHopeRaiseOnMindEntityChrome, 50)]
|
||||
[TestCase("chr", HelpCureHopeRaiseOnMindEntityChrome, 30)]
|
||||
[TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)]
|
||||
[TestCase(Chrome, CandyCrushSagaFromKing, 0)]
|
||||
[TestCase("sql", MicrosoftSqlServerManagementStudio, 110)]
|
||||
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)] //double spacing intended
|
||||
[TestCase("sql", MicrosoftSqlServerManagementStudio, 109)]
|
||||
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 120)] //double spacing intended
|
||||
public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring(
|
||||
string queryString, string compareString, int expectedScore)
|
||||
{
|
||||
|
|
@ -275,7 +281,40 @@ namespace Flow.Launcher.Test
|
|||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
|
||||
$"Should be greater than{Environment.NewLine}" +
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}");
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("red", "red colour", "metro red")]
|
||||
[TestCase("red", "this red colour", "this colour red")]
|
||||
[TestCase("red", "this red colour", "this colour is very red")]
|
||||
[TestCase("red", "this red colour", "this colour is surprisingly super awesome red and cool")]
|
||||
[TestCase("red", "this colour is surprisingly super red very and cool", "this colour is surprisingly super very red and cool")]
|
||||
public void WhenGivenTwoStrings_Scoring_ShouldGiveMoreWeightToTheStringCloserToIndexZero(
|
||||
string queryString, string compareString1, string compareString2)
|
||||
{
|
||||
// When
|
||||
var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular };
|
||||
|
||||
// Given
|
||||
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
|
||||
var compareString2Result = matcher.FuzzyMatch(queryString, compareString2);
|
||||
|
||||
Debug.WriteLine("");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}");
|
||||
Debug.WriteLine(
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}");
|
||||
Debug.WriteLine(
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
// Should
|
||||
Assert.True(compareString1Result.Score > compareString2Result.Score,
|
||||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
|
||||
$"Should be greater than{Environment.NewLine}" +
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")]
|
||||
|
|
|
|||
20
Flow.Launcher/Converters/TranslationConverter.cs
Normal file
20
Flow.Launcher/Converters/TranslationConverter.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
|
||||
namespace Flow.Launcher.Converters
|
||||
{
|
||||
public class TranlationConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var key = value.ToString();
|
||||
if (String.IsNullOrEmpty(key))
|
||||
return key;
|
||||
return InternationalizationManager.Instance.GetTranslation(key);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
|
||||
}
|
||||
}
|
||||
160
Flow.Launcher/CustomShortcutSetting.xaml
Normal file
160
Flow.Launcher/CustomShortcutSetting.xaml
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<Window
|
||||
x:Class="Flow.Launcher.CustomShortcutSetting"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
|
||||
Title="{DynamicResource customeQueryShortcutTitle}"
|
||||
Width="530"
|
||||
Background="{DynamicResource PopuBGColor}"
|
||||
Foreground="{DynamicResource PopupTextColor}"
|
||||
Icon="Images\app.png"
|
||||
ResizeMode="NoResize"
|
||||
SizeToContent="Height"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
DataContext="{Binding RelativeSource={RelativeSource Self}}">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||
</WindowChrome.WindowChrome>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="Escape" Command="Close" />
|
||||
</Window.InputBindings>
|
||||
<Window.CommandBindings>
|
||||
<CommandBinding Command="Close" Executed="cmdEsc_OnPress" />
|
||||
</Window.CommandBindings>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="80" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Click="BtnCancel_OnClick"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
Width="46"
|
||||
Height="32"
|
||||
Data="M 18,11 27,20 M 18,20 27,11"
|
||||
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
StrokeThickness="1">
|
||||
<Path.Style>
|
||||
<Style TargetType="Path">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="26,0,26,0">
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,12">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
FontFamily="Segoe UI"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource customQueryShortcut}"
|
||||
TextAlignment="Left" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
Text="{DynamicResource customeQueryShortcutTips}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,20,0,0" Orientation="Horizontal">
|
||||
<Grid Width="470">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource customShortcut}" />
|
||||
<TextBox
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="10"
|
||||
Text="{Binding Key}"
|
||||
/>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource customShortcutExpansion}" />
|
||||
|
||||
<DockPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
LastChildFill="True">
|
||||
<Button
|
||||
x:Name="btnTestShortcut"
|
||||
Margin="0,0,10,0"
|
||||
Padding="10,5,10,5"
|
||||
Click="BtnTestShortcut_OnClick"
|
||||
Content="{DynamicResource preview}"
|
||||
DockPanel.Dock="Right" />
|
||||
<TextBox
|
||||
x:Name="tbExpand"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding Value}"
|
||||
VerticalAlignment="Center" />
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="0,14,0,0"
|
||||
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnCancel"
|
||||
MinWidth="140"
|
||||
Margin="10,0,5,0"
|
||||
Click="BtnCancel_OnClick"
|
||||
Content="{DynamicResource cancel}" />
|
||||
<Button
|
||||
x:Name="btnAdd"
|
||||
MinWidth="140"
|
||||
Margin="5,0,10,0"
|
||||
Click="BtnAdd_OnClick"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
73
Flow.Launcher/CustomShortcutSetting.xaml.cs
Normal file
73
Flow.Launcher/CustomShortcutSetting.xaml.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class CustomShortcutSetting : Window
|
||||
{
|
||||
private SettingWindowViewModel viewModel;
|
||||
public string Key { get; set; } = String.Empty;
|
||||
public string Value { get; set; } = String.Empty;
|
||||
private string originalKey { get; init; } = null;
|
||||
private string originalValue { get; init; } = null;
|
||||
private bool update { get; init; } = false;
|
||||
|
||||
public CustomShortcutSetting(SettingWindowViewModel vm)
|
||||
{
|
||||
viewModel = vm;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public CustomShortcutSetting(string key, string value, SettingWindowViewModel vm)
|
||||
{
|
||||
viewModel = vm;
|
||||
Key = key;
|
||||
Value = value;
|
||||
originalKey = key;
|
||||
originalValue = value;
|
||||
update = true;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void BtnAdd_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (String.IsNullOrEmpty(Key) || String.IsNullOrEmpty(Value))
|
||||
{
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("emptyShortcut"));
|
||||
return;
|
||||
}
|
||||
// Check if key is modified or adding a new one
|
||||
if (((update && originalKey != Key) || !update)
|
||||
&& viewModel.ShortcutExists(Key))
|
||||
{
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("duplicateShortcut"));
|
||||
return;
|
||||
}
|
||||
DialogResult = !update || originalKey != Key || originalValue != Value;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.API.ChangeQuery(tbExpand.Text);
|
||||
Application.Current.MainWindow.Show();
|
||||
Application.Current.MainWindow.Opacity = 1;
|
||||
Application.Current.MainWindow.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -144,12 +144,18 @@
|
|||
<system:String x:Key="showOpenResultHotkey">Show Hotkey</system:String>
|
||||
<system:String x:Key="showOpenResultHotkeyToolTip">Show result selection hotkey with results.</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Custom Query Hotkey</system:String>
|
||||
<system:String x:Key="customQueryShortcut">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="customQuery">Query</system:String>
|
||||
<system:String x:Key="customShortcut">Shortcut</system:String>
|
||||
<system:String x:Key="customShortcutExpansion">Expanded</system:String>
|
||||
<system:String x:Key="builtinShortcutDescription">Description</system:String>
|
||||
<system:String x:Key="delete">Delete</system:String>
|
||||
<system:String x:Key="edit">Edit</system:String>
|
||||
<system:String x:Key="add">Add</system:String>
|
||||
<system:String x:Key="pleaseSelectAnItem">Please select an item</system:String>
|
||||
<system:String x:Key="deleteCustomHotkeyWarning">Are you sure you want to delete {0} plugin hotkey?</system:String>
|
||||
<system:String x:Key="deleteCustomShortcutWarning">Are you sure you want to delete shortcut: {0} with expansion {1}?</system:String>
|
||||
<system:String x:Key="shortcut_clipboard_description">Get text from clipboard.</system:String>
|
||||
<system:String x:Key="queryWindowShadowEffect">Query window shadow effect</system:String>
|
||||
<system:String x:Key="shadowEffectCPUUsage">Shadow effect has a substantial usage of GPU. Not recommended if your computer performance is limited.</system:String>
|
||||
<system:String x:Key="windowWidthSize">Window Width Size</system:String>
|
||||
|
|
@ -241,6 +247,12 @@
|
|||
<system:String x:Key="invalidPluginHotkey">Invalid plugin hotkey</system:String>
|
||||
<system:String x:Key="update">Update</system:String>
|
||||
|
||||
<!-- Custom Query Shortcut Dialog -->
|
||||
<system:String x:Key="customeQueryShortcutTitle">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="customeQueryShortcutTips">Enter a shortcut that automatically expands to the specified query.</system:String>
|
||||
<system:String x:Key="duplicateShortcut">Shortcut already exists, please enter a new Shortcut or edit the existing one.</system:String>
|
||||
<system:String x:Key="emptyShortcut">Shortcut and/or its expansion is empty.</system:String>
|
||||
|
||||
<!-- Hotkey Control -->
|
||||
<system:String x:Key="hotkeyUnavailable">Hotkey Unavailable</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@
|
|||
<ContentControl>
|
||||
<flowlauncher:ResultListBox x:Name="ResultListBox"
|
||||
DataContext="{Binding Results}"
|
||||
PreviewMouseDown="OnPreviewMouseButtonDown" />
|
||||
PreviewMouseLeftButtonUp="OnPreviewMouseButtonDown" />
|
||||
</ContentControl>
|
||||
</Border>
|
||||
<Border Style="{DynamicResource WindowRadius}">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
|
@ -20,6 +20,11 @@ using Flow.Launcher.Infrastructure;
|
|||
using System.Windows.Media;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System.Text;
|
||||
using DataObject = System.Windows.DataObject;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.IO;
|
||||
using System.Windows.Threading;
|
||||
using System.Windows.Data;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,12 +25,15 @@
|
|||
VirtualizingStackPanel.IsVirtualizing="True"
|
||||
VirtualizingStackPanel.VirtualizationMode="Standard"
|
||||
Visibility="{Binding Visbility}"
|
||||
mc:Ignorable="d">
|
||||
mc:Ignorable="d"
|
||||
PreviewMouseMove="ResultList_MouseMove"
|
||||
PreviewMouseLeftButtonDown="ResultList_PreviewMouseLeftButtonDown">
|
||||
<!-- IsSynchronizedWithCurrentItem: http://stackoverflow.com/a/7833798/2833083 -->
|
||||
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button HorizontalAlignment="Stretch">
|
||||
<Button
|
||||
HorizontalAlignment="Stretch">
|
||||
<Button.Template>
|
||||
<ControlTemplate>
|
||||
<ContentPresenter Content="{TemplateBinding Button.Content}" />
|
||||
|
|
@ -84,7 +87,7 @@
|
|||
x:Name="ImageIcon"
|
||||
Width="{Binding IconXY}"
|
||||
Height="{Binding IconXY}"
|
||||
Margin="0,0,0,0"
|
||||
Margin="0,0,0,0" IsHitTestVisible="False"
|
||||
HorizontalAlignment="Center"
|
||||
Source="{Binding Image, TargetNullValue={x:Null}}"
|
||||
Stretch="Uniform"
|
||||
|
|
@ -134,6 +137,7 @@
|
|||
x:Name="Title"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
IsHitTestVisible="False"
|
||||
Style="{DynamicResource ItemTitleStyle}"
|
||||
Text="{Binding Result.Title}"
|
||||
ToolTip="{Binding ShowTitleToolTip}">
|
||||
|
|
@ -147,6 +151,7 @@
|
|||
<TextBlock
|
||||
x:Name="SubTitle"
|
||||
Grid.Row="1"
|
||||
IsHitTestVisible="False"
|
||||
Style="{DynamicResource ItemSubTitleStyle}"
|
||||
Text="{Binding Result.SubTitle}"
|
||||
ToolTip="{Binding ShowSubTitleToolTip}" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
using System.Windows;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using Flow.Launcher.ViewModel;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
|
|
@ -24,7 +27,7 @@ namespace Flow.Launcher
|
|||
|
||||
private void OnMouseEnter(object sender, MouseEventArgs e)
|
||||
{
|
||||
lock(_lock)
|
||||
lock (_lock)
|
||||
{
|
||||
curItem = (ListBoxItem)sender;
|
||||
var p = e.GetPosition((IInputElement)sender);
|
||||
|
|
@ -34,7 +37,7 @@ namespace Flow.Launcher
|
|||
|
||||
private void OnMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
lock(_lock)
|
||||
lock (_lock)
|
||||
{
|
||||
var p = e.GetPosition((IInputElement)sender);
|
||||
if (_lastpos != p)
|
||||
|
|
@ -46,7 +49,7 @@ namespace Flow.Launcher
|
|||
|
||||
private void ListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
lock(_lock)
|
||||
lock (_lock)
|
||||
{
|
||||
if (curItem != null)
|
||||
{
|
||||
|
|
@ -54,5 +57,51 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Point start;
|
||||
private string path;
|
||||
private string query;
|
||||
|
||||
private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })
|
||||
return;
|
||||
|
||||
path = result.Result.CopyText;
|
||||
query = result.Result.OriginQuery.RawQuery;
|
||||
start = e.GetPosition(null);
|
||||
}
|
||||
|
||||
private void ResultList_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.LeftButton != MouseButtonState.Pressed)
|
||||
{
|
||||
start = default;
|
||||
path = string.Empty;
|
||||
query = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(path) && !Directory.Exists(path))
|
||||
return;
|
||||
|
||||
Point mousePosition = e.GetPosition(null);
|
||||
Vector diff = this.start - mousePosition;
|
||||
|
||||
if (Math.Abs(diff.X) < SystemParameters.MinimumHorizontalDragDistance
|
||||
|| Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance)
|
||||
return;
|
||||
|
||||
var data = new DataObject(DataFormats.FileDrop, new[]
|
||||
{
|
||||
path
|
||||
});
|
||||
DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy);
|
||||
|
||||
App.API.ChangeQuery(query, true);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
<converters:BorderClipConverter x:Key="BorderClipConverter" />
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
<converters:TextConverter x:Key="TextConverter" />
|
||||
<converters:TranlationConverter x:Key="TranlationConverter" />
|
||||
<CollectionViewSource x:Key="SortedFonts" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
|
||||
<CollectionViewSource.SortDescriptions>
|
||||
<scm:SortDescription PropertyName="Source" />
|
||||
|
|
@ -2257,9 +2258,17 @@
|
|||
<RowDefinition Height="43" />
|
||||
<RowDefinition Height="80" />
|
||||
<RowDefinition Height="146" />
|
||||
|
||||
<RowDefinition Height="50" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="50" />
|
||||
|
||||
<RowDefinition Height="50" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="50" />
|
||||
|
||||
<RowDefinition Height="50" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
|
|
@ -2364,6 +2373,7 @@
|
|||
Text="{DynamicResource customQueryHotkey}" />
|
||||
<ListView
|
||||
Grid.Row="4"
|
||||
MinHeight="160"
|
||||
Margin="0,0,0,0"
|
||||
Background="{DynamicResource Color02B}"
|
||||
BorderBrush="DarkGray"
|
||||
|
|
@ -2404,7 +2414,7 @@
|
|||
<Button
|
||||
MinWidth="100"
|
||||
Margin="10"
|
||||
Click="OnnEditCustomHotkeyClick"
|
||||
Click="OnEditCustomHotkeyClick"
|
||||
Content="{DynamicResource edit}" />
|
||||
<Button
|
||||
MinWidth="100"
|
||||
|
|
@ -2412,6 +2422,104 @@
|
|||
Click="OnAddCustomHotkeyClick"
|
||||
Content="{DynamicResource add}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Margin="0,0,12,2"
|
||||
Padding="0,12,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource Color05B}"
|
||||
Text="{DynamicResource customQueryShortcut}" />
|
||||
<ListView
|
||||
Grid.Row="7"
|
||||
Name="customShortcutView"
|
||||
MinHeight="160"
|
||||
Margin="0,6,0,0"
|
||||
Background="{DynamicResource Color02B}"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
ItemsSource="{Binding CustomShortcuts}"
|
||||
SelectedItem="{Binding SelectedCustomShortcut}"
|
||||
Style="{StaticResource {x:Static GridView.GridViewStyleKey}}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Width="180" Header="{DynamicResource customShortcut}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Key}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn Width="540" Header="{DynamicResource customShortcutExpansion}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Value}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
<StackPanel
|
||||
Grid.Row="8"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
MinWidth="100"
|
||||
Margin="10"
|
||||
Click="OnDeleteCustomShortCutClick"
|
||||
Content="{DynamicResource delete}" />
|
||||
<Button
|
||||
MinWidth="100"
|
||||
Margin="10"
|
||||
Click="OnEditCustomShortCutClick"
|
||||
Content="{DynamicResource edit}" />
|
||||
<Button
|
||||
MinWidth="100"
|
||||
Margin="10,10,0,10"
|
||||
Click="OnAddCustomShortCutClick"
|
||||
Content="{DynamicResource add}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Margin="0,0,12,2"
|
||||
Padding="0,12,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource Color05B}"
|
||||
Text="Built-in Shortcuts" />
|
||||
<ListView
|
||||
Grid.Row="10"
|
||||
MinHeight="160"
|
||||
Margin="0,6,0,0"
|
||||
Background="{DynamicResource Color02B}"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
ItemsSource="{Binding BuiltinShortcuts}"
|
||||
Style="{StaticResource {x:Static GridView.GridViewStyleKey}}">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Width="180" Header="{DynamicResource customShortcut}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Key}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn Width="540" Header="{DynamicResource builtinShortcutDescription}">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Description, Converter={StaticResource TranlationConverter}}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
private void OnnEditCustomHotkeyClick(object sender, RoutedEventArgs e)
|
||||
private void OnEditCustomHotkeyClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = viewModel.SelectedCustomPluginHotkey;
|
||||
if (item != null)
|
||||
|
|
@ -385,11 +385,34 @@ namespace Flow.Launcher
|
|||
restoreButton.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_StateChanged(object sender, EventArgs e)
|
||||
{
|
||||
RefreshMaximizeRestoreButton();
|
||||
}
|
||||
|
||||
#region Shortcut
|
||||
|
||||
private void OnDeleteCustomShortCutClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
viewModel.DeleteSelectedCustomShortcut();
|
||||
}
|
||||
|
||||
private void OnEditCustomShortCutClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (viewModel.EditSelectedCustomShortcut())
|
||||
{
|
||||
customShortcutView.Items.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAddCustomShortCutClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
viewModel.AddCustomShortcut();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private CollectionView pluginListView;
|
||||
private CollectionView pluginStoreView;
|
||||
|
||||
|
|
@ -467,6 +490,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
ClockDisplay();
|
||||
}
|
||||
|
||||
public void ClockDisplay()
|
||||
{
|
||||
if (settings.UseClock)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ using Flow.Launcher.Plugin.SharedCommands;
|
|||
using Flow.Launcher.Storage;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using System.Text;
|
||||
using System.Threading.Channels;
|
||||
using ISavable = Flow.Launcher.Plugin.ISavable;
|
||||
using System.IO;
|
||||
|
|
@ -621,7 +622,9 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
_updateSource?.Cancel();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(QueryText))
|
||||
var query = ConstructQuery(QueryText, Settings.CustomShortcuts, Settings.BuiltinShortcuts);
|
||||
|
||||
if (query == null) // shortcut expanded
|
||||
{
|
||||
Results.Clear();
|
||||
Results.Visbility = Visibility.Collapsed;
|
||||
|
|
@ -645,8 +648,7 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
if (currentCancellationToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
var query = QueryBuilder.Build(QueryText, PluginManager.NonGlobalPlugins);
|
||||
|
||||
|
||||
// handle the exclusiveness of plugin using action keyword
|
||||
RemoveOldQueryResults(query);
|
||||
|
|
@ -736,6 +738,40 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
private Query ConstructQuery(string queryText, IEnumerable<CustomShortcutModel> customShortcuts, IEnumerable<BuiltinShortcutModel> builtInShortcuts)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(queryText))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder queryBuilder = new(queryText);
|
||||
StringBuilder queryBuilderTmp = new(queryText);
|
||||
|
||||
foreach (var shortcut in customShortcuts)
|
||||
{
|
||||
if (queryBuilder.Equals(shortcut.Key))
|
||||
{
|
||||
queryBuilder.Replace(shortcut.Key, shortcut.Expand());
|
||||
}
|
||||
|
||||
queryBuilder.Replace('@' + shortcut.Key, shortcut.Expand());
|
||||
}
|
||||
|
||||
foreach (var shortcut in builtInShortcuts)
|
||||
{
|
||||
queryBuilder.Replace(shortcut.Key, shortcut.Expand());
|
||||
queryBuilderTmp.Replace(shortcut.Key, shortcut.Expand());
|
||||
}
|
||||
|
||||
// show expanded builtin shortcuts
|
||||
// use private field to avoid infinite recursion
|
||||
_queryText = queryBuilderTmp.ToString();
|
||||
|
||||
var query = QueryBuilder.Build(queryBuilder.ToString().Trim(), PluginManager.NonGlobalPlugins);
|
||||
return query;
|
||||
}
|
||||
|
||||
private void RemoveOldQueryResults(Query query)
|
||||
{
|
||||
if (_lastQuery?.ActionKeyword != query?.ActionKeyword)
|
||||
|
|
@ -966,28 +1002,26 @@ namespace Flow.Launcher.ViewModel
|
|||
var result = Results.SelectedItem?.Result;
|
||||
if (result != null)
|
||||
{
|
||||
string copyText = string.IsNullOrEmpty(result.CopyText) ? result.SubTitle : result.CopyText;
|
||||
string copyText = result.CopyText;
|
||||
var isFile = File.Exists(copyText);
|
||||
var isFolder = Directory.Exists(copyText);
|
||||
if (isFile || isFolder)
|
||||
{
|
||||
var paths = new StringCollection();
|
||||
paths.Add(copyText);
|
||||
var paths = new StringCollection
|
||||
{
|
||||
copyText
|
||||
};
|
||||
|
||||
Clipboard.SetFileDropList(paths);
|
||||
App.API.ShowMsg(
|
||||
App.API.GetTranslation("copy")
|
||||
+ " "
|
||||
+ (isFile ? App.API.GetTranslation("fileTitle") : App.API.GetTranslation("folderTitle")),
|
||||
$"{App.API.GetTranslation("copy")} {(isFile ? App.API.GetTranslation("fileTitle") : App.API.GetTranslation("folderTitle"))}",
|
||||
App.API.GetTranslation("completedSuccessfully"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Clipboard.SetDataObject(copyText.ToString());
|
||||
Clipboard.SetDataObject(copyText);
|
||||
App.API.ShowMsg(
|
||||
App.API.GetTranslation("copy")
|
||||
+ " "
|
||||
+ App.API.GetTranslation("textTitle"),
|
||||
$"{App.API.GetTranslation("copy")} {App.API.GetTranslation("textTitle")}",
|
||||
App.API.GetTranslation("completedSuccessfully"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ using Flow.Launcher.Infrastructure.Storage;
|
|||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
{
|
||||
|
|
@ -212,11 +213,19 @@ namespace Flow.Launcher.ViewModel
|
|||
}
|
||||
}
|
||||
|
||||
public List<string> OpenResultModifiersList => new List<string> { KeyConstant.Alt, KeyConstant.Ctrl, $"{KeyConstant.Ctrl}+{KeyConstant.Alt}" };
|
||||
public List<string> OpenResultModifiersList => new List<string>
|
||||
{
|
||||
KeyConstant.Alt,
|
||||
KeyConstant.Ctrl,
|
||||
$"{KeyConstant.Ctrl}+{KeyConstant.Alt}"
|
||||
};
|
||||
private Internationalization _translater => InternationalizationManager.Instance;
|
||||
public List<Language> Languages => _translater.LoadAvailableLanguages();
|
||||
public IEnumerable<int> MaxResultsRange => Enumerable.Range(2, 16);
|
||||
|
||||
public ObservableCollection<CustomShortcutModel> CustomShortcuts => Settings.CustomShortcuts;
|
||||
public ObservableCollection<BuiltinShortcutModel> BuiltinShortcuts => Settings.BuiltinShortcuts;
|
||||
|
||||
public string TestProxy()
|
||||
{
|
||||
var proxyServer = Settings.Proxy.Server;
|
||||
|
|
@ -275,7 +284,10 @@ namespace Flow.Launcher.ViewModel
|
|||
var metadatas = PluginManager.AllPlugins
|
||||
.OrderBy(x => x.Metadata.Disabled)
|
||||
.ThenBy(y => y.Metadata.Name)
|
||||
.Select(p => new PluginViewModel { PluginPair = p })
|
||||
.Select(p => new PluginViewModel
|
||||
{
|
||||
PluginPair = p
|
||||
})
|
||||
.ToList();
|
||||
return metadatas;
|
||||
}
|
||||
|
|
@ -397,7 +409,11 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
var key = $"ColorScheme{e}";
|
||||
var display = _translater.GetTranslation(key);
|
||||
var m = new ColorScheme { Display = display, Value = e, };
|
||||
var m = new ColorScheme
|
||||
{
|
||||
Display = display,
|
||||
Value = e,
|
||||
};
|
||||
modes.Add(m);
|
||||
}
|
||||
return modes;
|
||||
|
|
@ -522,7 +538,10 @@ namespace Flow.Launcher.ViewModel
|
|||
bitmap.DecodePixelWidth = 800;
|
||||
bitmap.DecodePixelHeight = 600;
|
||||
bitmap.EndInit();
|
||||
var brush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
|
||||
var brush = new ImageBrush(bitmap)
|
||||
{
|
||||
Stretch = Stretch.UniformToFill
|
||||
};
|
||||
return brush;
|
||||
}
|
||||
else
|
||||
|
|
@ -550,19 +569,19 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
Title = "WebSearch",
|
||||
SubTitle = "Search the web with different search engine support",
|
||||
IcoPath =Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png")
|
||||
IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.WebSearch\Images\web_search.png")
|
||||
},
|
||||
new Result
|
||||
{
|
||||
Title = "Program",
|
||||
SubTitle = "Launch programs as admin or a different user",
|
||||
IcoPath =Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.Program\Images\program.png")
|
||||
IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.Program\Images\program.png")
|
||||
},
|
||||
new Result
|
||||
{
|
||||
Title = "ProcessKiller",
|
||||
SubTitle = "Terminate unwanted processes",
|
||||
IcoPath =Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png")
|
||||
IcoPath = Path.Combine(Constant.ProgramDirectory, @"Plugins\Flow.Launcher.Plugin.ProcessKiller\Images\app.png")
|
||||
}
|
||||
};
|
||||
var vm = new ResultsViewModel(Settings);
|
||||
|
|
@ -576,8 +595,8 @@ namespace Flow.Launcher.ViewModel
|
|||
get
|
||||
{
|
||||
if (Fonts.SystemFontFamilies.Count(o =>
|
||||
o.FamilyNames.Values != null &&
|
||||
o.FamilyNames.Values.Contains(Settings.QueryBoxFont)) > 0)
|
||||
o.FamilyNames.Values != null &&
|
||||
o.FamilyNames.Values.Contains(Settings.QueryBoxFont)) > 0)
|
||||
{
|
||||
var font = new FontFamily(Settings.QueryBoxFont);
|
||||
return font;
|
||||
|
|
@ -604,7 +623,7 @@ namespace Flow.Launcher.ViewModel
|
|||
Settings.QueryBoxFontStyle,
|
||||
Settings.QueryBoxFontWeight,
|
||||
Settings.QueryBoxFontStretch
|
||||
));
|
||||
));
|
||||
return typeface;
|
||||
}
|
||||
set
|
||||
|
|
@ -621,8 +640,8 @@ namespace Flow.Launcher.ViewModel
|
|||
get
|
||||
{
|
||||
if (Fonts.SystemFontFamilies.Count(o =>
|
||||
o.FamilyNames.Values != null &&
|
||||
o.FamilyNames.Values.Contains(Settings.ResultFont)) > 0)
|
||||
o.FamilyNames.Values != null &&
|
||||
o.FamilyNames.Values.Contains(Settings.ResultFont)) > 0)
|
||||
{
|
||||
var font = new FontFamily(Settings.ResultFont);
|
||||
return font;
|
||||
|
|
@ -649,7 +668,7 @@ namespace Flow.Launcher.ViewModel
|
|||
Settings.ResultFontStyle,
|
||||
Settings.ResultFontWeight,
|
||||
Settings.ResultFontStretch
|
||||
));
|
||||
));
|
||||
return typeface;
|
||||
}
|
||||
set
|
||||
|
|
@ -671,6 +690,65 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
#endregion
|
||||
|
||||
#region shortcut
|
||||
|
||||
public CustomShortcutModel? SelectedCustomShortcut { get; set; }
|
||||
|
||||
public void DeleteSelectedCustomShortcut()
|
||||
{
|
||||
var item = SelectedCustomShortcut;
|
||||
if (item == null)
|
||||
{
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
return;
|
||||
}
|
||||
|
||||
string deleteWarning = string.Format(
|
||||
InternationalizationManager.Instance.GetTranslation("deleteCustomShortcutWarning"),
|
||||
item?.Key, item?.Value);
|
||||
if (MessageBox.Show(deleteWarning, InternationalizationManager.Instance.GetTranslation("delete"),
|
||||
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
Settings.CustomShortcuts.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
public bool EditSelectedCustomShortcut()
|
||||
{
|
||||
var item = SelectedCustomShortcut;
|
||||
if (item == null)
|
||||
{
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
|
||||
return false;
|
||||
}
|
||||
|
||||
var shortcutSettingWindow = new CustomShortcutSetting(item.Key, item.Value, this);
|
||||
if (shortcutSettingWindow.ShowDialog() == true)
|
||||
{
|
||||
item.Key = shortcutSettingWindow.Key;
|
||||
item.Value = shortcutSettingWindow.Value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void AddCustomShortcut()
|
||||
{
|
||||
var shortcutSettingWindow = new CustomShortcutSetting(this);
|
||||
if (shortcutSettingWindow.ShowDialog() == true)
|
||||
{
|
||||
var shortcut = new CustomShortcutModel(shortcutSettingWindow.Key, shortcutSettingWindow.Value);
|
||||
Settings.CustomShortcuts.Add(shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShortcutExists(string key)
|
||||
{
|
||||
return Settings.CustomShortcuts.Any(x => x.Key == key) || Settings.BuiltinShortcuts.Any(x => x.Key == key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region about
|
||||
|
||||
public string Website => Constant.Website;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
contextMenu = new ContextMenu(Context);
|
||||
pluginManager = new PluginsManager(Context, Settings);
|
||||
|
||||
await pluginManager.UpdateManifestAsync();
|
||||
_ = pluginManager.UpdateManifestAsync();
|
||||
}
|
||||
|
||||
public List<Result> LoadContextMenus(Result selectedResult)
|
||||
|
|
@ -74,4 +74,4 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@
|
|||
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_excutable_types">File Suffixes</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_URL_types">URL Protocols</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_URL_steam">Steam Games</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_URL_epic">Epic Games</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_URL_http">Http/Https</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_custom_urls">Custom URL Protocols</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_custom_file_types">Custom File Suffixes</system:String>
|
||||
<system:String x:Key="flowlauncher_plugin_program_suffixes_tooltip">
|
||||
|
|
|
|||
|
|
@ -189,9 +189,9 @@
|
|||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource flowlauncher_plugin_program_suffixes_URL_types}" />
|
||||
<CheckBox Name="steam" Margin="10,0,0,0" IsChecked="{Binding ProtocolsStatus[steam]}">Steam Games</CheckBox>
|
||||
<CheckBox Name="epic" Margin="10,0,0,0" IsChecked="{Binding ProtocolsStatus[epic]}">Epic Games</CheckBox>
|
||||
<CheckBox Name="http" Margin="10,0,0,0" IsChecked="{Binding ProtocolsStatus[http]}">Http/Https</CheckBox>
|
||||
<CheckBox Name="steam" Margin="10,0,0,0" IsChecked="{Binding ProtocolsStatus[steam]}" Content="{DynamicResource flowlauncher_plugin_program_suffixes_URL_steam}"></CheckBox>
|
||||
<CheckBox Name="epic" Margin="10,0,0,0" IsChecked="{Binding ProtocolsStatus[epic]}" Content="{DynamicResource flowlauncher_plugin_program_suffixes_URL_epic}"></CheckBox>
|
||||
<CheckBox Name="http" Margin="10,0,0,0" IsChecked="{Binding ProtocolsStatus[http]}" Content="{DynamicResource flowlauncher_plugin_program_suffixes_URL_http}"></CheckBox>
|
||||
<CheckBox
|
||||
Name="CustomProtocol"
|
||||
Margin="10,0,0,0"
|
||||
|
|
|
|||
|
|
@ -390,7 +390,6 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
_ => Win32Program(x)
|
||||
});
|
||||
|
||||
|
||||
return programs;
|
||||
}
|
||||
|
||||
|
|
@ -411,7 +410,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
ShortcutExtension => LnkProgram(x),
|
||||
UrlExtension => UrlProgram(x),
|
||||
_ => Win32Program(x)
|
||||
}).Where(x => x.Valid);
|
||||
});
|
||||
return programs;
|
||||
}
|
||||
|
||||
|
|
@ -569,7 +568,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
|
||||
autoIndexPrograms = ProgramsHasher(autoIndexPrograms);
|
||||
|
||||
return programs.Concat(autoIndexPrograms).Distinct().ToArray();
|
||||
return programs.Concat(autoIndexPrograms).Where(x => x.Valid).Distinct().ToArray();
|
||||
}
|
||||
#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
|
||||
catch (Exception)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Windows.Foundation.Metadata;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Program
|
||||
{
|
||||
|
|
@ -11,7 +12,10 @@ namespace Flow.Launcher.Plugin.Program
|
|||
public DateTime LastIndexTime { get; set; }
|
||||
public List<ProgramSource> ProgramSources { get; set; } = new List<ProgramSource>();
|
||||
public List<DisabledProgramSource> DisabledProgramSources { get; set; } = new List<DisabledProgramSource>();
|
||||
public string[] CustomSuffixes { get; set; } = Array.Empty<string>();
|
||||
|
||||
[Obsolete, JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string[] ProgramSuffixes { get; set; } = null;
|
||||
public string[] CustomSuffixes { get; set; } = Array.Empty<string>(); // Custom suffixes only
|
||||
public string[] CustomProtocols { get; set; } = Array.Empty<string>();
|
||||
|
||||
public Dictionary<string, bool> BuiltinSuffixesStatus { get; set; } = new Dictionary<string, bool>{
|
||||
|
|
@ -32,6 +36,7 @@ namespace Flow.Launcher.Plugin.Program
|
|||
|
||||
public string[] GetSuffixes()
|
||||
{
|
||||
RemoveRedundantSuffixes();
|
||||
List<string> extensions = new List<string>();
|
||||
foreach (var item in BuiltinSuffixesStatus)
|
||||
{
|
||||
|
|
@ -84,6 +89,24 @@ namespace Flow.Launcher.Plugin.Program
|
|||
}
|
||||
}
|
||||
|
||||
private void RemoveRedundantSuffixes()
|
||||
{
|
||||
// Migrate to new settings
|
||||
// CustomSuffixes no longer contains custom suffixes
|
||||
// users has tweaked the settings
|
||||
// or this function has been executed once
|
||||
if (UseCustomSuffixes == true || ProgramSuffixes == null)
|
||||
return;
|
||||
var suffixes = ProgramSuffixes.ToList();
|
||||
foreach(var item in BuiltinSuffixesStatus)
|
||||
{
|
||||
suffixes.Remove(item.Key);
|
||||
}
|
||||
CustomSuffixes = suffixes.ToArray(); // Custom suffixes
|
||||
UseCustomSuffixes = CustomSuffixes.Length != 0; // Search custom suffixes or not
|
||||
ProgramSuffixes = null;
|
||||
}
|
||||
|
||||
public bool EnableStartMenuSource { get; set; } = true;
|
||||
public bool EnableDescription { get; set; } = false;
|
||||
public bool HideAppsPath { get; set; } = true;
|
||||
|
|
|
|||
Loading…
Reference in a new issue