Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Files.Controls/Omnibar/Omnibar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,17 @@ internal protected void ChangeTextBoxText(string text)
}
}

internal void ChangeTextBoxTextFromMode(string text)
{
if (string.Equals(_textBox.Text, text, StringComparison.Ordinal))
{
return;
}

_textChangeReason = OmnibarTextChangeReason.ProgrammaticChange;
ChangeTextBoxText(text);
}

private void SubmitQuery(object? item)
{
if (CurrentSelectedMode is null)
Expand Down
5 changes: 3 additions & 2 deletions src/Files.Controls/Omnibar/OmnibarMode.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ public partial class OmnibarMode
partial void OnTextChanged(string? newValue)
{
if (_ownerRef is null || _ownerRef.TryGetTarget(out var owner) is false)

{
return;
}

owner.ChangeTextBoxText(newValue ?? string.Empty);
owner.ChangeTextBoxTextFromMode(newValue ?? string.Empty);
}
}
}
12 changes: 9 additions & 3 deletions src/Files.Core/Browsing/BrowseSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Files.Core.Browsing;
public sealed class BrowseSession : IBrowseSession, IBrowsePrefetchTarget, IInteractiveBrowseSession
{
private const int InitialEnumerationBatchSize = 32;
private const int SearchInitialEnumerationBatchSize = 1;
private const int EnumerationBatchSize = 256;
private const int MaximumEnumerationBatchSize = 1024;
private static readonly TimeSpan PropertySortDebounce = TimeSpan.FromMilliseconds(150);
Expand Down Expand Up @@ -178,8 +179,8 @@ private async ValueTask NavigateCoreAsync(BrowseLocation location, nint ownerWin

await nextContext.StartAsync(cancellationToken).ConfigureAwait(false);
nextProjection = new BrowseItemProjection(nextViewSettings, _presentationStore.GetSortPropertyValue);
var pendingBatch = new List<IStorableModel>(InitialEnumerationBatchSize);
var targetBatchSize = InitialEnumerationBatchSize;
var targetBatchSize = location is SearchLocation ? SearchInitialEnumerationBatchSize : InitialEnumerationBatchSize;
var pendingBatch = new List<IStorableModel>(targetBatchSize);
var firstItemReturned = false;
CoreDiagnosticLog.Write("BrowseSession", $"Enumeration START generation={generation} elapsedMs={Stopwatch.GetElapsedTime(navigationStartTimestamp).TotalMilliseconds:F1}");
var nextItemSequence = ownerWindowHandle is not 0 && nextLocationContext is IInteractiveBrowseLocationContext interactiveContext
Expand Down Expand Up @@ -214,7 +215,12 @@ private async ValueTask NavigateCoreAsync(BrowseLocation location, nint ownerWin
: await SortInitialEnumerationBatchAsync(nextLocationContext, nextProjection, pendingBatch, nextViewSettings, cancellationToken).ConfigureAwait(false);
PublishEnumerationBatch(location, nextViewSettings, nextContext, nextProjection, batchToPublish, ref previousState, ref enumerationActivated);
pendingBatch.Clear();
targetBatchSize = Math.Min(MaximumEnumerationBatchSize, enumerationActivated && targetBatchSize is InitialEnumerationBatchSize ? EnumerationBatchSize : checked(targetBatchSize * 2));
targetBatchSize = targetBatchSize switch
{
SearchInitialEnumerationBatchSize => InitialEnumerationBatchSize,
InitialEnumerationBatchSize => EnumerationBatchSize,
_ => Math.Min(MaximumEnumerationBatchSize, checked(targetBatchSize * 2)),
};
await Task.Yield();
}
CoreDiagnosticLog.Write("BrowseSession", $"Enumeration END generation={generation} items={nextItems.Count} elapsedMs={Stopwatch.GetElapsedTime(navigationStartTimestamp).TotalMilliseconds:F1}");
Expand Down
2 changes: 1 addition & 1 deletion src/Files.Core/Browsing/FolderBrowseLocationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Files.Core.Browsing;
/// <summary>
/// Keeps a resolved folder model alive for the duration of a browse location.
/// </summary>
public sealed class FolderBrowseLocationContext : IBrowseLocationContext, IBrowseLocationItemResolver, IBrowseLocationItemSorter, IInteractiveBrowseLocationContext
public sealed class FolderBrowseLocationContext : IBrowseLocationContext, IBrowseLocationItemResolver, IBrowseLocationItemSorter, IInteractiveBrowseLocationContext, IWindowsShellColumnProvider
{
private readonly FolderLocation _location;

Expand Down
13 changes: 13 additions & 0 deletions src/Files.Core/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,19 @@ SafeArrayGetDim
SafeArrayGetLBound
SafeArrayGetUBound
IObjectWithSite
IQueryContinue
ICondition
IConditionFactory
IConditionFactory2
ISchemaProvider
IEntity
ITokenCollection
QUERY_PARSER_MANAGER_OPTION
STRUCTURED_QUERY_MULTIOPTION
STRUCTURED_QUERY_SINGLE_OPTION
ISearchFolderItemFactory
QueryParserManager
SearchFolderItemFactory
IOpenControlPanel
OpenControlPanel
SharingConfigurationManager
Expand Down
53 changes: 48 additions & 5 deletions src/Files.Core/Sessions/BrowsePaneSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ public enum PaneNavigationMode

/// <summary>Replaces the current history entry.</summary>
Replace,

/// <summary>Adds the search target unless the current committed entry is another search, which it replaces.</summary>
UpdateSearch,

/// <summary>Moves back to a matching search origin when possible; otherwise replaces the current entry.</summary>
ExitSearch,
}

/// <summary>
Expand Down Expand Up @@ -89,7 +95,7 @@ public async ValueTask NavigateAsync(BrowseLocation location, PaneNavigationMode
{
ArgumentNullException.ThrowIfNull(location);

if (mode is not PaneNavigationMode.Push and not PaneNavigationMode.Replace)
if (mode is not PaneNavigationMode.Push and not PaneNavigationMode.Replace and not PaneNavigationMode.UpdateSearch and not PaneNavigationMode.ExitSearch)
{
throw new ArgumentOutOfRangeException(nameof(mode));
}
Expand All @@ -100,10 +106,7 @@ public async ValueTask NavigateAsync(BrowseLocation location, PaneNavigationMode
try
{
EnsureActive();
await NavigateAndCommitAsync(location, () =>
{
if (mode is PaneNavigationMode.Push) { History.Push(location); } else { History.Replace(location); }
}, ownerWindowHandle, navigation.Token).ConfigureAwait(false);
await NavigateAndCommitAsync(location, () => CommitNavigationHistory(location, mode), ownerWindowHandle, navigation.Token).ConfigureAwait(false);
}
finally
{
Expand Down Expand Up @@ -294,6 +297,46 @@ public ValueTask DisposeAsync()
}
}

private void CommitNavigationHistory(BrowseLocation location, PaneNavigationMode mode)
{
if (mode is PaneNavigationMode.Push)
{
History.Push(location);

return;
}

if (mode is PaneNavigationMode.Replace)
{
History.Replace(location);

return;
}

if (mode is PaneNavigationMode.UpdateSearch)
{
if (History.Current is SearchLocation)
{
History.Replace(location);
}
else
{
History.Push(location);
}

return;
}

if (History.Current is SearchLocation && History.TryGetBack(out var previous, out var targetIndex) && Equals(previous, location))
{
History.TryMoveTo(targetIndex, location);

return;
}

History.Replace(location);
}

private async Task NavigateAndCommitAsync(BrowseLocation target, Action commitHistory, nint ownerWindowHandle, CancellationToken cancellationToken)
{
var previousGeneration = BrowseSession.Generation;
Expand Down
152 changes: 152 additions & 0 deletions src/Files.Core/Windows/Interop/Extras.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.Win32.SafeHandles;
using Windows.Win32.Foundation;
using Windows.Win32.System.Com;
using Windows.Win32.System.Com.Urlmon;
using Windows.Win32.System.Com.StructuredStorage;
using Windows.Win32.System.IO;
using Windows.Win32.System.Search.Common;
using Windows.Win32.UI.Shell;
using Windows.Win32.UI.Shell.Common;
using Windows.Win32.UI.Shell.PropertiesSystem;
Expand Down Expand Up @@ -99,3 +101,153 @@ internal static partial NTSTATUS NtFsControlFile(SafeFileHandle fileHandle, Safe
nint inputBuffer, uint inputBufferLength, ref byte outputBuffer, uint outputBufferLength);
}
}

namespace Windows.Win32.System.Com
{
/// <summary>Provides a strongly typed query-continuation service to Windows Shell enumerators.</summary>
[GeneratedComInterface(Options = ComInterfaceOptions.ManagedObjectWrapper)]
[Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal partial interface IQueryContinueServiceProvider
{
/// <summary>Returns the supported query-continuation service.</summary>
/// <param name="serviceId">The requested service identifier.</param>
/// <param name="interfaceId">The requested interface identifier.</param>
/// <param name="service">Receives the query-continuation service.</param>
/// <returns>The HRESULT describing whether the service is available.</returns>
[PreserveSig]
HRESULT QueryService(in Guid serviceId, in Guid interfaceId, out IQueryContinue? service);
}
}

namespace Windows.Win32.System.Search
{
/// <summary>Creates configured Windows Structured Query parsers.</summary>
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16, Options = ComInterfaceOptions.ComObjectWrapper)]
[Guid("A879E3C4-AF77-44FB-8F37-EBD1487CF920")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal partial interface IQueryParserManager
{
/// <summary>Creates and loads a parser for a catalog and keyword language.</summary>
/// <param name="catalog">The catalog name.</param>
/// <param name="keywordLanguage">The keyword language identifier.</param>
/// <param name="interfaceId">The requested parser interface identifier.</param>
/// <param name="queryParser">Receives the parser.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT CreateLoadedParser(string catalog, ushort keywordLanguage, in Guid interfaceId, out IQueryParser? queryParser);

/// <summary>Initializes natural-query and wildcard options on a parser.</summary>
/// <param name="understandNaturalQuerySyntax">Whether natural query syntax is enabled.</param>
/// <param name="automaticWildcard">Whether automatic wildcard matching is enabled.</param>
/// <param name="queryParser">The parser to initialize.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT InitializeOptions(BOOL understandNaturalQuerySyntax, BOOL automaticWildcard, IQueryParser? queryParser);

/// <summary>Sets a parser-manager option.</summary>
/// <param name="option">The option to set.</param>
/// <param name="value">The option value.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT SetOption(QUERY_PARSER_MANAGER_OPTION option, in PROPVARIANT value);
}

/// <summary>Parses Windows Structured Query input.</summary>
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16, Options = ComInterfaceOptions.ComObjectWrapper)]
[Guid("2EBDEE67-3505-43F8-9946-EA44ABC8E5B0")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal partial interface IQueryParser
{
/// <summary>Parses query text into a query solution.</summary>
/// <param name="input">The query text.</param>
/// <param name="customProperties">The optional custom-property enumerator.</param>
/// <param name="solution">Receives the query solution.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT Parse(string input, IEnumUnknown? customProperties, out IQuerySolution? solution);

/// <summary>Sets a single parser option.</summary>
/// <param name="option">The option to set.</param>
/// <param name="value">The option value.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT SetOption(STRUCTURED_QUERY_SINGLE_OPTION option, in PROPVARIANT value);

/// <summary>Gets a single parser option.</summary>
/// <param name="option">The option to get.</param>
/// <param name="value">Receives the option value.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT GetOption(STRUCTURED_QUERY_SINGLE_OPTION option, out PROPVARIANT value);

/// <summary>Sets a keyed parser option.</summary>
/// <param name="option">The multi-option to set.</param>
/// <param name="optionKey">The option key.</param>
/// <param name="value">The option value.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT SetMultiOption(STRUCTURED_QUERY_MULTIOPTION option, string optionKey, in PROPVARIANT value);

/// <summary>Gets the parser schema provider.</summary>
/// <param name="schemaProvider">Receives the schema provider.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT GetSchemaProvider(out ISchemaProvider? schemaProvider);

/// <summary>Restates a condition as query text.</summary>
/// <param name="condition">The optional condition.</param>
/// <param name="useEnglish">Whether to use English keywords.</param>
/// <param name="queryString">Receives the allocated query text.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT RestateToString(ICondition? condition, BOOL useEnglish, out PWSTR queryString);

/// <summary>Parses a value for a named property.</summary>
/// <param name="propertyName">The canonical property name.</param>
/// <param name="input">The property-value text.</param>
/// <param name="solution">Receives the query solution.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT ParsePropertyValue(string propertyName, string input, out IQuerySolution? solution);

/// <summary>Restates a property condition as property and query text.</summary>
/// <param name="condition">The optional condition.</param>
/// <param name="useEnglish">Whether to use English keywords.</param>
/// <param name="propertyName">Receives the allocated property name.</param>
/// <param name="queryString">Receives the allocated query text.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT RestatePropertyValueToString(ICondition? condition, BOOL useEnglish, out PWSTR propertyName, out PWSTR queryString);
}

/// <summary>Contains a parsed Structured Query condition and diagnostics.</summary>
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16, Options = ComInterfaceOptions.ComObjectWrapper)]
[Guid("D6EBC66B-8921-4193-AFDD-A1789FB7FF57")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal unsafe partial interface IQuerySolution : IConditionFactory
{
/// <summary>Gets the parsed query condition and optional main entity type.</summary>
/// <param name="queryNode">Receives the query condition.</param>
/// <param name="mainType">Receives the optional main entity type.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT GetQuery(out ICondition? queryNode, out IEntity? mainType);

/// <summary>Gets parse errors through a requested enumerator interface.</summary>
/// <param name="interfaceId">The requested parse-error interface identifier.</param>
/// <param name="parseErrors">Receives the parse-error interface.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT GetErrors(in Guid interfaceId, [MarshalAs(UnmanagedType.Interface)] out object? parseErrors);

/// <summary>Gets the lexical data retained by the parser.</summary>
/// <param name="inputString">Receives the allocated input text.</param>
/// <param name="tokens">Receives the token collection.</param>
/// <param name="locale">Receives the input locale identifier.</param>
/// <param name="wordBreaker">Receives the word breaker.</param>
/// <returns>The HRESULT returned by Structured Query.</returns>
[PreserveSig]
HRESULT GetLexicalData(out PWSTR inputString, out ITokenCollection? tokens, out uint locale, [MarshalAs(UnmanagedType.Interface)] out object? wordBreaker);
}
}
9 changes: 9 additions & 0 deletions src/Files.Core/Windows/Items/WindowsShellItemResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ public Task<T> InvokeConcurrentAsync<T>(WindowsItemLocator locator, Func<IShellI
return _scheduler.InvokeConcurrentAsync(() => InvokeCore(locator, action), cancellationToken);
}

internal Task<T> InvokeSearchAsync<T>(WindowsItemLocator locator, Func<IShellItem, T> action, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(locator);

ArgumentNullException.ThrowIfNull(action);

return _scheduler.InvokeSearchAsync(() => InvokeCore(locator, action), cancellationToken);
}

public Task<T> InvokeOperationAsync<T>(WindowsItemLocator locator, Func<IShellItem, T> action, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(locator);
Expand Down
5 changes: 4 additions & 1 deletion src/Files.Core/Windows/Items/WindowsStorableDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ namespace Files.Core.Windows;
/// <summary>
/// Describes a Shell item without retaining an apartment-bound COM object.
/// </summary>
internal sealed record WindowsStorableDescriptor(string ItemId, StorageAddress Address, WindowsItemLocator Locator, WindowsStorableSnapshot Snapshot);
internal sealed record WindowsStorableDescriptor(string ItemId, StorageAddress Address, WindowsItemLocator Locator, WindowsStorableSnapshot Snapshot)
{
internal bool IsSearchFolder { get; init; }
}
Loading
Loading