Merge branch 'dev' into localize-missing-python-js-dialog

This commit is contained in:
Yan 2024-06-17 05:33:32 +06:00 committed by GitHub
commit a3c7b2b40f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 763 additions and 124 deletions

View file

@ -72,4 +72,8 @@ changes:
# Sum all the line removed in the PR
deletions: {{ branch.diff.files_metadata | map(attr='deletions') | sum }}
# Calculate the ratio of new code
ratio: {{ (changes.additions / (changes.additions + changes.deletions)) * 100 | round(2) }}
ratio: {{ (changes.additions / (changes.additions + changes.deletions)) * 100 | round(2) }}
has:
screenshot_link: {{ pr.description | includes(regex=r/!\[.*\]\(.*(jpg|svg|png|gif|psd).*\)/) }}
image_uploaded: {{ pr.description | includes(regex=r/<img.*src.*(jpg|svg|png|gif|psd).*>/) }}

View file

@ -98,6 +98,7 @@ Português
Português (Brasil)
Italiano
Slovenský
quicklook
Tiếng Việt
Droplex
Preinstalled

19
.github/workflows/pr_assignee.yml vendored Normal file
View file

@ -0,0 +1,19 @@
name: Assign PR to creator
# Due to GitHub token limitation, only able to assign org members not authors from forks.
# https://github.com/thomaseizinger/assign-pr-creator-action/issues/3
on:
pull_request:
types: [opened]
branches-ignore:
- l10n_dev
jobs:
automation:
runs-on: ubuntu-latest
steps:
- name: Assign PR to creator
uses: thomaseizinger/assign-pr-creator-action@v1.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

19
.github/workflows/pr_milestone.yml vendored Normal file
View file

@ -0,0 +1,19 @@
name: Set Milestone
# Assigns the earliest created milestone that matches the below glob pattern.
on:
pull_request:
types: [opened]
jobs:
automation:
runs-on: ubuntu-latest
steps:
- name: set-milestone
uses: andrefcdias/add-to-milestone@v1.3.0
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
milestone: "+([0-9]).+([0-9]).+([0-9])"
use-expression: true

View file

