Merge branch 'dev' into 240605-Fix-RightTop-Position

This commit is contained in:
DB P 2024-06-19 10:53:57 +09:00 committed by GitHub
commit 05cea15d5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1094 additions and 125 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

31
.github/pr-labeler.yml vendored Normal file
View file

@ -0,0 +1,31 @@
# The bot always updates the labels, add/remove as necessary [default: false]
alwaysReplace: false
# Treats the text and labels as case sensitive [default: true]
caseSensitive: false
# Array of labels to be applied to the PR [default: []]
customLabels:
# Finds the `text` within the PR title and body and applies the `label`
- text: 'bug'
label: 'bug'
- text: 'fix'
label: 'bug'
- text: 'dependabot'
label: 'bug'
- text: 'New Crowdin updates'
label: 'bug'
- text: 'New Crowdin updates'
label: 'kind/i18n'
- text: 'feature'
label: 'enhancement'
- text: 'add new'
label: 'enhancement'
- text: 'refactor'
label: 'enhancement'
- text: 'refactor'
label: 'Code Refactor'
# Search the body of the PR for the `text` [default: true]
searchBody: true
# Search the title of the PR for the `text` [default: true]
searchTitle: true
# Search for whole words only [default: false]
wholeWords: false

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;
@ -90,6 +90,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,
};
}
}

View file

@ -95,7 +95,6 @@
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
<PackageReference Include="NHotkey.Wpf" Version="2.1.1" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
<PackageReference Include="SharpVectors" Version="1.8.2" />
<PackageReference Include="VirtualizingWrapPanel" Version="1.5.8" />
</ItemGroup>

View file

@ -6,8 +6,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
Name="FlowMainWindow"
@ -427,7 +425,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 +437,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

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

View file

@ -62,13 +62,13 @@
<StackPanel
x:Name="HotkeyArea"
Grid.Column="2"
Margin="0,0,10,0"
Margin="0 0 10 0"
VerticalAlignment="Center"
Visibility="{Binding ShowOpenResultHotkey}">
<TextBlock
x:Name="Hotkey"
Margin="12,0,12,0"
Padding="0,0,0,0"
Margin="12 0 12 0"
Padding="0 0 0 0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{DynamicResource ItemHotkeyStyle}">
@ -97,17 +97,18 @@
<Border
Grid.Column="1"
Margin="9,0,0,0"
Margin="9 0 0 0"
BorderBrush="Transparent"
BorderThickness="1">
<Image
x:Name="ImageIcon"
Margin="0,0,0,0"
Margin="0 0 0 0"
HorizontalAlignment="Center"
IsHitTestVisible="False"
RenderOptions.BitmapScalingMode="Fant"
Source="{Binding Image, TargetNullValue={x:Null}}"
Stretch="Uniform"
StretchDirection="DownOnly"
Style="{DynamicResource ImageIconStyle}"
Visibility="{Binding ShowIcon}">
<Image.Clip>
@ -130,7 +131,7 @@
</Border>
<Border
Grid.Column="1"
Margin="9,0,0,0"
Margin="9 0 0 0"
BorderBrush="Transparent"
BorderThickness="0">
<TextBlock
@ -146,7 +147,7 @@
<Grid
Grid.Column="1"
Margin="6,0,10,0"
Margin="6 0 10 0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<Grid.RowDefinitions>
@ -172,7 +173,7 @@
<TextBlock
x:Name="Title"
Grid.Row="0"
Margin="0,0,0,1"
Margin="0 0 0 1"
VerticalAlignment="Bottom"
DockPanel.Dock="Left"
FontSize="{Binding Settings.ResultItemFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
@ -192,7 +193,7 @@
<TextBlock
x:Name="SubTitle"
Grid.Row="1"
Margin="0,1,0,0"
Margin="0 1 0 0"
VerticalAlignment="Top"
FontSize="{Binding Settings.ResultSubItemFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="False"
@ -221,9 +222,10 @@
<!-- http://stackoverflow.com/questions/16819577/setting-background-color-or-wpf-4-0-listbox-windows-8/#16820062 -->
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Height" Value="{Binding Settings.ItemHeightSize}" />
<Setter Property="Visibility" Value="Visible" />
<EventSetter Event="MouseEnter" Handler="OnMouseEnter" />
<EventSetter Event="MouseMove" Handler="OnMouseMove" />
<Setter Property="Height" Value="{Binding Settings.ItemHeightSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="BorderThickness" Value="0" />
@ -256,4 +258,4 @@
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</ListBox>

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

