-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Feature: Added Copy functionality to sidebar and home page widgets #17969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
yaira2
merged 8 commits into
files-community:main
from
workbysaran:sg/feature-sidebar-copy
Dec 19, 2025
+303
−5
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7ea052e
Feature: Copy option to right click menu for sidebar
workbysaran 565563f
Feature: Copy option to right click menu for home screen widgets.
workbysaran 0004c41
Fix: Primary commands (icon buttons) were not displaying in sidebar a…
workbysaran a35dc01
Fix: Disabled the copy option in the right-click menu for the Tags se…
workbysaran 83abb04
Fix: Context menu not closing after clicking primary commands in side…
workbysaran 1ae9656
Fix: Copy button showing for non-copyable locations in home widget co…
workbysaran d5eb2d5
Fix: Revert the changes related to CommandBarFlyout Placement setting.
workbysaran 93a3033
Fix: Added the Copy option to the right-click menu for Recent Files (…
workbysaran File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
118 changes: 118 additions & 0 deletions
118
src/Files.App/Actions/FileSystem/CopyItemFromHomeAction.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| // Copyright (c) Files Community | ||
| // Licensed under the MIT License. | ||
|
|
||
| using Microsoft.Extensions.Logging; | ||
| using System.IO; | ||
| using Windows.ApplicationModel.DataTransfer; | ||
| using Windows.Storage; | ||
|
|
||
| namespace Files.App.Actions | ||
| { | ||
| [GeneratedRichCommand] | ||
| internal sealed partial class CopyItemFromHomeAction : ObservableObject, IAction | ||
| { | ||
| private readonly IContentPageContext context; | ||
| private readonly IHomePageContext HomePageContext; | ||
|
|
||
| public string Label | ||
| => Strings.Copy.GetLocalizedResource(); | ||
|
|
||
| public string Description | ||
| => Strings.CopyItemDescription.GetLocalizedFormatResource(1); | ||
|
|
||
| public RichGlyph Glyph | ||
| => new(themedIconStyle: "App.ThemedIcons.Copy"); | ||
| public bool IsExecutable | ||
| => GetIsExecutable(); | ||
|
|
||
| public CopyItemFromHomeAction() | ||
| { | ||
| context = Ioc.Default.GetRequiredService<IContentPageContext>(); | ||
| HomePageContext = Ioc.Default.GetRequiredService<IHomePageContext>(); | ||
| } | ||
|
|
||
| public async Task ExecuteAsync(object? parameter = null) | ||
| { | ||
| if (HomePageContext.RightClickedItem is null) | ||
| return; | ||
|
|
||
| var item = HomePageContext.RightClickedItem; | ||
| var itemPath = item.Path; | ||
|
|
||
| if (string.IsNullOrEmpty(itemPath)) | ||
| return; | ||
|
|
||
| try | ||
| { | ||
| var dataPackage = new DataPackage() { RequestedOperation = DataPackageOperation.Copy }; | ||
| IStorageItem? storageItem = null; | ||
|
|
||
| var folderResult = await context.ShellPage?.ShellViewModel?.GetFolderFromPathAsync(itemPath)!; | ||
| if (folderResult) | ||
| storageItem = folderResult.Result; | ||
|
|
||
| if (storageItem is null) | ||
| { | ||
| await CopyPathFallback(itemPath); | ||
| return; | ||
| } | ||
|
|
||
| if (storageItem is SystemStorageFolder or SystemStorageFile) | ||
| { | ||
| var standardItems = await new[] { storageItem }.ToStandardStorageItemsAsync(); | ||
| if (standardItems.Any()) | ||
| storageItem = standardItems.First(); | ||
| } | ||
|
|
||
| dataPackage.Properties.PackageFamilyName = Windows.ApplicationModel.Package.Current.Id.FamilyName; | ||
| dataPackage.SetStorageItems(new[] { storageItem }, false); | ||
|
|
||
| Clipboard.SetContent(dataPackage); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| if ((FileSystemStatusCode)ex.HResult is FileSystemStatusCode.Unauthorized) | ||
| { | ||
| await CopyPathFallback(itemPath); | ||
| return; | ||
| } | ||
|
|
||
| } | ||
| } | ||
|
|
||
| private bool GetIsExecutable() | ||
| { | ||
| var item = HomePageContext.RightClickedItem; | ||
|
|
||
| return HomePageContext.IsAnyItemRightClicked | ||
| && item is not null | ||
| && !IsNonCopyableLocation(item); | ||
| } | ||
|
|
||
| private async Task CopyPathFallback(string path) | ||
| { | ||
| try | ||
| { | ||
| await FileOperationsHelpers.SetClipboard(new[] { path }, DataPackageOperation.Copy); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| App.Logger.LogWarning(ex, "Failed to copy path to clipboard."); | ||
| } | ||
| } | ||
|
|
||
| private bool IsNonCopyableLocation(WidgetCardItem item) | ||
| { | ||
| if (string.IsNullOrEmpty(item.Path)) | ||
| return true; | ||
|
|
||
| var normalizedPath = Constants.UserEnvironmentPaths.ShellPlaces.GetValueOrDefault( | ||
| item.Path.ToUpperInvariant(), | ||
| item.Path); | ||
|
|
||
| return string.Equals(normalizedPath, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase) || | ||
| string.Equals(normalizedPath, Constants.UserEnvironmentPaths.NetworkFolderPath, StringComparison.OrdinalIgnoreCase) || | ||
| string.Equals(normalizedPath, Constants.UserEnvironmentPaths.MyComputerPath, StringComparison.OrdinalIgnoreCase); | ||
| } | ||
| } | ||
| } | ||
121 changes: 121 additions & 0 deletions
121
src/Files.App/Actions/Sidebar/CopyItemFromSidebarAction.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| // Copyright (c) Files Community | ||
| // Licensed under the MIT License. | ||
|
|
||
| using Microsoft.Extensions.Logging; | ||
| using System.IO; | ||
| using Windows.ApplicationModel.DataTransfer; | ||
| using Windows.Storage; | ||
|
|
||
| namespace Files.App.Actions | ||
| { | ||
| [GeneratedRichCommand] | ||
| internal sealed partial class CopyItemFromSidebarAction : ObservableObject, IAction | ||
| { | ||
| private readonly IContentPageContext context; | ||
| private readonly ISidebarContext SidebarContext; | ||
|
|
||
| public string Label | ||
| => Strings.Copy.GetLocalizedResource(); | ||
|
|
||
| public string Description | ||
| => Strings.CopyItemDescription.GetLocalizedFormatResource(1); | ||
|
|
||
| public RichGlyph Glyph | ||
| => new(themedIconStyle: "App.ThemedIcons.Copy"); | ||
| public bool IsExecutable | ||
| => GetIsExecutable(); | ||
|
|
||
| public CopyItemFromSidebarAction() | ||
| { | ||
| context = Ioc.Default.GetRequiredService<IContentPageContext>(); | ||
| SidebarContext = Ioc.Default.GetRequiredService<ISidebarContext>(); | ||
| } | ||
|
|
||
| public async Task ExecuteAsync(object? parameter = null) | ||
| { | ||
| if (SidebarContext.RightClickedItem is null) | ||
| return; | ||
|
|
||
| var item = SidebarContext.RightClickedItem; | ||
| var itemPath = item.Path; | ||
|
|
||
| if (string.IsNullOrEmpty(itemPath)) | ||
| return; | ||
|
|
||
| try | ||
| { | ||
| var dataPackage = new DataPackage() { RequestedOperation = DataPackageOperation.Copy }; | ||
| IStorageItem? storageItem = null; | ||
|
|
||
| var folderResult = await context.ShellPage?.ShellViewModel?.GetFolderFromPathAsync(itemPath)!; | ||
workbysaran marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (folderResult) | ||
| storageItem = folderResult.Result; | ||
|
|
||
| if (storageItem is null) | ||
| { | ||
| await CopyPathFallback(itemPath); | ||
| return; | ||
| } | ||
|
|
||
| if (storageItem is SystemStorageFolder or SystemStorageFile) | ||
| { | ||
| var standardItems = await new[] { storageItem }.ToStandardStorageItemsAsync(); | ||
| if (standardItems.Any()) | ||
| storageItem = standardItems.First(); | ||
| } | ||
|
|
||
| dataPackage.Properties.PackageFamilyName = Windows.ApplicationModel.Package.Current.Id.FamilyName; | ||
| dataPackage.SetStorageItems(new[] { storageItem }, false); | ||
|
|
||
| Clipboard.SetContent(dataPackage); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| if ((FileSystemStatusCode)ex.HResult is FileSystemStatusCode.Unauthorized) | ||
| { | ||
| await CopyPathFallback(itemPath); | ||
| return; | ||
| } | ||
|
|
||
| } | ||
| } | ||
|
|
||
| private bool GetIsExecutable() | ||
| { | ||
| var item = SidebarContext.RightClickedItem; | ||
|
|
||
| return SidebarContext.IsItemRightClicked | ||
| && item is not null | ||
| && item.MenuOptions.IsLocationItem | ||
| && !IsNonCopyableLocation(item); | ||
| } | ||
|
|
||
| private async Task CopyPathFallback(string path) | ||
| { | ||
| try | ||
| { | ||
| await FileOperationsHelpers.SetClipboard(new[] { path }, DataPackageOperation.Copy); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| App.Logger.LogWarning(ex, "Failed to copy path to clipboard."); | ||
| } | ||
| } | ||
|
|
||
| private bool IsNonCopyableLocation(INavigationControlItem item) | ||
| { | ||
| if (string.IsNullOrEmpty(item.Path)) | ||
| return true; | ||
|
|
||
| var normalizedPath = Constants.UserEnvironmentPaths.ShellPlaces.GetValueOrDefault( | ||
| item.Path.ToUpperInvariant(), | ||
| item.Path); | ||
|
|
||
| return item.Path.StartsWith("tag:", StringComparison.OrdinalIgnoreCase) || | ||
| string.Equals(item.Path, "Home", StringComparison.OrdinalIgnoreCase) || | ||
| string.Equals(normalizedPath, Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase) || | ||
| string.Equals(normalizedPath, Constants.UserEnvironmentPaths.NetworkFolderPath, StringComparison.OrdinalIgnoreCase) || | ||
| string.Equals(normalizedPath, Constants.UserEnvironmentPaths.MyComputerPath, StringComparison.OrdinalIgnoreCase); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.