@ -1,4 +1,4 @@
using Flow.Launcher.Core.ExternalPlugins;
using Flow.Launcher.Core.ExternalPlugins;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -91,6 +91,48 @@ namespace Flow.Launcher.Core.Plugin
}).ToArray());
}
public static async Task OpenExternalPreviewAsync(string path, bool sendFailToast = true)
{
await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch
{
IAsyncExternalPreview p => p.OpenPreviewAsync(path, sendFailToast),
_ => Task.CompletedTask,
}).ToArray());
}
public static async Task CloseExternalPreviewAsync()
{
await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch
{
IAsyncExternalPreview p => p.ClosePreviewAsync(),
_ => Task.CompletedTask,
}).ToArray());
}
public static async Task SwitchExternalPreviewAsync(string path, bool sendFailToast = true)
{
await Task.WhenAll(AllPlugins.Select(plugin => plugin.Plugin switch
{
IAsyncExternalPreview p => p.SwitchPreviewAsync(path, sendFailToast),
_ => Task.CompletedTask,
}).ToArray());
}
public static bool UseExternalPreview()
{
return GetPluginsForInterface<IAsyncExternalPreview>().Any(x => !x.Metadata.Disabled);
}
public static bool AllowAlwaysPreview()
{
var plugin = GetPluginsForInterface<IAsyncExternalPreview>().FirstOrDefault(x => !x.Metadata.Disabled);
if (plugin is null)
return false;
return ((IAsyncExternalPreview)plugin.Plugin).AllowAlwaysPreview();
}
static PluginManager()
{
// validate user directory

View file

@ -185,7 +185,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings
/// when false Alphabet static service will always return empty results
/// </summary>
public bool ShouldUsePinyin { get; set; } = false;
public bool AlwaysPreview { get; set; } = false;
public bool AlwaysStartEn { get; set; } = false;
private SearchPrecisionScore _querySearchPrecision = SearchPrecisionScore.Regular;

View file

@ -0,0 +1,40 @@
using System.Threading.Tasks;
namespace Flow.Launcher.Plugin
{
/// <summary>
/// This interface is for plugins that wish to provide file preview (external preview)
/// via a third party app instead of the default preview.
/// </summary>
public interface IAsyncExternalPreview : IFeatures
{
/// <summary>
/// Method for opening/showing the preview.
/// </summary>
/// <param name="path">The file path to open the preview for</param>
/// <param name="sendFailToast">Whether to send a toast message notification on failure for the user</param>
public Task OpenPreviewAsync(string path, bool sendFailToast = true);
/// <summary>
/// Method for closing/hiding the preview.
/// </summary>
public Task ClosePreviewAsync();
/// <summary>
/// Method for switching the preview to the next file result.
/// This requires the external preview be already open/showing
/// </summary>
/// <param name="path">The file path to switch the preview for</param>
/// <param name="sendFailToast">Whether to send a toast message notification on failure for the user</param>
public Task SwitchPreviewAsync(string path, bool sendFailToast = true);
/// <summary>
/// Allows the preview plugin to override the AlwaysPreview setting. Typically useful if plugin's preview does not
/// fully work well with being shown together when the query window appears with results.
/// When AlwaysPreview setting is on and this is set to false, the preview will not be shown when query
/// window appears with results, instead the internal preview will be shown.
/// </summary>
/// <returns></returns>
public bool AllowAlwaysPreview();
}
}

View file

@ -270,12 +270,12 @@ namespace Flow.Launcher.Plugin
/// <summary>
/// Full image used for preview panel
/// </summary>
public string PreviewImagePath { get; set; }
public string PreviewImagePath { get; set; } = null;
/// <summary>
/// Determines if the preview image should occupy the full width of the preview panel.
/// </summary>
public bool IsMedia { get; set; }
public bool IsMedia { get; set; } = false;
/// <summary>
/// Result description text that is shown at the bottom of the preview panel.
@ -283,12 +283,17 @@ namespace Flow.Launcher.Plugin
/// <remarks>
/// When a value is not set, the <see cref="SubTitle"/> will be used.
/// </remarks>
public string Description { get; set; }
public string Description { get; set; } = null;
/// <summary>
/// Delegate to get the preview panel's image
/// </summary>
public IconDelegate PreviewDelegate { get; set; }
public IconDelegate PreviewDelegate { get; set; } = null;
/// <summary>
/// File path of the result. For third-party programs providing external preview.
/// </summary>
public string FilePath { get; set; } = null;
/// <summary>
/// Default instance of <see cref="PreviewInfo"/>
@ -299,6 +304,7 @@ namespace Flow.Launcher.Plugin
Description = null,
IsMedia = false,
PreviewDelegate = null,
FilePath = null,
};
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -189,6 +189,8 @@
<system:String x:Key="AnimationSpeedCustom">Custom</system:String>
<system:String x:Key="Clock">Clock</system:String>
<system:String x:Key="Date">Date</system:String>
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
<!-- Setting Hotkey -->
@ -213,6 +215,7 @@
<system:String x:Key="CycleHistoryUpHotkey">Cycle Previous Query</system:String>
<system:String x:Key="CycleHistoryDownHotkey">Cycle Next Query</system:String>
<system:String x:Key="OpenContextMenuHotkey">Open Context Menu</system:String>
<system:String x:Key="OpenNativeContextMenuHotkey">Open Native Context Menu</system:String>
<system:String x:Key="SettingWindowHotkey">Open Setting Window</system:String>
<system:String x:Key="CopyFilePathHotkey">Copy File Path</system:String>
<system:String x:Key="ToggleGameModeHotkey">Toggle Game Mode</system:String>
@ -357,6 +360,9 @@
<system:String x:Key="commonCancel">Cancel</system:String>
<system:String x:Key="commonReset">Reset</system:String>
<system:String x:Key="commonDelete">Delete</system:String>
<system:String x:Key="commonOK">OK</system:String>
<system:String x:Key="commonYes">Yes</system:String>
<system:String x:Key="commonNo">No</system:String>
<!-- Crash Reporter -->
<system:String x:Key="reportWindow_version">Version</system:String>

View file

@ -427,7 +427,7 @@
VerticalAlignment="Stretch"
Background="Transparent"
ShowsPreview="True"
Visibility="{Binding PreviewVisible, Converter={StaticResource BoolToVisibilityConverter}}">
Visibility="{Binding InternalPreviewVisible, Converter={StaticResource BoolToVisibilityConverter}}">
<GridSplitter.Template>
<ControlTemplate TargetType="{x:Type GridSplitter}">
<Border Style="{DynamicResource PreviewBorderStyle}" />
@ -439,7 +439,7 @@
Grid.Column="2"
VerticalAlignment="Stretch"
Style="{DynamicResource PreviewArea}"
Visibility="{Binding PreviewVisible, Converter={StaticResource BoolToVisibilityConverter}}">
Visibility="{Binding InternalPreviewVisible, Converter={StaticResource BoolToVisibilityConverter}}">
<Border
MinHeight="380"
d:DataContext="{d:DesignInstance vm:ResultViewModel}"

