Skip to content
Open
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
5 changes: 5 additions & 0 deletions src/Files.App.Controls/Sidebar/ISidebarItemModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,10 @@ public interface ISidebarItemModel : INotifyPropertyChanged
/// Expansion participant that keeps the regular row appearance (icon + normal text) instead of the section-header style.
/// </summary>
bool IsLeafWithChildren => false;

/// <summary>
/// Indicates whether the item supports reorder dropping.
/// </summary>
bool IsReorderDropItem => false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CanBeReordered?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yair100 This is the kind of behavior I was trying to avoid with Center Drop => Create Shortcut.

}
}
188 changes: 122 additions & 66 deletions src/Files.App.Controls/Sidebar/SidebarItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Automation.Peers;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Runtime.InteropServices;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;

Expand All @@ -31,6 +36,7 @@ public sealed partial class SidebarItem : Control
private bool isTemplateWired;
private DispatcherQueueTimer? dragOverTimer;
private DispatcherQueueTimer? dragOverExpandTimer;
private SidebarItemDropPosition lastDropPosition = SidebarItemDropPosition.Center;

public SidebarItem()
{
Expand Down Expand Up @@ -126,7 +132,17 @@ public void HandleItemChange()
HookupItemChangeListener(null, Item);
UpdateExpansionState();
ReevaluateSelection();
CanDrag = Item?.Path is string path && Path.IsPathRooted(path);

if (Item is not null)
{
CanDrag = IsValidDropPath(Item.Path);
UseReorderDrop = !IsGroupHeader && CanDrag && Item.IsReorderDropItem;
}
else
{
CanDrag = false;
UseReorderDrop = false;
}
}

private void HookupOwners()
Expand Down Expand Up @@ -190,32 +206,36 @@ private void Item_PropertyChanged(object? sender, PropertyChangedEventArgs e)
}
}

private static bool IsValidDropPath(string? path)
=> path is not null && (System.IO.Path.IsPathRooted(path) || path.StartsWith("Shell:", StringComparison.OrdinalIgnoreCase));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not put system specific code inside Files.App.Controls. It should be done within Files.App. Sidebar reordering should work in any other scenario.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0x5bfa Thanks for the review, I will work on it this week.


private void SidebarItem_DragStarting(UIElement sender, DragStartingEventArgs args)
{
if (Item?.Path is not string dragPath || !Path.IsPathRooted(dragPath))
if (Item?.Path is not string dragPath || !IsValidDropPath(dragPath))
return;

args.Data.SetData(StandardDataFormats.Text, dragPath);
args.Data.RequestedOperation = DataPackageOperation.Move | DataPackageOperation.Copy | DataPackageOperation.Link;
args.Data.SetDataProvider(StandardDataFormats.StorageItems, async request =>
SafetyExtensions.IgnoreExceptions(() =>
{
var deferral = request.GetDeferral();
try
args.Data.SetData(StandardDataFormats.Text, dragPath);
args.Data.RequestedOperation = DataPackageOperation.Move | DataPackageOperation.Copy | DataPackageOperation.Link;
args.Data.SetDataProvider(StandardDataFormats.StorageItems, async request =>
{
if (Directory.Exists(dragPath))
var deferral = SafetyExtensions.IgnoreExceptions(() => request.GetDeferral(), null, typeof(COMException));
try
{
var folder = await StorageFolder.GetFolderFromPathAsync(dragPath);
request.SetData(new IStorageItem[] { folder });
if (Directory.Exists(dragPath))
{
var folder = await StorageFolder.GetFolderFromPathAsync(dragPath);
request.SetData(new IStorageItem[] { folder });
}
}
}
catch
{
}
finally
{
deferral.Complete();
}
});
finally
{
if (deferral is not null)
SafetyExtensions.IgnoreExceptions(() => deferral.Complete(), null, typeof(COMException));
}
});
}, null, typeof(COMException));
}