@ -9,6 +9,7 @@ using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Plugin.Explorer.Search;
using Flow.Launcher.Plugin.Explorer.Search.QuickAccessLinks;
using System.Linq;
using Flow.Launcher.Plugin.Explorer.Helper;
using MessageBox = System.Windows.Forms.MessageBox;
using MessageBoxIcon = System.Windows.Forms.MessageBoxIcon;
using MessageBoxButton = System.Windows.Forms.MessageBoxButtons;
@ -252,6 +253,45 @@ namespace Flow.Launcher.Plugin.Explorer
IcoPath = Constants.DifferentUserIconImagePath,
Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\ue748"),
});
if (record.Type is ResultType.File or ResultType.Folder && Settings.ShowInlinedWindowsContextMenu)
{
var includedItems = Settings
.WindowsContextMenuIncludedItems
.Replace("\r", "")
.Split("\n")
.Where(v => !string.IsNullOrWhiteSpace(v))
.ToArray();
var excludedItems = Settings
.WindowsContextMenuExcludedItems
.Replace("\r", "")
.Split("\n")
.Where(v => !string.IsNullOrWhiteSpace(v))
.ToArray();
var menuItems = ShellContextMenuDisplayHelper
.GetContextMenuWithIcons(record.FullPath)
.Where(contextMenuItem =>
(includedItems.Length == 0 || includedItems.Any(filter =>
contextMenuItem.Label.Contains(filter, StringComparison.OrdinalIgnoreCase)
)) &&
(excludedItems.Length == 0 || !excludedItems.Any(filter =>
contextMenuItem.Label.Contains(filter, StringComparison.OrdinalIgnoreCase)
))
);
foreach (var menuItem in menuItems)
{
contextMenus.Add(new Result
{
Title = menuItem.Label,
Icon = () => menuItem.Icon,
Action = _ =>
{
ShellContextMenuDisplayHelper.ExecuteContextMenuItem(record.FullPath, menuItem.CommandId);
return true;
}
});
}
}
}
return contextMenus;

View file