View file

@ -630,7 +630,7 @@ namespace Flow.Launcher
if (_settings.UseAnimation)
await Task.Delay(100);
if (_settings.HideWhenDeactivated)
if (_settings.HideWhenDeactivated && !_viewModel.ExternalPreviewVisible)
{
_viewModel.Hide();
}

View file

@ -0,0 +1,142 @@
<Window
x:Class="Flow.Launcher.MessageBoxEx"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Flow.Launcher"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="MessageBoxWindow"
Width="420"
Height="Auto"
Background="{DynamicResource PopuBGColor}"
Foreground="{DynamicResource PopupTextColor}"
ResizeMode="NoResize"
SizeToContent="Height"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<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 Height="Auto" />
<RowDefinition />
<RowDefinition MinHeight="68" />
</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="Button_Cancel"
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>
<StackPanel Grid.Row="1" Margin="30 0 30 24">
<Grid Grid.Column="0" Margin="0 0 0 12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
Name="Img"
Grid.Column="0"
Width="18"
Height="18"
Margin="0 0 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="Fant"
Stretch="UniformToFill"
Visibility="Collapsed" />
<TextBlock
x:Name="TitleTextBlock"
Grid.Column="1"
MaxWidth="400"
Margin="10 0 26 0"
VerticalAlignment="Center"
FontFamily="Segoe UI"
FontSize="20"
FontWeight="SemiBold"
TextAlignment="Left"
TextWrapping="Wrap" />
</Grid>
<TextBlock
x:Name="DescTextBlock"
Grid.Column="1"
MaxWidth="400"
Margin="0 0 26 0"
HorizontalAlignment="Stretch"
FontSize="14"
TextAlignment="Left"
TextWrapping="Wrap" />
</StackPanel>
<Border
Grid.Row="2"
Margin="0 0 0 0"
Background="{DynamicResource PopupButtonAreaBGColor}"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="0 1 0 0">
<WrapPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button
x:Name="btnCancel"
MinWidth="140"
Margin="5 0 5 0"
Click="Button_Click"
Content="{DynamicResource commonCancel}" />
<Button
x:Name="btnNo"
MinWidth="140"
Margin="5 0 5 0"
Click="Button_Click"
Content="{DynamicResource commonNo}" />
<Button
x:Name="btnOk"
MinWidth="140"
Margin="5 0 5 0"
Click="Button_Click"
Content="{DynamicResource commonOK}" />
<Button
x:Name="btnYes"
MinWidth="140"
Margin="5 0 5 0"
Click="Button_Click"
Content="{DynamicResource commonYes}" />
</WrapPanel>
</Border>
</Grid>
</Window>

View file

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
using YamlDotNet.Core.Tokens;
namespace Flow.Launcher
{
public partial class MessageBoxEx : Window
{
public MessageBoxEx()
{
InitializeComponent();
}
public enum MessageBoxType
{
ConfirmationWithYesNo = 0,
ConfirmationWithYesNoCancel,
YesNo,
Information,
Error,
Warning
}
public enum MessageBoxImage
{
Warning = 0,
Question,
Information,
Error,
None
}
static MessageBoxEx msgBox;
static MessageBoxResult _result = MessageBoxResult.No;
/// 1 parameter
public static MessageBoxResult Show(string msg)
{
return Show(string.Empty, msg, MessageBoxButton.OK, MessageBoxImage.None);
}
// 2 parameter
public static MessageBoxResult Show(string caption, string text)
{
return Show(caption, text, MessageBoxButton.OK, MessageBoxImage.None);
}
/// 3 parameter
public static MessageBoxResult Show(string caption, string msg, MessageBoxType type)
{
switch (type)
{
case MessageBoxType.ConfirmationWithYesNo:
return Show(caption, msg, MessageBoxButton.YesNo,
MessageBoxImage.Question);
case MessageBoxType.YesNo:
return Show(caption, msg, MessageBoxButton.YesNo,
MessageBoxImage.Question);
case MessageBoxType.ConfirmationWithYesNoCancel:
return Show(caption, msg, MessageBoxButton.YesNoCancel,
MessageBoxImage.Question);
case MessageBoxType.Information:
return Show(caption, msg, MessageBoxButton.OK,
MessageBoxImage.Information);
case MessageBoxType.Error:
return Show(caption, msg, MessageBoxButton.OK,
MessageBoxImage.Error);
case MessageBoxType.Warning:
return Show(caption, msg, MessageBoxButton.OK,
MessageBoxImage.Warning);
default:
return MessageBoxResult.No;
}
}
// 4 parameter, Final Display Message.
public static MessageBoxResult Show(string caption, string text, MessageBoxButton button, MessageBoxImage image)
{
msgBox = new MessageBoxEx();
msgBox.TitleTextBlock.Text = text;
msgBox.DescTextBlock.Text = caption;
msgBox.Title = text;
SetVisibilityOfButtons(button);
SetImageOfMessageBox(image);
msgBox.ShowDialog();
return _result;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (sender == btnOk)
_result = MessageBoxResult.OK;
else if (sender == btnYes)
_result = MessageBoxResult.Yes;
else if (sender == btnNo)
_result = MessageBoxResult.No;
else if (sender == btnCancel)
_result = MessageBoxResult.Cancel;
else
_result = MessageBoxResult.None;
msgBox.Close();
msgBox = null;
}
private static void SetVisibilityOfButtons(MessageBoxButton button)
{
switch (button)
{
case MessageBoxButton.OK:
msgBox.btnCancel.Visibility = Visibility.Collapsed;
msgBox.btnNo.Visibility = Visibility.Collapsed;
msgBox.btnYes.Visibility = Visibility.Collapsed;
msgBox.btnOk.Focus();
break;
case MessageBoxButton.OKCancel:
msgBox.btnNo.Visibility = Visibility.Collapsed;
msgBox.btnYes.Visibility = Visibility.Collapsed;
msgBox.btnCancel.Focus();
break;
case MessageBoxButton.YesNo:
msgBox.btnOk.Visibility = Visibility.Collapsed;
msgBox.btnCancel.Visibility = Visibility.Collapsed;
msgBox.btnNo.Focus();
break;
case MessageBoxButton.YesNoCancel:
msgBox.btnOk.Visibility = Visibility.Collapsed;
msgBox.btnCancel.Focus();
break;
default:
break;
}
}
private static void SetImageOfMessageBox(MessageBoxImage image)
{
switch (image)
{
case MessageBoxImage.Warning:
msgBox.SetImage("Warning.png");
msgBox.Img.Visibility = Visibility.Visible;
break;
case MessageBoxImage.Question:
msgBox.SetImage("Question.png");
msgBox.Img.Visibility = Visibility.Visible;
break;
case MessageBoxImage.Information:
msgBox.SetImage("Information.png");
msgBox.Img.Visibility = Visibility.Visible;
break;
case MessageBoxImage.Error:
msgBox.SetImage("Error.png");
msgBox.Img.Visibility = Visibility.Visible;
break;
default:
msgBox.Img.Visibility = Visibility.Collapsed;
break;
}
}
private void SetImage(string imageName)
{
string uri = Constant.ProgramDirectory + "/Images/" + imageName;
var uriSource = new Uri(uri, UriKind.RelativeOrAbsolute);
Img.Source = new BitmapImage(uriSource);
}
private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e)
{
DialogResult = false;
Close();
}
private void Button_Cancel(object sender, RoutedEventArgs e)
{
msgBox.Close();
msgBox = null;
}
}
}

View file

@ -8,6 +8,8 @@
<converters:TextConverter x:Key="TextConverter" />
<core:TranslationConverter x:Key="TranslationConverter" />
<!-- Icon for Theme Type Label -->
<Geometry x:Key="circle_half_stroke_solid">F1 M512,512z M0,0z M448,256C448,150,362,64,256,64L256,448C362,448,448,362,448,256z M0,256A256,256,0,1,1,512,256A256,256,0,1,1,0,256z</Geometry>
<Style x:Key="StoreItemFocusVisualStyleKey">
<Setter Property="Control.Template">
<Setter.Value>

View file

@ -11,6 +11,7 @@ using Flow.Launcher.Core.Resource;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using static Flow.Launcher.MessageBoxEx;
namespace Flow.Launcher.SettingPages.ViewModels;
@ -62,10 +63,10 @@ public partial class SettingsPaneAboutViewModel : BaseModel
[RelayCommand]
private void AskClearLogFolderConfirmation()
{
var confirmResult = MessageBox.Show(
var confirmResult = MessageBoxEx.Show(
InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"),
InternationalizationManager.Instance.GetTranslation("clearlogfolder"),
MessageBoxButton.YesNo
MessageBoxType.YesNo
);
if (confirmResult == MessageBoxResult.Yes)

View file

@ -5,9 +5,9 @@
xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
xmlns:userSettings="clr-namespace:Flow.Launcher.Infrastructure.UserSettings;assembly=Flow.Launcher.Infrastructure"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:userSettings="clr-namespace:Flow.Launcher.Infrastructure.UserSettings;assembly=Flow.Launcher.Infrastructure"
xmlns:viewModels="clr-namespace:Flow.Launcher.SettingPages.ViewModels"
Title="Hotkey"
d:DataContext="{d:DesignInstance viewModels:SettingsPaneHotkeyViewModel}"
@ -33,9 +33,9 @@
Sub="{DynamicResource flowlauncherHotkeyToolTip}">
<flowlauncher:HotkeyControl
ChangeHotkey="{Binding SetTogglingHotkeyCommand}"
HotkeySettings="{Binding Settings}"
DefaultHotkey="Alt+Space"
Hotkey="{Binding Settings.Hotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="True"
WindowTitle="{DynamicResource flowlauncherHotkey}" />
</cc:Card>
@ -45,17 +45,15 @@
Icon="&#xe8a1;"
Sub="{DynamicResource previewHotkeyToolTip}">
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey="F1"
Hotkey="{Binding Settings.PreviewHotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False"
WindowTitle="{DynamicResource previewHotkey}" />
</cc:Card>
<cc:CardGroup Margin="0 12 0 0">
<cc:Card
Title="{DynamicResource openResultModifiers}"
Sub="{DynamicResource openResultModifiersToolTip}">
<cc:Card Title="{DynamicResource openResultModifiers}" Sub="{DynamicResource openResultModifiersToolTip}">
<ComboBox
Width="120"
FontSize="14"
@ -106,9 +104,9 @@
Icon="&#xede3;"
Type="Inside">
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey="Ctrl+I"
Hotkey="{Binding Settings.OpenContextMenuHotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:Card>
<cc:Card
@ -117,15 +115,20 @@
Type="Inside">
<cc:HotkeyDisplay Keys="Shift+Enter" />
</cc:Card>
<cc:Card
Title="{DynamicResource OpenNativeContextMenuHotkey}"
Icon="&#xede3;"
Type="Inside">
<cc:HotkeyDisplay Keys="Alt+Enter" />
</cc:Card>
<cc:Card
Title="{DynamicResource SettingWindowHotkey}"
Icon="&#xe713;"
Type="Inside">
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey="Ctrl+I"
Hotkey="{Binding Settings.SettingWindowHotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:Card>
<cc:Card
@ -172,9 +175,9 @@
Icon="&#xf0ad;"
Type="Inside">
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey=""
Hotkey="{Binding Settings.SelectPrevPageHotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:Card>
<cc:Card
@ -182,9 +185,9 @@
Icon="&#xf0ae;"
Type="Inside">
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey=""
Hotkey="{Binding Settings.SelectNextPageHotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:Card>
@ -217,9 +220,9 @@
Sub="{DynamicResource autoCompleteHotkeyToolTip}">
<cc:ExCard.SideContent>
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey="Ctrl+Tab"
Hotkey="{Binding Settings.AutoCompleteHotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:ExCard.SideContent>
<cc:Card
@ -227,9 +230,9 @@
Sub="{DynamicResource AdditionalHotkeyToolTip}"
Type="InsideFit">
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey=""
Hotkey="{Binding Settings.AutoCompleteHotkey2}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:Card>
</cc:ExCard>
@ -240,9 +243,9 @@
Icon="&#xe74a;">
<cc:ExCard.SideContent>
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey="Shift+Tab"
Hotkey="{Binding Settings.SelectPrevItemHotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:ExCard.SideContent>
<cc:Card
@ -250,9 +253,9 @@
Sub="{DynamicResource AdditionalHotkeyToolTip}"
Type="InsideFit">
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey=""
Hotkey="{Binding Settings.SelectPrevItemHotkey2}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:Card>
</cc:ExCard>
@ -263,9 +266,9 @@
Icon="&#xe74b;">
<cc:ExCard.SideContent>
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey="Tab"
Hotkey="{Binding Settings.SelectNextItemHotkey}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:ExCard.SideContent>
<cc:Card
@ -273,9 +276,9 @@
Sub="{DynamicResource AdditionalHotkeyToolTip}"
Type="InsideFit">
<flowlauncher:HotkeyControl
HotkeySettings="{Binding Settings}"
DefaultHotkey=""
Hotkey="{Binding Settings.SelectNextItemHotkey2}"
HotkeySettings="{Binding Settings}"
ValidateKeyGesture="False" />
</cc:Card>
</cc:ExCard>
@ -435,8 +438,7 @@
<GridViewColumn Width="430" Header="{DynamicResource builtinShortcutDescription}">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="{x:Type userSettings:BuiltinShortcutModel}">
<TextBlock
Text="{Binding Description, Converter={StaticResource TranslationConverter}}" />
<TextBlock Text="{Binding Description, Converter={StaticResource TranslationConverter}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

View file

@ -390,17 +390,21 @@
Icon="&#xe790;">
<cc:ExCard.SideContent>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
<ui:FontIcon
Margin="0 2 8 0"
<ui:PathIcon
Width="12"
Margin="0 1 8 0"
VerticalAlignment="Center"
FontSize="12"
Glyph="&#xED66;"
Data="{DynamicResource circle_half_stroke_solid}"
ToolTip="{DynamicResource TypeIsDarkToolTip}"
ToolTipService.InitialShowDelay="0"
Visibility="{Binding SelectedTheme.IsDark, Converter={StaticResource BoolToVisibilityConverter}}" />
<ui:FontIcon
Margin="0 2 8 0"
VerticalAlignment="Center"
FontSize="12"
Glyph="&#xEB42;"
ToolTip="{DynamicResource TypeHasBlurToolTip}"
ToolTipService.InitialShowDelay="0"
Visibility="{Binding SelectedTheme.HasBlur, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock Text="{Binding SelectedTheme.Name}" />
</StackPanel>
@ -433,17 +437,21 @@
VerticalAlignment="Center"
Text="{Binding Name}"
TextWrapping="Wrap" />
<ui:FontIcon
<ui:PathIcon
Width="12"
Margin="8 1 0 0"
VerticalAlignment="Center"
FontSize="12"
Glyph="&#xED66;"
Data="{DynamicResource circle_half_stroke_solid}"
ToolTip="{DynamicResource TypeIsDarkToolTip}"
ToolTipService.InitialShowDelay="0"
Visibility="{Binding IsDark, Converter={StaticResource BoolToVisibilityConverter}}" />
<ui:FontIcon
Margin="8 1 0 0"
VerticalAlignment="Center"
FontSize="12"
Glyph="&#xEB42;"
ToolTip="{DynamicResource TypeHasBlurToolTip}"
ToolTipService.InitialShowDelay="0"
Visibility="{Binding HasBlur, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
</Grid>

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@ -407,6 +407,10 @@ namespace Flow.Launcher.ViewModel
}
}
#endregion
#region BasicCommands
[RelayCommand]
private void OpenSetting()
{
@ -581,56 +585,6 @@ namespace Flow.Launcher.ViewModel
Settings.MaxResultsToShow -= 1;
}
[RelayCommand]
public void TogglePreview()
{
if (!PreviewVisible)
{
ShowPreview();
}
else
{
HidePreview();
}
ContextMenu.IsPreviewOn = PreviewVisible;
History.IsPreviewOn = PreviewVisible;
Results.IsPreviewOn = PreviewVisible;
}
private void ShowPreview()
{
ResultAreaColumn = 1;
PreviewVisible = true;
Results.SelectedItem?.LoadPreviewImage();
}
private void HidePreview()
{
ResultAreaColumn = 3;
PreviewVisible = false;
}
public void ResetPreview()
{
if (Settings.AlwaysPreview == true)
{
ShowPreview();
}
else
{
HidePreview();
}
}
private void UpdatePreview()
{
if (PreviewVisible)
{
Results.SelectedItem?.LoadPreviewImage();
}
}
/// <summary>
/// we need move cursor to end when we manually changed query
/// but we don't want to move cursor to end when query is updated from TextBox
@ -805,10 +759,186 @@ namespace Flow.Launcher.ViewModel
public string Image => Constant.QueryTextBoxIconImagePath;
public bool StartWithEnglishMode => Settings.AlwaysStartEn;
#endregion
public bool PreviewVisible { get; set; } = false;
#region Preview
public int ResultAreaColumn { get; set; } = 1;
public bool InternalPreviewVisible
{
get
{
if (ResultAreaColumn == ResultAreaColumnPreviewShown)
return true;
if (ResultAreaColumn == ResultAreaColumnPreviewHidden)
return false;
#if DEBUG
throw new NotImplementedException("ResultAreaColumn should match ResultAreaColumnPreviewShown/ResultAreaColumnPreviewHidden value");
#else
Log.Error("MainViewModel", "ResultAreaColumnPreviewHidden/ResultAreaColumnPreviewShown int value not implemented", "InternalPreviewVisible");
#endif
return false;
}
}
private static readonly int ResultAreaColumnPreviewShown = 1;
private static readonly int ResultAreaColumnPreviewHidden = 3;
public int ResultAreaColumn { get; set; } = ResultAreaColumnPreviewShown;
// This is not a reliable indicator of whether external preview is visible due to the
// ability of manually closing/exiting the external preview program which, does not inform flow that
// preview is no longer available.
public bool ExternalPreviewVisible { get; set; } = false;
private void ShowPreview()
{
var useExternalPreview = PluginManager.UseExternalPreview();
switch (useExternalPreview)
{
case true
when CanExternalPreviewSelectedResult(out var path):
// Internal preview may still be on when user switches to external
if (InternalPreviewVisible)
HideInternalPreview();
OpenExternalPreview(path);
break;
case true
when !CanExternalPreviewSelectedResult(out var _):
if (ExternalPreviewVisible)
CloseExternalPreview();
ShowInternalPreview();
break;
case false:
ShowInternalPreview();
break;
}
}
private void HidePreview()
{
if (PluginManager.UseExternalPreview())
CloseExternalPreview();
if (InternalPreviewVisible)
HideInternalPreview();
}
[RelayCommand]
private void TogglePreview()
{
if (InternalPreviewVisible || ExternalPreviewVisible)
{
HidePreview();
}
else
{
ShowPreview();
}
}
private void ToggleInternalPreview()
{
if (!InternalPreviewVisible)
{
ShowInternalPreview();
}
else
{
HideInternalPreview();
}
}
private void OpenExternalPreview(string path, bool sendFailToast = true)
{
_ = PluginManager.OpenExternalPreviewAsync(path, sendFailToast).ConfigureAwait(false);
ExternalPreviewVisible = true;
}
private void CloseExternalPreview()
{
_ = PluginManager.CloseExternalPreviewAsync().ConfigureAwait(false);
ExternalPreviewVisible = false;
}
private void SwitchExternalPreview(string path, bool sendFailToast = true)
{
_ = PluginManager.SwitchExternalPreviewAsync(path,sendFailToast).ConfigureAwait(false);
}
private void ShowInternalPreview()
{
ResultAreaColumn = ResultAreaColumnPreviewShown;
Results.SelectedItem?.LoadPreviewImage();
}
private void HideInternalPreview()
{
ResultAreaColumn = ResultAreaColumnPreviewHidden;
}
public void ResetPreview()
{
switch (Settings.AlwaysPreview)
{
case true
when PluginManager.AllowAlwaysPreview() && CanExternalPreviewSelectedResult(out var path):
OpenExternalPreview(path);
break;
case true:
ShowInternalPreview();
break;
case false:
HidePreview();
break;
}
}
private void UpdatePreview()
{
switch (PluginManager.UseExternalPreview())
{
case true
when CanExternalPreviewSelectedResult(out var path):
if (ExternalPreviewVisible)
{
SwitchExternalPreview(path, false);
}
else if (InternalPreviewVisible)
{
HideInternalPreview();
OpenExternalPreview(path);
}
break;
case true
when !CanExternalPreviewSelectedResult(out var _):
if (ExternalPreviewVisible)
{
CloseExternalPreview();
ShowInternalPreview();
}
break;
case false
when InternalPreviewVisible:
Results.SelectedItem?.LoadPreviewImage();
break;
}
}
private bool CanExternalPreviewSelectedResult(out string path)
{
path = Results.SelectedItem?.Result?.Preview.FilePath;
return !string.IsNullOrEmpty(path);
}
#endregion
@ -1232,6 +1362,9 @@ namespace Flow.Launcher.ViewModel
lastContextMenuResult = new Result();
lastContextMenuResults = new List<Result>();
if (ExternalPreviewVisible)
CloseExternalPreview();
if (!SelectedIsFromQueryResults())
{
SelectedResults = Results;

View file

@ -222,34 +222,7 @@ namespace Flow.Launcher.Plugin.Explorer
if (record.Type is ResultType.Volume)
return false;
var screenWithMouseCursor = System.Windows.Forms.Screen.FromPoint(System.Windows.Forms.Cursor.Position);
var xOfScreenCenter = screenWithMouseCursor.WorkingArea.Left + screenWithMouseCursor.WorkingArea.Width / 2;
var yOfScreenCenter = screenWithMouseCursor.WorkingArea.Top + screenWithMouseCursor.WorkingArea.Height / 2;
var showPosition = new System.Drawing.Point(xOfScreenCenter, yOfScreenCenter);
switch (record.Type)
{
case ResultType.File:
{
var fileInfos = new FileInfo[]
{
new(record.FullPath)
};
new Peter.ShellContextMenu().ShowContextMenu(fileInfos, showPosition);
break;
}
case ResultType.Folder:
{
var directoryInfos = new DirectoryInfo[]
{
new(record.FullPath)
};
new Peter.ShellContextMenu().ShowContextMenu(directoryInfos, showPosition);
break;
}
}
ResultManager.ShowNativeContextMenu(record.FullPath, record.Type);
return false;
},

View file

@ -11,6 +11,7 @@ using System.Windows.Input;
using Path = System.IO.Path;
using System.Windows.Controls;
using Flow.Launcher.Plugin.Explorer.Views;
using Peter;
namespace Flow.Launcher.Plugin.Explorer.Search
{
@ -70,6 +71,27 @@ namespace Flow.Launcher.Plugin.Explorer.Search
};
}
internal static void ShowNativeContextMenu(string path, ResultType type)
{
var screenWithMouseCursor = System.Windows.Forms.Screen.FromPoint(System.Windows.Forms.Cursor.Position);
var xOfScreenCenter = screenWithMouseCursor.WorkingArea.Left + screenWithMouseCursor.WorkingArea.Width / 2;
var yOfScreenCenter = screenWithMouseCursor.WorkingArea.Top + screenWithMouseCursor.WorkingArea.Height / 2;
var showPosition = new System.Drawing.Point(xOfScreenCenter, yOfScreenCenter);
switch (type)
{
case ResultType.File:
var fileInfo = new FileInfo[] { new(path) };
new ShellContextMenu().ShowContextMenu(fileInfo, showPosition);
break;
case ResultType.Folder:
var folderInfo = new System.IO.DirectoryInfo[] { new(path) };
new ShellContextMenu().ShowContextMenu(folderInfo, showPosition);
break;
}
}
internal static Result CreateFolderResult(string title, string subtitle, string path, Query query, int score = 0, bool windowsIndexed = false)
{
return new Result
@ -80,8 +102,17 @@ namespace Flow.Launcher.Plugin.Explorer.Search
AutoCompleteText = GetAutoCompleteText(title, query, path, ResultType.Folder),
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData,
CopyText = path,
Preview = new Result.PreviewInfo
{
FilePath = path,
},
Action = c =>
{
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt)
{
ShowNativeContextMenu(path, ResultType.Folder);
return false;
}
// open folder
if (c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift))
{
@ -165,6 +196,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search
Score = 500,
ProgressBar = progressValue,
ProgressBarColor = progressBarColor,
Preview = new Result.PreviewInfo
{
FilePath = path,
},
Action = _ =>
{
OpenFolder(path);
@ -218,8 +253,13 @@ namespace Flow.Launcher.Plugin.Explorer.Search
IcoPath = folderPath,
Score = 500,
CopyText = folderPath,
Action = _ =>
Action = c =>
{
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt)
{
ShowNativeContextMenu(folderPath, ResultType.Folder);
return false;
}
OpenFolder(folderPath);
return true;
},
@ -229,10 +269,7 @@ namespace Flow.Launcher.Plugin.Explorer.Search
internal static Result CreateFileResult(string filePath, Query query, int score = 0, bool windowsIndexed = false)
{
Result.PreviewInfo preview = IsMedia(Path.GetExtension(filePath))
? new Result.PreviewInfo { IsMedia = true, PreviewImagePath = filePath, }
: Result.PreviewInfo.Default;
bool isMedia = IsMedia(Path.GetExtension(filePath));
var title = Path.GetFileName(filePath);
@ -243,7 +280,12 @@ namespace Flow.Launcher.Plugin.Explorer.Search
Title = title,
SubTitle = Path.GetDirectoryName(filePath),
IcoPath = filePath,
Preview = preview,
Preview = new Result.PreviewInfo
{
IsMedia = isMedia,
PreviewImagePath = isMedia ? filePath : null,
FilePath = filePath,
},
AutoCompleteText = GetAutoCompleteText(title, query, filePath, ResultType.File),
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData,
Score = score,
@ -251,6 +293,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search
PreviewPanel = new Lazy<UserControl>(() => new PreviewPanel(Settings, filePath)),
Action = c =>
{
if (c.SpecialKeyState.ToModifierKeys() == ModifierKeys.Alt)
{
ShowNativeContextMenu(filePath, ResultType.File);
return false;
}
try
{
if (c.SpecialKeyState.ToModifierKeys() == (ModifierKeys.Control | ModifierKeys.Shift))