private void SetFlyoutOpen(bool isOpen = true)
Expand Down Expand Up @@ -464,58 +484,92 @@ private void Item_PointerReleased(object sender, Microsoft.UI.Xaml.Input.Pointer

private async void ItemBorder_DragOver(object sender, DragEventArgs e)
{
var insertsAbove = DetermineDropTargetPosition(e);
if (insertsAbove == SidebarItemDropPosition.Center)
{
VisualStateManager.GoToState(this, "DragOnTop", true);
}
else if (insertsAbove == SidebarItemDropPosition.Top)
{
VisualStateManager.GoToState(this, "DragInsertAbove", true);
}
else if (insertsAbove == SidebarItemDropPosition.Bottom)
// Expected to fail with COMException if the OLE drag payload is stale
var deferral = SafetyExtensions.IgnoreExceptions(() => e.GetDeferral(), null, typeof(COMException));

try
{
VisualStateManager.GoToState(this, "DragInsertBelow", true);
}
var dropPosition = DetermineDropTargetPosition(e);

Owner?.RaiseItemDragOver(this, insertsAbove, e);
if (Owner is not null)
Owner.RaiseItemDragOver(this, dropPosition, e);

var openDelay = Owner?.HoverToOpenDelay ?? TimeSpan.Zero;
var expandDelay = Owner?.HoverToExpandDelay ?? TimeSpan.Zero;
var isCenter = insertsAbove == SidebarItemDropPosition.Center;
var canHoverOpen = openDelay > TimeSpan.Zero && isCenter && Item is not null && (!IsGroupHeader || Item.IsLeafWithChildren);
var canHoverExpand = expandDelay > TimeSpan.Zero && isCenter && HasChildren && CollapseEnabled;
if (canHoverExpand)
{
dragOverExpandTimer ??= DispatcherQueue.CreateTimer();
dragOverExpandTimer.Debounce(
() =>
{
dragOverExpandTimer!.Stop();
IsExpanded = true;
},
expandDelay,
false);
}
else
{
dragOverExpandTimer?.Stop();
}
if (canHoverOpen)
{
dragOverTimer ??= DispatcherQueue.CreateTimer();
dragOverTimer.Debounce(
() =>
{
dragOverTimer!.Stop();
RaiseItemInvoked(PointerUpdateKind.Other);
},
openDelay,
false);
bool isHandled = false;
DataPackageOperation acceptedOperation = DataPackageOperation.None;

var propertiesRead = SafetyExtensions.IgnoreExceptions(() =>
{
isHandled = e.Handled;
acceptedOperation = e.AcceptedOperation;
}, null, typeof(COMException));

if (dropPosition != lastDropPosition)
{
acceptedOperation = DataPackageOperation.None;
}
lastDropPosition = dropPosition;

if (!propertiesRead || !isHandled || acceptedOperation == DataPackageOperation.None)
{
VisualStateManager.GoToState(this, "Normal", true);
return;
}

if (dropPosition == SidebarItemDropPosition.Center)
{
VisualStateManager.GoToState(this, "DragOnTop", true);
}
else if (dropPosition == SidebarItemDropPosition.Top)
{
VisualStateManager.GoToState(this, "DragInsertAbove", true);
}
else if (dropPosition == SidebarItemDropPosition.Bottom)
{
VisualStateManager.GoToState(this, "DragInsertBelow", true);
}

var openDelay = Owner?.HoverToOpenDelay ?? TimeSpan.Zero;
var expandDelay = Owner?.HoverToExpandDelay ?? TimeSpan.Zero;
var isCenter = dropPosition == SidebarItemDropPosition.Center;
var canHoverOpen = openDelay > TimeSpan.Zero && isCenter && Item is not null && (!IsGroupHeader || Item.IsLeafWithChildren);
var canHoverExpand = expandDelay > TimeSpan.Zero && isCenter && HasChildren && CollapseEnabled;
if (canHoverExpand)
{
dragOverExpandTimer ??= DispatcherQueue.CreateTimer();
dragOverExpandTimer.Debounce(
() =>
{
dragOverExpandTimer!.Stop();
IsExpanded = true;
},
expandDelay,
false);
}
else
{
dragOverExpandTimer?.Stop();
}
if (canHoverOpen)
{
dragOverTimer ??= DispatcherQueue.CreateTimer();
dragOverTimer.Debounce(
() =>
{
dragOverTimer!.Stop();
RaiseItemInvoked(PointerUpdateKind.Other);
},
openDelay,
false);
}
else
{
dragOverTimer?.Stop();
}
}
else
finally
{
dragOverTimer?.Stop();
if (deferral is not null)
SafetyExtensions.IgnoreExceptions(() => deferral.Complete(), null, typeof(COMException));
}
}

Expand All @@ -529,13 +583,15 @@ private void ItemBorder_DragLeave(object sender, DragEventArgs e)
{
dragOverTimer?.Stop();
dragOverExpandTimer?.Stop();
lastDropPosition = SidebarItemDropPosition.Center;
UpdatePointerState();
}

private void ItemBorder_Drop(object sender, DragEventArgs e)
{
dragOverTimer?.Stop();
dragOverExpandTimer?.Stop();
lastDropPosition = SidebarItemDropPosition.Center;
UpdatePointerState();
Owner?.RaiseItemDropped(this, DetermineDropTargetPosition(e), e);
}
Expand Down
4 changes: 1 addition & 3 deletions src/Files.App.Controls/Sidebar/SidebarStyles.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Copyright (c) Files Community. Licensed under the MIT License. -->
<!-- Copyright (c) Files Community. Licensed under the MIT License. -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Expand Down Expand Up @@ -350,8 +350,6 @@
<VisualState.Setters>
<Setter Target="DragTargetIndicator.Visibility" Value="Visible" />
<Setter Target="DragTargetIndicator.VerticalAlignment" Value="Top" />
<Setter Target="ElementGrid.Margin" Value="4,-2" />
<Setter Target="ElementGrid.Height" Value="34" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="DragInsertBelow">
Expand Down
17 changes: 15 additions & 2 deletions src/Files.App.Controls/Sidebar/SidebarView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using Microsoft.UI.Input;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Markup;
using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation;
using Windows.System;
using System.Runtime.InteropServices;
using Windows.UI.Core;

namespace Files.App.Controls
Expand Down Expand Up @@ -51,13 +53,24 @@ internal void RaiseContextRequested(SidebarItem item, Point e)
internal void RaiseItemDropped(SidebarItem sideBarItem, SidebarItemDropPosition dropPosition, DragEventArgs rawEvent)
{
if (sideBarItem.Item is null) return;
ItemDropped?.Invoke(this, new(sideBarItem.Item, rawEvent.DataView, dropPosition, rawEvent));

// Expected to fail with COMException if the OLE drag payload is stale
var dataView = SafetyExtensions.IgnoreExceptions(() => rawEvent.DataView, null, typeof(COMException));
if (dataView is null) return;

ItemDropped?.Invoke(this, new(sideBarItem.Item, dataView, dropPosition, rawEvent));
}

internal void RaiseItemDragOver(SidebarItem sideBarItem, SidebarItemDropPosition dropPosition, DragEventArgs rawEvent)
{
if (sideBarItem.Item is null) return;
ItemDragOver?.Invoke(this, new(sideBarItem.Item, rawEvent.DataView, dropPosition, rawEvent));

// Expected to fail with COMException if the OLE drag payload is stale
var dataView = SafetyExtensions.IgnoreExceptions(() => rawEvent.DataView, null, typeof(COMException));
if (dataView is null) return;

var args = new ItemDragOverEventArgs(sideBarItem.Item, dataView, dropPosition, rawEvent);
ItemDragOver?.Invoke(this, args);
}

private void UpdateMinimalMode()
Expand Down
2 changes: 2 additions & 0 deletions src/Files.App/Data/Contracts/INavigationControlItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public interface INavigationControlItem : IComparable<INavigationControlItem>, I

public string Path { get; }

bool ISidebarItemModel.IsReorderDropItem => Section == SectionType.Pinned;

public SectionType Section { get; }

public NavigationControlItemType ItemType { get; }
Expand Down
2 changes: 2 additions & 0 deletions src/Files.App/Data/Items/LocationItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public string Path
ToolTip = string.IsNullOrEmpty(Path) ||
Path.Contains('?', StringComparison.Ordinal) ||
Path.StartsWith("shell:", StringComparison.OrdinalIgnoreCase) ||
Path.StartsWith("::{", StringComparison.Ordinal) ||
Path.StartsWith(@"\\SHELL\", StringComparison.OrdinalIgnoreCase) ||
Path.EndsWith(ShellLibraryItem.EXTENSION, StringComparison.OrdinalIgnoreCase) ||
Path == "Home" ||
Path == "ReleaseNotes" ||
Expand Down
Loading
Loading