@ -0,0 +1,524 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Flow.Launcher.Plugin.Explorer.Helper;
public static class ShellContextMenuDisplayHelper
{
#region DllImport
[DllImport("shell32.dll")]
private static extern Int32 SHGetMalloc(out IntPtr hObject);
[DllImport("shell32.dll")]
private static extern Int32 SHParseDisplayName(
[MarshalAs(UnmanagedType.LPWStr)] string pszName,
IntPtr pbc,
out IntPtr ppidl,
UInt32 sfgaoIn,
out UInt32 psfgaoOut
);
[DllImport("shell32.dll")]
private static extern Int32 SHBindToParent(
IntPtr pidl,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
out IntPtr ppv,
ref IntPtr ppidlLast
);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreatePopupMenu();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool DestroyMenu(IntPtr hMenu);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern uint GetMenuItemCount(IntPtr hMenu);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern uint GetMenuString(
IntPtr hMenu, uint uIDItem, StringBuilder lpString, int nMaxCount, uint uFlag
);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetSubMenu(IntPtr hMenu, int nPos);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool GetMenuItemInfo(IntPtr hMenu, uint uItem, bool fByPosition, ref MENUITEMINFO lpmii);
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
#endregion
#region Constants
private const uint ContextMenuStartId = 0x0001;
private const uint ContextMenuEndId = 0x7FFF;
private static readonly string[] IgnoredContextMenuCommands =
{
// We haven't managed to make these work, so we don't display them in the context menu.
"Share",
"Windows.ModernShare",
"PinToStartScreen",
"CopyAsPath",
// Hide functionality provided by the Explorer plugin itself
"Copy",
"Delete"
};
#endregion
#region Enums
[Flags]
enum ContextMenuFlags : uint
{
Normal = 0x00000000,
DefaultOnly = 0x00000001,
VerbsOnly = 0x00000002,
Explore = 0x00000004,
NoVerbs = 0x00000008,
CanRename = 0x00000010,
NoDefault = 0x00000020,
IncludeStatic = 0x00000040,
ItemMenu = 0x00000080,
ExtendedVerbs = 0x00000100,
DisabledVerbs = 0x00000200,
AsyncVerbState = 0x00000400,
OptimizeForInvoke = 0x00000800,
SyncCascadeMenu = 0x00001000,
DoNotPickDefault = 0x00002000,
Reserved = 0xffff0000
}
[Flags]
enum ContextMenuInvokeCommandFlags : uint
{
Icon = 0x00000010,
Hotkey = 0x00000020,
FlagNoUi = 0x00000400,
Unicode = 0x00004000,
NoConsole = 0x00008000,
AsyncOk = 0x00100000,
NoZoneChecks = 0x00800000,
ShiftDown = 0x10000000,
ControlDown = 0x40000000,
FlagLogUsage = 0x04000000,
PointInvoke = 0x20000000
}
[Flags]
enum MenuItemInformationMask : uint
{
Bitmap = 0x00000080,
Checkmarks = 0x00000008,
Data = 0x00000020,
Ftype = 0x00000100,
Id = 0x00000002,
State = 0x00000001,
String = 0x00000040,
Submenu = 0x00000004,
Type = 0x00000010
}
enum MenuItemFtype : uint
{
Bitmap = 0x00000004,
MenuBarBreak = 0x00000020,
MenuBreak = 0x00000040,
OwnerDraw = 0x00000100,
RadioCheck = 0x00000200,
RightJustify = 0x00004000,
RightOrder = 0x00002000,
Separator = 0x00000800,
String = 0x00000000,
}
enum GetCommandStringFlags : uint
{
VerbA = 0x00000000,
HelpTextA = 0x00000001,
ValidateA = 0x00000002,
Unicode = VerbW,
Verb = VerbW,
VerbW = 0x00000004,
HelpText = HelpTextW,
HelpTextW = 0x00000005,
Validate = ValidateW,
ValidateW = 0x00000006,
VerbIconW = 0x00000014
}
#endregion
private static IMalloc GetMalloc()
{
SHGetMalloc(out var pMalloc);
return (IMalloc)Marshal.GetTypedObjectForIUnknown(pMalloc, typeof(IMalloc));
}
public static void ExecuteContextMenuItem(string fileName, uint menuItemId)
{
IMalloc malloc = null;
IntPtr originalPidl = IntPtr.Zero;
IntPtr pShellFolder = IntPtr.Zero;
IntPtr pContextMenu = IntPtr.Zero;
IntPtr hMenu = IntPtr.Zero;
IContextMenu contextMenu = null;
IShellFolder shellFolder = null;
try
{
malloc = GetMalloc();
var hr = SHParseDisplayName(fileName, IntPtr.Zero, out var pidl, 0, out _);
if (hr != 0) throw new Exception("SHParseDisplayName failed");
originalPidl = pidl;
var guid = typeof(IShellFolder).GUID;
hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);
if (hr != 0) throw new Exception("SHBindToParent failed");
shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
hr = shellFolder.GetUIObjectOf(
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu
);
if (hr != 0) throw new Exception("GetUIObjectOf failed");
contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
hMenu = CreatePopupMenu();
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Explore);
var directory = Path.GetDirectoryName(fileName);
var invokeCommandInfo = new CMINVOKECOMMANDINFO
{
cbSize = (uint)Marshal.SizeOf(typeof(CMINVOKECOMMANDINFO)),
fMask = (uint)ContextMenuInvokeCommandFlags.Unicode,
hwnd = IntPtr.Zero,
lpVerb = (IntPtr)(menuItemId - ContextMenuStartId),
lpParameters = null,
lpDirectory = null,
nShow = 1,
hIcon = IntPtr.Zero,
};
hr = contextMenu.InvokeCommand(ref invokeCommandInfo);
if (hr != 0)
{
throw new Exception($"InvokeCommand failed with code {hr:X}");
}
}
finally
{
if (hMenu != IntPtr.Zero)
DestroyMenu(hMenu);
if (contextMenu != null)
Marshal.ReleaseComObject(contextMenu);
if (pContextMenu != IntPtr.Zero)
Marshal.Release(pContextMenu);
if (shellFolder != null)
Marshal.ReleaseComObject(shellFolder);
if (pShellFolder != IntPtr.Zero)
Marshal.Release(pShellFolder);
if (originalPidl != IntPtr.Zero)
malloc?.Free(originalPidl);
if (malloc != null)
Marshal.ReleaseComObject(malloc);
}
}
public static List<ContextMenuItem> GetContextMenuWithIcons(string filePath)
{
IMalloc malloc = null;
IntPtr originalPidl = IntPtr.Zero;
IntPtr pShellFolder = IntPtr.Zero;
IntPtr pContextMenu = IntPtr.Zero;
IntPtr hMenu = IntPtr.Zero;
IShellFolder shellFolder = null;
IContextMenu contextMenu = null;
try
{
malloc = GetMalloc();
var hr = SHParseDisplayName(filePath, IntPtr.Zero, out var pidl, 0, out _);
if (hr != 0) throw new Exception("SHParseDisplayName failed");
originalPidl = pidl;
var guid = typeof(IShellFolder).GUID;
hr = SHBindToParent(pidl, guid, out pShellFolder, ref pidl);
if (hr != 0) throw new Exception("SHBindToParent failed");
shellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pShellFolder, typeof(IShellFolder));
hr = shellFolder.GetUIObjectOf(
IntPtr.Zero, 1, new[] { pidl }, typeof(IContextMenu).GUID, IntPtr.Zero, out pContextMenu
);
if (hr != 0) throw new Exception("GetUIObjectOf failed");
contextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(pContextMenu, typeof(IContextMenu));
// Without waiting, some items, such as "Send to > Documents", don't always appear, which shifts item ids
// even though it shouldn't. Please replace this if you find a better way to fix this bug.
Thread.Sleep(200);
hMenu = CreatePopupMenu();
contextMenu.QueryContextMenu(hMenu, 0, ContextMenuStartId, ContextMenuEndId, (uint)ContextMenuFlags.Explore);
var menuItems = new List<ContextMenuItem>();
ProcessMenuWithIcons(hMenu, contextMenu, menuItems);
return menuItems;
}
finally
{
if (hMenu != IntPtr.Zero)
DestroyMenu(hMenu);
if (contextMenu != null)
Marshal.ReleaseComObject(contextMenu);
if (pContextMenu != IntPtr.Zero)
Marshal.Release(pContextMenu);
if (shellFolder != null)
Marshal.ReleaseComObject(shellFolder);
if (pShellFolder != IntPtr.Zero)
Marshal.Release(pShellFolder);
if (originalPidl != IntPtr.Zero)
malloc?.Free(originalPidl);
if (malloc != null)
Marshal.ReleaseComObject(malloc);
}
}
private static void ProcessMenuWithIcons(IntPtr hMenu, IContextMenu contextMenu, List<ContextMenuItem> menuItems, string prefix = "")
{
uint menuCount = GetMenuItemCount(hMenu);
for (uint i = 0; i < menuCount; i++)
{
var mii = new MENUITEMINFO
{
cbSize = (uint)Marshal.SizeOf(typeof(MENUITEMINFO)),
fMask = (uint)(MenuItemInformationMask.Bitmap | MenuItemInformationMask.Ftype |
MenuItemInformationMask.Submenu | MenuItemInformationMask.Id)
};
GetMenuItemInfo(hMenu, i, true, ref mii);
var menuText = new StringBuilder(256);
uint result = GetMenuString(hMenu, mii.wID, menuText, menuText.Capacity, 0);
if (result == 0 || string.IsNullOrWhiteSpace(menuText.ToString()))
{
continue;
}
menuText.Replace("&", "");
IntPtr hSubMenu = GetSubMenu(hMenu, (int)i);
if (hSubMenu != IntPtr.Zero)
{
ProcessMenuWithIcons(hSubMenu, contextMenu, menuItems, prefix + menuText + " > ");
}
else if (!string.IsNullOrWhiteSpace(menuText.ToString()))
{
var commandBuilder = new StringBuilder(256);
contextMenu.GetCommandString(
mii.wID - ContextMenuStartId,
(uint)GetCommandStringFlags.Verb,
IntPtr.Zero,
commandBuilder,
commandBuilder.Capacity
);
if (IgnoredContextMenuCommands.Contains(commandBuilder.ToString(), StringComparer.OrdinalIgnoreCase))
{
continue;
}
ImageSource icon = null;
if (mii.hbmpItem != IntPtr.Zero)
{
icon = GetBitmapSourceFromHBitmap(mii.hbmpItem);
}
else if (mii.hbmpChecked != IntPtr.Zero)
{
icon = GetBitmapSourceFromHBitmap(mii.hbmpChecked);
}
menuItems.Add(new ContextMenuItem(prefix + menuText, icon, mii.wID));
}
}
}
private static BitmapSource GetBitmapSourceFromHBitmap(IntPtr hBitmap)
{
try
{
var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight(16, 16)
);
if (!DeleteObject(hBitmap))
{
throw new Exception("Failed to delete HBitmap.");
}
return bitmapSource;
}
catch (COMException)
{
// ignore
}
return null;
}
}
#region Data Structures
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E6-0000-0000-C000-000000000046")]
public interface IShellFolder
{
[PreserveSig]
int ParseDisplayName(
IntPtr hwnd, IntPtr pbc, [In, MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, out uint pchEaten,
out IntPtr ppidl, ref uint pdwAttributes
);
[PreserveSig]
int EnumObjects(IntPtr hwnd, uint grfFlags, out IntPtr ppenumIDList);
[PreserveSig]
int BindToObject(IntPtr pidl, IntPtr pbc, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
[PreserveSig]
int BindToStorage(IntPtr pidl, IntPtr pbc, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
[PreserveSig]
int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);
[PreserveSig]
int CreateViewObject(IntPtr hwndOwner, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
[PreserveSig]
int GetAttributesOf(
uint cidl, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] apidl, ref uint rgfInOut
);
[PreserveSig]
int GetUIObjectOf(
IntPtr hwndOwner, uint cidl, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] apidl,
[In, MarshalAs(UnmanagedType.LPStruct)]
Guid riid, IntPtr rgfReserved, out IntPtr ppv
);
[PreserveSig]
int GetDisplayNameOf(IntPtr pidl, uint uFlags, IntPtr pName);
[PreserveSig]
int SetNameOf(
IntPtr hwnd, IntPtr pidl, [In, MarshalAs(UnmanagedType.LPWStr)] string pszName, uint uFlags, out IntPtr ppidlOut
);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000002-0000-0000-C000-000000000046")]
public interface IMalloc
{
[PreserveSig]
IntPtr Alloc(UInt32 cb);
[PreserveSig]
IntPtr Realloc(IntPtr pv, UInt32 cb);
[PreserveSig]
void Free(IntPtr pv);
[PreserveSig]
UInt32 GetSize(IntPtr pv);
[PreserveSig]
Int16 DidAlloc(IntPtr pv);
[PreserveSig]
void HeapMinimize();
}
[StructLayout(LayoutKind.Sequential)]
public struct CMINVOKECOMMANDINFO
{
public uint cbSize;
public uint fMask;
public IntPtr hwnd;
public IntPtr lpVerb;
[MarshalAs(UnmanagedType.LPStr)] public string lpParameters;
[MarshalAs(UnmanagedType.LPStr)] public string lpDirectory;
public int nShow;
public uint dwHotKey;
public IntPtr hIcon;
}
[StructLayout(LayoutKind.Sequential)]
public struct MENUITEMINFO
{
public uint cbSize;
public uint fMask;
public uint fType;
public uint fState;
public uint wID;
public IntPtr hSubMenu;
public IntPtr hbmpChecked;
public IntPtr hbmpUnchecked;
public IntPtr dwItemData;
public IntPtr dwTypeData;
public uint cch;
public IntPtr hbmpItem;
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E4-0000-0000-C000-000000000046")]
public interface IContextMenu
{
[PreserveSig]
int QueryContextMenu(IntPtr hmenu, uint indexMenu, uint idCmdFirst, uint idCmdLast, uint uFlags);
[PreserveSig]
int InvokeCommand(ref CMINVOKECOMMANDINFO pici);
[PreserveSig]
int GetCommandString(uint idcmd, uint uflags, IntPtr reserved, StringBuilder commandstring, int cch);
}
public record ContextMenuItem(string Label, ImageSource Icon, uint CommandId);
#endregion

View file

@ -152,4 +152,9 @@
<system:String x:Key="flowlauncher_plugin_everything_enable_content_search">Do you want to enable content search for Everything?</system:String>
<system:String x:Key="flowlauncher_plugin_everything_enable_content_search_tips">It can be very slow without index (which is only supported in Everything v1.5+)</system:String>
<!-- Native Context Menu -->
<system:String x:Key="plugin_explorer_native_context_menu_header">Native Context Menu</system:String>
<system:String x:Key="plugin_explorer_native_context_menu_display_context_menu">Display native context menu (experimental)</system:String>
<system:String x:Key="plugin_explorer_native_context_menu_include_patterns_guide">Below you can specify items you want to include in the context menu, they can be partial (e.g. 'pen wit') or complete ('Open with').</system:String>
<system:String x:Key="plugin_explorer_native_context_menu_exclude_patterns_guide">Below you can specify items you want to exclude from context menu, they can be partial (e.g. 'pen wit') or complete ('Open with')</system:String>
</ResourceDictionary>

View file

@ -102,6 +102,10 @@ 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)
@ -192,6 +196,10 @@ namespace Flow.Launcher.Plugin.Explorer.Search
Score = 500,
ProgressBar = progressValue,
ProgressBarColor = progressBarColor,
Preview = new Result.PreviewInfo
{
FilePath = path,
},
Action = _ =>
{
OpenFolder(path);
@ -261,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);
@ -275,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,

View file

@ -28,7 +28,11 @@ namespace Flow.Launcher.Plugin.Explorer
public bool UseLocationAsWorkingDir { get; set; } = false;
public bool ShowWindowsContextMenu { get; set; } = true;
public bool ShowInlinedWindowsContextMenu { get; set; } = false;
public string WindowsContextMenuIncludedItems { get; set; } = string.Empty;
public string WindowsContextMenuExcludedItems { get; set; } = string.Empty;
public bool DefaultOpenFolderInFileManager { get; set; } = false;
@ -62,7 +66,7 @@ namespace Flow.Launcher.Plugin.Explorer
public bool ShowModifiedDateInPreviewPanel { get; set; } = true;
public string PreviewPanelDateFormat { get; set; } = "yyyy-MM-dd";
public string PreviewPanelTimeFormat { get; set; } = "HH:mm";
private EverythingSearchManager _everythingManagerInstance;

View file

@ -102,6 +102,40 @@ namespace Flow.Launcher.Plugin.Explorer.ViewModels
#endregion
#region Native Context Menu
public bool ShowWindowsContextMenu
{
get => Settings.ShowInlinedWindowsContextMenu;
set
{
Settings.ShowInlinedWindowsContextMenu = value;
OnPropertyChanged();
}
}
public string WindowsContextMenuIncludedItems
{
get => Settings.WindowsContextMenuIncludedItems;
set
{
Settings.WindowsContextMenuIncludedItems = value;
OnPropertyChanged();
}
}
public string WindowsContextMenuExcludedItems
{
get => Settings.WindowsContextMenuExcludedItems;
set
{
Settings.WindowsContextMenuExcludedItems = value;
OnPropertyChanged();
}
}
#endregion
#region Preview Panel
public bool ShowFileSizeInPreviewPanel

View file

@ -22,7 +22,7 @@
<Border
x:Name="LayoutRoot"
Margin="0,0,0,0"
Margin="0 0 0 0"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<Grid>
@ -105,13 +105,13 @@
</Setter>
</Style>
<DataTemplate x:Key="ListViewTemplateAccessLinks" DataType="qa:AccessLink">
<TextBlock Margin="0,5,0,5" Text="{Binding Path, Mode=OneTime}" />
<TextBlock Margin="0 5 0 5" Text="{Binding Path, Mode=OneTime}" />
</DataTemplate>
<core:TranslationConverter x:Key="TranslationConverter" />
<DataTemplate x:Key="ListViewActionKeywords" DataType="{x:Type views:ActionKeywordModel}">
<Grid>
<TextBlock
Margin="0,5,0,0"
Margin="0 5 0 0"
IsEnabled="{Binding Enabled}"
Text="{Binding Description, Mode=OneTime, Converter={StaticResource TranslationConverter}}">
<TextBlock.Style>
@ -128,7 +128,7 @@
</TextBlock.Style>
</TextBlock>
<TextBlock
Margin="250,5,0,0"
Margin="250 5 0 0"
IsEnabled="{Binding Enabled}"
Text="{Binding Keyword}">
<TextBlock.Style>
@ -160,20 +160,20 @@
Width="Auto"
Header="{DynamicResource plugin_explorer_generalsetting_header}"
Style="{DynamicResource ExplorerTabItem}">
<StackPanel Grid.Row="0" Margin="30,10,0,0">
<StackPanel Grid.Row="0" Margin="30 10 0 0">
<StackPanel>
<StackPanel Orientation="Vertical">
<CheckBox
Margin="0,10,0,0"
Margin="0 10 0 0"
HorizontalAlignment="Left"
Content="{DynamicResource plugin_explorer_use_location_as_working_dir}"
IsChecked="{Binding Settings.UseLocationAsWorkingDir}" />
<CheckBox
Margin="0,10,0,0"
Margin="0 10 0 0"
HorizontalAlignment="Left"
Content="{DynamicResource plugin_explorer_default_open_in_file_manager}"
IsChecked="{Binding Settings.DefaultOpenFolderInFileManager}" />
<StackPanel Margin="0,10,0,10" Orientation="Horizontal">
<StackPanel Margin="0 10 0 10" Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="A" />
@ -188,7 +188,7 @@
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="0,6,16,6"
Margin="0 6 16 6"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_file_editor_path}"
@ -196,7 +196,7 @@
<StackPanel
Grid.Row="0"
Grid.Column="1"
Margin="0,6,6,6"
Margin="0 6 6 6"
Orientation="Horizontal">
<TextBox
Width="250"
@ -206,7 +206,7 @@
TextWrapping="NoWrap" />
<Button
MinWidth="50"
Margin="5,0,0,0"
Margin="5 0 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding OpenFileEditorPath}"
@ -216,7 +216,7 @@
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="0,6,16,6"
Margin="0 6 16 6"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_folder_editor_path}"
@ -224,7 +224,7 @@
<StackPanel
Grid.Row="1"
Grid.Column="1"
Margin="0,6,6,6"
Margin="0 6 6 6"
Orientation="Horizontal">
<TextBox
Width="250"
@ -234,7 +234,7 @@
TextWrapping="NoWrap" />
<Button
MinWidth="50"
Margin="5,0,0,0"
Margin="5 0 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding OpenFolderEditorPath}"
@ -244,7 +244,7 @@
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="0,16,6,6"
Margin="0 16 6 6"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_shell_path}"
@ -255,14 +255,14 @@
Orientation="Horizontal">
<TextBox
Width="250"
Margin="0,10,0,0"
Margin="0 10 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding ShellPath}"
TextWrapping="NoWrap" />
<Button
MinWidth="50"
Margin="5,10,0,0"
Margin="5 10 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding OpenShellPath}"
@ -271,7 +271,7 @@
</Grid>
</StackPanel>
<StackPanel Margin="0,0,0,5" Orientation="Horizontal">
<StackPanel Margin="0 0 0 5" Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="C" />
@ -285,7 +285,7 @@
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="0,0,10,0"
Margin="0 0 10 0"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_Index_Search_Engine}"
TextBlock.Foreground="{DynamicResource Color05B}" />
@ -293,7 +293,7 @@
Grid.Row="0"
Grid.Column="1"
Width="250"
Margin="10,15,10,10"
Margin="10 15 10 10"
HorizontalAlignment="Left"
VerticalAlignment="Center"
DisplayMemberPath="Description"
@ -302,7 +302,7 @@
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="0,0,10,0"
Margin="0 0 10 0"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_Content_Search_Engine}"
TextBlock.Foreground="{DynamicResource Color05B}" />
@ -319,7 +319,7 @@
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="0,0,10,0"
Margin="0 0 10 0"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_Directory_Recursive_Search_Engine}"
TextBlock.Foreground="{DynamicResource Color05B}" />
@ -337,8 +337,8 @@
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button
Margin="0,10,10,20"
Padding="20,10,20,10"
Margin="0 10 10 20"
Padding="20 10 20 10"
Click="btnOpenIndexingOptions_Click"
Content="{DynamicResource plugin_explorer_Open_Window_Index_Option}" />
</StackPanel>
@ -348,50 +348,106 @@
</StackPanel>
</StackPanel>
</TabItem>
<TabItem
Width="Auto"
Header="{DynamicResource plugin_explorer_native_context_menu_header}"
Style="{DynamicResource ExplorerTabItem}">
<StackPanel Margin="30 20 10 10">
<CheckBox
Margin="0 0 0 0"
Content="{DynamicResource plugin_explorer_native_context_menu_display_context_menu}"
IsChecked="{Binding ShowWindowsContextMenu}" />
<Grid Margin="0 10 20 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="0 0 6 6"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_native_context_menu_include_patterns_guide}"
TextWrapping="Wrap" />
<TextBox
Grid.Row="1"
Grid.Column="0"
Height="300"
Margin="0 6 6 0"
AcceptsReturn="True"
Text="{Binding WindowsContextMenuIncludedItems}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="0"
Grid.Column="1"
Margin="6 0 0 6"
Foreground="{DynamicResource Color05B}"
Text="{DynamicResource plugin_explorer_native_context_menu_exclude_patterns_guide}"
TextWrapping="Wrap" />
<TextBox
Grid.Row="1"
Grid.Column="1"
Height="300"
Margin="6 6 0 0"
AcceptsReturn="True"
Text="{Binding WindowsContextMenuExcludedItems}"
TextWrapping="Wrap" />
</Grid>
</StackPanel>
</TabItem>
<TabItem
Width="Auto"
Header="{DynamicResource plugin_explorer_previewpanel_setting_header}"
Style="{DynamicResource ExplorerTabItem}">
<StackPanel Margin="30,20,0,10">
<StackPanel Margin="30 20 0 10">
<TextBlock Foreground="{DynamicResource Color05B}" Text="{DynamicResource plugin_explorer_previewpanel_file_info_label}" />
<CheckBox
Margin="0,10,0,0"
Margin="0 10 0 0"
Content="{DynamicResource plugin_explorer_previewpanel_display_file_size_checkbox}"
IsChecked="{Binding ShowFileSizeInPreviewPanel}" />
<CheckBox
Margin="0,10,0,0"
Margin="0 10 0 0"
Content="{DynamicResource plugin_explorer_previewpanel_display_file_creation_checkbox}"
IsChecked="{Binding ShowCreatedDateInPreviewPanel}" />
<CheckBox
Margin="0,10,0,0"
Margin="0 10 0 0"
Content="{DynamicResource plugin_explorer_previewpanel_display_file_modification_checkbox}"
IsChecked="{Binding ShowModifiedDateInPreviewPanel}" />
<StackPanel
Margin="0,20,0,0"
Margin="0 20 0 0"
IsEnabled="{Binding ShowPreviewPanelDateTimeChoices}"
Visibility="{Binding PreviewPanelDateTimeChoicesVisibility}">
<TextBlock Foreground="{DynamicResource Color05B}" Text="{DynamicResource plugin_explorer_previewpanel_date_and_time_format_label}" />
<StackPanel Margin="0,10,0,0" Orientation="Horizontal">
<StackPanel Margin="0 10 0 0" Orientation="Horizontal">
<ComboBox
Width="200"
ItemsSource="{Binding DateFormatList}"
SelectedItem="{Binding PreviewPanelDateFormat}" />
<TextBlock
Margin="12,0,0,0"
Margin="12 0 0 0"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{Binding PreviewPanelDateFormatDemo}" />
</StackPanel>
<StackPanel Margin="0,10,0,10" Orientation="Horizontal">
<StackPanel Margin="0 10 0 10" Orientation="Horizontal">
<ComboBox
Width="200"
ItemsSource="{Binding TimeFormatList}"
SelectedItem="{Binding PreviewPanelTimeFormat}" />
<TextBlock
Margin="12,0,0,0"
Margin="12 0 0 0"
VerticalAlignment="Center"
Foreground="{DynamicResource Color05B}"
Text="{Binding PreviewPanelTimeFormatDemo}" />
@ -405,12 +461,12 @@
Style="{DynamicResource ExplorerTabItem}">
<StackPanel Margin="10" Orientation="Vertical">
<CheckBox
Margin="20,10,0,0"
Margin="20 10 0 0"
HorizontalAlignment="Left"
Content="{DynamicResource flowlauncher_plugin_everything_search_fullpath}"
IsChecked="{Binding Settings.EverythingSearchFullPath}" />
<StackPanel Orientation="Horizontal">
<Grid Margin="20,10,0,10">
<Grid Margin="20 10 0 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="F" />
<ColumnDefinition Width="Auto" />
@ -423,7 +479,7 @@
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="0,0,20,0"
Margin="0 0 20 0"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_everything_sort_option}"
TextBlock.Foreground="{DynamicResource Color05B}" />
@ -447,7 +503,7 @@
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="0,15,20,0"
Margin="0 15 20 0"
VerticalAlignment="Center"
Text="{DynamicResource plugin_explorer_everything_installed_path}"
TextBlock.Foreground="{DynamicResource Color05B}" />
@ -455,14 +511,14 @@
Grid.Row="2"
Grid.Column="1"
MinWidth="350"
Margin="0,15,0,0"
Margin="0 15 0 0"
Text="{Binding EverythingInstalledPath}" />
</Grid>
</StackPanel>
<StackPanel>
<TextBlock
Name="tbFastSortWarning"
Margin="20,10,10,10"
Margin="20 10 10 10"
VerticalAlignment="Center"
DockPanel.Dock="Right"
Foreground="Orange"
@ -479,7 +535,7 @@
Style="{DynamicResource ExplorerTabItem}">
<DockPanel HorizontalAlignment="Stretch">
<Border
Margin="10,10,10,5"
Margin="10 10 10 5"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="1"
DockPanel.Dock="Top">
@ -508,7 +564,7 @@
<ScrollViewer>
<DockPanel HorizontalAlignment="Stretch">
<Border
Margin="10,10,10,5"
Margin="10 10 10 5"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="1"
DockPanel.Dock="Top">
@ -556,7 +612,7 @@
<ScrollViewer>
<DockPanel HorizontalAlignment="Stretch">
<Border
Margin="10,10,10,5"
Margin="10 10 10 5"
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
BorderThickness="1"
DockPanel.Dock="Top">