Merge branch 'Flow-Launcher:dev' into SearchProgramsInPATH

This commit is contained in:
VictoriousRaptor 2022-11-08 13:08:48 +08:00 committed by GitHub
commit 40d06e96c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 686 additions and 41 deletions

View file

@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using NLog;

View file

@ -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 ""; });
}
}
}

View file

@ -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; }

View file

@ -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)

View 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();
}
}

View 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>

View 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();
}
}
}

View file

@ -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>

View file

@ -300,7 +300,7 @@
<ContentControl>
<flowlauncher:ResultListBox x:Name="ResultListBox"
DataContext="{Binding Results}"
PreviewMouseDown="OnPreviewMouseButtonDown" />
PreviewMouseLeftButtonUp="OnPreviewMouseButtonDown" />
</ContentControl>
</Border>
<Border Style="{DynamicResource WindowRadius}">

View file

@ -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;

View file

@ -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}" />

View file

@ -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;
}
}
}

View file

@ -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>

View file

@ -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)

View file

@ -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"));
}
}

View file

@ -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;