mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'Flow-Launcher:dev' into SearchProgramsInPATH
This commit is contained in:
commit
40d06e96c5
16 changed files with 686 additions and 41 deletions
|
|
@ -1,4 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NLog;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,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" />
|
||||
|
|
@ -2345,9 +2346,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"
|
||||
|
|
@ -2452,6 +2461,7 @@
|
|||
Text="{DynamicResource customQueryHotkey}" />
|
||||
<ListView
|
||||
Grid.Row="4"
|
||||
MinHeight="160"
|
||||
Margin="0,0,0,0"
|
||||
Background="{DynamicResource Color02B}"
|
||||
BorderBrush="DarkGray"
|
||||
|
|
@ -2492,7 +2502,7 @@
|
|||
<Button
|
||||
MinWidth="100"
|
||||
Margin="10"
|
||||
Click="OnnEditCustomHotkeyClick"
|
||||
Click="OnEditCustomHotkeyClick"
|
||||
Content="{DynamicResource edit}" />
|
||||
<Button
|
||||
MinWidth="100"
|
||||
|
|
@ -2500,6 +2510,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>
|
||||
|
|
|
|||
|
|
@ -154,7 +154,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)
|
||||
|
|
@ -381,11 +381,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;
|
||||
|
||||
|
|
@ -463,6 +486,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;
|
||||
|
|
@ -520,7 +536,10 @@ namespace Flow.Launcher.ViewModel
|
|||
bitmap.BeginInit();
|
||||
bitmap.StreamSource = memStream;
|
||||
bitmap.EndInit();
|
||||
var brush = new ImageBrush(bitmap) { Stretch = Stretch.UniformToFill };
|
||||
var brush = new ImageBrush(bitmap)
|
||||
{
|
||||
Stretch = Stretch.UniformToFill
|
||||
};
|
||||
return brush;
|
||||
}
|
||||
else
|
||||
|
|
@ -548,19 +567,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);
|
||||
|
|
@ -574,8 +593,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;
|
||||
|
|
@ -602,7 +621,7 @@ namespace Flow.Launcher.ViewModel
|
|||
Settings.QueryBoxFontStyle,
|
||||
Settings.QueryBoxFontWeight,
|
||||
Settings.QueryBoxFontStretch
|
||||
));
|
||||
));
|
||||
return typeface;
|
||||
}
|
||||
set
|
||||
|
|
@ -619,8 +638,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;
|
||||
|
|
@ -647,7 +666,7 @@ namespace Flow.Launcher.ViewModel
|
|||
Settings.ResultFontStyle,
|
||||
Settings.ResultFontWeight,
|
||||
Settings.ResultFontStretch
|
||||
));
|
||||
));
|
||||
return typeface;
|
||||
}
|
||||
set
|
||||
|
|
@ -669,6 +688,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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue